1 /*! jQuery UI - v1.9.2 - 2012-11-23
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.progressbar.js, jquery.ui.tabs.js
4 * Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // prevent duplicate loading
12 // this is only a problem because we proxy existing functions
13 // and we don't want to double proxy them
51 focus: function( delay, fn ) {
52 return typeof delay === "number" ?
53 this.each(function() {
55 setTimeout(function() {
62 this._focus.apply( this, arguments );
65 scrollParent: function() {
67 if (($.ui.ie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
68 scrollParent = this.parents().filter(function() {
69 return (/(relative|absolute|fixed)/).test($.css(this,'position')) && (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
72 scrollParent = this.parents().filter(function() {
73 return (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
77 return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
80 zIndex: function( zIndex ) {
81 if ( zIndex !== undefined ) {
82 return this.css( "zIndex", zIndex );
86 var elem = $( this[ 0 ] ), position, value;
87 while ( elem.length && elem[ 0 ] !== document ) {
88 // Ignore z-index if position is set to a value where z-index is ignored by the browser
89 // This makes behavior of this function consistent across browsers
90 // WebKit always returns auto if the element is positioned
91 position = elem.css( "position" );
92 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
93 // IE returns 0 when zIndex is not specified
94 // other browsers return a string
95 // we ignore the case of nested elements with an explicit value of 0
96 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
97 value = parseInt( elem.css( "zIndex" ), 10 );
98 if ( !isNaN( value ) && value !== 0 ) {
102 elem = elem.parent();
109 uniqueId: function() {
110 return this.each(function() {
112 this.id = "ui-id-" + (++uuid);
117 removeUniqueId: function() {
118 return this.each(function() {
119 if ( runiqueId.test( this.id ) ) {
120 $( this ).removeAttr( "id" );
127 function focusable( element, isTabIndexNotNaN ) {
128 var map, mapName, img,
129 nodeName = element.nodeName.toLowerCase();
130 if ( "area" === nodeName ) {
131 map = element.parentNode;
133 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
136 img = $( "img[usemap=#" + mapName + "]" )[0];
137 return !!img && visible( img );
139 return ( /input|select|textarea|button|object/.test( nodeName ) ?
142 element.href || isTabIndexNotNaN :
144 // the element and all of its ancestors must be visible
148 function visible( element ) {
149 return $.expr.filters.visible( element ) &&
150 !$( element ).parents().andSelf().filter(function() {
151 return $.css( this, "visibility" ) === "hidden";
155 $.extend( $.expr[ ":" ], {
156 data: $.expr.createPseudo ?
157 $.expr.createPseudo(function( dataName ) {
158 return function( elem ) {
159 return !!$.data( elem, dataName );
162 // support: jQuery <1.8
163 function( elem, i, match ) {
164 return !!$.data( elem, match[ 3 ] );
167 focusable: function( element ) {
168 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
171 tabbable: function( element ) {
172 var tabIndex = $.attr( element, "tabindex" ),
173 isTabIndexNaN = isNaN( tabIndex );
174 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
180 var body = document.body,
181 div = body.appendChild( div = document.createElement( "div" ) );
183 // access offsetHeight before setting the style to prevent a layout bug
184 // in IE 9 which causes the element to continue to take up space even
185 // after it is removed from the DOM (#8026)
188 $.extend( div.style, {
195 $.support.minHeight = div.offsetHeight === 100;
196 $.support.selectstart = "onselectstart" in div;
198 // set display to none to avoid a layout bug in IE
199 // http://dev.jquery.com/ticket/4014
200 body.removeChild( div ).style.display = "none";
203 // support: jQuery <1.8
204 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
205 $.each( [ "Width", "Height" ], function( i, name ) {
206 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
207 type = name.toLowerCase(),
209 innerWidth: $.fn.innerWidth,
210 innerHeight: $.fn.innerHeight,
211 outerWidth: $.fn.outerWidth,
212 outerHeight: $.fn.outerHeight
215 function reduce( elem, size, border, margin ) {
216 $.each( side, function() {
217 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
219 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
222 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
228 $.fn[ "inner" + name ] = function( size ) {
229 if ( size === undefined ) {
230 return orig[ "inner" + name ].call( this );
233 return this.each(function() {
234 $( this ).css( type, reduce( this, size ) + "px" );
238 $.fn[ "outer" + name] = function( size, margin ) {
239 if ( typeof size !== "number" ) {
240 return orig[ "outer" + name ].call( this, size );
243 return this.each(function() {
244 $( this).css( type, reduce( this, size, true, margin ) + "px" );
250 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
251 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
252 $.fn.removeData = (function( removeData ) {
253 return function( key ) {
254 if ( arguments.length ) {
255 return removeData.call( this, $.camelCase( key ) );
257 return removeData.call( this );
260 })( $.fn.removeData );
270 var uaMatch = /msie ([\w.]+)/.exec( navigator.userAgent.toLowerCase() ) || [];
271 $.ui.ie = uaMatch.length ? true : false;
272 $.ui.ie6 = parseFloat( uaMatch[ 1 ], 10 ) === 6;
276 disableSelection: function() {
277 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
278 ".ui-disableSelection", function( event ) {
279 event.preventDefault();
283 enableSelection: function() {
284 return this.unbind( ".ui-disableSelection" );
289 // $.ui.plugin is deprecated. Use the proxy pattern instead.
291 add: function( module, option, set ) {
293 proto = $.ui[ module ].prototype;
295 proto.plugins[ i ] = proto.plugins[ i ] || [];
296 proto.plugins[ i ].push( [ option, set[ i ] ] );
299 call: function( instance, name, args ) {
301 set = instance.plugins[ name ];
302 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
306 for ( i = 0; i < set.length; i++ ) {
307 if ( instance.options[ set[ i ][ 0 ] ] ) {
308 set[ i ][ 1 ].apply( instance.element, args );
314 contains: $.contains,
316 // only used by resizable
317 hasScroll: function( el, a ) {
319 //If overflow is hidden, the element might have extra content, but the user wants to hide it
320 if ( $( el ).css( "overflow" ) === "hidden") {
324 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
327 if ( el[ scroll ] > 0 ) {
331 // TODO: determine which cases actually cause this to happen
332 // if the element doesn't have the scroll set, see if it's possible to
335 has = ( el[ scroll ] > 0 );
340 // these are odd functions, fix the API or move into individual plugins
341 isOverAxis: function( x, reference, size ) {
342 //Determines when x coordinate is over "b" element axis
343 return ( x > reference ) && ( x < ( reference + size ) );
345 isOver: function( y, x, top, left, height, width ) {
346 //Determines when x, y coordinates is over "b" element
347 return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
352 (function( $, undefined ) {
355 slice = Array.prototype.slice,
356 _cleanData = $.cleanData;
357 $.cleanData = function( elems ) {
358 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
360 $( elem ).triggerHandler( "remove" );
361 // http://bugs.jquery.com/ticket/8235
367 $.widget = function( name, base, prototype ) {
368 var fullName, existingConstructor, constructor, basePrototype,
369 namespace = name.split( "." )[ 0 ];
371 name = name.split( "." )[ 1 ];
372 fullName = namespace + "-" + name;
379 // create selector for plugin
380 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
381 return !!$.data( elem, fullName );
384 $[ namespace ] = $[ namespace ] || {};
385 existingConstructor = $[ namespace ][ name ];
386 constructor = $[ namespace ][ name ] = function( options, element ) {
387 // allow instantiation without "new" keyword
388 if ( !this._createWidget ) {
389 return new constructor( options, element );
392 // allow instantiation without initializing for simple inheritance
393 // must use "new" keyword (the code above always passes args)
394 if ( arguments.length ) {
395 this._createWidget( options, element );
398 // extend with the existing constructor to carry over any static properties
399 $.extend( constructor, existingConstructor, {
400 version: prototype.version,
401 // copy the object used to create the prototype in case we need to
402 // redefine the widget later
403 _proto: $.extend( {}, prototype ),
404 // track widgets that inherit from this widget in case this widget is
405 // redefined after a widget inherits from it
406 _childConstructors: []
409 basePrototype = new base();
410 // we need to make the options hash a property directly on the new instance
411 // otherwise we'll modify the options hash on the prototype that we're
413 basePrototype.options = $.widget.extend( {}, basePrototype.options );
414 $.each( prototype, function( prop, value ) {
415 if ( $.isFunction( value ) ) {
416 prototype[ prop ] = (function() {
417 var _super = function() {
418 return base.prototype[ prop ].apply( this, arguments );
420 _superApply = function( args ) {
421 return base.prototype[ prop ].apply( this, args );
424 var __super = this._super,
425 __superApply = this._superApply,
428 this._super = _super;
429 this._superApply = _superApply;
431 returnValue = value.apply( this, arguments );
433 this._super = __super;
434 this._superApply = __superApply;
441 constructor.prototype = $.widget.extend( basePrototype, {
442 // TODO: remove support for widgetEventPrefix
443 // always use the name + a colon as the prefix, e.g., draggable:start
444 // don't prefix for widgets that aren't DOM-based
445 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
447 constructor: constructor,
448 namespace: namespace,
450 // TODO remove widgetBaseClass, see #8155
451 widgetBaseClass: fullName,
452 widgetFullName: fullName
455 // If this widget is being redefined then we need to find all widgets that
456 // are inheriting from it and redefine all of them so that they inherit from
457 // the new version of this widget. We're essentially trying to replace one
458 // level in the prototype chain.
459 if ( existingConstructor ) {
460 $.each( existingConstructor._childConstructors, function( i, child ) {
461 var childPrototype = child.prototype;
463 // redefine the child widget using the same prototype that was
464 // originally used, but inherit from the new version of the base
465 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
467 // remove the list of existing child constructors from the old constructor
468 // so the old child constructors can be garbage collected
469 delete existingConstructor._childConstructors;
471 base._childConstructors.push( constructor );
474 $.widget.bridge( name, constructor );
477 $.widget.extend = function( target ) {
478 var input = slice.call( arguments, 1 ),
480 inputLength = input.length,
483 for ( ; inputIndex < inputLength; inputIndex++ ) {
484 for ( key in input[ inputIndex ] ) {
485 value = input[ inputIndex ][ key ];
486 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
488 if ( $.isPlainObject( value ) ) {
489 target[ key ] = $.isPlainObject( target[ key ] ) ?
490 $.widget.extend( {}, target[ key ], value ) :
491 // Don't extend strings, arrays, etc. with objects
492 $.widget.extend( {}, value );
493 // Copy everything else by reference
495 target[ key ] = value;
503 $.widget.bridge = function( name, object ) {
504 var fullName = object.prototype.widgetFullName || name;
505 $.fn[ name ] = function( options ) {
506 var isMethodCall = typeof options === "string",
507 args = slice.call( arguments, 1 ),
510 // allow multiple hashes to be passed on init
511 options = !isMethodCall && args.length ?
512 $.widget.extend.apply( null, [ options ].concat(args) ) :
515 if ( isMethodCall ) {
516 this.each(function() {
518 instance = $.data( this, fullName );
520 return $.error( "cannot call methods on " + name + " prior to initialization; " +
521 "attempted to call method '" + options + "'" );
523 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
524 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
526 methodValue = instance[ options ].apply( instance, args );
527 if ( methodValue !== instance && methodValue !== undefined ) {
528 returnValue = methodValue && methodValue.jquery ?
529 returnValue.pushStack( methodValue.get() ) :
535 this.each(function() {
536 var instance = $.data( this, fullName );
538 instance.option( options || {} )._init();
540 $.data( this, fullName, new object( options, this ) );
549 $.Widget = function( /* options, element */ ) {};
550 $.Widget._childConstructors = [];
552 $.Widget.prototype = {
553 widgetName: "widget",
554 widgetEventPrefix: "",
555 defaultElement: "<div>",
562 _createWidget: function( options, element ) {
563 element = $( element || this.defaultElement || this )[ 0 ];
564 this.element = $( element );
566 this.eventNamespace = "." + this.widgetName + this.uuid;
567 this.options = $.widget.extend( {},
569 this._getCreateOptions(),
573 this.hoverable = $();
574 this.focusable = $();
576 if ( element !== this ) {
578 // TODO remove dual storage
579 $.data( element, this.widgetName, this );
580 $.data( element, this.widgetFullName, this );
581 this._on( true, this.element, {
582 remove: function( event ) {
583 if ( event.target === element ) {
588 this.document = $( element.style ?
589 // element within the document
590 element.ownerDocument :
591 // element is window or document
592 element.document || element );
593 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
597 this._trigger( "create", null, this._getCreateEventData() );
600 _getCreateOptions: $.noop,
601 _getCreateEventData: $.noop,
605 destroy: function() {
607 // we can probably remove the unbind calls in 2.0
608 // all event bindings should go through this._on()
610 .unbind( this.eventNamespace )
612 // TODO remove dual storage
613 .removeData( this.widgetName )
614 .removeData( this.widgetFullName )
615 // support: jquery <1.6.3
616 // http://bugs.jquery.com/ticket/9413
617 .removeData( $.camelCase( this.widgetFullName ) );
619 .unbind( this.eventNamespace )
620 .removeAttr( "aria-disabled" )
622 this.widgetFullName + "-disabled " +
623 "ui-state-disabled" );
625 // clean up events and states
626 this.bindings.unbind( this.eventNamespace );
627 this.hoverable.removeClass( "ui-state-hover" );
628 this.focusable.removeClass( "ui-state-focus" );
636 option: function( key, value ) {
642 if ( arguments.length === 0 ) {
643 // don't return a reference to the internal hash
644 return $.widget.extend( {}, this.options );
647 if ( typeof key === "string" ) {
648 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
650 parts = key.split( "." );
652 if ( parts.length ) {
653 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
654 for ( i = 0; i < parts.length - 1; i++ ) {
655 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
656 curOption = curOption[ parts[ i ] ];
659 if ( value === undefined ) {
660 return curOption[ key ] === undefined ? null : curOption[ key ];
662 curOption[ key ] = value;
664 if ( value === undefined ) {
665 return this.options[ key ] === undefined ? null : this.options[ key ];
667 options[ key ] = value;
671 this._setOptions( options );
675 _setOptions: function( options ) {
678 for ( key in options ) {
679 this._setOption( key, options[ key ] );
684 _setOption: function( key, value ) {
685 this.options[ key ] = value;
687 if ( key === "disabled" ) {
689 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
690 .attr( "aria-disabled", value );
691 this.hoverable.removeClass( "ui-state-hover" );
692 this.focusable.removeClass( "ui-state-focus" );
699 return this._setOption( "disabled", false );
701 disable: function() {
702 return this._setOption( "disabled", true );
705 _on: function( suppressDisabledCheck, element, handlers ) {
709 // no suppressDisabledCheck flag, shuffle arguments
710 if ( typeof suppressDisabledCheck !== "boolean" ) {
712 element = suppressDisabledCheck;
713 suppressDisabledCheck = false;
716 // no element argument, shuffle and use this.element
719 element = this.element;
720 delegateElement = this.widget();
722 // accept selectors, DOM elements
723 element = delegateElement = $( element );
724 this.bindings = this.bindings.add( element );
727 $.each( handlers, function( event, handler ) {
728 function handlerProxy() {
729 // allow widgets to customize the disabled handling
730 // - disabled as an array instead of boolean
731 // - disabled class as method for disabling individual parts
732 if ( !suppressDisabledCheck &&
733 ( instance.options.disabled === true ||
734 $( this ).hasClass( "ui-state-disabled" ) ) ) {
737 return ( typeof handler === "string" ? instance[ handler ] : handler )
738 .apply( instance, arguments );
741 // copy the guid so direct unbinding works
742 if ( typeof handler !== "string" ) {
743 handlerProxy.guid = handler.guid =
744 handler.guid || handlerProxy.guid || $.guid++;
747 var match = event.match( /^(\w+)\s*(.*)$/ ),
748 eventName = match[1] + instance.eventNamespace,
751 delegateElement.delegate( selector, eventName, handlerProxy );
753 element.bind( eventName, handlerProxy );
758 _off: function( element, eventName ) {
759 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
760 element.unbind( eventName ).undelegate( eventName );
763 _delay: function( handler, delay ) {
764 function handlerProxy() {
765 return ( typeof handler === "string" ? instance[ handler ] : handler )
766 .apply( instance, arguments );
769 return setTimeout( handlerProxy, delay || 0 );
772 _hoverable: function( element ) {
773 this.hoverable = this.hoverable.add( element );
775 mouseenter: function( event ) {
776 $( event.currentTarget ).addClass( "ui-state-hover" );
778 mouseleave: function( event ) {
779 $( event.currentTarget ).removeClass( "ui-state-hover" );
784 _focusable: function( element ) {
785 this.focusable = this.focusable.add( element );
787 focusin: function( event ) {
788 $( event.currentTarget ).addClass( "ui-state-focus" );
790 focusout: function( event ) {
791 $( event.currentTarget ).removeClass( "ui-state-focus" );
796 _trigger: function( type, event, data ) {
798 callback = this.options[ type ];
801 event = $.Event( event );
802 event.type = ( type === this.widgetEventPrefix ?
804 this.widgetEventPrefix + type ).toLowerCase();
805 // the original event may come from any element
806 // so we need to reset the target on the new event
807 event.target = this.element[ 0 ];
809 // copy original event properties over to the new event
810 orig = event.originalEvent;
812 for ( prop in orig ) {
813 if ( !( prop in event ) ) {
814 event[ prop ] = orig[ prop ];
819 this.element.trigger( event, data );
820 return !( $.isFunction( callback ) &&
821 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
822 event.isDefaultPrevented() );
826 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
827 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
828 if ( typeof options === "string" ) {
829 options = { effect: options };
832 effectName = !options ?
834 options === true || typeof options === "number" ?
836 options.effect || defaultEffect;
837 options = options || {};
838 if ( typeof options === "number" ) {
839 options = { duration: options };
841 hasOptions = !$.isEmptyObject( options );
842 options.complete = callback;
843 if ( options.delay ) {
844 element.delay( options.delay );
846 if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) {
847 element[ method ]( options );
848 } else if ( effectName !== method && element[ effectName ] ) {
849 element[ effectName ]( options.duration, options.easing, callback );
851 element.queue(function( next ) {
852 $( this )[ method ]();
854 callback.call( element[ 0 ] );
863 if ( $.uiBackCompat !== false ) {
864 $.Widget.prototype._getCreateOptions = function() {
865 return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
870 (function( $, undefined ) {
872 var mouseHandled = false;
873 $( document ).mouseup( function( e ) {
874 mouseHandled = false;
877 $.widget("ui.mouse", {
880 cancel: 'input,textarea,button,select,option',
884 _mouseInit: function() {
888 .bind('mousedown.'+this.widgetName, function(event) {
889 return that._mouseDown(event);
891 .bind('click.'+this.widgetName, function(event) {
892 if (true === $.data(event.target, that.widgetName + '.preventClickEvent')) {
893 $.removeData(event.target, that.widgetName + '.preventClickEvent');
894 event.stopImmediatePropagation();
899 this.started = false;
902 // TODO: make sure destroying one instance of mouse doesn't mess with
903 // other instances of mouse
904 _mouseDestroy: function() {
905 this.element.unbind('.'+this.widgetName);
906 if ( this._mouseMoveDelegate ) {
908 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
909 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
913 _mouseDown: function(event) {
914 // don't let more than one widget handle mouseStart
915 if( mouseHandled ) { return; }
917 // we may have missed mouseup (out of window)
918 (this._mouseStarted && this._mouseUp(event));
920 this._mouseDownEvent = event;
923 btnIsLeft = (event.which === 1),
924 // event.target.nodeName works around a bug in IE 8 with
925 // disabled inputs (#7620)
926 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
927 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
931 this.mouseDelayMet = !this.options.delay;
932 if (!this.mouseDelayMet) {
933 this._mouseDelayTimer = setTimeout(function() {
934 that.mouseDelayMet = true;
935 }, this.options.delay);
938 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
939 this._mouseStarted = (this._mouseStart(event) !== false);
940 if (!this._mouseStarted) {
941 event.preventDefault();
946 // Click event may never have fired (Gecko & Opera)
947 if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
948 $.removeData(event.target, this.widgetName + '.preventClickEvent');
951 // these delegates are required to keep context
952 this._mouseMoveDelegate = function(event) {
953 return that._mouseMove(event);
955 this._mouseUpDelegate = function(event) {
956 return that._mouseUp(event);
959 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
960 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
962 event.preventDefault();
968 _mouseMove: function(event) {
969 // IE mouseup check - mouseup happened when mouse was out of window
970 if ($.ui.ie && !(document.documentMode >= 9) && !event.button) {
971 return this._mouseUp(event);
974 if (this._mouseStarted) {
975 this._mouseDrag(event);
976 return event.preventDefault();
979 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
981 (this._mouseStart(this._mouseDownEvent, event) !== false);
982 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
985 return !this._mouseStarted;
988 _mouseUp: function(event) {
990 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
991 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
993 if (this._mouseStarted) {
994 this._mouseStarted = false;
996 if (event.target === this._mouseDownEvent.target) {
997 $.data(event.target, this.widgetName + '.preventClickEvent', true);
1000 this._mouseStop(event);
1006 _mouseDistanceMet: function(event) {
1008 Math.abs(this._mouseDownEvent.pageX - event.pageX),
1009 Math.abs(this._mouseDownEvent.pageY - event.pageY)
1010 ) >= this.options.distance
1014 _mouseDelayMet: function(event) {
1015 return this.mouseDelayMet;
1018 // These are placeholder methods, to be overriden by extending plugin
1019 _mouseStart: function(event) {},
1020 _mouseDrag: function(event) {},
1021 _mouseStop: function(event) {},
1022 _mouseCapture: function(event) { return true; }
1026 (function( $, undefined ) {
1028 $.widget("ui.draggable", $.ui.mouse, {
1030 widgetEventPrefix: "drag",
1035 connectToSortable: false,
1044 refreshPositions: false,
1046 revertDuration: 500,
1049 scrollSensitivity: 20,
1057 _create: function() {
1059 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
1060 this.element[0].style.position = 'relative';
1062 (this.options.addClasses && this.element.addClass("ui-draggable"));
1063 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
1069 _destroy: function() {
1070 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1071 this._mouseDestroy();
1074 _mouseCapture: function(event) {
1076 var o = this.options;
1078 // among others, prevent a drag on a resizable-handle
1079 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
1082 //Quit if we're not on a valid handle
1083 this.handle = this._getHandle(event);
1087 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1088 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1090 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1091 position: "absolute", opacity: "0.001", zIndex: 1000
1093 .css($(this).offset())
1101 _mouseStart: function(event) {
1103 var o = this.options;
1105 //Create and append the visible helper
1106 this.helper = this._createHelper(event);
1108 this.helper.addClass("ui-draggable-dragging");
1110 //Cache the helper size
1111 this._cacheHelperProportions();
1113 //If ddmanager is used for droppables, set the global draggable
1115 $.ui.ddmanager.current = this;
1118 * - Position generation -
1119 * This block generates everything position related - it's the core of draggables.
1122 //Cache the margins of the original element
1123 this._cacheMargins();
1125 //Store the helper's css position
1126 this.cssPosition = this.helper.css("position");
1127 this.scrollParent = this.helper.scrollParent();
1129 //The element's absolute position on the page minus margins
1130 this.offset = this.positionAbs = this.element.offset();
1132 top: this.offset.top - this.margins.top,
1133 left: this.offset.left - this.margins.left
1136 $.extend(this.offset, {
1137 click: { //Where the click happened, relative to the element
1138 left: event.pageX - this.offset.left,
1139 top: event.pageY - this.offset.top
1141 parent: this._getParentOffset(),
1142 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1145 //Generate the original position
1146 this.originalPosition = this.position = this._generatePosition(event);
1147 this.originalPageX = event.pageX;
1148 this.originalPageY = event.pageY;
1150 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1151 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1153 //Set a containment if given in the options
1155 this._setContainment();
1157 //Trigger event + callbacks
1158 if(this._trigger("start", event) === false) {
1163 //Recache the helper size
1164 this._cacheHelperProportions();
1166 //Prepare the droppable offsets
1167 if ($.ui.ddmanager && !o.dropBehaviour)
1168 $.ui.ddmanager.prepareOffsets(this, event);
1171 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1173 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1174 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
1179 _mouseDrag: function(event, noPropagation) {
1181 //Compute the helpers position
1182 this.position = this._generatePosition(event);
1183 this.positionAbs = this._convertPositionTo("absolute");
1185 //Call plugins and callbacks and use the resulting position if something is returned
1186 if (!noPropagation) {
1187 var ui = this._uiHash();
1188 if(this._trigger('drag', event, ui) === false) {
1192 this.position = ui.position;
1195 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
1196 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
1197 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
1202 _mouseStop: function(event) {
1204 //If we are using droppables, inform the manager about the drop
1205 var dropped = false;
1206 if ($.ui.ddmanager && !this.options.dropBehaviour)
1207 dropped = $.ui.ddmanager.drop(this, event);
1209 //if a drop comes from outside (a sortable)
1211 dropped = this.dropped;
1212 this.dropped = false;
1215 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1216 var element = this.element[0], elementInDom = false;
1217 while ( element && (element = element.parentNode) ) {
1218 if (element == document ) {
1219 elementInDom = true;
1222 if ( !elementInDom && this.options.helper === "original" )
1225 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))) {
1227 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1228 if(that._trigger("stop", event) !== false) {
1233 if(this._trigger("stop", event) !== false) {
1241 _mouseUp: function(event) {
1242 //Remove frame helpers
1243 $("div.ui-draggable-iframeFix").each(function() {
1244 this.parentNode.removeChild(this);
1247 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1248 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
1250 return $.ui.mouse.prototype._mouseUp.call(this, event);
1253 cancel: function() {
1255 if(this.helper.is(".ui-draggable-dragging")) {
1265 _getHandle: function(event) {
1267 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1268 $(this.options.handle, this.element)
1272 if(this == event.target) handle = true;
1279 _createHelper: function(event) {
1281 var o = this.options;
1282 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
1284 if(!helper.parents('body').length)
1285 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
1287 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
1288 helper.css("position", "absolute");
1294 _adjustOffsetFromHelper: function(obj) {
1295 if (typeof obj == 'string') {
1296 obj = obj.split(' ');
1298 if ($.isArray(obj)) {
1299 obj = {left: +obj[0], top: +obj[1] || 0};
1301 if ('left' in obj) {
1302 this.offset.click.left = obj.left + this.margins.left;
1304 if ('right' in obj) {
1305 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1308 this.offset.click.top = obj.top + this.margins.top;
1310 if ('bottom' in obj) {
1311 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1315 _getParentOffset: function() {
1317 //Get the offsetParent and cache its position
1318 this.offsetParent = this.helper.offsetParent();
1319 var po = this.offsetParent.offset();
1321 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1322 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1323 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1324 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1325 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1326 po.left += this.scrollParent.scrollLeft();
1327 po.top += this.scrollParent.scrollTop();
1330 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1331 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
1332 po = { top: 0, left: 0 };
1335 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1336 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1341 _getRelativeOffset: function() {
1343 if(this.cssPosition == "relative") {
1344 var p = this.element.position();
1346 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1347 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1350 return { top: 0, left: 0 };
1355 _cacheMargins: function() {
1357 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1358 top: (parseInt(this.element.css("marginTop"),10) || 0),
1359 right: (parseInt(this.element.css("marginRight"),10) || 0),
1360 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1364 _cacheHelperProportions: function() {
1365 this.helperProportions = {
1366 width: this.helper.outerWidth(),
1367 height: this.helper.outerHeight()
1371 _setContainment: function() {
1373 var o = this.options;
1374 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
1375 if(o.containment == 'document' || o.containment == 'window') this.containment = [
1376 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1377 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1378 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
1379 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1382 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
1383 var c = $(o.containment);
1384 var ce = c[0]; if(!ce) return;
1385 var co = c.offset();
1386 var over = ($(ce).css("overflow") != 'hidden');
1388 this.containment = [
1389 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1390 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1391 (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 - this.margins.right,
1392 (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 - this.margins.bottom
1394 this.relative_container = c;
1396 } else if(o.containment.constructor == Array) {
1397 this.containment = o.containment;
1402 _convertPositionTo: function(d, pos) {
1404 if(!pos) pos = this.position;
1405 var mod = d == "absolute" ? 1 : -1;
1406 var o = this.options, 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);
1410 pos.top // The absolute mouse position
1411 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1412 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
1413 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1416 pos.left // The absolute mouse position
1417 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1418 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
1419 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1425 _generatePosition: function(event) {
1427 var o = this.options, 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);
1428 var pageX = event.pageX;
1429 var pageY = event.pageY;
1432 * - Position constraining -
1433 * Constrain the position to a mix of grid, containment.
1436 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1438 if(this.containment) {
1439 if (this.relative_container){
1440 var co = this.relative_container.offset();
1441 containment = [ this.containment[0] + co.left,
1442 this.containment[1] + co.top,
1443 this.containment[2] + co.left,
1444 this.containment[3] + co.top ];
1447 containment = this.containment;
1450 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
1451 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
1452 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
1453 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
1457 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1458 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1459 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;
1461 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1462 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;
1469 pageY // The absolute mouse position
1470 - this.offset.click.top // Click offset (relative to the element)
1471 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
1472 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
1473 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1476 pageX // The absolute mouse position
1477 - this.offset.click.left // Click offset (relative to the element)
1478 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
1479 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
1480 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1486 _clear: function() {
1487 this.helper.removeClass("ui-draggable-dragging");
1488 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
1489 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1491 this.cancelHelperRemoval = false;
1494 // From now on bulk stuff - mainly helpers
1496 _trigger: function(type, event, ui) {
1497 ui = ui || this._uiHash();
1498 $.ui.plugin.call(this, type, [event, ui]);
1499 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
1500 return $.Widget.prototype._trigger.call(this, type, event, ui);
1505 _uiHash: function(event) {
1507 helper: this.helper,
1508 position: this.position,
1509 originalPosition: this.originalPosition,
1510 offset: this.positionAbs
1516 $.ui.plugin.add("draggable", "connectToSortable", {
1517 start: function(event, ui) {
1519 var inst = $(this).data("draggable"), o = inst.options,
1520 uiSortable = $.extend({}, ui, { item: inst.element });
1521 inst.sortables = [];
1522 $(o.connectToSortable).each(function() {
1523 var sortable = $.data(this, 'sortable');
1524 if (sortable && !sortable.options.disabled) {
1525 inst.sortables.push({
1527 shouldRevert: sortable.options.revert
1529 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).
1530 sortable._trigger("activate", event, uiSortable);
1535 stop: function(event, ui) {
1537 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1538 var inst = $(this).data("draggable"),
1539 uiSortable = $.extend({}, ui, { item: inst.element });
1541 $.each(inst.sortables, function() {
1542 if(this.instance.isOver) {
1544 this.instance.isOver = 0;
1546 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1547 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1549 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
1550 if(this.shouldRevert) this.instance.options.revert = true;
1552 //Trigger the stop of the sortable
1553 this.instance._mouseStop(event);
1555 this.instance.options.helper = this.instance.options._helper;
1557 //If the helper has been the original item, restore properties in the sortable
1558 if(inst.options.helper == 'original')
1559 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
1562 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1563 this.instance._trigger("deactivate", event, uiSortable);
1569 drag: function(event, ui) {
1571 var inst = $(this).data("draggable"), that = this;
1573 var checkPos = function(o) {
1574 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
1575 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
1576 var itemHeight = o.height, itemWidth = o.width;
1577 var itemTop = o.top, itemLeft = o.left;
1579 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
1582 $.each(inst.sortables, function(i) {
1584 var innermostIntersecting = false;
1585 var thisSortable = this;
1586 //Copy over some variables to allow calling the sortable's native _intersectsWith
1587 this.instance.positionAbs = inst.positionAbs;
1588 this.instance.helperProportions = inst.helperProportions;
1589 this.instance.offset.click = inst.offset.click;
1591 if(this.instance._intersectsWith(this.instance.containerCache)) {
1592 innermostIntersecting = true;
1593 $.each(inst.sortables, function () {
1594 this.instance.positionAbs = inst.positionAbs;
1595 this.instance.helperProportions = inst.helperProportions;
1596 this.instance.offset.click = inst.offset.click;
1597 if (this != thisSortable
1598 && this.instance._intersectsWith(this.instance.containerCache)
1599 && $.ui.contains(thisSortable.instance.element[0], this.instance.element[0]))
1600 innermostIntersecting = false;
1601 return innermostIntersecting;
1606 if(innermostIntersecting) {
1607 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1608 if(!this.instance.isOver) {
1610 this.instance.isOver = 1;
1611 //Now we fake the start of dragging for the sortable instance,
1612 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1613 //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)
1614 this.instance.currentItem = $(that).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
1615 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1616 this.instance.options.helper = function() { return ui.helper[0]; };
1618 event.target = this.instance.currentItem[0];
1619 this.instance._mouseCapture(event, true);
1620 this.instance._mouseStart(event, true, true);
1622 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1623 this.instance.offset.click.top = inst.offset.click.top;
1624 this.instance.offset.click.left = inst.offset.click.left;
1625 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1626 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1628 inst._trigger("toSortable", event);
1629 inst.dropped = this.instance.element; //draggable revert needs that
1630 //hack so receive/update callbacks work (mostly)
1631 inst.currentItem = inst.element;
1632 this.instance.fromOutside = inst;
1636 //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
1637 if(this.instance.currentItem) this.instance._mouseDrag(event);
1641 //If it doesn't intersect with the sortable, and it intersected before,
1642 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1643 if(this.instance.isOver) {
1645 this.instance.isOver = 0;
1646 this.instance.cancelHelperRemoval = true;
1648 //Prevent reverting on this forced stop
1649 this.instance.options.revert = false;
1651 // The out event needs to be triggered independently
1652 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
1654 this.instance._mouseStop(event, true);
1655 this.instance.options.helper = this.instance.options._helper;
1657 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1658 this.instance.currentItem.remove();
1659 if(this.instance.placeholder) this.instance.placeholder.remove();
1661 inst._trigger("fromSortable", event);
1662 inst.dropped = false; //draggable revert needs that
1672 $.ui.plugin.add("draggable", "cursor", {
1673 start: function(event, ui) {
1674 var t = $('body'), o = $(this).data('draggable').options;
1675 if (t.css("cursor")) o._cursor = t.css("cursor");
1676 t.css("cursor", o.cursor);
1678 stop: function(event, ui) {
1679 var o = $(this).data('draggable').options;
1680 if (o._cursor) $('body').css("cursor", o._cursor);
1684 $.ui.plugin.add("draggable", "opacity", {
1685 start: function(event, ui) {
1686 var t = $(ui.helper), o = $(this).data('draggable').options;
1687 if(t.css("opacity")) o._opacity = t.css("opacity");
1688 t.css('opacity', o.opacity);
1690 stop: function(event, ui) {
1691 var o = $(this).data('draggable').options;
1692 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
1696 $.ui.plugin.add("draggable", "scroll", {
1697 start: function(event, ui) {
1698 var i = $(this).data("draggable");
1699 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
1701 drag: function(event, ui) {
1703 var i = $(this).data("draggable"), o = i.options, scrolled = false;
1705 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
1707 if(!o.axis || o.axis != 'x') {
1708 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
1709 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1710 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
1711 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1714 if(!o.axis || o.axis != 'y') {
1715 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
1716 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1717 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
1718 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1723 if(!o.axis || o.axis != 'x') {
1724 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
1725 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1726 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
1727 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1730 if(!o.axis || o.axis != 'y') {
1731 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
1732 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1733 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
1734 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1739 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
1740 $.ui.ddmanager.prepareOffsets(i, event);
1745 $.ui.plugin.add("draggable", "snap", {
1746 start: function(event, ui) {
1748 var i = $(this).data("draggable"), o = i.options;
1749 i.snapElements = [];
1751 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
1752 var $t = $(this); var $o = $t.offset();
1753 if(this != i.element[0]) i.snapElements.push({
1755 width: $t.outerWidth(), height: $t.outerHeight(),
1756 top: $o.top, left: $o.left
1761 drag: function(event, ui) {
1763 var inst = $(this).data("draggable"), o = inst.options;
1764 var d = o.snapTolerance;
1766 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1767 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1769 for (var i = inst.snapElements.length - 1; i >= 0; i--){
1771 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
1772 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
1774 //Yes, I know, this is insane ;)
1775 if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
1776 if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1777 inst.snapElements[i].snapping = false;
1781 if(o.snapMode != 'inner') {
1782 var ts = Math.abs(t - y2) <= d;
1783 var bs = Math.abs(b - y1) <= d;
1784 var ls = Math.abs(l - x2) <= d;
1785 var rs = Math.abs(r - x1) <= d;
1786 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1787 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1788 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1789 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1792 var first = (ts || bs || ls || rs);
1794 if(o.snapMode != 'outer') {
1795 var ts = Math.abs(t - y1) <= d;
1796 var bs = Math.abs(b - y2) <= d;
1797 var ls = Math.abs(l - x1) <= d;
1798 var rs = Math.abs(r - x2) <= d;
1799 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1800 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1801 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1802 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1805 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
1806 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1807 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1814 $.ui.plugin.add("draggable", "stack", {
1815 start: function(event, ui) {
1817 var o = $(this).data("draggable").options;
1819 var group = $.makeArray($(o.stack)).sort(function(a,b) {
1820 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1822 if (!group.length) { return; }
1824 var min = parseInt(group[0].style.zIndex) || 0;
1825 $(group).each(function(i) {
1826 this.style.zIndex = min + i;
1829 this[0].style.zIndex = min + group.length;
1834 $.ui.plugin.add("draggable", "zIndex", {
1835 start: function(event, ui) {
1836 var t = $(ui.helper), o = $(this).data("draggable").options;
1837 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
1838 t.css('zIndex', o.zIndex);
1840 stop: function(event, ui) {
1841 var o = $(this).data("draggable").options;
1842 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
1847 (function( $, undefined ) {
1849 $.widget("ui.droppable", {
1851 widgetEventPrefix: "drop",
1859 tolerance: 'intersect'
1861 _create: function() {
1863 var o = this.options, accept = o.accept;
1864 this.isover = 0; this.isout = 1;
1866 this.accept = $.isFunction(accept) ? accept : function(d) {
1867 return d.is(accept);
1870 //Store the droppable's proportions
1871 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1873 // Add the reference and positions to the manager
1874 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1875 $.ui.ddmanager.droppables[o.scope].push(this);
1877 (o.addClasses && this.element.addClass("ui-droppable"));
1881 _destroy: function() {
1882 var drop = $.ui.ddmanager.droppables[this.options.scope];
1883 for ( var i = 0; i < drop.length; i++ )
1884 if ( drop[i] == this )
1887 this.element.removeClass("ui-droppable ui-droppable-disabled");
1890 _setOption: function(key, value) {
1892 if(key == 'accept') {
1893 this.accept = $.isFunction(value) ? value : function(d) {
1897 $.Widget.prototype._setOption.apply(this, arguments);
1900 _activate: function(event) {
1901 var draggable = $.ui.ddmanager.current;
1902 if(this.options.activeClass) this.element.addClass(this.options.activeClass);
1903 (draggable && this._trigger('activate', event, this.ui(draggable)));
1906 _deactivate: function(event) {
1907 var draggable = $.ui.ddmanager.current;
1908 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1909 (draggable && this._trigger('deactivate', event, this.ui(draggable)));
1912 _over: function(event) {
1914 var draggable = $.ui.ddmanager.current;
1915 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1917 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1918 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
1919 this._trigger('over', event, this.ui(draggable));
1924 _out: function(event) {
1926 var draggable = $.ui.ddmanager.current;
1927 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1929 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1930 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1931 this._trigger('out', event, this.ui(draggable));
1936 _drop: function(event,custom) {
1938 var draggable = custom || $.ui.ddmanager.current;
1939 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
1941 var childrenIntersection = false;
1942 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
1943 var inst = $.data(this, 'droppable');
1946 && !inst.options.disabled
1947 && inst.options.scope == draggable.options.scope
1948 && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
1949 && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
1950 ) { childrenIntersection = true; return false; }
1952 if(childrenIntersection) return false;
1954 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1955 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1956 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1957 this._trigger('drop', event, this.ui(draggable));
1958 return this.element;
1967 draggable: (c.currentItem || c.element),
1969 position: c.position,
1970 offset: c.positionAbs
1976 $.ui.intersect = function(draggable, droppable, toleranceMode) {
1978 if (!droppable.offset) return false;
1980 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
1981 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
1982 var l = droppable.offset.left, r = l + droppable.proportions.width,
1983 t = droppable.offset.top, b = t + droppable.proportions.height;
1985 switch (toleranceMode) {
1987 return (l <= x1 && x2 <= r
1988 && t <= y1 && y2 <= b);
1991 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
1992 && x2 - (draggable.helperProportions.width / 2) < r // Left Half
1993 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
1994 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
1997 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
1998 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
1999 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
2004 (y1 >= t && y1 <= b) || // Top edge touching
2005 (y2 >= t && y2 <= b) || // Bottom edge touching
2006 (y1 < t && y2 > b) // Surrounded vertically
2008 (x1 >= l && x1 <= r) || // Left edge touching
2009 (x2 >= l && x2 <= r) || // Right edge touching
2010 (x1 < l && x2 > r) // Surrounded horizontally
2021 This manager tracks offsets of draggables and droppables
2025 droppables: { 'default': [] },
2026 prepareOffsets: function(t, event) {
2028 var m = $.ui.ddmanager.droppables[t.options.scope] || [];
2029 var type = event ? event.type : null; // workaround for #2317
2030 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
2032 droppablesLoop: for (var i = 0; i < m.length; i++) {
2034 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
2035 for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
2036 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
2038 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
2040 m[i].offset = m[i].element.offset();
2041 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2046 drop: function(draggable, event) {
2048 var dropped = false;
2049 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2051 if(!this.options) return;
2052 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
2053 dropped = this._drop.call(this, event) || dropped;
2055 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2056 this.isout = 1; this.isover = 0;
2057 this._deactivate.call(this, event);
2064 dragStart: function( draggable, event ) {
2065 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2066 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2067 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2070 drag: function(draggable, event) {
2072 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2073 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
2075 //Run through all droppables and check their positions based on specific tolerance options
2076 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2078 if(this.options.disabled || this.greedyChild || !this.visible) return;
2079 var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
2081 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
2085 if (this.options.greedy) {
2086 // find droppable parents with same scope
2087 var scope = this.options.scope;
2088 var parent = this.element.parents(':data(droppable)').filter(function () {
2089 return $.data(this, 'droppable').options.scope === scope;
2092 if (parent.length) {
2093 parentInstance = $.data(parent[0], 'droppable');
2094 parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
2098 // we just moved into a greedy child
2099 if (parentInstance && c == 'isover') {
2100 parentInstance['isover'] = 0;
2101 parentInstance['isout'] = 1;
2102 parentInstance._out.call(parentInstance, event);
2105 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
2106 this[c == "isover" ? "_over" : "_out"].call(this, event);
2108 // we just moved out of a greedy child
2109 if (parentInstance && c == 'isout') {
2110 parentInstance['isout'] = 0;
2111 parentInstance['isover'] = 1;
2112 parentInstance._over.call(parentInstance, event);
2117 dragStop: function( draggable, event ) {
2118 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2119 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2120 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2125 (function( $, undefined ) {
2127 $.widget("ui.resizable", $.ui.mouse, {
2129 widgetEventPrefix: "resize",
2133 animateDuration: "slow",
2134 animateEasing: "swing",
2148 _create: function() {
2150 var that = this, o = this.options;
2151 this.element.addClass("ui-resizable");
2154 _aspectRatio: !!(o.aspectRatio),
2155 aspectRatio: o.aspectRatio,
2156 originalElement: this.element,
2157 _proportionallyResizeElements: [],
2158 _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
2161 //Wrap the element if it cannot hold child nodes
2162 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2164 //Create a wrapper element and set the wrapper to the new current internal element
2166 $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
2167 position: this.element.css('position'),
2168 width: this.element.outerWidth(),
2169 height: this.element.outerHeight(),
2170 top: this.element.css('top'),
2171 left: this.element.css('left')
2175 //Overwrite the original this.element
2176 this.element = this.element.parent().data(
2177 "resizable", this.element.data('resizable')
2180 this.elementIsWrapper = true;
2182 //Move margins to the wrapper
2183 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2184 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2186 //Prevent Safari textarea resize
2187 this.originalResizeStyle = this.originalElement.css('resize');
2188 this.originalElement.css('resize', 'none');
2190 //Push the actual element to our proportionallyResize internal array
2191 this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
2193 // avoid IE jump (hard set the margin)
2194 this.originalElement.css({ margin: this.originalElement.css('margin') });
2196 // fix handlers offset
2197 this._proportionallyResize();
2201 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' });
2202 if(this.handles.constructor == String) {
2204 if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
2205 var n = this.handles.split(","); this.handles = {};
2207 for(var i = 0; i < n.length; i++) {
2209 var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
2210 var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
2212 // Apply zIndex to all handles - see #7960
2213 axis.css({ zIndex: o.zIndex });
2215 //TODO : What's going on here?
2216 if ('se' == handle) {
2217 axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
2220 //Insert into internal handles object and append to element
2221 this.handles[handle] = '.ui-resizable-'+handle;
2222 this.element.append(axis);
2227 this._renderAxis = function(target) {
2229 target = target || this.element;
2231 for(var i in this.handles) {
2233 if(this.handles[i].constructor == String)
2234 this.handles[i] = $(this.handles[i], this.element).show();
2236 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2237 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2239 var axis = $(this.handles[i], this.element), padWrapper = 0;
2241 //Checking the correct pad and border
2242 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2244 //The padding type i have to apply...
2245 var padPos = [ 'padding',
2246 /ne|nw|n/.test(i) ? 'Top' :
2247 /se|sw|s/.test(i) ? 'Bottom' :
2248 /^e$/.test(i) ? 'Right' : 'Left' ].join("");
2250 target.css(padPos, padWrapper);
2252 this._proportionallyResize();
2256 //TODO: What's that good for? There's not anything to be executed left
2257 if(!$(this.handles[i]).length)
2263 //TODO: make renderAxis a prototype function
2264 this._renderAxis(this.element);
2266 this._handles = $('.ui-resizable-handle', this.element)
2267 .disableSelection();
2269 //Matching axis name
2270 this._handles.mouseover(function() {
2271 if (!that.resizing) {
2273 var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2274 //Axis, default = se
2275 that.axis = axis && axis[1] ? axis[1] : 'se';
2279 //If we want to auto hide the elements
2281 this._handles.hide();
2283 .addClass("ui-resizable-autohide")
2284 .mouseenter(function() {
2285 if (o.disabled) return;
2286 $(this).removeClass("ui-resizable-autohide");
2287 that._handles.show();
2289 .mouseleave(function(){
2290 if (o.disabled) return;
2291 if (!that.resizing) {
2292 $(this).addClass("ui-resizable-autohide");
2293 that._handles.hide();
2298 //Initialize the mouse interaction
2303 _destroy: function() {
2305 this._mouseDestroy();
2307 var _destroy = function(exp) {
2308 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2309 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
2312 //TODO: Unwrap at same DOM position
2313 if (this.elementIsWrapper) {
2314 _destroy(this.element);
2315 var wrapper = this.element;
2316 this.originalElement.css({
2317 position: wrapper.css('position'),
2318 width: wrapper.outerWidth(),
2319 height: wrapper.outerHeight(),
2320 top: wrapper.css('top'),
2321 left: wrapper.css('left')
2322 }).insertAfter( wrapper );
2326 this.originalElement.css('resize', this.originalResizeStyle);
2327 _destroy(this.originalElement);
2332 _mouseCapture: function(event) {
2334 for (var i in this.handles) {
2335 if ($(this.handles[i])[0] == event.target) {
2340 return !this.options.disabled && handle;
2343 _mouseStart: function(event) {
2345 var o = this.options, iniPos = this.element.position(), el = this.element;
2347 this.resizing = true;
2348 this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
2350 // bugfix for http://dev.jquery.com/ticket/1749
2351 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
2352 el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
2355 this._renderProxy();
2357 var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
2359 if (o.containment) {
2360 curleft += $(o.containment).scrollLeft() || 0;
2361 curtop += $(o.containment).scrollTop() || 0;
2364 //Store needed variables
2365 this.offset = this.helper.offset();
2366 this.position = { left: curleft, top: curtop };
2367 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2368 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2369 this.originalPosition = { left: curleft, top: curtop };
2370 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2371 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2374 this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2376 var cursor = $('.ui-resizable-' + this.axis).css('cursor');
2377 $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
2379 el.addClass("ui-resizable-resizing");
2380 this._propagate("start", event);
2384 _mouseDrag: function(event) {
2386 //Increase performance, avoid regex
2387 var el = this.helper, o = this.options, props = {},
2388 that = this, smp = this.originalMousePosition, a = this.axis;
2390 var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
2391 var trigger = this._change[a];
2392 if (!trigger) return false;
2394 // Calculate the attrs that will be change
2395 var data = trigger.apply(this, [event, dx, dy]);
2397 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2398 this._updateVirtualBoundaries(event.shiftKey);
2399 if (this._aspectRatio || event.shiftKey)
2400 data = this._updateRatio(data, event);
2402 data = this._respectSize(data, event);
2404 // plugins callbacks need to be called first
2405 this._propagate("resize", event);
2408 top: this.position.top + "px", left: this.position.left + "px",
2409 width: this.size.width + "px", height: this.size.height + "px"
2412 if (!this._helper && this._proportionallyResizeElements.length)
2413 this._proportionallyResize();
2415 this._updateCache(data);
2417 // calling the user callback at the end
2418 this._trigger('resize', event, this.ui());
2423 _mouseStop: function(event) {
2425 this.resizing = false;
2426 var o = this.options, that = this;
2429 var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2430 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2431 soffsetw = ista ? 0 : that.sizeDiff.width;
2433 var s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) },
2434 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
2435 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
2438 this.element.css($.extend(s, { top: top, left: left }));
2440 that.helper.height(that.size.height);
2441 that.helper.width(that.size.width);
2443 if (this._helper && !o.animate) this._proportionallyResize();
2446 $('body').css('cursor', 'auto');
2448 this.element.removeClass("ui-resizable-resizing");
2450 this._propagate("stop", event);
2452 if (this._helper) this.helper.remove();
2457 _updateVirtualBoundaries: function(forceAspectRatio) {
2458 var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
2461 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2462 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2463 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2464 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2467 if(this._aspectRatio || forceAspectRatio) {
2468 // We want to create an enclosing box whose aspect ration is the requested one
2469 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2470 pMinWidth = b.minHeight * this.aspectRatio;
2471 pMinHeight = b.minWidth / this.aspectRatio;
2472 pMaxWidth = b.maxHeight * this.aspectRatio;
2473 pMaxHeight = b.maxWidth / this.aspectRatio;
2475 if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
2476 if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
2477 if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
2478 if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
2480 this._vBoundaries = b;
2483 _updateCache: function(data) {
2484 var o = this.options;
2485 this.offset = this.helper.offset();
2486 if (isNumber(data.left)) this.position.left = data.left;
2487 if (isNumber(data.top)) this.position.top = data.top;
2488 if (isNumber(data.height)) this.size.height = data.height;
2489 if (isNumber(data.width)) this.size.width = data.width;
2492 _updateRatio: function(data, event) {
2494 var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
2496 if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
2497 else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
2500 data.left = cpos.left + (csize.width - data.width);
2504 data.top = cpos.top + (csize.height - data.height);
2505 data.left = cpos.left + (csize.width - data.width);
2511 _respectSize: function(data, event) {
2513 var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
2514 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2515 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
2517 if (isminw) data.width = o.minWidth;
2518 if (isminh) data.height = o.minHeight;
2519 if (ismaxw) data.width = o.maxWidth;
2520 if (ismaxh) data.height = o.maxHeight;
2522 var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
2523 var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2525 if (isminw && cw) data.left = dw - o.minWidth;
2526 if (ismaxw && cw) data.left = dw - o.maxWidth;
2527 if (isminh && ch) data.top = dh - o.minHeight;
2528 if (ismaxh && ch) data.top = dh - o.maxHeight;
2530 // fixing jump error on top/left - bug #2330
2531 var isNotwh = !data.width && !data.height;
2532 if (isNotwh && !data.left && data.top) data.top = null;
2533 else if (isNotwh && !data.top && data.left) data.left = null;
2538 _proportionallyResize: function() {
2540 var o = this.options;
2541 if (!this._proportionallyResizeElements.length) return;
2542 var element = this.helper || this.element;
2544 for (var i=0; i < this._proportionallyResizeElements.length; i++) {
2546 var prel = this._proportionallyResizeElements[i];
2548 if (!this.borderDif) {
2549 var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
2550 p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
2552 this.borderDif = $.map(b, function(v, i) {
2553 var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
2554 return border + padding;
2559 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2560 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2567 _renderProxy: function() {
2569 var el = this.element, o = this.options;
2570 this.elementOffset = el.offset();
2574 this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
2576 // fix ie6 offset TODO: This seems broken
2577 var ie6offset = ($.ui.ie6 ? 1 : 0),
2578 pxyoffset = ( $.ui.ie6 ? 2 : -1 );
2580 this.helper.addClass(this._helper).css({
2581 width: this.element.outerWidth() + pxyoffset,
2582 height: this.element.outerHeight() + pxyoffset,
2583 position: 'absolute',
2584 left: this.elementOffset.left - ie6offset +'px',
2585 top: this.elementOffset.top - ie6offset +'px',
2586 zIndex: ++o.zIndex //TODO: Don't modify option
2591 .disableSelection();
2594 this.helper = this.element;
2600 e: function(event, dx, dy) {
2601 return { width: this.originalSize.width + dx };
2603 w: function(event, dx, dy) {
2604 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2605 return { left: sp.left + dx, width: cs.width - dx };
2607 n: function(event, dx, dy) {
2608 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
2609 return { top: sp.top + dy, height: cs.height - dy };
2611 s: function(event, dx, dy) {
2612 return { height: this.originalSize.height + dy };
2614 se: function(event, dx, dy) {
2615 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2617 sw: function(event, dx, dy) {
2618 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2620 ne: function(event, dx, dy) {
2621 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2623 nw: function(event, dx, dy) {
2624 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2628 _propagate: function(n, event) {
2629 $.ui.plugin.call(this, n, [event, this.ui()]);
2630 (n != "resize" && this._trigger(n, event, this.ui()));
2637 originalElement: this.originalElement,
2638 element: this.element,
2639 helper: this.helper,
2640 position: this.position,
2642 originalSize: this.originalSize,
2643 originalPosition: this.originalPosition
2650 * Resizable Extensions
2653 $.ui.plugin.add("resizable", "alsoResize", {
2655 start: function (event, ui) {
2656 var that = $(this).data("resizable"), o = that.options;
2658 var _store = function (exp) {
2659 $(exp).each(function() {
2661 el.data("resizable-alsoresize", {
2662 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
2663 left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10)
2668 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
2669 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
2670 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
2672 _store(o.alsoResize);
2676 resize: function (event, ui) {
2677 var that = $(this).data("resizable"), o = that.options, os = that.originalSize, op = that.originalPosition;
2680 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
2681 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
2684 _alsoResize = function (exp, c) {
2685 $(exp).each(function() {
2686 var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
2687 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
2689 $.each(css, function (i, prop) {
2690 var sum = (start[prop]||0) + (delta[prop]||0);
2691 if (sum && sum >= 0)
2692 style[prop] = sum || null;
2699 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
2700 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
2702 _alsoResize(o.alsoResize);
2706 stop: function (event, ui) {
2707 $(this).removeData("resizable-alsoresize");
2711 $.ui.plugin.add("resizable", "animate", {
2713 stop: function(event, ui) {
2714 var that = $(this).data("resizable"), o = that.options;
2716 var pr = that._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2717 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2718 soffsetw = ista ? 0 : that.sizeDiff.width;
2720 var style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
2721 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
2722 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
2724 that.element.animate(
2725 $.extend(style, top && left ? { top: top, left: left } : {}), {
2726 duration: o.animateDuration,
2727 easing: o.animateEasing,
2731 width: parseInt(that.element.css('width'), 10),
2732 height: parseInt(that.element.css('height'), 10),
2733 top: parseInt(that.element.css('top'), 10),
2734 left: parseInt(that.element.css('left'), 10)
2737 if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
2739 // propagating resize, and updating values for each animation step
2740 that._updateCache(data);
2741 that._propagate("resize", event);
2750 $.ui.plugin.add("resizable", "containment", {
2752 start: function(event, ui) {
2753 var that = $(this).data("resizable"), o = that.options, el = that.element;
2754 var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2757 that.containerElement = $(ce);
2759 if (/document/.test(oc) || oc == document) {
2760 that.containerOffset = { left: 0, top: 0 };
2761 that.containerPosition = { left: 0, top: 0 };
2764 element: $(document), left: 0, top: 0,
2765 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2769 // i'm a node, so compute top, left, right, bottom
2771 var element = $(ce), p = [];
2772 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2774 that.containerOffset = element.offset();
2775 that.containerPosition = element.position();
2776 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2778 var co = that.containerOffset, ch = that.containerSize.height, cw = that.containerSize.width,
2779 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
2782 element: ce, left: co.left, top: co.top, width: width, height: height
2787 resize: function(event, ui) {
2788 var that = $(this).data("resizable"), o = that.options,
2789 ps = that.containerSize, co = that.containerOffset, cs = that.size, cp = that.position,
2790 pRatio = that._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = that.containerElement;
2792 if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
2794 if (cp.left < (that._helper ? co.left : 0)) {
2795 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
2796 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
2797 that.position.left = o.helper ? co.left : 0;
2800 if (cp.top < (that._helper ? co.top : 0)) {
2801 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
2802 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
2803 that.position.top = that._helper ? co.top : 0;
2806 that.offset.left = that.parentData.left+that.position.left;
2807 that.offset.top = that.parentData.top+that.position.top;
2809 var woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ),
2810 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
2812 var isParent = that.containerElement.get(0) == that.element.parent().get(0),
2813 isOffsetRelative = /relative|absolute/.test(that.containerElement.css('position'));
2815 if(isParent && isOffsetRelative) woset -= that.parentData.left;
2817 if (woset + that.size.width >= that.parentData.width) {
2818 that.size.width = that.parentData.width - woset;
2819 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
2822 if (hoset + that.size.height >= that.parentData.height) {
2823 that.size.height = that.parentData.height - hoset;
2824 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
2828 stop: function(event, ui){
2829 var that = $(this).data("resizable"), o = that.options, cp = that.position,
2830 co = that.containerOffset, cop = that.containerPosition, ce = that.containerElement;
2832 var helper = $(that.helper), ho = helper.offset(), w = helper.outerWidth() - that.sizeDiff.width, h = helper.outerHeight() - that.sizeDiff.height;
2834 if (that._helper && !o.animate && (/relative/).test(ce.css('position')))
2835 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2837 if (that._helper && !o.animate && (/static/).test(ce.css('position')))
2838 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
2843 $.ui.plugin.add("resizable", "ghost", {
2845 start: function(event, ui) {
2847 var that = $(this).data("resizable"), o = that.options, cs = that.size;
2849 that.ghost = that.originalElement.clone();
2851 .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
2852 .addClass('ui-resizable-ghost')
2853 .addClass(typeof o.ghost == 'string' ? o.ghost : '');
2855 that.ghost.appendTo(that.helper);
2859 resize: function(event, ui){
2860 var that = $(this).data("resizable"), o = that.options;
2861 if (that.ghost) that.ghost.css({ position: 'relative', height: that.size.height, width: that.size.width });
2864 stop: function(event, ui){
2865 var that = $(this).data("resizable"), o = that.options;
2866 if (that.ghost && that.helper) that.helper.get(0).removeChild(that.ghost.get(0));
2871 $.ui.plugin.add("resizable", "grid", {
2873 resize: function(event, ui) {
2874 var that = $(this).data("resizable"), o = that.options, cs = that.size, os = that.originalSize, op = that.originalPosition, a = that.axis, ratio = o._aspectRatio || event.shiftKey;
2875 o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
2876 var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
2878 if (/^(se|s|e)$/.test(a)) {
2879 that.size.width = os.width + ox;
2880 that.size.height = os.height + oy;
2882 else if (/^(ne)$/.test(a)) {
2883 that.size.width = os.width + ox;
2884 that.size.height = os.height + oy;
2885 that.position.top = op.top - oy;
2887 else if (/^(sw)$/.test(a)) {
2888 that.size.width = os.width + ox;
2889 that.size.height = os.height + oy;
2890 that.position.left = op.left - ox;
2893 that.size.width = os.width + ox;
2894 that.size.height = os.height + oy;
2895 that.position.top = op.top - oy;
2896 that.position.left = op.left - ox;
2902 var num = function(v) {
2903 return parseInt(v, 10) || 0;
2906 var isNumber = function(value) {
2907 return !isNaN(parseInt(value, 10));
2911 (function( $, undefined ) {
2913 $.widget("ui.selectable", $.ui.mouse, {
2922 _create: function() {
2925 this.element.addClass("ui-selectable");
2927 this.dragged = false;
2929 // cache selectee children based on filter
2931 this.refresh = function() {
2932 selectees = $(that.options.filter, that.element[0]);
2933 selectees.addClass("ui-selectee");
2934 selectees.each(function() {
2935 var $this = $(this);
2936 var pos = $this.offset();
2937 $.data(this, "selectable-item", {
2942 right: pos.left + $this.outerWidth(),
2943 bottom: pos.top + $this.outerHeight(),
2944 startselected: false,
2945 selected: $this.hasClass('ui-selected'),
2946 selecting: $this.hasClass('ui-selecting'),
2947 unselecting: $this.hasClass('ui-unselecting')
2953 this.selectees = selectees.addClass("ui-selectee");
2957 this.helper = $("<div class='ui-selectable-helper'></div>");
2960 _destroy: function() {
2962 .removeClass("ui-selectee")
2963 .removeData("selectable-item");
2965 .removeClass("ui-selectable ui-selectable-disabled");
2966 this._mouseDestroy();
2969 _mouseStart: function(event) {
2972 this.opos = [event.pageX, event.pageY];
2974 if (this.options.disabled)
2977 var options = this.options;
2979 this.selectees = $(options.filter, this.element[0]);
2981 this._trigger("start", event);
2983 $(options.appendTo).append(this.helper);
2984 // position helper (lasso)
2986 "left": event.clientX,
2987 "top": event.clientY,
2992 if (options.autoRefresh) {
2996 this.selectees.filter('.ui-selected').each(function() {
2997 var selectee = $.data(this, "selectable-item");
2998 selectee.startselected = true;
2999 if (!event.metaKey && !event.ctrlKey) {
3000 selectee.$element.removeClass('ui-selected');
3001 selectee.selected = false;
3002 selectee.$element.addClass('ui-unselecting');
3003 selectee.unselecting = true;
3004 // selectable UNSELECTING callback
3005 that._trigger("unselecting", event, {
3006 unselecting: selectee.element
3011 $(event.target).parents().andSelf().each(function() {
3012 var selectee = $.data(this, "selectable-item");
3014 var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
3016 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3017 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3018 selectee.unselecting = !doSelect;
3019 selectee.selecting = doSelect;
3020 selectee.selected = doSelect;
3021 // selectable (UN)SELECTING callback
3023 that._trigger("selecting", event, {
3024 selecting: selectee.element
3027 that._trigger("unselecting", event, {
3028 unselecting: selectee.element
3037 _mouseDrag: function(event) {
3039 this.dragged = true;
3041 if (this.options.disabled)
3044 var options = this.options;
3046 var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
3047 if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
3048 if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
3049 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3051 this.selectees.each(function() {
3052 var selectee = $.data(this, "selectable-item");
3053 //prevent helper from being selected if appendTo: selectable
3054 if (!selectee || selectee.element == that.element[0])
3057 if (options.tolerance == 'touch') {
3058 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3059 } else if (options.tolerance == 'fit') {
3060 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3065 if (selectee.selected) {
3066 selectee.$element.removeClass('ui-selected');
3067 selectee.selected = false;
3069 if (selectee.unselecting) {
3070 selectee.$element.removeClass('ui-unselecting');
3071 selectee.unselecting = false;
3073 if (!selectee.selecting) {
3074 selectee.$element.addClass('ui-selecting');
3075 selectee.selecting = true;
3076 // selectable SELECTING callback
3077 that._trigger("selecting", event, {
3078 selecting: selectee.element
3083 if (selectee.selecting) {
3084 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3085 selectee.$element.removeClass('ui-selecting');
3086 selectee.selecting = false;
3087 selectee.$element.addClass('ui-selected');
3088 selectee.selected = true;
3090 selectee.$element.removeClass('ui-selecting');
3091 selectee.selecting = false;
3092 if (selectee.startselected) {
3093 selectee.$element.addClass('ui-unselecting');
3094 selectee.unselecting = true;
3096 // selectable UNSELECTING callback
3097 that._trigger("unselecting", event, {
3098 unselecting: selectee.element
3102 if (selectee.selected) {
3103 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3104 selectee.$element.removeClass('ui-selected');
3105 selectee.selected = false;
3107 selectee.$element.addClass('ui-unselecting');
3108 selectee.unselecting = true;
3109 // selectable UNSELECTING callback
3110 that._trigger("unselecting", event, {
3111 unselecting: selectee.element
3121 _mouseStop: function(event) {
3124 this.dragged = false;
3126 var options = this.options;
3128 $('.ui-unselecting', this.element[0]).each(function() {
3129 var selectee = $.data(this, "selectable-item");
3130 selectee.$element.removeClass('ui-unselecting');
3131 selectee.unselecting = false;
3132 selectee.startselected = false;
3133 that._trigger("unselected", event, {
3134 unselected: selectee.element
3137 $('.ui-selecting', this.element[0]).each(function() {
3138 var selectee = $.data(this, "selectable-item");
3139 selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
3140 selectee.selecting = false;
3141 selectee.selected = true;
3142 selectee.startselected = true;
3143 that._trigger("selected", event, {
3144 selected: selectee.element
3147 this._trigger("stop", event);
3149 this.helper.remove();
3157 (function( $, undefined ) {
3159 $.widget("ui.sortable", $.ui.mouse, {
3161 widgetEventPrefix: "sort",
3171 forcePlaceholderSize: false,
3172 forceHelperSize: false,
3181 scrollSensitivity: 20,
3184 tolerance: "intersect",
3187 _create: function() {
3189 var o = this.options;
3190 this.containerCache = {};
3191 this.element.addClass("ui-sortable");
3196 //Let's determine if the items are being displayed horizontally
3197 this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
3199 //Let's determine the parent's offset
3200 this.offset = this.element.offset();
3202 //Initialize mouse events for interaction
3210 _destroy: function() {
3212 .removeClass("ui-sortable ui-sortable-disabled");
3213 this._mouseDestroy();
3215 for ( var i = this.items.length - 1; i >= 0; i-- )
3216 this.items[i].item.removeData(this.widgetName + "-item");
3221 _setOption: function(key, value){
3222 if ( key === "disabled" ) {
3223 this.options[ key ] = value;
3225 this.widget().toggleClass( "ui-sortable-disabled", !!value );
3227 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3228 $.Widget.prototype._setOption.apply(this, arguments);
3232 _mouseCapture: function(event, overrideHandle) {
3235 if (this.reverting) {
3239 if(this.options.disabled || this.options.type == 'static') return false;
3241 //We have to refresh the items data once first
3242 this._refreshItems(event);
3244 //Find out if the clicked node (or one of its parents) is a actual item in this.items
3245 var currentItem = null, nodes = $(event.target).parents().each(function() {
3246 if($.data(this, that.widgetName + '-item') == that) {
3247 currentItem = $(this);
3251 if($.data(event.target, that.widgetName + '-item') == that) currentItem = $(event.target);
3253 if(!currentItem) return false;
3254 if(this.options.handle && !overrideHandle) {
3255 var validHandle = false;
3257 $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
3258 if(!validHandle) return false;
3261 this.currentItem = currentItem;
3262 this._removeCurrentsFromItems();
3267 _mouseStart: function(event, overrideHandle, noActivation) {
3269 var o = this.options;
3270 this.currentContainer = this;
3272 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3273 this.refreshPositions();
3275 //Create and append the visible helper
3276 this.helper = this._createHelper(event);
3278 //Cache the helper size
3279 this._cacheHelperProportions();
3282 * - Position generation -
3283 * This block generates everything position related - it's the core of draggables.
3286 //Cache the margins of the original element
3287 this._cacheMargins();
3289 //Get the next scrolling parent
3290 this.scrollParent = this.helper.scrollParent();
3292 //The element's absolute position on the page minus margins
3293 this.offset = this.currentItem.offset();
3295 top: this.offset.top - this.margins.top,
3296 left: this.offset.left - this.margins.left
3299 $.extend(this.offset, {
3300 click: { //Where the click happened, relative to the element
3301 left: event.pageX - this.offset.left,
3302 top: event.pageY - this.offset.top
3304 parent: this._getParentOffset(),
3305 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3308 // Only after we got the offset, we can change the helper's position to absolute
3309 // TODO: Still need to figure out a way to make relative sorting possible
3310 this.helper.css("position", "absolute");
3311 this.cssPosition = this.helper.css("position");
3313 //Generate the original position
3314 this.originalPosition = this._generatePosition(event);
3315 this.originalPageX = event.pageX;
3316 this.originalPageY = event.pageY;
3318 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
3319 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3321 //Cache the former DOM position
3322 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3324 //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
3325 if(this.helper[0] != this.currentItem[0]) {
3326 this.currentItem.hide();
3329 //Create the placeholder
3330 this._createPlaceholder();
3332 //Set a containment if given in the options
3334 this._setContainment();
3336 if(o.cursor) { // cursor option
3337 if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
3338 $('body').css("cursor", o.cursor);
3341 if(o.opacity) { // opacity option
3342 if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
3343 this.helper.css("opacity", o.opacity);
3346 if(o.zIndex) { // zIndex option
3347 if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
3348 this.helper.css("zIndex", o.zIndex);
3352 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
3353 this.overflowOffset = this.scrollParent.offset();
3356 this._trigger("start", event, this._uiHash());
3358 //Recache the helper size
3359 if(!this._preserveHelperProportions)
3360 this._cacheHelperProportions();
3363 //Post 'activate' events to possible containers
3365 for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, this._uiHash(this)); }
3368 //Prepare possible droppables
3370 $.ui.ddmanager.current = this;
3372 if ($.ui.ddmanager && !o.dropBehaviour)
3373 $.ui.ddmanager.prepareOffsets(this, event);
3375 this.dragging = true;
3377 this.helper.addClass("ui-sortable-helper");
3378 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3383 _mouseDrag: function(event) {
3385 //Compute the helpers position
3386 this.position = this._generatePosition(event);
3387 this.positionAbs = this._convertPositionTo("absolute");
3389 if (!this.lastPositionAbs) {
3390 this.lastPositionAbs = this.positionAbs;
3394 if(this.options.scroll) {
3395 var o = this.options, scrolled = false;
3396 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
3398 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
3399 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3400 else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
3401 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3403 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
3404 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3405 else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
3406 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3410 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
3411 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3412 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
3413 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3415 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
3416 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3417 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
3418 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3422 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
3423 $.ui.ddmanager.prepareOffsets(this, event);
3426 //Regenerate the absolute position used for position checks
3427 this.positionAbs = this._convertPositionTo("absolute");
3429 //Set the helper position
3430 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
3431 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
3434 for (var i = this.items.length - 1; i >= 0; i--) {
3436 //Cache variables and intersection, continue if no intersection
3437 var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
3438 if (!intersection) continue;
3440 // Only put the placeholder inside the current Container, skip all
3441 // items form other containers. This works because when moving
3442 // an item from one container to another the
3443 // currentContainer is switched before the placeholder is moved.
3445 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3446 // beetween the outer and inner container.
3447 if (item.instance !== this.currentContainer) continue;
3449 if (itemElement != this.currentItem[0] //cannot intersect with itself
3450 && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
3451 && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
3452 && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
3453 //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
3456 this.direction = intersection == 1 ? "down" : "up";
3458 if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
3459 this._rearrange(event, item);
3464 this._trigger("change", event, this._uiHash());
3469 //Post events to containers
3470 this._contactContainers(event);
3472 //Interconnect with droppables
3473 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
3476 this._trigger('sort', event, this._uiHash());
3478 this.lastPositionAbs = this.positionAbs;
3483 _mouseStop: function(event, noPropagation) {
3487 //If we are using droppables, inform the manager about the drop
3488 if ($.ui.ddmanager && !this.options.dropBehaviour)
3489 $.ui.ddmanager.drop(this, event);
3491 if(this.options.revert) {
3493 var cur = this.placeholder.offset();
3495 this.reverting = true;
3497 $(this.helper).animate({
3498 left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
3499 top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
3500 }, parseInt(this.options.revert, 10) || 500, function() {
3504 this._clear(event, noPropagation);
3511 cancel: function() {
3515 this._mouseUp({ target: null });
3517 if(this.options.helper == "original")
3518 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3520 this.currentItem.show();
3522 //Post deactivating events to containers
3523 for (var i = this.containers.length - 1; i >= 0; i--){
3524 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3525 if(this.containers[i].containerCache.over) {
3526 this.containers[i]._trigger("out", null, this._uiHash(this));
3527 this.containers[i].containerCache.over = 0;
3533 if (this.placeholder) {
3534 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3535 if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3536 if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
3545 if(this.domPosition.prev) {
3546 $(this.domPosition.prev).after(this.currentItem);
3548 $(this.domPosition.parent).prepend(this.currentItem);
3556 serialize: function(o) {
3558 var items = this._getItemsAsjQuery(o && o.connected);
3559 var str = []; o = o || {};
3561 $(items).each(function() {
3562 var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
3563 if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
3566 if(!str.length && o.key) {
3567 str.push(o.key + '=');
3570 return str.join('&');
3574 toArray: function(o) {
3576 var items = this._getItemsAsjQuery(o && o.connected);
3577 var ret = []; o = o || {};
3579 items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
3584 /* Be careful with the following core functions */
3585 _intersectsWith: function(item) {
3587 var x1 = this.positionAbs.left,
3588 x2 = x1 + this.helperProportions.width,
3589 y1 = this.positionAbs.top,
3590 y2 = y1 + this.helperProportions.height;
3595 b = t + item.height;
3597 var dyClick = this.offset.click.top,
3598 dxClick = this.offset.click.left;
3600 var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
3602 if( this.options.tolerance == "pointer"
3603 || this.options.forcePointerForContainers
3604 || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
3606 return isOverElement;
3609 return (l < x1 + (this.helperProportions.width / 2) // Right Half
3610 && x2 - (this.helperProportions.width / 2) < r // Left Half
3611 && t < y1 + (this.helperProportions.height / 2) // Bottom Half
3612 && y2 - (this.helperProportions.height / 2) < b ); // Top Half
3617 _intersectsWithPointer: function(item) {
3619 var isOverElementHeight = (this.options.axis === 'x') || $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
3620 isOverElementWidth = (this.options.axis === 'y') || $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
3621 isOverElement = isOverElementHeight && isOverElementWidth,
3622 verticalDirection = this._getDragVerticalDirection(),
3623 horizontalDirection = this._getDragHorizontalDirection();
3628 return this.floating ?
3629 ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
3630 : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
3634 _intersectsWithSides: function(item) {
3636 var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
3637 isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
3638 verticalDirection = this._getDragVerticalDirection(),
3639 horizontalDirection = this._getDragHorizontalDirection();
3641 if (this.floating && horizontalDirection) {
3642 return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
3644 return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
3649 _getDragVerticalDirection: function() {
3650 var delta = this.positionAbs.top - this.lastPositionAbs.top;
3651 return delta != 0 && (delta > 0 ? "down" : "up");
3654 _getDragHorizontalDirection: function() {
3655 var delta = this.positionAbs.left - this.lastPositionAbs.left;
3656 return delta != 0 && (delta > 0 ? "right" : "left");
3659 refresh: function(event) {
3660 this._refreshItems(event);
3661 this.refreshPositions();
3665 _connectWith: function() {
3666 var options = this.options;
3667 return options.connectWith.constructor == String
3668 ? [options.connectWith]
3669 : options.connectWith;
3672 _getItemsAsjQuery: function(connected) {
3676 var connectWith = this._connectWith();
3678 if(connectWith && connected) {
3679 for (var i = connectWith.length - 1; i >= 0; i--){
3680 var cur = $(connectWith[i]);
3681 for (var j = cur.length - 1; j >= 0; j--){
3682 var inst = $.data(cur[j], this.widgetName);
3683 if(inst && inst != this && !inst.options.disabled) {
3684 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]);
3690 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]);
3692 for (var i = queries.length - 1; i >= 0; i--){
3693 queries[i][0].each(function() {
3702 _removeCurrentsFromItems: function() {
3704 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
3706 this.items = $.grep(this.items, function (item) {
3707 for (var j=0; j < list.length; j++) {
3708 if(list[j] == item.item[0])
3716 _refreshItems: function(event) {
3719 this.containers = [this];
3720 var items = this.items;
3721 var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
3722 var connectWith = this._connectWith();
3724 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
3725 for (var i = connectWith.length - 1; i >= 0; i--){
3726 var cur = $(connectWith[i]);
3727 for (var j = cur.length - 1; j >= 0; j--){
3728 var inst = $.data(cur[j], this.widgetName);
3729 if(inst && inst != this && !inst.options.disabled) {
3730 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
3731 this.containers.push(inst);
3737 for (var i = queries.length - 1; i >= 0; i--) {
3738 var targetData = queries[i][1];
3739 var _queries = queries[i][0];
3741 for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
3742 var item = $(_queries[j]);
3744 item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
3748 instance: targetData,
3749 width: 0, height: 0,
3757 refreshPositions: function(fast) {
3759 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
3760 if(this.offsetParent && this.helper) {
3761 this.offset.parent = this._getParentOffset();
3764 for (var i = this.items.length - 1; i >= 0; i--){
3765 var item = this.items[i];
3767 //We ignore calculating positions of all connected containers when we're not over them
3768 if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
3771 var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
3774 item.width = t.outerWidth();
3775 item.height = t.outerHeight();
3783 if(this.options.custom && this.options.custom.refreshContainers) {
3784 this.options.custom.refreshContainers.call(this);
3786 for (var i = this.containers.length - 1; i >= 0; i--){
3787 var p = this.containers[i].element.offset();
3788 this.containers[i].containerCache.left = p.left;
3789 this.containers[i].containerCache.top = p.top;
3790 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
3791 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
3798 _createPlaceholder: function(that) {
3799 that = that || this;
3800 var o = that.options;
3802 if(!o.placeholder || o.placeholder.constructor == String) {
3803 var className = o.placeholder;
3805 element: function() {
3807 var el = $(document.createElement(that.currentItem[0].nodeName))
3808 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
3809 .removeClass("ui-sortable-helper")[0];
3812 el.style.visibility = "hidden";
3816 update: function(container, p) {
3818 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
3819 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
3820 if(className && !o.forcePlaceholderSize) return;
3822 //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
3823 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css('paddingTop')||0, 10) - parseInt(that.currentItem.css('paddingBottom')||0, 10)); };
3824 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css('paddingLeft')||0, 10) - parseInt(that.currentItem.css('paddingRight')||0, 10)); };
3829 //Create the placeholder
3830 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
3832 //Append it after the actual current item
3833 that.currentItem.after(that.placeholder);
3835 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
3836 o.placeholder.update(that, that.placeholder);
3840 _contactContainers: function(event) {
3842 // get innermost container that intersects with item
3843 var innermostContainer = null, innermostIndex = null;
3846 for (var i = this.containers.length - 1; i >= 0; i--){
3848 // never consider a container that's located within the item itself
3849 if($.contains(this.currentItem[0], this.containers[i].element[0]))
3852 if(this._intersectsWith(this.containers[i].containerCache)) {
3854 // if we've already found a container and it's more "inner" than this, then continue
3855 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0]))
3858 innermostContainer = this.containers[i];
3862 // container doesn't intersect. trigger "out" event if necessary
3863 if(this.containers[i].containerCache.over) {
3864 this.containers[i]._trigger("out", event, this._uiHash(this));
3865 this.containers[i].containerCache.over = 0;
3871 // if no intersecting containers found, return
3872 if(!innermostContainer) return;
3874 // move the item into the container if it's not there already
3875 if(this.containers.length === 1) {
3876 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
3877 this.containers[innermostIndex].containerCache.over = 1;
3880 //When entering a new container, we will find the item with the least distance and append our item near it
3881 var dist = 10000; var itemWithLeastDistance = null;
3882 var posProperty = this.containers[innermostIndex].floating ? 'left' : 'top';
3883 var sizeProperty = this.containers[innermostIndex].floating ? 'width' : 'height';
3884 var base = this.positionAbs[posProperty] + this.offset.click[posProperty];
3885 for (var j = this.items.length - 1; j >= 0; j--) {
3886 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
3887 if(this.items[j].item[0] == this.currentItem[0]) continue;
3888 var cur = this.items[j].item.offset()[posProperty];
3889 var nearBottom = false;
3890 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
3892 cur += this.items[j][sizeProperty];
3895 if(Math.abs(cur - base) < dist) {
3896 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
3897 this.direction = nearBottom ? "up": "down";
3901 if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
3904 this.currentContainer = this.containers[innermostIndex];
3905 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
3906 this._trigger("change", event, this._uiHash());
3907 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
3909 //Update the placeholder
3910 this.options.placeholder.update(this.currentContainer, this.placeholder);
3912 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
3913 this.containers[innermostIndex].containerCache.over = 1;
3919 _createHelper: function(event) {
3921 var o = this.options;
3922 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
3924 if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
3925 $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
3927 if(helper[0] == this.currentItem[0])
3928 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") };
3930 if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
3931 if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
3937 _adjustOffsetFromHelper: function(obj) {
3938 if (typeof obj == 'string') {
3939 obj = obj.split(' ');
3941 if ($.isArray(obj)) {
3942 obj = {left: +obj[0], top: +obj[1] || 0};
3944 if ('left' in obj) {
3945 this.offset.click.left = obj.left + this.margins.left;
3947 if ('right' in obj) {
3948 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
3951 this.offset.click.top = obj.top + this.margins.top;
3953 if ('bottom' in obj) {
3954 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
3958 _getParentOffset: function() {
3961 //Get the offsetParent and cache its position
3962 this.offsetParent = this.helper.offsetParent();
3963 var po = this.offsetParent.offset();
3965 // This is a special case where we need to modify a offset calculated on start, since the following happened:
3966 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
3967 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
3968 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
3969 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
3970 po.left += this.scrollParent.scrollLeft();
3971 po.top += this.scrollParent.scrollTop();
3974 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
3975 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
3976 po = { top: 0, left: 0 };
3979 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
3980 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
3985 _getRelativeOffset: function() {
3987 if(this.cssPosition == "relative") {
3988 var p = this.currentItem.position();
3990 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
3991 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
3994 return { top: 0, left: 0 };
3999 _cacheMargins: function() {
4001 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4002 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4006 _cacheHelperProportions: function() {
4007 this.helperProportions = {
4008 width: this.helper.outerWidth(),
4009 height: this.helper.outerHeight()
4013 _setContainment: function() {
4015 var o = this.options;
4016 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
4017 if(o.containment == 'document' || o.containment == 'window') this.containment = [
4018 0 - this.offset.relative.left - this.offset.parent.left,
4019 0 - this.offset.relative.top - this.offset.parent.top,
4020 $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
4021 ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4024 if(!(/^(document|window|parent)$/).test(o.containment)) {
4025 var ce = $(o.containment)[0];
4026 var co = $(o.containment).offset();
4027 var over = ($(ce).css("overflow") != 'hidden');
4029 this.containment = [
4030 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4031 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4032 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,
4033 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
4039 _convertPositionTo: function(d, pos) {
4041 if(!pos) pos = this.position;
4042 var mod = d == "absolute" ? 1 : -1;
4043 var o = this.options, 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);
4047 pos.top // The absolute mouse position
4048 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4049 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
4050 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4053 pos.left // The absolute mouse position
4054 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4055 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
4056 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4062 _generatePosition: function(event) {
4064 var o = this.options, 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);
4066 // This is another very weird special case that only happens for relative elements:
4067 // 1. If the css position is relative
4068 // 2. and the scroll parent is the document or similar to the offset parent
4069 // we have to refresh the relative offset during the scroll so there are no jumps
4070 if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
4071 this.offset.relative = this._getRelativeOffset();
4074 var pageX = event.pageX;
4075 var pageY = event.pageY;
4078 * - Position constraining -
4079 * Constrain the position to a mix of grid, containment.
4082 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4084 if(this.containment) {
4085 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
4086 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
4087 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
4088 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
4092 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4093 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;
4095 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4096 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;
4103 pageY // The absolute mouse position
4104 - this.offset.click.top // Click offset (relative to the element)
4105 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
4106 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
4107 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4110 pageX // The absolute mouse position
4111 - this.offset.click.left // Click offset (relative to the element)
4112 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
4113 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
4114 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4120 _rearrange: function(event, i, a, hardRefresh) {
4122 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));
4124 //Various things done here to improve the performance:
4125 // 1. we create a setTimeout, that calls refreshPositions
4126 // 2. on the instance, we have a counter variable, that get's higher after every append
4127 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4128 // 4. this lets only the last addition to the timeout stack through
4129 this.counter = this.counter ? ++this.counter : 1;
4130 var counter = this.counter;
4132 this._delay(function() {
4133 if(counter == this.counter) this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4138 _clear: function(event, noPropagation) {
4140 this.reverting = false;
4141 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4142 // everything else normalized again
4143 var delayedTriggers = [];
4145 // We first have to update the dom position of the actual currentItem
4146 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4147 if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
4148 this._noFinalSort = null;
4150 if(this.helper[0] == this.currentItem[0]) {
4151 for(var i in this._storedCSS) {
4152 if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
4154 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4156 this.currentItem.show();
4159 if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4160 if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4162 // Check if the items Container has Changed and trigger appropriate
4164 if (this !== this.currentContainer) {
4165 if(!noPropagation) {
4166 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4167 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4168 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4173 //Post events to containers
4174 for (var i = this.containers.length - 1; i >= 0; i--){
4175 if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4176 if(this.containers[i].containerCache.over) {
4177 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4178 this.containers[i].containerCache.over = 0;
4182 //Do what was originally in plugins
4183 if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
4184 if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
4185 if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
4187 this.dragging = false;
4188 if(this.cancelHelperRemoval) {
4189 if(!noPropagation) {
4190 this._trigger("beforeStop", event, this._uiHash());
4191 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4192 this._trigger("stop", event, this._uiHash());
4195 this.fromOutside = false;
4199 if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
4201 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4202 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4204 if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
4206 if(!noPropagation) {
4207 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4208 this._trigger("stop", event, this._uiHash());
4211 this.fromOutside = false;
4216 _trigger: function() {
4217 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4222 _uiHash: function(_inst) {
4223 var inst = _inst || this;
4225 helper: inst.helper,
4226 placeholder: inst.placeholder || $([]),
4227 position: inst.position,
4228 originalPosition: inst.originalPosition,
4229 offset: inst.positionAbs,
4230 item: inst.currentItem,
4231 sender: _inst ? _inst.element : null
4238 (function( $, undefined ) {
4240 $.widget( "ui.progressbar", {
4249 _create: function() {
4251 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
4253 role: "progressbar",
4254 "aria-valuemin": this.min,
4255 "aria-valuemax": this.options.max,
4256 "aria-valuenow": this._value()
4259 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
4260 .appendTo( this.element );
4262 this.oldValue = this._value();
4263 this._refreshValue();
4266 _destroy: function() {
4268 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
4269 .removeAttr( "role" )
4270 .removeAttr( "aria-valuemin" )
4271 .removeAttr( "aria-valuemax" )
4272 .removeAttr( "aria-valuenow" );
4274 this.valueDiv.remove();
4277 value: function( newValue ) {
4278 if ( newValue === undefined ) {
4279 return this._value();
4282 this._setOption( "value", newValue );
4286 _setOption: function( key, value ) {
4287 if ( key === "value" ) {
4288 this.options.value = value;
4289 this._refreshValue();
4290 if ( this._value() === this.options.max ) {
4291 this._trigger( "complete" );
4295 this._super( key, value );
4298 _value: function() {
4299 var val = this.options.value;
4300 // normalize invalid value
4301 if ( typeof val !== "number" ) {
4304 return Math.min( this.options.max, Math.max( this.min, val ) );
4307 _percentage: function() {
4308 return 100 * this._value() / this.options.max;
4311 _refreshValue: function() {
4312 var value = this.value(),
4313 percentage = this._percentage();
4315 if ( this.oldValue !== value ) {
4316 this.oldValue = value;
4317 this._trigger( "change" );
4321 .toggle( value > this.min )
4322 .toggleClass( "ui-corner-right", value === this.options.max )
4323 .width( percentage.toFixed(0) + "%" );
4324 this.element.attr( "aria-valuenow", value );
4329 (function( $, undefined ) {
4334 function getNextTabId() {
4338 function isLocal( anchor ) {
4339 return anchor.hash.length > 1 &&
4340 anchor.href.replace( rhash, "" ) ===
4341 location.href.replace( rhash, "" )
4342 // support: Safari 5.1
4343 // Safari 5.1 doesn't encode spaces in window.location
4344 // but it does encode spaces from anchors (#8777)
4345 .replace( /\s/g, "%20" );
4348 $.widget( "ui.tabs", {
4355 heightStyle: "content",
4361 beforeActivate: null,
4366 _create: function() {
4368 options = this.options,
4369 active = options.active,
4370 locationHash = location.hash.substring( 1 );
4372 this.running = false;
4375 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
4376 .toggleClass( "ui-tabs-collapsible", options.collapsible )
4377 // Prevent users from focusing disabled tabs via click
4378 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
4379 if ( $( this ).is( ".ui-state-disabled" ) ) {
4380 event.preventDefault();
4384 // Preventing the default action in mousedown doesn't prevent IE
4385 // from focusing the element, so if the anchor gets focused, blur.
4386 // We don't have to worry about focusing the previously focused
4387 // element since clicking on a non-focusable element should focus
4389 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
4390 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
4395 this._processTabs();
4397 if ( active === null ) {
4398 // check the fragment identifier in the URL
4399 if ( locationHash ) {
4400 this.tabs.each(function( i, tab ) {
4401 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
4408 // check for a tab marked active via a class
4409 if ( active === null ) {
4410 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
4413 // no active tab, set to false
4414 if ( active === null || active === -1 ) {
4415 active = this.tabs.length ? 0 : false;
4419 // handle numbers: negative, out of range
4420 if ( active !== false ) {
4421 active = this.tabs.index( this.tabs.eq( active ) );
4422 if ( active === -1 ) {
4423 active = options.collapsible ? false : 0;
4426 options.active = active;
4428 // don't allow collapsible: false and active: false
4429 if ( !options.collapsible && options.active === false && this.anchors.length ) {
4433 // Take disabling tabs via class attribute from HTML
4434 // into account and update option properly.
4435 if ( $.isArray( options.disabled ) ) {
4436 options.disabled = $.unique( options.disabled.concat(
4437 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
4438 return that.tabs.index( li );
4443 // check for length avoids error when initializing empty list
4444 if ( this.options.active !== false && this.anchors.length ) {
4445 this.active = this._findActive( this.options.active );
4452 if ( this.active.length ) {
4453 this.load( options.active );
4457 _getCreateEventData: function() {
4460 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
4464 _tabKeydown: function( event ) {
4465 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
4466 selectedIndex = this.tabs.index( focusedTab ),
4467 goingForward = true;
4469 if ( this._handlePageNav( event ) ) {
4473 switch ( event.keyCode ) {
4474 case $.ui.keyCode.RIGHT:
4475 case $.ui.keyCode.DOWN:
4478 case $.ui.keyCode.UP:
4479 case $.ui.keyCode.LEFT:
4480 goingForward = false;
4483 case $.ui.keyCode.END:
4484 selectedIndex = this.anchors.length - 1;
4486 case $.ui.keyCode.HOME:
4489 case $.ui.keyCode.SPACE:
4490 // Activate only, no collapsing
4491 event.preventDefault();
4492 clearTimeout( this.activating );
4493 this._activate( selectedIndex );
4495 case $.ui.keyCode.ENTER:
4496 // Toggle (cancel delayed activation, allow collapsing)
4497 event.preventDefault();
4498 clearTimeout( this.activating );
4499 // Determine if we should collapse or activate
4500 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
4506 // Focus the appropriate tab, based on which key was pressed
4507 event.preventDefault();
4508 clearTimeout( this.activating );
4509 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
4511 // Navigating with control key will prevent automatic activation
4512 if ( !event.ctrlKey ) {
4513 // Update aria-selected immediately so that AT think the tab is already selected.
4514 // Otherwise AT may confuse the user by stating that they need to activate the tab,
4515 // but the tab will already be activated by the time the announcement finishes.
4516 focusedTab.attr( "aria-selected", "false" );
4517 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
4519 this.activating = this._delay(function() {
4520 this.option( "active", selectedIndex );
4525 _panelKeydown: function( event ) {
4526 if ( this._handlePageNav( event ) ) {
4530 // Ctrl+up moves focus to the current tab
4531 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
4532 event.preventDefault();
4533 this.active.focus();
4537 // Alt+page up/down moves focus to the previous/next tab (and activates)
4538 _handlePageNav: function( event ) {
4539 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
4540 this._activate( this._focusNextTab( this.options.active - 1, false ) );
4543 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
4544 this._activate( this._focusNextTab( this.options.active + 1, true ) );
4549 _findNextTab: function( index, goingForward ) {
4550 var lastTabIndex = this.tabs.length - 1;
4552 function constrain() {
4553 if ( index > lastTabIndex ) {
4557 index = lastTabIndex;
4562 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
4563 index = goingForward ? index + 1 : index - 1;
4569 _focusNextTab: function( index, goingForward ) {
4570 index = this._findNextTab( index, goingForward );
4571 this.tabs.eq( index ).focus();
4575 _setOption: function( key, value ) {
4576 if ( key === "active" ) {
4577 // _activate() will handle invalid values and update this.options
4578 this._activate( value );
4582 if ( key === "disabled" ) {
4583 // don't use the widget factory's disabled handling
4584 this._setupDisabled( value );
4588 this._super( key, value);
4590 if ( key === "collapsible" ) {
4591 this.element.toggleClass( "ui-tabs-collapsible", value );
4592 // Setting collapsible: false while collapsed; open first panel
4593 if ( !value && this.options.active === false ) {
4594 this._activate( 0 );
4598 if ( key === "event" ) {
4599 this._setupEvents( value );
4602 if ( key === "heightStyle" ) {
4603 this._setupHeightStyle( value );
4607 _tabId: function( tab ) {
4608 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
4611 _sanitizeSelector: function( hash ) {
4612 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
4615 refresh: function() {
4616 var options = this.options,
4617 lis = this.tablist.children( ":has(a[href])" );
4619 // get disabled tabs from class attribute from HTML
4620 // this will get converted to a boolean if needed in _refresh()
4621 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
4622 return lis.index( tab );
4625 this._processTabs();
4627 // was collapsed or no tabs
4628 if ( options.active === false || !this.anchors.length ) {
4629 options.active = false;
4631 // was active, but active tab is gone
4632 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
4633 // all remaining tabs are disabled
4634 if ( this.tabs.length === options.disabled.length ) {
4635 options.active = false;
4637 // activate previous tab
4639 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
4641 // was active, active tab still exists
4643 // make sure active index is correct
4644 options.active = this.tabs.index( this.active );
4650 _refresh: function() {
4651 this._setupDisabled( this.options.disabled );
4652 this._setupEvents( this.options.event );
4653 this._setupHeightStyle( this.options.heightStyle );
4655 this.tabs.not( this.active ).attr({
4656 "aria-selected": "false",
4659 this.panels.not( this._getPanelForTab( this.active ) )
4662 "aria-expanded": "false",
4663 "aria-hidden": "true"
4666 // Make sure one tab is in the tab order
4667 if ( !this.active.length ) {
4668 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
4671 .addClass( "ui-tabs-active ui-state-active" )
4673 "aria-selected": "true",
4676 this._getPanelForTab( this.active )
4679 "aria-expanded": "true",
4680 "aria-hidden": "false"
4685 _processTabs: function() {
4688 this.tablist = this._getList()
4689 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
4690 .attr( "role", "tablist" );
4692 this.tabs = this.tablist.find( "> li:has(a[href])" )
4693 .addClass( "ui-state-default ui-corner-top" )
4699 this.anchors = this.tabs.map(function() {
4700 return $( "a", this )[ 0 ];
4702 .addClass( "ui-tabs-anchor" )
4704 role: "presentation",
4710 this.anchors.each(function( i, anchor ) {
4711 var selector, panel, panelId,
4712 anchorId = $( anchor ).uniqueId().attr( "id" ),
4713 tab = $( anchor ).closest( "li" ),
4714 originalAriaControls = tab.attr( "aria-controls" );
4717 if ( isLocal( anchor ) ) {
4718 selector = anchor.hash;
4719 panel = that.element.find( that._sanitizeSelector( selector ) );
4722 panelId = that._tabId( tab );
4723 selector = "#" + panelId;
4724 panel = that.element.find( selector );
4725 if ( !panel.length ) {
4726 panel = that._createPanel( panelId );
4727 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
4729 panel.attr( "aria-live", "polite" );
4732 if ( panel.length) {
4733 that.panels = that.panels.add( panel );
4735 if ( originalAriaControls ) {
4736 tab.data( "ui-tabs-aria-controls", originalAriaControls );
4739 "aria-controls": selector.substring( 1 ),
4740 "aria-labelledby": anchorId
4742 panel.attr( "aria-labelledby", anchorId );
4746 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
4747 .attr( "role", "tabpanel" );
4750 // allow overriding how to find the list for rare usage scenarios (#7715)
4751 _getList: function() {
4752 return this.element.find( "ol,ul" ).eq( 0 );
4755 _createPanel: function( id ) {
4758 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
4759 .data( "ui-tabs-destroy", true );
4762 _setupDisabled: function( disabled ) {
4763 if ( $.isArray( disabled ) ) {
4764 if ( !disabled.length ) {
4766 } else if ( disabled.length === this.anchors.length ) {
4772 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
4773 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
4775 .addClass( "ui-state-disabled" )
4776 .attr( "aria-disabled", "true" );
4779 .removeClass( "ui-state-disabled" )
4780 .removeAttr( "aria-disabled" );
4784 this.options.disabled = disabled;
4787 _setupEvents: function( event ) {
4789 click: function( event ) {
4790 event.preventDefault();
4794 $.each( event.split(" "), function( index, eventName ) {
4795 events[ eventName ] = "_eventHandler";
4799 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
4800 this._on( this.anchors, events );
4801 this._on( this.tabs, { keydown: "_tabKeydown" } );
4802 this._on( this.panels, { keydown: "_panelKeydown" } );
4804 this._focusable( this.tabs );
4805 this._hoverable( this.tabs );
4808 _setupHeightStyle: function( heightStyle ) {
4809 var maxHeight, overflow,
4810 parent = this.element.parent();
4812 if ( heightStyle === "fill" ) {
4813 // IE 6 treats height like minHeight, so we need to turn off overflow
4814 // in order to get a reliable height
4815 // we use the minHeight support test because we assume that only
4816 // browsers that don't support minHeight will treat height as minHeight
4817 if ( !$.support.minHeight ) {
4818 overflow = parent.css( "overflow" );
4819 parent.css( "overflow", "hidden");
4821 maxHeight = parent.height();
4822 this.element.siblings( ":visible" ).each(function() {
4823 var elem = $( this ),
4824 position = elem.css( "position" );
4826 if ( position === "absolute" || position === "fixed" ) {
4829 maxHeight -= elem.outerHeight( true );
4832 parent.css( "overflow", overflow );
4835 this.element.children().not( this.panels ).each(function() {
4836 maxHeight -= $( this ).outerHeight( true );
4839 this.panels.each(function() {
4840 $( this ).height( Math.max( 0, maxHeight -
4841 $( this ).innerHeight() + $( this ).height() ) );
4843 .css( "overflow", "auto" );
4844 } else if ( heightStyle === "auto" ) {
4846 this.panels.each(function() {
4847 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
4848 }).height( maxHeight );
4852 _eventHandler: function( event ) {
4853 var options = this.options,
4854 active = this.active,
4855 anchor = $( event.currentTarget ),
4856 tab = anchor.closest( "li" ),
4857 clickedIsActive = tab[ 0 ] === active[ 0 ],
4858 collapsing = clickedIsActive && options.collapsible,
4859 toShow = collapsing ? $() : this._getPanelForTab( tab ),
4860 toHide = !active.length ? $() : this._getPanelForTab( active ),
4864 newTab: collapsing ? $() : tab,
4868 event.preventDefault();
4870 if ( tab.hasClass( "ui-state-disabled" ) ||
4871 // tab is already loading
4872 tab.hasClass( "ui-tabs-loading" ) ||
4873 // can't switch durning an animation
4875 // click on active header, but not collapsible
4876 ( clickedIsActive && !options.collapsible ) ||
4877 // allow canceling activation
4878 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
4882 options.active = collapsing ? false : this.tabs.index( tab );
4884 this.active = clickedIsActive ? $() : tab;
4889 if ( !toHide.length && !toShow.length ) {
4890 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
4893 if ( toShow.length ) {
4894 this.load( this.tabs.index( tab ), event );
4896 this._toggle( event, eventData );
4899 // handles show/hide for selecting tabs
4900 _toggle: function( event, eventData ) {
4902 toShow = eventData.newPanel,
4903 toHide = eventData.oldPanel;
4905 this.running = true;
4907 function complete() {
4908 that.running = false;
4909 that._trigger( "activate", event, eventData );
4913 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
4915 if ( toShow.length && that.options.show ) {
4916 that._show( toShow, that.options.show, complete );
4923 // start out by hiding, then showing, then completing
4924 if ( toHide.length && this.options.hide ) {
4925 this._hide( toHide, this.options.hide, function() {
4926 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
4930 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
4936 "aria-expanded": "false",
4937 "aria-hidden": "true"
4939 eventData.oldTab.attr( "aria-selected", "false" );
4940 // If we're switching tabs, remove the old tab from the tab order.
4941 // If we're opening from collapsed state, remove the previous tab from the tab order.
4942 // If we're collapsing, then keep the collapsing tab in the tab order.
4943 if ( toShow.length && toHide.length ) {
4944 eventData.oldTab.attr( "tabIndex", -1 );
4945 } else if ( toShow.length ) {
4946 this.tabs.filter(function() {
4947 return $( this ).attr( "tabIndex" ) === 0;
4949 .attr( "tabIndex", -1 );
4953 "aria-expanded": "true",
4954 "aria-hidden": "false"
4956 eventData.newTab.attr({
4957 "aria-selected": "true",
4962 _activate: function( index ) {
4964 active = this._findActive( index );
4966 // trying to activate the already active panel
4967 if ( active[ 0 ] === this.active[ 0 ] ) {
4971 // trying to collapse, simulate a click on the current active header
4972 if ( !active.length ) {
4973 active = this.active;
4976 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
4977 this._eventHandler({
4979 currentTarget: anchor,
4980 preventDefault: $.noop
4984 _findActive: function( index ) {
4985 return index === false ? $() : this.tabs.eq( index );
4988 _getIndex: function( index ) {
4989 // meta-function to give users option to provide a href string instead of a numerical index.
4990 if ( typeof index === "string" ) {
4991 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
4997 _destroy: function() {
5002 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
5005 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
5006 .removeAttr( "role" );
5009 .removeClass( "ui-tabs-anchor" )
5010 .removeAttr( "role" )
5011 .removeAttr( "tabIndex" )
5012 .removeData( "href.tabs" )
5013 .removeData( "load.tabs" )
5016 this.tabs.add( this.panels ).each(function() {
5017 if ( $.data( this, "ui-tabs-destroy" ) ) {
5021 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
5022 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
5023 .removeAttr( "tabIndex" )
5024 .removeAttr( "aria-live" )
5025 .removeAttr( "aria-busy" )
5026 .removeAttr( "aria-selected" )
5027 .removeAttr( "aria-labelledby" )
5028 .removeAttr( "aria-hidden" )
5029 .removeAttr( "aria-expanded" )
5030 .removeAttr( "role" );
5034 this.tabs.each(function() {
5036 prev = li.data( "ui-tabs-aria-controls" );
5038 li.attr( "aria-controls", prev );
5040 li.removeAttr( "aria-controls" );
5046 if ( this.options.heightStyle !== "content" ) {
5047 this.panels.css( "height", "" );
5051 enable: function( index ) {
5052 var disabled = this.options.disabled;
5053 if ( disabled === false ) {
5057 if ( index === undefined ) {
5060 index = this._getIndex( index );
5061 if ( $.isArray( disabled ) ) {
5062 disabled = $.map( disabled, function( num ) {
5063 return num !== index ? num : null;
5066 disabled = $.map( this.tabs, function( li, num ) {
5067 return num !== index ? num : null;
5071 this._setupDisabled( disabled );
5074 disable: function( index ) {
5075 var disabled = this.options.disabled;
5076 if ( disabled === true ) {
5080 if ( index === undefined ) {
5083 index = this._getIndex( index );
5084 if ( $.inArray( index, disabled ) !== -1 ) {
5087 if ( $.isArray( disabled ) ) {
5088 disabled = $.merge( [ index ], disabled ).sort();
5090 disabled = [ index ];
5093 this._setupDisabled( disabled );
5096 load: function( index, event ) {
5097 index = this._getIndex( index );
5099 tab = this.tabs.eq( index ),
5100 anchor = tab.find( ".ui-tabs-anchor" ),
5101 panel = this._getPanelForTab( tab ),
5108 if ( isLocal( anchor[ 0 ] ) ) {
5112 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
5114 // support: jQuery <1.8
5115 // jQuery <1.8 returns false if the request is canceled in beforeSend,
5116 // but as of 1.8, $.ajax() always returns a jqXHR object.
5117 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
5118 tab.addClass( "ui-tabs-loading" );
5119 panel.attr( "aria-busy", "true" );
5122 .success(function( response ) {
5123 // support: jQuery <1.8
5124 // http://bugs.jquery.com/ticket/11778
5125 setTimeout(function() {
5126 panel.html( response );
5127 that._trigger( "load", event, eventData );
5130 .complete(function( jqXHR, status ) {
5131 // support: jQuery <1.8
5132 // http://bugs.jquery.com/ticket/11778
5133 setTimeout(function() {
5134 if ( status === "abort" ) {
5135 that.panels.stop( false, true );
5138 tab.removeClass( "ui-tabs-loading" );
5139 panel.removeAttr( "aria-busy" );
5141 if ( jqXHR === that.xhr ) {
5149 // TODO: Remove this function in 1.10 when ajaxOptions is removed
5150 _ajaxSettings: function( anchor, event, eventData ) {
5153 url: anchor.attr( "href" ),
5154 beforeSend: function( jqXHR, settings ) {
5155 return that._trigger( "beforeLoad", event,
5156 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
5161 _getPanelForTab: function( tab ) {
5162 var id = $( tab ).attr( "aria-controls" );
5163 return this.element.find( this._sanitizeSelector( "#" + id ) );
5168 if ( $.uiBackCompat !== false ) {
5170 // helper method for a lot of the back compat extensions
5171 $.ui.tabs.prototype._ui = function( tab, panel ) {
5175 index: this.anchors.index( tab )
5180 $.widget( "ui.tabs", $.ui.tabs, {
5181 url: function( index, url ) {
5182 this.anchors.eq( index ).attr( "href", url );
5186 // TODO: Remove _ajaxSettings() method when removing this extension
5187 // ajaxOptions and cache options
5188 $.widget( "ui.tabs", $.ui.tabs, {
5194 _create: function() {
5199 this._on({ tabsbeforeload: function( event, ui ) {
5200 // tab is already cached
5201 if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) {
5202 event.preventDefault();
5206 ui.jqXHR.success(function() {
5207 if ( that.options.cache ) {
5208 $.data( ui.tab[ 0 ], "cache.tabs", true );
5214 _ajaxSettings: function( anchor, event, ui ) {
5215 var ajaxOptions = this.options.ajaxOptions;
5216 return $.extend( {}, ajaxOptions, {
5217 error: function( xhr, status ) {
5219 // Passing index avoid a race condition when this method is
5220 // called after the user has selected another tab.
5221 // Pass the anchor that initiated this request allows
5222 // loadError to manipulate the tab content panel via $(a.hash)
5224 xhr, status, ui.tab.closest( "li" ).index(), ui.tab[ 0 ] );
5228 }, this._superApply( arguments ) );
5231 _setOption: function( key, value ) {
5232 // reset cache if switching from cached to not cached
5233 if ( key === "cache" && value === false ) {
5234 this.anchors.removeData( "cache.tabs" );
5236 this._super( key, value );
5239 _destroy: function() {
5240 this.anchors.removeData( "cache.tabs" );
5244 url: function( index ){
5245 this.anchors.eq( index ).removeData( "cache.tabs" );
5246 this._superApply( arguments );
5251 $.widget( "ui.tabs", $.ui.tabs, {
5260 $.widget( "ui.tabs", $.ui.tabs, {
5262 spinner: "<em>Loading…</em>"
5264 _create: function() {
5267 tabsbeforeload: function( event, ui ) {
5268 // Don't react to nested tabs or tabs that don't use a spinner
5269 if ( event.target !== this.element[ 0 ] ||
5270 !this.options.spinner ) {
5274 var span = ui.tab.find( "span" ),
5276 span.html( this.options.spinner );
5277 ui.jqXHR.complete(function() {
5285 // enable/disable events
5286 $.widget( "ui.tabs", $.ui.tabs, {
5292 enable: function( index ) {
5293 var options = this.options,
5296 if ( index && options.disabled === true ||
5297 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) !== -1 ) ) {
5301 this._superApply( arguments );
5304 this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
5308 disable: function( index ) {
5309 var options = this.options,
5312 if ( index && options.disabled === false ||
5313 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) === -1 ) ) {
5317 this._superApply( arguments );
5320 this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
5325 // add/remove methods and events
5326 $.widget( "ui.tabs", $.ui.tabs, {
5330 tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
5333 add: function( url, label, index ) {
5334 if ( index === undefined ) {
5335 index = this.anchors.length;
5338 var doInsertAfter, panel,
5339 options = this.options,
5340 li = $( options.tabTemplate
5341 .replace( /#\{href\}/g, url )
5342 .replace( /#\{label\}/g, label ) ),
5343 id = !url.indexOf( "#" ) ?
5344 url.replace( "#", "" ) :
5347 li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
5348 li.attr( "aria-controls", id );
5350 doInsertAfter = index >= this.tabs.length;
5352 // try to find an existing element before creating a new one
5353 panel = this.element.find( "#" + id );
5354 if ( !panel.length ) {
5355 panel = this._createPanel( id );
5356 if ( doInsertAfter ) {
5358 panel.insertAfter( this.panels.eq( -1 ) );
5360 panel.appendTo( this.element );
5363 panel.insertBefore( this.panels[ index ] );
5366 panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide();
5368 if ( doInsertAfter ) {
5369 li.appendTo( this.tablist );
5371 li.insertBefore( this.tabs[ index ] );
5374 options.disabled = $.map( options.disabled, function( n ) {
5375 return n >= index ? ++n : n;
5379 if ( this.tabs.length === 1 && options.active === false ) {
5380 this.option( "active", 0 );
5383 this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
5387 remove: function( index ) {
5388 index = this._getIndex( index );
5389 var options = this.options,
5390 tab = this.tabs.eq( index ).remove(),
5391 panel = this._getPanelForTab( tab ).remove();
5393 // If selected tab was removed focus tab to the right or
5394 // in case the last tab was removed the tab to the left.
5395 // We check for more than 2 tabs, because if there are only 2,
5396 // then when we remove this tab, there will only be one tab left
5397 // so we don't need to detect which tab to activate.
5398 if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
5399 this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
5402 options.disabled = $.map(
5403 $.grep( options.disabled, function( n ) {
5407 return n >= index ? --n : n;
5412 this._trigger( "remove", null, this._ui( tab.find( "a" )[ 0 ], panel[ 0 ] ) );
5418 $.widget( "ui.tabs", $.ui.tabs, {
5419 length: function() {
5420 return this.anchors.length;
5424 // panel ids (idPrefix option + title attribute)
5425 $.widget( "ui.tabs", $.ui.tabs, {
5427 idPrefix: "ui-tabs-"
5430 _tabId: function( tab ) {
5431 var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
5433 return $( a ).closest( "li" ).attr( "aria-controls" ) ||
5434 a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
5435 this.options.idPrefix + getNextTabId();
5439 // _createPanel method
5440 $.widget( "ui.tabs", $.ui.tabs, {
5442 panelTemplate: "<div></div>"
5445 _createPanel: function( id ) {
5446 return $( this.options.panelTemplate )
5448 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
5449 .data( "ui-tabs-destroy", true );
5454 $.widget( "ui.tabs", $.ui.tabs, {
5455 _create: function() {
5456 var options = this.options;
5457 if ( options.active === null && options.selected !== undefined ) {
5458 options.active = options.selected === -1 ? false : options.selected;
5461 options.selected = options.active;
5462 if ( options.selected === false ) {
5463 options.selected = -1;
5467 _setOption: function( key, value ) {
5468 if ( key !== "selected" ) {
5469 return this._super( key, value );
5472 var options = this.options;
5473 this._super( "active", value === -1 ? false : value );
5474 options.selected = options.active;
5475 if ( options.selected === false ) {
5476 options.selected = -1;
5480 _eventHandler: function() {
5481 this._superApply( arguments );
5482 this.options.selected = this.options.active;
5483 if ( this.options.selected === false ) {
5484 this.options.selected = -1;
5489 // show and select event
5490 $.widget( "ui.tabs", $.ui.tabs, {
5495 _create: function() {
5497 if ( this.options.active !== false ) {
5498 this._trigger( "show", null, this._ui(
5499 this.active.find( ".ui-tabs-anchor" )[ 0 ],
5500 this._getPanelForTab( this.active )[ 0 ] ) );
5503 _trigger: function( type, event, data ) {
5505 ret = this._superApply( arguments );
5511 if ( type === "beforeActivate" ) {
5512 tab = data.newTab.length ? data.newTab : data.oldTab;
5513 panel = data.newPanel.length ? data.newPanel : data.oldPanel;
5514 ret = this._super( "select", event, {
5515 tab: tab.find( ".ui-tabs-anchor" )[ 0],
5517 index: tab.closest( "li" ).index()
5519 } else if ( type === "activate" && data.newTab.length ) {
5520 ret = this._super( "show", event, {
5521 tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
5522 panel: data.newPanel[ 0 ],
5523 index: data.newTab.closest( "li" ).index()
5531 $.widget( "ui.tabs", $.ui.tabs, {
5532 select: function( index ) {
5533 index = this._getIndex( index );
5534 if ( index === -1 ) {
5535 if ( this.options.collapsible && this.options.selected !== -1 ) {
5536 index = this.options.selected;
5541 this.anchors.eq( index ).trigger( this.options.event + this.eventNamespace );
5550 $.widget( "ui.tabs", $.ui.tabs, {
5552 cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
5554 _create: function() {
5555 var options = this.options,
5557 if ( options.active == null && options.cookie ) {
5558 active = parseInt( this._cookie(), 10 );
5559 if ( active === -1 ) {
5562 options.active = active;
5566 _cookie: function( active ) {
5567 var cookie = [ this.cookie ||
5568 ( this.cookie = this.options.cookie.name || "ui-tabs-" + (++listId) ) ];
5569 if ( arguments.length ) {
5570 cookie.push( active === false ? -1 : active );
5571 cookie.push( this.options.cookie );
5573 return $.cookie.apply( null, cookie );
5575 _refresh: function() {
5577 if ( this.options.cookie ) {
5578 this._cookie( this.options.active, this.options.cookie );
5581 _eventHandler: function() {
5582 this._superApply( arguments );
5583 if ( this.options.cookie ) {
5584 this._cookie( this.options.active, this.options.cookie );
5587 _destroy: function() {
5589 if ( this.options.cookie ) {
5590 this._cookie( null, this.options.cookie );
5598 $.widget( "ui.tabs", $.ui.tabs, {
5599 _trigger: function( type, event, data ) {
5600 var _data = $.extend( {}, data );
5601 if ( type === "load" ) {
5602 _data.panel = _data.panel[ 0 ];
5603 _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
5605 return this._super( type, event, _data );
5610 // The new animation options (show, hide) conflict with the old show callback.
5611 // The old fx option wins over show/hide anyway (always favor back-compat).
5612 // If a user wants to use the new animation API, they must give up the old API.
5613 $.widget( "ui.tabs", $.ui.tabs, {
5615 fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
5618 _getFx: function() {
5620 fx = this.options.fx;
5623 if ( $.isArray( fx ) ) {
5631 return fx ? { show: show, hide: hide } : null;
5634 _toggle: function( event, eventData ) {
5636 toShow = eventData.newPanel,
5637 toHide = eventData.oldPanel,
5641 return this._super( event, eventData );
5644 that.running = true;
5646 function complete() {
5647 that.running = false;
5648 that._trigger( "activate", event, eventData );
5652 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
5654 if ( toShow.length && fx.show ) {
5656 .animate( fx.show, fx.show.duration, function() {
5665 // start out by hiding, then showing, then completing
5666 if ( toHide.length && fx.hide ) {
5667 toHide.animate( fx.hide, fx.hide.duration, function() {
5668 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
5672 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );