1 /*! jQuery UI - v1.9.0 - 2012-10-23
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js
4 * Copyright (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 (($.browser.msie && (/(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" );
126 // support: jQuery <1.8
127 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
128 $.each( [ "Width", "Height" ], function( i, name ) {
129 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
130 type = name.toLowerCase(),
132 innerWidth: $.fn.innerWidth,
133 innerHeight: $.fn.innerHeight,
134 outerWidth: $.fn.outerWidth,
135 outerHeight: $.fn.outerHeight
138 function reduce( elem, size, border, margin ) {
139 $.each( side, function() {
140 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
142 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
145 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
151 $.fn[ "inner" + name ] = function( size ) {
152 if ( size === undefined ) {
153 return orig[ "inner" + name ].call( this );
156 return this.each(function() {
157 $( this ).css( type, reduce( this, size ) + "px" );
161 $.fn[ "outer" + name] = function( size, margin ) {
162 if ( typeof size !== "number" ) {
163 return orig[ "outer" + name ].call( this, size );
166 return this.each(function() {
167 $( this).css( type, reduce( this, size, true, margin ) + "px" );
174 function focusable( element, isTabIndexNotNaN ) {
175 var map, mapName, img,
176 nodeName = element.nodeName.toLowerCase();
177 if ( "area" === nodeName ) {
178 map = element.parentNode;
180 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
183 img = $( "img[usemap=#" + mapName + "]" )[0];
184 return !!img && visible( img );
186 return ( /input|select|textarea|button|object/.test( nodeName ) ?
189 element.href || isTabIndexNotNaN :
191 // the element and all of its ancestors must be visible
195 function visible( element ) {
196 return !$( element ).parents().andSelf().filter(function() {
197 return $.css( this, "visibility" ) === "hidden" ||
198 $.expr.filters.hidden( this );
202 $.extend( $.expr[ ":" ], {
203 data: $.expr.createPseudo ?
204 $.expr.createPseudo(function( dataName ) {
205 return function( elem ) {
206 return !!$.data( elem, dataName );
209 // support: jQuery <1.8
210 function( elem, i, match ) {
211 return !!$.data( elem, match[ 3 ] );
214 focusable: function( element ) {
215 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
218 tabbable: function( element ) {
219 var tabIndex = $.attr( element, "tabindex" ),
220 isTabIndexNaN = isNaN( tabIndex );
221 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
227 var body = document.body,
228 div = body.appendChild( div = document.createElement( "div" ) );
230 // access offsetHeight before setting the style to prevent a layout bug
231 // in IE 9 which causes the element to continue to take up space even
232 // after it is removed from the DOM (#8026)
235 $.extend( div.style, {
242 $.support.minHeight = div.offsetHeight === 100;
243 $.support.selectstart = "onselectstart" in div;
245 // set display to none to avoid a layout bug in IE
246 // http://dev.jquery.com/ticket/4014
247 body.removeChild( div ).style.display = "none";
257 disableSelection: function() {
258 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
259 ".ui-disableSelection", function( event ) {
260 event.preventDefault();
264 enableSelection: function() {
265 return this.unbind( ".ui-disableSelection" );
270 // $.ui.plugin is deprecated. Use the proxy pattern instead.
272 add: function( module, option, set ) {
274 proto = $.ui[ module ].prototype;
276 proto.plugins[ i ] = proto.plugins[ i ] || [];
277 proto.plugins[ i ].push( [ option, set[ i ] ] );
280 call: function( instance, name, args ) {
282 set = instance.plugins[ name ];
283 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
287 for ( i = 0; i < set.length; i++ ) {
288 if ( instance.options[ set[ i ][ 0 ] ] ) {
289 set[ i ][ 1 ].apply( instance.element, args );
295 contains: $.contains,
297 // only used by resizable
298 hasScroll: function( el, a ) {
300 //If overflow is hidden, the element might have extra content, but the user wants to hide it
301 if ( $( el ).css( "overflow" ) === "hidden") {
305 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
308 if ( el[ scroll ] > 0 ) {
312 // TODO: determine which cases actually cause this to happen
313 // if the element doesn't have the scroll set, see if it's possible to
316 has = ( el[ scroll ] > 0 );
321 // these are odd functions, fix the API or move into individual plugins
322 isOverAxis: function( x, reference, size ) {
323 //Determines when x coordinate is over "b" element axis
324 return ( x > reference ) && ( x < ( reference + size ) );
326 isOver: function( y, x, top, left, height, width ) {
327 //Determines when x, y coordinates is over "b" element
328 return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
333 (function( $, undefined ) {
336 slice = Array.prototype.slice,
337 _cleanData = $.cleanData;
338 $.cleanData = function( elems ) {
339 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
341 $( elem ).triggerHandler( "remove" );
342 // http://bugs.jquery.com/ticket/8235
348 $.widget = function( name, base, prototype ) {
349 var fullName, existingConstructor, constructor, basePrototype,
350 namespace = name.split( "." )[ 0 ];
352 name = name.split( "." )[ 1 ];
353 fullName = namespace + "-" + name;
360 // create selector for plugin
361 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
362 return !!$.data( elem, fullName );
365 $[ namespace ] = $[ namespace ] || {};
366 existingConstructor = $[ namespace ][ name ];
367 constructor = $[ namespace ][ name ] = function( options, element ) {
368 // allow instantiation without "new" keyword
369 if ( !this._createWidget ) {
370 return new constructor( options, element );
373 // allow instantiation without initializing for simple inheritance
374 // must use "new" keyword (the code above always passes args)
375 if ( arguments.length ) {
376 this._createWidget( options, element );
379 // extend with the existing constructor to carry over any static properties
380 $.extend( constructor, existingConstructor, {
381 version: prototype.version,
382 // copy the object used to create the prototype in case we need to
383 // redefine the widget later
384 _proto: $.extend( {}, prototype ),
385 // track widgets that inherit from this widget in case this widget is
386 // redefined after a widget inherits from it
387 _childConstructors: []
390 basePrototype = new base();
391 // we need to make the options hash a property directly on the new instance
392 // otherwise we'll modify the options hash on the prototype that we're
394 basePrototype.options = $.widget.extend( {}, basePrototype.options );
395 $.each( prototype, function( prop, value ) {
396 if ( $.isFunction( value ) ) {
397 prototype[ prop ] = (function() {
398 var _super = function() {
399 return base.prototype[ prop ].apply( this, arguments );
401 _superApply = function( args ) {
402 return base.prototype[ prop ].apply( this, args );
405 var __super = this._super,
406 __superApply = this._superApply,
409 this._super = _super;
410 this._superApply = _superApply;
412 returnValue = value.apply( this, arguments );
414 this._super = __super;
415 this._superApply = __superApply;
422 constructor.prototype = $.widget.extend( basePrototype, {
423 // TODO: remove support for widgetEventPrefix
424 // always use the name + a colon as the prefix, e.g., draggable:start
425 // don't prefix for widgets that aren't DOM-based
426 widgetEventPrefix: name
428 constructor: constructor,
429 namespace: namespace,
431 // TODO remove widgetBaseClass, see #8155
432 widgetBaseClass: fullName,
433 widgetFullName: fullName
436 // If this widget is being redefined then we need to find all widgets that
437 // are inheriting from it and redefine all of them so that they inherit from
438 // the new version of this widget. We're essentially trying to replace one
439 // level in the prototype chain.
440 if ( existingConstructor ) {
441 $.each( existingConstructor._childConstructors, function( i, child ) {
442 var childPrototype = child.prototype;
444 // redefine the child widget using the same prototype that was
445 // originally used, but inherit from the new version of the base
446 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
448 // remove the list of existing child constructors from the old constructor
449 // so the old child constructors can be garbage collected
450 delete existingConstructor._childConstructors;
452 base._childConstructors.push( constructor );
455 $.widget.bridge( name, constructor );
458 $.widget.extend = function( target ) {
459 var input = slice.call( arguments, 1 ),
461 inputLength = input.length,
464 for ( ; inputIndex < inputLength; inputIndex++ ) {
465 for ( key in input[ inputIndex ] ) {
466 value = input[ inputIndex ][ key ];
467 if (input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
468 target[ key ] = $.isPlainObject( value ) ? $.widget.extend( {}, target[ key ], value ) : value;
475 $.widget.bridge = function( name, object ) {
476 var fullName = object.prototype.widgetFullName;
477 $.fn[ name ] = function( options ) {
478 var isMethodCall = typeof options === "string",
479 args = slice.call( arguments, 1 ),
482 // allow multiple hashes to be passed on init
483 options = !isMethodCall && args.length ?
484 $.widget.extend.apply( null, [ options ].concat(args) ) :
487 if ( isMethodCall ) {
488 this.each(function() {
490 instance = $.data( this, fullName );
492 return $.error( "cannot call methods on " + name + " prior to initialization; " +
493 "attempted to call method '" + options + "'" );
495 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
496 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
498 methodValue = instance[ options ].apply( instance, args );
499 if ( methodValue !== instance && methodValue !== undefined ) {
500 returnValue = methodValue && methodValue.jquery ?
501 returnValue.pushStack( methodValue.get() ) :
507 this.each(function() {
508 var instance = $.data( this, fullName );
510 instance.option( options || {} )._init();
512 new object( options, this );
521 $.Widget = function( options, element ) {};
522 $.Widget._childConstructors = [];
524 $.Widget.prototype = {
525 widgetName: "widget",
526 widgetEventPrefix: "",
527 defaultElement: "<div>",
534 _createWidget: function( options, element ) {
535 element = $( element || this.defaultElement || this )[ 0 ];
536 this.element = $( element );
538 this.eventNamespace = "." + this.widgetName + this.uuid;
539 this.options = $.widget.extend( {},
541 this._getCreateOptions(),
545 this.hoverable = $();
546 this.focusable = $();
548 if ( element !== this ) {
550 // TODO remove dual storage
551 $.data( element, this.widgetName, this );
552 $.data( element, this.widgetFullName, this );
553 this._on({ remove: "destroy" });
554 this.document = $( element.style ?
555 // element within the document
556 element.ownerDocument :
557 // element is window or document
558 element.document || element );
559 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
563 this._trigger( "create", null, this._getCreateEventData() );
566 _getCreateOptions: $.noop,
567 _getCreateEventData: $.noop,
571 destroy: function() {
573 // we can probably remove the unbind calls in 2.0
574 // all event bindings should go through this._on()
576 .unbind( this.eventNamespace )
578 // TODO remove dual storage
579 .removeData( this.widgetName )
580 .removeData( this.widgetFullName )
581 // support: jquery <1.6.3
582 // http://bugs.jquery.com/ticket/9413
583 .removeData( $.camelCase( this.widgetFullName ) );
585 .unbind( this.eventNamespace )
586 .removeAttr( "aria-disabled" )
588 this.widgetFullName + "-disabled " +
589 "ui-state-disabled" );
591 // clean up events and states
592 this.bindings.unbind( this.eventNamespace );
593 this.hoverable.removeClass( "ui-state-hover" );
594 this.focusable.removeClass( "ui-state-focus" );
602 option: function( key, value ) {
608 if ( arguments.length === 0 ) {
609 // don't return a reference to the internal hash
610 return $.widget.extend( {}, this.options );
613 if ( typeof key === "string" ) {
614 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
616 parts = key.split( "." );
618 if ( parts.length ) {
619 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
620 for ( i = 0; i < parts.length - 1; i++ ) {
621 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
622 curOption = curOption[ parts[ i ] ];
625 if ( value === undefined ) {
626 return curOption[ key ] === undefined ? null : curOption[ key ];
628 curOption[ key ] = value;
630 if ( value === undefined ) {
631 return this.options[ key ] === undefined ? null : this.options[ key ];
633 options[ key ] = value;
637 this._setOptions( options );
641 _setOptions: function( options ) {
644 for ( key in options ) {
645 this._setOption( key, options[ key ] );
650 _setOption: function( key, value ) {
651 this.options[ key ] = value;
653 if ( key === "disabled" ) {
655 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
656 .attr( "aria-disabled", value );
657 this.hoverable.removeClass( "ui-state-hover" );
658 this.focusable.removeClass( "ui-state-focus" );
665 return this._setOption( "disabled", false );
667 disable: function() {
668 return this._setOption( "disabled", true );
671 _on: function( element, handlers ) {
672 // no element argument, shuffle and use this.element
675 element = this.element;
677 // accept selectors, DOM elements
678 element = $( element );
679 this.bindings = this.bindings.add( element );
683 $.each( handlers, function( event, handler ) {
684 function handlerProxy() {
685 // allow widgets to customize the disabled handling
686 // - disabled as an array instead of boolean
687 // - disabled class as method for disabling individual parts
688 if ( instance.options.disabled === true ||
689 $( this ).hasClass( "ui-state-disabled" ) ) {
692 return ( typeof handler === "string" ? instance[ handler ] : handler )
693 .apply( instance, arguments );
696 // copy the guid so direct unbinding works
697 if ( typeof handler !== "string" ) {
698 handlerProxy.guid = handler.guid =
699 handler.guid || handlerProxy.guid || $.guid++;
702 var match = event.match( /^(\w+)\s*(.*)$/ ),
703 eventName = match[1] + instance.eventNamespace,
706 instance.widget().delegate( selector, eventName, handlerProxy );
708 element.bind( eventName, handlerProxy );
713 _off: function( element, eventName ) {
714 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
715 element.unbind( eventName ).undelegate( eventName );
718 _delay: function( handler, delay ) {
719 function handlerProxy() {
720 return ( typeof handler === "string" ? instance[ handler ] : handler )
721 .apply( instance, arguments );
724 return setTimeout( handlerProxy, delay || 0 );
727 _hoverable: function( element ) {
728 this.hoverable = this.hoverable.add( element );
730 mouseenter: function( event ) {
731 $( event.currentTarget ).addClass( "ui-state-hover" );
733 mouseleave: function( event ) {
734 $( event.currentTarget ).removeClass( "ui-state-hover" );
739 _focusable: function( element ) {
740 this.focusable = this.focusable.add( element );
742 focusin: function( event ) {
743 $( event.currentTarget ).addClass( "ui-state-focus" );
745 focusout: function( event ) {
746 $( event.currentTarget ).removeClass( "ui-state-focus" );
751 _trigger: function( type, event, data ) {
753 callback = this.options[ type ];
756 event = $.Event( event );
757 event.type = ( type === this.widgetEventPrefix ?
759 this.widgetEventPrefix + type ).toLowerCase();
760 // the original event may come from any element
761 // so we need to reset the target on the new event
762 event.target = this.element[ 0 ];
764 // copy original event properties over to the new event
765 orig = event.originalEvent;
767 for ( prop in orig ) {
768 if ( !( prop in event ) ) {
769 event[ prop ] = orig[ prop ];
774 this.element.trigger( event, data );
775 return !( $.isFunction( callback ) &&
776 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
777 event.isDefaultPrevented() );
781 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
782 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
783 if ( typeof options === "string" ) {
784 options = { effect: options };
787 effectName = !options ?
789 options === true || typeof options === "number" ?
791 options.effect || defaultEffect;
792 options = options || {};
793 if ( typeof options === "number" ) {
794 options = { duration: options };
796 hasOptions = !$.isEmptyObject( options );
797 options.complete = callback;
798 if ( options.delay ) {
799 element.delay( options.delay );
801 if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) {
802 element[ method ]( options );
803 } else if ( effectName !== method && element[ effectName ] ) {
804 element[ effectName ]( options.duration, options.easing, callback );
806 element.queue(function( next ) {
807 $( this )[ method ]();
809 callback.call( element[ 0 ] );
818 if ( $.uiBackCompat !== false ) {
819 $.Widget.prototype._getCreateOptions = function() {
820 return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
825 (function( $, undefined ) {
827 var mouseHandled = false;
828 $( document ).mouseup( function( e ) {
829 mouseHandled = false;
832 $.widget("ui.mouse", {
835 cancel: 'input,textarea,button,select,option',
839 _mouseInit: function() {
843 .bind('mousedown.'+this.widgetName, function(event) {
844 return that._mouseDown(event);
846 .bind('click.'+this.widgetName, function(event) {
847 if (true === $.data(event.target, that.widgetName + '.preventClickEvent')) {
848 $.removeData(event.target, that.widgetName + '.preventClickEvent');
849 event.stopImmediatePropagation();
854 this.started = false;
857 // TODO: make sure destroying one instance of mouse doesn't mess with
858 // other instances of mouse
859 _mouseDestroy: function() {
860 this.element.unbind('.'+this.widgetName);
861 if ( this._mouseMoveDelegate ) {
863 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
864 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
868 _mouseDown: function(event) {
869 // don't let more than one widget handle mouseStart
870 if( mouseHandled ) { return; }
872 // we may have missed mouseup (out of window)
873 (this._mouseStarted && this._mouseUp(event));
875 this._mouseDownEvent = event;
878 btnIsLeft = (event.which === 1),
879 // event.target.nodeName works around a bug in IE 8 with
880 // disabled inputs (#7620)
881 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
882 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
886 this.mouseDelayMet = !this.options.delay;
887 if (!this.mouseDelayMet) {
888 this._mouseDelayTimer = setTimeout(function() {
889 that.mouseDelayMet = true;
890 }, this.options.delay);
893 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
894 this._mouseStarted = (this._mouseStart(event) !== false);
895 if (!this._mouseStarted) {
896 event.preventDefault();
901 // Click event may never have fired (Gecko & Opera)
902 if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
903 $.removeData(event.target, this.widgetName + '.preventClickEvent');
906 // these delegates are required to keep context
907 this._mouseMoveDelegate = function(event) {
908 return that._mouseMove(event);
910 this._mouseUpDelegate = function(event) {
911 return that._mouseUp(event);
914 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
915 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
917 event.preventDefault();
923 _mouseMove: function(event) {
924 // IE mouseup check - mouseup happened when mouse was out of window
925 if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
926 return this._mouseUp(event);
929 if (this._mouseStarted) {
930 this._mouseDrag(event);
931 return event.preventDefault();
934 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
936 (this._mouseStart(this._mouseDownEvent, event) !== false);
937 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
940 return !this._mouseStarted;
943 _mouseUp: function(event) {
945 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
946 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
948 if (this._mouseStarted) {
949 this._mouseStarted = false;
951 if (event.target === this._mouseDownEvent.target) {
952 $.data(event.target, this.widgetName + '.preventClickEvent', true);
955 this._mouseStop(event);
961 _mouseDistanceMet: function(event) {
963 Math.abs(this._mouseDownEvent.pageX - event.pageX),
964 Math.abs(this._mouseDownEvent.pageY - event.pageY)
965 ) >= this.options.distance
969 _mouseDelayMet: function(event) {
970 return this.mouseDelayMet;
973 // These are placeholder methods, to be overriden by extending plugin
974 _mouseStart: function(event) {},
975 _mouseDrag: function(event) {},
976 _mouseStop: function(event) {},
977 _mouseCapture: function(event) { return true; }
981 (function( $, undefined ) {
985 var cachedScrollbarWidth,
989 rhorizontal = /left|center|right/,
990 rvertical = /top|center|bottom/,
991 roffset = /[\+\-]\d+%?/,
994 _position = $.fn.position;
996 function getOffsets( offsets, width, height ) {
998 parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
999 parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1002 function parseCss( element, property ) {
1003 return parseInt( $.css( element, property ), 10 ) || 0;
1007 scrollbarWidth: function() {
1008 if ( cachedScrollbarWidth !== undefined ) {
1009 return cachedScrollbarWidth;
1012 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1013 innerDiv = div.children()[0];
1015 $( "body" ).append( div );
1016 w1 = innerDiv.offsetWidth;
1017 div.css( "overflow", "scroll" );
1019 w2 = innerDiv.offsetWidth;
1022 w2 = div[0].clientWidth;
1027 return (cachedScrollbarWidth = w1 - w2);
1029 getScrollInfo: function( within ) {
1030 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
1031 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
1032 hasOverflowX = overflowX === "scroll" ||
1033 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1034 hasOverflowY = overflowY === "scroll" ||
1035 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1037 width: hasOverflowX ? $.position.scrollbarWidth() : 0,
1038 height: hasOverflowY ? $.position.scrollbarWidth() : 0
1041 getWithinInfo: function( element ) {
1042 var withinElement = $( element || window ),
1043 isWindow = $.isWindow( withinElement[0] );
1045 element: withinElement,
1047 offset: withinElement.offset() || { left: 0, top: 0 },
1048 scrollLeft: withinElement.scrollLeft(),
1049 scrollTop: withinElement.scrollTop(),
1050 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1051 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1056 $.fn.position = function( options ) {
1057 if ( !options || !options.of ) {
1058 return _position.apply( this, arguments );
1061 // make a copy, we don't want to modify arguments
1062 options = $.extend( {}, options );
1064 var atOffset, targetWidth, targetHeight, targetOffset, basePosition,
1065 target = $( options.of ),
1066 within = $.position.getWithinInfo( options.within ),
1067 scrollInfo = $.position.getScrollInfo( within ),
1068 targetElem = target[0],
1069 collision = ( options.collision || "flip" ).split( " " ),
1072 if ( targetElem.nodeType === 9 ) {
1073 targetWidth = target.width();
1074 targetHeight = target.height();
1075 targetOffset = { top: 0, left: 0 };
1076 } else if ( $.isWindow( targetElem ) ) {
1077 targetWidth = target.width();
1078 targetHeight = target.height();
1079 targetOffset = { top: target.scrollTop(), left: target.scrollLeft() };
1080 } else if ( targetElem.preventDefault ) {
1081 // force left top to allow flipping
1082 options.at = "left top";
1083 targetWidth = targetHeight = 0;
1084 targetOffset = { top: targetElem.pageY, left: targetElem.pageX };
1086 targetWidth = target.outerWidth();
1087 targetHeight = target.outerHeight();
1088 targetOffset = target.offset();
1090 // clone to reuse original targetOffset later
1091 basePosition = $.extend( {}, targetOffset );
1093 // force my and at to have valid horizontal and vertical positions
1094 // if a value is missing or invalid, it will be converted to center
1095 $.each( [ "my", "at" ], function() {
1096 var pos = ( options[ this ] || "" ).split( " " ),
1100 if ( pos.length === 1) {
1101 pos = rhorizontal.test( pos[ 0 ] ) ?
1102 pos.concat( [ "center" ] ) :
1103 rvertical.test( pos[ 0 ] ) ?
1104 [ "center" ].concat( pos ) :
1105 [ "center", "center" ];
1107 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1108 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1110 // calculate offsets
1111 horizontalOffset = roffset.exec( pos[ 0 ] );
1112 verticalOffset = roffset.exec( pos[ 1 ] );
1114 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1115 verticalOffset ? verticalOffset[ 0 ] : 0
1118 // reduce to just the positions without the offsets
1120 rposition.exec( pos[ 0 ] )[ 0 ],
1121 rposition.exec( pos[ 1 ] )[ 0 ]
1125 // normalize collision option
1126 if ( collision.length === 1 ) {
1127 collision[ 1 ] = collision[ 0 ];
1130 if ( options.at[ 0 ] === "right" ) {
1131 basePosition.left += targetWidth;
1132 } else if ( options.at[ 0 ] === "center" ) {
1133 basePosition.left += targetWidth / 2;
1136 if ( options.at[ 1 ] === "bottom" ) {
1137 basePosition.top += targetHeight;
1138 } else if ( options.at[ 1 ] === "center" ) {
1139 basePosition.top += targetHeight / 2;
1142 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1143 basePosition.left += atOffset[ 0 ];
1144 basePosition.top += atOffset[ 1 ];
1146 return this.each(function() {
1147 var collisionPosition, using,
1149 elemWidth = elem.outerWidth(),
1150 elemHeight = elem.outerHeight(),
1151 marginLeft = parseCss( this, "marginLeft" ),
1152 marginTop = parseCss( this, "marginTop" ),
1153 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1154 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1155 position = $.extend( {}, basePosition ),
1156 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1158 if ( options.my[ 0 ] === "right" ) {
1159 position.left -= elemWidth;
1160 } else if ( options.my[ 0 ] === "center" ) {
1161 position.left -= elemWidth / 2;
1164 if ( options.my[ 1 ] === "bottom" ) {
1165 position.top -= elemHeight;
1166 } else if ( options.my[ 1 ] === "center" ) {
1167 position.top -= elemHeight / 2;
1170 position.left += myOffset[ 0 ];
1171 position.top += myOffset[ 1 ];
1173 // if the browser doesn't support fractions, then round for consistent results
1174 if ( !$.support.offsetFractions ) {
1175 position.left = round( position.left );
1176 position.top = round( position.top );
1179 collisionPosition = {
1180 marginLeft: marginLeft,
1181 marginTop: marginTop
1184 $.each( [ "left", "top" ], function( i, dir ) {
1185 if ( $.ui.position[ collision[ i ] ] ) {
1186 $.ui.position[ collision[ i ] ][ dir ]( position, {
1187 targetWidth: targetWidth,
1188 targetHeight: targetHeight,
1189 elemWidth: elemWidth,
1190 elemHeight: elemHeight,
1191 collisionPosition: collisionPosition,
1192 collisionWidth: collisionWidth,
1193 collisionHeight: collisionHeight,
1194 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1203 if ( $.fn.bgiframe ) {
1207 if ( options.using ) {
1208 // adds feedback as second argument to using callback, if present
1209 using = function( props ) {
1210 var left = targetOffset.left - position.left,
1211 right = left + targetWidth - elemWidth,
1212 top = targetOffset.top - position.top,
1213 bottom = top + targetHeight - elemHeight,
1217 left: targetOffset.left,
1218 top: targetOffset.top,
1220 height: targetHeight
1224 left: position.left,
1229 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1230 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1232 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1233 feedback.horizontal = "center";
1235 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1236 feedback.vertical = "middle";
1238 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1239 feedback.important = "horizontal";
1241 feedback.important = "vertical";
1243 options.using.call( this, props, feedback );
1247 elem.offset( $.extend( position, { using: using } ) );
1253 left: function( position, data ) {
1254 var within = data.within,
1255 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1256 outerWidth = within.width,
1257 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1258 overLeft = withinOffset - collisionPosLeft,
1259 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1262 // element is wider than within
1263 if ( data.collisionWidth > outerWidth ) {
1264 // element is initially over the left side of within
1265 if ( overLeft > 0 && overRight <= 0 ) {
1266 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1267 position.left += overLeft - newOverRight;
1268 // element is initially over right side of within
1269 } else if ( overRight > 0 && overLeft <= 0 ) {
1270 position.left = withinOffset;
1271 // element is initially over both left and right sides of within
1273 if ( overLeft > overRight ) {
1274 position.left = withinOffset + outerWidth - data.collisionWidth;
1276 position.left = withinOffset;
1279 // too far left -> align with left edge
1280 } else if ( overLeft > 0 ) {
1281 position.left += overLeft;
1282 // too far right -> align with right edge
1283 } else if ( overRight > 0 ) {
1284 position.left -= overRight;
1285 // adjust based on position and margin
1287 position.left = max( position.left - collisionPosLeft, position.left );
1290 top: function( position, data ) {
1291 var within = data.within,
1292 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1293 outerHeight = data.within.height,
1294 collisionPosTop = position.top - data.collisionPosition.marginTop,
1295 overTop = withinOffset - collisionPosTop,
1296 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1299 // element is taller than within
1300 if ( data.collisionHeight > outerHeight ) {
1301 // element is initially over the top of within
1302 if ( overTop > 0 && overBottom <= 0 ) {
1303 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1304 position.top += overTop - newOverBottom;
1305 // element is initially over bottom of within
1306 } else if ( overBottom > 0 && overTop <= 0 ) {
1307 position.top = withinOffset;
1308 // element is initially over both top and bottom of within
1310 if ( overTop > overBottom ) {
1311 position.top = withinOffset + outerHeight - data.collisionHeight;
1313 position.top = withinOffset;
1316 // too far up -> align with top
1317 } else if ( overTop > 0 ) {
1318 position.top += overTop;
1319 // too far down -> align with bottom edge
1320 } else if ( overBottom > 0 ) {
1321 position.top -= overBottom;
1322 // adjust based on position and margin
1324 position.top = max( position.top - collisionPosTop, position.top );
1329 left: function( position, data ) {
1330 var within = data.within,
1331 withinOffset = within.offset.left + within.scrollLeft,
1332 outerWidth = within.width,
1333 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1334 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1335 overLeft = collisionPosLeft - offsetLeft,
1336 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1337 myOffset = data.my[ 0 ] === "left" ?
1339 data.my[ 0 ] === "right" ?
1342 atOffset = data.at[ 0 ] === "left" ?
1344 data.at[ 0 ] === "right" ?
1347 offset = -2 * data.offset[ 0 ],
1351 if ( overLeft < 0 ) {
1352 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1353 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1354 position.left += myOffset + atOffset + offset;
1357 else if ( overRight > 0 ) {
1358 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1359 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1360 position.left += myOffset + atOffset + offset;
1364 top: function( position, data ) {
1365 var within = data.within,
1366 withinOffset = within.offset.top + within.scrollTop,
1367 outerHeight = within.height,
1368 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1369 collisionPosTop = position.top - data.collisionPosition.marginTop,
1370 overTop = collisionPosTop - offsetTop,
1371 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1372 top = data.my[ 1 ] === "top",
1375 data.my[ 1 ] === "bottom" ?
1378 atOffset = data.at[ 1 ] === "top" ?
1380 data.at[ 1 ] === "bottom" ?
1381 -data.targetHeight :
1383 offset = -2 * data.offset[ 1 ],
1386 if ( overTop < 0 ) {
1387 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1388 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1389 position.top += myOffset + atOffset + offset;
1392 else if ( overBottom > 0 ) {
1393 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1394 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1395 position.top += myOffset + atOffset + offset;
1402 $.ui.position.flip.left.apply( this, arguments );
1403 $.ui.position.fit.left.apply( this, arguments );
1406 $.ui.position.flip.top.apply( this, arguments );
1407 $.ui.position.fit.top.apply( this, arguments );
1412 // fraction support test
1414 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1415 body = document.getElementsByTagName( "body" )[ 0 ],
1416 div = document.createElement( "div" );
1418 //Create a "fake body" for testing based on method used in jQuery.support
1419 testElement = document.createElement( body ? "div" : "body" );
1420 testElementStyle = {
1421 visibility: "hidden",
1429 $.extend( testElementStyle, {
1430 position: "absolute",
1435 for ( i in testElementStyle ) {
1436 testElement.style[ i ] = testElementStyle[ i ];
1438 testElement.appendChild( div );
1439 testElementParent = body || document.documentElement;
1440 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1442 div.style.cssText = "position: absolute; left: 10.7432222px;";
1444 offsetLeft = $( div ).offset().left;
1445 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1447 testElement.innerHTML = "";
1448 testElementParent.removeChild( testElement );
1452 if ( $.uiBackCompat !== false ) {
1455 var _position = $.fn.position;
1456 $.fn.position = function( options ) {
1457 if ( !options || !options.offset ) {
1458 return _position.call( this, options );
1460 var offset = options.offset.split( " " ),
1461 at = options.at.split( " " );
1462 if ( offset.length === 1 ) {
1463 offset[ 1 ] = offset[ 0 ];
1465 if ( /^\d/.test( offset[ 0 ] ) ) {
1466 offset[ 0 ] = "+" + offset[ 0 ];
1468 if ( /^\d/.test( offset[ 1 ] ) ) {
1469 offset[ 1 ] = "+" + offset[ 1 ];
1471 if ( at.length === 1 ) {
1472 if ( /left|center|right/.test( at[ 0 ] ) ) {
1479 return _position.call( this, $.extend( options, {
1480 at: at[ 0 ] + offset[ 0 ] + " " + at[ 1 ] + offset[ 1 ],
1488 (function( $, undefined ) {
1490 $.widget("ui.draggable", $.ui.mouse, {
1492 widgetEventPrefix: "drag",
1497 connectToSortable: false,
1506 refreshPositions: false,
1508 revertDuration: 500,
1511 scrollSensitivity: 20,
1519 _create: function() {
1521 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
1522 this.element[0].style.position = 'relative';
1524 (this.options.addClasses && this.element.addClass("ui-draggable"));
1525 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
1531 _destroy: function() {
1532 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1533 this._mouseDestroy();
1536 _mouseCapture: function(event) {
1538 var o = this.options;
1540 // among others, prevent a drag on a resizable-handle
1541 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
1544 //Quit if we're not on a valid handle
1545 this.handle = this._getHandle(event);
1549 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1550 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1552 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1553 position: "absolute", opacity: "0.001", zIndex: 1000
1555 .css($(this).offset())
1563 _mouseStart: function(event) {
1565 var o = this.options;
1567 //Create and append the visible helper
1568 this.helper = this._createHelper(event);
1570 this.helper.addClass("ui-draggable-dragging");
1572 //Cache the helper size
1573 this._cacheHelperProportions();
1575 //If ddmanager is used for droppables, set the global draggable
1577 $.ui.ddmanager.current = this;
1580 * - Position generation -
1581 * This block generates everything position related - it's the core of draggables.
1584 //Cache the margins of the original element
1585 this._cacheMargins();
1587 //Store the helper's css position
1588 this.cssPosition = this.helper.css("position");
1589 this.scrollParent = this.helper.scrollParent();
1591 //The element's absolute position on the page minus margins
1592 this.offset = this.positionAbs = this.element.offset();
1594 top: this.offset.top - this.margins.top,
1595 left: this.offset.left - this.margins.left
1598 $.extend(this.offset, {
1599 click: { //Where the click happened, relative to the element
1600 left: event.pageX - this.offset.left,
1601 top: event.pageY - this.offset.top
1603 parent: this._getParentOffset(),
1604 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1607 //Generate the original position
1608 this.originalPosition = this.position = this._generatePosition(event);
1609 this.originalPageX = event.pageX;
1610 this.originalPageY = event.pageY;
1612 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1613 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1615 //Set a containment if given in the options
1617 this._setContainment();
1619 //Trigger event + callbacks
1620 if(this._trigger("start", event) === false) {
1625 //Recache the helper size
1626 this._cacheHelperProportions();
1628 //Prepare the droppable offsets
1629 if ($.ui.ddmanager && !o.dropBehaviour)
1630 $.ui.ddmanager.prepareOffsets(this, event);
1633 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1635 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1636 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
1641 _mouseDrag: function(event, noPropagation) {
1643 //Compute the helpers position
1644 this.position = this._generatePosition(event);
1645 this.positionAbs = this._convertPositionTo("absolute");
1647 //Call plugins and callbacks and use the resulting position if something is returned
1648 if (!noPropagation) {
1649 var ui = this._uiHash();
1650 if(this._trigger('drag', event, ui) === false) {
1654 this.position = ui.position;
1657 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
1658 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
1659 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
1664 _mouseStop: function(event) {
1666 //If we are using droppables, inform the manager about the drop
1667 var dropped = false;
1668 if ($.ui.ddmanager && !this.options.dropBehaviour)
1669 dropped = $.ui.ddmanager.drop(this, event);
1671 //if a drop comes from outside (a sortable)
1673 dropped = this.dropped;
1674 this.dropped = false;
1677 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1678 var element = this.element[0], elementInDom = false;
1679 while ( element && (element = element.parentNode) ) {
1680 if (element == document ) {
1681 elementInDom = true;
1684 if ( !elementInDom && this.options.helper === "original" )
1687 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))) {
1689 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1690 if(that._trigger("stop", event) !== false) {
1695 if(this._trigger("stop", event) !== false) {
1703 _mouseUp: function(event) {
1704 //Remove frame helpers
1705 $("div.ui-draggable-iframeFix").each(function() {
1706 this.parentNode.removeChild(this);
1709 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1710 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
1712 return $.ui.mouse.prototype._mouseUp.call(this, event);
1715 cancel: function() {
1717 if(this.helper.is(".ui-draggable-dragging")) {
1727 _getHandle: function(event) {
1729 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1730 $(this.options.handle, this.element)
1734 if(this == event.target) handle = true;
1741 _createHelper: function(event) {
1743 var o = this.options;
1744 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
1746 if(!helper.parents('body').length)
1747 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
1749 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
1750 helper.css("position", "absolute");
1756 _adjustOffsetFromHelper: function(obj) {
1757 if (typeof obj == 'string') {
1758 obj = obj.split(' ');
1760 if ($.isArray(obj)) {
1761 obj = {left: +obj[0], top: +obj[1] || 0};
1763 if ('left' in obj) {
1764 this.offset.click.left = obj.left + this.margins.left;
1766 if ('right' in obj) {
1767 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1770 this.offset.click.top = obj.top + this.margins.top;
1772 if ('bottom' in obj) {
1773 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1777 _getParentOffset: function() {
1779 //Get the offsetParent and cache its position
1780 this.offsetParent = this.helper.offsetParent();
1781 var po = this.offsetParent.offset();
1783 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1784 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1785 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1786 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1787 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1788 po.left += this.scrollParent.scrollLeft();
1789 po.top += this.scrollParent.scrollTop();
1792 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1793 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
1794 po = { top: 0, left: 0 };
1797 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1798 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1803 _getRelativeOffset: function() {
1805 if(this.cssPosition == "relative") {
1806 var p = this.element.position();
1808 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1809 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1812 return { top: 0, left: 0 };
1817 _cacheMargins: function() {
1819 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1820 top: (parseInt(this.element.css("marginTop"),10) || 0),
1821 right: (parseInt(this.element.css("marginRight"),10) || 0),
1822 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1826 _cacheHelperProportions: function() {
1827 this.helperProportions = {
1828 width: this.helper.outerWidth(),
1829 height: this.helper.outerHeight()
1833 _setContainment: function() {
1835 var o = this.options;
1836 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
1837 if(o.containment == 'document' || o.containment == 'window') this.containment = [
1838 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1839 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1840 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
1841 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1844 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
1845 var c = $(o.containment);
1846 var ce = c[0]; if(!ce) return;
1847 var co = c.offset();
1848 var over = ($(ce).css("overflow") != 'hidden');
1850 this.containment = [
1851 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1852 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1853 (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,
1854 (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
1856 this.relative_container = c;
1858 } else if(o.containment.constructor == Array) {
1859 this.containment = o.containment;
1864 _convertPositionTo: function(d, pos) {
1866 if(!pos) pos = this.position;
1867 var mod = d == "absolute" ? 1 : -1;
1868 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);
1872 pos.top // The absolute mouse position
1873 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1874 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
1875 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1878 pos.left // The absolute mouse position
1879 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1880 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
1881 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1887 _generatePosition: function(event) {
1889 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);
1890 var pageX = event.pageX;
1891 var pageY = event.pageY;
1894 * - Position constraining -
1895 * Constrain the position to a mix of grid, containment.
1898 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1900 if(this.containment) {
1901 if (this.relative_container){
1902 var co = this.relative_container.offset();
1903 containment = [ this.containment[0] + co.left,
1904 this.containment[1] + co.top,
1905 this.containment[2] + co.left,
1906 this.containment[3] + co.top ];
1909 containment = this.containment;
1912 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
1913 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
1914 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
1915 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
1919 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1920 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1921 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;
1923 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1924 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;
1931 pageY // The absolute mouse position
1932 - this.offset.click.top // Click offset (relative to the element)
1933 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
1934 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
1935 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1938 pageX // The absolute mouse position
1939 - this.offset.click.left // Click offset (relative to the element)
1940 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
1941 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
1942 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1948 _clear: function() {
1949 this.helper.removeClass("ui-draggable-dragging");
1950 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
1951 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1953 this.cancelHelperRemoval = false;
1956 // From now on bulk stuff - mainly helpers
1958 _trigger: function(type, event, ui) {
1959 ui = ui || this._uiHash();
1960 $.ui.plugin.call(this, type, [event, ui]);
1961 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
1962 return $.Widget.prototype._trigger.call(this, type, event, ui);
1967 _uiHash: function(event) {
1969 helper: this.helper,
1970 position: this.position,
1971 originalPosition: this.originalPosition,
1972 offset: this.positionAbs
1978 $.ui.plugin.add("draggable", "connectToSortable", {
1979 start: function(event, ui) {
1981 var inst = $(this).data("draggable"), o = inst.options,
1982 uiSortable = $.extend({}, ui, { item: inst.element });
1983 inst.sortables = [];
1984 $(o.connectToSortable).each(function() {
1985 var sortable = $.data(this, 'sortable');
1986 if (sortable && !sortable.options.disabled) {
1987 inst.sortables.push({
1989 shouldRevert: sortable.options.revert
1991 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).
1992 sortable._trigger("activate", event, uiSortable);
1997 stop: function(event, ui) {
1999 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
2000 var inst = $(this).data("draggable"),
2001 uiSortable = $.extend({}, ui, { item: inst.element });
2003 $.each(inst.sortables, function() {
2004 if(this.instance.isOver) {
2006 this.instance.isOver = 0;
2008 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
2009 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
2011 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
2012 if(this.shouldRevert) this.instance.options.revert = true;
2014 //Trigger the stop of the sortable
2015 this.instance._mouseStop(event);
2017 this.instance.options.helper = this.instance.options._helper;
2019 //If the helper has been the original item, restore properties in the sortable
2020 if(inst.options.helper == 'original')
2021 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
2024 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
2025 this.instance._trigger("deactivate", event, uiSortable);
2031 drag: function(event, ui) {
2033 var inst = $(this).data("draggable"), that = this;
2035 var checkPos = function(o) {
2036 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
2037 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
2038 var itemHeight = o.height, itemWidth = o.width;
2039 var itemTop = o.top, itemLeft = o.left;
2041 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
2044 $.each(inst.sortables, function(i) {
2046 //Copy over some variables to allow calling the sortable's native _intersectsWith
2047 this.instance.positionAbs = inst.positionAbs;
2048 this.instance.helperProportions = inst.helperProportions;
2049 this.instance.offset.click = inst.offset.click;
2051 if(this.instance._intersectsWith(this.instance.containerCache)) {
2053 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
2054 if(!this.instance.isOver) {
2056 this.instance.isOver = 1;
2057 //Now we fake the start of dragging for the sortable instance,
2058 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
2059 //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)
2060 this.instance.currentItem = $(that).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
2061 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
2062 this.instance.options.helper = function() { return ui.helper[0]; };
2064 event.target = this.instance.currentItem[0];
2065 this.instance._mouseCapture(event, true);
2066 this.instance._mouseStart(event, true, true);
2068 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
2069 this.instance.offset.click.top = inst.offset.click.top;
2070 this.instance.offset.click.left = inst.offset.click.left;
2071 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
2072 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
2074 inst._trigger("toSortable", event);
2075 inst.dropped = this.instance.element; //draggable revert needs that
2076 //hack so receive/update callbacks work (mostly)
2077 inst.currentItem = inst.element;
2078 this.instance.fromOutside = inst;
2082 //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
2083 if(this.instance.currentItem) this.instance._mouseDrag(event);
2087 //If it doesn't intersect with the sortable, and it intersected before,
2088 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
2089 if(this.instance.isOver) {
2091 this.instance.isOver = 0;
2092 this.instance.cancelHelperRemoval = true;
2094 //Prevent reverting on this forced stop
2095 this.instance.options.revert = false;
2097 // The out event needs to be triggered independently
2098 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
2100 this.instance._mouseStop(event, true);
2101 this.instance.options.helper = this.instance.options._helper;
2103 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
2104 this.instance.currentItem.remove();
2105 if(this.instance.placeholder) this.instance.placeholder.remove();
2107 inst._trigger("fromSortable", event);
2108 inst.dropped = false; //draggable revert needs that
2118 $.ui.plugin.add("draggable", "cursor", {
2119 start: function(event, ui) {
2120 var t = $('body'), o = $(this).data('draggable').options;
2121 if (t.css("cursor")) o._cursor = t.css("cursor");
2122 t.css("cursor", o.cursor);
2124 stop: function(event, ui) {
2125 var o = $(this).data('draggable').options;
2126 if (o._cursor) $('body').css("cursor", o._cursor);
2130 $.ui.plugin.add("draggable", "opacity", {
2131 start: function(event, ui) {
2132 var t = $(ui.helper), o = $(this).data('draggable').options;
2133 if(t.css("opacity")) o._opacity = t.css("opacity");
2134 t.css('opacity', o.opacity);
2136 stop: function(event, ui) {
2137 var o = $(this).data('draggable').options;
2138 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
2142 $.ui.plugin.add("draggable", "scroll", {
2143 start: function(event, ui) {
2144 var i = $(this).data("draggable");
2145 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
2147 drag: function(event, ui) {
2149 var i = $(this).data("draggable"), o = i.options, scrolled = false;
2151 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
2153 if(!o.axis || o.axis != 'x') {
2154 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
2155 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
2156 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
2157 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
2160 if(!o.axis || o.axis != 'y') {
2161 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
2162 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
2163 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
2164 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
2169 if(!o.axis || o.axis != 'x') {
2170 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
2171 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2172 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
2173 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2176 if(!o.axis || o.axis != 'y') {
2177 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
2178 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2179 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
2180 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2185 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
2186 $.ui.ddmanager.prepareOffsets(i, event);
2191 $.ui.plugin.add("draggable", "snap", {
2192 start: function(event, ui) {
2194 var i = $(this).data("draggable"), o = i.options;
2195 i.snapElements = [];
2197 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
2198 var $t = $(this); var $o = $t.offset();
2199 if(this != i.element[0]) i.snapElements.push({
2201 width: $t.outerWidth(), height: $t.outerHeight(),
2202 top: $o.top, left: $o.left
2207 drag: function(event, ui) {
2209 var inst = $(this).data("draggable"), o = inst.options;
2210 var d = o.snapTolerance;
2212 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2213 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2215 for (var i = inst.snapElements.length - 1; i >= 0; i--){
2217 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
2218 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
2220 //Yes, I know, this is insane ;)
2221 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))) {
2222 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 })));
2223 inst.snapElements[i].snapping = false;
2227 if(o.snapMode != 'inner') {
2228 var ts = Math.abs(t - y2) <= d;
2229 var bs = Math.abs(b - y1) <= d;
2230 var ls = Math.abs(l - x2) <= d;
2231 var rs = Math.abs(r - x1) <= d;
2232 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2233 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
2234 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
2235 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
2238 var first = (ts || bs || ls || rs);
2240 if(o.snapMode != 'outer') {
2241 var ts = Math.abs(t - y1) <= d;
2242 var bs = Math.abs(b - y2) <= d;
2243 var ls = Math.abs(l - x1) <= d;
2244 var rs = Math.abs(r - x2) <= d;
2245 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
2246 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2247 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
2248 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
2251 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
2252 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2253 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2260 $.ui.plugin.add("draggable", "stack", {
2261 start: function(event, ui) {
2263 var o = $(this).data("draggable").options;
2265 var group = $.makeArray($(o.stack)).sort(function(a,b) {
2266 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
2268 if (!group.length) { return; }
2270 var min = parseInt(group[0].style.zIndex) || 0;
2271 $(group).each(function(i) {
2272 this.style.zIndex = min + i;
2275 this[0].style.zIndex = min + group.length;
2280 $.ui.plugin.add("draggable", "zIndex", {
2281 start: function(event, ui) {
2282 var t = $(ui.helper), o = $(this).data("draggable").options;
2283 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
2284 t.css('zIndex', o.zIndex);
2286 stop: function(event, ui) {
2287 var o = $(this).data("draggable").options;
2288 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
2293 (function( $, undefined ) {
2295 $.widget("ui.droppable", {
2297 widgetEventPrefix: "drop",
2305 tolerance: 'intersect'
2307 _create: function() {
2309 var o = this.options, accept = o.accept;
2310 this.isover = 0; this.isout = 1;
2312 this.accept = $.isFunction(accept) ? accept : function(d) {
2313 return d.is(accept);
2316 //Store the droppable's proportions
2317 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
2319 // Add the reference and positions to the manager
2320 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
2321 $.ui.ddmanager.droppables[o.scope].push(this);
2323 (o.addClasses && this.element.addClass("ui-droppable"));
2327 _destroy: function() {
2328 var drop = $.ui.ddmanager.droppables[this.options.scope];
2329 for ( var i = 0; i < drop.length; i++ )
2330 if ( drop[i] == this )
2333 this.element.removeClass("ui-droppable ui-droppable-disabled");
2336 _setOption: function(key, value) {
2338 if(key == 'accept') {
2339 this.accept = $.isFunction(value) ? value : function(d) {
2343 $.Widget.prototype._setOption.apply(this, arguments);
2346 _activate: function(event) {
2347 var draggable = $.ui.ddmanager.current;
2348 if(this.options.activeClass) this.element.addClass(this.options.activeClass);
2349 (draggable && this._trigger('activate', event, this.ui(draggable)));
2352 _deactivate: function(event) {
2353 var draggable = $.ui.ddmanager.current;
2354 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
2355 (draggable && this._trigger('deactivate', event, this.ui(draggable)));
2358 _over: function(event) {
2360 var draggable = $.ui.ddmanager.current;
2361 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
2363 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2364 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
2365 this._trigger('over', event, this.ui(draggable));
2370 _out: function(event) {
2372 var draggable = $.ui.ddmanager.current;
2373 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
2375 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2376 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
2377 this._trigger('out', event, this.ui(draggable));
2382 _drop: function(event,custom) {
2384 var draggable = custom || $.ui.ddmanager.current;
2385 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
2387 var childrenIntersection = false;
2388 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
2389 var inst = $.data(this, 'droppable');
2392 && !inst.options.disabled
2393 && inst.options.scope == draggable.options.scope
2394 && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
2395 && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2396 ) { childrenIntersection = true; return false; }
2398 if(childrenIntersection) return false;
2400 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2401 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
2402 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
2403 this._trigger('drop', event, this.ui(draggable));
2404 return this.element;
2413 draggable: (c.currentItem || c.element),
2415 position: c.position,
2416 offset: c.positionAbs
2422 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2424 if (!droppable.offset) return false;
2426 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2427 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
2428 var l = droppable.offset.left, r = l + droppable.proportions.width,
2429 t = droppable.offset.top, b = t + droppable.proportions.height;
2431 switch (toleranceMode) {
2433 return (l <= x1 && x2 <= r
2434 && t <= y1 && y2 <= b);
2437 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
2438 && x2 - (draggable.helperProportions.width / 2) < r // Left Half
2439 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
2440 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2443 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
2444 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
2445 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
2450 (y1 >= t && y1 <= b) || // Top edge touching
2451 (y2 >= t && y2 <= b) || // Bottom edge touching
2452 (y1 < t && y2 > b) // Surrounded vertically
2454 (x1 >= l && x1 <= r) || // Left edge touching
2455 (x2 >= l && x2 <= r) || // Right edge touching
2456 (x1 < l && x2 > r) // Surrounded horizontally
2467 This manager tracks offsets of draggables and droppables
2471 droppables: { 'default': [] },
2472 prepareOffsets: function(t, event) {
2474 var m = $.ui.ddmanager.droppables[t.options.scope] || [];
2475 var type = event ? event.type : null; // workaround for #2317
2476 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
2478 droppablesLoop: for (var i = 0; i < m.length; i++) {
2480 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
2481 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
2482 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
2484 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
2486 m[i].offset = m[i].element.offset();
2487 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2492 drop: function(draggable, event) {
2494 var dropped = false;
2495 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2497 if(!this.options) return;
2498 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
2499 dropped = this._drop.call(this, event) || dropped;
2501 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2502 this.isout = 1; this.isover = 0;
2503 this._deactivate.call(this, event);
2510 dragStart: function( draggable, event ) {
2511 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2512 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2513 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2516 drag: function(draggable, event) {
2518 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2519 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
2521 //Run through all droppables and check their positions based on specific tolerance options
2522 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2524 if(this.options.disabled || this.greedyChild || !this.visible) return;
2525 var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
2527 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
2531 if (this.options.greedy) {
2532 // find droppable parents with same scope
2533 var scope = this.options.scope;
2534 var parent = this.element.parents(':data(droppable)').filter(function () {
2535 return $.data(this, 'droppable').options.scope === scope;
2538 if (parent.length) {
2539 parentInstance = $.data(parent[0], 'droppable');
2540 parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
2544 // we just moved into a greedy child
2545 if (parentInstance && c == 'isover') {
2546 parentInstance['isover'] = 0;
2547 parentInstance['isout'] = 1;
2548 parentInstance._out.call(parentInstance, event);
2551 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
2552 this[c == "isover" ? "_over" : "_out"].call(this, event);
2554 // we just moved out of a greedy child
2555 if (parentInstance && c == 'isout') {
2556 parentInstance['isout'] = 0;
2557 parentInstance['isover'] = 1;
2558 parentInstance._over.call(parentInstance, event);
2563 dragStop: function( draggable, event ) {
2564 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2565 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2566 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
2571 (function( $, undefined ) {
2573 $.widget("ui.resizable", $.ui.mouse, {
2575 widgetEventPrefix: "resize",
2579 animateDuration: "slow",
2580 animateEasing: "swing",
2594 _create: function() {
2596 var that = this, o = this.options;
2597 this.element.addClass("ui-resizable");
2600 _aspectRatio: !!(o.aspectRatio),
2601 aspectRatio: o.aspectRatio,
2602 originalElement: this.element,
2603 _proportionallyResizeElements: [],
2604 _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
2607 //Wrap the element if it cannot hold child nodes
2608 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2610 //Create a wrapper element and set the wrapper to the new current internal element
2612 $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
2613 position: this.element.css('position'),
2614 width: this.element.outerWidth(),
2615 height: this.element.outerHeight(),
2616 top: this.element.css('top'),
2617 left: this.element.css('left')
2621 //Overwrite the original this.element
2622 this.element = this.element.parent().data(
2623 "resizable", this.element.data('resizable')
2626 this.elementIsWrapper = true;
2628 //Move margins to the wrapper
2629 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2630 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2632 //Prevent Safari textarea resize
2633 this.originalResizeStyle = this.originalElement.css('resize');
2634 this.originalElement.css('resize', 'none');
2636 //Push the actual element to our proportionallyResize internal array
2637 this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
2639 // avoid IE jump (hard set the margin)
2640 this.originalElement.css({ margin: this.originalElement.css('margin') });
2642 // fix handlers offset
2643 this._proportionallyResize();
2647 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' });
2648 if(this.handles.constructor == String) {
2650 if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
2651 var n = this.handles.split(","); this.handles = {};
2653 for(var i = 0; i < n.length; i++) {
2655 var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
2656 var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
2658 // Apply zIndex to all handles - see #7960
2659 axis.css({ zIndex: o.zIndex });
2661 //TODO : What's going on here?
2662 if ('se' == handle) {
2663 axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
2666 //Insert into internal handles object and append to element
2667 this.handles[handle] = '.ui-resizable-'+handle;
2668 this.element.append(axis);
2673 this._renderAxis = function(target) {
2675 target = target || this.element;
2677 for(var i in this.handles) {
2679 if(this.handles[i].constructor == String)
2680 this.handles[i] = $(this.handles[i], this.element).show();
2682 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2683 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2685 var axis = $(this.handles[i], this.element), padWrapper = 0;
2687 //Checking the correct pad and border
2688 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2690 //The padding type i have to apply...
2691 var padPos = [ 'padding',
2692 /ne|nw|n/.test(i) ? 'Top' :
2693 /se|sw|s/.test(i) ? 'Bottom' :
2694 /^e$/.test(i) ? 'Right' : 'Left' ].join("");
2696 target.css(padPos, padWrapper);
2698 this._proportionallyResize();
2702 //TODO: What's that good for? There's not anything to be executed left
2703 if(!$(this.handles[i]).length)
2709 //TODO: make renderAxis a prototype function
2710 this._renderAxis(this.element);
2712 this._handles = $('.ui-resizable-handle', this.element)
2713 .disableSelection();
2715 //Matching axis name
2716 this._handles.mouseover(function() {
2717 if (!that.resizing) {
2719 var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2720 //Axis, default = se
2721 that.axis = axis && axis[1] ? axis[1] : 'se';
2725 //If we want to auto hide the elements
2727 this._handles.hide();
2729 .addClass("ui-resizable-autohide")
2730 .mouseenter(function() {
2731 if (o.disabled) return;
2732 $(this).removeClass("ui-resizable-autohide");
2733 that._handles.show();
2735 .mouseleave(function(){
2736 if (o.disabled) return;
2737 if (!that.resizing) {
2738 $(this).addClass("ui-resizable-autohide");
2739 that._handles.hide();
2744 //Initialize the mouse interaction
2749 _destroy: function() {
2751 this._mouseDestroy();
2753 var _destroy = function(exp) {
2754 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2755 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
2758 //TODO: Unwrap at same DOM position
2759 if (this.elementIsWrapper) {
2760 _destroy(this.element);
2761 var wrapper = this.element;
2763 this.originalElement.css({
2764 position: wrapper.css('position'),
2765 width: wrapper.outerWidth(),
2766 height: wrapper.outerHeight(),
2767 top: wrapper.css('top'),
2768 left: wrapper.css('left')
2773 this.originalElement.css('resize', this.originalResizeStyle);
2774 _destroy(this.originalElement);
2779 _mouseCapture: function(event) {
2781 for (var i in this.handles) {
2782 if ($(this.handles[i])[0] == event.target) {
2787 return !this.options.disabled && handle;
2790 _mouseStart: function(event) {
2792 var o = this.options, iniPos = this.element.position(), el = this.element;
2794 this.resizing = true;
2795 this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
2797 // bugfix for http://dev.jquery.com/ticket/1749
2798 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
2799 el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
2802 this._renderProxy();
2804 var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
2806 if (o.containment) {
2807 curleft += $(o.containment).scrollLeft() || 0;
2808 curtop += $(o.containment).scrollTop() || 0;
2811 //Store needed variables
2812 this.offset = this.helper.offset();
2813 this.position = { left: curleft, top: curtop };
2814 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2815 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2816 this.originalPosition = { left: curleft, top: curtop };
2817 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2818 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2821 this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2823 var cursor = $('.ui-resizable-' + this.axis).css('cursor');
2824 $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
2826 el.addClass("ui-resizable-resizing");
2827 this._propagate("start", event);
2831 _mouseDrag: function(event) {
2833 //Increase performance, avoid regex
2834 var el = this.helper, o = this.options, props = {},
2835 that = this, smp = this.originalMousePosition, a = this.axis;
2837 var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
2838 var trigger = this._change[a];
2839 if (!trigger) return false;
2841 // Calculate the attrs that will be change
2842 var data = trigger.apply(this, [event, dx, dy]);
2844 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2845 this._updateVirtualBoundaries(event.shiftKey);
2846 if (this._aspectRatio || event.shiftKey)
2847 data = this._updateRatio(data, event);
2849 data = this._respectSize(data, event);
2851 // plugins callbacks need to be called first
2852 this._propagate("resize", event);
2855 top: this.position.top + "px", left: this.position.left + "px",
2856 width: this.size.width + "px", height: this.size.height + "px"
2859 if (!this._helper && this._proportionallyResizeElements.length)
2860 this._proportionallyResize();
2862 this._updateCache(data);
2864 // calling the user callback at the end
2865 this._trigger('resize', event, this.ui());
2870 _mouseStop: function(event) {
2872 this.resizing = false;
2873 var o = this.options, that = this;
2876 var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2877 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2878 soffsetw = ista ? 0 : that.sizeDiff.width;
2880 var s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) },
2881 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
2882 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
2885 this.element.css($.extend(s, { top: top, left: left }));
2887 that.helper.height(that.size.height);
2888 that.helper.width(that.size.width);
2890 if (this._helper && !o.animate) this._proportionallyResize();
2893 $('body').css('cursor', 'auto');
2895 this.element.removeClass("ui-resizable-resizing");
2897 this._propagate("stop", event);
2899 if (this._helper) this.helper.remove();
2904 _updateVirtualBoundaries: function(forceAspectRatio) {
2905 var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
2908 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2909 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2910 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2911 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2914 if(this._aspectRatio || forceAspectRatio) {
2915 // We want to create an enclosing box whose aspect ration is the requested one
2916 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2917 pMinWidth = b.minHeight * this.aspectRatio;
2918 pMinHeight = b.minWidth / this.aspectRatio;
2919 pMaxWidth = b.maxHeight * this.aspectRatio;
2920 pMaxHeight = b.maxWidth / this.aspectRatio;
2922 if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
2923 if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
2924 if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
2925 if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
2927 this._vBoundaries = b;
2930 _updateCache: function(data) {
2931 var o = this.options;
2932 this.offset = this.helper.offset();
2933 if (isNumber(data.left)) this.position.left = data.left;
2934 if (isNumber(data.top)) this.position.top = data.top;
2935 if (isNumber(data.height)) this.size.height = data.height;
2936 if (isNumber(data.width)) this.size.width = data.width;
2939 _updateRatio: function(data, event) {
2941 var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
2943 if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
2944 else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
2947 data.left = cpos.left + (csize.width - data.width);
2951 data.top = cpos.top + (csize.height - data.height);
2952 data.left = cpos.left + (csize.width - data.width);
2958 _respectSize: function(data, event) {
2960 var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
2961 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2962 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
2964 if (isminw) data.width = o.minWidth;
2965 if (isminh) data.height = o.minHeight;
2966 if (ismaxw) data.width = o.maxWidth;
2967 if (ismaxh) data.height = o.maxHeight;
2969 var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
2970 var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2972 if (isminw && cw) data.left = dw - o.minWidth;
2973 if (ismaxw && cw) data.left = dw - o.maxWidth;
2974 if (isminh && ch) data.top = dh - o.minHeight;
2975 if (ismaxh && ch) data.top = dh - o.maxHeight;
2977 // fixing jump error on top/left - bug #2330
2978 var isNotwh = !data.width && !data.height;
2979 if (isNotwh && !data.left && data.top) data.top = null;
2980 else if (isNotwh && !data.top && data.left) data.left = null;
2985 _proportionallyResize: function() {
2987 var o = this.options;
2988 if (!this._proportionallyResizeElements.length) return;
2989 var element = this.helper || this.element;
2991 for (var i=0; i < this._proportionallyResizeElements.length; i++) {
2993 var prel = this._proportionallyResizeElements[i];
2995 if (!this.borderDif) {
2996 var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
2997 p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
2999 this.borderDif = $.map(b, function(v, i) {
3000 var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
3001 return border + padding;
3006 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
3007 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
3014 _renderProxy: function() {
3016 var el = this.element, o = this.options;
3017 this.elementOffset = el.offset();
3021 this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
3023 // fix ie6 offset TODO: This seems broken
3024 var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
3025 pxyoffset = ( ie6 ? 2 : -1 );
3027 this.helper.addClass(this._helper).css({
3028 width: this.element.outerWidth() + pxyoffset,
3029 height: this.element.outerHeight() + pxyoffset,
3030 position: 'absolute',
3031 left: this.elementOffset.left - ie6offset +'px',
3032 top: this.elementOffset.top - ie6offset +'px',
3033 zIndex: ++o.zIndex //TODO: Don't modify option
3038 .disableSelection();
3041 this.helper = this.element;
3047 e: function(event, dx, dy) {
3048 return { width: this.originalSize.width + dx };
3050 w: function(event, dx, dy) {
3051 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
3052 return { left: sp.left + dx, width: cs.width - dx };
3054 n: function(event, dx, dy) {
3055 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
3056 return { top: sp.top + dy, height: cs.height - dy };
3058 s: function(event, dx, dy) {
3059 return { height: this.originalSize.height + dy };
3061 se: function(event, dx, dy) {
3062 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3064 sw: function(event, dx, dy) {
3065 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3067 ne: function(event, dx, dy) {
3068 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3070 nw: function(event, dx, dy) {
3071 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3075 _propagate: function(n, event) {
3076 $.ui.plugin.call(this, n, [event, this.ui()]);
3077 (n != "resize" && this._trigger(n, event, this.ui()));
3084 originalElement: this.originalElement,
3085 element: this.element,
3086 helper: this.helper,
3087 position: this.position,
3089 originalSize: this.originalSize,
3090 originalPosition: this.originalPosition
3097 * Resizable Extensions
3100 $.ui.plugin.add("resizable", "alsoResize", {
3102 start: function (event, ui) {
3103 var that = $(this).data("resizable"), o = that.options;
3105 var _store = function (exp) {
3106 $(exp).each(function() {
3108 el.data("resizable-alsoresize", {
3109 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3110 left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10)
3115 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
3116 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3117 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3119 _store(o.alsoResize);
3123 resize: function (event, ui) {
3124 var that = $(this).data("resizable"), o = that.options, os = that.originalSize, op = that.originalPosition;
3127 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3128 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3131 _alsoResize = function (exp, c) {
3132 $(exp).each(function() {
3133 var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
3134 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
3136 $.each(css, function (i, prop) {
3137 var sum = (start[prop]||0) + (delta[prop]||0);
3138 if (sum && sum >= 0)
3139 style[prop] = sum || null;
3146 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
3147 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3149 _alsoResize(o.alsoResize);
3153 stop: function (event, ui) {
3154 $(this).removeData("resizable-alsoresize");
3158 $.ui.plugin.add("resizable", "animate", {
3160 stop: function(event, ui) {
3161 var that = $(this).data("resizable"), o = that.options;
3163 var pr = that._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3164 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
3165 soffsetw = ista ? 0 : that.sizeDiff.width;
3167 var style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
3168 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
3169 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
3171 that.element.animate(
3172 $.extend(style, top && left ? { top: top, left: left } : {}), {
3173 duration: o.animateDuration,
3174 easing: o.animateEasing,
3178 width: parseInt(that.element.css('width'), 10),
3179 height: parseInt(that.element.css('height'), 10),
3180 top: parseInt(that.element.css('top'), 10),
3181 left: parseInt(that.element.css('left'), 10)
3184 if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
3186 // propagating resize, and updating values for each animation step
3187 that._updateCache(data);
3188 that._propagate("resize", event);
3197 $.ui.plugin.add("resizable", "containment", {
3199 start: function(event, ui) {
3200 var that = $(this).data("resizable"), o = that.options, el = that.element;
3201 var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
3204 that.containerElement = $(ce);
3206 if (/document/.test(oc) || oc == document) {
3207 that.containerOffset = { left: 0, top: 0 };
3208 that.containerPosition = { left: 0, top: 0 };
3211 element: $(document), left: 0, top: 0,
3212 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
3216 // i'm a node, so compute top, left, right, bottom
3218 var element = $(ce), p = [];
3219 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
3221 that.containerOffset = element.offset();
3222 that.containerPosition = element.position();
3223 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
3225 var co = that.containerOffset, ch = that.containerSize.height, cw = that.containerSize.width,
3226 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3229 element: ce, left: co.left, top: co.top, width: width, height: height
3234 resize: function(event, ui) {
3235 var that = $(this).data("resizable"), o = that.options,
3236 ps = that.containerSize, co = that.containerOffset, cs = that.size, cp = that.position,
3237 pRatio = that._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = that.containerElement;
3239 if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
3241 if (cp.left < (that._helper ? co.left : 0)) {
3242 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3243 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
3244 that.position.left = o.helper ? co.left : 0;
3247 if (cp.top < (that._helper ? co.top : 0)) {
3248 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3249 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
3250 that.position.top = that._helper ? co.top : 0;
3253 that.offset.left = that.parentData.left+that.position.left;
3254 that.offset.top = that.parentData.top+that.position.top;
3256 var woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ),
3257 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3259 var isParent = that.containerElement.get(0) == that.element.parent().get(0),
3260 isOffsetRelative = /relative|absolute/.test(that.containerElement.css('position'));
3262 if(isParent && isOffsetRelative) woset -= that.parentData.left;
3264 if (woset + that.size.width >= that.parentData.width) {
3265 that.size.width = that.parentData.width - woset;
3266 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
3269 if (hoset + that.size.height >= that.parentData.height) {
3270 that.size.height = that.parentData.height - hoset;
3271 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
3275 stop: function(event, ui){
3276 var that = $(this).data("resizable"), o = that.options, cp = that.position,
3277 co = that.containerOffset, cop = that.containerPosition, ce = that.containerElement;
3279 var helper = $(that.helper), ho = helper.offset(), w = helper.outerWidth() - that.sizeDiff.width, h = helper.outerHeight() - that.sizeDiff.height;
3281 if (that._helper && !o.animate && (/relative/).test(ce.css('position')))
3282 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3284 if (that._helper && !o.animate && (/static/).test(ce.css('position')))
3285 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3290 $.ui.plugin.add("resizable", "ghost", {
3292 start: function(event, ui) {
3294 var that = $(this).data("resizable"), o = that.options, cs = that.size;
3296 that.ghost = that.originalElement.clone();
3298 .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3299 .addClass('ui-resizable-ghost')
3300 .addClass(typeof o.ghost == 'string' ? o.ghost : '');
3302 that.ghost.appendTo(that.helper);
3306 resize: function(event, ui){
3307 var that = $(this).data("resizable"), o = that.options;
3308 if (that.ghost) that.ghost.css({ position: 'relative', height: that.size.height, width: that.size.width });
3311 stop: function(event, ui){
3312 var that = $(this).data("resizable"), o = that.options;
3313 if (that.ghost && that.helper) that.helper.get(0).removeChild(that.ghost.get(0));
3318 $.ui.plugin.add("resizable", "grid", {
3320 resize: function(event, ui) {
3321 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;
3322 o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
3323 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);
3325 if (/^(se|s|e)$/.test(a)) {
3326 that.size.width = os.width + ox;
3327 that.size.height = os.height + oy;
3329 else if (/^(ne)$/.test(a)) {
3330 that.size.width = os.width + ox;
3331 that.size.height = os.height + oy;
3332 that.position.top = op.top - oy;
3334 else if (/^(sw)$/.test(a)) {
3335 that.size.width = os.width + ox;
3336 that.size.height = os.height + oy;
3337 that.position.left = op.left - ox;
3340 that.size.width = os.width + ox;
3341 that.size.height = os.height + oy;
3342 that.position.top = op.top - oy;
3343 that.position.left = op.left - ox;
3349 var num = function(v) {
3350 return parseInt(v, 10) || 0;
3353 var isNumber = function(value) {
3354 return !isNaN(parseInt(value, 10));
3358 (function( $, undefined ) {
3360 $.widget("ui.selectable", $.ui.mouse, {
3369 _create: function() {
3372 this.element.addClass("ui-selectable");
3374 this.dragged = false;
3376 // cache selectee children based on filter
3378 this.refresh = function() {
3379 selectees = $(that.options.filter, that.element[0]);
3380 selectees.addClass("ui-selectee");
3381 selectees.each(function() {
3382 var $this = $(this);
3383 var pos = $this.offset();
3384 $.data(this, "selectable-item", {
3389 right: pos.left + $this.outerWidth(),
3390 bottom: pos.top + $this.outerHeight(),
3391 startselected: false,
3392 selected: $this.hasClass('ui-selected'),
3393 selecting: $this.hasClass('ui-selecting'),
3394 unselecting: $this.hasClass('ui-unselecting')
3400 this.selectees = selectees.addClass("ui-selectee");
3404 this.helper = $("<div class='ui-selectable-helper'></div>");
3407 _destroy: function() {
3409 .removeClass("ui-selectee")
3410 .removeData("selectable-item");
3412 .removeClass("ui-selectable ui-selectable-disabled");
3413 this._mouseDestroy();
3416 _mouseStart: function(event) {
3419 this.opos = [event.pageX, event.pageY];
3421 if (this.options.disabled)
3424 var options = this.options;
3426 this.selectees = $(options.filter, this.element[0]);
3428 this._trigger("start", event);
3430 $(options.appendTo).append(this.helper);
3431 // position helper (lasso)
3433 "left": event.clientX,
3434 "top": event.clientY,
3439 if (options.autoRefresh) {
3443 this.selectees.filter('.ui-selected').each(function() {
3444 var selectee = $.data(this, "selectable-item");
3445 selectee.startselected = true;
3446 if (!event.metaKey && !event.ctrlKey) {
3447 selectee.$element.removeClass('ui-selected');
3448 selectee.selected = false;
3449 selectee.$element.addClass('ui-unselecting');
3450 selectee.unselecting = true;
3451 // selectable UNSELECTING callback
3452 that._trigger("unselecting", event, {
3453 unselecting: selectee.element
3458 $(event.target).parents().andSelf().each(function() {
3459 var selectee = $.data(this, "selectable-item");
3461 var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
3463 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3464 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3465 selectee.unselecting = !doSelect;
3466 selectee.selecting = doSelect;
3467 selectee.selected = doSelect;
3468 // selectable (UN)SELECTING callback
3470 that._trigger("selecting", event, {
3471 selecting: selectee.element
3474 that._trigger("unselecting", event, {
3475 unselecting: selectee.element
3484 _mouseDrag: function(event) {
3486 this.dragged = true;
3488 if (this.options.disabled)
3491 var options = this.options;
3493 var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
3494 if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
3495 if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
3496 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3498 this.selectees.each(function() {
3499 var selectee = $.data(this, "selectable-item");
3500 //prevent helper from being selected if appendTo: selectable
3501 if (!selectee || selectee.element == that.element[0])
3504 if (options.tolerance == 'touch') {
3505 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3506 } else if (options.tolerance == 'fit') {
3507 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3512 if (selectee.selected) {
3513 selectee.$element.removeClass('ui-selected');
3514 selectee.selected = false;
3516 if (selectee.unselecting) {
3517 selectee.$element.removeClass('ui-unselecting');
3518 selectee.unselecting = false;
3520 if (!selectee.selecting) {
3521 selectee.$element.addClass('ui-selecting');
3522 selectee.selecting = true;
3523 // selectable SELECTING callback
3524 that._trigger("selecting", event, {
3525 selecting: selectee.element
3530 if (selectee.selecting) {
3531 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3532 selectee.$element.removeClass('ui-selecting');
3533 selectee.selecting = false;
3534 selectee.$element.addClass('ui-selected');
3535 selectee.selected = true;
3537 selectee.$element.removeClass('ui-selecting');
3538 selectee.selecting = false;
3539 if (selectee.startselected) {
3540 selectee.$element.addClass('ui-unselecting');
3541 selectee.unselecting = true;
3543 // selectable UNSELECTING callback
3544 that._trigger("unselecting", event, {
3545 unselecting: selectee.element
3549 if (selectee.selected) {
3550 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3551 selectee.$element.removeClass('ui-selected');
3552 selectee.selected = false;
3554 selectee.$element.addClass('ui-unselecting');
3555 selectee.unselecting = true;
3556 // selectable UNSELECTING callback
3557 that._trigger("unselecting", event, {
3558 unselecting: selectee.element
3568 _mouseStop: function(event) {
3571 this.dragged = false;
3573 var options = this.options;
3575 $('.ui-unselecting', this.element[0]).each(function() {
3576 var selectee = $.data(this, "selectable-item");
3577 selectee.$element.removeClass('ui-unselecting');
3578 selectee.unselecting = false;
3579 selectee.startselected = false;
3580 that._trigger("unselected", event, {
3581 unselected: selectee.element
3584 $('.ui-selecting', this.element[0]).each(function() {
3585 var selectee = $.data(this, "selectable-item");
3586 selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
3587 selectee.selecting = false;
3588 selectee.selected = true;
3589 selectee.startselected = true;
3590 that._trigger("selected", event, {
3591 selected: selectee.element
3594 this._trigger("stop", event);
3596 this.helper.remove();
3604 (function( $, undefined ) {
3606 $.widget("ui.sortable", $.ui.mouse, {
3608 widgetEventPrefix: "sort",
3618 forcePlaceholderSize: false,
3619 forceHelperSize: false,
3628 scrollSensitivity: 20,
3631 tolerance: "intersect",
3634 _create: function() {
3636 var o = this.options;
3637 this.containerCache = {};
3638 this.element.addClass("ui-sortable");
3643 //Let's determine if the items are being displayed horizontally
3644 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;
3646 //Let's determine the parent's offset
3647 this.offset = this.element.offset();
3649 //Initialize mouse events for interaction
3657 _destroy: function() {
3659 .removeClass("ui-sortable ui-sortable-disabled");
3660 this._mouseDestroy();
3662 for ( var i = this.items.length - 1; i >= 0; i-- )
3663 this.items[i].item.removeData(this.widgetName + "-item");
3668 _setOption: function(key, value){
3669 if ( key === "disabled" ) {
3670 this.options[ key ] = value;
3672 this.widget().toggleClass( "ui-sortable-disabled", !!value );
3674 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3675 $.Widget.prototype._setOption.apply(this, arguments);
3679 _mouseCapture: function(event, overrideHandle) {
3682 if (this.reverting) {
3686 if(this.options.disabled || this.options.type == 'static') return false;
3688 //We have to refresh the items data once first
3689 this._refreshItems(event);
3691 //Find out if the clicked node (or one of its parents) is a actual item in this.items
3692 var currentItem = null, nodes = $(event.target).parents().each(function() {
3693 if($.data(this, that.widgetName + '-item') == that) {
3694 currentItem = $(this);
3698 if($.data(event.target, that.widgetName + '-item') == that) currentItem = $(event.target);
3700 if(!currentItem) return false;
3701 if(this.options.handle && !overrideHandle) {
3702 var validHandle = false;
3704 $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
3705 if(!validHandle) return false;
3708 this.currentItem = currentItem;
3709 this._removeCurrentsFromItems();
3714 _mouseStart: function(event, overrideHandle, noActivation) {
3716 var o = this.options;
3717 this.currentContainer = this;
3719 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3720 this.refreshPositions();
3722 //Create and append the visible helper
3723 this.helper = this._createHelper(event);
3725 //Cache the helper size
3726 this._cacheHelperProportions();
3729 * - Position generation -
3730 * This block generates everything position related - it's the core of draggables.
3733 //Cache the margins of the original element
3734 this._cacheMargins();
3736 //Get the next scrolling parent
3737 this.scrollParent = this.helper.scrollParent();
3739 //The element's absolute position on the page minus margins
3740 this.offset = this.currentItem.offset();
3742 top: this.offset.top - this.margins.top,
3743 left: this.offset.left - this.margins.left
3746 $.extend(this.offset, {
3747 click: { //Where the click happened, relative to the element
3748 left: event.pageX - this.offset.left,
3749 top: event.pageY - this.offset.top
3751 parent: this._getParentOffset(),
3752 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3755 // Only after we got the offset, we can change the helper's position to absolute
3756 // TODO: Still need to figure out a way to make relative sorting possible
3757 this.helper.css("position", "absolute");
3758 this.cssPosition = this.helper.css("position");
3760 //Generate the original position
3761 this.originalPosition = this._generatePosition(event);
3762 this.originalPageX = event.pageX;
3763 this.originalPageY = event.pageY;
3765 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
3766 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3768 //Cache the former DOM position
3769 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3771 //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
3772 if(this.helper[0] != this.currentItem[0]) {
3773 this.currentItem.hide();
3776 //Create the placeholder
3777 this._createPlaceholder();
3779 //Set a containment if given in the options
3781 this._setContainment();
3783 if(o.cursor) { // cursor option
3784 if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
3785 $('body').css("cursor", o.cursor);
3788 if(o.opacity) { // opacity option
3789 if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
3790 this.helper.css("opacity", o.opacity);
3793 if(o.zIndex) { // zIndex option
3794 if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
3795 this.helper.css("zIndex", o.zIndex);
3799 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
3800 this.overflowOffset = this.scrollParent.offset();
3803 this._trigger("start", event, this._uiHash());
3805 //Recache the helper size
3806 if(!this._preserveHelperProportions)
3807 this._cacheHelperProportions();
3810 //Post 'activate' events to possible containers
3812 for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, this._uiHash(this)); }
3815 //Prepare possible droppables
3817 $.ui.ddmanager.current = this;
3819 if ($.ui.ddmanager && !o.dropBehaviour)
3820 $.ui.ddmanager.prepareOffsets(this, event);
3822 this.dragging = true;
3824 this.helper.addClass("ui-sortable-helper");
3825 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3830 _mouseDrag: function(event) {
3832 //Compute the helpers position
3833 this.position = this._generatePosition(event);
3834 this.positionAbs = this._convertPositionTo("absolute");
3836 if (!this.lastPositionAbs) {
3837 this.lastPositionAbs = this.positionAbs;
3841 if(this.options.scroll) {
3842 var o = this.options, scrolled = false;
3843 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
3845 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
3846 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3847 else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
3848 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3850 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
3851 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3852 else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
3853 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3857 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
3858 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3859 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
3860 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3862 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
3863 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3864 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
3865 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3869 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
3870 $.ui.ddmanager.prepareOffsets(this, event);
3873 //Regenerate the absolute position used for position checks
3874 this.positionAbs = this._convertPositionTo("absolute");
3876 //Set the helper position
3877 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
3878 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
3881 for (var i = this.items.length - 1; i >= 0; i--) {
3883 //Cache variables and intersection, continue if no intersection
3884 var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
3885 if (!intersection) continue;
3887 // Only put the placeholder inside the current Container, skip all
3888 // items form other containers. This works because when moving
3889 // an item from one container to another the
3890 // currentContainer is switched before the placeholder is moved.
3892 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3893 // beetween the outer and inner container.
3894 if (item.instance !== this.currentContainer) continue;
3896 if (itemElement != this.currentItem[0] //cannot intersect with itself
3897 && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
3898 && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
3899 && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
3900 //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
3903 this.direction = intersection == 1 ? "down" : "up";
3905 if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
3906 this._rearrange(event, item);
3911 this._trigger("change", event, this._uiHash());
3916 //Post events to containers
3917 this._contactContainers(event);
3919 //Interconnect with droppables
3920 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
3923 this._trigger('sort', event, this._uiHash());
3925 this.lastPositionAbs = this.positionAbs;
3930 _mouseStop: function(event, noPropagation) {
3934 //If we are using droppables, inform the manager about the drop
3935 if ($.ui.ddmanager && !this.options.dropBehaviour)
3936 $.ui.ddmanager.drop(this, event);
3938 if(this.options.revert) {
3940 var cur = this.placeholder.offset();
3942 this.reverting = true;
3944 $(this.helper).animate({
3945 left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
3946 top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
3947 }, parseInt(this.options.revert, 10) || 500, function() {
3951 this._clear(event, noPropagation);
3958 cancel: function() {
3962 this._mouseUp({ target: null });
3964 if(this.options.helper == "original")
3965 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3967 this.currentItem.show();
3969 //Post deactivating events to containers
3970 for (var i = this.containers.length - 1; i >= 0; i--){
3971 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3972 if(this.containers[i].containerCache.over) {
3973 this.containers[i]._trigger("out", null, this._uiHash(this));
3974 this.containers[i].containerCache.over = 0;
3980 if (this.placeholder) {
3981 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3982 if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3983 if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
3992 if(this.domPosition.prev) {
3993 $(this.domPosition.prev).after(this.currentItem);
3995 $(this.domPosition.parent).prepend(this.currentItem);
4003 serialize: function(o) {
4005 var items = this._getItemsAsjQuery(o && o.connected);
4006 var str = []; o = o || {};
4008 $(items).each(function() {
4009 var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
4010 if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
4013 if(!str.length && o.key) {
4014 str.push(o.key + '=');
4017 return str.join('&');
4021 toArray: function(o) {
4023 var items = this._getItemsAsjQuery(o && o.connected);
4024 var ret = []; o = o || {};
4026 items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
4031 /* Be careful with the following core functions */
4032 _intersectsWith: function(item) {
4034 var x1 = this.positionAbs.left,
4035 x2 = x1 + this.helperProportions.width,
4036 y1 = this.positionAbs.top,
4037 y2 = y1 + this.helperProportions.height;
4042 b = t + item.height;
4044 var dyClick = this.offset.click.top,
4045 dxClick = this.offset.click.left;
4047 var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
4049 if( this.options.tolerance == "pointer"
4050 || this.options.forcePointerForContainers
4051 || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
4053 return isOverElement;
4056 return (l < x1 + (this.helperProportions.width / 2) // Right Half
4057 && x2 - (this.helperProportions.width / 2) < r // Left Half
4058 && t < y1 + (this.helperProportions.height / 2) // Bottom Half
4059 && y2 - (this.helperProportions.height / 2) < b ); // Top Half
4064 _intersectsWithPointer: function(item) {
4066 var isOverElementHeight = (this.options.axis === 'x') || $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4067 isOverElementWidth = (this.options.axis === 'y') || $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4068 isOverElement = isOverElementHeight && isOverElementWidth,
4069 verticalDirection = this._getDragVerticalDirection(),
4070 horizontalDirection = this._getDragHorizontalDirection();
4075 return this.floating ?
4076 ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
4077 : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
4081 _intersectsWithSides: function(item) {
4083 var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4084 isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4085 verticalDirection = this._getDragVerticalDirection(),
4086 horizontalDirection = this._getDragHorizontalDirection();
4088 if (this.floating && horizontalDirection) {
4089 return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
4091 return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
4096 _getDragVerticalDirection: function() {
4097 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4098 return delta != 0 && (delta > 0 ? "down" : "up");
4101 _getDragHorizontalDirection: function() {
4102 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4103 return delta != 0 && (delta > 0 ? "right" : "left");
4106 refresh: function(event) {
4107 this._refreshItems(event);
4108 this.refreshPositions();
4112 _connectWith: function() {
4113 var options = this.options;
4114 return options.connectWith.constructor == String
4115 ? [options.connectWith]
4116 : options.connectWith;
4119 _getItemsAsjQuery: function(connected) {
4123 var connectWith = this._connectWith();
4125 if(connectWith && connected) {
4126 for (var i = connectWith.length - 1; i >= 0; i--){
4127 var cur = $(connectWith[i]);
4128 for (var j = cur.length - 1; j >= 0; j--){
4129 var inst = $.data(cur[j], this.widgetName);
4130 if(inst && inst != this && !inst.options.disabled) {
4131 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]);
4137 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]);
4139 for (var i = queries.length - 1; i >= 0; i--){
4140 queries[i][0].each(function() {
4149 _removeCurrentsFromItems: function() {
4151 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4153 for (var i=0; i < this.items.length; i++) {
4155 for (var j=0; j < list.length; j++) {
4156 if(list[j] == this.items[i].item[0])
4157 this.items.splice(i,1);
4164 _refreshItems: function(event) {
4167 this.containers = [this];
4168 var items = this.items;
4169 var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
4170 var connectWith = this._connectWith();
4172 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4173 for (var i = connectWith.length - 1; i >= 0; i--){
4174 var cur = $(connectWith[i]);
4175 for (var j = cur.length - 1; j >= 0; j--){
4176 var inst = $.data(cur[j], this.widgetName);
4177 if(inst && inst != this && !inst.options.disabled) {
4178 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4179 this.containers.push(inst);
4185 for (var i = queries.length - 1; i >= 0; i--) {
4186 var targetData = queries[i][1];
4187 var _queries = queries[i][0];
4189 for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4190 var item = $(_queries[j]);
4192 item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
4196 instance: targetData,
4197 width: 0, height: 0,
4205 refreshPositions: function(fast) {
4207 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4208 if(this.offsetParent && this.helper) {
4209 this.offset.parent = this._getParentOffset();
4212 for (var i = this.items.length - 1; i >= 0; i--){
4213 var item = this.items[i];
4215 //We ignore calculating positions of all connected containers when we're not over them
4216 if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
4219 var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4222 item.width = t.outerWidth();
4223 item.height = t.outerHeight();
4231 if(this.options.custom && this.options.custom.refreshContainers) {
4232 this.options.custom.refreshContainers.call(this);
4234 for (var i = this.containers.length - 1; i >= 0; i--){
4235 var p = this.containers[i].element.offset();
4236 this.containers[i].containerCache.left = p.left;
4237 this.containers[i].containerCache.top = p.top;
4238 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4239 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4246 _createPlaceholder: function(that) {
4247 that = that || this;
4248 var o = that.options;
4250 if(!o.placeholder || o.placeholder.constructor == String) {
4251 var className = o.placeholder;
4253 element: function() {
4255 var el = $(document.createElement(that.currentItem[0].nodeName))
4256 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4257 .removeClass("ui-sortable-helper")[0];
4260 el.style.visibility = "hidden";
4264 update: function(container, p) {
4266 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4267 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4268 if(className && !o.forcePlaceholderSize) return;
4270 //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
4271 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css('paddingTop')||0, 10) - parseInt(that.currentItem.css('paddingBottom')||0, 10)); };
4272 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css('paddingLeft')||0, 10) - parseInt(that.currentItem.css('paddingRight')||0, 10)); };
4277 //Create the placeholder
4278 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4280 //Append it after the actual current item
4281 that.currentItem.after(that.placeholder);
4283 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4284 o.placeholder.update(that, that.placeholder);
4288 _contactContainers: function(event) {
4290 // get innermost container that intersects with item
4291 var innermostContainer = null, innermostIndex = null;
4294 for (var i = this.containers.length - 1; i >= 0; i--){
4296 // never consider a container that's located within the item itself
4297 if($.contains(this.currentItem[0], this.containers[i].element[0]))
4300 if(this._intersectsWith(this.containers[i].containerCache)) {
4302 // if we've already found a container and it's more "inner" than this, then continue
4303 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0]))
4306 innermostContainer = this.containers[i];
4310 // container doesn't intersect. trigger "out" event if necessary
4311 if(this.containers[i].containerCache.over) {
4312 this.containers[i]._trigger("out", event, this._uiHash(this));
4313 this.containers[i].containerCache.over = 0;
4319 // if no intersecting containers found, return
4320 if(!innermostContainer) return;
4322 // move the item into the container if it's not there already
4323 if(this.containers.length === 1) {
4324 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4325 this.containers[innermostIndex].containerCache.over = 1;
4326 } else if(this.currentContainer != this.containers[innermostIndex]) {
4328 //When entering a new container, we will find the item with the least distance and append our item near it
4329 var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
4330 for (var j = this.items.length - 1; j >= 0; j--) {
4331 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
4332 var cur = this.containers[innermostIndex].floating ? this.items[j].item.offset().left : this.items[j].item.offset().top;
4333 if(Math.abs(cur - base) < dist) {
4334 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4335 this.direction = (cur - base > 0) ? 'down' : 'up';
4339 if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
4342 this.currentContainer = this.containers[innermostIndex];
4343 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4344 this._trigger("change", event, this._uiHash());
4345 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4347 //Update the placeholder
4348 this.options.placeholder.update(this.currentContainer, this.placeholder);
4350 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4351 this.containers[innermostIndex].containerCache.over = 1;
4357 _createHelper: function(event) {
4359 var o = this.options;
4360 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
4362 if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
4363 $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4365 if(helper[0] == this.currentItem[0])
4366 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") };
4368 if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
4369 if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
4375 _adjustOffsetFromHelper: function(obj) {
4376 if (typeof obj == 'string') {
4377 obj = obj.split(' ');
4379 if ($.isArray(obj)) {
4380 obj = {left: +obj[0], top: +obj[1] || 0};
4382 if ('left' in obj) {
4383 this.offset.click.left = obj.left + this.margins.left;
4385 if ('right' in obj) {
4386 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4389 this.offset.click.top = obj.top + this.margins.top;
4391 if ('bottom' in obj) {
4392 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4396 _getParentOffset: function() {
4399 //Get the offsetParent and cache its position
4400 this.offsetParent = this.helper.offsetParent();
4401 var po = this.offsetParent.offset();
4403 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4404 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4405 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4406 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4407 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4408 po.left += this.scrollParent.scrollLeft();
4409 po.top += this.scrollParent.scrollTop();
4412 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
4413 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
4414 po = { top: 0, left: 0 };
4417 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4418 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4423 _getRelativeOffset: function() {
4425 if(this.cssPosition == "relative") {
4426 var p = this.currentItem.position();
4428 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4429 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4432 return { top: 0, left: 0 };
4437 _cacheMargins: function() {
4439 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4440 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4444 _cacheHelperProportions: function() {
4445 this.helperProportions = {
4446 width: this.helper.outerWidth(),
4447 height: this.helper.outerHeight()
4451 _setContainment: function() {
4453 var o = this.options;
4454 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
4455 if(o.containment == 'document' || o.containment == 'window') this.containment = [
4456 0 - this.offset.relative.left - this.offset.parent.left,
4457 0 - this.offset.relative.top - this.offset.parent.top,
4458 $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
4459 ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4462 if(!(/^(document|window|parent)$/).test(o.containment)) {
4463 var ce = $(o.containment)[0];
4464 var co = $(o.containment).offset();
4465 var over = ($(ce).css("overflow") != 'hidden');
4467 this.containment = [
4468 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4469 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4470 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,
4471 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
4477 _convertPositionTo: function(d, pos) {
4479 if(!pos) pos = this.position;
4480 var mod = d == "absolute" ? 1 : -1;
4481 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);
4485 pos.top // The absolute mouse position
4486 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4487 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
4488 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4491 pos.left // The absolute mouse position
4492 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
4493 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
4494 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4500 _generatePosition: function(event) {
4502 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);
4504 // This is another very weird special case that only happens for relative elements:
4505 // 1. If the css position is relative
4506 // 2. and the scroll parent is the document or similar to the offset parent
4507 // we have to refresh the relative offset during the scroll so there are no jumps
4508 if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
4509 this.offset.relative = this._getRelativeOffset();
4512 var pageX = event.pageX;
4513 var pageY = event.pageY;
4516 * - Position constraining -
4517 * Constrain the position to a mix of grid, containment.
4520 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4522 if(this.containment) {
4523 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
4524 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
4525 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
4526 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
4530 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4531 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;
4533 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4534 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;
4541 pageY // The absolute mouse position
4542 - this.offset.click.top // Click offset (relative to the element)
4543 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
4544 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
4545 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4548 pageX // The absolute mouse position
4549 - this.offset.click.left // Click offset (relative to the element)
4550 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
4551 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
4552 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4558 _rearrange: function(event, i, a, hardRefresh) {
4560 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));
4562 //Various things done here to improve the performance:
4563 // 1. we create a setTimeout, that calls refreshPositions
4564 // 2. on the instance, we have a counter variable, that get's higher after every append
4565 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4566 // 4. this lets only the last addition to the timeout stack through
4567 this.counter = this.counter ? ++this.counter : 1;
4568 var counter = this.counter;
4570 this._delay(function() {
4571 if(counter == this.counter) this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4576 _clear: function(event, noPropagation) {
4578 this.reverting = false;
4579 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4580 // everything else normalized again
4581 var delayedTriggers = [];
4583 // We first have to update the dom position of the actual currentItem
4584 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4585 if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
4586 this._noFinalSort = null;
4588 if(this.helper[0] == this.currentItem[0]) {
4589 for(var i in this._storedCSS) {
4590 if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
4592 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4594 this.currentItem.show();
4597 if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4598 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
4600 // Check if the items Container has Changed and trigger appropriate
4602 if (this !== this.currentContainer) {
4603 if(!noPropagation) {
4604 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4605 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4606 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4611 //Post events to containers
4612 for (var i = this.containers.length - 1; i >= 0; i--){
4613 if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4614 if(this.containers[i].containerCache.over) {
4615 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4616 this.containers[i].containerCache.over = 0;
4620 //Do what was originally in plugins
4621 if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
4622 if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
4623 if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
4625 this.dragging = false;
4626 if(this.cancelHelperRemoval) {
4627 if(!noPropagation) {
4628 this._trigger("beforeStop", event, this._uiHash());
4629 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4630 this._trigger("stop", event, this._uiHash());
4633 this.fromOutside = false;
4637 if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
4639 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4640 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4642 if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
4644 if(!noPropagation) {
4645 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
4646 this._trigger("stop", event, this._uiHash());
4649 this.fromOutside = false;
4654 _trigger: function() {
4655 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4660 _uiHash: function(_inst) {
4661 var inst = _inst || this;
4663 helper: inst.helper,
4664 placeholder: inst.placeholder || $([]),
4665 position: inst.position,
4666 originalPosition: inst.originalPosition,
4667 offset: inst.positionAbs,
4668 item: inst.currentItem,
4669 sender: _inst ? _inst.element : null
4676 (function( $, undefined ) {
4682 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
4683 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
4684 showProps.height = showProps.paddingTop = showProps.paddingBottom =
4685 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
4687 $.widget( "ui.accordion", {
4694 header: "> li > :first-child,> :not(li):even",
4695 heightStyle: "auto",
4697 activeHeader: "ui-icon-triangle-1-s",
4698 header: "ui-icon-triangle-1-e"
4703 beforeActivate: null
4706 _create: function() {
4707 var accordionId = this.accordionId = "ui-accordion-" +
4708 (this.element.attr( "id" ) || ++uid),
4709 options = this.options;
4711 this.prevShow = this.prevHide = $();
4712 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
4714 this.headers = this.element.find( options.header )
4715 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
4716 this._hoverable( this.headers );
4717 this._focusable( this.headers );
4720 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
4723 // don't allow collapsible: false and active: false
4724 if ( !options.collapsible && options.active === false ) {
4727 // handle negative values
4728 if ( options.active < 0 ) {
4729 options.active += this.headers.length;
4731 this.active = this._findActive( options.active )
4732 .addClass( "ui-accordion-header-active ui-state-active" )
4733 .toggleClass( "ui-corner-all ui-corner-top" );
4735 .addClass( "ui-accordion-content-active" )
4738 this._createIcons();
4739 this.originalHeight = this.element[0].style.height;
4743 this.element.attr( "role", "tablist" );
4746 .attr( "role", "tab" )
4747 .each(function( i ) {
4748 var header = $( this ),
4749 headerId = header.attr( "id" ),
4750 panel = header.next(),
4751 panelId = panel.attr( "id" );
4753 headerId = accordionId + "-header-" + i;
4754 header.attr( "id", headerId );
4757 panelId = accordionId + "-panel-" + i;
4758 panel.attr( "id", panelId );
4760 header.attr( "aria-controls", panelId );
4761 panel.attr( "aria-labelledby", headerId );
4764 .attr( "role", "tabpanel" );
4769 "aria-selected": "false",
4774 "aria-expanded": "false",
4775 "aria-hidden": "true"
4779 // make sure at least one header is in the tab order
4780 if ( !this.active.length ) {
4781 this.headers.eq( 0 ).attr( "tabIndex", 0 );
4784 "aria-selected": "true",
4789 "aria-expanded": "true",
4790 "aria-hidden": "false"
4794 this._on( this.headers, { keydown: "_keydown" });
4795 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
4796 this._setupEvents( options.event );
4799 _getCreateEventData: function() {
4801 header: this.active,
4802 content: !this.active.length ? $() : this.active.next()
4806 _createIcons: function() {
4807 var icons = this.options.icons;
4810 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
4811 .prependTo( this.headers );
4812 this.active.children( ".ui-accordion-header-icon" )
4813 .removeClass( icons.header )
4814 .addClass( icons.activeHeader );
4815 this.headers.addClass( "ui-accordion-icons" );
4819 _destroyIcons: function() {
4821 .removeClass( "ui-accordion-icons" )
4822 .children( ".ui-accordion-header-icon" )
4826 _destroy: function() {
4829 // clean up main element
4831 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
4832 .removeAttr( "role" );
4836 .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
4837 .removeAttr( "role" )
4838 .removeAttr( "aria-selected" )
4839 .removeAttr( "aria-controls" )
4840 .removeAttr( "tabIndex" )
4842 if ( /^ui-accordion/.test( this.id ) ) {
4843 this.removeAttribute( "id" );
4846 this._destroyIcons();
4848 // clean up content panels
4849 contents = this.headers.next()
4850 .css( "display", "" )
4851 .removeAttr( "role" )
4852 .removeAttr( "aria-expanded" )
4853 .removeAttr( "aria-hidden" )
4854 .removeAttr( "aria-labelledby" )
4855 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
4857 if ( /^ui-accordion/.test( this.id ) ) {
4858 this.removeAttribute( "id" );
4861 if ( this.options.heightStyle !== "content" ) {
4862 this.element.css( "height", this.originalHeight );
4863 contents.css( "height", "" );
4867 _setOption: function( key, value ) {
4868 if ( key === "active" ) {
4869 // _activate() will handle invalid values and update this.options
4870 this._activate( value );
4874 if ( key === "event" ) {
4875 if ( this.options.event ) {
4876 this._off( this.headers, this.options.event );
4878 this._setupEvents( value );
4881 this._super( key, value );
4883 // setting collapsible: false while collapsed; open first panel
4884 if ( key === "collapsible" && !value && this.options.active === false ) {
4885 this._activate( 0 );
4888 if ( key === "icons" ) {
4889 this._destroyIcons();
4891 this._createIcons();
4895 // #5332 - opacity doesn't cascade to positioned elements in IE
4896 // so we need to add the disabled class to the headers and panels
4897 if ( key === "disabled" ) {
4898 this.headers.add( this.headers.next() )
4899 .toggleClass( "ui-state-disabled", !!value );
4903 _keydown: function( event ) {
4904 if ( event.altKey || event.ctrlKey ) {
4908 var keyCode = $.ui.keyCode,
4909 length = this.headers.length,
4910 currentIndex = this.headers.index( event.target ),
4913 switch ( event.keyCode ) {
4916 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
4920 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
4924 this._eventHandler( event );
4927 toFocus = this.headers[ 0 ];
4930 toFocus = this.headers[ length - 1 ];
4935 $( event.target ).attr( "tabIndex", -1 );
4936 $( toFocus ).attr( "tabIndex", 0 );
4938 event.preventDefault();
4942 _panelKeyDown : function( event ) {
4943 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
4944 $( event.currentTarget ).prev().focus();
4948 refresh: function() {
4949 var maxHeight, overflow,
4950 heightStyle = this.options.heightStyle,
4951 parent = this.element.parent();
4953 this.element.css( "height", this.originalHeight );
4955 if ( heightStyle === "fill" ) {
4956 // IE 6 treats height like minHeight, so we need to turn off overflow
4957 // in order to get a reliable height
4958 // we use the minHeight support test because we assume that only
4959 // browsers that don't support minHeight will treat height as minHeight
4960 if ( !$.support.minHeight ) {
4961 overflow = parent.css( "overflow" );
4962 parent.css( "overflow", "hidden");
4964 maxHeight = parent.height();
4965 this.element.siblings( ":visible" ).each(function() {
4966 var elem = $( this ),
4967 position = elem.css( "position" );
4969 if ( position === "absolute" || position === "fixed" ) {
4972 maxHeight -= elem.outerHeight( true );
4975 parent.css( "overflow", overflow );
4978 this.headers.each(function() {
4979 maxHeight -= $( this ).outerHeight( true );
4984 $( this ).height( Math.max( 0, maxHeight -
4985 $( this ).innerHeight() + $( this ).height() ) );
4987 .css( "overflow", "auto" );
4988 } else if ( heightStyle === "auto" ) {
4992 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
4994 .height( maxHeight );
4997 if ( heightStyle !== "content" ) {
4998 this.element.height( this.element.height() );
5002 _activate: function( index ) {
5003 var active = this._findActive( index )[ 0 ];
5005 // trying to activate the already active panel
5006 if ( active === this.active[ 0 ] ) {
5010 // trying to collapse, simulate a click on the currently active header
5011 active = active || this.active[ 0 ];
5013 this._eventHandler({
5015 currentTarget: active,
5016 preventDefault: $.noop
5020 _findActive: function( selector ) {
5021 return typeof selector === "number" ? this.headers.eq( selector ) : $();
5024 _setupEvents: function( event ) {
5029 $.each( event.split(" "), function( index, eventName ) {
5030 events[ eventName ] = "_eventHandler";
5032 this._on( this.headers, events );
5035 _eventHandler: function( event ) {
5036 var options = this.options,
5037 active = this.active,
5038 clicked = $( event.currentTarget ),
5039 clickedIsActive = clicked[ 0 ] === active[ 0 ],
5040 collapsing = clickedIsActive && options.collapsible,
5041 toShow = collapsing ? $() : clicked.next(),
5042 toHide = active.next(),
5046 newHeader: collapsing ? $() : clicked,
5050 event.preventDefault();
5053 // click on active header, but not collapsible
5054 ( clickedIsActive && !options.collapsible ) ||
5055 // allow canceling activation
5056 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
5060 options.active = collapsing ? false : this.headers.index( clicked );
5062 // when the call to ._toggle() comes after the class changes
5063 // it causes a very odd bug in IE 8 (see #6720)
5064 this.active = clickedIsActive ? $() : clicked;
5065 this._toggle( eventData );
5068 // corner classes on the previously active header stay after the animation
5069 active.removeClass( "ui-accordion-header-active ui-state-active" );
5070 if ( options.icons ) {
5071 active.children( ".ui-accordion-header-icon" )
5072 .removeClass( options.icons.activeHeader )
5073 .addClass( options.icons.header );
5076 if ( !clickedIsActive ) {
5078 .removeClass( "ui-corner-all" )
5079 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
5080 if ( options.icons ) {
5081 clicked.children( ".ui-accordion-header-icon" )
5082 .removeClass( options.icons.header )
5083 .addClass( options.icons.activeHeader );
5088 .addClass( "ui-accordion-content-active" );
5092 _toggle: function( data ) {
5093 var toShow = data.newPanel,
5094 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
5096 // handle activating a panel during the animation for another activation
5097 this.prevShow.add( this.prevHide ).stop( true, true );
5098 this.prevShow = toShow;
5099 this.prevHide = toHide;
5101 if ( this.options.animate ) {
5102 this._animate( toShow, toHide, data );
5106 this._toggleComplete( data );
5110 "aria-expanded": "false",
5111 "aria-hidden": "true"
5113 toHide.prev().attr( "aria-selected", "false" );
5114 // if we're switching panels, remove the old header from the tab order
5115 // if we're opening from collapsed state, remove the previous header from the tab order
5116 // if we're collapsing, then keep the collapsing header in the tab order
5117 if ( toShow.length && toHide.length ) {
5118 toHide.prev().attr( "tabIndex", -1 );
5119 } else if ( toShow.length ) {
5120 this.headers.filter(function() {
5121 return $( this ).attr( "tabIndex" ) === 0;
5123 .attr( "tabIndex", -1 );
5128 "aria-expanded": "true",
5129 "aria-hidden": "false"
5133 "aria-selected": "true",
5138 _animate: function( toShow, toHide, data ) {
5139 var total, easing, duration,
5142 down = toShow.length &&
5143 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
5144 animate = this.options.animate || {},
5145 options = down && animate.down || animate,
5146 complete = function() {
5147 that._toggleComplete( data );
5150 if ( typeof options === "number" ) {
5153 if ( typeof options === "string" ) {
5156 // fall back from options to animation in case of partial down settings
5157 easing = easing || options.easing || animate.easing;
5158 duration = duration || options.duration || animate.duration;
5160 if ( !toHide.length ) {
5161 return toShow.animate( showProps, duration, easing, complete );
5163 if ( !toShow.length ) {
5164 return toHide.animate( hideProps, duration, easing, complete );
5167 total = toShow.show().outerHeight();
5168 toHide.animate( hideProps, {
5171 step: function( now, fx ) {
5172 fx.now = Math.round( now );
5177 .animate( showProps, {
5181 step: function( now, fx ) {
5182 fx.now = Math.round( now );
5183 if ( fx.prop !== "height" ) {
5185 } else if ( that.options.heightStyle !== "content" ) {
5186 fx.now = Math.round( total - toHide.outerHeight() - adjust );
5193 _toggleComplete: function( data ) {
5194 var toHide = data.oldPanel;
5197 .removeClass( "ui-accordion-content-active" )
5199 .removeClass( "ui-corner-top" )
5200 .addClass( "ui-corner-all" );
5202 // Work around for rendering bug in IE (#5421)
5203 if ( toHide.length ) {
5204 toHide.parent()[0].className = toHide.parent()[0].className;
5207 this._trigger( "activate", null, data );
5214 if ( $.uiBackCompat !== false ) {
5215 // navigation options
5216 (function( $, prototype ) {
5217 $.extend( prototype.options, {
5219 navigationFilter: function() {
5220 return this.href.toLowerCase() === location.href.toLowerCase();
5224 var _create = prototype._create;
5225 prototype._create = function() {
5226 if ( this.options.navigation ) {
5228 headers = this.element.find( this.options.header ),
5229 content = headers.next(),
5230 current = headers.add( content )
5232 .filter( this.options.navigationFilter )
5235 headers.add( content ).each( function( index ) {
5236 if ( $.contains( this, current ) ) {
5237 that.options.active = Math.floor( index / 2 );
5243 _create.call( this );
5245 }( jQuery, jQuery.ui.accordion.prototype ) );
5248 (function( $, prototype ) {
5249 $.extend( prototype.options, {
5250 heightStyle: null, // remove default so we fall back to old values
5251 autoHeight: true, // use heightStyle: "auto"
5252 clearStyle: false, // use heightStyle: "content"
5253 fillSpace: false // use heightStyle: "fill"
5256 var _create = prototype._create,
5257 _setOption = prototype._setOption;
5259 $.extend( prototype, {
5260 _create: function() {
5261 this.options.heightStyle = this.options.heightStyle ||
5262 this._mergeHeightStyle();
5264 _create.call( this );
5267 _setOption: function( key, value ) {
5268 if ( key === "autoHeight" || key === "clearStyle" || key === "fillSpace" ) {
5269 this.options.heightStyle = this._mergeHeightStyle();
5271 _setOption.apply( this, arguments );
5274 _mergeHeightStyle: function() {
5275 var options = this.options;
5277 if ( options.fillSpace ) {
5281 if ( options.clearStyle ) {
5285 if ( options.autoHeight ) {
5290 }( jQuery, jQuery.ui.accordion.prototype ) );
5293 (function( $, prototype ) {
5294 $.extend( prototype.options.icons, {
5295 activeHeader: null, // remove default so we fall back to old values
5296 headerSelected: "ui-icon-triangle-1-s"
5299 var _createIcons = prototype._createIcons;
5300 prototype._createIcons = function() {
5301 if ( this.options.icons ) {
5302 this.options.icons.activeHeader = this.options.icons.activeHeader ||
5303 this.options.icons.headerSelected;
5305 _createIcons.call( this );
5307 }( jQuery, jQuery.ui.accordion.prototype ) );
5309 // expanded active option, activate method
5310 (function( $, prototype ) {
5311 prototype.activate = prototype._activate;
5313 var _findActive = prototype._findActive;
5314 prototype._findActive = function( index ) {
5315 if ( index === -1 ) {
5318 if ( index && typeof index !== "number" ) {
5319 index = this.headers.index( this.headers.filter( index ) );
5320 if ( index === -1 ) {
5324 return _findActive.call( this, index );
5326 }( jQuery, jQuery.ui.accordion.prototype ) );
5329 jQuery.ui.accordion.prototype.resize = jQuery.ui.accordion.prototype.refresh;
5332 (function( $, prototype ) {
5333 $.extend( prototype.options, {
5338 var _trigger = prototype._trigger;
5339 prototype._trigger = function( type, event, data ) {
5340 var ret = _trigger.apply( this, arguments );
5345 if ( type === "beforeActivate" ) {
5346 ret = _trigger.call( this, "changestart", event, {
5347 oldHeader: data.oldHeader,
5348 oldContent: data.oldPanel,
5349 newHeader: data.newHeader,
5350 newContent: data.newPanel
5352 } else if ( type === "activate" ) {
5353 ret = _trigger.call( this, "change", event, {
5354 oldHeader: data.oldHeader,
5355 oldContent: data.oldPanel,
5356 newHeader: data.newHeader,
5357 newContent: data.newPanel
5362 }( jQuery, jQuery.ui.accordion.prototype ) );
5365 // NOTE: this only provides support for "slide", "bounceslide", and easings
5366 // not the full $.ui.accordion.animations API
5367 (function( $, prototype ) {
5368 $.extend( prototype.options, {
5373 var _create = prototype._create;
5374 prototype._create = function() {
5375 var options = this.options;
5376 if ( options.animate === null ) {
5377 if ( !options.animated ) {
5378 options.animate = false;
5379 } else if ( options.animated === "slide" ) {
5380 options.animate = 300;
5381 } else if ( options.animated === "bounceslide" ) {
5385 easing: "easeOutBounce",
5390 options.animate = options.animated;
5394 _create.call( this );
5396 }( jQuery, jQuery.ui.accordion.prototype ) );
5400 (function( $, undefined ) {
5402 // used to prevent race conditions with remote data sources
5403 var requestIndex = 0;
5405 $.widget( "ui.autocomplete", {
5407 defaultElement: "<input>",
5432 _create: function() {
5433 // Some browsers only repeat keydown events, not keypress events,
5434 // so we use the suppressKeyPress flag to determine if we've already
5435 // handled the keydown event. #7269
5436 // Unfortunately the code for & in keypress is the same as the up arrow,
5437 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
5438 // events when we know the keydown event was used to modify the
5439 // search term. #7799
5440 var suppressKeyPress, suppressKeyPressRepeat, suppressInput;
5442 this.isMultiLine = this._isMultiLine();
5443 this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];
5444 this.isNewMenu = true;
5447 .addClass( "ui-autocomplete-input" )
5448 .attr( "autocomplete", "off" );
5451 keydown: function( event ) {
5452 if ( this.element.prop( "readOnly" ) ) {
5453 suppressKeyPress = true;
5454 suppressInput = true;
5455 suppressKeyPressRepeat = true;
5459 suppressKeyPress = false;
5460 suppressInput = false;
5461 suppressKeyPressRepeat = false;
5462 var keyCode = $.ui.keyCode;
5463 switch( event.keyCode ) {
5464 case keyCode.PAGE_UP:
5465 suppressKeyPress = true;
5466 this._move( "previousPage", event );
5468 case keyCode.PAGE_DOWN:
5469 suppressKeyPress = true;
5470 this._move( "nextPage", event );
5473 suppressKeyPress = true;
5474 this._keyEvent( "previous", event );
5477 suppressKeyPress = true;
5478 this._keyEvent( "next", event );
5481 case keyCode.NUMPAD_ENTER:
5482 // when menu is open and has focus
5483 if ( this.menu.active ) {
5484 // #6055 - Opera still allows the keypress to occur
5485 // which causes forms to submit
5486 suppressKeyPress = true;
5487 event.preventDefault();
5488 this.menu.select( event );
5492 if ( this.menu.active ) {
5493 this.menu.select( event );
5496 case keyCode.ESCAPE:
5497 if ( this.menu.element.is( ":visible" ) ) {
5498 this._value( this.term );
5499 this.close( event );
5500 // Different browsers have different default behavior for escape
5501 // Single press can mean undo or clear
5502 // Double press in IE means clear the whole form
5503 event.preventDefault();
5507 suppressKeyPressRepeat = true;
5508 // search timeout should be triggered before the input value is changed
5509 this._searchTimeout( event );
5513 keypress: function( event ) {
5514 if ( suppressKeyPress ) {
5515 suppressKeyPress = false;
5516 event.preventDefault();
5519 if ( suppressKeyPressRepeat ) {
5523 // replicate some key handlers to allow them to repeat in Firefox and Opera
5524 var keyCode = $.ui.keyCode;
5525 switch( event.keyCode ) {
5526 case keyCode.PAGE_UP:
5527 this._move( "previousPage", event );
5529 case keyCode.PAGE_DOWN:
5530 this._move( "nextPage", event );
5533 this._keyEvent( "previous", event );
5536 this._keyEvent( "next", event );
5540 input: function( event ) {
5541 if ( suppressInput ) {
5542 suppressInput = false;
5543 event.preventDefault();
5546 this._searchTimeout( event );
5549 this.selectedItem = null;
5550 this.previous = this._value();
5552 blur: function( event ) {
5553 if ( this.cancelBlur ) {
5554 delete this.cancelBlur;
5558 clearTimeout( this.searching );
5559 this.close( event );
5560 this._change( event );
5565 this.menu = $( "<ul>" )
5566 .addClass( "ui-autocomplete" )
5567 .appendTo( this.document.find( this.options.appendTo || "body" )[ 0 ] )
5569 // custom key handling for now
5571 // disable ARIA support, the live region takes care of that
5574 .zIndex( this.element.zIndex() + 1 )
5577 this._on( this.menu.element, {
5578 mousedown: function( event ) {
5579 // prevent moving focus out of the text field
5580 event.preventDefault();
5582 // IE doesn't prevent moving focus even with event.preventDefault()
5583 // so we set a flag to know when we should ignore the blur event
5584 this.cancelBlur = true;
5585 this._delay(function() {
5586 delete this.cancelBlur;
5589 // clicking on the scrollbar causes focus to shift to the body
5590 // but we can't detect a mouseup or a click immediately afterward
5591 // so we have to track the next mousedown and close the menu if
5592 // the user clicks somewhere outside of the autocomplete
5593 var menuElement = this.menu.element[ 0 ];
5594 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
5595 this._delay(function() {
5597 this.document.one( "mousedown", function( event ) {
5598 if ( event.target !== that.element[ 0 ] &&
5599 event.target !== menuElement &&
5600 !$.contains( menuElement, event.target ) ) {
5607 menufocus: function( event, ui ) {
5608 // #7024 - Prevent accidental activation of menu items in Firefox
5609 if ( this.isNewMenu ) {
5610 this.isNewMenu = false;
5611 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
5614 this.document.one( "mousemove", function() {
5615 $( event.target ).trigger( event.originalEvent );
5622 // back compat for _renderItem using item.autocomplete, via #7810
5623 // TODO remove the fallback, see #8156
5624 var item = ui.item.data( "ui-autocomplete-item" ) || ui.item.data( "item.autocomplete" );
5625 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
5626 // use value to match what will end up in the input, if it was a key event
5627 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
5628 this._value( item.value );
5631 // Normally the input is populated with the item's value as the
5632 // menu is navigated, causing screen readers to notice a change and
5633 // announce the item. Since the focus event was canceled, this doesn't
5634 // happen, so we update the live region so that screen readers can
5635 // still notice the change and announce it.
5636 this.liveRegion.text( item.value );
5639 menuselect: function( event, ui ) {
5640 // back compat for _renderItem using item.autocomplete, via #7810
5641 // TODO remove the fallback, see #8156
5642 var item = ui.item.data( "ui-autocomplete-item" ) || ui.item.data( "item.autocomplete" ),
5643 previous = this.previous;
5645 // only trigger when focus was lost (click on menu)
5646 if ( this.element[0] !== this.document[0].activeElement ) {
5647 this.element.focus();
5648 this.previous = previous;
5649 // #6109 - IE triggers two focus events and the second
5650 // is asynchronous, so we need to reset the previous
5651 // term synchronously and asynchronously :-(
5652 this._delay(function() {
5653 this.previous = previous;
5654 this.selectedItem = item;
5658 if ( false !== this._trigger( "select", event, { item: item } ) ) {
5659 this._value( item.value );
5661 // reset the term after the select event
5662 // this allows custom select handling to work properly
5663 this.term = this._value();
5665 this.close( event );
5666 this.selectedItem = item;
5670 this.liveRegion = $( "<span>", {
5672 "aria-live": "polite"
5674 .addClass( "ui-helper-hidden-accessible" )
5675 .insertAfter( this.element );
5677 if ( $.fn.bgiframe ) {
5678 this.menu.element.bgiframe();
5681 // turning off autocomplete prevents the browser from remembering the
5682 // value when navigating through history, so we re-enable autocomplete
5683 // if the page is unloaded before the widget is destroyed. #7790
5684 this._on( this.window, {
5685 beforeunload: function() {
5686 this.element.removeAttr( "autocomplete" );
5691 _destroy: function() {
5692 clearTimeout( this.searching );
5694 .removeClass( "ui-autocomplete-input" )
5695 .removeAttr( "autocomplete" );
5696 this.menu.element.remove();
5697 this.liveRegion.remove();
5700 _setOption: function( key, value ) {
5701 this._super( key, value );
5702 if ( key === "source" ) {
5705 if ( key === "appendTo" ) {
5706 this.menu.element.appendTo( this.document.find( value || "body" )[0] );
5708 if ( key === "disabled" && value && this.xhr ) {
5713 _isMultiLine: function() {
5714 // Textareas are always multi-line
5715 if ( this.element.is( "textarea" ) ) {
5718 // Inputs are always single-line, even if inside a contentEditable element
5719 // IE also treats inputs as contentEditable
5720 if ( this.element.is( "input" ) ) {
5723 // All other element types are determined by whether or not they're contentEditable
5724 return this.element.prop( "isContentEditable" );
5727 _initSource: function() {
5730 if ( $.isArray(this.options.source) ) {
5731 array = this.options.source;
5732 this.source = function( request, response ) {
5733 response( $.ui.autocomplete.filter( array, request.term ) );
5735 } else if ( typeof this.options.source === "string" ) {
5736 url = this.options.source;
5737 this.source = function( request, response ) {
5745 success: function( data, status ) {
5754 this.source = this.options.source;
5758 _searchTimeout: function( event ) {
5759 clearTimeout( this.searching );
5760 this.searching = this._delay(function() {
5761 // only search if the value has changed
5762 if ( this.term !== this._value() ) {
5763 this.selectedItem = null;
5764 this.search( null, event );
5766 }, this.options.delay );
5769 search: function( value, event ) {
5770 value = value != null ? value : this._value();
5772 // always save the actual value, not the one passed as an argument
5773 this.term = this._value();
5775 if ( value.length < this.options.minLength ) {
5776 return this.close( event );
5779 if ( this._trigger( "search", event ) === false ) {
5783 return this._search( value );
5786 _search: function( value ) {
5788 this.element.addClass( "ui-autocomplete-loading" );
5789 this.cancelSearch = false;
5791 this.source( { term: value }, this._response() );
5794 _response: function() {
5796 index = ++requestIndex;
5798 return function( content ) {
5799 if ( index === requestIndex ) {
5800 that.__response( content );
5804 if ( !that.pending ) {
5805 that.element.removeClass( "ui-autocomplete-loading" );
5810 __response: function( content ) {
5812 content = this._normalize( content );
5814 this._trigger( "response", null, { content: content } );
5815 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
5816 this._suggest( content );
5817 this._trigger( "open" );
5819 // use ._close() instead of .close() so we don't cancel future searches
5824 close: function( event ) {
5825 this.cancelSearch = true;
5826 this._close( event );
5829 _close: function( event ) {
5830 if ( this.menu.element.is( ":visible" ) ) {
5831 this.menu.element.hide();
5833 this.isNewMenu = true;
5834 this._trigger( "close", event );
5838 _change: function( event ) {
5839 if ( this.previous !== this._value() ) {
5840 this._trigger( "change", event, { item: this.selectedItem } );
5844 _normalize: function( items ) {
5845 // assume all items have the right format when the first item is complete
5846 if ( items.length && items[0].label && items[0].value ) {
5849 return $.map( items, function( item ) {
5850 if ( typeof item === "string" ) {
5857 label: item.label || item.value,
5858 value: item.value || item.label
5863 _suggest: function( items ) {
5864 var ul = this.menu.element
5866 .zIndex( this.element.zIndex() + 1 );
5867 this._renderMenu( ul, items );
5868 this.menu.refresh();
5870 // size and position menu
5873 ul.position( $.extend({
5875 }, this.options.position ));
5877 if ( this.options.autoFocus ) {
5882 _resizeMenu: function() {
5883 var ul = this.menu.element;
5884 ul.outerWidth( Math.max(
5885 // Firefox wraps long text (possibly a rounding bug)
5886 // so we add 1px to avoid the wrapping (#7513)
5887 ul.width( "" ).outerWidth() + 1,
5888 this.element.outerWidth()
5892 _renderMenu: function( ul, items ) {
5894 $.each( items, function( index, item ) {
5895 that._renderItemData( ul, item );
5899 _renderItemData: function( ul, item ) {
5900 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
5903 _renderItem: function( ul, item ) {
5905 .append( $( "<a>" ).text( item.label ) )
5909 _move: function( direction, event ) {
5910 if ( !this.menu.element.is( ":visible" ) ) {
5911 this.search( null, event );
5914 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
5915 this.menu.isLastItem() && /^next/.test( direction ) ) {
5916 this._value( this.term );
5920 this.menu[ direction ]( event );
5923 widget: function() {
5924 return this.menu.element;
5927 _value: function( value ) {
5928 return this.valueMethod.apply( this.element, arguments );
5931 _keyEvent: function( keyEvent, event ) {
5932 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
5933 this._move( keyEvent, event );
5935 // prevents moving cursor to beginning/end of the text field in some browsers
5936 event.preventDefault();
5941 $.extend( $.ui.autocomplete, {
5942 escapeRegex: function( value ) {
5943 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
5945 filter: function(array, term) {
5946 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
5947 return $.grep( array, function(value) {
5948 return matcher.test( value.label || value.value || value );
5954 // live region extension, adding a `messages` option
5955 // NOTE: This is an experimental API. We are still investigating
5956 // a full solution for string manipulation and internationalization.
5957 $.widget( "ui.autocomplete", $.ui.autocomplete, {
5960 noResults: "No search results.",
5961 results: function( amount ) {
5962 return amount + ( amount > 1 ? " results are" : " result is" ) +
5963 " available, use up and down arrow keys to navigate.";
5968 __response: function( content ) {
5970 this._superApply( arguments );
5971 if ( this.options.disabled || this.cancelSearch ) {
5974 if ( content && content.length ) {
5975 message = this.options.messages.results( content.length );
5977 message = this.options.messages.noResults;
5979 this.liveRegion.text( message );
5985 (function( $, undefined ) {
5987 var lastActive, startXPos, startYPos, clickDragged,
5988 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
5989 stateClasses = "ui-state-hover ui-state-active ",
5990 typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
5991 formResetHandler = function() {
5992 var buttons = $( this ).find( ":ui-button" );
5993 setTimeout(function() {
5994 buttons.button( "refresh" );
5997 radioGroup = function( radio ) {
5998 var name = radio.name,
6003 radios = $( form ).find( "[name='" + name + "']" );
6005 radios = $( "[name='" + name + "']", radio.ownerDocument )
6006 .filter(function() {
6014 $.widget( "ui.button", {
6016 defaultElement: "<button>",
6026 _create: function() {
6027 this.element.closest( "form" )
6028 .unbind( "reset" + this.eventNamespace )
6029 .bind( "reset" + this.eventNamespace, formResetHandler );
6031 if ( typeof this.options.disabled !== "boolean" ) {
6032 this.options.disabled = !!this.element.prop( "disabled" );
6034 this.element.prop( "disabled", this.options.disabled );
6037 this._determineButtonType();
6038 this.hasTitle = !!this.buttonElement.attr( "title" );
6041 options = this.options,
6042 toggleButton = this.type === "checkbox" || this.type === "radio",
6043 hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ),
6044 focusClass = "ui-state-focus";
6046 if ( options.label === null ) {
6047 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
6051 .addClass( baseClasses )
6052 .attr( "role", "button" )
6053 .bind( "mouseenter" + this.eventNamespace, function() {
6054 if ( options.disabled ) {
6057 $( this ).addClass( "ui-state-hover" );
6058 if ( this === lastActive ) {
6059 $( this ).addClass( "ui-state-active" );
6062 .bind( "mouseleave" + this.eventNamespace, function() {
6063 if ( options.disabled ) {
6066 $( this ).removeClass( hoverClass );
6068 .bind( "click" + this.eventNamespace, function( event ) {
6069 if ( options.disabled ) {
6070 event.preventDefault();
6071 event.stopImmediatePropagation();
6076 .bind( "focus" + this.eventNamespace, function() {
6077 // no need to check disabled, focus won't be triggered anyway
6078 that.buttonElement.addClass( focusClass );
6080 .bind( "blur" + this.eventNamespace, function() {
6081 that.buttonElement.removeClass( focusClass );
6084 if ( toggleButton ) {
6085 this.element.bind( "change" + this.eventNamespace, function() {
6086 if ( clickDragged ) {
6091 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
6092 // prevents issue where button state changes but checkbox/radio checked state
6093 // does not in Firefox (see ticket #6970)
6095 .bind( "mousedown" + this.eventNamespace, function( event ) {
6096 if ( options.disabled ) {
6099 clickDragged = false;
6100 startXPos = event.pageX;
6101 startYPos = event.pageY;
6103 .bind( "mouseup" + this.eventNamespace, function( event ) {
6104 if ( options.disabled ) {
6107 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
6108 clickDragged = true;
6113 if ( this.type === "checkbox" ) {
6114 this.buttonElement.bind( "click" + this.eventNamespace, function() {
6115 if ( options.disabled || clickDragged ) {
6118 $( this ).toggleClass( "ui-state-active" );
6119 that.buttonElement.attr( "aria-pressed", that.element[0].checked );
6121 } else if ( this.type === "radio" ) {
6122 this.buttonElement.bind( "click" + this.eventNamespace, function() {
6123 if ( options.disabled || clickDragged ) {
6126 $( this ).addClass( "ui-state-active" );
6127 that.buttonElement.attr( "aria-pressed", "true" );
6129 var radio = that.element[ 0 ];
6133 return $( this ).button( "widget" )[ 0 ];
6135 .removeClass( "ui-state-active" )
6136 .attr( "aria-pressed", "false" );
6140 .bind( "mousedown" + this.eventNamespace, function() {
6141 if ( options.disabled ) {
6144 $( this ).addClass( "ui-state-active" );
6146 that.document.one( "mouseup", function() {
6150 .bind( "mouseup" + this.eventNamespace, function() {
6151 if ( options.disabled ) {
6154 $( this ).removeClass( "ui-state-active" );
6156 .bind( "keydown" + this.eventNamespace, function(event) {
6157 if ( options.disabled ) {
6160 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
6161 $( this ).addClass( "ui-state-active" );
6164 .bind( "keyup" + this.eventNamespace, function() {
6165 $( this ).removeClass( "ui-state-active" );
6168 if ( this.buttonElement.is("a") ) {
6169 this.buttonElement.keyup(function(event) {
6170 if ( event.keyCode === $.ui.keyCode.SPACE ) {
6171 // TODO pass through original event correctly (just as 2nd argument doesn't work)
6178 // TODO: pull out $.Widget's handling for the disabled option into
6179 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
6180 // be overridden by individual plugins
6181 this._setOption( "disabled", options.disabled );
6182 this._resetButton();
6185 _determineButtonType: function() {
6186 var ancestor, labelSelector, checked;
6188 if ( this.element.is("[type=checkbox]") ) {
6189 this.type = "checkbox";
6190 } else if ( this.element.is("[type=radio]") ) {
6191 this.type = "radio";
6192 } else if ( this.element.is("input") ) {
6193 this.type = "input";
6195 this.type = "button";
6198 if ( this.type === "checkbox" || this.type === "radio" ) {
6199 // we don't search against the document in case the element
6200 // is disconnected from the DOM
6201 ancestor = this.element.parents().last();
6202 labelSelector = "label[for='" + this.element.attr("id") + "']";
6203 this.buttonElement = ancestor.find( labelSelector );
6204 if ( !this.buttonElement.length ) {
6205 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
6206 this.buttonElement = ancestor.filter( labelSelector );
6207 if ( !this.buttonElement.length ) {
6208 this.buttonElement = ancestor.find( labelSelector );
6211 this.element.addClass( "ui-helper-hidden-accessible" );
6213 checked = this.element.is( ":checked" );
6215 this.buttonElement.addClass( "ui-state-active" );
6217 this.buttonElement.prop( "aria-pressed", checked );
6219 this.buttonElement = this.element;
6223 widget: function() {
6224 return this.buttonElement;
6227 _destroy: function() {
6229 .removeClass( "ui-helper-hidden-accessible" );
6231 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
6232 .removeAttr( "role" )
6233 .removeAttr( "aria-pressed" )
6234 .html( this.buttonElement.find(".ui-button-text").html() );
6236 if ( !this.hasTitle ) {
6237 this.buttonElement.removeAttr( "title" );
6241 _setOption: function( key, value ) {
6242 this._super( key, value );
6243 if ( key === "disabled" ) {
6245 this.element.prop( "disabled", true );
6247 this.element.prop( "disabled", false );
6251 this._resetButton();
6254 refresh: function() {
6255 var isDisabled = this.element.is( ":disabled" );
6256 if ( isDisabled !== this.options.disabled ) {
6257 this._setOption( "disabled", isDisabled );
6259 if ( this.type === "radio" ) {
6260 radioGroup( this.element[0] ).each(function() {
6261 if ( $( this ).is( ":checked" ) ) {
6262 $( this ).button( "widget" )
6263 .addClass( "ui-state-active" )
6264 .attr( "aria-pressed", "true" );
6266 $( this ).button( "widget" )
6267 .removeClass( "ui-state-active" )
6268 .attr( "aria-pressed", "false" );
6271 } else if ( this.type === "checkbox" ) {
6272 if ( this.element.is( ":checked" ) ) {
6274 .addClass( "ui-state-active" )
6275 .attr( "aria-pressed", "true" );
6278 .removeClass( "ui-state-active" )
6279 .attr( "aria-pressed", "false" );
6284 _resetButton: function() {
6285 if ( this.type === "input" ) {
6286 if ( this.options.label ) {
6287 this.element.val( this.options.label );
6291 var buttonElement = this.buttonElement.removeClass( typeClasses ),
6292 buttonText = $( "<span></span>", this.document[0] )
6293 .addClass( "ui-button-text" )
6294 .html( this.options.label )
6295 .appendTo( buttonElement.empty() )
6297 icons = this.options.icons,
6298 multipleIcons = icons.primary && icons.secondary,
6301 if ( icons.primary || icons.secondary ) {
6302 if ( this.options.text ) {
6303 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
6306 if ( icons.primary ) {
6307 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
6310 if ( icons.secondary ) {
6311 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
6314 if ( !this.options.text ) {
6315 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
6317 if ( !this.hasTitle ) {
6318 buttonElement.attr( "title", $.trim( buttonText ) );
6322 buttonClasses.push( "ui-button-text-only" );
6324 buttonElement.addClass( buttonClasses.join( " " ) );
6328 $.widget( "ui.buttonset", {
6331 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(button)"
6334 _create: function() {
6335 this.element.addClass( "ui-buttonset" );
6342 _setOption: function( key, value ) {
6343 if ( key === "disabled" ) {
6344 this.buttons.button( "option", key, value );
6347 this._super( key, value );
6350 refresh: function() {
6351 var rtl = this.element.css( "direction" ) === "rtl";
6353 this.buttons = this.element.find( this.options.items )
6354 .filter( ":ui-button" )
6355 .button( "refresh" )
6357 .not( ":ui-button" )
6361 return $( this ).button( "widget" )[ 0 ];
6363 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
6365 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
6368 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
6373 _destroy: function() {
6374 this.element.removeClass( "ui-buttonset" );
6377 return $( this ).button( "widget" )[ 0 ];
6379 .removeClass( "ui-corner-left ui-corner-right" )
6381 .button( "destroy" );
6386 (function( $, undefined ) {
6388 $.extend($.ui, { datepicker: { version: "1.9.0" } });
6390 var PROP_NAME = 'datepicker';
6391 var dpuuid = new Date().getTime();
6394 /* Date picker manager.
6395 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
6396 Settings for (groups of) date pickers are maintained in an instance object,
6397 allowing multiple different settings on the same page. */
6399 function Datepicker() {
6400 this.debug = false; // Change this to true to start debugging
6401 this._curInst = null; // The current instance in use
6402 this._keyEvent = false; // If the last event was a key event
6403 this._disabledInputs = []; // List of date picker inputs that have been disabled
6404 this._datepickerShowing = false; // True if the popup picker is showing , false if not
6405 this._inDialog = false; // True if showing within a "dialog", false if not
6406 this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
6407 this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
6408 this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
6409 this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
6410 this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
6411 this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
6412 this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
6413 this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
6414 this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
6415 this.regional = []; // Available regional settings, indexed by language code
6416 this.regional[''] = { // Default regional settings
6417 closeText: 'Done', // Display text for close link
6418 prevText: 'Prev', // Display text for previous month link
6419 nextText: 'Next', // Display text for next month link
6420 currentText: 'Today', // Display text for current month link
6421 monthNames: ['January','February','March','April','May','June',
6422 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
6423 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
6424 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
6425 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
6426 dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
6427 weekHeader: 'Wk', // Column header for week of the year
6428 dateFormat: 'mm/dd/yy', // See format options on parseDate
6429 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
6430 isRTL: false, // True if right-to-left language, false if left-to-right
6431 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
6432 yearSuffix: '' // Additional text to append to the year in the month headers
6434 this._defaults = { // Global defaults for all the date picker instances
6435 showOn: 'focus', // 'focus' for popup on focus,
6436 // 'button' for trigger button, or 'both' for either
6437 showAnim: 'fadeIn', // Name of jQuery animation for popup
6438 showOptions: {}, // Options for enhanced animations
6439 defaultDate: null, // Used when field is blank: actual date,
6440 // +/-number for offset from today, null for today
6441 appendText: '', // Display text following the input box, e.g. showing the format
6442 buttonText: '...', // Text for trigger button
6443 buttonImage: '', // URL for trigger button image
6444 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
6445 hideIfNoPrevNext: false, // True to hide next/previous month links
6446 // if not applicable, false to just disable them
6447 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
6448 gotoCurrent: false, // True if today link goes back to current selection instead
6449 changeMonth: false, // True if month can be selected directly, false if only prev/next
6450 changeYear: false, // True if year can be selected directly, false if only prev/next
6451 yearRange: 'c-10:c+10', // Range of years to display in drop-down,
6452 // either relative to today's year (-nn:+nn), relative to currently displayed year
6453 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
6454 showOtherMonths: false, // True to show dates in other months, false to leave blank
6455 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
6456 showWeek: false, // True to show week of the year, false to not show it
6457 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
6458 // takes a Date and returns the number of the week for it
6459 shortYearCutoff: '+10', // Short year values < this are in the current century,
6460 // > this are in the previous century,
6461 // string value starting with '+' for current year + value
6462 minDate: null, // The earliest selectable date, or null for no limit
6463 maxDate: null, // The latest selectable date, or null for no limit
6464 duration: 'fast', // Duration of display/closure
6465 beforeShowDay: null, // Function that takes a date and returns an array with
6466 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
6467 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
6468 beforeShow: null, // Function that takes an input field and
6469 // returns a set of custom settings for the date picker
6470 onSelect: null, // Define a callback function when a date is selected
6471 onChangeMonthYear: null, // Define a callback function when the month or year is changed
6472 onClose: null, // Define a callback function when the datepicker is closed
6473 numberOfMonths: 1, // Number of months to show at a time
6474 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
6475 stepMonths: 1, // Number of months to step back/forward
6476 stepBigMonths: 12, // Number of months to step back/forward for the big links
6477 altField: '', // Selector for an alternate field to store selected dates into
6478 altFormat: '', // The date format to use for the alternate field
6479 constrainInput: true, // The input is constrained by the current date format
6480 showButtonPanel: false, // True to show button panel, false to not show it
6481 autoSize: false, // True to size the input for the date format, false to leave as is
6482 disabled: false // The initial disabled state
6484 $.extend(this._defaults, this.regional['']);
6485 this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
6488 $.extend(Datepicker.prototype, {
6489 /* Class name added to elements to indicate already configured with a date picker. */
6490 markerClassName: 'hasDatepicker',
6492 //Keep track of the maximum number of rows displayed (see #7043)
6495 /* Debug logging (if enabled). */
6498 console.log.apply('', arguments);
6501 // TODO rename to "widget" when switching to widget factory
6502 _widgetDatepicker: function() {
6506 /* Override the default settings for all instances of the date picker.
6507 @param settings object - the new settings to use as defaults (anonymous object)
6508 @return the manager object */
6509 setDefaults: function(settings) {
6510 extendRemove(this._defaults, settings || {});
6514 /* Attach the date picker to a jQuery selection.
6515 @param target element - the target input field or division or span
6516 @param settings object - the new settings to use for this date picker instance (anonymous) */
6517 _attachDatepicker: function(target, settings) {
6518 // check for settings on the control itself - in namespace 'date:'
6519 var inlineSettings = null;
6520 for (var attrName in this._defaults) {
6521 var attrValue = target.getAttribute('date:' + attrName);
6523 inlineSettings = inlineSettings || {};
6525 inlineSettings[attrName] = eval(attrValue);
6527 inlineSettings[attrName] = attrValue;
6531 var nodeName = target.nodeName.toLowerCase();
6532 var inline = (nodeName == 'div' || nodeName == 'span');
6535 target.id = 'dp' + this.uuid;
6537 var inst = this._newInst($(target), inline);
6538 inst.settings = $.extend({}, settings || {}, inlineSettings || {});
6539 if (nodeName == 'input') {
6540 this._connectDatepicker(target, inst);
6541 } else if (inline) {
6542 this._inlineDatepicker(target, inst);
6546 /* Create a new instance object. */
6547 _newInst: function(target, inline) {
6548 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
6549 return {id: id, input: target, // associated target
6550 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
6551 drawMonth: 0, drawYear: 0, // month being drawn
6552 inline: inline, // is datepicker inline or not
6553 dpDiv: (!inline ? this.dpDiv : // presentation div
6554 bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
6557 /* Attach the date picker to an input field. */
6558 _connectDatepicker: function(target, inst) {
6559 var input = $(target);
6560 inst.append = $([]);
6561 inst.trigger = $([]);
6562 if (input.hasClass(this.markerClassName))
6564 this._attachments(input, inst);
6565 input.addClass(this.markerClassName).keydown(this._doKeyDown).
6566 keypress(this._doKeyPress).keyup(this._doKeyUp).
6567 bind("setData.datepicker", function(event, key, value) {
6568 inst.settings[key] = value;
6569 }).bind("getData.datepicker", function(event, key) {
6570 return this._get(inst, key);
6572 this._autoSize(inst);
6573 $.data(target, PROP_NAME, inst);
6574 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
6575 if( inst.settings.disabled ) {
6576 this._disableDatepicker( target );
6580 /* Make attachments based on settings. */
6581 _attachments: function(input, inst) {
6582 var appendText = this._get(inst, 'appendText');
6583 var isRTL = this._get(inst, 'isRTL');
6585 inst.append.remove();
6587 inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
6588 input[isRTL ? 'before' : 'after'](inst.append);
6590 input.unbind('focus', this._showDatepicker);
6592 inst.trigger.remove();
6593 var showOn = this._get(inst, 'showOn');
6594 if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
6595 input.focus(this._showDatepicker);
6596 if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
6597 var buttonText = this._get(inst, 'buttonText');
6598 var buttonImage = this._get(inst, 'buttonImage');
6599 inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
6600 $('<img/>').addClass(this._triggerClass).
6601 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
6602 $('<button type="button"></button>').addClass(this._triggerClass).
6603 html(buttonImage == '' ? buttonText : $('<img/>').attr(
6604 { src:buttonImage, alt:buttonText, title:buttonText })));
6605 input[isRTL ? 'before' : 'after'](inst.trigger);
6606 inst.trigger.click(function() {
6607 if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
6608 $.datepicker._hideDatepicker();
6609 else if ($.datepicker._datepickerShowing && $.datepicker._lastInput != input[0]) {
6610 $.datepicker._hideDatepicker();
6611 $.datepicker._showDatepicker(input[0]);
6613 $.datepicker._showDatepicker(input[0]);
6619 /* Apply the maximum length for the date format. */
6620 _autoSize: function(inst) {
6621 if (this._get(inst, 'autoSize') && !inst.inline) {
6622 var date = new Date(2009, 12 - 1, 20); // Ensure double digits
6623 var dateFormat = this._get(inst, 'dateFormat');
6624 if (dateFormat.match(/[DM]/)) {
6625 var findMax = function(names) {
6628 for (var i = 0; i < names.length; i++) {
6629 if (names[i].length > max) {
6630 max = names[i].length;
6636 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
6637 'monthNames' : 'monthNamesShort'))));
6638 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
6639 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
6641 inst.input.attr('size', this._formatDate(inst, date).length);
6645 /* Attach an inline date picker to a div. */
6646 _inlineDatepicker: function(target, inst) {
6647 var divSpan = $(target);
6648 if (divSpan.hasClass(this.markerClassName))
6650 divSpan.addClass(this.markerClassName).append(inst.dpDiv).
6651 bind("setData.datepicker", function(event, key, value){
6652 inst.settings[key] = value;
6653 }).bind("getData.datepicker", function(event, key){
6654 return this._get(inst, key);
6656 $.data(target, PROP_NAME, inst);
6657 this._setDate(inst, this._getDefaultDate(inst), true);
6658 this._updateDatepicker(inst);
6659 this._updateAlternate(inst);
6660 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
6661 if( inst.settings.disabled ) {
6662 this._disableDatepicker( target );
6664 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
6665 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
6666 inst.dpDiv.css( "display", "block" );
6669 /* Pop-up the date picker in a "dialog" box.
6670 @param input element - ignored
6671 @param date string or Date - the initial date to display
6672 @param onSelect function - the function to call when a date is selected
6673 @param settings object - update the dialog date picker instance's settings (anonymous object)
6674 @param pos int[2] - coordinates for the dialog's position within the screen or
6675 event - with x/y coordinates or
6676 leave empty for default (screen centre)
6677 @return the manager object */
6678 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
6679 var inst = this._dialogInst; // internal instance
6682 var id = 'dp' + this.uuid;
6683 this._dialogInput = $('<input type="text" id="' + id +
6684 '" style="position: absolute; top: -100px; width: 0px;"/>');
6685 this._dialogInput.keydown(this._doKeyDown);
6686 $('body').append(this._dialogInput);
6687 inst = this._dialogInst = this._newInst(this._dialogInput, false);
6689 $.data(this._dialogInput[0], PROP_NAME, inst);
6691 extendRemove(inst.settings, settings || {});
6692 date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
6693 this._dialogInput.val(date);
6695 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
6697 var browserWidth = document.documentElement.clientWidth;
6698 var browserHeight = document.documentElement.clientHeight;
6699 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
6700 var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
6701 this._pos = // should use actual width/height below
6702 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
6705 // move input on screen for focus, but hidden behind dialog
6706 this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
6707 inst.settings.onSelect = onSelect;
6708 this._inDialog = true;
6709 this.dpDiv.addClass(this._dialogClass);
6710 this._showDatepicker(this._dialogInput[0]);
6712 $.blockUI(this.dpDiv);
6713 $.data(this._dialogInput[0], PROP_NAME, inst);
6717 /* Detach a datepicker from its control.
6718 @param target element - the target input field or division or span */
6719 _destroyDatepicker: function(target) {
6720 var $target = $(target);
6721 var inst = $.data(target, PROP_NAME);
6722 if (!$target.hasClass(this.markerClassName)) {
6725 var nodeName = target.nodeName.toLowerCase();
6726 $.removeData(target, PROP_NAME);
6727 if (nodeName == 'input') {
6728 inst.append.remove();
6729 inst.trigger.remove();
6730 $target.removeClass(this.markerClassName).
6731 unbind('focus', this._showDatepicker).
6732 unbind('keydown', this._doKeyDown).
6733 unbind('keypress', this._doKeyPress).
6734 unbind('keyup', this._doKeyUp);
6735 } else if (nodeName == 'div' || nodeName == 'span')
6736 $target.removeClass(this.markerClassName).empty();
6739 /* Enable the date picker to a jQuery selection.
6740 @param target element - the target input field or division or span */
6741 _enableDatepicker: function(target) {
6742 var $target = $(target);
6743 var inst = $.data(target, PROP_NAME);
6744 if (!$target.hasClass(this.markerClassName)) {
6747 var nodeName = target.nodeName.toLowerCase();
6748 if (nodeName == 'input') {
6749 target.disabled = false;
6750 inst.trigger.filter('button').
6751 each(function() { this.disabled = false; }).end().
6752 filter('img').css({opacity: '1.0', cursor: ''});
6754 else if (nodeName == 'div' || nodeName == 'span') {
6755 var inline = $target.children('.' + this._inlineClass);
6756 inline.children().removeClass('ui-state-disabled');
6757 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
6758 prop("disabled", false);
6760 this._disabledInputs = $.map(this._disabledInputs,
6761 function(value) { return (value == target ? null : value); }); // delete entry
6764 /* Disable the date picker to a jQuery selection.
6765 @param target element - the target input field or division or span */
6766 _disableDatepicker: function(target) {
6767 var $target = $(target);
6768 var inst = $.data(target, PROP_NAME);
6769 if (!$target.hasClass(this.markerClassName)) {
6772 var nodeName = target.nodeName.toLowerCase();
6773 if (nodeName == 'input') {
6774 target.disabled = true;
6775 inst.trigger.filter('button').
6776 each(function() { this.disabled = true; }).end().
6777 filter('img').css({opacity: '0.5', cursor: 'default'});
6779 else if (nodeName == 'div' || nodeName == 'span') {
6780 var inline = $target.children('.' + this._inlineClass);
6781 inline.children().addClass('ui-state-disabled');
6782 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
6783 prop("disabled", true);
6785 this._disabledInputs = $.map(this._disabledInputs,
6786 function(value) { return (value == target ? null : value); }); // delete entry
6787 this._disabledInputs[this._disabledInputs.length] = target;
6790 /* Is the first field in a jQuery collection disabled as a datepicker?
6791 @param target element - the target input field or division or span
6792 @return boolean - true if disabled, false if enabled */
6793 _isDisabledDatepicker: function(target) {
6797 for (var i = 0; i < this._disabledInputs.length; i++) {
6798 if (this._disabledInputs[i] == target)
6804 /* Retrieve the instance data for the target control.
6805 @param target element - the target input field or division or span
6806 @return object - the associated instance data
6807 @throws error if a jQuery problem getting data */
6808 _getInst: function(target) {
6810 return $.data(target, PROP_NAME);
6813 throw 'Missing instance data for this datepicker';
6817 /* Update or retrieve the settings for a date picker attached to an input field or division.
6818 @param target element - the target input field or division or span
6819 @param name object - the new settings to update or
6820 string - the name of the setting to change or retrieve,
6821 when retrieving also 'all' for all instance settings or
6822 'defaults' for all global defaults
6823 @param value any - the new value for the setting
6824 (omit if above is an object or to retrieve a value) */
6825 _optionDatepicker: function(target, name, value) {
6826 var inst = this._getInst(target);
6827 if (arguments.length == 2 && typeof name == 'string') {
6828 return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
6829 (inst ? (name == 'all' ? $.extend({}, inst.settings) :
6830 this._get(inst, name)) : null));
6832 var settings = name || {};
6833 if (typeof name == 'string') {
6835 settings[name] = value;
6838 if (this._curInst == inst) {
6839 this._hideDatepicker();
6841 var date = this._getDateDatepicker(target, true);
6842 var minDate = this._getMinMaxDate(inst, 'min');
6843 var maxDate = this._getMinMaxDate(inst, 'max');
6844 extendRemove(inst.settings, settings);
6845 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
6846 if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
6847 inst.settings.minDate = this._formatDate(inst, minDate);
6848 if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
6849 inst.settings.maxDate = this._formatDate(inst, maxDate);
6850 this._attachments($(target), inst);
6851 this._autoSize(inst);
6852 this._setDate(inst, date);
6853 this._updateAlternate(inst);
6854 this._updateDatepicker(inst);
6858 // change method deprecated
6859 _changeDatepicker: function(target, name, value) {
6860 this._optionDatepicker(target, name, value);
6863 /* Redraw the date picker attached to an input field or division.
6864 @param target element - the target input field or division or span */
6865 _refreshDatepicker: function(target) {
6866 var inst = this._getInst(target);
6868 this._updateDatepicker(inst);
6872 /* Set the dates for a jQuery selection.
6873 @param target element - the target input field or division or span
6874 @param date Date - the new date */
6875 _setDateDatepicker: function(target, date) {
6876 var inst = this._getInst(target);
6878 this._setDate(inst, date);
6879 this._updateDatepicker(inst);
6880 this._updateAlternate(inst);
6884 /* Get the date(s) for the first entry in a jQuery selection.
6885 @param target element - the target input field or division or span
6886 @param noDefault boolean - true if no default date is to be used
6887 @return Date - the current date */
6888 _getDateDatepicker: function(target, noDefault) {
6889 var inst = this._getInst(target);
6890 if (inst && !inst.inline)
6891 this._setDateFromField(inst, noDefault);
6892 return (inst ? this._getDate(inst) : null);
6895 /* Handle keystrokes. */
6896 _doKeyDown: function(event) {
6897 var inst = $.datepicker._getInst(event.target);
6899 var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
6900 inst._keyEvent = true;
6901 if ($.datepicker._datepickerShowing)
6902 switch (event.keyCode) {
6903 case 9: $.datepicker._hideDatepicker();
6905 break; // hide on tab out
6906 case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
6907 $.datepicker._currentClass + ')', inst.dpDiv);
6909 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
6910 var onSelect = $.datepicker._get(inst, 'onSelect');
6912 var dateStr = $.datepicker._formatDate(inst);
6914 // trigger custom callback
6915 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
6918 $.datepicker._hideDatepicker();
6919 return false; // don't submit the form
6920 break; // select the value on enter
6921 case 27: $.datepicker._hideDatepicker();
6922 break; // hide on escape
6923 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
6924 -$.datepicker._get(inst, 'stepBigMonths') :
6925 -$.datepicker._get(inst, 'stepMonths')), 'M');
6926 break; // previous month/year on page up/+ ctrl
6927 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
6928 +$.datepicker._get(inst, 'stepBigMonths') :
6929 +$.datepicker._get(inst, 'stepMonths')), 'M');
6930 break; // next month/year on page down/+ ctrl
6931 case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
6932 handled = event.ctrlKey || event.metaKey;
6933 break; // clear on ctrl or command +end
6934 case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
6935 handled = event.ctrlKey || event.metaKey;
6936 break; // current on ctrl or command +home
6937 case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
6938 handled = event.ctrlKey || event.metaKey;
6939 // -1 day on ctrl or command +left
6940 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
6941 -$.datepicker._get(inst, 'stepBigMonths') :
6942 -$.datepicker._get(inst, 'stepMonths')), 'M');
6943 // next month/year on alt +left on Mac
6945 case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
6946 handled = event.ctrlKey || event.metaKey;
6947 break; // -1 week on ctrl or command +up
6948 case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
6949 handled = event.ctrlKey || event.metaKey;
6950 // +1 day on ctrl or command +right
6951 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
6952 +$.datepicker._get(inst, 'stepBigMonths') :
6953 +$.datepicker._get(inst, 'stepMonths')), 'M');
6954 // next month/year on alt +right
6956 case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
6957 handled = event.ctrlKey || event.metaKey;
6958 break; // +1 week on ctrl or command +down
6959 default: handled = false;
6961 else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
6962 $.datepicker._showDatepicker(this);
6967 event.preventDefault();
6968 event.stopPropagation();
6972 /* Filter entered characters - based on date format. */
6973 _doKeyPress: function(event) {
6974 var inst = $.datepicker._getInst(event.target);
6975 if ($.datepicker._get(inst, 'constrainInput')) {
6976 var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
6977 var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
6978 return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
6982 /* Synchronise manual entry and field/alternate field. */
6983 _doKeyUp: function(event) {
6984 var inst = $.datepicker._getInst(event.target);
6985 if (inst.input.val() != inst.lastVal) {
6987 var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
6988 (inst.input ? inst.input.val() : null),
6989 $.datepicker._getFormatConfig(inst));
6990 if (date) { // only if valid
6991 $.datepicker._setDateFromField(inst);
6992 $.datepicker._updateAlternate(inst);
6993 $.datepicker._updateDatepicker(inst);
6997 $.datepicker.log(err);
7003 /* Pop-up the date picker for a given input field.
7004 If false returned from beforeShow event handler do not show.
7005 @param input element - the input field attached to the date picker or
7006 event - if triggered by focus */
7007 _showDatepicker: function(input) {
7008 input = input.target || input;
7009 if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
7010 input = $('input', input.parentNode)[0];
7011 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
7013 var inst = $.datepicker._getInst(input);
7014 if ($.datepicker._curInst && $.datepicker._curInst != inst) {
7015 $.datepicker._curInst.dpDiv.stop(true, true);
7016 if ( inst && $.datepicker._datepickerShowing ) {
7017 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
7020 var beforeShow = $.datepicker._get(inst, 'beforeShow');
7021 var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
7022 if(beforeShowSettings === false){
7026 extendRemove(inst.settings, beforeShowSettings);
7027 inst.lastVal = null;
7028 $.datepicker._lastInput = input;
7029 $.datepicker._setDateFromField(inst);
7030 if ($.datepicker._inDialog) // hide cursor
7032 if (!$.datepicker._pos) { // position below input
7033 $.datepicker._pos = $.datepicker._findPos(input);
7034 $.datepicker._pos[1] += input.offsetHeight; // add the height
7036 var isFixed = false;
7037 $(input).parents().each(function() {
7038 isFixed |= $(this).css('position') == 'fixed';
7041 var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
7042 $.datepicker._pos = null;
7043 //to avoid flashes on Firefox
7045 // determine sizing offscreen
7046 inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
7047 $.datepicker._updateDatepicker(inst);
7048 // fix width for dynamic number of date pickers
7049 // and adjust position before showing
7050 offset = $.datepicker._checkOffset(inst, offset, isFixed);
7051 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
7052 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
7053 left: offset.left + 'px', top: offset.top + 'px'});
7055 var showAnim = $.datepicker._get(inst, 'showAnim');
7056 var duration = $.datepicker._get(inst, 'duration');
7057 var postProcess = function() {
7058 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
7059 if( !! cover.length ){
7060 var borders = $.datepicker._getBorders(inst.dpDiv);
7061 cover.css({left: -borders[0], top: -borders[1],
7062 width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
7065 inst.dpDiv.zIndex($(input).zIndex()+1);
7066 $.datepicker._datepickerShowing = true;
7068 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
7069 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
7070 inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
7072 inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
7073 if (!showAnim || !duration)
7075 if (inst.input.is(':visible') && !inst.input.is(':disabled'))
7077 $.datepicker._curInst = inst;
7081 /* Generate the date picker content. */
7082 _updateDatepicker: function(inst) {
7083 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
7084 var borders = $.datepicker._getBorders(inst.dpDiv);
7085 instActive = inst; // for delegate hover events
7086 inst.dpDiv.empty().append(this._generateHTML(inst));
7087 this._attachHandlers(inst);
7088 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
7089 if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
7090 cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
7092 inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
7093 var numMonths = this._getNumberOfMonths(inst);
7094 var cols = numMonths[1];
7096 inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
7098 inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
7099 inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
7100 'Class']('ui-datepicker-multi');
7101 inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
7102 'Class']('ui-datepicker-rtl');
7103 if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
7104 // #6694 - don't focus the input if it's already focused
7105 // this breaks the change event in IE
7106 inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
7108 // deffered render of the years select (to avoid flashes on Firefox)
7109 if( inst.yearshtml ){
7110 var origyearshtml = inst.yearshtml;
7111 setTimeout(function(){
7112 //assure that inst.yearshtml didn't change.
7113 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
7114 inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
7116 origyearshtml = inst.yearshtml = null;
7121 /* Retrieve the size of left and top borders for an element.
7122 @param elem (jQuery object) the element of interest
7123 @return (number[2]) the left and top borders */
7124 _getBorders: function(elem) {
7125 var convert = function(value) {
7126 return {thin: 1, medium: 2, thick: 3}[value] || value;
7128 return [parseFloat(convert(elem.css('border-left-width'))),
7129 parseFloat(convert(elem.css('border-top-width')))];
7132 /* Check positioning to remain on screen. */
7133 _checkOffset: function(inst, offset, isFixed) {
7134 var dpWidth = inst.dpDiv.outerWidth();
7135 var dpHeight = inst.dpDiv.outerHeight();
7136 var inputWidth = inst.input ? inst.input.outerWidth() : 0;
7137 var inputHeight = inst.input ? inst.input.outerHeight() : 0;
7138 var viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft());
7139 var viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
7141 offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
7142 offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
7143 offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
7145 // now check if datepicker is showing outside window viewport - move to a better place if so.
7146 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
7147 Math.abs(offset.left + dpWidth - viewWidth) : 0);
7148 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
7149 Math.abs(dpHeight + inputHeight) : 0);
7154 /* Find an object's position on the screen. */
7155 _findPos: function(obj) {
7156 var inst = this._getInst(obj);
7157 var isRTL = this._get(inst, 'isRTL');
7158 while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
7159 obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
7161 var position = $(obj).offset();
7162 return [position.left, position.top];
7165 /* Hide the date picker from view.
7166 @param input element - the input field attached to the date picker */
7167 _hideDatepicker: function(input) {
7168 var inst = this._curInst;
7169 if (!inst || (input && inst != $.data(input, PROP_NAME)))
7171 if (this._datepickerShowing) {
7172 var showAnim = this._get(inst, 'showAnim');
7173 var duration = this._get(inst, 'duration');
7174 var postProcess = function() {
7175 $.datepicker._tidyDialog(inst);
7178 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
7179 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
7180 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
7182 inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
7183 (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
7186 this._datepickerShowing = false;
7187 var onClose = this._get(inst, 'onClose');
7189 onClose.apply((inst.input ? inst.input[0] : null),
7190 [(inst.input ? inst.input.val() : ''), inst]);
7191 this._lastInput = null;
7192 if (this._inDialog) {
7193 this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
7196 $('body').append(this.dpDiv);
7199 this._inDialog = false;
7203 /* Tidy up after a dialog display. */
7204 _tidyDialog: function(inst) {
7205 inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
7208 /* Close date picker if clicked elsewhere. */
7209 _checkExternalClick: function(event) {
7210 if (!$.datepicker._curInst)
7213 var $target = $(event.target),
7214 inst = $.datepicker._getInst($target[0]);
7216 if ( ( ( $target[0].id != $.datepicker._mainDivId &&
7217 $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
7218 !$target.hasClass($.datepicker.markerClassName) &&
7219 !$target.closest("." + $.datepicker._triggerClass).length &&
7220 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
7221 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
7222 $.datepicker._hideDatepicker();
7225 /* Adjust one of the date sub-fields. */
7226 _adjustDate: function(id, offset, period) {
7228 var inst = this._getInst(target[0]);
7229 if (this._isDisabledDatepicker(target[0])) {
7232 this._adjustInstDate(inst, offset +
7233 (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
7235 this._updateDatepicker(inst);
7238 /* Action for current link. */
7239 _gotoToday: function(id) {
7241 var inst = this._getInst(target[0]);
7242 if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
7243 inst.selectedDay = inst.currentDay;
7244 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
7245 inst.drawYear = inst.selectedYear = inst.currentYear;
7248 var date = new Date();
7249 inst.selectedDay = date.getDate();
7250 inst.drawMonth = inst.selectedMonth = date.getMonth();
7251 inst.drawYear = inst.selectedYear = date.getFullYear();
7253 this._notifyChange(inst);
7254 this._adjustDate(target);
7257 /* Action for selecting a new month/year. */
7258 _selectMonthYear: function(id, select, period) {
7260 var inst = this._getInst(target[0]);
7261 inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
7262 inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
7263 parseInt(select.options[select.selectedIndex].value,10);
7264 this._notifyChange(inst);
7265 this._adjustDate(target);
7268 /* Action for selecting a day. */
7269 _selectDay: function(id, month, year, td) {
7271 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
7274 var inst = this._getInst(target[0]);
7275 inst.selectedDay = inst.currentDay = $('a', td).html();
7276 inst.selectedMonth = inst.currentMonth = month;
7277 inst.selectedYear = inst.currentYear = year;
7278 this._selectDate(id, this._formatDate(inst,
7279 inst.currentDay, inst.currentMonth, inst.currentYear));
7282 /* Erase the input field and hide the date picker. */
7283 _clearDate: function(id) {
7285 var inst = this._getInst(target[0]);
7286 this._selectDate(target, '');
7289 /* Update the input field with the selected date. */
7290 _selectDate: function(id, dateStr) {
7292 var inst = this._getInst(target[0]);
7293 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
7295 inst.input.val(dateStr);
7296 this._updateAlternate(inst);
7297 var onSelect = this._get(inst, 'onSelect');
7299 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
7300 else if (inst.input)
7301 inst.input.trigger('change'); // fire the change event
7303 this._updateDatepicker(inst);
7305 this._hideDatepicker();
7306 this._lastInput = inst.input[0];
7307 if (typeof(inst.input[0]) != 'object')
7308 inst.input.focus(); // restore focus
7309 this._lastInput = null;
7313 /* Update any alternate field to synchronise with the main field. */
7314 _updateAlternate: function(inst) {
7315 var altField = this._get(inst, 'altField');
7316 if (altField) { // update alternate field too
7317 var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
7318 var date = this._getDate(inst);
7319 var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
7320 $(altField).each(function() { $(this).val(dateStr); });
7324 /* Set as beforeShowDay function to prevent selection of weekends.
7325 @param date Date - the date to customise
7326 @return [boolean, string] - is this date selectable?, what is its CSS class? */
7327 noWeekends: function(date) {
7328 var day = date.getDay();
7329 return [(day > 0 && day < 6), ''];
7332 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
7333 @param date Date - the date to get the week for
7334 @return number - the number of the week within the year that contains this date */
7335 iso8601Week: function(date) {
7336 var checkDate = new Date(date.getTime());
7337 // Find Thursday of this week starting on Monday
7338 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
7339 var time = checkDate.getTime();
7340 checkDate.setMonth(0); // Compare with Jan 1
7341 checkDate.setDate(1);
7342 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
7345 /* Parse a string value into a date object.
7346 See formatDate below for the possible formats.
7348 @param format string - the expected format of the date
7349 @param value string - the date in the above format
7350 @param settings Object - attributes include:
7351 shortYearCutoff number - the cutoff year for determining the century (optional)
7352 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
7353 dayNames string[7] - names of the days from Sunday (optional)
7354 monthNamesShort string[12] - abbreviated names of the months (optional)
7355 monthNames string[12] - names of the months (optional)
7356 @return Date - the extracted date value or null if value is blank */
7357 parseDate: function (format, value, settings) {
7358 if (format == null || value == null)
7359 throw 'Invalid arguments';
7360 value = (typeof value == 'object' ? value.toString() : value + '');
7363 var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
7364 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
7365 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
7366 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
7367 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
7368 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
7369 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
7374 var literal = false;
7375 // Check whether a format character is doubled
7376 var lookAhead = function(match) {
7377 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
7382 // Extract a number from the string value
7383 var getNumber = function(match) {
7384 var isDoubled = lookAhead(match);
7385 var size = (match == '@' ? 14 : (match == '!' ? 20 :
7386 (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
7387 var digits = new RegExp('^\\d{1,' + size + '}');
7388 var num = value.substring(iValue).match(digits);
7390 throw 'Missing number at position ' + iValue;
7391 iValue += num[0].length;
7392 return parseInt(num[0], 10);
7394 // Extract a name from the string value and convert to an index
7395 var getName = function(match, shortNames, longNames) {
7396 var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
7398 }).sort(function (a, b) {
7399 return -(a[1].length - b[1].length);
7402 $.each(names, function (i, pair) {
7404 if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
7406 iValue += name.length;
7413 throw 'Unknown name at position ' + iValue;
7415 // Confirm that a literal character matches the string value
7416 var checkLiteral = function() {
7417 if (value.charAt(iValue) != format.charAt(iFormat))
7418 throw 'Unexpected literal at position ' + iValue;
7422 for (var iFormat = 0; iFormat < format.length; iFormat++) {
7424 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
7429 switch (format.charAt(iFormat)) {
7431 day = getNumber('d');
7434 getName('D', dayNamesShort, dayNames);
7437 doy = getNumber('o');
7440 month = getNumber('m');
7443 month = getName('M', monthNamesShort, monthNames);
7446 year = getNumber('y');
7449 var date = new Date(getNumber('@'));
7450 year = date.getFullYear();
7451 month = date.getMonth() + 1;
7452 day = date.getDate();
7455 var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
7456 year = date.getFullYear();
7457 month = date.getMonth() + 1;
7458 day = date.getDate();
7470 if (iValue < value.length){
7471 var extra = value.substr(iValue);
7472 if (!/^\s+/.test(extra)) {
7473 throw "Extra/unparsed characters found in date: " + extra;
7477 year = new Date().getFullYear();
7478 else if (year < 100)
7479 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
7480 (year <= shortYearCutoff ? 0 : -100);
7485 var dim = this._getDaysInMonth(year, month - 1);
7492 var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
7493 if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
7494 throw 'Invalid date'; // E.g. 31/02/00
7498 /* Standard date formats. */
7499 ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
7500 COOKIE: 'D, dd M yy',
7501 ISO_8601: 'yy-mm-dd',
7502 RFC_822: 'D, d M y',
7503 RFC_850: 'DD, dd-M-y',
7504 RFC_1036: 'D, d M y',
7505 RFC_1123: 'D, d M yy',
7506 RFC_2822: 'D, d M yy',
7507 RSS: 'D, d M y', // RFC 822
7510 W3C: 'yy-mm-dd', // ISO 8601
7512 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
7513 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
7515 /* Format a date object into a string value.
7516 The format can be combinations of the following:
7517 d - day of month (no leading zero)
7518 dd - day of month (two digit)
7519 o - day of year (no leading zeros)
7520 oo - day of year (three digit)
7523 m - month of year (no leading zero)
7524 mm - month of year (two digit)
7525 M - month name short
7526 MM - month name long
7527 y - year (two digit)
7528 yy - year (four digit)
7529 @ - Unix timestamp (ms since 01/01/1970)
7530 ! - Windows ticks (100ns since 01/01/0001)
7531 '...' - literal text
7534 @param format string - the desired format of the date
7535 @param date Date - the date value to format
7536 @param settings Object - attributes include:
7537 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
7538 dayNames string[7] - names of the days from Sunday (optional)
7539 monthNamesShort string[12] - abbreviated names of the months (optional)
7540 monthNames string[12] - names of the months (optional)
7541 @return string - the date in the above format */
7542 formatDate: function (format, date, settings) {
7545 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
7546 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
7547 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
7548 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
7549 // Check whether a format character is doubled
7550 var lookAhead = function(match) {
7551 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
7556 // Format a number, with leading zero if necessary
7557 var formatNumber = function(match, value, len) {
7558 var num = '' + value;
7559 if (lookAhead(match))
7560 while (num.length < len)
7564 // Format a name, short or long as requested
7565 var formatName = function(match, value, shortNames, longNames) {
7566 return (lookAhead(match) ? longNames[value] : shortNames[value]);
7569 var literal = false;
7571 for (var iFormat = 0; iFormat < format.length; iFormat++) {
7573 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
7576 output += format.charAt(iFormat);
7578 switch (format.charAt(iFormat)) {
7580 output += formatNumber('d', date.getDate(), 2);
7583 output += formatName('D', date.getDay(), dayNamesShort, dayNames);
7586 output += formatNumber('o',
7587 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
7590 output += formatNumber('m', date.getMonth() + 1, 2);
7593 output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
7596 output += (lookAhead('y') ? date.getFullYear() :
7597 (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
7600 output += date.getTime();
7603 output += date.getTime() * 10000 + this._ticksTo1970;
7612 output += format.charAt(iFormat);
7618 /* Extract all possible characters from the date format. */
7619 _possibleChars: function (format) {
7621 var literal = false;
7622 // Check whether a format character is doubled
7623 var lookAhead = function(match) {
7624 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
7629 for (var iFormat = 0; iFormat < format.length; iFormat++)
7631 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
7634 chars += format.charAt(iFormat);
7636 switch (format.charAt(iFormat)) {
7637 case 'd': case 'm': case 'y': case '@':
7638 chars += '0123456789';
7641 return null; // Accept anything
7649 chars += format.charAt(iFormat);
7654 /* Get a setting value, defaulting if necessary. */
7655 _get: function(inst, name) {
7656 return inst.settings[name] !== undefined ?
7657 inst.settings[name] : this._defaults[name];
7660 /* Parse existing date and initialise date picker. */
7661 _setDateFromField: function(inst, noDefault) {
7662 if (inst.input.val() == inst.lastVal) {
7665 var dateFormat = this._get(inst, 'dateFormat');
7666 var dates = inst.lastVal = inst.input ? inst.input.val() : null;
7667 var date, defaultDate;
7668 date = defaultDate = this._getDefaultDate(inst);
7669 var settings = this._getFormatConfig(inst);
7671 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
7674 dates = (noDefault ? '' : dates);
7676 inst.selectedDay = date.getDate();
7677 inst.drawMonth = inst.selectedMonth = date.getMonth();
7678 inst.drawYear = inst.selectedYear = date.getFullYear();
7679 inst.currentDay = (dates ? date.getDate() : 0);
7680 inst.currentMonth = (dates ? date.getMonth() : 0);
7681 inst.currentYear = (dates ? date.getFullYear() : 0);
7682 this._adjustInstDate(inst);
7685 /* Retrieve the default date shown on opening. */
7686 _getDefaultDate: function(inst) {
7687 return this._restrictMinMax(inst,
7688 this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
7691 /* A date may be specified as an exact value or a relative one. */
7692 _determineDate: function(inst, date, defaultDate) {
7693 var offsetNumeric = function(offset) {
7694 var date = new Date();
7695 date.setDate(date.getDate() + offset);
7698 var offsetString = function(offset) {
7700 return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
7701 offset, $.datepicker._getFormatConfig(inst));
7706 var date = (offset.toLowerCase().match(/^c/) ?
7707 $.datepicker._getDate(inst) : null) || new Date();
7708 var year = date.getFullYear();
7709 var month = date.getMonth();
7710 var day = date.getDate();
7711 var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
7712 var matches = pattern.exec(offset);
7714 switch (matches[2] || 'd') {
7715 case 'd' : case 'D' :
7716 day += parseInt(matches[1],10); break;
7717 case 'w' : case 'W' :
7718 day += parseInt(matches[1],10) * 7; break;
7719 case 'm' : case 'M' :
7720 month += parseInt(matches[1],10);
7721 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
7723 case 'y': case 'Y' :
7724 year += parseInt(matches[1],10);
7725 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
7728 matches = pattern.exec(offset);
7730 return new Date(year, month, day);
7732 var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
7733 (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
7734 newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
7736 newDate.setHours(0);
7737 newDate.setMinutes(0);
7738 newDate.setSeconds(0);
7739 newDate.setMilliseconds(0);
7741 return this._daylightSavingAdjust(newDate);
7744 /* Handle switch to/from daylight saving.
7745 Hours may be non-zero on daylight saving cut-over:
7746 > 12 when midnight changeover, but then cannot generate
7747 midnight datetime, so jump to 1AM, otherwise reset.
7748 @param date (Date) the date to check
7749 @return (Date) the corrected date */
7750 _daylightSavingAdjust: function(date) {
7751 if (!date) return null;
7752 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
7756 /* Set the date(s) directly. */
7757 _setDate: function(inst, date, noChange) {
7759 var origMonth = inst.selectedMonth;
7760 var origYear = inst.selectedYear;
7761 var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
7762 inst.selectedDay = inst.currentDay = newDate.getDate();
7763 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
7764 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
7765 if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
7766 this._notifyChange(inst);
7767 this._adjustInstDate(inst);
7769 inst.input.val(clear ? '' : this._formatDate(inst));
7773 /* Retrieve the date(s) directly. */
7774 _getDate: function(inst) {
7775 var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
7776 this._daylightSavingAdjust(new Date(
7777 inst.currentYear, inst.currentMonth, inst.currentDay)));
7781 /* Attach the onxxx handlers. These are declared statically so
7782 * they work with static code transformers like Caja.
7784 _attachHandlers: function(inst) {
7785 var stepMonths = this._get(inst, 'stepMonths');
7786 var id = '#' + inst.id.replace( /\\\\/g, "\\" );
7787 inst.dpDiv.find('[data-handler]').map(function () {
7790 window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, -stepMonths, 'M');
7793 window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, +stepMonths, 'M');
7796 window['DP_jQuery_' + dpuuid].datepicker._hideDatepicker();
7798 today: function () {
7799 window['DP_jQuery_' + dpuuid].datepicker._gotoToday(id);
7801 selectDay: function () {
7802 window['DP_jQuery_' + dpuuid].datepicker._selectDay(id, +this.getAttribute('data-month'), +this.getAttribute('data-year'), this);
7805 selectMonth: function () {
7806 window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'M');
7809 selectYear: function () {
7810 window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'Y');
7814 $(this).bind(this.getAttribute('data-event'), handler[this.getAttribute('data-handler')]);
7818 /* Generate the HTML for the current state of the date picker. */
7819 _generateHTML: function(inst) {
7820 var today = new Date();
7821 today = this._daylightSavingAdjust(
7822 new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
7823 var isRTL = this._get(inst, 'isRTL');
7824 var showButtonPanel = this._get(inst, 'showButtonPanel');
7825 var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
7826 var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
7827 var numMonths = this._getNumberOfMonths(inst);
7828 var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
7829 var stepMonths = this._get(inst, 'stepMonths');
7830 var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
7831 var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
7832 new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
7833 var minDate = this._getMinMaxDate(inst, 'min');
7834 var maxDate = this._getMinMaxDate(inst, 'max');
7835 var drawMonth = inst.drawMonth - showCurrentAtPos;
7836 var drawYear = inst.drawYear;
7837 if (drawMonth < 0) {
7842 var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
7843 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
7844 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
7845 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
7847 if (drawMonth < 0) {
7853 inst.drawMonth = drawMonth;
7854 inst.drawYear = drawYear;
7855 var prevText = this._get(inst, 'prevText');
7856 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
7857 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
7858 this._getFormatConfig(inst)));
7859 var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
7860 '<a class="ui-datepicker-prev ui-corner-all" data-handler="prev" data-event="click"' +
7861 ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
7862 (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
7863 var nextText = this._get(inst, 'nextText');
7864 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
7865 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
7866 this._getFormatConfig(inst)));
7867 var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
7868 '<a class="ui-datepicker-next ui-corner-all" data-handler="next" data-event="click"' +
7869 ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
7870 (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
7871 var currentText = this._get(inst, 'currentText');
7872 var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
7873 currentText = (!navigationAsDateFormat ? currentText :
7874 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
7875 var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">' +
7876 this._get(inst, 'closeText') + '</button>' : '');
7877 var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
7878 (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" data-handler="today" data-event="click"' +
7879 '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
7880 var firstDay = parseInt(this._get(inst, 'firstDay'),10);
7881 firstDay = (isNaN(firstDay) ? 0 : firstDay);
7882 var showWeek = this._get(inst, 'showWeek');
7883 var dayNames = this._get(inst, 'dayNames');
7884 var dayNamesShort = this._get(inst, 'dayNamesShort');
7885 var dayNamesMin = this._get(inst, 'dayNamesMin');
7886 var monthNames = this._get(inst, 'monthNames');
7887 var monthNamesShort = this._get(inst, 'monthNamesShort');
7888 var beforeShowDay = this._get(inst, 'beforeShowDay');
7889 var showOtherMonths = this._get(inst, 'showOtherMonths');
7890 var selectOtherMonths = this._get(inst, 'selectOtherMonths');
7891 var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
7892 var defaultDate = this._getDefaultDate(inst);
7894 for (var row = 0; row < numMonths[0]; row++) {
7897 for (var col = 0; col < numMonths[1]; col++) {
7898 var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
7899 var cornerClass = ' ui-corner-all';
7902 calender += '<div class="ui-datepicker-group';
7903 if (numMonths[1] > 1)
7905 case 0: calender += ' ui-datepicker-group-first';
7906 cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
7907 case numMonths[1]-1: calender += ' ui-datepicker-group-last';
7908 cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
7909 default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
7913 calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
7914 (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
7915 (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
7916 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
7917 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
7918 '</div><table class="ui-datepicker-calendar"><thead>' +
7920 var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
7921 for (var dow = 0; dow < 7; dow++) { // days of the week
7922 var day = (dow + firstDay) % 7;
7923 thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
7924 '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
7926 calender += thead + '</tr></thead><tbody>';
7927 var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
7928 if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
7929 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
7930 var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
7931 var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
7932 var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
7933 this.maxRows = numRows;
7934 var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
7935 for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
7937 var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
7938 this._get(inst, 'calculateWeek')(printDate) + '</td>');
7939 for (var dow = 0; dow < 7; dow++) { // create date picker days
7940 var daySettings = (beforeShowDay ?
7941 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
7942 var otherMonth = (printDate.getMonth() != drawMonth);
7943 var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
7944 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
7945 tbody += '<td class="' +
7946 ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
7947 (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
7948 ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
7949 (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
7950 // or defaultDate is current printedDate and defaultDate is selectedDate
7951 ' ' + this._dayOverClass : '') + // highlight selected day
7952 (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
7953 (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
7954 (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
7955 (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
7956 ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
7957 (unselectable ? '' : ' data-handler="selectDay" data-event="click" data-month="' + printDate.getMonth() + '" data-year="' + printDate.getFullYear() + '"') + '>' + // actions
7958 (otherMonth && !showOtherMonths ? ' ' : // display for other months
7959 (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
7960 (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
7961 (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
7962 (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
7963 '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
7964 printDate.setDate(printDate.getDate() + 1);
7965 printDate = this._daylightSavingAdjust(printDate);
7967 calender += tbody + '</tr>';
7970 if (drawMonth > 11) {
7974 calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
7975 ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
7980 html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
7981 '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
7982 inst._keyEvent = false;
7986 /* Generate the month and year header. */
7987 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
7988 secondary, monthNames, monthNamesShort) {
7989 var changeMonth = this._get(inst, 'changeMonth');
7990 var changeYear = this._get(inst, 'changeYear');
7991 var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
7992 var html = '<div class="ui-datepicker-title">';
7995 if (secondary || !changeMonth)
7996 monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
7998 var inMinYear = (minDate && minDate.getFullYear() == drawYear);
7999 var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
8000 monthHtml += '<select class="ui-datepicker-month" data-handler="selectMonth" data-event="change">';
8001 for (var month = 0; month < 12; month++) {
8002 if ((!inMinYear || month >= minDate.getMonth()) &&
8003 (!inMaxYear || month <= maxDate.getMonth()))
8004 monthHtml += '<option value="' + month + '"' +
8005 (month == drawMonth ? ' selected="selected"' : '') +
8006 '>' + monthNamesShort[month] + '</option>';
8008 monthHtml += '</select>';
8010 if (!showMonthAfterYear)
8011 html += monthHtml + (secondary || !(changeMonth && changeYear) ? ' ' : '');
8013 if ( !inst.yearshtml ) {
8014 inst.yearshtml = '';
8015 if (secondary || !changeYear)
8016 html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
8018 // determine range of years to display
8019 var years = this._get(inst, 'yearRange').split(':');
8020 var thisYear = new Date().getFullYear();
8021 var determineYear = function(value) {
8022 var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
8023 (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
8024 parseInt(value, 10)));
8025 return (isNaN(year) ? thisYear : year);
8027 var year = determineYear(years[0]);
8028 var endYear = Math.max(year, determineYear(years[1] || ''));
8029 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
8030 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
8031 inst.yearshtml += '<select class="ui-datepicker-year" data-handler="selectYear" data-event="change">';
8032 for (; year <= endYear; year++) {
8033 inst.yearshtml += '<option value="' + year + '"' +
8034 (year == drawYear ? ' selected="selected"' : '') +
8035 '>' + year + '</option>';
8037 inst.yearshtml += '</select>';
8039 html += inst.yearshtml;
8040 inst.yearshtml = null;
8043 html += this._get(inst, 'yearSuffix');
8044 if (showMonthAfterYear)
8045 html += (secondary || !(changeMonth && changeYear) ? ' ' : '') + monthHtml;
8046 html += '</div>'; // Close datepicker_header
8050 /* Adjust one of the date sub-fields. */
8051 _adjustInstDate: function(inst, offset, period) {
8052 var year = inst.drawYear + (period == 'Y' ? offset : 0);
8053 var month = inst.drawMonth + (period == 'M' ? offset : 0);
8054 var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
8055 (period == 'D' ? offset : 0);
8056 var date = this._restrictMinMax(inst,
8057 this._daylightSavingAdjust(new Date(year, month, day)));
8058 inst.selectedDay = date.getDate();
8059 inst.drawMonth = inst.selectedMonth = date.getMonth();
8060 inst.drawYear = inst.selectedYear = date.getFullYear();
8061 if (period == 'M' || period == 'Y')
8062 this._notifyChange(inst);
8065 /* Ensure a date is within any min/max bounds. */
8066 _restrictMinMax: function(inst, date) {
8067 var minDate = this._getMinMaxDate(inst, 'min');
8068 var maxDate = this._getMinMaxDate(inst, 'max');
8069 var newDate = (minDate && date < minDate ? minDate : date);
8070 newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
8074 /* Notify change of month/year. */
8075 _notifyChange: function(inst) {
8076 var onChange = this._get(inst, 'onChangeMonthYear');
8078 onChange.apply((inst.input ? inst.input[0] : null),
8079 [inst.selectedYear, inst.selectedMonth + 1, inst]);
8082 /* Determine the number of months to show. */
8083 _getNumberOfMonths: function(inst) {
8084 var numMonths = this._get(inst, 'numberOfMonths');
8085 return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
8088 /* Determine the current maximum date - ensure no time components are set. */
8089 _getMinMaxDate: function(inst, minMax) {
8090 return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
8093 /* Find the number of days in a given month. */
8094 _getDaysInMonth: function(year, month) {
8095 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
8098 /* Find the day of the week of the first of a month. */
8099 _getFirstDayOfMonth: function(year, month) {
8100 return new Date(year, month, 1).getDay();
8103 /* Determines if we should allow a "next/prev" month display change. */
8104 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
8105 var numMonths = this._getNumberOfMonths(inst);
8106 var date = this._daylightSavingAdjust(new Date(curYear,
8107 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
8109 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
8110 return this._isInRange(inst, date);
8113 /* Is the given date in the accepted range? */
8114 _isInRange: function(inst, date) {
8115 var minDate = this._getMinMaxDate(inst, 'min');
8116 var maxDate = this._getMinMaxDate(inst, 'max');
8117 return ((!minDate || date.getTime() >= minDate.getTime()) &&
8118 (!maxDate || date.getTime() <= maxDate.getTime()));
8121 /* Provide the configuration settings for formatting/parsing. */
8122 _getFormatConfig: function(inst) {
8123 var shortYearCutoff = this._get(inst, 'shortYearCutoff');
8124 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
8125 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
8126 return {shortYearCutoff: shortYearCutoff,
8127 dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
8128 monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
8131 /* Format the given date for display. */
8132 _formatDate: function(inst, day, month, year) {
8134 inst.currentDay = inst.selectedDay;
8135 inst.currentMonth = inst.selectedMonth;
8136 inst.currentYear = inst.selectedYear;
8138 var date = (day ? (typeof day == 'object' ? day :
8139 this._daylightSavingAdjust(new Date(year, month, day))) :
8140 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
8141 return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
8146 * Bind hover events for datepicker elements.
8147 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
8148 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
8150 function bindHover(dpDiv) {
8151 var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
8152 return dpDiv.delegate(selector, 'mouseout', function() {
8153 $(this).removeClass('ui-state-hover');
8154 if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
8155 if (this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
8157 .delegate(selector, 'mouseover', function(){
8158 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
8159 $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
8160 $(this).addClass('ui-state-hover');
8161 if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
8162 if (this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
8167 /* jQuery extend now ignores nulls! */
8168 function extendRemove(target, props) {
8169 $.extend(target, props);
8170 for (var name in props)
8171 if (props[name] == null || props[name] == undefined)
8172 target[name] = props[name];
8176 /* Invoke the datepicker functionality.
8177 @param options string - a command, optionally followed by additional parameters or
8178 Object - settings for attaching new datepicker functionality
8179 @return jQuery object */
8180 $.fn.datepicker = function(options){
8182 /* Verify an empty collection wasn't passed - Fixes #6976 */
8183 if ( !this.length ) {
8187 /* Initialise the date picker. */
8188 if (!$.datepicker.initialized) {
8189 $(document).mousedown($.datepicker._checkExternalClick).
8190 find(document.body).append($.datepicker.dpDiv);
8191 $.datepicker.initialized = true;
8194 var otherArgs = Array.prototype.slice.call(arguments, 1);
8195 if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
8196 return $.datepicker['_' + options + 'Datepicker'].
8197 apply($.datepicker, [this[0]].concat(otherArgs));
8198 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
8199 return $.datepicker['_' + options + 'Datepicker'].
8200 apply($.datepicker, [this[0]].concat(otherArgs));
8201 return this.each(function() {
8202 typeof options == 'string' ?
8203 $.datepicker['_' + options + 'Datepicker'].
8204 apply($.datepicker, [this].concat(otherArgs)) :
8205 $.datepicker._attachDatepicker(this, options);
8209 $.datepicker = new Datepicker(); // singleton instance
8210 $.datepicker.initialized = false;
8211 $.datepicker.uuid = new Date().getTime();
8212 $.datepicker.version = "1.9.0";
8214 // Workaround for #4055
8215 // Add another global to avoid noConflict issues with inline event handlers
8216 window['DP_jQuery_' + dpuuid] = $;
8219 (function( $, undefined ) {
8221 var uiDialogClasses = "ui-dialog ui-widget ui-widget-content ui-corner-all ",
8222 sizeRelatedOptions = {
8231 resizableRelatedOptions = {
8238 $.widget("ui.dialog", {
8243 closeOnEscape: true,
8259 // ensure that the titlebar is never outside the document
8260 using: function( pos ) {
8261 var topOffset = $( this ).css( pos ).offset().top;
8262 if ( topOffset < 0 ) {
8263 $( this ).css( "top", pos.top - topOffset );
8275 _create: function() {
8276 this.originalTitle = this.element.attr( "title" );
8277 // #5742 - .attr() might return a DOMElement
8278 if ( typeof this.originalTitle !== "string" ) {
8279 this.originalTitle = "";
8281 this.oldPosition = {
8282 parent: this.element.parent(),
8283 index: this.element.parent().children().index( this.element )
8285 this.options.title = this.options.title || this.originalTitle;
8287 options = this.options,
8289 title = options.title || " ",
8291 uiDialog = ( this.uiDialog = $( "<div>" ) )
8292 .addClass( uiDialogClasses + options.dialogClass )
8295 outline: 0, // TODO: move to stylesheet
8296 zIndex: options.zIndex
8298 // setting tabIndex makes the div focusable
8299 .attr( "tabIndex", -1)
8300 .keydown(function( event ) {
8301 if ( options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
8302 event.keyCode === $.ui.keyCode.ESCAPE ) {
8303 that.close( event );
8304 event.preventDefault();
8307 .mousedown(function( event ) {
8308 that.moveToTop( false, event );
8310 .appendTo( "body" ),
8312 uiDialogContent = this.element
8314 .removeAttr( "title" )
8315 .addClass( "ui-dialog-content ui-widget-content" )
8316 .appendTo( uiDialog ),
8318 uiDialogTitlebar = ( this.uiDialogTitlebar = $( "<div>" ) )
8319 .addClass( "ui-dialog-titlebar ui-widget-header " +
8320 "ui-corner-all ui-helper-clearfix" )
8321 .prependTo( uiDialog ),
8323 uiDialogTitlebarClose = $( "<a href='#'></a>" )
8324 .addClass( "ui-dialog-titlebar-close ui-corner-all" )
8325 .attr( "role", "button" )
8326 .click(function( event ) {
8327 event.preventDefault();
8328 that.close( event );
8330 .appendTo( uiDialogTitlebar ),
8332 uiDialogTitlebarCloseText = ( this.uiDialogTitlebarCloseText = $( "<span>" ) )
8333 .addClass( "ui-icon ui-icon-closethick" )
8334 .text( options.closeText )
8335 .appendTo( uiDialogTitlebarClose ),
8337 uiDialogTitle = $( "<span>" )
8339 .addClass( "ui-dialog-title" )
8341 .prependTo( uiDialogTitlebar ),
8343 uiDialogButtonPane = ( this.uiDialogButtonPane = $( "<div>" ) )
8344 .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" ),
8346 uiButtonSet = ( this.uiButtonSet = $( "<div>" ) )
8347 .addClass( "ui-dialog-buttonset" )
8348 .appendTo( uiDialogButtonPane );
8352 "aria-labelledby": uiDialogTitle.attr( "id" )
8355 uiDialogTitlebar.find( "*" ).add( uiDialogTitlebar ).disableSelection();
8356 this._hoverable( uiDialogTitlebarClose );
8357 this._focusable( uiDialogTitlebarClose );
8359 if ( options.draggable && $.fn.draggable ) {
8360 this._makeDraggable();
8362 if ( options.resizable && $.fn.resizable ) {
8363 this._makeResizable();
8366 this._createButtons( options.buttons );
8367 this._isOpen = false;
8369 if ( $.fn.bgiframe ) {
8370 uiDialog.bgiframe();
8373 // prevent tabbing out of modal dialogs
8374 this._on( uiDialog, { keydown: function( event ) {
8375 if ( !options.modal || event.keyCode !== $.ui.keyCode.TAB ) {
8379 var tabbables = $( ":tabbable", uiDialog ),
8380 first = tabbables.filter( ":first" ),
8381 last = tabbables.filter( ":last" );
8383 if ( event.target === last[0] && !event.shiftKey ) {
8386 } else if ( event.target === first[0] && event.shiftKey ) {
8394 if ( this.options.autoOpen ) {
8399 _destroy: function() {
8401 oldPosition = this.oldPosition;
8403 if ( this.overlay ) {
8404 this.overlay.destroy();
8406 this.uiDialog.hide();
8408 .removeClass( "ui-dialog-content ui-widget-content" )
8410 .appendTo( "body" );
8411 this.uiDialog.remove();
8413 if ( this.originalTitle ) {
8414 this.element.attr( "title", this.originalTitle );
8417 next = oldPosition.parent.children().eq( oldPosition.index );
8418 // Don't try to place the dialog next to itself (#8613)
8419 if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
8420 next.before( this.element );
8422 oldPosition.parent.append( this.element );
8426 widget: function() {
8427 return this.uiDialog;
8430 close: function( event ) {
8434 if ( !this._isOpen ) {
8438 if ( false === this._trigger( "beforeClose", event ) ) {
8442 this._isOpen = false;
8444 if ( this.overlay ) {
8445 this.overlay.destroy();
8448 if ( this.options.hide ) {
8449 this.uiDialog.hide( this.options.hide, function() {
8450 that._trigger( "close", event );
8453 this.uiDialog.hide();
8454 this._trigger( "close", event );
8457 $.ui.dialog.overlay.resize();
8459 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
8460 if ( this.options.modal ) {
8462 $( ".ui-dialog" ).each(function() {
8463 if ( this !== that.uiDialog[0] ) {
8464 thisZ = $( this ).css( "z-index" );
8465 if ( !isNaN( thisZ ) ) {
8466 maxZ = Math.max( maxZ, thisZ );
8470 $.ui.dialog.maxZ = maxZ;
8476 isOpen: function() {
8477 return this._isOpen;
8480 // the force parameter allows us to move modal dialogs to their correct
8482 moveToTop: function( force, event ) {
8483 var options = this.options,
8486 if ( ( options.modal && !force ) ||
8487 ( !options.stack && !options.modal ) ) {
8488 return this._trigger( "focus", event );
8491 if ( options.zIndex > $.ui.dialog.maxZ ) {
8492 $.ui.dialog.maxZ = options.zIndex;
8494 if ( this.overlay ) {
8495 $.ui.dialog.maxZ += 1;
8496 $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ;
8497 this.overlay.$el.css( "z-index", $.ui.dialog.overlay.maxZ );
8500 // Save and then restore scroll
8501 // Opera 9.5+ resets when parent z-index is changed.
8502 // http://bugs.jqueryui.com/ticket/3193
8504 scrollTop: this.element.scrollTop(),
8505 scrollLeft: this.element.scrollLeft()
8507 $.ui.dialog.maxZ += 1;
8508 this.uiDialog.css( "z-index", $.ui.dialog.maxZ );
8509 this.element.attr( saveScroll );
8510 this._trigger( "focus", event );
8516 if ( this._isOpen ) {
8521 options = this.options,
8522 uiDialog = this.uiDialog;
8525 this._position( options.position );
8526 uiDialog.show( options.show );
8527 this.overlay = options.modal ? new $.ui.dialog.overlay( this ) : null;
8528 this.moveToTop( true );
8530 // set focus to the first tabbable element in the content area or the first button
8531 // if there are no tabbable elements, set focus on the dialog itself
8532 hasFocus = this.element.find( ":tabbable" );
8533 if ( !hasFocus.length ) {
8534 hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
8535 if ( !hasFocus.length ) {
8536 hasFocus = uiDialog;
8539 hasFocus.eq( 0 ).focus();
8541 this._isOpen = true;
8542 this._trigger( "open" );
8547 _createButtons: function( buttons ) {
8548 var uiDialogButtonPane, uiButtonSet,
8552 // if we already have a button pane, remove it
8553 this.uiDialogButtonPane.remove();
8554 this.uiButtonSet.empty();
8556 if ( typeof buttons === "object" && buttons !== null ) {
8557 $.each( buttons, function() {
8558 return !(hasButtons = true);
8562 $.each( buttons, function( name, props ) {
8563 props = $.isFunction( props ) ?
8564 { click: props, text: name } :
8566 var button = $( "<button type='button'>" )
8567 .attr( props, true )
8570 props.click.apply( that.element[0], arguments );
8572 .appendTo( that.uiButtonSet );
8573 if ( $.fn.button ) {
8577 this.uiDialog.addClass( "ui-dialog-buttons" );
8578 this.uiDialogButtonPane.appendTo( this.uiDialog );
8580 this.uiDialog.removeClass( "ui-dialog-buttons" );
8584 _makeDraggable: function() {
8586 options = this.options;
8588 function filteredUi( ui ) {
8590 position: ui.position,
8595 this.uiDialog.draggable({
8596 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
8597 handle: ".ui-dialog-titlebar",
8598 containment: "document",
8599 start: function( event, ui ) {
8601 .addClass( "ui-dialog-dragging" );
8602 that._trigger( "dragStart", event, filteredUi( ui ) );
8604 drag: function( event, ui ) {
8605 that._trigger( "drag", event, filteredUi( ui ) );
8607 stop: function( event, ui ) {
8608 options.position = [
8609 ui.position.left - that.document.scrollLeft(),
8610 ui.position.top - that.document.scrollTop()
8613 .removeClass( "ui-dialog-dragging" );
8614 that._trigger( "dragStop", event, filteredUi( ui ) );
8615 $.ui.dialog.overlay.resize();
8620 _makeResizable: function( handles ) {
8621 handles = (handles === undefined ? this.options.resizable : handles);
8623 options = this.options,
8624 // .ui-resizable has position: relative defined in the stylesheet
8625 // but dialogs have to use absolute or fixed positioning
8626 position = this.uiDialog.css( "position" ),
8627 resizeHandles = typeof handles === 'string' ?
8629 "n,e,s,w,se,sw,ne,nw";
8631 function filteredUi( ui ) {
8633 originalPosition: ui.originalPosition,
8634 originalSize: ui.originalSize,
8635 position: ui.position,
8640 this.uiDialog.resizable({
8641 cancel: ".ui-dialog-content",
8642 containment: "document",
8643 alsoResize: this.element,
8644 maxWidth: options.maxWidth,
8645 maxHeight: options.maxHeight,
8646 minWidth: options.minWidth,
8647 minHeight: this._minHeight(),
8648 handles: resizeHandles,
8649 start: function( event, ui ) {
8650 $( this ).addClass( "ui-dialog-resizing" );
8651 that._trigger( "resizeStart", event, filteredUi( ui ) );
8653 resize: function( event, ui ) {
8654 that._trigger( "resize", event, filteredUi( ui ) );
8656 stop: function( event, ui ) {
8657 $( this ).removeClass( "ui-dialog-resizing" );
8658 options.height = $( this ).height();
8659 options.width = $( this ).width();
8660 that._trigger( "resizeStop", event, filteredUi( ui ) );
8661 $.ui.dialog.overlay.resize();
8664 .css( "position", position )
8665 .find( ".ui-resizable-se" )
8666 .addClass( "ui-icon ui-icon-grip-diagonal-se" );
8669 _minHeight: function() {
8670 var options = this.options;
8672 if ( options.height === "auto" ) {
8673 return options.minHeight;
8675 return Math.min( options.minHeight, options.height );
8679 _position: function( position ) {
8685 // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
8686 // if (typeof position == 'string' || $.isArray(position)) {
8687 // myAt = $.isArray(position) ? position : position.split(' ');
8689 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
8690 myAt = position.split ? position.split( " " ) : [ position[ 0 ], position[ 1 ] ];
8691 if ( myAt.length === 1 ) {
8692 myAt[ 1 ] = myAt[ 0 ];
8695 $.each( [ "left", "top" ], function( i, offsetPosition ) {
8696 if ( +myAt[ i ] === myAt[ i ] ) {
8697 offset[ i ] = myAt[ i ];
8698 myAt[ i ] = offsetPosition;
8703 my: myAt.join( " " ),
8704 at: myAt.join( " " ),
8705 offset: offset.join( " " )
8709 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
8711 position = $.ui.dialog.prototype.options.position;
8714 // need to show the dialog to get the actual offset in the position plugin
8715 isVisible = this.uiDialog.is( ":visible" );
8717 this.uiDialog.show();
8719 this.uiDialog.position( position );
8721 this.uiDialog.hide();
8725 _setOptions: function( options ) {
8727 resizableOptions = {},
8730 $.each( options, function( key, value ) {
8731 that._setOption( key, value );
8733 if ( key in sizeRelatedOptions ) {
8736 if ( key in resizableRelatedOptions ) {
8737 resizableOptions[ key ] = value;
8744 if ( this.uiDialog.is( ":data(resizable)" ) ) {
8745 this.uiDialog.resizable( "option", resizableOptions );
8749 _setOption: function( key, value ) {
8750 var isDraggable, isResizable,
8751 uiDialog = this.uiDialog;
8755 this._createButtons( value );
8758 // ensure that we always pass a string
8759 this.uiDialogTitlebarCloseText.text( "" + value );
8763 .removeClass( this.options.dialogClass )
8764 .addClass( uiDialogClasses + value );
8768 uiDialog.addClass( "ui-dialog-disabled" );
8770 uiDialog.removeClass( "ui-dialog-disabled" );
8774 isDraggable = uiDialog.is( ":data(draggable)" );
8775 if ( isDraggable && !value ) {
8776 uiDialog.draggable( "destroy" );
8779 if ( !isDraggable && value ) {
8780 this._makeDraggable();
8784 this._position( value );
8787 // currently resizable, becoming non-resizable
8788 isResizable = uiDialog.is( ":data(resizable)" );
8789 if ( isResizable && !value ) {
8790 uiDialog.resizable( "destroy" );
8793 // currently resizable, changing handles
8794 if ( isResizable && typeof value === "string" ) {
8795 uiDialog.resizable( "option", "handles", value );
8798 // currently non-resizable, becoming resizable
8799 if ( !isResizable && value !== false ) {
8800 this._makeResizable( value );
8804 // convert whatever was passed in o a string, for html() to not throw up
8805 $( ".ui-dialog-title", this.uiDialogTitlebar )
8806 .html( "" + ( value || " " ) );
8810 this._super( key, value );
8814 /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
8815 * divs will both have width and height set, so we need to reset them
8817 var nonContentHeight, minContentHeight, autoHeight,
8818 options = this.options,
8819 isVisible = this.uiDialog.is( ":visible" );
8821 // reset content sizing
8822 this.element.show().css({
8828 if ( options.minWidth > options.width ) {
8829 options.width = options.minWidth;
8832 // reset wrapper sizing
8833 // determine the height of all the non-content elements
8834 nonContentHeight = this.uiDialog.css({
8836 width: options.width
8839 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
8841 if ( options.height === "auto" ) {
8842 // only needed for IE6 support
8843 if ( $.support.minHeight ) {
8845 minHeight: minContentHeight,
8849 this.uiDialog.show();
8850 autoHeight = this.element.css( "height", "auto" ).height();
8852 this.uiDialog.hide();
8854 this.element.height( Math.max( autoHeight, minContentHeight ) );
8857 this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
8860 if (this.uiDialog.is( ":data(resizable)" ) ) {
8861 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
8866 $.extend($.ui.dialog, {
8870 getTitleId: function($el) {
8871 var id = $el.attr( "id" );
8876 return "ui-dialog-title-" + id;
8879 overlay: function( dialog ) {
8880 this.$el = $.ui.dialog.overlay.create( dialog );
8884 $.extend( $.ui.dialog.overlay, {
8886 // reuse old instances due to IE memory leak with alpha transparency (see #5185)
8890 "focus,mousedown,mouseup,keydown,keypress,click".split( "," ),
8892 return event + ".dialog-overlay";
8895 create: function( dialog ) {
8896 if ( this.instances.length === 0 ) {
8897 // prevent use of anchors and inputs
8898 // we use a setTimeout in case the overlay is created from an
8899 // event that we're going to be cancelling (see #2804)
8900 setTimeout(function() {
8901 // handle $(el).dialog().dialog('close') (see #4065)
8902 if ( $.ui.dialog.overlay.instances.length ) {
8903 $( document ).bind( $.ui.dialog.overlay.events, function( event ) {
8904 // stop events if the z-index of the target is < the z-index of the overlay
8905 // we cannot return true when we don't want to cancel the event (#3523)
8906 if ( $( event.target ).zIndex() < $.ui.dialog.overlay.maxZ ) {
8913 // handle window resize
8914 $( window ).bind( "resize.dialog-overlay", $.ui.dialog.overlay.resize );
8917 var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) );
8919 // allow closing by pressing the escape key
8920 $( document ).bind( "keydown.dialog-overlay", function( event ) {
8921 var instances = $.ui.dialog.overlay.instances;
8922 // only react to the event if we're the top overlay
8923 if ( instances.length !== 0 && instances[ instances.length - 1 ] === $el &&
8924 dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
8925 event.keyCode === $.ui.keyCode.ESCAPE ) {
8927 dialog.close( event );
8928 event.preventDefault();
8932 $el.appendTo( document.body ).css({
8933 width: this.width(),
8934 height: this.height()
8937 if ( $.fn.bgiframe ) {
8941 this.instances.push( $el );
8945 destroy: function( $el ) {
8946 var indexOf = $.inArray( $el, this.instances ),
8949 if ( indexOf !== -1 ) {
8950 this.oldInstances.push( this.instances.splice( indexOf, 1 )[ 0 ] );
8953 if ( this.instances.length === 0 ) {
8954 $( [ document, window ] ).unbind( ".dialog-overlay" );
8957 $el.height( 0 ).width( 0 ).remove();
8959 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
8960 $.each( this.instances, function() {
8961 maxZ = Math.max( maxZ, this.css( "z-index" ) );
8966 height: function() {
8970 if ( $.browser.msie ) {
8971 scrollHeight = Math.max(
8972 document.documentElement.scrollHeight,
8973 document.body.scrollHeight
8975 offsetHeight = Math.max(
8976 document.documentElement.offsetHeight,
8977 document.body.offsetHeight
8980 if ( scrollHeight < offsetHeight ) {
8981 return $( window ).height() + "px";
8983 return scrollHeight + "px";
8985 // handle "good" browsers
8987 return $( document ).height() + "px";
8995 if ( $.browser.msie ) {
8996 scrollWidth = Math.max(
8997 document.documentElement.scrollWidth,
8998 document.body.scrollWidth
9000 offsetWidth = Math.max(
9001 document.documentElement.offsetWidth,
9002 document.body.offsetWidth
9005 if ( scrollWidth < offsetWidth ) {
9006 return $( window ).width() + "px";
9008 return scrollWidth + "px";
9010 // handle "good" browsers
9012 return $( document ).width() + "px";
9016 resize: function() {
9017 /* If the dialog is draggable and the user drags it past the
9018 * right edge of the window, the document becomes wider so we
9019 * need to stretch the overlay. If the user then drags the
9020 * dialog back to the left, the document will become narrower,
9021 * so we need to shrink the overlay to the appropriate size.
9022 * This is handled by shrinking the overlay before setting it
9023 * to the full document size.
9025 var $overlays = $( [] );
9026 $.each( $.ui.dialog.overlay.instances, function() {
9027 $overlays = $overlays.add( this );
9034 width: $.ui.dialog.overlay.width(),
9035 height: $.ui.dialog.overlay.height()
9040 $.extend( $.ui.dialog.overlay.prototype, {
9041 destroy: function() {
9042 $.ui.dialog.overlay.destroy( this.$el );
9047 (function( $, undefined ) {
9049 var mouseHandled = false;
9051 $.widget( "ui.menu", {
9053 defaultElement: "<ul>",
9057 submenu: "ui-icon-carat-1-e"
9072 _create: function() {
9073 this.activeMenu = this.element;
9076 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9077 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
9079 role: this.options.role,
9082 // need to catch all clicks on disabled menu
9083 // not possible through _on
9084 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
9085 if ( this.options.disabled ) {
9086 event.preventDefault();
9090 if ( this.options.disabled ) {
9092 .addClass( "ui-state-disabled" )
9093 .attr( "aria-disabled", "true" );
9097 // Prevent focus from sticking to links inside menu after clicking
9098 // them (focus should always stay on UL during navigation).
9099 "mousedown .ui-menu-item > a": function( event ) {
9100 event.preventDefault();
9102 "click .ui-state-disabled > a": function( event ) {
9103 event.preventDefault();
9105 "click .ui-menu-item:has(a)": function( event ) {
9106 var target = $( event.target ).closest( ".ui-menu-item" );
9107 if ( !mouseHandled && target.not( ".ui-state-disabled" ).length ) {
9108 mouseHandled = true;
9110 this.select( event );
9111 // Open submenu on click
9112 if ( target.has( ".ui-menu" ).length ) {
9113 this.expand( event );
9114 } else if ( !this.element.is( ":focus" ) ) {
9115 // Redirect focus to the menu
9116 this.element.trigger( "focus", [ true ] );
9118 // If the active item is on the top level, let it stay active.
9119 // Otherwise, blur the active item since it is no longer visible.
9120 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
9121 clearTimeout( this.timer );
9126 "mouseenter .ui-menu-item": function( event ) {
9127 var target = $( event.currentTarget );
9128 // Remove ui-state-active class from siblings of the newly focused menu item
9129 // to avoid a jump caused by adjacent elements both having a class with a border
9130 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
9131 this.focus( event, target );
9133 mouseleave: "collapseAll",
9134 "mouseleave .ui-menu": "collapseAll",
9135 focus: function( event, keepActiveItem ) {
9136 // If there's already an active item, keep it active
9137 // If not, activate the first item
9138 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
9140 if ( !keepActiveItem ) {
9141 this.focus( event, item );
9144 blur: function( event ) {
9145 this._delay(function() {
9146 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
9147 this.collapseAll( event );
9156 // Clicks outside of a menu collapse any open menus
9157 this._on( this.document, {
9158 click: function( event ) {
9159 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
9160 this.collapseAll( event );
9163 // Reset the mouseHandled flag
9164 mouseHandled = false;
9169 _destroy: function() {
9170 // Destroy (sub)menus
9172 .removeAttr( "aria-activedescendant" )
9173 .find( ".ui-menu" ).andSelf()
9174 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
9175 .removeAttr( "role" )
9176 .removeAttr( "tabIndex" )
9177 .removeAttr( "aria-labelledby" )
9178 .removeAttr( "aria-expanded" )
9179 .removeAttr( "aria-hidden" )
9180 .removeAttr( "aria-disabled" )
9184 // Destroy menu items
9185 this.element.find( ".ui-menu-item" )
9186 .removeClass( "ui-menu-item" )
9187 .removeAttr( "role" )
9188 .removeAttr( "aria-disabled" )
9191 .removeClass( "ui-corner-all ui-state-hover" )
9192 .removeAttr( "tabIndex" )
9193 .removeAttr( "role" )
9194 .removeAttr( "aria-haspopup" )
9195 .children().each( function() {
9196 var elem = $( this );
9197 if ( elem.data( "ui-menu-submenu-carat" ) ) {
9202 // Destroy menu dividers
9203 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
9206 _keydown: function( event ) {
9207 var match, prev, character, skip, regex,
9208 preventDefault = true;
9210 function escape( value ) {
9211 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
9214 switch ( event.keyCode ) {
9215 case $.ui.keyCode.PAGE_UP:
9216 this.previousPage( event );
9218 case $.ui.keyCode.PAGE_DOWN:
9219 this.nextPage( event );
9221 case $.ui.keyCode.HOME:
9222 this._move( "first", "first", event );
9224 case $.ui.keyCode.END:
9225 this._move( "last", "last", event );
9227 case $.ui.keyCode.UP:
9228 this.previous( event );
9230 case $.ui.keyCode.DOWN:
9233 case $.ui.keyCode.LEFT:
9234 this.collapse( event );
9236 case $.ui.keyCode.RIGHT:
9237 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
9238 this.expand( event );
9241 case $.ui.keyCode.ENTER:
9242 case $.ui.keyCode.SPACE:
9243 this._activate( event );
9245 case $.ui.keyCode.ESCAPE:
9246 this.collapse( event );
9249 preventDefault = false;
9250 prev = this.previousFilter || "";
9251 character = String.fromCharCode( event.keyCode );
9254 clearTimeout( this.filterTimer );
9256 if ( character === prev ) {
9259 character = prev + character;
9262 regex = new RegExp( "^" + escape( character ), "i" );
9263 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9264 return regex.test( $( this ).children( "a" ).text() );
9266 match = skip && match.index( this.active.next() ) !== -1 ?
9267 this.active.nextAll( ".ui-menu-item" ) :
9270 // If no matches on the current filter, reset to the last character pressed
9271 // to move down the menu to the first item that starts with that character
9272 if ( !match.length ) {
9273 character = String.fromCharCode( event.keyCode );
9274 regex = new RegExp( "^" + escape( character ), "i" );
9275 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9276 return regex.test( $( this ).children( "a" ).text() );
9280 if ( match.length ) {
9281 this.focus( event, match );
9282 if ( match.length > 1 ) {
9283 this.previousFilter = character;
9284 this.filterTimer = this._delay(function() {
9285 delete this.previousFilter;
9288 delete this.previousFilter;
9291 delete this.previousFilter;
9295 if ( preventDefault ) {
9296 event.preventDefault();
9300 _activate: function( event ) {
9301 if ( !this.active.is( ".ui-state-disabled" ) ) {
9302 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
9303 this.expand( event );
9305 this.select( event );
9310 refresh: function() {
9311 // Initialize nested menus
9313 icon = this.options.icons.submenu,
9314 submenus = this.element.find( this.options.menus + ":not(.ui-menu)" )
9315 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9318 role: this.options.role,
9319 "aria-hidden": "true",
9320 "aria-expanded": "false"
9323 // Don't refresh list items that are already adapted
9324 menus = submenus.add( this.element );
9326 menus.children( ":not(.ui-menu-item):has(a)" )
9327 .addClass( "ui-menu-item" )
9328 .attr( "role", "presentation" )
9331 .addClass( "ui-corner-all" )
9334 role: this._itemRole()
9337 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
9338 menus.children( ":not(.ui-menu-item)" ).each(function() {
9339 var item = $( this );
9340 // hyphen, em dash, en dash
9341 if ( !/[^\-—–\s]/.test( item.text() ) ) {
9342 item.addClass( "ui-widget-content ui-menu-divider" );
9346 // Add aria-disabled attribute to any disabled menu item
9347 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
9349 submenus.each(function() {
9350 var menu = $( this ),
9351 item = menu.prev( "a" ),
9352 submenuCarat = $( "<span>" )
9353 .addClass( "ui-menu-icon ui-icon " + icon )
9354 .data( "ui-menu-submenu-carat", true );
9357 .attr( "aria-haspopup", "true" )
9358 .prepend( submenuCarat );
9359 menu.attr( "aria-labelledby", item.attr( "id" ) );
9362 // If the active item has been removed, blur the menu
9363 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
9368 _itemRole: function() {
9372 }[ this.options.role ];
9375 focus: function( event, item ) {
9376 var nested, focused;
9377 this.blur( event, event && event.type === "focus" );
9379 this._scrollIntoView( item );
9381 this.active = item.first();
9382 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
9383 // Only update aria-activedescendant if there's a role
9384 // otherwise we assume focus is managed elsewhere
9385 if ( this.options.role ) {
9386 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
9389 // Highlight active parent menu item, if any
9392 .closest( ".ui-menu-item" )
9393 .children( "a:first" )
9394 .addClass( "ui-state-active" );
9396 if ( event && event.type === "keydown" ) {
9399 this.timer = this._delay(function() {
9404 nested = item.children( ".ui-menu" );
9405 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
9406 this._startOpening(nested);
9408 this.activeMenu = item.parent();
9410 this._trigger( "focus", event, { item: item } );
9413 _scrollIntoView: function( item ) {
9414 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
9415 if ( this._hasScroll() ) {
9416 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
9417 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
9418 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
9419 scroll = this.activeMenu.scrollTop();
9420 elementHeight = this.activeMenu.height();
9421 itemHeight = item.height();
9424 this.activeMenu.scrollTop( scroll + offset );
9425 } else if ( offset + itemHeight > elementHeight ) {
9426 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
9431 blur: function( event, fromFocus ) {
9433 clearTimeout( this.timer );
9436 if ( !this.active ) {
9440 this.active.children( "a" ).removeClass( "ui-state-focus" );
9443 this._trigger( "blur", event, { item: this.active } );
9446 _startOpening: function( submenu ) {
9447 clearTimeout( this.timer );
9449 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
9450 // shift in the submenu position when mousing over the carat icon
9451 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
9455 this.timer = this._delay(function() {
9457 this._open( submenu );
9461 _open: function( submenu ) {
9462 var position = $.extend({
9464 }, this.options.position );
9466 clearTimeout( this.timer );
9467 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
9469 .attr( "aria-hidden", "true" );
9473 .removeAttr( "aria-hidden" )
9474 .attr( "aria-expanded", "true" )
9475 .position( position );
9478 collapseAll: function( event, all ) {
9479 clearTimeout( this.timer );
9480 this.timer = this._delay(function() {
9481 // If we were passed an event, look for the submenu that contains the event
9482 var currentMenu = all ? this.element :
9483 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
9485 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
9486 if ( !currentMenu.length ) {
9487 currentMenu = this.element;
9490 this._close( currentMenu );
9493 this.activeMenu = currentMenu;
9497 // With no arguments, closes the currently active menu - if nothing is active
9498 // it closes all menus. If passed an argument, it will search for menus BELOW
9499 _close: function( startMenu ) {
9501 startMenu = this.active ? this.active.parent() : this.element;
9507 .attr( "aria-hidden", "true" )
9508 .attr( "aria-expanded", "false" )
9510 .find( "a.ui-state-active" )
9511 .removeClass( "ui-state-active" );
9514 collapse: function( event ) {
9515 var newItem = this.active &&
9516 this.active.parent().closest( ".ui-menu-item", this.element );
9517 if ( newItem && newItem.length ) {
9519 this.focus( event, newItem );
9523 expand: function( event ) {
9524 var newItem = this.active &&
9526 .children( ".ui-menu " )
9527 .children( ".ui-menu-item" )
9530 if ( newItem && newItem.length ) {
9531 this._open( newItem.parent() );
9533 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
9534 this._delay(function() {
9535 this.focus( event, newItem );
9540 next: function( event ) {
9541 this._move( "next", "first", event );
9544 previous: function( event ) {
9545 this._move( "prev", "last", event );
9548 isFirstItem: function() {
9549 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
9552 isLastItem: function() {
9553 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
9556 _move: function( direction, filter, event ) {
9558 if ( this.active ) {
9559 if ( direction === "first" || direction === "last" ) {
9561 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
9565 [ direction + "All" ]( ".ui-menu-item" )
9569 if ( !next || !next.length || !this.active ) {
9570 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
9573 this.focus( event, next );
9576 nextPage: function( event ) {
9577 var item, base, height;
9579 if ( !this.active ) {
9583 if ( this.isLastItem() ) {
9586 if ( this._hasScroll() ) {
9587 base = this.active.offset().top;
9588 height = this.element.height();
9589 this.active.nextAll( ".ui-menu-item" ).each(function() {
9591 return item.offset().top - base - height < 0;
9594 this.focus( event, item );
9596 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
9597 [ !this.active ? "first" : "last" ]() );
9601 previousPage: function( event ) {
9602 var item, base, height;
9603 if ( !this.active ) {
9607 if ( this.isFirstItem() ) {
9610 if ( this._hasScroll() ) {
9611 base = this.active.offset().top;
9612 height = this.element.height();
9613 this.active.prevAll( ".ui-menu-item" ).each(function() {
9615 return item.offset().top - base + height > 0;
9618 this.focus( event, item );
9620 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
9624 _hasScroll: function() {
9625 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
9628 select: function( event ) {
9629 // TODO: It should never be possible to not have an active item at this
9630 // point, but the tests don't trigger mouseenter before click.
9631 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
9632 var ui = { item: this.active };
9633 if ( !this.active.has( ".ui-menu" ).length ) {
9634 this.collapseAll( event, true );
9636 this._trigger( "select", event, ui );
9641 (function( $, undefined ) {
9643 $.widget( "ui.progressbar", {
9652 _create: function() {
9654 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
9656 role: "progressbar",
9657 "aria-valuemin": this.min,
9658 "aria-valuemax": this.options.max,
9659 "aria-valuenow": this._value()
9662 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
9663 .appendTo( this.element );
9665 this.oldValue = this._value();
9666 this._refreshValue();
9669 _destroy: function() {
9671 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
9672 .removeAttr( "role" )
9673 .removeAttr( "aria-valuemin" )
9674 .removeAttr( "aria-valuemax" )
9675 .removeAttr( "aria-valuenow" );
9677 this.valueDiv.remove();
9680 value: function( newValue ) {
9681 if ( newValue === undefined ) {
9682 return this._value();
9685 this._setOption( "value", newValue );
9689 _setOption: function( key, value ) {
9690 if ( key === "value" ) {
9691 this.options.value = value;
9692 this._refreshValue();
9693 if ( this._value() === this.options.max ) {
9694 this._trigger( "complete" );
9698 this._super( key, value );
9701 _value: function() {
9702 var val = this.options.value;
9703 // normalize invalid value
9704 if ( typeof val !== "number" ) {
9707 return Math.min( this.options.max, Math.max( this.min, val ) );
9710 _percentage: function() {
9711 return 100 * this._value() / this.options.max;
9714 _refreshValue: function() {
9715 var value = this.value(),
9716 percentage = this._percentage();
9718 if ( this.oldValue !== value ) {
9719 this.oldValue = value;
9720 this._trigger( "change" );
9724 .toggle( value > this.min )
9725 .toggleClass( "ui-corner-right", value === this.options.max )
9726 .width( percentage.toFixed(0) + "%" );
9727 this.element.attr( "aria-valuenow", value );
9732 (function( $, undefined ) {
9734 // number of pages in a slider
9735 // (how many times can you page up/down to go through the whole range)
9738 $.widget( "ui.slider", $.ui.mouse, {
9740 widgetEventPrefix: "slide",
9747 orientation: "horizontal",
9754 _create: function() {
9757 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
9758 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
9759 handleCount = ( o.values && o.values.length ) || 1,
9762 this._keySliding = false;
9763 this._mouseSliding = false;
9764 this._animateOff = true;
9765 this._handleIndex = null;
9766 this._detectOrientation();
9770 .addClass( "ui-slider" +
9771 " ui-slider-" + this.orientation +
9773 " ui-widget-content" +
9775 ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
9780 if ( o.range === true ) {
9782 o.values = [ this._valueMin(), this._valueMin() ];
9784 if ( o.values.length && o.values.length !== 2 ) {
9785 o.values = [ o.values[0], o.values[0] ];
9789 this.range = $( "<div></div>" )
9790 .appendTo( this.element )
9791 .addClass( "ui-slider-range" +
9792 // note: this isn't the most fittingly semantic framework class for this element,
9793 // but worked best visually with a variety of themes
9794 " ui-widget-header" +
9795 ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
9798 for ( i = existingHandles.length; i < handleCount; i++ ) {
9799 handles.push( handle );
9802 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
9804 this.handle = this.handles.eq( 0 );
9806 this.handles.add( this.range ).filter( "a" )
9807 .click(function( event ) {
9808 event.preventDefault();
9810 .mouseenter(function() {
9811 if ( !o.disabled ) {
9812 $( this ).addClass( "ui-state-hover" );
9815 .mouseleave(function() {
9816 $( this ).removeClass( "ui-state-hover" );
9819 if ( !o.disabled ) {
9820 $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
9821 $( this ).addClass( "ui-state-focus" );
9827 $( this ).removeClass( "ui-state-focus" );
9830 this.handles.each(function( i ) {
9831 $( this ).data( "ui-slider-handle-index", i );
9834 this._on( this.handles, {
9835 keydown: function( event ) {
9836 var allowed, curVal, newVal, step,
9837 index = $( event.target ).data( "ui-slider-handle-index" );
9839 switch ( event.keyCode ) {
9840 case $.ui.keyCode.HOME:
9841 case $.ui.keyCode.END:
9842 case $.ui.keyCode.PAGE_UP:
9843 case $.ui.keyCode.PAGE_DOWN:
9844 case $.ui.keyCode.UP:
9845 case $.ui.keyCode.RIGHT:
9846 case $.ui.keyCode.DOWN:
9847 case $.ui.keyCode.LEFT:
9848 event.preventDefault();
9849 if ( !this._keySliding ) {
9850 this._keySliding = true;
9851 $( event.target ).addClass( "ui-state-active" );
9852 allowed = this._start( event, index );
9853 if ( allowed === false ) {
9860 step = this.options.step;
9861 if ( this.options.values && this.options.values.length ) {
9862 curVal = newVal = this.values( index );
9864 curVal = newVal = this.value();
9867 switch ( event.keyCode ) {
9868 case $.ui.keyCode.HOME:
9869 newVal = this._valueMin();
9871 case $.ui.keyCode.END:
9872 newVal = this._valueMax();
9874 case $.ui.keyCode.PAGE_UP:
9875 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
9877 case $.ui.keyCode.PAGE_DOWN:
9878 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
9880 case $.ui.keyCode.UP:
9881 case $.ui.keyCode.RIGHT:
9882 if ( curVal === this._valueMax() ) {
9885 newVal = this._trimAlignValue( curVal + step );
9887 case $.ui.keyCode.DOWN:
9888 case $.ui.keyCode.LEFT:
9889 if ( curVal === this._valueMin() ) {
9892 newVal = this._trimAlignValue( curVal - step );
9896 this._slide( event, index, newVal );
9898 keyup: function( event ) {
9899 var index = $( event.target ).data( "ui-slider-handle-index" );
9901 if ( this._keySliding ) {
9902 this._keySliding = false;
9903 this._stop( event, index );
9904 this._change( event, index );
9905 $( event.target ).removeClass( "ui-state-active" );
9910 this._refreshValue();
9912 this._animateOff = false;
9915 _destroy: function() {
9916 this.handles.remove();
9917 this.range.remove();
9920 .removeClass( "ui-slider" +
9921 " ui-slider-horizontal" +
9922 " ui-slider-vertical" +
9923 " ui-slider-disabled" +
9925 " ui-widget-content" +
9928 this._mouseDestroy();
9931 _mouseCapture: function( event ) {
9932 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
9940 this.elementSize = {
9941 width: this.element.outerWidth(),
9942 height: this.element.outerHeight()
9944 this.elementOffset = this.element.offset();
9946 position = { x: event.pageX, y: event.pageY };
9947 normValue = this._normValueFromMouse( position );
9948 distance = this._valueMax() - this._valueMin() + 1;
9949 this.handles.each(function( i ) {
9950 var thisDistance = Math.abs( normValue - that.values(i) );
9951 if ( distance > thisDistance ) {
9952 distance = thisDistance;
9953 closestHandle = $( this );
9958 // workaround for bug #3736 (if both handles of a range are at 0,
9959 // the first is always used as the one with least distance,
9960 // and moving it is obviously prevented by preventing negative ranges)
9961 if( o.range === true && this.values(1) === o.min ) {
9963 closestHandle = $( this.handles[index] );
9966 allowed = this._start( event, index );
9967 if ( allowed === false ) {
9970 this._mouseSliding = true;
9972 this._handleIndex = index;
9975 .addClass( "ui-state-active" )
9978 offset = closestHandle.offset();
9979 mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
9980 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
9981 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
9982 top: event.pageY - offset.top -
9983 ( closestHandle.height() / 2 ) -
9984 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
9985 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
9986 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
9989 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
9990 this._slide( event, index, normValue );
9992 this._animateOff = true;
9996 _mouseStart: function( event ) {
10000 _mouseDrag: function( event ) {
10001 var position = { x: event.pageX, y: event.pageY },
10002 normValue = this._normValueFromMouse( position );
10004 this._slide( event, this._handleIndex, normValue );
10009 _mouseStop: function( event ) {
10010 this.handles.removeClass( "ui-state-active" );
10011 this._mouseSliding = false;
10013 this._stop( event, this._handleIndex );
10014 this._change( event, this._handleIndex );
10016 this._handleIndex = null;
10017 this._clickOffset = null;
10018 this._animateOff = false;
10023 _detectOrientation: function() {
10024 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
10027 _normValueFromMouse: function( position ) {
10034 if ( this.orientation === "horizontal" ) {
10035 pixelTotal = this.elementSize.width;
10036 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
10038 pixelTotal = this.elementSize.height;
10039 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
10042 percentMouse = ( pixelMouse / pixelTotal );
10043 if ( percentMouse > 1 ) {
10046 if ( percentMouse < 0 ) {
10049 if ( this.orientation === "vertical" ) {
10050 percentMouse = 1 - percentMouse;
10053 valueTotal = this._valueMax() - this._valueMin();
10054 valueMouse = this._valueMin() + percentMouse * valueTotal;
10056 return this._trimAlignValue( valueMouse );
10059 _start: function( event, index ) {
10061 handle: this.handles[ index ],
10062 value: this.value()
10064 if ( this.options.values && this.options.values.length ) {
10065 uiHash.value = this.values( index );
10066 uiHash.values = this.values();
10068 return this._trigger( "start", event, uiHash );
10071 _slide: function( event, index, newVal ) {
10076 if ( this.options.values && this.options.values.length ) {
10077 otherVal = this.values( index ? 0 : 1 );
10079 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
10080 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
10085 if ( newVal !== this.values( index ) ) {
10086 newValues = this.values();
10087 newValues[ index ] = newVal;
10088 // A slide can be canceled by returning false from the slide callback
10089 allowed = this._trigger( "slide", event, {
10090 handle: this.handles[ index ],
10094 otherVal = this.values( index ? 0 : 1 );
10095 if ( allowed !== false ) {
10096 this.values( index, newVal, true );
10100 if ( newVal !== this.value() ) {
10101 // A slide can be canceled by returning false from the slide callback
10102 allowed = this._trigger( "slide", event, {
10103 handle: this.handles[ index ],
10106 if ( allowed !== false ) {
10107 this.value( newVal );
10113 _stop: function( event, index ) {
10115 handle: this.handles[ index ],
10116 value: this.value()
10118 if ( this.options.values && this.options.values.length ) {
10119 uiHash.value = this.values( index );
10120 uiHash.values = this.values();
10123 this._trigger( "stop", event, uiHash );
10126 _change: function( event, index ) {
10127 if ( !this._keySliding && !this._mouseSliding ) {
10129 handle: this.handles[ index ],
10130 value: this.value()
10132 if ( this.options.values && this.options.values.length ) {
10133 uiHash.value = this.values( index );
10134 uiHash.values = this.values();
10137 this._trigger( "change", event, uiHash );
10141 value: function( newValue ) {
10142 if ( arguments.length ) {
10143 this.options.value = this._trimAlignValue( newValue );
10144 this._refreshValue();
10145 this._change( null, 0 );
10149 return this._value();
10152 values: function( index, newValue ) {
10157 if ( arguments.length > 1 ) {
10158 this.options.values[ index ] = this._trimAlignValue( newValue );
10159 this._refreshValue();
10160 this._change( null, index );
10164 if ( arguments.length ) {
10165 if ( $.isArray( arguments[ 0 ] ) ) {
10166 vals = this.options.values;
10167 newValues = arguments[ 0 ];
10168 for ( i = 0; i < vals.length; i += 1 ) {
10169 vals[ i ] = this._trimAlignValue( newValues[ i ] );
10170 this._change( null, i );
10172 this._refreshValue();
10174 if ( this.options.values && this.options.values.length ) {
10175 return this._values( index );
10177 return this.value();
10181 return this._values();
10185 _setOption: function( key, value ) {
10189 if ( $.isArray( this.options.values ) ) {
10190 valsLength = this.options.values.length;
10193 $.Widget.prototype._setOption.apply( this, arguments );
10198 this.handles.filter( ".ui-state-focus" ).blur();
10199 this.handles.removeClass( "ui-state-hover" );
10200 this.handles.prop( "disabled", true );
10201 this.element.addClass( "ui-disabled" );
10203 this.handles.prop( "disabled", false );
10204 this.element.removeClass( "ui-disabled" );
10207 case "orientation":
10208 this._detectOrientation();
10210 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
10211 .addClass( "ui-slider-" + this.orientation );
10212 this._refreshValue();
10215 this._animateOff = true;
10216 this._refreshValue();
10217 this._change( null, 0 );
10218 this._animateOff = false;
10221 this._animateOff = true;
10222 this._refreshValue();
10223 for ( i = 0; i < valsLength; i += 1 ) {
10224 this._change( null, i );
10226 this._animateOff = false;
10231 //internal value getter
10232 // _value() returns value trimmed by min and max, aligned by step
10233 _value: function() {
10234 var val = this.options.value;
10235 val = this._trimAlignValue( val );
10240 //internal values getter
10241 // _values() returns array of values trimmed by min and max, aligned by step
10242 // _values( index ) returns single value trimmed by min and max, aligned by step
10243 _values: function( index ) {
10248 if ( arguments.length ) {
10249 val = this.options.values[ index ];
10250 val = this._trimAlignValue( val );
10254 // .slice() creates a copy of the array
10255 // this copy gets trimmed by min and max and then returned
10256 vals = this.options.values.slice();
10257 for ( i = 0; i < vals.length; i+= 1) {
10258 vals[ i ] = this._trimAlignValue( vals[ i ] );
10265 // returns the step-aligned value that val is closest to, between (inclusive) min and max
10266 _trimAlignValue: function( val ) {
10267 if ( val <= this._valueMin() ) {
10268 return this._valueMin();
10270 if ( val >= this._valueMax() ) {
10271 return this._valueMax();
10273 var step = ( this.options.step > 0 ) ? this.options.step : 1,
10274 valModStep = (val - this._valueMin()) % step,
10275 alignValue = val - valModStep;
10277 if ( Math.abs(valModStep) * 2 >= step ) {
10278 alignValue += ( valModStep > 0 ) ? step : ( -step );
10281 // Since JavaScript has problems with large floats, round
10282 // the final value to 5 digits after the decimal point (see #4124)
10283 return parseFloat( alignValue.toFixed(5) );
10286 _valueMin: function() {
10287 return this.options.min;
10290 _valueMax: function() {
10291 return this.options.max;
10294 _refreshValue: function() {
10295 var lastValPercent, valPercent, value, valueMin, valueMax,
10296 oRange = this.options.range,
10299 animate = ( !this._animateOff ) ? o.animate : false,
10302 if ( this.options.values && this.options.values.length ) {
10303 this.handles.each(function( i, j ) {
10304 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
10305 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
10306 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
10307 if ( that.options.range === true ) {
10308 if ( that.orientation === "horizontal" ) {
10310 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
10313 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
10317 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
10320 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
10324 lastValPercent = valPercent;
10327 value = this.value();
10328 valueMin = this._valueMin();
10329 valueMax = this._valueMax();
10330 valPercent = ( valueMax !== valueMin ) ?
10331 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
10333 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
10334 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
10336 if ( oRange === "min" && this.orientation === "horizontal" ) {
10337 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
10339 if ( oRange === "max" && this.orientation === "horizontal" ) {
10340 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
10342 if ( oRange === "min" && this.orientation === "vertical" ) {
10343 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
10345 if ( oRange === "max" && this.orientation === "vertical" ) {
10346 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
10356 function modifier( fn ) {
10357 return function() {
10358 var previous = this.element.val();
10359 fn.apply( this, arguments );
10361 if ( previous !== this.element.val() ) {
10362 this._trigger( "change" );
10367 $.widget( "ui.spinner", {
10369 defaultElement: "<input>",
10370 widgetEventPrefix: "spin",
10374 down: "ui-icon-triangle-1-s",
10375 up: "ui-icon-triangle-1-n"
10380 numberFormat: null,
10390 _create: function() {
10391 // handle string values that need to be parsed
10392 this._setOption( "max", this.options.max );
10393 this._setOption( "min", this.options.min );
10394 this._setOption( "step", this.options.step );
10396 // format the value, but don't constrain
10397 this._value( this.element.val(), true );
10400 this._on( this._events );
10403 // turning off autocomplete prevents the browser from remembering the
10404 // value when navigating through history, so we re-enable autocomplete
10405 // if the page is unloaded before the widget is destroyed. #7790
10406 this._on( this.window, {
10407 beforeunload: function() {
10408 this.element.removeAttr( "autocomplete" );
10413 _getCreateOptions: function() {
10415 element = this.element;
10417 $.each( [ "min", "max", "step" ], function( i, option ) {
10418 var value = element.attr( option );
10419 if ( value !== undefined && value.length ) {
10420 options[ option ] = value;
10428 keydown: function( event ) {
10429 if ( this._start( event ) && this._keydown( event ) ) {
10430 event.preventDefault();
10434 focus: function() {
10435 this.uiSpinner.addClass( "ui-state-active" );
10436 this.previous = this.element.val();
10438 blur: function( event ) {
10439 if ( this.cancelBlur ) {
10440 delete this.cancelBlur;
10445 this.uiSpinner.removeClass( "ui-state-active" );
10446 if ( this.previous !== this.element.val() ) {
10447 this._trigger( "change", event );
10450 mousewheel: function( event, delta ) {
10454 if ( !this.spinning && !this._start( event ) ) {
10458 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
10459 clearTimeout( this.mousewheelTimer );
10460 this.mousewheelTimer = this._delay(function() {
10461 if ( this.spinning ) {
10462 this._stop( event );
10465 event.preventDefault();
10467 "mousedown .ui-spinner-button": function( event ) {
10470 // We never want the buttons to have focus; whenever the user is
10471 // interacting with the spinner, the focus should be on the input.
10472 // If the input is focused then this.previous is properly set from
10473 // when the input first received focus. If the input is not focused
10474 // then we need to set this.previous based on the value before spinning.
10475 previous = this.element[0] === this.document[0].activeElement ?
10476 this.previous : this.element.val();
10477 function checkFocus() {
10478 var isActive = this.element[0] === this.document[0].activeElement;
10480 this.element.focus();
10481 this.previous = previous;
10483 // IE sets focus asynchronously, so we need to check if focus
10484 // moved off of the input because the user clicked on the button.
10485 this._delay(function() {
10486 this.previous = previous;
10491 // ensure focus is on (or stays on) the text field
10492 event.preventDefault();
10493 checkFocus.call( this );
10496 // IE doesn't prevent moving focus even with event.preventDefault()
10497 // so we set a flag to know when we should ignore the blur event
10498 // and check (again) if focus moved off of the input.
10499 this.cancelBlur = true;
10500 this._delay(function() {
10501 delete this.cancelBlur;
10502 checkFocus.call( this );
10505 if ( this._start( event ) === false ) {
10509 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
10511 "mouseup .ui-spinner-button": "_stop",
10512 "mouseenter .ui-spinner-button": function( event ) {
10513 // button will add ui-state-active if mouse was down while mouseleave and kept down
10514 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
10518 if ( this._start( event ) === false ) {
10521 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
10523 // TODO: do we really want to consider this a stop?
10524 // shouldn't we just stop the repeater and wait until mouseup before
10525 // we trigger the stop event?
10526 "mouseleave .ui-spinner-button": "_stop"
10529 _draw: function() {
10530 var uiSpinner = this.uiSpinner = this.element
10531 .addClass( "ui-spinner-input" )
10532 .attr( "autocomplete", "off" )
10533 .wrap( this._uiSpinnerHtml() )
10536 .append( this._buttonHtml() );
10537 this._hoverable( uiSpinner );
10539 this.element.attr( "role", "spinbutton" );
10542 this.buttons = uiSpinner.find( ".ui-spinner-button" )
10543 .attr( "tabIndex", -1 )
10545 .removeClass( "ui-corner-all" );
10547 // IE 6 doesn't understand height: 50% for the buttons
10548 // unless the wrapper has an explicit height
10549 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
10550 uiSpinner.height() > 0 ) {
10551 uiSpinner.height( uiSpinner.height() );
10554 // disable spinner if element was already disabled
10555 if ( this.options.disabled ) {
10560 _keydown: function( event ) {
10561 var options = this.options,
10562 keyCode = $.ui.keyCode;
10564 switch ( event.keyCode ) {
10566 this._repeat( null, 1, event );
10569 this._repeat( null, -1, event );
10571 case keyCode.PAGE_UP:
10572 this._repeat( null, options.page, event );
10574 case keyCode.PAGE_DOWN:
10575 this._repeat( null, -options.page, event );
10582 _uiSpinnerHtml: function() {
10583 return "<span class='ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all'></span>";
10586 _buttonHtml: function() {
10588 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
10589 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
10591 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
10592 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
10596 _start: function( event ) {
10597 if ( !this.spinning && this._trigger( "start", event ) === false ) {
10601 if ( !this.counter ) {
10604 this.spinning = true;
10608 _repeat: function( i, steps, event ) {
10611 clearTimeout( this.timer );
10612 this.timer = this._delay(function() {
10613 this._repeat( 40, steps, event );
10616 this._spin( steps * this.options.step, event );
10619 _spin: function( step, event ) {
10620 var value = this.value() || 0;
10622 if ( !this.counter ) {
10626 value = this._adjustValue( value + step * this._increment( this.counter ) );
10628 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
10629 this._value( value );
10634 _increment: function( i ) {
10635 var incremental = this.options.incremental;
10637 if ( incremental ) {
10638 return $.isFunction( incremental ) ?
10640 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
10646 _precision: function() {
10647 var precision = this._precisionOf( this.options.step );
10648 if ( this.options.min !== null ) {
10649 precision = Math.max( precision, this._precisionOf( this.options.min ) );
10654 _precisionOf: function( num ) {
10655 var str = num.toString(),
10656 decimal = str.indexOf( "." );
10657 return decimal === -1 ? 0 : str.length - decimal - 1;
10660 _adjustValue: function( value ) {
10661 var base, aboveMin,
10662 options = this.options;
10664 // make sure we're at a valid step
10665 // - find out where we are relative to the base (min or 0)
10666 base = options.min !== null ? options.min : 0;
10667 aboveMin = value - base;
10668 // - round to the nearest step
10669 aboveMin = Math.round(aboveMin / options.step) * options.step;
10670 // - rounding is based on 0, so adjust back to our base
10671 value = base + aboveMin;
10673 // fix precision from bad JS floating point math
10674 value = parseFloat( value.toFixed( this._precision() ) );
10677 if ( options.max !== null && value > options.max) {
10678 return options.max;
10680 if ( options.min !== null && value < options.min ) {
10681 return options.min;
10687 _stop: function( event ) {
10688 if ( !this.spinning ) {
10692 clearTimeout( this.timer );
10693 clearTimeout( this.mousewheelTimer );
10695 this.spinning = false;
10696 this._trigger( "stop", event );
10699 _setOption: function( key, value ) {
10700 if ( key === "culture" || key === "numberFormat" ) {
10701 var prevValue = this._parse( this.element.val() );
10702 this.options[ key ] = value;
10703 this.element.val( this._format( prevValue ) );
10707 if ( key === "max" || key === "min" || key === "step" ) {
10708 if ( typeof value === "string" ) {
10709 value = this._parse( value );
10713 this._super( key, value );
10715 if ( key === "disabled" ) {
10717 this.element.prop( "disabled", true );
10718 this.buttons.button( "disable" );
10720 this.element.prop( "disabled", false );
10721 this.buttons.button( "enable" );
10726 _setOptions: modifier(function( options ) {
10727 this._super( options );
10728 this._value( this.element.val() );
10731 _parse: function( val ) {
10732 if ( typeof val === "string" && val !== "" ) {
10733 val = window.Globalize && this.options.numberFormat ?
10734 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
10736 return val === "" || isNaN( val ) ? null : val;
10739 _format: function( value ) {
10740 if ( value === "" ) {
10743 return window.Globalize && this.options.numberFormat ?
10744 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
10748 _refresh: function() {
10749 this.element.attr({
10750 "aria-valuemin": this.options.min,
10751 "aria-valuemax": this.options.max,
10752 // TODO: what should we do with values that can't be parsed?
10753 "aria-valuenow": this._parse( this.element.val() )
10757 // update the value without triggering change
10758 _value: function( value, allowAny ) {
10760 if ( value !== "" ) {
10761 parsed = this._parse( value );
10762 if ( parsed !== null ) {
10764 parsed = this._adjustValue( parsed );
10766 value = this._format( parsed );
10769 this.element.val( value );
10773 _destroy: function() {
10775 .removeClass( "ui-spinner-input" )
10776 .prop( "disabled", false )
10777 .removeAttr( "autocomplete" )
10778 .removeAttr( "role" )
10779 .removeAttr( "aria-valuemin" )
10780 .removeAttr( "aria-valuemax" )
10781 .removeAttr( "aria-valuenow" );
10782 this.uiSpinner.replaceWith( this.element );
10785 stepUp: modifier(function( steps ) {
10786 this._stepUp( steps );
10788 _stepUp: function( steps ) {
10789 this._spin( (steps || 1) * this.options.step );
10792 stepDown: modifier(function( steps ) {
10793 this._stepDown( steps );
10795 _stepDown: function( steps ) {
10796 this._spin( (steps || 1) * -this.options.step );
10799 pageUp: modifier(function( pages ) {
10800 this._stepUp( (pages || 1) * this.options.page );
10803 pageDown: modifier(function( pages ) {
10804 this._stepDown( (pages || 1) * this.options.page );
10807 value: function( newVal ) {
10808 if ( !arguments.length ) {
10809 return this._parse( this.element.val() );
10811 modifier( this._value ).call( this, newVal );
10814 widget: function() {
10815 return this.uiSpinner;
10820 (function( $, undefined ) {
10825 function getNextTabId() {
10829 function isLocal( anchor ) {
10830 // clone the node to work around IE 6 not normalizing the href property
10831 // if it's manually set, i.e., a.href = "#foo" kills the normalization
10832 anchor = anchor.cloneNode( false );
10833 return anchor.hash.length > 1 &&
10834 anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" );
10837 $.widget( "ui.tabs", {
10842 collapsible: false,
10844 heightStyle: "content",
10850 beforeActivate: null,
10855 _create: function() {
10858 options = this.options,
10859 active = options.active;
10861 this.running = false;
10864 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
10865 .toggleClass( "ui-tabs-collapsible", options.collapsible )
10866 // Prevent users from focusing disabled tabs via click
10867 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
10868 if ( $( this ).is( ".ui-state-disabled" ) ) {
10869 event.preventDefault();
10873 // Preventing the default action in mousedown doesn't prevent IE
10874 // from focusing the element, so if the anchor gets focused, blur.
10875 // We don't have to worry about focusing the previously focused
10876 // element since clicking on a non-focusable element should focus
10877 // the body anyway.
10878 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
10879 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
10884 this._processTabs();
10886 if ( active === null ) {
10887 // check the fragment identifier in the URL
10888 if ( location.hash ) {
10889 this.anchors.each(function( i, anchor ) {
10890 if ( anchor.hash === location.hash ) {
10897 // check for a tab marked active via a class
10898 if ( active === null ) {
10899 active = this.tabs.filter( ".ui-tabs-active" ).index();
10902 // no active tab, set to false
10903 if ( active === null || active === -1 ) {
10904 active = this.tabs.length ? 0 : false;
10908 // handle numbers: negative, out of range
10909 if ( active !== false ) {
10910 active = this.tabs.index( this.tabs.eq( active ) );
10911 if ( active === -1 ) {
10912 active = options.collapsible ? false : 0;
10915 options.active = active;
10917 // don't allow collapsible: false and active: false
10918 if ( !options.collapsible && options.active === false && this.anchors.length ) {
10919 options.active = 0;
10922 // Take disabling tabs via class attribute from HTML
10923 // into account and update option properly.
10924 if ( $.isArray( options.disabled ) ) {
10925 options.disabled = $.unique( options.disabled.concat(
10926 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
10927 return that.tabs.index( li );
10932 // check for length avoids error when initializing empty list
10933 if ( this.options.active !== false && this.anchors.length ) {
10934 this.active = this._findActive( this.options.active );
10941 if ( this.active.length ) {
10942 this.load( options.active );
10946 _getCreateEventData: function() {
10949 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
10953 _tabKeydown: function( event ) {
10954 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
10955 selectedIndex = this.tabs.index( focusedTab ),
10956 goingForward = true;
10958 if ( this._handlePageNav( event ) ) {
10962 switch ( event.keyCode ) {
10963 case $.ui.keyCode.RIGHT:
10964 case $.ui.keyCode.DOWN:
10967 case $.ui.keyCode.UP:
10968 case $.ui.keyCode.LEFT:
10969 goingForward = false;
10972 case $.ui.keyCode.END:
10973 selectedIndex = this.anchors.length - 1;
10975 case $.ui.keyCode.HOME:
10978 case $.ui.keyCode.SPACE:
10979 // Activate only, no collapsing
10980 event.preventDefault();
10981 clearTimeout( this.activating );
10982 this._activate( selectedIndex );
10984 case $.ui.keyCode.ENTER:
10985 // Toggle (cancel delayed activation, allow collapsing)
10986 event.preventDefault();
10987 clearTimeout( this.activating );
10988 // Determine if we should collapse or activate
10989 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
10995 // Focus the appropriate tab, based on which key was pressed
10996 event.preventDefault();
10997 clearTimeout( this.activating );
10998 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
11000 // Navigating with control key will prevent automatic activation
11001 if ( !event.ctrlKey ) {
11002 // Update aria-selected immediately so that AT think the tab is already selected.
11003 // Otherwise AT may confuse the user by stating that they need to activate the tab,
11004 // but the tab will already be activated by the time the announcement finishes.
11005 focusedTab.attr( "aria-selected", "false" );
11006 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
11008 this.activating = this._delay(function() {
11009 this.option( "active", selectedIndex );
11014 _panelKeydown: function( event ) {
11015 if ( this._handlePageNav( event ) ) {
11019 // Ctrl+up moves focus to the current tab
11020 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
11021 event.preventDefault();
11022 this.active.focus();
11026 // Alt+page up/down moves focus to the previous/next tab (and activates)
11027 _handlePageNav: function( event ) {
11028 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
11029 this._activate( this._focusNextTab( this.options.active - 1, false ) );
11032 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
11033 this._activate( this._focusNextTab( this.options.active + 1, true ) );
11038 _findNextTab: function( index, goingForward ) {
11039 var lastTabIndex = this.tabs.length - 1;
11041 function constrain() {
11042 if ( index > lastTabIndex ) {
11046 index = lastTabIndex;
11051 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
11052 index = goingForward ? index + 1 : index - 1;
11058 _focusNextTab: function( index, goingForward ) {
11059 index = this._findNextTab( index, goingForward );
11060 this.tabs.eq( index ).focus();
11064 _setOption: function( key, value ) {
11065 if ( key === "active" ) {
11066 // _activate() will handle invalid values and update this.options
11067 this._activate( value );
11071 if ( key === "disabled" ) {
11072 // don't use the widget factory's disabled handling
11073 this._setupDisabled( value );
11077 this._super( key, value);
11079 if ( key === "collapsible" ) {
11080 this.element.toggleClass( "ui-tabs-collapsible", value );
11081 // Setting collapsible: false while collapsed; open first panel
11082 if ( !value && this.options.active === false ) {
11083 this._activate( 0 );
11087 if ( key === "event" ) {
11088 this._setupEvents( value );
11091 if ( key === "heightStyle" ) {
11092 this._setupHeightStyle( value );
11096 _tabId: function( tab ) {
11097 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
11100 _sanitizeSelector: function( hash ) {
11101 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
11104 refresh: function() {
11106 options = this.options,
11107 lis = this.tablist.children( ":has(a[href])" );
11109 // get disabled tabs from class attribute from HTML
11110 // this will get converted to a boolean if needed in _refresh()
11111 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
11112 return lis.index( tab );
11115 this._processTabs();
11117 // was collapsed or no tabs
11118 if ( options.active === false || !this.anchors.length ) {
11119 options.active = false;
11121 // was active, but active tab is gone
11122 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
11123 // all remaining tabs are disabled
11124 if ( this.tabs.length === options.disabled.length ) {
11125 options.active = false;
11127 // activate previous tab
11129 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
11131 // was active, active tab still exists
11133 // make sure active index is correct
11134 options.active = this.tabs.index( this.active );
11140 _refresh: function() {
11141 this._setupDisabled( this.options.disabled );
11142 this._setupEvents( this.options.event );
11143 this._setupHeightStyle( this.options.heightStyle );
11145 this.tabs.not( this.active ).attr({
11146 "aria-selected": "false",
11149 this.panels.not( this._getPanelForTab( this.active ) )
11152 "aria-expanded": "false",
11153 "aria-hidden": "true"
11156 // Make sure one tab is in the tab order
11157 if ( !this.active.length ) {
11158 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
11161 .addClass( "ui-tabs-active ui-state-active" )
11163 "aria-selected": "true",
11166 this._getPanelForTab( this.active )
11169 "aria-expanded": "true",
11170 "aria-hidden": "false"
11175 _processTabs: function() {
11178 this.tablist = this._getList()
11179 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
11180 .attr( "role", "tablist" );
11182 this.tabs = this.tablist.find( "> li:has(a[href])" )
11183 .addClass( "ui-state-default ui-corner-top" )
11189 this.anchors = this.tabs.map(function() {
11190 return $( "a", this )[ 0 ];
11192 .addClass( "ui-tabs-anchor" )
11194 role: "presentation",
11200 this.anchors.each(function( i, anchor ) {
11201 var selector, panel, panelId,
11202 anchorId = $( anchor ).uniqueId().attr( "id" ),
11203 tab = $( anchor ).closest( "li" ),
11204 originalAriaControls = tab.attr( "aria-controls" );
11207 if ( isLocal( anchor ) ) {
11208 selector = anchor.hash;
11209 panel = that.element.find( that._sanitizeSelector( selector ) );
11212 panelId = that._tabId( tab );
11213 selector = "#" + panelId;
11214 panel = that.element.find( selector );
11215 if ( !panel.length ) {
11216 panel = that._createPanel( panelId );
11217 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
11219 panel.attr( "aria-live", "polite" );
11222 if ( panel.length) {
11223 that.panels = that.panels.add( panel );
11225 if ( originalAriaControls ) {
11226 tab.data( "ui-tabs-aria-controls", originalAriaControls );
11229 "aria-controls": selector.substring( 1 ),
11230 "aria-labelledby": anchorId
11232 panel.attr( "aria-labelledby", anchorId );
11236 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
11237 .attr( "role", "tabpanel" );
11240 // allow overriding how to find the list for rare usage scenarios (#7715)
11241 _getList: function() {
11242 return this.element.find( "ol,ul" ).eq( 0 );
11245 _createPanel: function( id ) {
11246 return $( "<div>" )
11248 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
11249 .data( "ui-tabs-destroy", true );
11252 _setupDisabled: function( disabled ) {
11253 if ( $.isArray( disabled ) ) {
11254 if ( !disabled.length ) {
11256 } else if ( disabled.length === this.anchors.length ) {
11262 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
11263 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
11265 .addClass( "ui-state-disabled" )
11266 .attr( "aria-disabled", "true" );
11269 .removeClass( "ui-state-disabled" )
11270 .removeAttr( "aria-disabled" );
11274 this.options.disabled = disabled;
11277 _setupEvents: function( event ) {
11279 click: function( event ) {
11280 event.preventDefault();
11284 $.each( event.split(" "), function( index, eventName ) {
11285 events[ eventName ] = "_eventHandler";
11289 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
11290 this._on( this.anchors, events );
11291 this._on( this.tabs, { keydown: "_tabKeydown" } );
11292 this._on( this.panels, { keydown: "_panelKeydown" } );
11294 this._focusable( this.tabs );
11295 this._hoverable( this.tabs );
11298 _setupHeightStyle: function( heightStyle ) {
11299 var maxHeight, overflow,
11300 parent = this.element.parent();
11302 if ( heightStyle === "fill" ) {
11303 // IE 6 treats height like minHeight, so we need to turn off overflow
11304 // in order to get a reliable height
11305 // we use the minHeight support test because we assume that only
11306 // browsers that don't support minHeight will treat height as minHeight
11307 if ( !$.support.minHeight ) {
11308 overflow = parent.css( "overflow" );
11309 parent.css( "overflow", "hidden");
11311 maxHeight = parent.height();
11312 this.element.siblings( ":visible" ).each(function() {
11313 var elem = $( this ),
11314 position = elem.css( "position" );
11316 if ( position === "absolute" || position === "fixed" ) {
11319 maxHeight -= elem.outerHeight( true );
11322 parent.css( "overflow", overflow );
11325 this.element.children().not( this.panels ).each(function() {
11326 maxHeight -= $( this ).outerHeight( true );
11329 this.panels.each(function() {
11330 $( this ).height( Math.max( 0, maxHeight -
11331 $( this ).innerHeight() + $( this ).height() ) );
11333 .css( "overflow", "auto" );
11334 } else if ( heightStyle === "auto" ) {
11336 this.panels.each(function() {
11337 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
11338 }).height( maxHeight );
11342 _eventHandler: function( event ) {
11343 var options = this.options,
11344 active = this.active,
11345 anchor = $( event.currentTarget ),
11346 tab = anchor.closest( "li" ),
11347 clickedIsActive = tab[ 0 ] === active[ 0 ],
11348 collapsing = clickedIsActive && options.collapsible,
11349 toShow = collapsing ? $() : this._getPanelForTab( tab ),
11350 toHide = !active.length ? $() : this._getPanelForTab( active ),
11354 newTab: collapsing ? $() : tab,
11358 event.preventDefault();
11360 if ( tab.hasClass( "ui-state-disabled" ) ||
11361 // tab is already loading
11362 tab.hasClass( "ui-tabs-loading" ) ||
11363 // can't switch durning an animation
11365 // click on active header, but not collapsible
11366 ( clickedIsActive && !options.collapsible ) ||
11367 // allow canceling activation
11368 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
11372 options.active = collapsing ? false : this.tabs.index( tab );
11374 this.active = clickedIsActive ? $() : tab;
11379 if ( !toHide.length && !toShow.length ) {
11380 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
11383 if ( toShow.length ) {
11384 this.load( this.tabs.index( tab ), event );
11386 this._toggle( event, eventData );
11389 // handles show/hide for selecting tabs
11390 _toggle: function( event, eventData ) {
11392 toShow = eventData.newPanel,
11393 toHide = eventData.oldPanel;
11395 this.running = true;
11397 function complete() {
11398 that.running = false;
11399 that._trigger( "activate", event, eventData );
11403 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
11405 if ( toShow.length && that.options.show ) {
11406 that._show( toShow, that.options.show, complete );
11413 // start out by hiding, then showing, then completing
11414 if ( toHide.length && this.options.hide ) {
11415 this._hide( toHide, this.options.hide, function() {
11416 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
11420 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
11426 "aria-expanded": "false",
11427 "aria-hidden": "true"
11429 eventData.oldTab.attr( "aria-selected", "false" );
11430 // If we're switching tabs, remove the old tab from the tab order.
11431 // If we're opening from collapsed state, remove the previous tab from the tab order.
11432 // If we're collapsing, then keep the collapsing tab in the tab order.
11433 if ( toShow.length && toHide.length ) {
11434 eventData.oldTab.attr( "tabIndex", -1 );
11435 } else if ( toShow.length ) {
11436 this.tabs.filter(function() {
11437 return $( this ).attr( "tabIndex" ) === 0;
11439 .attr( "tabIndex", -1 );
11443 "aria-expanded": "true",
11444 "aria-hidden": "false"
11446 eventData.newTab.attr({
11447 "aria-selected": "true",
11452 _activate: function( index ) {
11454 active = this._findActive( index );
11456 // trying to activate the already active panel
11457 if ( active[ 0 ] === this.active[ 0 ] ) {
11461 // trying to collapse, simulate a click on the current active header
11462 if ( !active.length ) {
11463 active = this.active;
11466 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
11467 this._eventHandler({
11469 currentTarget: anchor,
11470 preventDefault: $.noop
11474 _findActive: function( index ) {
11475 return index === false ? $() : this.tabs.eq( index );
11478 _getIndex: function( index ) {
11479 // meta-function to give users option to provide a href string instead of a numerical index.
11480 if ( typeof index === "string" ) {
11481 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
11487 _destroy: function() {
11492 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
11495 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
11496 .removeAttr( "role" );
11499 .removeClass( "ui-tabs-anchor" )
11500 .removeAttr( "role" )
11501 .removeAttr( "tabIndex" )
11502 .removeData( "href.tabs" )
11503 .removeData( "load.tabs" )
11506 this.tabs.add( this.panels ).each(function() {
11507 if ( $.data( this, "ui-tabs-destroy" ) ) {
11508 $( this ).remove();
11511 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
11512 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
11513 .removeAttr( "tabIndex" )
11514 .removeAttr( "aria-live" )
11515 .removeAttr( "aria-busy" )
11516 .removeAttr( "aria-selected" )
11517 .removeAttr( "aria-labelledby" )
11518 .removeAttr( "aria-hidden" )
11519 .removeAttr( "aria-expanded" )
11520 .removeAttr( "role" );
11524 this.tabs.each(function() {
11525 var li = $( this ),
11526 prev = li.data( "ui-tabs-aria-controls" );
11528 li.attr( "aria-controls", prev );
11530 li.removeAttr( "aria-controls" );
11534 if ( this.options.heightStyle !== "content" ) {
11535 this.panels.css( "height", "" );
11539 enable: function( index ) {
11540 var disabled = this.options.disabled;
11541 if ( disabled === false ) {
11545 if ( index === undefined ) {
11548 index = this._getIndex( index );
11549 if ( $.isArray( disabled ) ) {
11550 disabled = $.map( disabled, function( num ) {
11551 return num !== index ? num : null;
11554 disabled = $.map( this.tabs, function( li, num ) {
11555 return num !== index ? num : null;
11559 this._setupDisabled( disabled );
11562 disable: function( index ) {
11563 var disabled = this.options.disabled;
11564 if ( disabled === true ) {
11568 if ( index === undefined ) {
11571 index = this._getIndex( index );
11572 if ( $.inArray( index, disabled ) !== -1 ) {
11575 if ( $.isArray( disabled ) ) {
11576 disabled = $.merge( [ index ], disabled ).sort();
11578 disabled = [ index ];
11581 this._setupDisabled( disabled );
11584 load: function( index, event ) {
11585 index = this._getIndex( index );
11587 tab = this.tabs.eq( index ),
11588 anchor = tab.find( ".ui-tabs-anchor" ),
11589 panel = this._getPanelForTab( tab ),
11596 if ( isLocal( anchor[ 0 ] ) ) {
11600 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
11602 // support: jQuery <1.8
11603 // jQuery <1.8 returns false if the request is canceled in beforeSend,
11604 // but as of 1.8, $.ajax() always returns a jqXHR object.
11605 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
11606 tab.addClass( "ui-tabs-loading" );
11607 panel.attr( "aria-busy", "true" );
11610 .success(function( response ) {
11611 // support: jQuery <1.8
11612 // http://bugs.jquery.com/ticket/11778
11613 setTimeout(function() {
11614 panel.html( response );
11615 that._trigger( "load", event, eventData );
11618 .complete(function( jqXHR, status ) {
11619 // support: jQuery <1.8
11620 // http://bugs.jquery.com/ticket/11778
11621 setTimeout(function() {
11622 if ( status === "abort" ) {
11623 that.panels.stop( false, true );
11626 tab.removeClass( "ui-tabs-loading" );
11627 panel.removeAttr( "aria-busy" );
11629 if ( jqXHR === that.xhr ) {
11637 // TODO: Remove this function in 1.10 when ajaxOptions is removed
11638 _ajaxSettings: function( anchor, event, eventData ) {
11641 url: anchor.attr( "href" ),
11642 beforeSend: function( jqXHR, settings ) {
11643 return that._trigger( "beforeLoad", event,
11644 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
11649 _getPanelForTab: function( tab ) {
11650 var id = $( tab ).attr( "aria-controls" );
11651 return this.element.find( this._sanitizeSelector( "#" + id ) );
11656 if ( $.uiBackCompat !== false ) {
11658 // helper method for a lot of the back compat extensions
11659 $.ui.tabs.prototype._ui = function( tab, panel ) {
11663 index: this.anchors.index( tab )
11668 $.widget( "ui.tabs", $.ui.tabs, {
11669 url: function( index, url ) {
11670 this.anchors.eq( index ).attr( "href", url );
11674 // TODO: Remove _ajaxSettings() method when removing this extension
11675 // ajaxOptions and cache options
11676 $.widget( "ui.tabs", $.ui.tabs, {
11682 _create: function() {
11687 this._on({ tabsbeforeload: function( event, ui ) {
11688 // tab is already cached
11689 if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) {
11690 event.preventDefault();
11694 ui.jqXHR.success(function() {
11695 if ( that.options.cache ) {
11696 $.data( ui.tab[ 0 ], "cache.tabs", true );
11702 _ajaxSettings: function( anchor, event, ui ) {
11703 var ajaxOptions = this.options.ajaxOptions;
11704 return $.extend( {}, ajaxOptions, {
11705 error: function( xhr, s, e ) {
11707 // Passing index avoid a race condition when this method is
11708 // called after the user has selected another tab.
11709 // Pass the anchor that initiated this request allows
11710 // loadError to manipulate the tab content panel via $(a.hash)
11712 xhr, s, ui.tab.closest( "li" ).index(), ui.tab[ 0 ] );
11716 }, this._superApply( arguments ) );
11719 _setOption: function( key, value ) {
11720 // reset cache if switching from cached to not cached
11721 if ( key === "cache" && value === false ) {
11722 this.anchors.removeData( "cache.tabs" );
11724 this._super( key, value );
11727 _destroy: function() {
11728 this.anchors.removeData( "cache.tabs" );
11732 url: function( index, url ){
11733 this.anchors.eq( index ).removeData( "cache.tabs" );
11734 this._superApply( arguments );
11739 $.widget( "ui.tabs", $.ui.tabs, {
11740 abort: function() {
11748 $.widget( "ui.tabs", $.ui.tabs, {
11750 spinner: "<em>Loading…</em>"
11752 _create: function() {
11755 tabsbeforeload: function( event, ui ) {
11756 // Don't react to nested tabs or tabs that don't use a spinner
11757 if ( event.target !== this.element[ 0 ] ||
11758 !this.options.spinner ) {
11762 var span = ui.tab.find( "span" ),
11763 html = span.html();
11764 span.html( this.options.spinner );
11765 ui.jqXHR.complete(function() {
11773 // enable/disable events
11774 $.widget( "ui.tabs", $.ui.tabs, {
11780 enable: function( index ) {
11781 var options = this.options,
11784 if ( index && options.disabled === true ||
11785 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) !== -1 ) ) {
11789 this._superApply( arguments );
11792 this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
11796 disable: function( index ) {
11797 var options = this.options,
11800 if ( index && options.disabled === false ||
11801 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) === -1 ) ) {
11805 this._superApply( arguments );
11808 this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
11813 // add/remove methods and events
11814 $.widget( "ui.tabs", $.ui.tabs, {
11818 tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
11821 add: function( url, label, index ) {
11822 if ( index === undefined ) {
11823 index = this.anchors.length;
11826 var doInsertAfter, panel,
11827 options = this.options,
11828 li = $( options.tabTemplate
11829 .replace( /#\{href\}/g, url )
11830 .replace( /#\{label\}/g, label ) ),
11831 id = !url.indexOf( "#" ) ?
11832 url.replace( "#", "" ) :
11835 li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
11836 li.attr( "aria-controls", id );
11838 doInsertAfter = index >= this.tabs.length;
11840 // try to find an existing element before creating a new one
11841 panel = this.element.find( "#" + id );
11842 if ( !panel.length ) {
11843 panel = this._createPanel( id );
11844 if ( doInsertAfter ) {
11846 panel.insertAfter( this.panels.eq( -1 ) );
11848 panel.appendTo( this.element );
11851 panel.insertBefore( this.panels[ index ] );
11854 panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide();
11856 if ( doInsertAfter ) {
11857 li.appendTo( this.tablist );
11859 li.insertBefore( this.tabs[ index ] );
11862 options.disabled = $.map( options.disabled, function( n ) {
11863 return n >= index ? ++n : n;
11867 if ( this.tabs.length === 1 && options.active === false ) {
11868 this.option( "active", 0 );
11871 this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
11875 remove: function( index ) {
11876 index = this._getIndex( index );
11877 var options = this.options,
11878 tab = this.tabs.eq( index ).remove(),
11879 panel = this._getPanelForTab( tab ).remove();
11881 // If selected tab was removed focus tab to the right or
11882 // in case the last tab was removed the tab to the left.
11883 // We check for more than 2 tabs, because if there are only 2,
11884 // then when we remove this tab, there will only be one tab left
11885 // so we don't need to detect which tab to activate.
11886 if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
11887 this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
11890 options.disabled = $.map(
11891 $.grep( options.disabled, function( n ) {
11892 return n !== index;
11895 return n >= index ? --n : n;
11900 this._trigger( "remove", null, this._ui( tab.find( "a" )[ 0 ], panel[ 0 ] ) );
11906 $.widget( "ui.tabs", $.ui.tabs, {
11907 length: function() {
11908 return this.anchors.length;
11912 // panel ids (idPrefix option + title attribute)
11913 $.widget( "ui.tabs", $.ui.tabs, {
11915 idPrefix: "ui-tabs-"
11918 _tabId: function( tab ) {
11919 var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
11921 return $( a ).closest( "li" ).attr( "aria-controls" ) ||
11922 a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
11923 this.options.idPrefix + getNextTabId();
11927 // _createPanel method
11928 $.widget( "ui.tabs", $.ui.tabs, {
11930 panelTemplate: "<div></div>"
11933 _createPanel: function( id ) {
11934 return $( this.options.panelTemplate )
11936 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
11937 .data( "ui-tabs-destroy", true );
11942 $.widget( "ui.tabs", $.ui.tabs, {
11943 _create: function() {
11944 var options = this.options;
11945 if ( options.active === null && options.selected !== undefined ) {
11946 options.active = options.selected === -1 ? false : options.selected;
11949 options.selected = options.active;
11950 if ( options.selected === false ) {
11951 options.selected = -1;
11955 _setOption: function( key, value ) {
11956 if ( key !== "selected" ) {
11957 return this._super( key, value );
11960 var options = this.options;
11961 this._super( "active", value === -1 ? false : value );
11962 options.selected = options.active;
11963 if ( options.selected === false ) {
11964 options.selected = -1;
11968 _eventHandler: function( event ) {
11969 this._superApply( arguments );
11970 this.options.selected = this.options.active;
11971 if ( this.options.selected === false ) {
11972 this.options.selected = -1;
11977 // show and select event
11978 $.widget( "ui.tabs", $.ui.tabs, {
11983 _create: function() {
11985 if ( this.options.active !== false ) {
11986 this._trigger( "show", null, this._ui(
11987 this.active.find( ".ui-tabs-anchor" )[ 0 ],
11988 this._getPanelForTab( this.active )[ 0 ] ) );
11991 _trigger: function( type, event, data ) {
11992 var ret = this._superApply( arguments );
11996 if ( type === "beforeActivate" && data.newTab.length ) {
11997 ret = this._super( "select", event, {
11998 tab: data.newTab.find( ".ui-tabs-anchor" )[ 0],
11999 panel: data.newPanel[ 0 ],
12000 index: data.newTab.closest( "li" ).index()
12002 } else if ( type === "activate" && data.newTab.length ) {
12003 ret = this._super( "show", event, {
12004 tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
12005 panel: data.newPanel[ 0 ],
12006 index: data.newTab.closest( "li" ).index()
12014 $.widget( "ui.tabs", $.ui.tabs, {
12015 select: function( index ) {
12016 index = this._getIndex( index );
12017 if ( index === -1 ) {
12018 if ( this.options.collapsible && this.options.selected !== -1 ) {
12019 index = this.options.selected;
12024 this.anchors.eq( index ).trigger( this.options.event + this.eventNamespace );
12033 $.widget( "ui.tabs", $.ui.tabs, {
12035 cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
12037 _create: function() {
12038 var options = this.options,
12040 if ( options.active == null && options.cookie ) {
12041 active = parseInt( this._cookie(), 10 );
12042 if ( active === -1 ) {
12045 options.active = active;
12049 _cookie: function( active ) {
12050 var cookie = [ this.cookie ||
12051 ( this.cookie = this.options.cookie.name || "ui-tabs-" + (++listId) ) ];
12052 if ( arguments.length ) {
12053 cookie.push( active === false ? -1 : active );
12054 cookie.push( this.options.cookie );
12056 return $.cookie.apply( null, cookie );
12058 _refresh: function() {
12060 if ( this.options.cookie ) {
12061 this._cookie( this.options.active, this.options.cookie );
12064 _eventHandler: function( event ) {
12065 this._superApply( arguments );
12066 if ( this.options.cookie ) {
12067 this._cookie( this.options.active, this.options.cookie );
12070 _destroy: function() {
12072 if ( this.options.cookie ) {
12073 this._cookie( null, this.options.cookie );
12081 $.widget( "ui.tabs", $.ui.tabs, {
12082 _trigger: function( type, event, data ) {
12083 var _data = $.extend( {}, data );
12084 if ( type === "load" ) {
12085 _data.panel = _data.panel[ 0 ];
12086 _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
12088 return this._super( type, event, _data );
12093 // The new animation options (show, hide) conflict with the old show callback.
12094 // The old fx option wins over show/hide anyway (always favor back-compat).
12095 // If a user wants to use the new animation API, they must give up the old API.
12096 $.widget( "ui.tabs", $.ui.tabs, {
12098 fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
12101 _getFx: function() {
12103 fx = this.options.fx;
12106 if ( $.isArray( fx ) ) {
12114 return fx ? { show: show, hide: hide } : null;
12117 _toggle: function( event, eventData ) {
12119 toShow = eventData.newPanel,
12120 toHide = eventData.oldPanel,
12121 fx = this._getFx();
12124 return this._super( event, eventData );
12127 that.running = true;
12129 function complete() {
12130 that.running = false;
12131 that._trigger( "activate", event, eventData );
12135 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
12137 if ( toShow.length && fx.show ) {
12139 .animate( fx.show, fx.show.duration, function() {
12148 // start out by hiding, then showing, then completing
12149 if ( toHide.length && fx.hide ) {
12150 toHide.animate( fx.hide, fx.hide.duration, function() {
12151 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
12155 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
12166 var increments = 0;
12168 function addDescribedBy( elem, id ) {
12169 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
12170 describedby.push( id );
12172 .data( "ui-tooltip-id", id )
12173 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
12176 function removeDescribedBy( elem ) {
12177 var id = elem.data( "ui-tooltip-id" ),
12178 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
12179 index = $.inArray( id, describedby );
12180 if ( index !== -1 ) {
12181 describedby.splice( index, 1 );
12184 elem.removeData( "ui-tooltip-id" );
12185 describedby = $.trim( describedby.join( " " ) );
12186 if ( describedby ) {
12187 elem.attr( "aria-describedby", describedby );
12189 elem.removeAttr( "aria-describedby" );
12193 $.widget( "ui.tooltip", {
12196 content: function() {
12197 return $( this ).attr( "title" );
12202 my: "left+15 center",
12203 at: "right center",
12204 collision: "flipfit flipfit"
12207 tooltipClass: null,
12215 _create: function() {
12221 // IDs of generated tooltips, needed for destroy
12222 this.tooltips = {};
12225 _setOption: function( key, value ) {
12228 if ( key === "disabled" ) {
12229 this[ value ? "_disable" : "_enable" ]();
12230 this.options[ key ] = value;
12231 // disable element style changes
12235 this._super( key, value );
12237 if ( key === "content" ) {
12238 $.each( this.tooltips, function( id, element ) {
12239 that._updateContent( element );
12244 _disable: function() {
12247 // close open tooltips
12248 $.each( this.tooltips, function( id, element ) {
12249 var event = $.Event( "blur" );
12250 event.target = event.currentTarget = element[0];
12251 that.close( event, true );
12254 // remove title attributes to prevent native tooltips
12255 this.element.find( this.options.items ).andSelf().each(function() {
12256 var element = $( this );
12257 if ( element.is( "[title]" ) ) {
12259 .data( "ui-tooltip-title", element.attr( "title" ) )
12260 .attr( "title", "" );
12265 _enable: function() {
12266 // restore title attributes
12267 this.element.find( this.options.items ).andSelf().each(function() {
12268 var element = $( this );
12269 if ( element.data( "ui-tooltip-title" ) ) {
12270 element.attr( "title", element.data( "ui-tooltip-title" ) );
12275 open: function( event ) {
12276 var target = $( event ? event.target : this.element )
12277 .closest( this.options.items );
12279 // No element to show a tooltip for
12280 if ( !target.length ) {
12284 // If the tooltip is open and we're tracking then reposition the tooltip.
12285 // This makes sure that a tracking tooltip doesn't obscure a focused element
12286 // if the user was hovering when the element gained focused.
12287 if ( this.options.track && target.data( "ui-tooltip-id" ) ) {
12288 this._find( target ).position( $.extend({
12290 }, this.options.position ) );
12291 // Stop tracking (#8622)
12292 this._off( this.document, "mousemove" );
12296 if ( target.attr( "title" ) ) {
12297 target.data( "ui-tooltip-title", target.attr( "title" ) );
12300 target.data( "tooltip-open", true );
12302 this._updateContent( target, event );
12305 _updateContent: function( target, event ) {
12307 contentOption = this.options.content,
12310 if ( typeof contentOption === "string" ) {
12311 return this._open( event, target, contentOption );
12314 content = contentOption.call( target[0], function( response ) {
12315 // ignore async response if tooltip was closed already
12316 if ( !target.data( "tooltip-open" ) ) {
12319 // IE may instantly serve a cached response for ajax requests
12320 // delay this call to _open so the other call to _open runs first
12321 that._delay(function() {
12322 this._open( event, target, response );
12326 this._open( event, target, content );
12330 _open: function( event, target, content ) {
12331 var tooltip, positionOption;
12336 // Content can be updated multiple times. If the tooltip already
12337 // exists, then just update the content and bail.
12338 tooltip = this._find( target );
12339 if ( tooltip.length ) {
12340 tooltip.find( ".ui-tooltip-content" ).html( content );
12344 // if we have a title, clear it to prevent the native tooltip
12345 // we have to check first to avoid defining a title if none exists
12346 // (we don't want to cause an element to start matching [title])
12348 // We use removeAttr only for key events, to allow IE to export the correct
12349 // accessible attributes. For mouse events, set to empty string to avoid
12350 // native tooltip showing up (happens only when removing inside mouseover).
12351 if ( target.is( "[title]" ) ) {
12352 if ( event && event.type === "mouseover" ) {
12353 target.attr( "title", "" );
12355 target.removeAttr( "title" );
12359 tooltip = this._tooltip( target );
12360 addDescribedBy( target, tooltip.attr( "id" ) );
12361 tooltip.find( ".ui-tooltip-content" ).html( content );
12363 function position( event ) {
12364 positionOption.of = event;
12365 tooltip.position( positionOption );
12367 if ( this.options.track && event && /^mouse/.test( event.originalEvent.type ) ) {
12368 positionOption = $.extend( {}, this.options.position );
12369 this._on( this.document, {
12370 mousemove: position
12372 // trigger once to override element-relative positioning
12375 tooltip.position( $.extend({
12377 }, this.options.position ) );
12382 this._show( tooltip, this.options.show );
12384 this._trigger( "open", event, { tooltip: tooltip } );
12386 this._on( target, {
12387 mouseleave: "close",
12389 keyup: function( event ) {
12390 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
12391 var fakeEvent = $.Event(event);
12392 fakeEvent.currentTarget = target[0];
12393 this.close( fakeEvent, true );
12399 close: function( event, force ) {
12401 target = $( event ? event.currentTarget : this.element ),
12402 tooltip = this._find( target );
12404 // disabling closes the tooltip, so we need to track when we're closing
12405 // to avoid an infinite loop in case the tooltip becomes disabled on close
12406 if ( this.closing ) {
12410 // don't close if the element has focus
12411 // this prevents the tooltip from closing if you hover while focused
12413 // we have to check the event type because tabbing out of the document
12414 // may leave the element as the activeElement
12415 if ( !force && event && event.type !== "focusout" &&
12416 this.document[0].activeElement === target[0] ) {
12420 // only set title if we had one before (see comment in _open())
12421 if ( target.data( "ui-tooltip-title" ) ) {
12422 target.attr( "title", target.data( "ui-tooltip-title" ) );
12425 removeDescribedBy( target );
12427 tooltip.stop( true );
12428 this._hide( tooltip, this.options.hide, function() {
12429 $( this ).remove();
12430 delete that.tooltips[ this.id ];
12433 target.removeData( "tooltip-open" );
12434 this._off( target, "mouseleave focusout keyup" );
12435 this._off( this.document, "mousemove" );
12437 this.closing = true;
12438 this._trigger( "close", event, { tooltip: tooltip } );
12439 this.closing = false;
12442 _tooltip: function( element ) {
12443 var id = "ui-tooltip-" + increments++,
12444 tooltip = $( "<div>" )
12449 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
12450 ( this.options.tooltipClass || "" ) );
12452 .addClass( "ui-tooltip-content" )
12453 .appendTo( tooltip );
12454 tooltip.appendTo( this.document[0].body );
12455 if ( $.fn.bgiframe ) {
12456 tooltip.bgiframe();
12458 this.tooltips[ id ] = element;
12462 _find: function( target ) {
12463 var id = target.data( "ui-tooltip-id" );
12464 return id ? $( "#" + id ) : $();
12467 _destroy: function() {
12470 // close open tooltips
12471 $.each( this.tooltips, function( id, element ) {
12472 // Delegate to close method to handle common cleanup
12473 var event = $.Event( "blur" );
12474 event.target = event.currentTarget = element[0];
12475 that.close( event, true );
12477 // Remove immediately; destroying an open tooltip doesn't use the
12479 $( "#" + id ).remove();
12481 // Restore the title
12482 if ( element.data( "ui-tooltip-title" ) ) {
12483 element.attr( "title", element.data( "ui-tooltip-title" ) );
12484 element.removeData( "ui-tooltip-title" );
12491 ;(jQuery.effects || (function($, undefined) {
12493 var backCompat = $.uiBackCompat !== false,
12494 // prefix used for storing data on .data()
12495 dataSpace = "ui-effects-";
12502 * jQuery Color Animations v2.0.0
12503 * http://jquery.com/
12505 * Copyright 2012 jQuery Foundation and other contributors
12506 * Released under the MIT license.
12507 * http://jquery.org/license
12509 * Date: Mon Aug 13 13:41:02 2012 -0500
12511 (function( jQuery, undefined ) {
12513 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor".split(" "),
12515 // plusequals test for += 100 -= 100
12516 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
12517 // a set of RE's that can match strings and generate color tuples.
12519 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
12520 parse: function( execResult ) {
12529 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
12530 parse: function( execResult ) {
12532 execResult[ 1 ] * 2.55,
12533 execResult[ 2 ] * 2.55,
12534 execResult[ 3 ] * 2.55,
12539 // this regex ignores A-F because it's compared against an already lowercased string
12540 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
12541 parse: function( execResult ) {
12543 parseInt( execResult[ 1 ], 16 ),
12544 parseInt( execResult[ 2 ], 16 ),
12545 parseInt( execResult[ 3 ], 16 )
12549 // this regex ignores A-F because it's compared against an already lowercased string
12550 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
12551 parse: function( execResult ) {
12553 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
12554 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
12555 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
12559 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
12561 parse: function( execResult ) {
12564 execResult[ 2 ] / 100,
12565 execResult[ 3 ] / 100,
12572 color = jQuery.Color = function( color, green, blue, alpha ) {
12573 return new jQuery.Color.fn.parse( color, green, blue, alpha );
12623 support = color.support = {},
12625 // element for support tests
12626 supportElem = jQuery( "<p>" )[ 0 ],
12628 // colors = jQuery.Color.names
12631 // local aliases of functions called often
12632 each = jQuery.each;
12634 // determine rgba support immediately
12635 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
12636 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
12638 // define cache name and alpha properties
12639 // for rgba and hsla spaces
12640 each( spaces, function( spaceName, space ) {
12641 space.cache = "_" + spaceName;
12642 space.props.alpha = {
12649 function clamp( value, prop, allowEmpty ) {
12650 var type = propTypes[ prop.type ] || {};
12652 if ( value == null ) {
12653 return (allowEmpty || !prop.def) ? null : prop.def;
12656 // ~~ is an short way of doing floor for positive numbers
12657 value = type.floor ? ~~value : parseFloat( value );
12659 // IE will pass in empty strings as value for alpha,
12660 // which will hit this case
12661 if ( isNaN( value ) ) {
12666 // we add mod before modding to make sure that negatives values
12667 // get converted properly: -10 -> 350
12668 return (value + type.mod) % type.mod;
12671 // for now all property types without mod have min and max
12672 return 0 > value ? 0 : type.max < value ? type.max : value;
12675 function stringParse( string ) {
12676 var inst = color(),
12677 rgba = inst._rgba = [];
12679 string = string.toLowerCase();
12681 each( stringParsers, function( i, parser ) {
12683 match = parser.re.exec( string ),
12684 values = match && parser.parse( match ),
12685 spaceName = parser.space || "rgba";
12688 parsed = inst[ spaceName ]( values );
12690 // if this was an rgba parse the assignment might happen twice
12692 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
12693 rgba = inst._rgba = parsed._rgba;
12695 // exit each( stringParsers ) here because we matched
12700 // Found a stringParser that handled it
12701 if ( rgba.length ) {
12703 // if this came from a parsed string, force "transparent" when alpha is 0
12704 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
12705 if ( rgba.join() === "0,0,0,0" ) {
12706 jQuery.extend( rgba, colors.transparent );
12712 return colors[ string ];
12715 color.fn = jQuery.extend( color.prototype, {
12716 parse: function( red, green, blue, alpha ) {
12717 if ( red === undefined ) {
12718 this._rgba = [ null, null, null, null ];
12721 if ( red.jquery || red.nodeType ) {
12722 red = jQuery( red ).css( green );
12727 type = jQuery.type( red ),
12728 rgba = this._rgba = [],
12731 // more than 1 argument specified - assume ( red, green, blue, alpha )
12732 if ( green !== undefined ) {
12733 red = [ red, green, blue, alpha ];
12737 if ( type === "string" ) {
12738 return this.parse( stringParse( red ) || colors._default );
12741 if ( type === "array" ) {
12742 each( spaces.rgba.props, function( key, prop ) {
12743 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
12748 if ( type === "object" ) {
12749 if ( red instanceof color ) {
12750 each( spaces, function( spaceName, space ) {
12751 if ( red[ space.cache ] ) {
12752 inst[ space.cache ] = red[ space.cache ].slice();
12756 each( spaces, function( spaceName, space ) {
12757 var cache = space.cache;
12758 each( space.props, function( key, prop ) {
12760 // if the cache doesn't exist, and we know how to convert
12761 if ( !inst[ cache ] && space.to ) {
12763 // if the value was null, we don't need to copy it
12764 // if the key was alpha, we don't need to copy it either
12765 if ( key === "alpha" || red[ key ] == null ) {
12768 inst[ cache ] = space.to( inst._rgba );
12771 // this is the only case where we allow nulls for ALL properties.
12772 // call clamp with alwaysAllowEmpty
12773 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
12776 // everything defined but alpha?
12777 if ( inst[ cache ] && $.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
12778 // use the default of 1
12779 inst[ cache ][ 3 ] = 1;
12780 if ( space.from ) {
12781 inst._rgba = space.from( inst[ cache ] );
12789 is: function( compare ) {
12790 var is = color( compare ),
12794 each( spaces, function( _, space ) {
12796 isCache = is[ space.cache ];
12798 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
12799 each( space.props, function( _, prop ) {
12800 if ( isCache[ prop.idx ] != null ) {
12801 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
12810 _space: function() {
12813 each( spaces, function( spaceName, space ) {
12814 if ( inst[ space.cache ] ) {
12815 used.push( spaceName );
12820 transition: function( other, distance ) {
12821 var end = color( other ),
12822 spaceName = end._space(),
12823 space = spaces[ spaceName ],
12824 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
12825 start = startColor[ space.cache ] || space.to( startColor._rgba ),
12826 result = start.slice();
12828 end = end[ space.cache ];
12829 each( space.props, function( key, prop ) {
12830 var index = prop.idx,
12831 startValue = start[ index ],
12832 endValue = end[ index ],
12833 type = propTypes[ prop.type ] || {};
12835 // if null, don't override start value
12836 if ( endValue === null ) {
12839 // if null - use end
12840 if ( startValue === null ) {
12841 result[ index ] = endValue;
12844 if ( endValue - startValue > type.mod / 2 ) {
12845 startValue += type.mod;
12846 } else if ( startValue - endValue > type.mod / 2 ) {
12847 startValue -= type.mod;
12850 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
12853 return this[ spaceName ]( result );
12855 blend: function( opaque ) {
12856 // if we are already opaque - return ourself
12857 if ( this._rgba[ 3 ] === 1 ) {
12861 var rgb = this._rgba.slice(),
12863 blend = color( opaque )._rgba;
12865 return color( jQuery.map( rgb, function( v, i ) {
12866 return ( 1 - a ) * blend[ i ] + a * v;
12869 toRgbaString: function() {
12870 var prefix = "rgba(",
12871 rgba = jQuery.map( this._rgba, function( v, i ) {
12872 return v == null ? ( i > 2 ? 1 : 0 ) : v;
12875 if ( rgba[ 3 ] === 1 ) {
12880 return prefix + rgba.join() + ")";
12882 toHslaString: function() {
12883 var prefix = "hsla(",
12884 hsla = jQuery.map( this.hsla(), function( v, i ) {
12890 if ( i && i < 3 ) {
12891 v = Math.round( v * 100 ) + "%";
12896 if ( hsla[ 3 ] === 1 ) {
12900 return prefix + hsla.join() + ")";
12902 toHexString: function( includeAlpha ) {
12903 var rgba = this._rgba.slice(),
12904 alpha = rgba.pop();
12906 if ( includeAlpha ) {
12907 rgba.push( ~~( alpha * 255 ) );
12910 return "#" + jQuery.map( rgba, function( v, i ) {
12912 // default to 0 when nulls exist
12913 v = ( v || 0 ).toString( 16 );
12914 return v.length === 1 ? "0" + v : v;
12917 toString: function() {
12918 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
12921 color.fn.parse.prototype = color.fn;
12923 // hsla conversions adapted from:
12924 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
12926 function hue2rgb( p, q, h ) {
12929 return p + (q - p) * h * 6;
12935 return p + (q - p) * ((2/3) - h) * 6;
12940 spaces.hsla.to = function ( rgba ) {
12941 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
12942 return [ null, null, null, rgba[ 3 ] ];
12944 var r = rgba[ 0 ] / 255,
12945 g = rgba[ 1 ] / 255,
12946 b = rgba[ 2 ] / 255,
12948 max = Math.max( r, g, b ),
12949 min = Math.min( r, g, b ),
12955 if ( min === max ) {
12957 } else if ( r === max ) {
12958 h = ( 60 * ( g - b ) / diff ) + 360;
12959 } else if ( g === max ) {
12960 h = ( 60 * ( b - r ) / diff ) + 120;
12962 h = ( 60 * ( r - g ) / diff ) + 240;
12965 if ( l === 0 || l === 1 ) {
12967 } else if ( l <= 0.5 ) {
12970 s = diff / ( 2 - add );
12972 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
12975 spaces.hsla.from = function ( hsla ) {
12976 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
12977 return [ null, null, null, hsla[ 3 ] ];
12979 var h = hsla[ 0 ] / 360,
12983 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
12988 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
12989 Math.round( hue2rgb( p, q, h ) * 255 ),
12990 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
12996 each( spaces, function( spaceName, space ) {
12997 var props = space.props,
12998 cache = space.cache,
13002 // makes rgba() and hsla()
13003 color.fn[ spaceName ] = function( value ) {
13005 // generate a cache for this space if it doesn't exist
13006 if ( to && !this[ cache ] ) {
13007 this[ cache ] = to( this._rgba );
13009 if ( value === undefined ) {
13010 return this[ cache ].slice();
13014 type = jQuery.type( value ),
13015 arr = ( type === "array" || type === "object" ) ? value : arguments,
13016 local = this[ cache ].slice();
13018 each( props, function( key, prop ) {
13019 var val = arr[ type === "object" ? key : prop.idx ];
13020 if ( val == null ) {
13021 val = local[ prop.idx ];
13023 local[ prop.idx ] = clamp( val, prop );
13027 ret = color( from( local ) );
13028 ret[ cache ] = local;
13031 return color( local );
13035 // makes red() green() blue() alpha() hue() saturation() lightness()
13036 each( props, function( key, prop ) {
13037 // alpha is included in more than one space
13038 if ( color.fn[ key ] ) {
13041 color.fn[ key ] = function( value ) {
13042 var vtype = jQuery.type( value ),
13043 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
13044 local = this[ fn ](),
13045 cur = local[ prop.idx ],
13048 if ( vtype === "undefined" ) {
13052 if ( vtype === "function" ) {
13053 value = value.call( this, cur );
13054 vtype = jQuery.type( value );
13056 if ( value == null && prop.empty ) {
13059 if ( vtype === "string" ) {
13060 match = rplusequals.exec( value );
13062 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
13065 local[ prop.idx ] = value;
13066 return this[ fn ]( local );
13071 // add .fx.step functions
13072 each( stepHooks, function( i, hook ) {
13073 jQuery.cssHooks[ hook ] = {
13074 set: function( elem, value ) {
13075 var parsed, curElem,
13076 backgroundColor = "";
13078 if ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) {
13079 value = color( parsed || value );
13080 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
13081 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
13083 (backgroundColor === "" || backgroundColor === "transparent") &&
13084 curElem && curElem.style
13087 backgroundColor = jQuery.css( curElem, "backgroundColor" );
13088 curElem = curElem.parentNode;
13093 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
13098 value = value.toRgbaString();
13101 elem.style[ hook ] = value;
13103 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
13107 jQuery.fx.step[ hook ] = function( fx ) {
13108 if ( !fx.colorInit ) {
13109 fx.start = color( fx.elem, hook );
13110 fx.end = color( fx.end );
13111 fx.colorInit = true;
13113 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
13117 jQuery.cssHooks.borderColor = {
13118 expand: function( value ) {
13121 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
13122 expanded[ "border" + part + "Color" ] = value;
13128 // Basic color names only.
13129 // Usage of any of the other color names requires adding yourself or including
13130 // jquery.color.svg-names.js.
13131 colors = jQuery.Color.names = {
13132 // 4.1. Basic color keywords
13136 fuchsia: "#ff00ff",
13150 // 4.2.3. "transparent" color keyword
13151 transparent: [ null, null, null, 0 ],
13153 _default: "#ffffff"
13160 /******************************************************************************/
13161 /****************************** CLASS ANIMATIONS ******************************/
13162 /******************************************************************************/
13165 var classAnimationActions = [ "add", "remove", "toggle" ],
13166 shorthandStyles = {
13178 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
13179 $.fx.step[ prop ] = function( fx ) {
13180 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
13181 jQuery.style( fx.elem, prop, fx.end );
13187 function getElementStyles() {
13188 var style = this.ownerDocument.defaultView ?
13189 this.ownerDocument.defaultView.getComputedStyle( this, null ) :
13196 // webkit enumerates style porperties
13197 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
13198 len = style.length;
13200 key = style[ len ];
13201 if ( typeof style[ key ] === "string" ) {
13202 newStyle[ $.camelCase( key ) ] = style[ key ];
13206 for ( key in style ) {
13207 if ( typeof style[ key ] === "string" ) {
13208 newStyle[ key ] = style[ key ];
13217 function styleDifference( oldStyle, newStyle ) {
13221 for ( name in newStyle ) {
13222 value = newStyle[ name ];
13223 if ( oldStyle[ name ] !== value ) {
13224 if ( !shorthandStyles[ name ] ) {
13225 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
13226 diff[ name ] = value;
13235 $.effects.animateClass = function( value, duration, easing, callback ) {
13236 var o = $.speed( duration, easing, callback );
13238 return this.queue( function() {
13239 var animated = $( this ),
13240 baseClass = animated.attr( "class" ) || "",
13242 allAnimations = o.children ? animated.find( "*" ).andSelf() : animated;
13244 // map the animated objects to store the original styles.
13245 allAnimations = allAnimations.map(function() {
13246 var el = $( this );
13249 start: getElementStyles.call( this )
13253 // apply class change
13254 applyClassChange = function() {
13255 $.each( classAnimationActions, function(i, action) {
13256 if ( value[ action ] ) {
13257 animated[ action + "Class" ]( value[ action ] );
13261 applyClassChange();
13263 // map all animated objects again - calculate new styles and diff
13264 allAnimations = allAnimations.map(function() {
13265 this.end = getElementStyles.call( this.el[ 0 ] );
13266 this.diff = styleDifference( this.start, this.end );
13270 // apply original class
13271 animated.attr( "class", baseClass );
13273 // map all animated objects again - this time collecting a promise
13274 allAnimations = allAnimations.map(function() {
13275 var styleInfo = this,
13276 dfd = $.Deferred(),
13277 opts = jQuery.extend({}, o, {
13279 complete: function() {
13280 dfd.resolve( styleInfo );
13284 this.el.animate( this.diff, opts );
13285 return dfd.promise();
13288 // once all animations have completed:
13289 $.when.apply( $, allAnimations.get() ).done(function() {
13291 // set the final class
13292 applyClassChange();
13294 // for each animated element,
13295 // clear all css properties that were animated
13296 $.each( arguments, function() {
13298 $.each( this.diff, function(key) {
13303 // this is guarnteed to be there if you use jQuery.speed()
13304 // it also handles dequeuing the next anim...
13305 o.complete.call( animated[ 0 ] );
13311 _addClass: $.fn.addClass,
13312 addClass: function( classNames, speed, easing, callback ) {
13314 $.effects.animateClass.call( this,
13315 { add: classNames }, speed, easing, callback ) :
13316 this._addClass( classNames );
13319 _removeClass: $.fn.removeClass,
13320 removeClass: function( classNames, speed, easing, callback ) {
13322 $.effects.animateClass.call( this,
13323 { remove: classNames }, speed, easing, callback ) :
13324 this._removeClass( classNames );
13327 _toggleClass: $.fn.toggleClass,
13328 toggleClass: function( classNames, force, speed, easing, callback ) {
13329 if ( typeof force === "boolean" || force === undefined ) {
13331 // without speed parameter
13332 return this._toggleClass( classNames, force );
13334 return $.effects.animateClass.call( this,
13335 (force ? { add: classNames } : { remove: classNames }),
13336 speed, easing, callback );
13339 // without force parameter
13340 return $.effects.animateClass.call( this,
13341 { toggle: classNames }, force, speed, easing );
13345 switchClass: function( remove, add, speed, easing, callback) {
13346 return $.effects.animateClass.call( this, {
13349 }, speed, easing, callback );
13355 /******************************************************************************/
13356 /*********************************** EFFECTS **********************************/
13357 /******************************************************************************/
13361 $.extend( $.effects, {
13364 // Saves a set of properties in a data storage
13365 save: function( element, set ) {
13366 for( var i=0; i < set.length; i++ ) {
13367 if ( set[ i ] !== null ) {
13368 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
13373 // Restores a set of previously saved properties from a data storage
13374 restore: function( element, set ) {
13376 for( i=0; i < set.length; i++ ) {
13377 if ( set[ i ] !== null ) {
13378 val = element.data( dataSpace + set[ i ] );
13379 // support: jQuery 1.6.2
13380 // http://bugs.jquery.com/ticket/9917
13381 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
13382 // We can't differentiate between "" and 0 here, so we just assume
13383 // empty string since it's likely to be a more common value...
13384 if ( val === undefined ) {
13387 element.css( set[ i ], val );
13392 setMode: function( el, mode ) {
13393 if (mode === "toggle") {
13394 mode = el.is( ":hidden" ) ? "show" : "hide";
13399 // Translates a [top,left] array into a baseline value
13400 // this should be a little more flexible in the future to handle a string & hash
13401 getBaseline: function( origin, original ) {
13403 switch ( origin[ 0 ] ) {
13404 case "top": y = 0; break;
13405 case "middle": y = 0.5; break;
13406 case "bottom": y = 1; break;
13407 default: y = origin[ 0 ] / original.height;
13409 switch ( origin[ 1 ] ) {
13410 case "left": x = 0; break;
13411 case "center": x = 0.5; break;
13412 case "right": x = 1; break;
13413 default: x = origin[ 1 ] / original.width;
13421 // Wraps the element around a wrapper that copies position properties
13422 createWrapper: function( element ) {
13424 // if the element is already wrapped, return it
13425 if ( element.parent().is( ".ui-effects-wrapper" )) {
13426 return element.parent();
13429 // wrap the element
13431 width: element.outerWidth(true),
13432 height: element.outerHeight(true),
13433 "float": element.css( "float" )
13435 wrapper = $( "<div></div>" )
13436 .addClass( "ui-effects-wrapper" )
13439 background: "transparent",
13444 // Store the size in case width/height are defined in % - Fixes #5245
13446 width: element.width(),
13447 height: element.height()
13449 active = document.activeElement;
13451 // support: Firefox
13452 // Firefox incorrectly exposes anonymous content
13453 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
13457 active = document.body;
13460 element.wrap( wrapper );
13462 // Fixes #7595 - Elements lose focus when wrapped.
13463 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
13464 $( active ).focus();
13467 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
13469 // transfer positioning properties to the wrapper
13470 if ( element.css( "position" ) === "static" ) {
13471 wrapper.css({ position: "relative" });
13472 element.css({ position: "relative" });
13475 position: element.css( "position" ),
13476 zIndex: element.css( "z-index" )
13478 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
13479 props[ pos ] = element.css( pos );
13480 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
13481 props[ pos ] = "auto";
13485 position: "relative",
13494 return wrapper.css( props ).show();
13497 removeWrapper: function( element ) {
13498 var active = document.activeElement;
13500 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
13501 element.parent().replaceWith( element );
13503 // Fixes #7595 - Elements lose focus when wrapped.
13504 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
13505 $( active ).focus();
13513 setTransition: function( element, list, factor, value ) {
13514 value = value || {};
13515 $.each( list, function( i, x ) {
13516 var unit = element.cssUnit( x );
13517 if ( unit[ 0 ] > 0 ) {
13518 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
13525 // return an effect options object for the given parameters:
13526 function _normalizeArguments( effect, options, speed, callback ) {
13528 // allow passing all optinos as the first parameter
13529 if ( $.isPlainObject( effect ) ) {
13531 effect = effect.effect;
13534 // convert to an object
13535 effect = { effect: effect };
13538 if ( options === undefined ) {
13542 // catch (effect, callback)
13543 if ( $.isFunction( options ) ) {
13544 callback = options;
13549 // catch (effect, speed, ?)
13550 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
13556 // catch (effect, options, callback)
13557 if ( $.isFunction( speed ) ) {
13562 // add options to effect
13564 $.extend( effect, options );
13567 speed = speed || options.duration;
13568 effect.duration = $.fx.off ? 0 :
13569 typeof speed === "number" ? speed :
13570 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
13571 $.fx.speeds._default;
13573 effect.complete = callback || options.complete;
13578 function standardSpeed( speed ) {
13579 // valid standard speeds
13580 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
13584 // invalid strings - treat as "normal" speed
13585 if ( typeof speed === "string" && !$.effects.effect[ speed ] ) {
13586 // TODO: remove in 2.0 (#7115)
13587 if ( backCompat && $.effects[ speed ] ) {
13597 effect: function( effect, options, speed, callback ) {
13598 var args = _normalizeArguments.apply( this, arguments ),
13600 queue = args.queue,
13601 effectMethod = $.effects.effect[ args.effect ],
13603 // DEPRECATED: remove in 2.0 (#7115)
13604 oldEffectMethod = !effectMethod && backCompat && $.effects[ args.effect ];
13606 if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
13607 // delegate to the original method (e.g., .show()) if possible
13609 return this[ mode ]( args.duration, args.complete );
13611 return this.each( function() {
13612 if ( args.complete ) {
13613 args.complete.call( this );
13619 function run( next ) {
13620 var elem = $( this ),
13621 complete = args.complete,
13625 if ( $.isFunction( complete ) ) {
13626 complete.call( elem[0] );
13628 if ( $.isFunction( next ) ) {
13633 // if the element is hiddden and mode is hide,
13634 // or element is visible and mode is show
13635 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
13638 effectMethod.call( elem[0], args, done );
13642 // TODO: remove this check in 2.0, effectMethod will always be true
13643 if ( effectMethod ) {
13644 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
13646 // DEPRECATED: remove in 2.0 (#7115)
13647 return oldEffectMethod.call(this, {
13649 duration: args.duration,
13650 callback: args.complete,
13657 show: function( speed ) {
13658 if ( standardSpeed( speed ) ) {
13659 return this._show.apply( this, arguments );
13661 var args = _normalizeArguments.apply( this, arguments );
13662 args.mode = "show";
13663 return this.effect.call( this, args );
13668 hide: function( speed ) {
13669 if ( standardSpeed( speed ) ) {
13670 return this._hide.apply( this, arguments );
13672 var args = _normalizeArguments.apply( this, arguments );
13673 args.mode = "hide";
13674 return this.effect.call( this, args );
13678 // jQuery core overloads toggle and creates _toggle
13679 __toggle: $.fn.toggle,
13680 toggle: function( speed ) {
13681 if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
13682 return this.__toggle.apply( this, arguments );
13684 var args = _normalizeArguments.apply( this, arguments );
13685 args.mode = "toggle";
13686 return this.effect.call( this, args );
13690 // helper functions
13691 cssUnit: function(key) {
13692 var style = this.css( key ),
13695 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
13696 if ( style.indexOf( unit ) > 0 ) {
13697 val = [ parseFloat( style ), unit ];
13706 /******************************************************************************/
13707 /*********************************** EASING ***********************************/
13708 /******************************************************************************/
13712 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
13714 var baseEasings = {};
13716 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
13717 baseEasings[ name ] = function( p ) {
13718 return Math.pow( p, i + 2 );
13722 $.extend( baseEasings, {
13723 Sine: function ( p ) {
13724 return 1 - Math.cos( p * Math.PI / 2 );
13726 Circ: function ( p ) {
13727 return 1 - Math.sqrt( 1 - p * p );
13729 Elastic: function( p ) {
13730 return p === 0 || p === 1 ? p :
13731 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
13733 Back: function( p ) {
13734 return p * p * ( 3 * p - 2 );
13736 Bounce: function ( p ) {
13740 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
13741 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
13745 $.each( baseEasings, function( name, easeIn ) {
13746 $.easing[ "easeIn" + name ] = easeIn;
13747 $.easing[ "easeOut" + name ] = function( p ) {
13748 return 1 - easeIn( 1 - p );
13750 $.easing[ "easeInOut" + name ] = function( p ) {
13752 easeIn( p * 2 ) / 2 :
13753 1 - easeIn( p * -2 + 2 ) / 2;
13760 (function( $, undefined ) {
13762 var rvertical = /up|down|vertical/,
13763 rpositivemotion = /up|left|vertical|horizontal/;
13765 $.effects.effect.blind = function( o, done ) {
13767 var el = $( this ),
13768 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
13769 mode = $.effects.setMode( el, o.mode || "hide" ),
13770 direction = o.direction || "up",
13771 vertical = rvertical.test( direction ),
13772 ref = vertical ? "height" : "width",
13773 ref2 = vertical ? "top" : "left",
13774 motion = rpositivemotion.test( direction ),
13776 show = mode === "show",
13777 wrapper, distance, margin;
13779 // if already wrapped, the wrapper's properties are my property. #6245
13780 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
13781 $.effects.save( el.parent(), props );
13783 $.effects.save( el, props );
13786 wrapper = $.effects.createWrapper( el ).css({
13790 distance = wrapper[ ref ]();
13791 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
13793 animation[ ref ] = show ? distance : 0;
13796 .css( vertical ? "bottom" : "right", 0 )
13797 .css( vertical ? "top" : "left", "auto" )
13798 .css({ position: "absolute" });
13800 animation[ ref2 ] = show ? margin : distance + margin;
13803 // start at 0 if we are showing
13805 wrapper.css( ref, 0 );
13807 wrapper.css( ref2, margin + distance );
13812 wrapper.animate( animation, {
13813 duration: o.duration,
13816 complete: function() {
13817 if ( mode === "hide" ) {
13820 $.effects.restore( el, props );
13821 $.effects.removeWrapper( el );
13829 (function( $, undefined ) {
13831 $.effects.effect.bounce = function( o, done ) {
13832 var el = $( this ),
13833 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
13836 mode = $.effects.setMode( el, o.mode || "effect" ),
13837 hide = mode === "hide",
13838 show = mode === "show",
13839 direction = o.direction || "up",
13840 distance = o.distance,
13841 times = o.times || 5,
13843 // number of internal animations
13844 anims = times * 2 + ( show || hide ? 1 : 0 ),
13845 speed = o.duration / anims,
13849 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
13850 motion = ( direction === "up" || direction === "left" ),
13855 // we will need to re-assemble the queue to stack our animations in place
13856 queue = el.queue(),
13857 queuelen = queue.length;
13859 // Avoid touching opacity to prevent clearType and PNG issues in IE
13860 if ( show || hide ) {
13861 props.push( "opacity" );
13864 $.effects.save( el, props );
13866 $.effects.createWrapper( el ); // Create Wrapper
13868 // default distance for the BIGGEST bounce is the outer Distance / 3
13870 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
13874 downAnim = { opacity: 1 };
13875 downAnim[ ref ] = 0;
13877 // if we are showing, force opacity 0 and set the initial position
13878 // then do the "first" animation
13879 el.css( "opacity", 0 )
13880 .css( ref, motion ? -distance * 2 : distance * 2 )
13881 .animate( downAnim, speed, easing );
13884 // start at the smallest distance if we are hiding
13886 distance = distance / Math.pow( 2, times - 1 );
13890 downAnim[ ref ] = 0;
13891 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
13892 for ( i = 0; i < times; i++ ) {
13894 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
13896 el.animate( upAnim, speed, easing )
13897 .animate( downAnim, speed, easing );
13899 distance = hide ? distance * 2 : distance / 2;
13902 // Last Bounce when Hiding
13904 upAnim = { opacity: 0 };
13905 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
13907 el.animate( upAnim, speed, easing );
13910 el.queue(function() {
13914 $.effects.restore( el, props );
13915 $.effects.removeWrapper( el );
13919 // inject all the animations we just queued to be first in line (after "inprogress")
13920 if ( queuelen > 1) {
13921 queue.splice.apply( queue,
13922 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
13929 (function( $, undefined ) {
13931 $.effects.effect.clip = function( o, done ) {
13933 var el = $( this ),
13934 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
13935 mode = $.effects.setMode( el, o.mode || "hide" ),
13936 show = mode === "show",
13937 direction = o.direction || "vertical",
13938 vert = direction === "vertical",
13939 size = vert ? "height" : "width",
13940 position = vert ? "top" : "left",
13942 wrapper, animate, distance;
13945 $.effects.save( el, props );
13949 wrapper = $.effects.createWrapper( el ).css({
13952 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
13953 distance = animate[ size ]();
13957 animate.css( size, 0 );
13958 animate.css( position, distance / 2 );
13961 // Create Animation Object:
13962 animation[ size ] = show ? distance : 0;
13963 animation[ position ] = show ? 0 : distance / 2;
13966 animate.animate( animation, {
13968 duration: o.duration,
13970 complete: function() {
13974 $.effects.restore( el, props );
13975 $.effects.removeWrapper( el );
13983 (function( $, undefined ) {
13985 $.effects.effect.drop = function( o, done ) {
13987 var el = $( this ),
13988 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
13989 mode = $.effects.setMode( el, o.mode || "hide" ),
13990 show = mode === "show",
13991 direction = o.direction || "left",
13992 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
13993 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
13995 opacity: show ? 1 : 0
14000 $.effects.save( el, props );
14002 $.effects.createWrapper( el );
14004 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
14008 .css( "opacity", 0 )
14009 .css( ref, motion === "pos" ? -distance : distance );
14013 animation[ ref ] = ( show ?
14014 ( motion === "pos" ? "+=" : "-=" ) :
14015 ( motion === "pos" ? "-=" : "+=" ) ) +
14019 el.animate( animation, {
14021 duration: o.duration,
14023 complete: function() {
14024 if ( mode === "hide" ) {
14027 $.effects.restore( el, props );
14028 $.effects.removeWrapper( el );
14035 (function( $, undefined ) {
14037 $.effects.effect.explode = function( o, done ) {
14039 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
14042 mode = $.effects.setMode( el, o.mode || "hide" ),
14043 show = mode === "show",
14045 // show and then visibility:hidden the element before calculating offset
14046 offset = el.show().css( "visibility", "hidden" ).offset(),
14048 // width and height of a piece
14049 width = Math.ceil( el.outerWidth() / cells ),
14050 height = Math.ceil( el.outerHeight() / rows ),
14054 i, j, left, top, mx, my;
14056 // children animate complete:
14057 function childComplete() {
14058 pieces.push( this );
14059 if ( pieces.length === rows * cells ) {
14064 // clone the element for each row and cell.
14065 for( i = 0; i < rows ; i++ ) { // ===>
14066 top = offset.top + i * height;
14067 my = i - ( rows - 1 ) / 2 ;
14069 for( j = 0; j < cells ; j++ ) { // |||
14070 left = offset.left + j * width;
14071 mx = j - ( cells - 1 ) / 2 ;
14073 // Create a clone of the now hidden main element that will be absolute positioned
14074 // within a wrapper div off the -left and -top equal to size of our pieces
14077 .appendTo( "body" )
14078 .wrap( "<div></div>" )
14080 position: "absolute",
14081 visibility: "visible",
14086 // select the wrapper - make it overflow: hidden and absolute positioned based on
14087 // where the original was located +left and +top equal to the size of pieces
14089 .addClass( "ui-effects-explode" )
14091 position: "absolute",
14092 overflow: "hidden",
14095 left: left + ( show ? mx * width : 0 ),
14096 top: top + ( show ? my * height : 0 ),
14097 opacity: show ? 0 : 1
14099 left: left + ( show ? 0 : mx * width ),
14100 top: top + ( show ? 0 : my * height ),
14101 opacity: show ? 1 : 0
14102 }, o.duration || 500, o.easing, childComplete );
14106 function animComplete() {
14108 visibility: "visible"
14110 $( pieces ).remove();
14119 (function( $, undefined ) {
14121 $.effects.effect.fade = function( o, done ) {
14122 var el = $( this ),
14123 mode = $.effects.setMode( el, o.mode || "toggle" );
14129 duration: o.duration,
14136 (function( $, undefined ) {
14138 $.effects.effect.fold = function( o, done ) {
14141 var el = $( this ),
14142 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14143 mode = $.effects.setMode( el, o.mode || "hide" ),
14144 show = mode === "show",
14145 hide = mode === "hide",
14146 size = o.size || 15,
14147 percent = /([0-9]+)%/.exec( size ),
14148 horizFirst = !!o.horizFirst,
14149 widthFirst = show !== horizFirst,
14150 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
14151 duration = o.duration / 2,
14156 $.effects.save( el, props );
14160 wrapper = $.effects.createWrapper( el ).css({
14163 distance = widthFirst ?
14164 [ wrapper.width(), wrapper.height() ] :
14165 [ wrapper.height(), wrapper.width() ];
14168 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
14171 wrapper.css( horizFirst ? {
14181 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
14182 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
14186 .animate( animation1, duration, o.easing )
14187 .animate( animation2, duration, o.easing, function() {
14191 $.effects.restore( el, props );
14192 $.effects.removeWrapper( el );
14199 (function( $, undefined ) {
14201 $.effects.effect.highlight = function( o, done ) {
14202 var elem = $( this ),
14203 props = [ "backgroundImage", "backgroundColor", "opacity" ],
14204 mode = $.effects.setMode( elem, o.mode || "show" ),
14206 backgroundColor: elem.css( "backgroundColor" )
14209 if (mode === "hide") {
14210 animation.opacity = 0;
14213 $.effects.save( elem, props );
14218 backgroundImage: "none",
14219 backgroundColor: o.color || "#ffff99"
14221 .animate( animation, {
14223 duration: o.duration,
14225 complete: function() {
14226 if ( mode === "hide" ) {
14229 $.effects.restore( elem, props );
14236 (function( $, undefined ) {
14238 $.effects.effect.pulsate = function( o, done ) {
14239 var elem = $( this ),
14240 mode = $.effects.setMode( elem, o.mode || "show" ),
14241 show = mode === "show",
14242 hide = mode === "hide",
14243 showhide = ( show || mode === "hide" ),
14245 // showing or hiding leaves of the "last" animation
14246 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
14247 duration = o.duration / anims,
14249 queue = elem.queue(),
14250 queuelen = queue.length,
14253 if ( show || !elem.is(":visible")) {
14254 elem.css( "opacity", 0 ).show();
14258 // anims - 1 opacity "toggles"
14259 for ( i = 1; i < anims; i++ ) {
14262 }, duration, o.easing );
14263 animateTo = 1 - animateTo;
14268 }, duration, o.easing);
14270 elem.queue(function() {
14277 // We just queued up "anims" animations, we need to put them next in the queue
14278 if ( queuelen > 1 ) {
14279 queue.splice.apply( queue,
14280 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
14286 (function( $, undefined ) {
14288 $.effects.effect.puff = function( o, done ) {
14289 var elem = $( this ),
14290 mode = $.effects.setMode( elem, o.mode || "hide" ),
14291 hide = mode === "hide",
14292 percent = parseInt( o.percent, 10 ) || 150,
14293 factor = percent / 100,
14295 height: elem.height(),
14296 width: elem.width()
14305 percent: hide ? percent : 100,
14309 height: original.height * factor,
14310 width: original.width * factor
14317 $.effects.effect.scale = function( o, done ) {
14320 var el = $( this ),
14321 options = $.extend( true, {}, o ),
14322 mode = $.effects.setMode( el, o.mode || "effect" ),
14323 percent = parseInt( o.percent, 10 ) ||
14324 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
14325 direction = o.direction || "both",
14328 height: el.height(),
14330 outerHeight: el.outerHeight(),
14331 outerWidth: el.outerWidth()
14334 y: direction !== "horizontal" ? (percent / 100) : 1,
14335 x: direction !== "vertical" ? (percent / 100) : 1
14338 // We are going to pass this effect to the size effect:
14339 options.effect = "size";
14340 options.queue = false;
14341 options.complete = done;
14343 // Set default origin and restore for show/hide
14344 if ( mode !== "effect" ) {
14345 options.origin = origin || ["middle","center"];
14346 options.restore = true;
14349 options.from = o.from || ( mode === "show" ? { height: 0, width: 0 } : original );
14351 height: original.height * factor.y,
14352 width: original.width * factor.x,
14353 outerHeight: original.outerHeight * factor.y,
14354 outerWidth: original.outerWidth * factor.x
14357 // Fade option to support puff
14358 if ( options.fade ) {
14359 if ( mode === "show" ) {
14360 options.from.opacity = 0;
14361 options.to.opacity = 1;
14363 if ( mode === "hide" ) {
14364 options.from.opacity = 1;
14365 options.to.opacity = 0;
14370 el.effect( options );
14374 $.effects.effect.size = function( o, done ) {
14377 var el = $( this ),
14378 props = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
14381 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
14383 // Copy for children
14384 props2 = [ "width", "height", "overflow" ],
14385 cProps = [ "fontSize" ],
14386 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
14387 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
14390 mode = $.effects.setMode( el, o.mode || "effect" ),
14391 restore = o.restore || mode !== "effect",
14392 scale = o.scale || "both",
14393 origin = o.origin || [ "middle", "center" ],
14394 original, baseline, factor,
14395 position = el.css( "position" );
14397 if ( mode === "show" ) {
14401 height: el.height(),
14403 outerHeight: el.outerHeight(),
14404 outerWidth: el.outerWidth()
14407 el.from = o.from || original;
14408 el.to = o.to || original;
14410 // Set scaling factor
14413 y: el.from.height / original.height,
14414 x: el.from.width / original.width
14417 y: el.to.height / original.height,
14418 x: el.to.width / original.width
14422 // Scale the css box
14423 if ( scale === "box" || scale === "both" ) {
14425 // Vertical props scaling
14426 if ( factor.from.y !== factor.to.y ) {
14427 props = props.concat( vProps );
14428 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
14429 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
14432 // Horizontal props scaling
14433 if ( factor.from.x !== factor.to.x ) {
14434 props = props.concat( hProps );
14435 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
14436 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
14440 // Scale the content
14441 if ( scale === "content" || scale === "both" ) {
14443 // Vertical props scaling
14444 if ( factor.from.y !== factor.to.y ) {
14445 props = props.concat( cProps );
14446 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
14447 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
14451 $.effects.save( el, restore ? props : props1 );
14453 $.effects.createWrapper( el );
14454 el.css( "overflow", "hidden" ).css( el.from );
14457 if (origin) { // Calculate baseline shifts
14458 baseline = $.effects.getBaseline( origin, original );
14459 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
14460 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
14461 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
14462 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
14464 el.css( el.from ); // set top & left
14467 if ( scale === "content" || scale === "both" ) { // Scale the children
14469 // Add margins/font-size
14470 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
14471 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
14472 props2 = props.concat(vProps).concat(hProps);
14474 el.find( "*[width]" ).each( function(){
14475 var child = $( this ),
14477 height: child.height(),
14478 width: child.width()
14481 $.effects.save(child, props2);
14485 height: c_original.height * factor.from.y,
14486 width: c_original.width * factor.from.x
14489 height: c_original.height * factor.to.y,
14490 width: c_original.width * factor.to.x
14493 // Vertical props scaling
14494 if ( factor.from.y !== factor.to.y ) {
14495 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
14496 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
14499 // Horizontal props scaling
14500 if ( factor.from.x !== factor.to.x ) {
14501 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
14502 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
14505 // Animate children
14506 child.css( child.from );
14507 child.animate( child.to, o.duration, o.easing, function() {
14509 // Restore children
14511 $.effects.restore( child, props2 );
14518 el.animate( el.to, {
14520 duration: o.duration,
14522 complete: function() {
14523 if ( el.to.opacity === 0 ) {
14524 el.css( "opacity", el.from.opacity );
14526 if( mode === "hide" ) {
14529 $.effects.restore( el, restore ? props : props1 );
14532 // we need to calculate our new positioning based on the scaling
14533 if ( position === "static" ) {
14535 position: "relative",
14540 $.each([ "top", "left" ], function( idx, pos ) {
14541 el.css( pos, function( _, str ) {
14542 var val = parseInt( str, 10 ),
14543 toRef = idx ? el.to.left : el.to.top;
14545 // if original was "auto", recalculate the new value from wrapper
14546 if ( str === "auto" ) {
14547 return toRef + "px";
14550 return val + toRef + "px";
14556 $.effects.removeWrapper( el );
14564 (function( $, undefined ) {
14566 $.effects.effect.shake = function( o, done ) {
14568 var el = $( this ),
14569 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14570 mode = $.effects.setMode( el, o.mode || "effect" ),
14571 direction = o.direction || "left",
14572 distance = o.distance || 20,
14573 times = o.times || 3,
14574 anims = times * 2 + 1,
14575 speed = Math.round(o.duration/anims),
14576 ref = (direction === "up" || direction === "down") ? "top" : "left",
14577 positiveMotion = (direction === "up" || direction === "left"),
14583 // we will need to re-assemble the queue to stack our animations in place
14584 queue = el.queue(),
14585 queuelen = queue.length;
14587 $.effects.save( el, props );
14589 $.effects.createWrapper( el );
14592 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
14593 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
14594 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
14597 el.animate( animation, speed, o.easing );
14600 for ( i = 1; i < times; i++ ) {
14601 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
14604 .animate( animation1, speed, o.easing )
14605 .animate( animation, speed / 2, o.easing )
14606 .queue(function() {
14607 if ( mode === "hide" ) {
14610 $.effects.restore( el, props );
14611 $.effects.removeWrapper( el );
14615 // inject all the animations we just queued to be first in line (after "inprogress")
14616 if ( queuelen > 1) {
14617 queue.splice.apply( queue,
14618 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
14625 (function( $, undefined ) {
14627 $.effects.effect.slide = function( o, done ) {
14630 var el = $( this ),
14631 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
14632 mode = $.effects.setMode( el, o.mode || "show" ),
14633 show = mode === "show",
14634 direction = o.direction || "left",
14635 ref = (direction === "up" || direction === "down") ? "top" : "left",
14636 positiveMotion = (direction === "up" || direction === "left"),
14641 $.effects.save( el, props );
14643 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
14645 $.effects.createWrapper( el ).css({
14650 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
14654 animation[ ref ] = ( show ?
14655 ( positiveMotion ? "+=" : "-=") :
14656 ( positiveMotion ? "-=" : "+=")) +
14660 el.animate( animation, {
14662 duration: o.duration,
14664 complete: function() {
14665 if ( mode === "hide" ) {
14668 $.effects.restore( el, props );
14669 $.effects.removeWrapper( el );
14676 (function( $, undefined ) {
14678 $.effects.effect.transfer = function( o, done ) {
14679 var elem = $( this ),
14680 target = $( o.to ),
14681 targetFixed = target.css( "position" ) === "fixed",
14683 fixTop = targetFixed ? body.scrollTop() : 0,
14684 fixLeft = targetFixed ? body.scrollLeft() : 0,
14685 endPosition = target.offset(),
14687 top: endPosition.top - fixTop ,
14688 left: endPosition.left - fixLeft ,
14689 height: target.innerHeight(),
14690 width: target.innerWidth()
14692 startPosition = elem.offset(),
14693 transfer = $( '<div class="ui-effects-transfer"></div>' )
14694 .appendTo( document.body )
14695 .addClass( o.className )
14697 top: startPosition.top - fixTop ,
14698 left: startPosition.left - fixLeft ,
14699 height: elem.innerHeight(),
14700 width: elem.innerWidth(),
14701 position: targetFixed ? "fixed" : "absolute"
14703 .animate( animation, o.duration, o.easing, function() {