1 /*! jQuery UI - v1.12.1 - 2019-08-16
3 * Includes: widget.js, position.js, data.js, keycode.js, scroll-parent.js, unique-id.js, widgets/sortable.js, widgets/autocomplete.js, widgets/datepicker.js, widgets/menu.js, widgets/mouse.js, widgets/slider.js, widgets/tabs.js, widgets/tooltip.js
4 * Copyright jQuery Foundation and other contributors; Licensed MIT */
7 if ( typeof define === "function" && define.amd ) {
9 // AMD. Register as an anonymous module.
10 define([ "jquery" ], factory );
20 var version = $.ui.version = "1.12.1";
24 * jQuery UI Widget 1.12.1
27 * Copyright jQuery Foundation and other contributors
28 * Released under the MIT license.
29 * http://jquery.org/license
34 //>>description: Provides a factory for creating stateful widgets with a common API.
35 //>>docs: http://api.jqueryui.com/jQuery.widget/
36 //>>demos: http://jqueryui.com/widget/
41 var widgetSlice = Array.prototype.slice;
43 $.cleanData = ( function( orig ) {
44 return function( elems ) {
46 for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
49 // Only trigger remove when necessary to save time
50 events = $._data( elem, "events" );
51 if ( events && events.remove ) {
52 $( elem ).triggerHandler( "remove" );
55 // Http://bugs.jquery.com/ticket/8235
62 $.widget = function( name, base, prototype ) {
63 var existingConstructor, constructor, basePrototype;
65 // ProxiedPrototype allows the provided prototype to remain unmodified
66 // so that it can be used as a mixin for multiple widgets (#8876)
67 var proxiedPrototype = {};
69 var namespace = name.split( "." )[ 0 ];
70 name = name.split( "." )[ 1 ];
71 var fullName = namespace + "-" + name;
78 if ( $.isArray( prototype ) ) {
79 prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
82 // Create selector for plugin
83 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
84 return !!$.data( elem, fullName );
87 $[ namespace ] = $[ namespace ] || {};
88 existingConstructor = $[ namespace ][ name ];
89 constructor = $[ namespace ][ name ] = function( options, element ) {
91 // Allow instantiation without "new" keyword
92 if ( !this._createWidget ) {
93 return new constructor( options, element );
96 // Allow instantiation without initializing for simple inheritance
97 // must use "new" keyword (the code above always passes args)
98 if ( arguments.length ) {
99 this._createWidget( options, element );
103 // Extend with the existing constructor to carry over any static properties
104 $.extend( constructor, existingConstructor, {
105 version: prototype.version,
107 // Copy the object used to create the prototype in case we need to
108 // redefine the widget later
109 _proto: $.extend( {}, prototype ),
111 // Track widgets that inherit from this widget in case this widget is
112 // redefined after a widget inherits from it
113 _childConstructors: []
116 basePrototype = new base();
118 // We need to make the options hash a property directly on the new instance
119 // otherwise we'll modify the options hash on the prototype that we're
121 basePrototype.options = $.widget.extend( {}, basePrototype.options );
122 $.each( prototype, function( prop, value ) {
123 if ( !$.isFunction( value ) ) {
124 proxiedPrototype[ prop ] = value;
127 proxiedPrototype[ prop ] = ( function() {
129 return base.prototype[ prop ].apply( this, arguments );
132 function _superApply( args ) {
133 return base.prototype[ prop ].apply( this, args );
137 var __super = this._super;
138 var __superApply = this._superApply;
141 this._super = _super;
142 this._superApply = _superApply;
144 returnValue = value.apply( this, arguments );
146 this._super = __super;
147 this._superApply = __superApply;
153 constructor.prototype = $.widget.extend( basePrototype, {
155 // TODO: remove support for widgetEventPrefix
156 // always use the name + a colon as the prefix, e.g., draggable:start
157 // don't prefix for widgets that aren't DOM-based
158 widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
159 }, proxiedPrototype, {
160 constructor: constructor,
161 namespace: namespace,
163 widgetFullName: fullName
166 // If this widget is being redefined then we need to find all widgets that
167 // are inheriting from it and redefine all of them so that they inherit from
168 // the new version of this widget. We're essentially trying to replace one
169 // level in the prototype chain.
170 if ( existingConstructor ) {
171 $.each( existingConstructor._childConstructors, function( i, child ) {
172 var childPrototype = child.prototype;
174 // Redefine the child widget using the same prototype that was
175 // originally used, but inherit from the new version of the base
176 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
180 // Remove the list of existing child constructors from the old constructor
181 // so the old child constructors can be garbage collected
182 delete existingConstructor._childConstructors;
184 base._childConstructors.push( constructor );
187 $.widget.bridge( name, constructor );
192 $.widget.extend = function( target ) {
193 var input = widgetSlice.call( arguments, 1 );
195 var inputLength = input.length;
199 for ( ; inputIndex < inputLength; inputIndex++ ) {
200 for ( key in input[ inputIndex ] ) {
201 value = input[ inputIndex ][ key ];
202 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
205 if ( $.isPlainObject( value ) ) {
206 target[ key ] = $.isPlainObject( target[ key ] ) ?
207 $.widget.extend( {}, target[ key ], value ) :
209 // Don't extend strings, arrays, etc. with objects
210 $.widget.extend( {}, value );
212 // Copy everything else by reference
214 target[ key ] = value;
222 $.widget.bridge = function( name, object ) {
223 var fullName = object.prototype.widgetFullName || name;
224 $.fn[ name ] = function( options ) {
225 var isMethodCall = typeof options === "string";
226 var args = widgetSlice.call( arguments, 1 );
227 var returnValue = this;
229 if ( isMethodCall ) {
231 // If this is an empty collection, we need to have the instance method
232 // return undefined instead of the jQuery instance
233 if ( !this.length && options === "instance" ) {
234 returnValue = undefined;
236 this.each( function() {
238 var instance = $.data( this, fullName );
240 if ( options === "instance" ) {
241 returnValue = instance;
246 return $.error( "cannot call methods on " + name +
247 " prior to initialization; " +
248 "attempted to call method '" + options + "'" );
251 if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
252 return $.error( "no such method '" + options + "' for " + name +
253 " widget instance" );
256 methodValue = instance[ options ].apply( instance, args );
258 if ( methodValue !== instance && methodValue !== undefined ) {
259 returnValue = methodValue && methodValue.jquery ?
260 returnValue.pushStack( methodValue.get() ) :
268 // Allow multiple hashes to be passed on init
270 options = $.widget.extend.apply( null, [ options ].concat( args ) );
273 this.each( function() {
274 var instance = $.data( this, fullName );
276 instance.option( options || {} );
277 if ( instance._init ) {
281 $.data( this, fullName, new object( options, this ) );
290 $.Widget = function( /* options, element */ ) {};
291 $.Widget._childConstructors = [];
293 $.Widget.prototype = {
294 widgetName: "widget",
295 widgetEventPrefix: "",
296 defaultElement: "<div>",
306 _createWidget: function( options, element ) {
307 element = $( element || this.defaultElement || this )[ 0 ];
308 this.element = $( element );
309 this.uuid = widgetUuid++;
310 this.eventNamespace = "." + this.widgetName + this.uuid;
313 this.hoverable = $();
314 this.focusable = $();
315 this.classesElementLookup = {};
317 if ( element !== this ) {
318 $.data( element, this.widgetFullName, this );
319 this._on( true, this.element, {
320 remove: function( event ) {
321 if ( event.target === element ) {
326 this.document = $( element.style ?
328 // Element within the document
329 element.ownerDocument :
331 // Element is window or document
332 element.document || element );
333 this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
336 this.options = $.widget.extend( {},
338 this._getCreateOptions(),
343 if ( this.options.disabled ) {
344 this._setOptionDisabled( this.options.disabled );
347 this._trigger( "create", null, this._getCreateEventData() );
351 _getCreateOptions: function() {
355 _getCreateEventData: $.noop,
361 destroy: function() {
365 $.each( this.classesElementLookup, function( key, value ) {
366 that._removeClass( value, key );
369 // We can probably remove the unbind calls in 2.0
370 // all event bindings should go through this._on()
372 .off( this.eventNamespace )
373 .removeData( this.widgetFullName );
375 .off( this.eventNamespace )
376 .removeAttr( "aria-disabled" );
378 // Clean up events and states
379 this.bindings.off( this.eventNamespace );
388 option: function( key, value ) {
394 if ( arguments.length === 0 ) {
396 // Don't return a reference to the internal hash
397 return $.widget.extend( {}, this.options );
400 if ( typeof key === "string" ) {
402 // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
404 parts = key.split( "." );
406 if ( parts.length ) {
407 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
408 for ( i = 0; i < parts.length - 1; i++ ) {
409 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
410 curOption = curOption[ parts[ i ] ];
413 if ( arguments.length === 1 ) {
414 return curOption[ key ] === undefined ? null : curOption[ key ];
416 curOption[ key ] = value;
418 if ( arguments.length === 1 ) {
419 return this.options[ key ] === undefined ? null : this.options[ key ];
421 options[ key ] = value;
425 this._setOptions( options );
430 _setOptions: function( options ) {
433 for ( key in options ) {
434 this._setOption( key, options[ key ] );
440 _setOption: function( key, value ) {
441 if ( key === "classes" ) {
442 this._setOptionClasses( value );
445 this.options[ key ] = value;
447 if ( key === "disabled" ) {
448 this._setOptionDisabled( value );
454 _setOptionClasses: function( value ) {
455 var classKey, elements, currentElements;
457 for ( classKey in value ) {
458 currentElements = this.classesElementLookup[ classKey ];
459 if ( value[ classKey ] === this.options.classes[ classKey ] ||
461 !currentElements.length ) {
465 // We are doing this to create a new jQuery object because the _removeClass() call
466 // on the next line is going to destroy the reference to the current elements being
467 // tracked. We need to save a copy of this collection so that we can add the new classes
469 elements = $( currentElements.get() );
470 this._removeClass( currentElements, classKey );
472 // We don't use _addClass() here, because that uses this.options.classes
473 // for generating the string of classes. We want to use the value passed in from
474 // _setOption(), this is the new value of the classes option which was passed to
475 // _setOption(). We pass this value directly to _classes().
476 elements.addClass( this._classes( {
485 _setOptionDisabled: function( value ) {
486 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
488 // If the widget is becoming disabled, then nothing is interactive
490 this._removeClass( this.hoverable, null, "ui-state-hover" );
491 this._removeClass( this.focusable, null, "ui-state-focus" );
496 return this._setOptions( { disabled: false } );
499 disable: function() {
500 return this._setOptions( { disabled: true } );
503 _classes: function( options ) {
507 options = $.extend( {
508 element: this.element,
509 classes: this.options.classes || {}
512 function processClassString( classes, checkOption ) {
514 for ( i = 0; i < classes.length; i++ ) {
515 current = that.classesElementLookup[ classes[ i ] ] || $();
517 current = $( $.unique( current.get().concat( options.element.get() ) ) );
519 current = $( current.not( options.element ).get() );
521 that.classesElementLookup[ classes[ i ] ] = current;
522 full.push( classes[ i ] );
523 if ( checkOption && options.classes[ classes[ i ] ] ) {
524 full.push( options.classes[ classes[ i ] ] );
529 this._on( options.element, {
530 "remove": "_untrackClassesElement"
533 if ( options.keys ) {
534 processClassString( options.keys.match( /\S+/g ) || [], true );
536 if ( options.extra ) {
537 processClassString( options.extra.match( /\S+/g ) || [] );
540 return full.join( " " );
543 _untrackClassesElement: function( event ) {
545 $.each( that.classesElementLookup, function( key, value ) {
546 if ( $.inArray( event.target, value ) !== -1 ) {
547 that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
552 _removeClass: function( element, keys, extra ) {
553 return this._toggleClass( element, keys, extra, false );
556 _addClass: function( element, keys, extra ) {
557 return this._toggleClass( element, keys, extra, true );
560 _toggleClass: function( element, keys, extra, add ) {
561 add = ( typeof add === "boolean" ) ? add : extra;
562 var shift = ( typeof element === "string" || element === null ),
564 extra: shift ? keys : extra,
565 keys: shift ? element : keys,
566 element: shift ? this.element : element,
569 options.element.toggleClass( this._classes( options ), add );
573 _on: function( suppressDisabledCheck, element, handlers ) {
577 // No suppressDisabledCheck flag, shuffle arguments
578 if ( typeof suppressDisabledCheck !== "boolean" ) {
580 element = suppressDisabledCheck;
581 suppressDisabledCheck = false;
584 // No element argument, shuffle and use this.element
587 element = this.element;
588 delegateElement = this.widget();
590 element = delegateElement = $( element );
591 this.bindings = this.bindings.add( element );
594 $.each( handlers, function( event, handler ) {
595 function handlerProxy() {
597 // Allow widgets to customize the disabled handling
598 // - disabled as an array instead of boolean
599 // - disabled class as method for disabling individual parts
600 if ( !suppressDisabledCheck &&
601 ( instance.options.disabled === true ||
602 $( this ).hasClass( "ui-state-disabled" ) ) ) {
605 return ( typeof handler === "string" ? instance[ handler ] : handler )
606 .apply( instance, arguments );
609 // Copy the guid so direct unbinding works
610 if ( typeof handler !== "string" ) {
611 handlerProxy.guid = handler.guid =
612 handler.guid || handlerProxy.guid || $.guid++;
615 var match = event.match( /^([\w:-]*)\s*(.*)$/ );
616 var eventName = match[ 1 ] + instance.eventNamespace;
617 var selector = match[ 2 ];
620 delegateElement.on( eventName, selector, handlerProxy );
622 element.on( eventName, handlerProxy );
627 _off: function( element, eventName ) {
628 eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
630 element.off( eventName ).off( eventName );
632 // Clear the stack to avoid memory leaks (#10056)
633 this.bindings = $( this.bindings.not( element ).get() );
634 this.focusable = $( this.focusable.not( element ).get() );
635 this.hoverable = $( this.hoverable.not( element ).get() );
638 _delay: function( handler, delay ) {
639 function handlerProxy() {
640 return ( typeof handler === "string" ? instance[ handler ] : handler )
641 .apply( instance, arguments );
644 return setTimeout( handlerProxy, delay || 0 );
647 _hoverable: function( element ) {
648 this.hoverable = this.hoverable.add( element );
650 mouseenter: function( event ) {
651 this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
653 mouseleave: function( event ) {
654 this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
659 _focusable: function( element ) {
660 this.focusable = this.focusable.add( element );
662 focusin: function( event ) {
663 this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
665 focusout: function( event ) {
666 this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
671 _trigger: function( type, event, data ) {
673 var callback = this.options[ type ];
676 event = $.Event( event );
677 event.type = ( type === this.widgetEventPrefix ?
679 this.widgetEventPrefix + type ).toLowerCase();
681 // The original event may come from any element
682 // so we need to reset the target on the new event
683 event.target = this.element[ 0 ];
685 // Copy original event properties over to the new event
686 orig = event.originalEvent;
688 for ( prop in orig ) {
689 if ( !( prop in event ) ) {
690 event[ prop ] = orig[ prop ];
695 this.element.trigger( event, data );
696 return !( $.isFunction( callback ) &&
697 callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
698 event.isDefaultPrevented() );
702 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
703 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
704 if ( typeof options === "string" ) {
705 options = { effect: options };
709 var effectName = !options ?
711 options === true || typeof options === "number" ?
713 options.effect || defaultEffect;
715 options = options || {};
716 if ( typeof options === "number" ) {
717 options = { duration: options };
720 hasOptions = !$.isEmptyObject( options );
721 options.complete = callback;
723 if ( options.delay ) {
724 element.delay( options.delay );
727 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
728 element[ method ]( options );
729 } else if ( effectName !== method && element[ effectName ] ) {
730 element[ effectName ]( options.duration, options.easing, callback );
732 element.queue( function( next ) {
733 $( this )[ method ]();
735 callback.call( element[ 0 ] );
743 var widget = $.widget;
747 * jQuery UI Position 1.12.1
748 * http://jqueryui.com
750 * Copyright jQuery Foundation and other contributors
751 * Released under the MIT license.
752 * http://jquery.org/license
754 * http://api.jqueryui.com/position/
759 //>>description: Positions elements relative to other elements.
760 //>>docs: http://api.jqueryui.com/position/
761 //>>demos: http://jqueryui.com/position/
765 var cachedScrollbarWidth,
768 rhorizontal = /left|center|right/,
769 rvertical = /top|center|bottom/,
770 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
773 _position = $.fn.position;
775 function getOffsets( offsets, width, height ) {
777 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
778 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
782 function parseCss( element, property ) {
783 return parseInt( $.css( element, property ), 10 ) || 0;
786 function getDimensions( elem ) {
788 if ( raw.nodeType === 9 ) {
791 height: elem.height(),
792 offset: { top: 0, left: 0 }
795 if ( $.isWindow( raw ) ) {
798 height: elem.height(),
799 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
802 if ( raw.preventDefault ) {
806 offset: { top: raw.pageY, left: raw.pageX }
810 width: elem.outerWidth(),
811 height: elem.outerHeight(),
812 offset: elem.offset()
817 scrollbarWidth: function() {
818 if ( cachedScrollbarWidth !== undefined ) {
819 return cachedScrollbarWidth;
823 "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
824 "<div style='height:100px;width:auto;'></div></div>" ),
825 innerDiv = div.children()[ 0 ];
827 $( "body" ).append( div );
828 w1 = innerDiv.offsetWidth;
829 div.css( "overflow", "scroll" );
831 w2 = innerDiv.offsetWidth;
834 w2 = div[ 0 ].clientWidth;
839 return ( cachedScrollbarWidth = w1 - w2 );
841 getScrollInfo: function( within ) {
842 var overflowX = within.isWindow || within.isDocument ? "" :
843 within.element.css( "overflow-x" ),
844 overflowY = within.isWindow || within.isDocument ? "" :
845 within.element.css( "overflow-y" ),
846 hasOverflowX = overflowX === "scroll" ||
847 ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
848 hasOverflowY = overflowY === "scroll" ||
849 ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
851 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
852 height: hasOverflowX ? $.position.scrollbarWidth() : 0
855 getWithinInfo: function( element ) {
856 var withinElement = $( element || window ),
857 isWindow = $.isWindow( withinElement[ 0 ] ),
858 isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
859 hasOffset = !isWindow && !isDocument;
861 element: withinElement,
863 isDocument: isDocument,
864 offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
865 scrollLeft: withinElement.scrollLeft(),
866 scrollTop: withinElement.scrollTop(),
867 width: withinElement.outerWidth(),
868 height: withinElement.outerHeight()
873 $.fn.position = function( options ) {
874 if ( !options || !options.of ) {
875 return _position.apply( this, arguments );
878 // Make a copy, we don't want to modify arguments
879 options = $.extend( {}, options );
881 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
882 target = $( options.of ),
883 within = $.position.getWithinInfo( options.within ),
884 scrollInfo = $.position.getScrollInfo( within ),
885 collision = ( options.collision || "flip" ).split( " " ),
888 dimensions = getDimensions( target );
889 if ( target[ 0 ].preventDefault ) {
891 // Force left top to allow flipping
892 options.at = "left top";
894 targetWidth = dimensions.width;
895 targetHeight = dimensions.height;
896 targetOffset = dimensions.offset;
898 // Clone to reuse original targetOffset later
899 basePosition = $.extend( {}, targetOffset );
901 // Force my and at to have valid horizontal and vertical positions
902 // if a value is missing or invalid, it will be converted to center
903 $.each( [ "my", "at" ], function() {
904 var pos = ( options[ this ] || "" ).split( " " ),
908 if ( pos.length === 1 ) {
909 pos = rhorizontal.test( pos[ 0 ] ) ?
910 pos.concat( [ "center" ] ) :
911 rvertical.test( pos[ 0 ] ) ?
912 [ "center" ].concat( pos ) :
913 [ "center", "center" ];
915 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
916 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
919 horizontalOffset = roffset.exec( pos[ 0 ] );
920 verticalOffset = roffset.exec( pos[ 1 ] );
922 horizontalOffset ? horizontalOffset[ 0 ] : 0,
923 verticalOffset ? verticalOffset[ 0 ] : 0
926 // Reduce to just the positions without the offsets
928 rposition.exec( pos[ 0 ] )[ 0 ],
929 rposition.exec( pos[ 1 ] )[ 0 ]
933 // Normalize collision option
934 if ( collision.length === 1 ) {
935 collision[ 1 ] = collision[ 0 ];
938 if ( options.at[ 0 ] === "right" ) {
939 basePosition.left += targetWidth;
940 } else if ( options.at[ 0 ] === "center" ) {
941 basePosition.left += targetWidth / 2;
944 if ( options.at[ 1 ] === "bottom" ) {
945 basePosition.top += targetHeight;
946 } else if ( options.at[ 1 ] === "center" ) {
947 basePosition.top += targetHeight / 2;
950 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
951 basePosition.left += atOffset[ 0 ];
952 basePosition.top += atOffset[ 1 ];
954 return this.each( function() {
955 var collisionPosition, using,
957 elemWidth = elem.outerWidth(),
958 elemHeight = elem.outerHeight(),
959 marginLeft = parseCss( this, "marginLeft" ),
960 marginTop = parseCss( this, "marginTop" ),
961 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
963 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
965 position = $.extend( {}, basePosition ),
966 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
968 if ( options.my[ 0 ] === "right" ) {
969 position.left -= elemWidth;
970 } else if ( options.my[ 0 ] === "center" ) {
971 position.left -= elemWidth / 2;
974 if ( options.my[ 1 ] === "bottom" ) {
975 position.top -= elemHeight;
976 } else if ( options.my[ 1 ] === "center" ) {
977 position.top -= elemHeight / 2;
980 position.left += myOffset[ 0 ];
981 position.top += myOffset[ 1 ];
983 collisionPosition = {
984 marginLeft: marginLeft,
988 $.each( [ "left", "top" ], function( i, dir ) {
989 if ( $.ui.position[ collision[ i ] ] ) {
990 $.ui.position[ collision[ i ] ][ dir ]( position, {
991 targetWidth: targetWidth,
992 targetHeight: targetHeight,
993 elemWidth: elemWidth,
994 elemHeight: elemHeight,
995 collisionPosition: collisionPosition,
996 collisionWidth: collisionWidth,
997 collisionHeight: collisionHeight,
998 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1007 if ( options.using ) {
1009 // Adds feedback as second argument to using callback, if present
1010 using = function( props ) {
1011 var left = targetOffset.left - position.left,
1012 right = left + targetWidth - elemWidth,
1013 top = targetOffset.top - position.top,
1014 bottom = top + targetHeight - elemHeight,
1018 left: targetOffset.left,
1019 top: targetOffset.top,
1021 height: targetHeight
1025 left: position.left,
1030 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1031 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1033 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1034 feedback.horizontal = "center";
1036 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1037 feedback.vertical = "middle";
1039 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1040 feedback.important = "horizontal";
1042 feedback.important = "vertical";
1044 options.using.call( this, props, feedback );
1048 elem.offset( $.extend( position, { using: using } ) );
1054 left: function( position, data ) {
1055 var within = data.within,
1056 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1057 outerWidth = within.width,
1058 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1059 overLeft = withinOffset - collisionPosLeft,
1060 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1063 // Element is wider than within
1064 if ( data.collisionWidth > outerWidth ) {
1066 // Element is initially over the left side of within
1067 if ( overLeft > 0 && overRight <= 0 ) {
1068 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
1070 position.left += overLeft - newOverRight;
1072 // Element is initially over right side of within
1073 } else if ( overRight > 0 && overLeft <= 0 ) {
1074 position.left = withinOffset;
1076 // Element is initially over both left and right sides of within
1078 if ( overLeft > overRight ) {
1079 position.left = withinOffset + outerWidth - data.collisionWidth;
1081 position.left = withinOffset;
1085 // Too far left -> align with left edge
1086 } else if ( overLeft > 0 ) {
1087 position.left += overLeft;
1089 // Too far right -> align with right edge
1090 } else if ( overRight > 0 ) {
1091 position.left -= overRight;
1093 // Adjust based on position and margin
1095 position.left = max( position.left - collisionPosLeft, position.left );
1098 top: function( position, data ) {
1099 var within = data.within,
1100 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1101 outerHeight = data.within.height,
1102 collisionPosTop = position.top - data.collisionPosition.marginTop,
1103 overTop = withinOffset - collisionPosTop,
1104 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1107 // Element is taller than within
1108 if ( data.collisionHeight > outerHeight ) {
1110 // Element is initially over the top of within
1111 if ( overTop > 0 && overBottom <= 0 ) {
1112 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
1114 position.top += overTop - newOverBottom;
1116 // Element is initially over bottom of within
1117 } else if ( overBottom > 0 && overTop <= 0 ) {
1118 position.top = withinOffset;
1120 // Element is initially over both top and bottom of within
1122 if ( overTop > overBottom ) {
1123 position.top = withinOffset + outerHeight - data.collisionHeight;
1125 position.top = withinOffset;
1129 // Too far up -> align with top
1130 } else if ( overTop > 0 ) {
1131 position.top += overTop;
1133 // Too far down -> align with bottom edge
1134 } else if ( overBottom > 0 ) {
1135 position.top -= overBottom;
1137 // Adjust based on position and margin
1139 position.top = max( position.top - collisionPosTop, position.top );
1144 left: function( position, data ) {
1145 var within = data.within,
1146 withinOffset = within.offset.left + within.scrollLeft,
1147 outerWidth = within.width,
1148 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1149 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1150 overLeft = collisionPosLeft - offsetLeft,
1151 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1152 myOffset = data.my[ 0 ] === "left" ?
1154 data.my[ 0 ] === "right" ?
1157 atOffset = data.at[ 0 ] === "left" ?
1159 data.at[ 0 ] === "right" ?
1162 offset = -2 * data.offset[ 0 ],
1166 if ( overLeft < 0 ) {
1167 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
1168 outerWidth - withinOffset;
1169 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1170 position.left += myOffset + atOffset + offset;
1172 } else if ( overRight > 0 ) {
1173 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
1174 atOffset + offset - offsetLeft;
1175 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1176 position.left += myOffset + atOffset + offset;
1180 top: function( position, data ) {
1181 var within = data.within,
1182 withinOffset = within.offset.top + within.scrollTop,
1183 outerHeight = within.height,
1184 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1185 collisionPosTop = position.top - data.collisionPosition.marginTop,
1186 overTop = collisionPosTop - offsetTop,
1187 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1188 top = data.my[ 1 ] === "top",
1191 data.my[ 1 ] === "bottom" ?
1194 atOffset = data.at[ 1 ] === "top" ?
1196 data.at[ 1 ] === "bottom" ?
1197 -data.targetHeight :
1199 offset = -2 * data.offset[ 1 ],
1202 if ( overTop < 0 ) {
1203 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
1204 outerHeight - withinOffset;
1205 if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
1206 position.top += myOffset + atOffset + offset;
1208 } else if ( overBottom > 0 ) {
1209 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
1211 if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
1212 position.top += myOffset + atOffset + offset;
1219 $.ui.position.flip.left.apply( this, arguments );
1220 $.ui.position.fit.left.apply( this, arguments );
1223 $.ui.position.flip.top.apply( this, arguments );
1224 $.ui.position.fit.top.apply( this, arguments );
1231 var position = $.ui.position;
1235 * jQuery UI :data 1.12.1
1236 * http://jqueryui.com
1238 * Copyright jQuery Foundation and other contributors
1239 * Released under the MIT license.
1240 * http://jquery.org/license
1243 //>>label: :data Selector
1245 //>>description: Selects elements which have data stored under the specified key.
1246 //>>docs: http://api.jqueryui.com/data-selector/
1249 var data = $.extend( $.expr[ ":" ], {
1250 data: $.expr.createPseudo ?
1251 $.expr.createPseudo( function( dataName ) {
1252 return function( elem ) {
1253 return !!$.data( elem, dataName );
1257 // Support: jQuery <1.8
1258 function( elem, i, match ) {
1259 return !!$.data( elem, match[ 3 ] );
1264 * jQuery UI Keycode 1.12.1
1265 * http://jqueryui.com
1267 * Copyright jQuery Foundation and other contributors
1268 * Released under the MIT license.
1269 * http://jquery.org/license
1274 //>>description: Provide keycodes as keynames
1275 //>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
1278 var keycode = $.ui.keyCode = {
1299 * jQuery UI Scroll Parent 1.12.1
1300 * http://jqueryui.com
1302 * Copyright jQuery Foundation and other contributors
1303 * Released under the MIT license.
1304 * http://jquery.org/license
1307 //>>label: scrollParent
1309 //>>description: Get the closest ancestor element that is scrollable.
1310 //>>docs: http://api.jqueryui.com/scrollParent/
1314 var scrollParent = $.fn.scrollParent = function( includeHidden ) {
1315 var position = this.css( "position" ),
1316 excludeStaticParent = position === "absolute",
1317 overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
1318 scrollParent = this.parents().filter( function() {
1319 var parent = $( this );
1320 if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
1323 return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
1324 parent.css( "overflow-x" ) );
1327 return position === "fixed" || !scrollParent.length ?
1328 $( this[ 0 ].ownerDocument || document ) :
1334 * jQuery UI Unique ID 1.12.1
1335 * http://jqueryui.com
1337 * Copyright jQuery Foundation and other contributors
1338 * Released under the MIT license.
1339 * http://jquery.org/license
1344 //>>description: Functions to generate and remove uniqueId's
1345 //>>docs: http://api.jqueryui.com/uniqueId/
1349 var uniqueId = $.fn.extend( {
1350 uniqueId: ( function() {
1354 return this.each( function() {
1356 this.id = "ui-id-" + ( ++uuid );
1362 removeUniqueId: function() {
1363 return this.each( function() {
1364 if ( /^ui-id-\d+$/.test( this.id ) ) {
1365 $( this ).removeAttr( "id" );
1374 // This file is deprecated
1375 var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
1378 * jQuery UI Mouse 1.12.1
1379 * http://jqueryui.com
1381 * Copyright jQuery Foundation and other contributors
1382 * Released under the MIT license.
1383 * http://jquery.org/license
1388 //>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
1389 //>>docs: http://api.jqueryui.com/mouse/
1393 var mouseHandled = false;
1394 $( document ).on( "mouseup", function() {
1395 mouseHandled = false;
1398 var widgetsMouse = $.widget( "ui.mouse", {
1401 cancel: "input, textarea, button, select, option",
1405 _mouseInit: function() {
1409 .on( "mousedown." + this.widgetName, function( event ) {
1410 return that._mouseDown( event );
1412 .on( "click." + this.widgetName, function( event ) {
1413 if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
1414 $.removeData( event.target, that.widgetName + ".preventClickEvent" );
1415 event.stopImmediatePropagation();
1420 this.started = false;
1423 // TODO: make sure destroying one instance of mouse doesn't mess with
1424 // other instances of mouse
1425 _mouseDestroy: function() {
1426 this.element.off( "." + this.widgetName );
1427 if ( this._mouseMoveDelegate ) {
1429 .off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1430 .off( "mouseup." + this.widgetName, this._mouseUpDelegate );
1434 _mouseDown: function( event ) {
1436 // don't let more than one widget handle mouseStart
1437 if ( mouseHandled ) {
1441 this._mouseMoved = false;
1443 // We may have missed mouseup (out of window)
1444 ( this._mouseStarted && this._mouseUp( event ) );
1446 this._mouseDownEvent = event;
1449 btnIsLeft = ( event.which === 1 ),
1451 // event.target.nodeName works around a bug in IE 8 with
1452 // disabled inputs (#7620)
1453 elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
1454 $( event.target ).closest( this.options.cancel ).length : false );
1455 if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
1459 this.mouseDelayMet = !this.options.delay;
1460 if ( !this.mouseDelayMet ) {
1461 this._mouseDelayTimer = setTimeout( function() {
1462 that.mouseDelayMet = true;
1463 }, this.options.delay );
1466 if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
1467 this._mouseStarted = ( this._mouseStart( event ) !== false );
1468 if ( !this._mouseStarted ) {
1469 event.preventDefault();
1474 // Click event may never have fired (Gecko & Opera)
1475 if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
1476 $.removeData( event.target, this.widgetName + ".preventClickEvent" );
1479 // These delegates are required to keep context
1480 this._mouseMoveDelegate = function( event ) {
1481 return that._mouseMove( event );
1483 this._mouseUpDelegate = function( event ) {
1484 return that._mouseUp( event );
1488 .on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1489 .on( "mouseup." + this.widgetName, this._mouseUpDelegate );
1491 event.preventDefault();
1493 mouseHandled = true;
1497 _mouseMove: function( event ) {
1499 // Only check for mouseups outside the document if you've moved inside the document
1500 // at least once. This prevents the firing of mouseup in the case of IE<9, which will
1501 // fire a mousemove event if content is placed under the cursor. See #7778
1503 if ( this._mouseMoved ) {
1505 // IE mouseup check - mouseup happened when mouse was out of window
1506 if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
1508 return this._mouseUp( event );
1510 // Iframe mouseup check - mouseup occurred in another document
1511 } else if ( !event.which ) {
1513 // Support: Safari <=8 - 9
1514 // Safari sets which to 0 if you press any of the following keys
1515 // during a drag (#14461)
1516 if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
1517 event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
1518 this.ignoreMissingWhich = true;
1519 } else if ( !this.ignoreMissingWhich ) {
1520 return this._mouseUp( event );
1525 if ( event.which || event.button ) {
1526 this._mouseMoved = true;
1529 if ( this._mouseStarted ) {
1530 this._mouseDrag( event );
1531 return event.preventDefault();
1534 if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
1535 this._mouseStarted =
1536 ( this._mouseStart( this._mouseDownEvent, event ) !== false );
1537 ( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
1540 return !this._mouseStarted;
1543 _mouseUp: function( event ) {
1545 .off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1546 .off( "mouseup." + this.widgetName, this._mouseUpDelegate );
1548 if ( this._mouseStarted ) {
1549 this._mouseStarted = false;
1551 if ( event.target === this._mouseDownEvent.target ) {
1552 $.data( event.target, this.widgetName + ".preventClickEvent", true );
1555 this._mouseStop( event );
1558 if ( this._mouseDelayTimer ) {
1559 clearTimeout( this._mouseDelayTimer );
1560 delete this._mouseDelayTimer;
1563 this.ignoreMissingWhich = false;
1564 mouseHandled = false;
1565 event.preventDefault();
1568 _mouseDistanceMet: function( event ) {
1570 Math.abs( this._mouseDownEvent.pageX - event.pageX ),
1571 Math.abs( this._mouseDownEvent.pageY - event.pageY )
1572 ) >= this.options.distance
1576 _mouseDelayMet: function( /* event */ ) {
1577 return this.mouseDelayMet;
1580 // These are placeholder methods, to be overriden by extending plugin
1581 _mouseStart: function( /* event */ ) {},
1582 _mouseDrag: function( /* event */ ) {},
1583 _mouseStop: function( /* event */ ) {},
1584 _mouseCapture: function( /* event */ ) { return true; }
1589 * jQuery UI Sortable 1.12.1
1590 * http://jqueryui.com
1592 * Copyright jQuery Foundation and other contributors
1593 * Released under the MIT license.
1594 * http://jquery.org/license
1598 //>>group: Interactions
1599 //>>description: Enables items in a list to be sorted using the mouse.
1600 //>>docs: http://api.jqueryui.com/sortable/
1601 //>>demos: http://jqueryui.com/sortable/
1602 //>>css.structure: ../../themes/base/sortable.css
1606 var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, {
1608 widgetEventPrefix: "sort",
1618 forcePlaceholderSize: false,
1619 forceHelperSize: false,
1628 scrollSensitivity: 20,
1631 tolerance: "intersect",
1649 _isOverAxis: function( x, reference, size ) {
1650 return ( x >= reference ) && ( x < ( reference + size ) );
1653 _isFloating: function( item ) {
1654 return ( /left|right/ ).test( item.css( "float" ) ) ||
1655 ( /inline|table-cell/ ).test( item.css( "display" ) );
1658 _create: function() {
1659 this.containerCache = {};
1660 this._addClass( "ui-sortable" );
1665 //Let's determine the parent's offset
1666 this.offset = this.element.offset();
1668 //Initialize mouse events for interaction
1671 this._setHandleClassName();
1678 _setOption: function( key, value ) {
1679 this._super( key, value );
1681 if ( key === "handle" ) {
1682 this._setHandleClassName();
1686 _setHandleClassName: function() {
1688 this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
1689 $.each( this.items, function() {
1691 this.instance.options.handle ?
1692 this.item.find( this.instance.options.handle ) :
1694 "ui-sortable-handle"
1699 _destroy: function() {
1700 this._mouseDestroy();
1702 for ( var i = this.items.length - 1; i >= 0; i-- ) {
1703 this.items[ i ].item.removeData( this.widgetName + "-item" );
1709 _mouseCapture: function( event, overrideHandle ) {
1710 var currentItem = null,
1711 validHandle = false,
1714 if ( this.reverting ) {
1718 if ( this.options.disabled || this.options.type === "static" ) {
1722 //We have to refresh the items data once first
1723 this._refreshItems( event );
1725 //Find out if the clicked node (or one of its parents) is a actual item in this.items
1726 $( event.target ).parents().each( function() {
1727 if ( $.data( this, that.widgetName + "-item" ) === that ) {
1728 currentItem = $( this );
1732 if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
1733 currentItem = $( event.target );
1736 if ( !currentItem ) {
1739 if ( this.options.handle && !overrideHandle ) {
1740 $( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
1741 if ( this === event.target ) {
1745 if ( !validHandle ) {
1750 this.currentItem = currentItem;
1751 this._removeCurrentsFromItems();
1756 _mouseStart: function( event, overrideHandle, noActivation ) {
1761 this.currentContainer = this;
1763 //We only need to call refreshPositions, because the refreshItems call has been moved to
1765 this.refreshPositions();
1767 //Create and append the visible helper
1768 this.helper = this._createHelper( event );
1770 //Cache the helper size
1771 this._cacheHelperProportions();
1774 * - Position generation -
1775 * This block generates everything position related - it's the core of draggables.
1778 //Cache the margins of the original element
1779 this._cacheMargins();
1781 //Get the next scrolling parent
1782 this.scrollParent = this.helper.scrollParent();
1784 //The element's absolute position on the page minus margins
1785 this.offset = this.currentItem.offset();
1787 top: this.offset.top - this.margins.top,
1788 left: this.offset.left - this.margins.left
1791 $.extend( this.offset, {
1792 click: { //Where the click happened, relative to the element
1793 left: event.pageX - this.offset.left,
1794 top: event.pageY - this.offset.top
1796 parent: this._getParentOffset(),
1798 // This is a relative to absolute position minus the actual position calculation -
1799 // only used for relative positioned helper
1800 relative: this._getRelativeOffset()
1803 // Only after we got the offset, we can change the helper's position to absolute
1804 // TODO: Still need to figure out a way to make relative sorting possible
1805 this.helper.css( "position", "absolute" );
1806 this.cssPosition = this.helper.css( "position" );
1808 //Generate the original position
1809 this.originalPosition = this._generatePosition( event );
1810 this.originalPageX = event.pageX;
1811 this.originalPageY = event.pageY;
1813 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1814 ( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
1816 //Cache the former DOM position
1817 this.domPosition = {
1818 prev: this.currentItem.prev()[ 0 ],
1819 parent: this.currentItem.parent()[ 0 ]
1822 // If the helper is not the original, hide the original so it's not playing any role during
1823 // the drag, won't cause anything bad this way
1824 if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
1825 this.currentItem.hide();
1828 //Create the placeholder
1829 this._createPlaceholder();
1831 //Set a containment if given in the options
1832 if ( o.containment ) {
1833 this._setContainment();
1836 if ( o.cursor && o.cursor !== "auto" ) { // cursor option
1837 body = this.document.find( "body" );
1840 this.storedCursor = body.css( "cursor" );
1841 body.css( "cursor", o.cursor );
1843 this.storedStylesheet =
1844 $( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
1847 if ( o.opacity ) { // opacity option
1848 if ( this.helper.css( "opacity" ) ) {
1849 this._storedOpacity = this.helper.css( "opacity" );
1851 this.helper.css( "opacity", o.opacity );
1854 if ( o.zIndex ) { // zIndex option
1855 if ( this.helper.css( "zIndex" ) ) {
1856 this._storedZIndex = this.helper.css( "zIndex" );
1858 this.helper.css( "zIndex", o.zIndex );
1862 if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1863 this.scrollParent[ 0 ].tagName !== "HTML" ) {
1864 this.overflowOffset = this.scrollParent.offset();
1868 this._trigger( "start", event, this._uiHash() );
1870 //Recache the helper size
1871 if ( !this._preserveHelperProportions ) {
1872 this._cacheHelperProportions();
1875 //Post "activate" events to possible containers
1876 if ( !noActivation ) {
1877 for ( i = this.containers.length - 1; i >= 0; i-- ) {
1878 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
1882 //Prepare possible droppables
1883 if ( $.ui.ddmanager ) {
1884 $.ui.ddmanager.current = this;
1887 if ( $.ui.ddmanager && !o.dropBehaviour ) {
1888 $.ui.ddmanager.prepareOffsets( this, event );
1891 this.dragging = true;
1893 this._addClass( this.helper, "ui-sortable-helper" );
1895 // Execute the drag once - this causes the helper not to be visiblebefore getting its
1897 this._mouseDrag( event );
1902 _mouseDrag: function( event ) {
1903 var i, item, itemElement, intersection,
1907 //Compute the helpers position
1908 this.position = this._generatePosition( event );
1909 this.positionAbs = this._convertPositionTo( "absolute" );
1911 if ( !this.lastPositionAbs ) {
1912 this.lastPositionAbs = this.positionAbs;
1916 if ( this.options.scroll ) {
1917 if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1918 this.scrollParent[ 0 ].tagName !== "HTML" ) {
1920 if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
1921 event.pageY < o.scrollSensitivity ) {
1922 this.scrollParent[ 0 ].scrollTop =
1923 scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
1924 } else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
1925 this.scrollParent[ 0 ].scrollTop =
1926 scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
1929 if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
1930 event.pageX < o.scrollSensitivity ) {
1931 this.scrollParent[ 0 ].scrollLeft = scrolled =
1932 this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
1933 } else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
1934 this.scrollParent[ 0 ].scrollLeft = scrolled =
1935 this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
1940 if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
1941 scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
1942 } else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
1943 o.scrollSensitivity ) {
1944 scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
1947 if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
1948 scrolled = this.document.scrollLeft(
1949 this.document.scrollLeft() - o.scrollSpeed
1951 } else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
1952 o.scrollSensitivity ) {
1953 scrolled = this.document.scrollLeft(
1954 this.document.scrollLeft() + o.scrollSpeed
1960 if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
1961 $.ui.ddmanager.prepareOffsets( this, event );
1965 //Regenerate the absolute position used for position checks
1966 this.positionAbs = this._convertPositionTo( "absolute" );
1968 //Set the helper position
1969 if ( !this.options.axis || this.options.axis !== "y" ) {
1970 this.helper[ 0 ].style.left = this.position.left + "px";
1972 if ( !this.options.axis || this.options.axis !== "x" ) {
1973 this.helper[ 0 ].style.top = this.position.top + "px";
1977 for ( i = this.items.length - 1; i >= 0; i-- ) {
1979 //Cache variables and intersection, continue if no intersection
1980 item = this.items[ i ];
1981 itemElement = item.item[ 0 ];
1982 intersection = this._intersectsWithPointer( item );
1983 if ( !intersection ) {
1987 // Only put the placeholder inside the current Container, skip all
1988 // items from other containers. This works because when moving
1989 // an item from one container to another the
1990 // currentContainer is switched before the placeholder is moved.
1992 // Without this, moving items in "sub-sortables" can cause
1993 // the placeholder to jitter between the outer and inner container.
1994 if ( item.instance !== this.currentContainer ) {
1998 // Cannot intersect with itself
1999 // no useless actions that have been done before
2000 // no action if the item moved is the parent of the item checked
2001 if ( itemElement !== this.currentItem[ 0 ] &&
2002 this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement &&
2003 !$.contains( this.placeholder[ 0 ], itemElement ) &&
2004 ( this.options.type === "semi-dynamic" ?
2005 !$.contains( this.element[ 0 ], itemElement ) :
2010 this.direction = intersection === 1 ? "down" : "up";
2012 if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) {
2013 this._rearrange( event, item );
2018 this._trigger( "change", event, this._uiHash() );
2023 //Post events to containers
2024 this._contactContainers( event );
2026 //Interconnect with droppables
2027 if ( $.ui.ddmanager ) {
2028 $.ui.ddmanager.drag( this, event );
2032 this._trigger( "sort", event, this._uiHash() );
2034 this.lastPositionAbs = this.positionAbs;
2039 _mouseStop: function( event, noPropagation ) {
2045 //If we are using droppables, inform the manager about the drop
2046 if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
2047 $.ui.ddmanager.drop( this, event );
2050 if ( this.options.revert ) {
2052 cur = this.placeholder.offset(),
2053 axis = this.options.axis,
2056 if ( !axis || axis === "x" ) {
2057 animation.left = cur.left - this.offset.parent.left - this.margins.left +
2058 ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
2060 this.offsetParent[ 0 ].scrollLeft
2063 if ( !axis || axis === "y" ) {
2064 animation.top = cur.top - this.offset.parent.top - this.margins.top +
2065 ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
2067 this.offsetParent[ 0 ].scrollTop
2070 this.reverting = true;
2071 $( this.helper ).animate(
2073 parseInt( this.options.revert, 10 ) || 500,
2075 that._clear( event );
2079 this._clear( event, noPropagation );
2086 cancel: function() {
2088 if ( this.dragging ) {
2090 this._mouseUp( new $.Event( "mouseup", { target: null } ) );
2092 if ( this.options.helper === "original" ) {
2093 this.currentItem.css( this._storedCSS );
2094 this._removeClass( this.currentItem, "ui-sortable-helper" );
2096 this.currentItem.show();
2099 //Post deactivating events to containers
2100 for ( var i = this.containers.length - 1; i >= 0; i-- ) {
2101 this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
2102 if ( this.containers[ i ].containerCache.over ) {
2103 this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
2104 this.containers[ i ].containerCache.over = 0;
2110 if ( this.placeholder ) {
2112 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
2113 // it unbinds ALL events from the original node!
2114 if ( this.placeholder[ 0 ].parentNode ) {
2115 this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
2117 if ( this.options.helper !== "original" && this.helper &&
2118 this.helper[ 0 ].parentNode ) {
2119 this.helper.remove();
2129 if ( this.domPosition.prev ) {
2130 $( this.domPosition.prev ).after( this.currentItem );
2132 $( this.domPosition.parent ).prepend( this.currentItem );
2140 serialize: function( o ) {
2142 var items = this._getItemsAsjQuery( o && o.connected ),
2146 $( items ).each( function() {
2147 var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
2148 .match( o.expression || ( /(.+)[\-=_](.+)/ ) );
2151 ( o.key || res[ 1 ] + "[]" ) +
2152 "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
2156 if ( !str.length && o.key ) {
2157 str.push( o.key + "=" );
2160 return str.join( "&" );
2164 toArray: function( o ) {
2166 var items = this._getItemsAsjQuery( o && o.connected ),
2171 items.each( function() {
2172 ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
2178 /* Be careful with the following core functions */
2179 _intersectsWith: function( item ) {
2181 var x1 = this.positionAbs.left,
2182 x2 = x1 + this.helperProportions.width,
2183 y1 = this.positionAbs.top,
2184 y2 = y1 + this.helperProportions.height,
2188 b = t + item.height,
2189 dyClick = this.offset.click.top,
2190 dxClick = this.offset.click.left,
2191 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
2192 ( y1 + dyClick ) < b ),
2193 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
2194 ( x1 + dxClick ) < r ),
2195 isOverElement = isOverElementHeight && isOverElementWidth;
2197 if ( this.options.tolerance === "pointer" ||
2198 this.options.forcePointerForContainers ||
2199 ( this.options.tolerance !== "pointer" &&
2200 this.helperProportions[ this.floating ? "width" : "height" ] >
2201 item[ this.floating ? "width" : "height" ] )
2203 return isOverElement;
2206 return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
2207 x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
2208 t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
2209 y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half
2214 _intersectsWithPointer: function( item ) {
2215 var verticalDirection, horizontalDirection,
2216 isOverElementHeight = ( this.options.axis === "x" ) ||
2218 this.positionAbs.top + this.offset.click.top, item.top, item.height ),
2219 isOverElementWidth = ( this.options.axis === "y" ) ||
2221 this.positionAbs.left + this.offset.click.left, item.left, item.width ),
2222 isOverElement = isOverElementHeight && isOverElementWidth;
2224 if ( !isOverElement ) {
2228 verticalDirection = this._getDragVerticalDirection();
2229 horizontalDirection = this._getDragHorizontalDirection();
2231 return this.floating ?
2232 ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 )
2233 : ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );
2237 _intersectsWithSides: function( item ) {
2239 var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
2240 this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
2241 isOverRightHalf = this._isOverAxis( this.positionAbs.left +
2242 this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
2243 verticalDirection = this._getDragVerticalDirection(),
2244 horizontalDirection = this._getDragHorizontalDirection();
2246 if ( this.floating && horizontalDirection ) {
2247 return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
2248 ( horizontalDirection === "left" && !isOverRightHalf ) );
2250 return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
2251 ( verticalDirection === "up" && !isOverBottomHalf ) );
2256 _getDragVerticalDirection: function() {
2257 var delta = this.positionAbs.top - this.lastPositionAbs.top;
2258 return delta !== 0 && ( delta > 0 ? "down" : "up" );
2261 _getDragHorizontalDirection: function() {
2262 var delta = this.positionAbs.left - this.lastPositionAbs.left;
2263 return delta !== 0 && ( delta > 0 ? "right" : "left" );
2266 refresh: function( event ) {
2267 this._refreshItems( event );
2268 this._setHandleClassName();
2269 this.refreshPositions();
2273 _connectWith: function() {
2274 var options = this.options;
2275 return options.connectWith.constructor === String ?
2276 [ options.connectWith ] :
2277 options.connectWith;
2280 _getItemsAsjQuery: function( connected ) {
2282 var i, j, cur, inst,
2285 connectWith = this._connectWith();
2287 if ( connectWith && connected ) {
2288 for ( i = connectWith.length - 1; i >= 0; i-- ) {
2289 cur = $( connectWith[ i ], this.document[ 0 ] );
2290 for ( j = cur.length - 1; j >= 0; j-- ) {
2291 inst = $.data( cur[ j ], this.widgetFullName );
2292 if ( inst && inst !== this && !inst.options.disabled ) {
2293 queries.push( [ $.isFunction( inst.options.items ) ?
2294 inst.options.items.call( inst.element ) :
2295 $( inst.options.items, inst.element )
2296 .not( ".ui-sortable-helper" )
2297 .not( ".ui-sortable-placeholder" ), inst ] );
2303 queries.push( [ $.isFunction( this.options.items ) ?
2305 .call( this.element, null, { options: this.options, item: this.currentItem } ) :
2306 $( this.options.items, this.element )
2307 .not( ".ui-sortable-helper" )
2308 .not( ".ui-sortable-placeholder" ), this ] );
2310 function addItems() {
2313 for ( i = queries.length - 1; i >= 0; i-- ) {
2314 queries[ i ][ 0 ].each( addItems );
2321 _removeCurrentsFromItems: function() {
2323 var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );
2325 this.items = $.grep( this.items, function( item ) {
2326 for ( var j = 0; j < list.length; j++ ) {
2327 if ( list[ j ] === item.item[ 0 ] ) {
2336 _refreshItems: function( event ) {
2339 this.containers = [ this ];
2341 var i, j, cur, inst, targetData, _queries, item, queriesLength,
2343 queries = [ [ $.isFunction( this.options.items ) ?
2344 this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
2345 $( this.options.items, this.element ), this ] ],
2346 connectWith = this._connectWith();
2348 //Shouldn't be run the first time through due to massive slow-down
2349 if ( connectWith && this.ready ) {
2350 for ( i = connectWith.length - 1; i >= 0; i-- ) {
2351 cur = $( connectWith[ i ], this.document[ 0 ] );
2352 for ( j = cur.length - 1; j >= 0; j-- ) {
2353 inst = $.data( cur[ j ], this.widgetFullName );
2354 if ( inst && inst !== this && !inst.options.disabled ) {
2355 queries.push( [ $.isFunction( inst.options.items ) ?
2357 .call( inst.element[ 0 ], event, { item: this.currentItem } ) :
2358 $( inst.options.items, inst.element ), inst ] );
2359 this.containers.push( inst );
2365 for ( i = queries.length - 1; i >= 0; i-- ) {
2366 targetData = queries[ i ][ 1 ];
2367 _queries = queries[ i ][ 0 ];
2369 for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
2370 item = $( _queries[ j ] );
2372 // Data for target checking (mouse manager)
2373 item.data( this.widgetName + "-item", targetData );
2377 instance: targetData,
2378 width: 0, height: 0,
2386 refreshPositions: function( fast ) {
2388 // Determine whether items are being displayed horizontally
2389 this.floating = this.items.length ?
2390 this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
2393 //This has to be redone because due to the item being moved out/into the offsetParent,
2394 // the offsetParent's position will change
2395 if ( this.offsetParent && this.helper ) {
2396 this.offset.parent = this._getParentOffset();
2401 for ( i = this.items.length - 1; i >= 0; i-- ) {
2402 item = this.items[ i ];
2404 //We ignore calculating positions of all connected containers when we're not over them
2405 if ( item.instance !== this.currentContainer && this.currentContainer &&
2406 item.item[ 0 ] !== this.currentItem[ 0 ] ) {
2410 t = this.options.toleranceElement ?
2411 $( this.options.toleranceElement, item.item ) :
2415 item.width = t.outerWidth();
2416 item.height = t.outerHeight();
2424 if ( this.options.custom && this.options.custom.refreshContainers ) {
2425 this.options.custom.refreshContainers.call( this );
2427 for ( i = this.containers.length - 1; i >= 0; i-- ) {
2428 p = this.containers[ i ].element.offset();
2429 this.containers[ i ].containerCache.left = p.left;
2430 this.containers[ i ].containerCache.top = p.top;
2431 this.containers[ i ].containerCache.width =
2432 this.containers[ i ].element.outerWidth();
2433 this.containers[ i ].containerCache.height =
2434 this.containers[ i ].element.outerHeight();
2441 _createPlaceholder: function( that ) {
2442 that = that || this;
2446 if ( !o.placeholder || o.placeholder.constructor === String ) {
2447 className = o.placeholder;
2449 element: function() {
2451 var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(),
2452 element = $( "<" + nodeName + ">", that.document[ 0 ] );
2454 that._addClass( element, "ui-sortable-placeholder",
2455 className || that.currentItem[ 0 ].className )
2456 ._removeClass( element, "ui-sortable-helper" );
2458 if ( nodeName === "tbody" ) {
2459 that._createTrPlaceholder(
2460 that.currentItem.find( "tr" ).eq( 0 ),
2461 $( "<tr>", that.document[ 0 ] ).appendTo( element )
2463 } else if ( nodeName === "tr" ) {
2464 that._createTrPlaceholder( that.currentItem, element );
2465 } else if ( nodeName === "img" ) {
2466 element.attr( "src", that.currentItem.attr( "src" ) );
2470 element.css( "visibility", "hidden" );
2475 update: function( container, p ) {
2477 // 1. If a className is set as 'placeholder option, we don't force sizes -
2478 // the class is responsible for that
2479 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a
2480 // class name is specified
2481 if ( className && !o.forcePlaceholderSize ) {
2485 //If the element doesn't have a actual height by itself (without styles coming
2486 // from a stylesheet), it receives the inline height from the dragged item
2487 if ( !p.height() ) {
2489 that.currentItem.innerHeight() -
2490 parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
2491 parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
2495 that.currentItem.innerWidth() -
2496 parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
2497 parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
2503 //Create the placeholder
2504 that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );
2506 //Append it after the actual current item
2507 that.currentItem.after( that.placeholder );
2509 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
2510 o.placeholder.update( that, that.placeholder );
2514 _createTrPlaceholder: function( sourceTr, targetTr ) {
2517 sourceTr.children().each( function() {
2518 $( "<td> </td>", that.document[ 0 ] )
2519 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
2520 .appendTo( targetTr );
2524 _contactContainers: function( event ) {
2525 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
2527 innermostContainer = null,
2528 innermostIndex = null;
2530 // Get innermost container that intersects with item
2531 for ( i = this.containers.length - 1; i >= 0; i-- ) {
2533 // Never consider a container that's located within the item itself
2534 if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
2538 if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {
2540 // If we've already found a container and it's more "inner" than this, then continue
2541 if ( innermostContainer &&
2543 this.containers[ i ].element[ 0 ],
2544 innermostContainer.element[ 0 ] ) ) {
2548 innermostContainer = this.containers[ i ];
2553 // container doesn't intersect. trigger "out" event if necessary
2554 if ( this.containers[ i ].containerCache.over ) {
2555 this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
2556 this.containers[ i ].containerCache.over = 0;
2562 // If no intersecting containers found, return
2563 if ( !innermostContainer ) {
2567 // Move the item into the container if it's not there already
2568 if ( this.containers.length === 1 ) {
2569 if ( !this.containers[ innermostIndex ].containerCache.over ) {
2570 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
2571 this.containers[ innermostIndex ].containerCache.over = 1;
2575 // When entering a new container, we will find the item with the least distance and
2576 // append our item near it
2578 itemWithLeastDistance = null;
2579 floating = innermostContainer.floating || this._isFloating( this.currentItem );
2580 posProperty = floating ? "left" : "top";
2581 sizeProperty = floating ? "width" : "height";
2582 axis = floating ? "pageX" : "pageY";
2584 for ( j = this.items.length - 1; j >= 0; j-- ) {
2586 this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
2590 if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
2594 cur = this.items[ j ].item.offset()[ posProperty ];
2596 if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
2600 if ( Math.abs( event[ axis ] - cur ) < dist ) {
2601 dist = Math.abs( event[ axis ] - cur );
2602 itemWithLeastDistance = this.items[ j ];
2603 this.direction = nearBottom ? "up" : "down";
2607 //Check if dropOnEmpty is enabled
2608 if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
2612 if ( this.currentContainer === this.containers[ innermostIndex ] ) {
2613 if ( !this.currentContainer.containerCache.over ) {
2614 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
2615 this.currentContainer.containerCache.over = 1;
2620 itemWithLeastDistance ?
2621 this._rearrange( event, itemWithLeastDistance, null, true ) :
2622 this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
2623 this._trigger( "change", event, this._uiHash() );
2624 this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
2625 this.currentContainer = this.containers[ innermostIndex ];
2627 //Update the placeholder
2628 this.options.placeholder.update( this.currentContainer, this.placeholder );
2630 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
2631 this.containers[ innermostIndex ].containerCache.over = 1;
2636 _createHelper: function( event ) {
2638 var o = this.options,
2639 helper = $.isFunction( o.helper ) ?
2640 $( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
2641 ( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );
2643 //Add the helper to the DOM if that didn't happen already
2644 if ( !helper.parents( "body" ).length ) {
2645 $( o.appendTo !== "parent" ?
2647 this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] );
2650 if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
2652 width: this.currentItem[ 0 ].style.width,
2653 height: this.currentItem[ 0 ].style.height,
2654 position: this.currentItem.css( "position" ),
2655 top: this.currentItem.css( "top" ),
2656 left: this.currentItem.css( "left" )
2660 if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
2661 helper.width( this.currentItem.width() );
2663 if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
2664 helper.height( this.currentItem.height() );
2671 _adjustOffsetFromHelper: function( obj ) {
2672 if ( typeof obj === "string" ) {
2673 obj = obj.split( " " );
2675 if ( $.isArray( obj ) ) {
2676 obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
2678 if ( "left" in obj ) {
2679 this.offset.click.left = obj.left + this.margins.left;
2681 if ( "right" in obj ) {
2682 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
2684 if ( "top" in obj ) {
2685 this.offset.click.top = obj.top + this.margins.top;
2687 if ( "bottom" in obj ) {
2688 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
2692 _getParentOffset: function() {
2694 //Get the offsetParent and cache its position
2695 this.offsetParent = this.helper.offsetParent();
2696 var po = this.offsetParent.offset();
2698 // This is a special case where we need to modify a offset calculated on start, since the
2699 // following happened:
2700 // 1. The position of the helper is absolute, so it's position is calculated based on the
2701 // next positioned parent
2702 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
2703 // the document, which means that the scroll is included in the initial calculation of the
2704 // offset of the parent, and never recalculated upon drag
2705 if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
2706 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
2707 po.left += this.scrollParent.scrollLeft();
2708 po.top += this.scrollParent.scrollTop();
2711 // This needs to be actually done for all browsers, since pageX/pageY includes this
2712 // information with an ugly IE fix
2713 if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
2714 ( this.offsetParent[ 0 ].tagName &&
2715 this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
2716 po = { top: 0, left: 0 };
2720 top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
2721 left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
2726 _getRelativeOffset: function() {
2728 if ( this.cssPosition === "relative" ) {
2729 var p = this.currentItem.position();
2731 top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
2732 this.scrollParent.scrollTop(),
2733 left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
2734 this.scrollParent.scrollLeft()
2737 return { top: 0, left: 0 };
2742 _cacheMargins: function() {
2744 left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
2745 top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
2749 _cacheHelperProportions: function() {
2750 this.helperProportions = {
2751 width: this.helper.outerWidth(),
2752 height: this.helper.outerHeight()
2756 _setContainment: function() {
2760 if ( o.containment === "parent" ) {
2761 o.containment = this.helper[ 0 ].parentNode;
2763 if ( o.containment === "document" || o.containment === "window" ) {
2764 this.containment = [
2765 0 - this.offset.relative.left - this.offset.parent.left,
2766 0 - this.offset.relative.top - this.offset.parent.top,
2767 o.containment === "document" ?
2768 this.document.width() :
2769 this.window.width() - this.helperProportions.width - this.margins.left,
2770 ( o.containment === "document" ?
2771 ( this.document.height() || document.body.parentNode.scrollHeight ) :
2772 this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
2773 ) - this.helperProportions.height - this.margins.top
2777 if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
2778 ce = $( o.containment )[ 0 ];
2779 co = $( o.containment ).offset();
2780 over = ( $( ce ).css( "overflow" ) !== "hidden" );
2782 this.containment = [
2783 co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
2784 ( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
2785 co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
2786 ( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
2787 co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
2788 ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
2789 ( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
2790 this.helperProportions.width - this.margins.left,
2791 co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
2792 ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
2793 ( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
2794 this.helperProportions.height - this.margins.top
2800 _convertPositionTo: function( d, pos ) {
2803 pos = this.position;
2805 var mod = d === "absolute" ? 1 : -1,
2806 scroll = this.cssPosition === "absolute" &&
2807 !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
2808 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
2811 scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
2816 // The absolute mouse position
2819 // Only for relative positioned nodes: Relative offset from element to offset parent
2820 this.offset.relative.top * mod +
2822 // The offsetParent's offset without borders (offset + border)
2823 this.offset.parent.top * mod -
2824 ( ( this.cssPosition === "fixed" ?
2825 -this.scrollParent.scrollTop() :
2826 ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
2830 // The absolute mouse position
2833 // Only for relative positioned nodes: Relative offset from element to offset parent
2834 this.offset.relative.left * mod +
2836 // The offsetParent's offset without borders (offset + border)
2837 this.offset.parent.left * mod -
2838 ( ( this.cssPosition === "fixed" ?
2839 -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
2840 scroll.scrollLeft() ) * mod )
2846 _generatePosition: function( event ) {
2850 pageX = event.pageX,
2851 pageY = event.pageY,
2852 scroll = this.cssPosition === "absolute" &&
2853 !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
2854 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
2857 scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
2859 // This is another very weird special case that only happens for relative elements:
2860 // 1. If the css position is relative
2861 // 2. and the scroll parent is the document or similar to the offset parent
2862 // we have to refresh the relative offset during the scroll so there are no jumps
2863 if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
2864 this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
2865 this.offset.relative = this._getRelativeOffset();
2869 * - Position constraining -
2870 * Constrain the position to a mix of grid, containment.
2873 if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options
2875 if ( this.containment ) {
2876 if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
2877 pageX = this.containment[ 0 ] + this.offset.click.left;
2879 if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
2880 pageY = this.containment[ 1 ] + this.offset.click.top;
2882 if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
2883 pageX = this.containment[ 2 ] + this.offset.click.left;
2885 if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
2886 pageY = this.containment[ 3 ] + this.offset.click.top;
2891 top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
2892 o.grid[ 1 ] ) * o.grid[ 1 ];
2893 pageY = this.containment ?
2894 ( ( top - this.offset.click.top >= this.containment[ 1 ] &&
2895 top - this.offset.click.top <= this.containment[ 3 ] ) ?
2897 ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
2898 top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
2901 left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
2902 o.grid[ 0 ] ) * o.grid[ 0 ];
2903 pageX = this.containment ?
2904 ( ( left - this.offset.click.left >= this.containment[ 0 ] &&
2905 left - this.offset.click.left <= this.containment[ 2 ] ) ?
2907 ( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
2908 left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
2917 // The absolute mouse position
2920 // Click offset (relative to the element)
2921 this.offset.click.top -
2923 // Only for relative positioned nodes: Relative offset from element to offset parent
2924 this.offset.relative.top -
2926 // The offsetParent's offset without borders (offset + border)
2927 this.offset.parent.top +
2928 ( ( this.cssPosition === "fixed" ?
2929 -this.scrollParent.scrollTop() :
2930 ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
2934 // The absolute mouse position
2937 // Click offset (relative to the element)
2938 this.offset.click.left -
2940 // Only for relative positioned nodes: Relative offset from element to offset parent
2941 this.offset.relative.left -
2943 // The offsetParent's offset without borders (offset + border)
2944 this.offset.parent.left +
2945 ( ( this.cssPosition === "fixed" ?
2946 -this.scrollParent.scrollLeft() :
2947 scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
2953 _rearrange: function( event, i, a, hardRefresh ) {
2955 a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) :
2956 i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
2957 ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
2959 //Various things done here to improve the performance:
2960 // 1. we create a setTimeout, that calls refreshPositions
2961 // 2. on the instance, we have a counter variable, that get's higher after every append
2962 // 3. on the local scope, we copy the counter variable, and check in the timeout,
2963 // if it's still the same
2964 // 4. this lets only the last addition to the timeout stack through
2965 this.counter = this.counter ? ++this.counter : 1;
2966 var counter = this.counter;
2968 this._delay( function() {
2969 if ( counter === this.counter ) {
2971 //Precompute after each DOM insertion, NOT on mousemove
2972 this.refreshPositions( !hardRefresh );
2978 _clear: function( event, noPropagation ) {
2980 this.reverting = false;
2982 // We delay all events that have to be triggered to after the point where the placeholder
2983 // has been removed and everything else normalized again
2985 delayedTriggers = [];
2987 // We first have to update the dom position of the actual currentItem
2988 // Note: don't do it if the current item is already removed (by a user), or it gets
2989 // reappended (see #4088)
2990 if ( !this._noFinalSort && this.currentItem.parent().length ) {
2991 this.placeholder.before( this.currentItem );
2993 this._noFinalSort = null;
2995 if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
2996 for ( i in this._storedCSS ) {
2997 if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
2998 this._storedCSS[ i ] = "";
3001 this.currentItem.css( this._storedCSS );
3002 this._removeClass( this.currentItem, "ui-sortable-helper" );
3004 this.currentItem.show();
3007 if ( this.fromOutside && !noPropagation ) {
3008 delayedTriggers.push( function( event ) {
3009 this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
3012 if ( ( this.fromOutside ||
3013 this.domPosition.prev !==
3014 this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
3015 this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {
3017 // Trigger update callback if the DOM position has changed
3018 delayedTriggers.push( function( event ) {
3019 this._trigger( "update", event, this._uiHash() );
3023 // Check if the items Container has Changed and trigger appropriate
3025 if ( this !== this.currentContainer ) {
3026 if ( !noPropagation ) {
3027 delayedTriggers.push( function( event ) {
3028 this._trigger( "remove", event, this._uiHash() );
3030 delayedTriggers.push( ( function( c ) {
3031 return function( event ) {
3032 c._trigger( "receive", event, this._uiHash( this ) );
3034 } ).call( this, this.currentContainer ) );
3035 delayedTriggers.push( ( function( c ) {
3036 return function( event ) {
3037 c._trigger( "update", event, this._uiHash( this ) );
3039 } ).call( this, this.currentContainer ) );
3043 //Post events to containers
3044 function delayEvent( type, instance, container ) {
3045 return function( event ) {
3046 container._trigger( type, event, instance._uiHash( instance ) );
3049 for ( i = this.containers.length - 1; i >= 0; i-- ) {
3050 if ( !noPropagation ) {
3051 delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
3053 if ( this.containers[ i ].containerCache.over ) {
3054 delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
3055 this.containers[ i ].containerCache.over = 0;
3059 //Do what was originally in plugins
3060 if ( this.storedCursor ) {
3061 this.document.find( "body" ).css( "cursor", this.storedCursor );
3062 this.storedStylesheet.remove();
3064 if ( this._storedOpacity ) {
3065 this.helper.css( "opacity", this._storedOpacity );
3067 if ( this._storedZIndex ) {
3068 this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
3071 this.dragging = false;
3073 if ( !noPropagation ) {
3074 this._trigger( "beforeStop", event, this._uiHash() );
3077 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
3078 // it unbinds ALL events from the original node!
3079 this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
3081 if ( !this.cancelHelperRemoval ) {
3082 if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
3083 this.helper.remove();
3088 if ( !noPropagation ) {
3089 for ( i = 0; i < delayedTriggers.length; i++ ) {
3091 // Trigger all delayed events
3092 delayedTriggers[ i ].call( this, event );
3094 this._trigger( "stop", event, this._uiHash() );
3097 this.fromOutside = false;
3098 return !this.cancelHelperRemoval;
3102 _trigger: function() {
3103 if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
3108 _uiHash: function( _inst ) {
3109 var inst = _inst || this;
3111 helper: inst.helper,
3112 placeholder: inst.placeholder || $( [] ),
3113 position: inst.position,
3114 originalPosition: inst.originalPosition,
3115 offset: inst.positionAbs,
3116 item: inst.currentItem,
3117 sender: _inst ? _inst.element : null
3125 var safeActiveElement = $.ui.safeActiveElement = function( document ) {
3128 // Support: IE 9 only
3129 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
3131 activeElement = document.activeElement;
3133 activeElement = document.body;
3136 // Support: IE 9 - 11 only
3137 // IE may return null instead of an element
3138 // Interestingly, this only seems to occur when NOT in an iframe
3139 if ( !activeElement ) {
3140 activeElement = document.body;
3143 // Support: IE 11 only
3144 // IE11 returns a seemingly empty object in some cases when accessing
3145 // document.activeElement from an <iframe>
3146 if ( !activeElement.nodeName ) {
3147 activeElement = document.body;
3150 return activeElement;
3155 * jQuery UI Menu 1.12.1
3156 * http://jqueryui.com
3158 * Copyright jQuery Foundation and other contributors
3159 * Released under the MIT license.
3160 * http://jquery.org/license
3165 //>>description: Creates nestable menus.
3166 //>>docs: http://api.jqueryui.com/menu/
3167 //>>demos: http://jqueryui.com/menu/
3168 //>>css.structure: ../../themes/base/core.css
3169 //>>css.structure: ../../themes/base/menu.css
3170 //>>css.theme: ../../themes/base/theme.css
3174 var widgetsMenu = $.widget( "ui.menu", {
3176 defaultElement: "<ul>",
3180 submenu: "ui-icon-caret-1-e"
3196 _create: function() {
3197 this.activeMenu = this.element;
3199 // Flag used to prevent firing of the click handler
3200 // as the event bubbles up through nested menus
3201 this.mouseHandled = false;
3205 role: this.options.role,
3209 this._addClass( "ui-menu", "ui-widget ui-widget-content" );
3212 // Prevent focus from sticking to links inside menu after clicking
3213 // them (focus should always stay on UL during navigation).
3214 "mousedown .ui-menu-item": function( event ) {
3215 event.preventDefault();
3217 "click .ui-menu-item": function( event ) {
3218 var target = $( event.target );
3219 var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
3220 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
3221 this.select( event );
3223 // Only set the mouseHandled flag if the event will bubble, see #9469.
3224 if ( !event.isPropagationStopped() ) {
3225 this.mouseHandled = true;
3228 // Open submenu on click
3229 if ( target.has( ".ui-menu" ).length ) {
3230 this.expand( event );
3231 } else if ( !this.element.is( ":focus" ) &&
3232 active.closest( ".ui-menu" ).length ) {
3234 // Redirect focus to the menu
3235 this.element.trigger( "focus", [ true ] );
3237 // If the active item is on the top level, let it stay active.
3238 // Otherwise, blur the active item since it is no longer visible.
3239 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
3240 clearTimeout( this.timer );
3245 "mouseenter .ui-menu-item": function( event ) {
3247 // Ignore mouse events while typeahead is active, see #10458.
3248 // Prevents focusing the wrong item when typeahead causes a scroll while the mouse
3249 // is over an item in the menu
3250 if ( this.previousFilter ) {
3254 var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
3255 target = $( event.currentTarget );
3257 // Ignore bubbled events on parent items, see #11641
3258 if ( actualTarget[ 0 ] !== target[ 0 ] ) {
3262 // Remove ui-state-active class from siblings of the newly focused menu item
3263 // to avoid a jump caused by adjacent elements both having a class with a border
3264 this._removeClass( target.siblings().children( ".ui-state-active" ),
3265 null, "ui-state-active" );
3266 this.focus( event, target );
3268 mouseleave: "collapseAll",
3269 "mouseleave .ui-menu": "collapseAll",
3270 focus: function( event, keepActiveItem ) {
3272 // If there's already an active item, keep it active
3273 // If not, activate the first item
3274 var item = this.active || this.element.find( this.options.items ).eq( 0 );
3276 if ( !keepActiveItem ) {
3277 this.focus( event, item );
3280 blur: function( event ) {
3281 this._delay( function() {
3282 var notContained = !$.contains(
3284 $.ui.safeActiveElement( this.document[ 0 ] )
3286 if ( notContained ) {
3287 this.collapseAll( event );
3296 // Clicks outside of a menu collapse any open menus
3297 this._on( this.document, {
3298 click: function( event ) {
3299 if ( this._closeOnDocumentClick( event ) ) {
3300 this.collapseAll( event );
3303 // Reset the mouseHandled flag
3304 this.mouseHandled = false;
3309 _destroy: function() {
3310 var items = this.element.find( ".ui-menu-item" )
3311 .removeAttr( "role aria-disabled" ),
3312 submenus = items.children( ".ui-menu-item-wrapper" )
3314 .removeAttr( "tabIndex role aria-haspopup" );
3316 // Destroy (sub)menus
3318 .removeAttr( "aria-activedescendant" )
3319 .find( ".ui-menu" ).addBack()
3320 .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
3325 submenus.children().each( function() {
3326 var elem = $( this );
3327 if ( elem.data( "ui-menu-submenu-caret" ) ) {
3333 _keydown: function( event ) {
3334 var match, prev, character, skip,
3335 preventDefault = true;
3337 switch ( event.keyCode ) {
3338 case $.ui.keyCode.PAGE_UP:
3339 this.previousPage( event );
3341 case $.ui.keyCode.PAGE_DOWN:
3342 this.nextPage( event );
3344 case $.ui.keyCode.HOME:
3345 this._move( "first", "first", event );
3347 case $.ui.keyCode.END:
3348 this._move( "last", "last", event );
3350 case $.ui.keyCode.UP:
3351 this.previous( event );
3353 case $.ui.keyCode.DOWN:
3356 case $.ui.keyCode.LEFT:
3357 this.collapse( event );
3359 case $.ui.keyCode.RIGHT:
3360 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
3361 this.expand( event );
3364 case $.ui.keyCode.ENTER:
3365 case $.ui.keyCode.SPACE:
3366 this._activate( event );
3368 case $.ui.keyCode.ESCAPE:
3369 this.collapse( event );
3372 preventDefault = false;
3373 prev = this.previousFilter || "";
3376 // Support number pad values
3377 character = event.keyCode >= 96 && event.keyCode <= 105 ?
3378 ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );
3380 clearTimeout( this.filterTimer );
3382 if ( character === prev ) {
3385 character = prev + character;
3388 match = this._filterMenuItems( character );
3389 match = skip && match.index( this.active.next() ) !== -1 ?
3390 this.active.nextAll( ".ui-menu-item" ) :
3393 // If no matches on the current filter, reset to the last character pressed
3394 // to move down the menu to the first item that starts with that character
3395 if ( !match.length ) {
3396 character = String.fromCharCode( event.keyCode );
3397 match = this._filterMenuItems( character );
3400 if ( match.length ) {
3401 this.focus( event, match );
3402 this.previousFilter = character;
3403 this.filterTimer = this._delay( function() {
3404 delete this.previousFilter;
3407 delete this.previousFilter;
3411 if ( preventDefault ) {
3412 event.preventDefault();
3416 _activate: function( event ) {
3417 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
3418 if ( this.active.children( "[aria-haspopup='true']" ).length ) {
3419 this.expand( event );
3421 this.select( event );
3426 refresh: function() {
3427 var menus, items, newSubmenus, newItems, newWrappers,
3429 icon = this.options.icons.submenu,
3430 submenus = this.element.find( this.options.menus );
3432 this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
3434 // Initialize nested menus
3435 newSubmenus = submenus.filter( ":not(.ui-menu)" )
3438 role: this.options.role,
3439 "aria-hidden": "true",
3440 "aria-expanded": "false"
3443 var menu = $( this ),
3445 submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
3447 that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
3449 .attr( "aria-haspopup", "true" )
3450 .prepend( submenuCaret );
3451 menu.attr( "aria-labelledby", item.attr( "id" ) );
3454 this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
3456 menus = submenus.add( this.element );
3457 items = menus.find( this.options.items );
3459 // Initialize menu-items containing spaces and/or dashes only as dividers
3460 items.not( ".ui-menu-item" ).each( function() {
3461 var item = $( this );
3462 if ( that._isDivider( item ) ) {
3463 that._addClass( item, "ui-menu-divider", "ui-widget-content" );
3467 // Don't refresh list items that are already adapted
3468 newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
3469 newWrappers = newItems.children()
3474 role: this._itemRole()
3476 this._addClass( newItems, "ui-menu-item" )
3477 ._addClass( newWrappers, "ui-menu-item-wrapper" );
3479 // Add aria-disabled attribute to any disabled menu item
3480 items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
3482 // If the active item has been removed, blur the menu
3483 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
3488 _itemRole: function() {
3492 }[ this.options.role ];
3495 _setOption: function( key, value ) {
3496 if ( key === "icons" ) {
3497 var icons = this.element.find( ".ui-menu-icon" );
3498 this._removeClass( icons, null, this.options.icons.submenu )
3499 ._addClass( icons, null, value.submenu );
3501 this._super( key, value );
3504 _setOptionDisabled: function( value ) {
3505 this._super( value );
3507 this.element.attr( "aria-disabled", String( value ) );
3508 this._toggleClass( null, "ui-state-disabled", !!value );
3511 focus: function( event, item ) {
3512 var nested, focused, activeParent;
3513 this.blur( event, event && event.type === "focus" );
3515 this._scrollIntoView( item );
3517 this.active = item.first();
3519 focused = this.active.children( ".ui-menu-item-wrapper" );
3520 this._addClass( focused, null, "ui-state-active" );
3522 // Only update aria-activedescendant if there's a role
3523 // otherwise we assume focus is managed elsewhere
3524 if ( this.options.role ) {
3525 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
3528 // Highlight active parent menu item, if any
3529 activeParent = this.active
3531 .closest( ".ui-menu-item" )
3532 .children( ".ui-menu-item-wrapper" );
3533 this._addClass( activeParent, null, "ui-state-active" );
3535 if ( event && event.type === "keydown" ) {
3538 this.timer = this._delay( function() {
3543 nested = item.children( ".ui-menu" );
3544 if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
3545 this._startOpening( nested );
3547 this.activeMenu = item.parent();
3549 this._trigger( "focus", event, { item: item } );
3552 _scrollIntoView: function( item ) {
3553 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
3554 if ( this._hasScroll() ) {
3555 borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
3556 paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
3557 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
3558 scroll = this.activeMenu.scrollTop();
3559 elementHeight = this.activeMenu.height();
3560 itemHeight = item.outerHeight();
3563 this.activeMenu.scrollTop( scroll + offset );
3564 } else if ( offset + itemHeight > elementHeight ) {
3565 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
3570 blur: function( event, fromFocus ) {
3572 clearTimeout( this.timer );
3575 if ( !this.active ) {
3579 this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
3580 null, "ui-state-active" );
3582 this._trigger( "blur", event, { item: this.active } );
3586 _startOpening: function( submenu ) {
3587 clearTimeout( this.timer );
3589 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
3590 // shift in the submenu position when mousing over the caret icon
3591 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
3595 this.timer = this._delay( function() {
3597 this._open( submenu );
3601 _open: function( submenu ) {
3602 var position = $.extend( {
3604 }, this.options.position );
3606 clearTimeout( this.timer );
3607 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
3609 .attr( "aria-hidden", "true" );
3613 .removeAttr( "aria-hidden" )
3614 .attr( "aria-expanded", "true" )
3615 .position( position );
3618 collapseAll: function( event, all ) {
3619 clearTimeout( this.timer );
3620 this.timer = this._delay( function() {
3622 // If we were passed an event, look for the submenu that contains the event
3623 var currentMenu = all ? this.element :
3624 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
3626 // If we found no valid submenu ancestor, use the main menu to close all
3628 if ( !currentMenu.length ) {
3629 currentMenu = this.element;
3632 this._close( currentMenu );
3636 // Work around active item staying active after menu is blurred
3637 this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );
3639 this.activeMenu = currentMenu;
3643 // With no arguments, closes the currently active menu - if nothing is active
3644 // it closes all menus. If passed an argument, it will search for menus BELOW
3645 _close: function( startMenu ) {
3647 startMenu = this.active ? this.active.parent() : this.element;
3650 startMenu.find( ".ui-menu" )
3652 .attr( "aria-hidden", "true" )
3653 .attr( "aria-expanded", "false" );
3656 _closeOnDocumentClick: function( event ) {
3657 return !$( event.target ).closest( ".ui-menu" ).length;
3660 _isDivider: function( item ) {
3662 // Match hyphen, em dash, en dash
3663 return !/[^\-\u2014\u2013\s]/.test( item.text() );
3666 collapse: function( event ) {
3667 var newItem = this.active &&
3668 this.active.parent().closest( ".ui-menu-item", this.element );
3669 if ( newItem && newItem.length ) {
3671 this.focus( event, newItem );
3675 expand: function( event ) {
3676 var newItem = this.active &&
3678 .children( ".ui-menu " )
3679 .find( this.options.items )
3682 if ( newItem && newItem.length ) {
3683 this._open( newItem.parent() );
3685 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
3686 this._delay( function() {
3687 this.focus( event, newItem );
3692 next: function( event ) {
3693 this._move( "next", "first", event );
3696 previous: function( event ) {
3697 this._move( "prev", "last", event );
3700 isFirstItem: function() {
3701 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
3704 isLastItem: function() {
3705 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
3708 _move: function( direction, filter, event ) {
3710 if ( this.active ) {
3711 if ( direction === "first" || direction === "last" ) {
3713 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
3717 [ direction + "All" ]( ".ui-menu-item" )
3721 if ( !next || !next.length || !this.active ) {
3722 next = this.activeMenu.find( this.options.items )[ filter ]();
3725 this.focus( event, next );
3728 nextPage: function( event ) {
3729 var item, base, height;
3731 if ( !this.active ) {
3735 if ( this.isLastItem() ) {
3738 if ( this._hasScroll() ) {
3739 base = this.active.offset().top;
3740 height = this.element.height();
3741 this.active.nextAll( ".ui-menu-item" ).each( function() {
3743 return item.offset().top - base - height < 0;
3746 this.focus( event, item );
3748 this.focus( event, this.activeMenu.find( this.options.items )
3749 [ !this.active ? "first" : "last" ]() );
3753 previousPage: function( event ) {
3754 var item, base, height;
3755 if ( !this.active ) {
3759 if ( this.isFirstItem() ) {
3762 if ( this._hasScroll() ) {
3763 base = this.active.offset().top;
3764 height = this.element.height();
3765 this.active.prevAll( ".ui-menu-item" ).each( function() {
3767 return item.offset().top - base + height > 0;
3770 this.focus( event, item );
3772 this.focus( event, this.activeMenu.find( this.options.items ).first() );
3776 _hasScroll: function() {
3777 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
3780 select: function( event ) {
3782 // TODO: It should never be possible to not have an active item at this
3783 // point, but the tests don't trigger mouseenter before click.
3784 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
3785 var ui = { item: this.active };
3786 if ( !this.active.has( ".ui-menu" ).length ) {
3787 this.collapseAll( event, true );
3789 this._trigger( "select", event, ui );
3792 _filterMenuItems: function( character ) {
3793 var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
3794 regex = new RegExp( "^" + escapedCharacter, "i" );
3796 return this.activeMenu
3797 .find( this.options.items )
3799 // Only match on items, not dividers or other content (#10571)
3800 .filter( ".ui-menu-item" )
3801 .filter( function() {
3803 $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
3810 * jQuery UI Autocomplete 1.12.1
3811 * http://jqueryui.com
3813 * Copyright jQuery Foundation and other contributors
3814 * Released under the MIT license.
3815 * http://jquery.org/license
3818 //>>label: Autocomplete
3820 //>>description: Lists suggested words as the user is typing.
3821 //>>docs: http://api.jqueryui.com/autocomplete/
3822 //>>demos: http://jqueryui.com/autocomplete/
3823 //>>css.structure: ../../themes/base/core.css
3824 //>>css.structure: ../../themes/base/autocomplete.css
3825 //>>css.theme: ../../themes/base/theme.css
3829 $.widget( "ui.autocomplete", {
3831 defaultElement: "<input>",
3857 _create: function() {
3859 // Some browsers only repeat keydown events, not keypress events,
3860 // so we use the suppressKeyPress flag to determine if we've already
3861 // handled the keydown event. #7269
3862 // Unfortunately the code for & in keypress is the same as the up arrow,
3863 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
3864 // events when we know the keydown event was used to modify the
3865 // search term. #7799
3866 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
3867 nodeName = this.element[ 0 ].nodeName.toLowerCase(),
3868 isTextarea = nodeName === "textarea",
3869 isInput = nodeName === "input";
3871 // Textareas are always multi-line
3872 // Inputs are always single-line, even if inside a contentEditable element
3873 // IE also treats inputs as contentEditable
3874 // All other element types are determined by whether or not they're contentEditable
3875 this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
3877 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
3878 this.isNewMenu = true;
3880 this._addClass( "ui-autocomplete-input" );
3881 this.element.attr( "autocomplete", "off" );
3883 this._on( this.element, {
3884 keydown: function( event ) {
3885 if ( this.element.prop( "readOnly" ) ) {
3886 suppressKeyPress = true;
3887 suppressInput = true;
3888 suppressKeyPressRepeat = true;
3892 suppressKeyPress = false;
3893 suppressInput = false;
3894 suppressKeyPressRepeat = false;
3895 var keyCode = $.ui.keyCode;
3896 switch ( event.keyCode ) {
3897 case keyCode.PAGE_UP:
3898 suppressKeyPress = true;
3899 this._move( "previousPage", event );
3901 case keyCode.PAGE_DOWN:
3902 suppressKeyPress = true;
3903 this._move( "nextPage", event );
3906 suppressKeyPress = true;
3907 this._keyEvent( "previous", event );
3910 suppressKeyPress = true;
3911 this._keyEvent( "next", event );
3915 // when menu is open and has focus
3916 if ( this.menu.active ) {
3918 // #6055 - Opera still allows the keypress to occur
3919 // which causes forms to submit
3920 suppressKeyPress = true;
3921 event.preventDefault();
3922 this.menu.select( event );
3926 if ( this.menu.active ) {
3927 this.menu.select( event );
3930 case keyCode.ESCAPE:
3931 if ( this.menu.element.is( ":visible" ) ) {
3932 if ( !this.isMultiLine ) {
3933 this._value( this.term );
3935 this.close( event );
3937 // Different browsers have different default behavior for escape
3938 // Single press can mean undo or clear
3939 // Double press in IE means clear the whole form
3940 event.preventDefault();
3944 suppressKeyPressRepeat = true;
3946 // search timeout should be triggered before the input value is changed
3947 this._searchTimeout( event );
3951 keypress: function( event ) {
3952 if ( suppressKeyPress ) {
3953 suppressKeyPress = false;
3954 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
3955 event.preventDefault();
3959 if ( suppressKeyPressRepeat ) {
3963 // Replicate some key handlers to allow them to repeat in Firefox and Opera
3964 var keyCode = $.ui.keyCode;
3965 switch ( event.keyCode ) {
3966 case keyCode.PAGE_UP:
3967 this._move( "previousPage", event );
3969 case keyCode.PAGE_DOWN:
3970 this._move( "nextPage", event );
3973 this._keyEvent( "previous", event );
3976 this._keyEvent( "next", event );
3980 input: function( event ) {
3981 if ( suppressInput ) {
3982 suppressInput = false;
3983 event.preventDefault();
3986 this._searchTimeout( event );
3989 this.selectedItem = null;
3990 this.previous = this._value();
3992 blur: function( event ) {
3993 if ( this.cancelBlur ) {
3994 delete this.cancelBlur;
3998 clearTimeout( this.searching );
3999 this.close( event );
4000 this._change( event );
4005 this.menu = $( "<ul>" )
4006 .appendTo( this._appendTo() )
4009 // disable ARIA support, the live region takes care of that
4013 .menu( "instance" );
4015 this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
4016 this._on( this.menu.element, {
4017 mousedown: function( event ) {
4019 // prevent moving focus out of the text field
4020 event.preventDefault();
4022 // IE doesn't prevent moving focus even with event.preventDefault()
4023 // so we set a flag to know when we should ignore the blur event
4024 this.cancelBlur = true;
4025 this._delay( function() {
4026 delete this.cancelBlur;
4028 // Support: IE 8 only
4029 // Right clicking a menu item or selecting text from the menu items will
4030 // result in focus moving out of the input. However, we've already received
4031 // and ignored the blur event because of the cancelBlur flag set above. So
4032 // we restore focus to ensure that the menu closes properly based on the user's
4034 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
4035 this.element.trigger( "focus" );
4039 menufocus: function( event, ui ) {
4043 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
4044 if ( this.isNewMenu ) {
4045 this.isNewMenu = false;
4046 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
4049 this.document.one( "mousemove", function() {
4050 $( event.target ).trigger( event.originalEvent );
4057 item = ui.item.data( "ui-autocomplete-item" );
4058 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
4060 // use value to match what will end up in the input, if it was a key event
4061 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
4062 this._value( item.value );
4066 // Announce the value in the liveRegion
4067 label = ui.item.attr( "aria-label" ) || item.value;
4068 if ( label && $.trim( label ).length ) {
4069 this.liveRegion.children().hide();
4070 $( "<div>" ).text( label ).appendTo( this.liveRegion );
4073 menuselect: function( event, ui ) {
4074 var item = ui.item.data( "ui-autocomplete-item" ),
4075 previous = this.previous;
4077 // Only trigger when focus was lost (click on menu)
4078 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
4079 this.element.trigger( "focus" );
4080 this.previous = previous;
4082 // #6109 - IE triggers two focus events and the second
4083 // is asynchronous, so we need to reset the previous
4084 // term synchronously and asynchronously :-(
4085 this._delay( function() {
4086 this.previous = previous;
4087 this.selectedItem = item;
4091 if ( false !== this._trigger( "select", event, { item: item } ) ) {
4092 this._value( item.value );
4095 // reset the term after the select event
4096 // this allows custom select handling to work properly
4097 this.term = this._value();
4099 this.close( event );
4100 this.selectedItem = item;
4104 this.liveRegion = $( "<div>", {
4106 "aria-live": "assertive",
4107 "aria-relevant": "additions"
4109 .appendTo( this.document[ 0 ].body );
4111 this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
4113 // Turning off autocomplete prevents the browser from remembering the
4114 // value when navigating through history, so we re-enable autocomplete
4115 // if the page is unloaded before the widget is destroyed. #7790
4116 this._on( this.window, {
4117 beforeunload: function() {
4118 this.element.removeAttr( "autocomplete" );
4123 _destroy: function() {
4124 clearTimeout( this.searching );
4125 this.element.removeAttr( "autocomplete" );
4126 this.menu.element.remove();
4127 this.liveRegion.remove();
4130 _setOption: function( key, value ) {
4131 this._super( key, value );
4132 if ( key === "source" ) {
4135 if ( key === "appendTo" ) {
4136 this.menu.element.appendTo( this._appendTo() );
4138 if ( key === "disabled" && value && this.xhr ) {
4143 _isEventTargetInWidget: function( event ) {
4144 var menuElement = this.menu.element[ 0 ];
4146 return event.target === this.element[ 0 ] ||
4147 event.target === menuElement ||
4148 $.contains( menuElement, event.target );
4151 _closeOnClickOutside: function( event ) {
4152 if ( !this._isEventTargetInWidget( event ) ) {
4157 _appendTo: function() {
4158 var element = this.options.appendTo;
4161 element = element.jquery || element.nodeType ?
4163 this.document.find( element ).eq( 0 );
4166 if ( !element || !element[ 0 ] ) {
4167 element = this.element.closest( ".ui-front, dialog" );
4170 if ( !element.length ) {
4171 element = this.document[ 0 ].body;
4177 _initSource: function() {
4180 if ( $.isArray( this.options.source ) ) {
4181 array = this.options.source;
4182 this.source = function( request, response ) {
4183 response( $.ui.autocomplete.filter( array, request.term ) );
4185 } else if ( typeof this.options.source === "string" ) {
4186 url = this.options.source;
4187 this.source = function( request, response ) {
4191 that.xhr = $.ajax( {
4195 success: function( data ) {
4204 this.source = this.options.source;
4208 _searchTimeout: function( event ) {
4209 clearTimeout( this.searching );
4210 this.searching = this._delay( function() {
4212 // Search if the value has changed, or if the user retypes the same value (see #7434)
4213 var equalValues = this.term === this._value(),
4214 menuVisible = this.menu.element.is( ":visible" ),
4215 modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
4217 if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
4218 this.selectedItem = null;
4219 this.search( null, event );
4221 }, this.options.delay );
4224 search: function( value, event ) {
4225 value = value != null ? value : this._value();
4227 // Always save the actual value, not the one passed as an argument
4228 this.term = this._value();
4230 if ( value.length < this.options.minLength ) {
4231 return this.close( event );
4234 if ( this._trigger( "search", event ) === false ) {
4238 return this._search( value );
4241 _search: function( value ) {
4243 this._addClass( "ui-autocomplete-loading" );
4244 this.cancelSearch = false;
4246 this.source( { term: value }, this._response() );
4249 _response: function() {
4250 var index = ++this.requestIndex;
4252 return $.proxy( function( content ) {
4253 if ( index === this.requestIndex ) {
4254 this.__response( content );
4258 if ( !this.pending ) {
4259 this._removeClass( "ui-autocomplete-loading" );
4264 __response: function( content ) {
4266 content = this._normalize( content );
4268 this._trigger( "response", null, { content: content } );
4269 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
4270 this._suggest( content );
4271 this._trigger( "open" );
4274 // use ._close() instead of .close() so we don't cancel future searches
4279 close: function( event ) {
4280 this.cancelSearch = true;
4281 this._close( event );
4284 _close: function( event ) {
4286 // Remove the handler that closes the menu on outside clicks
4287 this._off( this.document, "mousedown" );
4289 if ( this.menu.element.is( ":visible" ) ) {
4290 this.menu.element.hide();
4292 this.isNewMenu = true;
4293 this._trigger( "close", event );
4297 _change: function( event ) {
4298 if ( this.previous !== this._value() ) {
4299 this._trigger( "change", event, { item: this.selectedItem } );
4303 _normalize: function( items ) {
4305 // assume all items have the right format when the first item is complete
4306 if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
4309 return $.map( items, function( item ) {
4310 if ( typeof item === "string" ) {
4316 return $.extend( {}, item, {
4317 label: item.label || item.value,
4318 value: item.value || item.label
4323 _suggest: function( items ) {
4324 var ul = this.menu.element.empty();
4325 this._renderMenu( ul, items );
4326 this.isNewMenu = true;
4327 this.menu.refresh();
4329 // Size and position menu
4332 ul.position( $.extend( {
4334 }, this.options.position ) );
4336 if ( this.options.autoFocus ) {
4340 // Listen for interactions outside of the widget (#6642)
4341 this._on( this.document, {
4342 mousedown: "_closeOnClickOutside"
4346 _resizeMenu: function() {
4347 var ul = this.menu.element;
4348 ul.outerWidth( Math.max(
4350 // Firefox wraps long text (possibly a rounding bug)
4351 // so we add 1px to avoid the wrapping (#7513)
4352 ul.width( "" ).outerWidth() + 1,
4353 this.element.outerWidth()
4357 _renderMenu: function( ul, items ) {
4359 $.each( items, function( index, item ) {
4360 that._renderItemData( ul, item );
4364 _renderItemData: function( ul, item ) {
4365 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
4368 _renderItem: function( ul, item ) {
4370 .append( $( "<div>" ).text( item.label ) )
4374 _move: function( direction, event ) {
4375 if ( !this.menu.element.is( ":visible" ) ) {
4376 this.search( null, event );
4379 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
4380 this.menu.isLastItem() && /^next/.test( direction ) ) {
4382 if ( !this.isMultiLine ) {
4383 this._value( this.term );
4389 this.menu[ direction ]( event );
4392 widget: function() {
4393 return this.menu.element;
4396 _value: function() {
4397 return this.valueMethod.apply( this.element, arguments );
4400 _keyEvent: function( keyEvent, event ) {
4401 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
4402 this._move( keyEvent, event );
4404 // Prevents moving cursor to beginning/end of the text field in some browsers
4405 event.preventDefault();
4409 // Support: Chrome <=50
4410 // We should be able to just use this.element.prop( "isContentEditable" )
4411 // but hidden elements always report false in Chrome.
4412 // https://code.google.com/p/chromium/issues/detail?id=313082
4413 _isContentEditable: function( element ) {
4414 if ( !element.length ) {
4418 var editable = element.prop( "contentEditable" );
4420 if ( editable === "inherit" ) {
4421 return this._isContentEditable( element.parent() );
4424 return editable === "true";
4428 $.extend( $.ui.autocomplete, {
4429 escapeRegex: function( value ) {
4430 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
4432 filter: function( array, term ) {
4433 var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
4434 return $.grep( array, function( value ) {
4435 return matcher.test( value.label || value.value || value );
4440 // Live region extension, adding a `messages` option
4441 // NOTE: This is an experimental API. We are still investigating
4442 // a full solution for string manipulation and internationalization.
4443 $.widget( "ui.autocomplete", $.ui.autocomplete, {
4446 noResults: "No search results.",
4447 results: function( amount ) {
4448 return amount + ( amount > 1 ? " results are" : " result is" ) +
4449 " available, use up and down arrow keys to navigate.";
4454 __response: function( content ) {
4456 this._superApply( arguments );
4457 if ( this.options.disabled || this.cancelSearch ) {
4460 if ( content && content.length ) {
4461 message = this.options.messages.results( content.length );
4463 message = this.options.messages.noResults;
4465 this.liveRegion.children().hide();
4466 $( "<div>" ).text( message ).appendTo( this.liveRegion );
4470 var widgetsAutocomplete = $.ui.autocomplete;
4473 // jscs:disable maximumLineLength
4474 /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
4476 * jQuery UI Datepicker 1.12.1
4477 * http://jqueryui.com
4479 * Copyright jQuery Foundation and other contributors
4480 * Released under the MIT license.
4481 * http://jquery.org/license
4484 //>>label: Datepicker
4486 //>>description: Displays a calendar from an input or inline for selecting dates.
4487 //>>docs: http://api.jqueryui.com/datepicker/
4488 //>>demos: http://jqueryui.com/datepicker/
4489 //>>css.structure: ../../themes/base/core.css
4490 //>>css.structure: ../../themes/base/datepicker.css
4491 //>>css.theme: ../../themes/base/theme.css
4495 $.extend( $.ui, { datepicker: { version: "1.12.1" } } );
4497 var datepicker_instActive;
4499 function datepicker_getZindex( elem ) {
4500 var position, value;
4501 while ( elem.length && elem[ 0 ] !== document ) {
4503 // Ignore z-index if position is set to a value where z-index is ignored by the browser
4504 // This makes behavior of this function consistent across browsers
4505 // WebKit always returns auto if the element is positioned
4506 position = elem.css( "position" );
4507 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
4509 // IE returns 0 when zIndex is not specified
4510 // other browsers return a string
4511 // we ignore the case of nested elements with an explicit value of 0
4512 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
4513 value = parseInt( elem.css( "zIndex" ), 10 );
4514 if ( !isNaN( value ) && value !== 0 ) {
4518 elem = elem.parent();
4523 /* Date picker manager.
4524 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
4525 Settings for (groups of) date pickers are maintained in an instance object,
4526 allowing multiple different settings on the same page. */
4528 function Datepicker() {
4529 this._curInst = null; // The current instance in use
4530 this._keyEvent = false; // If the last event was a key event
4531 this._disabledInputs = []; // List of date picker inputs that have been disabled
4532 this._datepickerShowing = false; // True if the popup picker is showing , false if not
4533 this._inDialog = false; // True if showing within a "dialog", false if not
4534 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
4535 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
4536 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
4537 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
4538 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
4539 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
4540 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
4541 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
4542 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
4543 this.regional = []; // Available regional settings, indexed by language code
4544 this.regional[ "" ] = { // Default regional settings
4545 closeText: "Done", // Display text for close link
4546 prevText: "Prev", // Display text for previous month link
4547 nextText: "Next", // Display text for next month link
4548 currentText: "Today", // Display text for current month link
4549 monthNames: [ "January","February","March","April","May","June",
4550 "July","August","September","October","November","December" ], // Names of months for drop-down and formatting
4551 monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
4552 dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
4553 dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
4554 dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday
4555 weekHeader: "Wk", // Column header for week of the year
4556 dateFormat: "mm/dd/yy", // See format options on parseDate
4557 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
4558 isRTL: false, // True if right-to-left language, false if left-to-right
4559 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
4560 yearSuffix: "" // Additional text to append to the year in the month headers
4562 this._defaults = { // Global defaults for all the date picker instances
4563 showOn: "focus", // "focus" for popup on focus,
4564 // "button" for trigger button, or "both" for either
4565 showAnim: "fadeIn", // Name of jQuery animation for popup
4566 showOptions: {}, // Options for enhanced animations
4567 defaultDate: null, // Used when field is blank: actual date,
4568 // +/-number for offset from today, null for today
4569 appendText: "", // Display text following the input box, e.g. showing the format
4570 buttonText: "...", // Text for trigger button
4571 buttonImage: "", // URL for trigger button image
4572 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
4573 hideIfNoPrevNext: false, // True to hide next/previous month links
4574 // if not applicable, false to just disable them
4575 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
4576 gotoCurrent: false, // True if today link goes back to current selection instead
4577 changeMonth: false, // True if month can be selected directly, false if only prev/next
4578 changeYear: false, // True if year can be selected directly, false if only prev/next
4579 yearRange: "c-10:c+10", // Range of years to display in drop-down,
4580 // either relative to today's year (-nn:+nn), relative to currently displayed year
4581 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
4582 showOtherMonths: false, // True to show dates in other months, false to leave blank
4583 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
4584 showWeek: false, // True to show week of the year, false to not show it
4585 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
4586 // takes a Date and returns the number of the week for it
4587 shortYearCutoff: "+10", // Short year values < this are in the current century,
4588 // > this are in the previous century,
4589 // string value starting with "+" for current year + value
4590 minDate: null, // The earliest selectable date, or null for no limit
4591 maxDate: null, // The latest selectable date, or null for no limit
4592 duration: "fast", // Duration of display/closure
4593 beforeShowDay: null, // Function that takes a date and returns an array with
4594 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
4595 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
4596 beforeShow: null, // Function that takes an input field and
4597 // returns a set of custom settings for the date picker
4598 onSelect: null, // Define a callback function when a date is selected
4599 onChangeMonthYear: null, // Define a callback function when the month or year is changed
4600 onClose: null, // Define a callback function when the datepicker is closed
4601 numberOfMonths: 1, // Number of months to show at a time
4602 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
4603 stepMonths: 1, // Number of months to step back/forward
4604 stepBigMonths: 12, // Number of months to step back/forward for the big links
4605 altField: "", // Selector for an alternate field to store selected dates into
4606 altFormat: "", // The date format to use for the alternate field
4607 constrainInput: true, // The input is constrained by the current date format
4608 showButtonPanel: false, // True to show button panel, false to not show it
4609 autoSize: false, // True to size the input for the date format, false to leave as is
4610 disabled: false // The initial disabled state
4612 $.extend( this._defaults, this.regional[ "" ] );
4613 this.regional.en = $.extend( true, {}, this.regional[ "" ] );
4614 this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
4615 this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
4618 $.extend( Datepicker.prototype, {
4619 /* Class name added to elements to indicate already configured with a date picker. */
4620 markerClassName: "hasDatepicker",
4622 //Keep track of the maximum number of rows displayed (see #7043)
4625 // TODO rename to "widget" when switching to widget factory
4626 _widgetDatepicker: function() {
4630 /* Override the default settings for all instances of the date picker.
4631 * @param settings object - the new settings to use as defaults (anonymous object)
4632 * @return the manager object
4634 setDefaults: function( settings ) {
4635 datepicker_extendRemove( this._defaults, settings || {} );
4639 /* Attach the date picker to a jQuery selection.
4640 * @param target element - the target input field or division or span
4641 * @param settings object - the new settings to use for this date picker instance (anonymous)
4643 _attachDatepicker: function( target, settings ) {
4644 var nodeName, inline, inst;
4645 nodeName = target.nodeName.toLowerCase();
4646 inline = ( nodeName === "div" || nodeName === "span" );
4649 target.id = "dp" + this.uuid;
4651 inst = this._newInst( $( target ), inline );
4652 inst.settings = $.extend( {}, settings || {} );
4653 if ( nodeName === "input" ) {
4654 this._connectDatepicker( target, inst );
4655 } else if ( inline ) {
4656 this._inlineDatepicker( target, inst );
4660 /* Create a new instance object. */
4661 _newInst: function( target, inline ) {
4662 var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
4663 return { id: id, input: target, // associated target
4664 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
4665 drawMonth: 0, drawYear: 0, // month being drawn
4666 inline: inline, // is datepicker inline or not
4667 dpDiv: ( !inline ? this.dpDiv : // presentation div
4668 datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
4671 /* Attach the date picker to an input field. */
4672 _connectDatepicker: function( target, inst ) {
4673 var input = $( target );
4674 inst.append = $( [] );
4675 inst.trigger = $( [] );
4676 if ( input.hasClass( this.markerClassName ) ) {
4679 this._attachments( input, inst );
4680 input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
4681 on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
4682 this._autoSize( inst );
4683 $.data( target, "datepicker", inst );
4685 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
4686 if ( inst.settings.disabled ) {
4687 this._disableDatepicker( target );
4691 /* Make attachments based on settings. */
4692 _attachments: function( input, inst ) {
4693 var showOn, buttonText, buttonImage,
4694 appendText = this._get( inst, "appendText" ),
4695 isRTL = this._get( inst, "isRTL" );
4697 if ( inst.append ) {
4698 inst.append.remove();
4701 inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
4702 input[ isRTL ? "before" : "after" ]( inst.append );
4705 input.off( "focus", this._showDatepicker );
4707 if ( inst.trigger ) {
4708 inst.trigger.remove();
4711 showOn = this._get( inst, "showOn" );
4712 if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
4713 input.on( "focus", this._showDatepicker );
4715 if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
4716 buttonText = this._get( inst, "buttonText" );
4717 buttonImage = this._get( inst, "buttonImage" );
4718 inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
4719 $( "<img/>" ).addClass( this._triggerClass ).
4720 attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
4721 $( "<button type='button'></button>" ).addClass( this._triggerClass ).
4722 html( !buttonImage ? buttonText : $( "<img/>" ).attr(
4723 { src:buttonImage, alt:buttonText, title:buttonText } ) ) );
4724 input[ isRTL ? "before" : "after" ]( inst.trigger );
4725 inst.trigger.on( "click", function() {
4726 if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
4727 $.datepicker._hideDatepicker();
4728 } else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
4729 $.datepicker._hideDatepicker();
4730 $.datepicker._showDatepicker( input[ 0 ] );
4732 $.datepicker._showDatepicker( input[ 0 ] );
4739 /* Apply the maximum length for the date format. */
4740 _autoSize: function( inst ) {
4741 if ( this._get( inst, "autoSize" ) && !inst.inline ) {
4742 var findMax, max, maxI, i,
4743 date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
4744 dateFormat = this._get( inst, "dateFormat" );
4746 if ( dateFormat.match( /[DM]/ ) ) {
4747 findMax = function( names ) {
4750 for ( i = 0; i < names.length; i++ ) {
4751 if ( names[ i ].length > max ) {
4752 max = names[ i ].length;
4758 date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
4759 "monthNames" : "monthNamesShort" ) ) ) );
4760 date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
4761 "dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
4763 inst.input.attr( "size", this._formatDate( inst, date ).length );
4767 /* Attach an inline date picker to a div. */
4768 _inlineDatepicker: function( target, inst ) {
4769 var divSpan = $( target );
4770 if ( divSpan.hasClass( this.markerClassName ) ) {
4773 divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
4774 $.data( target, "datepicker", inst );
4775 this._setDate( inst, this._getDefaultDate( inst ), true );
4776 this._updateDatepicker( inst );
4777 this._updateAlternate( inst );
4779 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
4780 if ( inst.settings.disabled ) {
4781 this._disableDatepicker( target );
4784 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
4785 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
4786 inst.dpDiv.css( "display", "block" );
4789 /* Pop-up the date picker in a "dialog" box.
4790 * @param input element - ignored
4791 * @param date string or Date - the initial date to display
4792 * @param onSelect function - the function to call when a date is selected
4793 * @param settings object - update the dialog date picker instance's settings (anonymous object)
4794 * @param pos int[2] - coordinates for the dialog's position within the screen or
4795 * event - with x/y coordinates or
4796 * leave empty for default (screen centre)
4797 * @return the manager object
4799 _dialogDatepicker: function( input, date, onSelect, settings, pos ) {
4800 var id, browserWidth, browserHeight, scrollX, scrollY,
4801 inst = this._dialogInst; // internal instance
4805 id = "dp" + this.uuid;
4806 this._dialogInput = $( "<input type='text' id='" + id +
4807 "' style='position: absolute; top: -100px; width: 0px;'/>" );
4808 this._dialogInput.on( "keydown", this._doKeyDown );
4809 $( "body" ).append( this._dialogInput );
4810 inst = this._dialogInst = this._newInst( this._dialogInput, false );
4812 $.data( this._dialogInput[ 0 ], "datepicker", inst );
4814 datepicker_extendRemove( inst.settings, settings || {} );
4815 date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
4816 this._dialogInput.val( date );
4818 this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
4820 browserWidth = document.documentElement.clientWidth;
4821 browserHeight = document.documentElement.clientHeight;
4822 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
4823 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
4824 this._pos = // should use actual width/height below
4825 [ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
4828 // Move input on screen for focus, but hidden behind dialog
4829 this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
4830 inst.settings.onSelect = onSelect;
4831 this._inDialog = true;
4832 this.dpDiv.addClass( this._dialogClass );
4833 this._showDatepicker( this._dialogInput[ 0 ] );
4835 $.blockUI( this.dpDiv );
4837 $.data( this._dialogInput[ 0 ], "datepicker", inst );
4841 /* Detach a datepicker from its control.
4842 * @param target element - the target input field or division or span
4844 _destroyDatepicker: function( target ) {
4846 $target = $( target ),
4847 inst = $.data( target, "datepicker" );
4849 if ( !$target.hasClass( this.markerClassName ) ) {
4853 nodeName = target.nodeName.toLowerCase();
4854 $.removeData( target, "datepicker" );
4855 if ( nodeName === "input" ) {
4856 inst.append.remove();
4857 inst.trigger.remove();
4858 $target.removeClass( this.markerClassName ).
4859 off( "focus", this._showDatepicker ).
4860 off( "keydown", this._doKeyDown ).
4861 off( "keypress", this._doKeyPress ).
4862 off( "keyup", this._doKeyUp );
4863 } else if ( nodeName === "div" || nodeName === "span" ) {
4864 $target.removeClass( this.markerClassName ).empty();
4867 if ( datepicker_instActive === inst ) {
4868 datepicker_instActive = null;
4872 /* Enable the date picker to a jQuery selection.
4873 * @param target element - the target input field or division or span
4875 _enableDatepicker: function( target ) {
4876 var nodeName, inline,
4877 $target = $( target ),
4878 inst = $.data( target, "datepicker" );
4880 if ( !$target.hasClass( this.markerClassName ) ) {
4884 nodeName = target.nodeName.toLowerCase();
4885 if ( nodeName === "input" ) {
4886 target.disabled = false;
4887 inst.trigger.filter( "button" ).
4888 each( function() { this.disabled = false; } ).end().
4889 filter( "img" ).css( { opacity: "1.0", cursor: "" } );
4890 } else if ( nodeName === "div" || nodeName === "span" ) {
4891 inline = $target.children( "." + this._inlineClass );
4892 inline.children().removeClass( "ui-state-disabled" );
4893 inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
4894 prop( "disabled", false );
4896 this._disabledInputs = $.map( this._disabledInputs,
4897 function( value ) { return ( value === target ? null : value ); } ); // delete entry
4900 /* Disable the date picker to a jQuery selection.
4901 * @param target element - the target input field or division or span
4903 _disableDatepicker: function( target ) {
4904 var nodeName, inline,
4905 $target = $( target ),
4906 inst = $.data( target, "datepicker" );
4908 if ( !$target.hasClass( this.markerClassName ) ) {
4912 nodeName = target.nodeName.toLowerCase();
4913 if ( nodeName === "input" ) {
4914 target.disabled = true;
4915 inst.trigger.filter( "button" ).
4916 each( function() { this.disabled = true; } ).end().
4917 filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
4918 } else if ( nodeName === "div" || nodeName === "span" ) {
4919 inline = $target.children( "." + this._inlineClass );
4920 inline.children().addClass( "ui-state-disabled" );
4921 inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
4922 prop( "disabled", true );
4924 this._disabledInputs = $.map( this._disabledInputs,
4925 function( value ) { return ( value === target ? null : value ); } ); // delete entry
4926 this._disabledInputs[ this._disabledInputs.length ] = target;
4929 /* Is the first field in a jQuery collection disabled as a datepicker?
4930 * @param target element - the target input field or division or span
4931 * @return boolean - true if disabled, false if enabled
4933 _isDisabledDatepicker: function( target ) {
4937 for ( var i = 0; i < this._disabledInputs.length; i++ ) {
4938 if ( this._disabledInputs[ i ] === target ) {
4945 /* Retrieve the instance data for the target control.
4946 * @param target element - the target input field or division or span
4947 * @return object - the associated instance data
4948 * @throws error if a jQuery problem getting data
4950 _getInst: function( target ) {
4952 return $.data( target, "datepicker" );
4955 throw "Missing instance data for this datepicker";
4959 /* Update or retrieve the settings for a date picker attached to an input field or division.
4960 * @param target element - the target input field or division or span
4961 * @param name object - the new settings to update or
4962 * string - the name of the setting to change or retrieve,
4963 * when retrieving also "all" for all instance settings or
4964 * "defaults" for all global defaults
4965 * @param value any - the new value for the setting
4966 * (omit if above is an object or to retrieve a value)
4968 _optionDatepicker: function( target, name, value ) {
4969 var settings, date, minDate, maxDate,
4970 inst = this._getInst( target );
4972 if ( arguments.length === 2 && typeof name === "string" ) {
4973 return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
4974 ( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
4975 this._get( inst, name ) ) : null ) );
4978 settings = name || {};
4979 if ( typeof name === "string" ) {
4981 settings[ name ] = value;
4985 if ( this._curInst === inst ) {
4986 this._hideDatepicker();
4989 date = this._getDateDatepicker( target, true );
4990 minDate = this._getMinMaxDate( inst, "min" );
4991 maxDate = this._getMinMaxDate( inst, "max" );
4992 datepicker_extendRemove( inst.settings, settings );
4994 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
4995 if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
4996 inst.settings.minDate = this._formatDate( inst, minDate );
4998 if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
4999 inst.settings.maxDate = this._formatDate( inst, maxDate );
5001 if ( "disabled" in settings ) {
5002 if ( settings.disabled ) {
5003 this._disableDatepicker( target );
5005 this._enableDatepicker( target );
5008 this._attachments( $( target ), inst );
5009 this._autoSize( inst );
5010 this._setDate( inst, date );
5011 this._updateAlternate( inst );
5012 this._updateDatepicker( inst );
5016 // Change method deprecated
5017 _changeDatepicker: function( target, name, value ) {
5018 this._optionDatepicker( target, name, value );
5021 /* Redraw the date picker attached to an input field or division.
5022 * @param target element - the target input field or division or span
5024 _refreshDatepicker: function( target ) {
5025 var inst = this._getInst( target );
5027 this._updateDatepicker( inst );
5031 /* Set the dates for a jQuery selection.
5032 * @param target element - the target input field or division or span
5033 * @param date Date - the new date
5035 _setDateDatepicker: function( target, date ) {
5036 var inst = this._getInst( target );
5038 this._setDate( inst, date );
5039 this._updateDatepicker( inst );
5040 this._updateAlternate( inst );
5044 /* Get the date(s) for the first entry in a jQuery selection.
5045 * @param target element - the target input field or division or span
5046 * @param noDefault boolean - true if no default date is to be used
5047 * @return Date - the current date
5049 _getDateDatepicker: function( target, noDefault ) {
5050 var inst = this._getInst( target );
5051 if ( inst && !inst.inline ) {
5052 this._setDateFromField( inst, noDefault );
5054 return ( inst ? this._getDate( inst ) : null );
5057 /* Handle keystrokes. */
5058 _doKeyDown: function( event ) {
5059 var onSelect, dateStr, sel,
5060 inst = $.datepicker._getInst( event.target ),
5062 isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );
5064 inst._keyEvent = true;
5065 if ( $.datepicker._datepickerShowing ) {
5066 switch ( event.keyCode ) {
5067 case 9: $.datepicker._hideDatepicker();
5069 break; // hide on tab out
5070 case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
5071 $.datepicker._currentClass + ")", inst.dpDiv );
5073 $.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
5076 onSelect = $.datepicker._get( inst, "onSelect" );
5078 dateStr = $.datepicker._formatDate( inst );
5080 // Trigger custom callback
5081 onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
5083 $.datepicker._hideDatepicker();
5086 return false; // don't submit the form
5087 case 27: $.datepicker._hideDatepicker();
5088 break; // hide on escape
5089 case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
5090 -$.datepicker._get( inst, "stepBigMonths" ) :
5091 -$.datepicker._get( inst, "stepMonths" ) ), "M" );
5092 break; // previous month/year on page up/+ ctrl
5093 case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
5094 +$.datepicker._get( inst, "stepBigMonths" ) :
5095 +$.datepicker._get( inst, "stepMonths" ) ), "M" );
5096 break; // next month/year on page down/+ ctrl
5097 case 35: if ( event.ctrlKey || event.metaKey ) {
5098 $.datepicker._clearDate( event.target );
5100 handled = event.ctrlKey || event.metaKey;
5101 break; // clear on ctrl or command +end
5102 case 36: if ( event.ctrlKey || event.metaKey ) {
5103 $.datepicker._gotoToday( event.target );
5105 handled = event.ctrlKey || event.metaKey;
5106 break; // current on ctrl or command +home
5107 case 37: if ( event.ctrlKey || event.metaKey ) {
5108 $.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
5110 handled = event.ctrlKey || event.metaKey;
5112 // -1 day on ctrl or command +left
5113 if ( event.originalEvent.altKey ) {
5114 $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
5115 -$.datepicker._get( inst, "stepBigMonths" ) :
5116 -$.datepicker._get( inst, "stepMonths" ) ), "M" );
5119 // next month/year on alt +left on Mac
5121 case 38: if ( event.ctrlKey || event.metaKey ) {
5122 $.datepicker._adjustDate( event.target, -7, "D" );
5124 handled = event.ctrlKey || event.metaKey;
5125 break; // -1 week on ctrl or command +up
5126 case 39: if ( event.ctrlKey || event.metaKey ) {
5127 $.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
5129 handled = event.ctrlKey || event.metaKey;
5131 // +1 day on ctrl or command +right
5132 if ( event.originalEvent.altKey ) {
5133 $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
5134 +$.datepicker._get( inst, "stepBigMonths" ) :
5135 +$.datepicker._get( inst, "stepMonths" ) ), "M" );
5138 // next month/year on alt +right
5140 case 40: if ( event.ctrlKey || event.metaKey ) {
5141 $.datepicker._adjustDate( event.target, +7, "D" );
5143 handled = event.ctrlKey || event.metaKey;
5144 break; // +1 week on ctrl or command +down
5145 default: handled = false;
5147 } else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
5148 $.datepicker._showDatepicker( this );
5154 event.preventDefault();
5155 event.stopPropagation();
5159 /* Filter entered characters - based on date format. */
5160 _doKeyPress: function( event ) {
5162 inst = $.datepicker._getInst( event.target );
5164 if ( $.datepicker._get( inst, "constrainInput" ) ) {
5165 chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
5166 chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
5167 return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
5171 /* Synchronise manual entry and field/alternate field. */
5172 _doKeyUp: function( event ) {
5174 inst = $.datepicker._getInst( event.target );
5176 if ( inst.input.val() !== inst.lastVal ) {
5178 date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
5179 ( inst.input ? inst.input.val() : null ),
5180 $.datepicker._getFormatConfig( inst ) );
5182 if ( date ) { // only if valid
5183 $.datepicker._setDateFromField( inst );
5184 $.datepicker._updateAlternate( inst );
5185 $.datepicker._updateDatepicker( inst );
5194 /* Pop-up the date picker for a given input field.
5195 * If false returned from beforeShow event handler do not show.
5196 * @param input element - the input field attached to the date picker or
5197 * event - if triggered by focus
5199 _showDatepicker: function( input ) {
5200 input = input.target || input;
5201 if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
5202 input = $( "input", input.parentNode )[ 0 ];
5205 if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
5209 var inst, beforeShow, beforeShowSettings, isFixed,
5210 offset, showAnim, duration;
5212 inst = $.datepicker._getInst( input );
5213 if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
5214 $.datepicker._curInst.dpDiv.stop( true, true );
5215 if ( inst && $.datepicker._datepickerShowing ) {
5216 $.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
5220 beforeShow = $.datepicker._get( inst, "beforeShow" );
5221 beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
5222 if ( beforeShowSettings === false ) {
5225 datepicker_extendRemove( inst.settings, beforeShowSettings );
5227 inst.lastVal = null;
5228 $.datepicker._lastInput = input;
5229 $.datepicker._setDateFromField( inst );
5231 if ( $.datepicker._inDialog ) { // hide cursor
5234 if ( !$.datepicker._pos ) { // position below input
5235 $.datepicker._pos = $.datepicker._findPos( input );
5236 $.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
5240 $( input ).parents().each( function() {
5241 isFixed |= $( this ).css( "position" ) === "fixed";
5245 offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
5246 $.datepicker._pos = null;
5248 //to avoid flashes on Firefox
5251 // determine sizing offscreen
5252 inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
5253 $.datepicker._updateDatepicker( inst );
5255 // fix width for dynamic number of date pickers
5256 // and adjust position before showing
5257 offset = $.datepicker._checkOffset( inst, offset, isFixed );
5258 inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
5259 "static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
5260 left: offset.left + "px", top: offset.top + "px" } );
5262 if ( !inst.inline ) {
5263 showAnim = $.datepicker._get( inst, "showAnim" );
5264 duration = $.datepicker._get( inst, "duration" );
5265 inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
5266 $.datepicker._datepickerShowing = true;
5268 if ( $.effects && $.effects.effect[ showAnim ] ) {
5269 inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
5271 inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
5274 if ( $.datepicker._shouldFocusInput( inst ) ) {
5275 inst.input.trigger( "focus" );
5278 $.datepicker._curInst = inst;
5282 /* Generate the date picker content. */
5283 _updateDatepicker: function( inst ) {
5284 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
5285 datepicker_instActive = inst; // for delegate hover events
5286 inst.dpDiv.empty().append( this._generateHTML( inst ) );
5287 this._attachHandlers( inst );
5290 numMonths = this._getNumberOfMonths( inst ),
5291 cols = numMonths[ 1 ],
5293 activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
5295 if ( activeCell.length > 0 ) {
5296 datepicker_handleMouseover.apply( activeCell.get( 0 ) );
5299 inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
5301 inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
5303 inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
5304 "Class" ]( "ui-datepicker-multi" );
5305 inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
5306 "Class" ]( "ui-datepicker-rtl" );
5308 if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
5309 inst.input.trigger( "focus" );
5312 // Deffered render of the years select (to avoid flashes on Firefox)
5313 if ( inst.yearshtml ) {
5314 origyearshtml = inst.yearshtml;
5315 setTimeout( function() {
5317 //assure that inst.yearshtml didn't change.
5318 if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
5319 inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml );
5321 origyearshtml = inst.yearshtml = null;
5326 // #6694 - don't focus the input if it's already focused
5327 // this breaks the change event in IE
5328 // Support: IE and jQuery <1.9
5329 _shouldFocusInput: function( inst ) {
5330 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
5333 /* Check positioning to remain on screen. */
5334 _checkOffset: function( inst, offset, isFixed ) {
5335 var dpWidth = inst.dpDiv.outerWidth(),
5336 dpHeight = inst.dpDiv.outerHeight(),
5337 inputWidth = inst.input ? inst.input.outerWidth() : 0,
5338 inputHeight = inst.input ? inst.input.outerHeight() : 0,
5339 viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
5340 viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );
5342 offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
5343 offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
5344 offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;
5346 // Now check if datepicker is showing outside window viewport - move to a better place if so.
5347 offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
5348 Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
5349 offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
5350 Math.abs( dpHeight + inputHeight ) : 0 );
5355 /* Find an object's position on the screen. */
5356 _findPos: function( obj ) {
5358 inst = this._getInst( obj ),
5359 isRTL = this._get( inst, "isRTL" );
5361 while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) {
5362 obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
5365 position = $( obj ).offset();
5366 return [ position.left, position.top ];
5369 /* Hide the date picker from view.
5370 * @param input element - the input field attached to the date picker
5372 _hideDatepicker: function( input ) {
5373 var showAnim, duration, postProcess, onClose,
5374 inst = this._curInst;
5376 if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
5380 if ( this._datepickerShowing ) {
5381 showAnim = this._get( inst, "showAnim" );
5382 duration = this._get( inst, "duration" );
5383 postProcess = function() {
5384 $.datepicker._tidyDialog( inst );
5387 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
5388 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
5389 inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
5391 inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
5392 ( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
5398 this._datepickerShowing = false;
5400 onClose = this._get( inst, "onClose" );
5402 onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
5405 this._lastInput = null;
5406 if ( this._inDialog ) {
5407 this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
5410 $( "body" ).append( this.dpDiv );
5413 this._inDialog = false;
5417 /* Tidy up after a dialog display. */
5418 _tidyDialog: function( inst ) {
5419 inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
5422 /* Close date picker if clicked elsewhere. */
5423 _checkExternalClick: function( event ) {
5424 if ( !$.datepicker._curInst ) {
5428 var $target = $( event.target ),
5429 inst = $.datepicker._getInst( $target[ 0 ] );
5431 if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
5432 $target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
5433 !$target.hasClass( $.datepicker.markerClassName ) &&
5434 !$target.closest( "." + $.datepicker._triggerClass ).length &&
5435 $.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
5436 ( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
5437 $.datepicker._hideDatepicker();
5441 /* Adjust one of the date sub-fields. */
5442 _adjustDate: function( id, offset, period ) {
5443 var target = $( id ),
5444 inst = this._getInst( target[ 0 ] );
5446 if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
5449 this._adjustInstDate( inst, offset +
5450 ( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning
5452 this._updateDatepicker( inst );
5455 /* Action for current link. */
5456 _gotoToday: function( id ) {
5459 inst = this._getInst( target[ 0 ] );
5461 if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
5462 inst.selectedDay = inst.currentDay;
5463 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
5464 inst.drawYear = inst.selectedYear = inst.currentYear;
5467 inst.selectedDay = date.getDate();
5468 inst.drawMonth = inst.selectedMonth = date.getMonth();
5469 inst.drawYear = inst.selectedYear = date.getFullYear();
5471 this._notifyChange( inst );
5472 this._adjustDate( target );
5475 /* Action for selecting a new month/year. */
5476 _selectMonthYear: function( id, select, period ) {
5477 var target = $( id ),
5478 inst = this._getInst( target[ 0 ] );
5480 inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
5481 inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
5482 parseInt( select.options[ select.selectedIndex ].value, 10 );
5484 this._notifyChange( inst );
5485 this._adjustDate( target );
5488 /* Action for selecting a day. */
5489 _selectDay: function( id, month, year, td ) {
5493 if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
5497 inst = this._getInst( target[ 0 ] );
5498 inst.selectedDay = inst.currentDay = $( "a", td ).html();
5499 inst.selectedMonth = inst.currentMonth = month;
5500 inst.selectedYear = inst.currentYear = year;
5501 this._selectDate( id, this._formatDate( inst,
5502 inst.currentDay, inst.currentMonth, inst.currentYear ) );
5505 /* Erase the input field and hide the date picker. */
5506 _clearDate: function( id ) {
5507 var target = $( id );
5508 this._selectDate( target, "" );
5511 /* Update the input field with the selected date. */
5512 _selectDate: function( id, dateStr ) {
5515 inst = this._getInst( target[ 0 ] );
5517 dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
5519 inst.input.val( dateStr );
5521 this._updateAlternate( inst );
5523 onSelect = this._get( inst, "onSelect" );
5525 onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] ); // trigger custom callback
5526 } else if ( inst.input ) {
5527 inst.input.trigger( "change" ); // fire the change event
5530 if ( inst.inline ) {
5531 this._updateDatepicker( inst );
5533 this._hideDatepicker();
5534 this._lastInput = inst.input[ 0 ];
5535 if ( typeof( inst.input[ 0 ] ) !== "object" ) {
5536 inst.input.trigger( "focus" ); // restore focus
5538 this._lastInput = null;
5542 /* Update any alternate field to synchronise with the main field. */
5543 _updateAlternate: function( inst ) {
5544 var altFormat, date, dateStr,
5545 altField = this._get( inst, "altField" );
5547 if ( altField ) { // update alternate field too
5548 altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
5549 date = this._getDate( inst );
5550 dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
5551 $( altField ).val( dateStr );
5555 /* Set as beforeShowDay function to prevent selection of weekends.
5556 * @param date Date - the date to customise
5557 * @return [boolean, string] - is this date selectable?, what is its CSS class?
5559 noWeekends: function( date ) {
5560 var day = date.getDay();
5561 return [ ( day > 0 && day < 6 ), "" ];
5564 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
5565 * @param date Date - the date to get the week for
5566 * @return number - the number of the week within the year that contains this date
5568 iso8601Week: function( date ) {
5570 checkDate = new Date( date.getTime() );
5572 // Find Thursday of this week starting on Monday
5573 checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
5575 time = checkDate.getTime();
5576 checkDate.setMonth( 0 ); // Compare with Jan 1
5577 checkDate.setDate( 1 );
5578 return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
5581 /* Parse a string value into a date object.
5582 * See formatDate below for the possible formats.
5584 * @param format string - the expected format of the date
5585 * @param value string - the date in the above format
5586 * @param settings Object - attributes include:
5587 * shortYearCutoff number - the cutoff year for determining the century (optional)
5588 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
5589 * dayNames string[7] - names of the days from Sunday (optional)
5590 * monthNamesShort string[12] - abbreviated names of the months (optional)
5591 * monthNames string[12] - names of the months (optional)
5592 * @return Date - the extracted date value or null if value is blank
5594 parseDate: function( format, value, settings ) {
5595 if ( format == null || value == null ) {
5596 throw "Invalid arguments";
5599 value = ( typeof value === "object" ? value.toString() : value + "" );
5600 if ( value === "" ) {
5604 var iFormat, dim, extra,
5606 shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
5607 shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
5608 new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
5609 dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
5610 dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
5611 monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
5612 monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
5620 // Check whether a format character is doubled
5621 lookAhead = function( match ) {
5622 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
5629 // Extract a number from the string value
5630 getNumber = function( match ) {
5631 var isDoubled = lookAhead( match ),
5632 size = ( match === "@" ? 14 : ( match === "!" ? 20 :
5633 ( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
5634 minSize = ( match === "y" ? size : 1 ),
5635 digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
5636 num = value.substring( iValue ).match( digits );
5638 throw "Missing number at position " + iValue;
5640 iValue += num[ 0 ].length;
5641 return parseInt( num[ 0 ], 10 );
5644 // Extract a name from the string value and convert to an index
5645 getName = function( match, shortNames, longNames ) {
5647 names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
5648 return [ [ k, v ] ];
5649 } ).sort( function( a, b ) {
5650 return -( a[ 1 ].length - b[ 1 ].length );
5653 $.each( names, function( i, pair ) {
5654 var name = pair[ 1 ];
5655 if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
5657 iValue += name.length;
5661 if ( index !== -1 ) {
5664 throw "Unknown name at position " + iValue;
5668 // Confirm that a literal character matches the string value
5669 checkLiteral = function() {
5670 if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
5671 throw "Unexpected literal at position " + iValue;
5676 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
5678 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
5684 switch ( format.charAt( iFormat ) ) {
5686 day = getNumber( "d" );
5689 getName( "D", dayNamesShort, dayNames );
5692 doy = getNumber( "o" );
5695 month = getNumber( "m" );
5698 month = getName( "M", monthNamesShort, monthNames );
5701 year = getNumber( "y" );
5704 date = new Date( getNumber( "@" ) );
5705 year = date.getFullYear();
5706 month = date.getMonth() + 1;
5707 day = date.getDate();
5710 date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
5711 year = date.getFullYear();
5712 month = date.getMonth() + 1;
5713 day = date.getDate();
5716 if ( lookAhead( "'" ) ) {
5728 if ( iValue < value.length ) {
5729 extra = value.substr( iValue );
5730 if ( !/^\s+/.test( extra ) ) {
5731 throw "Extra/unparsed characters found in date: " + extra;
5735 if ( year === -1 ) {
5736 year = new Date().getFullYear();
5737 } else if ( year < 100 ) {
5738 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
5739 ( year <= shortYearCutoff ? 0 : -100 );
5746 dim = this._getDaysInMonth( year, month - 1 );
5755 date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
5756 if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
5757 throw "Invalid date"; // E.g. 31/02/00
5762 /* Standard date formats. */
5763 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
5764 COOKIE: "D, dd M yy",
5765 ISO_8601: "yy-mm-dd",
5766 RFC_822: "D, d M y",
5767 RFC_850: "DD, dd-M-y",
5768 RFC_1036: "D, d M y",
5769 RFC_1123: "D, d M yy",
5770 RFC_2822: "D, d M yy",
5771 RSS: "D, d M y", // RFC 822
5774 W3C: "yy-mm-dd", // ISO 8601
5776 _ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
5777 Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),
5779 /* Format a date object into a string value.
5780 * The format can be combinations of the following:
5781 * d - day of month (no leading zero)
5782 * dd - day of month (two digit)
5783 * o - day of year (no leading zeros)
5784 * oo - day of year (three digit)
5785 * D - day name short
5786 * DD - day name long
5787 * m - month of year (no leading zero)
5788 * mm - month of year (two digit)
5789 * M - month name short
5790 * MM - month name long
5791 * y - year (two digit)
5792 * yy - year (four digit)
5793 * @ - Unix timestamp (ms since 01/01/1970)
5794 * ! - Windows ticks (100ns since 01/01/0001)
5795 * "..." - literal text
5798 * @param format string - the desired format of the date
5799 * @param date Date - the date value to format
5800 * @param settings Object - attributes include:
5801 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
5802 * dayNames string[7] - names of the days from Sunday (optional)
5803 * monthNamesShort string[12] - abbreviated names of the months (optional)
5804 * monthNames string[12] - names of the months (optional)
5805 * @return string - the date in the above format
5807 formatDate: function( format, date, settings ) {
5813 dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
5814 dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
5815 monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
5816 monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
5818 // Check whether a format character is doubled
5819 lookAhead = function( match ) {
5820 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
5827 // Format a number, with leading zero if necessary
5828 formatNumber = function( match, value, len ) {
5829 var num = "" + value;
5830 if ( lookAhead( match ) ) {
5831 while ( num.length < len ) {
5838 // Format a name, short or long as requested
5839 formatName = function( match, value, shortNames, longNames ) {
5840 return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
5846 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
5848 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
5851 output += format.charAt( iFormat );
5854 switch ( format.charAt( iFormat ) ) {
5856 output += formatNumber( "d", date.getDate(), 2 );
5859 output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
5862 output += formatNumber( "o",
5863 Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
5866 output += formatNumber( "m", date.getMonth() + 1, 2 );
5869 output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
5872 output += ( lookAhead( "y" ) ? date.getFullYear() :
5873 ( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
5876 output += date.getTime();
5879 output += date.getTime() * 10000 + this._ticksTo1970;
5882 if ( lookAhead( "'" ) ) {
5889 output += format.charAt( iFormat );
5897 /* Extract all possible characters from the date format. */
5898 _possibleChars: function( format ) {
5903 // Check whether a format character is doubled
5904 lookAhead = function( match ) {
5905 var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
5912 for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
5914 if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
5917 chars += format.charAt( iFormat );
5920 switch ( format.charAt( iFormat ) ) {
5921 case "d": case "m": case "y": case "@":
5922 chars += "0123456789";
5925 return null; // Accept anything
5927 if ( lookAhead( "'" ) ) {
5934 chars += format.charAt( iFormat );
5941 /* Get a setting value, defaulting if necessary. */
5942 _get: function( inst, name ) {
5943 return inst.settings[ name ] !== undefined ?
5944 inst.settings[ name ] : this._defaults[ name ];
5947 /* Parse existing date and initialise date picker. */
5948 _setDateFromField: function( inst, noDefault ) {
5949 if ( inst.input.val() === inst.lastVal ) {
5953 var dateFormat = this._get( inst, "dateFormat" ),
5954 dates = inst.lastVal = inst.input ? inst.input.val() : null,
5955 defaultDate = this._getDefaultDate( inst ),
5957 settings = this._getFormatConfig( inst );
5960 date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
5962 dates = ( noDefault ? "" : dates );
5964 inst.selectedDay = date.getDate();
5965 inst.drawMonth = inst.selectedMonth = date.getMonth();
5966 inst.drawYear = inst.selectedYear = date.getFullYear();
5967 inst.currentDay = ( dates ? date.getDate() : 0 );
5968 inst.currentMonth = ( dates ? date.getMonth() : 0 );
5969 inst.currentYear = ( dates ? date.getFullYear() : 0 );
5970 this._adjustInstDate( inst );
5973 /* Retrieve the default date shown on opening. */
5974 _getDefaultDate: function( inst ) {
5975 return this._restrictMinMax( inst,
5976 this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
5979 /* A date may be specified as an exact value or a relative one. */
5980 _determineDate: function( inst, date, defaultDate ) {
5981 var offsetNumeric = function( offset ) {
5982 var date = new Date();
5983 date.setDate( date.getDate() + offset );
5986 offsetString = function( offset ) {
5988 return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
5989 offset, $.datepicker._getFormatConfig( inst ) );
5996 var date = ( offset.toLowerCase().match( /^c/ ) ?
5997 $.datepicker._getDate( inst ) : null ) || new Date(),
5998 year = date.getFullYear(),
5999 month = date.getMonth(),
6000 day = date.getDate(),
6001 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
6002 matches = pattern.exec( offset );
6005 switch ( matches[ 2 ] || "d" ) {
6006 case "d" : case "D" :
6007 day += parseInt( matches[ 1 ], 10 ); break;
6008 case "w" : case "W" :
6009 day += parseInt( matches[ 1 ], 10 ) * 7; break;
6010 case "m" : case "M" :
6011 month += parseInt( matches[ 1 ], 10 );
6012 day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
6014 case "y": case "Y" :
6015 year += parseInt( matches[ 1 ], 10 );
6016 day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
6019 matches = pattern.exec( offset );
6021 return new Date( year, month, day );
6023 newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
6024 ( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );
6026 newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
6028 newDate.setHours( 0 );
6029 newDate.setMinutes( 0 );
6030 newDate.setSeconds( 0 );
6031 newDate.setMilliseconds( 0 );
6033 return this._daylightSavingAdjust( newDate );
6036 /* Handle switch to/from daylight saving.
6037 * Hours may be non-zero on daylight saving cut-over:
6038 * > 12 when midnight changeover, but then cannot generate
6039 * midnight datetime, so jump to 1AM, otherwise reset.
6040 * @param date (Date) the date to check
6041 * @return (Date) the corrected date
6043 _daylightSavingAdjust: function( date ) {
6047 date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
6051 /* Set the date(s) directly. */
6052 _setDate: function( inst, date, noChange ) {
6054 origMonth = inst.selectedMonth,
6055 origYear = inst.selectedYear,
6056 newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );
6058 inst.selectedDay = inst.currentDay = newDate.getDate();
6059 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
6060 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
6061 if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
6062 this._notifyChange( inst );
6064 this._adjustInstDate( inst );
6066 inst.input.val( clear ? "" : this._formatDate( inst ) );
6070 /* Retrieve the date(s) directly. */
6071 _getDate: function( inst ) {
6072 var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
6073 this._daylightSavingAdjust( new Date(
6074 inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
6078 /* Attach the onxxx handlers. These are declared statically so
6079 * they work with static code transformers like Caja.
6081 _attachHandlers: function( inst ) {
6082 var stepMonths = this._get( inst, "stepMonths" ),
6083 id = "#" + inst.id.replace( /\\\\/g, "\\" );
6084 inst.dpDiv.find( "[data-handler]" ).map( function() {
6087 $.datepicker._adjustDate( id, -stepMonths, "M" );
6090 $.datepicker._adjustDate( id, +stepMonths, "M" );
6093 $.datepicker._hideDatepicker();
6096 $.datepicker._gotoToday( id );
6098 selectDay: function() {
6099 $.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
6102 selectMonth: function() {
6103 $.datepicker._selectMonthYear( id, this, "M" );
6106 selectYear: function() {
6107 $.datepicker._selectMonthYear( id, this, "Y" );
6111 $( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
6115 /* Generate the HTML for the current state of the date picker. */
6116 _generateHTML: function( inst ) {
6117 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
6118 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
6119 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
6120 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
6121 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
6122 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
6123 tempDate = new Date(),
6124 today = this._daylightSavingAdjust(
6125 new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
6126 isRTL = this._get( inst, "isRTL" ),
6127 showButtonPanel = this._get( inst, "showButtonPanel" ),
6128 hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
6129 navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
6130 numMonths = this._getNumberOfMonths( inst ),
6131 showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
6132 stepMonths = this._get( inst, "stepMonths" ),
6133 isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
6134 currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
6135 new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
6136 minDate = this._getMinMaxDate( inst, "min" ),
6137 maxDate = this._getMinMaxDate( inst, "max" ),
6138 drawMonth = inst.drawMonth - showCurrentAtPos,
6139 drawYear = inst.drawYear;
6141 if ( drawMonth < 0 ) {
6146 maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
6147 maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
6148 maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
6149 while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
6151 if ( drawMonth < 0 ) {
6157 inst.drawMonth = drawMonth;
6158 inst.drawYear = drawYear;
6160 prevText = this._get( inst, "prevText" );
6161 prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
6162 this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
6163 this._getFormatConfig( inst ) ) );
6165 prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
6166 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
6167 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
6168 ( 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>" ) );
6170 nextText = this._get( inst, "nextText" );
6171 nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
6172 this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
6173 this._getFormatConfig( inst ) ) );
6175 next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
6176 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
6177 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
6178 ( 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>" ) );
6180 currentText = this._get( inst, "currentText" );
6181 gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
6182 currentText = ( !navigationAsDateFormat ? currentText :
6183 this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
6185 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'>" +
6186 this._get( inst, "closeText" ) + "</button>" : "" );
6188 buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
6189 ( 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'" +
6190 ">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";
6192 firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
6193 firstDay = ( isNaN( firstDay ) ? 0 : firstDay );
6195 showWeek = this._get( inst, "showWeek" );
6196 dayNames = this._get( inst, "dayNames" );
6197 dayNamesMin = this._get( inst, "dayNamesMin" );
6198 monthNames = this._get( inst, "monthNames" );
6199 monthNamesShort = this._get( inst, "monthNamesShort" );
6200 beforeShowDay = this._get( inst, "beforeShowDay" );
6201 showOtherMonths = this._get( inst, "showOtherMonths" );
6202 selectOtherMonths = this._get( inst, "selectOtherMonths" );
6203 defaultDate = this._getDefaultDate( inst );
6206 for ( row = 0; row < numMonths[ 0 ]; row++ ) {
6209 for ( col = 0; col < numMonths[ 1 ]; col++ ) {
6210 selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
6211 cornerClass = " ui-corner-all";
6213 if ( isMultiMonth ) {
6214 calender += "<div class='ui-datepicker-group";
6215 if ( numMonths[ 1 ] > 1 ) {
6217 case 0: calender += " ui-datepicker-group-first";
6218 cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
6219 case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
6220 cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
6221 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
6226 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
6227 ( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
6228 ( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
6229 this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
6230 row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
6231 "</div><table class='ui-datepicker-calendar'><thead>" +
6233 thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
6234 for ( dow = 0; dow < 7; dow++ ) { // days of the week
6235 day = ( dow + firstDay ) % 7;
6236 thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
6237 "<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
6239 calender += thead + "</tr></thead><tbody>";
6240 daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
6241 if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
6242 inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
6244 leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
6245 curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
6246 numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
6247 this.maxRows = numRows;
6248 printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
6249 for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
6251 tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
6252 this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
6253 for ( dow = 0; dow < 7; dow++ ) { // create date picker days
6254 daySettings = ( beforeShowDay ?
6255 beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
6256 otherMonth = ( printDate.getMonth() !== drawMonth );
6257 unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
6258 ( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
6259 tbody += "<td class='" +
6260 ( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
6261 ( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
6262 ( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
6263 ( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?
6265 // or defaultDate is current printedDate and defaultDate is selectedDate
6266 " " + this._dayOverClass : "" ) + // highlight selected day
6267 ( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) + // highlight unselectable days
6268 ( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
6269 ( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
6270 ( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
6271 ( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "'" ) + "'" : "" ) + // cell title
6272 ( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
6273 ( otherMonth && !showOtherMonths ? " " : // display for other months
6274 ( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
6275 ( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
6276 ( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
6277 ( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
6278 "' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
6279 printDate.setDate( printDate.getDate() + 1 );
6280 printDate = this._daylightSavingAdjust( printDate );
6282 calender += tbody + "</tr>";
6285 if ( drawMonth > 11 ) {
6289 calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
6290 ( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
6295 html += buttonPanel;
6296 inst._keyEvent = false;
6300 /* Generate the month and year header. */
6301 _generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
6302 secondary, monthNames, monthNamesShort ) {
6304 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
6305 changeMonth = this._get( inst, "changeMonth" ),
6306 changeYear = this._get( inst, "changeYear" ),
6307 showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
6308 html = "<div class='ui-datepicker-title'>",
6312 if ( secondary || !changeMonth ) {
6313 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
6315 inMinYear = ( minDate && minDate.getFullYear() === drawYear );
6316 inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
6317 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
6318 for ( month = 0; month < 12; month++ ) {
6319 if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
6320 monthHtml += "<option value='" + month + "'" +
6321 ( month === drawMonth ? " selected='selected'" : "" ) +
6322 ">" + monthNamesShort[ month ] + "</option>";
6325 monthHtml += "</select>";
6328 if ( !showMonthAfterYear ) {
6329 html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? " " : "" );
6333 if ( !inst.yearshtml ) {
6334 inst.yearshtml = "";
6335 if ( secondary || !changeYear ) {
6336 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
6339 // determine range of years to display
6340 years = this._get( inst, "yearRange" ).split( ":" );
6341 thisYear = new Date().getFullYear();
6342 determineYear = function( value ) {
6343 var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
6344 ( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
6345 parseInt( value, 10 ) ) );
6346 return ( isNaN( year ) ? thisYear : year );
6348 year = determineYear( years[ 0 ] );
6349 endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
6350 year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
6351 endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
6352 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
6353 for ( ; year <= endYear; year++ ) {
6354 inst.yearshtml += "<option value='" + year + "'" +
6355 ( year === drawYear ? " selected='selected'" : "" ) +
6356 ">" + year + "</option>";
6358 inst.yearshtml += "</select>";
6360 html += inst.yearshtml;
6361 inst.yearshtml = null;
6365 html += this._get( inst, "yearSuffix" );
6366 if ( showMonthAfterYear ) {
6367 html += ( secondary || !( changeMonth && changeYear ) ? " " : "" ) + monthHtml;
6369 html += "</div>"; // Close datepicker_header
6373 /* Adjust one of the date sub-fields. */
6374 _adjustInstDate: function( inst, offset, period ) {
6375 var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
6376 month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
6377 day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
6378 date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
6380 inst.selectedDay = date.getDate();
6381 inst.drawMonth = inst.selectedMonth = date.getMonth();
6382 inst.drawYear = inst.selectedYear = date.getFullYear();
6383 if ( period === "M" || period === "Y" ) {
6384 this._notifyChange( inst );
6388 /* Ensure a date is within any min/max bounds. */
6389 _restrictMinMax: function( inst, date ) {
6390 var minDate = this._getMinMaxDate( inst, "min" ),
6391 maxDate = this._getMinMaxDate( inst, "max" ),
6392 newDate = ( minDate && date < minDate ? minDate : date );
6393 return ( maxDate && newDate > maxDate ? maxDate : newDate );
6396 /* Notify change of month/year. */
6397 _notifyChange: function( inst ) {
6398 var onChange = this._get( inst, "onChangeMonthYear" );
6400 onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
6401 [ inst.selectedYear, inst.selectedMonth + 1, inst ] );
6405 /* Determine the number of months to show. */
6406 _getNumberOfMonths: function( inst ) {
6407 var numMonths = this._get( inst, "numberOfMonths" );
6408 return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
6411 /* Determine the current maximum date - ensure no time components are set. */
6412 _getMinMaxDate: function( inst, minMax ) {
6413 return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
6416 /* Find the number of days in a given month. */
6417 _getDaysInMonth: function( year, month ) {
6418 return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
6421 /* Find the day of the week of the first of a month. */
6422 _getFirstDayOfMonth: function( year, month ) {
6423 return new Date( year, month, 1 ).getDay();
6426 /* Determines if we should allow a "next/prev" month display change. */
6427 _canAdjustMonth: function( inst, offset, curYear, curMonth ) {
6428 var numMonths = this._getNumberOfMonths( inst ),
6429 date = this._daylightSavingAdjust( new Date( curYear,
6430 curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
6433 date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
6435 return this._isInRange( inst, date );
6438 /* Is the given date in the accepted range? */
6439 _isInRange: function( inst, date ) {
6440 var yearSplit, currentYear,
6441 minDate = this._getMinMaxDate( inst, "min" ),
6442 maxDate = this._getMinMaxDate( inst, "max" ),
6445 years = this._get( inst, "yearRange" );
6447 yearSplit = years.split( ":" );
6448 currentYear = new Date().getFullYear();
6449 minYear = parseInt( yearSplit[ 0 ], 10 );
6450 maxYear = parseInt( yearSplit[ 1 ], 10 );
6451 if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
6452 minYear += currentYear;
6454 if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
6455 maxYear += currentYear;
6459 return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
6460 ( !maxDate || date.getTime() <= maxDate.getTime() ) &&
6461 ( !minYear || date.getFullYear() >= minYear ) &&
6462 ( !maxYear || date.getFullYear() <= maxYear ) );
6465 /* Provide the configuration settings for formatting/parsing. */
6466 _getFormatConfig: function( inst ) {
6467 var shortYearCutoff = this._get( inst, "shortYearCutoff" );
6468 shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
6469 new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
6470 return { shortYearCutoff: shortYearCutoff,
6471 dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
6472 monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
6475 /* Format the given date for display. */
6476 _formatDate: function( inst, day, month, year ) {
6478 inst.currentDay = inst.selectedDay;
6479 inst.currentMonth = inst.selectedMonth;
6480 inst.currentYear = inst.selectedYear;
6482 var date = ( day ? ( typeof day === "object" ? day :
6483 this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
6484 this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
6485 return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
6490 * Bind hover events for datepicker elements.
6491 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
6492 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
6494 function datepicker_bindHover( dpDiv ) {
6495 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
6496 return dpDiv.on( "mouseout", selector, function() {
6497 $( this ).removeClass( "ui-state-hover" );
6498 if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
6499 $( this ).removeClass( "ui-datepicker-prev-hover" );
6501 if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
6502 $( this ).removeClass( "ui-datepicker-next-hover" );
6505 .on( "mouseover", selector, datepicker_handleMouseover );
6508 function datepicker_handleMouseover() {
6509 if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
6510 $( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
6511 $( this ).addClass( "ui-state-hover" );
6512 if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
6513 $( this ).addClass( "ui-datepicker-prev-hover" );
6515 if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
6516 $( this ).addClass( "ui-datepicker-next-hover" );
6521 /* jQuery extend now ignores nulls! */
6522 function datepicker_extendRemove( target, props ) {
6523 $.extend( target, props );
6524 for ( var name in props ) {
6525 if ( props[ name ] == null ) {
6526 target[ name ] = props[ name ];
6532 /* Invoke the datepicker functionality.
6533 @param options string - a command, optionally followed by additional parameters or
6534 Object - settings for attaching new datepicker functionality
6535 @return jQuery object */
6536 $.fn.datepicker = function( options ) {
6538 /* Verify an empty collection wasn't passed - Fixes #6976 */
6539 if ( !this.length ) {
6543 /* Initialise the date picker. */
6544 if ( !$.datepicker.initialized ) {
6545 $( document ).on( "mousedown", $.datepicker._checkExternalClick );
6546 $.datepicker.initialized = true;
6549 /* Append datepicker main container to body if not exist. */
6550 if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
6551 $( "body" ).append( $.datepicker.dpDiv );
6554 var otherArgs = Array.prototype.slice.call( arguments, 1 );
6555 if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
6556 return $.datepicker[ "_" + options + "Datepicker" ].
6557 apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
6559 if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
6560 return $.datepicker[ "_" + options + "Datepicker" ].
6561 apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
6563 return this.each( function() {
6564 typeof options === "string" ?
6565 $.datepicker[ "_" + options + "Datepicker" ].
6566 apply( $.datepicker, [ this ].concat( otherArgs ) ) :
6567 $.datepicker._attachDatepicker( this, options );
6571 $.datepicker = new Datepicker(); // singleton instance
6572 $.datepicker.initialized = false;
6573 $.datepicker.uuid = new Date().getTime();
6574 $.datepicker.version = "1.12.1";
6576 var widgetsDatepicker = $.datepicker;
6580 * jQuery UI Slider 1.12.1
6581 * http://jqueryui.com
6583 * Copyright jQuery Foundation and other contributors
6584 * Released under the MIT license.
6585 * http://jquery.org/license
6590 //>>description: Displays a flexible slider with ranges and accessibility via keyboard.
6591 //>>docs: http://api.jqueryui.com/slider/
6592 //>>demos: http://jqueryui.com/slider/
6593 //>>css.structure: ../../themes/base/core.css
6594 //>>css.structure: ../../themes/base/slider.css
6595 //>>css.theme: ../../themes/base/theme.css
6599 var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, {
6601 widgetEventPrefix: "slide",
6606 "ui-slider": "ui-corner-all",
6607 "ui-slider-handle": "ui-corner-all",
6609 // Note: ui-widget-header isn't the most fittingly semantic framework class for this
6610 // element, but worked best visually with a variety of themes
6611 "ui-slider-range": "ui-corner-all ui-widget-header"
6616 orientation: "horizontal",
6629 // Number of pages in a slider
6630 // (how many times can you page up/down to go through the whole range)
6633 _create: function() {
6634 this._keySliding = false;
6635 this._mouseSliding = false;
6636 this._animateOff = true;
6637 this._handleIndex = null;
6638 this._detectOrientation();
6640 this._calculateNewMax();
6642 this._addClass( "ui-slider ui-slider-" + this.orientation,
6643 "ui-widget ui-widget-content" );
6647 this._animateOff = false;
6650 _refresh: function() {
6651 this._createRange();
6652 this._createHandles();
6653 this._setupEvents();
6654 this._refreshValue();
6657 _createHandles: function() {
6659 options = this.options,
6660 existingHandles = this.element.find( ".ui-slider-handle" ),
6661 handle = "<span tabindex='0'></span>",
6664 handleCount = ( options.values && options.values.length ) || 1;
6666 if ( existingHandles.length > handleCount ) {
6667 existingHandles.slice( handleCount ).remove();
6668 existingHandles = existingHandles.slice( 0, handleCount );
6671 for ( i = existingHandles.length; i < handleCount; i++ ) {
6672 handles.push( handle );
6675 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
6677 this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );
6679 this.handle = this.handles.eq( 0 );
6681 this.handles.each( function( i ) {
6683 .data( "ui-slider-handle-index", i )
6684 .attr( "tabIndex", 0 );
6688 _createRange: function() {
6689 var options = this.options;
6691 if ( options.range ) {
6692 if ( options.range === true ) {
6693 if ( !options.values ) {
6694 options.values = [ this._valueMin(), this._valueMin() ];
6695 } else if ( options.values.length && options.values.length !== 2 ) {
6696 options.values = [ options.values[ 0 ], options.values[ 0 ] ];
6697 } else if ( $.isArray( options.values ) ) {
6698 options.values = options.values.slice( 0 );
6702 if ( !this.range || !this.range.length ) {
6703 this.range = $( "<div>" )
6704 .appendTo( this.element );
6706 this._addClass( this.range, "ui-slider-range" );
6708 this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );
6710 // Handle range switching from true to min/max
6716 if ( options.range === "min" || options.range === "max" ) {
6717 this._addClass( this.range, "ui-slider-range-" + options.range );
6721 this.range.remove();
6727 _setupEvents: function() {
6728 this._off( this.handles );
6729 this._on( this.handles, this._handleEvents );
6730 this._hoverable( this.handles );
6731 this._focusable( this.handles );
6734 _destroy: function() {
6735 this.handles.remove();
6737 this.range.remove();
6740 this._mouseDestroy();
6743 _mouseCapture: function( event ) {
6744 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
6752 this.elementSize = {
6753 width: this.element.outerWidth(),
6754 height: this.element.outerHeight()
6756 this.elementOffset = this.element.offset();
6758 position = { x: event.pageX, y: event.pageY };
6759 normValue = this._normValueFromMouse( position );
6760 distance = this._valueMax() - this._valueMin() + 1;
6761 this.handles.each( function( i ) {
6762 var thisDistance = Math.abs( normValue - that.values( i ) );
6763 if ( ( distance > thisDistance ) ||
6764 ( distance === thisDistance &&
6765 ( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
6766 distance = thisDistance;
6767 closestHandle = $( this );
6772 allowed = this._start( event, index );
6773 if ( allowed === false ) {
6776 this._mouseSliding = true;
6778 this._handleIndex = index;
6780 this._addClass( closestHandle, null, "ui-state-active" );
6781 closestHandle.trigger( "focus" );
6783 offset = closestHandle.offset();
6784 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
6785 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
6786 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
6787 top: event.pageY - offset.top -
6788 ( closestHandle.height() / 2 ) -
6789 ( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
6790 ( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
6791 ( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
6794 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
6795 this._slide( event, index, normValue );
6797 this._animateOff = true;
6801 _mouseStart: function() {
6805 _mouseDrag: function( event ) {
6806 var position = { x: event.pageX, y: event.pageY },
6807 normValue = this._normValueFromMouse( position );
6809 this._slide( event, this._handleIndex, normValue );
6814 _mouseStop: function( event ) {
6815 this._removeClass( this.handles, null, "ui-state-active" );
6816 this._mouseSliding = false;
6818 this._stop( event, this._handleIndex );
6819 this._change( event, this._handleIndex );
6821 this._handleIndex = null;
6822 this._clickOffset = null;
6823 this._animateOff = false;
6828 _detectOrientation: function() {
6829 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
6832 _normValueFromMouse: function( position ) {
6839 if ( this.orientation === "horizontal" ) {
6840 pixelTotal = this.elementSize.width;
6841 pixelMouse = position.x - this.elementOffset.left -
6842 ( this._clickOffset ? this._clickOffset.left : 0 );
6844 pixelTotal = this.elementSize.height;
6845 pixelMouse = position.y - this.elementOffset.top -
6846 ( this._clickOffset ? this._clickOffset.top : 0 );
6849 percentMouse = ( pixelMouse / pixelTotal );
6850 if ( percentMouse > 1 ) {
6853 if ( percentMouse < 0 ) {
6856 if ( this.orientation === "vertical" ) {
6857 percentMouse = 1 - percentMouse;
6860 valueTotal = this._valueMax() - this._valueMin();
6861 valueMouse = this._valueMin() + percentMouse * valueTotal;
6863 return this._trimAlignValue( valueMouse );
6866 _uiHash: function( index, value, values ) {
6868 handle: this.handles[ index ],
6870 value: value !== undefined ? value : this.value()
6873 if ( this._hasMultipleValues() ) {
6874 uiHash.value = value !== undefined ? value : this.values( index );
6875 uiHash.values = values || this.values();
6881 _hasMultipleValues: function() {
6882 return this.options.values && this.options.values.length;
6885 _start: function( event, index ) {
6886 return this._trigger( "start", event, this._uiHash( index ) );
6889 _slide: function( event, index, newVal ) {
6890 var allowed, otherVal,
6891 currentValue = this.value(),
6892 newValues = this.values();
6894 if ( this._hasMultipleValues() ) {
6895 otherVal = this.values( index ? 0 : 1 );
6896 currentValue = this.values( index );
6898 if ( this.options.values.length === 2 && this.options.range === true ) {
6899 newVal = index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
6902 newValues[ index ] = newVal;
6905 if ( newVal === currentValue ) {
6909 allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );
6911 // A slide can be canceled by returning false from the slide callback
6912 if ( allowed === false ) {
6916 if ( this._hasMultipleValues() ) {
6917 this.values( index, newVal );
6919 this.value( newVal );
6923 _stop: function( event, index ) {
6924 this._trigger( "stop", event, this._uiHash( index ) );
6927 _change: function( event, index ) {
6928 if ( !this._keySliding && !this._mouseSliding ) {
6930 //store the last changed value index for reference when handles overlap
6931 this._lastChangedValue = index;
6932 this._trigger( "change", event, this._uiHash( index ) );
6936 value: function( newValue ) {
6937 if ( arguments.length ) {
6938 this.options.value = this._trimAlignValue( newValue );
6939 this._refreshValue();
6940 this._change( null, 0 );
6944 return this._value();
6947 values: function( index, newValue ) {
6952 if ( arguments.length > 1 ) {
6953 this.options.values[ index ] = this._trimAlignValue( newValue );
6954 this._refreshValue();
6955 this._change( null, index );
6959 if ( arguments.length ) {
6960 if ( $.isArray( arguments[ 0 ] ) ) {
6961 vals = this.options.values;
6962 newValues = arguments[ 0 ];
6963 for ( i = 0; i < vals.length; i += 1 ) {
6964 vals[ i ] = this._trimAlignValue( newValues[ i ] );
6965 this._change( null, i );
6967 this._refreshValue();
6969 if ( this._hasMultipleValues() ) {
6970 return this._values( index );
6972 return this.value();
6976 return this._values();
6980 _setOption: function( key, value ) {
6984 if ( key === "range" && this.options.range === true ) {
6985 if ( value === "min" ) {
6986 this.options.value = this._values( 0 );
6987 this.options.values = null;
6988 } else if ( value === "max" ) {
6989 this.options.value = this._values( this.options.values.length - 1 );
6990 this.options.values = null;
6994 if ( $.isArray( this.options.values ) ) {
6995 valsLength = this.options.values.length;
6998 this._super( key, value );
7002 this._detectOrientation();
7003 this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
7004 ._addClass( "ui-slider-" + this.orientation );
7005 this._refreshValue();
7006 if ( this.options.range ) {
7007 this._refreshRange( value );
7010 // Reset positioning from previous orientation
7011 this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
7014 this._animateOff = true;
7015 this._refreshValue();
7016 this._change( null, 0 );
7017 this._animateOff = false;
7020 this._animateOff = true;
7021 this._refreshValue();
7023 // Start from the last handle to prevent unreachable handles (#9046)
7024 for ( i = valsLength - 1; i >= 0; i-- ) {
7025 this._change( null, i );
7027 this._animateOff = false;
7032 this._animateOff = true;
7033 this._calculateNewMax();
7034 this._refreshValue();
7035 this._animateOff = false;
7038 this._animateOff = true;
7040 this._animateOff = false;
7045 _setOptionDisabled: function( value ) {
7046 this._super( value );
7048 this._toggleClass( null, "ui-state-disabled", !!value );
7051 //internal value getter
7052 // _value() returns value trimmed by min and max, aligned by step
7053 _value: function() {
7054 var val = this.options.value;
7055 val = this._trimAlignValue( val );
7060 //internal values getter
7061 // _values() returns array of values trimmed by min and max, aligned by step
7062 // _values( index ) returns single value trimmed by min and max, aligned by step
7063 _values: function( index ) {
7068 if ( arguments.length ) {
7069 val = this.options.values[ index ];
7070 val = this._trimAlignValue( val );
7073 } else if ( this._hasMultipleValues() ) {
7075 // .slice() creates a copy of the array
7076 // this copy gets trimmed by min and max and then returned
7077 vals = this.options.values.slice();
7078 for ( i = 0; i < vals.length; i += 1 ) {
7079 vals[ i ] = this._trimAlignValue( vals[ i ] );
7088 // Returns the step-aligned value that val is closest to, between (inclusive) min and max
7089 _trimAlignValue: function( val ) {
7090 if ( val <= this._valueMin() ) {
7091 return this._valueMin();
7093 if ( val >= this._valueMax() ) {
7094 return this._valueMax();
7096 var step = ( this.options.step > 0 ) ? this.options.step : 1,
7097 valModStep = ( val - this._valueMin() ) % step,
7098 alignValue = val - valModStep;
7100 if ( Math.abs( valModStep ) * 2 >= step ) {
7101 alignValue += ( valModStep > 0 ) ? step : ( -step );
7104 // Since JavaScript has problems with large floats, round
7105 // the final value to 5 digits after the decimal point (see #4124)
7106 return parseFloat( alignValue.toFixed( 5 ) );
7109 _calculateNewMax: function() {
7110 var max = this.options.max,
7111 min = this._valueMin(),
7112 step = this.options.step,
7113 aboveMin = Math.round( ( max - min ) / step ) * step;
7114 max = aboveMin + min;
7115 if ( max > this.options.max ) {
7117 //If max is not divisible by step, rounding off may increase its value
7120 this.max = parseFloat( max.toFixed( this._precision() ) );
7123 _precision: function() {
7124 var precision = this._precisionOf( this.options.step );
7125 if ( this.options.min !== null ) {
7126 precision = Math.max( precision, this._precisionOf( this.options.min ) );
7131 _precisionOf: function( num ) {
7132 var str = num.toString(),
7133 decimal = str.indexOf( "." );
7134 return decimal === -1 ? 0 : str.length - decimal - 1;
7137 _valueMin: function() {
7138 return this.options.min;
7141 _valueMax: function() {
7145 _refreshRange: function( orientation ) {
7146 if ( orientation === "vertical" ) {
7147 this.range.css( { "width": "", "left": "" } );
7149 if ( orientation === "horizontal" ) {
7150 this.range.css( { "height": "", "bottom": "" } );
7154 _refreshValue: function() {
7155 var lastValPercent, valPercent, value, valueMin, valueMax,
7156 oRange = this.options.range,
7159 animate = ( !this._animateOff ) ? o.animate : false,
7162 if ( this._hasMultipleValues() ) {
7163 this.handles.each( function( i ) {
7164 valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() -
7165 that._valueMin() ) * 100;
7166 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
7167 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
7168 if ( that.options.range === true ) {
7169 if ( that.orientation === "horizontal" ) {
7171 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7172 left: valPercent + "%"
7176 that.range[ animate ? "animate" : "css" ]( {
7177 width: ( valPercent - lastValPercent ) + "%"
7185 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7186 bottom: ( valPercent ) + "%"
7190 that.range[ animate ? "animate" : "css" ]( {
7191 height: ( valPercent - lastValPercent ) + "%"
7199 lastValPercent = valPercent;
7202 value = this.value();
7203 valueMin = this._valueMin();
7204 valueMax = this._valueMax();
7205 valPercent = ( valueMax !== valueMin ) ?
7206 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
7208 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
7209 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
7211 if ( oRange === "min" && this.orientation === "horizontal" ) {
7212 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7213 width: valPercent + "%"
7216 if ( oRange === "max" && this.orientation === "horizontal" ) {
7217 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7218 width: ( 100 - valPercent ) + "%"
7221 if ( oRange === "min" && this.orientation === "vertical" ) {
7222 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7223 height: valPercent + "%"
7226 if ( oRange === "max" && this.orientation === "vertical" ) {
7227 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
7228 height: ( 100 - valPercent ) + "%"
7235 keydown: function( event ) {
7236 var allowed, curVal, newVal, step,
7237 index = $( event.target ).data( "ui-slider-handle-index" );
7239 switch ( event.keyCode ) {
7240 case $.ui.keyCode.HOME:
7241 case $.ui.keyCode.END:
7242 case $.ui.keyCode.PAGE_UP:
7243 case $.ui.keyCode.PAGE_DOWN:
7244 case $.ui.keyCode.UP:
7245 case $.ui.keyCode.RIGHT:
7246 case $.ui.keyCode.DOWN:
7247 case $.ui.keyCode.LEFT:
7248 event.preventDefault();
7249 if ( !this._keySliding ) {
7250 this._keySliding = true;
7251 this._addClass( $( event.target ), null, "ui-state-active" );
7252 allowed = this._start( event, index );
7253 if ( allowed === false ) {
7260 step = this.options.step;
7261 if ( this._hasMultipleValues() ) {
7262 curVal = newVal = this.values( index );
7264 curVal = newVal = this.value();
7267 switch ( event.keyCode ) {
7268 case $.ui.keyCode.HOME:
7269 newVal = this._valueMin();
7271 case $.ui.keyCode.END:
7272 newVal = this._valueMax();
7274 case $.ui.keyCode.PAGE_UP:
7275 newVal = this._trimAlignValue(
7276 curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
7279 case $.ui.keyCode.PAGE_DOWN:
7280 newVal = this._trimAlignValue(
7281 curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) );
7283 case $.ui.keyCode.UP:
7284 case $.ui.keyCode.RIGHT:
7285 if ( curVal === this._valueMax() ) {
7288 newVal = this._trimAlignValue( curVal + step );
7290 case $.ui.keyCode.DOWN:
7291 case $.ui.keyCode.LEFT:
7292 if ( curVal === this._valueMin() ) {
7295 newVal = this._trimAlignValue( curVal - step );
7299 this._slide( event, index, newVal );
7301 keyup: function( event ) {
7302 var index = $( event.target ).data( "ui-slider-handle-index" );
7304 if ( this._keySliding ) {
7305 this._keySliding = false;
7306 this._stop( event, index );
7307 this._change( event, index );
7308 this._removeClass( $( event.target ), null, "ui-state-active" );
7317 // Internal use only
7318 var escapeSelector = $.ui.escapeSelector = ( function() {
7319 var selectorEscape = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
7320 return function( selector ) {
7321 return selector.replace( selectorEscape, "\\$1" );
7327 * jQuery UI Tabs 1.12.1
7328 * http://jqueryui.com
7330 * Copyright jQuery Foundation and other contributors
7331 * Released under the MIT license.
7332 * http://jquery.org/license
7337 //>>description: Transforms a set of container elements into a tab structure.
7338 //>>docs: http://api.jqueryui.com/tabs/
7339 //>>demos: http://jqueryui.com/tabs/
7340 //>>css.structure: ../../themes/base/core.css
7341 //>>css.structure: ../../themes/base/tabs.css
7342 //>>css.theme: ../../themes/base/theme.css
7346 $.widget( "ui.tabs", {
7352 "ui-tabs": "ui-corner-all",
7353 "ui-tabs-nav": "ui-corner-all",
7354 "ui-tabs-panel": "ui-corner-bottom",
7355 "ui-tabs-tab": "ui-corner-top"
7359 heightStyle: "content",
7365 beforeActivate: null,
7370 _isLocal: ( function() {
7373 return function( anchor ) {
7374 var anchorUrl, locationUrl;
7376 anchorUrl = anchor.href.replace( rhash, "" );
7377 locationUrl = location.href.replace( rhash, "" );
7379 // Decoding may throw an error if the URL isn't UTF-8 (#9518)
7381 anchorUrl = decodeURIComponent( anchorUrl );
7382 } catch ( error ) {}
7384 locationUrl = decodeURIComponent( locationUrl );
7385 } catch ( error ) {}
7387 return anchor.hash.length > 1 && anchorUrl === locationUrl;
7391 _create: function() {
7393 options = this.options;
7395 this.running = false;
7397 this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
7398 this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
7400 this._processTabs();
7401 options.active = this._initialActive();
7403 // Take disabling tabs via class attribute from HTML
7404 // into account and update option properly.
7405 if ( $.isArray( options.disabled ) ) {
7406 options.disabled = $.unique( options.disabled.concat(
7407 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
7408 return that.tabs.index( li );
7413 // Check for length avoids error when initializing empty list
7414 if ( this.options.active !== false && this.anchors.length ) {
7415 this.active = this._findActive( options.active );
7422 if ( this.active.length ) {
7423 this.load( options.active );
7427 _initialActive: function() {
7428 var active = this.options.active,
7429 collapsible = this.options.collapsible,
7430 locationHash = location.hash.substring( 1 );
7432 if ( active === null ) {
7434 // check the fragment identifier in the URL
7435 if ( locationHash ) {
7436 this.tabs.each( function( i, tab ) {
7437 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
7444 // Check for a tab marked active via a class
7445 if ( active === null ) {
7446 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
7449 // No active tab, set to false
7450 if ( active === null || active === -1 ) {
7451 active = this.tabs.length ? 0 : false;
7455 // Handle numbers: negative, out of range
7456 if ( active !== false ) {
7457 active = this.tabs.index( this.tabs.eq( active ) );
7458 if ( active === -1 ) {
7459 active = collapsible ? false : 0;
7463 // Don't allow collapsible: false and active: false
7464 if ( !collapsible && active === false && this.anchors.length ) {
7471 _getCreateEventData: function() {
7474 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
7478 _tabKeydown: function( event ) {
7479 var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
7480 selectedIndex = this.tabs.index( focusedTab ),
7481 goingForward = true;
7483 if ( this._handlePageNav( event ) ) {
7487 switch ( event.keyCode ) {
7488 case $.ui.keyCode.RIGHT:
7489 case $.ui.keyCode.DOWN:
7492 case $.ui.keyCode.UP:
7493 case $.ui.keyCode.LEFT:
7494 goingForward = false;
7497 case $.ui.keyCode.END:
7498 selectedIndex = this.anchors.length - 1;
7500 case $.ui.keyCode.HOME:
7503 case $.ui.keyCode.SPACE:
7505 // Activate only, no collapsing
7506 event.preventDefault();
7507 clearTimeout( this.activating );
7508 this._activate( selectedIndex );
7510 case $.ui.keyCode.ENTER:
7512 // Toggle (cancel delayed activation, allow collapsing)
7513 event.preventDefault();
7514 clearTimeout( this.activating );
7516 // Determine if we should collapse or activate
7517 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
7523 // Focus the appropriate tab, based on which key was pressed
7524 event.preventDefault();
7525 clearTimeout( this.activating );
7526 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
7528 // Navigating with control/command key will prevent automatic activation
7529 if ( !event.ctrlKey && !event.metaKey ) {
7531 // Update aria-selected immediately so that AT think the tab is already selected.
7532 // Otherwise AT may confuse the user by stating that they need to activate the tab,
7533 // but the tab will already be activated by the time the announcement finishes.
7534 focusedTab.attr( "aria-selected", "false" );
7535 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
7537 this.activating = this._delay( function() {
7538 this.option( "active", selectedIndex );
7543 _panelKeydown: function( event ) {
7544 if ( this._handlePageNav( event ) ) {
7548 // Ctrl+up moves focus to the current tab
7549 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
7550 event.preventDefault();
7551 this.active.trigger( "focus" );
7555 // Alt+page up/down moves focus to the previous/next tab (and activates)
7556 _handlePageNav: function( event ) {
7557 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
7558 this._activate( this._focusNextTab( this.options.active - 1, false ) );
7561 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
7562 this._activate( this._focusNextTab( this.options.active + 1, true ) );
7567 _findNextTab: function( index, goingForward ) {
7568 var lastTabIndex = this.tabs.length - 1;
7570 function constrain() {
7571 if ( index > lastTabIndex ) {
7575 index = lastTabIndex;
7580 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
7581 index = goingForward ? index + 1 : index - 1;
7587 _focusNextTab: function( index, goingForward ) {
7588 index = this._findNextTab( index, goingForward );
7589 this.tabs.eq( index ).trigger( "focus" );
7593 _setOption: function( key, value ) {
7594 if ( key === "active" ) {
7596 // _activate() will handle invalid values and update this.options
7597 this._activate( value );
7601 this._super( key, value );
7603 if ( key === "collapsible" ) {
7604 this._toggleClass( "ui-tabs-collapsible", null, value );
7606 // Setting collapsible: false while collapsed; open first panel
7607 if ( !value && this.options.active === false ) {
7608 this._activate( 0 );
7612 if ( key === "event" ) {
7613 this._setupEvents( value );
7616 if ( key === "heightStyle" ) {
7617 this._setupHeightStyle( value );
7621 _sanitizeSelector: function( hash ) {
7622 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
7625 refresh: function() {
7626 var options = this.options,
7627 lis = this.tablist.children( ":has(a[href])" );
7629 // Get disabled tabs from class attribute from HTML
7630 // this will get converted to a boolean if needed in _refresh()
7631 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
7632 return lis.index( tab );
7635 this._processTabs();
7637 // Was collapsed or no tabs
7638 if ( options.active === false || !this.anchors.length ) {
7639 options.active = false;
7642 // was active, but active tab is gone
7643 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
7645 // all remaining tabs are disabled
7646 if ( this.tabs.length === options.disabled.length ) {
7647 options.active = false;
7650 // activate previous tab
7652 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
7655 // was active, active tab still exists
7658 // make sure active index is correct
7659 options.active = this.tabs.index( this.active );
7665 _refresh: function() {
7666 this._setOptionDisabled( this.options.disabled );
7667 this._setupEvents( this.options.event );
7668 this._setupHeightStyle( this.options.heightStyle );
7670 this.tabs.not( this.active ).attr( {
7671 "aria-selected": "false",
7672 "aria-expanded": "false",
7675 this.panels.not( this._getPanelForTab( this.active ) )
7678 "aria-hidden": "true"
7681 // Make sure one tab is in the tab order
7682 if ( !this.active.length ) {
7683 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
7687 "aria-selected": "true",
7688 "aria-expanded": "true",
7691 this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
7692 this._getPanelForTab( this.active )
7695 "aria-hidden": "false"
7700 _processTabs: function() {
7702 prevTabs = this.tabs,
7703 prevAnchors = this.anchors,
7704 prevPanels = this.panels;
7706 this.tablist = this._getList().attr( "role", "tablist" );
7707 this._addClass( this.tablist, "ui-tabs-nav",
7708 "ui-helper-reset ui-helper-clearfix ui-widget-header" );
7710 // Prevent users from focusing disabled tabs via click
7712 .on( "mousedown" + this.eventNamespace, "> li", function( event ) {
7713 if ( $( this ).is( ".ui-state-disabled" ) ) {
7714 event.preventDefault();
7719 // Preventing the default action in mousedown doesn't prevent IE
7720 // from focusing the element, so if the anchor gets focused, blur.
7721 // We don't have to worry about focusing the previously focused
7722 // element since clicking on a non-focusable element should focus
7724 .on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
7725 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
7730 this.tabs = this.tablist.find( "> li:has(a[href])" )
7735 this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
7737 this.anchors = this.tabs.map( function() {
7738 return $( "a", this )[ 0 ];
7741 role: "presentation",
7744 this._addClass( this.anchors, "ui-tabs-anchor" );
7748 this.anchors.each( function( i, anchor ) {
7749 var selector, panel, panelId,
7750 anchorId = $( anchor ).uniqueId().attr( "id" ),
7751 tab = $( anchor ).closest( "li" ),
7752 originalAriaControls = tab.attr( "aria-controls" );
7755 if ( that._isLocal( anchor ) ) {
7756 selector = anchor.hash;
7757 panelId = selector.substring( 1 );
7758 panel = that.element.find( that._sanitizeSelector( selector ) );
7763 // If the tab doesn't already have aria-controls,
7764 // generate an id by using a throw-away element
7765 panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
7766 selector = "#" + panelId;
7767 panel = that.element.find( selector );
7768 if ( !panel.length ) {
7769 panel = that._createPanel( panelId );
7770 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
7772 panel.attr( "aria-live", "polite" );
7775 if ( panel.length ) {
7776 that.panels = that.panels.add( panel );
7778 if ( originalAriaControls ) {
7779 tab.data( "ui-tabs-aria-controls", originalAriaControls );
7782 "aria-controls": panelId,
7783 "aria-labelledby": anchorId
7785 panel.attr( "aria-labelledby", anchorId );
7788 this.panels.attr( "role", "tabpanel" );
7789 this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
7791 // Avoid memory leaks (#10056)
7793 this._off( prevTabs.not( this.tabs ) );
7794 this._off( prevAnchors.not( this.anchors ) );
7795 this._off( prevPanels.not( this.panels ) );
7799 // Allow overriding how to find the list for rare usage scenarios (#7715)
7800 _getList: function() {
7801 return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
7804 _createPanel: function( id ) {
7807 .data( "ui-tabs-destroy", true );
7810 _setOptionDisabled: function( disabled ) {
7811 var currentItem, li, i;
7813 if ( $.isArray( disabled ) ) {
7814 if ( !disabled.length ) {
7816 } else if ( disabled.length === this.anchors.length ) {
7822 for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
7823 currentItem = $( li );
7824 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
7825 currentItem.attr( "aria-disabled", "true" );
7826 this._addClass( currentItem, null, "ui-state-disabled" );
7828 currentItem.removeAttr( "aria-disabled" );
7829 this._removeClass( currentItem, null, "ui-state-disabled" );
7833 this.options.disabled = disabled;
7835 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
7836 disabled === true );
7839 _setupEvents: function( event ) {
7842 $.each( event.split( " " ), function( index, eventName ) {
7843 events[ eventName ] = "_eventHandler";
7847 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
7849 // Always prevent the default action, even when disabled
7850 this._on( true, this.anchors, {
7851 click: function( event ) {
7852 event.preventDefault();
7855 this._on( this.anchors, events );
7856 this._on( this.tabs, { keydown: "_tabKeydown" } );
7857 this._on( this.panels, { keydown: "_panelKeydown" } );
7859 this._focusable( this.tabs );
7860 this._hoverable( this.tabs );
7863 _setupHeightStyle: function( heightStyle ) {
7865 parent = this.element.parent();
7867 if ( heightStyle === "fill" ) {
7868 maxHeight = parent.height();
7869 maxHeight -= this.element.outerHeight() - this.element.height();
7871 this.element.siblings( ":visible" ).each( function() {
7872 var elem = $( this ),
7873 position = elem.css( "position" );
7875 if ( position === "absolute" || position === "fixed" ) {
7878 maxHeight -= elem.outerHeight( true );
7881 this.element.children().not( this.panels ).each( function() {
7882 maxHeight -= $( this ).outerHeight( true );
7885 this.panels.each( function() {
7886 $( this ).height( Math.max( 0, maxHeight -
7887 $( this ).innerHeight() + $( this ).height() ) );
7889 .css( "overflow", "auto" );
7890 } else if ( heightStyle === "auto" ) {
7892 this.panels.each( function() {
7893 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
7894 } ).height( maxHeight );
7898 _eventHandler: function( event ) {
7899 var options = this.options,
7900 active = this.active,
7901 anchor = $( event.currentTarget ),
7902 tab = anchor.closest( "li" ),
7903 clickedIsActive = tab[ 0 ] === active[ 0 ],
7904 collapsing = clickedIsActive && options.collapsible,
7905 toShow = collapsing ? $() : this._getPanelForTab( tab ),
7906 toHide = !active.length ? $() : this._getPanelForTab( active ),
7910 newTab: collapsing ? $() : tab,
7914 event.preventDefault();
7916 if ( tab.hasClass( "ui-state-disabled" ) ||
7918 // tab is already loading
7919 tab.hasClass( "ui-tabs-loading" ) ||
7921 // can't switch durning an animation
7924 // click on active header, but not collapsible
7925 ( clickedIsActive && !options.collapsible ) ||
7927 // allow canceling activation
7928 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
7932 options.active = collapsing ? false : this.tabs.index( tab );
7934 this.active = clickedIsActive ? $() : tab;
7939 if ( !toHide.length && !toShow.length ) {
7940 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
7943 if ( toShow.length ) {
7944 this.load( this.tabs.index( tab ), event );
7946 this._toggle( event, eventData );
7949 // Handles show/hide for selecting tabs
7950 _toggle: function( event, eventData ) {
7952 toShow = eventData.newPanel,
7953 toHide = eventData.oldPanel;
7955 this.running = true;
7957 function complete() {
7958 that.running = false;
7959 that._trigger( "activate", event, eventData );
7963 that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
7965 if ( toShow.length && that.options.show ) {
7966 that._show( toShow, that.options.show, complete );
7973 // Start out by hiding, then showing, then completing
7974 if ( toHide.length && this.options.hide ) {
7975 this._hide( toHide, this.options.hide, function() {
7976 that._removeClass( eventData.oldTab.closest( "li" ),
7977 "ui-tabs-active", "ui-state-active" );
7981 this._removeClass( eventData.oldTab.closest( "li" ),
7982 "ui-tabs-active", "ui-state-active" );
7987 toHide.attr( "aria-hidden", "true" );
7988 eventData.oldTab.attr( {
7989 "aria-selected": "false",
7990 "aria-expanded": "false"
7993 // If we're switching tabs, remove the old tab from the tab order.
7994 // If we're opening from collapsed state, remove the previous tab from the tab order.
7995 // If we're collapsing, then keep the collapsing tab in the tab order.
7996 if ( toShow.length && toHide.length ) {
7997 eventData.oldTab.attr( "tabIndex", -1 );
7998 } else if ( toShow.length ) {
7999 this.tabs.filter( function() {
8000 return $( this ).attr( "tabIndex" ) === 0;
8002 .attr( "tabIndex", -1 );
8005 toShow.attr( "aria-hidden", "false" );
8006 eventData.newTab.attr( {
8007 "aria-selected": "true",
8008 "aria-expanded": "true",
8013 _activate: function( index ) {
8015 active = this._findActive( index );
8017 // Trying to activate the already active panel
8018 if ( active[ 0 ] === this.active[ 0 ] ) {
8022 // Trying to collapse, simulate a click on the current active header
8023 if ( !active.length ) {
8024 active = this.active;
8027 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
8028 this._eventHandler( {
8030 currentTarget: anchor,
8031 preventDefault: $.noop
8035 _findActive: function( index ) {
8036 return index === false ? $() : this.tabs.eq( index );
8039 _getIndex: function( index ) {
8041 // meta-function to give users option to provide a href string instead of a numerical index.
8042 if ( typeof index === "string" ) {
8043 index = this.anchors.index( this.anchors.filter( "[href$='" +
8044 $.ui.escapeSelector( index ) + "']" ) );
8050 _destroy: function() {
8056 .removeAttr( "role" )
8057 .off( this.eventNamespace );
8060 .removeAttr( "role tabIndex" )
8063 this.tabs.add( this.panels ).each( function() {
8064 if ( $.data( this, "ui-tabs-destroy" ) ) {
8067 $( this ).removeAttr( "role tabIndex " +
8068 "aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
8072 this.tabs.each( function() {
8074 prev = li.data( "ui-tabs-aria-controls" );
8077 .attr( "aria-controls", prev )
8078 .removeData( "ui-tabs-aria-controls" );
8080 li.removeAttr( "aria-controls" );
8086 if ( this.options.heightStyle !== "content" ) {
8087 this.panels.css( "height", "" );
8091 enable: function( index ) {
8092 var disabled = this.options.disabled;
8093 if ( disabled === false ) {
8097 if ( index === undefined ) {
8100 index = this._getIndex( index );
8101 if ( $.isArray( disabled ) ) {
8102 disabled = $.map( disabled, function( num ) {
8103 return num !== index ? num : null;
8106 disabled = $.map( this.tabs, function( li, num ) {
8107 return num !== index ? num : null;
8111 this._setOptionDisabled( disabled );
8114 disable: function( index ) {
8115 var disabled = this.options.disabled;
8116 if ( disabled === true ) {
8120 if ( index === undefined ) {
8123 index = this._getIndex( index );
8124 if ( $.inArray( index, disabled ) !== -1 ) {
8127 if ( $.isArray( disabled ) ) {
8128 disabled = $.merge( [ index ], disabled ).sort();
8130 disabled = [ index ];
8133 this._setOptionDisabled( disabled );
8136 load: function( index, event ) {
8137 index = this._getIndex( index );
8139 tab = this.tabs.eq( index ),
8140 anchor = tab.find( ".ui-tabs-anchor" ),
8141 panel = this._getPanelForTab( tab ),
8146 complete = function( jqXHR, status ) {
8147 if ( status === "abort" ) {
8148 that.panels.stop( false, true );
8151 that._removeClass( tab, "ui-tabs-loading" );
8152 panel.removeAttr( "aria-busy" );
8154 if ( jqXHR === that.xhr ) {
8160 if ( this._isLocal( anchor[ 0 ] ) ) {
8164 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
8166 // Support: jQuery <1.8
8167 // jQuery <1.8 returns false if the request is canceled in beforeSend,
8168 // but as of 1.8, $.ajax() always returns a jqXHR object.
8169 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
8170 this._addClass( tab, "ui-tabs-loading" );
8171 panel.attr( "aria-busy", "true" );
8174 .done( function( response, status, jqXHR ) {
8176 // support: jQuery <1.8
8177 // http://bugs.jquery.com/ticket/11778
8178 setTimeout( function() {
8179 panel.html( response );
8180 that._trigger( "load", event, eventData );
8182 complete( jqXHR, status );
8185 .fail( function( jqXHR, status ) {
8187 // support: jQuery <1.8
8188 // http://bugs.jquery.com/ticket/11778
8189 setTimeout( function() {
8190 complete( jqXHR, status );
8196 _ajaxSettings: function( anchor, event, eventData ) {
8200 // Support: IE <11 only
8201 // Strip any hash that exists to prevent errors with the Ajax request
8202 url: anchor.attr( "href" ).replace( /#.*$/, "" ),
8203 beforeSend: function( jqXHR, settings ) {
8204 return that._trigger( "beforeLoad", event,
8205 $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
8210 _getPanelForTab: function( tab ) {
8211 var id = $( tab ).attr( "aria-controls" );
8212 return this.element.find( this._sanitizeSelector( "#" + id ) );
8217 // TODO: Switch return back to widget declaration at top of file when this is removed
8218 if ( $.uiBackCompat !== false ) {
8220 // Backcompat for ui-tab class (now ui-tabs-tab)
8221 $.widget( "ui.tabs", $.ui.tabs, {
8222 _processTabs: function() {
8223 this._superApply( arguments );
8224 this._addClass( this.tabs, "ui-tab" );
8229 var widgetsTabs = $.ui.tabs;
8233 * jQuery UI Tooltip 1.12.1
8234 * http://jqueryui.com
8236 * Copyright jQuery Foundation and other contributors
8237 * Released under the MIT license.
8238 * http://jquery.org/license
8243 //>>description: Shows additional information for any element on hover or focus.
8244 //>>docs: http://api.jqueryui.com/tooltip/
8245 //>>demos: http://jqueryui.com/tooltip/
8246 //>>css.structure: ../../themes/base/core.css
8247 //>>css.structure: ../../themes/base/tooltip.css
8248 //>>css.theme: ../../themes/base/theme.css
8252 $.widget( "ui.tooltip", {
8256 "ui-tooltip": "ui-corner-all ui-widget-shadow"
8258 content: function() {
8260 // support: IE<9, Opera in jQuery <1.7
8261 // .text() can't accept undefined, so coerce to a string
8262 var title = $( this ).attr( "title" ) || "";
8264 // Escape title, since we're going from an attribute to raw HTML
8265 return $( "<a>" ).text( title ).html();
8269 // Disabled elements have inconsistent behavior across browsers (#8661)
8270 items: "[title]:not([disabled])",
8274 collision: "flipfit flip"
8284 _addDescribedBy: function( elem, id ) {
8285 var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
8286 describedby.push( id );
8288 .data( "ui-tooltip-id", id )
8289 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
8292 _removeDescribedBy: function( elem ) {
8293 var id = elem.data( "ui-tooltip-id" ),
8294 describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
8295 index = $.inArray( id, describedby );
8297 if ( index !== -1 ) {
8298 describedby.splice( index, 1 );
8301 elem.removeData( "ui-tooltip-id" );
8302 describedby = $.trim( describedby.join( " " ) );
8303 if ( describedby ) {
8304 elem.attr( "aria-describedby", describedby );
8306 elem.removeAttr( "aria-describedby" );
8310 _create: function() {
8316 // IDs of generated tooltips, needed for destroy
8319 // IDs of parent tooltips where we removed the title attribute
8322 // Append the aria-live region so tooltips announce correctly
8323 this.liveRegion = $( "<div>" )
8326 "aria-live": "assertive",
8327 "aria-relevant": "additions"
8329 .appendTo( this.document[ 0 ].body );
8330 this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
8332 this.disabledTitles = $( [] );
8335 _setOption: function( key, value ) {
8338 this._super( key, value );
8340 if ( key === "content" ) {
8341 $.each( this.tooltips, function( id, tooltipData ) {
8342 that._updateContent( tooltipData.element );
8347 _setOptionDisabled: function( value ) {
8348 this[ value ? "_disable" : "_enable" ]();
8351 _disable: function() {
8354 // Close open tooltips
8355 $.each( this.tooltips, function( id, tooltipData ) {
8356 var event = $.Event( "blur" );
8357 event.target = event.currentTarget = tooltipData.element[ 0 ];
8358 that.close( event, true );
8361 // Remove title attributes to prevent native tooltips
8362 this.disabledTitles = this.disabledTitles.add(
8363 this.element.find( this.options.items ).addBack()
8364 .filter( function() {
8365 var element = $( this );
8366 if ( element.is( "[title]" ) ) {
8368 .data( "ui-tooltip-title", element.attr( "title" ) )
8369 .removeAttr( "title" );
8375 _enable: function() {
8377 // restore title attributes
8378 this.disabledTitles.each( function() {
8379 var element = $( this );
8380 if ( element.data( "ui-tooltip-title" ) ) {
8381 element.attr( "title", element.data( "ui-tooltip-title" ) );
8384 this.disabledTitles = $( [] );
8387 open: function( event ) {
8389 target = $( event ? event.target : this.element )
8391 // we need closest here due to mouseover bubbling,
8392 // but always pointing at the same event target
8393 .closest( this.options.items );
8395 // No element to show a tooltip for or the tooltip is already open
8396 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
8400 if ( target.attr( "title" ) ) {
8401 target.data( "ui-tooltip-title", target.attr( "title" ) );
8404 target.data( "ui-tooltip-open", true );
8406 // Kill parent tooltips, custom or native, for hover
8407 if ( event && event.type === "mouseover" ) {
8408 target.parents().each( function() {
8409 var parent = $( this ),
8411 if ( parent.data( "ui-tooltip-open" ) ) {
8412 blurEvent = $.Event( "blur" );
8413 blurEvent.target = blurEvent.currentTarget = this;
8414 that.close( blurEvent, true );
8416 if ( parent.attr( "title" ) ) {
8418 that.parents[ this.id ] = {
8420 title: parent.attr( "title" )
8422 parent.attr( "title", "" );
8427 this._registerCloseHandlers( event, target );
8428 this._updateContent( target, event );
8431 _updateContent: function( target, event ) {
8433 contentOption = this.options.content,
8435 eventType = event ? event.type : null;
8437 if ( typeof contentOption === "string" || contentOption.nodeType ||
8438 contentOption.jquery ) {
8439 return this._open( event, target, contentOption );
8442 content = contentOption.call( target[ 0 ], function( response ) {
8444 // IE may instantly serve a cached response for ajax requests
8445 // delay this call to _open so the other call to _open runs first
8446 that._delay( function() {
8448 // Ignore async response if tooltip was closed already
8449 if ( !target.data( "ui-tooltip-open" ) ) {
8453 // JQuery creates a special event for focusin when it doesn't
8454 // exist natively. To improve performance, the native event
8455 // object is reused and the type is changed. Therefore, we can't
8456 // rely on the type being correct after the event finished
8457 // bubbling, so we set it back to the previous value. (#8740)
8459 event.type = eventType;
8461 this._open( event, target, response );
8465 this._open( event, target, content );
8469 _open: function( event, target, content ) {
8470 var tooltipData, tooltip, delayedShow, a11yContent,
8471 positionOption = $.extend( {}, this.options.position );
8477 // Content can be updated multiple times. If the tooltip already
8478 // exists, then just update the content and bail.
8479 tooltipData = this._find( target );
8480 if ( tooltipData ) {
8481 tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
8485 // If we have a title, clear it to prevent the native tooltip
8486 // we have to check first to avoid defining a title if none exists
8487 // (we don't want to cause an element to start matching [title])
8489 // We use removeAttr only for key events, to allow IE to export the correct
8490 // accessible attributes. For mouse events, set to empty string to avoid
8491 // native tooltip showing up (happens only when removing inside mouseover).
8492 if ( target.is( "[title]" ) ) {
8493 if ( event && event.type === "mouseover" ) {
8494 target.attr( "title", "" );
8496 target.removeAttr( "title" );
8500 tooltipData = this._tooltip( target );
8501 tooltip = tooltipData.tooltip;
8502 this._addDescribedBy( target, tooltip.attr( "id" ) );
8503 tooltip.find( ".ui-tooltip-content" ).html( content );
8505 // Support: Voiceover on OS X, JAWS on IE <= 9
8506 // JAWS announces deletions even when aria-relevant="additions"
8507 // Voiceover will sometimes re-read the entire log region's contents from the beginning
8508 this.liveRegion.children().hide();
8509 a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
8510 a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
8511 a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
8512 a11yContent.appendTo( this.liveRegion );
8514 function position( event ) {
8515 positionOption.of = event;
8516 if ( tooltip.is( ":hidden" ) ) {
8519 tooltip.position( positionOption );
8521 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
8522 this._on( this.document, {
8526 // trigger once to override element-relative positioning
8529 tooltip.position( $.extend( {
8531 }, this.options.position ) );
8536 this._show( tooltip, this.options.show );
8538 // Handle tracking tooltips that are shown with a delay (#8644). As soon
8539 // as the tooltip is visible, position the tooltip using the most recent
8541 // Adds the check to add the timers only when both delay and track options are set (#14682)
8542 if ( this.options.track && this.options.show && this.options.show.delay ) {
8543 delayedShow = this.delayedShow = setInterval( function() {
8544 if ( tooltip.is( ":visible" ) ) {
8545 position( positionOption.of );
8546 clearInterval( delayedShow );
8551 this._trigger( "open", event, { tooltip: tooltip } );
8554 _registerCloseHandlers: function( event, target ) {
8556 keyup: function( event ) {
8557 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
8558 var fakeEvent = $.Event( event );
8559 fakeEvent.currentTarget = target[ 0 ];
8560 this.close( fakeEvent, true );
8565 // Only bind remove handler for delegated targets. Non-delegated
8566 // tooltips will handle this in destroy.
8567 if ( target[ 0 ] !== this.element[ 0 ] ) {
8568 events.remove = function() {
8569 this._removeTooltip( this._find( target ).tooltip );
8573 if ( !event || event.type === "mouseover" ) {
8574 events.mouseleave = "close";
8576 if ( !event || event.type === "focusin" ) {
8577 events.focusout = "close";
8579 this._on( true, target, events );
8582 close: function( event ) {
8585 target = $( event ? event.currentTarget : this.element ),
8586 tooltipData = this._find( target );
8588 // The tooltip may already be closed
8589 if ( !tooltipData ) {
8591 // We set ui-tooltip-open immediately upon open (in open()), but only set the
8592 // additional data once there's actually content to show (in _open()). So even if the
8593 // tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
8594 // the period between open() and _open().
8595 target.removeData( "ui-tooltip-open" );
8599 tooltip = tooltipData.tooltip;
8601 // Disabling closes the tooltip, so we need to track when we're closing
8602 // to avoid an infinite loop in case the tooltip becomes disabled on close
8603 if ( tooltipData.closing ) {
8607 // Clear the interval for delayed tracking tooltips
8608 clearInterval( this.delayedShow );
8610 // Only set title if we had one before (see comment in _open())
8611 // If the title attribute has changed since open(), don't restore
8612 if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
8613 target.attr( "title", target.data( "ui-tooltip-title" ) );
8616 this._removeDescribedBy( target );
8618 tooltipData.hiding = true;
8619 tooltip.stop( true );
8620 this._hide( tooltip, this.options.hide, function() {
8621 that._removeTooltip( $( this ) );
8624 target.removeData( "ui-tooltip-open" );
8625 this._off( target, "mouseleave focusout keyup" );
8627 // Remove 'remove' binding only on delegated targets
8628 if ( target[ 0 ] !== this.element[ 0 ] ) {
8629 this._off( target, "remove" );
8631 this._off( this.document, "mousemove" );
8633 if ( event && event.type === "mouseleave" ) {
8634 $.each( this.parents, function( id, parent ) {
8635 $( parent.element ).attr( "title", parent.title );
8636 delete that.parents[ id ];
8640 tooltipData.closing = true;
8641 this._trigger( "close", event, { tooltip: tooltip } );
8642 if ( !tooltipData.hiding ) {
8643 tooltipData.closing = false;
8647 _tooltip: function( element ) {
8648 var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
8649 content = $( "<div>" ).appendTo( tooltip ),
8650 id = tooltip.uniqueId().attr( "id" );
8652 this._addClass( content, "ui-tooltip-content" );
8653 this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
8655 tooltip.appendTo( this._appendTo( element ) );
8657 return this.tooltips[ id ] = {
8663 _find: function( target ) {
8664 var id = target.data( "ui-tooltip-id" );
8665 return id ? this.tooltips[ id ] : null;
8668 _removeTooltip: function( tooltip ) {
8670 delete this.tooltips[ tooltip.attr( "id" ) ];
8673 _appendTo: function( target ) {
8674 var element = target.closest( ".ui-front, dialog" );
8676 if ( !element.length ) {
8677 element = this.document[ 0 ].body;
8683 _destroy: function() {
8686 // Close open tooltips
8687 $.each( this.tooltips, function( id, tooltipData ) {
8689 // Delegate to close method to handle common cleanup
8690 var event = $.Event( "blur" ),
8691 element = tooltipData.element;
8692 event.target = event.currentTarget = element[ 0 ];
8693 that.close( event, true );
8695 // Remove immediately; destroying an open tooltip doesn't use the
8697 $( "#" + id ).remove();
8699 // Restore the title
8700 if ( element.data( "ui-tooltip-title" ) ) {
8702 // If the title attribute has changed since open(), don't restore
8703 if ( !element.attr( "title" ) ) {
8704 element.attr( "title", element.data( "ui-tooltip-title" ) );
8706 element.removeData( "ui-tooltip-title" );
8709 this.liveRegion.remove();
8714 // TODO: Switch return back to widget declaration at top of file when this is removed
8715 if ( $.uiBackCompat !== false ) {
8717 // Backcompat for tooltipClass option
8718 $.widget( "ui.tooltip", $.ui.tooltip, {
8722 _tooltip: function() {
8723 var tooltipData = this._superApply( arguments );
8724 if ( this.options.tooltipClass ) {
8725 tooltipData.tooltip.addClass( this.options.tooltipClass );
8732 var widgetsTooltip = $.ui.tooltip;