1 /*! jQuery UI - v1.10.3 - 2013-08-18
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 2013 jQuery Foundation and other contributors Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // $.ui might exist from components with no dependencies, e.g., $.ui.position
45 focus: (function( orig ) {
46 return function( delay, fn ) {
47 return typeof delay === "number" ?
48 this.each(function() {
50 setTimeout(function() {
57 orig.apply( this, arguments );
61 scrollParent: function() {
63 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
64 scrollParent = this.parents().filter(function() {
65 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
68 scrollParent = this.parents().filter(function() {
69 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
73 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
76 zIndex: function( zIndex ) {
77 if ( zIndex !== undefined ) {
78 return this.css( "zIndex", zIndex );
82 var elem = $( this[ 0 ] ), position, value;
83 while ( elem.length && elem[ 0 ] !== document ) {
84 // Ignore z-index if position is set to a value where z-index is ignored by the browser
85 // This makes behavior of this function consistent across browsers
86 // WebKit always returns auto if the element is positioned
87 position = elem.css( "position" );
88 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
89 // IE returns 0 when zIndex is not specified
90 // other browsers return a string
91 // we ignore the case of nested elements with an explicit value of 0
92 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
93 value = parseInt( elem.css( "zIndex" ), 10 );
94 if ( !isNaN( value ) && value !== 0 ) {
105 uniqueId: function() {
106 return this.each(function() {
108 this.id = "ui-id-" + (++uuid);
113 removeUniqueId: function() {
114 return this.each(function() {
115 if ( runiqueId.test( this.id ) ) {
116 $( this ).removeAttr( "id" );
123 function focusable( element, isTabIndexNotNaN ) {
124 var map, mapName, img,
125 nodeName = element.nodeName.toLowerCase();
126 if ( "area" === nodeName ) {
127 map = element.parentNode;
129 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
132 img = $( "img[usemap=#" + mapName + "]" )[0];
133 return !!img && visible( img );
135 return ( /input|select|textarea|button|object/.test( nodeName ) ?
138 element.href || isTabIndexNotNaN :
140 // the element and all of its ancestors must be visible
144 function visible( element ) {
145 return $.expr.filters.visible( element ) &&
146 !$( element ).parents().addBack().filter(function() {
147 return $.css( this, "visibility" ) === "hidden";
151 $.extend( $.expr[ ":" ], {
152 data: $.expr.createPseudo ?
153 $.expr.createPseudo(function( dataName ) {
154 return function( elem ) {
155 return !!$.data( elem, dataName );
158 // support: jQuery <1.8
159 function( elem, i, match ) {
160 return !!$.data( elem, match[ 3 ] );
163 focusable: function( element ) {
164 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
167 tabbable: function( element ) {
168 var tabIndex = $.attr( element, "tabindex" ),
169 isTabIndexNaN = isNaN( tabIndex );
170 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
174 // support: jQuery <1.8
175 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
176 $.each( [ "Width", "Height" ], function( i, name ) {
177 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
178 type = name.toLowerCase(),
180 innerWidth: $.fn.innerWidth,
181 innerHeight: $.fn.innerHeight,
182 outerWidth: $.fn.outerWidth,
183 outerHeight: $.fn.outerHeight
186 function reduce( elem, size, border, margin ) {
187 $.each( side, function() {
188 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
190 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
193 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
199 $.fn[ "inner" + name ] = function( size ) {
200 if ( size === undefined ) {
201 return orig[ "inner" + name ].call( this );
204 return this.each(function() {
205 $( this ).css( type, reduce( this, size ) + "px" );
209 $.fn[ "outer" + name] = function( size, margin ) {
210 if ( typeof size !== "number" ) {
211 return orig[ "outer" + name ].call( this, size );
214 return this.each(function() {
215 $( this).css( type, reduce( this, size, true, margin ) + "px" );
221 // support: jQuery <1.8
222 if ( !$.fn.addBack ) {
223 $.fn.addBack = function( selector ) {
224 return this.add( selector == null ?
225 this.prevObject : this.prevObject.filter( selector )
230 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
231 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
232 $.fn.removeData = (function( removeData ) {
233 return function( key ) {
234 if ( arguments.length ) {
235 return removeData.call( this, $.camelCase( key ) );
237 return removeData.call( this );
240 })( $.fn.removeData );
248 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
250 $.support.selectstart = "onselectstart" in document.createElement( "div" );
252 disableSelection: function() {
253 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
254 ".ui-disableSelection", function( event ) {
255 event.preventDefault();
259 enableSelection: function() {
260 return this.unbind( ".ui-disableSelection" );
265 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
267 add: function( module, option, set ) {
269 proto = $.ui[ module ].prototype;
271 proto.plugins[ i ] = proto.plugins[ i ] || [];
272 proto.plugins[ i ].push( [ option, set[ i ] ] );
275 call: function( instance, name, args ) {
277 set = instance.plugins[ name ];
278 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
282 for ( i = 0; i < set.length; i++ ) {
283 if ( instance.options[ set[ i ][ 0 ] ] ) {
284 set[ i ][ 1 ].apply( instance.element, args );
290 // only used by resizable
291 hasScroll: function( el, a ) {
293 //If overflow is hidden, the element might have extra content, but the user wants to hide it
294 if ( $( el ).css( "overflow" ) === "hidden") {
298 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
301 if ( el[ scroll ] > 0 ) {
305 // TODO: determine which cases actually cause this to happen
306 // if the element doesn't have the scroll set, see if it's possible to
309 has = ( el[ scroll ] > 0 );
316 (function( $, undefined ) {
319 slice = Array.prototype.slice,
320 _cleanData = $.cleanData;
321 $.cleanData = function( elems ) {
322 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
324 $( elem ).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
331 $.widget = function( name, base, prototype ) {
332 var fullName, existingConstructor, constructor, basePrototype,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype = {},
336 namespace = name.split( "." )[ 0 ];
338 name = name.split( "." )[ 1 ];
339 fullName = namespace + "-" + name;
346 // create selector for plugin
347 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348 return !!$.data( elem, fullName );
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor = $[ namespace ][ name ];
353 constructor = $[ namespace ][ name ] = function( options, element ) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget ) {
356 return new constructor( options, element );
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments.length ) {
362 this._createWidget( options, element );
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor, {
367 version: prototype.version,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype = new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
380 basePrototype.options = $.widget.extend( {}, basePrototype.options );
381 $.each( prototype, function( prop, value ) {
382 if ( !$.isFunction( value ) ) {
383 proxiedPrototype[ prop ] = value;
386 proxiedPrototype[ prop ] = (function() {
387 var _super = function() {
388 return base.prototype[ prop ].apply( this, arguments );
390 _superApply = function( args ) {
391 return base.prototype[ prop ].apply( this, args );
394 var __super = this._super,
395 __superApply = this._superApply,
398 this._super = _super;
399 this._superApply = _superApply;
401 returnValue = value.apply( this, arguments );
403 this._super = __super;
404 this._superApply = __superApply;
410 constructor.prototype = $.widget.extend( basePrototype, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
415 }, proxiedPrototype, {
416 constructor: constructor,
417 namespace: namespace,
419 widgetFullName: fullName
422 // If this widget is being redefined then we need to find all widgets that
423 // are inheriting from it and redefine all of them so that they inherit from
424 // the new version of this widget. We're essentially trying to replace one
425 // level in the prototype chain.
426 if ( existingConstructor ) {
427 $.each( existingConstructor._childConstructors, function( i, child ) {
428 var childPrototype = child.prototype;
430 // redefine the child widget using the same prototype that was
431 // originally used, but inherit from the new version of the base
432 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
434 // remove the list of existing child constructors from the old constructor
435 // so the old child constructors can be garbage collected
436 delete existingConstructor._childConstructors;
438 base._childConstructors.push( constructor );
441 $.widget.bridge( name, constructor );
444 $.widget.extend = function( target ) {
445 var input = slice.call( arguments, 1 ),
447 inputLength = input.length,
450 for ( ; inputIndex < inputLength; inputIndex++ ) {
451 for ( key in input[ inputIndex ] ) {
452 value = input[ inputIndex ][ key ];
453 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
455 if ( $.isPlainObject( value ) ) {
456 target[ key ] = $.isPlainObject( target[ key ] ) ?
457 $.widget.extend( {}, target[ key ], value ) :
458 // Don't extend strings, arrays, etc. with objects
459 $.widget.extend( {}, value );
460 // Copy everything else by reference
462 target[ key ] = value;
470 $.widget.bridge = function( name, object ) {
471 var fullName = object.prototype.widgetFullName || name;
472 $.fn[ name ] = function( options ) {
473 var isMethodCall = typeof options === "string",
474 args = slice.call( arguments, 1 ),
477 // allow multiple hashes to be passed on init
478 options = !isMethodCall && args.length ?
479 $.widget.extend.apply( null, [ options ].concat(args) ) :
482 if ( isMethodCall ) {
483 this.each(function() {
485 instance = $.data( this, fullName );
487 return $.error( "cannot call methods on " + name + " prior to initialization; " +
488 "attempted to call method '" + options + "'" );
490 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
491 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
493 methodValue = instance[ options ].apply( instance, args );
494 if ( methodValue !== instance && methodValue !== undefined ) {
495 returnValue = methodValue && methodValue.jquery ?
496 returnValue.pushStack( methodValue.get() ) :
502 this.each(function() {
503 var instance = $.data( this, fullName );
505 instance.option( options || {} )._init();
507 $.data( this, fullName, new object( options, this ) );
516 $.Widget = function( /* options, element */ ) {};
517 $.Widget._childConstructors = [];
519 $.Widget.prototype = {
520 widgetName: "widget",
521 widgetEventPrefix: "",
522 defaultElement: "<div>",
529 _createWidget: function( options, element ) {
530 element = $( element || this.defaultElement || this )[ 0 ];
531 this.element = $( element );
533 this.eventNamespace = "." + this.widgetName + this.uuid;
534 this.options = $.widget.extend( {},
536 this._getCreateOptions(),
540 this.hoverable = $();
541 this.focusable = $();
543 if ( element !== this ) {
544 $.data( element, this.widgetFullName, this );
545 this._on( true, this.element, {
546 remove: function( event ) {
547 if ( event.target === element ) {
552 this.document = $( element.style ?
553 // element within the document
554 element.ownerDocument :
555 // element is window or document
556 element.document || element );
557 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
561 this._trigger( "create", null, this._getCreateEventData() );
564 _getCreateOptions: $.noop,
565 _getCreateEventData: $.noop,
569 destroy: function() {
571 // we can probably remove the unbind calls in 2.0
572 // all event bindings should go through this._on()
574 .unbind( this.eventNamespace )
576 // TODO remove dual storage
577 .removeData( this.widgetName )
578 .removeData( this.widgetFullName )
579 // support: jquery <1.6.3
580 // http://bugs.jquery.com/ticket/9413
581 .removeData( $.camelCase( this.widgetFullName ) );
583 .unbind( this.eventNamespace )
584 .removeAttr( "aria-disabled" )
586 this.widgetFullName + "-disabled " +
587 "ui-state-disabled" );
589 // clean up events and states
590 this.bindings.unbind( this.eventNamespace );
591 this.hoverable.removeClass( "ui-state-hover" );
592 this.focusable.removeClass( "ui-state-focus" );
600 option: function( key, value ) {
606 if ( arguments.length === 0 ) {
607 // don't return a reference to the internal hash
608 return $.widget.extend( {}, this.options );
611 if ( typeof key === "string" ) {
612 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
614 parts = key.split( "." );
616 if ( parts.length ) {
617 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
618 for ( i = 0; i < parts.length - 1; i++ ) {
619 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
620 curOption = curOption[ parts[ i ] ];
623 if ( value === undefined ) {
624 return curOption[ key ] === undefined ? null : curOption[ key ];
626 curOption[ key ] = value;
628 if ( value === undefined ) {
629 return this.options[ key ] === undefined ? null : this.options[ key ];
631 options[ key ] = value;
635 this._setOptions( options );
639 _setOptions: function( options ) {
642 for ( key in options ) {
643 this._setOption( key, options[ key ] );
648 _setOption: function( key, value ) {
649 this.options[ key ] = value;
651 if ( key === "disabled" ) {
653 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
654 .attr( "aria-disabled", value );
655 this.hoverable.removeClass( "ui-state-hover" );
656 this.focusable.removeClass( "ui-state-focus" );
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck, element, handlers ) {
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck !== "boolean" ) {
676 element = suppressDisabledCheck;
677 suppressDisabledCheck = false;
680 // no element argument, shuffle and use this.element
683 element = this.element;
684 delegateElement = this.widget();
686 // accept selectors, DOM elements
687 element = delegateElement = $( element );
688 this.bindings = this.bindings.add( element );
691 $.each( handlers, function( event, handler ) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck &&
697 ( instance.options.disabled === true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
701 return ( typeof handler === "string" ? instance[ handler ] : handler )
702 .apply( instance, arguments );
705 // copy the guid so direct unbinding works
706 if ( typeof handler !== "string" ) {
707 handlerProxy.guid = handler.guid =
708 handler.guid || handlerProxy.guid || $.guid++;
711 var match = event.match( /^(\w+)\s*(.*)$/ ),
712 eventName = match[1] + instance.eventNamespace,
715 delegateElement.delegate( selector, eventName, handlerProxy );
717 element.bind( eventName, handlerProxy );
722 _off: function( element, eventName ) {
723 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724 element.unbind( eventName ).undelegate( eventName );
727 _delay: function( handler, delay ) {
728 function handlerProxy() {
729 return ( typeof handler === "string" ? instance[ handler ] : handler )
730 .apply( instance, arguments );
733 return setTimeout( handlerProxy, delay || 0 );
736 _hoverable: function( element ) {
737 this.hoverable = this.hoverable.add( element );
739 mouseenter: function( event ) {
740 $( event.currentTarget ).addClass( "ui-state-hover" );
742 mouseleave: function( event ) {
743 $( event.currentTarget ).removeClass( "ui-state-hover" );
748 _focusable: function( element ) {
749 this.focusable = this.focusable.add( element );
751 focusin: function( event ) {
752 $( event.currentTarget ).addClass( "ui-state-focus" );
754 focusout: function( event ) {
755 $( event.currentTarget ).removeClass( "ui-state-focus" );
760 _trigger: function( type, event, data ) {
762 callback = this.options[ type ];
765 event = $.Event( event );
766 event.type = ( type === this.widgetEventPrefix ?
768 this.widgetEventPrefix + type ).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event.target = this.element[ 0 ];
773 // copy original event properties over to the new event
774 orig = event.originalEvent;
776 for ( prop in orig ) {
777 if ( !( prop in event ) ) {
778 event[ prop ] = orig[ prop ];
783 this.element.trigger( event, data );
784 return !( $.isFunction( callback ) &&
785 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
786 event.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792 if ( typeof options === "string" ) {
793 options = { effect: options };
796 effectName = !options ?
798 options === true || typeof options === "number" ?
800 options.effect || defaultEffect;
801 options = options || {};
802 if ( typeof options === "number" ) {
803 options = { duration: options };
805 hasOptions = !$.isEmptyObject( options );
806 options.complete = callback;
807 if ( options.delay ) {
808 element.delay( options.delay );
810 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811 element[ method ]( options );
812 } else if ( effectName !== method && element[ effectName ] ) {
813 element[ effectName ]( options.duration, options.easing, callback );
815 element.queue(function( next ) {
816 $( this )[ method ]();
818 callback.call( element[ 0 ] );
827 (function( $, undefined ) {
829 var mouseHandled = false;
830 $( document ).mouseup( function() {
831 mouseHandled = false;
834 $.widget("ui.mouse", {
837 cancel: "input,textarea,button,select,option",
841 _mouseInit: function() {
845 .bind("mousedown."+this.widgetName, function(event) {
846 return that._mouseDown(event);
848 .bind("click."+this.widgetName, function(event) {
849 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
850 $.removeData(event.target, that.widgetName + ".preventClickEvent");
851 event.stopImmediatePropagation();
856 this.started = false;
859 // TODO: make sure destroying one instance of mouse doesn't mess with
860 // other instances of mouse
861 _mouseDestroy: function() {
862 this.element.unbind("."+this.widgetName);
863 if ( this._mouseMoveDelegate ) {
865 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
866 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
870 _mouseDown: function(event) {
871 // don't let more than one widget handle mouseStart
872 if( mouseHandled ) { return; }
874 // we may have missed mouseup (out of window)
875 (this._mouseStarted && this._mouseUp(event));
877 this._mouseDownEvent = event;
880 btnIsLeft = (event.which === 1),
881 // event.target.nodeName works around a bug in IE 8 with
882 // disabled inputs (#7620)
883 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
884 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
888 this.mouseDelayMet = !this.options.delay;
889 if (!this.mouseDelayMet) {
890 this._mouseDelayTimer = setTimeout(function() {
891 that.mouseDelayMet = true;
892 }, this.options.delay);
895 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
896 this._mouseStarted = (this._mouseStart(event) !== false);
897 if (!this._mouseStarted) {
898 event.preventDefault();
903 // Click event may never have fired (Gecko & Opera)
904 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
905 $.removeData(event.target, this.widgetName + ".preventClickEvent");
908 // these delegates are required to keep context
909 this._mouseMoveDelegate = function(event) {
910 return that._mouseMove(event);
912 this._mouseUpDelegate = function(event) {
913 return that._mouseUp(event);
916 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
917 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
919 event.preventDefault();
925 _mouseMove: function(event) {
926 // IE mouseup check - mouseup happened when mouse was out of window
927 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
928 return this._mouseUp(event);
931 if (this._mouseStarted) {
932 this._mouseDrag(event);
933 return event.preventDefault();
936 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
938 (this._mouseStart(this._mouseDownEvent, event) !== false);
939 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
942 return !this._mouseStarted;
945 _mouseUp: function(event) {
947 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
948 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
950 if (this._mouseStarted) {
951 this._mouseStarted = false;
953 if (event.target === this._mouseDownEvent.target) {
954 $.data(event.target, this.widgetName + ".preventClickEvent", true);
957 this._mouseStop(event);
963 _mouseDistanceMet: function(event) {
965 Math.abs(this._mouseDownEvent.pageX - event.pageX),
966 Math.abs(this._mouseDownEvent.pageY - event.pageY)
967 ) >= this.options.distance
971 _mouseDelayMet: function(/* event */) {
972 return this.mouseDelayMet;
975 // These are placeholder methods, to be overriden by extending plugin
976 _mouseStart: function(/* event */) {},
977 _mouseDrag: function(/* event */) {},
978 _mouseStop: function(/* event */) {},
979 _mouseCapture: function(/* event */) { return true; }
983 (function( $, undefined ) {
987 var cachedScrollbarWidth,
991 rhorizontal = /left|center|right/,
992 rvertical = /top|center|bottom/,
993 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
996 _position = $.fn.position;
998 function getOffsets( offsets, width, height ) {
1000 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1001 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1005 function parseCss( element, property ) {
1006 return parseInt( $.css( element, property ), 10 ) || 0;
1009 function getDimensions( elem ) {
1011 if ( raw.nodeType === 9 ) {
1013 width: elem.width(),
1014 height: elem.height(),
1015 offset: { top: 0, left: 0 }
1018 if ( $.isWindow( raw ) ) {
1020 width: elem.width(),
1021 height: elem.height(),
1022 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
1025 if ( raw.preventDefault ) {
1029 offset: { top: raw.pageY, left: raw.pageX }
1033 width: elem.outerWidth(),
1034 height: elem.outerHeight(),
1035 offset: elem.offset()
1040 scrollbarWidth: function() {
1041 if ( cachedScrollbarWidth !== undefined ) {
1042 return cachedScrollbarWidth;
1045 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1046 innerDiv = div.children()[0];
1048 $( "body" ).append( div );
1049 w1 = innerDiv.offsetWidth;
1050 div.css( "overflow", "scroll" );
1052 w2 = innerDiv.offsetWidth;
1055 w2 = div[0].clientWidth;
1060 return (cachedScrollbarWidth = w1 - w2);
1062 getScrollInfo: function( within ) {
1063 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
1064 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
1065 hasOverflowX = overflowX === "scroll" ||
1066 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1067 hasOverflowY = overflowY === "scroll" ||
1068 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1070 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
1071 height: hasOverflowX ? $.position.scrollbarWidth() : 0
1074 getWithinInfo: function( element ) {
1075 var withinElement = $( element || window ),
1076 isWindow = $.isWindow( withinElement[0] );
1078 element: withinElement,
1080 offset: withinElement.offset() || { left: 0, top: 0 },
1081 scrollLeft: withinElement.scrollLeft(),
1082 scrollTop: withinElement.scrollTop(),
1083 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1084 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1089 $.fn.position = function( options ) {
1090 if ( !options || !options.of ) {
1091 return _position.apply( this, arguments );
1094 // make a copy, we don't want to modify arguments
1095 options = $.extend( {}, options );
1097 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
1098 target = $( options.of ),
1099 within = $.position.getWithinInfo( options.within ),
1100 scrollInfo = $.position.getScrollInfo( within ),
1101 collision = ( options.collision || "flip" ).split( " " ),
1104 dimensions = getDimensions( target );
1105 if ( target[0].preventDefault ) {
1106 // force left top to allow flipping
1107 options.at = "left top";
1109 targetWidth = dimensions.width;
1110 targetHeight = dimensions.height;
1111 targetOffset = dimensions.offset;
1112 // clone to reuse original targetOffset later
1113 basePosition = $.extend( {}, targetOffset );
1115 // force my and at to have valid horizontal and vertical positions
1116 // if a value is missing or invalid, it will be converted to center
1117 $.each( [ "my", "at" ], function() {
1118 var pos = ( options[ this ] || "" ).split( " " ),
1122 if ( pos.length === 1) {
1123 pos = rhorizontal.test( pos[ 0 ] ) ?
1124 pos.concat( [ "center" ] ) :
1125 rvertical.test( pos[ 0 ] ) ?
1126 [ "center" ].concat( pos ) :
1127 [ "center", "center" ];
1129 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1130 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1132 // calculate offsets
1133 horizontalOffset = roffset.exec( pos[ 0 ] );
1134 verticalOffset = roffset.exec( pos[ 1 ] );
1136 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1137 verticalOffset ? verticalOffset[ 0 ] : 0
1140 // reduce to just the positions without the offsets
1142 rposition.exec( pos[ 0 ] )[ 0 ],
1143 rposition.exec( pos[ 1 ] )[ 0 ]
1147 // normalize collision option
1148 if ( collision.length === 1 ) {
1149 collision[ 1 ] = collision[ 0 ];
1152 if ( options.at[ 0 ] === "right" ) {
1153 basePosition.left += targetWidth;
1154 } else if ( options.at[ 0 ] === "center" ) {
1155 basePosition.left += targetWidth / 2;
1158 if ( options.at[ 1 ] === "bottom" ) {
1159 basePosition.top += targetHeight;
1160 } else if ( options.at[ 1 ] === "center" ) {
1161 basePosition.top += targetHeight / 2;
1164 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1165 basePosition.left += atOffset[ 0 ];
1166 basePosition.top += atOffset[ 1 ];
1168 return this.each(function() {
1169 var collisionPosition, using,
1171 elemWidth = elem.outerWidth(),
1172 elemHeight = elem.outerHeight(),
1173 marginLeft = parseCss( this, "marginLeft" ),
1174 marginTop = parseCss( this, "marginTop" ),
1175 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1176 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1177 position = $.extend( {}, basePosition ),
1178 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1180 if ( options.my[ 0 ] === "right" ) {
1181 position.left -= elemWidth;
1182 } else if ( options.my[ 0 ] === "center" ) {
1183 position.left -= elemWidth / 2;
1186 if ( options.my[ 1 ] === "bottom" ) {
1187 position.top -= elemHeight;
1188 } else if ( options.my[ 1 ] === "center" ) {
1189 position.top -= elemHeight / 2;
1192 position.left += myOffset[ 0 ];
1193 position.top += myOffset[ 1 ];
1195 // if the browser doesn't support fractions, then round for consistent results
1196 if ( !$.support.offsetFractions ) {
1197 position.left = round( position.left );
1198 position.top = round( position.top );
1201 collisionPosition = {
1202 marginLeft: marginLeft,
1203 marginTop: marginTop
1206 $.each( [ "left", "top" ], function( i, dir ) {
1207 if ( $.ui.position[ collision[ i ] ] ) {
1208 $.ui.position[ collision[ i ] ][ dir ]( position, {
1209 targetWidth: targetWidth,
1210 targetHeight: targetHeight,
1211 elemWidth: elemWidth,
1212 elemHeight: elemHeight,
1213 collisionPosition: collisionPosition,
1214 collisionWidth: collisionWidth,
1215 collisionHeight: collisionHeight,
1216 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1225 if ( options.using ) {
1226 // adds feedback as second argument to using callback, if present
1227 using = function( props ) {
1228 var left = targetOffset.left - position.left,
1229 right = left + targetWidth - elemWidth,
1230 top = targetOffset.top - position.top,
1231 bottom = top + targetHeight - elemHeight,
1235 left: targetOffset.left,
1236 top: targetOffset.top,
1238 height: targetHeight
1242 left: position.left,
1247 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1248 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1250 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1251 feedback.horizontal = "center";
1253 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1254 feedback.vertical = "middle";
1256 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1257 feedback.important = "horizontal";
1259 feedback.important = "vertical";
1261 options.using.call( this, props, feedback );
1265 elem.offset( $.extend( position, { using: using } ) );
1271 left: function( position, data ) {
1272 var within = data.within,
1273 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1274 outerWidth = within.width,
1275 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1276 overLeft = withinOffset - collisionPosLeft,
1277 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1280 // element is wider than within
1281 if ( data.collisionWidth > outerWidth ) {
1282 // element is initially over the left side of within
1283 if ( overLeft > 0 && overRight <= 0 ) {
1284 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1285 position.left += overLeft - newOverRight;
1286 // element is initially over right side of within
1287 } else if ( overRight > 0 && overLeft <= 0 ) {
1288 position.left = withinOffset;
1289 // element is initially over both left and right sides of within
1291 if ( overLeft > overRight ) {
1292 position.left = withinOffset + outerWidth - data.collisionWidth;
1294 position.left = withinOffset;
1297 // too far left -> align with left edge
1298 } else if ( overLeft > 0 ) {
1299 position.left += overLeft;
1300 // too far right -> align with right edge
1301 } else if ( overRight > 0 ) {
1302 position.left -= overRight;
1303 // adjust based on position and margin
1305 position.left = max( position.left - collisionPosLeft, position.left );
1308 top: function( position, data ) {
1309 var within = data.within,
1310 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1311 outerHeight = data.within.height,
1312 collisionPosTop = position.top - data.collisionPosition.marginTop,
1313 overTop = withinOffset - collisionPosTop,
1314 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1317 // element is taller than within
1318 if ( data.collisionHeight > outerHeight ) {
1319 // element is initially over the top of within
1320 if ( overTop > 0 && overBottom <= 0 ) {
1321 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1322 position.top += overTop - newOverBottom;
1323 // element is initially over bottom of within
1324 } else if ( overBottom > 0 && overTop <= 0 ) {
1325 position.top = withinOffset;
1326 // element is initially over both top and bottom of within
1328 if ( overTop > overBottom ) {
1329 position.top = withinOffset + outerHeight - data.collisionHeight;
1331 position.top = withinOffset;
1334 // too far up -> align with top
1335 } else if ( overTop > 0 ) {
1336 position.top += overTop;
1337 // too far down -> align with bottom edge
1338 } else if ( overBottom > 0 ) {
1339 position.top -= overBottom;
1340 // adjust based on position and margin
1342 position.top = max( position.top - collisionPosTop, position.top );
1347 left: function( position, data ) {
1348 var within = data.within,
1349 withinOffset = within.offset.left + within.scrollLeft,
1350 outerWidth = within.width,
1351 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1352 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1353 overLeft = collisionPosLeft - offsetLeft,
1354 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1355 myOffset = data.my[ 0 ] === "left" ?
1357 data.my[ 0 ] === "right" ?
1360 atOffset = data.at[ 0 ] === "left" ?
1362 data.at[ 0 ] === "right" ?
1365 offset = -2 * data.offset[ 0 ],
1369 if ( overLeft < 0 ) {
1370 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1371 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1372 position.left += myOffset + atOffset + offset;
1375 else if ( overRight > 0 ) {
1376 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1377 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1378 position.left += myOffset + atOffset + offset;
1382 top: function( position, data ) {
1383 var within = data.within,
1384 withinOffset = within.offset.top + within.scrollTop,
1385 outerHeight = within.height,
1386 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1387 collisionPosTop = position.top - data.collisionPosition.marginTop,
1388 overTop = collisionPosTop - offsetTop,
1389 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1390 top = data.my[ 1 ] === "top",
1393 data.my[ 1 ] === "bottom" ?
1396 atOffset = data.at[ 1 ] === "top" ?
1398 data.at[ 1 ] === "bottom" ?
1399 -data.targetHeight :
1401 offset = -2 * data.offset[ 1 ],
1404 if ( overTop < 0 ) {
1405 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1406 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1407 position.top += myOffset + atOffset + offset;
1410 else if ( overBottom > 0 ) {
1411 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1412 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1413 position.top += myOffset + atOffset + offset;
1420 $.ui.position.flip.left.apply( this, arguments );
1421 $.ui.position.fit.left.apply( this, arguments );
1424 $.ui.position.flip.top.apply( this, arguments );
1425 $.ui.position.fit.top.apply( this, arguments );
1430 // fraction support test
1432 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1433 body = document.getElementsByTagName( "body" )[ 0 ],
1434 div = document.createElement( "div" );
1436 //Create a "fake body" for testing based on method used in jQuery.support
1437 testElement = document.createElement( body ? "div" : "body" );
1438 testElementStyle = {
1439 visibility: "hidden",
1447 $.extend( testElementStyle, {
1448 position: "absolute",
1453 for ( i in testElementStyle ) {
1454 testElement.style[ i ] = testElementStyle[ i ];
1456 testElement.appendChild( div );
1457 testElementParent = body || document.documentElement;
1458 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1460 div.style.cssText = "position: absolute; left: 10.7432222px;";
1462 offsetLeft = $( div ).offset().left;
1463 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1465 testElement.innerHTML = "";
1466 testElementParent.removeChild( testElement );
1470 (function( $, undefined ) {
1472 $.widget("ui.draggable", $.ui.mouse, {
1474 widgetEventPrefix: "drag",
1479 connectToSortable: false,
1488 refreshPositions: false,
1490 revertDuration: 500,
1493 scrollSensitivity: 20,
1506 _create: function() {
1508 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1509 this.element[0].style.position = "relative";
1511 if (this.options.addClasses){
1512 this.element.addClass("ui-draggable");
1514 if (this.options.disabled){
1515 this.element.addClass("ui-draggable-disabled");
1522 _destroy: function() {
1523 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1524 this._mouseDestroy();
1527 _mouseCapture: function(event) {
1529 var o = this.options;
1531 // among others, prevent a drag on a resizable-handle
1532 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1536 //Quit if we're not on a valid handle
1537 this.handle = this._getHandle(event);
1542 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1543 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1545 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1546 position: "absolute", opacity: "0.001", zIndex: 1000
1548 .css($(this).offset())
1556 _mouseStart: function(event) {
1558 var o = this.options;
1560 //Create and append the visible helper
1561 this.helper = this._createHelper(event);
1563 this.helper.addClass("ui-draggable-dragging");
1565 //Cache the helper size
1566 this._cacheHelperProportions();
1568 //If ddmanager is used for droppables, set the global draggable
1569 if($.ui.ddmanager) {
1570 $.ui.ddmanager.current = this;
1574 * - Position generation -
1575 * This block generates everything position related - it's the core of draggables.
1578 //Cache the margins of the original element
1579 this._cacheMargins();
1581 //Store the helper's css position
1582 this.cssPosition = this.helper.css( "position" );
1583 this.scrollParent = this.helper.scrollParent();
1584 this.offsetParent = this.helper.offsetParent();
1585 this.offsetParentCssPosition = this.offsetParent.css( "position" );
1587 //The element's absolute position on the page minus margins
1588 this.offset = this.positionAbs = this.element.offset();
1590 top: this.offset.top - this.margins.top,
1591 left: this.offset.left - this.margins.left
1594 //Reset scroll cache
1595 this.offset.scroll = false;
1597 $.extend(this.offset, {
1598 click: { //Where the click happened, relative to the element
1599 left: event.pageX - this.offset.left,
1600 top: event.pageY - this.offset.top
1602 parent: this._getParentOffset(),
1603 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1606 //Generate the original position
1607 this.originalPosition = this.position = this._generatePosition(event);
1608 this.originalPageX = event.pageX;
1609 this.originalPageY = event.pageY;
1611 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1612 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1614 //Set a containment if given in the options
1615 this._setContainment();
1617 //Trigger event + callbacks
1618 if(this._trigger("start", event) === false) {
1623 //Recache the helper size
1624 this._cacheHelperProportions();
1626 //Prepare the droppable offsets
1627 if ($.ui.ddmanager && !o.dropBehaviour) {
1628 $.ui.ddmanager.prepareOffsets(this, event);
1632 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1634 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1635 if ( $.ui.ddmanager ) {
1636 $.ui.ddmanager.dragStart(this, event);
1642 _mouseDrag: function(event, noPropagation) {
1643 // reset any necessary cached properties (see #5009)
1644 if ( this.offsetParentCssPosition === "fixed" ) {
1645 this.offset.parent = this._getParentOffset();
1648 //Compute the helpers position
1649 this.position = this._generatePosition(event);
1650 this.positionAbs = this._convertPositionTo("absolute");
1652 //Call plugins and callbacks and use the resulting position if something is returned
1653 if (!noPropagation) {
1654 var ui = this._uiHash();
1655 if(this._trigger("drag", event, ui) === false) {
1659 this.position = ui.position;
1662 if(!this.options.axis || this.options.axis !== "y") {
1663 this.helper[0].style.left = this.position.left+"px";
1665 if(!this.options.axis || this.options.axis !== "x") {
1666 this.helper[0].style.top = this.position.top+"px";
1668 if($.ui.ddmanager) {
1669 $.ui.ddmanager.drag(this, event);
1675 _mouseStop: function(event) {
1677 //If we are using droppables, inform the manager about the drop
1680 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1681 dropped = $.ui.ddmanager.drop(this, event);
1684 //if a drop comes from outside (a sortable)
1686 dropped = this.dropped;
1687 this.dropped = false;
1690 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1691 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
1695 if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1696 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1697 if(that._trigger("stop", event) !== false) {
1702 if(this._trigger("stop", event) !== false) {
1710 _mouseUp: function(event) {
1711 //Remove frame helpers
1712 $("div.ui-draggable-iframeFix").each(function() {
1713 this.parentNode.removeChild(this);
1716 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1717 if( $.ui.ddmanager ) {
1718 $.ui.ddmanager.dragStop(this, event);
1721 return $.ui.mouse.prototype._mouseUp.call(this, event);
1724 cancel: function() {
1726 if(this.helper.is(".ui-draggable-dragging")) {
1736 _getHandle: function(event) {
1737 return this.options.handle ?
1738 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1742 _createHelper: function(event) {
1744 var o = this.options,
1745 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1747 if(!helper.parents("body").length) {
1748 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1751 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1752 helper.css("position", "absolute");
1759 _adjustOffsetFromHelper: function(obj) {
1760 if (typeof obj === "string") {
1761 obj = obj.split(" ");
1763 if ($.isArray(obj)) {
1764 obj = {left: +obj[0], top: +obj[1] || 0};
1766 if ("left" in obj) {
1767 this.offset.click.left = obj.left + this.margins.left;
1769 if ("right" in obj) {
1770 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1773 this.offset.click.top = obj.top + this.margins.top;
1775 if ("bottom" in obj) {
1776 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1780 _getParentOffset: function() {
1782 //Get the offsetParent and cache its position
1783 var po = this.offsetParent.offset();
1785 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1786 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1787 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1788 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1789 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1790 po.left += this.scrollParent.scrollLeft();
1791 po.top += this.scrollParent.scrollTop();
1794 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1796 if((this.offsetParent[0] === document.body) ||
1797 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1798 po = { top: 0, left: 0 };
1802 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1803 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1808 _getRelativeOffset: function() {
1810 if(this.cssPosition === "relative") {
1811 var p = this.element.position();
1813 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1814 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1817 return { top: 0, left: 0 };
1822 _cacheMargins: function() {
1824 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1825 top: (parseInt(this.element.css("marginTop"),10) || 0),
1826 right: (parseInt(this.element.css("marginRight"),10) || 0),
1827 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1831 _cacheHelperProportions: function() {
1832 this.helperProportions = {
1833 width: this.helper.outerWidth(),
1834 height: this.helper.outerHeight()
1838 _setContainment: function() {
1843 if ( !o.containment ) {
1844 this.containment = null;
1848 if ( o.containment === "window" ) {
1849 this.containment = [
1850 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1851 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1852 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
1853 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1858 if ( o.containment === "document") {
1859 this.containment = [
1862 $( document ).width() - this.helperProportions.width - this.margins.left,
1863 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1868 if ( o.containment.constructor === Array ) {
1869 this.containment = o.containment;
1873 if ( o.containment === "parent" ) {
1874 o.containment = this.helper[ 0 ].parentNode;
1877 c = $( o.containment );
1884 over = c.css( "overflow" ) !== "hidden";
1886 this.containment = [
1887 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
1888 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
1889 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
1890 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
1892 this.relative_container = c;
1895 _convertPositionTo: function(d, pos) {
1898 pos = this.position;
1901 var mod = d === "absolute" ? 1 : -1,
1902 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
1905 if (!this.offset.scroll) {
1906 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1911 pos.top + // The absolute mouse position
1912 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1913 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1914 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
1917 pos.left + // The absolute mouse position
1918 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1919 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1920 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
1926 _generatePosition: function(event) {
1928 var containment, co, top, left,
1930 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
1931 pageX = event.pageX,
1932 pageY = event.pageY;
1935 if (!this.offset.scroll) {
1936 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1940 * - Position constraining -
1941 * Constrain the position to a mix of grid, containment.
1944 // If we are not dragging yet, we won't check for options
1945 if ( this.originalPosition ) {
1946 if ( this.containment ) {
1947 if ( this.relative_container ){
1948 co = this.relative_container.offset();
1950 this.containment[ 0 ] + co.left,
1951 this.containment[ 1 ] + co.top,
1952 this.containment[ 2 ] + co.left,
1953 this.containment[ 3 ] + co.top
1957 containment = this.containment;
1960 if(event.pageX - this.offset.click.left < containment[0]) {
1961 pageX = containment[0] + this.offset.click.left;
1963 if(event.pageY - this.offset.click.top < containment[1]) {
1964 pageY = containment[1] + this.offset.click.top;
1966 if(event.pageX - this.offset.click.left > containment[2]) {
1967 pageX = containment[2] + this.offset.click.left;
1969 if(event.pageY - this.offset.click.top > containment[3]) {
1970 pageY = containment[3] + this.offset.click.top;
1975 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1976 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1977 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1979 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1980 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1987 pageY - // The absolute mouse position
1988 this.offset.click.top - // Click offset (relative to the element)
1989 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1990 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1991 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
1994 pageX - // The absolute mouse position
1995 this.offset.click.left - // Click offset (relative to the element)
1996 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1997 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1998 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
2004 _clear: function() {
2005 this.helper.removeClass("ui-draggable-dragging");
2006 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
2007 this.helper.remove();
2010 this.cancelHelperRemoval = false;
2013 // From now on bulk stuff - mainly helpers
2015 _trigger: function(type, event, ui) {
2016 ui = ui || this._uiHash();
2017 $.ui.plugin.call(this, type, [event, ui]);
2018 //The absolute position has to be recalculated after plugins
2019 if(type === "drag") {
2020 this.positionAbs = this._convertPositionTo("absolute");
2022 return $.Widget.prototype._trigger.call(this, type, event, ui);
2027 _uiHash: function() {
2029 helper: this.helper,
2030 position: this.position,
2031 originalPosition: this.originalPosition,
2032 offset: this.positionAbs
2038 $.ui.plugin.add("draggable", "connectToSortable", {
2039 start: function(event, ui) {
2041 var inst = $(this).data("ui-draggable"), o = inst.options,
2042 uiSortable = $.extend({}, ui, { item: inst.element });
2043 inst.sortables = [];
2044 $(o.connectToSortable).each(function() {
2045 var sortable = $.data(this, "ui-sortable");
2046 if (sortable && !sortable.options.disabled) {
2047 inst.sortables.push({
2049 shouldRevert: sortable.options.revert
2051 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
2052 sortable._trigger("activate", event, uiSortable);
2057 stop: function(event, ui) {
2059 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
2060 var inst = $(this).data("ui-draggable"),
2061 uiSortable = $.extend({}, ui, { item: inst.element });
2063 $.each(inst.sortables, function() {
2064 if(this.instance.isOver) {
2066 this.instance.isOver = 0;
2068 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
2069 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
2071 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
2072 if(this.shouldRevert) {
2073 this.instance.options.revert = this.shouldRevert;
2076 //Trigger the stop of the sortable
2077 this.instance._mouseStop(event);
2079 this.instance.options.helper = this.instance.options._helper;
2081 //If the helper has been the original item, restore properties in the sortable
2082 if(inst.options.helper === "original") {
2083 this.instance.currentItem.css({ top: "auto", left: "auto" });
2087 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
2088 this.instance._trigger("deactivate", event, uiSortable);
2094 drag: function(event, ui) {
2096 var inst = $(this).data("ui-draggable"), that = this;
2098 $.each(inst.sortables, function() {
2100 var innermostIntersecting = false,
2101 thisSortable = this;
2103 //Copy over some variables to allow calling the sortable's native _intersectsWith
2104 this.instance.positionAbs = inst.positionAbs;
2105 this.instance.helperProportions = inst.helperProportions;
2106 this.instance.offset.click = inst.offset.click;
2108 if(this.instance._intersectsWith(this.instance.containerCache)) {
2109 innermostIntersecting = true;
2110 $.each(inst.sortables, function () {
2111 this.instance.positionAbs = inst.positionAbs;
2112 this.instance.helperProportions = inst.helperProportions;
2113 this.instance.offset.click = inst.offset.click;
2114 if (this !== thisSortable &&
2115 this.instance._intersectsWith(this.instance.containerCache) &&
2116 $.contains(thisSortable.instance.element[0], this.instance.element[0])
2118 innermostIntersecting = false;
2120 return innermostIntersecting;
2125 if(innermostIntersecting) {
2126 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
2127 if(!this.instance.isOver) {
2129 this.instance.isOver = 1;
2130 //Now we fake the start of dragging for the sortable instance,
2131 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
2132 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
2133 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
2134 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
2135 this.instance.options.helper = function() { return ui.helper[0]; };
2137 event.target = this.instance.currentItem[0];
2138 this.instance._mouseCapture(event, true);
2139 this.instance._mouseStart(event, true, true);
2141 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
2142 this.instance.offset.click.top = inst.offset.click.top;
2143 this.instance.offset.click.left = inst.offset.click.left;
2144 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
2145 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
2147 inst._trigger("toSortable", event);
2148 inst.dropped = this.instance.element; //draggable revert needs that
2149 //hack so receive/update callbacks work (mostly)
2150 inst.currentItem = inst.element;
2151 this.instance.fromOutside = inst;
2155 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
2156 if(this.instance.currentItem) {
2157 this.instance._mouseDrag(event);
2162 //If it doesn't intersect with the sortable, and it intersected before,
2163 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
2164 if(this.instance.isOver) {
2166 this.instance.isOver = 0;
2167 this.instance.cancelHelperRemoval = true;
2169 //Prevent reverting on this forced stop
2170 this.instance.options.revert = false;
2172 // The out event needs to be triggered independently
2173 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
2175 this.instance._mouseStop(event, true);
2176 this.instance.options.helper = this.instance.options._helper;
2178 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
2179 this.instance.currentItem.remove();
2180 if(this.instance.placeholder) {
2181 this.instance.placeholder.remove();
2184 inst._trigger("fromSortable", event);
2185 inst.dropped = false; //draggable revert needs that
2195 $.ui.plugin.add("draggable", "cursor", {
2197 var t = $("body"), o = $(this).data("ui-draggable").options;
2198 if (t.css("cursor")) {
2199 o._cursor = t.css("cursor");
2201 t.css("cursor", o.cursor);
2204 var o = $(this).data("ui-draggable").options;
2206 $("body").css("cursor", o._cursor);
2211 $.ui.plugin.add("draggable", "opacity", {
2212 start: function(event, ui) {
2213 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
2214 if(t.css("opacity")) {
2215 o._opacity = t.css("opacity");
2217 t.css("opacity", o.opacity);
2219 stop: function(event, ui) {
2220 var o = $(this).data("ui-draggable").options;
2222 $(ui.helper).css("opacity", o._opacity);
2227 $.ui.plugin.add("draggable", "scroll", {
2229 var i = $(this).data("ui-draggable");
2230 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
2231 i.overflowOffset = i.scrollParent.offset();
2234 drag: function( event ) {
2236 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
2238 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
2240 if(!o.axis || o.axis !== "x") {
2241 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
2242 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
2243 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
2244 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
2248 if(!o.axis || o.axis !== "y") {
2249 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
2250 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
2251 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
2252 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
2258 if(!o.axis || o.axis !== "x") {
2259 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
2260 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2261 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
2262 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2266 if(!o.axis || o.axis !== "y") {
2267 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
2268 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2269 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
2270 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2276 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
2277 $.ui.ddmanager.prepareOffsets(i, event);
2283 $.ui.plugin.add("draggable", "snap", {
2286 var i = $(this).data("ui-draggable"),
2289 i.snapElements = [];
2291 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
2294 if(this !== i.element[0]) {
2295 i.snapElements.push({
2297 width: $t.outerWidth(), height: $t.outerHeight(),
2298 top: $o.top, left: $o.left
2304 drag: function(event, ui) {
2306 var ts, bs, ls, rs, l, r, t, b, i, first,
2307 inst = $(this).data("ui-draggable"),
2309 d = o.snapTolerance,
2310 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2311 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2313 for (i = inst.snapElements.length - 1; i >= 0; i--){
2315 l = inst.snapElements[i].left;
2316 r = l + inst.snapElements[i].width;
2317 t = inst.snapElements[i].top;
2318 b = t + inst.snapElements[i].height;
2320 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
2321 if(inst.snapElements[i].snapping) {
2322 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2324 inst.snapElements[i].snapping = false;
2328 if(o.snapMode !== "inner") {
2329 ts = Math.abs(t - y2) <= d;
2330 bs = Math.abs(b - y1) <= d;
2331 ls = Math.abs(l - x2) <= d;
2332 rs = Math.abs(r - x1) <= d;
2334 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2337 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
2340 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
2343 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
2347 first = (ts || bs || ls || rs);
2349 if(o.snapMode !== "outer") {
2350 ts = Math.abs(t - y1) <= d;
2351 bs = Math.abs(b - y2) <= d;
2352 ls = Math.abs(l - x1) <= d;
2353 rs = Math.abs(r - x2) <= d;
2355 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
2358 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2361 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
2364 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
2368 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
2369 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2371 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2378 $.ui.plugin.add("draggable", "stack", {
2381 o = this.data("ui-draggable").options,
2382 group = $.makeArray($(o.stack)).sort(function(a,b) {
2383 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
2386 if (!group.length) { return; }
2388 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
2389 $(group).each(function(i) {
2390 $(this).css("zIndex", min + i);
2392 this.css("zIndex", (min + group.length));
2396 $.ui.plugin.add("draggable", "zIndex", {
2397 start: function(event, ui) {
2398 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
2399 if(t.css("zIndex")) {
2400 o._zIndex = t.css("zIndex");
2402 t.css("zIndex", o.zIndex);
2404 stop: function(event, ui) {
2405 var o = $(this).data("ui-draggable").options;
2407 $(ui.helper).css("zIndex", o._zIndex);
2413 (function( $, undefined ) {
2415 function isOverAxis( x, reference, size ) {
2416 return ( x > reference ) && ( x < ( reference + size ) );
2419 $.widget("ui.droppable", {
2421 widgetEventPrefix: "drop",
2429 tolerance: "intersect",
2438 _create: function() {
2440 var o = this.options,
2443 this.isover = false;
2446 this.accept = $.isFunction(accept) ? accept : function(d) {
2447 return d.is(accept);
2450 //Store the droppable's proportions
2451 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
2453 // Add the reference and positions to the manager
2454 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
2455 $.ui.ddmanager.droppables[o.scope].push(this);
2457 (o.addClasses && this.element.addClass("ui-droppable"));
2461 _destroy: function() {
2463 drop = $.ui.ddmanager.droppables[this.options.scope];
2465 for ( ; i < drop.length; i++ ) {
2466 if ( drop[i] === this ) {
2471 this.element.removeClass("ui-droppable ui-droppable-disabled");
2474 _setOption: function(key, value) {
2476 if(key === "accept") {
2477 this.accept = $.isFunction(value) ? value : function(d) {
2481 $.Widget.prototype._setOption.apply(this, arguments);
2484 _activate: function(event) {
2485 var draggable = $.ui.ddmanager.current;
2486 if(this.options.activeClass) {
2487 this.element.addClass(this.options.activeClass);
2490 this._trigger("activate", event, this.ui(draggable));
2494 _deactivate: function(event) {
2495 var draggable = $.ui.ddmanager.current;
2496 if(this.options.activeClass) {
2497 this.element.removeClass(this.options.activeClass);
2500 this._trigger("deactivate", event, this.ui(draggable));
2504 _over: function(event) {
2506 var draggable = $.ui.ddmanager.current;
2508 // Bail if draggable and droppable are same element
2509 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2513 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2514 if(this.options.hoverClass) {
2515 this.element.addClass(this.options.hoverClass);
2517 this._trigger("over", event, this.ui(draggable));
2522 _out: function(event) {
2524 var draggable = $.ui.ddmanager.current;
2526 // Bail if draggable and droppable are same element
2527 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2531 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2532 if(this.options.hoverClass) {
2533 this.element.removeClass(this.options.hoverClass);
2535 this._trigger("out", event, this.ui(draggable));
2540 _drop: function(event,custom) {
2542 var draggable = custom || $.ui.ddmanager.current,
2543 childrenIntersection = false;
2545 // Bail if draggable and droppable are same element
2546 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2550 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2551 var inst = $.data(this, "ui-droppable");
2553 inst.options.greedy &&
2554 !inst.options.disabled &&
2555 inst.options.scope === draggable.options.scope &&
2556 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2557 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2558 ) { childrenIntersection = true; return false; }
2560 if(childrenIntersection) {
2564 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2565 if(this.options.activeClass) {
2566 this.element.removeClass(this.options.activeClass);
2568 if(this.options.hoverClass) {
2569 this.element.removeClass(this.options.hoverClass);
2571 this._trigger("drop", event, this.ui(draggable));
2572 return this.element;
2581 draggable: (c.currentItem || c.element),
2583 position: c.position,
2584 offset: c.positionAbs
2590 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2592 if (!droppable.offset) {
2596 var draggableLeft, draggableTop,
2597 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2598 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2599 l = droppable.offset.left, r = l + droppable.proportions.width,
2600 t = droppable.offset.top, b = t + droppable.proportions.height;
2602 switch (toleranceMode) {
2604 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2606 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2607 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2608 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2609 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2611 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2612 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2613 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2616 (y1 >= t && y1 <= b) || // Top edge touching
2617 (y2 >= t && y2 <= b) || // Bottom edge touching
2618 (y1 < t && y2 > b) // Surrounded vertically
2620 (x1 >= l && x1 <= r) || // Left edge touching
2621 (x2 >= l && x2 <= r) || // Right edge touching
2622 (x1 < l && x2 > r) // Surrounded horizontally
2631 This manager tracks offsets of draggables and droppables
2635 droppables: { "default": [] },
2636 prepareOffsets: function(t, event) {
2639 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2640 type = event ? event.type : null, // workaround for #2317
2641 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2643 droppablesLoop: for (i = 0; i < m.length; i++) {
2645 //No disabled and non-accepted
2646 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2650 // Filter out elements in the current dragged item
2651 for (j=0; j < list.length; j++) {
2652 if(list[j] === m[i].element[0]) {
2653 m[i].proportions.height = 0;
2654 continue droppablesLoop;
2658 m[i].visible = m[i].element.css("display") !== "none";
2663 //Activate the droppable if used directly from draggables
2664 if(type === "mousedown") {
2665 m[i]._activate.call(m[i], event);
2668 m[i].offset = m[i].element.offset();
2669 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2674 drop: function(draggable, event) {
2676 var dropped = false;
2677 // Create a copy of the droppables in case the list changes during the drop (#9116)
2678 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2683 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2684 dropped = this._drop.call(this, event) || dropped;
2687 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2689 this.isover = false;
2690 this._deactivate.call(this, event);
2697 dragStart: function( draggable, event ) {
2698 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2699 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2700 if( !draggable.options.refreshPositions ) {
2701 $.ui.ddmanager.prepareOffsets( draggable, event );
2705 drag: function(draggable, event) {
2707 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2708 if(draggable.options.refreshPositions) {
2709 $.ui.ddmanager.prepareOffsets(draggable, event);
2712 //Run through all droppables and check their positions based on specific tolerance options
2713 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2715 if(this.options.disabled || this.greedyChild || !this.visible) {
2719 var parentInstance, scope, parent,
2720 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2721 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2726 if (this.options.greedy) {
2727 // find droppable parents with same scope
2728 scope = this.options.scope;
2729 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2730 return $.data(this, "ui-droppable").options.scope === scope;
2733 if (parent.length) {
2734 parentInstance = $.data(parent[0], "ui-droppable");
2735 parentInstance.greedyChild = (c === "isover");
2739 // we just moved into a greedy child
2740 if (parentInstance && c === "isover") {
2741 parentInstance.isover = false;
2742 parentInstance.isout = true;
2743 parentInstance._out.call(parentInstance, event);
2747 this[c === "isout" ? "isover" : "isout"] = false;
2748 this[c === "isover" ? "_over" : "_out"].call(this, event);
2750 // we just moved out of a greedy child
2751 if (parentInstance && c === "isout") {
2752 parentInstance.isout = false;
2753 parentInstance.isover = true;
2754 parentInstance._over.call(parentInstance, event);
2759 dragStop: function( draggable, event ) {
2760 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2761 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2762 if( !draggable.options.refreshPositions ) {
2763 $.ui.ddmanager.prepareOffsets( draggable, event );
2769 (function( $, undefined ) {
2772 return parseInt(v, 10) || 0;
2775 function isNumber(value) {
2776 return !isNaN(parseInt(value, 10));
2779 $.widget("ui.resizable", $.ui.mouse, {
2781 widgetEventPrefix: "resize",
2785 animateDuration: "slow",
2786 animateEasing: "swing",
2806 _create: function() {
2808 var n, i, handle, axis, hname,
2811 this.element.addClass("ui-resizable");
2814 _aspectRatio: !!(o.aspectRatio),
2815 aspectRatio: o.aspectRatio,
2816 originalElement: this.element,
2817 _proportionallyResizeElements: [],
2818 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2821 //Wrap the element if it cannot hold child nodes
2822 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2824 //Create a wrapper element and set the wrapper to the new current internal element
2826 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2827 position: this.element.css("position"),
2828 width: this.element.outerWidth(),
2829 height: this.element.outerHeight(),
2830 top: this.element.css("top"),
2831 left: this.element.css("left")
2835 //Overwrite the original this.element
2836 this.element = this.element.parent().data(
2837 "ui-resizable", this.element.data("ui-resizable")
2840 this.elementIsWrapper = true;
2842 //Move margins to the wrapper
2843 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2844 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2846 //Prevent Safari textarea resize
2847 this.originalResizeStyle = this.originalElement.css("resize");
2848 this.originalElement.css("resize", "none");
2850 //Push the actual element to our proportionallyResize internal array
2851 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2853 // avoid IE jump (hard set the margin)
2854 this.originalElement.css({ margin: this.originalElement.css("margin") });
2856 // fix handlers offset
2857 this._proportionallyResize();
2861 this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
2862 if(this.handles.constructor === String) {
2864 if ( this.handles === "all") {
2865 this.handles = "n,e,s,w,se,sw,ne,nw";
2868 n = this.handles.split(",");
2871 for(i = 0; i < n.length; i++) {
2873 handle = $.trim(n[i]);
2874 hname = "ui-resizable-"+handle;
2875 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2877 // Apply zIndex to all handles - see #7960
2878 axis.css({ zIndex: o.zIndex });
2880 //TODO : What's going on here?
2881 if ("se" === handle) {
2882 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2885 //Insert into internal handles object and append to element
2886 this.handles[handle] = ".ui-resizable-"+handle;
2887 this.element.append(axis);
2892 this._renderAxis = function(target) {
2894 var i, axis, padPos, padWrapper;
2896 target = target || this.element;
2898 for(i in this.handles) {
2900 if(this.handles[i].constructor === String) {
2901 this.handles[i] = $(this.handles[i], this.element).show();
2904 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2905 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2907 axis = $(this.handles[i], this.element);
2909 //Checking the correct pad and border
2910 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2912 //The padding type i have to apply...
2913 padPos = [ "padding",
2914 /ne|nw|n/.test(i) ? "Top" :
2915 /se|sw|s/.test(i) ? "Bottom" :
2916 /^e$/.test(i) ? "Right" : "Left" ].join("");
2918 target.css(padPos, padWrapper);
2920 this._proportionallyResize();
2924 //TODO: What's that good for? There's not anything to be executed left
2925 if(!$(this.handles[i]).length) {
2931 //TODO: make renderAxis a prototype function
2932 this._renderAxis(this.element);
2934 this._handles = $(".ui-resizable-handle", this.element)
2935 .disableSelection();
2937 //Matching axis name
2938 this._handles.mouseover(function() {
2939 if (!that.resizing) {
2940 if (this.className) {
2941 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2943 //Axis, default = se
2944 that.axis = axis && axis[1] ? axis[1] : "se";
2948 //If we want to auto hide the elements
2950 this._handles.hide();
2952 .addClass("ui-resizable-autohide")
2953 .mouseenter(function() {
2957 $(this).removeClass("ui-resizable-autohide");
2958 that._handles.show();
2960 .mouseleave(function(){
2964 if (!that.resizing) {
2965 $(this).addClass("ui-resizable-autohide");
2966 that._handles.hide();
2971 //Initialize the mouse interaction
2976 _destroy: function() {
2978 this._mouseDestroy();
2981 _destroy = function(exp) {
2982 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2983 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2986 //TODO: Unwrap at same DOM position
2987 if (this.elementIsWrapper) {
2988 _destroy(this.element);
2989 wrapper = this.element;
2990 this.originalElement.css({
2991 position: wrapper.css("position"),
2992 width: wrapper.outerWidth(),
2993 height: wrapper.outerHeight(),
2994 top: wrapper.css("top"),
2995 left: wrapper.css("left")
2996 }).insertAfter( wrapper );
3000 this.originalElement.css("resize", this.originalResizeStyle);
3001 _destroy(this.originalElement);
3006 _mouseCapture: function(event) {
3010 for (i in this.handles) {
3011 handle = $(this.handles[i])[0];
3012 if (handle === event.target || $.contains(handle, event.target)) {
3017 return !this.options.disabled && capture;
3020 _mouseStart: function(event) {
3022 var curleft, curtop, cursor,
3024 iniPos = this.element.position(),
3027 this.resizing = true;
3029 // bugfix for http://dev.jquery.com/ticket/1749
3030 if ( (/absolute/).test( el.css("position") ) ) {
3031 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
3032 } else if (el.is(".ui-draggable")) {
3033 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
3036 this._renderProxy();
3038 curleft = num(this.helper.css("left"));
3039 curtop = num(this.helper.css("top"));
3041 if (o.containment) {
3042 curleft += $(o.containment).scrollLeft() || 0;
3043 curtop += $(o.containment).scrollTop() || 0;
3046 //Store needed variables
3047 this.offset = this.helper.offset();
3048 this.position = { left: curleft, top: curtop };
3049 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3050 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3051 this.originalPosition = { left: curleft, top: curtop };
3052 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
3053 this.originalMousePosition = { left: event.pageX, top: event.pageY };
3056 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
3058 cursor = $(".ui-resizable-" + this.axis).css("cursor");
3059 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
3061 el.addClass("ui-resizable-resizing");
3062 this._propagate("start", event);
3066 _mouseDrag: function(event) {
3068 //Increase performance, avoid regex
3070 el = this.helper, props = {},
3071 smp = this.originalMousePosition,
3073 prevTop = this.position.top,
3074 prevLeft = this.position.left,
3075 prevWidth = this.size.width,
3076 prevHeight = this.size.height,
3077 dx = (event.pageX-smp.left)||0,
3078 dy = (event.pageY-smp.top)||0,
3079 trigger = this._change[a];
3085 // Calculate the attrs that will be change
3086 data = trigger.apply(this, [event, dx, dy]);
3088 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
3089 this._updateVirtualBoundaries(event.shiftKey);
3090 if (this._aspectRatio || event.shiftKey) {
3091 data = this._updateRatio(data, event);
3094 data = this._respectSize(data, event);
3096 this._updateCache(data);
3098 // plugins callbacks need to be called first
3099 this._propagate("resize", event);
3101 if (this.position.top !== prevTop) {
3102 props.top = this.position.top + "px";
3104 if (this.position.left !== prevLeft) {
3105 props.left = this.position.left + "px";
3107 if (this.size.width !== prevWidth) {
3108 props.width = this.size.width + "px";
3110 if (this.size.height !== prevHeight) {
3111 props.height = this.size.height + "px";
3115 if (!this._helper && this._proportionallyResizeElements.length) {
3116 this._proportionallyResize();
3119 // Call the user callback if the element was resized
3120 if ( ! $.isEmptyObject(props) ) {
3121 this._trigger("resize", event, this.ui());
3127 _mouseStop: function(event) {
3129 this.resizing = false;
3130 var pr, ista, soffseth, soffsetw, s, left, top,
3131 o = this.options, that = this;
3135 pr = this._proportionallyResizeElements;
3136 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
3137 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
3138 soffsetw = ista ? 0 : that.sizeDiff.width;
3140 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
3141 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
3142 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
3145 this.element.css($.extend(s, { top: top, left: left }));
3148 that.helper.height(that.size.height);
3149 that.helper.width(that.size.width);
3151 if (this._helper && !o.animate) {
3152 this._proportionallyResize();
3156 $("body").css("cursor", "auto");
3158 this.element.removeClass("ui-resizable-resizing");
3160 this._propagate("stop", event);
3163 this.helper.remove();
3170 _updateVirtualBoundaries: function(forceAspectRatio) {
3171 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
3175 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
3176 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
3177 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
3178 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
3181 if(this._aspectRatio || forceAspectRatio) {
3182 // We want to create an enclosing box whose aspect ration is the requested one
3183 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
3184 pMinWidth = b.minHeight * this.aspectRatio;
3185 pMinHeight = b.minWidth / this.aspectRatio;
3186 pMaxWidth = b.maxHeight * this.aspectRatio;
3187 pMaxHeight = b.maxWidth / this.aspectRatio;
3189 if(pMinWidth > b.minWidth) {
3190 b.minWidth = pMinWidth;
3192 if(pMinHeight > b.minHeight) {
3193 b.minHeight = pMinHeight;
3195 if(pMaxWidth < b.maxWidth) {
3196 b.maxWidth = pMaxWidth;
3198 if(pMaxHeight < b.maxHeight) {
3199 b.maxHeight = pMaxHeight;
3202 this._vBoundaries = b;
3205 _updateCache: function(data) {
3206 this.offset = this.helper.offset();
3207 if (isNumber(data.left)) {
3208 this.position.left = data.left;
3210 if (isNumber(data.top)) {
3211 this.position.top = data.top;
3213 if (isNumber(data.height)) {
3214 this.size.height = data.height;
3216 if (isNumber(data.width)) {
3217 this.size.width = data.width;
3221 _updateRatio: function( data ) {
3223 var cpos = this.position,
3227 if (isNumber(data.height)) {
3228 data.width = (data.height * this.aspectRatio);
3229 } else if (isNumber(data.width)) {
3230 data.height = (data.width / this.aspectRatio);
3234 data.left = cpos.left + (csize.width - data.width);
3238 data.top = cpos.top + (csize.height - data.height);
3239 data.left = cpos.left + (csize.width - data.width);
3245 _respectSize: function( data ) {
3247 var o = this._vBoundaries,
3249 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
3250 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
3251 dw = this.originalPosition.left + this.originalSize.width,
3252 dh = this.position.top + this.size.height,
3253 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
3255 data.width = o.minWidth;
3258 data.height = o.minHeight;
3261 data.width = o.maxWidth;
3264 data.height = o.maxHeight;
3268 data.left = dw - o.minWidth;
3271 data.left = dw - o.maxWidth;
3274 data.top = dh - o.minHeight;
3277 data.top = dh - o.maxHeight;
3280 // fixing jump error on top/left - bug #2330
3281 if (!data.width && !data.height && !data.left && data.top) {
3283 } else if (!data.width && !data.height && !data.top && data.left) {
3290 _proportionallyResize: function() {
3292 if (!this._proportionallyResizeElements.length) {
3296 var i, j, borders, paddings, prel,
3297 element = this.helper || this.element;
3299 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
3301 prel = this._proportionallyResizeElements[i];
3303 if (!this.borderDif) {
3304 this.borderDif = [];
3305 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
3306 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
3308 for ( j = 0; j < borders.length; j++ ) {
3309 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
3314 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
3315 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
3322 _renderProxy: function() {
3324 var el = this.element, o = this.options;
3325 this.elementOffset = el.offset();
3329 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
3331 this.helper.addClass(this._helper).css({
3332 width: this.element.outerWidth() - 1,
3333 height: this.element.outerHeight() - 1,
3334 position: "absolute",
3335 left: this.elementOffset.left +"px",
3336 top: this.elementOffset.top +"px",
3337 zIndex: ++o.zIndex //TODO: Don't modify option
3342 .disableSelection();
3345 this.helper = this.element;
3351 e: function(event, dx) {
3352 return { width: this.originalSize.width + dx };
3354 w: function(event, dx) {
3355 var cs = this.originalSize, sp = this.originalPosition;
3356 return { left: sp.left + dx, width: cs.width - dx };
3358 n: function(event, dx, dy) {
3359 var cs = this.originalSize, sp = this.originalPosition;
3360 return { top: sp.top + dy, height: cs.height - dy };
3362 s: function(event, dx, dy) {
3363 return { height: this.originalSize.height + dy };
3365 se: function(event, dx, dy) {
3366 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3368 sw: function(event, dx, dy) {
3369 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3371 ne: function(event, dx, dy) {
3372 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3374 nw: function(event, dx, dy) {
3375 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3379 _propagate: function(n, event) {
3380 $.ui.plugin.call(this, n, [event, this.ui()]);
3381 (n !== "resize" && this._trigger(n, event, this.ui()));
3388 originalElement: this.originalElement,
3389 element: this.element,
3390 helper: this.helper,
3391 position: this.position,
3393 originalSize: this.originalSize,
3394 originalPosition: this.originalPosition
3401 * Resizable Extensions
3404 $.ui.plugin.add("resizable", "animate", {
3406 stop: function( event ) {
3407 var that = $(this).data("ui-resizable"),
3409 pr = that._proportionallyResizeElements,
3410 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3411 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
3412 soffsetw = ista ? 0 : that.sizeDiff.width,
3413 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
3414 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
3415 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
3417 that.element.animate(
3418 $.extend(style, top && left ? { top: top, left: left } : {}), {
3419 duration: o.animateDuration,
3420 easing: o.animateEasing,
3424 width: parseInt(that.element.css("width"), 10),
3425 height: parseInt(that.element.css("height"), 10),
3426 top: parseInt(that.element.css("top"), 10),
3427 left: parseInt(that.element.css("left"), 10)
3430 if (pr && pr.length) {
3431 $(pr[0]).css({ width: data.width, height: data.height });
3434 // propagating resize, and updating values for each animation step
3435 that._updateCache(data);
3436 that._propagate("resize", event);
3445 $.ui.plugin.add("resizable", "containment", {
3448 var element, p, co, ch, cw, width, height,
3449 that = $(this).data("ui-resizable"),
3453 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
3459 that.containerElement = $(ce);
3461 if (/document/.test(oc) || oc === document) {
3462 that.containerOffset = { left: 0, top: 0 };
3463 that.containerPosition = { left: 0, top: 0 };
3466 element: $(document), left: 0, top: 0,
3467 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
3471 // i'm a node, so compute top, left, right, bottom
3475 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
3477 that.containerOffset = element.offset();
3478 that.containerPosition = element.position();
3479 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
3481 co = that.containerOffset;
3482 ch = that.containerSize.height;
3483 cw = that.containerSize.width;
3484 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
3485 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3488 element: ce, left: co.left, top: co.top, width: width, height: height
3493 resize: function( event ) {
3494 var woset, hoset, isParent, isOffsetRelative,
3495 that = $(this).data("ui-resizable"),
3497 co = that.containerOffset, cp = that.position,
3498 pRatio = that._aspectRatio || event.shiftKey,
3499 cop = { top:0, left:0 }, ce = that.containerElement;
3501 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
3505 if (cp.left < (that._helper ? co.left : 0)) {
3506 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3508 that.size.height = that.size.width / that.aspectRatio;
3510 that.position.left = o.helper ? co.left : 0;
3513 if (cp.top < (that._helper ? co.top : 0)) {
3514 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3516 that.size.width = that.size.height * that.aspectRatio;
3518 that.position.top = that._helper ? co.top : 0;
3521 that.offset.left = that.parentData.left+that.position.left;
3522 that.offset.top = that.parentData.top+that.position.top;
3524 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3525 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3527 isParent = that.containerElement.get(0) === that.element.parent().get(0);
3528 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3530 if(isParent && isOffsetRelative) {
3531 woset -= that.parentData.left;
3534 if (woset + that.size.width >= that.parentData.width) {
3535 that.size.width = that.parentData.width - woset;
3537 that.size.height = that.size.width / that.aspectRatio;
3541 if (hoset + that.size.height >= that.parentData.height) {
3542 that.size.height = that.parentData.height - hoset;
3544 that.size.width = that.size.height * that.aspectRatio;
3550 var that = $(this).data("ui-resizable"),
3552 co = that.containerOffset,
3553 cop = that.containerPosition,
3554 ce = that.containerElement,
3555 helper = $(that.helper),
3556 ho = helper.offset(),
3557 w = helper.outerWidth() - that.sizeDiff.width,
3558 h = helper.outerHeight() - that.sizeDiff.height;
3560 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3561 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3564 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3565 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3571 $.ui.plugin.add("resizable", "alsoResize", {
3573 start: function () {
3574 var that = $(this).data("ui-resizable"),
3576 _store = function (exp) {
3577 $(exp).each(function() {
3579 el.data("ui-resizable-alsoresize", {
3580 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3581 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3586 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3587 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3588 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3590 _store(o.alsoResize);
3594 resize: function (event, ui) {
3595 var that = $(this).data("ui-resizable"),
3597 os = that.originalSize,
3598 op = that.originalPosition,
3600 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3601 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3604 _alsoResize = function (exp, c) {
3605 $(exp).each(function() {
3606 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3607 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3609 $.each(css, function (i, prop) {
3610 var sum = (start[prop]||0) + (delta[prop]||0);
3611 if (sum && sum >= 0) {
3612 style[prop] = sum || null;
3620 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3621 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3623 _alsoResize(o.alsoResize);
3628 $(this).removeData("resizable-alsoresize");
3632 $.ui.plugin.add("resizable", "ghost", {
3636 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3638 that.ghost = that.originalElement.clone();
3640 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3641 .addClass("ui-resizable-ghost")
3642 .addClass(typeof o.ghost === "string" ? o.ghost : "");
3644 that.ghost.appendTo(that.helper);
3649 var that = $(this).data("ui-resizable");
3651 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3656 var that = $(this).data("ui-resizable");
3657 if (that.ghost && that.helper) {
3658 that.helper.get(0).removeChild(that.ghost.get(0));
3664 $.ui.plugin.add("resizable", "grid", {
3666 resize: function() {
3667 var that = $(this).data("ui-resizable"),
3670 os = that.originalSize,
3671 op = that.originalPosition,
3673 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3674 gridX = (grid[0]||1),
3675 gridY = (grid[1]||1),
3676 ox = Math.round((cs.width - os.width) / gridX) * gridX,
3677 oy = Math.round((cs.height - os.height) / gridY) * gridY,
3678 newWidth = os.width + ox,
3679 newHeight = os.height + oy,
3680 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3681 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3682 isMinWidth = o.minWidth && (o.minWidth > newWidth),
3683 isMinHeight = o.minHeight && (o.minHeight > newHeight);
3688 newWidth = newWidth + gridX;
3691 newHeight = newHeight + gridY;
3694 newWidth = newWidth - gridX;
3697 newHeight = newHeight - gridY;
3700 if (/^(se|s|e)$/.test(a)) {
3701 that.size.width = newWidth;
3702 that.size.height = newHeight;
3703 } else if (/^(ne)$/.test(a)) {
3704 that.size.width = newWidth;
3705 that.size.height = newHeight;
3706 that.position.top = op.top - oy;
3707 } else if (/^(sw)$/.test(a)) {
3708 that.size.width = newWidth;
3709 that.size.height = newHeight;
3710 that.position.left = op.left - ox;
3712 that.size.width = newWidth;
3713 that.size.height = newHeight;
3714 that.position.top = op.top - oy;
3715 that.position.left = op.left - ox;
3722 (function( $, undefined ) {
3724 $.widget("ui.selectable", $.ui.mouse, {
3741 _create: function() {
3745 this.element.addClass("ui-selectable");
3747 this.dragged = false;
3749 // cache selectee children based on filter
3750 this.refresh = function() {
3751 selectees = $(that.options.filter, that.element[0]);
3752 selectees.addClass("ui-selectee");
3753 selectees.each(function() {
3754 var $this = $(this),
3755 pos = $this.offset();
3756 $.data(this, "selectable-item", {
3761 right: pos.left + $this.outerWidth(),
3762 bottom: pos.top + $this.outerHeight(),
3763 startselected: false,
3764 selected: $this.hasClass("ui-selected"),
3765 selecting: $this.hasClass("ui-selecting"),
3766 unselecting: $this.hasClass("ui-unselecting")
3772 this.selectees = selectees.addClass("ui-selectee");
3776 this.helper = $("<div class='ui-selectable-helper'></div>");
3779 _destroy: function() {
3781 .removeClass("ui-selectee")
3782 .removeData("selectable-item");
3784 .removeClass("ui-selectable ui-selectable-disabled");
3785 this._mouseDestroy();
3788 _mouseStart: function(event) {
3790 options = this.options;
3792 this.opos = [event.pageX, event.pageY];
3794 if (this.options.disabled) {
3798 this.selectees = $(options.filter, this.element[0]);
3800 this._trigger("start", event);
3802 $(options.appendTo).append(this.helper);
3803 // position helper (lasso)
3805 "left": event.pageX,
3811 if (options.autoRefresh) {
3815 this.selectees.filter(".ui-selected").each(function() {
3816 var selectee = $.data(this, "selectable-item");
3817 selectee.startselected = true;
3818 if (!event.metaKey && !event.ctrlKey) {
3819 selectee.$element.removeClass("ui-selected");
3820 selectee.selected = false;
3821 selectee.$element.addClass("ui-unselecting");
3822 selectee.unselecting = true;
3823 // selectable UNSELECTING callback
3824 that._trigger("unselecting", event, {
3825 unselecting: selectee.element
3830 $(event.target).parents().addBack().each(function() {
3832 selectee = $.data(this, "selectable-item");
3834 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3836 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3837 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3838 selectee.unselecting = !doSelect;
3839 selectee.selecting = doSelect;
3840 selectee.selected = doSelect;
3841 // selectable (UN)SELECTING callback
3843 that._trigger("selecting", event, {
3844 selecting: selectee.element
3847 that._trigger("unselecting", event, {
3848 unselecting: selectee.element
3857 _mouseDrag: function(event) {
3859 this.dragged = true;
3861 if (this.options.disabled) {
3867 options = this.options,
3873 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3874 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
3875 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3877 this.selectees.each(function() {
3878 var selectee = $.data(this, "selectable-item"),
3881 //prevent helper from being selected if appendTo: selectable
3882 if (!selectee || selectee.element === that.element[0]) {
3886 if (options.tolerance === "touch") {
3887 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3888 } else if (options.tolerance === "fit") {
3889 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3894 if (selectee.selected) {
3895 selectee.$element.removeClass("ui-selected");
3896 selectee.selected = false;
3898 if (selectee.unselecting) {
3899 selectee.$element.removeClass("ui-unselecting");
3900 selectee.unselecting = false;
3902 if (!selectee.selecting) {
3903 selectee.$element.addClass("ui-selecting");
3904 selectee.selecting = true;
3905 // selectable SELECTING callback
3906 that._trigger("selecting", event, {
3907 selecting: selectee.element
3912 if (selectee.selecting) {
3913 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3914 selectee.$element.removeClass("ui-selecting");
3915 selectee.selecting = false;
3916 selectee.$element.addClass("ui-selected");
3917 selectee.selected = true;
3919 selectee.$element.removeClass("ui-selecting");
3920 selectee.selecting = false;
3921 if (selectee.startselected) {
3922 selectee.$element.addClass("ui-unselecting");
3923 selectee.unselecting = true;
3925 // selectable UNSELECTING callback
3926 that._trigger("unselecting", event, {
3927 unselecting: selectee.element
3931 if (selectee.selected) {
3932 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3933 selectee.$element.removeClass("ui-selected");
3934 selectee.selected = false;
3936 selectee.$element.addClass("ui-unselecting");
3937 selectee.unselecting = true;
3938 // selectable UNSELECTING callback
3939 that._trigger("unselecting", event, {
3940 unselecting: selectee.element
3950 _mouseStop: function(event) {
3953 this.dragged = false;
3955 $(".ui-unselecting", this.element[0]).each(function() {
3956 var selectee = $.data(this, "selectable-item");
3957 selectee.$element.removeClass("ui-unselecting");
3958 selectee.unselecting = false;
3959 selectee.startselected = false;
3960 that._trigger("unselected", event, {
3961 unselected: selectee.element
3964 $(".ui-selecting", this.element[0]).each(function() {
3965 var selectee = $.data(this, "selectable-item");
3966 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3967 selectee.selecting = false;
3968 selectee.selected = true;
3969 selectee.startselected = true;
3970 that._trigger("selected", event, {
3971 selected: selectee.element
3974 this._trigger("stop", event);
3976 this.helper.remove();
3984 (function( $, undefined ) {
3986 /*jshint loopfunc: true */
3988 function isOverAxis( x, reference, size ) {
3989 return ( x > reference ) && ( x < ( reference + size ) );
3992 function isFloating(item) {
3993 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3996 $.widget("ui.sortable", $.ui.mouse, {
3998 widgetEventPrefix: "sort",
4008 forcePlaceholderSize: false,
4009 forceHelperSize: false,
4018 scrollSensitivity: 20,
4021 tolerance: "intersect",
4038 _create: function() {
4040 var o = this.options;
4041 this.containerCache = {};
4042 this.element.addClass("ui-sortable");
4047 //Let's determine if the items are being displayed horizontally
4048 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
4050 //Let's determine the parent's offset
4051 this.offset = this.element.offset();
4053 //Initialize mouse events for interaction
4061 _destroy: function() {
4063 .removeClass("ui-sortable ui-sortable-disabled");
4064 this._mouseDestroy();
4066 for ( var i = this.items.length - 1; i >= 0; i-- ) {
4067 this.items[i].item.removeData(this.widgetName + "-item");
4073 _setOption: function(key, value){
4074 if ( key === "disabled" ) {
4075 this.options[ key ] = value;
4077 this.widget().toggleClass( "ui-sortable-disabled", !!value );
4079 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
4080 $.Widget.prototype._setOption.apply(this, arguments);
4084 _mouseCapture: function(event, overrideHandle) {
4085 var currentItem = null,
4086 validHandle = false,
4089 if (this.reverting) {
4093 if(this.options.disabled || this.options.type === "static") {
4097 //We have to refresh the items data once first
4098 this._refreshItems(event);
4100 //Find out if the clicked node (or one of its parents) is a actual item in this.items
4101 $(event.target).parents().each(function() {
4102 if($.data(this, that.widgetName + "-item") === that) {
4103 currentItem = $(this);
4107 if($.data(event.target, that.widgetName + "-item") === that) {
4108 currentItem = $(event.target);
4114 if(this.options.handle && !overrideHandle) {
4115 $(this.options.handle, currentItem).find("*").addBack().each(function() {
4116 if(this === event.target) {
4125 this.currentItem = currentItem;
4126 this._removeCurrentsFromItems();
4131 _mouseStart: function(event, overrideHandle, noActivation) {
4136 this.currentContainer = this;
4138 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
4139 this.refreshPositions();
4141 //Create and append the visible helper
4142 this.helper = this._createHelper(event);
4144 //Cache the helper size
4145 this._cacheHelperProportions();
4148 * - Position generation -
4149 * This block generates everything position related - it's the core of draggables.
4152 //Cache the margins of the original element
4153 this._cacheMargins();
4155 //Get the next scrolling parent
4156 this.scrollParent = this.helper.scrollParent();
4158 //The element's absolute position on the page minus margins
4159 this.offset = this.currentItem.offset();
4161 top: this.offset.top - this.margins.top,
4162 left: this.offset.left - this.margins.left
4165 $.extend(this.offset, {
4166 click: { //Where the click happened, relative to the element
4167 left: event.pageX - this.offset.left,
4168 top: event.pageY - this.offset.top
4170 parent: this._getParentOffset(),
4171 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
4174 // Only after we got the offset, we can change the helper's position to absolute
4175 // TODO: Still need to figure out a way to make relative sorting possible
4176 this.helper.css("position", "absolute");
4177 this.cssPosition = this.helper.css("position");
4179 //Generate the original position
4180 this.originalPosition = this._generatePosition(event);
4181 this.originalPageX = event.pageX;
4182 this.originalPageY = event.pageY;
4184 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
4185 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
4187 //Cache the former DOM position
4188 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
4190 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
4191 if(this.helper[0] !== this.currentItem[0]) {
4192 this.currentItem.hide();
4195 //Create the placeholder
4196 this._createPlaceholder();
4198 //Set a containment if given in the options
4200 this._setContainment();
4203 if( o.cursor && o.cursor !== "auto" ) { // cursor option
4204 body = this.document.find( "body" );
4207 this.storedCursor = body.css( "cursor" );
4208 body.css( "cursor", o.cursor );
4210 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
4213 if(o.opacity) { // opacity option
4214 if (this.helper.css("opacity")) {
4215 this._storedOpacity = this.helper.css("opacity");
4217 this.helper.css("opacity", o.opacity);
4220 if(o.zIndex) { // zIndex option
4221 if (this.helper.css("zIndex")) {
4222 this._storedZIndex = this.helper.css("zIndex");
4224 this.helper.css("zIndex", o.zIndex);
4228 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
4229 this.overflowOffset = this.scrollParent.offset();
4233 this._trigger("start", event, this._uiHash());
4235 //Recache the helper size
4236 if(!this._preserveHelperProportions) {
4237 this._cacheHelperProportions();
4241 //Post "activate" events to possible containers
4242 if( !noActivation ) {
4243 for ( i = this.containers.length - 1; i >= 0; i-- ) {
4244 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
4248 //Prepare possible droppables
4249 if($.ui.ddmanager) {
4250 $.ui.ddmanager.current = this;
4253 if ($.ui.ddmanager && !o.dropBehaviour) {
4254 $.ui.ddmanager.prepareOffsets(this, event);
4257 this.dragging = true;
4259 this.helper.addClass("ui-sortable-helper");
4260 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
4265 _mouseDrag: function(event) {
4266 var i, item, itemElement, intersection,
4270 //Compute the helpers position
4271 this.position = this._generatePosition(event);
4272 this.positionAbs = this._convertPositionTo("absolute");
4274 if (!this.lastPositionAbs) {
4275 this.lastPositionAbs = this.positionAbs;
4279 if(this.options.scroll) {
4280 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
4282 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
4283 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
4284 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
4285 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
4288 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
4289 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
4290 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
4291 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
4296 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
4297 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
4298 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
4299 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
4302 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
4303 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
4304 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
4305 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
4310 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
4311 $.ui.ddmanager.prepareOffsets(this, event);
4315 //Regenerate the absolute position used for position checks
4316 this.positionAbs = this._convertPositionTo("absolute");
4318 //Set the helper position
4319 if(!this.options.axis || this.options.axis !== "y") {
4320 this.helper[0].style.left = this.position.left+"px";
4322 if(!this.options.axis || this.options.axis !== "x") {
4323 this.helper[0].style.top = this.position.top+"px";
4327 for (i = this.items.length - 1; i >= 0; i--) {
4329 //Cache variables and intersection, continue if no intersection
4330 item = this.items[i];
4331 itemElement = item.item[0];
4332 intersection = this._intersectsWithPointer(item);
4333 if (!intersection) {
4337 // Only put the placeholder inside the current Container, skip all
4338 // items form other containers. This works because when moving
4339 // an item from one container to another the
4340 // currentContainer is switched before the placeholder is moved.
4342 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
4343 // beetween the outer and inner container.
4344 if (item.instance !== this.currentContainer) {
4348 // cannot intersect with itself
4349 // no useless actions that have been done before
4350 // no action if the item moved is the parent of the item checked
4351 if (itemElement !== this.currentItem[0] &&
4352 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
4353 !$.contains(this.placeholder[0], itemElement) &&
4354 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
4357 this.direction = intersection === 1 ? "down" : "up";
4359 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
4360 this._rearrange(event, item);
4365 this._trigger("change", event, this._uiHash());
4370 //Post events to containers
4371 this._contactContainers(event);
4373 //Interconnect with droppables
4374 if($.ui.ddmanager) {
4375 $.ui.ddmanager.drag(this, event);
4379 this._trigger("sort", event, this._uiHash());
4381 this.lastPositionAbs = this.positionAbs;
4386 _mouseStop: function(event, noPropagation) {
4392 //If we are using droppables, inform the manager about the drop
4393 if ($.ui.ddmanager && !this.options.dropBehaviour) {
4394 $.ui.ddmanager.drop(this, event);
4397 if(this.options.revert) {
4399 cur = this.placeholder.offset(),
4400 axis = this.options.axis,
4403 if ( !axis || axis === "x" ) {
4404 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
4406 if ( !axis || axis === "y" ) {
4407 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
4409 this.reverting = true;
4410 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
4414 this._clear(event, noPropagation);
4421 cancel: function() {
4425 this._mouseUp({ target: null });
4427 if(this.options.helper === "original") {
4428 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4430 this.currentItem.show();
4433 //Post deactivating events to containers
4434 for (var i = this.containers.length - 1; i >= 0; i--){
4435 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
4436 if(this.containers[i].containerCache.over) {
4437 this.containers[i]._trigger("out", null, this._uiHash(this));
4438 this.containers[i].containerCache.over = 0;
4444 if (this.placeholder) {
4445 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4446 if(this.placeholder[0].parentNode) {
4447 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4449 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
4450 this.helper.remove();
4460 if(this.domPosition.prev) {
4461 $(this.domPosition.prev).after(this.currentItem);
4463 $(this.domPosition.parent).prepend(this.currentItem);
4471 serialize: function(o) {
4473 var items = this._getItemsAsjQuery(o && o.connected),
4477 $(items).each(function() {
4478 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
4480 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4484 if(!str.length && o.key) {
4485 str.push(o.key + "=");
4488 return str.join("&");
4492 toArray: function(o) {
4494 var items = this._getItemsAsjQuery(o && o.connected),
4499 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
4504 /* Be careful with the following core functions */
4505 _intersectsWith: function(item) {
4507 var x1 = this.positionAbs.left,
4508 x2 = x1 + this.helperProportions.width,
4509 y1 = this.positionAbs.top,
4510 y2 = y1 + this.helperProportions.height,
4514 b = t + item.height,
4515 dyClick = this.offset.click.top,
4516 dxClick = this.offset.click.left,
4517 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
4518 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
4519 isOverElement = isOverElementHeight && isOverElementWidth;
4521 if ( this.options.tolerance === "pointer" ||
4522 this.options.forcePointerForContainers ||
4523 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4525 return isOverElement;
4528 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4529 x2 - (this.helperProportions.width / 2) < r && // Left Half
4530 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4531 y2 - (this.helperProportions.height / 2) < b ); // Top Half
4536 _intersectsWithPointer: function(item) {
4538 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4539 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4540 isOverElement = isOverElementHeight && isOverElementWidth,
4541 verticalDirection = this._getDragVerticalDirection(),
4542 horizontalDirection = this._getDragHorizontalDirection();
4544 if (!isOverElement) {
4548 return this.floating ?
4549 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4550 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4554 _intersectsWithSides: function(item) {
4556 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4557 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4558 verticalDirection = this._getDragVerticalDirection(),
4559 horizontalDirection = this._getDragHorizontalDirection();
4561 if (this.floating && horizontalDirection) {
4562 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4564 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4569 _getDragVerticalDirection: function() {
4570 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4571 return delta !== 0 && (delta > 0 ? "down" : "up");
4574 _getDragHorizontalDirection: function() {
4575 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4576 return delta !== 0 && (delta > 0 ? "right" : "left");
4579 refresh: function(event) {
4580 this._refreshItems(event);
4581 this.refreshPositions();
4585 _connectWith: function() {
4586 var options = this.options;
4587 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4590 _getItemsAsjQuery: function(connected) {
4592 var i, j, cur, inst,
4595 connectWith = this._connectWith();
4597 if(connectWith && connected) {
4598 for (i = connectWith.length - 1; i >= 0; i--){
4599 cur = $(connectWith[i]);
4600 for ( j = cur.length - 1; j >= 0; j--){
4601 inst = $.data(cur[j], this.widgetFullName);
4602 if(inst && inst !== this && !inst.options.disabled) {
4603 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
4609 queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
4611 for (i = queries.length - 1; i >= 0; i--){
4612 queries[i][0].each(function() {
4621 _removeCurrentsFromItems: function() {
4623 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4625 this.items = $.grep(this.items, function (item) {
4626 for (var j=0; j < list.length; j++) {
4627 if(list[j] === item.item[0]) {
4636 _refreshItems: function(event) {
4639 this.containers = [this];
4641 var i, j, cur, inst, targetData, _queries, item, queriesLength,
4643 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4644 connectWith = this._connectWith();
4646 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4647 for (i = connectWith.length - 1; i >= 0; i--){
4648 cur = $(connectWith[i]);
4649 for (j = cur.length - 1; j >= 0; j--){
4650 inst = $.data(cur[j], this.widgetFullName);
4651 if(inst && inst !== this && !inst.options.disabled) {
4652 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4653 this.containers.push(inst);
4659 for (i = queries.length - 1; i >= 0; i--) {
4660 targetData = queries[i][1];
4661 _queries = queries[i][0];
4663 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4664 item = $(_queries[j]);
4666 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4670 instance: targetData,
4671 width: 0, height: 0,
4679 refreshPositions: function(fast) {
4681 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4682 if(this.offsetParent && this.helper) {
4683 this.offset.parent = this._getParentOffset();
4688 for (i = this.items.length - 1; i >= 0; i--){
4689 item = this.items[i];
4691 //We ignore calculating positions of all connected containers when we're not over them
4692 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4696 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4699 item.width = t.outerWidth();
4700 item.height = t.outerHeight();
4708 if(this.options.custom && this.options.custom.refreshContainers) {
4709 this.options.custom.refreshContainers.call(this);
4711 for (i = this.containers.length - 1; i >= 0; i--){
4712 p = this.containers[i].element.offset();
4713 this.containers[i].containerCache.left = p.left;
4714 this.containers[i].containerCache.top = p.top;
4715 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4716 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4723 _createPlaceholder: function(that) {
4724 that = that || this;
4728 if(!o.placeholder || o.placeholder.constructor === String) {
4729 className = o.placeholder;
4731 element: function() {
4733 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4734 element = $( "<" + nodeName + ">", that.document[0] )
4735 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4736 .removeClass("ui-sortable-helper");
4738 if ( nodeName === "tr" ) {
4739 that.currentItem.children().each(function() {
4740 $( "<td> </td>", that.document[0] )
4741 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
4742 .appendTo( element );
4744 } else if ( nodeName === "img" ) {
4745 element.attr( "src", that.currentItem.attr( "src" ) );
4749 element.css( "visibility", "hidden" );
4754 update: function(container, p) {
4756 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4757 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4758 if(className && !o.forcePlaceholderSize) {
4762 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4763 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4764 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4769 //Create the placeholder
4770 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4772 //Append it after the actual current item
4773 that.currentItem.after(that.placeholder);
4775 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4776 o.placeholder.update(that, that.placeholder);
4780 _contactContainers: function(event) {
4781 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4782 innermostContainer = null,
4783 innermostIndex = null;
4785 // get innermost container that intersects with item
4786 for (i = this.containers.length - 1; i >= 0; i--) {
4788 // never consider a container that's located within the item itself
4789 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4793 if(this._intersectsWith(this.containers[i].containerCache)) {
4795 // if we've already found a container and it's more "inner" than this, then continue
4796 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4800 innermostContainer = this.containers[i];
4804 // container doesn't intersect. trigger "out" event if necessary
4805 if(this.containers[i].containerCache.over) {
4806 this.containers[i]._trigger("out", event, this._uiHash(this));
4807 this.containers[i].containerCache.over = 0;
4813 // if no intersecting containers found, return
4814 if(!innermostContainer) {
4818 // move the item into the container if it's not there already
4819 if(this.containers.length === 1) {
4820 if (!this.containers[innermostIndex].containerCache.over) {
4821 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4822 this.containers[innermostIndex].containerCache.over = 1;
4826 //When entering a new container, we will find the item with the least distance and append our item near it
4828 itemWithLeastDistance = null;
4829 floating = innermostContainer.floating || isFloating(this.currentItem);
4830 posProperty = floating ? "left" : "top";
4831 sizeProperty = floating ? "width" : "height";
4832 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4833 for (j = this.items.length - 1; j >= 0; j--) {
4834 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4837 if(this.items[j].item[0] === this.currentItem[0]) {
4840 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4843 cur = this.items[j].item.offset()[posProperty];
4845 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4847 cur += this.items[j][sizeProperty];
4850 if(Math.abs(cur - base) < dist) {
4851 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4852 this.direction = nearBottom ? "up": "down";
4856 //Check if dropOnEmpty is enabled
4857 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4861 if(this.currentContainer === this.containers[innermostIndex]) {
4865 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4866 this._trigger("change", event, this._uiHash());
4867 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4868 this.currentContainer = this.containers[innermostIndex];
4870 //Update the placeholder
4871 this.options.placeholder.update(this.currentContainer, this.placeholder);
4873 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4874 this.containers[innermostIndex].containerCache.over = 1;
4880 _createHelper: function(event) {
4882 var o = this.options,
4883 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4885 //Add the helper to the DOM if that didn't happen already
4886 if(!helper.parents("body").length) {
4887 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4890 if(helper[0] === this.currentItem[0]) {
4891 this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
4894 if(!helper[0].style.width || o.forceHelperSize) {
4895 helper.width(this.currentItem.width());
4897 if(!helper[0].style.height || o.forceHelperSize) {
4898 helper.height(this.currentItem.height());
4905 _adjustOffsetFromHelper: function(obj) {
4906 if (typeof obj === "string") {
4907 obj = obj.split(" ");
4909 if ($.isArray(obj)) {
4910 obj = {left: +obj[0], top: +obj[1] || 0};
4912 if ("left" in obj) {
4913 this.offset.click.left = obj.left + this.margins.left;
4915 if ("right" in obj) {
4916 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4919 this.offset.click.top = obj.top + this.margins.top;
4921 if ("bottom" in obj) {
4922 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4926 _getParentOffset: function() {
4929 //Get the offsetParent and cache its position
4930 this.offsetParent = this.helper.offsetParent();
4931 var po = this.offsetParent.offset();
4933 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4934 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4935 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4936 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4937 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4938 po.left += this.scrollParent.scrollLeft();
4939 po.top += this.scrollParent.scrollTop();
4942 // This needs to be actually done for all browsers, since pageX/pageY includes this information
4943 // with an ugly IE fix
4944 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4945 po = { top: 0, left: 0 };
4949 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4950 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4955 _getRelativeOffset: function() {
4957 if(this.cssPosition === "relative") {
4958 var p = this.currentItem.position();
4960 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4961 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4964 return { top: 0, left: 0 };
4969 _cacheMargins: function() {
4971 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4972 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4976 _cacheHelperProportions: function() {
4977 this.helperProportions = {
4978 width: this.helper.outerWidth(),
4979 height: this.helper.outerHeight()
4983 _setContainment: function() {
4987 if(o.containment === "parent") {
4988 o.containment = this.helper[0].parentNode;
4990 if(o.containment === "document" || o.containment === "window") {
4991 this.containment = [
4992 0 - this.offset.relative.left - this.offset.parent.left,
4993 0 - this.offset.relative.top - this.offset.parent.top,
4994 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4995 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4999 if(!(/^(document|window|parent)$/).test(o.containment)) {
5000 ce = $(o.containment)[0];
5001 co = $(o.containment).offset();
5002 over = ($(ce).css("overflow") !== "hidden");
5004 this.containment = [
5005 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
5006 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
5007 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
5008 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
5014 _convertPositionTo: function(d, pos) {
5017 pos = this.position;
5019 var mod = d === "absolute" ? 1 : -1,
5020 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
5021 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5025 pos.top + // The absolute mouse position
5026 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
5027 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
5028 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
5031 pos.left + // The absolute mouse position
5032 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
5033 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
5034 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
5040 _generatePosition: function(event) {
5044 pageX = event.pageX,
5045 pageY = event.pageY,
5046 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
5048 // This is another very weird special case that only happens for relative elements:
5049 // 1. If the css position is relative
5050 // 2. and the scroll parent is the document or similar to the offset parent
5051 // we have to refresh the relative offset during the scroll so there are no jumps
5052 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
5053 this.offset.relative = this._getRelativeOffset();
5057 * - Position constraining -
5058 * Constrain the position to a mix of grid, containment.
5061 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
5063 if(this.containment) {
5064 if(event.pageX - this.offset.click.left < this.containment[0]) {
5065 pageX = this.containment[0] + this.offset.click.left;
5067 if(event.pageY - this.offset.click.top < this.containment[1]) {
5068 pageY = this.containment[1] + this.offset.click.top;
5070 if(event.pageX - this.offset.click.left > this.containment[2]) {
5071 pageX = this.containment[2] + this.offset.click.left;
5073 if(event.pageY - this.offset.click.top > this.containment[3]) {
5074 pageY = this.containment[3] + this.offset.click.top;
5079 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
5080 pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
5082 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
5083 pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
5090 pageY - // The absolute mouse position
5091 this.offset.click.top - // Click offset (relative to the element)
5092 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
5093 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
5094 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
5097 pageX - // The absolute mouse position
5098 this.offset.click.left - // Click offset (relative to the element)
5099 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
5100 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
5101 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
5107 _rearrange: function(event, i, a, hardRefresh) {
5109 a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
5111 //Various things done here to improve the performance:
5112 // 1. we create a setTimeout, that calls refreshPositions
5113 // 2. on the instance, we have a counter variable, that get's higher after every append
5114 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
5115 // 4. this lets only the last addition to the timeout stack through
5116 this.counter = this.counter ? ++this.counter : 1;
5117 var counter = this.counter;
5119 this._delay(function() {
5120 if(counter === this.counter) {
5121 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
5127 _clear: function(event, noPropagation) {
5129 this.reverting = false;
5130 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
5131 // everything else normalized again
5133 delayedTriggers = [];
5135 // We first have to update the dom position of the actual currentItem
5136 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
5137 if(!this._noFinalSort && this.currentItem.parent().length) {
5138 this.placeholder.before(this.currentItem);
5140 this._noFinalSort = null;
5142 if(this.helper[0] === this.currentItem[0]) {
5143 for(i in this._storedCSS) {
5144 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
5145 this._storedCSS[i] = "";
5148 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
5150 this.currentItem.show();
5153 if(this.fromOutside && !noPropagation) {
5154 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
5156 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
5157 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
5160 // Check if the items Container has Changed and trigger appropriate
5162 if (this !== this.currentContainer) {
5163 if(!noPropagation) {
5164 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
5165 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
5166 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
5171 //Post events to containers
5172 for (i = this.containers.length - 1; i >= 0; i--){
5173 if(!noPropagation) {
5174 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
5176 if(this.containers[i].containerCache.over) {
5177 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
5178 this.containers[i].containerCache.over = 0;
5182 //Do what was originally in plugins
5183 if ( this.storedCursor ) {
5184 this.document.find( "body" ).css( "cursor", this.storedCursor );
5185 this.storedStylesheet.remove();
5187 if(this._storedOpacity) {
5188 this.helper.css("opacity", this._storedOpacity);
5190 if(this._storedZIndex) {
5191 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
5194 this.dragging = false;
5195 if(this.cancelHelperRemoval) {
5196 if(!noPropagation) {
5197 this._trigger("beforeStop", event, this._uiHash());
5198 for (i=0; i < delayedTriggers.length; i++) {
5199 delayedTriggers[i].call(this, event);
5200 } //Trigger all delayed events
5201 this._trigger("stop", event, this._uiHash());
5204 this.fromOutside = false;
5208 if(!noPropagation) {
5209 this._trigger("beforeStop", event, this._uiHash());
5212 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
5213 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
5215 if(this.helper[0] !== this.currentItem[0]) {
5216 this.helper.remove();
5220 if(!noPropagation) {
5221 for (i=0; i < delayedTriggers.length; i++) {
5222 delayedTriggers[i].call(this, event);
5223 } //Trigger all delayed events
5224 this._trigger("stop", event, this._uiHash());
5227 this.fromOutside = false;
5232 _trigger: function() {
5233 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
5238 _uiHash: function(_inst) {
5239 var inst = _inst || this;
5241 helper: inst.helper,
5242 placeholder: inst.placeholder || $([]),
5243 position: inst.position,
5244 originalPosition: inst.originalPosition,
5245 offset: inst.positionAbs,
5246 item: inst.currentItem,
5247 sender: _inst ? _inst.element : null
5254 (function( $, undefined ) {
5260 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
5261 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
5262 showProps.height = showProps.paddingTop = showProps.paddingBottom =
5263 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
5265 $.widget( "ui.accordion", {
5272 header: "> li > :first-child,> :not(li):even",
5273 heightStyle: "auto",
5275 activeHeader: "ui-icon-triangle-1-s",
5276 header: "ui-icon-triangle-1-e"
5281 beforeActivate: null
5284 _create: function() {
5285 var options = this.options;
5286 this.prevShow = this.prevHide = $();
5287 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
5289 .attr( "role", "tablist" );
5291 // don't allow collapsible: false and active: false / null
5292 if ( !options.collapsible && (options.active === false || options.active == null) ) {
5296 this._processPanels();
5297 // handle negative values
5298 if ( options.active < 0 ) {
5299 options.active += this.headers.length;
5304 _getCreateEventData: function() {
5306 header: this.active,
5307 panel: !this.active.length ? $() : this.active.next(),
5308 content: !this.active.length ? $() : this.active.next()
5312 _createIcons: function() {
5313 var icons = this.options.icons;
5316 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
5317 .prependTo( this.headers );
5318 this.active.children( ".ui-accordion-header-icon" )
5319 .removeClass( icons.header )
5320 .addClass( icons.activeHeader );
5321 this.headers.addClass( "ui-accordion-icons" );
5325 _destroyIcons: function() {
5327 .removeClass( "ui-accordion-icons" )
5328 .children( ".ui-accordion-header-icon" )
5332 _destroy: function() {
5335 // clean up main element
5337 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
5338 .removeAttr( "role" );
5342 .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" )
5343 .removeAttr( "role" )
5344 .removeAttr( "aria-selected" )
5345 .removeAttr( "aria-controls" )
5346 .removeAttr( "tabIndex" )
5348 if ( /^ui-accordion/.test( this.id ) ) {
5349 this.removeAttribute( "id" );
5352 this._destroyIcons();
5354 // clean up content panels
5355 contents = this.headers.next()
5356 .css( "display", "" )
5357 .removeAttr( "role" )
5358 .removeAttr( "aria-expanded" )
5359 .removeAttr( "aria-hidden" )
5360 .removeAttr( "aria-labelledby" )
5361 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
5363 if ( /^ui-accordion/.test( this.id ) ) {
5364 this.removeAttribute( "id" );
5367 if ( this.options.heightStyle !== "content" ) {
5368 contents.css( "height", "" );
5372 _setOption: function( key, value ) {
5373 if ( key === "active" ) {
5374 // _activate() will handle invalid values and update this.options
5375 this._activate( value );
5379 if ( key === "event" ) {
5380 if ( this.options.event ) {
5381 this._off( this.headers, this.options.event );
5383 this._setupEvents( value );
5386 this._super( key, value );
5388 // setting collapsible: false while collapsed; open first panel
5389 if ( key === "collapsible" && !value && this.options.active === false ) {
5390 this._activate( 0 );
5393 if ( key === "icons" ) {
5394 this._destroyIcons();
5396 this._createIcons();
5400 // #5332 - opacity doesn't cascade to positioned elements in IE
5401 // so we need to add the disabled class to the headers and panels
5402 if ( key === "disabled" ) {
5403 this.headers.add( this.headers.next() )
5404 .toggleClass( "ui-state-disabled", !!value );
5408 _keydown: function( event ) {
5409 /*jshint maxcomplexity:15*/
5410 if ( event.altKey || event.ctrlKey ) {
5414 var keyCode = $.ui.keyCode,
5415 length = this.headers.length,
5416 currentIndex = this.headers.index( event.target ),
5419 switch ( event.keyCode ) {
5422 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
5426 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
5430 this._eventHandler( event );
5433 toFocus = this.headers[ 0 ];
5436 toFocus = this.headers[ length - 1 ];
5441 $( event.target ).attr( "tabIndex", -1 );
5442 $( toFocus ).attr( "tabIndex", 0 );
5444 event.preventDefault();
5448 _panelKeyDown : function( event ) {
5449 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
5450 $( event.currentTarget ).prev().focus();
5454 refresh: function() {
5455 var options = this.options;
5456 this._processPanels();
5458 // was collapsed or no panel
5459 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
5460 options.active = false;
5462 // active false only when collapsible is true
5463 } else if ( options.active === false ) {
5464 this._activate( 0 );
5465 // was active, but active panel is gone
5466 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
5467 // all remaining panel are disabled
5468 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
5469 options.active = false;
5471 // activate previous panel
5473 this._activate( Math.max( 0, options.active - 1 ) );
5475 // was active, active panel still exists
5477 // make sure active index is correct
5478 options.active = this.headers.index( this.active );
5481 this._destroyIcons();
5486 _processPanels: function() {
5487 this.headers = this.element.find( this.options.header )
5488 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
5491 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
5492 .filter(":not(.ui-accordion-content-active)")
5496 _refresh: function() {
5498 options = this.options,
5499 heightStyle = options.heightStyle,
5500 parent = this.element.parent(),
5501 accordionId = this.accordionId = "ui-accordion-" +
5502 (this.element.attr( "id" ) || ++uid);
5504 this.active = this._findActive( options.active )
5505 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
5506 .removeClass( "ui-corner-all" );
5508 .addClass( "ui-accordion-content-active" )
5512 .attr( "role", "tab" )
5513 .each(function( i ) {
5514 var header = $( this ),
5515 headerId = header.attr( "id" ),
5516 panel = header.next(),
5517 panelId = panel.attr( "id" );
5519 headerId = accordionId + "-header-" + i;
5520 header.attr( "id", headerId );
5523 panelId = accordionId + "-panel-" + i;
5524 panel.attr( "id", panelId );
5526 header.attr( "aria-controls", panelId );
5527 panel.attr( "aria-labelledby", headerId );
5530 .attr( "role", "tabpanel" );
5535 "aria-selected": "false",
5540 "aria-expanded": "false",
5541 "aria-hidden": "true"
5545 // make sure at least one header is in the tab order
5546 if ( !this.active.length ) {
5547 this.headers.eq( 0 ).attr( "tabIndex", 0 );
5550 "aria-selected": "true",
5555 "aria-expanded": "true",
5556 "aria-hidden": "false"
5560 this._createIcons();
5562 this._setupEvents( options.event );
5564 if ( heightStyle === "fill" ) {
5565 maxHeight = parent.height();
5566 this.element.siblings( ":visible" ).each(function() {
5567 var elem = $( this ),
5568 position = elem.css( "position" );
5570 if ( position === "absolute" || position === "fixed" ) {
5573 maxHeight -= elem.outerHeight( true );
5576 this.headers.each(function() {
5577 maxHeight -= $( this ).outerHeight( true );
5582 $( this ).height( Math.max( 0, maxHeight -
5583 $( this ).innerHeight() + $( this ).height() ) );
5585 .css( "overflow", "auto" );
5586 } else if ( heightStyle === "auto" ) {
5590 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
5592 .height( maxHeight );
5596 _activate: function( index ) {
5597 var active = this._findActive( index )[ 0 ];
5599 // trying to activate the already active panel
5600 if ( active === this.active[ 0 ] ) {
5604 // trying to collapse, simulate a click on the currently active header
5605 active = active || this.active[ 0 ];
5607 this._eventHandler({
5609 currentTarget: active,
5610 preventDefault: $.noop
5614 _findActive: function( selector ) {
5615 return typeof selector === "number" ? this.headers.eq( selector ) : $();
5618 _setupEvents: function( event ) {
5623 $.each( event.split(" "), function( index, eventName ) {
5624 events[ eventName ] = "_eventHandler";
5628 this._off( this.headers.add( this.headers.next() ) );
5629 this._on( this.headers, events );
5630 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
5631 this._hoverable( this.headers );
5632 this._focusable( this.headers );
5635 _eventHandler: function( event ) {
5636 var options = this.options,
5637 active = this.active,
5638 clicked = $( event.currentTarget ),
5639 clickedIsActive = clicked[ 0 ] === active[ 0 ],
5640 collapsing = clickedIsActive && options.collapsible,
5641 toShow = collapsing ? $() : clicked.next(),
5642 toHide = active.next(),
5646 newHeader: collapsing ? $() : clicked,
5650 event.preventDefault();
5653 // click on active header, but not collapsible
5654 ( clickedIsActive && !options.collapsible ) ||
5655 // allow canceling activation
5656 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
5660 options.active = collapsing ? false : this.headers.index( clicked );
5662 // when the call to ._toggle() comes after the class changes
5663 // it causes a very odd bug in IE 8 (see #6720)
5664 this.active = clickedIsActive ? $() : clicked;
5665 this._toggle( eventData );
5668 // corner classes on the previously active header stay after the animation
5669 active.removeClass( "ui-accordion-header-active ui-state-active" );
5670 if ( options.icons ) {
5671 active.children( ".ui-accordion-header-icon" )
5672 .removeClass( options.icons.activeHeader )
5673 .addClass( options.icons.header );
5676 if ( !clickedIsActive ) {
5678 .removeClass( "ui-corner-all" )
5679 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
5680 if ( options.icons ) {
5681 clicked.children( ".ui-accordion-header-icon" )
5682 .removeClass( options.icons.header )
5683 .addClass( options.icons.activeHeader );
5688 .addClass( "ui-accordion-content-active" );
5692 _toggle: function( data ) {
5693 var toShow = data.newPanel,
5694 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
5696 // handle activating a panel during the animation for another activation
5697 this.prevShow.add( this.prevHide ).stop( true, true );
5698 this.prevShow = toShow;
5699 this.prevHide = toHide;
5701 if ( this.options.animate ) {
5702 this._animate( toShow, toHide, data );
5706 this._toggleComplete( data );
5710 "aria-expanded": "false",
5711 "aria-hidden": "true"
5713 toHide.prev().attr( "aria-selected", "false" );
5714 // if we're switching panels, remove the old header from the tab order
5715 // if we're opening from collapsed state, remove the previous header from the tab order
5716 // if we're collapsing, then keep the collapsing header in the tab order
5717 if ( toShow.length && toHide.length ) {
5718 toHide.prev().attr( "tabIndex", -1 );
5719 } else if ( toShow.length ) {
5720 this.headers.filter(function() {
5721 return $( this ).attr( "tabIndex" ) === 0;
5723 .attr( "tabIndex", -1 );
5728 "aria-expanded": "true",
5729 "aria-hidden": "false"
5733 "aria-selected": "true",
5738 _animate: function( toShow, toHide, data ) {
5739 var total, easing, duration,
5742 down = toShow.length &&
5743 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
5744 animate = this.options.animate || {},
5745 options = down && animate.down || animate,
5746 complete = function() {
5747 that._toggleComplete( data );
5750 if ( typeof options === "number" ) {
5753 if ( typeof options === "string" ) {
5756 // fall back from options to animation in case of partial down settings
5757 easing = easing || options.easing || animate.easing;
5758 duration = duration || options.duration || animate.duration;
5760 if ( !toHide.length ) {
5761 return toShow.animate( showProps, duration, easing, complete );
5763 if ( !toShow.length ) {
5764 return toHide.animate( hideProps, duration, easing, complete );
5767 total = toShow.show().outerHeight();
5768 toHide.animate( hideProps, {
5771 step: function( now, fx ) {
5772 fx.now = Math.round( now );
5777 .animate( showProps, {
5781 step: function( now, fx ) {
5782 fx.now = Math.round( now );
5783 if ( fx.prop !== "height" ) {
5785 } else if ( that.options.heightStyle !== "content" ) {
5786 fx.now = Math.round( total - toHide.outerHeight() - adjust );
5793 _toggleComplete: function( data ) {
5794 var toHide = data.oldPanel;
5797 .removeClass( "ui-accordion-content-active" )
5799 .removeClass( "ui-corner-top" )
5800 .addClass( "ui-corner-all" );
5802 // Work around for rendering bug in IE (#5421)
5803 if ( toHide.length ) {
5804 toHide.parent()[0].className = toHide.parent()[0].className;
5807 this._trigger( "activate", null, data );
5812 (function( $, undefined ) {
5814 // used to prevent race conditions with remote data sources
5815 var requestIndex = 0;
5817 $.widget( "ui.autocomplete", {
5819 defaultElement: "<input>",
5844 _create: function() {
5845 // Some browsers only repeat keydown events, not keypress events,
5846 // so we use the suppressKeyPress flag to determine if we've already
5847 // handled the keydown event. #7269
5848 // Unfortunately the code for & in keypress is the same as the up arrow,
5849 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
5850 // events when we know the keydown event was used to modify the
5851 // search term. #7799
5852 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
5853 nodeName = this.element[0].nodeName.toLowerCase(),
5854 isTextarea = nodeName === "textarea",
5855 isInput = nodeName === "input";
5858 // Textareas are always multi-line
5860 // Inputs are always single-line, even if inside a contentEditable element
5861 // IE also treats inputs as contentEditable
5863 // All other element types are determined by whether or not they're contentEditable
5864 this.element.prop( "isContentEditable" );
5866 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
5867 this.isNewMenu = true;
5870 .addClass( "ui-autocomplete-input" )
5871 .attr( "autocomplete", "off" );
5873 this._on( this.element, {
5874 keydown: function( event ) {
5875 /*jshint maxcomplexity:15*/
5876 if ( this.element.prop( "readOnly" ) ) {
5877 suppressKeyPress = true;
5878 suppressInput = true;
5879 suppressKeyPressRepeat = true;
5883 suppressKeyPress = false;
5884 suppressInput = false;
5885 suppressKeyPressRepeat = false;
5886 var keyCode = $.ui.keyCode;
5887 switch( event.keyCode ) {
5888 case keyCode.PAGE_UP:
5889 suppressKeyPress = true;
5890 this._move( "previousPage", event );
5892 case keyCode.PAGE_DOWN:
5893 suppressKeyPress = true;
5894 this._move( "nextPage", event );
5897 suppressKeyPress = true;
5898 this._keyEvent( "previous", event );
5901 suppressKeyPress = true;
5902 this._keyEvent( "next", event );
5905 case keyCode.NUMPAD_ENTER:
5906 // when menu is open and has focus
5907 if ( this.menu.active ) {
5908 // #6055 - Opera still allows the keypress to occur
5909 // which causes forms to submit
5910 suppressKeyPress = true;
5911 event.preventDefault();
5912 this.menu.select( event );
5916 if ( this.menu.active ) {
5917 this.menu.select( event );
5920 case keyCode.ESCAPE:
5921 if ( this.menu.element.is( ":visible" ) ) {
5922 this._value( this.term );
5923 this.close( event );
5924 // Different browsers have different default behavior for escape
5925 // Single press can mean undo or clear
5926 // Double press in IE means clear the whole form
5927 event.preventDefault();
5931 suppressKeyPressRepeat = true;
5932 // search timeout should be triggered before the input value is changed
5933 this._searchTimeout( event );
5937 keypress: function( event ) {
5938 if ( suppressKeyPress ) {
5939 suppressKeyPress = false;
5940 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
5941 event.preventDefault();
5945 if ( suppressKeyPressRepeat ) {
5949 // replicate some key handlers to allow them to repeat in Firefox and Opera
5950 var keyCode = $.ui.keyCode;
5951 switch( event.keyCode ) {
5952 case keyCode.PAGE_UP:
5953 this._move( "previousPage", event );
5955 case keyCode.PAGE_DOWN:
5956 this._move( "nextPage", event );
5959 this._keyEvent( "previous", event );
5962 this._keyEvent( "next", event );
5966 input: function( event ) {
5967 if ( suppressInput ) {
5968 suppressInput = false;
5969 event.preventDefault();
5972 this._searchTimeout( event );
5975 this.selectedItem = null;
5976 this.previous = this._value();
5978 blur: function( event ) {
5979 if ( this.cancelBlur ) {
5980 delete this.cancelBlur;
5984 clearTimeout( this.searching );
5985 this.close( event );
5986 this._change( event );
5991 this.menu = $( "<ul>" )
5992 .addClass( "ui-autocomplete ui-front" )
5993 .appendTo( this._appendTo() )
5995 // disable ARIA support, the live region takes care of that
6001 this._on( this.menu.element, {
6002 mousedown: function( event ) {
6003 // prevent moving focus out of the text field
6004 event.preventDefault();
6006 // IE doesn't prevent moving focus even with event.preventDefault()
6007 // so we set a flag to know when we should ignore the blur event
6008 this.cancelBlur = true;
6009 this._delay(function() {
6010 delete this.cancelBlur;
6013 // clicking on the scrollbar causes focus to shift to the body
6014 // but we can't detect a mouseup or a click immediately afterward
6015 // so we have to track the next mousedown and close the menu if
6016 // the user clicks somewhere outside of the autocomplete
6017 var menuElement = this.menu.element[ 0 ];
6018 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
6019 this._delay(function() {
6021 this.document.one( "mousedown", function( event ) {
6022 if ( event.target !== that.element[ 0 ] &&
6023 event.target !== menuElement &&
6024 !$.contains( menuElement, event.target ) ) {
6031 menufocus: function( event, ui ) {
6033 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
6034 if ( this.isNewMenu ) {
6035 this.isNewMenu = false;
6036 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
6039 this.document.one( "mousemove", function() {
6040 $( event.target ).trigger( event.originalEvent );
6047 var item = ui.item.data( "ui-autocomplete-item" );
6048 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
6049 // use value to match what will end up in the input, if it was a key event
6050 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
6051 this._value( item.value );
6054 // Normally the input is populated with the item's value as the
6055 // menu is navigated, causing screen readers to notice a change and
6056 // announce the item. Since the focus event was canceled, this doesn't
6057 // happen, so we update the live region so that screen readers can
6058 // still notice the change and announce it.
6059 this.liveRegion.text( item.value );
6062 menuselect: function( event, ui ) {
6063 var item = ui.item.data( "ui-autocomplete-item" ),
6064 previous = this.previous;
6066 // only trigger when focus was lost (click on menu)
6067 if ( this.element[0] !== this.document[0].activeElement ) {
6068 this.element.focus();
6069 this.previous = previous;
6070 // #6109 - IE triggers two focus events and the second
6071 // is asynchronous, so we need to reset the previous
6072 // term synchronously and asynchronously :-(
6073 this._delay(function() {
6074 this.previous = previous;
6075 this.selectedItem = item;
6079 if ( false !== this._trigger( "select", event, { item: item } ) ) {
6080 this._value( item.value );
6082 // reset the term after the select event
6083 // this allows custom select handling to work properly
6084 this.term = this._value();
6086 this.close( event );
6087 this.selectedItem = item;
6091 this.liveRegion = $( "<span>", {
6093 "aria-live": "polite"
6095 .addClass( "ui-helper-hidden-accessible" )
6096 .insertBefore( this.element );
6098 // turning off autocomplete prevents the browser from remembering the
6099 // value when navigating through history, so we re-enable autocomplete
6100 // if the page is unloaded before the widget is destroyed. #7790
6101 this._on( this.window, {
6102 beforeunload: function() {
6103 this.element.removeAttr( "autocomplete" );
6108 _destroy: function() {
6109 clearTimeout( this.searching );
6111 .removeClass( "ui-autocomplete-input" )
6112 .removeAttr( "autocomplete" );
6113 this.menu.element.remove();
6114 this.liveRegion.remove();
6117 _setOption: function( key, value ) {
6118 this._super( key, value );
6119 if ( key === "source" ) {
6122 if ( key === "appendTo" ) {
6123 this.menu.element.appendTo( this._appendTo() );
6125 if ( key === "disabled" && value && this.xhr ) {
6130 _appendTo: function() {
6131 var element = this.options.appendTo;
6134 element = element.jquery || element.nodeType ?
6136 this.document.find( element ).eq( 0 );
6140 element = this.element.closest( ".ui-front" );
6143 if ( !element.length ) {
6144 element = this.document[0].body;
6150 _initSource: function() {
6153 if ( $.isArray(this.options.source) ) {
6154 array = this.options.source;
6155 this.source = function( request, response ) {
6156 response( $.ui.autocomplete.filter( array, request.term ) );
6158 } else if ( typeof this.options.source === "string" ) {
6159 url = this.options.source;
6160 this.source = function( request, response ) {
6168 success: function( data ) {
6177 this.source = this.options.source;
6181 _searchTimeout: function( event ) {
6182 clearTimeout( this.searching );
6183 this.searching = this._delay(function() {
6184 // only search if the value has changed
6185 if ( this.term !== this._value() ) {
6186 this.selectedItem = null;
6187 this.search( null, event );
6189 }, this.options.delay );
6192 search: function( value, event ) {
6193 value = value != null ? value : this._value();
6195 // always save the actual value, not the one passed as an argument
6196 this.term = this._value();
6198 if ( value.length < this.options.minLength ) {
6199 return this.close( event );
6202 if ( this._trigger( "search", event ) === false ) {
6206 return this._search( value );
6209 _search: function( value ) {
6211 this.element.addClass( "ui-autocomplete-loading" );
6212 this.cancelSearch = false;
6214 this.source( { term: value }, this._response() );
6217 _response: function() {
6219 index = ++requestIndex;
6221 return function( content ) {
6222 if ( index === requestIndex ) {
6223 that.__response( content );
6227 if ( !that.pending ) {
6228 that.element.removeClass( "ui-autocomplete-loading" );
6233 __response: function( content ) {
6235 content = this._normalize( content );
6237 this._trigger( "response", null, { content: content } );
6238 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
6239 this._suggest( content );
6240 this._trigger( "open" );
6242 // use ._close() instead of .close() so we don't cancel future searches
6247 close: function( event ) {
6248 this.cancelSearch = true;
6249 this._close( event );
6252 _close: function( event ) {
6253 if ( this.menu.element.is( ":visible" ) ) {
6254 this.menu.element.hide();
6256 this.isNewMenu = true;
6257 this._trigger( "close", event );
6261 _change: function( event ) {
6262 if ( this.previous !== this._value() ) {
6263 this._trigger( "change", event, { item: this.selectedItem } );
6267 _normalize: function( items ) {
6268 // assume all items have the right format when the first item is complete
6269 if ( items.length && items[0].label && items[0].value ) {
6272 return $.map( items, function( item ) {
6273 if ( typeof item === "string" ) {
6280 label: item.label || item.value,
6281 value: item.value || item.label
6286 _suggest: function( items ) {
6287 var ul = this.menu.element.empty();
6288 this._renderMenu( ul, items );
6289 this.isNewMenu = true;
6290 this.menu.refresh();
6292 // size and position menu
6295 ul.position( $.extend({
6297 }, this.options.position ));
6299 if ( this.options.autoFocus ) {
6304 _resizeMenu: function() {
6305 var ul = this.menu.element;
6306 ul.outerWidth( Math.max(
6307 // Firefox wraps long text (possibly a rounding bug)
6308 // so we add 1px to avoid the wrapping (#7513)
6309 ul.width( "" ).outerWidth() + 1,
6310 this.element.outerWidth()
6314 _renderMenu: function( ul, items ) {
6316 $.each( items, function( index, item ) {
6317 that._renderItemData( ul, item );
6321 _renderItemData: function( ul, item ) {
6322 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
6325 _renderItem: function( ul, item ) {
6327 .append( $( "<a>" ).text( item.label ) )
6331 _move: function( direction, event ) {
6332 if ( !this.menu.element.is( ":visible" ) ) {
6333 this.search( null, event );
6336 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
6337 this.menu.isLastItem() && /^next/.test( direction ) ) {
6338 this._value( this.term );
6342 this.menu[ direction ]( event );
6345 widget: function() {
6346 return this.menu.element;
6349 _value: function() {
6350 return this.valueMethod.apply( this.element, arguments );
6353 _keyEvent: function( keyEvent, event ) {
6354 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
6355 this._move( keyEvent, event );
6357 // prevents moving cursor to beginning/end of the text field in some browsers
6358 event.preventDefault();
6363 $.extend( $.ui.autocomplete, {
6364 escapeRegex: function( value ) {
6365 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
6367 filter: function(array, term) {
6368 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
6369 return $.grep( array, function(value) {
6370 return matcher.test( value.label || value.value || value );
6376 // live region extension, adding a `messages` option
6377 // NOTE: This is an experimental API. We are still investigating
6378 // a full solution for string manipulation and internationalization.
6379 $.widget( "ui.autocomplete", $.ui.autocomplete, {
6382 noResults: "No search results.",
6383 results: function( amount ) {
6384 return amount + ( amount > 1 ? " results are" : " result is" ) +
6385 " available, use up and down arrow keys to navigate.";
6390 __response: function( content ) {
6392 this._superApply( arguments );
6393 if ( this.options.disabled || this.cancelSearch ) {
6396 if ( content && content.length ) {
6397 message = this.options.messages.results( content.length );
6399 message = this.options.messages.noResults;
6401 this.liveRegion.text( message );
6406 (function( $, undefined ) {
6408 var lastActive, startXPos, startYPos, clickDragged,
6409 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
6410 stateClasses = "ui-state-hover ui-state-active ",
6411 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",
6412 formResetHandler = function() {
6413 var form = $( this );
6414 setTimeout(function() {
6415 form.find( ":ui-button" ).button( "refresh" );
6418 radioGroup = function( radio ) {
6419 var name = radio.name,
6423 name = name.replace( /'/g, "\\'" );
6425 radios = $( form ).find( "[name='" + name + "']" );
6427 radios = $( "[name='" + name + "']", radio.ownerDocument )
6428 .filter(function() {
6436 $.widget( "ui.button", {
6438 defaultElement: "<button>",
6448 _create: function() {
6449 this.element.closest( "form" )
6450 .unbind( "reset" + this.eventNamespace )
6451 .bind( "reset" + this.eventNamespace, formResetHandler );
6453 if ( typeof this.options.disabled !== "boolean" ) {
6454 this.options.disabled = !!this.element.prop( "disabled" );
6456 this.element.prop( "disabled", this.options.disabled );
6459 this._determineButtonType();
6460 this.hasTitle = !!this.buttonElement.attr( "title" );
6463 options = this.options,
6464 toggleButton = this.type === "checkbox" || this.type === "radio",
6465 activeClass = !toggleButton ? "ui-state-active" : "",
6466 focusClass = "ui-state-focus";
6468 if ( options.label === null ) {
6469 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
6472 this._hoverable( this.buttonElement );
6475 .addClass( baseClasses )
6476 .attr( "role", "button" )
6477 .bind( "mouseenter" + this.eventNamespace, function() {
6478 if ( options.disabled ) {
6481 if ( this === lastActive ) {
6482 $( this ).addClass( "ui-state-active" );
6485 .bind( "mouseleave" + this.eventNamespace, function() {
6486 if ( options.disabled ) {
6489 $( this ).removeClass( activeClass );
6491 .bind( "click" + this.eventNamespace, function( event ) {
6492 if ( options.disabled ) {
6493 event.preventDefault();
6494 event.stopImmediatePropagation();
6499 .bind( "focus" + this.eventNamespace, function() {
6500 // no need to check disabled, focus won't be triggered anyway
6501 that.buttonElement.addClass( focusClass );
6503 .bind( "blur" + this.eventNamespace, function() {
6504 that.buttonElement.removeClass( focusClass );
6507 if ( toggleButton ) {
6508 this.element.bind( "change" + this.eventNamespace, function() {
6509 if ( clickDragged ) {
6514 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
6515 // prevents issue where button state changes but checkbox/radio checked state
6516 // does not in Firefox (see ticket #6970)
6518 .bind( "mousedown" + this.eventNamespace, function( event ) {
6519 if ( options.disabled ) {
6522 clickDragged = false;
6523 startXPos = event.pageX;
6524 startYPos = event.pageY;
6526 .bind( "mouseup" + this.eventNamespace, function( event ) {
6527 if ( options.disabled ) {
6530 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
6531 clickDragged = true;
6536 if ( this.type === "checkbox" ) {
6537 this.buttonElement.bind( "click" + this.eventNamespace, function() {
6538 if ( options.disabled || clickDragged ) {
6542 } else if ( this.type === "radio" ) {
6543 this.buttonElement.bind( "click" + this.eventNamespace, function() {
6544 if ( options.disabled || clickDragged ) {
6547 $( this ).addClass( "ui-state-active" );
6548 that.buttonElement.attr( "aria-pressed", "true" );
6550 var radio = that.element[ 0 ];
6554 return $( this ).button( "widget" )[ 0 ];
6556 .removeClass( "ui-state-active" )
6557 .attr( "aria-pressed", "false" );
6561 .bind( "mousedown" + this.eventNamespace, function() {
6562 if ( options.disabled ) {
6565 $( this ).addClass( "ui-state-active" );
6567 that.document.one( "mouseup", function() {
6571 .bind( "mouseup" + this.eventNamespace, function() {
6572 if ( options.disabled ) {
6575 $( this ).removeClass( "ui-state-active" );
6577 .bind( "keydown" + this.eventNamespace, function(event) {
6578 if ( options.disabled ) {
6581 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
6582 $( this ).addClass( "ui-state-active" );
6585 // see #8559, we bind to blur here in case the button element loses
6586 // focus between keydown and keyup, it would be left in an "active" state
6587 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
6588 $( this ).removeClass( "ui-state-active" );
6591 if ( this.buttonElement.is("a") ) {
6592 this.buttonElement.keyup(function(event) {
6593 if ( event.keyCode === $.ui.keyCode.SPACE ) {
6594 // TODO pass through original event correctly (just as 2nd argument doesn't work)
6601 // TODO: pull out $.Widget's handling for the disabled option into
6602 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
6603 // be overridden by individual plugins
6604 this._setOption( "disabled", options.disabled );
6605 this._resetButton();
6608 _determineButtonType: function() {
6609 var ancestor, labelSelector, checked;
6611 if ( this.element.is("[type=checkbox]") ) {
6612 this.type = "checkbox";
6613 } else if ( this.element.is("[type=radio]") ) {
6614 this.type = "radio";
6615 } else if ( this.element.is("input") ) {
6616 this.type = "input";
6618 this.type = "button";
6621 if ( this.type === "checkbox" || this.type === "radio" ) {
6622 // we don't search against the document in case the element
6623 // is disconnected from the DOM
6624 ancestor = this.element.parents().last();
6625 labelSelector = "label[for='" + this.element.attr("id") + "']";
6626 this.buttonElement = ancestor.find( labelSelector );
6627 if ( !this.buttonElement.length ) {
6628 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
6629 this.buttonElement = ancestor.filter( labelSelector );
6630 if ( !this.buttonElement.length ) {
6631 this.buttonElement = ancestor.find( labelSelector );
6634 this.element.addClass( "ui-helper-hidden-accessible" );
6636 checked = this.element.is( ":checked" );
6638 this.buttonElement.addClass( "ui-state-active" );
6640 this.buttonElement.prop( "aria-pressed", checked );
6642 this.buttonElement = this.element;
6646 widget: function() {
6647 return this.buttonElement;
6650 _destroy: function() {
6652 .removeClass( "ui-helper-hidden-accessible" );
6654 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
6655 .removeAttr( "role" )
6656 .removeAttr( "aria-pressed" )
6657 .html( this.buttonElement.find(".ui-button-text").html() );
6659 if ( !this.hasTitle ) {
6660 this.buttonElement.removeAttr( "title" );
6664 _setOption: function( key, value ) {
6665 this._super( key, value );
6666 if ( key === "disabled" ) {
6668 this.element.prop( "disabled", true );
6670 this.element.prop( "disabled", false );
6674 this._resetButton();
6677 refresh: function() {
6679 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
6681 if ( isDisabled !== this.options.disabled ) {
6682 this._setOption( "disabled", isDisabled );
6684 if ( this.type === "radio" ) {
6685 radioGroup( this.element[0] ).each(function() {
6686 if ( $( this ).is( ":checked" ) ) {
6687 $( this ).button( "widget" )
6688 .addClass( "ui-state-active" )
6689 .attr( "aria-pressed", "true" );
6691 $( this ).button( "widget" )
6692 .removeClass( "ui-state-active" )
6693 .attr( "aria-pressed", "false" );
6696 } else if ( this.type === "checkbox" ) {
6697 if ( this.element.is( ":checked" ) ) {
6699 .addClass( "ui-state-active" )
6700 .attr( "aria-pressed", "true" );
6703 .removeClass( "ui-state-active" )
6704 .attr( "aria-pressed", "false" );
6709 _resetButton: function() {
6710 if ( this.type === "input" ) {
6711 if ( this.options.label ) {
6712 this.element.val( this.options.label );
6716 var buttonElement = this.buttonElement.removeClass( typeClasses ),
6717 buttonText = $( "<span></span>", this.document[0] )
6718 .addClass( "ui-button-text" )
6719 .html( this.options.label )
6720 .appendTo( buttonElement.empty() )
6722 icons = this.options.icons,
6723 multipleIcons = icons.primary && icons.secondary,
6726 if ( icons.primary || icons.secondary ) {
6727 if ( this.options.text ) {
6728 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
6731 if ( icons.primary ) {
6732 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
6735 if ( icons.secondary ) {
6736 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
6739 if ( !this.options.text ) {
6740 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
6742 if ( !this.hasTitle ) {
6743 buttonElement.attr( "title", $.trim( buttonText ) );
6747 buttonClasses.push( "ui-button-text-only" );
6749 buttonElement.addClass( buttonClasses.join( " " ) );
6753 $.widget( "ui.buttonset", {
6756 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
6759 _create: function() {
6760 this.element.addClass( "ui-buttonset" );
6767 _setOption: function( key, value ) {
6768 if ( key === "disabled" ) {
6769 this.buttons.button( "option", key, value );
6772 this._super( key, value );
6775 refresh: function() {
6776 var rtl = this.element.css( "direction" ) === "rtl";
6778 this.buttons = this.element.find( this.options.items )
6779 .filter( ":ui-button" )
6780 .button( "refresh" )
6782 .not( ":ui-button" )
6786 return $( this ).button( "widget" )[ 0 ];
6788 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
6790 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
6793 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
6798 _destroy: function() {
6799 this.element.removeClass( "ui-buttonset" );
6802 return $( this ).button( "widget" )[ 0 ];
6804 .removeClass( "ui-corner-left ui-corner-right" )
6806 .button( "destroy" );
6811 (function( $, undefined ) {
6813 $.extend($.ui, { datepicker: { version: "1.10.3" } });
6815 var PROP_NAME = "datepicker",
6818 /* Date picker manager.
6819 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
6820 Settings for (groups of) date pickers are maintained in an instance object,
6821 allowing multiple different settings on the same page. */
6823 function Datepicker() {
6824 this._curInst = null; // The current instance in use
6825 this._keyEvent = false; // If the last event was a key event
6826 this._disabledInputs = []; // List of date picker inputs that have been disabled
6827 this._datepickerShowing = false; // True if the popup picker is showing , false if not
6828 this._inDialog = false; // True if showing within a "dialog", false if not
6829 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
6830 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
6831 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
6832 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
6833 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
6834 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
6835 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
6836 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
6837 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
6838 this.regional = []; // Available regional settings, indexed by language code
6839 this.regional[""] = { // Default regional settings
6840 closeText: "Done", // Display text for close link
6841 prevText: "Prev", // Display text for previous month link
6842 nextText: "Next", // Display text for next month link
6843 currentText: "Today", // Display text for current month link
6844 monthNames: ["January","February","March","April","May","June",
6845 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
6846 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
6847 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
6848 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
6849 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
6850 weekHeader: "Wk", // Column header for week of the year
6851 dateFormat: "mm/dd/yy", // See format options on parseDate
6852 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
6853 isRTL: false, // True if right-to-left language, false if left-to-right
6854 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
6855 yearSuffix: "" // Additional text to append to the year in the month headers
6857 this._defaults = { // Global defaults for all the date picker instances
6858 showOn: "focus", // "focus" for popup on focus,
6859 // "button" for trigger button, or "both" for either
6860 showAnim: "fadeIn", // Name of jQuery animation for popup
6861 showOptions: {}, // Options for enhanced animations
6862 defaultDate: null, // Used when field is blank: actual date,
6863 // +/-number for offset from today, null for today
6864 appendText: "", // Display text following the input box, e.g. showing the format
6865 buttonText: "...", // Text for trigger button
6866 buttonImage: "", // URL for trigger button image
6867 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
6868 hideIfNoPrevNext: false, // True to hide next/previous month links
6869 // if not applicable, false to just disable them
6870 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
6871 gotoCurrent: false, // True if today link goes back to current selection instead
6872 changeMonth: false, // True if month can be selected directly, false if only prev/next
6873 changeYear: false, // True if year can be selected directly, false if only prev/next
6874 yearRange: "c-10:c+10", // Range of years to display in drop-down,
6875 // either relative to today's year (-nn:+nn), relative to currently displayed year
6876 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
6877 showOtherMonths: false, // True to show dates in other months, false to leave blank
6878 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
6879 showWeek: false, // True to show week of the year, false to not show it
6880 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
6881 // takes a Date and returns the number of the week for it
6882 shortYearCutoff: "+10", // Short year values < this are in the current century,
6883 // > this are in the previous century,
6884 // string value starting with "+" for current year + value
6885 minDate: null, // The earliest selectable date, or null for no limit
6886 maxDate: null, // The latest selectable date, or null for no limit
6887 duration: "fast", // Duration of display/closure
6888 beforeShowDay: null, // Function that takes a date and returns an array with
6889 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
6890 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
6891 beforeShow: null, // Function that takes an input field and
6892 // returns a set of custom settings for the date picker
6893 onSelect: null, // Define a callback function when a date is selected
6894 onChangeMonthYear: null, // Define a callback function when the month or year is changed
6895 onClose: null, // Define a callback function when the datepicker is closed
6896 numberOfMonths: 1, // Number of months to show at a time
6897 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
6898 stepMonths: 1, // Number of months to step back/forward
6899 stepBigMonths: 12, // Number of months to step back/forward for the big links
6900 altField: "", // Selector for an alternate field to store selected dates into
6901 altFormat: "", // The date format to use for the alternate field
6902 constrainInput: true, // The input is constrained by the current date format
6903 showButtonPanel: false, // True to show button panel, false to not show it
6904 autoSize: false, // True to size the input for the date format, false to leave as is
6905 disabled: false // The initial disabled state
6907 $.extend(this._defaults, this.regional[""]);
6908 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
6911 $.extend(Datepicker.prototype, {
6912 /* Class name added to elements to indicate already configured with a date picker. */
6913 markerClassName: "hasDatepicker",
6915 //Keep track of the maximum number of rows displayed (see #7043)
6918 // TODO rename to "widget" when switching to widget factory
6919 _widgetDatepicker: function() {
6923 /* Override the default settings for all instances of the date picker.
6924 * @param settings object - the new settings to use as defaults (anonymous object)
6925 * @return the manager object
6927 setDefaults: function(settings) {
6928 extendRemove(this._defaults, settings || {});
6932 /* Attach the date picker to a jQuery selection.
6933 * @param target element - the target input field or division or span
6934 * @param settings object - the new settings to use for this date picker instance (anonymous)
6936 _attachDatepicker: function(target, settings) {
6937 var nodeName, inline, inst;
6938 nodeName = target.nodeName.toLowerCase();
6939 inline = (nodeName === "div" || nodeName === "span");
6942 target.id = "dp" + this.uuid;
6944 inst = this._newInst($(target), inline);
6945 inst.settings = $.extend({}, settings || {});
6946 if (nodeName === "input") {
6947 this._connectDatepicker(target, inst);
6948 } else if (inline) {
6949 this._inlineDatepicker(target, inst);
6953 /* Create a new instance object. */
6954 _newInst: function(target, inline) {
6955 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
6956 return {id: id, input: target, // associated target
6957 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
6958 drawMonth: 0, drawYear: 0, // month being drawn
6959 inline: inline, // is datepicker inline or not
6960 dpDiv: (!inline ? this.dpDiv : // presentation div
6961 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
6964 /* Attach the date picker to an input field. */
6965 _connectDatepicker: function(target, inst) {
6966 var input = $(target);
6967 inst.append = $([]);
6968 inst.trigger = $([]);
6969 if (input.hasClass(this.markerClassName)) {
6972 this._attachments(input, inst);
6973 input.addClass(this.markerClassName).keydown(this._doKeyDown).
6974 keypress(this._doKeyPress).keyup(this._doKeyUp);
6975 this._autoSize(inst);
6976 $.data(target, PROP_NAME, inst);
6977 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
6978 if( inst.settings.disabled ) {
6979 this._disableDatepicker( target );
6983 /* Make attachments based on settings. */
6984 _attachments: function(input, inst) {
6985 var showOn, buttonText, buttonImage,
6986 appendText = this._get(inst, "appendText"),
6987 isRTL = this._get(inst, "isRTL");
6990 inst.append.remove();
6993 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
6994 input[isRTL ? "before" : "after"](inst.append);
6997 input.unbind("focus", this._showDatepicker);
7000 inst.trigger.remove();
7003 showOn = this._get(inst, "showOn");
7004 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
7005 input.focus(this._showDatepicker);
7007 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
7008 buttonText = this._get(inst, "buttonText");
7009 buttonImage = this._get(inst, "buttonImage");
7010 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
7011 $("<img/>").addClass(this._triggerClass).
7012 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
7013 $("<button type='button'></button>").addClass(this._triggerClass).
7014 html(!buttonImage ? buttonText : $("<img/>").attr(
7015 { src:buttonImage, alt:buttonText, title:buttonText })));
7016 input[isRTL ? "before" : "after"](inst.trigger);
7017 inst.trigger.click(function() {
7018 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
7019 $.datepicker._hideDatepicker();
7020 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
7021 $.datepicker._hideDatepicker();
7022 $.datepicker._showDatepicker(input[0]);
7024 $.datepicker._showDatepicker(input[0]);
7031 /* Apply the maximum length for the date format. */
7032 _autoSize: function(inst) {
7033 if (this._get(inst, "autoSize") && !inst.inline) {
7034 var findMax, max, maxI, i,
7035 date = new Date(2009, 12 - 1, 20), // Ensure double digits
7036 dateFormat = this._get(inst, "dateFormat");
7038 if (dateFormat.match(/[DM]/)) {
7039 findMax = function(names) {
7042 for (i = 0; i < names.length; i++) {
7043 if (names[i].length > max) {
7044 max = names[i].length;
7050 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
7051 "monthNames" : "monthNamesShort"))));
7052 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
7053 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
7055 inst.input.attr("size", this._formatDate(inst, date).length);
7059 /* Attach an inline date picker to a div. */
7060 _inlineDatepicker: function(target, inst) {
7061 var divSpan = $(target);
7062 if (divSpan.hasClass(this.markerClassName)) {
7065 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
7066 $.data(target, PROP_NAME, inst);
7067 this._setDate(inst, this._getDefaultDate(inst), true);
7068 this._updateDatepicker(inst);
7069 this._updateAlternate(inst);
7070 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
7071 if( inst.settings.disabled ) {
7072 this._disableDatepicker( target );
7074 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
7075 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
7076 inst.dpDiv.css( "display", "block" );
7079 /* Pop-up the date picker in a "dialog" box.
7080 * @param input element - ignored
7081 * @param date string or Date - the initial date to display
7082 * @param onSelect function - the function to call when a date is selected
7083 * @param settings object - update the dialog date picker instance's settings (anonymous object)
7084 * @param pos int[2] - coordinates for the dialog's position within the screen or
7085 * event - with x/y coordinates or
7086 * leave empty for default (screen centre)
7087 * @return the manager object
7089 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
7090 var id, browserWidth, browserHeight, scrollX, scrollY,
7091 inst = this._dialogInst; // internal instance
7095 id = "dp" + this.uuid;
7096 this._dialogInput = $("<input type='text' id='" + id +
7097 "' style='position: absolute; top: -100px; width: 0px;'/>");
7098 this._dialogInput.keydown(this._doKeyDown);
7099 $("body").append(this._dialogInput);
7100 inst = this._dialogInst = this._newInst(this._dialogInput, false);
7102 $.data(this._dialogInput[0], PROP_NAME, inst);
7104 extendRemove(inst.settings, settings || {});
7105 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
7106 this._dialogInput.val(date);
7108 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
7110 browserWidth = document.documentElement.clientWidth;
7111 browserHeight = document.documentElement.clientHeight;
7112 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
7113 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
7114 this._pos = // should use actual width/height below
7115 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
7118 // move input on screen for focus, but hidden behind dialog
7119 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
7120 inst.settings.onSelect = onSelect;
7121 this._inDialog = true;
7122 this.dpDiv.addClass(this._dialogClass);
7123 this._showDatepicker(this._dialogInput[0]);
7125 $.blockUI(this.dpDiv);
7127 $.data(this._dialogInput[0], PROP_NAME, inst);
7131 /* Detach a datepicker from its control.
7132 * @param target element - the target input field or division or span
7134 _destroyDatepicker: function(target) {
7136 $target = $(target),
7137 inst = $.data(target, PROP_NAME);
7139 if (!$target.hasClass(this.markerClassName)) {
7143 nodeName = target.nodeName.toLowerCase();
7144 $.removeData(target, PROP_NAME);
7145 if (nodeName === "input") {
7146 inst.append.remove();
7147 inst.trigger.remove();
7148 $target.removeClass(this.markerClassName).
7149 unbind("focus", this._showDatepicker).
7150 unbind("keydown", this._doKeyDown).
7151 unbind("keypress", this._doKeyPress).
7152 unbind("keyup", this._doKeyUp);
7153 } else if (nodeName === "div" || nodeName === "span") {
7154 $target.removeClass(this.markerClassName).empty();
7158 /* Enable the date picker to a jQuery selection.
7159 * @param target element - the target input field or division or span
7161 _enableDatepicker: function(target) {
7162 var nodeName, inline,
7163 $target = $(target),
7164 inst = $.data(target, PROP_NAME);
7166 if (!$target.hasClass(this.markerClassName)) {
7170 nodeName = target.nodeName.toLowerCase();
7171 if (nodeName === "input") {
7172 target.disabled = false;
7173 inst.trigger.filter("button").
7174 each(function() { this.disabled = false; }).end().
7175 filter("img").css({opacity: "1.0", cursor: ""});
7176 } else if (nodeName === "div" || nodeName === "span") {
7177 inline = $target.children("." + this._inlineClass);
7178 inline.children().removeClass("ui-state-disabled");
7179 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7180 prop("disabled", false);
7182 this._disabledInputs = $.map(this._disabledInputs,
7183 function(value) { return (value === target ? null : value); }); // delete entry
7186 /* Disable the date picker to a jQuery selection.
7187 * @param target element - the target input field or division or span
7189 _disableDatepicker: function(target) {
7190 var nodeName, inline,
7191 $target = $(target),
7192 inst = $.data(target, PROP_NAME);
7194 if (!$target.hasClass(this.markerClassName)) {
7198 nodeName = target.nodeName.toLowerCase();
7199 if (nodeName === "input") {
7200 target.disabled = true;
7201 inst.trigger.filter("button").
7202 each(function() { this.disabled = true; }).end().
7203 filter("img").css({opacity: "0.5", cursor: "default"});
7204 } else if (nodeName === "div" || nodeName === "span") {
7205 inline = $target.children("." + this._inlineClass);
7206 inline.children().addClass("ui-state-disabled");
7207 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7208 prop("disabled", true);
7210 this._disabledInputs = $.map(this._disabledInputs,
7211 function(value) { return (value === target ? null : value); }); // delete entry
7212 this._disabledInputs[this._disabledInputs.length] = target;
7215 /* Is the first field in a jQuery collection disabled as a datepicker?
7216 * @param target element - the target input field or division or span
7217 * @return boolean - true if disabled, false if enabled
7219 _isDisabledDatepicker: function(target) {
7223 for (var i = 0; i < this._disabledInputs.length; i++) {
7224 if (this._disabledInputs[i] === target) {
7231 /* Retrieve the instance data for the target control.
7232 * @param target element - the target input field or division or span
7233 * @return object - the associated instance data
7234 * @throws error if a jQuery problem getting data
7236 _getInst: function(target) {
7238 return $.data(target, PROP_NAME);
7241 throw "Missing instance data for this datepicker";
7245 /* Update or retrieve the settings for a date picker attached to an input field or division.
7246 * @param target element - the target input field or division or span
7247 * @param name object - the new settings to update or
7248 * string - the name of the setting to change or retrieve,
7249 * when retrieving also "all" for all instance settings or
7250 * "defaults" for all global defaults
7251 * @param value any - the new value for the setting
7252 * (omit if above is an object or to retrieve a value)
7254 _optionDatepicker: function(target, name, value) {
7255 var settings, date, minDate, maxDate,
7256 inst = this._getInst(target);
7258 if (arguments.length === 2 && typeof name === "string") {
7259 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
7260 (inst ? (name === "all" ? $.extend({}, inst.settings) :
7261 this._get(inst, name)) : null));
7264 settings = name || {};
7265 if (typeof name === "string") {
7267 settings[name] = value;
7271 if (this._curInst === inst) {
7272 this._hideDatepicker();
7275 date = this._getDateDatepicker(target, true);
7276 minDate = this._getMinMaxDate(inst, "min");
7277 maxDate = this._getMinMaxDate(inst, "max");
7278 extendRemove(inst.settings, settings);
7279 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
7280 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
7281 inst.settings.minDate = this._formatDate(inst, minDate);
7283 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
7284 inst.settings.maxDate = this._formatDate(inst, maxDate);
7286 if ( "disabled" in settings ) {
7287 if ( settings.disabled ) {
7288 this._disableDatepicker(target);
7290 this._enableDatepicker(target);
7293 this._attachments($(target), inst);
7294 this._autoSize(inst);
7295 this._setDate(inst, date);
7296 this._updateAlternate(inst);
7297 this._updateDatepicker(inst);
7301 // change method deprecated
7302 _changeDatepicker: function(target, name, value) {
7303 this._optionDatepicker(target, name, value);
7306 /* Redraw the date picker attached to an input field or division.
7307 * @param target element - the target input field or division or span
7309 _refreshDatepicker: function(target) {
7310 var inst = this._getInst(target);
7312 this._updateDatepicker(inst);
7316 /* Set the dates for a jQuery selection.
7317 * @param target element - the target input field or division or span
7318 * @param date Date - the new date
7320 _setDateDatepicker: function(target, date) {
7321 var inst = this._getInst(target);
7323 this._setDate(inst, date);
7324 this._updateDatepicker(inst);
7325 this._updateAlternate(inst);
7329 /* Get the date(s) for the first entry in a jQuery selection.
7330 * @param target element - the target input field or division or span
7331 * @param noDefault boolean - true if no default date is to be used
7332 * @return Date - the current date
7334 _getDateDatepicker: function(target, noDefault) {
7335 var inst = this._getInst(target);
7336 if (inst && !inst.inline) {
7337 this._setDateFromField(inst, noDefault);
7339 return (inst ? this._getDate(inst) : null);
7342 /* Handle keystrokes. */
7343 _doKeyDown: function(event) {
7344 var onSelect, dateStr, sel,
7345 inst = $.datepicker._getInst(event.target),
7347 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
7349 inst._keyEvent = true;
7350 if ($.datepicker._datepickerShowing) {
7351 switch (event.keyCode) {
7352 case 9: $.datepicker._hideDatepicker();
7354 break; // hide on tab out
7355 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
7356 $.datepicker._currentClass + ")", inst.dpDiv);
7358 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
7361 onSelect = $.datepicker._get(inst, "onSelect");
7363 dateStr = $.datepicker._formatDate(inst);
7365 // trigger custom callback
7366 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
7368 $.datepicker._hideDatepicker();
7371 return false; // don't submit the form
7372 case 27: $.datepicker._hideDatepicker();
7373 break; // hide on escape
7374 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
7375 -$.datepicker._get(inst, "stepBigMonths") :
7376 -$.datepicker._get(inst, "stepMonths")), "M");
7377 break; // previous month/year on page up/+ ctrl
7378 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
7379 +$.datepicker._get(inst, "stepBigMonths") :
7380 +$.datepicker._get(inst, "stepMonths")), "M");
7381 break; // next month/year on page down/+ ctrl
7382 case 35: if (event.ctrlKey || event.metaKey) {
7383 $.datepicker._clearDate(event.target);
7385 handled = event.ctrlKey || event.metaKey;
7386 break; // clear on ctrl or command +end
7387 case 36: if (event.ctrlKey || event.metaKey) {
7388 $.datepicker._gotoToday(event.target);
7390 handled = event.ctrlKey || event.metaKey;
7391 break; // current on ctrl or command +home
7392 case 37: if (event.ctrlKey || event.metaKey) {
7393 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
7395 handled = event.ctrlKey || event.metaKey;
7396 // -1 day on ctrl or command +left
7397 if (event.originalEvent.altKey) {
7398 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
7399 -$.datepicker._get(inst, "stepBigMonths") :
7400 -$.datepicker._get(inst, "stepMonths")), "M");
7402 // next month/year on alt +left on Mac
7404 case 38: if (event.ctrlKey || event.metaKey) {
7405 $.datepicker._adjustDate(event.target, -7, "D");
7407 handled = event.ctrlKey || event.metaKey;
7408 break; // -1 week on ctrl or command +up
7409 case 39: if (event.ctrlKey || event.metaKey) {
7410 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
7412 handled = event.ctrlKey || event.metaKey;
7413 // +1 day on ctrl or command +right
7414 if (event.originalEvent.altKey) {
7415 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
7416 +$.datepicker._get(inst, "stepBigMonths") :
7417 +$.datepicker._get(inst, "stepMonths")), "M");
7419 // next month/year on alt +right
7421 case 40: if (event.ctrlKey || event.metaKey) {
7422 $.datepicker._adjustDate(event.target, +7, "D");
7424 handled = event.ctrlKey || event.metaKey;
7425 break; // +1 week on ctrl or command +down
7426 default: handled = false;
7428 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
7429 $.datepicker._showDatepicker(this);
7435 event.preventDefault();
7436 event.stopPropagation();
7440 /* Filter entered characters - based on date format. */
7441 _doKeyPress: function(event) {
7443 inst = $.datepicker._getInst(event.target);
7445 if ($.datepicker._get(inst, "constrainInput")) {
7446 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
7447 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
7448 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
7452 /* Synchronise manual entry and field/alternate field. */
7453 _doKeyUp: function(event) {
7455 inst = $.datepicker._getInst(event.target);
7457 if (inst.input.val() !== inst.lastVal) {
7459 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
7460 (inst.input ? inst.input.val() : null),
7461 $.datepicker._getFormatConfig(inst));
7463 if (date) { // only if valid
7464 $.datepicker._setDateFromField(inst);
7465 $.datepicker._updateAlternate(inst);
7466 $.datepicker._updateDatepicker(inst);
7475 /* Pop-up the date picker for a given input field.
7476 * If false returned from beforeShow event handler do not show.
7477 * @param input element - the input field attached to the date picker or
7478 * event - if triggered by focus
7480 _showDatepicker: function(input) {
7481 input = input.target || input;
7482 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
7483 input = $("input", input.parentNode)[0];
7486 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
7490 var inst, beforeShow, beforeShowSettings, isFixed,
7491 offset, showAnim, duration;
7493 inst = $.datepicker._getInst(input);
7494 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
7495 $.datepicker._curInst.dpDiv.stop(true, true);
7496 if ( inst && $.datepicker._datepickerShowing ) {
7497 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
7501 beforeShow = $.datepicker._get(inst, "beforeShow");
7502 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
7503 if(beforeShowSettings === false){
7506 extendRemove(inst.settings, beforeShowSettings);
7508 inst.lastVal = null;
7509 $.datepicker._lastInput = input;
7510 $.datepicker._setDateFromField(inst);
7512 if ($.datepicker._inDialog) { // hide cursor
7515 if (!$.datepicker._pos) { // position below input
7516 $.datepicker._pos = $.datepicker._findPos(input);
7517 $.datepicker._pos[1] += input.offsetHeight; // add the height
7521 $(input).parents().each(function() {
7522 isFixed |= $(this).css("position") === "fixed";
7526 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
7527 $.datepicker._pos = null;
7528 //to avoid flashes on Firefox
7530 // determine sizing offscreen
7531 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
7532 $.datepicker._updateDatepicker(inst);
7533 // fix width for dynamic number of date pickers
7534 // and adjust position before showing
7535 offset = $.datepicker._checkOffset(inst, offset, isFixed);
7536 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
7537 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
7538 left: offset.left + "px", top: offset.top + "px"});
7541 showAnim = $.datepicker._get(inst, "showAnim");
7542 duration = $.datepicker._get(inst, "duration");
7543 inst.dpDiv.zIndex($(input).zIndex()+1);
7544 $.datepicker._datepickerShowing = true;
7546 if ( $.effects && $.effects.effect[ showAnim ] ) {
7547 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
7549 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
7552 if ( $.datepicker._shouldFocusInput( inst ) ) {
7556 $.datepicker._curInst = inst;
7560 /* Generate the date picker content. */
7561 _updateDatepicker: function(inst) {
7562 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
7563 instActive = inst; // for delegate hover events
7564 inst.dpDiv.empty().append(this._generateHTML(inst));
7565 this._attachHandlers(inst);
7566 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
7569 numMonths = this._getNumberOfMonths(inst),
7570 cols = numMonths[1],
7573 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
7575 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
7577 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
7578 "Class"]("ui-datepicker-multi");
7579 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
7580 "Class"]("ui-datepicker-rtl");
7582 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
7586 // deffered render of the years select (to avoid flashes on Firefox)
7587 if( inst.yearshtml ){
7588 origyearshtml = inst.yearshtml;
7589 setTimeout(function(){
7590 //assure that inst.yearshtml didn't change.
7591 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
7592 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
7594 origyearshtml = inst.yearshtml = null;
7599 // #6694 - don't focus the input if it's already focused
7600 // this breaks the change event in IE
7601 // Support: IE and jQuery <1.9
7602 _shouldFocusInput: function( inst ) {
7603 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
7606 /* Check positioning to remain on screen. */
7607 _checkOffset: function(inst, offset, isFixed) {
7608 var dpWidth = inst.dpDiv.outerWidth(),
7609 dpHeight = inst.dpDiv.outerHeight(),
7610 inputWidth = inst.input ? inst.input.outerWidth() : 0,
7611 inputHeight = inst.input ? inst.input.outerHeight() : 0,
7612 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
7613 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
7615 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
7616 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
7617 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
7619 // now check if datepicker is showing outside window viewport - move to a better place if so.
7620 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
7621 Math.abs(offset.left + dpWidth - viewWidth) : 0);
7622 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
7623 Math.abs(dpHeight + inputHeight) : 0);
7628 /* Find an object's position on the screen. */
7629 _findPos: function(obj) {
7631 inst = this._getInst(obj),
7632 isRTL = this._get(inst, "isRTL");
7634 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
7635 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
7638 position = $(obj).offset();
7639 return [position.left, position.top];
7642 /* Hide the date picker from view.
7643 * @param input element - the input field attached to the date picker
7645 _hideDatepicker: function(input) {
7646 var showAnim, duration, postProcess, onClose,
7647 inst = this._curInst;
7649 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
7653 if (this._datepickerShowing) {
7654 showAnim = this._get(inst, "showAnim");
7655 duration = this._get(inst, "duration");
7656 postProcess = function() {
7657 $.datepicker._tidyDialog(inst);
7660 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
7661 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
7662 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
7664 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
7665 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
7671 this._datepickerShowing = false;
7673 onClose = this._get(inst, "onClose");
7675 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
7678 this._lastInput = null;
7679 if (this._inDialog) {
7680 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
7683 $("body").append(this.dpDiv);
7686 this._inDialog = false;
7690 /* Tidy up after a dialog display. */
7691 _tidyDialog: function(inst) {
7692 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
7695 /* Close date picker if clicked elsewhere. */
7696 _checkExternalClick: function(event) {
7697 if (!$.datepicker._curInst) {
7701 var $target = $(event.target),
7702 inst = $.datepicker._getInst($target[0]);
7704 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
7705 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
7706 !$target.hasClass($.datepicker.markerClassName) &&
7707 !$target.closest("." + $.datepicker._triggerClass).length &&
7708 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
7709 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
7710 $.datepicker._hideDatepicker();
7714 /* Adjust one of the date sub-fields. */
7715 _adjustDate: function(id, offset, period) {
7717 inst = this._getInst(target[0]);
7719 if (this._isDisabledDatepicker(target[0])) {
7722 this._adjustInstDate(inst, offset +
7723 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
7725 this._updateDatepicker(inst);
7728 /* Action for current link. */
7729 _gotoToday: function(id) {
7732 inst = this._getInst(target[0]);
7734 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
7735 inst.selectedDay = inst.currentDay;
7736 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
7737 inst.drawYear = inst.selectedYear = inst.currentYear;
7740 inst.selectedDay = date.getDate();
7741 inst.drawMonth = inst.selectedMonth = date.getMonth();
7742 inst.drawYear = inst.selectedYear = date.getFullYear();
7744 this._notifyChange(inst);
7745 this._adjustDate(target);
7748 /* Action for selecting a new month/year. */
7749 _selectMonthYear: function(id, select, period) {
7751 inst = this._getInst(target[0]);
7753 inst["selected" + (period === "M" ? "Month" : "Year")] =
7754 inst["draw" + (period === "M" ? "Month" : "Year")] =
7755 parseInt(select.options[select.selectedIndex].value,10);
7757 this._notifyChange(inst);
7758 this._adjustDate(target);
7761 /* Action for selecting a day. */
7762 _selectDay: function(id, month, year, td) {
7766 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
7770 inst = this._getInst(target[0]);
7771 inst.selectedDay = inst.currentDay = $("a", td).html();
7772 inst.selectedMonth = inst.currentMonth = month;
7773 inst.selectedYear = inst.currentYear = year;
7774 this._selectDate(id, this._formatDate(inst,
7775 inst.currentDay, inst.currentMonth, inst.currentYear));
7778 /* Erase the input field and hide the date picker. */
7779 _clearDate: function(id) {
7781 this._selectDate(target, "");
7784 /* Update the input field with the selected date. */
7785 _selectDate: function(id, dateStr) {
7788 inst = this._getInst(target[0]);
7790 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
7792 inst.input.val(dateStr);
7794 this._updateAlternate(inst);
7796 onSelect = this._get(inst, "onSelect");
7798 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
7799 } else if (inst.input) {
7800 inst.input.trigger("change"); // fire the change event
7804 this._updateDatepicker(inst);
7806 this._hideDatepicker();
7807 this._lastInput = inst.input[0];
7808 if (typeof(inst.input[0]) !== "object") {
7809 inst.input.focus(); // restore focus
7811 this._lastInput = null;
7815 /* Update any alternate field to synchronise with the main field. */
7816 _updateAlternate: function(inst) {
7817 var altFormat, date, dateStr,
7818 altField = this._get(inst, "altField");
7820 if (altField) { // update alternate field too
7821 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
7822 date = this._getDate(inst);
7823 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
7824 $(altField).each(function() { $(this).val(dateStr); });
7828 /* Set as beforeShowDay function to prevent selection of weekends.
7829 * @param date Date - the date to customise
7830 * @return [boolean, string] - is this date selectable?, what is its CSS class?
7832 noWeekends: function(date) {
7833 var day = date.getDay();
7834 return [(day > 0 && day < 6), ""];
7837 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
7838 * @param date Date - the date to get the week for
7839 * @return number - the number of the week within the year that contains this date
7841 iso8601Week: function(date) {
7843 checkDate = new Date(date.getTime());
7845 // Find Thursday of this week starting on Monday
7846 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
7848 time = checkDate.getTime();
7849 checkDate.setMonth(0); // Compare with Jan 1
7850 checkDate.setDate(1);
7851 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
7854 /* Parse a string value into a date object.
7855 * See formatDate below for the possible formats.
7857 * @param format string - the expected format of the date
7858 * @param value string - the date in the above format
7859 * @param settings Object - attributes include:
7860 * shortYearCutoff number - the cutoff year for determining the century (optional)
7861 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
7862 * dayNames string[7] - names of the days from Sunday (optional)
7863 * monthNamesShort string[12] - abbreviated names of the months (optional)
7864 * monthNames string[12] - names of the months (optional)
7865 * @return Date - the extracted date value or null if value is blank
7867 parseDate: function (format, value, settings) {
7868 if (format == null || value == null) {
7869 throw "Invalid arguments";
7872 value = (typeof value === "object" ? value.toString() : value + "");
7877 var iFormat, dim, extra,
7879 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
7880 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
7881 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
7882 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
7883 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
7884 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
7885 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
7892 // Check whether a format character is doubled
7893 lookAhead = function(match) {
7894 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
7900 // Extract a number from the string value
7901 getNumber = function(match) {
7902 var isDoubled = lookAhead(match),
7903 size = (match === "@" ? 14 : (match === "!" ? 20 :
7904 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
7905 digits = new RegExp("^\\d{1," + size + "}"),
7906 num = value.substring(iValue).match(digits);
7908 throw "Missing number at position " + iValue;
7910 iValue += num[0].length;
7911 return parseInt(num[0], 10);
7913 // Extract a name from the string value and convert to an index
7914 getName = function(match, shortNames, longNames) {
7916 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
7918 }).sort(function (a, b) {
7919 return -(a[1].length - b[1].length);
7922 $.each(names, function (i, pair) {
7924 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
7926 iValue += name.length;
7933 throw "Unknown name at position " + iValue;
7936 // Confirm that a literal character matches the string value
7937 checkLiteral = function() {
7938 if (value.charAt(iValue) !== format.charAt(iFormat)) {
7939 throw "Unexpected literal at position " + iValue;
7944 for (iFormat = 0; iFormat < format.length; iFormat++) {
7946 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
7952 switch (format.charAt(iFormat)) {
7954 day = getNumber("d");
7957 getName("D", dayNamesShort, dayNames);
7960 doy = getNumber("o");
7963 month = getNumber("m");
7966 month = getName("M", monthNamesShort, monthNames);
7969 year = getNumber("y");
7972 date = new Date(getNumber("@"));
7973 year = date.getFullYear();
7974 month = date.getMonth() + 1;
7975 day = date.getDate();
7978 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
7979 year = date.getFullYear();
7980 month = date.getMonth() + 1;
7981 day = date.getDate();
7984 if (lookAhead("'")){
7996 if (iValue < value.length){
7997 extra = value.substr(iValue);
7998 if (!/^\s+/.test(extra)) {
7999 throw "Extra/unparsed characters found in date: " + extra;
8004 year = new Date().getFullYear();
8005 } else if (year < 100) {
8006 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
8007 (year <= shortYearCutoff ? 0 : -100);
8014 dim = this._getDaysInMonth(year, month - 1);
8023 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
8024 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
8025 throw "Invalid date"; // E.g. 31/02/00
8030 /* Standard date formats. */
8031 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
8032 COOKIE: "D, dd M yy",
8033 ISO_8601: "yy-mm-dd",
8034 RFC_822: "D, d M y",
8035 RFC_850: "DD, dd-M-y",
8036 RFC_1036: "D, d M y",
8037 RFC_1123: "D, d M yy",
8038 RFC_2822: "D, d M yy",
8039 RSS: "D, d M y", // RFC 822
8042 W3C: "yy-mm-dd", // ISO 8601
8044 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
8045 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
8047 /* Format a date object into a string value.
8048 * The format can be combinations of the following:
8049 * d - day of month (no leading zero)
8050 * dd - day of month (two digit)
8051 * o - day of year (no leading zeros)
8052 * oo - day of year (three digit)
8053 * D - day name short
8054 * DD - day name long
8055 * m - month of year (no leading zero)
8056 * mm - month of year (two digit)
8057 * M - month name short
8058 * MM - month name long
8059 * y - year (two digit)
8060 * yy - year (four digit)
8061 * @ - Unix timestamp (ms since 01/01/1970)
8062 * ! - Windows ticks (100ns since 01/01/0001)
8063 * "..." - literal text
8066 * @param format string - the desired format of the date
8067 * @param date Date - the date value to format
8068 * @param settings Object - attributes include:
8069 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8070 * dayNames string[7] - names of the days from Sunday (optional)
8071 * monthNamesShort string[12] - abbreviated names of the months (optional)
8072 * monthNames string[12] - names of the months (optional)
8073 * @return string - the date in the above format
8075 formatDate: function (format, date, settings) {
8081 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8082 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8083 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8084 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8085 // Check whether a format character is doubled
8086 lookAhead = function(match) {
8087 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8093 // Format a number, with leading zero if necessary
8094 formatNumber = function(match, value, len) {
8095 var num = "" + value;
8096 if (lookAhead(match)) {
8097 while (num.length < len) {
8103 // Format a name, short or long as requested
8104 formatName = function(match, value, shortNames, longNames) {
8105 return (lookAhead(match) ? longNames[value] : shortNames[value]);
8111 for (iFormat = 0; iFormat < format.length; iFormat++) {
8113 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8116 output += format.charAt(iFormat);
8119 switch (format.charAt(iFormat)) {
8121 output += formatNumber("d", date.getDate(), 2);
8124 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
8127 output += formatNumber("o",
8128 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
8131 output += formatNumber("m", date.getMonth() + 1, 2);
8134 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
8137 output += (lookAhead("y") ? date.getFullYear() :
8138 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
8141 output += date.getTime();
8144 output += date.getTime() * 10000 + this._ticksTo1970;
8147 if (lookAhead("'")) {
8154 output += format.charAt(iFormat);
8162 /* Extract all possible characters from the date format. */
8163 _possibleChars: function (format) {
8167 // Check whether a format character is doubled
8168 lookAhead = function(match) {
8169 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8176 for (iFormat = 0; iFormat < format.length; iFormat++) {
8178 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8181 chars += format.charAt(iFormat);
8184 switch (format.charAt(iFormat)) {
8185 case "d": case "m": case "y": case "@":
8186 chars += "0123456789";
8189 return null; // Accept anything
8191 if (lookAhead("'")) {
8198 chars += format.charAt(iFormat);
8205 /* Get a setting value, defaulting if necessary. */
8206 _get: function(inst, name) {
8207 return inst.settings[name] !== undefined ?
8208 inst.settings[name] : this._defaults[name];
8211 /* Parse existing date and initialise date picker. */
8212 _setDateFromField: function(inst, noDefault) {
8213 if (inst.input.val() === inst.lastVal) {
8217 var dateFormat = this._get(inst, "dateFormat"),
8218 dates = inst.lastVal = inst.input ? inst.input.val() : null,
8219 defaultDate = this._getDefaultDate(inst),
8221 settings = this._getFormatConfig(inst);
8224 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
8226 dates = (noDefault ? "" : dates);
8228 inst.selectedDay = date.getDate();
8229 inst.drawMonth = inst.selectedMonth = date.getMonth();
8230 inst.drawYear = inst.selectedYear = date.getFullYear();
8231 inst.currentDay = (dates ? date.getDate() : 0);
8232 inst.currentMonth = (dates ? date.getMonth() : 0);
8233 inst.currentYear = (dates ? date.getFullYear() : 0);
8234 this._adjustInstDate(inst);
8237 /* Retrieve the default date shown on opening. */
8238 _getDefaultDate: function(inst) {
8239 return this._restrictMinMax(inst,
8240 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
8243 /* A date may be specified as an exact value or a relative one. */
8244 _determineDate: function(inst, date, defaultDate) {
8245 var offsetNumeric = function(offset) {
8246 var date = new Date();
8247 date.setDate(date.getDate() + offset);
8250 offsetString = function(offset) {
8252 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
8253 offset, $.datepicker._getFormatConfig(inst));
8259 var date = (offset.toLowerCase().match(/^c/) ?
8260 $.datepicker._getDate(inst) : null) || new Date(),
8261 year = date.getFullYear(),
8262 month = date.getMonth(),
8263 day = date.getDate(),
8264 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
8265 matches = pattern.exec(offset);
8268 switch (matches[2] || "d") {
8269 case "d" : case "D" :
8270 day += parseInt(matches[1],10); break;
8271 case "w" : case "W" :
8272 day += parseInt(matches[1],10) * 7; break;
8273 case "m" : case "M" :
8274 month += parseInt(matches[1],10);
8275 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
8277 case "y": case "Y" :
8278 year += parseInt(matches[1],10);
8279 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
8282 matches = pattern.exec(offset);
8284 return new Date(year, month, day);
8286 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
8287 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
8289 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
8291 newDate.setHours(0);
8292 newDate.setMinutes(0);
8293 newDate.setSeconds(0);
8294 newDate.setMilliseconds(0);
8296 return this._daylightSavingAdjust(newDate);
8299 /* Handle switch to/from daylight saving.
8300 * Hours may be non-zero on daylight saving cut-over:
8301 * > 12 when midnight changeover, but then cannot generate
8302 * midnight datetime, so jump to 1AM, otherwise reset.
8303 * @param date (Date) the date to check
8304 * @return (Date) the corrected date
8306 _daylightSavingAdjust: function(date) {
8310 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
8314 /* Set the date(s) directly. */
8315 _setDate: function(inst, date, noChange) {
8317 origMonth = inst.selectedMonth,
8318 origYear = inst.selectedYear,
8319 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
8321 inst.selectedDay = inst.currentDay = newDate.getDate();
8322 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
8323 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
8324 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
8325 this._notifyChange(inst);
8327 this._adjustInstDate(inst);
8329 inst.input.val(clear ? "" : this._formatDate(inst));
8333 /* Retrieve the date(s) directly. */
8334 _getDate: function(inst) {
8335 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
8336 this._daylightSavingAdjust(new Date(
8337 inst.currentYear, inst.currentMonth, inst.currentDay)));
8341 /* Attach the onxxx handlers. These are declared statically so
8342 * they work with static code transformers like Caja.
8344 _attachHandlers: function(inst) {
8345 var stepMonths = this._get(inst, "stepMonths"),
8346 id = "#" + inst.id.replace( /\\\\/g, "\\" );
8347 inst.dpDiv.find("[data-handler]").map(function () {
8350 $.datepicker._adjustDate(id, -stepMonths, "M");
8353 $.datepicker._adjustDate(id, +stepMonths, "M");
8356 $.datepicker._hideDatepicker();
8358 today: function () {
8359 $.datepicker._gotoToday(id);
8361 selectDay: function () {
8362 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
8365 selectMonth: function () {
8366 $.datepicker._selectMonthYear(id, this, "M");
8369 selectYear: function () {
8370 $.datepicker._selectMonthYear(id, this, "Y");
8374 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
8378 /* Generate the HTML for the current state of the date picker. */
8379 _generateHTML: function(inst) {
8380 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
8381 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
8382 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
8383 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
8384 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
8385 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
8386 tempDate = new Date(),
8387 today = this._daylightSavingAdjust(
8388 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
8389 isRTL = this._get(inst, "isRTL"),
8390 showButtonPanel = this._get(inst, "showButtonPanel"),
8391 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
8392 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
8393 numMonths = this._getNumberOfMonths(inst),
8394 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
8395 stepMonths = this._get(inst, "stepMonths"),
8396 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
8397 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
8398 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
8399 minDate = this._getMinMaxDate(inst, "min"),
8400 maxDate = this._getMinMaxDate(inst, "max"),
8401 drawMonth = inst.drawMonth - showCurrentAtPos,
8402 drawYear = inst.drawYear;
8404 if (drawMonth < 0) {
8409 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
8410 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
8411 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
8412 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
8414 if (drawMonth < 0) {
8420 inst.drawMonth = drawMonth;
8421 inst.drawYear = drawYear;
8423 prevText = this._get(inst, "prevText");
8424 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
8425 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
8426 this._getFormatConfig(inst)));
8428 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
8429 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
8430 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
8431 (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>"));
8433 nextText = this._get(inst, "nextText");
8434 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
8435 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
8436 this._getFormatConfig(inst)));
8438 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
8439 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
8440 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
8441 (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>"));
8443 currentText = this._get(inst, "currentText");
8444 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
8445 currentText = (!navigationAsDateFormat ? currentText :
8446 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
8448 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'>" +
8449 this._get(inst, "closeText") + "</button>" : "");
8451 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
8452 (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'" +
8453 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
8455 firstDay = parseInt(this._get(inst, "firstDay"),10);
8456 firstDay = (isNaN(firstDay) ? 0 : firstDay);
8458 showWeek = this._get(inst, "showWeek");
8459 dayNames = this._get(inst, "dayNames");
8460 dayNamesMin = this._get(inst, "dayNamesMin");
8461 monthNames = this._get(inst, "monthNames");
8462 monthNamesShort = this._get(inst, "monthNamesShort");
8463 beforeShowDay = this._get(inst, "beforeShowDay");
8464 showOtherMonths = this._get(inst, "showOtherMonths");
8465 selectOtherMonths = this._get(inst, "selectOtherMonths");
8466 defaultDate = this._getDefaultDate(inst);
8469 for (row = 0; row < numMonths[0]; row++) {
8472 for (col = 0; col < numMonths[1]; col++) {
8473 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
8474 cornerClass = " ui-corner-all";
8477 calender += "<div class='ui-datepicker-group";
8478 if (numMonths[1] > 1) {
8480 case 0: calender += " ui-datepicker-group-first";
8481 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
8482 case numMonths[1]-1: calender += " ui-datepicker-group-last";
8483 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
8484 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
8489 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
8490 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
8491 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
8492 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
8493 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
8494 "</div><table class='ui-datepicker-calendar'><thead>" +
8496 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
8497 for (dow = 0; dow < 7; dow++) { // days of the week
8498 day = (dow + firstDay) % 7;
8499 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
8500 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
8502 calender += thead + "</tr></thead><tbody>";
8503 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
8504 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
8505 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
8507 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
8508 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
8509 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
8510 this.maxRows = numRows;
8511 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
8512 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
8514 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
8515 this._get(inst, "calculateWeek")(printDate) + "</td>");
8516 for (dow = 0; dow < 7; dow++) { // create date picker days
8517 daySettings = (beforeShowDay ?
8518 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
8519 otherMonth = (printDate.getMonth() !== drawMonth);
8520 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
8521 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
8522 tbody += "<td class='" +
8523 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
8524 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
8525 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
8526 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
8527 // or defaultDate is current printedDate and defaultDate is selectedDate
8528 " " + this._dayOverClass : "") + // highlight selected day
8529 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
8530 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
8531 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
8532 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
8533 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
8534 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
8535 (otherMonth && !showOtherMonths ? " " : // display for other months
8536 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
8537 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
8538 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
8539 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
8540 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
8541 printDate.setDate(printDate.getDate() + 1);
8542 printDate = this._daylightSavingAdjust(printDate);
8544 calender += tbody + "</tr>";
8547 if (drawMonth > 11) {
8551 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
8552 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
8557 html += buttonPanel;
8558 inst._keyEvent = false;
8562 /* Generate the month and year header. */
8563 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
8564 secondary, monthNames, monthNamesShort) {
8566 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
8567 changeMonth = this._get(inst, "changeMonth"),
8568 changeYear = this._get(inst, "changeYear"),
8569 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
8570 html = "<div class='ui-datepicker-title'>",
8574 if (secondary || !changeMonth) {
8575 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
8577 inMinYear = (minDate && minDate.getFullYear() === drawYear);
8578 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
8579 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
8580 for ( month = 0; month < 12; month++) {
8581 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
8582 monthHtml += "<option value='" + month + "'" +
8583 (month === drawMonth ? " selected='selected'" : "") +
8584 ">" + monthNamesShort[month] + "</option>";
8587 monthHtml += "</select>";
8590 if (!showMonthAfterYear) {
8591 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
8595 if ( !inst.yearshtml ) {
8596 inst.yearshtml = "";
8597 if (secondary || !changeYear) {
8598 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
8600 // determine range of years to display
8601 years = this._get(inst, "yearRange").split(":");
8602 thisYear = new Date().getFullYear();
8603 determineYear = function(value) {
8604 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
8605 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
8606 parseInt(value, 10)));
8607 return (isNaN(year) ? thisYear : year);
8609 year = determineYear(years[0]);
8610 endYear = Math.max(year, determineYear(years[1] || ""));
8611 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
8612 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
8613 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
8614 for (; year <= endYear; year++) {
8615 inst.yearshtml += "<option value='" + year + "'" +
8616 (year === drawYear ? " selected='selected'" : "") +
8617 ">" + year + "</option>";
8619 inst.yearshtml += "</select>";
8621 html += inst.yearshtml;
8622 inst.yearshtml = null;
8626 html += this._get(inst, "yearSuffix");
8627 if (showMonthAfterYear) {
8628 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
8630 html += "</div>"; // Close datepicker_header
8634 /* Adjust one of the date sub-fields. */
8635 _adjustInstDate: function(inst, offset, period) {
8636 var year = inst.drawYear + (period === "Y" ? offset : 0),
8637 month = inst.drawMonth + (period === "M" ? offset : 0),
8638 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
8639 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
8641 inst.selectedDay = date.getDate();
8642 inst.drawMonth = inst.selectedMonth = date.getMonth();
8643 inst.drawYear = inst.selectedYear = date.getFullYear();
8644 if (period === "M" || period === "Y") {
8645 this._notifyChange(inst);
8649 /* Ensure a date is within any min/max bounds. */
8650 _restrictMinMax: function(inst, date) {
8651 var minDate = this._getMinMaxDate(inst, "min"),
8652 maxDate = this._getMinMaxDate(inst, "max"),
8653 newDate = (minDate && date < minDate ? minDate : date);
8654 return (maxDate && newDate > maxDate ? maxDate : newDate);
8657 /* Notify change of month/year. */
8658 _notifyChange: function(inst) {
8659 var onChange = this._get(inst, "onChangeMonthYear");
8661 onChange.apply((inst.input ? inst.input[0] : null),
8662 [inst.selectedYear, inst.selectedMonth + 1, inst]);
8666 /* Determine the number of months to show. */
8667 _getNumberOfMonths: function(inst) {
8668 var numMonths = this._get(inst, "numberOfMonths");
8669 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
8672 /* Determine the current maximum date - ensure no time components are set. */
8673 _getMinMaxDate: function(inst, minMax) {
8674 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
8677 /* Find the number of days in a given month. */
8678 _getDaysInMonth: function(year, month) {
8679 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
8682 /* Find the day of the week of the first of a month. */
8683 _getFirstDayOfMonth: function(year, month) {
8684 return new Date(year, month, 1).getDay();
8687 /* Determines if we should allow a "next/prev" month display change. */
8688 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
8689 var numMonths = this._getNumberOfMonths(inst),
8690 date = this._daylightSavingAdjust(new Date(curYear,
8691 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
8694 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
8696 return this._isInRange(inst, date);
8699 /* Is the given date in the accepted range? */
8700 _isInRange: function(inst, date) {
8701 var yearSplit, currentYear,
8702 minDate = this._getMinMaxDate(inst, "min"),
8703 maxDate = this._getMinMaxDate(inst, "max"),
8706 years = this._get(inst, "yearRange");
8708 yearSplit = years.split(":");
8709 currentYear = new Date().getFullYear();
8710 minYear = parseInt(yearSplit[0], 10);
8711 maxYear = parseInt(yearSplit[1], 10);
8712 if ( yearSplit[0].match(/[+\-].*/) ) {
8713 minYear += currentYear;
8715 if ( yearSplit[1].match(/[+\-].*/) ) {
8716 maxYear += currentYear;
8720 return ((!minDate || date.getTime() >= minDate.getTime()) &&
8721 (!maxDate || date.getTime() <= maxDate.getTime()) &&
8722 (!minYear || date.getFullYear() >= minYear) &&
8723 (!maxYear || date.getFullYear() <= maxYear));
8726 /* Provide the configuration settings for formatting/parsing. */
8727 _getFormatConfig: function(inst) {
8728 var shortYearCutoff = this._get(inst, "shortYearCutoff");
8729 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
8730 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
8731 return {shortYearCutoff: shortYearCutoff,
8732 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
8733 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
8736 /* Format the given date for display. */
8737 _formatDate: function(inst, day, month, year) {
8739 inst.currentDay = inst.selectedDay;
8740 inst.currentMonth = inst.selectedMonth;
8741 inst.currentYear = inst.selectedYear;
8743 var date = (day ? (typeof day === "object" ? day :
8744 this._daylightSavingAdjust(new Date(year, month, day))) :
8745 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
8746 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
8751 * Bind hover events for datepicker elements.
8752 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
8753 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
8755 function bindHover(dpDiv) {
8756 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
8757 return dpDiv.delegate(selector, "mouseout", function() {
8758 $(this).removeClass("ui-state-hover");
8759 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
8760 $(this).removeClass("ui-datepicker-prev-hover");
8762 if (this.className.indexOf("ui-datepicker-next") !== -1) {
8763 $(this).removeClass("ui-datepicker-next-hover");
8766 .delegate(selector, "mouseover", function(){
8767 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
8768 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
8769 $(this).addClass("ui-state-hover");
8770 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
8771 $(this).addClass("ui-datepicker-prev-hover");
8773 if (this.className.indexOf("ui-datepicker-next") !== -1) {
8774 $(this).addClass("ui-datepicker-next-hover");
8780 /* jQuery extend now ignores nulls! */
8781 function extendRemove(target, props) {
8782 $.extend(target, props);
8783 for (var name in props) {
8784 if (props[name] == null) {
8785 target[name] = props[name];
8791 /* Invoke the datepicker functionality.
8792 @param options string - a command, optionally followed by additional parameters or
8793 Object - settings for attaching new datepicker functionality
8794 @return jQuery object */
8795 $.fn.datepicker = function(options){
8797 /* Verify an empty collection wasn't passed - Fixes #6976 */
8798 if ( !this.length ) {
8802 /* Initialise the date picker. */
8803 if (!$.datepicker.initialized) {
8804 $(document).mousedown($.datepicker._checkExternalClick);
8805 $.datepicker.initialized = true;
8808 /* Append datepicker main container to body if not exist. */
8809 if ($("#"+$.datepicker._mainDivId).length === 0) {
8810 $("body").append($.datepicker.dpDiv);
8813 var otherArgs = Array.prototype.slice.call(arguments, 1);
8814 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
8815 return $.datepicker["_" + options + "Datepicker"].
8816 apply($.datepicker, [this[0]].concat(otherArgs));
8818 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
8819 return $.datepicker["_" + options + "Datepicker"].
8820 apply($.datepicker, [this[0]].concat(otherArgs));
8822 return this.each(function() {
8823 typeof options === "string" ?
8824 $.datepicker["_" + options + "Datepicker"].
8825 apply($.datepicker, [this].concat(otherArgs)) :
8826 $.datepicker._attachDatepicker(this, options);
8830 $.datepicker = new Datepicker(); // singleton instance
8831 $.datepicker.initialized = false;
8832 $.datepicker.uuid = new Date().getTime();
8833 $.datepicker.version = "1.10.3";
8836 (function( $, undefined ) {
8838 var sizeRelatedOptions = {
8847 resizableRelatedOptions = {
8854 $.widget( "ui.dialog", {
8860 closeOnEscape: true,
8876 // Ensure the titlebar is always visible
8877 using: function( pos ) {
8878 var topOffset = $( this ).css( pos ).offset().top;
8879 if ( topOffset < 0 ) {
8880 $( this ).css( "top", pos.top - topOffset );
8902 _create: function() {
8903 this.originalCss = {
8904 display: this.element[0].style.display,
8905 width: this.element[0].style.width,
8906 minHeight: this.element[0].style.minHeight,
8907 maxHeight: this.element[0].style.maxHeight,
8908 height: this.element[0].style.height
8910 this.originalPosition = {
8911 parent: this.element.parent(),
8912 index: this.element.parent().children().index( this.element )
8914 this.originalTitle = this.element.attr("title");
8915 this.options.title = this.options.title || this.originalTitle;
8917 this._createWrapper();
8921 .removeAttr("title")
8922 .addClass("ui-dialog-content ui-widget-content")
8923 .appendTo( this.uiDialog );
8925 this._createTitlebar();
8926 this._createButtonPane();
8928 if ( this.options.draggable && $.fn.draggable ) {
8929 this._makeDraggable();
8931 if ( this.options.resizable && $.fn.resizable ) {
8932 this._makeResizable();
8935 this._isOpen = false;
8939 if ( this.options.autoOpen ) {
8944 _appendTo: function() {
8945 var element = this.options.appendTo;
8946 if ( element && (element.jquery || element.nodeType) ) {
8947 return $( element );
8949 return this.document.find( element || "body" ).eq( 0 );
8952 _destroy: function() {
8954 originalPosition = this.originalPosition;
8956 this._destroyOverlay();
8960 .removeClass("ui-dialog-content ui-widget-content")
8961 .css( this.originalCss )
8962 // Without detaching first, the following becomes really slow
8965 this.uiDialog.stop( true, true ).remove();
8967 if ( this.originalTitle ) {
8968 this.element.attr( "title", this.originalTitle );
8971 next = originalPosition.parent.children().eq( originalPosition.index );
8972 // Don't try to place the dialog next to itself (#8613)
8973 if ( next.length && next[0] !== this.element[0] ) {
8974 next.before( this.element );
8976 originalPosition.parent.append( this.element );
8980 widget: function() {
8981 return this.uiDialog;
8987 close: function( event ) {
8990 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
8994 this._isOpen = false;
8995 this._destroyOverlay();
8997 if ( !this.opener.filter(":focusable").focus().length ) {
8998 // Hiding a focused element doesn't trigger blur in WebKit
8999 // so in case we have nothing to focus on, explicitly blur the active element
9000 // https://bugs.webkit.org/show_bug.cgi?id=47182
9001 $( this.document[0].activeElement ).blur();
9004 this._hide( this.uiDialog, this.options.hide, function() {
9005 that._trigger( "close", event );
9009 isOpen: function() {
9010 return this._isOpen;
9013 moveToTop: function() {
9017 _moveToTop: function( event, silent ) {
9018 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
9019 if ( moved && !silent ) {
9020 this._trigger( "focus", event );
9027 if ( this._isOpen ) {
9028 if ( this._moveToTop() ) {
9029 this._focusTabbable();
9034 this._isOpen = true;
9035 this.opener = $( this.document[0].activeElement );
9039 this._createOverlay();
9040 this._moveToTop( null, true );
9041 this._show( this.uiDialog, this.options.show, function() {
9042 that._focusTabbable();
9043 that._trigger("focus");
9046 this._trigger("open");
9049 _focusTabbable: function() {
9050 // Set focus to the first match:
9051 // 1. First element inside the dialog matching [autofocus]
9052 // 2. Tabbable element inside the content element
9053 // 3. Tabbable element inside the buttonpane
9054 // 4. The close button
9055 // 5. The dialog itself
9056 var hasFocus = this.element.find("[autofocus]");
9057 if ( !hasFocus.length ) {
9058 hasFocus = this.element.find(":tabbable");
9060 if ( !hasFocus.length ) {
9061 hasFocus = this.uiDialogButtonPane.find(":tabbable");
9063 if ( !hasFocus.length ) {
9064 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
9066 if ( !hasFocus.length ) {
9067 hasFocus = this.uiDialog;
9069 hasFocus.eq( 0 ).focus();
9072 _keepFocus: function( event ) {
9073 function checkFocus() {
9074 var activeElement = this.document[0].activeElement,
9075 isActive = this.uiDialog[0] === activeElement ||
9076 $.contains( this.uiDialog[0], activeElement );
9078 this._focusTabbable();
9081 event.preventDefault();
9082 checkFocus.call( this );
9084 // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
9085 // so we check again later
9086 this._delay( checkFocus );
9089 _createWrapper: function() {
9090 this.uiDialog = $("<div>")
9091 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
9092 this.options.dialogClass )
9095 // Setting tabIndex makes the div focusable
9099 .appendTo( this._appendTo() );
9101 this._on( this.uiDialog, {
9102 keydown: function( event ) {
9103 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
9104 event.keyCode === $.ui.keyCode.ESCAPE ) {
9105 event.preventDefault();
9106 this.close( event );
9110 // prevent tabbing out of dialogs
9111 if ( event.keyCode !== $.ui.keyCode.TAB ) {
9114 var tabbables = this.uiDialog.find(":tabbable"),
9115 first = tabbables.filter(":first"),
9116 last = tabbables.filter(":last");
9118 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
9120 event.preventDefault();
9121 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
9123 event.preventDefault();
9126 mousedown: function( event ) {
9127 if ( this._moveToTop( event ) ) {
9128 this._focusTabbable();
9133 // We assume that any existing aria-describedby attribute means
9134 // that the dialog content is marked up properly
9135 // otherwise we brute force the content as the description
9136 if ( !this.element.find("[aria-describedby]").length ) {
9137 this.uiDialog.attr({
9138 "aria-describedby": this.element.uniqueId().attr("id")
9143 _createTitlebar: function() {
9146 this.uiDialogTitlebar = $("<div>")
9147 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
9148 .prependTo( this.uiDialog );
9149 this._on( this.uiDialogTitlebar, {
9150 mousedown: function( event ) {
9151 // Don't prevent click on close button (#8838)
9152 // Focusing a dialog that is partially scrolled out of view
9153 // causes the browser to scroll it into view, preventing the click event
9154 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
9155 // Dialog isn't getting focus when dragging (#8063)
9156 this.uiDialog.focus();
9161 this.uiDialogTitlebarClose = $("<button></button>")
9163 label: this.options.closeText,
9165 primary: "ui-icon-closethick"
9169 .addClass("ui-dialog-titlebar-close")
9170 .appendTo( this.uiDialogTitlebar );
9171 this._on( this.uiDialogTitlebarClose, {
9172 click: function( event ) {
9173 event.preventDefault();
9174 this.close( event );
9178 uiDialogTitle = $("<span>")
9180 .addClass("ui-dialog-title")
9181 .prependTo( this.uiDialogTitlebar );
9182 this._title( uiDialogTitle );
9184 this.uiDialog.attr({
9185 "aria-labelledby": uiDialogTitle.attr("id")
9189 _title: function( title ) {
9190 if ( !this.options.title ) {
9191 title.html(" ");
9193 title.text( this.options.title );
9196 _createButtonPane: function() {
9197 this.uiDialogButtonPane = $("<div>")
9198 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
9200 this.uiButtonSet = $("<div>")
9201 .addClass("ui-dialog-buttonset")
9202 .appendTo( this.uiDialogButtonPane );
9204 this._createButtons();
9207 _createButtons: function() {
9209 buttons = this.options.buttons;
9211 // if we already have a button pane, remove it
9212 this.uiDialogButtonPane.remove();
9213 this.uiButtonSet.empty();
9215 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
9216 this.uiDialog.removeClass("ui-dialog-buttons");
9220 $.each( buttons, function( name, props ) {
9221 var click, buttonOptions;
9222 props = $.isFunction( props ) ?
9223 { click: props, text: name } :
9225 // Default to a non-submitting button
9226 props = $.extend( { type: "button" }, props );
9227 // Change the context for the click callback to be the main element
9228 click = props.click;
9229 props.click = function() {
9230 click.apply( that.element[0], arguments );
9234 text: props.showText
9237 delete props.showText;
9238 $( "<button></button>", props )
9239 .button( buttonOptions )
9240 .appendTo( that.uiButtonSet );
9242 this.uiDialog.addClass("ui-dialog-buttons");
9243 this.uiDialogButtonPane.appendTo( this.uiDialog );
9246 _makeDraggable: function() {
9248 options = this.options;
9250 function filteredUi( ui ) {
9252 position: ui.position,
9257 this.uiDialog.draggable({
9258 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
9259 handle: ".ui-dialog-titlebar",
9260 containment: "document",
9261 start: function( event, ui ) {
9262 $( this ).addClass("ui-dialog-dragging");
9263 that._blockFrames();
9264 that._trigger( "dragStart", event, filteredUi( ui ) );
9266 drag: function( event, ui ) {
9267 that._trigger( "drag", event, filteredUi( ui ) );
9269 stop: function( event, ui ) {
9270 options.position = [
9271 ui.position.left - that.document.scrollLeft(),
9272 ui.position.top - that.document.scrollTop()
9274 $( this ).removeClass("ui-dialog-dragging");
9275 that._unblockFrames();
9276 that._trigger( "dragStop", event, filteredUi( ui ) );
9281 _makeResizable: function() {
9283 options = this.options,
9284 handles = options.resizable,
9285 // .ui-resizable has position: relative defined in the stylesheet
9286 // but dialogs have to use absolute or fixed positioning
9287 position = this.uiDialog.css("position"),
9288 resizeHandles = typeof handles === "string" ?
9290 "n,e,s,w,se,sw,ne,nw";
9292 function filteredUi( ui ) {
9294 originalPosition: ui.originalPosition,
9295 originalSize: ui.originalSize,
9296 position: ui.position,
9301 this.uiDialog.resizable({
9302 cancel: ".ui-dialog-content",
9303 containment: "document",
9304 alsoResize: this.element,
9305 maxWidth: options.maxWidth,
9306 maxHeight: options.maxHeight,
9307 minWidth: options.minWidth,
9308 minHeight: this._minHeight(),
9309 handles: resizeHandles,
9310 start: function( event, ui ) {
9311 $( this ).addClass("ui-dialog-resizing");
9312 that._blockFrames();
9313 that._trigger( "resizeStart", event, filteredUi( ui ) );
9315 resize: function( event, ui ) {
9316 that._trigger( "resize", event, filteredUi( ui ) );
9318 stop: function( event, ui ) {
9319 options.height = $( this ).height();
9320 options.width = $( this ).width();
9321 $( this ).removeClass("ui-dialog-resizing");
9322 that._unblockFrames();
9323 that._trigger( "resizeStop", event, filteredUi( ui ) );
9326 .css( "position", position );
9329 _minHeight: function() {
9330 var options = this.options;
9332 return options.height === "auto" ?
9334 Math.min( options.minHeight, options.height );
9337 _position: function() {
9338 // Need to show the dialog to get the actual offset in the position plugin
9339 var isVisible = this.uiDialog.is(":visible");
9341 this.uiDialog.show();
9343 this.uiDialog.position( this.options.position );
9345 this.uiDialog.hide();
9349 _setOptions: function( options ) {
9352 resizableOptions = {};
9354 $.each( options, function( key, value ) {
9355 that._setOption( key, value );
9357 if ( key in sizeRelatedOptions ) {
9360 if ( key in resizableRelatedOptions ) {
9361 resizableOptions[ key ] = value;
9369 if ( this.uiDialog.is(":data(ui-resizable)") ) {
9370 this.uiDialog.resizable( "option", resizableOptions );
9374 _setOption: function( key, value ) {
9375 /*jshint maxcomplexity:15*/
9376 var isDraggable, isResizable,
9377 uiDialog = this.uiDialog;
9379 if ( key === "dialogClass" ) {
9381 .removeClass( this.options.dialogClass )
9385 if ( key === "disabled" ) {
9389 this._super( key, value );
9391 if ( key === "appendTo" ) {
9392 this.uiDialog.appendTo( this._appendTo() );
9395 if ( key === "buttons" ) {
9396 this._createButtons();
9399 if ( key === "closeText" ) {
9400 this.uiDialogTitlebarClose.button({
9401 // Ensure that we always pass a string
9406 if ( key === "draggable" ) {
9407 isDraggable = uiDialog.is(":data(ui-draggable)");
9408 if ( isDraggable && !value ) {
9409 uiDialog.draggable("destroy");
9412 if ( !isDraggable && value ) {
9413 this._makeDraggable();
9417 if ( key === "position" ) {
9421 if ( key === "resizable" ) {
9422 // currently resizable, becoming non-resizable
9423 isResizable = uiDialog.is(":data(ui-resizable)");
9424 if ( isResizable && !value ) {
9425 uiDialog.resizable("destroy");
9428 // currently resizable, changing handles
9429 if ( isResizable && typeof value === "string" ) {
9430 uiDialog.resizable( "option", "handles", value );
9433 // currently non-resizable, becoming resizable
9434 if ( !isResizable && value !== false ) {
9435 this._makeResizable();
9439 if ( key === "title" ) {
9440 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
9445 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
9446 // divs will both have width and height set, so we need to reset them
9447 var nonContentHeight, minContentHeight, maxContentHeight,
9448 options = this.options;
9450 // Reset content sizing
9451 this.element.show().css({
9458 if ( options.minWidth > options.width ) {
9459 options.width = options.minWidth;
9462 // reset wrapper sizing
9463 // determine the height of all the non-content elements
9464 nonContentHeight = this.uiDialog.css({
9466 width: options.width
9469 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
9470 maxContentHeight = typeof options.maxHeight === "number" ?
9471 Math.max( 0, options.maxHeight - nonContentHeight ) :
9474 if ( options.height === "auto" ) {
9476 minHeight: minContentHeight,
9477 maxHeight: maxContentHeight,
9481 this.element.height( Math.max( 0, options.height - nonContentHeight ) );
9484 if (this.uiDialog.is(":data(ui-resizable)") ) {
9485 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
9489 _blockFrames: function() {
9490 this.iframeBlocks = this.document.find( "iframe" ).map(function() {
9491 var iframe = $( this );
9495 position: "absolute",
9496 width: iframe.outerWidth(),
9497 height: iframe.outerHeight()
9499 .appendTo( iframe.parent() )
9500 .offset( iframe.offset() )[0];
9504 _unblockFrames: function() {
9505 if ( this.iframeBlocks ) {
9506 this.iframeBlocks.remove();
9507 delete this.iframeBlocks;
9511 _allowInteraction: function( event ) {
9512 if ( $( event.target ).closest(".ui-dialog").length ) {
9516 // TODO: Remove hack when datepicker implements
9517 // the .ui-front logic (#8989)
9518 return !!$( event.target ).closest(".ui-datepicker").length;
9521 _createOverlay: function() {
9522 if ( !this.options.modal ) {
9527 widgetFullName = this.widgetFullName;
9528 if ( !$.ui.dialog.overlayInstances ) {
9529 // Prevent use of anchors and inputs.
9530 // We use a delay in case the overlay is created from an
9531 // event that we're going to be cancelling. (#2804)
9532 this._delay(function() {
9533 // Handle .dialog().dialog("close") (#4065)
9534 if ( $.ui.dialog.overlayInstances ) {
9535 this.document.bind( "focusin.dialog", function( event ) {
9536 if ( !that._allowInteraction( event ) ) {
9537 event.preventDefault();
9538 $(".ui-dialog:visible:last .ui-dialog-content")
9539 .data( widgetFullName )._focusTabbable();
9546 this.overlay = $("<div>")
9547 .addClass("ui-widget-overlay ui-front")
9548 .appendTo( this._appendTo() );
9549 this._on( this.overlay, {
9550 mousedown: "_keepFocus"
9552 $.ui.dialog.overlayInstances++;
9555 _destroyOverlay: function() {
9556 if ( !this.options.modal ) {
9560 if ( this.overlay ) {
9561 $.ui.dialog.overlayInstances--;
9563 if ( !$.ui.dialog.overlayInstances ) {
9564 this.document.unbind( "focusin.dialog" );
9566 this.overlay.remove();
9567 this.overlay = null;
9572 $.ui.dialog.overlayInstances = 0;
9575 if ( $.uiBackCompat !== false ) {
9576 // position option with array notation
9577 // just override with old implementation
9578 $.widget( "ui.dialog", $.ui.dialog, {
9579 _position: function() {
9580 var position = this.options.position,
9586 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
9587 myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
9588 if ( myAt.length === 1 ) {
9592 $.each( [ "left", "top" ], function( i, offsetPosition ) {
9593 if ( +myAt[ i ] === myAt[ i ] ) {
9594 offset[ i ] = myAt[ i ];
9595 myAt[ i ] = offsetPosition;
9600 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
9601 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
9606 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
9608 position = $.ui.dialog.prototype.options.position;
9611 // need to show the dialog to get the actual offset in the position plugin
9612 isVisible = this.uiDialog.is(":visible");
9614 this.uiDialog.show();
9616 this.uiDialog.position( position );
9618 this.uiDialog.hide();
9625 (function( $, undefined ) {
9627 $.widget( "ui.menu", {
9629 defaultElement: "<ul>",
9633 submenu: "ui-icon-carat-1-e"
9648 _create: function() {
9649 this.activeMenu = this.element;
9650 // flag used to prevent firing of the click handler
9651 // as the event bubbles up through nested menus
9652 this.mouseHandled = false;
9655 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9656 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
9658 role: this.options.role,
9661 // need to catch all clicks on disabled menu
9662 // not possible through _on
9663 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
9664 if ( this.options.disabled ) {
9665 event.preventDefault();
9669 if ( this.options.disabled ) {
9671 .addClass( "ui-state-disabled" )
9672 .attr( "aria-disabled", "true" );
9676 // Prevent focus from sticking to links inside menu after clicking
9677 // them (focus should always stay on UL during navigation).
9678 "mousedown .ui-menu-item > a": function( event ) {
9679 event.preventDefault();
9681 "click .ui-state-disabled > a": function( event ) {
9682 event.preventDefault();
9684 "click .ui-menu-item:has(a)": function( event ) {
9685 var target = $( event.target ).closest( ".ui-menu-item" );
9686 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
9687 this.mouseHandled = true;
9689 this.select( event );
9690 // Open submenu on click
9691 if ( target.has( ".ui-menu" ).length ) {
9692 this.expand( event );
9693 } else if ( !this.element.is( ":focus" ) ) {
9694 // Redirect focus to the menu
9695 this.element.trigger( "focus", [ true ] );
9697 // If the active item is on the top level, let it stay active.
9698 // Otherwise, blur the active item since it is no longer visible.
9699 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
9700 clearTimeout( this.timer );
9705 "mouseenter .ui-menu-item": function( event ) {
9706 var target = $( event.currentTarget );
9707 // Remove ui-state-active class from siblings of the newly focused menu item
9708 // to avoid a jump caused by adjacent elements both having a class with a border
9709 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
9710 this.focus( event, target );
9712 mouseleave: "collapseAll",
9713 "mouseleave .ui-menu": "collapseAll",
9714 focus: function( event, keepActiveItem ) {
9715 // If there's already an active item, keep it active
9716 // If not, activate the first item
9717 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
9719 if ( !keepActiveItem ) {
9720 this.focus( event, item );
9723 blur: function( event ) {
9724 this._delay(function() {
9725 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
9726 this.collapseAll( event );
9735 // Clicks outside of a menu collapse any open menus
9736 this._on( this.document, {
9737 click: function( event ) {
9738 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
9739 this.collapseAll( event );
9742 // Reset the mouseHandled flag
9743 this.mouseHandled = false;
9748 _destroy: function() {
9749 // Destroy (sub)menus
9751 .removeAttr( "aria-activedescendant" )
9752 .find( ".ui-menu" ).addBack()
9753 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
9754 .removeAttr( "role" )
9755 .removeAttr( "tabIndex" )
9756 .removeAttr( "aria-labelledby" )
9757 .removeAttr( "aria-expanded" )
9758 .removeAttr( "aria-hidden" )
9759 .removeAttr( "aria-disabled" )
9763 // Destroy menu items
9764 this.element.find( ".ui-menu-item" )
9765 .removeClass( "ui-menu-item" )
9766 .removeAttr( "role" )
9767 .removeAttr( "aria-disabled" )
9770 .removeClass( "ui-corner-all ui-state-hover" )
9771 .removeAttr( "tabIndex" )
9772 .removeAttr( "role" )
9773 .removeAttr( "aria-haspopup" )
9774 .children().each( function() {
9775 var elem = $( this );
9776 if ( elem.data( "ui-menu-submenu-carat" ) ) {
9781 // Destroy menu dividers
9782 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
9785 _keydown: function( event ) {
9786 /*jshint maxcomplexity:20*/
9787 var match, prev, character, skip, regex,
9788 preventDefault = true;
9790 function escape( value ) {
9791 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
9794 switch ( event.keyCode ) {
9795 case $.ui.keyCode.PAGE_UP:
9796 this.previousPage( event );
9798 case $.ui.keyCode.PAGE_DOWN:
9799 this.nextPage( event );
9801 case $.ui.keyCode.HOME:
9802 this._move( "first", "first", event );
9804 case $.ui.keyCode.END:
9805 this._move( "last", "last", event );
9807 case $.ui.keyCode.UP:
9808 this.previous( event );
9810 case $.ui.keyCode.DOWN:
9813 case $.ui.keyCode.LEFT:
9814 this.collapse( event );
9816 case $.ui.keyCode.RIGHT:
9817 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
9818 this.expand( event );
9821 case $.ui.keyCode.ENTER:
9822 case $.ui.keyCode.SPACE:
9823 this._activate( event );
9825 case $.ui.keyCode.ESCAPE:
9826 this.collapse( event );
9829 preventDefault = false;
9830 prev = this.previousFilter || "";
9831 character = String.fromCharCode( event.keyCode );
9834 clearTimeout( this.filterTimer );
9836 if ( character === prev ) {
9839 character = prev + character;
9842 regex = new RegExp( "^" + escape( character ), "i" );
9843 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9844 return regex.test( $( this ).children( "a" ).text() );
9846 match = skip && match.index( this.active.next() ) !== -1 ?
9847 this.active.nextAll( ".ui-menu-item" ) :
9850 // If no matches on the current filter, reset to the last character pressed
9851 // to move down the menu to the first item that starts with that character
9852 if ( !match.length ) {
9853 character = String.fromCharCode( event.keyCode );
9854 regex = new RegExp( "^" + escape( character ), "i" );
9855 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9856 return regex.test( $( this ).children( "a" ).text() );
9860 if ( match.length ) {
9861 this.focus( event, match );
9862 if ( match.length > 1 ) {
9863 this.previousFilter = character;
9864 this.filterTimer = this._delay(function() {
9865 delete this.previousFilter;
9868 delete this.previousFilter;
9871 delete this.previousFilter;
9875 if ( preventDefault ) {
9876 event.preventDefault();
9880 _activate: function( event ) {
9881 if ( !this.active.is( ".ui-state-disabled" ) ) {
9882 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
9883 this.expand( event );
9885 this.select( event );
9890 refresh: function() {
9892 icon = this.options.icons.submenu,
9893 submenus = this.element.find( this.options.menus );
9895 // Initialize nested menus
9896 submenus.filter( ":not(.ui-menu)" )
9897 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9900 role: this.options.role,
9901 "aria-hidden": "true",
9902 "aria-expanded": "false"
9905 var menu = $( this ),
9906 item = menu.prev( "a" ),
9907 submenuCarat = $( "<span>" )
9908 .addClass( "ui-menu-icon ui-icon " + icon )
9909 .data( "ui-menu-submenu-carat", true );
9912 .attr( "aria-haspopup", "true" )
9913 .prepend( submenuCarat );
9914 menu.attr( "aria-labelledby", item.attr( "id" ) );
9917 menus = submenus.add( this.element );
9919 // Don't refresh list items that are already adapted
9920 menus.children( ":not(.ui-menu-item):has(a)" )
9921 .addClass( "ui-menu-item" )
9922 .attr( "role", "presentation" )
9925 .addClass( "ui-corner-all" )
9928 role: this._itemRole()
9931 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
9932 menus.children( ":not(.ui-menu-item)" ).each(function() {
9933 var item = $( this );
9934 // hyphen, em dash, en dash
9935 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
9936 item.addClass( "ui-widget-content ui-menu-divider" );
9940 // Add aria-disabled attribute to any disabled menu item
9941 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
9943 // If the active item has been removed, blur the menu
9944 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
9949 _itemRole: function() {
9953 }[ this.options.role ];
9956 _setOption: function( key, value ) {
9957 if ( key === "icons" ) {
9958 this.element.find( ".ui-menu-icon" )
9959 .removeClass( this.options.icons.submenu )
9960 .addClass( value.submenu );
9962 this._super( key, value );
9965 focus: function( event, item ) {
9966 var nested, focused;
9967 this.blur( event, event && event.type === "focus" );
9969 this._scrollIntoView( item );
9971 this.active = item.first();
9972 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
9973 // Only update aria-activedescendant if there's a role
9974 // otherwise we assume focus is managed elsewhere
9975 if ( this.options.role ) {
9976 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
9979 // Highlight active parent menu item, if any
9982 .closest( ".ui-menu-item" )
9983 .children( "a:first" )
9984 .addClass( "ui-state-active" );
9986 if ( event && event.type === "keydown" ) {
9989 this.timer = this._delay(function() {
9994 nested = item.children( ".ui-menu" );
9995 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
9996 this._startOpening(nested);
9998 this.activeMenu = item.parent();
10000 this._trigger( "focus", event, { item: item } );
10003 _scrollIntoView: function( item ) {
10004 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
10005 if ( this._hasScroll() ) {
10006 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
10007 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
10008 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
10009 scroll = this.activeMenu.scrollTop();
10010 elementHeight = this.activeMenu.height();
10011 itemHeight = item.height();
10013 if ( offset < 0 ) {
10014 this.activeMenu.scrollTop( scroll + offset );
10015 } else if ( offset + itemHeight > elementHeight ) {
10016 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
10021 blur: function( event, fromFocus ) {
10022 if ( !fromFocus ) {
10023 clearTimeout( this.timer );
10026 if ( !this.active ) {
10030 this.active.children( "a" ).removeClass( "ui-state-focus" );
10031 this.active = null;
10033 this._trigger( "blur", event, { item: this.active } );
10036 _startOpening: function( submenu ) {
10037 clearTimeout( this.timer );
10039 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
10040 // shift in the submenu position when mousing over the carat icon
10041 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
10045 this.timer = this._delay(function() {
10047 this._open( submenu );
10051 _open: function( submenu ) {
10052 var position = $.extend({
10054 }, this.options.position );
10056 clearTimeout( this.timer );
10057 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
10059 .attr( "aria-hidden", "true" );
10063 .removeAttr( "aria-hidden" )
10064 .attr( "aria-expanded", "true" )
10065 .position( position );
10068 collapseAll: function( event, all ) {
10069 clearTimeout( this.timer );
10070 this.timer = this._delay(function() {
10071 // If we were passed an event, look for the submenu that contains the event
10072 var currentMenu = all ? this.element :
10073 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
10075 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
10076 if ( !currentMenu.length ) {
10077 currentMenu = this.element;
10080 this._close( currentMenu );
10082 this.blur( event );
10083 this.activeMenu = currentMenu;
10087 // With no arguments, closes the currently active menu - if nothing is active
10088 // it closes all menus. If passed an argument, it will search for menus BELOW
10089 _close: function( startMenu ) {
10090 if ( !startMenu ) {
10091 startMenu = this.active ? this.active.parent() : this.element;
10095 .find( ".ui-menu" )
10097 .attr( "aria-hidden", "true" )
10098 .attr( "aria-expanded", "false" )
10100 .find( "a.ui-state-active" )
10101 .removeClass( "ui-state-active" );
10104 collapse: function( event ) {
10105 var newItem = this.active &&
10106 this.active.parent().closest( ".ui-menu-item", this.element );
10107 if ( newItem && newItem.length ) {
10109 this.focus( event, newItem );
10113 expand: function( event ) {
10114 var newItem = this.active &&
10116 .children( ".ui-menu " )
10117 .children( ".ui-menu-item" )
10120 if ( newItem && newItem.length ) {
10121 this._open( newItem.parent() );
10123 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
10124 this._delay(function() {
10125 this.focus( event, newItem );
10130 next: function( event ) {
10131 this._move( "next", "first", event );
10134 previous: function( event ) {
10135 this._move( "prev", "last", event );
10138 isFirstItem: function() {
10139 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
10142 isLastItem: function() {
10143 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
10146 _move: function( direction, filter, event ) {
10148 if ( this.active ) {
10149 if ( direction === "first" || direction === "last" ) {
10151 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
10155 [ direction + "All" ]( ".ui-menu-item" )
10159 if ( !next || !next.length || !this.active ) {
10160 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
10163 this.focus( event, next );
10166 nextPage: function( event ) {
10167 var item, base, height;
10169 if ( !this.active ) {
10170 this.next( event );
10173 if ( this.isLastItem() ) {
10176 if ( this._hasScroll() ) {
10177 base = this.active.offset().top;
10178 height = this.element.height();
10179 this.active.nextAll( ".ui-menu-item" ).each(function() {
10181 return item.offset().top - base - height < 0;
10184 this.focus( event, item );
10186 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
10187 [ !this.active ? "first" : "last" ]() );
10191 previousPage: function( event ) {
10192 var item, base, height;
10193 if ( !this.active ) {
10194 this.next( event );
10197 if ( this.isFirstItem() ) {
10200 if ( this._hasScroll() ) {
10201 base = this.active.offset().top;
10202 height = this.element.height();
10203 this.active.prevAll( ".ui-menu-item" ).each(function() {
10205 return item.offset().top - base + height > 0;
10208 this.focus( event, item );
10210 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
10214 _hasScroll: function() {
10215 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
10218 select: function( event ) {
10219 // TODO: It should never be possible to not have an active item at this
10220 // point, but the tests don't trigger mouseenter before click.
10221 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
10222 var ui = { item: this.active };
10223 if ( !this.active.has( ".ui-menu" ).length ) {
10224 this.collapseAll( event, true );
10226 this._trigger( "select", event, ui );
10231 (function( $, undefined ) {
10233 $.widget( "ui.progressbar", {
10245 _create: function() {
10246 // Constrain initial value
10247 this.oldValue = this.options.value = this._constrainedValue();
10250 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10252 // Only set static values, aria-valuenow and aria-valuemax are
10253 // set inside _refreshValue()
10254 role: "progressbar",
10255 "aria-valuemin": this.min
10258 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
10259 .appendTo( this.element );
10261 this._refreshValue();
10264 _destroy: function() {
10266 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10267 .removeAttr( "role" )
10268 .removeAttr( "aria-valuemin" )
10269 .removeAttr( "aria-valuemax" )
10270 .removeAttr( "aria-valuenow" );
10272 this.valueDiv.remove();
10275 value: function( newValue ) {
10276 if ( newValue === undefined ) {
10277 return this.options.value;
10280 this.options.value = this._constrainedValue( newValue );
10281 this._refreshValue();
10284 _constrainedValue: function( newValue ) {
10285 if ( newValue === undefined ) {
10286 newValue = this.options.value;
10289 this.indeterminate = newValue === false;
10292 if ( typeof newValue !== "number" ) {
10296 return this.indeterminate ? false :
10297 Math.min( this.options.max, Math.max( this.min, newValue ) );
10300 _setOptions: function( options ) {
10301 // Ensure "value" option is set after other values (like max)
10302 var value = options.value;
10303 delete options.value;
10305 this._super( options );
10307 this.options.value = this._constrainedValue( value );
10308 this._refreshValue();
10311 _setOption: function( key, value ) {
10312 if ( key === "max" ) {
10313 // Don't allow a max less than min
10314 value = Math.max( this.min, value );
10317 this._super( key, value );
10320 _percentage: function() {
10321 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
10324 _refreshValue: function() {
10325 var value = this.options.value,
10326 percentage = this._percentage();
10329 .toggle( this.indeterminate || value > this.min )
10330 .toggleClass( "ui-corner-right", value === this.options.max )
10331 .width( percentage.toFixed(0) + "%" );
10333 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
10335 if ( this.indeterminate ) {
10336 this.element.removeAttr( "aria-valuenow" );
10337 if ( !this.overlayDiv ) {
10338 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
10341 this.element.attr({
10342 "aria-valuemax": this.options.max,
10343 "aria-valuenow": value
10345 if ( this.overlayDiv ) {
10346 this.overlayDiv.remove();
10347 this.overlayDiv = null;
10351 if ( this.oldValue !== value ) {
10352 this.oldValue = value;
10353 this._trigger( "change" );
10355 if ( value === this.options.max ) {
10356 this._trigger( "complete" );
10362 (function( $, undefined ) {
10364 // number of pages in a slider
10365 // (how many times can you page up/down to go through the whole range)
10368 $.widget( "ui.slider", $.ui.mouse, {
10370 widgetEventPrefix: "slide",
10377 orientation: "horizontal",
10390 _create: function() {
10391 this._keySliding = false;
10392 this._mouseSliding = false;
10393 this._animateOff = true;
10394 this._handleIndex = null;
10395 this._detectOrientation();
10399 .addClass( "ui-slider" +
10400 " ui-slider-" + this.orientation +
10402 " ui-widget-content" +
10406 this._setOption( "disabled", this.options.disabled );
10408 this._animateOff = false;
10411 _refresh: function() {
10412 this._createRange();
10413 this._createHandles();
10414 this._setupEvents();
10415 this._refreshValue();
10418 _createHandles: function() {
10419 var i, handleCount,
10420 options = this.options,
10421 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
10422 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
10425 handleCount = ( options.values && options.values.length ) || 1;
10427 if ( existingHandles.length > handleCount ) {
10428 existingHandles.slice( handleCount ).remove();
10429 existingHandles = existingHandles.slice( 0, handleCount );
10432 for ( i = existingHandles.length; i < handleCount; i++ ) {
10433 handles.push( handle );
10436 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
10438 this.handle = this.handles.eq( 0 );
10440 this.handles.each(function( i ) {
10441 $( this ).data( "ui-slider-handle-index", i );
10445 _createRange: function() {
10446 var options = this.options,
10449 if ( options.range ) {
10450 if ( options.range === true ) {
10451 if ( !options.values ) {
10452 options.values = [ this._valueMin(), this._valueMin() ];
10453 } else if ( options.values.length && options.values.length !== 2 ) {
10454 options.values = [ options.values[0], options.values[0] ];
10455 } else if ( $.isArray( options.values ) ) {
10456 options.values = options.values.slice(0);
10460 if ( !this.range || !this.range.length ) {
10461 this.range = $( "<div></div>" )
10462 .appendTo( this.element );
10464 classes = "ui-slider-range" +
10465 // note: this isn't the most fittingly semantic framework class for this element,
10466 // but worked best visually with a variety of themes
10467 " ui-widget-header ui-corner-all";
10469 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
10470 // Handle range switching from true to min/max
10477 this.range.addClass( classes +
10478 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
10480 this.range = $([]);
10484 _setupEvents: function() {
10485 var elements = this.handles.add( this.range ).filter( "a" );
10486 this._off( elements );
10487 this._on( elements, this._handleEvents );
10488 this._hoverable( elements );
10489 this._focusable( elements );
10492 _destroy: function() {
10493 this.handles.remove();
10494 this.range.remove();
10497 .removeClass( "ui-slider" +
10498 " ui-slider-horizontal" +
10499 " ui-slider-vertical" +
10501 " ui-widget-content" +
10502 " ui-corner-all" );
10504 this._mouseDestroy();
10507 _mouseCapture: function( event ) {
10508 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
10512 if ( o.disabled ) {
10516 this.elementSize = {
10517 width: this.element.outerWidth(),
10518 height: this.element.outerHeight()
10520 this.elementOffset = this.element.offset();
10522 position = { x: event.pageX, y: event.pageY };
10523 normValue = this._normValueFromMouse( position );
10524 distance = this._valueMax() - this._valueMin() + 1;
10525 this.handles.each(function( i ) {
10526 var thisDistance = Math.abs( normValue - that.values(i) );
10527 if (( distance > thisDistance ) ||
10528 ( distance === thisDistance &&
10529 (i === that._lastChangedValue || that.values(i) === o.min ))) {
10530 distance = thisDistance;
10531 closestHandle = $( this );
10536 allowed = this._start( event, index );
10537 if ( allowed === false ) {
10540 this._mouseSliding = true;
10542 this._handleIndex = index;
10545 .addClass( "ui-state-active" )
10548 offset = closestHandle.offset();
10549 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
10550 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
10551 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
10552 top: event.pageY - offset.top -
10553 ( closestHandle.height() / 2 ) -
10554 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
10555 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
10556 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
10559 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
10560 this._slide( event, index, normValue );
10562 this._animateOff = true;
10566 _mouseStart: function() {
10570 _mouseDrag: function( event ) {
10571 var position = { x: event.pageX, y: event.pageY },
10572 normValue = this._normValueFromMouse( position );
10574 this._slide( event, this._handleIndex, normValue );
10579 _mouseStop: function( event ) {
10580 this.handles.removeClass( "ui-state-active" );
10581 this._mouseSliding = false;
10583 this._stop( event, this._handleIndex );
10584 this._change( event, this._handleIndex );
10586 this._handleIndex = null;
10587 this._clickOffset = null;
10588 this._animateOff = false;
10593 _detectOrientation: function() {
10594 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
10597 _normValueFromMouse: function( position ) {
10604 if ( this.orientation === "horizontal" ) {
10605 pixelTotal = this.elementSize.width;
10606 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
10608 pixelTotal = this.elementSize.height;
10609 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
10612 percentMouse = ( pixelMouse / pixelTotal );
10613 if ( percentMouse > 1 ) {
10616 if ( percentMouse < 0 ) {
10619 if ( this.orientation === "vertical" ) {
10620 percentMouse = 1 - percentMouse;
10623 valueTotal = this._valueMax() - this._valueMin();
10624 valueMouse = this._valueMin() + percentMouse * valueTotal;
10626 return this._trimAlignValue( valueMouse );
10629 _start: function( event, index ) {
10631 handle: this.handles[ index ],
10632 value: this.value()
10634 if ( this.options.values && this.options.values.length ) {
10635 uiHash.value = this.values( index );
10636 uiHash.values = this.values();
10638 return this._trigger( "start", event, uiHash );
10641 _slide: function( event, index, newVal ) {
10646 if ( this.options.values && this.options.values.length ) {
10647 otherVal = this.values( index ? 0 : 1 );
10649 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
10650 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
10655 if ( newVal !== this.values( index ) ) {
10656 newValues = this.values();
10657 newValues[ index ] = newVal;
10658 // A slide can be canceled by returning false from the slide callback
10659 allowed = this._trigger( "slide", event, {
10660 handle: this.handles[ index ],
10664 otherVal = this.values( index ? 0 : 1 );
10665 if ( allowed !== false ) {
10666 this.values( index, newVal, true );
10670 if ( newVal !== this.value() ) {
10671 // A slide can be canceled by returning false from the slide callback
10672 allowed = this._trigger( "slide", event, {
10673 handle: this.handles[ index ],
10676 if ( allowed !== false ) {
10677 this.value( newVal );
10683 _stop: function( event, index ) {
10685 handle: this.handles[ index ],
10686 value: this.value()
10688 if ( this.options.values && this.options.values.length ) {
10689 uiHash.value = this.values( index );
10690 uiHash.values = this.values();
10693 this._trigger( "stop", event, uiHash );
10696 _change: function( event, index ) {
10697 if ( !this._keySliding && !this._mouseSliding ) {
10699 handle: this.handles[ index ],
10700 value: this.value()
10702 if ( this.options.values && this.options.values.length ) {
10703 uiHash.value = this.values( index );
10704 uiHash.values = this.values();
10707 //store the last changed value index for reference when handles overlap
10708 this._lastChangedValue = index;
10710 this._trigger( "change", event, uiHash );
10714 value: function( newValue ) {
10715 if ( arguments.length ) {
10716 this.options.value = this._trimAlignValue( newValue );
10717 this._refreshValue();
10718 this._change( null, 0 );
10722 return this._value();
10725 values: function( index, newValue ) {
10730 if ( arguments.length > 1 ) {
10731 this.options.values[ index ] = this._trimAlignValue( newValue );
10732 this._refreshValue();
10733 this._change( null, index );
10737 if ( arguments.length ) {
10738 if ( $.isArray( arguments[ 0 ] ) ) {
10739 vals = this.options.values;
10740 newValues = arguments[ 0 ];
10741 for ( i = 0; i < vals.length; i += 1 ) {
10742 vals[ i ] = this._trimAlignValue( newValues[ i ] );
10743 this._change( null, i );
10745 this._refreshValue();
10747 if ( this.options.values && this.options.values.length ) {
10748 return this._values( index );
10750 return this.value();
10754 return this._values();
10758 _setOption: function( key, value ) {
10762 if ( key === "range" && this.options.range === true ) {
10763 if ( value === "min" ) {
10764 this.options.value = this._values( 0 );
10765 this.options.values = null;
10766 } else if ( value === "max" ) {
10767 this.options.value = this._values( this.options.values.length-1 );
10768 this.options.values = null;
10772 if ( $.isArray( this.options.values ) ) {
10773 valsLength = this.options.values.length;
10776 $.Widget.prototype._setOption.apply( this, arguments );
10779 case "orientation":
10780 this._detectOrientation();
10782 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
10783 .addClass( "ui-slider-" + this.orientation );
10784 this._refreshValue();
10787 this._animateOff = true;
10788 this._refreshValue();
10789 this._change( null, 0 );
10790 this._animateOff = false;
10793 this._animateOff = true;
10794 this._refreshValue();
10795 for ( i = 0; i < valsLength; i += 1 ) {
10796 this._change( null, i );
10798 this._animateOff = false;
10802 this._animateOff = true;
10803 this._refreshValue();
10804 this._animateOff = false;
10807 this._animateOff = true;
10809 this._animateOff = false;
10814 //internal value getter
10815 // _value() returns value trimmed by min and max, aligned by step
10816 _value: function() {
10817 var val = this.options.value;
10818 val = this._trimAlignValue( val );
10823 //internal values getter
10824 // _values() returns array of values trimmed by min and max, aligned by step
10825 // _values( index ) returns single value trimmed by min and max, aligned by step
10826 _values: function( index ) {
10831 if ( arguments.length ) {
10832 val = this.options.values[ index ];
10833 val = this._trimAlignValue( val );
10836 } else if ( this.options.values && this.options.values.length ) {
10837 // .slice() creates a copy of the array
10838 // this copy gets trimmed by min and max and then returned
10839 vals = this.options.values.slice();
10840 for ( i = 0; i < vals.length; i+= 1) {
10841 vals[ i ] = this._trimAlignValue( vals[ i ] );
10850 // returns the step-aligned value that val is closest to, between (inclusive) min and max
10851 _trimAlignValue: function( val ) {
10852 if ( val <= this._valueMin() ) {
10853 return this._valueMin();
10855 if ( val >= this._valueMax() ) {
10856 return this._valueMax();
10858 var step = ( this.options.step > 0 ) ? this.options.step : 1,
10859 valModStep = (val - this._valueMin()) % step,
10860 alignValue = val - valModStep;
10862 if ( Math.abs(valModStep) * 2 >= step ) {
10863 alignValue += ( valModStep > 0 ) ? step : ( -step );
10866 // Since JavaScript has problems with large floats, round
10867 // the final value to 5 digits after the decimal point (see #4124)
10868 return parseFloat( alignValue.toFixed(5) );
10871 _valueMin: function() {
10872 return this.options.min;
10875 _valueMax: function() {
10876 return this.options.max;
10879 _refreshValue: function() {
10880 var lastValPercent, valPercent, value, valueMin, valueMax,
10881 oRange = this.options.range,
10884 animate = ( !this._animateOff ) ? o.animate : false,
10887 if ( this.options.values && this.options.values.length ) {
10888 this.handles.each(function( i ) {
10889 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
10890 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
10891 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
10892 if ( that.options.range === true ) {
10893 if ( that.orientation === "horizontal" ) {
10895 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
10898 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
10902 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
10905 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
10909 lastValPercent = valPercent;
10912 value = this.value();
10913 valueMin = this._valueMin();
10914 valueMax = this._valueMax();
10915 valPercent = ( valueMax !== valueMin ) ?
10916 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
10918 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
10919 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
10921 if ( oRange === "min" && this.orientation === "horizontal" ) {
10922 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
10924 if ( oRange === "max" && this.orientation === "horizontal" ) {
10925 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
10927 if ( oRange === "min" && this.orientation === "vertical" ) {
10928 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
10930 if ( oRange === "max" && this.orientation === "vertical" ) {
10931 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
10937 keydown: function( event ) {
10938 /*jshint maxcomplexity:25*/
10939 var allowed, curVal, newVal, step,
10940 index = $( event.target ).data( "ui-slider-handle-index" );
10942 switch ( event.keyCode ) {
10943 case $.ui.keyCode.HOME:
10944 case $.ui.keyCode.END:
10945 case $.ui.keyCode.PAGE_UP:
10946 case $.ui.keyCode.PAGE_DOWN:
10947 case $.ui.keyCode.UP:
10948 case $.ui.keyCode.RIGHT:
10949 case $.ui.keyCode.DOWN:
10950 case $.ui.keyCode.LEFT:
10951 event.preventDefault();
10952 if ( !this._keySliding ) {
10953 this._keySliding = true;
10954 $( event.target ).addClass( "ui-state-active" );
10955 allowed = this._start( event, index );
10956 if ( allowed === false ) {
10963 step = this.options.step;
10964 if ( this.options.values && this.options.values.length ) {
10965 curVal = newVal = this.values( index );
10967 curVal = newVal = this.value();
10970 switch ( event.keyCode ) {
10971 case $.ui.keyCode.HOME:
10972 newVal = this._valueMin();
10974 case $.ui.keyCode.END:
10975 newVal = this._valueMax();
10977 case $.ui.keyCode.PAGE_UP:
10978 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
10980 case $.ui.keyCode.PAGE_DOWN:
10981 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
10983 case $.ui.keyCode.UP:
10984 case $.ui.keyCode.RIGHT:
10985 if ( curVal === this._valueMax() ) {
10988 newVal = this._trimAlignValue( curVal + step );
10990 case $.ui.keyCode.DOWN:
10991 case $.ui.keyCode.LEFT:
10992 if ( curVal === this._valueMin() ) {
10995 newVal = this._trimAlignValue( curVal - step );
10999 this._slide( event, index, newVal );
11001 click: function( event ) {
11002 event.preventDefault();
11004 keyup: function( event ) {
11005 var index = $( event.target ).data( "ui-slider-handle-index" );
11007 if ( this._keySliding ) {
11008 this._keySliding = false;
11009 this._stop( event, index );
11010 this._change( event, index );
11011 $( event.target ).removeClass( "ui-state-active" );
11021 function modifier( fn ) {
11022 return function() {
11023 var previous = this.element.val();
11024 fn.apply( this, arguments );
11026 if ( previous !== this.element.val() ) {
11027 this._trigger( "change" );
11032 $.widget( "ui.spinner", {
11034 defaultElement: "<input>",
11035 widgetEventPrefix: "spin",
11039 down: "ui-icon-triangle-1-s",
11040 up: "ui-icon-triangle-1-n"
11045 numberFormat: null,
11055 _create: function() {
11056 // handle string values that need to be parsed
11057 this._setOption( "max", this.options.max );
11058 this._setOption( "min", this.options.min );
11059 this._setOption( "step", this.options.step );
11061 // format the value, but don't constrain
11062 this._value( this.element.val(), true );
11065 this._on( this._events );
11068 // turning off autocomplete prevents the browser from remembering the
11069 // value when navigating through history, so we re-enable autocomplete
11070 // if the page is unloaded before the widget is destroyed. #7790
11071 this._on( this.window, {
11072 beforeunload: function() {
11073 this.element.removeAttr( "autocomplete" );
11078 _getCreateOptions: function() {
11080 element = this.element;
11082 $.each( [ "min", "max", "step" ], function( i, option ) {
11083 var value = element.attr( option );
11084 if ( value !== undefined && value.length ) {
11085 options[ option ] = value;
11093 keydown: function( event ) {
11094 if ( this._start( event ) && this._keydown( event ) ) {
11095 event.preventDefault();
11099 focus: function() {
11100 this.previous = this.element.val();
11102 blur: function( event ) {
11103 if ( this.cancelBlur ) {
11104 delete this.cancelBlur;
11110 if ( this.previous !== this.element.val() ) {
11111 this._trigger( "change", event );
11114 mousewheel: function( event, delta ) {
11118 if ( !this.spinning && !this._start( event ) ) {
11122 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
11123 clearTimeout( this.mousewheelTimer );
11124 this.mousewheelTimer = this._delay(function() {
11125 if ( this.spinning ) {
11126 this._stop( event );
11129 event.preventDefault();
11131 "mousedown .ui-spinner-button": function( event ) {
11134 // We never want the buttons to have focus; whenever the user is
11135 // interacting with the spinner, the focus should be on the input.
11136 // If the input is focused then this.previous is properly set from
11137 // when the input first received focus. If the input is not focused
11138 // then we need to set this.previous based on the value before spinning.
11139 previous = this.element[0] === this.document[0].activeElement ?
11140 this.previous : this.element.val();
11141 function checkFocus() {
11142 var isActive = this.element[0] === this.document[0].activeElement;
11144 this.element.focus();
11145 this.previous = previous;
11147 // IE sets focus asynchronously, so we need to check if focus
11148 // moved off of the input because the user clicked on the button.
11149 this._delay(function() {
11150 this.previous = previous;
11155 // ensure focus is on (or stays on) the text field
11156 event.preventDefault();
11157 checkFocus.call( this );
11160 // IE doesn't prevent moving focus even with event.preventDefault()
11161 // so we set a flag to know when we should ignore the blur event
11162 // and check (again) if focus moved off of the input.
11163 this.cancelBlur = true;
11164 this._delay(function() {
11165 delete this.cancelBlur;
11166 checkFocus.call( this );
11169 if ( this._start( event ) === false ) {
11173 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
11175 "mouseup .ui-spinner-button": "_stop",
11176 "mouseenter .ui-spinner-button": function( event ) {
11177 // button will add ui-state-active if mouse was down while mouseleave and kept down
11178 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
11182 if ( this._start( event ) === false ) {
11185 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
11187 // TODO: do we really want to consider this a stop?
11188 // shouldn't we just stop the repeater and wait until mouseup before
11189 // we trigger the stop event?
11190 "mouseleave .ui-spinner-button": "_stop"
11193 _draw: function() {
11194 var uiSpinner = this.uiSpinner = this.element
11195 .addClass( "ui-spinner-input" )
11196 .attr( "autocomplete", "off" )
11197 .wrap( this._uiSpinnerHtml() )
11200 .append( this._buttonHtml() );
11202 this.element.attr( "role", "spinbutton" );
11205 this.buttons = uiSpinner.find( ".ui-spinner-button" )
11206 .attr( "tabIndex", -1 )
11208 .removeClass( "ui-corner-all" );
11210 // IE 6 doesn't understand height: 50% for the buttons
11211 // unless the wrapper has an explicit height
11212 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
11213 uiSpinner.height() > 0 ) {
11214 uiSpinner.height( uiSpinner.height() );
11217 // disable spinner if element was already disabled
11218 if ( this.options.disabled ) {
11223 _keydown: function( event ) {
11224 var options = this.options,
11225 keyCode = $.ui.keyCode;
11227 switch ( event.keyCode ) {
11229 this._repeat( null, 1, event );
11232 this._repeat( null, -1, event );
11234 case keyCode.PAGE_UP:
11235 this._repeat( null, options.page, event );
11237 case keyCode.PAGE_DOWN:
11238 this._repeat( null, -options.page, event );
11245 _uiSpinnerHtml: function() {
11246 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
11249 _buttonHtml: function() {
11251 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
11252 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
11254 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
11255 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
11259 _start: function( event ) {
11260 if ( !this.spinning && this._trigger( "start", event ) === false ) {
11264 if ( !this.counter ) {
11267 this.spinning = true;
11271 _repeat: function( i, steps, event ) {
11274 clearTimeout( this.timer );
11275 this.timer = this._delay(function() {
11276 this._repeat( 40, steps, event );
11279 this._spin( steps * this.options.step, event );
11282 _spin: function( step, event ) {
11283 var value = this.value() || 0;
11285 if ( !this.counter ) {
11289 value = this._adjustValue( value + step * this._increment( this.counter ) );
11291 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
11292 this._value( value );
11297 _increment: function( i ) {
11298 var incremental = this.options.incremental;
11300 if ( incremental ) {
11301 return $.isFunction( incremental ) ?
11303 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
11309 _precision: function() {
11310 var precision = this._precisionOf( this.options.step );
11311 if ( this.options.min !== null ) {
11312 precision = Math.max( precision, this._precisionOf( this.options.min ) );
11317 _precisionOf: function( num ) {
11318 var str = num.toString(),
11319 decimal = str.indexOf( "." );
11320 return decimal === -1 ? 0 : str.length - decimal - 1;
11323 _adjustValue: function( value ) {
11324 var base, aboveMin,
11325 options = this.options;
11327 // make sure we're at a valid step
11328 // - find out where we are relative to the base (min or 0)
11329 base = options.min !== null ? options.min : 0;
11330 aboveMin = value - base;
11331 // - round to the nearest step
11332 aboveMin = Math.round(aboveMin / options.step) * options.step;
11333 // - rounding is based on 0, so adjust back to our base
11334 value = base + aboveMin;
11336 // fix precision from bad JS floating point math
11337 value = parseFloat( value.toFixed( this._precision() ) );
11340 if ( options.max !== null && value > options.max) {
11341 return options.max;
11343 if ( options.min !== null && value < options.min ) {
11344 return options.min;
11350 _stop: function( event ) {
11351 if ( !this.spinning ) {
11355 clearTimeout( this.timer );
11356 clearTimeout( this.mousewheelTimer );
11358 this.spinning = false;
11359 this._trigger( "stop", event );
11362 _setOption: function( key, value ) {
11363 if ( key === "culture" || key === "numberFormat" ) {
11364 var prevValue = this._parse( this.element.val() );
11365 this.options[ key ] = value;
11366 this.element.val( this._format( prevValue ) );
11370 if ( key === "max" || key === "min" || key === "step" ) {
11371 if ( typeof value === "string" ) {
11372 value = this._parse( value );
11375 if ( key === "icons" ) {
11376 this.buttons.first().find( ".ui-icon" )
11377 .removeClass( this.options.icons.up )
11378 .addClass( value.up );
11379 this.buttons.last().find( ".ui-icon" )
11380 .removeClass( this.options.icons.down )
11381 .addClass( value.down );
11384 this._super( key, value );
11386 if ( key === "disabled" ) {
11388 this.element.prop( "disabled", true );
11389 this.buttons.button( "disable" );
11391 this.element.prop( "disabled", false );
11392 this.buttons.button( "enable" );
11397 _setOptions: modifier(function( options ) {
11398 this._super( options );
11399 this._value( this.element.val() );
11402 _parse: function( val ) {
11403 if ( typeof val === "string" && val !== "" ) {
11404 val = window.Globalize && this.options.numberFormat ?
11405 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
11407 return val === "" || isNaN( val ) ? null : val;
11410 _format: function( value ) {
11411 if ( value === "" ) {
11414 return window.Globalize && this.options.numberFormat ?
11415 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
11419 _refresh: function() {
11420 this.element.attr({
11421 "aria-valuemin": this.options.min,
11422 "aria-valuemax": this.options.max,
11423 // TODO: what should we do with values that can't be parsed?
11424 "aria-valuenow": this._parse( this.element.val() )
11428 // update the value without triggering change
11429 _value: function( value, allowAny ) {
11431 if ( value !== "" ) {
11432 parsed = this._parse( value );
11433 if ( parsed !== null ) {
11435 parsed = this._adjustValue( parsed );
11437 value = this._format( parsed );
11440 this.element.val( value );
11444 _destroy: function() {
11446 .removeClass( "ui-spinner-input" )
11447 .prop( "disabled", false )
11448 .removeAttr( "autocomplete" )
11449 .removeAttr( "role" )
11450 .removeAttr( "aria-valuemin" )
11451 .removeAttr( "aria-valuemax" )
11452 .removeAttr( "aria-valuenow" );
11453 this.uiSpinner.replaceWith( this.element );
11456 stepUp: modifier(function( steps ) {
11457 this._stepUp( steps );
11459 _stepUp: function( steps ) {
11460 if ( this._start() ) {
11461 this._spin( (steps || 1) * this.options.step );
11466 stepDown: modifier(function( steps ) {
11467 this._stepDown( steps );
11469 _stepDown: function( steps ) {
11470 if ( this._start() ) {
11471 this._spin( (steps || 1) * -this.options.step );
11476 pageUp: modifier(function( pages ) {
11477 this._stepUp( (pages || 1) * this.options.page );
11480 pageDown: modifier(function( pages ) {
11481 this._stepDown( (pages || 1) * this.options.page );
11484 value: function( newVal ) {
11485 if ( !arguments.length ) {
11486 return this._parse( this.element.val() );
11488 modifier( this._value ).call( this, newVal );
11491 widget: function() {
11492 return this.uiSpinner;
11497 (function( $, undefined ) {
11502 function getNextTabId() {
11506 function isLocal( anchor ) {
11507 return anchor.hash.length > 1 &&
11508 decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
11509 decodeURIComponent( location.href.replace( rhash, "" ) );
11512 $.widget( "ui.tabs", {
11517 collapsible: false,
11519 heightStyle: "content",
11525 beforeActivate: null,
11530 _create: function() {
11532 options = this.options;
11534 this.running = false;
11537 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
11538 .toggleClass( "ui-tabs-collapsible", options.collapsible )
11539 // Prevent users from focusing disabled tabs via click
11540 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
11541 if ( $( this ).is( ".ui-state-disabled" ) ) {
11542 event.preventDefault();
11546 // Preventing the default action in mousedown doesn't prevent IE
11547 // from focusing the element, so if the anchor gets focused, blur.
11548 // We don't have to worry about focusing the previously focused
11549 // element since clicking on a non-focusable element should focus
11550 // the body anyway.
11551 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
11552 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
11557 this._processTabs();
11558 options.active = this._initialActive();
11560 // Take disabling tabs via class attribute from HTML
11561 // into account and update option properly.
11562 if ( $.isArray( options.disabled ) ) {
11563 options.disabled = $.unique( options.disabled.concat(
11564 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
11565 return that.tabs.index( li );
11570 // check for length avoids error when initializing empty list
11571 if ( this.options.active !== false && this.anchors.length ) {
11572 this.active = this._findActive( options.active );
11579 if ( this.active.length ) {
11580 this.load( options.active );
11584 _initialActive: function() {
11585 var active = this.options.active,
11586 collapsible = this.options.collapsible,
11587 locationHash = location.hash.substring( 1 );
11589 if ( active === null ) {
11590 // check the fragment identifier in the URL
11591 if ( locationHash ) {
11592 this.tabs.each(function( i, tab ) {
11593 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
11600 // check for a tab marked active via a class
11601 if ( active === null ) {
11602 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
11605 // no active tab, set to false
11606 if ( active === null || active === -1 ) {
11607 active = this.tabs.length ? 0 : false;
11611 // handle numbers: negative, out of range
11612 if ( active !== false ) {
11613 active = this.tabs.index( this.tabs.eq( active ) );
11614 if ( active === -1 ) {
11615 active = collapsible ? false : 0;
11619 // don't allow collapsible: false and active: false
11620 if ( !collapsible && active === false && this.anchors.length ) {
11627 _getCreateEventData: function() {
11630 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
11634 _tabKeydown: function( event ) {
11635 /*jshint maxcomplexity:15*/
11636 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
11637 selectedIndex = this.tabs.index( focusedTab ),
11638 goingForward = true;
11640 if ( this._handlePageNav( event ) ) {
11644 switch ( event.keyCode ) {
11645 case $.ui.keyCode.RIGHT:
11646 case $.ui.keyCode.DOWN:
11649 case $.ui.keyCode.UP:
11650 case $.ui.keyCode.LEFT:
11651 goingForward = false;
11654 case $.ui.keyCode.END:
11655 selectedIndex = this.anchors.length - 1;
11657 case $.ui.keyCode.HOME:
11660 case $.ui.keyCode.SPACE:
11661 // Activate only, no collapsing
11662 event.preventDefault();
11663 clearTimeout( this.activating );
11664 this._activate( selectedIndex );
11666 case $.ui.keyCode.ENTER:
11667 // Toggle (cancel delayed activation, allow collapsing)
11668 event.preventDefault();
11669 clearTimeout( this.activating );
11670 // Determine if we should collapse or activate
11671 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
11677 // Focus the appropriate tab, based on which key was pressed
11678 event.preventDefault();
11679 clearTimeout( this.activating );
11680 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
11682 // Navigating with control key will prevent automatic activation
11683 if ( !event.ctrlKey ) {
11684 // Update aria-selected immediately so that AT think the tab is already selected.
11685 // Otherwise AT may confuse the user by stating that they need to activate the tab,
11686 // but the tab will already be activated by the time the announcement finishes.
11687 focusedTab.attr( "aria-selected", "false" );
11688 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
11690 this.activating = this._delay(function() {
11691 this.option( "active", selectedIndex );
11696 _panelKeydown: function( event ) {
11697 if ( this._handlePageNav( event ) ) {
11701 // Ctrl+up moves focus to the current tab
11702 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
11703 event.preventDefault();
11704 this.active.focus();
11708 // Alt+page up/down moves focus to the previous/next tab (and activates)
11709 _handlePageNav: function( event ) {
11710 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
11711 this._activate( this._focusNextTab( this.options.active - 1, false ) );
11714 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
11715 this._activate( this._focusNextTab( this.options.active + 1, true ) );
11720 _findNextTab: function( index, goingForward ) {
11721 var lastTabIndex = this.tabs.length - 1;
11723 function constrain() {
11724 if ( index > lastTabIndex ) {
11728 index = lastTabIndex;
11733 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
11734 index = goingForward ? index + 1 : index - 1;
11740 _focusNextTab: function( index, goingForward ) {
11741 index = this._findNextTab( index, goingForward );
11742 this.tabs.eq( index ).focus();
11746 _setOption: function( key, value ) {
11747 if ( key === "active" ) {
11748 // _activate() will handle invalid values and update this.options
11749 this._activate( value );
11753 if ( key === "disabled" ) {
11754 // don't use the widget factory's disabled handling
11755 this._setupDisabled( value );
11759 this._super( key, value);
11761 if ( key === "collapsible" ) {
11762 this.element.toggleClass( "ui-tabs-collapsible", value );
11763 // Setting collapsible: false while collapsed; open first panel
11764 if ( !value && this.options.active === false ) {
11765 this._activate( 0 );
11769 if ( key === "event" ) {
11770 this._setupEvents( value );
11773 if ( key === "heightStyle" ) {
11774 this._setupHeightStyle( value );
11778 _tabId: function( tab ) {
11779 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
11782 _sanitizeSelector: function( hash ) {
11783 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
11786 refresh: function() {
11787 var options = this.options,
11788 lis = this.tablist.children( ":has(a[href])" );
11790 // get disabled tabs from class attribute from HTML
11791 // this will get converted to a boolean if needed in _refresh()
11792 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
11793 return lis.index( tab );
11796 this._processTabs();
11798 // was collapsed or no tabs
11799 if ( options.active === false || !this.anchors.length ) {
11800 options.active = false;
11802 // was active, but active tab is gone
11803 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
11804 // all remaining tabs are disabled
11805 if ( this.tabs.length === options.disabled.length ) {
11806 options.active = false;
11808 // activate previous tab
11810 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
11812 // was active, active tab still exists
11814 // make sure active index is correct
11815 options.active = this.tabs.index( this.active );
11821 _refresh: function() {
11822 this._setupDisabled( this.options.disabled );
11823 this._setupEvents( this.options.event );
11824 this._setupHeightStyle( this.options.heightStyle );
11826 this.tabs.not( this.active ).attr({
11827 "aria-selected": "false",
11830 this.panels.not( this._getPanelForTab( this.active ) )
11833 "aria-expanded": "false",
11834 "aria-hidden": "true"
11837 // Make sure one tab is in the tab order
11838 if ( !this.active.length ) {
11839 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
11842 .addClass( "ui-tabs-active ui-state-active" )
11844 "aria-selected": "true",
11847 this._getPanelForTab( this.active )
11850 "aria-expanded": "true",
11851 "aria-hidden": "false"
11856 _processTabs: function() {
11859 this.tablist = this._getList()
11860 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
11861 .attr( "role", "tablist" );
11863 this.tabs = this.tablist.find( "> li:has(a[href])" )
11864 .addClass( "ui-state-default ui-corner-top" )
11870 this.anchors = this.tabs.map(function() {
11871 return $( "a", this )[ 0 ];
11873 .addClass( "ui-tabs-anchor" )
11875 role: "presentation",
11881 this.anchors.each(function( i, anchor ) {
11882 var selector, panel, panelId,
11883 anchorId = $( anchor ).uniqueId().attr( "id" ),
11884 tab = $( anchor ).closest( "li" ),
11885 originalAriaControls = tab.attr( "aria-controls" );
11888 if ( isLocal( anchor ) ) {
11889 selector = anchor.hash;
11890 panel = that.element.find( that._sanitizeSelector( selector ) );
11893 panelId = that._tabId( tab );
11894 selector = "#" + panelId;
11895 panel = that.element.find( selector );
11896 if ( !panel.length ) {
11897 panel = that._createPanel( panelId );
11898 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
11900 panel.attr( "aria-live", "polite" );
11903 if ( panel.length) {
11904 that.panels = that.panels.add( panel );
11906 if ( originalAriaControls ) {
11907 tab.data( "ui-tabs-aria-controls", originalAriaControls );
11910 "aria-controls": selector.substring( 1 ),
11911 "aria-labelledby": anchorId
11913 panel.attr( "aria-labelledby", anchorId );
11917 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
11918 .attr( "role", "tabpanel" );
11921 // allow overriding how to find the list for rare usage scenarios (#7715)
11922 _getList: function() {
11923 return this.element.find( "ol,ul" ).eq( 0 );
11926 _createPanel: function( id ) {
11927 return $( "<div>" )
11929 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
11930 .data( "ui-tabs-destroy", true );
11933 _setupDisabled: function( disabled ) {
11934 if ( $.isArray( disabled ) ) {
11935 if ( !disabled.length ) {
11937 } else if ( disabled.length === this.anchors.length ) {
11943 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
11944 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
11946 .addClass( "ui-state-disabled" )
11947 .attr( "aria-disabled", "true" );
11950 .removeClass( "ui-state-disabled" )
11951 .removeAttr( "aria-disabled" );
11955 this.options.disabled = disabled;
11958 _setupEvents: function( event ) {
11960 click: function( event ) {
11961 event.preventDefault();
11965 $.each( event.split(" "), function( index, eventName ) {
11966 events[ eventName ] = "_eventHandler";
11970 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
11971 this._on( this.anchors, events );
11972 this._on( this.tabs, { keydown: "_tabKeydown" } );
11973 this._on( this.panels, { keydown: "_panelKeydown" } );
11975 this._focusable( this.tabs );
11976 this._hoverable( this.tabs );
11979 _setupHeightStyle: function( heightStyle ) {
11981 parent = this.element.parent();
11983 if ( heightStyle === "fill" ) {
11984 maxHeight = parent.height();
11985 maxHeight -= this.element.outerHeight() - this.element.height();
11987 this.element.siblings( ":visible" ).each(function() {
11988 var elem = $( this ),
11989 position = elem.css( "position" );
11991 if ( position === "absolute" || position === "fixed" ) {
11994 maxHeight -= elem.outerHeight( true );
11997 this.element.children().not( this.panels ).each(function() {
11998 maxHeight -= $( this ).outerHeight( true );
12001 this.panels.each(function() {
12002 $( this ).height( Math.max( 0, maxHeight -
12003 $( this ).innerHeight() + $( this ).height() ) );
12005 .css( "overflow", "auto" );
12006 } else if ( heightStyle === "auto" ) {
12008 this.panels.each(function() {
12009 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
12010 }).height( maxHeight );
12014 _eventHandler: function( event ) {
12015 var options = this.options,
12016 active = this.active,
12017 anchor = $( event.currentTarget ),
12018 tab = anchor.closest( "li" ),
12019 clickedIsActive = tab[ 0 ] === active[ 0 ],
12020 collapsing = clickedIsActive && options.collapsible,
12021 toShow = collapsing ? $() : this._getPanelForTab( tab ),
12022 toHide = !active.length ? $() : this._getPanelForTab( active ),
12026 newTab: collapsing ? $() : tab,
12030 event.preventDefault();
12032 if ( tab.hasClass( "ui-state-disabled" ) ||
12033 // tab is already loading
12034 tab.hasClass( "ui-tabs-loading" ) ||
12035 // can't switch durning an animation
12037 // click on active header, but not collapsible
12038 ( clickedIsActive && !options.collapsible ) ||
12039 // allow canceling activation
12040 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
12044 options.active = collapsing ? false : this.tabs.index( tab );
12046 this.active = clickedIsActive ? $() : tab;
12051 if ( !toHide.length && !toShow.length ) {
12052 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
12055 if ( toShow.length ) {
12056 this.load( this.tabs.index( tab ), event );
12058 this._toggle( event, eventData );
12061 // handles show/hide for selecting tabs
12062 _toggle: function( event, eventData ) {
12064 toShow = eventData.newPanel,
12065 toHide = eventData.oldPanel;
12067 this.running = true;
12069 function complete() {
12070 that.running = false;
12071 that._trigger( "activate", event, eventData );
12075 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
12077 if ( toShow.length && that.options.show ) {
12078 that._show( toShow, that.options.show, complete );
12085 // start out by hiding, then showing, then completing
12086 if ( toHide.length && this.options.hide ) {
12087 this._hide( toHide, this.options.hide, function() {
12088 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
12092 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
12098 "aria-expanded": "false",
12099 "aria-hidden": "true"
12101 eventData.oldTab.attr( "aria-selected", "false" );
12102 // If we're switching tabs, remove the old tab from the tab order.
12103 // If we're opening from collapsed state, remove the previous tab from the tab order.
12104 // If we're collapsing, then keep the collapsing tab in the tab order.
12105 if ( toShow.length && toHide.length ) {
12106 eventData.oldTab.attr( "tabIndex", -1 );
12107 } else if ( toShow.length ) {
12108 this.tabs.filter(function() {
12109 return $( this ).attr( "tabIndex" ) === 0;
12111 .attr( "tabIndex", -1 );
12115 "aria-expanded": "true",
12116 "aria-hidden": "false"
12118 eventData.newTab.attr({
12119 "aria-selected": "true",
12124 _activate: function( index ) {
12126 active = this._findActive( index );
12128 // trying to activate the already active panel
12129 if ( active[ 0 ] === this.active[ 0 ] ) {
12133 // trying to collapse, simulate a click on the current active header
12134 if ( !active.length ) {
12135 active = this.active;
12138 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
12139 this._eventHandler({
12141 currentTarget: anchor,
12142 preventDefault: $.noop
12146 _findActive: function( index ) {
12147 return index === false ? $() : this.tabs.eq( index );
12150 _getIndex: function( index ) {
12151 // meta-function to give users option to provide a href string instead of a numerical index.
12152 if ( typeof index === "string" ) {
12153 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
12159 _destroy: function() {
12164 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
12167 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
12168 .removeAttr( "role" );
12171 .removeClass( "ui-tabs-anchor" )
12172 .removeAttr( "role" )
12173 .removeAttr( "tabIndex" )
12176 this.tabs.add( this.panels ).each(function() {
12177 if ( $.data( this, "ui-tabs-destroy" ) ) {
12178 $( this ).remove();
12181 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
12182 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
12183 .removeAttr( "tabIndex" )
12184 .removeAttr( "aria-live" )
12185 .removeAttr( "aria-busy" )
12186 .removeAttr( "aria-selected" )
12187 .removeAttr( "aria-labelledby" )
12188 .removeAttr( "aria-hidden" )
12189 .removeAttr( "aria-expanded" )
12190 .removeAttr( "role" );
12194 this.tabs.each(function() {
12195 var li = $( this ),
12196 prev = li.data( "ui-tabs-aria-controls" );
12199 .attr( "aria-controls", prev )
12200 .removeData( "ui-tabs-aria-controls" );
12202 li.removeAttr( "aria-controls" );
12206 this.panels.show();
12208 if ( this.options.heightStyle !== "content" ) {
12209 this.panels.css( "height", "" );
12213 enable: function( index ) {
12214 var disabled = this.options.disabled;
12215 if ( disabled === false ) {
12219 if ( index === undefined ) {
12222 index = this._getIndex( index );
12223 if ( $.isArray( disabled ) ) {
12224 disabled = $.map( disabled, function( num ) {
12225 return num !== index ? num : null;
12228 disabled = $.map( this.tabs, function( li, num ) {
12229 return num !== index ? num : null;
12233 this._setupDisabled( disabled );
12236 disable: function( index ) {
12237 var disabled = this.options.disabled;
12238 if ( disabled === true ) {
12242 if ( index === undefined ) {
12245 index = this._getIndex( index );
12246 if ( $.inArray( index, disabled ) !== -1 ) {
12249 if ( $.isArray( disabled ) ) {
12250 disabled = $.merge( [ index ], disabled ).sort();
12252 disabled = [ index ];
12255 this._setupDisabled( disabled );
12258 load: function( index, event ) {
12259 index = this._getIndex( index );
12261 tab = this.tabs.eq( index ),
12262 anchor = tab.find( ".ui-tabs-anchor" ),
12263 panel = this._getPanelForTab( tab ),
12270 if ( isLocal( anchor[ 0 ] ) ) {
12274 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
12276 // support: jQuery <1.8
12277 // jQuery <1.8 returns false if the request is canceled in beforeSend,
12278 // but as of 1.8, $.ajax() always returns a jqXHR object.
12279 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
12280 tab.addClass( "ui-tabs-loading" );
12281 panel.attr( "aria-busy", "true" );
12284 .success(function( response ) {
12285 // support: jQuery <1.8
12286 // http://bugs.jquery.com/ticket/11778
12287 setTimeout(function() {
12288 panel.html( response );
12289 that._trigger( "load", event, eventData );
12292 .complete(function( jqXHR, status ) {
12293 // support: jQuery <1.8
12294 // http://bugs.jquery.com/ticket/11778
12295 setTimeout(function() {
12296 if ( status === "abort" ) {
12297 that.panels.stop( false, true );
12300 tab.removeClass( "ui-tabs-loading" );
12301 panel.removeAttr( "aria-busy" );
12303 if ( jqXHR === that.xhr ) {
12311 _ajaxSettings: function( anchor, event, eventData ) {
12314 url: anchor.attr( "href" ),
12315 beforeSend: function( jqXHR, settings ) {
12316 return that._trigger( "beforeLoad", event,
12317 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
12322 _getPanelForTab: function( tab ) {
12323 var id = $( tab ).attr( "aria-controls" );
12324 return this.element.find( this._sanitizeSelector( "#" + id ) );
12331 var increments = 0;
12333 function addDescribedBy( elem, id ) {
12334 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
12335 describedby.push( id );
12337 .data( "ui-tooltip-id", id )
12338 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
12341 function removeDescribedBy( elem ) {
12342 var id = elem.data( "ui-tooltip-id" ),
12343 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
12344 index = $.inArray( id, describedby );
12345 if ( index !== -1 ) {
12346 describedby.splice( index, 1 );
12349 elem.removeData( "ui-tooltip-id" );
12350 describedby = $.trim( describedby.join( " " ) );
12351 if ( describedby ) {
12352 elem.attr( "aria-describedby", describedby );
12354 elem.removeAttr( "aria-describedby" );
12358 $.widget( "ui.tooltip", {
12361 content: function() {
12362 // support: IE<9, Opera in jQuery <1.7
12363 // .text() can't accept undefined, so coerce to a string
12364 var title = $( this ).attr( "title" ) || "";
12365 // Escape title, since we're going from an attribute to raw HTML
12366 return $( "<a>" ).text( title ).html();
12369 // Disabled elements have inconsistent behavior across browsers (#8661)
12370 items: "[title]:not([disabled])",
12374 collision: "flipfit flip"
12377 tooltipClass: null,
12385 _create: function() {
12391 // IDs of generated tooltips, needed for destroy
12392 this.tooltips = {};
12393 // IDs of parent tooltips where we removed the title attribute
12396 if ( this.options.disabled ) {
12401 _setOption: function( key, value ) {
12404 if ( key === "disabled" ) {
12405 this[ value ? "_disable" : "_enable" ]();
12406 this.options[ key ] = value;
12407 // disable element style changes
12411 this._super( key, value );
12413 if ( key === "content" ) {
12414 $.each( this.tooltips, function( id, element ) {
12415 that._updateContent( element );
12420 _disable: function() {
12423 // close open tooltips
12424 $.each( this.tooltips, function( id, element ) {
12425 var event = $.Event( "blur" );
12426 event.target = event.currentTarget = element[0];
12427 that.close( event, true );
12430 // remove title attributes to prevent native tooltips
12431 this.element.find( this.options.items ).addBack().each(function() {
12432 var element = $( this );
12433 if ( element.is( "[title]" ) ) {
12435 .data( "ui-tooltip-title", element.attr( "title" ) )
12436 .attr( "title", "" );
12441 _enable: function() {
12442 // restore title attributes
12443 this.element.find( this.options.items ).addBack().each(function() {
12444 var element = $( this );
12445 if ( element.data( "ui-tooltip-title" ) ) {
12446 element.attr( "title", element.data( "ui-tooltip-title" ) );
12451 open: function( event ) {
12453 target = $( event ? event.target : this.element )
12454 // we need closest here due to mouseover bubbling,
12455 // but always pointing at the same event target
12456 .closest( this.options.items );
12458 // No element to show a tooltip for or the tooltip is already open
12459 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
12463 if ( target.attr( "title" ) ) {
12464 target.data( "ui-tooltip-title", target.attr( "title" ) );
12467 target.data( "ui-tooltip-open", true );
12469 // kill parent tooltips, custom or native, for hover
12470 if ( event && event.type === "mouseover" ) {
12471 target.parents().each(function() {
12472 var parent = $( this ),
12474 if ( parent.data( "ui-tooltip-open" ) ) {
12475 blurEvent = $.Event( "blur" );
12476 blurEvent.target = blurEvent.currentTarget = this;
12477 that.close( blurEvent, true );
12479 if ( parent.attr( "title" ) ) {
12481 that.parents[ this.id ] = {
12483 title: parent.attr( "title" )
12485 parent.attr( "title", "" );
12490 this._updateContent( target, event );
12493 _updateContent: function( target, event ) {
12495 contentOption = this.options.content,
12497 eventType = event ? event.type : null;
12499 if ( typeof contentOption === "string" ) {
12500 return this._open( event, target, contentOption );
12503 content = contentOption.call( target[0], function( response ) {
12504 // ignore async response if tooltip was closed already
12505 if ( !target.data( "ui-tooltip-open" ) ) {
12508 // IE may instantly serve a cached response for ajax requests
12509 // delay this call to _open so the other call to _open runs first
12510 that._delay(function() {
12511 // jQuery creates a special event for focusin when it doesn't
12512 // exist natively. To improve performance, the native event
12513 // object is reused and the type is changed. Therefore, we can't
12514 // rely on the type being correct after the event finished
12515 // bubbling, so we set it back to the previous value. (#8740)
12517 event.type = eventType;
12519 this._open( event, target, response );
12523 this._open( event, target, content );
12527 _open: function( event, target, content ) {
12528 var tooltip, events, delayedShow,
12529 positionOption = $.extend( {}, this.options.position );
12535 // Content can be updated multiple times. If the tooltip already
12536 // exists, then just update the content and bail.
12537 tooltip = this._find( target );
12538 if ( tooltip.length ) {
12539 tooltip.find( ".ui-tooltip-content" ).html( content );
12543 // if we have a title, clear it to prevent the native tooltip
12544 // we have to check first to avoid defining a title if none exists
12545 // (we don't want to cause an element to start matching [title])
12547 // We use removeAttr only for key events, to allow IE to export the correct
12548 // accessible attributes. For mouse events, set to empty string to avoid
12549 // native tooltip showing up (happens only when removing inside mouseover).
12550 if ( target.is( "[title]" ) ) {
12551 if ( event && event.type === "mouseover" ) {
12552 target.attr( "title", "" );
12554 target.removeAttr( "title" );
12558 tooltip = this._tooltip( target );
12559 addDescribedBy( target, tooltip.attr( "id" ) );
12560 tooltip.find( ".ui-tooltip-content" ).html( content );
12562 function position( event ) {
12563 positionOption.of = event;
12564 if ( tooltip.is( ":hidden" ) ) {
12567 tooltip.position( positionOption );
12569 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
12570 this._on( this.document, {
12571 mousemove: position
12573 // trigger once to override element-relative positioning
12576 tooltip.position( $.extend({
12578 }, this.options.position ) );
12583 this._show( tooltip, this.options.show );
12584 // Handle tracking tooltips that are shown with a delay (#8644). As soon
12585 // as the tooltip is visible, position the tooltip using the most recent
12587 if ( this.options.show && this.options.show.delay ) {
12588 delayedShow = this.delayedShow = setInterval(function() {
12589 if ( tooltip.is( ":visible" ) ) {
12590 position( positionOption.of );
12591 clearInterval( delayedShow );
12593 }, $.fx.interval );
12596 this._trigger( "open", event, { tooltip: tooltip } );
12599 keyup: function( event ) {
12600 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
12601 var fakeEvent = $.Event(event);
12602 fakeEvent.currentTarget = target[0];
12603 this.close( fakeEvent, true );
12606 remove: function() {
12607 this._removeTooltip( tooltip );
12610 if ( !event || event.type === "mouseover" ) {
12611 events.mouseleave = "close";
12613 if ( !event || event.type === "focusin" ) {
12614 events.focusout = "close";
12616 this._on( true, target, events );
12619 close: function( event ) {
12621 target = $( event ? event.currentTarget : this.element ),
12622 tooltip = this._find( target );
12624 // disabling closes the tooltip, so we need to track when we're closing
12625 // to avoid an infinite loop in case the tooltip becomes disabled on close
12626 if ( this.closing ) {
12630 // Clear the interval for delayed tracking tooltips
12631 clearInterval( this.delayedShow );
12633 // only set title if we had one before (see comment in _open())
12634 if ( target.data( "ui-tooltip-title" ) ) {
12635 target.attr( "title", target.data( "ui-tooltip-title" ) );
12638 removeDescribedBy( target );
12640 tooltip.stop( true );
12641 this._hide( tooltip, this.options.hide, function() {
12642 that._removeTooltip( $( this ) );
12645 target.removeData( "ui-tooltip-open" );
12646 this._off( target, "mouseleave focusout keyup" );
12647 // Remove 'remove' binding only on delegated targets
12648 if ( target[0] !== this.element[0] ) {
12649 this._off( target, "remove" );
12651 this._off( this.document, "mousemove" );
12653 if ( event && event.type === "mouseleave" ) {
12654 $.each( this.parents, function( id, parent ) {
12655 $( parent.element ).attr( "title", parent.title );
12656 delete that.parents[ id ];
12660 this.closing = true;
12661 this._trigger( "close", event, { tooltip: tooltip } );
12662 this.closing = false;
12665 _tooltip: function( element ) {
12666 var id = "ui-tooltip-" + increments++,
12667 tooltip = $( "<div>" )
12672 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
12673 ( this.options.tooltipClass || "" ) );
12675 .addClass( "ui-tooltip-content" )
12676 .appendTo( tooltip );
12677 tooltip.appendTo( this.document[0].body );
12678 this.tooltips[ id ] = element;
12682 _find: function( target ) {
12683 var id = target.data( "ui-tooltip-id" );
12684 return id ? $( "#" + id ) : $();
12687 _removeTooltip: function( tooltip ) {
12689 delete this.tooltips[ tooltip.attr( "id" ) ];
12692 _destroy: function() {
12695 // close open tooltips
12696 $.each( this.tooltips, function( id, element ) {
12697 // Delegate to close method to handle common cleanup
12698 var event = $.Event( "blur" );
12699 event.target = event.currentTarget = element[0];
12700 that.close( event, true );
12702 // Remove immediately; destroying an open tooltip doesn't use the
12704 $( "#" + id ).remove();
12706 // Restore the title
12707 if ( element.data( "ui-tooltip-title" ) ) {
12708 element.attr( "title", element.data( "ui-tooltip-title" ) );
12709 element.removeData( "ui-tooltip-title" );
12716 (function($, undefined) {
12718 var dataSpace = "ui-effects-";
12725 * jQuery Color Animations v2.1.2
12726 * https://github.com/jquery/jquery-color
12728 * Copyright 2013 jQuery Foundation and other contributors
12729 * Released under the MIT license.
12730 * http://jquery.org/license
12732 * Date: Wed Jan 16 08:47:09 2013 -0600
12734 (function( jQuery, undefined ) {
12736 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
12738 // plusequals test for += 100 -= 100
12739 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
12740 // a set of RE's that can match strings and generate color tuples.
12742 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
12743 parse: function( execResult ) {
12752 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
12753 parse: function( execResult ) {
12755 execResult[ 1 ] * 2.55,
12756 execResult[ 2 ] * 2.55,
12757 execResult[ 3 ] * 2.55,
12762 // this regex ignores A-F because it's compared against an already lowercased string
12763 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
12764 parse: function( execResult ) {
12766 parseInt( execResult[ 1 ], 16 ),
12767 parseInt( execResult[ 2 ], 16 ),
12768 parseInt( execResult[ 3 ], 16 )
12772 // this regex ignores A-F because it's compared against an already lowercased string
12773 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
12774 parse: function( execResult ) {
12776 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
12777 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
12778 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
12782 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
12784 parse: function( execResult ) {
12787 execResult[ 2 ] / 100,
12788 execResult[ 3 ] / 100,
12795 color = jQuery.Color = function( color, green, blue, alpha ) {
12796 return new jQuery.Color.fn.parse( color, green, blue, alpha );
12846 support = color.support = {},
12848 // element for support tests
12849 supportElem = jQuery( "<p>" )[ 0 ],
12851 // colors = jQuery.Color.names
12854 // local aliases of functions called often
12855 each = jQuery.each;
12857 // determine rgba support immediately
12858 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
12859 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
12861 // define cache name and alpha properties
12862 // for rgba and hsla spaces
12863 each( spaces, function( spaceName, space ) {
12864 space.cache = "_" + spaceName;
12865 space.props.alpha = {
12872 function clamp( value, prop, allowEmpty ) {
12873 var type = propTypes[ prop.type ] || {};
12875 if ( value == null ) {
12876 return (allowEmpty || !prop.def) ? null : prop.def;
12879 // ~~ is an short way of doing floor for positive numbers
12880 value = type.floor ? ~~value : parseFloat( value );
12882 // IE will pass in empty strings as value for alpha,
12883 // which will hit this case
12884 if ( isNaN( value ) ) {
12889 // we add mod before modding to make sure that negatives values
12890 // get converted properly: -10 -> 350
12891 return (value + type.mod) % type.mod;
12894 // for now all property types without mod have min and max
12895 return 0 > value ? 0 : type.max < value ? type.max : value;
12898 function stringParse( string ) {
12899 var inst = color(),
12900 rgba = inst._rgba = [];
12902 string = string.toLowerCase();
12904 each( stringParsers, function( i, parser ) {
12906 match = parser.re.exec( string ),
12907 values = match && parser.parse( match ),
12908 spaceName = parser.space || "rgba";
12911 parsed = inst[ spaceName ]( values );
12913 // if this was an rgba parse the assignment might happen twice
12915 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
12916 rgba = inst._rgba = parsed._rgba;
12918 // exit each( stringParsers ) here because we matched
12923 // Found a stringParser that handled it
12924 if ( rgba.length ) {
12926 // if this came from a parsed string, force "transparent" when alpha is 0
12927 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
12928 if ( rgba.join() === "0,0,0,0" ) {
12929 jQuery.extend( rgba, colors.transparent );
12935 return colors[ string ];
12938 color.fn = jQuery.extend( color.prototype, {
12939 parse: function( red, green, blue, alpha ) {
12940 if ( red === undefined ) {
12941 this._rgba = [ null, null, null, null ];
12944 if ( red.jquery || red.nodeType ) {
12945 red = jQuery( red ).css( green );
12950 type = jQuery.type( red ),
12951 rgba = this._rgba = [];
12953 // more than 1 argument specified - assume ( red, green, blue, alpha )
12954 if ( green !== undefined ) {
12955 red = [ red, green, blue, alpha ];
12959 if ( type === "string" ) {
12960 return this.parse( stringParse( red ) || colors._default );
12963 if ( type === "array" ) {
12964 each( spaces.rgba.props, function( key, prop ) {
12965 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
12970 if ( type === "object" ) {
12971 if ( red instanceof color ) {
12972 each( spaces, function( spaceName, space ) {
12973 if ( red[ space.cache ] ) {
12974 inst[ space.cache ] = red[ space.cache ].slice();
12978 each( spaces, function( spaceName, space ) {
12979 var cache = space.cache;
12980 each( space.props, function( key, prop ) {
12982 // if the cache doesn't exist, and we know how to convert
12983 if ( !inst[ cache ] && space.to ) {
12985 // if the value was null, we don't need to copy it
12986 // if the key was alpha, we don't need to copy it either
12987 if ( key === "alpha" || red[ key ] == null ) {
12990 inst[ cache ] = space.to( inst._rgba );
12993 // this is the only case where we allow nulls for ALL properties.
12994 // call clamp with alwaysAllowEmpty
12995 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
12998 // everything defined but alpha?
12999 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
13000 // use the default of 1
13001 inst[ cache ][ 3 ] = 1;
13002 if ( space.from ) {
13003 inst._rgba = space.from( inst[ cache ] );
13011 is: function( compare ) {
13012 var is = color( compare ),
13016 each( spaces, function( _, space ) {
13018 isCache = is[ space.cache ];
13020 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
13021 each( space.props, function( _, prop ) {
13022 if ( isCache[ prop.idx ] != null ) {
13023 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
13032 _space: function() {
13035 each( spaces, function( spaceName, space ) {
13036 if ( inst[ space.cache ] ) {
13037 used.push( spaceName );
13042 transition: function( other, distance ) {
13043 var end = color( other ),
13044 spaceName = end._space(),
13045 space = spaces[ spaceName ],
13046 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
13047 start = startColor[ space.cache ] || space.to( startColor._rgba ),
13048 result = start.slice();
13050 end = end[ space.cache ];
13051 each( space.props, function( key, prop ) {
13052 var index = prop.idx,
13053 startValue = start[ index ],
13054 endValue = end[ index ],
13055 type = propTypes[ prop.type ] || {};
13057 // if null, don't override start value
13058 if ( endValue === null ) {
13061 // if null - use end
13062 if ( startValue === null ) {
13063 result[ index ] = endValue;
13066 if ( endValue - startValue > type.mod / 2 ) {
13067 startValue += type.mod;
13068 } else if ( startValue - endValue > type.mod / 2 ) {
13069 startValue -= type.mod;
13072 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
13075 return this[ spaceName ]( result );
13077 blend: function( opaque ) {
13078 // if we are already opaque - return ourself
13079 if ( this._rgba[ 3 ] === 1 ) {
13083 var rgb = this._rgba.slice(),
13085 blend = color( opaque )._rgba;
13087 return color( jQuery.map( rgb, function( v, i ) {
13088 return ( 1 - a ) * blend[ i ] + a * v;
13091 toRgbaString: function() {
13092 var prefix = "rgba(",
13093 rgba = jQuery.map( this._rgba, function( v, i ) {
13094 return v == null ? ( i > 2 ? 1 : 0 ) : v;
13097 if ( rgba[ 3 ] === 1 ) {
13102 return prefix + rgba.join() + ")";
13104 toHslaString: function() {
13105 var prefix = "hsla(",
13106 hsla = jQuery.map( this.hsla(), function( v, i ) {
13112 if ( i && i < 3 ) {
13113 v = Math.round( v * 100 ) + "%";
13118 if ( hsla[ 3 ] === 1 ) {
13122 return prefix + hsla.join() + ")";
13124 toHexString: function( includeAlpha ) {
13125 var rgba = this._rgba.slice(),
13126 alpha = rgba.pop();
13128 if ( includeAlpha ) {
13129 rgba.push( ~~( alpha * 255 ) );
13132 return "#" + jQuery.map( rgba, function( v ) {
13134 // default to 0 when nulls exist
13135 v = ( v || 0 ).toString( 16 );
13136 return v.length === 1 ? "0" + v : v;
13139 toString: function() {
13140 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
13143 color.fn.parse.prototype = color.fn;
13145 // hsla conversions adapted from:
13146 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
13148 function hue2rgb( p, q, h ) {
13151 return p + (q - p) * h * 6;
13157 return p + (q - p) * ((2/3) - h) * 6;
13162 spaces.hsla.to = function ( rgba ) {
13163 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
13164 return [ null, null, null, rgba[ 3 ] ];
13166 var r = rgba[ 0 ] / 255,
13167 g = rgba[ 1 ] / 255,
13168 b = rgba[ 2 ] / 255,
13170 max = Math.max( r, g, b ),
13171 min = Math.min( r, g, b ),
13177 if ( min === max ) {
13179 } else if ( r === max ) {
13180 h = ( 60 * ( g - b ) / diff ) + 360;
13181 } else if ( g === max ) {
13182 h = ( 60 * ( b - r ) / diff ) + 120;
13184 h = ( 60 * ( r - g ) / diff ) + 240;
13187 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
13188 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
13189 if ( diff === 0 ) {
13191 } else if ( l <= 0.5 ) {
13194 s = diff / ( 2 - add );
13196 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
13199 spaces.hsla.from = function ( hsla ) {
13200 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
13201 return [ null, null, null, hsla[ 3 ] ];
13203 var h = hsla[ 0 ] / 360,
13207 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
13211 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
13212 Math.round( hue2rgb( p, q, h ) * 255 ),
13213 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
13219 each( spaces, function( spaceName, space ) {
13220 var props = space.props,
13221 cache = space.cache,
13225 // makes rgba() and hsla()
13226 color.fn[ spaceName ] = function( value ) {
13228 // generate a cache for this space if it doesn't exist
13229 if ( to && !this[ cache ] ) {
13230 this[ cache ] = to( this._rgba );
13232 if ( value === undefined ) {
13233 return this[ cache ].slice();
13237 type = jQuery.type( value ),
13238 arr = ( type === "array" || type === "object" ) ? value : arguments,
13239 local = this[ cache ].slice();
13241 each( props, function( key, prop ) {
13242 var val = arr[ type === "object" ? key : prop.idx ];
13243 if ( val == null ) {
13244 val = local[ prop.idx ];
13246 local[ prop.idx ] = clamp( val, prop );
13250 ret = color( from( local ) );
13251 ret[ cache ] = local;
13254 return color( local );
13258 // makes red() green() blue() alpha() hue() saturation() lightness()
13259 each( props, function( key, prop ) {
13260 // alpha is included in more than one space
13261 if ( color.fn[ key ] ) {
13264 color.fn[ key ] = function( value ) {
13265 var vtype = jQuery.type( value ),
13266 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
13267 local = this[ fn ](),
13268 cur = local[ prop.idx ],
13271 if ( vtype === "undefined" ) {
13275 if ( vtype === "function" ) {
13276 value = value.call( this, cur );
13277 vtype = jQuery.type( value );
13279 if ( value == null && prop.empty ) {
13282 if ( vtype === "string" ) {
13283 match = rplusequals.exec( value );
13285 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
13288 local[ prop.idx ] = value;
13289 return this[ fn ]( local );
13294 // add cssHook and .fx.step function for each named hook.
13295 // accept a space separated string of properties
13296 color.hook = function( hook ) {
13297 var hooks = hook.split( " " );
13298 each( hooks, function( i, hook ) {
13299 jQuery.cssHooks[ hook ] = {
13300 set: function( elem, value ) {
13301 var parsed, curElem,
13302 backgroundColor = "";
13304 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
13305 value = color( parsed || value );
13306 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
13307 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
13309 (backgroundColor === "" || backgroundColor === "transparent") &&
13310 curElem && curElem.style
13313 backgroundColor = jQuery.css( curElem, "backgroundColor" );
13314 curElem = curElem.parentNode;
13319 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
13324 value = value.toRgbaString();
13327 elem.style[ hook ] = value;
13329 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
13333 jQuery.fx.step[ hook ] = function( fx ) {
13334 if ( !fx.colorInit ) {
13335 fx.start = color( fx.elem, hook );
13336 fx.end = color( fx.end );
13337 fx.colorInit = true;
13339 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
13345 color.hook( stepHooks );
13347 jQuery.cssHooks.borderColor = {
13348 expand: function( value ) {
13351 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
13352 expanded[ "border" + part + "Color" ] = value;
13358 // Basic color names only.
13359 // Usage of any of the other color names requires adding yourself or including
13360 // jquery.color.svg-names.js.
13361 colors = jQuery.Color.names = {
13362 // 4.1. Basic color keywords
13366 fuchsia: "#ff00ff",
13380 // 4.2.3. "transparent" color keyword
13381 transparent: [ null, null, null, 0 ],
13383 _default: "#ffffff"
13389 /******************************************************************************/
13390 /****************************** CLASS ANIMATIONS ******************************/
13391 /******************************************************************************/
13394 var classAnimationActions = [ "add", "remove", "toggle" ],
13395 shorthandStyles = {
13407 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
13408 $.fx.step[ prop ] = function( fx ) {
13409 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
13410 jQuery.style( fx.elem, prop, fx.end );
13416 function getElementStyles( elem ) {
13418 style = elem.ownerDocument.defaultView ?
13419 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
13423 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
13424 len = style.length;
13426 key = style[ len ];
13427 if ( typeof style[ key ] === "string" ) {
13428 styles[ $.camelCase( key ) ] = style[ key ];
13431 // support: Opera, IE <9
13433 for ( key in style ) {
13434 if ( typeof style[ key ] === "string" ) {
13435 styles[ key ] = style[ key ];
13444 function styleDifference( oldStyle, newStyle ) {
13448 for ( name in newStyle ) {
13449 value = newStyle[ name ];
13450 if ( oldStyle[ name ] !== value ) {
13451 if ( !shorthandStyles[ name ] ) {
13452 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
13453 diff[ name ] = value;
13462 // support: jQuery <1.8
13463 if ( !$.fn.addBack ) {
13464 $.fn.addBack = function( selector ) {
13465 return this.add( selector == null ?
13466 this.prevObject : this.prevObject.filter( selector )
13471 $.effects.animateClass = function( value, duration, easing, callback ) {
13472 var o = $.speed( duration, easing, callback );
13474 return this.queue( function() {
13475 var animated = $( this ),
13476 baseClass = animated.attr( "class" ) || "",
13478 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
13480 // map the animated objects to store the original styles.
13481 allAnimations = allAnimations.map(function() {
13482 var el = $( this );
13485 start: getElementStyles( this )
13489 // apply class change
13490 applyClassChange = function() {
13491 $.each( classAnimationActions, function(i, action) {
13492 if ( value[ action ] ) {
13493 animated[ action + "Class" ]( value[ action ] );
13497 applyClassChange();
13499 // map all animated objects again - calculate new styles and diff
13500 allAnimations = allAnimations.map(function() {
13501 this.end = getElementStyles( this.el[ 0 ] );
13502 this.diff = styleDifference( this.start, this.end );
13506 // apply original class
13507 animated.attr( "class", baseClass );
13509 // map all animated objects again - this time collecting a promise
13510 allAnimations = allAnimations.map(function() {
13511 var styleInfo = this,
13512 dfd = $.Deferred(),
13513 opts = $.extend({}, o, {
13515 complete: function() {
13516 dfd.resolve( styleInfo );
13520 this.el.animate( this.diff, opts );
13521 return dfd.promise();
13524 // once all animations have completed:
13525 $.when.apply( $, allAnimations.get() ).done(function() {
13527 // set the final class
13528 applyClassChange();
13530 // for each animated element,
13531 // clear all css properties that were animated
13532 $.each( arguments, function() {
13534 $.each( this.diff, function(key) {
13539 // this is guarnteed to be there if you use jQuery.speed()
13540 // it also handles dequeuing the next anim...
13541 o.complete.call( animated[ 0 ] );
13547 addClass: (function( orig ) {
13548 return function( classNames, speed, easing, callback ) {
13550 $.effects.animateClass.call( this,
13551 { add: classNames }, speed, easing, callback ) :
13552 orig.apply( this, arguments );
13554 })( $.fn.addClass ),
13556 removeClass: (function( orig ) {
13557 return function( classNames, speed, easing, callback ) {
13558 return arguments.length > 1 ?
13559 $.effects.animateClass.call( this,
13560 { remove: classNames }, speed, easing, callback ) :
13561 orig.apply( this, arguments );
13563 })( $.fn.removeClass ),
13565 toggleClass: (function( orig ) {
13566 return function( classNames, force, speed, easing, callback ) {
13567 if ( typeof force === "boolean" || force === undefined ) {
13569 // without speed parameter
13570 return orig.apply( this, arguments );
13572 return $.effects.animateClass.call( this,
13573 (force ? { add: classNames } : { remove: classNames }),
13574 speed, easing, callback );
13577 // without force parameter
13578 return $.effects.animateClass.call( this,
13579 { toggle: classNames }, force, speed, easing );
13582 })( $.fn.toggleClass ),
13584 switchClass: function( remove, add, speed, easing, callback) {
13585 return $.effects.animateClass.call( this, {
13588 }, speed, easing, callback );
13594 /******************************************************************************/
13595 /*********************************** EFFECTS **********************************/
13596 /******************************************************************************/
13600 $.extend( $.effects, {
13603 // Saves a set of properties in a data storage
13604 save: function( element, set ) {
13605 for( var i=0; i < set.length; i++ ) {
13606 if ( set[ i ] !== null ) {
13607 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
13612 // Restores a set of previously saved properties from a data storage
13613 restore: function( element, set ) {
13615 for( i=0; i < set.length; i++ ) {
13616 if ( set[ i ] !== null ) {
13617 val = element.data( dataSpace + set[ i ] );
13618 // support: jQuery 1.6.2
13619 // http://bugs.jquery.com/ticket/9917
13620 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
13621 // We can't differentiate between "" and 0 here, so we just assume
13622 // empty string since it's likely to be a more common value...
13623 if ( val === undefined ) {
13626 element.css( set[ i ], val );
13631 setMode: function( el, mode ) {
13632 if (mode === "toggle") {
13633 mode = el.is( ":hidden" ) ? "show" : "hide";
13638 // Translates a [top,left] array into a baseline value
13639 // this should be a little more flexible in the future to handle a string & hash
13640 getBaseline: function( origin, original ) {
13642 switch ( origin[ 0 ] ) {
13643 case "top": y = 0; break;
13644 case "middle": y = 0.5; break;
13645 case "bottom": y = 1; break;
13646 default: y = origin[ 0 ] / original.height;
13648 switch ( origin[ 1 ] ) {
13649 case "left": x = 0; break;
13650 case "center": x = 0.5; break;
13651 case "right": x = 1; break;
13652 default: x = origin[ 1 ] / original.width;
13660 // Wraps the element around a wrapper that copies position properties
13661 createWrapper: function( element ) {
13663 // if the element is already wrapped, return it
13664 if ( element.parent().is( ".ui-effects-wrapper" )) {
13665 return element.parent();
13668 // wrap the element
13670 width: element.outerWidth(true),
13671 height: element.outerHeight(true),
13672 "float": element.css( "float" )
13674 wrapper = $( "<div></div>" )
13675 .addClass( "ui-effects-wrapper" )
13678 background: "transparent",
13683 // Store the size in case width/height are defined in % - Fixes #5245
13685 width: element.width(),
13686 height: element.height()
13688 active = document.activeElement;
13690 // support: Firefox
13691 // Firefox incorrectly exposes anonymous content
13692 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
13696 active = document.body;
13699 element.wrap( wrapper );
13701 // Fixes #7595 - Elements lose focus when wrapped.
13702 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
13703 $( active ).focus();
13706 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
13708 // transfer positioning properties to the wrapper
13709 if ( element.css( "position" ) === "static" ) {
13710 wrapper.css({ position: "relative" });
13711 element.css({ position: "relative" });
13714 position: element.css( "position" ),
13715 zIndex: element.css( "z-index" )
13717 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
13718 props[ pos ] = element.css( pos );
13719 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
13720 props[ pos ] = "auto";
13724 position: "relative",
13733 return wrapper.css( props ).show();
13736 removeWrapper: function( element ) {
13737 var active = document.activeElement;
13739 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
13740 element.parent().replaceWith( element );
13742 // Fixes #7595 - Elements lose focus when wrapped.
13743 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
13744 $( active ).focus();
13752 setTransition: function( element, list, factor, value ) {
13753 value = value || {};
13754 $.each( list, function( i, x ) {
13755 var unit = element.cssUnit( x );
13756 if ( unit[ 0 ] > 0 ) {
13757 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
13764 // return an effect options object for the given parameters:
13765 function _normalizeArguments( effect, options, speed, callback ) {
13767 // allow passing all options as the first parameter
13768 if ( $.isPlainObject( effect ) ) {
13770 effect = effect.effect;
13773 // convert to an object
13774 effect = { effect: effect };
13776 // catch (effect, null, ...)
13777 if ( options == null ) {
13781 // catch (effect, callback)
13782 if ( $.isFunction( options ) ) {
13783 callback = options;
13788 // catch (effect, speed, ?)
13789 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
13795 // catch (effect, options, callback)
13796 if ( $.isFunction( speed ) ) {
13801 // add options to effect
13803 $.extend( effect, options );
13806 speed = speed || options.duration;
13807 effect.duration = $.fx.off ? 0 :
13808 typeof speed === "number" ? speed :
13809 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
13810 $.fx.speeds._default;
13812 effect.complete = callback || options.complete;
13817 function standardAnimationOption( option ) {
13818 // Valid standard speeds (nothing, number, named speed)
13819 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
13823 // Invalid strings - treat as "normal" speed
13824 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
13828 // Complete callback
13829 if ( $.isFunction( option ) ) {
13833 // Options hash (but not naming an effect)
13834 if ( typeof option === "object" && !option.effect ) {
13838 // Didn't match any standard API
13843 effect: function( /* effect, options, speed, callback */ ) {
13844 var args = _normalizeArguments.apply( this, arguments ),
13846 queue = args.queue,
13847 effectMethod = $.effects.effect[ args.effect ];
13849 if ( $.fx.off || !effectMethod ) {
13850 // delegate to the original method (e.g., .show()) if possible
13852 return this[ mode ]( args.duration, args.complete );
13854 return this.each( function() {
13855 if ( args.complete ) {
13856 args.complete.call( this );
13862 function run( next ) {
13863 var elem = $( this ),
13864 complete = args.complete,
13868 if ( $.isFunction( complete ) ) {
13869 complete.call( elem[0] );
13871 if ( $.isFunction( next ) ) {
13876 // If the element already has the correct final state, delegate to
13877 // the core methods so the internal tracking of "olddisplay" works.
13878 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
13882 effectMethod.call( elem[0], args, done );
13886 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
13889 show: (function( orig ) {
13890 return function( option ) {
13891 if ( standardAnimationOption( option ) ) {
13892 return orig.apply( this, arguments );
13894 var args = _normalizeArguments.apply( this, arguments );
13895 args.mode = "show";
13896 return this.effect.call( this, args );
13901 hide: (function( orig ) {
13902 return function( option ) {
13903 if ( standardAnimationOption( option ) ) {
13904 return orig.apply( this, arguments );
13906 var args = _normalizeArguments.apply( this, arguments );
13907 args.mode = "hide";
13908 return this.effect.call( this, args );
13913 toggle: (function( orig ) {
13914 return function( option ) {
13915 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
13916 return orig.apply( this, arguments );
13918 var args = _normalizeArguments.apply( this, arguments );
13919 args.mode = "toggle";
13920 return this.effect.call( this, args );
13925 // helper functions
13926 cssUnit: function(key) {
13927 var style = this.css( key ),
13930 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
13931 if ( style.indexOf( unit ) > 0 ) {
13932 val = [ parseFloat( style ), unit ];
13941 /******************************************************************************/
13942 /*********************************** EASING ***********************************/
13943 /******************************************************************************/
13947 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
13949 var baseEasings = {};
13951 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
13952 baseEasings[ name ] = function( p ) {
13953 return Math.pow( p, i + 2 );
13957 $.extend( baseEasings, {
13958 Sine: function ( p ) {
13959 return 1 - Math.cos( p * Math.PI / 2 );
13961 Circ: function ( p ) {
13962 return 1 - Math.sqrt( 1 - p * p );
13964 Elastic: function( p ) {
13965 return p === 0 || p === 1 ? p :
13966 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
13968 Back: function( p ) {
13969 return p * p * ( 3 * p - 2 );
13971 Bounce: function ( p ) {
13975 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
13976 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
13980 $.each( baseEasings, function( name, easeIn ) {
13981 $.easing[ "easeIn" + name ] = easeIn;
13982 $.easing[ "easeOut" + name ] = function( p ) {
13983 return 1 - easeIn( 1 - p );
13985 $.easing[ "easeInOut" + name ] = function( p ) {
13987 easeIn( p * 2 ) / 2 :
13988 1 - easeIn( p * -2 + 2 ) / 2;
13995 (function( $, undefined ) {
13997 var rvertical = /up|down|vertical/,
13998 rpositivemotion = /up|left|vertical|horizontal/;
14000 $.effects.effect.blind = function( o, done ) {
14002 var el = $( this ),
14003 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14004 mode = $.effects.setMode( el, o.mode || "hide" ),
14005 direction = o.direction || "up",
14006 vertical = rvertical.test( direction ),
14007 ref = vertical ? "height" : "width",
14008 ref2 = vertical ? "top" : "left",
14009 motion = rpositivemotion.test( direction ),
14011 show = mode === "show",
14012 wrapper, distance, margin;
14014 // if already wrapped, the wrapper's properties are my property. #6245
14015 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
14016 $.effects.save( el.parent(), props );
14018 $.effects.save( el, props );
14021 wrapper = $.effects.createWrapper( el ).css({
14025 distance = wrapper[ ref ]();
14026 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
14028 animation[ ref ] = show ? distance : 0;
14031 .css( vertical ? "bottom" : "right", 0 )
14032 .css( vertical ? "top" : "left", "auto" )
14033 .css({ position: "absolute" });
14035 animation[ ref2 ] = show ? margin : distance + margin;
14038 // start at 0 if we are showing
14040 wrapper.css( ref, 0 );
14042 wrapper.css( ref2, margin + distance );
14047 wrapper.animate( animation, {
14048 duration: o.duration,
14051 complete: function() {
14052 if ( mode === "hide" ) {
14055 $.effects.restore( el, props );
14056 $.effects.removeWrapper( el );
14064 (function( $, undefined ) {
14066 $.effects.effect.bounce = function( o, done ) {
14067 var el = $( this ),
14068 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14071 mode = $.effects.setMode( el, o.mode || "effect" ),
14072 hide = mode === "hide",
14073 show = mode === "show",
14074 direction = o.direction || "up",
14075 distance = o.distance,
14076 times = o.times || 5,
14078 // number of internal animations
14079 anims = times * 2 + ( show || hide ? 1 : 0 ),
14080 speed = o.duration / anims,
14084 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
14085 motion = ( direction === "up" || direction === "left" ),
14090 // we will need to re-assemble the queue to stack our animations in place
14091 queue = el.queue(),
14092 queuelen = queue.length;
14094 // Avoid touching opacity to prevent clearType and PNG issues in IE
14095 if ( show || hide ) {
14096 props.push( "opacity" );
14099 $.effects.save( el, props );
14101 $.effects.createWrapper( el ); // Create Wrapper
14103 // default distance for the BIGGEST bounce is the outer Distance / 3
14105 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
14109 downAnim = { opacity: 1 };
14110 downAnim[ ref ] = 0;
14112 // if we are showing, force opacity 0 and set the initial position
14113 // then do the "first" animation
14114 el.css( "opacity", 0 )
14115 .css( ref, motion ? -distance * 2 : distance * 2 )
14116 .animate( downAnim, speed, easing );
14119 // start at the smallest distance if we are hiding
14121 distance = distance / Math.pow( 2, times - 1 );
14125 downAnim[ ref ] = 0;
14126 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
14127 for ( i = 0; i < times; i++ ) {
14129 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
14131 el.animate( upAnim, speed, easing )
14132 .animate( downAnim, speed, easing );
14134 distance = hide ? distance * 2 : distance / 2;
14137 // Last Bounce when Hiding
14139 upAnim = { opacity: 0 };
14140 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
14142 el.animate( upAnim, speed, easing );
14145 el.queue(function() {
14149 $.effects.restore( el, props );
14150 $.effects.removeWrapper( el );
14154 // inject all the animations we just queued to be first in line (after "inprogress")
14155 if ( queuelen > 1) {
14156 queue.splice.apply( queue,
14157 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
14164 (function( $, undefined ) {
14166 $.effects.effect.clip = function( o, done ) {
14168 var el = $( this ),
14169 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14170 mode = $.effects.setMode( el, o.mode || "hide" ),
14171 show = mode === "show",
14172 direction = o.direction || "vertical",
14173 vert = direction === "vertical",
14174 size = vert ? "height" : "width",
14175 position = vert ? "top" : "left",
14177 wrapper, animate, distance;
14180 $.effects.save( el, props );
14184 wrapper = $.effects.createWrapper( el ).css({
14187 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
14188 distance = animate[ size ]();
14192 animate.css( size, 0 );
14193 animate.css( position, distance / 2 );
14196 // Create Animation Object:
14197 animation[ size ] = show ? distance : 0;
14198 animation[ position ] = show ? 0 : distance / 2;
14201 animate.animate( animation, {
14203 duration: o.duration,
14205 complete: function() {
14209 $.effects.restore( el, props );
14210 $.effects.removeWrapper( el );
14218 (function( $, undefined ) {
14220 $.effects.effect.drop = function( o, done ) {
14222 var el = $( this ),
14223 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
14224 mode = $.effects.setMode( el, o.mode || "hide" ),
14225 show = mode === "show",
14226 direction = o.direction || "left",
14227 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
14228 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
14230 opacity: show ? 1 : 0
14235 $.effects.save( el, props );
14237 $.effects.createWrapper( el );
14239 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
14243 .css( "opacity", 0 )
14244 .css( ref, motion === "pos" ? -distance : distance );
14248 animation[ ref ] = ( show ?
14249 ( motion === "pos" ? "+=" : "-=" ) :
14250 ( motion === "pos" ? "-=" : "+=" ) ) +
14254 el.animate( animation, {
14256 duration: o.duration,
14258 complete: function() {
14259 if ( mode === "hide" ) {
14262 $.effects.restore( el, props );
14263 $.effects.removeWrapper( el );
14270 (function( $, undefined ) {
14272 $.effects.effect.explode = function( o, done ) {
14274 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
14277 mode = $.effects.setMode( el, o.mode || "hide" ),
14278 show = mode === "show",
14280 // show and then visibility:hidden the element before calculating offset
14281 offset = el.show().css( "visibility", "hidden" ).offset(),
14283 // width and height of a piece
14284 width = Math.ceil( el.outerWidth() / cells ),
14285 height = Math.ceil( el.outerHeight() / rows ),
14289 i, j, left, top, mx, my;
14291 // children animate complete:
14292 function childComplete() {
14293 pieces.push( this );
14294 if ( pieces.length === rows * cells ) {
14299 // clone the element for each row and cell.
14300 for( i = 0; i < rows ; i++ ) { // ===>
14301 top = offset.top + i * height;
14302 my = i - ( rows - 1 ) / 2 ;
14304 for( j = 0; j < cells ; j++ ) { // |||
14305 left = offset.left + j * width;
14306 mx = j - ( cells - 1 ) / 2 ;
14308 // Create a clone of the now hidden main element that will be absolute positioned
14309 // within a wrapper div off the -left and -top equal to size of our pieces
14312 .appendTo( "body" )
14313 .wrap( "<div></div>" )
14315 position: "absolute",
14316 visibility: "visible",
14321 // select the wrapper - make it overflow: hidden and absolute positioned based on
14322 // where the original was located +left and +top equal to the size of pieces
14324 .addClass( "ui-effects-explode" )
14326 position: "absolute",
14327 overflow: "hidden",
14330 left: left + ( show ? mx * width : 0 ),
14331 top: top + ( show ? my * height : 0 ),
14332 opacity: show ? 0 : 1
14334 left: left + ( show ? 0 : mx * width ),
14335 top: top + ( show ? 0 : my * height ),
14336 opacity: show ? 1 : 0
14337 }, o.duration || 500, o.easing, childComplete );
14341 function animComplete() {
14343 visibility: "visible"
14345 $( pieces ).remove();
14354 (function( $, undefined ) {
14356 $.effects.effect.fade = function( o, done ) {
14357 var el = $( this ),
14358 mode = $.effects.setMode( el, o.mode || "toggle" );
14364 duration: o.duration,
14371 (function( $, undefined ) {
14373 $.effects.effect.fold = function( o, done ) {
14376 var el = $( this ),
14377 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14378 mode = $.effects.setMode( el, o.mode || "hide" ),
14379 show = mode === "show",
14380 hide = mode === "hide",
14381 size = o.size || 15,
14382 percent = /([0-9]+)%/.exec( size ),
14383 horizFirst = !!o.horizFirst,
14384 widthFirst = show !== horizFirst,
14385 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
14386 duration = o.duration / 2,
14391 $.effects.save( el, props );
14395 wrapper = $.effects.createWrapper( el ).css({
14398 distance = widthFirst ?
14399 [ wrapper.width(), wrapper.height() ] :
14400 [ wrapper.height(), wrapper.width() ];
14403 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
14406 wrapper.css( horizFirst ? {
14416 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
14417 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
14421 .animate( animation1, duration, o.easing )
14422 .animate( animation2, duration, o.easing, function() {
14426 $.effects.restore( el, props );
14427 $.effects.removeWrapper( el );
14434 (function( $, undefined ) {
14436 $.effects.effect.highlight = function( o, done ) {
14437 var elem = $( this ),
14438 props = [ "backgroundImage", "backgroundColor", "opacity" ],
14439 mode = $.effects.setMode( elem, o.mode || "show" ),
14441 backgroundColor: elem.css( "backgroundColor" )
14444 if (mode === "hide") {
14445 animation.opacity = 0;
14448 $.effects.save( elem, props );
14453 backgroundImage: "none",
14454 backgroundColor: o.color || "#ffff99"
14456 .animate( animation, {
14458 duration: o.duration,
14460 complete: function() {
14461 if ( mode === "hide" ) {
14464 $.effects.restore( elem, props );
14471 (function( $, undefined ) {
14473 $.effects.effect.pulsate = function( o, done ) {
14474 var elem = $( this ),
14475 mode = $.effects.setMode( elem, o.mode || "show" ),
14476 show = mode === "show",
14477 hide = mode === "hide",
14478 showhide = ( show || mode === "hide" ),
14480 // showing or hiding leaves of the "last" animation
14481 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
14482 duration = o.duration / anims,
14484 queue = elem.queue(),
14485 queuelen = queue.length,
14488 if ( show || !elem.is(":visible")) {
14489 elem.css( "opacity", 0 ).show();
14493 // anims - 1 opacity "toggles"
14494 for ( i = 1; i < anims; i++ ) {
14497 }, duration, o.easing );
14498 animateTo = 1 - animateTo;
14503 }, duration, o.easing);
14505 elem.queue(function() {
14512 // We just queued up "anims" animations, we need to put them next in the queue
14513 if ( queuelen > 1 ) {
14514 queue.splice.apply( queue,
14515 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
14521 (function( $, undefined ) {
14523 $.effects.effect.puff = function( o, done ) {
14524 var elem = $( this ),
14525 mode = $.effects.setMode( elem, o.mode || "hide" ),
14526 hide = mode === "hide",
14527 percent = parseInt( o.percent, 10 ) || 150,
14528 factor = percent / 100,
14530 height: elem.height(),
14531 width: elem.width(),
14532 outerHeight: elem.outerHeight(),
14533 outerWidth: elem.outerWidth()
14542 percent: hide ? percent : 100,
14546 height: original.height * factor,
14547 width: original.width * factor,
14548 outerHeight: original.outerHeight * factor,
14549 outerWidth: original.outerWidth * factor
14556 $.effects.effect.scale = function( o, done ) {
14559 var el = $( this ),
14560 options = $.extend( true, {}, o ),
14561 mode = $.effects.setMode( el, o.mode || "effect" ),
14562 percent = parseInt( o.percent, 10 ) ||
14563 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
14564 direction = o.direction || "both",
14567 height: el.height(),
14569 outerHeight: el.outerHeight(),
14570 outerWidth: el.outerWidth()
14573 y: direction !== "horizontal" ? (percent / 100) : 1,
14574 x: direction !== "vertical" ? (percent / 100) : 1
14577 // We are going to pass this effect to the size effect:
14578 options.effect = "size";
14579 options.queue = false;
14580 options.complete = done;
14582 // Set default origin and restore for show/hide
14583 if ( mode !== "effect" ) {
14584 options.origin = origin || ["middle","center"];
14585 options.restore = true;
14588 options.from = o.from || ( mode === "show" ? {
14595 height: original.height * factor.y,
14596 width: original.width * factor.x,
14597 outerHeight: original.outerHeight * factor.y,
14598 outerWidth: original.outerWidth * factor.x
14601 // Fade option to support puff
14602 if ( options.fade ) {
14603 if ( mode === "show" ) {
14604 options.from.opacity = 0;
14605 options.to.opacity = 1;
14607 if ( mode === "hide" ) {
14608 options.from.opacity = 1;
14609 options.to.opacity = 0;
14614 el.effect( options );
14618 $.effects.effect.size = function( o, done ) {
14621 var original, baseline, factor,
14623 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
14626 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
14628 // Copy for children
14629 props2 = [ "width", "height", "overflow" ],
14630 cProps = [ "fontSize" ],
14631 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
14632 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
14635 mode = $.effects.setMode( el, o.mode || "effect" ),
14636 restore = o.restore || mode !== "effect",
14637 scale = o.scale || "both",
14638 origin = o.origin || [ "middle", "center" ],
14639 position = el.css( "position" ),
14640 props = restore ? props0 : props1,
14648 if ( mode === "show" ) {
14652 height: el.height(),
14654 outerHeight: el.outerHeight(),
14655 outerWidth: el.outerWidth()
14658 if ( o.mode === "toggle" && mode === "show" ) {
14659 el.from = o.to || zero;
14660 el.to = o.from || original;
14662 el.from = o.from || ( mode === "show" ? zero : original );
14663 el.to = o.to || ( mode === "hide" ? zero : original );
14666 // Set scaling factor
14669 y: el.from.height / original.height,
14670 x: el.from.width / original.width
14673 y: el.to.height / original.height,
14674 x: el.to.width / original.width
14678 // Scale the css box
14679 if ( scale === "box" || scale === "both" ) {
14681 // Vertical props scaling
14682 if ( factor.from.y !== factor.to.y ) {
14683 props = props.concat( vProps );
14684 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
14685 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
14688 // Horizontal props scaling
14689 if ( factor.from.x !== factor.to.x ) {
14690 props = props.concat( hProps );
14691 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
14692 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
14696 // Scale the content
14697 if ( scale === "content" || scale === "both" ) {
14699 // Vertical props scaling
14700 if ( factor.from.y !== factor.to.y ) {
14701 props = props.concat( cProps ).concat( props2 );
14702 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
14703 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
14707 $.effects.save( el, props );
14709 $.effects.createWrapper( el );
14710 el.css( "overflow", "hidden" ).css( el.from );
14713 if (origin) { // Calculate baseline shifts
14714 baseline = $.effects.getBaseline( origin, original );
14715 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
14716 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
14717 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
14718 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
14720 el.css( el.from ); // set top & left
14723 if ( scale === "content" || scale === "both" ) { // Scale the children
14725 // Add margins/font-size
14726 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
14727 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
14728 props2 = props0.concat(vProps).concat(hProps);
14730 el.find( "*[width]" ).each( function(){
14731 var child = $( this ),
14733 height: child.height(),
14734 width: child.width(),
14735 outerHeight: child.outerHeight(),
14736 outerWidth: child.outerWidth()
14739 $.effects.save(child, props2);
14743 height: c_original.height * factor.from.y,
14744 width: c_original.width * factor.from.x,
14745 outerHeight: c_original.outerHeight * factor.from.y,
14746 outerWidth: c_original.outerWidth * factor.from.x
14749 height: c_original.height * factor.to.y,
14750 width: c_original.width * factor.to.x,
14751 outerHeight: c_original.height * factor.to.y,
14752 outerWidth: c_original.width * factor.to.x
14755 // Vertical props scaling
14756 if ( factor.from.y !== factor.to.y ) {
14757 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
14758 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
14761 // Horizontal props scaling
14762 if ( factor.from.x !== factor.to.x ) {
14763 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
14764 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
14767 // Animate children
14768 child.css( child.from );
14769 child.animate( child.to, o.duration, o.easing, function() {
14771 // Restore children
14773 $.effects.restore( child, props2 );
14780 el.animate( el.to, {
14782 duration: o.duration,
14784 complete: function() {
14785 if ( el.to.opacity === 0 ) {
14786 el.css( "opacity", el.from.opacity );
14788 if( mode === "hide" ) {
14791 $.effects.restore( el, props );
14794 // we need to calculate our new positioning based on the scaling
14795 if ( position === "static" ) {
14797 position: "relative",
14802 $.each([ "top", "left" ], function( idx, pos ) {
14803 el.css( pos, function( _, str ) {
14804 var val = parseInt( str, 10 ),
14805 toRef = idx ? el.to.left : el.to.top;
14807 // if original was "auto", recalculate the new value from wrapper
14808 if ( str === "auto" ) {
14809 return toRef + "px";
14812 return val + toRef + "px";
14818 $.effects.removeWrapper( el );
14826 (function( $, undefined ) {
14828 $.effects.effect.shake = function( o, done ) {
14830 var el = $( this ),
14831 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
14832 mode = $.effects.setMode( el, o.mode || "effect" ),
14833 direction = o.direction || "left",
14834 distance = o.distance || 20,
14835 times = o.times || 3,
14836 anims = times * 2 + 1,
14837 speed = Math.round(o.duration/anims),
14838 ref = (direction === "up" || direction === "down") ? "top" : "left",
14839 positiveMotion = (direction === "up" || direction === "left"),
14845 // we will need to re-assemble the queue to stack our animations in place
14846 queue = el.queue(),
14847 queuelen = queue.length;
14849 $.effects.save( el, props );
14851 $.effects.createWrapper( el );
14854 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
14855 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
14856 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
14859 el.animate( animation, speed, o.easing );
14862 for ( i = 1; i < times; i++ ) {
14863 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
14866 .animate( animation1, speed, o.easing )
14867 .animate( animation, speed / 2, o.easing )
14868 .queue(function() {
14869 if ( mode === "hide" ) {
14872 $.effects.restore( el, props );
14873 $.effects.removeWrapper( el );
14877 // inject all the animations we just queued to be first in line (after "inprogress")
14878 if ( queuelen > 1) {
14879 queue.splice.apply( queue,
14880 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
14887 (function( $, undefined ) {
14889 $.effects.effect.slide = function( o, done ) {
14892 var el = $( this ),
14893 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
14894 mode = $.effects.setMode( el, o.mode || "show" ),
14895 show = mode === "show",
14896 direction = o.direction || "left",
14897 ref = (direction === "up" || direction === "down") ? "top" : "left",
14898 positiveMotion = (direction === "up" || direction === "left"),
14903 $.effects.save( el, props );
14905 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
14907 $.effects.createWrapper( el ).css({
14912 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
14916 animation[ ref ] = ( show ?
14917 ( positiveMotion ? "+=" : "-=") :
14918 ( positiveMotion ? "-=" : "+=")) +
14922 el.animate( animation, {
14924 duration: o.duration,
14926 complete: function() {
14927 if ( mode === "hide" ) {
14930 $.effects.restore( el, props );
14931 $.effects.removeWrapper( el );
14938 (function( $, undefined ) {
14940 $.effects.effect.transfer = function( o, done ) {
14941 var elem = $( this ),
14942 target = $( o.to ),
14943 targetFixed = target.css( "position" ) === "fixed",
14945 fixTop = targetFixed ? body.scrollTop() : 0,
14946 fixLeft = targetFixed ? body.scrollLeft() : 0,
14947 endPosition = target.offset(),
14949 top: endPosition.top - fixTop ,
14950 left: endPosition.left - fixLeft ,
14951 height: target.innerHeight(),
14952 width: target.innerWidth()
14954 startPosition = elem.offset(),
14955 transfer = $( "<div class='ui-effects-transfer'></div>" )
14956 .appendTo( document.body )
14957 .addClass( o.className )
14959 top: startPosition.top - fixTop ,
14960 left: startPosition.left - fixLeft ,
14961 height: elem.innerHeight(),
14962 width: elem.innerWidth(),
14963 position: targetFixed ? "fixed" : "absolute"
14965 .animate( animation, o.duration, o.easing, function() {