1 /*! jQuery UI - v1.10.3 - 2013-12-25
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.autocomplete.js, jquery.ui.menu.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js
4 * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // $.ui might exist from components with no dependencies, e.g., $.ui.position
45 focus: (function( orig ) {
46 return function( delay, fn ) {
47 return typeof delay === "number" ?
48 this.each(function() {
50 setTimeout(function() {
57 orig.apply( this, arguments );
61 scrollParent: function() {
63 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
64 scrollParent = this.parents().filter(function() {
65 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
68 scrollParent = this.parents().filter(function() {
69 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
73 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
76 zIndex: function( zIndex ) {
77 if ( zIndex !== undefined ) {
78 return this.css( "zIndex", zIndex );
82 var elem = $( this[ 0 ] ), position, value;
83 while ( elem.length && elem[ 0 ] !== document ) {
84 // Ignore z-index if position is set to a value where z-index is ignored by the browser
85 // This makes behavior of this function consistent across browsers
86 // WebKit always returns auto if the element is positioned
87 position = elem.css( "position" );
88 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
89 // IE returns 0 when zIndex is not specified
90 // other browsers return a string
91 // we ignore the case of nested elements with an explicit value of 0
92 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
93 value = parseInt( elem.css( "zIndex" ), 10 );
94 if ( !isNaN( value ) && value !== 0 ) {
105 uniqueId: function() {
106 return this.each(function() {
108 this.id = "ui-id-" + (++uuid);
113 removeUniqueId: function() {
114 return this.each(function() {
115 if ( runiqueId.test( this.id ) ) {
116 $( this ).removeAttr( "id" );
123 function focusable( element, isTabIndexNotNaN ) {
124 var map, mapName, img,
125 nodeName = element.nodeName.toLowerCase();
126 if ( "area" === nodeName ) {
127 map = element.parentNode;
129 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
132 img = $( "img[usemap=#" + mapName + "]" )[0];
133 return !!img && visible( img );
135 return ( /input|select|textarea|button|object/.test( nodeName ) ?
138 element.href || isTabIndexNotNaN :
140 // the element and all of its ancestors must be visible
144 function visible( element ) {
145 return $.expr.filters.visible( element ) &&
146 !$( element ).parents().addBack().filter(function() {
147 return $.css( this, "visibility" ) === "hidden";
151 $.extend( $.expr[ ":" ], {
152 data: $.expr.createPseudo ?
153 $.expr.createPseudo(function( dataName ) {
154 return function( elem ) {
155 return !!$.data( elem, dataName );
158 // support: jQuery <1.8
159 function( elem, i, match ) {
160 return !!$.data( elem, match[ 3 ] );
163 focusable: function( element ) {
164 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
167 tabbable: function( element ) {
168 var tabIndex = $.attr( element, "tabindex" ),
169 isTabIndexNaN = isNaN( tabIndex );
170 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
174 // support: jQuery <1.8
175 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
176 $.each( [ "Width", "Height" ], function( i, name ) {
177 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
178 type = name.toLowerCase(),
180 innerWidth: $.fn.innerWidth,
181 innerHeight: $.fn.innerHeight,
182 outerWidth: $.fn.outerWidth,
183 outerHeight: $.fn.outerHeight
186 function reduce( elem, size, border, margin ) {
187 $.each( side, function() {
188 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
190 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
193 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
199 $.fn[ "inner" + name ] = function( size ) {
200 if ( size === undefined ) {
201 return orig[ "inner" + name ].call( this );
204 return this.each(function() {
205 $( this ).css( type, reduce( this, size ) + "px" );
209 $.fn[ "outer" + name] = function( size, margin ) {
210 if ( typeof size !== "number" ) {
211 return orig[ "outer" + name ].call( this, size );
214 return this.each(function() {
215 $( this).css( type, reduce( this, size, true, margin ) + "px" );
221 // support: jQuery <1.8
222 if ( !$.fn.addBack ) {
223 $.fn.addBack = function( selector ) {
224 return this.add( selector == null ?
225 this.prevObject : this.prevObject.filter( selector )
230 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
231 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
232 $.fn.removeData = (function( removeData ) {
233 return function( key ) {
234 if ( arguments.length ) {
235 return removeData.call( this, $.camelCase( key ) );
237 return removeData.call( this );
240 })( $.fn.removeData );
248 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
250 $.support.selectstart = "onselectstart" in document.createElement( "div" );
252 disableSelection: function() {
253 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
254 ".ui-disableSelection", function( event ) {
255 event.preventDefault();
259 enableSelection: function() {
260 return this.unbind( ".ui-disableSelection" );
265 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
267 add: function( module, option, set ) {
269 proto = $.ui[ module ].prototype;
271 proto.plugins[ i ] = proto.plugins[ i ] || [];
272 proto.plugins[ i ].push( [ option, set[ i ] ] );
275 call: function( instance, name, args ) {
277 set = instance.plugins[ name ];
278 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
282 for ( i = 0; i < set.length; i++ ) {
283 if ( instance.options[ set[ i ][ 0 ] ] ) {
284 set[ i ][ 1 ].apply( instance.element, args );
290 // only used by resizable
291 hasScroll: function( el, a ) {
293 //If overflow is hidden, the element might have extra content, but the user wants to hide it
294 if ( $( el ).css( "overflow" ) === "hidden") {
298 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
301 if ( el[ scroll ] > 0 ) {
305 // TODO: determine which cases actually cause this to happen
306 // if the element doesn't have the scroll set, see if it's possible to
309 has = ( el[ scroll ] > 0 );
316 (function( $, undefined ) {
319 slice = Array.prototype.slice,
320 _cleanData = $.cleanData;
321 $.cleanData = function( elems ) {
322 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
324 $( elem ).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
331 $.widget = function( name, base, prototype ) {
332 var fullName, existingConstructor, constructor, basePrototype,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype = {},
336 namespace = name.split( "." )[ 0 ];
338 name = name.split( "." )[ 1 ];
339 fullName = namespace + "-" + name;
346 // create selector for plugin
347 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348 return !!$.data( elem, fullName );
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor = $[ namespace ][ name ];
353 constructor = $[ namespace ][ name ] = function( options, element ) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget ) {
356 return new constructor( options, element );
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments.length ) {
362 this._createWidget( options, element );
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor, {
367 version: prototype.version,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype = new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
380 basePrototype.options = $.widget.extend( {}, basePrototype.options );
381 $.each( prototype, function( prop, value ) {
382 if ( !$.isFunction( value ) ) {
383 proxiedPrototype[ prop ] = value;
386 proxiedPrototype[ prop ] = (function() {
387 var _super = function() {
388 return base.prototype[ prop ].apply( this, arguments );
390 _superApply = function( args ) {
391 return base.prototype[ prop ].apply( this, args );
394 var __super = this._super,
395 __superApply = this._superApply,
398 this._super = _super;
399 this._superApply = _superApply;
401 returnValue = value.apply( this, arguments );
403 this._super = __super;
404 this._superApply = __superApply;
410 constructor.prototype = $.widget.extend( basePrototype, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
415 }, proxiedPrototype, {
416 constructor: constructor,
417 namespace: namespace,
419 widgetFullName: fullName
422 // If this widget is being redefined then we need to find all widgets that
423 // are inheriting from it and redefine all of them so that they inherit from
424 // the new version of this widget. We're essentially trying to replace one
425 // level in the prototype chain.
426 if ( existingConstructor ) {
427 $.each( existingConstructor._childConstructors, function( i, child ) {
428 var childPrototype = child.prototype;
430 // redefine the child widget using the same prototype that was
431 // originally used, but inherit from the new version of the base
432 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
434 // remove the list of existing child constructors from the old constructor
435 // so the old child constructors can be garbage collected
436 delete existingConstructor._childConstructors;
438 base._childConstructors.push( constructor );
441 $.widget.bridge( name, constructor );
444 $.widget.extend = function( target ) {
445 var input = slice.call( arguments, 1 ),
447 inputLength = input.length,
450 for ( ; inputIndex < inputLength; inputIndex++ ) {
451 for ( key in input[ inputIndex ] ) {
452 value = input[ inputIndex ][ key ];
453 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
455 if ( $.isPlainObject( value ) ) {
456 target[ key ] = $.isPlainObject( target[ key ] ) ?
457 $.widget.extend( {}, target[ key ], value ) :
458 // Don't extend strings, arrays, etc. with objects
459 $.widget.extend( {}, value );
460 // Copy everything else by reference
462 target[ key ] = value;
470 $.widget.bridge = function( name, object ) {
471 var fullName = object.prototype.widgetFullName || name;
472 $.fn[ name ] = function( options ) {
473 var isMethodCall = typeof options === "string",
474 args = slice.call( arguments, 1 ),
477 // allow multiple hashes to be passed on init
478 options = !isMethodCall && args.length ?
479 $.widget.extend.apply( null, [ options ].concat(args) ) :
482 if ( isMethodCall ) {
483 this.each(function() {
485 instance = $.data( this, fullName );
487 return $.error( "cannot call methods on " + name + " prior to initialization; " +
488 "attempted to call method '" + options + "'" );
490 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
491 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
493 methodValue = instance[ options ].apply( instance, args );
494 if ( methodValue !== instance && methodValue !== undefined ) {
495 returnValue = methodValue && methodValue.jquery ?
496 returnValue.pushStack( methodValue.get() ) :
502 this.each(function() {
503 var instance = $.data( this, fullName );
505 instance.option( options || {} )._init();
507 $.data( this, fullName, new object( options, this ) );
516 $.Widget = function( /* options, element */ ) {};
517 $.Widget._childConstructors = [];
519 $.Widget.prototype = {
520 widgetName: "widget",
521 widgetEventPrefix: "",
522 defaultElement: "<div>",
529 _createWidget: function( options, element ) {
530 element = $( element || this.defaultElement || this )[ 0 ];
531 this.element = $( element );
533 this.eventNamespace = "." + this.widgetName + this.uuid;
534 this.options = $.widget.extend( {},
536 this._getCreateOptions(),
540 this.hoverable = $();
541 this.focusable = $();
543 if ( element !== this ) {
544 $.data( element, this.widgetFullName, this );
545 this._on( true, this.element, {
546 remove: function( event ) {
547 if ( event.target === element ) {
552 this.document = $( element.style ?
553 // element within the document
554 element.ownerDocument :
555 // element is window or document
556 element.document || element );
557 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
561 this._trigger( "create", null, this._getCreateEventData() );
564 _getCreateOptions: $.noop,
565 _getCreateEventData: $.noop,
569 destroy: function() {
571 // we can probably remove the unbind calls in 2.0
572 // all event bindings should go through this._on()
574 .unbind( this.eventNamespace )
576 // TODO remove dual storage
577 .removeData( this.widgetName )
578 .removeData( this.widgetFullName )
579 // support: jquery <1.6.3
580 // http://bugs.jquery.com/ticket/9413
581 .removeData( $.camelCase( this.widgetFullName ) );
583 .unbind( this.eventNamespace )
584 .removeAttr( "aria-disabled" )
586 this.widgetFullName + "-disabled " +
587 "ui-state-disabled" );
589 // clean up events and states
590 this.bindings.unbind( this.eventNamespace );
591 this.hoverable.removeClass( "ui-state-hover" );
592 this.focusable.removeClass( "ui-state-focus" );
600 option: function( key, value ) {
606 if ( arguments.length === 0 ) {
607 // don't return a reference to the internal hash
608 return $.widget.extend( {}, this.options );
611 if ( typeof key === "string" ) {
612 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
614 parts = key.split( "." );
616 if ( parts.length ) {
617 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
618 for ( i = 0; i < parts.length - 1; i++ ) {
619 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
620 curOption = curOption[ parts[ i ] ];
623 if ( value === undefined ) {
624 return curOption[ key ] === undefined ? null : curOption[ key ];
626 curOption[ key ] = value;
628 if ( value === undefined ) {
629 return this.options[ key ] === undefined ? null : this.options[ key ];
631 options[ key ] = value;
635 this._setOptions( options );
639 _setOptions: function( options ) {
642 for ( key in options ) {
643 this._setOption( key, options[ key ] );
648 _setOption: function( key, value ) {
649 this.options[ key ] = value;
651 if ( key === "disabled" ) {
653 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
654 .attr( "aria-disabled", value );
655 this.hoverable.removeClass( "ui-state-hover" );
656 this.focusable.removeClass( "ui-state-focus" );
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck, element, handlers ) {
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck !== "boolean" ) {
676 element = suppressDisabledCheck;
677 suppressDisabledCheck = false;
680 // no element argument, shuffle and use this.element
683 element = this.element;
684 delegateElement = this.widget();
686 // accept selectors, DOM elements
687 element = delegateElement = $( element );
688 this.bindings = this.bindings.add( element );
691 $.each( handlers, function( event, handler ) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck &&
697 ( instance.options.disabled === true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
701 return ( typeof handler === "string" ? instance[ handler ] : handler )
702 .apply( instance, arguments );
705 // copy the guid so direct unbinding works
706 if ( typeof handler !== "string" ) {
707 handlerProxy.guid = handler.guid =
708 handler.guid || handlerProxy.guid || $.guid++;
711 var match = event.match( /^(\w+)\s*(.*)$/ ),
712 eventName = match[1] + instance.eventNamespace,
715 delegateElement.delegate( selector, eventName, handlerProxy );
717 element.bind( eventName, handlerProxy );
722 _off: function( element, eventName ) {
723 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724 element.unbind( eventName ).undelegate( eventName );
727 _delay: function( handler, delay ) {
728 function handlerProxy() {
729 return ( typeof handler === "string" ? instance[ handler ] : handler )
730 .apply( instance, arguments );
733 return setTimeout( handlerProxy, delay || 0 );
736 _hoverable: function( element ) {
737 this.hoverable = this.hoverable.add( element );
739 mouseenter: function( event ) {
740 $( event.currentTarget ).addClass( "ui-state-hover" );
742 mouseleave: function( event ) {
743 $( event.currentTarget ).removeClass( "ui-state-hover" );
748 _focusable: function( element ) {
749 this.focusable = this.focusable.add( element );
751 focusin: function( event ) {
752 $( event.currentTarget ).addClass( "ui-state-focus" );
754 focusout: function( event ) {
755 $( event.currentTarget ).removeClass( "ui-state-focus" );
760 _trigger: function( type, event, data ) {
762 callback = this.options[ type ];
765 event = $.Event( event );
766 event.type = ( type === this.widgetEventPrefix ?
768 this.widgetEventPrefix + type ).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event.target = this.element[ 0 ];
773 // copy original event properties over to the new event
774 orig = event.originalEvent;
776 for ( prop in orig ) {
777 if ( !( prop in event ) ) {
778 event[ prop ] = orig[ prop ];
783 this.element.trigger( event, data );
784 return !( $.isFunction( callback ) &&
785 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
786 event.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792 if ( typeof options === "string" ) {
793 options = { effect: options };
796 effectName = !options ?
798 options === true || typeof options === "number" ?
800 options.effect || defaultEffect;
801 options = options || {};
802 if ( typeof options === "number" ) {
803 options = { duration: options };
805 hasOptions = !$.isEmptyObject( options );
806 options.complete = callback;
807 if ( options.delay ) {
808 element.delay( options.delay );
810 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811 element[ method ]( options );
812 } else if ( effectName !== method && element[ effectName ] ) {
813 element[ effectName ]( options.duration, options.easing, callback );
815 element.queue(function( next ) {
816 $( this )[ method ]();
818 callback.call( element[ 0 ] );
827 (function( $, undefined ) {
829 var mouseHandled = false;
830 $( document ).mouseup( function() {
831 mouseHandled = false;
834 $.widget("ui.mouse", {
837 cancel: "input,textarea,button,select,option",
841 _mouseInit: function() {
845 .bind("mousedown."+this.widgetName, function(event) {
846 return that._mouseDown(event);
848 .bind("click."+this.widgetName, function(event) {
849 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
850 $.removeData(event.target, that.widgetName + ".preventClickEvent");
851 event.stopImmediatePropagation();
856 this.started = false;
859 // TODO: make sure destroying one instance of mouse doesn't mess with
860 // other instances of mouse
861 _mouseDestroy: function() {
862 this.element.unbind("."+this.widgetName);
863 if ( this._mouseMoveDelegate ) {
865 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
866 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
870 _mouseDown: function(event) {
871 // don't let more than one widget handle mouseStart
872 if( mouseHandled ) { return; }
874 // we may have missed mouseup (out of window)
875 (this._mouseStarted && this._mouseUp(event));
877 this._mouseDownEvent = event;
880 btnIsLeft = (event.which === 1),
881 // event.target.nodeName works around a bug in IE 8 with
882 // disabled inputs (#7620)
883 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
884 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
888 this.mouseDelayMet = !this.options.delay;
889 if (!this.mouseDelayMet) {
890 this._mouseDelayTimer = setTimeout(function() {
891 that.mouseDelayMet = true;
892 }, this.options.delay);
895 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
896 this._mouseStarted = (this._mouseStart(event) !== false);
897 if (!this._mouseStarted) {
898 event.preventDefault();
903 // Click event may never have fired (Gecko & Opera)
904 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
905 $.removeData(event.target, this.widgetName + ".preventClickEvent");
908 // these delegates are required to keep context
909 this._mouseMoveDelegate = function(event) {
910 return that._mouseMove(event);
912 this._mouseUpDelegate = function(event) {
913 return that._mouseUp(event);
916 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
917 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
919 event.preventDefault();
925 _mouseMove: function(event) {
926 // IE mouseup check - mouseup happened when mouse was out of window
927 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
928 return this._mouseUp(event);
931 if (this._mouseStarted) {
932 this._mouseDrag(event);
933 return event.preventDefault();
936 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
938 (this._mouseStart(this._mouseDownEvent, event) !== false);
939 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
942 return !this._mouseStarted;
945 _mouseUp: function(event) {
947 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
948 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
950 if (this._mouseStarted) {
951 this._mouseStarted = false;
953 if (event.target === this._mouseDownEvent.target) {
954 $.data(event.target, this.widgetName + ".preventClickEvent", true);
957 this._mouseStop(event);
963 _mouseDistanceMet: function(event) {
965 Math.abs(this._mouseDownEvent.pageX - event.pageX),
966 Math.abs(this._mouseDownEvent.pageY - event.pageY)
967 ) >= this.options.distance
971 _mouseDelayMet: function(/* event */) {
972 return this.mouseDelayMet;
975 // These are placeholder methods, to be overriden by extending plugin
976 _mouseStart: function(/* event */) {},
977 _mouseDrag: function(/* event */) {},
978 _mouseStop: function(/* event */) {},
979 _mouseCapture: function(/* event */) { return true; }
983 (function( $, undefined ) {
987 var cachedScrollbarWidth,
991 rhorizontal = /left|center|right/,
992 rvertical = /top|center|bottom/,
993 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
996 _position = $.fn.position;
998 function getOffsets( offsets, width, height ) {
1000 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1001 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1005 function parseCss( element, property ) {
1006 return parseInt( $.css( element, property ), 10 ) || 0;
1009 function getDimensions( elem ) {
1011 if ( raw.nodeType === 9 ) {
1013 width: elem.width(),
1014 height: elem.height(),
1015 offset: { top: 0, left: 0 }
1018 if ( $.isWindow( raw ) ) {
1020 width: elem.width(),
1021 height: elem.height(),
1022 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
1025 if ( raw.preventDefault ) {
1029 offset: { top: raw.pageY, left: raw.pageX }
1033 width: elem.outerWidth(),
1034 height: elem.outerHeight(),
1035 offset: elem.offset()
1040 scrollbarWidth: function() {
1041 if ( cachedScrollbarWidth !== undefined ) {
1042 return cachedScrollbarWidth;
1045 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1046 innerDiv = div.children()[0];
1048 $( "body" ).append( div );
1049 w1 = innerDiv.offsetWidth;
1050 div.css( "overflow", "scroll" );
1052 w2 = innerDiv.offsetWidth;
1055 w2 = div[0].clientWidth;
1060 return (cachedScrollbarWidth = w1 - w2);
1062 getScrollInfo: function( within ) {
1063 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
1064 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
1065 hasOverflowX = overflowX === "scroll" ||
1066 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1067 hasOverflowY = overflowY === "scroll" ||
1068 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1070 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
1071 height: hasOverflowX ? $.position.scrollbarWidth() : 0
1074 getWithinInfo: function( element ) {
1075 var withinElement = $( element || window ),
1076 isWindow = $.isWindow( withinElement[0] );
1078 element: withinElement,
1080 offset: withinElement.offset() || { left: 0, top: 0 },
1081 scrollLeft: withinElement.scrollLeft(),
1082 scrollTop: withinElement.scrollTop(),
1083 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1084 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1089 $.fn.position = function( options ) {
1090 if ( !options || !options.of ) {
1091 return _position.apply( this, arguments );
1094 // make a copy, we don't want to modify arguments
1095 options = $.extend( {}, options );
1097 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
1098 target = $( options.of ),
1099 within = $.position.getWithinInfo( options.within ),
1100 scrollInfo = $.position.getScrollInfo( within ),
1101 collision = ( options.collision || "flip" ).split( " " ),
1104 dimensions = getDimensions( target );
1105 if ( target[0].preventDefault ) {
1106 // force left top to allow flipping
1107 options.at = "left top";
1109 targetWidth = dimensions.width;
1110 targetHeight = dimensions.height;
1111 targetOffset = dimensions.offset;
1112 // clone to reuse original targetOffset later
1113 basePosition = $.extend( {}, targetOffset );
1115 // force my and at to have valid horizontal and vertical positions
1116 // if a value is missing or invalid, it will be converted to center
1117 $.each( [ "my", "at" ], function() {
1118 var pos = ( options[ this ] || "" ).split( " " ),
1122 if ( pos.length === 1) {
1123 pos = rhorizontal.test( pos[ 0 ] ) ?
1124 pos.concat( [ "center" ] ) :
1125 rvertical.test( pos[ 0 ] ) ?
1126 [ "center" ].concat( pos ) :
1127 [ "center", "center" ];
1129 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1130 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1132 // calculate offsets
1133 horizontalOffset = roffset.exec( pos[ 0 ] );
1134 verticalOffset = roffset.exec( pos[ 1 ] );
1136 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1137 verticalOffset ? verticalOffset[ 0 ] : 0
1140 // reduce to just the positions without the offsets
1142 rposition.exec( pos[ 0 ] )[ 0 ],
1143 rposition.exec( pos[ 1 ] )[ 0 ]
1147 // normalize collision option
1148 if ( collision.length === 1 ) {
1149 collision[ 1 ] = collision[ 0 ];
1152 if ( options.at[ 0 ] === "right" ) {
1153 basePosition.left += targetWidth;
1154 } else if ( options.at[ 0 ] === "center" ) {
1155 basePosition.left += targetWidth / 2;
1158 if ( options.at[ 1 ] === "bottom" ) {
1159 basePosition.top += targetHeight;
1160 } else if ( options.at[ 1 ] === "center" ) {
1161 basePosition.top += targetHeight / 2;
1164 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1165 basePosition.left += atOffset[ 0 ];
1166 basePosition.top += atOffset[ 1 ];
1168 return this.each(function() {
1169 var collisionPosition, using,
1171 elemWidth = elem.outerWidth(),
1172 elemHeight = elem.outerHeight(),
1173 marginLeft = parseCss( this, "marginLeft" ),
1174 marginTop = parseCss( this, "marginTop" ),
1175 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1176 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1177 position = $.extend( {}, basePosition ),
1178 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1180 if ( options.my[ 0 ] === "right" ) {
1181 position.left -= elemWidth;
1182 } else if ( options.my[ 0 ] === "center" ) {
1183 position.left -= elemWidth / 2;
1186 if ( options.my[ 1 ] === "bottom" ) {
1187 position.top -= elemHeight;
1188 } else if ( options.my[ 1 ] === "center" ) {
1189 position.top -= elemHeight / 2;
1192 position.left += myOffset[ 0 ];
1193 position.top += myOffset[ 1 ];
1195 // if the browser doesn't support fractions, then round for consistent results
1196 if ( !$.support.offsetFractions ) {
1197 position.left = round( position.left );
1198 position.top = round( position.top );
1201 collisionPosition = {
1202 marginLeft: marginLeft,
1203 marginTop: marginTop
1206 $.each( [ "left", "top" ], function( i, dir ) {
1207 if ( $.ui.position[ collision[ i ] ] ) {
1208 $.ui.position[ collision[ i ] ][ dir ]( position, {
1209 targetWidth: targetWidth,
1210 targetHeight: targetHeight,
1211 elemWidth: elemWidth,
1212 elemHeight: elemHeight,
1213 collisionPosition: collisionPosition,
1214 collisionWidth: collisionWidth,
1215 collisionHeight: collisionHeight,
1216 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1225 if ( options.using ) {
1226 // adds feedback as second argument to using callback, if present
1227 using = function( props ) {
1228 var left = targetOffset.left - position.left,
1229 right = left + targetWidth - elemWidth,
1230 top = targetOffset.top - position.top,
1231 bottom = top + targetHeight - elemHeight,
1235 left: targetOffset.left,
1236 top: targetOffset.top,
1238 height: targetHeight
1242 left: position.left,
1247 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1248 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1250 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1251 feedback.horizontal = "center";
1253 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1254 feedback.vertical = "middle";
1256 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1257 feedback.important = "horizontal";
1259 feedback.important = "vertical";
1261 options.using.call( this, props, feedback );
1265 elem.offset( $.extend( position, { using: using } ) );
1271 left: function( position, data ) {
1272 var within = data.within,
1273 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1274 outerWidth = within.width,
1275 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1276 overLeft = withinOffset - collisionPosLeft,
1277 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1280 // element is wider than within
1281 if ( data.collisionWidth > outerWidth ) {
1282 // element is initially over the left side of within
1283 if ( overLeft > 0 && overRight <= 0 ) {
1284 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1285 position.left += overLeft - newOverRight;
1286 // element is initially over right side of within
1287 } else if ( overRight > 0 && overLeft <= 0 ) {
1288 position.left = withinOffset;
1289 // element is initially over both left and right sides of within
1291 if ( overLeft > overRight ) {
1292 position.left = withinOffset + outerWidth - data.collisionWidth;
1294 position.left = withinOffset;
1297 // too far left -> align with left edge
1298 } else if ( overLeft > 0 ) {
1299 position.left += overLeft;
1300 // too far right -> align with right edge
1301 } else if ( overRight > 0 ) {
1302 position.left -= overRight;
1303 // adjust based on position and margin
1305 position.left = max( position.left - collisionPosLeft, position.left );
1308 top: function( position, data ) {
1309 var within = data.within,
1310 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1311 outerHeight = data.within.height,
1312 collisionPosTop = position.top - data.collisionPosition.marginTop,
1313 overTop = withinOffset - collisionPosTop,
1314 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1317 // element is taller than within
1318 if ( data.collisionHeight > outerHeight ) {
1319 // element is initially over the top of within
1320 if ( overTop > 0 && overBottom <= 0 ) {
1321 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1322 position.top += overTop - newOverBottom;
1323 // element is initially over bottom of within
1324 } else if ( overBottom > 0 && overTop <= 0 ) {
1325 position.top = withinOffset;
1326 // element is initially over both top and bottom of within
1328 if ( overTop > overBottom ) {
1329 position.top = withinOffset + outerHeight - data.collisionHeight;
1331 position.top = withinOffset;
1334 // too far up -> align with top
1335 } else if ( overTop > 0 ) {
1336 position.top += overTop;
1337 // too far down -> align with bottom edge
1338 } else if ( overBottom > 0 ) {
1339 position.top -= overBottom;
1340 // adjust based on position and margin
1342 position.top = max( position.top - collisionPosTop, position.top );
1347 left: function( position, data ) {
1348 var within = data.within,
1349 withinOffset = within.offset.left + within.scrollLeft,
1350 outerWidth = within.width,
1351 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1352 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1353 overLeft = collisionPosLeft - offsetLeft,
1354 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1355 myOffset = data.my[ 0 ] === "left" ?
1357 data.my[ 0 ] === "right" ?
1360 atOffset = data.at[ 0 ] === "left" ?
1362 data.at[ 0 ] === "right" ?
1365 offset = -2 * data.offset[ 0 ],
1369 if ( overLeft < 0 ) {
1370 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1371 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1372 position.left += myOffset + atOffset + offset;
1375 else if ( overRight > 0 ) {
1376 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1377 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1378 position.left += myOffset + atOffset + offset;
1382 top: function( position, data ) {
1383 var within = data.within,
1384 withinOffset = within.offset.top + within.scrollTop,
1385 outerHeight = within.height,
1386 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1387 collisionPosTop = position.top - data.collisionPosition.marginTop,
1388 overTop = collisionPosTop - offsetTop,
1389 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1390 top = data.my[ 1 ] === "top",
1393 data.my[ 1 ] === "bottom" ?
1396 atOffset = data.at[ 1 ] === "top" ?
1398 data.at[ 1 ] === "bottom" ?
1399 -data.targetHeight :
1401 offset = -2 * data.offset[ 1 ],
1404 if ( overTop < 0 ) {
1405 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1406 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1407 position.top += myOffset + atOffset + offset;
1410 else if ( overBottom > 0 ) {
1411 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1412 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1413 position.top += myOffset + atOffset + offset;
1420 $.ui.position.flip.left.apply( this, arguments );
1421 $.ui.position.fit.left.apply( this, arguments );
1424 $.ui.position.flip.top.apply( this, arguments );
1425 $.ui.position.fit.top.apply( this, arguments );
1430 // fraction support test
1432 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1433 body = document.getElementsByTagName( "body" )[ 0 ],
1434 div = document.createElement( "div" );
1436 //Create a "fake body" for testing based on method used in jQuery.support
1437 testElement = document.createElement( body ? "div" : "body" );
1438 testElementStyle = {
1439 visibility: "hidden",
1447 $.extend( testElementStyle, {
1448 position: "absolute",
1453 for ( i in testElementStyle ) {
1454 testElement.style[ i ] = testElementStyle[ i ];
1456 testElement.appendChild( div );
1457 testElementParent = body || document.documentElement;
1458 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1460 div.style.cssText = "position: absolute; left: 10.7432222px;";
1462 offsetLeft = $( div ).offset().left;
1463 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1465 testElement.innerHTML = "";
1466 testElementParent.removeChild( testElement );
1470 (function( $, undefined ) {
1472 $.widget("ui.draggable", $.ui.mouse, {
1474 widgetEventPrefix: "drag",
1479 connectToSortable: false,
1488 refreshPositions: false,
1490 revertDuration: 500,
1493 scrollSensitivity: 20,
1506 _create: function() {
1508 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1509 this.element[0].style.position = "relative";
1511 if (this.options.addClasses){
1512 this.element.addClass("ui-draggable");
1514 if (this.options.disabled){
1515 this.element.addClass("ui-draggable-disabled");
1522 _destroy: function() {
1523 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1524 this._mouseDestroy();
1527 _mouseCapture: function(event) {
1529 var o = this.options;
1531 // among others, prevent a drag on a resizable-handle
1532 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1536 //Quit if we're not on a valid handle
1537 this.handle = this._getHandle(event);
1542 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1543 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1545 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1546 position: "absolute", opacity: "0.001", zIndex: 1000
1548 .css($(this).offset())
1556 _mouseStart: function(event) {
1558 var o = this.options;
1560 //Create and append the visible helper
1561 this.helper = this._createHelper(event);
1563 this.helper.addClass("ui-draggable-dragging");
1565 //Cache the helper size
1566 this._cacheHelperProportions();
1568 //If ddmanager is used for droppables, set the global draggable
1569 if($.ui.ddmanager) {
1570 $.ui.ddmanager.current = this;
1574 * - Position generation -
1575 * This block generates everything position related - it's the core of draggables.
1578 //Cache the margins of the original element
1579 this._cacheMargins();
1581 //Store the helper's css position
1582 this.cssPosition = this.helper.css( "position" );
1583 this.scrollParent = this.helper.scrollParent();
1584 this.offsetParent = this.helper.offsetParent();
1585 this.offsetParentCssPosition = this.offsetParent.css( "position" );
1587 //The element's absolute position on the page minus margins
1588 this.offset = this.positionAbs = this.element.offset();
1590 top: this.offset.top - this.margins.top,
1591 left: this.offset.left - this.margins.left
1594 //Reset scroll cache
1595 this.offset.scroll = false;
1597 $.extend(this.offset, {
1598 click: { //Where the click happened, relative to the element
1599 left: event.pageX - this.offset.left,
1600 top: event.pageY - this.offset.top
1602 parent: this._getParentOffset(),
1603 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1606 //Generate the original position
1607 this.originalPosition = this.position = this._generatePosition(event);
1608 this.originalPageX = event.pageX;
1609 this.originalPageY = event.pageY;
1611 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1612 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1614 //Set a containment if given in the options
1615 this._setContainment();
1617 //Trigger event + callbacks
1618 if(this._trigger("start", event) === false) {
1623 //Recache the helper size
1624 this._cacheHelperProportions();
1626 //Prepare the droppable offsets
1627 if ($.ui.ddmanager && !o.dropBehaviour) {
1628 $.ui.ddmanager.prepareOffsets(this, event);
1632 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1634 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1635 if ( $.ui.ddmanager ) {
1636 $.ui.ddmanager.dragStart(this, event);
1642 _mouseDrag: function(event, noPropagation) {
1643 // reset any necessary cached properties (see #5009)
1644 if ( this.offsetParentCssPosition === "fixed" ) {
1645 this.offset.parent = this._getParentOffset();
1648 //Compute the helpers position
1649 this.position = this._generatePosition(event);
1650 this.positionAbs = this._convertPositionTo("absolute");
1652 //Call plugins and callbacks and use the resulting position if something is returned
1653 if (!noPropagation) {
1654 var ui = this._uiHash();
1655 if(this._trigger("drag", event, ui) === false) {
1659 this.position = ui.position;
1662 if(!this.options.axis || this.options.axis !== "y") {
1663 this.helper[0].style.left = this.position.left+"px";
1665 if(!this.options.axis || this.options.axis !== "x") {
1666 this.helper[0].style.top = this.position.top+"px";
1668 if($.ui.ddmanager) {
1669 $.ui.ddmanager.drag(this, event);
1675 _mouseStop: function(event) {
1677 //If we are using droppables, inform the manager about the drop
1680 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1681 dropped = $.ui.ddmanager.drop(this, event);
1684 //if a drop comes from outside (a sortable)
1686 dropped = this.dropped;
1687 this.dropped = false;
1690 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1691 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
1695 if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1696 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1697 if(that._trigger("stop", event) !== false) {
1702 if(this._trigger("stop", event) !== false) {
1710 _mouseUp: function(event) {
1711 //Remove frame helpers
1712 $("div.ui-draggable-iframeFix").each(function() {
1713 this.parentNode.removeChild(this);
1716 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1717 if( $.ui.ddmanager ) {
1718 $.ui.ddmanager.dragStop(this, event);
1721 return $.ui.mouse.prototype._mouseUp.call(this, event);
1724 cancel: function() {
1726 if(this.helper.is(".ui-draggable-dragging")) {
1736 _getHandle: function(event) {
1737 return this.options.handle ?
1738 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1742 _createHelper: function(event) {
1744 var o = this.options,
1745 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1747 if(!helper.parents("body").length) {
1748 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1751 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1752 helper.css("position", "absolute");
1759 _adjustOffsetFromHelper: function(obj) {
1760 if (typeof obj === "string") {
1761 obj = obj.split(" ");
1763 if ($.isArray(obj)) {
1764 obj = {left: +obj[0], top: +obj[1] || 0};
1766 if ("left" in obj) {
1767 this.offset.click.left = obj.left + this.margins.left;
1769 if ("right" in obj) {
1770 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1773 this.offset.click.top = obj.top + this.margins.top;
1775 if ("bottom" in obj) {
1776 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1780 _getParentOffset: function() {
1782 //Get the offsetParent and cache its position
1783 var po = this.offsetParent.offset();
1785 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1786 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1787 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1788 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1789 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1790 po.left += this.scrollParent.scrollLeft();
1791 po.top += this.scrollParent.scrollTop();
1794 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1796 if((this.offsetParent[0] === document.body) ||
1797 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1798 po = { top: 0, left: 0 };
1802 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1803 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1808 _getRelativeOffset: function() {
1810 if(this.cssPosition === "relative") {
1811 var p = this.element.position();
1813 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1814 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1817 return { top: 0, left: 0 };
1822 _cacheMargins: function() {
1824 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1825 top: (parseInt(this.element.css("marginTop"),10) || 0),
1826 right: (parseInt(this.element.css("marginRight"),10) || 0),
1827 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1831 _cacheHelperProportions: function() {
1832 this.helperProportions = {
1833 width: this.helper.outerWidth(),
1834 height: this.helper.outerHeight()
1838 _setContainment: function() {
1843 if ( !o.containment ) {
1844 this.containment = null;
1848 if ( o.containment === "window" ) {
1849 this.containment = [
1850 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1851 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1852 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
1853 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1858 if ( o.containment === "document") {
1859 this.containment = [
1862 $( document ).width() - this.helperProportions.width - this.margins.left,
1863 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1868 if ( o.containment.constructor === Array ) {
1869 this.containment = o.containment;
1873 if ( o.containment === "parent" ) {
1874 o.containment = this.helper[ 0 ].parentNode;
1877 c = $( o.containment );
1884 over = c.css( "overflow" ) !== "hidden";
1886 this.containment = [
1887 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
1888 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
1889 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
1890 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
1892 this.relative_container = c;
1895 _convertPositionTo: function(d, pos) {
1898 pos = this.position;
1901 var mod = d === "absolute" ? 1 : -1,
1902 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
1905 if (!this.offset.scroll) {
1906 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1911 pos.top + // The absolute mouse position
1912 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1913 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1914 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
1917 pos.left + // The absolute mouse position
1918 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1919 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1920 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
1926 _generatePosition: function(event) {
1928 var containment, co, top, left,
1930 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
1931 pageX = event.pageX,
1932 pageY = event.pageY;
1935 if (!this.offset.scroll) {
1936 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1940 * - Position constraining -
1941 * Constrain the position to a mix of grid, containment.
1944 // If we are not dragging yet, we won't check for options
1945 if ( this.originalPosition ) {
1946 if ( this.containment ) {
1947 if ( this.relative_container ){
1948 co = this.relative_container.offset();
1950 this.containment[ 0 ] + co.left,
1951 this.containment[ 1 ] + co.top,
1952 this.containment[ 2 ] + co.left,
1953 this.containment[ 3 ] + co.top
1957 containment = this.containment;
1960 if(event.pageX - this.offset.click.left < containment[0]) {
1961 pageX = containment[0] + this.offset.click.left;
1963 if(event.pageY - this.offset.click.top < containment[1]) {
1964 pageY = containment[1] + this.offset.click.top;
1966 if(event.pageX - this.offset.click.left > containment[2]) {
1967 pageX = containment[2] + this.offset.click.left;
1969 if(event.pageY - this.offset.click.top > containment[3]) {
1970 pageY = containment[3] + this.offset.click.top;
1975 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1976 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1977 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1979 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1980 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1987 pageY - // The absolute mouse position
1988 this.offset.click.top - // Click offset (relative to the element)
1989 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1990 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1991 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
1994 pageX - // The absolute mouse position
1995 this.offset.click.left - // Click offset (relative to the element)
1996 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1997 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1998 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
2004 _clear: function() {
2005 this.helper.removeClass("ui-draggable-dragging");
2006 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
2007 this.helper.remove();
2010 this.cancelHelperRemoval = false;
2013 // From now on bulk stuff - mainly helpers
2015 _trigger: function(type, event, ui) {
2016 ui = ui || this._uiHash();
2017 $.ui.plugin.call(this, type, [event, ui]);
2018 //The absolute position has to be recalculated after plugins
2019 if(type === "drag") {
2020 this.positionAbs = this._convertPositionTo("absolute");
2022 return $.Widget.prototype._trigger.call(this, type, event, ui);
2027 _uiHash: function() {
2029 helper: this.helper,
2030 position: this.position,
2031 originalPosition: this.originalPosition,
2032 offset: this.positionAbs
2038 $.ui.plugin.add("draggable", "connectToSortable", {
2039 start: function(event, ui) {
2041 var inst = $(this).data("ui-draggable"), o = inst.options,
2042 uiSortable = $.extend({}, ui, { item: inst.element });
2043 inst.sortables = [];
2044 $(o.connectToSortable).each(function() {
2045 var sortable = $.data(this, "ui-sortable");
2046 if (sortable && !sortable.options.disabled) {
2047 inst.sortables.push({
2049 shouldRevert: sortable.options.revert
2051 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
2052 sortable._trigger("activate", event, uiSortable);
2057 stop: function(event, ui) {
2059 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
2060 var inst = $(this).data("ui-draggable"),
2061 uiSortable = $.extend({}, ui, { item: inst.element });
2063 $.each(inst.sortables, function() {
2064 if(this.instance.isOver) {
2066 this.instance.isOver = 0;
2068 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
2069 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
2071 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
2072 if(this.shouldRevert) {
2073 this.instance.options.revert = this.shouldRevert;
2076 //Trigger the stop of the sortable
2077 this.instance._mouseStop(event);
2079 this.instance.options.helper = this.instance.options._helper;
2081 //If the helper has been the original item, restore properties in the sortable
2082 if(inst.options.helper === "original") {
2083 this.instance.currentItem.css({ top: "auto", left: "auto" });
2087 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
2088 this.instance._trigger("deactivate", event, uiSortable);
2094 drag: function(event, ui) {
2096 var inst = $(this).data("ui-draggable"), that = this;
2098 $.each(inst.sortables, function() {
2100 var innermostIntersecting = false,
2101 thisSortable = this;
2103 //Copy over some variables to allow calling the sortable's native _intersectsWith
2104 this.instance.positionAbs = inst.positionAbs;
2105 this.instance.helperProportions = inst.helperProportions;
2106 this.instance.offset.click = inst.offset.click;
2108 if(this.instance._intersectsWith(this.instance.containerCache)) {
2109 innermostIntersecting = true;
2110 $.each(inst.sortables, function () {
2111 this.instance.positionAbs = inst.positionAbs;
2112 this.instance.helperProportions = inst.helperProportions;
2113 this.instance.offset.click = inst.offset.click;
2114 if (this !== thisSortable &&
2115 this.instance._intersectsWith(this.instance.containerCache) &&
2116 $.contains(thisSortable.instance.element[0], this.instance.element[0])
2118 innermostIntersecting = false;
2120 return innermostIntersecting;
2125 if(innermostIntersecting) {
2126 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
2127 if(!this.instance.isOver) {
2129 this.instance.isOver = 1;
2130 //Now we fake the start of dragging for the sortable instance,
2131 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
2132 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
2133 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
2134 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
2135 this.instance.options.helper = function() { return ui.helper[0]; };
2137 event.target = this.instance.currentItem[0];
2138 this.instance._mouseCapture(event, true);
2139 this.instance._mouseStart(event, true, true);
2141 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
2142 this.instance.offset.click.top = inst.offset.click.top;
2143 this.instance.offset.click.left = inst.offset.click.left;
2144 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
2145 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
2147 inst._trigger("toSortable", event);
2148 inst.dropped = this.instance.element; //draggable revert needs that
2149 //hack so receive/update callbacks work (mostly)
2150 inst.currentItem = inst.element;
2151 this.instance.fromOutside = inst;
2155 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
2156 if(this.instance.currentItem) {
2157 this.instance._mouseDrag(event);
2162 //If it doesn't intersect with the sortable, and it intersected before,
2163 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
2164 if(this.instance.isOver) {
2166 this.instance.isOver = 0;
2167 this.instance.cancelHelperRemoval = true;
2169 //Prevent reverting on this forced stop
2170 this.instance.options.revert = false;
2172 // The out event needs to be triggered independently
2173 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
2175 this.instance._mouseStop(event, true);
2176 this.instance.options.helper = this.instance.options._helper;
2178 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
2179 this.instance.currentItem.remove();
2180 if(this.instance.placeholder) {
2181 this.instance.placeholder.remove();
2184 inst._trigger("fromSortable", event);
2185 inst.dropped = false; //draggable revert needs that
2195 $.ui.plugin.add("draggable", "cursor", {
2197 var t = $("body"), o = $(this).data("ui-draggable").options;
2198 if (t.css("cursor")) {
2199 o._cursor = t.css("cursor");
2201 t.css("cursor", o.cursor);
2204 var o = $(this).data("ui-draggable").options;
2206 $("body").css("cursor", o._cursor);
2211 $.ui.plugin.add("draggable", "opacity", {
2212 start: function(event, ui) {
2213 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
2214 if(t.css("opacity")) {
2215 o._opacity = t.css("opacity");
2217 t.css("opacity", o.opacity);
2219 stop: function(event, ui) {
2220 var o = $(this).data("ui-draggable").options;
2222 $(ui.helper).css("opacity", o._opacity);
2227 $.ui.plugin.add("draggable", "scroll", {
2229 var i = $(this).data("ui-draggable");
2230 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
2231 i.overflowOffset = i.scrollParent.offset();
2234 drag: function( event ) {
2236 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
2238 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
2240 if(!o.axis || o.axis !== "x") {
2241 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
2242 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
2243 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
2244 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
2248 if(!o.axis || o.axis !== "y") {
2249 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
2250 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
2251 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
2252 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
2258 if(!o.axis || o.axis !== "x") {
2259 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
2260 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2261 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
2262 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2266 if(!o.axis || o.axis !== "y") {
2267 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
2268 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2269 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
2270 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2276 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
2277 $.ui.ddmanager.prepareOffsets(i, event);
2283 $.ui.plugin.add("draggable", "snap", {
2286 var i = $(this).data("ui-draggable"),
2289 i.snapElements = [];
2291 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
2294 if(this !== i.element[0]) {
2295 i.snapElements.push({
2297 width: $t.outerWidth(), height: $t.outerHeight(),
2298 top: $o.top, left: $o.left
2304 drag: function(event, ui) {
2306 var ts, bs, ls, rs, l, r, t, b, i, first,
2307 inst = $(this).data("ui-draggable"),
2309 d = o.snapTolerance,
2310 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2311 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2313 for (i = inst.snapElements.length - 1; i >= 0; i--){
2315 l = inst.snapElements[i].left;
2316 r = l + inst.snapElements[i].width;
2317 t = inst.snapElements[i].top;
2318 b = t + inst.snapElements[i].height;
2320 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
2321 if(inst.snapElements[i].snapping) {
2322 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2324 inst.snapElements[i].snapping = false;
2328 if(o.snapMode !== "inner") {
2329 ts = Math.abs(t - y2) <= d;
2330 bs = Math.abs(b - y1) <= d;
2331 ls = Math.abs(l - x2) <= d;
2332 rs = Math.abs(r - x1) <= d;
2334 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2337 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
2340 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
2343 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
2347 first = (ts || bs || ls || rs);
2349 if(o.snapMode !== "outer") {
2350 ts = Math.abs(t - y1) <= d;
2351 bs = Math.abs(b - y2) <= d;
2352 ls = Math.abs(l - x1) <= d;
2353 rs = Math.abs(r - x2) <= d;
2355 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
2358 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2361 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
2364 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
2368 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
2369 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2371 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2378 $.ui.plugin.add("draggable", "stack", {
2381 o = this.data("ui-draggable").options,
2382 group = $.makeArray($(o.stack)).sort(function(a,b) {
2383 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
2386 if (!group.length) { return; }
2388 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
2389 $(group).each(function(i) {
2390 $(this).css("zIndex", min + i);
2392 this.css("zIndex", (min + group.length));
2396 $.ui.plugin.add("draggable", "zIndex", {
2397 start: function(event, ui) {
2398 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
2399 if(t.css("zIndex")) {
2400 o._zIndex = t.css("zIndex");
2402 t.css("zIndex", o.zIndex);
2404 stop: function(event, ui) {
2405 var o = $(this).data("ui-draggable").options;
2407 $(ui.helper).css("zIndex", o._zIndex);
2413 (function( $, undefined ) {
2415 function isOverAxis( x, reference, size ) {
2416 return ( x > reference ) && ( x < ( reference + size ) );
2419 $.widget("ui.droppable", {
2421 widgetEventPrefix: "drop",
2429 tolerance: "intersect",
2438 _create: function() {
2440 var o = this.options,
2443 this.isover = false;
2446 this.accept = $.isFunction(accept) ? accept : function(d) {
2447 return d.is(accept);
2450 //Store the droppable's proportions
2451 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
2453 // Add the reference and positions to the manager
2454 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
2455 $.ui.ddmanager.droppables[o.scope].push(this);
2457 (o.addClasses && this.element.addClass("ui-droppable"));
2461 _destroy: function() {
2463 drop = $.ui.ddmanager.droppables[this.options.scope];
2465 for ( ; i < drop.length; i++ ) {
2466 if ( drop[i] === this ) {
2471 this.element.removeClass("ui-droppable ui-droppable-disabled");
2474 _setOption: function(key, value) {
2476 if(key === "accept") {
2477 this.accept = $.isFunction(value) ? value : function(d) {
2481 $.Widget.prototype._setOption.apply(this, arguments);
2484 _activate: function(event) {
2485 var draggable = $.ui.ddmanager.current;
2486 if(this.options.activeClass) {
2487 this.element.addClass(this.options.activeClass);
2490 this._trigger("activate", event, this.ui(draggable));
2494 _deactivate: function(event) {
2495 var draggable = $.ui.ddmanager.current;
2496 if(this.options.activeClass) {
2497 this.element.removeClass(this.options.activeClass);
2500 this._trigger("deactivate", event, this.ui(draggable));
2504 _over: function(event) {
2506 var draggable = $.ui.ddmanager.current;
2508 // Bail if draggable and droppable are same element
2509 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2513 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2514 if(this.options.hoverClass) {
2515 this.element.addClass(this.options.hoverClass);
2517 this._trigger("over", event, this.ui(draggable));
2522 _out: function(event) {
2524 var draggable = $.ui.ddmanager.current;
2526 // Bail if draggable and droppable are same element
2527 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2531 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2532 if(this.options.hoverClass) {
2533 this.element.removeClass(this.options.hoverClass);
2535 this._trigger("out", event, this.ui(draggable));
2540 _drop: function(event,custom) {
2542 var draggable = custom || $.ui.ddmanager.current,
2543 childrenIntersection = false;
2545 // Bail if draggable and droppable are same element
2546 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2550 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2551 var inst = $.data(this, "ui-droppable");
2553 inst.options.greedy &&
2554 !inst.options.disabled &&
2555 inst.options.scope === draggable.options.scope &&
2556 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2557 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2558 ) { childrenIntersection = true; return false; }
2560 if(childrenIntersection) {
2564 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2565 if(this.options.activeClass) {
2566 this.element.removeClass(this.options.activeClass);
2568 if(this.options.hoverClass) {
2569 this.element.removeClass(this.options.hoverClass);
2571 this._trigger("drop", event, this.ui(draggable));
2572 return this.element;
2581 draggable: (c.currentItem || c.element),
2583 position: c.position,
2584 offset: c.positionAbs
2590 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2592 if (!droppable.offset) {
2596 var draggableLeft, draggableTop,
2597 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2598 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2599 l = droppable.offset.left, r = l + droppable.proportions.width,
2600 t = droppable.offset.top, b = t + droppable.proportions.height;
2602 switch (toleranceMode) {
2604 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2606 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2607 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2608 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2609 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2611 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2612 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2613 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2616 (y1 >= t && y1 <= b) || // Top edge touching
2617 (y2 >= t && y2 <= b) || // Bottom edge touching
2618 (y1 < t && y2 > b) // Surrounded vertically
2620 (x1 >= l && x1 <= r) || // Left edge touching
2621 (x2 >= l && x2 <= r) || // Right edge touching
2622 (x1 < l && x2 > r) // Surrounded horizontally
2631 This manager tracks offsets of draggables and droppables
2635 droppables: { "default": [] },
2636 prepareOffsets: function(t, event) {
2639 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2640 type = event ? event.type : null, // workaround for #2317
2641 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2643 droppablesLoop: for (i = 0; i < m.length; i++) {
2645 //No disabled and non-accepted
2646 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2650 // Filter out elements in the current dragged item
2651 for (j=0; j < list.length; j++) {
2652 if(list[j] === m[i].element[0]) {
2653 m[i].proportions.height = 0;
2654 continue droppablesLoop;
2658 m[i].visible = m[i].element.css("display") !== "none";
2663 //Activate the droppable if used directly from draggables
2664 if(type === "mousedown") {
2665 m[i]._activate.call(m[i], event);
2668 m[i].offset = m[i].element.offset();
2669 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2674 drop: function(draggable, event) {
2676 var dropped = false;
2677 // Create a copy of the droppables in case the list changes during the drop (#9116)
2678 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2683 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2684 dropped = this._drop.call(this, event) || dropped;
2687 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2689 this.isover = false;
2690 this._deactivate.call(this, event);
2697 dragStart: function( draggable, event ) {
2698 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2699 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2700 if( !draggable.options.refreshPositions ) {
2701 $.ui.ddmanager.prepareOffsets( draggable, event );
2705 drag: function(draggable, event) {
2707 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2708 if(draggable.options.refreshPositions) {
2709 $.ui.ddmanager.prepareOffsets(draggable, event);
2712 //Run through all droppables and check their positions based on specific tolerance options
2713 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2715 if(this.options.disabled || this.greedyChild || !this.visible) {
2719 var parentInstance, scope, parent,
2720 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2721 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2726 if (this.options.greedy) {
2727 // find droppable parents with same scope
2728 scope = this.options.scope;
2729 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2730 return $.data(this, "ui-droppable").options.scope === scope;
2733 if (parent.length) {
2734 parentInstance = $.data(parent[0], "ui-droppable");
2735 parentInstance.greedyChild = (c === "isover");
2739 // we just moved into a greedy child
2740 if (parentInstance && c === "isover") {
2741 parentInstance.isover = false;
2742 parentInstance.isout = true;
2743 parentInstance._out.call(parentInstance, event);
2747 this[c === "isout" ? "isover" : "isout"] = false;
2748 this[c === "isover" ? "_over" : "_out"].call(this, event);
2750 // we just moved out of a greedy child
2751 if (parentInstance && c === "isout") {
2752 parentInstance.isout = false;
2753 parentInstance.isover = true;
2754 parentInstance._over.call(parentInstance, event);
2759 dragStop: function( draggable, event ) {
2760 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2761 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2762 if( !draggable.options.refreshPositions ) {
2763 $.ui.ddmanager.prepareOffsets( draggable, event );
2769 (function( $, undefined ) {
2772 return parseInt(v, 10) || 0;
2775 function isNumber(value) {
2776 return !isNaN(parseInt(value, 10));
2779 $.widget("ui.resizable", $.ui.mouse, {
2781 widgetEventPrefix: "resize",
2785 animateDuration: "slow",
2786 animateEasing: "swing",
2806 _create: function() {
2808 var n, i, handle, axis, hname,
2811 this.element.addClass("ui-resizable");
2814 _aspectRatio: !!(o.aspectRatio),
2815 aspectRatio: o.aspectRatio,
2816 originalElement: this.element,
2817 _proportionallyResizeElements: [],
2818 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2821 //Wrap the element if it cannot hold child nodes
2822 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2824 //Create a wrapper element and set the wrapper to the new current internal element
2826 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2827 position: this.element.css("position"),
2828 width: this.element.outerWidth(),
2829 height: this.element.outerHeight(),
2830 top: this.element.css("top"),
2831 left: this.element.css("left")
2835 //Overwrite the original this.element
2836 this.element = this.element.parent().data(
2837 "ui-resizable", this.element.data("ui-resizable")
2840 this.elementIsWrapper = true;
2842 //Move margins to the wrapper
2843 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2844 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2846 //Prevent Safari textarea resize
2847 this.originalResizeStyle = this.originalElement.css("resize");
2848 this.originalElement.css("resize", "none");
2850 //Push the actual element to our proportionallyResize internal array
2851 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2853 // avoid IE jump (hard set the margin)
2854 this.originalElement.css({ margin: this.originalElement.css("margin") });
2856 // fix handlers offset
2857 this._proportionallyResize();
2861 this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
2862 if(this.handles.constructor === String) {
2864 if ( this.handles === "all") {
2865 this.handles = "n,e,s,w,se,sw,ne,nw";
2868 n = this.handles.split(",");
2871 for(i = 0; i < n.length; i++) {
2873 handle = $.trim(n[i]);
2874 hname = "ui-resizable-"+handle;
2875 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2877 // Apply zIndex to all handles - see #7960
2878 axis.css({ zIndex: o.zIndex });
2880 //TODO : What's going on here?
2881 if ("se" === handle) {
2882 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2885 //Insert into internal handles object and append to element
2886 this.handles[handle] = ".ui-resizable-"+handle;
2887 this.element.append(axis);
2892 this._renderAxis = function(target) {
2894 var i, axis, padPos, padWrapper;
2896 target = target || this.element;
2898 for(i in this.handles) {
2900 if(this.handles[i].constructor === String) {
2901 this.handles[i] = $(this.handles[i], this.element).show();
2904 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2905 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2907 axis = $(this.handles[i], this.element);
2909 //Checking the correct pad and border
2910 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2912 //The padding type i have to apply...
2913 padPos = [ "padding",
2914 /ne|nw|n/.test(i) ? "Top" :
2915 /se|sw|s/.test(i) ? "Bottom" :
2916 /^e$/.test(i) ? "Right" : "Left" ].join("");
2918 target.css(padPos, padWrapper);
2920 this._proportionallyResize();
2924 //TODO: What's that good for? There's not anything to be executed left
2925 if(!$(this.handles[i]).length) {
2931 //TODO: make renderAxis a prototype function
2932 this._renderAxis(this.element);
2934 this._handles = $(".ui-resizable-handle", this.element)
2935 .disableSelection();
2937 //Matching axis name
2938 this._handles.mouseover(function() {
2939 if (!that.resizing) {
2940 if (this.className) {
2941 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2943 //Axis, default = se
2944 that.axis = axis && axis[1] ? axis[1] : "se";
2948 //If we want to auto hide the elements
2950 this._handles.hide();
2952 .addClass("ui-resizable-autohide")
2953 .mouseenter(function() {
2957 $(this).removeClass("ui-resizable-autohide");
2958 that._handles.show();
2960 .mouseleave(function(){
2964 if (!that.resizing) {
2965 $(this).addClass("ui-resizable-autohide");
2966 that._handles.hide();
2971 //Initialize the mouse interaction
2976 _destroy: function() {
2978 this._mouseDestroy();
2981 _destroy = function(exp) {
2982 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2983 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2986 //TODO: Unwrap at same DOM position
2987 if (this.elementIsWrapper) {
2988 _destroy(this.element);
2989 wrapper = this.element;
2990 this.originalElement.css({
2991 position: wrapper.css("position"),
2992 width: wrapper.outerWidth(),
2993 height: wrapper.outerHeight(),
2994 top: wrapper.css("top"),
2995 left: wrapper.css("left")
2996 }).insertAfter( wrapper );
3000 this.originalElement.css("resize", this.originalResizeStyle);
3001 _destroy(this.originalElement);
3006 _mouseCapture: function(event) {
3010 for (i in this.handles) {
3011 handle = $(this.handles[i])[0];
3012 if (handle === event.target || $.contains(handle, event.target)) {
3017 return !this.options.disabled && capture;
3020 _mouseStart: function(event) {
3022 var curleft, curtop, cursor,
3024 iniPos = this.element.position(),
3027 this.resizing = true;
3029 // bugfix for http://dev.jquery.com/ticket/1749
3030 if ( (/absolute/).test( el.css("position") ) ) {
3031 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
3032 } else if (el.is(".ui-draggable")) {
3033 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
3036 this._renderProxy();
3038 curleft = num(this.helper.css("left"));
3039 curtop = num(this.helper.css("top"));
3041 if (o.containment) {
3042 curleft += $(o.containment).scrollLeft() || 0;
3043 curtop += $(o.containment).scrollTop() || 0;
3046 //Store needed variables
3047 this.offset = this.helper.offset();
3048 this.position = { left: curleft, top: curtop };
3049 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3050 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3051 this.originalPosition = { left: curleft, top: curtop };
3052 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
3053 this.originalMousePosition = { left: event.pageX, top: event.pageY };
3056 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
3058 cursor = $(".ui-resizable-" + this.axis).css("cursor");
3059 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
3061 el.addClass("ui-resizable-resizing");
3062 this._propagate("start", event);
3066 _mouseDrag: function(event) {
3068 //Increase performance, avoid regex
3070 el = this.helper, props = {},
3071 smp = this.originalMousePosition,
3073 prevTop = this.position.top,
3074 prevLeft = this.position.left,
3075 prevWidth = this.size.width,
3076 prevHeight = this.size.height,
3077 dx = (event.pageX-smp.left)||0,
3078 dy = (event.pageY-smp.top)||0,
3079 trigger = this._change[a];
3085 // Calculate the attrs that will be change
3086 data = trigger.apply(this, [event, dx, dy]);
3088 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
3089 this._updateVirtualBoundaries(event.shiftKey);
3090 if (this._aspectRatio || event.shiftKey) {
3091 data = this._updateRatio(data, event);
3094 data = this._respectSize(data, event);
3096 this._updateCache(data);
3098 // plugins callbacks need to be called first
3099 this._propagate("resize", event);
3101 if (this.position.top !== prevTop) {
3102 props.top = this.position.top + "px";
3104 if (this.position.left !== prevLeft) {
3105 props.left = this.position.left + "px";
3107 if (this.size.width !== prevWidth) {
3108 props.width = this.size.width + "px";
3110 if (this.size.height !== prevHeight) {
3111 props.height = this.size.height + "px";
3115 if (!this._helper && this._proportionallyResizeElements.length) {
3116 this._proportionallyResize();
3119 // Call the user callback if the element was resized
3120 if ( ! $.isEmptyObject(props) ) {
3121 this._trigger("resize", event, this.ui());
3127 _mouseStop: function(event) {
3129 this.resizing = false;
3130 var pr, ista, soffseth, soffsetw, s, left, top,
3131 o = this.options, that = this;
3135 pr = this._proportionallyResizeElements;
3136 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
3137 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
3138 soffsetw = ista ? 0 : that.sizeDiff.width;
3140 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
3141 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
3142 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
3145 this.element.css($.extend(s, { top: top, left: left }));
3148 that.helper.height(that.size.height);
3149 that.helper.width(that.size.width);
3151 if (this._helper && !o.animate) {
3152 this._proportionallyResize();
3156 $("body").css("cursor", "auto");
3158 this.element.removeClass("ui-resizable-resizing");
3160 this._propagate("stop", event);
3163 this.helper.remove();
3170 _updateVirtualBoundaries: function(forceAspectRatio) {
3171 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
3175 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
3176 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
3177 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
3178 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
3181 if(this._aspectRatio || forceAspectRatio) {
3182 // We want to create an enclosing box whose aspect ration is the requested one
3183 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
3184 pMinWidth = b.minHeight * this.aspectRatio;
3185 pMinHeight = b.minWidth / this.aspectRatio;
3186 pMaxWidth = b.maxHeight * this.aspectRatio;
3187 pMaxHeight = b.maxWidth / this.aspectRatio;
3189 if(pMinWidth > b.minWidth) {
3190 b.minWidth = pMinWidth;
3192 if(pMinHeight > b.minHeight) {
3193 b.minHeight = pMinHeight;
3195 if(pMaxWidth < b.maxWidth) {
3196 b.maxWidth = pMaxWidth;
3198 if(pMaxHeight < b.maxHeight) {
3199 b.maxHeight = pMaxHeight;
3202 this._vBoundaries = b;
3205 _updateCache: function(data) {
3206 this.offset = this.helper.offset();
3207 if (isNumber(data.left)) {
3208 this.position.left = data.left;
3210 if (isNumber(data.top)) {
3211 this.position.top = data.top;
3213 if (isNumber(data.height)) {
3214 this.size.height = data.height;
3216 if (isNumber(data.width)) {
3217 this.size.width = data.width;
3221 _updateRatio: function( data ) {
3223 var cpos = this.position,
3227 if (isNumber(data.height)) {
3228 data.width = (data.height * this.aspectRatio);
3229 } else if (isNumber(data.width)) {
3230 data.height = (data.width / this.aspectRatio);
3234 data.left = cpos.left + (csize.width - data.width);
3238 data.top = cpos.top + (csize.height - data.height);
3239 data.left = cpos.left + (csize.width - data.width);
3245 _respectSize: function( data ) {
3247 var o = this._vBoundaries,
3249 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
3250 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
3251 dw = this.originalPosition.left + this.originalSize.width,
3252 dh = this.position.top + this.size.height,
3253 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
3255 data.width = o.minWidth;
3258 data.height = o.minHeight;
3261 data.width = o.maxWidth;
3264 data.height = o.maxHeight;
3268 data.left = dw - o.minWidth;
3271 data.left = dw - o.maxWidth;
3274 data.top = dh - o.minHeight;
3277 data.top = dh - o.maxHeight;
3280 // fixing jump error on top/left - bug #2330
3281 if (!data.width && !data.height && !data.left && data.top) {
3283 } else if (!data.width && !data.height && !data.top && data.left) {
3290 _proportionallyResize: function() {
3292 if (!this._proportionallyResizeElements.length) {
3296 var i, j, borders, paddings, prel,
3297 element = this.helper || this.element;
3299 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
3301 prel = this._proportionallyResizeElements[i];
3303 if (!this.borderDif) {
3304 this.borderDif = [];
3305 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
3306 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
3308 for ( j = 0; j < borders.length; j++ ) {
3309 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
3314 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
3315 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
3322 _renderProxy: function() {
3324 var el = this.element, o = this.options;
3325 this.elementOffset = el.offset();
3329 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
3331 this.helper.addClass(this._helper).css({
3332 width: this.element.outerWidth() - 1,
3333 height: this.element.outerHeight() - 1,
3334 position: "absolute",
3335 left: this.elementOffset.left +"px",
3336 top: this.elementOffset.top +"px",
3337 zIndex: ++o.zIndex //TODO: Don't modify option
3342 .disableSelection();
3345 this.helper = this.element;
3351 e: function(event, dx) {
3352 return { width: this.originalSize.width + dx };
3354 w: function(event, dx) {
3355 var cs = this.originalSize, sp = this.originalPosition;
3356 return { left: sp.left + dx, width: cs.width - dx };
3358 n: function(event, dx, dy) {
3359 var cs = this.originalSize, sp = this.originalPosition;
3360 return { top: sp.top + dy, height: cs.height - dy };
3362 s: function(event, dx, dy) {
3363 return { height: this.originalSize.height + dy };
3365 se: function(event, dx, dy) {
3366 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3368 sw: function(event, dx, dy) {
3369 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3371 ne: function(event, dx, dy) {
3372 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3374 nw: function(event, dx, dy) {
3375 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3379 _propagate: function(n, event) {
3380 $.ui.plugin.call(this, n, [event, this.ui()]);
3381 (n !== "resize" && this._trigger(n, event, this.ui()));
3388 originalElement: this.originalElement,
3389 element: this.element,
3390 helper: this.helper,
3391 position: this.position,
3393 originalSize: this.originalSize,
3394 originalPosition: this.originalPosition
3401 * Resizable Extensions
3404 $.ui.plugin.add("resizable", "animate", {
3406 stop: function( event ) {
3407 var that = $(this).data("ui-resizable"),
3409 pr = that._proportionallyResizeElements,
3410 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3411 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
3412 soffsetw = ista ? 0 : that.sizeDiff.width,
3413 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
3414 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
3415 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
3417 that.element.animate(
3418 $.extend(style, top && left ? { top: top, left: left } : {}), {
3419 duration: o.animateDuration,
3420 easing: o.animateEasing,
3424 width: parseInt(that.element.css("width"), 10),
3425 height: parseInt(that.element.css("height"), 10),
3426 top: parseInt(that.element.css("top"), 10),
3427 left: parseInt(that.element.css("left"), 10)
3430 if (pr && pr.length) {
3431 $(pr[0]).css({ width: data.width, height: data.height });
3434 // propagating resize, and updating values for each animation step
3435 that._updateCache(data);
3436 that._propagate("resize", event);
3445 $.ui.plugin.add("resizable", "containment", {
3448 var element, p, co, ch, cw, width, height,
3449 that = $(this).data("ui-resizable"),
3453 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
3459 that.containerElement = $(ce);
3461 if (/document/.test(oc) || oc === document) {
3462 that.containerOffset = { left: 0, top: 0 };
3463 that.containerPosition = { left: 0, top: 0 };
3466 element: $(document), left: 0, top: 0,
3467 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
3471 // i'm a node, so compute top, left, right, bottom
3475 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
3477 that.containerOffset = element.offset();
3478 that.containerPosition = element.position();
3479 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
3481 co = that.containerOffset;
3482 ch = that.containerSize.height;
3483 cw = that.containerSize.width;
3484 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
3485 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3488 element: ce, left: co.left, top: co.top, width: width, height: height
3493 resize: function( event ) {
3494 var woset, hoset, isParent, isOffsetRelative,
3495 that = $(this).data("ui-resizable"),
3497 co = that.containerOffset, cp = that.position,
3498 pRatio = that._aspectRatio || event.shiftKey,
3499 cop = { top:0, left:0 }, ce = that.containerElement;
3501 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
3505 if (cp.left < (that._helper ? co.left : 0)) {
3506 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3508 that.size.height = that.size.width / that.aspectRatio;
3510 that.position.left = o.helper ? co.left : 0;
3513 if (cp.top < (that._helper ? co.top : 0)) {
3514 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3516 that.size.width = that.size.height * that.aspectRatio;
3518 that.position.top = that._helper ? co.top : 0;
3521 that.offset.left = that.parentData.left+that.position.left;
3522 that.offset.top = that.parentData.top+that.position.top;
3524 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3525 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3527 isParent = that.containerElement.get(0) === that.element.parent().get(0);
3528 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3530 if(isParent && isOffsetRelative) {
3531 woset -= that.parentData.left;
3534 if (woset + that.size.width >= that.parentData.width) {
3535 that.size.width = that.parentData.width - woset;
3537 that.size.height = that.size.width / that.aspectRatio;
3541 if (hoset + that.size.height >= that.parentData.height) {
3542 that.size.height = that.parentData.height - hoset;
3544 that.size.width = that.size.height * that.aspectRatio;
3550 var that = $(this).data("ui-resizable"),
3552 co = that.containerOffset,
3553 cop = that.containerPosition,
3554 ce = that.containerElement,
3555 helper = $(that.helper),
3556 ho = helper.offset(),
3557 w = helper.outerWidth() - that.sizeDiff.width,
3558 h = helper.outerHeight() - that.sizeDiff.height;
3560 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3561 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3564 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3565 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3571 $.ui.plugin.add("resizable", "alsoResize", {
3573 start: function () {
3574 var that = $(this).data("ui-resizable"),
3576 _store = function (exp) {
3577 $(exp).each(function() {
3579 el.data("ui-resizable-alsoresize", {
3580 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3581 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3586 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3587 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3588 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3590 _store(o.alsoResize);
3594 resize: function (event, ui) {
3595 var that = $(this).data("ui-resizable"),
3597 os = that.originalSize,
3598 op = that.originalPosition,
3600 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3601 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3604 _alsoResize = function (exp, c) {
3605 $(exp).each(function() {
3606 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3607 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3609 $.each(css, function (i, prop) {
3610 var sum = (start[prop]||0) + (delta[prop]||0);
3611 if (sum && sum >= 0) {
3612 style[prop] = sum || null;
3620 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3621 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3623 _alsoResize(o.alsoResize);
3628 $(this).removeData("resizable-alsoresize");
3632 $.ui.plugin.add("resizable", "ghost", {
3636 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3638 that.ghost = that.originalElement.clone();
3640 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3641 .addClass("ui-resizable-ghost")
3642 .addClass(typeof o.ghost === "string" ? o.ghost : "");
3644 that.ghost.appendTo(that.helper);
3649 var that = $(this).data("ui-resizable");
3651 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3656 var that = $(this).data("ui-resizable");
3657 if (that.ghost && that.helper) {
3658 that.helper.get(0).removeChild(that.ghost.get(0));
3664 $.ui.plugin.add("resizable", "grid", {
3666 resize: function() {
3667 var that = $(this).data("ui-resizable"),
3670 os = that.originalSize,
3671 op = that.originalPosition,
3673 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3674 gridX = (grid[0]||1),
3675 gridY = (grid[1]||1),
3676 ox = Math.round((cs.width - os.width) / gridX) * gridX,
3677 oy = Math.round((cs.height - os.height) / gridY) * gridY,
3678 newWidth = os.width + ox,
3679 newHeight = os.height + oy,
3680 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3681 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3682 isMinWidth = o.minWidth && (o.minWidth > newWidth),
3683 isMinHeight = o.minHeight && (o.minHeight > newHeight);
3688 newWidth = newWidth + gridX;
3691 newHeight = newHeight + gridY;
3694 newWidth = newWidth - gridX;
3697 newHeight = newHeight - gridY;
3700 if (/^(se|s|e)$/.test(a)) {
3701 that.size.width = newWidth;
3702 that.size.height = newHeight;
3703 } else if (/^(ne)$/.test(a)) {
3704 that.size.width = newWidth;
3705 that.size.height = newHeight;
3706 that.position.top = op.top - oy;
3707 } else if (/^(sw)$/.test(a)) {
3708 that.size.width = newWidth;
3709 that.size.height = newHeight;
3710 that.position.left = op.left - ox;
3712 that.size.width = newWidth;
3713 that.size.height = newHeight;
3714 that.position.top = op.top - oy;
3715 that.position.left = op.left - ox;
3722 (function( $, undefined ) {
3724 $.widget("ui.selectable", $.ui.mouse, {
3741 _create: function() {
3745 this.element.addClass("ui-selectable");
3747 this.dragged = false;
3749 // cache selectee children based on filter
3750 this.refresh = function() {
3751 selectees = $(that.options.filter, that.element[0]);
3752 selectees.addClass("ui-selectee");
3753 selectees.each(function() {
3754 var $this = $(this),
3755 pos = $this.offset();
3756 $.data(this, "selectable-item", {
3761 right: pos.left + $this.outerWidth(),
3762 bottom: pos.top + $this.outerHeight(),
3763 startselected: false,
3764 selected: $this.hasClass("ui-selected"),
3765 selecting: $this.hasClass("ui-selecting"),
3766 unselecting: $this.hasClass("ui-unselecting")
3772 this.selectees = selectees.addClass("ui-selectee");
3776 this.helper = $("<div class='ui-selectable-helper'></div>");
3779 _destroy: function() {
3781 .removeClass("ui-selectee")
3782 .removeData("selectable-item");
3784 .removeClass("ui-selectable ui-selectable-disabled");
3785 this._mouseDestroy();
3788 _mouseStart: function(event) {
3790 options = this.options;
3792 this.opos = [event.pageX, event.pageY];
3794 if (this.options.disabled) {
3798 this.selectees = $(options.filter, this.element[0]);
3800 this._trigger("start", event);
3802 $(options.appendTo).append(this.helper);
3803 // position helper (lasso)
3805 "left": event.pageX,
3811 if (options.autoRefresh) {
3815 this.selectees.filter(".ui-selected").each(function() {
3816 var selectee = $.data(this, "selectable-item");
3817 selectee.startselected = true;
3818 if (!event.metaKey && !event.ctrlKey) {
3819 selectee.$element.removeClass("ui-selected");
3820 selectee.selected = false;
3821 selectee.$element.addClass("ui-unselecting");
3822 selectee.unselecting = true;
3823 // selectable UNSELECTING callback
3824 that._trigger("unselecting", event, {
3825 unselecting: selectee.element
3830 $(event.target).parents().addBack().each(function() {
3832 selectee = $.data(this, "selectable-item");
3834 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3836 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3837 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3838 selectee.unselecting = !doSelect;
3839 selectee.selecting = doSelect;
3840 selectee.selected = doSelect;
3841 // selectable (UN)SELECTING callback
3843 that._trigger("selecting", event, {
3844 selecting: selectee.element
3847 that._trigger("unselecting", event, {
3848 unselecting: selectee.element
3857 _mouseDrag: function(event) {
3859 this.dragged = true;
3861 if (this.options.disabled) {
3867 options = this.options,
3873 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3874 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
3875 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3877 this.selectees.each(function() {
3878 var selectee = $.data(this, "selectable-item"),
3881 //prevent helper from being selected if appendTo: selectable
3882 if (!selectee || selectee.element === that.element[0]) {
3886 if (options.tolerance === "touch") {
3887 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3888 } else if (options.tolerance === "fit") {
3889 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3894 if (selectee.selected) {
3895 selectee.$element.removeClass("ui-selected");
3896 selectee.selected = false;
3898 if (selectee.unselecting) {
3899 selectee.$element.removeClass("ui-unselecting");
3900 selectee.unselecting = false;
3902 if (!selectee.selecting) {
3903 selectee.$element.addClass("ui-selecting");
3904 selectee.selecting = true;
3905 // selectable SELECTING callback
3906 that._trigger("selecting", event, {
3907 selecting: selectee.element
3912 if (selectee.selecting) {
3913 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3914 selectee.$element.removeClass("ui-selecting");
3915 selectee.selecting = false;
3916 selectee.$element.addClass("ui-selected");
3917 selectee.selected = true;
3919 selectee.$element.removeClass("ui-selecting");
3920 selectee.selecting = false;
3921 if (selectee.startselected) {
3922 selectee.$element.addClass("ui-unselecting");
3923 selectee.unselecting = true;
3925 // selectable UNSELECTING callback
3926 that._trigger("unselecting", event, {
3927 unselecting: selectee.element
3931 if (selectee.selected) {
3932 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3933 selectee.$element.removeClass("ui-selected");
3934 selectee.selected = false;
3936 selectee.$element.addClass("ui-unselecting");
3937 selectee.unselecting = true;
3938 // selectable UNSELECTING callback
3939 that._trigger("unselecting", event, {
3940 unselecting: selectee.element
3950 _mouseStop: function(event) {
3953 this.dragged = false;
3955 $(".ui-unselecting", this.element[0]).each(function() {
3956 var selectee = $.data(this, "selectable-item");
3957 selectee.$element.removeClass("ui-unselecting");
3958 selectee.unselecting = false;
3959 selectee.startselected = false;
3960 that._trigger("unselected", event, {
3961 unselected: selectee.element
3964 $(".ui-selecting", this.element[0]).each(function() {
3965 var selectee = $.data(this, "selectable-item");
3966 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3967 selectee.selecting = false;
3968 selectee.selected = true;
3969 selectee.startselected = true;
3970 that._trigger("selected", event, {
3971 selected: selectee.element
3974 this._trigger("stop", event);
3976 this.helper.remove();
3984 (function( $, undefined ) {
3986 /*jshint loopfunc: true */
3988 function isOverAxis( x, reference, size ) {
3989 return ( x > reference ) && ( x < ( reference + size ) );
3992 function isFloating(item) {
3993 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3996 $.widget("ui.sortable", $.ui.mouse, {
3998 widgetEventPrefix: "sort",
4008 forcePlaceholderSize: false,
4009 forceHelperSize: false,
4018 scrollSensitivity: 20,
4021 tolerance: "intersect",
4038 _create: function() {
4040 var o = this.options;
4041 this.containerCache = {};
4042 this.element.addClass("ui-sortable");
4047 //Let's determine if the items are being displayed horizontally
4048 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
4050 //Let's determine the parent's offset
4051 this.offset = this.element.offset();
4053 //Initialize mouse events for interaction
4061 _destroy: function() {
4063 .removeClass("ui-sortable ui-sortable-disabled");
4064 this._mouseDestroy();
4066 for ( var i = this.items.length - 1; i >= 0; i-- ) {
4067 this.items[i].item.removeData(this.widgetName + "-item");
4073 _setOption: function(key, value){
4074 if ( key === "disabled" ) {
4075 this.options[ key ] = value;
4077 this.widget().toggleClass( "ui-sortable-disabled", !!value );
4079 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
4080 $.Widget.prototype._setOption.apply(this, arguments);
4084 _mouseCapture: function(event, overrideHandle) {
4085 var currentItem = null,
4086 validHandle = false,
4089 if (this.reverting) {
4093 if(this.options.disabled || this.options.type === "static") {
4097 //We have to refresh the items data once first
4098 this._refreshItems(event);
4100 //Find out if the clicked node (or one of its parents) is a actual item in this.items
4101 $(event.target).parents().each(function() {
4102 if($.data(this, that.widgetName + "-item") === that) {
4103 currentItem = $(this);
4107 if($.data(event.target, that.widgetName + "-item") === that) {
4108 currentItem = $(event.target);
4114 if(this.options.handle && !overrideHandle) {
4115 $(this.options.handle, currentItem).find("*").addBack().each(function() {
4116 if(this === event.target) {
4125 this.currentItem = currentItem;
4126 this._removeCurrentsFromItems();
4131 _mouseStart: function(event, overrideHandle, noActivation) {
4136 this.currentContainer = this;
4138 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
4139 this.refreshPositions();
4141 //Create and append the visible helper
4142 this.helper = this._createHelper(event);
4144 //Cache the helper size
4145 this._cacheHelperProportions();
4148 * - Position generation -
4149 * This block generates everything position related - it's the core of draggables.
4152 //Cache the margins of the original element
4153 this._cacheMargins();
4155 //Get the next scrolling parent
4156 this.scrollParent = this.helper.scrollParent();
4158 //The element's absolute position on the page minus margins
4159 this.offset = this.currentItem.offset();
4161 top: this.offset.top - this.margins.top,
4162 left: this.offset.left - this.margins.left
4165 $.extend(this.offset, {
4166 click: { //Where the click happened, relative to the element
4167 left: event.pageX - this.offset.left,
4168 top: event.pageY - this.offset.top
4170 parent: this._getParentOffset(),
4171 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
4174 // Only after we got the offset, we can change the helper's position to absolute
4175 // TODO: Still need to figure out a way to make relative sorting possible
4176 this.helper.css("position", "absolute");
4177 this.cssPosition = this.helper.css("position");
4179 //Generate the original position
4180 this.originalPosition = this._generatePosition(event);
4181 this.originalPageX = event.pageX;
4182 this.originalPageY = event.pageY;
4184 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
4185 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
4187 //Cache the former DOM position
4188 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
4190 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
4191 if(this.helper[0] !== this.currentItem[0]) {
4192 this.currentItem.hide();
4195 //Create the placeholder
4196 this._createPlaceholder();
4198 //Set a containment if given in the options
4200 this._setContainment();
4203 if( o.cursor && o.cursor !== "auto" ) { // cursor option
4204 body = this.document.find( "body" );
4207 this.storedCursor = body.css( "cursor" );
4208 body.css( "cursor", o.cursor );
4210 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
4213 if(o.opacity) { // opacity option
4214 if (this.helper.css("opacity")) {
4215 this._storedOpacity = this.helper.css("opacity");
4217 this.helper.css("opacity", o.opacity);
4220 if(o.zIndex) { // zIndex option
4221 if (this.helper.css("zIndex")) {
4222 this._storedZIndex = this.helper.css("zIndex");
4224 this.helper.css("zIndex", o.zIndex);
4228 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
4229 this.overflowOffset = this.scrollParent.offset();
4233 this._trigger("start", event, this._uiHash());
4235 //Recache the helper size
4236 if(!this._preserveHelperProportions) {
4237 this._cacheHelperProportions();
4241 //Post "activate" events to possible containers
4242 if( !noActivation ) {
4243 for ( i = this.containers.length - 1; i >= 0; i-- ) {
4244 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
4248 //Prepare possible droppables
4249 if($.ui.ddmanager) {
4250 $.ui.ddmanager.current = this;
4253 if ($.ui.ddmanager && !o.dropBehaviour) {
4254 $.ui.ddmanager.prepareOffsets(this, event);
4257 this.dragging = true;
4259 this.helper.addClass("ui-sortable-helper");
4260 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
4265 _mouseDrag: function(event) {
4266 var i, item, itemElement, intersection,
4270 //Compute the helpers position
4271 this.position = this._generatePosition(event);
4272 this.positionAbs = this._convertPositionTo("absolute");
4274 if (!this.lastPositionAbs) {
4275 this.lastPositionAbs = this.positionAbs;
4279 if(this.options.scroll) {
4280 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
4282 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
4283 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
4284 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
4285 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
4288 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
4289 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
4290 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
4291 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
4296 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
4297 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
4298 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
4299 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
4302 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
4303 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
4304 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
4305 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
4310 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
4311 $.ui.ddmanager.prepareOffsets(this, event);
4315 //Regenerate the absolute position used for position checks
4316 this.positionAbs = this._convertPositionTo("absolute");
4318 //Set the helper position
4319 if(!this.options.axis || this.options.axis !== "y") {
4320 this.helper[0].style.left = this.position.left+"px";
4322 if(!this.options.axis || this.options.axis !== "x") {
4323 this.helper[0].style.top = this.position.top+"px";
4327 for (i = this.items.length - 1; i >= 0; i--) {
4329 //Cache variables and intersection, continue if no intersection
4330 item = this.items[i];
4331 itemElement = item.item[0];
4332 intersection = this._intersectsWithPointer(item);
4333 if (!intersection) {
4337 // Only put the placeholder inside the current Container, skip all
4338 // items form other containers. This works because when moving
4339 // an item from one container to another the
4340 // currentContainer is switched before the placeholder is moved.
4342 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
4343 // beetween the outer and inner container.
4344 if (item.instance !== this.currentContainer) {
4348 // cannot intersect with itself
4349 // no useless actions that have been done before
4350 // no action if the item moved is the parent of the item checked
4351 if (itemElement !== this.currentItem[0] &&
4352 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
4353 !$.contains(this.placeholder[0], itemElement) &&
4354 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
4357 this.direction = intersection === 1 ? "down" : "up";
4359 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
4360 this._rearrange(event, item);
4365 this._trigger("change", event, this._uiHash());
4370 //Post events to containers
4371 this._contactContainers(event);
4373 //Interconnect with droppables
4374 if($.ui.ddmanager) {
4375 $.ui.ddmanager.drag(this, event);
4379 this._trigger("sort", event, this._uiHash());
4381 this.lastPositionAbs = this.positionAbs;
4386 _mouseStop: function(event, noPropagation) {
4392 //If we are using droppables, inform the manager about the drop
4393 if ($.ui.ddmanager && !this.options.dropBehaviour) {
4394 $.ui.ddmanager.drop(this, event);
4397 if(this.options.revert) {
4399 cur = this.placeholder.offset(),
4400 axis = this.options.axis,
4403 if ( !axis || axis === "x" ) {
4404 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
4406 if ( !axis || axis === "y" ) {
4407 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
4409 this.reverting = true;
4410 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
4414 this._clear(event, noPropagation);
4421 cancel: function() {
4425 this._mouseUp({ target: null });
4427 if(this.options.helper === "original") {
4428 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4430 this.currentItem.show();
4433 //Post deactivating events to containers
4434 for (var i = this.containers.length - 1; i >= 0; i--){
4435 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
4436 if(this.containers[i].containerCache.over) {
4437 this.containers[i]._trigger("out", null, this._uiHash(this));
4438 this.containers[i].containerCache.over = 0;
4444 if (this.placeholder) {
4445 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4446 if(this.placeholder[0].parentNode) {
4447 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4449 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
4450 this.helper.remove();
4460 if(this.domPosition.prev) {
4461 $(this.domPosition.prev).after(this.currentItem);
4463 $(this.domPosition.parent).prepend(this.currentItem);
4471 serialize: function(o) {
4473 var items = this._getItemsAsjQuery(o && o.connected),
4477 $(items).each(function() {
4478 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
4480 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4484 if(!str.length && o.key) {
4485 str.push(o.key + "=");
4488 return str.join("&");
4492 toArray: function(o) {
4494 var items = this._getItemsAsjQuery(o && o.connected),
4499 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
4504 /* Be careful with the following core functions */
4505 _intersectsWith: function(item) {
4507 var x1 = this.positionAbs.left,
4508 x2 = x1 + this.helperProportions.width,
4509 y1 = this.positionAbs.top,
4510 y2 = y1 + this.helperProportions.height,
4514 b = t + item.height,
4515 dyClick = this.offset.click.top,
4516 dxClick = this.offset.click.left,
4517 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
4518 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
4519 isOverElement = isOverElementHeight && isOverElementWidth;
4521 if ( this.options.tolerance === "pointer" ||
4522 this.options.forcePointerForContainers ||
4523 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4525 return isOverElement;
4528 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4529 x2 - (this.helperProportions.width / 2) < r && // Left Half
4530 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4531 y2 - (this.helperProportions.height / 2) < b ); // Top Half
4536 _intersectsWithPointer: function(item) {
4538 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4539 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4540 isOverElement = isOverElementHeight && isOverElementWidth,
4541 verticalDirection = this._getDragVerticalDirection(),
4542 horizontalDirection = this._getDragHorizontalDirection();
4544 if (!isOverElement) {
4548 return this.floating ?
4549 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4550 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4554 _intersectsWithSides: function(item) {
4556 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4557 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4558 verticalDirection = this._getDragVerticalDirection(),
4559 horizontalDirection = this._getDragHorizontalDirection();
4561 if (this.floating && horizontalDirection) {
4562 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4564 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4569 _getDragVerticalDirection: function() {
4570 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4571 return delta !== 0 && (delta > 0 ? "down" : "up");
4574 _getDragHorizontalDirection: function() {
4575 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4576 return delta !== 0 && (delta > 0 ? "right" : "left");
4579 refresh: function(event) {
4580 this._refreshItems(event);
4581 this.refreshPositions();
4585 _connectWith: function() {
4586 var options = this.options;
4587 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4590 _getItemsAsjQuery: function(connected) {
4592 var i, j, cur, inst,
4595 connectWith = this._connectWith();
4597 if(connectWith && connected) {
4598 for (i = connectWith.length - 1; i >= 0; i--){
4599 cur = $(connectWith[i]);
4600 for ( j = cur.length - 1; j >= 0; j--){
4601 inst = $.data(cur[j], this.widgetFullName);
4602 if(inst && inst !== this && !inst.options.disabled) {
4603 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
4609 queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
4611 for (i = queries.length - 1; i >= 0; i--){
4612 queries[i][0].each(function() {
4621 _removeCurrentsFromItems: function() {
4623 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4625 this.items = $.grep(this.items, function (item) {
4626 for (var j=0; j < list.length; j++) {
4627 if(list[j] === item.item[0]) {
4636 _refreshItems: function(event) {
4639 this.containers = [this];
4641 var i, j, cur, inst, targetData, _queries, item, queriesLength,
4643 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4644 connectWith = this._connectWith();
4646 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4647 for (i = connectWith.length - 1; i >= 0; i--){
4648 cur = $(connectWith[i]);
4649 for (j = cur.length - 1; j >= 0; j--){
4650 inst = $.data(cur[j], this.widgetFullName);
4651 if(inst && inst !== this && !inst.options.disabled) {
4652 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4653 this.containers.push(inst);
4659 for (i = queries.length - 1; i >= 0; i--) {
4660 targetData = queries[i][1];
4661 _queries = queries[i][0];
4663 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4664 item = $(_queries[j]);
4666 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4670 instance: targetData,
4671 width: 0, height: 0,
4679 refreshPositions: function(fast) {
4681 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4682 if(this.offsetParent && this.helper) {
4683 this.offset.parent = this._getParentOffset();
4688 for (i = this.items.length - 1; i >= 0; i--){
4689 item = this.items[i];
4691 //We ignore calculating positions of all connected containers when we're not over them
4692 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4696 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4699 item.width = t.outerWidth();
4700 item.height = t.outerHeight();
4708 if(this.options.custom && this.options.custom.refreshContainers) {
4709 this.options.custom.refreshContainers.call(this);
4711 for (i = this.containers.length - 1; i >= 0; i--){
4712 p = this.containers[i].element.offset();
4713 this.containers[i].containerCache.left = p.left;
4714 this.containers[i].containerCache.top = p.top;
4715 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4716 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4723 _createPlaceholder: function(that) {
4724 that = that || this;
4728 if(!o.placeholder || o.placeholder.constructor === String) {
4729 className = o.placeholder;
4731 element: function() {
4733 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4734 element = $( "<" + nodeName + ">", that.document[0] )
4735 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4736 .removeClass("ui-sortable-helper");
4738 if ( nodeName === "tr" ) {
4739 that.currentItem.children().each(function() {
4740 $( "<td> </td>", that.document[0] )
4741 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
4742 .appendTo( element );
4744 } else if ( nodeName === "img" ) {
4745 element.attr( "src", that.currentItem.attr( "src" ) );
4749 element.css( "visibility", "hidden" );
4754 update: function(container, p) {
4756 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4757 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4758 if(className && !o.forcePlaceholderSize) {
4762 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4763 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4764 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4769 //Create the placeholder
4770 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4772 //Append it after the actual current item
4773 that.currentItem.after(that.placeholder);
4775 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4776 o.placeholder.update(that, that.placeholder);
4780 _contactContainers: function(event) {
4781 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4782 innermostContainer = null,
4783 innermostIndex = null;
4785 // get innermost container that intersects with item
4786 for (i = this.containers.length - 1; i >= 0; i--) {
4788 // never consider a container that's located within the item itself
4789 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4793 if(this._intersectsWith(this.containers[i].containerCache)) {
4795 // if we've already found a container and it's more "inner" than this, then continue
4796 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4800 innermostContainer = this.containers[i];
4804 // container doesn't intersect. trigger "out" event if necessary
4805 if(this.containers[i].containerCache.over) {
4806 this.containers[i]._trigger("out", event, this._uiHash(this));
4807 this.containers[i].containerCache.over = 0;
4813 // if no intersecting containers found, return
4814 if(!innermostContainer) {
4818 // move the item into the container if it's not there already
4819 if(this.containers.length === 1) {
4820 if (!this.containers[innermostIndex].containerCache.over) {
4821 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4822 this.containers[innermostIndex].containerCache.over = 1;
4826 //When entering a new container, we will find the item with the least distance and append our item near it
4828 itemWithLeastDistance = null;
4829 floating = innermostContainer.floating || isFloating(this.currentItem);
4830 posProperty = floating ? "left" : "top";
4831 sizeProperty = floating ? "width" : "height";
4832 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4833 for (j = this.items.length - 1; j >= 0; j--) {
4834 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4837 if(this.items[j].item[0] === this.currentItem[0]) {
4840 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4843 cur = this.items[j].item.offset()[posProperty];
4845 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4847 cur += this.items[j][sizeProperty];
4850 if(Math.abs(cur - base) < dist) {
4851 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4852 this.direction = nearBottom ? "up": "down";
4856 //Check if dropOnEmpty is enabled
4857 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4861 if(this.currentContainer === this.containers[innermostIndex]) {
4865 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4866 this._trigger("change", event, this._uiHash());
4867 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4868 this.currentContainer = this.containers[innermostIndex];
4870 //Update the placeholder
4871 this.options.placeholder.update(this.currentContainer, this.placeholder);
4873 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4874 this.containers[innermostIndex].containerCache.over = 1;
4880 _createHelper: function(event) {
4882 var o = this.options,
4883 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4885 //Add the helper to the DOM if that didn't happen already
4886 if(!helper.parents("body").length) {
4887 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4890 if(helper[0] === this.currentItem[0]) {
4891 this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
4894 if(!helper[0].style.width || o.forceHelperSize) {
4895 helper.width(this.currentItem.width());
4897 if(!helper[0].style.height || o.forceHelperSize) {
4898 helper.height(this.currentItem.height());
4905 _adjustOffsetFromHelper: function(obj) {
4906 if (typeof obj === "string") {
4907 obj = obj.split(" ");
4909 if ($.isArray(obj)) {
4910 obj = {left: +obj[0], top: +obj[1] || 0};
4912 if ("left" in obj) {
4913 this.offset.click.left = obj.left + this.margins.left;
4915 if ("right" in obj) {
4916 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4919 this.offset.click.top = obj.top + this.margins.top;
4921 if ("bottom" in obj) {
4922 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4926 _getParentOffset: function() {
4929 //Get the offsetParent and cache its position
4930 this.offsetParent = this.helper.offsetParent();
4931 var po = this.offsetParent.offset();
4933 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4934 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4935 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4936 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4937 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4938 po.left += this.scrollParent.scrollLeft();
4939 po.top += this.scrollParent.scrollTop();
4942 // This needs to be actually done for all browsers, since pageX/pageY includes this information
4943 // with an ugly IE fix
4944 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4945 po = { top: 0, left: 0 };
4949 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4950 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4955 _getRelativeOffset: function() {
4957 if(this.cssPosition === "relative") {
4958 var p = this.currentItem.position();
4960 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4961 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4964 return { top: 0, left: 0 };
4969 _cacheMargins: function() {
4971 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4972 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4976 _cacheHelperProportions: function() {
4977 this.helperProportions = {
4978 width: this.helper.outerWidth(),
4979 height: this.helper.outerHeight()
4983 _setContainment: function() {
4987 if(o.containment === "parent") {
4988 o.containment = this.helper[0].parentNode;
4990 if(o.containment === "document" || o.containment === "window") {
4991 this.containment = [
4992 0 - this.offset.relative.left - this.offset.parent.left,
4993 0 - this.offset.relative.top - this.offset.parent.top,
4994 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4995 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4999 if(!(/^(document|window|parent)$/).test(o.containment)) {
5000 ce = $(o.containment)[0];
5001 co = $(o.containment).offset();
5002 over = ($(ce).css("overflow") !== "hidden");
5004 this.containment = [
5005 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
5006 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
5007 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
5008 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
5014 _convertPositionTo: function(d, pos) {
5017 pos = this.position;
5019 var mod = d === "absolute" ? 1 : -1,
5020 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
5021 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5025 pos.top + // The absolute mouse position
5026 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
5027 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
5028 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
5031 pos.left + // The absolute mouse position
5032 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
5033 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
5034 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
5040 _generatePosition: function(event) {
5044 pageX = event.pageX,
5045 pageY = event.pageY,
5046 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5048 // This is another very weird special case that only happens for relative elements:
5049 // 1. If the css position is relative
5050 // 2. and the scroll parent is the document or similar to the offset parent
5051 // we have to refresh the relative offset during the scroll so there are no jumps
5052 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
5053 this.offset.relative = this._getRelativeOffset();
5057 * - Position constraining -
5058 * Constrain the position to a mix of grid, containment.
5061 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
5063 if(this.containment) {
5064 if(event.pageX - this.offset.click.left < this.containment[0]) {
5065 pageX = this.containment[0] + this.offset.click.left;
5067 if(event.pageY - this.offset.click.top < this.containment[1]) {
5068 pageY = this.containment[1] + this.offset.click.top;
5070 if(event.pageX - this.offset.click.left > this.containment[2]) {
5071 pageX = this.containment[2] + this.offset.click.left;
5073 if(event.pageY - this.offset.click.top > this.containment[3]) {
5074 pageY = this.containment[3] + this.offset.click.top;
5079 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
5080 pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
5082 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
5083 pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
5090 pageY - // The absolute mouse position
5091 this.offset.click.top - // Click offset (relative to the element)
5092 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
5093 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
5094 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
5097 pageX - // The absolute mouse position
5098 this.offset.click.left - // Click offset (relative to the element)
5099 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
5100 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
5101 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
5107 _rearrange: function(event, i, a, hardRefresh) {
5109 a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
5111 //Various things done here to improve the performance:
5112 // 1. we create a setTimeout, that calls refreshPositions
5113 // 2. on the instance, we have a counter variable, that get's higher after every append
5114 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
5115 // 4. this lets only the last addition to the timeout stack through
5116 this.counter = this.counter ? ++this.counter : 1;
5117 var counter = this.counter;
5119 this._delay(function() {
5120 if(counter === this.counter) {
5121 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
5127 _clear: function(event, noPropagation) {
5129 this.reverting = false;
5130 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
5131 // everything else normalized again
5133 delayedTriggers = [];
5135 // We first have to update the dom position of the actual currentItem
5136 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
5137 if(!this._noFinalSort && this.currentItem.parent().length) {
5138 this.placeholder.before(this.currentItem);
5140 this._noFinalSort = null;
5142 if(this.helper[0] === this.currentItem[0]) {
5143 for(i in this._storedCSS) {
5144 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
5145 this._storedCSS[i] = "";
5148 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
5150 this.currentItem.show();
5153 if(this.fromOutside && !noPropagation) {
5154 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
5156 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
5157 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
5160 // Check if the items Container has Changed and trigger appropriate
5162 if (this !== this.currentContainer) {
5163 if(!noPropagation) {
5164 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
5165 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
5166 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
5171 //Post events to containers
5172 for (i = this.containers.length - 1; i >= 0; i--){
5173 if(!noPropagation) {
5174 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
5176 if(this.containers[i].containerCache.over) {
5177 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
5178 this.containers[i].containerCache.over = 0;
5182 //Do what was originally in plugins
5183 if ( this.storedCursor ) {
5184 this.document.find( "body" ).css( "cursor", this.storedCursor );
5185 this.storedStylesheet.remove();
5187 if(this._storedOpacity) {
5188 this.helper.css("opacity", this._storedOpacity);
5190 if(this._storedZIndex) {
5191 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
5194 this.dragging = false;
5195 if(this.cancelHelperRemoval) {
5196 if(!noPropagation) {
5197 this._trigger("beforeStop", event, this._uiHash());
5198 for (i=0; i < delayedTriggers.length; i++) {
5199 delayedTriggers[i].call(this, event);
5200 } //Trigger all delayed events
5201 this._trigger("stop", event, this._uiHash());
5204 this.fromOutside = false;
5208 if(!noPropagation) {
5209 this._trigger("beforeStop", event, this._uiHash());
5212 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
5213 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
5215 if(this.helper[0] !== this.currentItem[0]) {
5216 this.helper.remove();
5220 if(!noPropagation) {
5221 for (i=0; i < delayedTriggers.length; i++) {
5222 delayedTriggers[i].call(this, event);
5223 } //Trigger all delayed events
5224 this._trigger("stop", event, this._uiHash());
5227 this.fromOutside = false;
5232 _trigger: function() {
5233 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
5238 _uiHash: function(_inst) {
5239 var inst = _inst || this;
5241 helper: inst.helper,
5242 placeholder: inst.placeholder || $([]),
5243 position: inst.position,
5244 originalPosition: inst.originalPosition,
5245 offset: inst.positionAbs,
5246 item: inst.currentItem,
5247 sender: _inst ? _inst.element : null
5254 (function( $, undefined ) {
5256 // used to prevent race conditions with remote data sources
5257 var requestIndex = 0;
5259 $.widget( "ui.autocomplete", {
5261 defaultElement: "<input>",
5286 _create: function() {
5287 // Some browsers only repeat keydown events, not keypress events,
5288 // so we use the suppressKeyPress flag to determine if we've already
5289 // handled the keydown event. #7269
5290 // Unfortunately the code for & in keypress is the same as the up arrow,
5291 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
5292 // events when we know the keydown event was used to modify the
5293 // search term. #7799
5294 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
5295 nodeName = this.element[0].nodeName.toLowerCase(),
5296 isTextarea = nodeName === "textarea",
5297 isInput = nodeName === "input";
5300 // Textareas are always multi-line
5302 // Inputs are always single-line, even if inside a contentEditable element
5303 // IE also treats inputs as contentEditable
5305 // All other element types are determined by whether or not they're contentEditable
5306 this.element.prop( "isContentEditable" );
5308 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
5309 this.isNewMenu = true;
5312 .addClass( "ui-autocomplete-input" )
5313 .attr( "autocomplete", "off" );
5315 this._on( this.element, {
5316 keydown: function( event ) {
5317 /*jshint maxcomplexity:15*/
5318 if ( this.element.prop( "readOnly" ) ) {
5319 suppressKeyPress = true;
5320 suppressInput = true;
5321 suppressKeyPressRepeat = true;
5325 suppressKeyPress = false;
5326 suppressInput = false;
5327 suppressKeyPressRepeat = false;
5328 var keyCode = $.ui.keyCode;
5329 switch( event.keyCode ) {
5330 case keyCode.PAGE_UP:
5331 suppressKeyPress = true;
5332 this._move( "previousPage", event );
5334 case keyCode.PAGE_DOWN:
5335 suppressKeyPress = true;
5336 this._move( "nextPage", event );
5339 suppressKeyPress = true;
5340 this._keyEvent( "previous", event );
5343 suppressKeyPress = true;
5344 this._keyEvent( "next", event );
5347 case keyCode.NUMPAD_ENTER:
5348 // when menu is open and has focus
5349 if ( this.menu.active ) {
5350 // #6055 - Opera still allows the keypress to occur
5351 // which causes forms to submit
5352 suppressKeyPress = true;
5353 event.preventDefault();
5354 this.menu.select( event );
5358 if ( this.menu.active ) {
5359 this.menu.select( event );
5362 case keyCode.ESCAPE:
5363 if ( this.menu.element.is( ":visible" ) ) {
5364 this._value( this.term );
5365 this.close( event );
5366 // Different browsers have different default behavior for escape
5367 // Single press can mean undo or clear
5368 // Double press in IE means clear the whole form
5369 event.preventDefault();
5373 suppressKeyPressRepeat = true;
5374 // search timeout should be triggered before the input value is changed
5375 this._searchTimeout( event );
5379 keypress: function( event ) {
5380 if ( suppressKeyPress ) {
5381 suppressKeyPress = false;
5382 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
5383 event.preventDefault();
5387 if ( suppressKeyPressRepeat ) {
5391 // replicate some key handlers to allow them to repeat in Firefox and Opera
5392 var keyCode = $.ui.keyCode;
5393 switch( event.keyCode ) {
5394 case keyCode.PAGE_UP:
5395 this._move( "previousPage", event );
5397 case keyCode.PAGE_DOWN:
5398 this._move( "nextPage", event );
5401 this._keyEvent( "previous", event );
5404 this._keyEvent( "next", event );
5408 input: function( event ) {
5409 if ( suppressInput ) {
5410 suppressInput = false;
5411 event.preventDefault();
5414 this._searchTimeout( event );
5417 this.selectedItem = null;
5418 this.previous = this._value();
5420 blur: function( event ) {
5421 if ( this.cancelBlur ) {
5422 delete this.cancelBlur;
5426 clearTimeout( this.searching );
5427 this.close( event );
5428 this._change( event );
5433 this.menu = $( "<ul>" )
5434 .addClass( "ui-autocomplete ui-front" )
5435 .appendTo( this._appendTo() )
5437 // disable ARIA support, the live region takes care of that
5443 this._on( this.menu.element, {
5444 mousedown: function( event ) {
5445 // prevent moving focus out of the text field
5446 event.preventDefault();
5448 // IE doesn't prevent moving focus even with event.preventDefault()
5449 // so we set a flag to know when we should ignore the blur event
5450 this.cancelBlur = true;
5451 this._delay(function() {
5452 delete this.cancelBlur;
5455 // clicking on the scrollbar causes focus to shift to the body
5456 // but we can't detect a mouseup or a click immediately afterward
5457 // so we have to track the next mousedown and close the menu if
5458 // the user clicks somewhere outside of the autocomplete
5459 var menuElement = this.menu.element[ 0 ];
5460 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
5461 this._delay(function() {
5463 this.document.one( "mousedown", function( event ) {
5464 if ( event.target !== that.element[ 0 ] &&
5465 event.target !== menuElement &&
5466 !$.contains( menuElement, event.target ) ) {
5473 menufocus: function( event, ui ) {
5475 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
5476 if ( this.isNewMenu ) {
5477 this.isNewMenu = false;
5478 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
5481 this.document.one( "mousemove", function() {
5482 $( event.target ).trigger( event.originalEvent );
5489 var item = ui.item.data( "ui-autocomplete-item" );
5490 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
5491 // use value to match what will end up in the input, if it was a key event
5492 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
5493 this._value( item.value );
5496 // Normally the input is populated with the item's value as the
5497 // menu is navigated, causing screen readers to notice a change and
5498 // announce the item. Since the focus event was canceled, this doesn't
5499 // happen, so we update the live region so that screen readers can
5500 // still notice the change and announce it.
5501 this.liveRegion.text( item.value );
5504 menuselect: function( event, ui ) {
5505 var item = ui.item.data( "ui-autocomplete-item" ),
5506 previous = this.previous;
5508 // only trigger when focus was lost (click on menu)
5509 if ( this.element[0] !== this.document[0].activeElement ) {
5510 this.element.focus();
5511 this.previous = previous;
5512 // #6109 - IE triggers two focus events and the second
5513 // is asynchronous, so we need to reset the previous
5514 // term synchronously and asynchronously :-(
5515 this._delay(function() {
5516 this.previous = previous;
5517 this.selectedItem = item;
5521 if ( false !== this._trigger( "select", event, { item: item } ) ) {
5522 this._value( item.value );
5524 // reset the term after the select event
5525 // this allows custom select handling to work properly
5526 this.term = this._value();
5528 this.close( event );
5529 this.selectedItem = item;
5533 this.liveRegion = $( "<span>", {
5535 "aria-live": "polite"
5537 .addClass( "ui-helper-hidden-accessible" )
5538 .insertBefore( this.element );
5540 // turning off autocomplete prevents the browser from remembering the
5541 // value when navigating through history, so we re-enable autocomplete
5542 // if the page is unloaded before the widget is destroyed. #7790
5543 this._on( this.window, {
5544 beforeunload: function() {
5545 this.element.removeAttr( "autocomplete" );
5550 _destroy: function() {
5551 clearTimeout( this.searching );
5553 .removeClass( "ui-autocomplete-input" )
5554 .removeAttr( "autocomplete" );
5555 this.menu.element.remove();
5556 this.liveRegion.remove();
5559 _setOption: function( key, value ) {
5560 this._super( key, value );
5561 if ( key === "source" ) {
5564 if ( key === "appendTo" ) {
5565 this.menu.element.appendTo( this._appendTo() );
5567 if ( key === "disabled" && value && this.xhr ) {
5572 _appendTo: function() {
5573 var element = this.options.appendTo;
5576 element = element.jquery || element.nodeType ?
5578 this.document.find( element ).eq( 0 );
5582 element = this.element.closest( ".ui-front" );
5585 if ( !element.length ) {
5586 element = this.document[0].body;
5592 _initSource: function() {
5595 if ( $.isArray(this.options.source) ) {
5596 array = this.options.source;
5597 this.source = function( request, response ) {
5598 response( $.ui.autocomplete.filter( array, request.term ) );
5600 } else if ( typeof this.options.source === "string" ) {
5601 url = this.options.source;
5602 this.source = function( request, response ) {
5610 success: function( data ) {
5619 this.source = this.options.source;
5623 _searchTimeout: function( event ) {
5624 clearTimeout( this.searching );
5625 this.searching = this._delay(function() {
5626 // only search if the value has changed
5627 if ( this.term !== this._value() ) {
5628 this.selectedItem = null;
5629 this.search( null, event );
5631 }, this.options.delay );
5634 search: function( value, event ) {
5635 value = value != null ? value : this._value();
5637 // always save the actual value, not the one passed as an argument
5638 this.term = this._value();
5640 if ( value.length < this.options.minLength ) {
5641 return this.close( event );
5644 if ( this._trigger( "search", event ) === false ) {
5648 return this._search( value );
5651 _search: function( value ) {
5653 this.element.addClass( "ui-autocomplete-loading" );
5654 this.cancelSearch = false;
5656 this.source( { term: value }, this._response() );
5659 _response: function() {
5661 index = ++requestIndex;
5663 return function( content ) {
5664 if ( index === requestIndex ) {
5665 that.__response( content );
5669 if ( !that.pending ) {
5670 that.element.removeClass( "ui-autocomplete-loading" );
5675 __response: function( content ) {
5677 content = this._normalize( content );
5679 this._trigger( "response", null, { content: content } );
5680 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
5681 this._suggest( content );
5682 this._trigger( "open" );
5684 // use ._close() instead of .close() so we don't cancel future searches
5689 close: function( event ) {
5690 this.cancelSearch = true;
5691 this._close( event );
5694 _close: function( event ) {
5695 if ( this.menu.element.is( ":visible" ) ) {
5696 this.menu.element.hide();
5698 this.isNewMenu = true;
5699 this._trigger( "close", event );
5703 _change: function( event ) {
5704 if ( this.previous !== this._value() ) {
5705 this._trigger( "change", event, { item: this.selectedItem } );
5709 _normalize: function( items ) {
5710 // assume all items have the right format when the first item is complete
5711 if ( items.length && items[0].label && items[0].value ) {
5714 return $.map( items, function( item ) {
5715 if ( typeof item === "string" ) {
5722 label: item.label || item.value,
5723 value: item.value || item.label
5728 _suggest: function( items ) {
5729 var ul = this.menu.element.empty();
5730 this._renderMenu( ul, items );
5731 this.isNewMenu = true;
5732 this.menu.refresh();
5734 // size and position menu
5737 ul.position( $.extend({
5739 }, this.options.position ));
5741 if ( this.options.autoFocus ) {
5746 _resizeMenu: function() {
5747 var ul = this.menu.element;
5748 ul.outerWidth( Math.max(
5749 // Firefox wraps long text (possibly a rounding bug)
5750 // so we add 1px to avoid the wrapping (#7513)
5751 ul.width( "" ).outerWidth() + 1,
5752 this.element.outerWidth()
5756 _renderMenu: function( ul, items ) {
5758 $.each( items, function( index, item ) {
5759 that._renderItemData( ul, item );
5763 _renderItemData: function( ul, item ) {
5764 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
5767 _renderItem: function( ul, item ) {
5769 .append( $( "<a>" ).text( item.label ) )
5773 _move: function( direction, event ) {
5774 if ( !this.menu.element.is( ":visible" ) ) {
5775 this.search( null, event );
5778 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
5779 this.menu.isLastItem() && /^next/.test( direction ) ) {
5780 this._value( this.term );
5784 this.menu[ direction ]( event );
5787 widget: function() {
5788 return this.menu.element;
5791 _value: function() {
5792 return this.valueMethod.apply( this.element, arguments );
5795 _keyEvent: function( keyEvent, event ) {
5796 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
5797 this._move( keyEvent, event );
5799 // prevents moving cursor to beginning/end of the text field in some browsers
5800 event.preventDefault();
5805 $.extend( $.ui.autocomplete, {
5806 escapeRegex: function( value ) {
5807 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
5809 filter: function(array, term) {
5810 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
5811 return $.grep( array, function(value) {
5812 return matcher.test( value.label || value.value || value );
5818 // live region extension, adding a `messages` option
5819 // NOTE: This is an experimental API. We are still investigating
5820 // a full solution for string manipulation and internationalization.
5821 $.widget( "ui.autocomplete", $.ui.autocomplete, {
5824 noResults: "No search results.",
5825 results: function( amount ) {
5826 return amount + ( amount > 1 ? " results are" : " result is" ) +
5827 " available, use up and down arrow keys to navigate.";
5832 __response: function( content ) {
5834 this._superApply( arguments );
5835 if ( this.options.disabled || this.cancelSearch ) {
5838 if ( content && content.length ) {
5839 message = this.options.messages.results( content.length );
5841 message = this.options.messages.noResults;
5843 this.liveRegion.text( message );
5848 (function( $, undefined ) {
5850 $.widget( "ui.menu", {
5852 defaultElement: "<ul>",
5856 submenu: "ui-icon-carat-1-e"
5871 _create: function() {
5872 this.activeMenu = this.element;
5873 // flag used to prevent firing of the click handler
5874 // as the event bubbles up through nested menus
5875 this.mouseHandled = false;
5878 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
5879 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
5881 role: this.options.role,
5884 // need to catch all clicks on disabled menu
5885 // not possible through _on
5886 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
5887 if ( this.options.disabled ) {
5888 event.preventDefault();
5892 if ( this.options.disabled ) {
5894 .addClass( "ui-state-disabled" )
5895 .attr( "aria-disabled", "true" );
5899 // Prevent focus from sticking to links inside menu after clicking
5900 // them (focus should always stay on UL during navigation).
5901 "mousedown .ui-menu-item > a": function( event ) {
5902 event.preventDefault();
5904 "click .ui-state-disabled > a": function( event ) {
5905 event.preventDefault();
5907 "click .ui-menu-item:has(a)": function( event ) {
5908 var target = $( event.target ).closest( ".ui-menu-item" );
5909 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
5910 this.mouseHandled = true;
5912 this.select( event );
5913 // Open submenu on click
5914 if ( target.has( ".ui-menu" ).length ) {
5915 this.expand( event );
5916 } else if ( !this.element.is( ":focus" ) ) {
5917 // Redirect focus to the menu
5918 this.element.trigger( "focus", [ true ] );
5920 // If the active item is on the top level, let it stay active.
5921 // Otherwise, blur the active item since it is no longer visible.
5922 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
5923 clearTimeout( this.timer );
5928 "mouseenter .ui-menu-item": function( event ) {
5929 var target = $( event.currentTarget );
5930 // Remove ui-state-active class from siblings of the newly focused menu item
5931 // to avoid a jump caused by adjacent elements both having a class with a border
5932 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
5933 this.focus( event, target );
5935 mouseleave: "collapseAll",
5936 "mouseleave .ui-menu": "collapseAll",
5937 focus: function( event, keepActiveItem ) {
5938 // If there's already an active item, keep it active
5939 // If not, activate the first item
5940 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
5942 if ( !keepActiveItem ) {
5943 this.focus( event, item );
5946 blur: function( event ) {
5947 this._delay(function() {
5948 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
5949 this.collapseAll( event );
5958 // Clicks outside of a menu collapse any open menus
5959 this._on( this.document, {
5960 click: function( event ) {
5961 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
5962 this.collapseAll( event );
5965 // Reset the mouseHandled flag
5966 this.mouseHandled = false;
5971 _destroy: function() {
5972 // Destroy (sub)menus
5974 .removeAttr( "aria-activedescendant" )
5975 .find( ".ui-menu" ).addBack()
5976 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
5977 .removeAttr( "role" )
5978 .removeAttr( "tabIndex" )
5979 .removeAttr( "aria-labelledby" )
5980 .removeAttr( "aria-expanded" )
5981 .removeAttr( "aria-hidden" )
5982 .removeAttr( "aria-disabled" )
5986 // Destroy menu items
5987 this.element.find( ".ui-menu-item" )
5988 .removeClass( "ui-menu-item" )
5989 .removeAttr( "role" )
5990 .removeAttr( "aria-disabled" )
5993 .removeClass( "ui-corner-all ui-state-hover" )
5994 .removeAttr( "tabIndex" )
5995 .removeAttr( "role" )
5996 .removeAttr( "aria-haspopup" )
5997 .children().each( function() {
5998 var elem = $( this );
5999 if ( elem.data( "ui-menu-submenu-carat" ) ) {
6004 // Destroy menu dividers
6005 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
6008 _keydown: function( event ) {
6009 /*jshint maxcomplexity:20*/
6010 var match, prev, character, skip, regex,
6011 preventDefault = true;
6013 function escape( value ) {
6014 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
6017 switch ( event.keyCode ) {
6018 case $.ui.keyCode.PAGE_UP:
6019 this.previousPage( event );
6021 case $.ui.keyCode.PAGE_DOWN:
6022 this.nextPage( event );
6024 case $.ui.keyCode.HOME:
6025 this._move( "first", "first", event );
6027 case $.ui.keyCode.END:
6028 this._move( "last", "last", event );
6030 case $.ui.keyCode.UP:
6031 this.previous( event );
6033 case $.ui.keyCode.DOWN:
6036 case $.ui.keyCode.LEFT:
6037 this.collapse( event );
6039 case $.ui.keyCode.RIGHT:
6040 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
6041 this.expand( event );
6044 case $.ui.keyCode.ENTER:
6045 case $.ui.keyCode.SPACE:
6046 this._activate( event );
6048 case $.ui.keyCode.ESCAPE:
6049 this.collapse( event );
6052 preventDefault = false;
6053 prev = this.previousFilter || "";
6054 character = String.fromCharCode( event.keyCode );
6057 clearTimeout( this.filterTimer );
6059 if ( character === prev ) {
6062 character = prev + character;
6065 regex = new RegExp( "^" + escape( character ), "i" );
6066 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
6067 return regex.test( $( this ).children( "a" ).text() );
6069 match = skip && match.index( this.active.next() ) !== -1 ?
6070 this.active.nextAll( ".ui-menu-item" ) :
6073 // If no matches on the current filter, reset to the last character pressed
6074 // to move down the menu to the first item that starts with that character
6075 if ( !match.length ) {
6076 character = String.fromCharCode( event.keyCode );
6077 regex = new RegExp( "^" + escape( character ), "i" );
6078 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
6079 return regex.test( $( this ).children( "a" ).text() );
6083 if ( match.length ) {
6084 this.focus( event, match );
6085 if ( match.length > 1 ) {
6086 this.previousFilter = character;
6087 this.filterTimer = this._delay(function() {
6088 delete this.previousFilter;
6091 delete this.previousFilter;
6094 delete this.previousFilter;
6098 if ( preventDefault ) {
6099 event.preventDefault();
6103 _activate: function( event ) {
6104 if ( !this.active.is( ".ui-state-disabled" ) ) {
6105 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
6106 this.expand( event );
6108 this.select( event );
6113 refresh: function() {
6115 icon = this.options.icons.submenu,
6116 submenus = this.element.find( this.options.menus );
6118 // Initialize nested menus
6119 submenus.filter( ":not(.ui-menu)" )
6120 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
6123 role: this.options.role,
6124 "aria-hidden": "true",
6125 "aria-expanded": "false"
6128 var menu = $( this ),
6129 item = menu.prev( "a" ),
6130 submenuCarat = $( "<span>" )
6131 .addClass( "ui-menu-icon ui-icon " + icon )
6132 .data( "ui-menu-submenu-carat", true );
6135 .attr( "aria-haspopup", "true" )
6136 .prepend( submenuCarat );
6137 menu.attr( "aria-labelledby", item.attr( "id" ) );
6140 menus = submenus.add( this.element );
6142 // Don't refresh list items that are already adapted
6143 menus.children( ":not(.ui-menu-item):has(a)" )
6144 .addClass( "ui-menu-item" )
6145 .attr( "role", "presentation" )
6148 .addClass( "ui-corner-all" )
6151 role: this._itemRole()
6154 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
6155 menus.children( ":not(.ui-menu-item)" ).each(function() {
6156 var item = $( this );
6157 // hyphen, em dash, en dash
6158 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
6159 item.addClass( "ui-widget-content ui-menu-divider" );
6163 // Add aria-disabled attribute to any disabled menu item
6164 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
6166 // If the active item has been removed, blur the menu
6167 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
6172 _itemRole: function() {
6176 }[ this.options.role ];
6179 _setOption: function( key, value ) {
6180 if ( key === "icons" ) {
6181 this.element.find( ".ui-menu-icon" )
6182 .removeClass( this.options.icons.submenu )
6183 .addClass( value.submenu );
6185 this._super( key, value );
6188 focus: function( event, item ) {
6189 var nested, focused;
6190 this.blur( event, event && event.type === "focus" );
6192 this._scrollIntoView( item );
6194 this.active = item.first();
6195 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
6196 // Only update aria-activedescendant if there's a role
6197 // otherwise we assume focus is managed elsewhere
6198 if ( this.options.role ) {
6199 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
6202 // Highlight active parent menu item, if any
6205 .closest( ".ui-menu-item" )
6206 .children( "a:first" )
6207 .addClass( "ui-state-active" );
6209 if ( event && event.type === "keydown" ) {
6212 this.timer = this._delay(function() {
6217 nested = item.children( ".ui-menu" );
6218 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
6219 this._startOpening(nested);
6221 this.activeMenu = item.parent();
6223 this._trigger( "focus", event, { item: item } );
6226 _scrollIntoView: function( item ) {
6227 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
6228 if ( this._hasScroll() ) {
6229 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
6230 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
6231 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
6232 scroll = this.activeMenu.scrollTop();
6233 elementHeight = this.activeMenu.height();
6234 itemHeight = item.height();
6237 this.activeMenu.scrollTop( scroll + offset );
6238 } else if ( offset + itemHeight > elementHeight ) {
6239 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
6244 blur: function( event, fromFocus ) {
6246 clearTimeout( this.timer );
6249 if ( !this.active ) {
6253 this.active.children( "a" ).removeClass( "ui-state-focus" );
6256 this._trigger( "blur", event, { item: this.active } );
6259 _startOpening: function( submenu ) {
6260 clearTimeout( this.timer );
6262 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
6263 // shift in the submenu position when mousing over the carat icon
6264 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
6268 this.timer = this._delay(function() {
6270 this._open( submenu );
6274 _open: function( submenu ) {
6275 var position = $.extend({
6277 }, this.options.position );
6279 clearTimeout( this.timer );
6280 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
6282 .attr( "aria-hidden", "true" );
6286 .removeAttr( "aria-hidden" )
6287 .attr( "aria-expanded", "true" )
6288 .position( position );
6291 collapseAll: function( event, all ) {
6292 clearTimeout( this.timer );
6293 this.timer = this._delay(function() {
6294 // If we were passed an event, look for the submenu that contains the event
6295 var currentMenu = all ? this.element :
6296 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
6298 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
6299 if ( !currentMenu.length ) {
6300 currentMenu = this.element;
6303 this._close( currentMenu );
6306 this.activeMenu = currentMenu;
6310 // With no arguments, closes the currently active menu - if nothing is active
6311 // it closes all menus. If passed an argument, it will search for menus BELOW
6312 _close: function( startMenu ) {
6314 startMenu = this.active ? this.active.parent() : this.element;
6320 .attr( "aria-hidden", "true" )
6321 .attr( "aria-expanded", "false" )
6323 .find( "a.ui-state-active" )
6324 .removeClass( "ui-state-active" );
6327 collapse: function( event ) {
6328 var newItem = this.active &&
6329 this.active.parent().closest( ".ui-menu-item", this.element );
6330 if ( newItem && newItem.length ) {
6332 this.focus( event, newItem );
6336 expand: function( event ) {
6337 var newItem = this.active &&
6339 .children( ".ui-menu " )
6340 .children( ".ui-menu-item" )
6343 if ( newItem && newItem.length ) {
6344 this._open( newItem.parent() );
6346 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
6347 this._delay(function() {
6348 this.focus( event, newItem );
6353 next: function( event ) {
6354 this._move( "next", "first", event );
6357 previous: function( event ) {
6358 this._move( "prev", "last", event );
6361 isFirstItem: function() {
6362 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
6365 isLastItem: function() {
6366 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
6369 _move: function( direction, filter, event ) {
6371 if ( this.active ) {
6372 if ( direction === "first" || direction === "last" ) {
6374 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
6378 [ direction + "All" ]( ".ui-menu-item" )
6382 if ( !next || !next.length || !this.active ) {
6383 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
6386 this.focus( event, next );
6389 nextPage: function( event ) {
6390 var item, base, height;
6392 if ( !this.active ) {
6396 if ( this.isLastItem() ) {
6399 if ( this._hasScroll() ) {
6400 base = this.active.offset().top;
6401 height = this.element.height();
6402 this.active.nextAll( ".ui-menu-item" ).each(function() {
6404 return item.offset().top - base - height < 0;
6407 this.focus( event, item );
6409 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
6410 [ !this.active ? "first" : "last" ]() );
6414 previousPage: function( event ) {
6415 var item, base, height;
6416 if ( !this.active ) {
6420 if ( this.isFirstItem() ) {
6423 if ( this._hasScroll() ) {
6424 base = this.active.offset().top;
6425 height = this.element.height();
6426 this.active.prevAll( ".ui-menu-item" ).each(function() {
6428 return item.offset().top - base + height > 0;
6431 this.focus( event, item );
6433 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
6437 _hasScroll: function() {
6438 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
6441 select: function( event ) {
6442 // TODO: It should never be possible to not have an active item at this
6443 // point, but the tests don't trigger mouseenter before click.
6444 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
6445 var ui = { item: this.active };
6446 if ( !this.active.has( ".ui-menu" ).length ) {
6447 this.collapseAll( event, true );
6449 this._trigger( "select", event, ui );
6454 (function($, undefined) {
6456 var dataSpace = "ui-effects-";
6463 * jQuery Color Animations v2.1.2
6464 * https://github.com/jquery/jquery-color
6466 * Copyright 2013 jQuery Foundation and other contributors
6467 * Released under the MIT license.
6468 * http://jquery.org/license
6470 * Date: Wed Jan 16 08:47:09 2013 -0600
6472 (function( jQuery, undefined ) {
6474 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
6476 // plusequals test for += 100 -= 100
6477 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
6478 // a set of RE's that can match strings and generate color tuples.
6480 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
6481 parse: function( execResult ) {
6490 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
6491 parse: function( execResult ) {
6493 execResult[ 1 ] * 2.55,
6494 execResult[ 2 ] * 2.55,
6495 execResult[ 3 ] * 2.55,
6500 // this regex ignores A-F because it's compared against an already lowercased string
6501 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
6502 parse: function( execResult ) {
6504 parseInt( execResult[ 1 ], 16 ),
6505 parseInt( execResult[ 2 ], 16 ),
6506 parseInt( execResult[ 3 ], 16 )
6510 // this regex ignores A-F because it's compared against an already lowercased string
6511 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
6512 parse: function( execResult ) {
6514 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
6515 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
6516 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
6520 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
6522 parse: function( execResult ) {
6525 execResult[ 2 ] / 100,
6526 execResult[ 3 ] / 100,
6533 color = jQuery.Color = function( color, green, blue, alpha ) {
6534 return new jQuery.Color.fn.parse( color, green, blue, alpha );
6584 support = color.support = {},
6586 // element for support tests
6587 supportElem = jQuery( "<p>" )[ 0 ],
6589 // colors = jQuery.Color.names
6592 // local aliases of functions called often
6595 // determine rgba support immediately
6596 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
6597 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
6599 // define cache name and alpha properties
6600 // for rgba and hsla spaces
6601 each( spaces, function( spaceName, space ) {
6602 space.cache = "_" + spaceName;
6603 space.props.alpha = {
6610 function clamp( value, prop, allowEmpty ) {
6611 var type = propTypes[ prop.type ] || {};
6613 if ( value == null ) {
6614 return (allowEmpty || !prop.def) ? null : prop.def;
6617 // ~~ is an short way of doing floor for positive numbers
6618 value = type.floor ? ~~value : parseFloat( value );
6620 // IE will pass in empty strings as value for alpha,
6621 // which will hit this case
6622 if ( isNaN( value ) ) {
6627 // we add mod before modding to make sure that negatives values
6628 // get converted properly: -10 -> 350
6629 return (value + type.mod) % type.mod;
6632 // for now all property types without mod have min and max
6633 return 0 > value ? 0 : type.max < value ? type.max : value;
6636 function stringParse( string ) {
6638 rgba = inst._rgba = [];
6640 string = string.toLowerCase();
6642 each( stringParsers, function( i, parser ) {
6644 match = parser.re.exec( string ),
6645 values = match && parser.parse( match ),
6646 spaceName = parser.space || "rgba";
6649 parsed = inst[ spaceName ]( values );
6651 // if this was an rgba parse the assignment might happen twice
6653 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
6654 rgba = inst._rgba = parsed._rgba;
6656 // exit each( stringParsers ) here because we matched
6661 // Found a stringParser that handled it
6662 if ( rgba.length ) {
6664 // if this came from a parsed string, force "transparent" when alpha is 0
6665 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
6666 if ( rgba.join() === "0,0,0,0" ) {
6667 jQuery.extend( rgba, colors.transparent );
6673 return colors[ string ];
6676 color.fn = jQuery.extend( color.prototype, {
6677 parse: function( red, green, blue, alpha ) {
6678 if ( red === undefined ) {
6679 this._rgba = [ null, null, null, null ];
6682 if ( red.jquery || red.nodeType ) {
6683 red = jQuery( red ).css( green );
6688 type = jQuery.type( red ),
6689 rgba = this._rgba = [];
6691 // more than 1 argument specified - assume ( red, green, blue, alpha )
6692 if ( green !== undefined ) {
6693 red = [ red, green, blue, alpha ];
6697 if ( type === "string" ) {
6698 return this.parse( stringParse( red ) || colors._default );
6701 if ( type === "array" ) {
6702 each( spaces.rgba.props, function( key, prop ) {
6703 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
6708 if ( type === "object" ) {
6709 if ( red instanceof color ) {
6710 each( spaces, function( spaceName, space ) {
6711 if ( red[ space.cache ] ) {
6712 inst[ space.cache ] = red[ space.cache ].slice();
6716 each( spaces, function( spaceName, space ) {
6717 var cache = space.cache;
6718 each( space.props, function( key, prop ) {
6720 // if the cache doesn't exist, and we know how to convert
6721 if ( !inst[ cache ] && space.to ) {
6723 // if the value was null, we don't need to copy it
6724 // if the key was alpha, we don't need to copy it either
6725 if ( key === "alpha" || red[ key ] == null ) {
6728 inst[ cache ] = space.to( inst._rgba );
6731 // this is the only case where we allow nulls for ALL properties.
6732 // call clamp with alwaysAllowEmpty
6733 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
6736 // everything defined but alpha?
6737 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
6738 // use the default of 1
6739 inst[ cache ][ 3 ] = 1;
6741 inst._rgba = space.from( inst[ cache ] );
6749 is: function( compare ) {
6750 var is = color( compare ),
6754 each( spaces, function( _, space ) {
6756 isCache = is[ space.cache ];
6758 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
6759 each( space.props, function( _, prop ) {
6760 if ( isCache[ prop.idx ] != null ) {
6761 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
6770 _space: function() {
6773 each( spaces, function( spaceName, space ) {
6774 if ( inst[ space.cache ] ) {
6775 used.push( spaceName );
6780 transition: function( other, distance ) {
6781 var end = color( other ),
6782 spaceName = end._space(),
6783 space = spaces[ spaceName ],
6784 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
6785 start = startColor[ space.cache ] || space.to( startColor._rgba ),
6786 result = start.slice();
6788 end = end[ space.cache ];
6789 each( space.props, function( key, prop ) {
6790 var index = prop.idx,
6791 startValue = start[ index ],
6792 endValue = end[ index ],
6793 type = propTypes[ prop.type ] || {};
6795 // if null, don't override start value
6796 if ( endValue === null ) {
6799 // if null - use end
6800 if ( startValue === null ) {
6801 result[ index ] = endValue;
6804 if ( endValue - startValue > type.mod / 2 ) {
6805 startValue += type.mod;
6806 } else if ( startValue - endValue > type.mod / 2 ) {
6807 startValue -= type.mod;
6810 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
6813 return this[ spaceName ]( result );
6815 blend: function( opaque ) {
6816 // if we are already opaque - return ourself
6817 if ( this._rgba[ 3 ] === 1 ) {
6821 var rgb = this._rgba.slice(),
6823 blend = color( opaque )._rgba;
6825 return color( jQuery.map( rgb, function( v, i ) {
6826 return ( 1 - a ) * blend[ i ] + a * v;
6829 toRgbaString: function() {
6830 var prefix = "rgba(",
6831 rgba = jQuery.map( this._rgba, function( v, i ) {
6832 return v == null ? ( i > 2 ? 1 : 0 ) : v;
6835 if ( rgba[ 3 ] === 1 ) {
6840 return prefix + rgba.join() + ")";
6842 toHslaString: function() {
6843 var prefix = "hsla(",
6844 hsla = jQuery.map( this.hsla(), function( v, i ) {
6851 v = Math.round( v * 100 ) + "%";
6856 if ( hsla[ 3 ] === 1 ) {
6860 return prefix + hsla.join() + ")";
6862 toHexString: function( includeAlpha ) {
6863 var rgba = this._rgba.slice(),
6866 if ( includeAlpha ) {
6867 rgba.push( ~~( alpha * 255 ) );
6870 return "#" + jQuery.map( rgba, function( v ) {
6872 // default to 0 when nulls exist
6873 v = ( v || 0 ).toString( 16 );
6874 return v.length === 1 ? "0" + v : v;
6877 toString: function() {
6878 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
6881 color.fn.parse.prototype = color.fn;
6883 // hsla conversions adapted from:
6884 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
6886 function hue2rgb( p, q, h ) {
6889 return p + (q - p) * h * 6;
6895 return p + (q - p) * ((2/3) - h) * 6;
6900 spaces.hsla.to = function ( rgba ) {
6901 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
6902 return [ null, null, null, rgba[ 3 ] ];
6904 var r = rgba[ 0 ] / 255,
6905 g = rgba[ 1 ] / 255,
6906 b = rgba[ 2 ] / 255,
6908 max = Math.max( r, g, b ),
6909 min = Math.min( r, g, b ),
6915 if ( min === max ) {
6917 } else if ( r === max ) {
6918 h = ( 60 * ( g - b ) / diff ) + 360;
6919 } else if ( g === max ) {
6920 h = ( 60 * ( b - r ) / diff ) + 120;
6922 h = ( 60 * ( r - g ) / diff ) + 240;
6925 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
6926 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
6929 } else if ( l <= 0.5 ) {
6932 s = diff / ( 2 - add );
6934 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
6937 spaces.hsla.from = function ( hsla ) {
6938 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
6939 return [ null, null, null, hsla[ 3 ] ];
6941 var h = hsla[ 0 ] / 360,
6945 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
6949 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
6950 Math.round( hue2rgb( p, q, h ) * 255 ),
6951 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
6957 each( spaces, function( spaceName, space ) {
6958 var props = space.props,
6959 cache = space.cache,
6963 // makes rgba() and hsla()
6964 color.fn[ spaceName ] = function( value ) {
6966 // generate a cache for this space if it doesn't exist
6967 if ( to && !this[ cache ] ) {
6968 this[ cache ] = to( this._rgba );
6970 if ( value === undefined ) {
6971 return this[ cache ].slice();
6975 type = jQuery.type( value ),
6976 arr = ( type === "array" || type === "object" ) ? value : arguments,
6977 local = this[ cache ].slice();
6979 each( props, function( key, prop ) {
6980 var val = arr[ type === "object" ? key : prop.idx ];
6981 if ( val == null ) {
6982 val = local[ prop.idx ];
6984 local[ prop.idx ] = clamp( val, prop );
6988 ret = color( from( local ) );
6989 ret[ cache ] = local;
6992 return color( local );
6996 // makes red() green() blue() alpha() hue() saturation() lightness()
6997 each( props, function( key, prop ) {
6998 // alpha is included in more than one space
6999 if ( color.fn[ key ] ) {
7002 color.fn[ key ] = function( value ) {
7003 var vtype = jQuery.type( value ),
7004 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
7005 local = this[ fn ](),
7006 cur = local[ prop.idx ],
7009 if ( vtype === "undefined" ) {
7013 if ( vtype === "function" ) {
7014 value = value.call( this, cur );
7015 vtype = jQuery.type( value );
7017 if ( value == null && prop.empty ) {
7020 if ( vtype === "string" ) {
7021 match = rplusequals.exec( value );
7023 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
7026 local[ prop.idx ] = value;
7027 return this[ fn ]( local );
7032 // add cssHook and .fx.step function for each named hook.
7033 // accept a space separated string of properties
7034 color.hook = function( hook ) {
7035 var hooks = hook.split( " " );
7036 each( hooks, function( i, hook ) {
7037 jQuery.cssHooks[ hook ] = {
7038 set: function( elem, value ) {
7039 var parsed, curElem,
7040 backgroundColor = "";
7042 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
7043 value = color( parsed || value );
7044 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
7045 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
7047 (backgroundColor === "" || backgroundColor === "transparent") &&
7048 curElem && curElem.style
7051 backgroundColor = jQuery.css( curElem, "backgroundColor" );
7052 curElem = curElem.parentNode;
7057 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
7062 value = value.toRgbaString();
7065 elem.style[ hook ] = value;
7067 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
7071 jQuery.fx.step[ hook ] = function( fx ) {
7072 if ( !fx.colorInit ) {
7073 fx.start = color( fx.elem, hook );
7074 fx.end = color( fx.end );
7075 fx.colorInit = true;
7077 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
7083 color.hook( stepHooks );
7085 jQuery.cssHooks.borderColor = {
7086 expand: function( value ) {
7089 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
7090 expanded[ "border" + part + "Color" ] = value;
7096 // Basic color names only.
7097 // Usage of any of the other color names requires adding yourself or including
7098 // jquery.color.svg-names.js.
7099 colors = jQuery.Color.names = {
7100 // 4.1. Basic color keywords
7118 // 4.2.3. "transparent" color keyword
7119 transparent: [ null, null, null, 0 ],
7127 /******************************************************************************/
7128 /****************************** CLASS ANIMATIONS ******************************/
7129 /******************************************************************************/
7132 var classAnimationActions = [ "add", "remove", "toggle" ],
7145 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
7146 $.fx.step[ prop ] = function( fx ) {
7147 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
7148 jQuery.style( fx.elem, prop, fx.end );
7154 function getElementStyles( elem ) {
7156 style = elem.ownerDocument.defaultView ?
7157 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
7161 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
7165 if ( typeof style[ key ] === "string" ) {
7166 styles[ $.camelCase( key ) ] = style[ key ];
7169 // support: Opera, IE <9
7171 for ( key in style ) {
7172 if ( typeof style[ key ] === "string" ) {
7173 styles[ key ] = style[ key ];
7182 function styleDifference( oldStyle, newStyle ) {
7186 for ( name in newStyle ) {
7187 value = newStyle[ name ];
7188 if ( oldStyle[ name ] !== value ) {
7189 if ( !shorthandStyles[ name ] ) {
7190 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
7191 diff[ name ] = value;
7200 // support: jQuery <1.8
7201 if ( !$.fn.addBack ) {
7202 $.fn.addBack = function( selector ) {
7203 return this.add( selector == null ?
7204 this.prevObject : this.prevObject.filter( selector )
7209 $.effects.animateClass = function( value, duration, easing, callback ) {
7210 var o = $.speed( duration, easing, callback );
7212 return this.queue( function() {
7213 var animated = $( this ),
7214 baseClass = animated.attr( "class" ) || "",
7216 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
7218 // map the animated objects to store the original styles.
7219 allAnimations = allAnimations.map(function() {
7223 start: getElementStyles( this )
7227 // apply class change
7228 applyClassChange = function() {
7229 $.each( classAnimationActions, function(i, action) {
7230 if ( value[ action ] ) {
7231 animated[ action + "Class" ]( value[ action ] );
7237 // map all animated objects again - calculate new styles and diff
7238 allAnimations = allAnimations.map(function() {
7239 this.end = getElementStyles( this.el[ 0 ] );
7240 this.diff = styleDifference( this.start, this.end );
7244 // apply original class
7245 animated.attr( "class", baseClass );
7247 // map all animated objects again - this time collecting a promise
7248 allAnimations = allAnimations.map(function() {
7249 var styleInfo = this,
7251 opts = $.extend({}, o, {
7253 complete: function() {
7254 dfd.resolve( styleInfo );
7258 this.el.animate( this.diff, opts );
7259 return dfd.promise();
7262 // once all animations have completed:
7263 $.when.apply( $, allAnimations.get() ).done(function() {
7265 // set the final class
7268 // for each animated element,
7269 // clear all css properties that were animated
7270 $.each( arguments, function() {
7272 $.each( this.diff, function(key) {
7277 // this is guarnteed to be there if you use jQuery.speed()
7278 // it also handles dequeuing the next anim...
7279 o.complete.call( animated[ 0 ] );
7285 addClass: (function( orig ) {
7286 return function( classNames, speed, easing, callback ) {
7288 $.effects.animateClass.call( this,
7289 { add: classNames }, speed, easing, callback ) :
7290 orig.apply( this, arguments );
7292 })( $.fn.addClass ),
7294 removeClass: (function( orig ) {
7295 return function( classNames, speed, easing, callback ) {
7296 return arguments.length > 1 ?
7297 $.effects.animateClass.call( this,
7298 { remove: classNames }, speed, easing, callback ) :
7299 orig.apply( this, arguments );
7301 })( $.fn.removeClass ),
7303 toggleClass: (function( orig ) {
7304 return function( classNames, force, speed, easing, callback ) {
7305 if ( typeof force === "boolean" || force === undefined ) {
7307 // without speed parameter
7308 return orig.apply( this, arguments );
7310 return $.effects.animateClass.call( this,
7311 (force ? { add: classNames } : { remove: classNames }),
7312 speed, easing, callback );
7315 // without force parameter
7316 return $.effects.animateClass.call( this,
7317 { toggle: classNames }, force, speed, easing );
7320 })( $.fn.toggleClass ),
7322 switchClass: function( remove, add, speed, easing, callback) {
7323 return $.effects.animateClass.call( this, {
7326 }, speed, easing, callback );
7332 /******************************************************************************/
7333 /*********************************** EFFECTS **********************************/
7334 /******************************************************************************/
7338 $.extend( $.effects, {
7341 // Saves a set of properties in a data storage
7342 save: function( element, set ) {
7343 for( var i=0; i < set.length; i++ ) {
7344 if ( set[ i ] !== null ) {
7345 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
7350 // Restores a set of previously saved properties from a data storage
7351 restore: function( element, set ) {
7353 for( i=0; i < set.length; i++ ) {
7354 if ( set[ i ] !== null ) {
7355 val = element.data( dataSpace + set[ i ] );
7356 // support: jQuery 1.6.2
7357 // http://bugs.jquery.com/ticket/9917
7358 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
7359 // We can't differentiate between "" and 0 here, so we just assume
7360 // empty string since it's likely to be a more common value...
7361 if ( val === undefined ) {
7364 element.css( set[ i ], val );
7369 setMode: function( el, mode ) {
7370 if (mode === "toggle") {
7371 mode = el.is( ":hidden" ) ? "show" : "hide";
7376 // Translates a [top,left] array into a baseline value
7377 // this should be a little more flexible in the future to handle a string & hash
7378 getBaseline: function( origin, original ) {
7380 switch ( origin[ 0 ] ) {
7381 case "top": y = 0; break;
7382 case "middle": y = 0.5; break;
7383 case "bottom": y = 1; break;
7384 default: y = origin[ 0 ] / original.height;
7386 switch ( origin[ 1 ] ) {
7387 case "left": x = 0; break;
7388 case "center": x = 0.5; break;
7389 case "right": x = 1; break;
7390 default: x = origin[ 1 ] / original.width;
7398 // Wraps the element around a wrapper that copies position properties
7399 createWrapper: function( element ) {
7401 // if the element is already wrapped, return it
7402 if ( element.parent().is( ".ui-effects-wrapper" )) {
7403 return element.parent();
7408 width: element.outerWidth(true),
7409 height: element.outerHeight(true),
7410 "float": element.css( "float" )
7412 wrapper = $( "<div></div>" )
7413 .addClass( "ui-effects-wrapper" )
7416 background: "transparent",
7421 // Store the size in case width/height are defined in % - Fixes #5245
7423 width: element.width(),
7424 height: element.height()
7426 active = document.activeElement;
7429 // Firefox incorrectly exposes anonymous content
7430 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
7434 active = document.body;
7437 element.wrap( wrapper );
7439 // Fixes #7595 - Elements lose focus when wrapped.
7440 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
7441 $( active ).focus();
7444 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
7446 // transfer positioning properties to the wrapper
7447 if ( element.css( "position" ) === "static" ) {
7448 wrapper.css({ position: "relative" });
7449 element.css({ position: "relative" });
7452 position: element.css( "position" ),
7453 zIndex: element.css( "z-index" )
7455 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
7456 props[ pos ] = element.css( pos );
7457 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
7458 props[ pos ] = "auto";
7462 position: "relative",
7471 return wrapper.css( props ).show();
7474 removeWrapper: function( element ) {
7475 var active = document.activeElement;
7477 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
7478 element.parent().replaceWith( element );
7480 // Fixes #7595 - Elements lose focus when wrapped.
7481 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
7482 $( active ).focus();
7490 setTransition: function( element, list, factor, value ) {
7491 value = value || {};
7492 $.each( list, function( i, x ) {
7493 var unit = element.cssUnit( x );
7494 if ( unit[ 0 ] > 0 ) {
7495 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
7502 // return an effect options object for the given parameters:
7503 function _normalizeArguments( effect, options, speed, callback ) {
7505 // allow passing all options as the first parameter
7506 if ( $.isPlainObject( effect ) ) {
7508 effect = effect.effect;
7511 // convert to an object
7512 effect = { effect: effect };
7514 // catch (effect, null, ...)
7515 if ( options == null ) {
7519 // catch (effect, callback)
7520 if ( $.isFunction( options ) ) {
7526 // catch (effect, speed, ?)
7527 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
7533 // catch (effect, options, callback)
7534 if ( $.isFunction( speed ) ) {
7539 // add options to effect
7541 $.extend( effect, options );
7544 speed = speed || options.duration;
7545 effect.duration = $.fx.off ? 0 :
7546 typeof speed === "number" ? speed :
7547 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
7548 $.fx.speeds._default;
7550 effect.complete = callback || options.complete;
7555 function standardAnimationOption( option ) {
7556 // Valid standard speeds (nothing, number, named speed)
7557 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
7561 // Invalid strings - treat as "normal" speed
7562 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
7566 // Complete callback
7567 if ( $.isFunction( option ) ) {
7571 // Options hash (but not naming an effect)
7572 if ( typeof option === "object" && !option.effect ) {
7576 // Didn't match any standard API
7581 effect: function( /* effect, options, speed, callback */ ) {
7582 var args = _normalizeArguments.apply( this, arguments ),
7585 effectMethod = $.effects.effect[ args.effect ];
7587 if ( $.fx.off || !effectMethod ) {
7588 // delegate to the original method (e.g., .show()) if possible
7590 return this[ mode ]( args.duration, args.complete );
7592 return this.each( function() {
7593 if ( args.complete ) {
7594 args.complete.call( this );
7600 function run( next ) {
7601 var elem = $( this ),
7602 complete = args.complete,
7606 if ( $.isFunction( complete ) ) {
7607 complete.call( elem[0] );
7609 if ( $.isFunction( next ) ) {
7614 // If the element already has the correct final state, delegate to
7615 // the core methods so the internal tracking of "olddisplay" works.
7616 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
7620 effectMethod.call( elem[0], args, done );
7624 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
7627 show: (function( orig ) {
7628 return function( option ) {
7629 if ( standardAnimationOption( option ) ) {
7630 return orig.apply( this, arguments );
7632 var args = _normalizeArguments.apply( this, arguments );
7634 return this.effect.call( this, args );
7639 hide: (function( orig ) {
7640 return function( option ) {
7641 if ( standardAnimationOption( option ) ) {
7642 return orig.apply( this, arguments );
7644 var args = _normalizeArguments.apply( this, arguments );
7646 return this.effect.call( this, args );
7651 toggle: (function( orig ) {
7652 return function( option ) {
7653 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
7654 return orig.apply( this, arguments );
7656 var args = _normalizeArguments.apply( this, arguments );
7657 args.mode = "toggle";
7658 return this.effect.call( this, args );
7664 cssUnit: function(key) {
7665 var style = this.css( key ),
7668 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
7669 if ( style.indexOf( unit ) > 0 ) {
7670 val = [ parseFloat( style ), unit ];
7679 /******************************************************************************/
7680 /*********************************** EASING ***********************************/
7681 /******************************************************************************/
7685 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
7687 var baseEasings = {};
7689 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
7690 baseEasings[ name ] = function( p ) {
7691 return Math.pow( p, i + 2 );
7695 $.extend( baseEasings, {
7696 Sine: function ( p ) {
7697 return 1 - Math.cos( p * Math.PI / 2 );
7699 Circ: function ( p ) {
7700 return 1 - Math.sqrt( 1 - p * p );
7702 Elastic: function( p ) {
7703 return p === 0 || p === 1 ? p :
7704 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
7706 Back: function( p ) {
7707 return p * p * ( 3 * p - 2 );
7709 Bounce: function ( p ) {
7713 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
7714 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
7718 $.each( baseEasings, function( name, easeIn ) {
7719 $.easing[ "easeIn" + name ] = easeIn;
7720 $.easing[ "easeOut" + name ] = function( p ) {
7721 return 1 - easeIn( 1 - p );
7723 $.easing[ "easeInOut" + name ] = function( p ) {
7725 easeIn( p * 2 ) / 2 :
7726 1 - easeIn( p * -2 + 2 ) / 2;
7733 (function( $, undefined ) {
7735 var rvertical = /up|down|vertical/,
7736 rpositivemotion = /up|left|vertical|horizontal/;
7738 $.effects.effect.blind = function( o, done ) {
7741 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
7742 mode = $.effects.setMode( el, o.mode || "hide" ),
7743 direction = o.direction || "up",
7744 vertical = rvertical.test( direction ),
7745 ref = vertical ? "height" : "width",
7746 ref2 = vertical ? "top" : "left",
7747 motion = rpositivemotion.test( direction ),
7749 show = mode === "show",
7750 wrapper, distance, margin;
7752 // if already wrapped, the wrapper's properties are my property. #6245
7753 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
7754 $.effects.save( el.parent(), props );
7756 $.effects.save( el, props );
7759 wrapper = $.effects.createWrapper( el ).css({
7763 distance = wrapper[ ref ]();
7764 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
7766 animation[ ref ] = show ? distance : 0;
7769 .css( vertical ? "bottom" : "right", 0 )
7770 .css( vertical ? "top" : "left", "auto" )
7771 .css({ position: "absolute" });
7773 animation[ ref2 ] = show ? margin : distance + margin;
7776 // start at 0 if we are showing
7778 wrapper.css( ref, 0 );
7780 wrapper.css( ref2, margin + distance );
7785 wrapper.animate( animation, {
7786 duration: o.duration,
7789 complete: function() {
7790 if ( mode === "hide" ) {
7793 $.effects.restore( el, props );
7794 $.effects.removeWrapper( el );
7802 (function( $, undefined ) {
7804 $.effects.effect.bounce = function( o, done ) {
7806 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
7809 mode = $.effects.setMode( el, o.mode || "effect" ),
7810 hide = mode === "hide",
7811 show = mode === "show",
7812 direction = o.direction || "up",
7813 distance = o.distance,
7814 times = o.times || 5,
7816 // number of internal animations
7817 anims = times * 2 + ( show || hide ? 1 : 0 ),
7818 speed = o.duration / anims,
7822 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
7823 motion = ( direction === "up" || direction === "left" ),
7828 // we will need to re-assemble the queue to stack our animations in place
7830 queuelen = queue.length;
7832 // Avoid touching opacity to prevent clearType and PNG issues in IE
7833 if ( show || hide ) {
7834 props.push( "opacity" );
7837 $.effects.save( el, props );
7839 $.effects.createWrapper( el ); // Create Wrapper
7841 // default distance for the BIGGEST bounce is the outer Distance / 3
7843 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
7847 downAnim = { opacity: 1 };
7848 downAnim[ ref ] = 0;
7850 // if we are showing, force opacity 0 and set the initial position
7851 // then do the "first" animation
7852 el.css( "opacity", 0 )
7853 .css( ref, motion ? -distance * 2 : distance * 2 )
7854 .animate( downAnim, speed, easing );
7857 // start at the smallest distance if we are hiding
7859 distance = distance / Math.pow( 2, times - 1 );
7863 downAnim[ ref ] = 0;
7864 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
7865 for ( i = 0; i < times; i++ ) {
7867 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
7869 el.animate( upAnim, speed, easing )
7870 .animate( downAnim, speed, easing );
7872 distance = hide ? distance * 2 : distance / 2;
7875 // Last Bounce when Hiding
7877 upAnim = { opacity: 0 };
7878 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
7880 el.animate( upAnim, speed, easing );
7883 el.queue(function() {
7887 $.effects.restore( el, props );
7888 $.effects.removeWrapper( el );
7892 // inject all the animations we just queued to be first in line (after "inprogress")
7893 if ( queuelen > 1) {
7894 queue.splice.apply( queue,
7895 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
7902 (function( $, undefined ) {
7904 $.effects.effect.clip = function( o, done ) {
7907 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
7908 mode = $.effects.setMode( el, o.mode || "hide" ),
7909 show = mode === "show",
7910 direction = o.direction || "vertical",
7911 vert = direction === "vertical",
7912 size = vert ? "height" : "width",
7913 position = vert ? "top" : "left",
7915 wrapper, animate, distance;
7918 $.effects.save( el, props );
7922 wrapper = $.effects.createWrapper( el ).css({
7925 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
7926 distance = animate[ size ]();
7930 animate.css( size, 0 );
7931 animate.css( position, distance / 2 );
7934 // Create Animation Object:
7935 animation[ size ] = show ? distance : 0;
7936 animation[ position ] = show ? 0 : distance / 2;
7939 animate.animate( animation, {
7941 duration: o.duration,
7943 complete: function() {
7947 $.effects.restore( el, props );
7948 $.effects.removeWrapper( el );
7956 (function( $, undefined ) {
7958 $.effects.effect.drop = function( o, done ) {
7961 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
7962 mode = $.effects.setMode( el, o.mode || "hide" ),
7963 show = mode === "show",
7964 direction = o.direction || "left",
7965 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
7966 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
7968 opacity: show ? 1 : 0
7973 $.effects.save( el, props );
7975 $.effects.createWrapper( el );
7977 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
7981 .css( "opacity", 0 )
7982 .css( ref, motion === "pos" ? -distance : distance );
7986 animation[ ref ] = ( show ?
7987 ( motion === "pos" ? "+=" : "-=" ) :
7988 ( motion === "pos" ? "-=" : "+=" ) ) +
7992 el.animate( animation, {
7994 duration: o.duration,
7996 complete: function() {
7997 if ( mode === "hide" ) {
8000 $.effects.restore( el, props );
8001 $.effects.removeWrapper( el );
8008 (function( $, undefined ) {
8010 $.effects.effect.explode = function( o, done ) {
8012 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
8015 mode = $.effects.setMode( el, o.mode || "hide" ),
8016 show = mode === "show",
8018 // show and then visibility:hidden the element before calculating offset
8019 offset = el.show().css( "visibility", "hidden" ).offset(),
8021 // width and height of a piece
8022 width = Math.ceil( el.outerWidth() / cells ),
8023 height = Math.ceil( el.outerHeight() / rows ),
8027 i, j, left, top, mx, my;
8029 // children animate complete:
8030 function childComplete() {
8031 pieces.push( this );
8032 if ( pieces.length === rows * cells ) {
8037 // clone the element for each row and cell.
8038 for( i = 0; i < rows ; i++ ) { // ===>
8039 top = offset.top + i * height;
8040 my = i - ( rows - 1 ) / 2 ;
8042 for( j = 0; j < cells ; j++ ) { // |||
8043 left = offset.left + j * width;
8044 mx = j - ( cells - 1 ) / 2 ;
8046 // Create a clone of the now hidden main element that will be absolute positioned
8047 // within a wrapper div off the -left and -top equal to size of our pieces
8051 .wrap( "<div></div>" )
8053 position: "absolute",
8054 visibility: "visible",
8059 // select the wrapper - make it overflow: hidden and absolute positioned based on
8060 // where the original was located +left and +top equal to the size of pieces
8062 .addClass( "ui-effects-explode" )
8064 position: "absolute",
8068 left: left + ( show ? mx * width : 0 ),
8069 top: top + ( show ? my * height : 0 ),
8070 opacity: show ? 0 : 1
8072 left: left + ( show ? 0 : mx * width ),
8073 top: top + ( show ? 0 : my * height ),
8074 opacity: show ? 1 : 0
8075 }, o.duration || 500, o.easing, childComplete );
8079 function animComplete() {
8081 visibility: "visible"
8083 $( pieces ).remove();
8092 (function( $, undefined ) {
8094 $.effects.effect.fade = function( o, done ) {
8096 mode = $.effects.setMode( el, o.mode || "toggle" );
8102 duration: o.duration,
8109 (function( $, undefined ) {
8111 $.effects.effect.fold = function( o, done ) {
8115 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8116 mode = $.effects.setMode( el, o.mode || "hide" ),
8117 show = mode === "show",
8118 hide = mode === "hide",
8119 size = o.size || 15,
8120 percent = /([0-9]+)%/.exec( size ),
8121 horizFirst = !!o.horizFirst,
8122 widthFirst = show !== horizFirst,
8123 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
8124 duration = o.duration / 2,
8129 $.effects.save( el, props );
8133 wrapper = $.effects.createWrapper( el ).css({
8136 distance = widthFirst ?
8137 [ wrapper.width(), wrapper.height() ] :
8138 [ wrapper.height(), wrapper.width() ];
8141 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
8144 wrapper.css( horizFirst ? {
8154 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
8155 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
8159 .animate( animation1, duration, o.easing )
8160 .animate( animation2, duration, o.easing, function() {
8164 $.effects.restore( el, props );
8165 $.effects.removeWrapper( el );
8172 (function( $, undefined ) {
8174 $.effects.effect.highlight = function( o, done ) {
8175 var elem = $( this ),
8176 props = [ "backgroundImage", "backgroundColor", "opacity" ],
8177 mode = $.effects.setMode( elem, o.mode || "show" ),
8179 backgroundColor: elem.css( "backgroundColor" )
8182 if (mode === "hide") {
8183 animation.opacity = 0;
8186 $.effects.save( elem, props );
8191 backgroundImage: "none",
8192 backgroundColor: o.color || "#ffff99"
8194 .animate( animation, {
8196 duration: o.duration,
8198 complete: function() {
8199 if ( mode === "hide" ) {
8202 $.effects.restore( elem, props );
8209 (function( $, undefined ) {
8211 $.effects.effect.pulsate = function( o, done ) {
8212 var elem = $( this ),
8213 mode = $.effects.setMode( elem, o.mode || "show" ),
8214 show = mode === "show",
8215 hide = mode === "hide",
8216 showhide = ( show || mode === "hide" ),
8218 // showing or hiding leaves of the "last" animation
8219 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
8220 duration = o.duration / anims,
8222 queue = elem.queue(),
8223 queuelen = queue.length,
8226 if ( show || !elem.is(":visible")) {
8227 elem.css( "opacity", 0 ).show();
8231 // anims - 1 opacity "toggles"
8232 for ( i = 1; i < anims; i++ ) {
8235 }, duration, o.easing );
8236 animateTo = 1 - animateTo;
8241 }, duration, o.easing);
8243 elem.queue(function() {
8250 // We just queued up "anims" animations, we need to put them next in the queue
8251 if ( queuelen > 1 ) {
8252 queue.splice.apply( queue,
8253 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8259 (function( $, undefined ) {
8261 $.effects.effect.puff = function( o, done ) {
8262 var elem = $( this ),
8263 mode = $.effects.setMode( elem, o.mode || "hide" ),
8264 hide = mode === "hide",
8265 percent = parseInt( o.percent, 10 ) || 150,
8266 factor = percent / 100,
8268 height: elem.height(),
8269 width: elem.width(),
8270 outerHeight: elem.outerHeight(),
8271 outerWidth: elem.outerWidth()
8280 percent: hide ? percent : 100,
8284 height: original.height * factor,
8285 width: original.width * factor,
8286 outerHeight: original.outerHeight * factor,
8287 outerWidth: original.outerWidth * factor
8294 $.effects.effect.scale = function( o, done ) {
8298 options = $.extend( true, {}, o ),
8299 mode = $.effects.setMode( el, o.mode || "effect" ),
8300 percent = parseInt( o.percent, 10 ) ||
8301 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
8302 direction = o.direction || "both",
8305 height: el.height(),
8307 outerHeight: el.outerHeight(),
8308 outerWidth: el.outerWidth()
8311 y: direction !== "horizontal" ? (percent / 100) : 1,
8312 x: direction !== "vertical" ? (percent / 100) : 1
8315 // We are going to pass this effect to the size effect:
8316 options.effect = "size";
8317 options.queue = false;
8318 options.complete = done;
8320 // Set default origin and restore for show/hide
8321 if ( mode !== "effect" ) {
8322 options.origin = origin || ["middle","center"];
8323 options.restore = true;
8326 options.from = o.from || ( mode === "show" ? {
8333 height: original.height * factor.y,
8334 width: original.width * factor.x,
8335 outerHeight: original.outerHeight * factor.y,
8336 outerWidth: original.outerWidth * factor.x
8339 // Fade option to support puff
8340 if ( options.fade ) {
8341 if ( mode === "show" ) {
8342 options.from.opacity = 0;
8343 options.to.opacity = 1;
8345 if ( mode === "hide" ) {
8346 options.from.opacity = 1;
8347 options.to.opacity = 0;
8352 el.effect( options );
8356 $.effects.effect.size = function( o, done ) {
8359 var original, baseline, factor,
8361 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
8364 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
8366 // Copy for children
8367 props2 = [ "width", "height", "overflow" ],
8368 cProps = [ "fontSize" ],
8369 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
8370 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
8373 mode = $.effects.setMode( el, o.mode || "effect" ),
8374 restore = o.restore || mode !== "effect",
8375 scale = o.scale || "both",
8376 origin = o.origin || [ "middle", "center" ],
8377 position = el.css( "position" ),
8378 props = restore ? props0 : props1,
8386 if ( mode === "show" ) {
8390 height: el.height(),
8392 outerHeight: el.outerHeight(),
8393 outerWidth: el.outerWidth()
8396 if ( o.mode === "toggle" && mode === "show" ) {
8397 el.from = o.to || zero;
8398 el.to = o.from || original;
8400 el.from = o.from || ( mode === "show" ? zero : original );
8401 el.to = o.to || ( mode === "hide" ? zero : original );
8404 // Set scaling factor
8407 y: el.from.height / original.height,
8408 x: el.from.width / original.width
8411 y: el.to.height / original.height,
8412 x: el.to.width / original.width
8416 // Scale the css box
8417 if ( scale === "box" || scale === "both" ) {
8419 // Vertical props scaling
8420 if ( factor.from.y !== factor.to.y ) {
8421 props = props.concat( vProps );
8422 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
8423 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
8426 // Horizontal props scaling
8427 if ( factor.from.x !== factor.to.x ) {
8428 props = props.concat( hProps );
8429 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
8430 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
8434 // Scale the content
8435 if ( scale === "content" || scale === "both" ) {
8437 // Vertical props scaling
8438 if ( factor.from.y !== factor.to.y ) {
8439 props = props.concat( cProps ).concat( props2 );
8440 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
8441 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
8445 $.effects.save( el, props );
8447 $.effects.createWrapper( el );
8448 el.css( "overflow", "hidden" ).css( el.from );
8451 if (origin) { // Calculate baseline shifts
8452 baseline = $.effects.getBaseline( origin, original );
8453 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
8454 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
8455 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
8456 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
8458 el.css( el.from ); // set top & left
8461 if ( scale === "content" || scale === "both" ) { // Scale the children
8463 // Add margins/font-size
8464 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
8465 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
8466 props2 = props0.concat(vProps).concat(hProps);
8468 el.find( "*[width]" ).each( function(){
8469 var child = $( this ),
8471 height: child.height(),
8472 width: child.width(),
8473 outerHeight: child.outerHeight(),
8474 outerWidth: child.outerWidth()
8477 $.effects.save(child, props2);
8481 height: c_original.height * factor.from.y,
8482 width: c_original.width * factor.from.x,
8483 outerHeight: c_original.outerHeight * factor.from.y,
8484 outerWidth: c_original.outerWidth * factor.from.x
8487 height: c_original.height * factor.to.y,
8488 width: c_original.width * factor.to.x,
8489 outerHeight: c_original.height * factor.to.y,
8490 outerWidth: c_original.width * factor.to.x
8493 // Vertical props scaling
8494 if ( factor.from.y !== factor.to.y ) {
8495 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
8496 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
8499 // Horizontal props scaling
8500 if ( factor.from.x !== factor.to.x ) {
8501 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
8502 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
8506 child.css( child.from );
8507 child.animate( child.to, o.duration, o.easing, function() {
8511 $.effects.restore( child, props2 );
8518 el.animate( el.to, {
8520 duration: o.duration,
8522 complete: function() {
8523 if ( el.to.opacity === 0 ) {
8524 el.css( "opacity", el.from.opacity );
8526 if( mode === "hide" ) {
8529 $.effects.restore( el, props );
8532 // we need to calculate our new positioning based on the scaling
8533 if ( position === "static" ) {
8535 position: "relative",
8540 $.each([ "top", "left" ], function( idx, pos ) {
8541 el.css( pos, function( _, str ) {
8542 var val = parseInt( str, 10 ),
8543 toRef = idx ? el.to.left : el.to.top;
8545 // if original was "auto", recalculate the new value from wrapper
8546 if ( str === "auto" ) {
8547 return toRef + "px";
8550 return val + toRef + "px";
8556 $.effects.removeWrapper( el );
8564 (function( $, undefined ) {
8566 $.effects.effect.shake = function( o, done ) {
8569 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8570 mode = $.effects.setMode( el, o.mode || "effect" ),
8571 direction = o.direction || "left",
8572 distance = o.distance || 20,
8573 times = o.times || 3,
8574 anims = times * 2 + 1,
8575 speed = Math.round(o.duration/anims),
8576 ref = (direction === "up" || direction === "down") ? "top" : "left",
8577 positiveMotion = (direction === "up" || direction === "left"),
8583 // we will need to re-assemble the queue to stack our animations in place
8585 queuelen = queue.length;
8587 $.effects.save( el, props );
8589 $.effects.createWrapper( el );
8592 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
8593 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
8594 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
8597 el.animate( animation, speed, o.easing );
8600 for ( i = 1; i < times; i++ ) {
8601 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
8604 .animate( animation1, speed, o.easing )
8605 .animate( animation, speed / 2, o.easing )
8607 if ( mode === "hide" ) {
8610 $.effects.restore( el, props );
8611 $.effects.removeWrapper( el );
8615 // inject all the animations we just queued to be first in line (after "inprogress")
8616 if ( queuelen > 1) {
8617 queue.splice.apply( queue,
8618 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8625 (function( $, undefined ) {
8627 $.effects.effect.slide = function( o, done ) {
8631 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
8632 mode = $.effects.setMode( el, o.mode || "show" ),
8633 show = mode === "show",
8634 direction = o.direction || "left",
8635 ref = (direction === "up" || direction === "down") ? "top" : "left",
8636 positiveMotion = (direction === "up" || direction === "left"),
8641 $.effects.save( el, props );
8643 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
8645 $.effects.createWrapper( el ).css({
8650 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
8654 animation[ ref ] = ( show ?
8655 ( positiveMotion ? "+=" : "-=") :
8656 ( positiveMotion ? "-=" : "+=")) +
8660 el.animate( animation, {
8662 duration: o.duration,
8664 complete: function() {
8665 if ( mode === "hide" ) {
8668 $.effects.restore( el, props );
8669 $.effects.removeWrapper( el );
8676 (function( $, undefined ) {
8678 $.effects.effect.transfer = function( o, done ) {
8679 var elem = $( this ),
8681 targetFixed = target.css( "position" ) === "fixed",
8683 fixTop = targetFixed ? body.scrollTop() : 0,
8684 fixLeft = targetFixed ? body.scrollLeft() : 0,
8685 endPosition = target.offset(),
8687 top: endPosition.top - fixTop ,
8688 left: endPosition.left - fixLeft ,
8689 height: target.innerHeight(),
8690 width: target.innerWidth()
8692 startPosition = elem.offset(),
8693 transfer = $( "<div class='ui-effects-transfer'></div>" )
8694 .appendTo( document.body )
8695 .addClass( o.className )
8697 top: startPosition.top - fixTop ,
8698 left: startPosition.left - fixLeft ,
8699 height: elem.innerHeight(),
8700 width: elem.innerWidth(),
8701 position: targetFixed ? "fixed" : "absolute"
8703 .animate( animation, o.duration, o.easing, function() {