1 /*! jQuery UI - v1.10.3 - 2013-05-03
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.position.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
4 * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
5 (function( $, undefined ) {
8 runiqueId = /^ui-id-\d+$/;
10 // $.ui might exist from components with no dependencies, e.g., $.ui.position
44 focus: (function( orig ) {
45 return function( delay, fn ) {
46 return typeof delay === "number" ?
47 this.each(function() {
49 setTimeout(function() {
56 orig.apply( this, arguments );
60 scrollParent: function() {
62 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
63 scrollParent = this.parents().filter(function() {
64 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
67 scrollParent = this.parents().filter(function() {
68 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
72 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
75 zIndex: function( zIndex ) {
76 if ( zIndex !== undefined ) {
77 return this.css( "zIndex", zIndex );
81 var elem = $( this[ 0 ] ), position, value;
82 while ( elem.length && elem[ 0 ] !== document ) {
83 // Ignore z-index if position is set to a value where z-index is ignored by the browser
84 // This makes behavior of this function consistent across browsers
85 // WebKit always returns auto if the element is positioned
86 position = elem.css( "position" );
87 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
88 // IE returns 0 when zIndex is not specified
89 // other browsers return a string
90 // we ignore the case of nested elements with an explicit value of 0
91 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
92 value = parseInt( elem.css( "zIndex" ), 10 );
93 if ( !isNaN( value ) && value !== 0 ) {
104 uniqueId: function() {
105 return this.each(function() {
107 this.id = "ui-id-" + (++uuid);
112 removeUniqueId: function() {
113 return this.each(function() {
114 if ( runiqueId.test( this.id ) ) {
115 $( this ).removeAttr( "id" );
122 function focusable( element, isTabIndexNotNaN ) {
123 var map, mapName, img,
124 nodeName = element.nodeName.toLowerCase();
125 if ( "area" === nodeName ) {
126 map = element.parentNode;
128 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
131 img = $( "img[usemap=#" + mapName + "]" )[0];
132 return !!img && visible( img );
134 return ( /input|select|textarea|button|object/.test( nodeName ) ?
137 element.href || isTabIndexNotNaN :
139 // the element and all of its ancestors must be visible
143 function visible( element ) {
144 return $.expr.filters.visible( element ) &&
145 !$( element ).parents().addBack().filter(function() {
146 return $.css( this, "visibility" ) === "hidden";
150 $.extend( $.expr[ ":" ], {
151 data: $.expr.createPseudo ?
152 $.expr.createPseudo(function( dataName ) {
153 return function( elem ) {
154 return !!$.data( elem, dataName );
157 // support: jQuery <1.8
158 function( elem, i, match ) {
159 return !!$.data( elem, match[ 3 ] );
162 focusable: function( element ) {
163 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
166 tabbable: function( element ) {
167 var tabIndex = $.attr( element, "tabindex" ),
168 isTabIndexNaN = isNaN( tabIndex );
169 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
173 // support: jQuery <1.8
174 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
175 $.each( [ "Width", "Height" ], function( i, name ) {
176 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
177 type = name.toLowerCase(),
179 innerWidth: $.fn.innerWidth,
180 innerHeight: $.fn.innerHeight,
181 outerWidth: $.fn.outerWidth,
182 outerHeight: $.fn.outerHeight
185 function reduce( elem, size, border, margin ) {
186 $.each( side, function() {
187 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
189 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
192 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
198 $.fn[ "inner" + name ] = function( size ) {
199 if ( size === undefined ) {
200 return orig[ "inner" + name ].call( this );
203 return this.each(function() {
204 $( this ).css( type, reduce( this, size ) + "px" );
208 $.fn[ "outer" + name] = function( size, margin ) {
209 if ( typeof size !== "number" ) {
210 return orig[ "outer" + name ].call( this, size );
213 return this.each(function() {
214 $( this).css( type, reduce( this, size, true, margin ) + "px" );
220 // support: jQuery <1.8
221 if ( !$.fn.addBack ) {
222 $.fn.addBack = function( selector ) {
223 return this.add( selector == null ?
224 this.prevObject : this.prevObject.filter( selector )
229 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
230 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
231 $.fn.removeData = (function( removeData ) {
232 return function( key ) {
233 if ( arguments.length ) {
234 return removeData.call( this, $.camelCase( key ) );
236 return removeData.call( this );
239 })( $.fn.removeData );
247 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
249 $.support.selectstart = "onselectstart" in document.createElement( "div" );
251 disableSelection: function() {
252 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
253 ".ui-disableSelection", function( event ) {
254 event.preventDefault();
258 enableSelection: function() {
259 return this.unbind( ".ui-disableSelection" );
264 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
266 add: function( module, option, set ) {
268 proto = $.ui[ module ].prototype;
270 proto.plugins[ i ] = proto.plugins[ i ] || [];
271 proto.plugins[ i ].push( [ option, set[ i ] ] );
274 call: function( instance, name, args ) {
276 set = instance.plugins[ name ];
277 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
281 for ( i = 0; i < set.length; i++ ) {
282 if ( instance.options[ set[ i ][ 0 ] ] ) {
283 set[ i ][ 1 ].apply( instance.element, args );
289 // only used by resizable
290 hasScroll: function( el, a ) {
292 //If overflow is hidden, the element might have extra content, but the user wants to hide it
293 if ( $( el ).css( "overflow" ) === "hidden") {
297 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
300 if ( el[ scroll ] > 0 ) {
304 // TODO: determine which cases actually cause this to happen
305 // if the element doesn't have the scroll set, see if it's possible to
308 has = ( el[ scroll ] > 0 );
316 (function( $, undefined ) {
319 slice = Array.prototype.slice,
320 _cleanData = $.cleanData;
321 $.cleanData = function( elems ) {
322 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
324 $( elem ).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
331 $.widget = function( name, base, prototype ) {
332 var fullName, existingConstructor, constructor, basePrototype,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype = {},
336 namespace = name.split( "." )[ 0 ];
338 name = name.split( "." )[ 1 ];
339 fullName = namespace + "-" + name;
346 // create selector for plugin
347 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348 return !!$.data( elem, fullName );
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor = $[ namespace ][ name ];
353 constructor = $[ namespace ][ name ] = function( options, element ) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget ) {
356 return new constructor( options, element );
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments.length ) {
362 this._createWidget( options, element );
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor, {
367 version: prototype.version,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype = new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
380 basePrototype.options = $.widget.extend( {}, basePrototype.options );
381 $.each( prototype, function( prop, value ) {
382 if ( !$.isFunction( value ) ) {
383 proxiedPrototype[ prop ] = value;
386 proxiedPrototype[ prop ] = (function() {
387 var _super = function() {
388 return base.prototype[ prop ].apply( this, arguments );
390 _superApply = function( args ) {
391 return base.prototype[ prop ].apply( this, args );
394 var __super = this._super,
395 __superApply = this._superApply,
398 this._super = _super;
399 this._superApply = _superApply;
401 returnValue = value.apply( this, arguments );
403 this._super = __super;
404 this._superApply = __superApply;
410 constructor.prototype = $.widget.extend( basePrototype, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
415 }, proxiedPrototype, {
416 constructor: constructor,
417 namespace: namespace,
419 widgetFullName: fullName
422 // If this widget is being redefined then we need to find all widgets that
423 // are inheriting from it and redefine all of them so that they inherit from
424 // the new version of this widget. We're essentially trying to replace one
425 // level in the prototype chain.
426 if ( existingConstructor ) {
427 $.each( existingConstructor._childConstructors, function( i, child ) {
428 var childPrototype = child.prototype;
430 // redefine the child widget using the same prototype that was
431 // originally used, but inherit from the new version of the base
432 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
434 // remove the list of existing child constructors from the old constructor
435 // so the old child constructors can be garbage collected
436 delete existingConstructor._childConstructors;
438 base._childConstructors.push( constructor );
441 $.widget.bridge( name, constructor );
444 $.widget.extend = function( target ) {
445 var input = slice.call( arguments, 1 ),
447 inputLength = input.length,
450 for ( ; inputIndex < inputLength; inputIndex++ ) {
451 for ( key in input[ inputIndex ] ) {
452 value = input[ inputIndex ][ key ];
453 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
455 if ( $.isPlainObject( value ) ) {
456 target[ key ] = $.isPlainObject( target[ key ] ) ?
457 $.widget.extend( {}, target[ key ], value ) :
458 // Don't extend strings, arrays, etc. with objects
459 $.widget.extend( {}, value );
460 // Copy everything else by reference
462 target[ key ] = value;
470 $.widget.bridge = function( name, object ) {
471 var fullName = object.prototype.widgetFullName || name;
472 $.fn[ name ] = function( options ) {
473 var isMethodCall = typeof options === "string",
474 args = slice.call( arguments, 1 ),
477 // allow multiple hashes to be passed on init
478 options = !isMethodCall && args.length ?
479 $.widget.extend.apply( null, [ options ].concat(args) ) :
482 if ( isMethodCall ) {
483 this.each(function() {
485 instance = $.data( this, fullName );
487 return $.error( "cannot call methods on " + name + " prior to initialization; " +
488 "attempted to call method '" + options + "'" );
490 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
491 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
493 methodValue = instance[ options ].apply( instance, args );
494 if ( methodValue !== instance && methodValue !== undefined ) {
495 returnValue = methodValue && methodValue.jquery ?
496 returnValue.pushStack( methodValue.get() ) :
502 this.each(function() {
503 var instance = $.data( this, fullName );
505 instance.option( options || {} )._init();
507 $.data( this, fullName, new object( options, this ) );
516 $.Widget = function( /* options, element */ ) {};
517 $.Widget._childConstructors = [];
519 $.Widget.prototype = {
520 widgetName: "widget",
521 widgetEventPrefix: "",
522 defaultElement: "<div>",
529 _createWidget: function( options, element ) {
530 element = $( element || this.defaultElement || this )[ 0 ];
531 this.element = $( element );
533 this.eventNamespace = "." + this.widgetName + this.uuid;
534 this.options = $.widget.extend( {},
536 this._getCreateOptions(),
540 this.hoverable = $();
541 this.focusable = $();
543 if ( element !== this ) {
544 $.data( element, this.widgetFullName, this );
545 this._on( true, this.element, {
546 remove: function( event ) {
547 if ( event.target === element ) {
552 this.document = $( element.style ?
553 // element within the document
554 element.ownerDocument :
555 // element is window or document
556 element.document || element );
557 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
561 this._trigger( "create", null, this._getCreateEventData() );
564 _getCreateOptions: $.noop,
565 _getCreateEventData: $.noop,
569 destroy: function() {
571 // we can probably remove the unbind calls in 2.0
572 // all event bindings should go through this._on()
574 .unbind( this.eventNamespace )
576 // TODO remove dual storage
577 .removeData( this.widgetName )
578 .removeData( this.widgetFullName )
579 // support: jquery <1.6.3
580 // http://bugs.jquery.com/ticket/9413
581 .removeData( $.camelCase( this.widgetFullName ) );
583 .unbind( this.eventNamespace )
584 .removeAttr( "aria-disabled" )
586 this.widgetFullName + "-disabled " +
587 "ui-state-disabled" );
589 // clean up events and states
590 this.bindings.unbind( this.eventNamespace );
591 this.hoverable.removeClass( "ui-state-hover" );
592 this.focusable.removeClass( "ui-state-focus" );
600 option: function( key, value ) {
606 if ( arguments.length === 0 ) {
607 // don't return a reference to the internal hash
608 return $.widget.extend( {}, this.options );
611 if ( typeof key === "string" ) {
612 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
614 parts = key.split( "." );
616 if ( parts.length ) {
617 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
618 for ( i = 0; i < parts.length - 1; i++ ) {
619 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
620 curOption = curOption[ parts[ i ] ];
623 if ( value === undefined ) {
624 return curOption[ key ] === undefined ? null : curOption[ key ];
626 curOption[ key ] = value;
628 if ( value === undefined ) {
629 return this.options[ key ] === undefined ? null : this.options[ key ];
631 options[ key ] = value;
635 this._setOptions( options );
639 _setOptions: function( options ) {
642 for ( key in options ) {
643 this._setOption( key, options[ key ] );
648 _setOption: function( key, value ) {
649 this.options[ key ] = value;
651 if ( key === "disabled" ) {
653 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
654 .attr( "aria-disabled", value );
655 this.hoverable.removeClass( "ui-state-hover" );
656 this.focusable.removeClass( "ui-state-focus" );
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck, element, handlers ) {
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck !== "boolean" ) {
676 element = suppressDisabledCheck;
677 suppressDisabledCheck = false;
680 // no element argument, shuffle and use this.element
683 element = this.element;
684 delegateElement = this.widget();
686 // accept selectors, DOM elements
687 element = delegateElement = $( element );
688 this.bindings = this.bindings.add( element );
691 $.each( handlers, function( event, handler ) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck &&
697 ( instance.options.disabled === true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
701 return ( typeof handler === "string" ? instance[ handler ] : handler )
702 .apply( instance, arguments );
705 // copy the guid so direct unbinding works
706 if ( typeof handler !== "string" ) {
707 handlerProxy.guid = handler.guid =
708 handler.guid || handlerProxy.guid || $.guid++;
711 var match = event.match( /^(\w+)\s*(.*)$/ ),
712 eventName = match[1] + instance.eventNamespace,
715 delegateElement.delegate( selector, eventName, handlerProxy );
717 element.bind( eventName, handlerProxy );
722 _off: function( element, eventName ) {
723 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724 element.unbind( eventName ).undelegate( eventName );
727 _delay: function( handler, delay ) {
728 function handlerProxy() {
729 return ( typeof handler === "string" ? instance[ handler ] : handler )
730 .apply( instance, arguments );
733 return setTimeout( handlerProxy, delay || 0 );
736 _hoverable: function( element ) {
737 this.hoverable = this.hoverable.add( element );
739 mouseenter: function( event ) {
740 $( event.currentTarget ).addClass( "ui-state-hover" );
742 mouseleave: function( event ) {
743 $( event.currentTarget ).removeClass( "ui-state-hover" );
748 _focusable: function( element ) {
749 this.focusable = this.focusable.add( element );
751 focusin: function( event ) {
752 $( event.currentTarget ).addClass( "ui-state-focus" );
754 focusout: function( event ) {
755 $( event.currentTarget ).removeClass( "ui-state-focus" );
760 _trigger: function( type, event, data ) {
762 callback = this.options[ type ];
765 event = $.Event( event );
766 event.type = ( type === this.widgetEventPrefix ?
768 this.widgetEventPrefix + type ).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event.target = this.element[ 0 ];
773 // copy original event properties over to the new event
774 orig = event.originalEvent;
776 for ( prop in orig ) {
777 if ( !( prop in event ) ) {
778 event[ prop ] = orig[ prop ];
783 this.element.trigger( event, data );
784 return !( $.isFunction( callback ) &&
785 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
786 event.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792 if ( typeof options === "string" ) {
793 options = { effect: options };
796 effectName = !options ?
798 options === true || typeof options === "number" ?
800 options.effect || defaultEffect;
801 options = options || {};
802 if ( typeof options === "number" ) {
803 options = { duration: options };
805 hasOptions = !$.isEmptyObject( options );
806 options.complete = callback;
807 if ( options.delay ) {
808 element.delay( options.delay );
810 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811 element[ method ]( options );
812 } else if ( effectName !== method && element[ effectName ] ) {
813 element[ effectName ]( options.duration, options.easing, callback );
815 element.queue(function( next ) {
816 $( this )[ method ]();
818 callback.call( element[ 0 ] );
828 (function( $, undefined ) {
830 var mouseHandled = false;
831 $( document ).mouseup( function() {
832 mouseHandled = false;
835 $.widget("ui.mouse", {
838 cancel: "input,textarea,button,select,option",
842 _mouseInit: function() {
846 .bind("mousedown."+this.widgetName, function(event) {
847 return that._mouseDown(event);
849 .bind("click."+this.widgetName, function(event) {
850 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
851 $.removeData(event.target, that.widgetName + ".preventClickEvent");
852 event.stopImmediatePropagation();
857 this.started = false;
860 // TODO: make sure destroying one instance of mouse doesn't mess with
861 // other instances of mouse
862 _mouseDestroy: function() {
863 this.element.unbind("."+this.widgetName);
864 if ( this._mouseMoveDelegate ) {
866 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
867 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
871 _mouseDown: function(event) {
872 // don't let more than one widget handle mouseStart
873 if( mouseHandled ) { return; }
875 // we may have missed mouseup (out of window)
876 (this._mouseStarted && this._mouseUp(event));
878 this._mouseDownEvent = event;
881 btnIsLeft = (event.which === 1),
882 // event.target.nodeName works around a bug in IE 8 with
883 // disabled inputs (#7620)
884 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
885 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
889 this.mouseDelayMet = !this.options.delay;
890 if (!this.mouseDelayMet) {
891 this._mouseDelayTimer = setTimeout(function() {
892 that.mouseDelayMet = true;
893 }, this.options.delay);
896 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
897 this._mouseStarted = (this._mouseStart(event) !== false);
898 if (!this._mouseStarted) {
899 event.preventDefault();
904 // Click event may never have fired (Gecko & Opera)
905 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
906 $.removeData(event.target, this.widgetName + ".preventClickEvent");
909 // these delegates are required to keep context
910 this._mouseMoveDelegate = function(event) {
911 return that._mouseMove(event);
913 this._mouseUpDelegate = function(event) {
914 return that._mouseUp(event);
917 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
918 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
920 event.preventDefault();
926 _mouseMove: function(event) {
927 // IE mouseup check - mouseup happened when mouse was out of window
928 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
929 return this._mouseUp(event);
932 if (this._mouseStarted) {
933 this._mouseDrag(event);
934 return event.preventDefault();
937 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
939 (this._mouseStart(this._mouseDownEvent, event) !== false);
940 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
943 return !this._mouseStarted;
946 _mouseUp: function(event) {
948 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
949 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
951 if (this._mouseStarted) {
952 this._mouseStarted = false;
954 if (event.target === this._mouseDownEvent.target) {
955 $.data(event.target, this.widgetName + ".preventClickEvent", true);
958 this._mouseStop(event);
964 _mouseDistanceMet: function(event) {
966 Math.abs(this._mouseDownEvent.pageX - event.pageX),
967 Math.abs(this._mouseDownEvent.pageY - event.pageY)
968 ) >= this.options.distance
972 _mouseDelayMet: function(/* event */) {
973 return this.mouseDelayMet;
976 // These are placeholder methods, to be overriden by extending plugin
977 _mouseStart: function(/* event */) {},
978 _mouseDrag: function(/* event */) {},
979 _mouseStop: function(/* event */) {},
980 _mouseCapture: function(/* event */) { return true; }
985 (function( $, undefined ) {
987 $.widget("ui.draggable", $.ui.mouse, {
989 widgetEventPrefix: "drag",
994 connectToSortable: false,
1003 refreshPositions: false,
1005 revertDuration: 500,
1008 scrollSensitivity: 20,
1021 _create: function() {
1023 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1024 this.element[0].style.position = "relative";
1026 if (this.options.addClasses){
1027 this.element.addClass("ui-draggable");
1029 if (this.options.disabled){
1030 this.element.addClass("ui-draggable-disabled");
1037 _destroy: function() {
1038 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1039 this._mouseDestroy();
1042 _mouseCapture: function(event) {
1044 var o = this.options;
1046 // among others, prevent a drag on a resizable-handle
1047 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1051 //Quit if we're not on a valid handle
1052 this.handle = this._getHandle(event);
1057 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1058 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1060 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1061 position: "absolute", opacity: "0.001", zIndex: 1000
1063 .css($(this).offset())
1071 _mouseStart: function(event) {
1073 var o = this.options;
1075 //Create and append the visible helper
1076 this.helper = this._createHelper(event);
1078 this.helper.addClass("ui-draggable-dragging");
1080 //Cache the helper size
1081 this._cacheHelperProportions();
1083 //If ddmanager is used for droppables, set the global draggable
1084 if($.ui.ddmanager) {
1085 $.ui.ddmanager.current = this;
1089 * - Position generation -
1090 * This block generates everything position related - it's the core of draggables.
1093 //Cache the margins of the original element
1094 this._cacheMargins();
1096 //Store the helper's css position
1097 this.cssPosition = this.helper.css( "position" );
1098 this.scrollParent = this.helper.scrollParent();
1099 this.offsetParent = this.helper.offsetParent();
1100 this.offsetParentCssPosition = this.offsetParent.css( "position" );
1102 //The element's absolute position on the page minus margins
1103 this.offset = this.positionAbs = this.element.offset();
1105 top: this.offset.top - this.margins.top,
1106 left: this.offset.left - this.margins.left
1109 //Reset scroll cache
1110 this.offset.scroll = false;
1112 $.extend(this.offset, {
1113 click: { //Where the click happened, relative to the element
1114 left: event.pageX - this.offset.left,
1115 top: event.pageY - this.offset.top
1117 parent: this._getParentOffset(),
1118 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1121 //Generate the original position
1122 this.originalPosition = this.position = this._generatePosition(event);
1123 this.originalPageX = event.pageX;
1124 this.originalPageY = event.pageY;
1126 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1127 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1129 //Set a containment if given in the options
1130 this._setContainment();
1132 //Trigger event + callbacks
1133 if(this._trigger("start", event) === false) {
1138 //Recache the helper size
1139 this._cacheHelperProportions();
1141 //Prepare the droppable offsets
1142 if ($.ui.ddmanager && !o.dropBehaviour) {
1143 $.ui.ddmanager.prepareOffsets(this, event);
1147 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1149 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1150 if ( $.ui.ddmanager ) {
1151 $.ui.ddmanager.dragStart(this, event);
1157 _mouseDrag: function(event, noPropagation) {
1158 // reset any necessary cached properties (see #5009)
1159 if ( this.offsetParentCssPosition === "fixed" ) {
1160 this.offset.parent = this._getParentOffset();
1163 //Compute the helpers position
1164 this.position = this._generatePosition(event);
1165 this.positionAbs = this._convertPositionTo("absolute");
1167 //Call plugins and callbacks and use the resulting position if something is returned
1168 if (!noPropagation) {
1169 var ui = this._uiHash();
1170 if(this._trigger("drag", event, ui) === false) {
1174 this.position = ui.position;
1177 if(!this.options.axis || this.options.axis !== "y") {
1178 this.helper[0].style.left = this.position.left+"px";
1180 if(!this.options.axis || this.options.axis !== "x") {
1181 this.helper[0].style.top = this.position.top+"px";
1183 if($.ui.ddmanager) {
1184 $.ui.ddmanager.drag(this, event);
1190 _mouseStop: function(event) {
1192 //If we are using droppables, inform the manager about the drop
1195 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1196 dropped = $.ui.ddmanager.drop(this, event);
1199 //if a drop comes from outside (a sortable)
1201 dropped = this.dropped;
1202 this.dropped = false;
1205 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1206 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
1210 if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1211 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1212 if(that._trigger("stop", event) !== false) {
1217 if(this._trigger("stop", event) !== false) {
1225 _mouseUp: function(event) {
1226 //Remove frame helpers
1227 $("div.ui-draggable-iframeFix").each(function() {
1228 this.parentNode.removeChild(this);
1231 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1232 if( $.ui.ddmanager ) {
1233 $.ui.ddmanager.dragStop(this, event);
1236 return $.ui.mouse.prototype._mouseUp.call(this, event);
1239 cancel: function() {
1241 if(this.helper.is(".ui-draggable-dragging")) {
1251 _getHandle: function(event) {
1252 return this.options.handle ?
1253 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1257 _createHelper: function(event) {
1259 var o = this.options,
1260 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1262 if(!helper.parents("body").length) {
1263 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1266 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1267 helper.css("position", "absolute");
1274 _adjustOffsetFromHelper: function(obj) {
1275 if (typeof obj === "string") {
1276 obj = obj.split(" ");
1278 if ($.isArray(obj)) {
1279 obj = {left: +obj[0], top: +obj[1] || 0};
1281 if ("left" in obj) {
1282 this.offset.click.left = obj.left + this.margins.left;
1284 if ("right" in obj) {
1285 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1288 this.offset.click.top = obj.top + this.margins.top;
1290 if ("bottom" in obj) {
1291 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1295 _getParentOffset: function() {
1297 //Get the offsetParent and cache its position
1298 var po = this.offsetParent.offset();
1300 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1301 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1302 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1303 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1304 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1305 po.left += this.scrollParent.scrollLeft();
1306 po.top += this.scrollParent.scrollTop();
1309 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1311 if((this.offsetParent[0] === document.body) ||
1312 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1313 po = { top: 0, left: 0 };
1317 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1318 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1323 _getRelativeOffset: function() {
1325 if(this.cssPosition === "relative") {
1326 var p = this.element.position();
1328 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1329 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1332 return { top: 0, left: 0 };
1337 _cacheMargins: function() {
1339 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1340 top: (parseInt(this.element.css("marginTop"),10) || 0),
1341 right: (parseInt(this.element.css("marginRight"),10) || 0),
1342 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1346 _cacheHelperProportions: function() {
1347 this.helperProportions = {
1348 width: this.helper.outerWidth(),
1349 height: this.helper.outerHeight()
1353 _setContainment: function() {
1358 if ( !o.containment ) {
1359 this.containment = null;
1363 if ( o.containment === "window" ) {
1364 this.containment = [
1365 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1366 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1367 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
1368 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1373 if ( o.containment === "document") {
1374 this.containment = [
1377 $( document ).width() - this.helperProportions.width - this.margins.left,
1378 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1383 if ( o.containment.constructor === Array ) {
1384 this.containment = o.containment;
1388 if ( o.containment === "parent" ) {
1389 o.containment = this.helper[ 0 ].parentNode;
1392 c = $( o.containment );
1399 over = c.css( "overflow" ) !== "hidden";
1401 this.containment = [
1402 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
1403 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
1404 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
1405 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
1407 this.relative_container = c;
1410 _convertPositionTo: function(d, pos) {
1413 pos = this.position;
1416 var mod = d === "absolute" ? 1 : -1,
1417 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
1420 if (!this.offset.scroll) {
1421 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1426 pos.top + // The absolute mouse position
1427 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1428 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1429 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
1432 pos.left + // The absolute mouse position
1433 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1434 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1435 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
1441 _generatePosition: function(event) {
1443 var containment, co, top, left,
1445 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
1446 pageX = event.pageX,
1447 pageY = event.pageY;
1450 if (!this.offset.scroll) {
1451 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1455 * - Position constraining -
1456 * Constrain the position to a mix of grid, containment.
1459 // If we are not dragging yet, we won't check for options
1460 if ( this.originalPosition ) {
1461 if ( this.containment ) {
1462 if ( this.relative_container ){
1463 co = this.relative_container.offset();
1465 this.containment[ 0 ] + co.left,
1466 this.containment[ 1 ] + co.top,
1467 this.containment[ 2 ] + co.left,
1468 this.containment[ 3 ] + co.top
1472 containment = this.containment;
1475 if(event.pageX - this.offset.click.left < containment[0]) {
1476 pageX = containment[0] + this.offset.click.left;
1478 if(event.pageY - this.offset.click.top < containment[1]) {
1479 pageY = containment[1] + this.offset.click.top;
1481 if(event.pageX - this.offset.click.left > containment[2]) {
1482 pageX = containment[2] + this.offset.click.left;
1484 if(event.pageY - this.offset.click.top > containment[3]) {
1485 pageY = containment[3] + this.offset.click.top;
1490 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1491 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1492 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1494 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1495 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1502 pageY - // The absolute mouse position
1503 this.offset.click.top - // Click offset (relative to the element)
1504 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1505 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1506 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
1509 pageX - // The absolute mouse position
1510 this.offset.click.left - // Click offset (relative to the element)
1511 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1512 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1513 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
1519 _clear: function() {
1520 this.helper.removeClass("ui-draggable-dragging");
1521 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1522 this.helper.remove();
1525 this.cancelHelperRemoval = false;
1528 // From now on bulk stuff - mainly helpers
1530 _trigger: function(type, event, ui) {
1531 ui = ui || this._uiHash();
1532 $.ui.plugin.call(this, type, [event, ui]);
1533 //The absolute position has to be recalculated after plugins
1534 if(type === "drag") {
1535 this.positionAbs = this._convertPositionTo("absolute");
1537 return $.Widget.prototype._trigger.call(this, type, event, ui);
1542 _uiHash: function() {
1544 helper: this.helper,
1545 position: this.position,
1546 originalPosition: this.originalPosition,
1547 offset: this.positionAbs
1553 $.ui.plugin.add("draggable", "connectToSortable", {
1554 start: function(event, ui) {
1556 var inst = $(this).data("ui-draggable"), o = inst.options,
1557 uiSortable = $.extend({}, ui, { item: inst.element });
1558 inst.sortables = [];
1559 $(o.connectToSortable).each(function() {
1560 var sortable = $.data(this, "ui-sortable");
1561 if (sortable && !sortable.options.disabled) {
1562 inst.sortables.push({
1564 shouldRevert: sortable.options.revert
1566 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
1567 sortable._trigger("activate", event, uiSortable);
1572 stop: function(event, ui) {
1574 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1575 var inst = $(this).data("ui-draggable"),
1576 uiSortable = $.extend({}, ui, { item: inst.element });
1578 $.each(inst.sortables, function() {
1579 if(this.instance.isOver) {
1581 this.instance.isOver = 0;
1583 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1584 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1586 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
1587 if(this.shouldRevert) {
1588 this.instance.options.revert = this.shouldRevert;
1591 //Trigger the stop of the sortable
1592 this.instance._mouseStop(event);
1594 this.instance.options.helper = this.instance.options._helper;
1596 //If the helper has been the original item, restore properties in the sortable
1597 if(inst.options.helper === "original") {
1598 this.instance.currentItem.css({ top: "auto", left: "auto" });
1602 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1603 this.instance._trigger("deactivate", event, uiSortable);
1609 drag: function(event, ui) {
1611 var inst = $(this).data("ui-draggable"), that = this;
1613 $.each(inst.sortables, function() {
1615 var innermostIntersecting = false,
1616 thisSortable = this;
1618 //Copy over some variables to allow calling the sortable's native _intersectsWith
1619 this.instance.positionAbs = inst.positionAbs;
1620 this.instance.helperProportions = inst.helperProportions;
1621 this.instance.offset.click = inst.offset.click;
1623 if(this.instance._intersectsWith(this.instance.containerCache)) {
1624 innermostIntersecting = true;
1625 $.each(inst.sortables, function () {
1626 this.instance.positionAbs = inst.positionAbs;
1627 this.instance.helperProportions = inst.helperProportions;
1628 this.instance.offset.click = inst.offset.click;
1629 if (this !== thisSortable &&
1630 this.instance._intersectsWith(this.instance.containerCache) &&
1631 $.contains(thisSortable.instance.element[0], this.instance.element[0])
1633 innermostIntersecting = false;
1635 return innermostIntersecting;
1640 if(innermostIntersecting) {
1641 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1642 if(!this.instance.isOver) {
1644 this.instance.isOver = 1;
1645 //Now we fake the start of dragging for the sortable instance,
1646 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1647 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
1648 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
1649 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1650 this.instance.options.helper = function() { return ui.helper[0]; };
1652 event.target = this.instance.currentItem[0];
1653 this.instance._mouseCapture(event, true);
1654 this.instance._mouseStart(event, true, true);
1656 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1657 this.instance.offset.click.top = inst.offset.click.top;
1658 this.instance.offset.click.left = inst.offset.click.left;
1659 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1660 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1662 inst._trigger("toSortable", event);
1663 inst.dropped = this.instance.element; //draggable revert needs that
1664 //hack so receive/update callbacks work (mostly)
1665 inst.currentItem = inst.element;
1666 this.instance.fromOutside = inst;
1670 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1671 if(this.instance.currentItem) {
1672 this.instance._mouseDrag(event);
1677 //If it doesn't intersect with the sortable, and it intersected before,
1678 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1679 if(this.instance.isOver) {
1681 this.instance.isOver = 0;
1682 this.instance.cancelHelperRemoval = true;
1684 //Prevent reverting on this forced stop
1685 this.instance.options.revert = false;
1687 // The out event needs to be triggered independently
1688 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
1690 this.instance._mouseStop(event, true);
1691 this.instance.options.helper = this.instance.options._helper;
1693 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1694 this.instance.currentItem.remove();
1695 if(this.instance.placeholder) {
1696 this.instance.placeholder.remove();
1699 inst._trigger("fromSortable", event);
1700 inst.dropped = false; //draggable revert needs that
1710 $.ui.plugin.add("draggable", "cursor", {
1712 var t = $("body"), o = $(this).data("ui-draggable").options;
1713 if (t.css("cursor")) {
1714 o._cursor = t.css("cursor");
1716 t.css("cursor", o.cursor);
1719 var o = $(this).data("ui-draggable").options;
1721 $("body").css("cursor", o._cursor);
1726 $.ui.plugin.add("draggable", "opacity", {
1727 start: function(event, ui) {
1728 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1729 if(t.css("opacity")) {
1730 o._opacity = t.css("opacity");
1732 t.css("opacity", o.opacity);
1734 stop: function(event, ui) {
1735 var o = $(this).data("ui-draggable").options;
1737 $(ui.helper).css("opacity", o._opacity);
1742 $.ui.plugin.add("draggable", "scroll", {
1744 var i = $(this).data("ui-draggable");
1745 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1746 i.overflowOffset = i.scrollParent.offset();
1749 drag: function( event ) {
1751 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
1753 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1755 if(!o.axis || o.axis !== "x") {
1756 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
1757 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1758 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
1759 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1763 if(!o.axis || o.axis !== "y") {
1764 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
1765 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1766 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
1767 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1773 if(!o.axis || o.axis !== "x") {
1774 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
1775 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1776 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
1777 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1781 if(!o.axis || o.axis !== "y") {
1782 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
1783 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1784 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
1785 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1791 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
1792 $.ui.ddmanager.prepareOffsets(i, event);
1798 $.ui.plugin.add("draggable", "snap", {
1801 var i = $(this).data("ui-draggable"),
1804 i.snapElements = [];
1806 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
1809 if(this !== i.element[0]) {
1810 i.snapElements.push({
1812 width: $t.outerWidth(), height: $t.outerHeight(),
1813 top: $o.top, left: $o.left
1819 drag: function(event, ui) {
1821 var ts, bs, ls, rs, l, r, t, b, i, first,
1822 inst = $(this).data("ui-draggable"),
1824 d = o.snapTolerance,
1825 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1826 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1828 for (i = inst.snapElements.length - 1; i >= 0; i--){
1830 l = inst.snapElements[i].left;
1831 r = l + inst.snapElements[i].width;
1832 t = inst.snapElements[i].top;
1833 b = t + inst.snapElements[i].height;
1835 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
1836 if(inst.snapElements[i].snapping) {
1837 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1839 inst.snapElements[i].snapping = false;
1843 if(o.snapMode !== "inner") {
1844 ts = Math.abs(t - y2) <= d;
1845 bs = Math.abs(b - y1) <= d;
1846 ls = Math.abs(l - x2) <= d;
1847 rs = Math.abs(r - x1) <= d;
1849 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1852 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1855 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1858 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1862 first = (ts || bs || ls || rs);
1864 if(o.snapMode !== "outer") {
1865 ts = Math.abs(t - y1) <= d;
1866 bs = Math.abs(b - y2) <= d;
1867 ls = Math.abs(l - x1) <= d;
1868 rs = Math.abs(r - x2) <= d;
1870 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1873 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1876 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1879 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1883 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
1884 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1886 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1893 $.ui.plugin.add("draggable", "stack", {
1896 o = this.data("ui-draggable").options,
1897 group = $.makeArray($(o.stack)).sort(function(a,b) {
1898 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1901 if (!group.length) { return; }
1903 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
1904 $(group).each(function(i) {
1905 $(this).css("zIndex", min + i);
1907 this.css("zIndex", (min + group.length));
1911 $.ui.plugin.add("draggable", "zIndex", {
1912 start: function(event, ui) {
1913 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1914 if(t.css("zIndex")) {
1915 o._zIndex = t.css("zIndex");
1917 t.css("zIndex", o.zIndex);
1919 stop: function(event, ui) {
1920 var o = $(this).data("ui-draggable").options;
1922 $(ui.helper).css("zIndex", o._zIndex);
1929 (function( $, undefined ) {
1931 function isOverAxis( x, reference, size ) {
1932 return ( x > reference ) && ( x < ( reference + size ) );
1935 $.widget("ui.droppable", {
1937 widgetEventPrefix: "drop",
1945 tolerance: "intersect",
1954 _create: function() {
1956 var o = this.options,
1959 this.isover = false;
1962 this.accept = $.isFunction(accept) ? accept : function(d) {
1963 return d.is(accept);
1966 //Store the droppable's proportions
1967 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1969 // Add the reference and positions to the manager
1970 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1971 $.ui.ddmanager.droppables[o.scope].push(this);
1973 (o.addClasses && this.element.addClass("ui-droppable"));
1977 _destroy: function() {
1979 drop = $.ui.ddmanager.droppables[this.options.scope];
1981 for ( ; i < drop.length; i++ ) {
1982 if ( drop[i] === this ) {
1987 this.element.removeClass("ui-droppable ui-droppable-disabled");
1990 _setOption: function(key, value) {
1992 if(key === "accept") {
1993 this.accept = $.isFunction(value) ? value : function(d) {
1997 $.Widget.prototype._setOption.apply(this, arguments);
2000 _activate: function(event) {
2001 var draggable = $.ui.ddmanager.current;
2002 if(this.options.activeClass) {
2003 this.element.addClass(this.options.activeClass);
2006 this._trigger("activate", event, this.ui(draggable));
2010 _deactivate: function(event) {
2011 var draggable = $.ui.ddmanager.current;
2012 if(this.options.activeClass) {
2013 this.element.removeClass(this.options.activeClass);
2016 this._trigger("deactivate", event, this.ui(draggable));
2020 _over: function(event) {
2022 var draggable = $.ui.ddmanager.current;
2024 // Bail if draggable and droppable are same element
2025 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2029 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2030 if(this.options.hoverClass) {
2031 this.element.addClass(this.options.hoverClass);
2033 this._trigger("over", event, this.ui(draggable));
2038 _out: function(event) {
2040 var draggable = $.ui.ddmanager.current;
2042 // Bail if draggable and droppable are same element
2043 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2047 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2048 if(this.options.hoverClass) {
2049 this.element.removeClass(this.options.hoverClass);
2051 this._trigger("out", event, this.ui(draggable));
2056 _drop: function(event,custom) {
2058 var draggable = custom || $.ui.ddmanager.current,
2059 childrenIntersection = false;
2061 // Bail if draggable and droppable are same element
2062 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2066 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2067 var inst = $.data(this, "ui-droppable");
2069 inst.options.greedy &&
2070 !inst.options.disabled &&
2071 inst.options.scope === draggable.options.scope &&
2072 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2073 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2074 ) { childrenIntersection = true; return false; }
2076 if(childrenIntersection) {
2080 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2081 if(this.options.activeClass) {
2082 this.element.removeClass(this.options.activeClass);
2084 if(this.options.hoverClass) {
2085 this.element.removeClass(this.options.hoverClass);
2087 this._trigger("drop", event, this.ui(draggable));
2088 return this.element;
2097 draggable: (c.currentItem || c.element),
2099 position: c.position,
2100 offset: c.positionAbs
2106 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2108 if (!droppable.offset) {
2112 var draggableLeft, draggableTop,
2113 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2114 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2115 l = droppable.offset.left, r = l + droppable.proportions.width,
2116 t = droppable.offset.top, b = t + droppable.proportions.height;
2118 switch (toleranceMode) {
2120 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2122 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2123 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2124 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2125 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2127 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2128 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2129 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2132 (y1 >= t && y1 <= b) || // Top edge touching
2133 (y2 >= t && y2 <= b) || // Bottom edge touching
2134 (y1 < t && y2 > b) // Surrounded vertically
2136 (x1 >= l && x1 <= r) || // Left edge touching
2137 (x2 >= l && x2 <= r) || // Right edge touching
2138 (x1 < l && x2 > r) // Surrounded horizontally
2147 This manager tracks offsets of draggables and droppables
2151 droppables: { "default": [] },
2152 prepareOffsets: function(t, event) {
2155 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2156 type = event ? event.type : null, // workaround for #2317
2157 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2159 droppablesLoop: for (i = 0; i < m.length; i++) {
2161 //No disabled and non-accepted
2162 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2166 // Filter out elements in the current dragged item
2167 for (j=0; j < list.length; j++) {
2168 if(list[j] === m[i].element[0]) {
2169 m[i].proportions.height = 0;
2170 continue droppablesLoop;
2174 m[i].visible = m[i].element.css("display") !== "none";
2179 //Activate the droppable if used directly from draggables
2180 if(type === "mousedown") {
2181 m[i]._activate.call(m[i], event);
2184 m[i].offset = m[i].element.offset();
2185 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2190 drop: function(draggable, event) {
2192 var dropped = false;
2193 // Create a copy of the droppables in case the list changes during the drop (#9116)
2194 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2199 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2200 dropped = this._drop.call(this, event) || dropped;
2203 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2205 this.isover = false;
2206 this._deactivate.call(this, event);
2213 dragStart: function( draggable, event ) {
2214 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2215 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2216 if( !draggable.options.refreshPositions ) {
2217 $.ui.ddmanager.prepareOffsets( draggable, event );
2221 drag: function(draggable, event) {
2223 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2224 if(draggable.options.refreshPositions) {
2225 $.ui.ddmanager.prepareOffsets(draggable, event);
2228 //Run through all droppables and check their positions based on specific tolerance options
2229 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2231 if(this.options.disabled || this.greedyChild || !this.visible) {
2235 var parentInstance, scope, parent,
2236 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2237 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2242 if (this.options.greedy) {
2243 // find droppable parents with same scope
2244 scope = this.options.scope;
2245 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2246 return $.data(this, "ui-droppable").options.scope === scope;
2249 if (parent.length) {
2250 parentInstance = $.data(parent[0], "ui-droppable");
2251 parentInstance.greedyChild = (c === "isover");
2255 // we just moved into a greedy child
2256 if (parentInstance && c === "isover") {
2257 parentInstance.isover = false;
2258 parentInstance.isout = true;
2259 parentInstance._out.call(parentInstance, event);
2263 this[c === "isout" ? "isover" : "isout"] = false;
2264 this[c === "isover" ? "_over" : "_out"].call(this, event);
2266 // we just moved out of a greedy child
2267 if (parentInstance && c === "isout") {
2268 parentInstance.isout = false;
2269 parentInstance.isover = true;
2270 parentInstance._over.call(parentInstance, event);
2275 dragStop: function( draggable, event ) {
2276 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2277 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2278 if( !draggable.options.refreshPositions ) {
2279 $.ui.ddmanager.prepareOffsets( draggable, event );
2286 (function( $, undefined ) {
2289 return parseInt(v, 10) || 0;
2292 function isNumber(value) {
2293 return !isNaN(parseInt(value, 10));
2296 $.widget("ui.resizable", $.ui.mouse, {
2298 widgetEventPrefix: "resize",
2302 animateDuration: "slow",
2303 animateEasing: "swing",
2323 _create: function() {
2325 var n, i, handle, axis, hname,
2328 this.element.addClass("ui-resizable");
2331 _aspectRatio: !!(o.aspectRatio),
2332 aspectRatio: o.aspectRatio,
2333 originalElement: this.element,
2334 _proportionallyResizeElements: [],
2335 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2338 //Wrap the element if it cannot hold child nodes
2339 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2341 //Create a wrapper element and set the wrapper to the new current internal element
2343 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2344 position: this.element.css("position"),
2345 width: this.element.outerWidth(),
2346 height: this.element.outerHeight(),
2347 top: this.element.css("top"),
2348 left: this.element.css("left")
2352 //Overwrite the original this.element
2353 this.element = this.element.parent().data(
2354 "ui-resizable", this.element.data("ui-resizable")
2357 this.elementIsWrapper = true;
2359 //Move margins to the wrapper
2360 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2361 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2363 //Prevent Safari textarea resize
2364 this.originalResizeStyle = this.originalElement.css("resize");
2365 this.originalElement.css("resize", "none");
2367 //Push the actual element to our proportionallyResize internal array
2368 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2370 // avoid IE jump (hard set the margin)
2371 this.originalElement.css({ margin: this.originalElement.css("margin") });
2373 // fix handlers offset
2374 this._proportionallyResize();
2378 this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
2379 if(this.handles.constructor === String) {
2381 if ( this.handles === "all") {
2382 this.handles = "n,e,s,w,se,sw,ne,nw";
2385 n = this.handles.split(",");
2388 for(i = 0; i < n.length; i++) {
2390 handle = $.trim(n[i]);
2391 hname = "ui-resizable-"+handle;
2392 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2394 // Apply zIndex to all handles - see #7960
2395 axis.css({ zIndex: o.zIndex });
2397 //TODO : What's going on here?
2398 if ("se" === handle) {
2399 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2402 //Insert into internal handles object and append to element
2403 this.handles[handle] = ".ui-resizable-"+handle;
2404 this.element.append(axis);
2409 this._renderAxis = function(target) {
2411 var i, axis, padPos, padWrapper;
2413 target = target || this.element;
2415 for(i in this.handles) {
2417 if(this.handles[i].constructor === String) {
2418 this.handles[i] = $(this.handles[i], this.element).show();
2421 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2422 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2424 axis = $(this.handles[i], this.element);
2426 //Checking the correct pad and border
2427 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2429 //The padding type i have to apply...
2430 padPos = [ "padding",
2431 /ne|nw|n/.test(i) ? "Top" :
2432 /se|sw|s/.test(i) ? "Bottom" :
2433 /^e$/.test(i) ? "Right" : "Left" ].join("");
2435 target.css(padPos, padWrapper);
2437 this._proportionallyResize();
2441 //TODO: What's that good for? There's not anything to be executed left
2442 if(!$(this.handles[i]).length) {
2448 //TODO: make renderAxis a prototype function
2449 this._renderAxis(this.element);
2451 this._handles = $(".ui-resizable-handle", this.element)
2452 .disableSelection();
2454 //Matching axis name
2455 this._handles.mouseover(function() {
2456 if (!that.resizing) {
2457 if (this.className) {
2458 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2460 //Axis, default = se
2461 that.axis = axis && axis[1] ? axis[1] : "se";
2465 //If we want to auto hide the elements
2467 this._handles.hide();
2469 .addClass("ui-resizable-autohide")
2470 .mouseenter(function() {
2474 $(this).removeClass("ui-resizable-autohide");
2475 that._handles.show();
2477 .mouseleave(function(){
2481 if (!that.resizing) {
2482 $(this).addClass("ui-resizable-autohide");
2483 that._handles.hide();
2488 //Initialize the mouse interaction
2493 _destroy: function() {
2495 this._mouseDestroy();
2498 _destroy = function(exp) {
2499 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2500 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2503 //TODO: Unwrap at same DOM position
2504 if (this.elementIsWrapper) {
2505 _destroy(this.element);
2506 wrapper = this.element;
2507 this.originalElement.css({
2508 position: wrapper.css("position"),
2509 width: wrapper.outerWidth(),
2510 height: wrapper.outerHeight(),
2511 top: wrapper.css("top"),
2512 left: wrapper.css("left")
2513 }).insertAfter( wrapper );
2517 this.originalElement.css("resize", this.originalResizeStyle);
2518 _destroy(this.originalElement);
2523 _mouseCapture: function(event) {
2527 for (i in this.handles) {
2528 handle = $(this.handles[i])[0];
2529 if (handle === event.target || $.contains(handle, event.target)) {
2534 return !this.options.disabled && capture;
2537 _mouseStart: function(event) {
2539 var curleft, curtop, cursor,
2541 iniPos = this.element.position(),
2544 this.resizing = true;
2546 // bugfix for http://dev.jquery.com/ticket/1749
2547 if ( (/absolute/).test( el.css("position") ) ) {
2548 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
2549 } else if (el.is(".ui-draggable")) {
2550 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
2553 this._renderProxy();
2555 curleft = num(this.helper.css("left"));
2556 curtop = num(this.helper.css("top"));
2558 if (o.containment) {
2559 curleft += $(o.containment).scrollLeft() || 0;
2560 curtop += $(o.containment).scrollTop() || 0;
2563 //Store needed variables
2564 this.offset = this.helper.offset();
2565 this.position = { left: curleft, top: curtop };
2566 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2567 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2568 this.originalPosition = { left: curleft, top: curtop };
2569 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2570 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2573 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2575 cursor = $(".ui-resizable-" + this.axis).css("cursor");
2576 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
2578 el.addClass("ui-resizable-resizing");
2579 this._propagate("start", event);
2583 _mouseDrag: function(event) {
2585 //Increase performance, avoid regex
2587 el = this.helper, props = {},
2588 smp = this.originalMousePosition,
2590 prevTop = this.position.top,
2591 prevLeft = this.position.left,
2592 prevWidth = this.size.width,
2593 prevHeight = this.size.height,
2594 dx = (event.pageX-smp.left)||0,
2595 dy = (event.pageY-smp.top)||0,
2596 trigger = this._change[a];
2602 // Calculate the attrs that will be change
2603 data = trigger.apply(this, [event, dx, dy]);
2605 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2606 this._updateVirtualBoundaries(event.shiftKey);
2607 if (this._aspectRatio || event.shiftKey) {
2608 data = this._updateRatio(data, event);
2611 data = this._respectSize(data, event);
2613 this._updateCache(data);
2615 // plugins callbacks need to be called first
2616 this._propagate("resize", event);
2618 if (this.position.top !== prevTop) {
2619 props.top = this.position.top + "px";
2621 if (this.position.left !== prevLeft) {
2622 props.left = this.position.left + "px";
2624 if (this.size.width !== prevWidth) {
2625 props.width = this.size.width + "px";
2627 if (this.size.height !== prevHeight) {
2628 props.height = this.size.height + "px";
2632 if (!this._helper && this._proportionallyResizeElements.length) {
2633 this._proportionallyResize();
2636 // Call the user callback if the element was resized
2637 if ( ! $.isEmptyObject(props) ) {
2638 this._trigger("resize", event, this.ui());
2644 _mouseStop: function(event) {
2646 this.resizing = false;
2647 var pr, ista, soffseth, soffsetw, s, left, top,
2648 o = this.options, that = this;
2652 pr = this._proportionallyResizeElements;
2653 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
2654 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
2655 soffsetw = ista ? 0 : that.sizeDiff.width;
2657 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
2658 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
2659 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2662 this.element.css($.extend(s, { top: top, left: left }));
2665 that.helper.height(that.size.height);
2666 that.helper.width(that.size.width);
2668 if (this._helper && !o.animate) {
2669 this._proportionallyResize();
2673 $("body").css("cursor", "auto");
2675 this.element.removeClass("ui-resizable-resizing");
2677 this._propagate("stop", event);
2680 this.helper.remove();
2687 _updateVirtualBoundaries: function(forceAspectRatio) {
2688 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
2692 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2693 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2694 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2695 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2698 if(this._aspectRatio || forceAspectRatio) {
2699 // We want to create an enclosing box whose aspect ration is the requested one
2700 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2701 pMinWidth = b.minHeight * this.aspectRatio;
2702 pMinHeight = b.minWidth / this.aspectRatio;
2703 pMaxWidth = b.maxHeight * this.aspectRatio;
2704 pMaxHeight = b.maxWidth / this.aspectRatio;
2706 if(pMinWidth > b.minWidth) {
2707 b.minWidth = pMinWidth;
2709 if(pMinHeight > b.minHeight) {
2710 b.minHeight = pMinHeight;
2712 if(pMaxWidth < b.maxWidth) {
2713 b.maxWidth = pMaxWidth;
2715 if(pMaxHeight < b.maxHeight) {
2716 b.maxHeight = pMaxHeight;
2719 this._vBoundaries = b;
2722 _updateCache: function(data) {
2723 this.offset = this.helper.offset();
2724 if (isNumber(data.left)) {
2725 this.position.left = data.left;
2727 if (isNumber(data.top)) {
2728 this.position.top = data.top;
2730 if (isNumber(data.height)) {
2731 this.size.height = data.height;
2733 if (isNumber(data.width)) {
2734 this.size.width = data.width;
2738 _updateRatio: function( data ) {
2740 var cpos = this.position,
2744 if (isNumber(data.height)) {
2745 data.width = (data.height * this.aspectRatio);
2746 } else if (isNumber(data.width)) {
2747 data.height = (data.width / this.aspectRatio);
2751 data.left = cpos.left + (csize.width - data.width);
2755 data.top = cpos.top + (csize.height - data.height);
2756 data.left = cpos.left + (csize.width - data.width);
2762 _respectSize: function( data ) {
2764 var o = this._vBoundaries,
2766 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2767 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
2768 dw = this.originalPosition.left + this.originalSize.width,
2769 dh = this.position.top + this.size.height,
2770 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2772 data.width = o.minWidth;
2775 data.height = o.minHeight;
2778 data.width = o.maxWidth;
2781 data.height = o.maxHeight;
2785 data.left = dw - o.minWidth;
2788 data.left = dw - o.maxWidth;
2791 data.top = dh - o.minHeight;
2794 data.top = dh - o.maxHeight;
2797 // fixing jump error on top/left - bug #2330
2798 if (!data.width && !data.height && !data.left && data.top) {
2800 } else if (!data.width && !data.height && !data.top && data.left) {
2807 _proportionallyResize: function() {
2809 if (!this._proportionallyResizeElements.length) {
2813 var i, j, borders, paddings, prel,
2814 element = this.helper || this.element;
2816 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
2818 prel = this._proportionallyResizeElements[i];
2820 if (!this.borderDif) {
2821 this.borderDif = [];
2822 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
2823 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
2825 for ( j = 0; j < borders.length; j++ ) {
2826 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
2831 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2832 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2839 _renderProxy: function() {
2841 var el = this.element, o = this.options;
2842 this.elementOffset = el.offset();
2846 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
2848 this.helper.addClass(this._helper).css({
2849 width: this.element.outerWidth() - 1,
2850 height: this.element.outerHeight() - 1,
2851 position: "absolute",
2852 left: this.elementOffset.left +"px",
2853 top: this.elementOffset.top +"px",
2854 zIndex: ++o.zIndex //TODO: Don't modify option
2859 .disableSelection();
2862 this.helper = this.element;
2868 e: function(event, dx) {
2869 return { width: this.originalSize.width + dx };
2871 w: function(event, dx) {
2872 var cs = this.originalSize, sp = this.originalPosition;
2873 return { left: sp.left + dx, width: cs.width - dx };
2875 n: function(event, dx, dy) {
2876 var cs = this.originalSize, sp = this.originalPosition;
2877 return { top: sp.top + dy, height: cs.height - dy };
2879 s: function(event, dx, dy) {
2880 return { height: this.originalSize.height + dy };
2882 se: function(event, dx, dy) {
2883 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2885 sw: function(event, dx, dy) {
2886 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2888 ne: function(event, dx, dy) {
2889 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2891 nw: function(event, dx, dy) {
2892 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2896 _propagate: function(n, event) {
2897 $.ui.plugin.call(this, n, [event, this.ui()]);
2898 (n !== "resize" && this._trigger(n, event, this.ui()));
2905 originalElement: this.originalElement,
2906 element: this.element,
2907 helper: this.helper,
2908 position: this.position,
2910 originalSize: this.originalSize,
2911 originalPosition: this.originalPosition
2918 * Resizable Extensions
2921 $.ui.plugin.add("resizable", "animate", {
2923 stop: function( event ) {
2924 var that = $(this).data("ui-resizable"),
2926 pr = that._proportionallyResizeElements,
2927 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2928 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2929 soffsetw = ista ? 0 : that.sizeDiff.width,
2930 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
2931 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
2932 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2934 that.element.animate(
2935 $.extend(style, top && left ? { top: top, left: left } : {}), {
2936 duration: o.animateDuration,
2937 easing: o.animateEasing,
2941 width: parseInt(that.element.css("width"), 10),
2942 height: parseInt(that.element.css("height"), 10),
2943 top: parseInt(that.element.css("top"), 10),
2944 left: parseInt(that.element.css("left"), 10)
2947 if (pr && pr.length) {
2948 $(pr[0]).css({ width: data.width, height: data.height });
2951 // propagating resize, and updating values for each animation step
2952 that._updateCache(data);
2953 that._propagate("resize", event);
2962 $.ui.plugin.add("resizable", "containment", {
2965 var element, p, co, ch, cw, width, height,
2966 that = $(this).data("ui-resizable"),
2970 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2976 that.containerElement = $(ce);
2978 if (/document/.test(oc) || oc === document) {
2979 that.containerOffset = { left: 0, top: 0 };
2980 that.containerPosition = { left: 0, top: 0 };
2983 element: $(document), left: 0, top: 0,
2984 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2988 // i'm a node, so compute top, left, right, bottom
2992 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2994 that.containerOffset = element.offset();
2995 that.containerPosition = element.position();
2996 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2998 co = that.containerOffset;
2999 ch = that.containerSize.height;
3000 cw = that.containerSize.width;
3001 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
3002 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3005 element: ce, left: co.left, top: co.top, width: width, height: height
3010 resize: function( event ) {
3011 var woset, hoset, isParent, isOffsetRelative,
3012 that = $(this).data("ui-resizable"),
3014 co = that.containerOffset, cp = that.position,
3015 pRatio = that._aspectRatio || event.shiftKey,
3016 cop = { top:0, left:0 }, ce = that.containerElement;
3018 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
3022 if (cp.left < (that._helper ? co.left : 0)) {
3023 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3025 that.size.height = that.size.width / that.aspectRatio;
3027 that.position.left = o.helper ? co.left : 0;
3030 if (cp.top < (that._helper ? co.top : 0)) {
3031 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3033 that.size.width = that.size.height * that.aspectRatio;
3035 that.position.top = that._helper ? co.top : 0;
3038 that.offset.left = that.parentData.left+that.position.left;
3039 that.offset.top = that.parentData.top+that.position.top;
3041 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3042 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3044 isParent = that.containerElement.get(0) === that.element.parent().get(0);
3045 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3047 if(isParent && isOffsetRelative) {
3048 woset -= that.parentData.left;
3051 if (woset + that.size.width >= that.parentData.width) {
3052 that.size.width = that.parentData.width - woset;
3054 that.size.height = that.size.width / that.aspectRatio;
3058 if (hoset + that.size.height >= that.parentData.height) {
3059 that.size.height = that.parentData.height - hoset;
3061 that.size.width = that.size.height * that.aspectRatio;
3067 var that = $(this).data("ui-resizable"),
3069 co = that.containerOffset,
3070 cop = that.containerPosition,
3071 ce = that.containerElement,
3072 helper = $(that.helper),
3073 ho = helper.offset(),
3074 w = helper.outerWidth() - that.sizeDiff.width,
3075 h = helper.outerHeight() - that.sizeDiff.height;
3077 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3078 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3081 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3082 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3088 $.ui.plugin.add("resizable", "alsoResize", {
3090 start: function () {
3091 var that = $(this).data("ui-resizable"),
3093 _store = function (exp) {
3094 $(exp).each(function() {
3096 el.data("ui-resizable-alsoresize", {
3097 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3098 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3103 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3104 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3105 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3107 _store(o.alsoResize);
3111 resize: function (event, ui) {
3112 var that = $(this).data("ui-resizable"),
3114 os = that.originalSize,
3115 op = that.originalPosition,
3117 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3118 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3121 _alsoResize = function (exp, c) {
3122 $(exp).each(function() {
3123 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3124 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3126 $.each(css, function (i, prop) {
3127 var sum = (start[prop]||0) + (delta[prop]||0);
3128 if (sum && sum >= 0) {
3129 style[prop] = sum || null;
3137 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3138 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3140 _alsoResize(o.alsoResize);
3145 $(this).removeData("resizable-alsoresize");
3149 $.ui.plugin.add("resizable", "ghost", {
3153 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3155 that.ghost = that.originalElement.clone();
3157 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3158 .addClass("ui-resizable-ghost")
3159 .addClass(typeof o.ghost === "string" ? o.ghost : "");
3161 that.ghost.appendTo(that.helper);
3166 var that = $(this).data("ui-resizable");
3168 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3173 var that = $(this).data("ui-resizable");
3174 if (that.ghost && that.helper) {
3175 that.helper.get(0).removeChild(that.ghost.get(0));
3181 $.ui.plugin.add("resizable", "grid", {
3183 resize: function() {
3184 var that = $(this).data("ui-resizable"),
3187 os = that.originalSize,
3188 op = that.originalPosition,
3190 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3191 gridX = (grid[0]||1),
3192 gridY = (grid[1]||1),
3193 ox = Math.round((cs.width - os.width) / gridX) * gridX,
3194 oy = Math.round((cs.height - os.height) / gridY) * gridY,
3195 newWidth = os.width + ox,
3196 newHeight = os.height + oy,
3197 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3198 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3199 isMinWidth = o.minWidth && (o.minWidth > newWidth),
3200 isMinHeight = o.minHeight && (o.minHeight > newHeight);
3205 newWidth = newWidth + gridX;
3208 newHeight = newHeight + gridY;
3211 newWidth = newWidth - gridX;
3214 newHeight = newHeight - gridY;
3217 if (/^(se|s|e)$/.test(a)) {
3218 that.size.width = newWidth;
3219 that.size.height = newHeight;
3220 } else if (/^(ne)$/.test(a)) {
3221 that.size.width = newWidth;
3222 that.size.height = newHeight;
3223 that.position.top = op.top - oy;
3224 } else if (/^(sw)$/.test(a)) {
3225 that.size.width = newWidth;
3226 that.size.height = newHeight;
3227 that.position.left = op.left - ox;
3229 that.size.width = newWidth;
3230 that.size.height = newHeight;
3231 that.position.top = op.top - oy;
3232 that.position.left = op.left - ox;
3240 (function( $, undefined ) {
3242 $.widget("ui.selectable", $.ui.mouse, {
3259 _create: function() {
3263 this.element.addClass("ui-selectable");
3265 this.dragged = false;
3267 // cache selectee children based on filter
3268 this.refresh = function() {
3269 selectees = $(that.options.filter, that.element[0]);
3270 selectees.addClass("ui-selectee");
3271 selectees.each(function() {
3272 var $this = $(this),
3273 pos = $this.offset();
3274 $.data(this, "selectable-item", {
3279 right: pos.left + $this.outerWidth(),
3280 bottom: pos.top + $this.outerHeight(),
3281 startselected: false,
3282 selected: $this.hasClass("ui-selected"),
3283 selecting: $this.hasClass("ui-selecting"),
3284 unselecting: $this.hasClass("ui-unselecting")
3290 this.selectees = selectees.addClass("ui-selectee");
3294 this.helper = $("<div class='ui-selectable-helper'></div>");
3297 _destroy: function() {
3299 .removeClass("ui-selectee")
3300 .removeData("selectable-item");
3302 .removeClass("ui-selectable ui-selectable-disabled");
3303 this._mouseDestroy();
3306 _mouseStart: function(event) {
3308 options = this.options;
3310 this.opos = [event.pageX, event.pageY];
3312 if (this.options.disabled) {
3316 this.selectees = $(options.filter, this.element[0]);
3318 this._trigger("start", event);
3320 $(options.appendTo).append(this.helper);
3321 // position helper (lasso)
3323 "left": event.pageX,
3329 if (options.autoRefresh) {
3333 this.selectees.filter(".ui-selected").each(function() {
3334 var selectee = $.data(this, "selectable-item");
3335 selectee.startselected = true;
3336 if (!event.metaKey && !event.ctrlKey) {
3337 selectee.$element.removeClass("ui-selected");
3338 selectee.selected = false;
3339 selectee.$element.addClass("ui-unselecting");
3340 selectee.unselecting = true;
3341 // selectable UNSELECTING callback
3342 that._trigger("unselecting", event, {
3343 unselecting: selectee.element
3348 $(event.target).parents().addBack().each(function() {
3350 selectee = $.data(this, "selectable-item");
3352 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3354 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3355 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3356 selectee.unselecting = !doSelect;
3357 selectee.selecting = doSelect;
3358 selectee.selected = doSelect;
3359 // selectable (UN)SELECTING callback
3361 that._trigger("selecting", event, {
3362 selecting: selectee.element
3365 that._trigger("unselecting", event, {
3366 unselecting: selectee.element
3375 _mouseDrag: function(event) {
3377 this.dragged = true;
3379 if (this.options.disabled) {
3385 options = this.options,
3391 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3392 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
3393 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3395 this.selectees.each(function() {
3396 var selectee = $.data(this, "selectable-item"),
3399 //prevent helper from being selected if appendTo: selectable
3400 if (!selectee || selectee.element === that.element[0]) {
3404 if (options.tolerance === "touch") {
3405 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3406 } else if (options.tolerance === "fit") {
3407 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3412 if (selectee.selected) {
3413 selectee.$element.removeClass("ui-selected");
3414 selectee.selected = false;
3416 if (selectee.unselecting) {
3417 selectee.$element.removeClass("ui-unselecting");
3418 selectee.unselecting = false;
3420 if (!selectee.selecting) {
3421 selectee.$element.addClass("ui-selecting");
3422 selectee.selecting = true;
3423 // selectable SELECTING callback
3424 that._trigger("selecting", event, {
3425 selecting: selectee.element
3430 if (selectee.selecting) {
3431 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3432 selectee.$element.removeClass("ui-selecting");
3433 selectee.selecting = false;
3434 selectee.$element.addClass("ui-selected");
3435 selectee.selected = true;
3437 selectee.$element.removeClass("ui-selecting");
3438 selectee.selecting = false;
3439 if (selectee.startselected) {
3440 selectee.$element.addClass("ui-unselecting");
3441 selectee.unselecting = true;
3443 // selectable UNSELECTING callback
3444 that._trigger("unselecting", event, {
3445 unselecting: selectee.element
3449 if (selectee.selected) {
3450 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3451 selectee.$element.removeClass("ui-selected");
3452 selectee.selected = false;
3454 selectee.$element.addClass("ui-unselecting");
3455 selectee.unselecting = true;
3456 // selectable UNSELECTING callback
3457 that._trigger("unselecting", event, {
3458 unselecting: selectee.element
3468 _mouseStop: function(event) {
3471 this.dragged = false;
3473 $(".ui-unselecting", this.element[0]).each(function() {
3474 var selectee = $.data(this, "selectable-item");
3475 selectee.$element.removeClass("ui-unselecting");
3476 selectee.unselecting = false;
3477 selectee.startselected = false;
3478 that._trigger("unselected", event, {
3479 unselected: selectee.element
3482 $(".ui-selecting", this.element[0]).each(function() {
3483 var selectee = $.data(this, "selectable-item");
3484 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3485 selectee.selecting = false;
3486 selectee.selected = true;
3487 selectee.startselected = true;
3488 that._trigger("selected", event, {
3489 selected: selectee.element
3492 this._trigger("stop", event);
3494 this.helper.remove();
3503 (function( $, undefined ) {
3505 /*jshint loopfunc: true */
3507 function isOverAxis( x, reference, size ) {
3508 return ( x > reference ) && ( x < ( reference + size ) );
3511 function isFloating(item) {
3512 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3515 $.widget("ui.sortable", $.ui.mouse, {
3517 widgetEventPrefix: "sort",
3527 forcePlaceholderSize: false,
3528 forceHelperSize: false,
3537 scrollSensitivity: 20,
3540 tolerance: "intersect",
3557 _create: function() {
3559 var o = this.options;
3560 this.containerCache = {};
3561 this.element.addClass("ui-sortable");
3566 //Let's determine if the items are being displayed horizontally
3567 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
3569 //Let's determine the parent's offset
3570 this.offset = this.element.offset();
3572 //Initialize mouse events for interaction
3580 _destroy: function() {
3582 .removeClass("ui-sortable ui-sortable-disabled");
3583 this._mouseDestroy();
3585 for ( var i = this.items.length - 1; i >= 0; i-- ) {
3586 this.items[i].item.removeData(this.widgetName + "-item");
3592 _setOption: function(key, value){
3593 if ( key === "disabled" ) {
3594 this.options[ key ] = value;
3596 this.widget().toggleClass( "ui-sortable-disabled", !!value );
3598 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3599 $.Widget.prototype._setOption.apply(this, arguments);
3603 _mouseCapture: function(event, overrideHandle) {
3604 var currentItem = null,
3605 validHandle = false,
3608 if (this.reverting) {
3612 if(this.options.disabled || this.options.type === "static") {
3616 //We have to refresh the items data once first
3617 this._refreshItems(event);
3619 //Find out if the clicked node (or one of its parents) is a actual item in this.items
3620 $(event.target).parents().each(function() {
3621 if($.data(this, that.widgetName + "-item") === that) {
3622 currentItem = $(this);
3626 if($.data(event.target, that.widgetName + "-item") === that) {
3627 currentItem = $(event.target);
3633 if(this.options.handle && !overrideHandle) {
3634 $(this.options.handle, currentItem).find("*").addBack().each(function() {
3635 if(this === event.target) {
3644 this.currentItem = currentItem;
3645 this._removeCurrentsFromItems();
3650 _mouseStart: function(event, overrideHandle, noActivation) {
3655 this.currentContainer = this;
3657 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3658 this.refreshPositions();
3660 //Create and append the visible helper
3661 this.helper = this._createHelper(event);
3663 //Cache the helper size
3664 this._cacheHelperProportions();
3667 * - Position generation -
3668 * This block generates everything position related - it's the core of draggables.
3671 //Cache the margins of the original element
3672 this._cacheMargins();
3674 //Get the next scrolling parent
3675 this.scrollParent = this.helper.scrollParent();
3677 //The element's absolute position on the page minus margins
3678 this.offset = this.currentItem.offset();
3680 top: this.offset.top - this.margins.top,
3681 left: this.offset.left - this.margins.left
3684 $.extend(this.offset, {
3685 click: { //Where the click happened, relative to the element
3686 left: event.pageX - this.offset.left,
3687 top: event.pageY - this.offset.top
3689 parent: this._getParentOffset(),
3690 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3693 // Only after we got the offset, we can change the helper's position to absolute
3694 // TODO: Still need to figure out a way to make relative sorting possible
3695 this.helper.css("position", "absolute");
3696 this.cssPosition = this.helper.css("position");
3698 //Generate the original position
3699 this.originalPosition = this._generatePosition(event);
3700 this.originalPageX = event.pageX;
3701 this.originalPageY = event.pageY;
3703 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
3704 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3706 //Cache the former DOM position
3707 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3709 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
3710 if(this.helper[0] !== this.currentItem[0]) {
3711 this.currentItem.hide();
3714 //Create the placeholder
3715 this._createPlaceholder();
3717 //Set a containment if given in the options
3719 this._setContainment();
3722 if( o.cursor && o.cursor !== "auto" ) { // cursor option
3723 body = this.document.find( "body" );
3726 this.storedCursor = body.css( "cursor" );
3727 body.css( "cursor", o.cursor );
3729 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
3732 if(o.opacity) { // opacity option
3733 if (this.helper.css("opacity")) {
3734 this._storedOpacity = this.helper.css("opacity");
3736 this.helper.css("opacity", o.opacity);
3739 if(o.zIndex) { // zIndex option
3740 if (this.helper.css("zIndex")) {
3741 this._storedZIndex = this.helper.css("zIndex");
3743 this.helper.css("zIndex", o.zIndex);
3747 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3748 this.overflowOffset = this.scrollParent.offset();
3752 this._trigger("start", event, this._uiHash());
3754 //Recache the helper size
3755 if(!this._preserveHelperProportions) {
3756 this._cacheHelperProportions();
3760 //Post "activate" events to possible containers
3761 if( !noActivation ) {
3762 for ( i = this.containers.length - 1; i >= 0; i-- ) {
3763 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
3767 //Prepare possible droppables
3768 if($.ui.ddmanager) {
3769 $.ui.ddmanager.current = this;
3772 if ($.ui.ddmanager && !o.dropBehaviour) {
3773 $.ui.ddmanager.prepareOffsets(this, event);
3776 this.dragging = true;
3778 this.helper.addClass("ui-sortable-helper");
3779 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3784 _mouseDrag: function(event) {
3785 var i, item, itemElement, intersection,
3789 //Compute the helpers position
3790 this.position = this._generatePosition(event);
3791 this.positionAbs = this._convertPositionTo("absolute");
3793 if (!this.lastPositionAbs) {
3794 this.lastPositionAbs = this.positionAbs;
3798 if(this.options.scroll) {
3799 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3801 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
3802 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3803 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
3804 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3807 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
3808 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3809 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
3810 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3815 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
3816 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3817 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
3818 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3821 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
3822 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3823 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
3824 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3829 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
3830 $.ui.ddmanager.prepareOffsets(this, event);
3834 //Regenerate the absolute position used for position checks
3835 this.positionAbs = this._convertPositionTo("absolute");
3837 //Set the helper position
3838 if(!this.options.axis || this.options.axis !== "y") {
3839 this.helper[0].style.left = this.position.left+"px";
3841 if(!this.options.axis || this.options.axis !== "x") {
3842 this.helper[0].style.top = this.position.top+"px";
3846 for (i = this.items.length - 1; i >= 0; i--) {
3848 //Cache variables and intersection, continue if no intersection
3849 item = this.items[i];
3850 itemElement = item.item[0];
3851 intersection = this._intersectsWithPointer(item);
3852 if (!intersection) {
3856 // Only put the placeholder inside the current Container, skip all
3857 // items form other containers. This works because when moving
3858 // an item from one container to another the
3859 // currentContainer is switched before the placeholder is moved.
3861 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3862 // beetween the outer and inner container.
3863 if (item.instance !== this.currentContainer) {
3867 // cannot intersect with itself
3868 // no useless actions that have been done before
3869 // no action if the item moved is the parent of the item checked
3870 if (itemElement !== this.currentItem[0] &&
3871 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
3872 !$.contains(this.placeholder[0], itemElement) &&
3873 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
3876 this.direction = intersection === 1 ? "down" : "up";
3878 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
3879 this._rearrange(event, item);
3884 this._trigger("change", event, this._uiHash());
3889 //Post events to containers
3890 this._contactContainers(event);
3892 //Interconnect with droppables
3893 if($.ui.ddmanager) {
3894 $.ui.ddmanager.drag(this, event);
3898 this._trigger("sort", event, this._uiHash());
3900 this.lastPositionAbs = this.positionAbs;
3905 _mouseStop: function(event, noPropagation) {
3911 //If we are using droppables, inform the manager about the drop
3912 if ($.ui.ddmanager && !this.options.dropBehaviour) {
3913 $.ui.ddmanager.drop(this, event);
3916 if(this.options.revert) {
3918 cur = this.placeholder.offset(),
3919 axis = this.options.axis,
3922 if ( !axis || axis === "x" ) {
3923 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
3925 if ( !axis || axis === "y" ) {
3926 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
3928 this.reverting = true;
3929 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
3933 this._clear(event, noPropagation);
3940 cancel: function() {
3944 this._mouseUp({ target: null });
3946 if(this.options.helper === "original") {
3947 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3949 this.currentItem.show();
3952 //Post deactivating events to containers
3953 for (var i = this.containers.length - 1; i >= 0; i--){
3954 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3955 if(this.containers[i].containerCache.over) {
3956 this.containers[i]._trigger("out", null, this._uiHash(this));
3957 this.containers[i].containerCache.over = 0;
3963 if (this.placeholder) {
3964 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3965 if(this.placeholder[0].parentNode) {
3966 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3968 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
3969 this.helper.remove();
3979 if(this.domPosition.prev) {
3980 $(this.domPosition.prev).after(this.currentItem);
3982 $(this.domPosition.parent).prepend(this.currentItem);
3990 serialize: function(o) {
3992 var items = this._getItemsAsjQuery(o && o.connected),
3996 $(items).each(function() {
3997 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
3999 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4003 if(!str.length && o.key) {
4004 str.push(o.key + "=");
4007 return str.join("&");
4011 toArray: function(o) {
4013 var items = this._getItemsAsjQuery(o && o.connected),
4018 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
4023 /* Be careful with the following core functions */
4024 _intersectsWith: function(item) {
4026 var x1 = this.positionAbs.left,
4027 x2 = x1 + this.helperProportions.width,
4028 y1 = this.positionAbs.top,
4029 y2 = y1 + this.helperProportions.height,
4033 b = t + item.height,
4034 dyClick = this.offset.click.top,
4035 dxClick = this.offset.click.left,
4036 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
4037 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
4038 isOverElement = isOverElementHeight && isOverElementWidth;
4040 if ( this.options.tolerance === "pointer" ||
4041 this.options.forcePointerForContainers ||
4042 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4044 return isOverElement;
4047 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4048 x2 - (this.helperProportions.width / 2) < r && // Left Half
4049 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4050 y2 - (this.helperProportions.height / 2) < b ); // Top Half
4055 _intersectsWithPointer: function(item) {
4057 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4058 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4059 isOverElement = isOverElementHeight && isOverElementWidth,
4060 verticalDirection = this._getDragVerticalDirection(),
4061 horizontalDirection = this._getDragHorizontalDirection();
4063 if (!isOverElement) {
4067 return this.floating ?
4068 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4069 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4073 _intersectsWithSides: function(item) {
4075 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4076 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4077 verticalDirection = this._getDragVerticalDirection(),
4078 horizontalDirection = this._getDragHorizontalDirection();
4080 if (this.floating && horizontalDirection) {
4081 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4083 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4088 _getDragVerticalDirection: function() {
4089 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4090 return delta !== 0 && (delta > 0 ? "down" : "up");
4093 _getDragHorizontalDirection: function() {
4094 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4095 return delta !== 0 && (delta > 0 ? "right" : "left");
4098 refresh: function(event) {
4099 this._refreshItems(event);
4100 this.refreshPositions();
4104 _connectWith: function() {
4105 var options = this.options;
4106 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4109 _getItemsAsjQuery: function(connected) {
4111 var i, j, cur, inst,
4114 connectWith = this._connectWith();
4116 if(connectWith && connected) {
4117 for (i = connectWith.length - 1; i >= 0; i--){
4118 cur = $(connectWith[i]);
4119 for ( j = cur.length - 1; j >= 0; j--){
4120 inst = $.data(cur[j], this.widgetFullName);
4121 if(inst && inst !== this && !inst.options.disabled) {
4122 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
4128 queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
4130 for (i = queries.length - 1; i >= 0; i--){
4131 queries[i][0].each(function() {
4140 _removeCurrentsFromItems: function() {
4142 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4144 this.items = $.grep(this.items, function (item) {
4145 for (var j=0; j < list.length; j++) {
4146 if(list[j] === item.item[0]) {
4155 _refreshItems: function(event) {
4158 this.containers = [this];
4160 var i, j, cur, inst, targetData, _queries, item, queriesLength,
4162 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4163 connectWith = this._connectWith();
4165 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4166 for (i = connectWith.length - 1; i >= 0; i--){
4167 cur = $(connectWith[i]);
4168 for (j = cur.length - 1; j >= 0; j--){
4169 inst = $.data(cur[j], this.widgetFullName);
4170 if(inst && inst !== this && !inst.options.disabled) {
4171 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4172 this.containers.push(inst);
4178 for (i = queries.length - 1; i >= 0; i--) {
4179 targetData = queries[i][1];
4180 _queries = queries[i][0];
4182 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4183 item = $(_queries[j]);
4185 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4189 instance: targetData,
4190 width: 0, height: 0,
4198 refreshPositions: function(fast) {
4200 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4201 if(this.offsetParent && this.helper) {
4202 this.offset.parent = this._getParentOffset();
4207 for (i = this.items.length - 1; i >= 0; i--){
4208 item = this.items[i];
4210 //We ignore calculating positions of all connected containers when we're not over them
4211 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4215 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4218 item.width = t.outerWidth();
4219 item.height = t.outerHeight();
4227 if(this.options.custom && this.options.custom.refreshContainers) {
4228 this.options.custom.refreshContainers.call(this);
4230 for (i = this.containers.length - 1; i >= 0; i--){
4231 p = this.containers[i].element.offset();
4232 this.containers[i].containerCache.left = p.left;
4233 this.containers[i].containerCache.top = p.top;
4234 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4235 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4242 _createPlaceholder: function(that) {
4243 that = that || this;
4247 if(!o.placeholder || o.placeholder.constructor === String) {
4248 className = o.placeholder;
4250 element: function() {
4252 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4253 element = $( "<" + nodeName + ">", that.document[0] )
4254 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4255 .removeClass("ui-sortable-helper");
4257 if ( nodeName === "tr" ) {
4258 that.currentItem.children().each(function() {
4259 $( "<td> </td>", that.document[0] )
4260 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
4261 .appendTo( element );
4263 } else if ( nodeName === "img" ) {
4264 element.attr( "src", that.currentItem.attr( "src" ) );
4268 element.css( "visibility", "hidden" );
4273 update: function(container, p) {
4275 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4276 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4277 if(className && !o.forcePlaceholderSize) {
4281 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4282 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4283 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4288 //Create the placeholder
4289 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4291 //Append it after the actual current item
4292 that.currentItem.after(that.placeholder);
4294 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4295 o.placeholder.update(that, that.placeholder);
4299 _contactContainers: function(event) {
4300 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4301 innermostContainer = null,
4302 innermostIndex = null;
4304 // get innermost container that intersects with item
4305 for (i = this.containers.length - 1; i >= 0; i--) {
4307 // never consider a container that's located within the item itself
4308 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4312 if(this._intersectsWith(this.containers[i].containerCache)) {
4314 // if we've already found a container and it's more "inner" than this, then continue
4315 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4319 innermostContainer = this.containers[i];
4323 // container doesn't intersect. trigger "out" event if necessary
4324 if(this.containers[i].containerCache.over) {
4325 this.containers[i]._trigger("out", event, this._uiHash(this));
4326 this.containers[i].containerCache.over = 0;
4332 // if no intersecting containers found, return
4333 if(!innermostContainer) {
4337 // move the item into the container if it's not there already
4338 if(this.containers.length === 1) {
4339 if (!this.containers[innermostIndex].containerCache.over) {
4340 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4341 this.containers[innermostIndex].containerCache.over = 1;
4345 //When entering a new container, we will find the item with the least distance and append our item near it
4347 itemWithLeastDistance = null;
4348 floating = innermostContainer.floating || isFloating(this.currentItem);
4349 posProperty = floating ? "left" : "top";
4350 sizeProperty = floating ? "width" : "height";
4351 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4352 for (j = this.items.length - 1; j >= 0; j--) {
4353 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4356 if(this.items[j].item[0] === this.currentItem[0]) {
4359 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4362 cur = this.items[j].item.offset()[posProperty];
4364 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4366 cur += this.items[j][sizeProperty];
4369 if(Math.abs(cur - base) < dist) {
4370 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4371 this.direction = nearBottom ? "up": "down";
4375 //Check if dropOnEmpty is enabled
4376 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4380 if(this.currentContainer === this.containers[innermostIndex]) {
4384 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4385 this._trigger("change", event, this._uiHash());
4386 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4387 this.currentContainer = this.containers[innermostIndex];
4389 //Update the placeholder
4390 this.options.placeholder.update(this.currentContainer, this.placeholder);
4392 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4393 this.containers[innermostIndex].containerCache.over = 1;
4399 _createHelper: function(event) {
4401 var o = this.options,
4402 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4404 //Add the helper to the DOM if that didn't happen already
4405 if(!helper.parents("body").length) {
4406 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4409 if(helper[0] === this.currentItem[0]) {
4410 this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
4413 if(!helper[0].style.width || o.forceHelperSize) {
4414 helper.width(this.currentItem.width());
4416 if(!helper[0].style.height || o.forceHelperSize) {
4417 helper.height(this.currentItem.height());
4424 _adjustOffsetFromHelper: function(obj) {
4425 if (typeof obj === "string") {
4426 obj = obj.split(" ");
4428 if ($.isArray(obj)) {
4429 obj = {left: +obj[0], top: +obj[1] || 0};
4431 if ("left" in obj) {
4432 this.offset.click.left = obj.left + this.margins.left;
4434 if ("right" in obj) {
4435 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4438 this.offset.click.top = obj.top + this.margins.top;
4440 if ("bottom" in obj) {
4441 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4445 _getParentOffset: function() {
4448 //Get the offsetParent and cache its position
4449 this.offsetParent = this.helper.offsetParent();
4450 var po = this.offsetParent.offset();
4452 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4453 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4454 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4455 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4456 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4457 po.left += this.scrollParent.scrollLeft();
4458 po.top += this.scrollParent.scrollTop();
4461 // This needs to be actually done for all browsers, since pageX/pageY includes this information
4462 // with an ugly IE fix
4463 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4464 po = { top: 0, left: 0 };
4468 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4469 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4474 _getRelativeOffset: function() {
4476 if(this.cssPosition === "relative") {
4477 var p = this.currentItem.position();
4479 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4480 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4483 return { top: 0, left: 0 };
4488 _cacheMargins: function() {
4490 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4491 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4495 _cacheHelperProportions: function() {
4496 this.helperProportions = {
4497 width: this.helper.outerWidth(),
4498 height: this.helper.outerHeight()
4502 _setContainment: function() {
4506 if(o.containment === "parent") {
4507 o.containment = this.helper[0].parentNode;
4509 if(o.containment === "document" || o.containment === "window") {
4510 this.containment = [
4511 0 - this.offset.relative.left - this.offset.parent.left,
4512 0 - this.offset.relative.top - this.offset.parent.top,
4513 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4514 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4518 if(!(/^(document|window|parent)$/).test(o.containment)) {
4519 ce = $(o.containment)[0];
4520 co = $(o.containment).offset();
4521 over = ($(ce).css("overflow") !== "hidden");
4523 this.containment = [
4524 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4525 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4526 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
4527 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
4533 _convertPositionTo: function(d, pos) {
4536 pos = this.position;
4538 var mod = d === "absolute" ? 1 : -1,
4539 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
4540 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4544 pos.top + // The absolute mouse position
4545 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4546 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
4547 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4550 pos.left + // The absolute mouse position
4551 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4552 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
4553 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4559 _generatePosition: function(event) {
4563 pageX = event.pageX,
4564 pageY = event.pageY,
4565 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4567 // This is another very weird special case that only happens for relative elements:
4568 // 1. If the css position is relative
4569 // 2. and the scroll parent is the document or similar to the offset parent
4570 // we have to refresh the relative offset during the scroll so there are no jumps
4571 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
4572 this.offset.relative = this._getRelativeOffset();
4576 * - Position constraining -
4577 * Constrain the position to a mix of grid, containment.
4580 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4582 if(this.containment) {
4583 if(event.pageX - this.offset.click.left < this.containment[0]) {
4584 pageX = this.containment[0] + this.offset.click.left;
4586 if(event.pageY - this.offset.click.top < this.containment[1]) {
4587 pageY = this.containment[1] + this.offset.click.top;
4589 if(event.pageX - this.offset.click.left > this.containment[2]) {
4590 pageX = this.containment[2] + this.offset.click.left;
4592 if(event.pageY - this.offset.click.top > this.containment[3]) {
4593 pageY = this.containment[3] + this.offset.click.top;
4598 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4599 pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
4601 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4602 pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
4609 pageY - // The absolute mouse position
4610 this.offset.click.top - // Click offset (relative to the element)
4611 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
4612 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
4613 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4616 pageX - // The absolute mouse position
4617 this.offset.click.left - // Click offset (relative to the element)
4618 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
4619 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
4620 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4626 _rearrange: function(event, i, a, hardRefresh) {
4628 a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
4630 //Various things done here to improve the performance:
4631 // 1. we create a setTimeout, that calls refreshPositions
4632 // 2. on the instance, we have a counter variable, that get's higher after every append
4633 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4634 // 4. this lets only the last addition to the timeout stack through
4635 this.counter = this.counter ? ++this.counter : 1;
4636 var counter = this.counter;
4638 this._delay(function() {
4639 if(counter === this.counter) {
4640 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4646 _clear: function(event, noPropagation) {
4648 this.reverting = false;
4649 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4650 // everything else normalized again
4652 delayedTriggers = [];
4654 // We first have to update the dom position of the actual currentItem
4655 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4656 if(!this._noFinalSort && this.currentItem.parent().length) {
4657 this.placeholder.before(this.currentItem);
4659 this._noFinalSort = null;
4661 if(this.helper[0] === this.currentItem[0]) {
4662 for(i in this._storedCSS) {
4663 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
4664 this._storedCSS[i] = "";
4667 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4669 this.currentItem.show();
4672 if(this.fromOutside && !noPropagation) {
4673 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4675 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
4676 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4679 // Check if the items Container has Changed and trigger appropriate
4681 if (this !== this.currentContainer) {
4682 if(!noPropagation) {
4683 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4684 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4685 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4690 //Post events to containers
4691 for (i = this.containers.length - 1; i >= 0; i--){
4692 if(!noPropagation) {
4693 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4695 if(this.containers[i].containerCache.over) {
4696 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4697 this.containers[i].containerCache.over = 0;
4701 //Do what was originally in plugins
4702 if ( this.storedCursor ) {
4703 this.document.find( "body" ).css( "cursor", this.storedCursor );
4704 this.storedStylesheet.remove();
4706 if(this._storedOpacity) {
4707 this.helper.css("opacity", this._storedOpacity);
4709 if(this._storedZIndex) {
4710 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
4713 this.dragging = false;
4714 if(this.cancelHelperRemoval) {
4715 if(!noPropagation) {
4716 this._trigger("beforeStop", event, this._uiHash());
4717 for (i=0; i < delayedTriggers.length; i++) {
4718 delayedTriggers[i].call(this, event);
4719 } //Trigger all delayed events
4720 this._trigger("stop", event, this._uiHash());
4723 this.fromOutside = false;
4727 if(!noPropagation) {
4728 this._trigger("beforeStop", event, this._uiHash());
4731 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4732 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4734 if(this.helper[0] !== this.currentItem[0]) {
4735 this.helper.remove();
4739 if(!noPropagation) {
4740 for (i=0; i < delayedTriggers.length; i++) {
4741 delayedTriggers[i].call(this, event);
4742 } //Trigger all delayed events
4743 this._trigger("stop", event, this._uiHash());
4746 this.fromOutside = false;
4751 _trigger: function() {
4752 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4757 _uiHash: function(_inst) {
4758 var inst = _inst || this;
4760 helper: inst.helper,
4761 placeholder: inst.placeholder || $([]),
4762 position: inst.position,
4763 originalPosition: inst.originalPosition,
4764 offset: inst.positionAbs,
4765 item: inst.currentItem,
4766 sender: _inst ? _inst.element : null
4774 (function($, undefined) {
4776 var dataSpace = "ui-effects-";
4783 * jQuery Color Animations v2.1.2
4784 * https://github.com/jquery/jquery-color
4786 * Copyright 2013 jQuery Foundation and other contributors
4787 * Released under the MIT license.
4788 * http://jquery.org/license
4790 * Date: Wed Jan 16 08:47:09 2013 -0600
4792 (function( jQuery, undefined ) {
4794 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
4796 // plusequals test for += 100 -= 100
4797 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
4798 // a set of RE's that can match strings and generate color tuples.
4800 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4801 parse: function( execResult ) {
4810 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4811 parse: function( execResult ) {
4813 execResult[ 1 ] * 2.55,
4814 execResult[ 2 ] * 2.55,
4815 execResult[ 3 ] * 2.55,
4820 // this regex ignores A-F because it's compared against an already lowercased string
4821 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
4822 parse: function( execResult ) {
4824 parseInt( execResult[ 1 ], 16 ),
4825 parseInt( execResult[ 2 ], 16 ),
4826 parseInt( execResult[ 3 ], 16 )
4830 // this regex ignores A-F because it's compared against an already lowercased string
4831 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
4832 parse: function( execResult ) {
4834 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
4835 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
4836 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
4840 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4842 parse: function( execResult ) {
4845 execResult[ 2 ] / 100,
4846 execResult[ 3 ] / 100,
4853 color = jQuery.Color = function( color, green, blue, alpha ) {
4854 return new jQuery.Color.fn.parse( color, green, blue, alpha );
4904 support = color.support = {},
4906 // element for support tests
4907 supportElem = jQuery( "<p>" )[ 0 ],
4909 // colors = jQuery.Color.names
4912 // local aliases of functions called often
4915 // determine rgba support immediately
4916 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
4917 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
4919 // define cache name and alpha properties
4920 // for rgba and hsla spaces
4921 each( spaces, function( spaceName, space ) {
4922 space.cache = "_" + spaceName;
4923 space.props.alpha = {
4930 function clamp( value, prop, allowEmpty ) {
4931 var type = propTypes[ prop.type ] || {};
4933 if ( value == null ) {
4934 return (allowEmpty || !prop.def) ? null : prop.def;
4937 // ~~ is an short way of doing floor for positive numbers
4938 value = type.floor ? ~~value : parseFloat( value );
4940 // IE will pass in empty strings as value for alpha,
4941 // which will hit this case
4942 if ( isNaN( value ) ) {
4947 // we add mod before modding to make sure that negatives values
4948 // get converted properly: -10 -> 350
4949 return (value + type.mod) % type.mod;
4952 // for now all property types without mod have min and max
4953 return 0 > value ? 0 : type.max < value ? type.max : value;
4956 function stringParse( string ) {
4958 rgba = inst._rgba = [];
4960 string = string.toLowerCase();
4962 each( stringParsers, function( i, parser ) {
4964 match = parser.re.exec( string ),
4965 values = match && parser.parse( match ),
4966 spaceName = parser.space || "rgba";
4969 parsed = inst[ spaceName ]( values );
4971 // if this was an rgba parse the assignment might happen twice
4973 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
4974 rgba = inst._rgba = parsed._rgba;
4976 // exit each( stringParsers ) here because we matched
4981 // Found a stringParser that handled it
4982 if ( rgba.length ) {
4984 // if this came from a parsed string, force "transparent" when alpha is 0
4985 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
4986 if ( rgba.join() === "0,0,0,0" ) {
4987 jQuery.extend( rgba, colors.transparent );
4993 return colors[ string ];
4996 color.fn = jQuery.extend( color.prototype, {
4997 parse: function( red, green, blue, alpha ) {
4998 if ( red === undefined ) {
4999 this._rgba = [ null, null, null, null ];
5002 if ( red.jquery || red.nodeType ) {
5003 red = jQuery( red ).css( green );
5008 type = jQuery.type( red ),
5009 rgba = this._rgba = [];
5011 // more than 1 argument specified - assume ( red, green, blue, alpha )
5012 if ( green !== undefined ) {
5013 red = [ red, green, blue, alpha ];
5017 if ( type === "string" ) {
5018 return this.parse( stringParse( red ) || colors._default );
5021 if ( type === "array" ) {
5022 each( spaces.rgba.props, function( key, prop ) {
5023 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
5028 if ( type === "object" ) {
5029 if ( red instanceof color ) {
5030 each( spaces, function( spaceName, space ) {
5031 if ( red[ space.cache ] ) {
5032 inst[ space.cache ] = red[ space.cache ].slice();
5036 each( spaces, function( spaceName, space ) {
5037 var cache = space.cache;
5038 each( space.props, function( key, prop ) {
5040 // if the cache doesn't exist, and we know how to convert
5041 if ( !inst[ cache ] && space.to ) {
5043 // if the value was null, we don't need to copy it
5044 // if the key was alpha, we don't need to copy it either
5045 if ( key === "alpha" || red[ key ] == null ) {
5048 inst[ cache ] = space.to( inst._rgba );
5051 // this is the only case where we allow nulls for ALL properties.
5052 // call clamp with alwaysAllowEmpty
5053 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
5056 // everything defined but alpha?
5057 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
5058 // use the default of 1
5059 inst[ cache ][ 3 ] = 1;
5061 inst._rgba = space.from( inst[ cache ] );
5069 is: function( compare ) {
5070 var is = color( compare ),
5074 each( spaces, function( _, space ) {
5076 isCache = is[ space.cache ];
5078 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
5079 each( space.props, function( _, prop ) {
5080 if ( isCache[ prop.idx ] != null ) {
5081 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
5090 _space: function() {
5093 each( spaces, function( spaceName, space ) {
5094 if ( inst[ space.cache ] ) {
5095 used.push( spaceName );
5100 transition: function( other, distance ) {
5101 var end = color( other ),
5102 spaceName = end._space(),
5103 space = spaces[ spaceName ],
5104 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
5105 start = startColor[ space.cache ] || space.to( startColor._rgba ),
5106 result = start.slice();
5108 end = end[ space.cache ];
5109 each( space.props, function( key, prop ) {
5110 var index = prop.idx,
5111 startValue = start[ index ],
5112 endValue = end[ index ],
5113 type = propTypes[ prop.type ] || {};
5115 // if null, don't override start value
5116 if ( endValue === null ) {
5119 // if null - use end
5120 if ( startValue === null ) {
5121 result[ index ] = endValue;
5124 if ( endValue - startValue > type.mod / 2 ) {
5125 startValue += type.mod;
5126 } else if ( startValue - endValue > type.mod / 2 ) {
5127 startValue -= type.mod;
5130 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
5133 return this[ spaceName ]( result );
5135 blend: function( opaque ) {
5136 // if we are already opaque - return ourself
5137 if ( this._rgba[ 3 ] === 1 ) {
5141 var rgb = this._rgba.slice(),
5143 blend = color( opaque )._rgba;
5145 return color( jQuery.map( rgb, function( v, i ) {
5146 return ( 1 - a ) * blend[ i ] + a * v;
5149 toRgbaString: function() {
5150 var prefix = "rgba(",
5151 rgba = jQuery.map( this._rgba, function( v, i ) {
5152 return v == null ? ( i > 2 ? 1 : 0 ) : v;
5155 if ( rgba[ 3 ] === 1 ) {
5160 return prefix + rgba.join() + ")";
5162 toHslaString: function() {
5163 var prefix = "hsla(",
5164 hsla = jQuery.map( this.hsla(), function( v, i ) {
5171 v = Math.round( v * 100 ) + "%";
5176 if ( hsla[ 3 ] === 1 ) {
5180 return prefix + hsla.join() + ")";
5182 toHexString: function( includeAlpha ) {
5183 var rgba = this._rgba.slice(),
5186 if ( includeAlpha ) {
5187 rgba.push( ~~( alpha * 255 ) );
5190 return "#" + jQuery.map( rgba, function( v ) {
5192 // default to 0 when nulls exist
5193 v = ( v || 0 ).toString( 16 );
5194 return v.length === 1 ? "0" + v : v;
5197 toString: function() {
5198 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
5201 color.fn.parse.prototype = color.fn;
5203 // hsla conversions adapted from:
5204 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
5206 function hue2rgb( p, q, h ) {
5209 return p + (q - p) * h * 6;
5215 return p + (q - p) * ((2/3) - h) * 6;
5220 spaces.hsla.to = function ( rgba ) {
5221 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
5222 return [ null, null, null, rgba[ 3 ] ];
5224 var r = rgba[ 0 ] / 255,
5225 g = rgba[ 1 ] / 255,
5226 b = rgba[ 2 ] / 255,
5228 max = Math.max( r, g, b ),
5229 min = Math.min( r, g, b ),
5235 if ( min === max ) {
5237 } else if ( r === max ) {
5238 h = ( 60 * ( g - b ) / diff ) + 360;
5239 } else if ( g === max ) {
5240 h = ( 60 * ( b - r ) / diff ) + 120;
5242 h = ( 60 * ( r - g ) / diff ) + 240;
5245 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
5246 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
5249 } else if ( l <= 0.5 ) {
5252 s = diff / ( 2 - add );
5254 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
5257 spaces.hsla.from = function ( hsla ) {
5258 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
5259 return [ null, null, null, hsla[ 3 ] ];
5261 var h = hsla[ 0 ] / 360,
5265 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
5269 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
5270 Math.round( hue2rgb( p, q, h ) * 255 ),
5271 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
5277 each( spaces, function( spaceName, space ) {
5278 var props = space.props,
5279 cache = space.cache,
5283 // makes rgba() and hsla()
5284 color.fn[ spaceName ] = function( value ) {
5286 // generate a cache for this space if it doesn't exist
5287 if ( to && !this[ cache ] ) {
5288 this[ cache ] = to( this._rgba );
5290 if ( value === undefined ) {
5291 return this[ cache ].slice();
5295 type = jQuery.type( value ),
5296 arr = ( type === "array" || type === "object" ) ? value : arguments,
5297 local = this[ cache ].slice();
5299 each( props, function( key, prop ) {
5300 var val = arr[ type === "object" ? key : prop.idx ];
5301 if ( val == null ) {
5302 val = local[ prop.idx ];
5304 local[ prop.idx ] = clamp( val, prop );
5308 ret = color( from( local ) );
5309 ret[ cache ] = local;
5312 return color( local );
5316 // makes red() green() blue() alpha() hue() saturation() lightness()
5317 each( props, function( key, prop ) {
5318 // alpha is included in more than one space
5319 if ( color.fn[ key ] ) {
5322 color.fn[ key ] = function( value ) {
5323 var vtype = jQuery.type( value ),
5324 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
5325 local = this[ fn ](),
5326 cur = local[ prop.idx ],
5329 if ( vtype === "undefined" ) {
5333 if ( vtype === "function" ) {
5334 value = value.call( this, cur );
5335 vtype = jQuery.type( value );
5337 if ( value == null && prop.empty ) {
5340 if ( vtype === "string" ) {
5341 match = rplusequals.exec( value );
5343 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
5346 local[ prop.idx ] = value;
5347 return this[ fn ]( local );
5352 // add cssHook and .fx.step function for each named hook.
5353 // accept a space separated string of properties
5354 color.hook = function( hook ) {
5355 var hooks = hook.split( " " );
5356 each( hooks, function( i, hook ) {
5357 jQuery.cssHooks[ hook ] = {
5358 set: function( elem, value ) {
5359 var parsed, curElem,
5360 backgroundColor = "";
5362 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
5363 value = color( parsed || value );
5364 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
5365 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
5367 (backgroundColor === "" || backgroundColor === "transparent") &&
5368 curElem && curElem.style
5371 backgroundColor = jQuery.css( curElem, "backgroundColor" );
5372 curElem = curElem.parentNode;
5377 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
5382 value = value.toRgbaString();
5385 elem.style[ hook ] = value;
5387 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
5391 jQuery.fx.step[ hook ] = function( fx ) {
5392 if ( !fx.colorInit ) {
5393 fx.start = color( fx.elem, hook );
5394 fx.end = color( fx.end );
5395 fx.colorInit = true;
5397 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
5403 color.hook( stepHooks );
5405 jQuery.cssHooks.borderColor = {
5406 expand: function( value ) {
5409 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
5410 expanded[ "border" + part + "Color" ] = value;
5416 // Basic color names only.
5417 // Usage of any of the other color names requires adding yourself or including
5418 // jquery.color.svg-names.js.
5419 colors = jQuery.Color.names = {
5420 // 4.1. Basic color keywords
5438 // 4.2.3. "transparent" color keyword
5439 transparent: [ null, null, null, 0 ],
5447 /******************************************************************************/
5448 /****************************** CLASS ANIMATIONS ******************************/
5449 /******************************************************************************/
5452 var classAnimationActions = [ "add", "remove", "toggle" ],
5465 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
5466 $.fx.step[ prop ] = function( fx ) {
5467 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
5468 jQuery.style( fx.elem, prop, fx.end );
5474 function getElementStyles( elem ) {
5476 style = elem.ownerDocument.defaultView ?
5477 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
5481 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
5485 if ( typeof style[ key ] === "string" ) {
5486 styles[ $.camelCase( key ) ] = style[ key ];
5489 // support: Opera, IE <9
5491 for ( key in style ) {
5492 if ( typeof style[ key ] === "string" ) {
5493 styles[ key ] = style[ key ];
5502 function styleDifference( oldStyle, newStyle ) {
5506 for ( name in newStyle ) {
5507 value = newStyle[ name ];
5508 if ( oldStyle[ name ] !== value ) {
5509 if ( !shorthandStyles[ name ] ) {
5510 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
5511 diff[ name ] = value;
5520 // support: jQuery <1.8
5521 if ( !$.fn.addBack ) {
5522 $.fn.addBack = function( selector ) {
5523 return this.add( selector == null ?
5524 this.prevObject : this.prevObject.filter( selector )
5529 $.effects.animateClass = function( value, duration, easing, callback ) {
5530 var o = $.speed( duration, easing, callback );
5532 return this.queue( function() {
5533 var animated = $( this ),
5534 baseClass = animated.attr( "class" ) || "",
5536 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
5538 // map the animated objects to store the original styles.
5539 allAnimations = allAnimations.map(function() {
5543 start: getElementStyles( this )
5547 // apply class change
5548 applyClassChange = function() {
5549 $.each( classAnimationActions, function(i, action) {
5550 if ( value[ action ] ) {
5551 animated[ action + "Class" ]( value[ action ] );
5557 // map all animated objects again - calculate new styles and diff
5558 allAnimations = allAnimations.map(function() {
5559 this.end = getElementStyles( this.el[ 0 ] );
5560 this.diff = styleDifference( this.start, this.end );
5564 // apply original class
5565 animated.attr( "class", baseClass );
5567 // map all animated objects again - this time collecting a promise
5568 allAnimations = allAnimations.map(function() {
5569 var styleInfo = this,
5571 opts = $.extend({}, o, {
5573 complete: function() {
5574 dfd.resolve( styleInfo );
5578 this.el.animate( this.diff, opts );
5579 return dfd.promise();
5582 // once all animations have completed:
5583 $.when.apply( $, allAnimations.get() ).done(function() {
5585 // set the final class
5588 // for each animated element,
5589 // clear all css properties that were animated
5590 $.each( arguments, function() {
5592 $.each( this.diff, function(key) {
5597 // this is guarnteed to be there if you use jQuery.speed()
5598 // it also handles dequeuing the next anim...
5599 o.complete.call( animated[ 0 ] );
5605 addClass: (function( orig ) {
5606 return function( classNames, speed, easing, callback ) {
5608 $.effects.animateClass.call( this,
5609 { add: classNames }, speed, easing, callback ) :
5610 orig.apply( this, arguments );
5612 })( $.fn.addClass ),
5614 removeClass: (function( orig ) {
5615 return function( classNames, speed, easing, callback ) {
5616 return arguments.length > 1 ?
5617 $.effects.animateClass.call( this,
5618 { remove: classNames }, speed, easing, callback ) :
5619 orig.apply( this, arguments );
5621 })( $.fn.removeClass ),
5623 toggleClass: (function( orig ) {
5624 return function( classNames, force, speed, easing, callback ) {
5625 if ( typeof force === "boolean" || force === undefined ) {
5627 // without speed parameter
5628 return orig.apply( this, arguments );
5630 return $.effects.animateClass.call( this,
5631 (force ? { add: classNames } : { remove: classNames }),
5632 speed, easing, callback );
5635 // without force parameter
5636 return $.effects.animateClass.call( this,
5637 { toggle: classNames }, force, speed, easing );
5640 })( $.fn.toggleClass ),
5642 switchClass: function( remove, add, speed, easing, callback) {
5643 return $.effects.animateClass.call( this, {
5646 }, speed, easing, callback );
5652 /******************************************************************************/
5653 /*********************************** EFFECTS **********************************/
5654 /******************************************************************************/
5658 $.extend( $.effects, {
5661 // Saves a set of properties in a data storage
5662 save: function( element, set ) {
5663 for( var i=0; i < set.length; i++ ) {
5664 if ( set[ i ] !== null ) {
5665 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
5670 // Restores a set of previously saved properties from a data storage
5671 restore: function( element, set ) {
5673 for( i=0; i < set.length; i++ ) {
5674 if ( set[ i ] !== null ) {
5675 val = element.data( dataSpace + set[ i ] );
5676 // support: jQuery 1.6.2
5677 // http://bugs.jquery.com/ticket/9917
5678 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
5679 // We can't differentiate between "" and 0 here, so we just assume
5680 // empty string since it's likely to be a more common value...
5681 if ( val === undefined ) {
5684 element.css( set[ i ], val );
5689 setMode: function( el, mode ) {
5690 if (mode === "toggle") {
5691 mode = el.is( ":hidden" ) ? "show" : "hide";
5696 // Translates a [top,left] array into a baseline value
5697 // this should be a little more flexible in the future to handle a string & hash
5698 getBaseline: function( origin, original ) {
5700 switch ( origin[ 0 ] ) {
5701 case "top": y = 0; break;
5702 case "middle": y = 0.5; break;
5703 case "bottom": y = 1; break;
5704 default: y = origin[ 0 ] / original.height;
5706 switch ( origin[ 1 ] ) {
5707 case "left": x = 0; break;
5708 case "center": x = 0.5; break;
5709 case "right": x = 1; break;
5710 default: x = origin[ 1 ] / original.width;
5718 // Wraps the element around a wrapper that copies position properties
5719 createWrapper: function( element ) {
5721 // if the element is already wrapped, return it
5722 if ( element.parent().is( ".ui-effects-wrapper" )) {
5723 return element.parent();
5728 width: element.outerWidth(true),
5729 height: element.outerHeight(true),
5730 "float": element.css( "float" )
5732 wrapper = $( "<div></div>" )
5733 .addClass( "ui-effects-wrapper" )
5736 background: "transparent",
5741 // Store the size in case width/height are defined in % - Fixes #5245
5743 width: element.width(),
5744 height: element.height()
5746 active = document.activeElement;
5749 // Firefox incorrectly exposes anonymous content
5750 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
5754 active = document.body;
5757 element.wrap( wrapper );
5759 // Fixes #7595 - Elements lose focus when wrapped.
5760 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5761 $( active ).focus();
5764 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
5766 // transfer positioning properties to the wrapper
5767 if ( element.css( "position" ) === "static" ) {
5768 wrapper.css({ position: "relative" });
5769 element.css({ position: "relative" });
5772 position: element.css( "position" ),
5773 zIndex: element.css( "z-index" )
5775 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
5776 props[ pos ] = element.css( pos );
5777 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
5778 props[ pos ] = "auto";
5782 position: "relative",
5791 return wrapper.css( props ).show();
5794 removeWrapper: function( element ) {
5795 var active = document.activeElement;
5797 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
5798 element.parent().replaceWith( element );
5800 // Fixes #7595 - Elements lose focus when wrapped.
5801 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5802 $( active ).focus();
5810 setTransition: function( element, list, factor, value ) {
5811 value = value || {};
5812 $.each( list, function( i, x ) {
5813 var unit = element.cssUnit( x );
5814 if ( unit[ 0 ] > 0 ) {
5815 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
5822 // return an effect options object for the given parameters:
5823 function _normalizeArguments( effect, options, speed, callback ) {
5825 // allow passing all options as the first parameter
5826 if ( $.isPlainObject( effect ) ) {
5828 effect = effect.effect;
5831 // convert to an object
5832 effect = { effect: effect };
5834 // catch (effect, null, ...)
5835 if ( options == null ) {
5839 // catch (effect, callback)
5840 if ( $.isFunction( options ) ) {
5846 // catch (effect, speed, ?)
5847 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
5853 // catch (effect, options, callback)
5854 if ( $.isFunction( speed ) ) {
5859 // add options to effect
5861 $.extend( effect, options );
5864 speed = speed || options.duration;
5865 effect.duration = $.fx.off ? 0 :
5866 typeof speed === "number" ? speed :
5867 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
5868 $.fx.speeds._default;
5870 effect.complete = callback || options.complete;
5875 function standardAnimationOption( option ) {
5876 // Valid standard speeds (nothing, number, named speed)
5877 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
5881 // Invalid strings - treat as "normal" speed
5882 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
5886 // Complete callback
5887 if ( $.isFunction( option ) ) {
5891 // Options hash (but not naming an effect)
5892 if ( typeof option === "object" && !option.effect ) {
5896 // Didn't match any standard API
5901 effect: function( /* effect, options, speed, callback */ ) {
5902 var args = _normalizeArguments.apply( this, arguments ),
5905 effectMethod = $.effects.effect[ args.effect ];
5907 if ( $.fx.off || !effectMethod ) {
5908 // delegate to the original method (e.g., .show()) if possible
5910 return this[ mode ]( args.duration, args.complete );
5912 return this.each( function() {
5913 if ( args.complete ) {
5914 args.complete.call( this );
5920 function run( next ) {
5921 var elem = $( this ),
5922 complete = args.complete,
5926 if ( $.isFunction( complete ) ) {
5927 complete.call( elem[0] );
5929 if ( $.isFunction( next ) ) {
5934 // If the element already has the correct final state, delegate to
5935 // the core methods so the internal tracking of "olddisplay" works.
5936 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
5940 effectMethod.call( elem[0], args, done );
5944 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
5947 show: (function( orig ) {
5948 return function( option ) {
5949 if ( standardAnimationOption( option ) ) {
5950 return orig.apply( this, arguments );
5952 var args = _normalizeArguments.apply( this, arguments );
5954 return this.effect.call( this, args );
5959 hide: (function( orig ) {
5960 return function( option ) {
5961 if ( standardAnimationOption( option ) ) {
5962 return orig.apply( this, arguments );
5964 var args = _normalizeArguments.apply( this, arguments );
5966 return this.effect.call( this, args );
5971 toggle: (function( orig ) {
5972 return function( option ) {
5973 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
5974 return orig.apply( this, arguments );
5976 var args = _normalizeArguments.apply( this, arguments );
5977 args.mode = "toggle";
5978 return this.effect.call( this, args );
5984 cssUnit: function(key) {
5985 var style = this.css( key ),
5988 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
5989 if ( style.indexOf( unit ) > 0 ) {
5990 val = [ parseFloat( style ), unit ];
5999 /******************************************************************************/
6000 /*********************************** EASING ***********************************/
6001 /******************************************************************************/
6005 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
6007 var baseEasings = {};
6009 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
6010 baseEasings[ name ] = function( p ) {
6011 return Math.pow( p, i + 2 );
6015 $.extend( baseEasings, {
6016 Sine: function ( p ) {
6017 return 1 - Math.cos( p * Math.PI / 2 );
6019 Circ: function ( p ) {
6020 return 1 - Math.sqrt( 1 - p * p );
6022 Elastic: function( p ) {
6023 return p === 0 || p === 1 ? p :
6024 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
6026 Back: function( p ) {
6027 return p * p * ( 3 * p - 2 );
6029 Bounce: function ( p ) {
6033 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
6034 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
6038 $.each( baseEasings, function( name, easeIn ) {
6039 $.easing[ "easeIn" + name ] = easeIn;
6040 $.easing[ "easeOut" + name ] = function( p ) {
6041 return 1 - easeIn( 1 - p );
6043 $.easing[ "easeInOut" + name ] = function( p ) {
6045 easeIn( p * 2 ) / 2 :
6046 1 - easeIn( p * -2 + 2 ) / 2;
6054 (function( $, undefined ) {
6060 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
6061 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
6062 showProps.height = showProps.paddingTop = showProps.paddingBottom =
6063 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
6065 $.widget( "ui.accordion", {
6072 header: "> li > :first-child,> :not(li):even",
6073 heightStyle: "auto",
6075 activeHeader: "ui-icon-triangle-1-s",
6076 header: "ui-icon-triangle-1-e"
6081 beforeActivate: null
6084 _create: function() {
6085 var options = this.options;
6086 this.prevShow = this.prevHide = $();
6087 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
6089 .attr( "role", "tablist" );
6091 // don't allow collapsible: false and active: false / null
6092 if ( !options.collapsible && (options.active === false || options.active == null) ) {
6096 this._processPanels();
6097 // handle negative values
6098 if ( options.active < 0 ) {
6099 options.active += this.headers.length;
6104 _getCreateEventData: function() {
6106 header: this.active,
6107 panel: !this.active.length ? $() : this.active.next(),
6108 content: !this.active.length ? $() : this.active.next()
6112 _createIcons: function() {
6113 var icons = this.options.icons;
6116 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
6117 .prependTo( this.headers );
6118 this.active.children( ".ui-accordion-header-icon" )
6119 .removeClass( icons.header )
6120 .addClass( icons.activeHeader );
6121 this.headers.addClass( "ui-accordion-icons" );
6125 _destroyIcons: function() {
6127 .removeClass( "ui-accordion-icons" )
6128 .children( ".ui-accordion-header-icon" )
6132 _destroy: function() {
6135 // clean up main element
6137 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
6138 .removeAttr( "role" );
6142 .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
6143 .removeAttr( "role" )
6144 .removeAttr( "aria-selected" )
6145 .removeAttr( "aria-controls" )
6146 .removeAttr( "tabIndex" )
6148 if ( /^ui-accordion/.test( this.id ) ) {
6149 this.removeAttribute( "id" );
6152 this._destroyIcons();
6154 // clean up content panels
6155 contents = this.headers.next()
6156 .css( "display", "" )
6157 .removeAttr( "role" )
6158 .removeAttr( "aria-expanded" )
6159 .removeAttr( "aria-hidden" )
6160 .removeAttr( "aria-labelledby" )
6161 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
6163 if ( /^ui-accordion/.test( this.id ) ) {
6164 this.removeAttribute( "id" );
6167 if ( this.options.heightStyle !== "content" ) {
6168 contents.css( "height", "" );
6172 _setOption: function( key, value ) {
6173 if ( key === "active" ) {
6174 // _activate() will handle invalid values and update this.options
6175 this._activate( value );
6179 if ( key === "event" ) {
6180 if ( this.options.event ) {
6181 this._off( this.headers, this.options.event );
6183 this._setupEvents( value );
6186 this._super( key, value );
6188 // setting collapsible: false while collapsed; open first panel
6189 if ( key === "collapsible" && !value && this.options.active === false ) {
6190 this._activate( 0 );
6193 if ( key === "icons" ) {
6194 this._destroyIcons();
6196 this._createIcons();
6200 // #5332 - opacity doesn't cascade to positioned elements in IE
6201 // so we need to add the disabled class to the headers and panels
6202 if ( key === "disabled" ) {
6203 this.headers.add( this.headers.next() )
6204 .toggleClass( "ui-state-disabled", !!value );
6208 _keydown: function( event ) {
6209 /*jshint maxcomplexity:15*/
6210 if ( event.altKey || event.ctrlKey ) {
6214 var keyCode = $.ui.keyCode,
6215 length = this.headers.length,
6216 currentIndex = this.headers.index( event.target ),
6219 switch ( event.keyCode ) {
6222 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
6226 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
6230 this._eventHandler( event );
6233 toFocus = this.headers[ 0 ];
6236 toFocus = this.headers[ length - 1 ];
6241 $( event.target ).attr( "tabIndex", -1 );
6242 $( toFocus ).attr( "tabIndex", 0 );
6244 event.preventDefault();
6248 _panelKeyDown : function( event ) {
6249 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
6250 $( event.currentTarget ).prev().focus();
6254 refresh: function() {
6255 var options = this.options;
6256 this._processPanels();
6258 // was collapsed or no panel
6259 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
6260 options.active = false;
6262 // active false only when collapsible is true
6263 } else if ( options.active === false ) {
6264 this._activate( 0 );
6265 // was active, but active panel is gone
6266 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
6267 // all remaining panel are disabled
6268 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
6269 options.active = false;
6271 // activate previous panel
6273 this._activate( Math.max( 0, options.active - 1 ) );
6275 // was active, active panel still exists
6277 // make sure active index is correct
6278 options.active = this.headers.index( this.active );
6281 this._destroyIcons();
6286 _processPanels: function() {
6287 this.headers = this.element.find( this.options.header )
6288 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
6291 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
6292 .filter(":not(.ui-accordion-content-active)")
6296 _refresh: function() {
6298 options = this.options,
6299 heightStyle = options.heightStyle,
6300 parent = this.element.parent(),
6301 accordionId = this.accordionId = "ui-accordion-" +
6302 (this.element.attr( "id" ) || ++uid);
6304 this.active = this._findActive( options.active )
6305 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
6306 .removeClass( "ui-corner-all" );
6308 .addClass( "ui-accordion-content-active" )
6312 .attr( "role", "tab" )
6313 .each(function( i ) {
6314 var header = $( this ),
6315 headerId = header.attr( "id" ),
6316 panel = header.next(),
6317 panelId = panel.attr( "id" );
6319 headerId = accordionId + "-header-" + i;
6320 header.attr( "id", headerId );
6323 panelId = accordionId + "-panel-" + i;
6324 panel.attr( "id", panelId );
6326 header.attr( "aria-controls", panelId );
6327 panel.attr( "aria-labelledby", headerId );
6330 .attr( "role", "tabpanel" );
6335 "aria-selected": "false",
6340 "aria-expanded": "false",
6341 "aria-hidden": "true"
6345 // make sure at least one header is in the tab order
6346 if ( !this.active.length ) {
6347 this.headers.eq( 0 ).attr( "tabIndex", 0 );
6350 "aria-selected": "true",
6355 "aria-expanded": "true",
6356 "aria-hidden": "false"
6360 this._createIcons();
6362 this._setupEvents( options.event );
6364 if ( heightStyle === "fill" ) {
6365 maxHeight = parent.height();
6366 this.element.siblings( ":visible" ).each(function() {
6367 var elem = $( this ),
6368 position = elem.css( "position" );
6370 if ( position === "absolute" || position === "fixed" ) {
6373 maxHeight -= elem.outerHeight( true );
6376 this.headers.each(function() {
6377 maxHeight -= $( this ).outerHeight( true );
6382 $( this ).height( Math.max( 0, maxHeight -
6383 $( this ).innerHeight() + $( this ).height() ) );
6385 .css( "overflow", "auto" );
6386 } else if ( heightStyle === "auto" ) {
6390 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
6392 .height( maxHeight );
6396 _activate: function( index ) {
6397 var active = this._findActive( index )[ 0 ];
6399 // trying to activate the already active panel
6400 if ( active === this.active[ 0 ] ) {
6404 // trying to collapse, simulate a click on the currently active header
6405 active = active || this.active[ 0 ];
6407 this._eventHandler({
6409 currentTarget: active,
6410 preventDefault: $.noop
6414 _findActive: function( selector ) {
6415 return typeof selector === "number" ? this.headers.eq( selector ) : $();
6418 _setupEvents: function( event ) {
6423 $.each( event.split(" "), function( index, eventName ) {
6424 events[ eventName ] = "_eventHandler";
6428 this._off( this.headers.add( this.headers.next() ) );
6429 this._on( this.headers, events );
6430 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
6431 this._hoverable( this.headers );
6432 this._focusable( this.headers );
6435 _eventHandler: function( event ) {
6436 var options = this.options,
6437 active = this.active,
6438 clicked = $( event.currentTarget ),
6439 clickedIsActive = clicked[ 0 ] === active[ 0 ],
6440 collapsing = clickedIsActive && options.collapsible,
6441 toShow = collapsing ? $() : clicked.next(),
6442 toHide = active.next(),
6446 newHeader: collapsing ? $() : clicked,
6450 event.preventDefault();
6453 // click on active header, but not collapsible
6454 ( clickedIsActive && !options.collapsible ) ||
6455 // allow canceling activation
6456 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
6460 options.active = collapsing ? false : this.headers.index( clicked );
6462 // when the call to ._toggle() comes after the class changes
6463 // it causes a very odd bug in IE 8 (see #6720)
6464 this.active = clickedIsActive ? $() : clicked;
6465 this._toggle( eventData );
6468 // corner classes on the previously active header stay after the animation
6469 active.removeClass( "ui-accordion-header-active ui-state-active" );
6470 if ( options.icons ) {
6471 active.children( ".ui-accordion-header-icon" )
6472 .removeClass( options.icons.activeHeader )
6473 .addClass( options.icons.header );
6476 if ( !clickedIsActive ) {
6478 .removeClass( "ui-corner-all" )
6479 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
6480 if ( options.icons ) {
6481 clicked.children( ".ui-accordion-header-icon" )
6482 .removeClass( options.icons.header )
6483 .addClass( options.icons.activeHeader );
6488 .addClass( "ui-accordion-content-active" );
6492 _toggle: function( data ) {
6493 var toShow = data.newPanel,
6494 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
6496 // handle activating a panel during the animation for another activation
6497 this.prevShow.add( this.prevHide ).stop( true, true );
6498 this.prevShow = toShow;
6499 this.prevHide = toHide;
6501 if ( this.options.animate ) {
6502 this._animate( toShow, toHide, data );
6506 this._toggleComplete( data );
6510 "aria-expanded": "false",
6511 "aria-hidden": "true"
6513 toHide.prev().attr( "aria-selected", "false" );
6514 // if we're switching panels, remove the old header from the tab order
6515 // if we're opening from collapsed state, remove the previous header from the tab order
6516 // if we're collapsing, then keep the collapsing header in the tab order
6517 if ( toShow.length && toHide.length ) {
6518 toHide.prev().attr( "tabIndex", -1 );
6519 } else if ( toShow.length ) {
6520 this.headers.filter(function() {
6521 return $( this ).attr( "tabIndex" ) === 0;
6523 .attr( "tabIndex", -1 );
6528 "aria-expanded": "true",
6529 "aria-hidden": "false"
6533 "aria-selected": "true",
6538 _animate: function( toShow, toHide, data ) {
6539 var total, easing, duration,
6542 down = toShow.length &&
6543 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
6544 animate = this.options.animate || {},
6545 options = down && animate.down || animate,
6546 complete = function() {
6547 that._toggleComplete( data );
6550 if ( typeof options === "number" ) {
6553 if ( typeof options === "string" ) {
6556 // fall back from options to animation in case of partial down settings
6557 easing = easing || options.easing || animate.easing;
6558 duration = duration || options.duration || animate.duration;
6560 if ( !toHide.length ) {
6561 return toShow.animate( showProps, duration, easing, complete );
6563 if ( !toShow.length ) {
6564 return toHide.animate( hideProps, duration, easing, complete );
6567 total = toShow.show().outerHeight();
6568 toHide.animate( hideProps, {
6571 step: function( now, fx ) {
6572 fx.now = Math.round( now );
6577 .animate( showProps, {
6581 step: function( now, fx ) {
6582 fx.now = Math.round( now );
6583 if ( fx.prop !== "height" ) {
6585 } else if ( that.options.heightStyle !== "content" ) {
6586 fx.now = Math.round( total - toHide.outerHeight() - adjust );
6593 _toggleComplete: function( data ) {
6594 var toHide = data.oldPanel;
6597 .removeClass( "ui-accordion-content-active" )
6599 .removeClass( "ui-corner-top" )
6600 .addClass( "ui-corner-all" );
6602 // Work around for rendering bug in IE (#5421)
6603 if ( toHide.length ) {
6604 toHide.parent()[0].className = toHide.parent()[0].className;
6607 this._trigger( "activate", null, data );
6613 (function( $, undefined ) {
6615 // used to prevent race conditions with remote data sources
6616 var requestIndex = 0;
6618 $.widget( "ui.autocomplete", {
6620 defaultElement: "<input>",
6645 _create: function() {
6646 // Some browsers only repeat keydown events, not keypress events,
6647 // so we use the suppressKeyPress flag to determine if we've already
6648 // handled the keydown event. #7269
6649 // Unfortunately the code for & in keypress is the same as the up arrow,
6650 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
6651 // events when we know the keydown event was used to modify the
6652 // search term. #7799
6653 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
6654 nodeName = this.element[0].nodeName.toLowerCase(),
6655 isTextarea = nodeName === "textarea",
6656 isInput = nodeName === "input";
6659 // Textareas are always multi-line
6661 // Inputs are always single-line, even if inside a contentEditable element
6662 // IE also treats inputs as contentEditable
6664 // All other element types are determined by whether or not they're contentEditable
6665 this.element.prop( "isContentEditable" );
6667 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
6668 this.isNewMenu = true;
6671 .addClass( "ui-autocomplete-input" )
6672 .attr( "autocomplete", "off" );
6674 this._on( this.element, {
6675 keydown: function( event ) {
6676 /*jshint maxcomplexity:15*/
6677 if ( this.element.prop( "readOnly" ) ) {
6678 suppressKeyPress = true;
6679 suppressInput = true;
6680 suppressKeyPressRepeat = true;
6684 suppressKeyPress = false;
6685 suppressInput = false;
6686 suppressKeyPressRepeat = false;
6687 var keyCode = $.ui.keyCode;
6688 switch( event.keyCode ) {
6689 case keyCode.PAGE_UP:
6690 suppressKeyPress = true;
6691 this._move( "previousPage", event );
6693 case keyCode.PAGE_DOWN:
6694 suppressKeyPress = true;
6695 this._move( "nextPage", event );
6698 suppressKeyPress = true;
6699 this._keyEvent( "previous", event );
6702 suppressKeyPress = true;
6703 this._keyEvent( "next", event );
6706 case keyCode.NUMPAD_ENTER:
6707 // when menu is open and has focus
6708 if ( this.menu.active ) {
6709 // #6055 - Opera still allows the keypress to occur
6710 // which causes forms to submit
6711 suppressKeyPress = true;
6712 event.preventDefault();
6713 this.menu.select( event );
6717 if ( this.menu.active ) {
6718 this.menu.select( event );
6721 case keyCode.ESCAPE:
6722 if ( this.menu.element.is( ":visible" ) ) {
6723 this._value( this.term );
6724 this.close( event );
6725 // Different browsers have different default behavior for escape
6726 // Single press can mean undo or clear
6727 // Double press in IE means clear the whole form
6728 event.preventDefault();
6732 suppressKeyPressRepeat = true;
6733 // search timeout should be triggered before the input value is changed
6734 this._searchTimeout( event );
6738 keypress: function( event ) {
6739 if ( suppressKeyPress ) {
6740 suppressKeyPress = false;
6741 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
6742 event.preventDefault();
6746 if ( suppressKeyPressRepeat ) {
6750 // replicate some key handlers to allow them to repeat in Firefox and Opera
6751 var keyCode = $.ui.keyCode;
6752 switch( event.keyCode ) {
6753 case keyCode.PAGE_UP:
6754 this._move( "previousPage", event );
6756 case keyCode.PAGE_DOWN:
6757 this._move( "nextPage", event );
6760 this._keyEvent( "previous", event );
6763 this._keyEvent( "next", event );
6767 input: function( event ) {
6768 if ( suppressInput ) {
6769 suppressInput = false;
6770 event.preventDefault();
6773 this._searchTimeout( event );
6776 this.selectedItem = null;
6777 this.previous = this._value();
6779 blur: function( event ) {
6780 if ( this.cancelBlur ) {
6781 delete this.cancelBlur;
6785 clearTimeout( this.searching );
6786 this.close( event );
6787 this._change( event );
6792 this.menu = $( "<ul>" )
6793 .addClass( "ui-autocomplete ui-front" )
6794 .appendTo( this._appendTo() )
6796 // disable ARIA support, the live region takes care of that
6802 this._on( this.menu.element, {
6803 mousedown: function( event ) {
6804 // prevent moving focus out of the text field
6805 event.preventDefault();
6807 // IE doesn't prevent moving focus even with event.preventDefault()
6808 // so we set a flag to know when we should ignore the blur event
6809 this.cancelBlur = true;
6810 this._delay(function() {
6811 delete this.cancelBlur;
6814 // clicking on the scrollbar causes focus to shift to the body
6815 // but we can't detect a mouseup or a click immediately afterward
6816 // so we have to track the next mousedown and close the menu if
6817 // the user clicks somewhere outside of the autocomplete
6818 var menuElement = this.menu.element[ 0 ];
6819 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
6820 this._delay(function() {
6822 this.document.one( "mousedown", function( event ) {
6823 if ( event.target !== that.element[ 0 ] &&
6824 event.target !== menuElement &&
6825 !$.contains( menuElement, event.target ) ) {
6832 menufocus: function( event, ui ) {
6834 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
6835 if ( this.isNewMenu ) {
6836 this.isNewMenu = false;
6837 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
6840 this.document.one( "mousemove", function() {
6841 $( event.target ).trigger( event.originalEvent );
6848 var item = ui.item.data( "ui-autocomplete-item" );
6849 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
6850 // use value to match what will end up in the input, if it was a key event
6851 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
6852 this._value( item.value );
6855 // Normally the input is populated with the item's value as the
6856 // menu is navigated, causing screen readers to notice a change and
6857 // announce the item. Since the focus event was canceled, this doesn't
6858 // happen, so we update the live region so that screen readers can
6859 // still notice the change and announce it.
6860 this.liveRegion.text( item.value );
6863 menuselect: function( event, ui ) {
6864 var item = ui.item.data( "ui-autocomplete-item" ),
6865 previous = this.previous;
6867 // only trigger when focus was lost (click on menu)
6868 if ( this.element[0] !== this.document[0].activeElement ) {
6869 this.element.focus();
6870 this.previous = previous;
6871 // #6109 - IE triggers two focus events and the second
6872 // is asynchronous, so we need to reset the previous
6873 // term synchronously and asynchronously :-(
6874 this._delay(function() {
6875 this.previous = previous;
6876 this.selectedItem = item;
6880 if ( false !== this._trigger( "select", event, { item: item } ) ) {
6881 this._value( item.value );
6883 // reset the term after the select event
6884 // this allows custom select handling to work properly
6885 this.term = this._value();
6887 this.close( event );
6888 this.selectedItem = item;
6892 this.liveRegion = $( "<span>", {
6894 "aria-live": "polite"
6896 .addClass( "ui-helper-hidden-accessible" )
6897 .insertBefore( this.element );
6899 // turning off autocomplete prevents the browser from remembering the
6900 // value when navigating through history, so we re-enable autocomplete
6901 // if the page is unloaded before the widget is destroyed. #7790
6902 this._on( this.window, {
6903 beforeunload: function() {
6904 this.element.removeAttr( "autocomplete" );
6909 _destroy: function() {
6910 clearTimeout( this.searching );
6912 .removeClass( "ui-autocomplete-input" )
6913 .removeAttr( "autocomplete" );
6914 this.menu.element.remove();
6915 this.liveRegion.remove();
6918 _setOption: function( key, value ) {
6919 this._super( key, value );
6920 if ( key === "source" ) {
6923 if ( key === "appendTo" ) {
6924 this.menu.element.appendTo( this._appendTo() );
6926 if ( key === "disabled" && value && this.xhr ) {
6931 _appendTo: function() {
6932 var element = this.options.appendTo;
6935 element = element.jquery || element.nodeType ?
6937 this.document.find( element ).eq( 0 );
6941 element = this.element.closest( ".ui-front" );
6944 if ( !element.length ) {
6945 element = this.document[0].body;
6951 _initSource: function() {
6954 if ( $.isArray(this.options.source) ) {
6955 array = this.options.source;
6956 this.source = function( request, response ) {
6957 response( $.ui.autocomplete.filter( array, request.term ) );
6959 } else if ( typeof this.options.source === "string" ) {
6960 url = this.options.source;
6961 this.source = function( request, response ) {
6969 success: function( data ) {
6978 this.source = this.options.source;
6982 _searchTimeout: function( event ) {
6983 clearTimeout( this.searching );
6984 this.searching = this._delay(function() {
6985 // only search if the value has changed
6986 if ( this.term !== this._value() ) {
6987 this.selectedItem = null;
6988 this.search( null, event );
6990 }, this.options.delay );
6993 search: function( value, event ) {
6994 value = value != null ? value : this._value();
6996 // always save the actual value, not the one passed as an argument
6997 this.term = this._value();
6999 if ( value.length < this.options.minLength ) {
7000 return this.close( event );
7003 if ( this._trigger( "search", event ) === false ) {
7007 return this._search( value );
7010 _search: function( value ) {
7012 this.element.addClass( "ui-autocomplete-loading" );
7013 this.cancelSearch = false;
7015 this.source( { term: value }, this._response() );
7018 _response: function() {
7020 index = ++requestIndex;
7022 return function( content ) {
7023 if ( index === requestIndex ) {
7024 that.__response( content );
7028 if ( !that.pending ) {
7029 that.element.removeClass( "ui-autocomplete-loading" );
7034 __response: function( content ) {
7036 content = this._normalize( content );
7038 this._trigger( "response", null, { content: content } );
7039 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
7040 this._suggest( content );
7041 this._trigger( "open" );
7043 // use ._close() instead of .close() so we don't cancel future searches
7048 close: function( event ) {
7049 this.cancelSearch = true;
7050 this._close( event );
7053 _close: function( event ) {
7054 if ( this.menu.element.is( ":visible" ) ) {
7055 this.menu.element.hide();
7057 this.isNewMenu = true;
7058 this._trigger( "close", event );
7062 _change: function( event ) {
7063 if ( this.previous !== this._value() ) {
7064 this._trigger( "change", event, { item: this.selectedItem } );
7068 _normalize: function( items ) {
7069 // assume all items have the right format when the first item is complete
7070 if ( items.length && items[0].label && items[0].value ) {
7073 return $.map( items, function( item ) {
7074 if ( typeof item === "string" ) {
7081 label: item.label || item.value,
7082 value: item.value || item.label
7087 _suggest: function( items ) {
7088 var ul = this.menu.element.empty();
7089 this._renderMenu( ul, items );
7090 this.isNewMenu = true;
7091 this.menu.refresh();
7093 // size and position menu
7096 ul.position( $.extend({
7098 }, this.options.position ));
7100 if ( this.options.autoFocus ) {
7105 _resizeMenu: function() {
7106 var ul = this.menu.element;
7107 ul.outerWidth( Math.max(
7108 // Firefox wraps long text (possibly a rounding bug)
7109 // so we add 1px to avoid the wrapping (#7513)
7110 ul.width( "" ).outerWidth() + 1,
7111 this.element.outerWidth()
7115 _renderMenu: function( ul, items ) {
7117 $.each( items, function( index, item ) {
7118 that._renderItemData( ul, item );
7122 _renderItemData: function( ul, item ) {
7123 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
7126 _renderItem: function( ul, item ) {
7128 .append( $( "<a>" ).text( item.label ) )
7132 _move: function( direction, event ) {
7133 if ( !this.menu.element.is( ":visible" ) ) {
7134 this.search( null, event );
7137 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
7138 this.menu.isLastItem() && /^next/.test( direction ) ) {
7139 this._value( this.term );
7143 this.menu[ direction ]( event );
7146 widget: function() {
7147 return this.menu.element;
7150 _value: function() {
7151 return this.valueMethod.apply( this.element, arguments );
7154 _keyEvent: function( keyEvent, event ) {
7155 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
7156 this._move( keyEvent, event );
7158 // prevents moving cursor to beginning/end of the text field in some browsers
7159 event.preventDefault();
7164 $.extend( $.ui.autocomplete, {
7165 escapeRegex: function( value ) {
7166 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
7168 filter: function(array, term) {
7169 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
7170 return $.grep( array, function(value) {
7171 return matcher.test( value.label || value.value || value );
7177 // live region extension, adding a `messages` option
7178 // NOTE: This is an experimental API. We are still investigating
7179 // a full solution for string manipulation and internationalization.
7180 $.widget( "ui.autocomplete", $.ui.autocomplete, {
7183 noResults: "No search results.",
7184 results: function( amount ) {
7185 return amount + ( amount > 1 ? " results are" : " result is" ) +
7186 " available, use up and down arrow keys to navigate.";
7191 __response: function( content ) {
7193 this._superApply( arguments );
7194 if ( this.options.disabled || this.cancelSearch ) {
7197 if ( content && content.length ) {
7198 message = this.options.messages.results( content.length );
7200 message = this.options.messages.noResults;
7202 this.liveRegion.text( message );
7208 (function( $, undefined ) {
7210 var lastActive, startXPos, startYPos, clickDragged,
7211 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
7212 stateClasses = "ui-state-hover ui-state-active ",
7213 typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
7214 formResetHandler = function() {
7215 var form = $( this );
7216 setTimeout(function() {
7217 form.find( ":ui-button" ).button( "refresh" );
7220 radioGroup = function( radio ) {
7221 var name = radio.name,
7225 name = name.replace( /'/g, "\\'" );
7227 radios = $( form ).find( "[name='" + name + "']" );
7229 radios = $( "[name='" + name + "']", radio.ownerDocument )
7230 .filter(function() {
7238 $.widget( "ui.button", {
7240 defaultElement: "<button>",
7250 _create: function() {
7251 this.element.closest( "form" )
7252 .unbind( "reset" + this.eventNamespace )
7253 .bind( "reset" + this.eventNamespace, formResetHandler );
7255 if ( typeof this.options.disabled !== "boolean" ) {
7256 this.options.disabled = !!this.element.prop( "disabled" );
7258 this.element.prop( "disabled", this.options.disabled );
7261 this._determineButtonType();
7262 this.hasTitle = !!this.buttonElement.attr( "title" );
7265 options = this.options,
7266 toggleButton = this.type === "checkbox" || this.type === "radio",
7267 activeClass = !toggleButton ? "ui-state-active" : "",
7268 focusClass = "ui-state-focus";
7270 if ( options.label === null ) {
7271 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
7274 this._hoverable( this.buttonElement );
7277 .addClass( baseClasses )
7278 .attr( "role", "button" )
7279 .bind( "mouseenter" + this.eventNamespace, function() {
7280 if ( options.disabled ) {
7283 if ( this === lastActive ) {
7284 $( this ).addClass( "ui-state-active" );
7287 .bind( "mouseleave" + this.eventNamespace, function() {
7288 if ( options.disabled ) {
7291 $( this ).removeClass( activeClass );
7293 .bind( "click" + this.eventNamespace, function( event ) {
7294 if ( options.disabled ) {
7295 event.preventDefault();
7296 event.stopImmediatePropagation();
7301 .bind( "focus" + this.eventNamespace, function() {
7302 // no need to check disabled, focus won't be triggered anyway
7303 that.buttonElement.addClass( focusClass );
7305 .bind( "blur" + this.eventNamespace, function() {
7306 that.buttonElement.removeClass( focusClass );
7309 if ( toggleButton ) {
7310 this.element.bind( "change" + this.eventNamespace, function() {
7311 if ( clickDragged ) {
7316 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
7317 // prevents issue where button state changes but checkbox/radio checked state
7318 // does not in Firefox (see ticket #6970)
7320 .bind( "mousedown" + this.eventNamespace, function( event ) {
7321 if ( options.disabled ) {
7324 clickDragged = false;
7325 startXPos = event.pageX;
7326 startYPos = event.pageY;
7328 .bind( "mouseup" + this.eventNamespace, function( event ) {
7329 if ( options.disabled ) {
7332 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
7333 clickDragged = true;
7338 if ( this.type === "checkbox" ) {
7339 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7340 if ( options.disabled || clickDragged ) {
7344 } else if ( this.type === "radio" ) {
7345 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7346 if ( options.disabled || clickDragged ) {
7349 $( this ).addClass( "ui-state-active" );
7350 that.buttonElement.attr( "aria-pressed", "true" );
7352 var radio = that.element[ 0 ];
7356 return $( this ).button( "widget" )[ 0 ];
7358 .removeClass( "ui-state-active" )
7359 .attr( "aria-pressed", "false" );
7363 .bind( "mousedown" + this.eventNamespace, function() {
7364 if ( options.disabled ) {
7367 $( this ).addClass( "ui-state-active" );
7369 that.document.one( "mouseup", function() {
7373 .bind( "mouseup" + this.eventNamespace, function() {
7374 if ( options.disabled ) {
7377 $( this ).removeClass( "ui-state-active" );
7379 .bind( "keydown" + this.eventNamespace, function(event) {
7380 if ( options.disabled ) {
7383 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
7384 $( this ).addClass( "ui-state-active" );
7387 // see #8559, we bind to blur here in case the button element loses
7388 // focus between keydown and keyup, it would be left in an "active" state
7389 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
7390 $( this ).removeClass( "ui-state-active" );
7393 if ( this.buttonElement.is("a") ) {
7394 this.buttonElement.keyup(function(event) {
7395 if ( event.keyCode === $.ui.keyCode.SPACE ) {
7396 // TODO pass through original event correctly (just as 2nd argument doesn't work)
7403 // TODO: pull out $.Widget's handling for the disabled option into
7404 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
7405 // be overridden by individual plugins
7406 this._setOption( "disabled", options.disabled );
7407 this._resetButton();
7410 _determineButtonType: function() {
7411 var ancestor, labelSelector, checked;
7413 if ( this.element.is("[type=checkbox]") ) {
7414 this.type = "checkbox";
7415 } else if ( this.element.is("[type=radio]") ) {
7416 this.type = "radio";
7417 } else if ( this.element.is("input") ) {
7418 this.type = "input";
7420 this.type = "button";
7423 if ( this.type === "checkbox" || this.type === "radio" ) {
7424 // we don't search against the document in case the element
7425 // is disconnected from the DOM
7426 ancestor = this.element.parents().last();
7427 labelSelector = "label[for='" + this.element.attr("id") + "']";
7428 this.buttonElement = ancestor.find( labelSelector );
7429 if ( !this.buttonElement.length ) {
7430 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
7431 this.buttonElement = ancestor.filter( labelSelector );
7432 if ( !this.buttonElement.length ) {
7433 this.buttonElement = ancestor.find( labelSelector );
7436 this.element.addClass( "ui-helper-hidden-accessible" );
7438 checked = this.element.is( ":checked" );
7440 this.buttonElement.addClass( "ui-state-active" );
7442 this.buttonElement.prop( "aria-pressed", checked );
7444 this.buttonElement = this.element;
7448 widget: function() {
7449 return this.buttonElement;
7452 _destroy: function() {
7454 .removeClass( "ui-helper-hidden-accessible" );
7456 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
7457 .removeAttr( "role" )
7458 .removeAttr( "aria-pressed" )
7459 .html( this.buttonElement.find(".ui-button-text").html() );
7461 if ( !this.hasTitle ) {
7462 this.buttonElement.removeAttr( "title" );
7466 _setOption: function( key, value ) {
7467 this._super( key, value );
7468 if ( key === "disabled" ) {
7470 this.element.prop( "disabled", true );
7472 this.element.prop( "disabled", false );
7476 this._resetButton();
7479 refresh: function() {
7481 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
7483 if ( isDisabled !== this.options.disabled ) {
7484 this._setOption( "disabled", isDisabled );
7486 if ( this.type === "radio" ) {
7487 radioGroup( this.element[0] ).each(function() {
7488 if ( $( this ).is( ":checked" ) ) {
7489 $( this ).button( "widget" )
7490 .addClass( "ui-state-active" )
7491 .attr( "aria-pressed", "true" );
7493 $( this ).button( "widget" )
7494 .removeClass( "ui-state-active" )
7495 .attr( "aria-pressed", "false" );
7498 } else if ( this.type === "checkbox" ) {
7499 if ( this.element.is( ":checked" ) ) {
7501 .addClass( "ui-state-active" )
7502 .attr( "aria-pressed", "true" );
7505 .removeClass( "ui-state-active" )
7506 .attr( "aria-pressed", "false" );
7511 _resetButton: function() {
7512 if ( this.type === "input" ) {
7513 if ( this.options.label ) {
7514 this.element.val( this.options.label );
7518 var buttonElement = this.buttonElement.removeClass( typeClasses ),
7519 buttonText = $( "<span></span>", this.document[0] )
7520 .addClass( "ui-button-text" )
7521 .html( this.options.label )
7522 .appendTo( buttonElement.empty() )
7524 icons = this.options.icons,
7525 multipleIcons = icons.primary && icons.secondary,
7528 if ( icons.primary || icons.secondary ) {
7529 if ( this.options.text ) {
7530 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
7533 if ( icons.primary ) {
7534 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
7537 if ( icons.secondary ) {
7538 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
7541 if ( !this.options.text ) {
7542 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
7544 if ( !this.hasTitle ) {
7545 buttonElement.attr( "title", $.trim( buttonText ) );
7549 buttonClasses.push( "ui-button-text-only" );
7551 buttonElement.addClass( buttonClasses.join( " " ) );
7555 $.widget( "ui.buttonset", {
7558 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
7561 _create: function() {
7562 this.element.addClass( "ui-buttonset" );
7569 _setOption: function( key, value ) {
7570 if ( key === "disabled" ) {
7571 this.buttons.button( "option", key, value );
7574 this._super( key, value );
7577 refresh: function() {
7578 var rtl = this.element.css( "direction" ) === "rtl";
7580 this.buttons = this.element.find( this.options.items )
7581 .filter( ":ui-button" )
7582 .button( "refresh" )
7584 .not( ":ui-button" )
7588 return $( this ).button( "widget" )[ 0 ];
7590 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
7592 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
7595 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
7600 _destroy: function() {
7601 this.element.removeClass( "ui-buttonset" );
7604 return $( this ).button( "widget" )[ 0 ];
7606 .removeClass( "ui-corner-left ui-corner-right" )
7608 .button( "destroy" );
7614 (function( $, undefined ) {
7616 $.extend($.ui, { datepicker: { version: "1.10.3" } });
7618 var PROP_NAME = "datepicker",
7621 /* Date picker manager.
7622 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
7623 Settings for (groups of) date pickers are maintained in an instance object,
7624 allowing multiple different settings on the same page. */
7626 function Datepicker() {
7627 this._curInst = null; // The current instance in use
7628 this._keyEvent = false; // If the last event was a key event
7629 this._disabledInputs = []; // List of date picker inputs that have been disabled
7630 this._datepickerShowing = false; // True if the popup picker is showing , false if not
7631 this._inDialog = false; // True if showing within a "dialog", false if not
7632 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
7633 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
7634 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
7635 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
7636 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
7637 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
7638 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
7639 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
7640 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
7641 this.regional = []; // Available regional settings, indexed by language code
7642 this.regional[""] = { // Default regional settings
7643 closeText: "Done", // Display text for close link
7644 prevText: "Prev", // Display text for previous month link
7645 nextText: "Next", // Display text for next month link
7646 currentText: "Today", // Display text for current month link
7647 monthNames: ["January","February","March","April","May","June",
7648 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
7649 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
7650 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
7651 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
7652 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
7653 weekHeader: "Wk", // Column header for week of the year
7654 dateFormat: "mm/dd/yy", // See format options on parseDate
7655 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
7656 isRTL: false, // True if right-to-left language, false if left-to-right
7657 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
7658 yearSuffix: "" // Additional text to append to the year in the month headers
7660 this._defaults = { // Global defaults for all the date picker instances
7661 showOn: "focus", // "focus" for popup on focus,
7662 // "button" for trigger button, or "both" for either
7663 showAnim: "fadeIn", // Name of jQuery animation for popup
7664 showOptions: {}, // Options for enhanced animations
7665 defaultDate: null, // Used when field is blank: actual date,
7666 // +/-number for offset from today, null for today
7667 appendText: "", // Display text following the input box, e.g. showing the format
7668 buttonText: "...", // Text for trigger button
7669 buttonImage: "", // URL for trigger button image
7670 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
7671 hideIfNoPrevNext: false, // True to hide next/previous month links
7672 // if not applicable, false to just disable them
7673 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
7674 gotoCurrent: false, // True if today link goes back to current selection instead
7675 changeMonth: false, // True if month can be selected directly, false if only prev/next
7676 changeYear: false, // True if year can be selected directly, false if only prev/next
7677 yearRange: "c-10:c+10", // Range of years to display in drop-down,
7678 // either relative to today's year (-nn:+nn), relative to currently displayed year
7679 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
7680 showOtherMonths: false, // True to show dates in other months, false to leave blank
7681 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
7682 showWeek: false, // True to show week of the year, false to not show it
7683 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
7684 // takes a Date and returns the number of the week for it
7685 shortYearCutoff: "+10", // Short year values < this are in the current century,
7686 // > this are in the previous century,
7687 // string value starting with "+" for current year + value
7688 minDate: null, // The earliest selectable date, or null for no limit
7689 maxDate: null, // The latest selectable date, or null for no limit
7690 duration: "fast", // Duration of display/closure
7691 beforeShowDay: null, // Function that takes a date and returns an array with
7692 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
7693 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
7694 beforeShow: null, // Function that takes an input field and
7695 // returns a set of custom settings for the date picker
7696 onSelect: null, // Define a callback function when a date is selected
7697 onChangeMonthYear: null, // Define a callback function when the month or year is changed
7698 onClose: null, // Define a callback function when the datepicker is closed
7699 numberOfMonths: 1, // Number of months to show at a time
7700 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
7701 stepMonths: 1, // Number of months to step back/forward
7702 stepBigMonths: 12, // Number of months to step back/forward for the big links
7703 altField: "", // Selector for an alternate field to store selected dates into
7704 altFormat: "", // The date format to use for the alternate field
7705 constrainInput: true, // The input is constrained by the current date format
7706 showButtonPanel: false, // True to show button panel, false to not show it
7707 autoSize: false, // True to size the input for the date format, false to leave as is
7708 disabled: false // The initial disabled state
7710 $.extend(this._defaults, this.regional[""]);
7711 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
7714 $.extend(Datepicker.prototype, {
7715 /* Class name added to elements to indicate already configured with a date picker. */
7716 markerClassName: "hasDatepicker",
7718 //Keep track of the maximum number of rows displayed (see #7043)
7721 // TODO rename to "widget" when switching to widget factory
7722 _widgetDatepicker: function() {
7726 /* Override the default settings for all instances of the date picker.
7727 * @param settings object - the new settings to use as defaults (anonymous object)
7728 * @return the manager object
7730 setDefaults: function(settings) {
7731 extendRemove(this._defaults, settings || {});
7735 /* Attach the date picker to a jQuery selection.
7736 * @param target element - the target input field or division or span
7737 * @param settings object - the new settings to use for this date picker instance (anonymous)
7739 _attachDatepicker: function(target, settings) {
7740 var nodeName, inline, inst;
7741 nodeName = target.nodeName.toLowerCase();
7742 inline = (nodeName === "div" || nodeName === "span");
7745 target.id = "dp" + this.uuid;
7747 inst = this._newInst($(target), inline);
7748 inst.settings = $.extend({}, settings || {});
7749 if (nodeName === "input") {
7750 this._connectDatepicker(target, inst);
7751 } else if (inline) {
7752 this._inlineDatepicker(target, inst);
7756 /* Create a new instance object. */
7757 _newInst: function(target, inline) {
7758 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
7759 return {id: id, input: target, // associated target
7760 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
7761 drawMonth: 0, drawYear: 0, // month being drawn
7762 inline: inline, // is datepicker inline or not
7763 dpDiv: (!inline ? this.dpDiv : // presentation div
7764 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
7767 /* Attach the date picker to an input field. */
7768 _connectDatepicker: function(target, inst) {
7769 var input = $(target);
7770 inst.append = $([]);
7771 inst.trigger = $([]);
7772 if (input.hasClass(this.markerClassName)) {
7775 this._attachments(input, inst);
7776 input.addClass(this.markerClassName).keydown(this._doKeyDown).
7777 keypress(this._doKeyPress).keyup(this._doKeyUp);
7778 this._autoSize(inst);
7779 $.data(target, PROP_NAME, inst);
7780 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
7781 if( inst.settings.disabled ) {
7782 this._disableDatepicker( target );
7786 /* Make attachments based on settings. */
7787 _attachments: function(input, inst) {
7788 var showOn, buttonText, buttonImage,
7789 appendText = this._get(inst, "appendText"),
7790 isRTL = this._get(inst, "isRTL");
7793 inst.append.remove();
7796 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
7797 input[isRTL ? "before" : "after"](inst.append);
7800 input.unbind("focus", this._showDatepicker);
7803 inst.trigger.remove();
7806 showOn = this._get(inst, "showOn");
7807 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
7808 input.focus(this._showDatepicker);
7810 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
7811 buttonText = this._get(inst, "buttonText");
7812 buttonImage = this._get(inst, "buttonImage");
7813 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
7814 $("<img/>").addClass(this._triggerClass).
7815 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
7816 $("<button type='button'></button>").addClass(this._triggerClass).
7817 html(!buttonImage ? buttonText : $("<img/>").attr(
7818 { src:buttonImage, alt:buttonText, title:buttonText })));
7819 input[isRTL ? "before" : "after"](inst.trigger);
7820 inst.trigger.click(function() {
7821 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
7822 $.datepicker._hideDatepicker();
7823 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
7824 $.datepicker._hideDatepicker();
7825 $.datepicker._showDatepicker(input[0]);
7827 $.datepicker._showDatepicker(input[0]);
7834 /* Apply the maximum length for the date format. */
7835 _autoSize: function(inst) {
7836 if (this._get(inst, "autoSize") && !inst.inline) {
7837 var findMax, max, maxI, i,
7838 date = new Date(2009, 12 - 1, 20), // Ensure double digits
7839 dateFormat = this._get(inst, "dateFormat");
7841 if (dateFormat.match(/[DM]/)) {
7842 findMax = function(names) {
7845 for (i = 0; i < names.length; i++) {
7846 if (names[i].length > max) {
7847 max = names[i].length;
7853 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
7854 "monthNames" : "monthNamesShort"))));
7855 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
7856 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
7858 inst.input.attr("size", this._formatDate(inst, date).length);
7862 /* Attach an inline date picker to a div. */
7863 _inlineDatepicker: function(target, inst) {
7864 var divSpan = $(target);
7865 if (divSpan.hasClass(this.markerClassName)) {
7868 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
7869 $.data(target, PROP_NAME, inst);
7870 this._setDate(inst, this._getDefaultDate(inst), true);
7871 this._updateDatepicker(inst);
7872 this._updateAlternate(inst);
7873 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
7874 if( inst.settings.disabled ) {
7875 this._disableDatepicker( target );
7877 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
7878 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
7879 inst.dpDiv.css( "display", "block" );
7882 /* Pop-up the date picker in a "dialog" box.
7883 * @param input element - ignored
7884 * @param date string or Date - the initial date to display
7885 * @param onSelect function - the function to call when a date is selected
7886 * @param settings object - update the dialog date picker instance's settings (anonymous object)
7887 * @param pos int[2] - coordinates for the dialog's position within the screen or
7888 * event - with x/y coordinates or
7889 * leave empty for default (screen centre)
7890 * @return the manager object
7892 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
7893 var id, browserWidth, browserHeight, scrollX, scrollY,
7894 inst = this._dialogInst; // internal instance
7898 id = "dp" + this.uuid;
7899 this._dialogInput = $("<input type='text' id='" + id +
7900 "' style='position: absolute; top: -100px; width: 0px;'/>");
7901 this._dialogInput.keydown(this._doKeyDown);
7902 $("body").append(this._dialogInput);
7903 inst = this._dialogInst = this._newInst(this._dialogInput, false);
7905 $.data(this._dialogInput[0], PROP_NAME, inst);
7907 extendRemove(inst.settings, settings || {});
7908 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
7909 this._dialogInput.val(date);
7911 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
7913 browserWidth = document.documentElement.clientWidth;
7914 browserHeight = document.documentElement.clientHeight;
7915 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
7916 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
7917 this._pos = // should use actual width/height below
7918 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
7921 // move input on screen for focus, but hidden behind dialog
7922 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
7923 inst.settings.onSelect = onSelect;
7924 this._inDialog = true;
7925 this.dpDiv.addClass(this._dialogClass);
7926 this._showDatepicker(this._dialogInput[0]);
7928 $.blockUI(this.dpDiv);
7930 $.data(this._dialogInput[0], PROP_NAME, inst);
7934 /* Detach a datepicker from its control.
7935 * @param target element - the target input field or division or span
7937 _destroyDatepicker: function(target) {
7939 $target = $(target),
7940 inst = $.data(target, PROP_NAME);
7942 if (!$target.hasClass(this.markerClassName)) {
7946 nodeName = target.nodeName.toLowerCase();
7947 $.removeData(target, PROP_NAME);
7948 if (nodeName === "input") {
7949 inst.append.remove();
7950 inst.trigger.remove();
7951 $target.removeClass(this.markerClassName).
7952 unbind("focus", this._showDatepicker).
7953 unbind("keydown", this._doKeyDown).
7954 unbind("keypress", this._doKeyPress).
7955 unbind("keyup", this._doKeyUp);
7956 } else if (nodeName === "div" || nodeName === "span") {
7957 $target.removeClass(this.markerClassName).empty();
7961 /* Enable the date picker to a jQuery selection.
7962 * @param target element - the target input field or division or span
7964 _enableDatepicker: function(target) {
7965 var nodeName, inline,
7966 $target = $(target),
7967 inst = $.data(target, PROP_NAME);
7969 if (!$target.hasClass(this.markerClassName)) {
7973 nodeName = target.nodeName.toLowerCase();
7974 if (nodeName === "input") {
7975 target.disabled = false;
7976 inst.trigger.filter("button").
7977 each(function() { this.disabled = false; }).end().
7978 filter("img").css({opacity: "1.0", cursor: ""});
7979 } else if (nodeName === "div" || nodeName === "span") {
7980 inline = $target.children("." + this._inlineClass);
7981 inline.children().removeClass("ui-state-disabled");
7982 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7983 prop("disabled", false);
7985 this._disabledInputs = $.map(this._disabledInputs,
7986 function(value) { return (value === target ? null : value); }); // delete entry
7989 /* Disable the date picker to a jQuery selection.
7990 * @param target element - the target input field or division or span
7992 _disableDatepicker: function(target) {
7993 var nodeName, inline,
7994 $target = $(target),
7995 inst = $.data(target, PROP_NAME);
7997 if (!$target.hasClass(this.markerClassName)) {
8001 nodeName = target.nodeName.toLowerCase();
8002 if (nodeName === "input") {
8003 target.disabled = true;
8004 inst.trigger.filter("button").
8005 each(function() { this.disabled = true; }).end().
8006 filter("img").css({opacity: "0.5", cursor: "default"});
8007 } else if (nodeName === "div" || nodeName === "span") {
8008 inline = $target.children("." + this._inlineClass);
8009 inline.children().addClass("ui-state-disabled");
8010 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
8011 prop("disabled", true);
8013 this._disabledInputs = $.map(this._disabledInputs,
8014 function(value) { return (value === target ? null : value); }); // delete entry
8015 this._disabledInputs[this._disabledInputs.length] = target;
8018 /* Is the first field in a jQuery collection disabled as a datepicker?
8019 * @param target element - the target input field or division or span
8020 * @return boolean - true if disabled, false if enabled
8022 _isDisabledDatepicker: function(target) {
8026 for (var i = 0; i < this._disabledInputs.length; i++) {
8027 if (this._disabledInputs[i] === target) {
8034 /* Retrieve the instance data for the target control.
8035 * @param target element - the target input field or division or span
8036 * @return object - the associated instance data
8037 * @throws error if a jQuery problem getting data
8039 _getInst: function(target) {
8041 return $.data(target, PROP_NAME);
8044 throw "Missing instance data for this datepicker";
8048 /* Update or retrieve the settings for a date picker attached to an input field or division.
8049 * @param target element - the target input field or division or span
8050 * @param name object - the new settings to update or
8051 * string - the name of the setting to change or retrieve,
8052 * when retrieving also "all" for all instance settings or
8053 * "defaults" for all global defaults
8054 * @param value any - the new value for the setting
8055 * (omit if above is an object or to retrieve a value)
8057 _optionDatepicker: function(target, name, value) {
8058 var settings, date, minDate, maxDate,
8059 inst = this._getInst(target);
8061 if (arguments.length === 2 && typeof name === "string") {
8062 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
8063 (inst ? (name === "all" ? $.extend({}, inst.settings) :
8064 this._get(inst, name)) : null));
8067 settings = name || {};
8068 if (typeof name === "string") {
8070 settings[name] = value;
8074 if (this._curInst === inst) {
8075 this._hideDatepicker();
8078 date = this._getDateDatepicker(target, true);
8079 minDate = this._getMinMaxDate(inst, "min");
8080 maxDate = this._getMinMaxDate(inst, "max");
8081 extendRemove(inst.settings, settings);
8082 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
8083 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
8084 inst.settings.minDate = this._formatDate(inst, minDate);
8086 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
8087 inst.settings.maxDate = this._formatDate(inst, maxDate);
8089 if ( "disabled" in settings ) {
8090 if ( settings.disabled ) {
8091 this._disableDatepicker(target);
8093 this._enableDatepicker(target);
8096 this._attachments($(target), inst);
8097 this._autoSize(inst);
8098 this._setDate(inst, date);
8099 this._updateAlternate(inst);
8100 this._updateDatepicker(inst);
8104 // change method deprecated
8105 _changeDatepicker: function(target, name, value) {
8106 this._optionDatepicker(target, name, value);
8109 /* Redraw the date picker attached to an input field or division.
8110 * @param target element - the target input field or division or span
8112 _refreshDatepicker: function(target) {
8113 var inst = this._getInst(target);
8115 this._updateDatepicker(inst);
8119 /* Set the dates for a jQuery selection.
8120 * @param target element - the target input field or division or span
8121 * @param date Date - the new date
8123 _setDateDatepicker: function(target, date) {
8124 var inst = this._getInst(target);
8126 this._setDate(inst, date);
8127 this._updateDatepicker(inst);
8128 this._updateAlternate(inst);
8132 /* Get the date(s) for the first entry in a jQuery selection.
8133 * @param target element - the target input field or division or span
8134 * @param noDefault boolean - true if no default date is to be used
8135 * @return Date - the current date
8137 _getDateDatepicker: function(target, noDefault) {
8138 var inst = this._getInst(target);
8139 if (inst && !inst.inline) {
8140 this._setDateFromField(inst, noDefault);
8142 return (inst ? this._getDate(inst) : null);
8145 /* Handle keystrokes. */
8146 _doKeyDown: function(event) {
8147 var onSelect, dateStr, sel,
8148 inst = $.datepicker._getInst(event.target),
8150 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
8152 inst._keyEvent = true;
8153 if ($.datepicker._datepickerShowing) {
8154 switch (event.keyCode) {
8155 case 9: $.datepicker._hideDatepicker();
8157 break; // hide on tab out
8158 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
8159 $.datepicker._currentClass + ")", inst.dpDiv);
8161 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
8164 onSelect = $.datepicker._get(inst, "onSelect");
8166 dateStr = $.datepicker._formatDate(inst);
8168 // trigger custom callback
8169 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
8171 $.datepicker._hideDatepicker();
8174 return false; // don't submit the form
8175 case 27: $.datepicker._hideDatepicker();
8176 break; // hide on escape
8177 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8178 -$.datepicker._get(inst, "stepBigMonths") :
8179 -$.datepicker._get(inst, "stepMonths")), "M");
8180 break; // previous month/year on page up/+ ctrl
8181 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8182 +$.datepicker._get(inst, "stepBigMonths") :
8183 +$.datepicker._get(inst, "stepMonths")), "M");
8184 break; // next month/year on page down/+ ctrl
8185 case 35: if (event.ctrlKey || event.metaKey) {
8186 $.datepicker._clearDate(event.target);
8188 handled = event.ctrlKey || event.metaKey;
8189 break; // clear on ctrl or command +end
8190 case 36: if (event.ctrlKey || event.metaKey) {
8191 $.datepicker._gotoToday(event.target);
8193 handled = event.ctrlKey || event.metaKey;
8194 break; // current on ctrl or command +home
8195 case 37: if (event.ctrlKey || event.metaKey) {
8196 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
8198 handled = event.ctrlKey || event.metaKey;
8199 // -1 day on ctrl or command +left
8200 if (event.originalEvent.altKey) {
8201 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8202 -$.datepicker._get(inst, "stepBigMonths") :
8203 -$.datepicker._get(inst, "stepMonths")), "M");
8205 // next month/year on alt +left on Mac
8207 case 38: if (event.ctrlKey || event.metaKey) {
8208 $.datepicker._adjustDate(event.target, -7, "D");
8210 handled = event.ctrlKey || event.metaKey;
8211 break; // -1 week on ctrl or command +up
8212 case 39: if (event.ctrlKey || event.metaKey) {
8213 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
8215 handled = event.ctrlKey || event.metaKey;
8216 // +1 day on ctrl or command +right
8217 if (event.originalEvent.altKey) {
8218 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8219 +$.datepicker._get(inst, "stepBigMonths") :
8220 +$.datepicker._get(inst, "stepMonths")), "M");
8222 // next month/year on alt +right
8224 case 40: if (event.ctrlKey || event.metaKey) {
8225 $.datepicker._adjustDate(event.target, +7, "D");
8227 handled = event.ctrlKey || event.metaKey;
8228 break; // +1 week on ctrl or command +down
8229 default: handled = false;
8231 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
8232 $.datepicker._showDatepicker(this);
8238 event.preventDefault();
8239 event.stopPropagation();
8243 /* Filter entered characters - based on date format. */
8244 _doKeyPress: function(event) {
8246 inst = $.datepicker._getInst(event.target);
8248 if ($.datepicker._get(inst, "constrainInput")) {
8249 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
8250 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
8251 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
8255 /* Synchronise manual entry and field/alternate field. */
8256 _doKeyUp: function(event) {
8258 inst = $.datepicker._getInst(event.target);
8260 if (inst.input.val() !== inst.lastVal) {
8262 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
8263 (inst.input ? inst.input.val() : null),
8264 $.datepicker._getFormatConfig(inst));
8266 if (date) { // only if valid
8267 $.datepicker._setDateFromField(inst);
8268 $.datepicker._updateAlternate(inst);
8269 $.datepicker._updateDatepicker(inst);
8278 /* Pop-up the date picker for a given input field.
8279 * If false returned from beforeShow event handler do not show.
8280 * @param input element - the input field attached to the date picker or
8281 * event - if triggered by focus
8283 _showDatepicker: function(input) {
8284 input = input.target || input;
8285 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
8286 input = $("input", input.parentNode)[0];
8289 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
8293 var inst, beforeShow, beforeShowSettings, isFixed,
8294 offset, showAnim, duration;
8296 inst = $.datepicker._getInst(input);
8297 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
8298 $.datepicker._curInst.dpDiv.stop(true, true);
8299 if ( inst && $.datepicker._datepickerShowing ) {
8300 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
8304 beforeShow = $.datepicker._get(inst, "beforeShow");
8305 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
8306 if(beforeShowSettings === false){
8309 extendRemove(inst.settings, beforeShowSettings);
8311 inst.lastVal = null;
8312 $.datepicker._lastInput = input;
8313 $.datepicker._setDateFromField(inst);
8315 if ($.datepicker._inDialog) { // hide cursor
8318 if (!$.datepicker._pos) { // position below input
8319 $.datepicker._pos = $.datepicker._findPos(input);
8320 $.datepicker._pos[1] += input.offsetHeight; // add the height
8324 $(input).parents().each(function() {
8325 isFixed |= $(this).css("position") === "fixed";
8329 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
8330 $.datepicker._pos = null;
8331 //to avoid flashes on Firefox
8333 // determine sizing offscreen
8334 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
8335 $.datepicker._updateDatepicker(inst);
8336 // fix width for dynamic number of date pickers
8337 // and adjust position before showing
8338 offset = $.datepicker._checkOffset(inst, offset, isFixed);
8339 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
8340 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
8341 left: offset.left + "px", top: offset.top + "px"});
8344 showAnim = $.datepicker._get(inst, "showAnim");
8345 duration = $.datepicker._get(inst, "duration");
8346 inst.dpDiv.zIndex($(input).zIndex()+1);
8347 $.datepicker._datepickerShowing = true;
8349 if ( $.effects && $.effects.effect[ showAnim ] ) {
8350 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
8352 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
8355 if ( $.datepicker._shouldFocusInput( inst ) ) {
8359 $.datepicker._curInst = inst;
8363 /* Generate the date picker content. */
8364 _updateDatepicker: function(inst) {
8365 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
8366 instActive = inst; // for delegate hover events
8367 inst.dpDiv.empty().append(this._generateHTML(inst));
8368 this._attachHandlers(inst);
8369 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
8372 numMonths = this._getNumberOfMonths(inst),
8373 cols = numMonths[1],
8376 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
8378 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
8380 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
8381 "Class"]("ui-datepicker-multi");
8382 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
8383 "Class"]("ui-datepicker-rtl");
8385 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
8389 // deffered render of the years select (to avoid flashes on Firefox)
8390 if( inst.yearshtml ){
8391 origyearshtml = inst.yearshtml;
8392 setTimeout(function(){
8393 //assure that inst.yearshtml didn't change.
8394 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
8395 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
8397 origyearshtml = inst.yearshtml = null;
8402 // #6694 - don't focus the input if it's already focused
8403 // this breaks the change event in IE
8404 // Support: IE and jQuery <1.9
8405 _shouldFocusInput: function( inst ) {
8406 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
8409 /* Check positioning to remain on screen. */
8410 _checkOffset: function(inst, offset, isFixed) {
8411 var dpWidth = inst.dpDiv.outerWidth(),
8412 dpHeight = inst.dpDiv.outerHeight(),
8413 inputWidth = inst.input ? inst.input.outerWidth() : 0,
8414 inputHeight = inst.input ? inst.input.outerHeight() : 0,
8415 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
8416 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
8418 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
8419 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
8420 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
8422 // now check if datepicker is showing outside window viewport - move to a better place if so.
8423 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
8424 Math.abs(offset.left + dpWidth - viewWidth) : 0);
8425 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
8426 Math.abs(dpHeight + inputHeight) : 0);
8431 /* Find an object's position on the screen. */
8432 _findPos: function(obj) {
8434 inst = this._getInst(obj),
8435 isRTL = this._get(inst, "isRTL");
8437 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
8438 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
8441 position = $(obj).offset();
8442 return [position.left, position.top];
8445 /* Hide the date picker from view.
8446 * @param input element - the input field attached to the date picker
8448 _hideDatepicker: function(input) {
8449 var showAnim, duration, postProcess, onClose,
8450 inst = this._curInst;
8452 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
8456 if (this._datepickerShowing) {
8457 showAnim = this._get(inst, "showAnim");
8458 duration = this._get(inst, "duration");
8459 postProcess = function() {
8460 $.datepicker._tidyDialog(inst);
8463 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
8464 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
8465 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
8467 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
8468 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
8474 this._datepickerShowing = false;
8476 onClose = this._get(inst, "onClose");
8478 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
8481 this._lastInput = null;
8482 if (this._inDialog) {
8483 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
8486 $("body").append(this.dpDiv);
8489 this._inDialog = false;
8493 /* Tidy up after a dialog display. */
8494 _tidyDialog: function(inst) {
8495 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
8498 /* Close date picker if clicked elsewhere. */
8499 _checkExternalClick: function(event) {
8500 if (!$.datepicker._curInst) {
8504 var $target = $(event.target),
8505 inst = $.datepicker._getInst($target[0]);
8507 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
8508 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
8509 !$target.hasClass($.datepicker.markerClassName) &&
8510 !$target.closest("." + $.datepicker._triggerClass).length &&
8511 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
8512 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
8513 $.datepicker._hideDatepicker();
8517 /* Adjust one of the date sub-fields. */
8518 _adjustDate: function(id, offset, period) {
8520 inst = this._getInst(target[0]);
8522 if (this._isDisabledDatepicker(target[0])) {
8525 this._adjustInstDate(inst, offset +
8526 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
8528 this._updateDatepicker(inst);
8531 /* Action for current link. */
8532 _gotoToday: function(id) {
8535 inst = this._getInst(target[0]);
8537 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
8538 inst.selectedDay = inst.currentDay;
8539 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
8540 inst.drawYear = inst.selectedYear = inst.currentYear;
8543 inst.selectedDay = date.getDate();
8544 inst.drawMonth = inst.selectedMonth = date.getMonth();
8545 inst.drawYear = inst.selectedYear = date.getFullYear();
8547 this._notifyChange(inst);
8548 this._adjustDate(target);
8551 /* Action for selecting a new month/year. */
8552 _selectMonthYear: function(id, select, period) {
8554 inst = this._getInst(target[0]);
8556 inst["selected" + (period === "M" ? "Month" : "Year")] =
8557 inst["draw" + (period === "M" ? "Month" : "Year")] =
8558 parseInt(select.options[select.selectedIndex].value,10);
8560 this._notifyChange(inst);
8561 this._adjustDate(target);
8564 /* Action for selecting a day. */
8565 _selectDay: function(id, month, year, td) {
8569 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
8573 inst = this._getInst(target[0]);
8574 inst.selectedDay = inst.currentDay = $("a", td).html();
8575 inst.selectedMonth = inst.currentMonth = month;
8576 inst.selectedYear = inst.currentYear = year;
8577 this._selectDate(id, this._formatDate(inst,
8578 inst.currentDay, inst.currentMonth, inst.currentYear));
8581 /* Erase the input field and hide the date picker. */
8582 _clearDate: function(id) {
8584 this._selectDate(target, "");
8587 /* Update the input field with the selected date. */
8588 _selectDate: function(id, dateStr) {
8591 inst = this._getInst(target[0]);
8593 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
8595 inst.input.val(dateStr);
8597 this._updateAlternate(inst);
8599 onSelect = this._get(inst, "onSelect");
8601 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
8602 } else if (inst.input) {
8603 inst.input.trigger("change"); // fire the change event
8607 this._updateDatepicker(inst);
8609 this._hideDatepicker();
8610 this._lastInput = inst.input[0];
8611 if (typeof(inst.input[0]) !== "object") {
8612 inst.input.focus(); // restore focus
8614 this._lastInput = null;
8618 /* Update any alternate field to synchronise with the main field. */
8619 _updateAlternate: function(inst) {
8620 var altFormat, date, dateStr,
8621 altField = this._get(inst, "altField");
8623 if (altField) { // update alternate field too
8624 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
8625 date = this._getDate(inst);
8626 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
8627 $(altField).each(function() { $(this).val(dateStr); });
8631 /* Set as beforeShowDay function to prevent selection of weekends.
8632 * @param date Date - the date to customise
8633 * @return [boolean, string] - is this date selectable?, what is its CSS class?
8635 noWeekends: function(date) {
8636 var day = date.getDay();
8637 return [(day > 0 && day < 6), ""];
8640 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
8641 * @param date Date - the date to get the week for
8642 * @return number - the number of the week within the year that contains this date
8644 iso8601Week: function(date) {
8646 checkDate = new Date(date.getTime());
8648 // Find Thursday of this week starting on Monday
8649 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
8651 time = checkDate.getTime();
8652 checkDate.setMonth(0); // Compare with Jan 1
8653 checkDate.setDate(1);
8654 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
8657 /* Parse a string value into a date object.
8658 * See formatDate below for the possible formats.
8660 * @param format string - the expected format of the date
8661 * @param value string - the date in the above format
8662 * @param settings Object - attributes include:
8663 * shortYearCutoff number - the cutoff year for determining the century (optional)
8664 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8665 * dayNames string[7] - names of the days from Sunday (optional)
8666 * monthNamesShort string[12] - abbreviated names of the months (optional)
8667 * monthNames string[12] - names of the months (optional)
8668 * @return Date - the extracted date value or null if value is blank
8670 parseDate: function (format, value, settings) {
8671 if (format == null || value == null) {
8672 throw "Invalid arguments";
8675 value = (typeof value === "object" ? value.toString() : value + "");
8680 var iFormat, dim, extra,
8682 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
8683 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
8684 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
8685 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8686 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8687 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8688 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8695 // Check whether a format character is doubled
8696 lookAhead = function(match) {
8697 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8703 // Extract a number from the string value
8704 getNumber = function(match) {
8705 var isDoubled = lookAhead(match),
8706 size = (match === "@" ? 14 : (match === "!" ? 20 :
8707 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
8708 digits = new RegExp("^\\d{1," + size + "}"),
8709 num = value.substring(iValue).match(digits);
8711 throw "Missing number at position " + iValue;
8713 iValue += num[0].length;
8714 return parseInt(num[0], 10);
8716 // Extract a name from the string value and convert to an index
8717 getName = function(match, shortNames, longNames) {
8719 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
8721 }).sort(function (a, b) {
8722 return -(a[1].length - b[1].length);
8725 $.each(names, function (i, pair) {
8727 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
8729 iValue += name.length;
8736 throw "Unknown name at position " + iValue;
8739 // Confirm that a literal character matches the string value
8740 checkLiteral = function() {
8741 if (value.charAt(iValue) !== format.charAt(iFormat)) {
8742 throw "Unexpected literal at position " + iValue;
8747 for (iFormat = 0; iFormat < format.length; iFormat++) {
8749 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8755 switch (format.charAt(iFormat)) {
8757 day = getNumber("d");
8760 getName("D", dayNamesShort, dayNames);
8763 doy = getNumber("o");
8766 month = getNumber("m");
8769 month = getName("M", monthNamesShort, monthNames);
8772 year = getNumber("y");
8775 date = new Date(getNumber("@"));
8776 year = date.getFullYear();
8777 month = date.getMonth() + 1;
8778 day = date.getDate();
8781 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
8782 year = date.getFullYear();
8783 month = date.getMonth() + 1;
8784 day = date.getDate();
8787 if (lookAhead("'")){
8799 if (iValue < value.length){
8800 extra = value.substr(iValue);
8801 if (!/^\s+/.test(extra)) {
8802 throw "Extra/unparsed characters found in date: " + extra;
8807 year = new Date().getFullYear();
8808 } else if (year < 100) {
8809 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
8810 (year <= shortYearCutoff ? 0 : -100);
8817 dim = this._getDaysInMonth(year, month - 1);
8826 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
8827 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
8828 throw "Invalid date"; // E.g. 31/02/00
8833 /* Standard date formats. */
8834 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
8835 COOKIE: "D, dd M yy",
8836 ISO_8601: "yy-mm-dd",
8837 RFC_822: "D, d M y",
8838 RFC_850: "DD, dd-M-y",
8839 RFC_1036: "D, d M y",
8840 RFC_1123: "D, d M yy",
8841 RFC_2822: "D, d M yy",
8842 RSS: "D, d M y", // RFC 822
8845 W3C: "yy-mm-dd", // ISO 8601
8847 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
8848 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
8850 /* Format a date object into a string value.
8851 * The format can be combinations of the following:
8852 * d - day of month (no leading zero)
8853 * dd - day of month (two digit)
8854 * o - day of year (no leading zeros)
8855 * oo - day of year (three digit)
8856 * D - day name short
8857 * DD - day name long
8858 * m - month of year (no leading zero)
8859 * mm - month of year (two digit)
8860 * M - month name short
8861 * MM - month name long
8862 * y - year (two digit)
8863 * yy - year (four digit)
8864 * @ - Unix timestamp (ms since 01/01/1970)
8865 * ! - Windows ticks (100ns since 01/01/0001)
8866 * "..." - literal text
8869 * @param format string - the desired format of the date
8870 * @param date Date - the date value to format
8871 * @param settings Object - attributes include:
8872 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8873 * dayNames string[7] - names of the days from Sunday (optional)
8874 * monthNamesShort string[12] - abbreviated names of the months (optional)
8875 * monthNames string[12] - names of the months (optional)
8876 * @return string - the date in the above format
8878 formatDate: function (format, date, settings) {
8884 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8885 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8886 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8887 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8888 // Check whether a format character is doubled
8889 lookAhead = function(match) {
8890 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8896 // Format a number, with leading zero if necessary
8897 formatNumber = function(match, value, len) {
8898 var num = "" + value;
8899 if (lookAhead(match)) {
8900 while (num.length < len) {
8906 // Format a name, short or long as requested
8907 formatName = function(match, value, shortNames, longNames) {
8908 return (lookAhead(match) ? longNames[value] : shortNames[value]);
8914 for (iFormat = 0; iFormat < format.length; iFormat++) {
8916 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8919 output += format.charAt(iFormat);
8922 switch (format.charAt(iFormat)) {
8924 output += formatNumber("d", date.getDate(), 2);
8927 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
8930 output += formatNumber("o",
8931 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
8934 output += formatNumber("m", date.getMonth() + 1, 2);
8937 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
8940 output += (lookAhead("y") ? date.getFullYear() :
8941 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
8944 output += date.getTime();
8947 output += date.getTime() * 10000 + this._ticksTo1970;
8950 if (lookAhead("'")) {
8957 output += format.charAt(iFormat);
8965 /* Extract all possible characters from the date format. */
8966 _possibleChars: function (format) {
8970 // Check whether a format character is doubled
8971 lookAhead = function(match) {
8972 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8979 for (iFormat = 0; iFormat < format.length; iFormat++) {
8981 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8984 chars += format.charAt(iFormat);
8987 switch (format.charAt(iFormat)) {
8988 case "d": case "m": case "y": case "@":
8989 chars += "0123456789";
8992 return null; // Accept anything
8994 if (lookAhead("'")) {
9001 chars += format.charAt(iFormat);
9008 /* Get a setting value, defaulting if necessary. */
9009 _get: function(inst, name) {
9010 return inst.settings[name] !== undefined ?
9011 inst.settings[name] : this._defaults[name];
9014 /* Parse existing date and initialise date picker. */
9015 _setDateFromField: function(inst, noDefault) {
9016 if (inst.input.val() === inst.lastVal) {
9020 var dateFormat = this._get(inst, "dateFormat"),
9021 dates = inst.lastVal = inst.input ? inst.input.val() : null,
9022 defaultDate = this._getDefaultDate(inst),
9024 settings = this._getFormatConfig(inst);
9027 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
9029 dates = (noDefault ? "" : dates);
9031 inst.selectedDay = date.getDate();
9032 inst.drawMonth = inst.selectedMonth = date.getMonth();
9033 inst.drawYear = inst.selectedYear = date.getFullYear();
9034 inst.currentDay = (dates ? date.getDate() : 0);
9035 inst.currentMonth = (dates ? date.getMonth() : 0);
9036 inst.currentYear = (dates ? date.getFullYear() : 0);
9037 this._adjustInstDate(inst);
9040 /* Retrieve the default date shown on opening. */
9041 _getDefaultDate: function(inst) {
9042 return this._restrictMinMax(inst,
9043 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
9046 /* A date may be specified as an exact value or a relative one. */
9047 _determineDate: function(inst, date, defaultDate) {
9048 var offsetNumeric = function(offset) {
9049 var date = new Date();
9050 date.setDate(date.getDate() + offset);
9053 offsetString = function(offset) {
9055 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
9056 offset, $.datepicker._getFormatConfig(inst));
9062 var date = (offset.toLowerCase().match(/^c/) ?
9063 $.datepicker._getDate(inst) : null) || new Date(),
9064 year = date.getFullYear(),
9065 month = date.getMonth(),
9066 day = date.getDate(),
9067 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
9068 matches = pattern.exec(offset);
9071 switch (matches[2] || "d") {
9072 case "d" : case "D" :
9073 day += parseInt(matches[1],10); break;
9074 case "w" : case "W" :
9075 day += parseInt(matches[1],10) * 7; break;
9076 case "m" : case "M" :
9077 month += parseInt(matches[1],10);
9078 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9080 case "y": case "Y" :
9081 year += parseInt(matches[1],10);
9082 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9085 matches = pattern.exec(offset);
9087 return new Date(year, month, day);
9089 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
9090 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
9092 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
9094 newDate.setHours(0);
9095 newDate.setMinutes(0);
9096 newDate.setSeconds(0);
9097 newDate.setMilliseconds(0);
9099 return this._daylightSavingAdjust(newDate);
9102 /* Handle switch to/from daylight saving.
9103 * Hours may be non-zero on daylight saving cut-over:
9104 * > 12 when midnight changeover, but then cannot generate
9105 * midnight datetime, so jump to 1AM, otherwise reset.
9106 * @param date (Date) the date to check
9107 * @return (Date) the corrected date
9109 _daylightSavingAdjust: function(date) {
9113 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
9117 /* Set the date(s) directly. */
9118 _setDate: function(inst, date, noChange) {
9120 origMonth = inst.selectedMonth,
9121 origYear = inst.selectedYear,
9122 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
9124 inst.selectedDay = inst.currentDay = newDate.getDate();
9125 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
9126 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
9127 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
9128 this._notifyChange(inst);
9130 this._adjustInstDate(inst);
9132 inst.input.val(clear ? "" : this._formatDate(inst));
9136 /* Retrieve the date(s) directly. */
9137 _getDate: function(inst) {
9138 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
9139 this._daylightSavingAdjust(new Date(
9140 inst.currentYear, inst.currentMonth, inst.currentDay)));
9144 /* Attach the onxxx handlers. These are declared statically so
9145 * they work with static code transformers like Caja.
9147 _attachHandlers: function(inst) {
9148 var stepMonths = this._get(inst, "stepMonths"),
9149 id = "#" + inst.id.replace( /\\\\/g, "\\" );
9150 inst.dpDiv.find("[data-handler]").map(function () {
9153 $.datepicker._adjustDate(id, -stepMonths, "M");
9156 $.datepicker._adjustDate(id, +stepMonths, "M");
9159 $.datepicker._hideDatepicker();
9161 today: function () {
9162 $.datepicker._gotoToday(id);
9164 selectDay: function () {
9165 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
9168 selectMonth: function () {
9169 $.datepicker._selectMonthYear(id, this, "M");
9172 selectYear: function () {
9173 $.datepicker._selectMonthYear(id, this, "Y");
9177 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
9181 /* Generate the HTML for the current state of the date picker. */
9182 _generateHTML: function(inst) {
9183 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
9184 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
9185 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
9186 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
9187 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
9188 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
9189 tempDate = new Date(),
9190 today = this._daylightSavingAdjust(
9191 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
9192 isRTL = this._get(inst, "isRTL"),
9193 showButtonPanel = this._get(inst, "showButtonPanel"),
9194 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
9195 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
9196 numMonths = this._getNumberOfMonths(inst),
9197 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
9198 stepMonths = this._get(inst, "stepMonths"),
9199 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
9200 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
9201 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
9202 minDate = this._getMinMaxDate(inst, "min"),
9203 maxDate = this._getMinMaxDate(inst, "max"),
9204 drawMonth = inst.drawMonth - showCurrentAtPos,
9205 drawYear = inst.drawYear;
9207 if (drawMonth < 0) {
9212 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
9213 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
9214 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
9215 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
9217 if (drawMonth < 0) {
9223 inst.drawMonth = drawMonth;
9224 inst.drawYear = drawYear;
9226 prevText = this._get(inst, "prevText");
9227 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
9228 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
9229 this._getFormatConfig(inst)));
9231 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
9232 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
9233 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
9234 (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>"));
9236 nextText = this._get(inst, "nextText");
9237 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
9238 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
9239 this._getFormatConfig(inst)));
9241 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
9242 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
9243 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
9244 (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>"));
9246 currentText = this._get(inst, "currentText");
9247 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
9248 currentText = (!navigationAsDateFormat ? currentText :
9249 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
9251 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'>" +
9252 this._get(inst, "closeText") + "</button>" : "");
9254 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
9255 (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'" +
9256 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
9258 firstDay = parseInt(this._get(inst, "firstDay"),10);
9259 firstDay = (isNaN(firstDay) ? 0 : firstDay);
9261 showWeek = this._get(inst, "showWeek");
9262 dayNames = this._get(inst, "dayNames");
9263 dayNamesMin = this._get(inst, "dayNamesMin");
9264 monthNames = this._get(inst, "monthNames");
9265 monthNamesShort = this._get(inst, "monthNamesShort");
9266 beforeShowDay = this._get(inst, "beforeShowDay");
9267 showOtherMonths = this._get(inst, "showOtherMonths");
9268 selectOtherMonths = this._get(inst, "selectOtherMonths");
9269 defaultDate = this._getDefaultDate(inst);
9272 for (row = 0; row < numMonths[0]; row++) {
9275 for (col = 0; col < numMonths[1]; col++) {
9276 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
9277 cornerClass = " ui-corner-all";
9280 calender += "<div class='ui-datepicker-group";
9281 if (numMonths[1] > 1) {
9283 case 0: calender += " ui-datepicker-group-first";
9284 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
9285 case numMonths[1]-1: calender += " ui-datepicker-group-last";
9286 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
9287 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
9292 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
9293 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
9294 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
9295 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
9296 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
9297 "</div><table class='ui-datepicker-calendar'><thead>" +
9299 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
9300 for (dow = 0; dow < 7; dow++) { // days of the week
9301 day = (dow + firstDay) % 7;
9302 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
9303 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
9305 calender += thead + "</tr></thead><tbody>";
9306 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
9307 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
9308 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
9310 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
9311 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
9312 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
9313 this.maxRows = numRows;
9314 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
9315 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
9317 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
9318 this._get(inst, "calculateWeek")(printDate) + "</td>");
9319 for (dow = 0; dow < 7; dow++) { // create date picker days
9320 daySettings = (beforeShowDay ?
9321 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
9322 otherMonth = (printDate.getMonth() !== drawMonth);
9323 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
9324 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
9325 tbody += "<td class='" +
9326 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
9327 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
9328 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
9329 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
9330 // or defaultDate is current printedDate and defaultDate is selectedDate
9331 " " + this._dayOverClass : "") + // highlight selected day
9332 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
9333 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
9334 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
9335 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
9336 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
9337 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
9338 (otherMonth && !showOtherMonths ? " " : // display for other months
9339 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
9340 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
9341 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
9342 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
9343 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
9344 printDate.setDate(printDate.getDate() + 1);
9345 printDate = this._daylightSavingAdjust(printDate);
9347 calender += tbody + "</tr>";
9350 if (drawMonth > 11) {
9354 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
9355 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
9360 html += buttonPanel;
9361 inst._keyEvent = false;
9365 /* Generate the month and year header. */
9366 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
9367 secondary, monthNames, monthNamesShort) {
9369 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
9370 changeMonth = this._get(inst, "changeMonth"),
9371 changeYear = this._get(inst, "changeYear"),
9372 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
9373 html = "<div class='ui-datepicker-title'>",
9377 if (secondary || !changeMonth) {
9378 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
9380 inMinYear = (minDate && minDate.getFullYear() === drawYear);
9381 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
9382 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
9383 for ( month = 0; month < 12; month++) {
9384 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
9385 monthHtml += "<option value='" + month + "'" +
9386 (month === drawMonth ? " selected='selected'" : "") +
9387 ">" + monthNamesShort[month] + "</option>";
9390 monthHtml += "</select>";
9393 if (!showMonthAfterYear) {
9394 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
9398 if ( !inst.yearshtml ) {
9399 inst.yearshtml = "";
9400 if (secondary || !changeYear) {
9401 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
9403 // determine range of years to display
9404 years = this._get(inst, "yearRange").split(":");
9405 thisYear = new Date().getFullYear();
9406 determineYear = function(value) {
9407 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
9408 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
9409 parseInt(value, 10)));
9410 return (isNaN(year) ? thisYear : year);
9412 year = determineYear(years[0]);
9413 endYear = Math.max(year, determineYear(years[1] || ""));
9414 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
9415 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
9416 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
9417 for (; year <= endYear; year++) {
9418 inst.yearshtml += "<option value='" + year + "'" +
9419 (year === drawYear ? " selected='selected'" : "") +
9420 ">" + year + "</option>";
9422 inst.yearshtml += "</select>";
9424 html += inst.yearshtml;
9425 inst.yearshtml = null;
9429 html += this._get(inst, "yearSuffix");
9430 if (showMonthAfterYear) {
9431 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
9433 html += "</div>"; // Close datepicker_header
9437 /* Adjust one of the date sub-fields. */
9438 _adjustInstDate: function(inst, offset, period) {
9439 var year = inst.drawYear + (period === "Y" ? offset : 0),
9440 month = inst.drawMonth + (period === "M" ? offset : 0),
9441 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
9442 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
9444 inst.selectedDay = date.getDate();
9445 inst.drawMonth = inst.selectedMonth = date.getMonth();
9446 inst.drawYear = inst.selectedYear = date.getFullYear();
9447 if (period === "M" || period === "Y") {
9448 this._notifyChange(inst);
9452 /* Ensure a date is within any min/max bounds. */
9453 _restrictMinMax: function(inst, date) {
9454 var minDate = this._getMinMaxDate(inst, "min"),
9455 maxDate = this._getMinMaxDate(inst, "max"),
9456 newDate = (minDate && date < minDate ? minDate : date);
9457 return (maxDate && newDate > maxDate ? maxDate : newDate);
9460 /* Notify change of month/year. */
9461 _notifyChange: function(inst) {
9462 var onChange = this._get(inst, "onChangeMonthYear");
9464 onChange.apply((inst.input ? inst.input[0] : null),
9465 [inst.selectedYear, inst.selectedMonth + 1, inst]);
9469 /* Determine the number of months to show. */
9470 _getNumberOfMonths: function(inst) {
9471 var numMonths = this._get(inst, "numberOfMonths");
9472 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
9475 /* Determine the current maximum date - ensure no time components are set. */
9476 _getMinMaxDate: function(inst, minMax) {
9477 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
9480 /* Find the number of days in a given month. */
9481 _getDaysInMonth: function(year, month) {
9482 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
9485 /* Find the day of the week of the first of a month. */
9486 _getFirstDayOfMonth: function(year, month) {
9487 return new Date(year, month, 1).getDay();
9490 /* Determines if we should allow a "next/prev" month display change. */
9491 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
9492 var numMonths = this._getNumberOfMonths(inst),
9493 date = this._daylightSavingAdjust(new Date(curYear,
9494 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
9497 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
9499 return this._isInRange(inst, date);
9502 /* Is the given date in the accepted range? */
9503 _isInRange: function(inst, date) {
9504 var yearSplit, currentYear,
9505 minDate = this._getMinMaxDate(inst, "min"),
9506 maxDate = this._getMinMaxDate(inst, "max"),
9509 years = this._get(inst, "yearRange");
9511 yearSplit = years.split(":");
9512 currentYear = new Date().getFullYear();
9513 minYear = parseInt(yearSplit[0], 10);
9514 maxYear = parseInt(yearSplit[1], 10);
9515 if ( yearSplit[0].match(/[+\-].*/) ) {
9516 minYear += currentYear;
9518 if ( yearSplit[1].match(/[+\-].*/) ) {
9519 maxYear += currentYear;
9523 return ((!minDate || date.getTime() >= minDate.getTime()) &&
9524 (!maxDate || date.getTime() <= maxDate.getTime()) &&
9525 (!minYear || date.getFullYear() >= minYear) &&
9526 (!maxYear || date.getFullYear() <= maxYear));
9529 /* Provide the configuration settings for formatting/parsing. */
9530 _getFormatConfig: function(inst) {
9531 var shortYearCutoff = this._get(inst, "shortYearCutoff");
9532 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
9533 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
9534 return {shortYearCutoff: shortYearCutoff,
9535 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
9536 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
9539 /* Format the given date for display. */
9540 _formatDate: function(inst, day, month, year) {
9542 inst.currentDay = inst.selectedDay;
9543 inst.currentMonth = inst.selectedMonth;
9544 inst.currentYear = inst.selectedYear;
9546 var date = (day ? (typeof day === "object" ? day :
9547 this._daylightSavingAdjust(new Date(year, month, day))) :
9548 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
9549 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
9554 * Bind hover events for datepicker elements.
9555 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
9556 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
9558 function bindHover(dpDiv) {
9559 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
9560 return dpDiv.delegate(selector, "mouseout", function() {
9561 $(this).removeClass("ui-state-hover");
9562 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9563 $(this).removeClass("ui-datepicker-prev-hover");
9565 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9566 $(this).removeClass("ui-datepicker-next-hover");
9569 .delegate(selector, "mouseover", function(){
9570 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
9571 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
9572 $(this).addClass("ui-state-hover");
9573 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9574 $(this).addClass("ui-datepicker-prev-hover");
9576 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9577 $(this).addClass("ui-datepicker-next-hover");
9583 /* jQuery extend now ignores nulls! */
9584 function extendRemove(target, props) {
9585 $.extend(target, props);
9586 for (var name in props) {
9587 if (props[name] == null) {
9588 target[name] = props[name];
9594 /* Invoke the datepicker functionality.
9595 @param options string - a command, optionally followed by additional parameters or
9596 Object - settings for attaching new datepicker functionality
9597 @return jQuery object */
9598 $.fn.datepicker = function(options){
9600 /* Verify an empty collection wasn't passed - Fixes #6976 */
9601 if ( !this.length ) {
9605 /* Initialise the date picker. */
9606 if (!$.datepicker.initialized) {
9607 $(document).mousedown($.datepicker._checkExternalClick);
9608 $.datepicker.initialized = true;
9611 /* Append datepicker main container to body if not exist. */
9612 if ($("#"+$.datepicker._mainDivId).length === 0) {
9613 $("body").append($.datepicker.dpDiv);
9616 var otherArgs = Array.prototype.slice.call(arguments, 1);
9617 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
9618 return $.datepicker["_" + options + "Datepicker"].
9619 apply($.datepicker, [this[0]].concat(otherArgs));
9621 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
9622 return $.datepicker["_" + options + "Datepicker"].
9623 apply($.datepicker, [this[0]].concat(otherArgs));
9625 return this.each(function() {
9626 typeof options === "string" ?
9627 $.datepicker["_" + options + "Datepicker"].
9628 apply($.datepicker, [this].concat(otherArgs)) :
9629 $.datepicker._attachDatepicker(this, options);
9633 $.datepicker = new Datepicker(); // singleton instance
9634 $.datepicker.initialized = false;
9635 $.datepicker.uuid = new Date().getTime();
9636 $.datepicker.version = "1.10.3";
9640 (function( $, undefined ) {
9642 var sizeRelatedOptions = {
9651 resizableRelatedOptions = {
9658 $.widget( "ui.dialog", {
9664 closeOnEscape: true,
9680 // Ensure the titlebar is always visible
9681 using: function( pos ) {
9682 var topOffset = $( this ).css( pos ).offset().top;
9683 if ( topOffset < 0 ) {
9684 $( this ).css( "top", pos.top - topOffset );
9706 _create: function() {
9707 this.originalCss = {
9708 display: this.element[0].style.display,
9709 width: this.element[0].style.width,
9710 minHeight: this.element[0].style.minHeight,
9711 maxHeight: this.element[0].style.maxHeight,
9712 height: this.element[0].style.height
9714 this.originalPosition = {
9715 parent: this.element.parent(),
9716 index: this.element.parent().children().index( this.element )
9718 this.originalTitle = this.element.attr("title");
9719 this.options.title = this.options.title || this.originalTitle;
9721 this._createWrapper();
9725 .removeAttr("title")
9726 .addClass("ui-dialog-content ui-widget-content")
9727 .appendTo( this.uiDialog );
9729 this._createTitlebar();
9730 this._createButtonPane();
9732 if ( this.options.draggable && $.fn.draggable ) {
9733 this._makeDraggable();
9735 if ( this.options.resizable && $.fn.resizable ) {
9736 this._makeResizable();
9739 this._isOpen = false;
9743 if ( this.options.autoOpen ) {
9748 _appendTo: function() {
9749 var element = this.options.appendTo;
9750 if ( element && (element.jquery || element.nodeType) ) {
9751 return $( element );
9753 return this.document.find( element || "body" ).eq( 0 );
9756 _destroy: function() {
9758 originalPosition = this.originalPosition;
9760 this._destroyOverlay();
9764 .removeClass("ui-dialog-content ui-widget-content")
9765 .css( this.originalCss )
9766 // Without detaching first, the following becomes really slow
9769 this.uiDialog.stop( true, true ).remove();
9771 if ( this.originalTitle ) {
9772 this.element.attr( "title", this.originalTitle );
9775 next = originalPosition.parent.children().eq( originalPosition.index );
9776 // Don't try to place the dialog next to itself (#8613)
9777 if ( next.length && next[0] !== this.element[0] ) {
9778 next.before( this.element );
9780 originalPosition.parent.append( this.element );
9784 widget: function() {
9785 return this.uiDialog;
9791 close: function( event ) {
9794 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
9798 this._isOpen = false;
9799 this._destroyOverlay();
9801 if ( !this.opener.filter(":focusable").focus().length ) {
9802 // Hiding a focused element doesn't trigger blur in WebKit
9803 // so in case we have nothing to focus on, explicitly blur the active element
9804 // https://bugs.webkit.org/show_bug.cgi?id=47182
9805 $( this.document[0].activeElement ).blur();
9808 this._hide( this.uiDialog, this.options.hide, function() {
9809 that._trigger( "close", event );
9813 isOpen: function() {
9814 return this._isOpen;
9817 moveToTop: function() {
9821 _moveToTop: function( event, silent ) {
9822 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
9823 if ( moved && !silent ) {
9824 this._trigger( "focus", event );
9831 if ( this._isOpen ) {
9832 if ( this._moveToTop() ) {
9833 this._focusTabbable();
9838 this._isOpen = true;
9839 this.opener = $( this.document[0].activeElement );
9843 this._createOverlay();
9844 this._moveToTop( null, true );
9845 this._show( this.uiDialog, this.options.show, function() {
9846 that._focusTabbable();
9847 that._trigger("focus");
9850 this._trigger("open");
9853 _focusTabbable: function() {
9854 // Set focus to the first match:
9855 // 1. First element inside the dialog matching [autofocus]
9856 // 2. Tabbable element inside the content element
9857 // 3. Tabbable element inside the buttonpane
9858 // 4. The close button
9859 // 5. The dialog itself
9860 var hasFocus = this.element.find("[autofocus]");
9861 if ( !hasFocus.length ) {
9862 hasFocus = this.element.find(":tabbable");
9864 if ( !hasFocus.length ) {
9865 hasFocus = this.uiDialogButtonPane.find(":tabbable");
9867 if ( !hasFocus.length ) {
9868 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
9870 if ( !hasFocus.length ) {
9871 hasFocus = this.uiDialog;
9873 hasFocus.eq( 0 ).focus();
9876 _keepFocus: function( event ) {
9877 function checkFocus() {
9878 var activeElement = this.document[0].activeElement,
9879 isActive = this.uiDialog[0] === activeElement ||
9880 $.contains( this.uiDialog[0], activeElement );
9882 this._focusTabbable();
9885 event.preventDefault();
9886 checkFocus.call( this );
9888 // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
9889 // so we check again later
9890 this._delay( checkFocus );
9893 _createWrapper: function() {
9894 this.uiDialog = $("<div>")
9895 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
9896 this.options.dialogClass )
9899 // Setting tabIndex makes the div focusable
9903 .appendTo( this._appendTo() );
9905 this._on( this.uiDialog, {
9906 keydown: function( event ) {
9907 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
9908 event.keyCode === $.ui.keyCode.ESCAPE ) {
9909 event.preventDefault();
9910 this.close( event );
9914 // prevent tabbing out of dialogs
9915 if ( event.keyCode !== $.ui.keyCode.TAB ) {
9918 var tabbables = this.uiDialog.find(":tabbable"),
9919 first = tabbables.filter(":first"),
9920 last = tabbables.filter(":last");
9922 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
9924 event.preventDefault();
9925 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
9927 event.preventDefault();
9930 mousedown: function( event ) {
9931 if ( this._moveToTop( event ) ) {
9932 this._focusTabbable();
9937 // We assume that any existing aria-describedby attribute means
9938 // that the dialog content is marked up properly
9939 // otherwise we brute force the content as the description
9940 if ( !this.element.find("[aria-describedby]").length ) {
9941 this.uiDialog.attr({
9942 "aria-describedby": this.element.uniqueId().attr("id")
9947 _createTitlebar: function() {
9950 this.uiDialogTitlebar = $("<div>")
9951 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
9952 .prependTo( this.uiDialog );
9953 this._on( this.uiDialogTitlebar, {
9954 mousedown: function( event ) {
9955 // Don't prevent click on close button (#8838)
9956 // Focusing a dialog that is partially scrolled out of view
9957 // causes the browser to scroll it into view, preventing the click event
9958 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
9959 // Dialog isn't getting focus when dragging (#8063)
9960 this.uiDialog.focus();
9965 this.uiDialogTitlebarClose = $("<button></button>")
9967 label: this.options.closeText,
9969 primary: "ui-icon-closethick"
9973 .addClass("ui-dialog-titlebar-close")
9974 .appendTo( this.uiDialogTitlebar );
9975 this._on( this.uiDialogTitlebarClose, {
9976 click: function( event ) {
9977 event.preventDefault();
9978 this.close( event );
9982 uiDialogTitle = $("<span>")
9984 .addClass("ui-dialog-title")
9985 .prependTo( this.uiDialogTitlebar );
9986 this._title( uiDialogTitle );
9988 this.uiDialog.attr({
9989 "aria-labelledby": uiDialogTitle.attr("id")
9993 _title: function( title ) {
9994 if ( !this.options.title ) {
9995 title.html(" ");
9997 title.text( this.options.title );
10000 _createButtonPane: function() {
10001 this.uiDialogButtonPane = $("<div>")
10002 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
10004 this.uiButtonSet = $("<div>")
10005 .addClass("ui-dialog-buttonset")
10006 .appendTo( this.uiDialogButtonPane );
10008 this._createButtons();
10011 _createButtons: function() {
10013 buttons = this.options.buttons;
10015 // if we already have a button pane, remove it
10016 this.uiDialogButtonPane.remove();
10017 this.uiButtonSet.empty();
10019 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
10020 this.uiDialog.removeClass("ui-dialog-buttons");
10024 $.each( buttons, function( name, props ) {
10025 var click, buttonOptions;
10026 props = $.isFunction( props ) ?
10027 { click: props, text: name } :
10029 // Default to a non-submitting button
10030 props = $.extend( { type: "button" }, props );
10031 // Change the context for the click callback to be the main element
10032 click = props.click;
10033 props.click = function() {
10034 click.apply( that.element[0], arguments );
10037 icons: props.icons,
10038 text: props.showText
10040 delete props.icons;
10041 delete props.showText;
10042 $( "<button></button>", props )
10043 .button( buttonOptions )
10044 .appendTo( that.uiButtonSet );
10046 this.uiDialog.addClass("ui-dialog-buttons");
10047 this.uiDialogButtonPane.appendTo( this.uiDialog );
10050 _makeDraggable: function() {
10052 options = this.options;
10054 function filteredUi( ui ) {
10056 position: ui.position,
10061 this.uiDialog.draggable({
10062 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
10063 handle: ".ui-dialog-titlebar",
10064 containment: "document",
10065 start: function( event, ui ) {
10066 $( this ).addClass("ui-dialog-dragging");
10067 that._blockFrames();
10068 that._trigger( "dragStart", event, filteredUi( ui ) );
10070 drag: function( event, ui ) {
10071 that._trigger( "drag", event, filteredUi( ui ) );
10073 stop: function( event, ui ) {
10074 options.position = [
10075 ui.position.left - that.document.scrollLeft(),
10076 ui.position.top - that.document.scrollTop()
10078 $( this ).removeClass("ui-dialog-dragging");
10079 that._unblockFrames();
10080 that._trigger( "dragStop", event, filteredUi( ui ) );
10085 _makeResizable: function() {
10087 options = this.options,
10088 handles = options.resizable,
10089 // .ui-resizable has position: relative defined in the stylesheet
10090 // but dialogs have to use absolute or fixed positioning
10091 position = this.uiDialog.css("position"),
10092 resizeHandles = typeof handles === "string" ?
10094 "n,e,s,w,se,sw,ne,nw";
10096 function filteredUi( ui ) {
10098 originalPosition: ui.originalPosition,
10099 originalSize: ui.originalSize,
10100 position: ui.position,
10105 this.uiDialog.resizable({
10106 cancel: ".ui-dialog-content",
10107 containment: "document",
10108 alsoResize: this.element,
10109 maxWidth: options.maxWidth,
10110 maxHeight: options.maxHeight,
10111 minWidth: options.minWidth,
10112 minHeight: this._minHeight(),
10113 handles: resizeHandles,
10114 start: function( event, ui ) {
10115 $( this ).addClass("ui-dialog-resizing");
10116 that._blockFrames();
10117 that._trigger( "resizeStart", event, filteredUi( ui ) );
10119 resize: function( event, ui ) {
10120 that._trigger( "resize", event, filteredUi( ui ) );
10122 stop: function( event, ui ) {
10123 options.height = $( this ).height();
10124 options.width = $( this ).width();
10125 $( this ).removeClass("ui-dialog-resizing");
10126 that._unblockFrames();
10127 that._trigger( "resizeStop", event, filteredUi( ui ) );
10130 .css( "position", position );
10133 _minHeight: function() {
10134 var options = this.options;
10136 return options.height === "auto" ?
10137 options.minHeight :
10138 Math.min( options.minHeight, options.height );
10141 _position: function() {
10142 // Need to show the dialog to get the actual offset in the position plugin
10143 var isVisible = this.uiDialog.is(":visible");
10144 if ( !isVisible ) {
10145 this.uiDialog.show();
10147 this.uiDialog.position( this.options.position );
10148 if ( !isVisible ) {
10149 this.uiDialog.hide();
10153 _setOptions: function( options ) {
10156 resizableOptions = {};
10158 $.each( options, function( key, value ) {
10159 that._setOption( key, value );
10161 if ( key in sizeRelatedOptions ) {
10164 if ( key in resizableRelatedOptions ) {
10165 resizableOptions[ key ] = value;
10173 if ( this.uiDialog.is(":data(ui-resizable)") ) {
10174 this.uiDialog.resizable( "option", resizableOptions );
10178 _setOption: function( key, value ) {
10179 /*jshint maxcomplexity:15*/
10180 var isDraggable, isResizable,
10181 uiDialog = this.uiDialog;
10183 if ( key === "dialogClass" ) {
10185 .removeClass( this.options.dialogClass )
10186 .addClass( value );
10189 if ( key === "disabled" ) {
10193 this._super( key, value );
10195 if ( key === "appendTo" ) {
10196 this.uiDialog.appendTo( this._appendTo() );
10199 if ( key === "buttons" ) {
10200 this._createButtons();
10203 if ( key === "closeText" ) {
10204 this.uiDialogTitlebarClose.button({
10205 // Ensure that we always pass a string
10210 if ( key === "draggable" ) {
10211 isDraggable = uiDialog.is(":data(ui-draggable)");
10212 if ( isDraggable && !value ) {
10213 uiDialog.draggable("destroy");
10216 if ( !isDraggable && value ) {
10217 this._makeDraggable();
10221 if ( key === "position" ) {
10225 if ( key === "resizable" ) {
10226 // currently resizable, becoming non-resizable
10227 isResizable = uiDialog.is(":data(ui-resizable)");
10228 if ( isResizable && !value ) {
10229 uiDialog.resizable("destroy");
10232 // currently resizable, changing handles
10233 if ( isResizable && typeof value === "string" ) {
10234 uiDialog.resizable( "option", "handles", value );
10237 // currently non-resizable, becoming resizable
10238 if ( !isResizable && value !== false ) {
10239 this._makeResizable();
10243 if ( key === "title" ) {
10244 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
10248 _size: function() {
10249 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
10250 // divs will both have width and height set, so we need to reset them
10251 var nonContentHeight, minContentHeight, maxContentHeight,
10252 options = this.options;
10254 // Reset content sizing
10255 this.element.show().css({
10262 if ( options.minWidth > options.width ) {
10263 options.width = options.minWidth;
10266 // reset wrapper sizing
10267 // determine the height of all the non-content elements
10268 nonContentHeight = this.uiDialog.css({
10270 width: options.width
10273 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
10274 maxContentHeight = typeof options.maxHeight === "number" ?
10275 Math.max( 0, options.maxHeight - nonContentHeight ) :
10278 if ( options.height === "auto" ) {
10280 minHeight: minContentHeight,
10281 maxHeight: maxContentHeight,
10285 this.element.height( Math.max( 0, options.height - nonContentHeight ) );
10288 if (this.uiDialog.is(":data(ui-resizable)") ) {
10289 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
10293 _blockFrames: function() {
10294 this.iframeBlocks = this.document.find( "iframe" ).map(function() {
10295 var iframe = $( this );
10297 return $( "<div>" )
10299 position: "absolute",
10300 width: iframe.outerWidth(),
10301 height: iframe.outerHeight()
10303 .appendTo( iframe.parent() )
10304 .offset( iframe.offset() )[0];
10308 _unblockFrames: function() {
10309 if ( this.iframeBlocks ) {
10310 this.iframeBlocks.remove();
10311 delete this.iframeBlocks;
10315 _allowInteraction: function( event ) {
10316 if ( $( event.target ).closest(".ui-dialog").length ) {
10320 // TODO: Remove hack when datepicker implements
10321 // the .ui-front logic (#8989)
10322 return !!$( event.target ).closest(".ui-datepicker").length;
10325 _createOverlay: function() {
10326 if ( !this.options.modal ) {
10331 widgetFullName = this.widgetFullName;
10332 if ( !$.ui.dialog.overlayInstances ) {
10333 // Prevent use of anchors and inputs.
10334 // We use a delay in case the overlay is created from an
10335 // event that we're going to be cancelling. (#2804)
10336 this._delay(function() {
10337 // Handle .dialog().dialog("close") (#4065)
10338 if ( $.ui.dialog.overlayInstances ) {
10339 this.document.bind( "focusin.dialog", function( event ) {
10340 if ( !that._allowInteraction( event ) ) {
10341 event.preventDefault();
10342 $(".ui-dialog:visible:last .ui-dialog-content")
10343 .data( widgetFullName )._focusTabbable();
10350 this.overlay = $("<div>")
10351 .addClass("ui-widget-overlay ui-front")
10352 .appendTo( this._appendTo() );
10353 this._on( this.overlay, {
10354 mousedown: "_keepFocus"
10356 $.ui.dialog.overlayInstances++;
10359 _destroyOverlay: function() {
10360 if ( !this.options.modal ) {
10364 if ( this.overlay ) {
10365 $.ui.dialog.overlayInstances--;
10367 if ( !$.ui.dialog.overlayInstances ) {
10368 this.document.unbind( "focusin.dialog" );
10370 this.overlay.remove();
10371 this.overlay = null;
10376 $.ui.dialog.overlayInstances = 0;
10379 if ( $.uiBackCompat !== false ) {
10380 // position option with array notation
10381 // just override with old implementation
10382 $.widget( "ui.dialog", $.ui.dialog, {
10383 _position: function() {
10384 var position = this.options.position,
10390 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
10391 myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
10392 if ( myAt.length === 1 ) {
10396 $.each( [ "left", "top" ], function( i, offsetPosition ) {
10397 if ( +myAt[ i ] === myAt[ i ] ) {
10398 offset[ i ] = myAt[ i ];
10399 myAt[ i ] = offsetPosition;
10404 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
10405 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
10410 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
10412 position = $.ui.dialog.prototype.options.position;
10415 // need to show the dialog to get the actual offset in the position plugin
10416 isVisible = this.uiDialog.is(":visible");
10417 if ( !isVisible ) {
10418 this.uiDialog.show();
10420 this.uiDialog.position( position );
10421 if ( !isVisible ) {
10422 this.uiDialog.hide();
10430 (function( $, undefined ) {
10432 var rvertical = /up|down|vertical/,
10433 rpositivemotion = /up|left|vertical|horizontal/;
10435 $.effects.effect.blind = function( o, done ) {
10437 var el = $( this ),
10438 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10439 mode = $.effects.setMode( el, o.mode || "hide" ),
10440 direction = o.direction || "up",
10441 vertical = rvertical.test( direction ),
10442 ref = vertical ? "height" : "width",
10443 ref2 = vertical ? "top" : "left",
10444 motion = rpositivemotion.test( direction ),
10446 show = mode === "show",
10447 wrapper, distance, margin;
10449 // if already wrapped, the wrapper's properties are my property. #6245
10450 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
10451 $.effects.save( el.parent(), props );
10453 $.effects.save( el, props );
10456 wrapper = $.effects.createWrapper( el ).css({
10460 distance = wrapper[ ref ]();
10461 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
10463 animation[ ref ] = show ? distance : 0;
10466 .css( vertical ? "bottom" : "right", 0 )
10467 .css( vertical ? "top" : "left", "auto" )
10468 .css({ position: "absolute" });
10470 animation[ ref2 ] = show ? margin : distance + margin;
10473 // start at 0 if we are showing
10475 wrapper.css( ref, 0 );
10477 wrapper.css( ref2, margin + distance );
10482 wrapper.animate( animation, {
10483 duration: o.duration,
10486 complete: function() {
10487 if ( mode === "hide" ) {
10490 $.effects.restore( el, props );
10491 $.effects.removeWrapper( el );
10500 (function( $, undefined ) {
10502 $.effects.effect.bounce = function( o, done ) {
10503 var el = $( this ),
10504 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10507 mode = $.effects.setMode( el, o.mode || "effect" ),
10508 hide = mode === "hide",
10509 show = mode === "show",
10510 direction = o.direction || "up",
10511 distance = o.distance,
10512 times = o.times || 5,
10514 // number of internal animations
10515 anims = times * 2 + ( show || hide ? 1 : 0 ),
10516 speed = o.duration / anims,
10520 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10521 motion = ( direction === "up" || direction === "left" ),
10526 // we will need to re-assemble the queue to stack our animations in place
10527 queue = el.queue(),
10528 queuelen = queue.length;
10530 // Avoid touching opacity to prevent clearType and PNG issues in IE
10531 if ( show || hide ) {
10532 props.push( "opacity" );
10535 $.effects.save( el, props );
10537 $.effects.createWrapper( el ); // Create Wrapper
10539 // default distance for the BIGGEST bounce is the outer Distance / 3
10541 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
10545 downAnim = { opacity: 1 };
10546 downAnim[ ref ] = 0;
10548 // if we are showing, force opacity 0 and set the initial position
10549 // then do the "first" animation
10550 el.css( "opacity", 0 )
10551 .css( ref, motion ? -distance * 2 : distance * 2 )
10552 .animate( downAnim, speed, easing );
10555 // start at the smallest distance if we are hiding
10557 distance = distance / Math.pow( 2, times - 1 );
10561 downAnim[ ref ] = 0;
10562 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
10563 for ( i = 0; i < times; i++ ) {
10565 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10567 el.animate( upAnim, speed, easing )
10568 .animate( downAnim, speed, easing );
10570 distance = hide ? distance * 2 : distance / 2;
10573 // Last Bounce when Hiding
10575 upAnim = { opacity: 0 };
10576 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10578 el.animate( upAnim, speed, easing );
10581 el.queue(function() {
10585 $.effects.restore( el, props );
10586 $.effects.removeWrapper( el );
10590 // inject all the animations we just queued to be first in line (after "inprogress")
10591 if ( queuelen > 1) {
10592 queue.splice.apply( queue,
10593 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10601 (function( $, undefined ) {
10603 $.effects.effect.clip = function( o, done ) {
10605 var el = $( this ),
10606 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10607 mode = $.effects.setMode( el, o.mode || "hide" ),
10608 show = mode === "show",
10609 direction = o.direction || "vertical",
10610 vert = direction === "vertical",
10611 size = vert ? "height" : "width",
10612 position = vert ? "top" : "left",
10614 wrapper, animate, distance;
10617 $.effects.save( el, props );
10621 wrapper = $.effects.createWrapper( el ).css({
10624 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
10625 distance = animate[ size ]();
10629 animate.css( size, 0 );
10630 animate.css( position, distance / 2 );
10633 // Create Animation Object:
10634 animation[ size ] = show ? distance : 0;
10635 animation[ position ] = show ? 0 : distance / 2;
10638 animate.animate( animation, {
10640 duration: o.duration,
10642 complete: function() {
10646 $.effects.restore( el, props );
10647 $.effects.removeWrapper( el );
10656 (function( $, undefined ) {
10658 $.effects.effect.drop = function( o, done ) {
10660 var el = $( this ),
10661 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
10662 mode = $.effects.setMode( el, o.mode || "hide" ),
10663 show = mode === "show",
10664 direction = o.direction || "left",
10665 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10666 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
10668 opacity: show ? 1 : 0
10673 $.effects.save( el, props );
10675 $.effects.createWrapper( el );
10677 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
10681 .css( "opacity", 0 )
10682 .css( ref, motion === "pos" ? -distance : distance );
10686 animation[ ref ] = ( show ?
10687 ( motion === "pos" ? "+=" : "-=" ) :
10688 ( motion === "pos" ? "-=" : "+=" ) ) +
10692 el.animate( animation, {
10694 duration: o.duration,
10696 complete: function() {
10697 if ( mode === "hide" ) {
10700 $.effects.restore( el, props );
10701 $.effects.removeWrapper( el );
10709 (function( $, undefined ) {
10711 $.effects.effect.explode = function( o, done ) {
10713 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
10716 mode = $.effects.setMode( el, o.mode || "hide" ),
10717 show = mode === "show",
10719 // show and then visibility:hidden the element before calculating offset
10720 offset = el.show().css( "visibility", "hidden" ).offset(),
10722 // width and height of a piece
10723 width = Math.ceil( el.outerWidth() / cells ),
10724 height = Math.ceil( el.outerHeight() / rows ),
10728 i, j, left, top, mx, my;
10730 // children animate complete:
10731 function childComplete() {
10732 pieces.push( this );
10733 if ( pieces.length === rows * cells ) {
10738 // clone the element for each row and cell.
10739 for( i = 0; i < rows ; i++ ) { // ===>
10740 top = offset.top + i * height;
10741 my = i - ( rows - 1 ) / 2 ;
10743 for( j = 0; j < cells ; j++ ) { // |||
10744 left = offset.left + j * width;
10745 mx = j - ( cells - 1 ) / 2 ;
10747 // Create a clone of the now hidden main element that will be absolute positioned
10748 // within a wrapper div off the -left and -top equal to size of our pieces
10751 .appendTo( "body" )
10752 .wrap( "<div></div>" )
10754 position: "absolute",
10755 visibility: "visible",
10760 // select the wrapper - make it overflow: hidden and absolute positioned based on
10761 // where the original was located +left and +top equal to the size of pieces
10763 .addClass( "ui-effects-explode" )
10765 position: "absolute",
10766 overflow: "hidden",
10769 left: left + ( show ? mx * width : 0 ),
10770 top: top + ( show ? my * height : 0 ),
10771 opacity: show ? 0 : 1
10773 left: left + ( show ? 0 : mx * width ),
10774 top: top + ( show ? 0 : my * height ),
10775 opacity: show ? 1 : 0
10776 }, o.duration || 500, o.easing, childComplete );
10780 function animComplete() {
10782 visibility: "visible"
10784 $( pieces ).remove();
10794 (function( $, undefined ) {
10796 $.effects.effect.fade = function( o, done ) {
10797 var el = $( this ),
10798 mode = $.effects.setMode( el, o.mode || "toggle" );
10804 duration: o.duration,
10812 (function( $, undefined ) {
10814 $.effects.effect.fold = function( o, done ) {
10817 var el = $( this ),
10818 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10819 mode = $.effects.setMode( el, o.mode || "hide" ),
10820 show = mode === "show",
10821 hide = mode === "hide",
10822 size = o.size || 15,
10823 percent = /([0-9]+)%/.exec( size ),
10824 horizFirst = !!o.horizFirst,
10825 widthFirst = show !== horizFirst,
10826 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
10827 duration = o.duration / 2,
10832 $.effects.save( el, props );
10836 wrapper = $.effects.createWrapper( el ).css({
10839 distance = widthFirst ?
10840 [ wrapper.width(), wrapper.height() ] :
10841 [ wrapper.height(), wrapper.width() ];
10844 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
10847 wrapper.css( horizFirst ? {
10857 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
10858 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
10862 .animate( animation1, duration, o.easing )
10863 .animate( animation2, duration, o.easing, function() {
10867 $.effects.restore( el, props );
10868 $.effects.removeWrapper( el );
10876 (function( $, undefined ) {
10878 $.effects.effect.highlight = function( o, done ) {
10879 var elem = $( this ),
10880 props = [ "backgroundImage", "backgroundColor", "opacity" ],
10881 mode = $.effects.setMode( elem, o.mode || "show" ),
10883 backgroundColor: elem.css( "backgroundColor" )
10886 if (mode === "hide") {
10887 animation.opacity = 0;
10890 $.effects.save( elem, props );
10895 backgroundImage: "none",
10896 backgroundColor: o.color || "#ffff99"
10898 .animate( animation, {
10900 duration: o.duration,
10902 complete: function() {
10903 if ( mode === "hide" ) {
10906 $.effects.restore( elem, props );
10914 (function( $, undefined ) {
10916 $.effects.effect.pulsate = function( o, done ) {
10917 var elem = $( this ),
10918 mode = $.effects.setMode( elem, o.mode || "show" ),
10919 show = mode === "show",
10920 hide = mode === "hide",
10921 showhide = ( show || mode === "hide" ),
10923 // showing or hiding leaves of the "last" animation
10924 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
10925 duration = o.duration / anims,
10927 queue = elem.queue(),
10928 queuelen = queue.length,
10931 if ( show || !elem.is(":visible")) {
10932 elem.css( "opacity", 0 ).show();
10936 // anims - 1 opacity "toggles"
10937 for ( i = 1; i < anims; i++ ) {
10940 }, duration, o.easing );
10941 animateTo = 1 - animateTo;
10946 }, duration, o.easing);
10948 elem.queue(function() {
10955 // We just queued up "anims" animations, we need to put them next in the queue
10956 if ( queuelen > 1 ) {
10957 queue.splice.apply( queue,
10958 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10965 (function( $, undefined ) {
10967 $.effects.effect.puff = function( o, done ) {
10968 var elem = $( this ),
10969 mode = $.effects.setMode( elem, o.mode || "hide" ),
10970 hide = mode === "hide",
10971 percent = parseInt( o.percent, 10 ) || 150,
10972 factor = percent / 100,
10974 height: elem.height(),
10975 width: elem.width(),
10976 outerHeight: elem.outerHeight(),
10977 outerWidth: elem.outerWidth()
10986 percent: hide ? percent : 100,
10990 height: original.height * factor,
10991 width: original.width * factor,
10992 outerHeight: original.outerHeight * factor,
10993 outerWidth: original.outerWidth * factor
11000 $.effects.effect.scale = function( o, done ) {
11003 var el = $( this ),
11004 options = $.extend( true, {}, o ),
11005 mode = $.effects.setMode( el, o.mode || "effect" ),
11006 percent = parseInt( o.percent, 10 ) ||
11007 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
11008 direction = o.direction || "both",
11011 height: el.height(),
11013 outerHeight: el.outerHeight(),
11014 outerWidth: el.outerWidth()
11017 y: direction !== "horizontal" ? (percent / 100) : 1,
11018 x: direction !== "vertical" ? (percent / 100) : 1
11021 // We are going to pass this effect to the size effect:
11022 options.effect = "size";
11023 options.queue = false;
11024 options.complete = done;
11026 // Set default origin and restore for show/hide
11027 if ( mode !== "effect" ) {
11028 options.origin = origin || ["middle","center"];
11029 options.restore = true;
11032 options.from = o.from || ( mode === "show" ? {
11039 height: original.height * factor.y,
11040 width: original.width * factor.x,
11041 outerHeight: original.outerHeight * factor.y,
11042 outerWidth: original.outerWidth * factor.x
11045 // Fade option to support puff
11046 if ( options.fade ) {
11047 if ( mode === "show" ) {
11048 options.from.opacity = 0;
11049 options.to.opacity = 1;
11051 if ( mode === "hide" ) {
11052 options.from.opacity = 1;
11053 options.to.opacity = 0;
11058 el.effect( options );
11062 $.effects.effect.size = function( o, done ) {
11065 var original, baseline, factor,
11067 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
11070 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
11072 // Copy for children
11073 props2 = [ "width", "height", "overflow" ],
11074 cProps = [ "fontSize" ],
11075 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
11076 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
11079 mode = $.effects.setMode( el, o.mode || "effect" ),
11080 restore = o.restore || mode !== "effect",
11081 scale = o.scale || "both",
11082 origin = o.origin || [ "middle", "center" ],
11083 position = el.css( "position" ),
11084 props = restore ? props0 : props1,
11092 if ( mode === "show" ) {
11096 height: el.height(),
11098 outerHeight: el.outerHeight(),
11099 outerWidth: el.outerWidth()
11102 if ( o.mode === "toggle" && mode === "show" ) {
11103 el.from = o.to || zero;
11104 el.to = o.from || original;
11106 el.from = o.from || ( mode === "show" ? zero : original );
11107 el.to = o.to || ( mode === "hide" ? zero : original );
11110 // Set scaling factor
11113 y: el.from.height / original.height,
11114 x: el.from.width / original.width
11117 y: el.to.height / original.height,
11118 x: el.to.width / original.width
11122 // Scale the css box
11123 if ( scale === "box" || scale === "both" ) {
11125 // Vertical props scaling
11126 if ( factor.from.y !== factor.to.y ) {
11127 props = props.concat( vProps );
11128 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
11129 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
11132 // Horizontal props scaling
11133 if ( factor.from.x !== factor.to.x ) {
11134 props = props.concat( hProps );
11135 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
11136 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
11140 // Scale the content
11141 if ( scale === "content" || scale === "both" ) {
11143 // Vertical props scaling
11144 if ( factor.from.y !== factor.to.y ) {
11145 props = props.concat( cProps ).concat( props2 );
11146 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
11147 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
11151 $.effects.save( el, props );
11153 $.effects.createWrapper( el );
11154 el.css( "overflow", "hidden" ).css( el.from );
11157 if (origin) { // Calculate baseline shifts
11158 baseline = $.effects.getBaseline( origin, original );
11159 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
11160 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
11161 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
11162 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
11164 el.css( el.from ); // set top & left
11167 if ( scale === "content" || scale === "both" ) { // Scale the children
11169 // Add margins/font-size
11170 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
11171 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
11172 props2 = props0.concat(vProps).concat(hProps);
11174 el.find( "*[width]" ).each( function(){
11175 var child = $( this ),
11177 height: child.height(),
11178 width: child.width(),
11179 outerHeight: child.outerHeight(),
11180 outerWidth: child.outerWidth()
11183 $.effects.save(child, props2);
11187 height: c_original.height * factor.from.y,
11188 width: c_original.width * factor.from.x,
11189 outerHeight: c_original.outerHeight * factor.from.y,
11190 outerWidth: c_original.outerWidth * factor.from.x
11193 height: c_original.height * factor.to.y,
11194 width: c_original.width * factor.to.x,
11195 outerHeight: c_original.height * factor.to.y,
11196 outerWidth: c_original.width * factor.to.x
11199 // Vertical props scaling
11200 if ( factor.from.y !== factor.to.y ) {
11201 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
11202 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
11205 // Horizontal props scaling
11206 if ( factor.from.x !== factor.to.x ) {
11207 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
11208 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
11211 // Animate children
11212 child.css( child.from );
11213 child.animate( child.to, o.duration, o.easing, function() {
11215 // Restore children
11217 $.effects.restore( child, props2 );
11224 el.animate( el.to, {
11226 duration: o.duration,
11228 complete: function() {
11229 if ( el.to.opacity === 0 ) {
11230 el.css( "opacity", el.from.opacity );
11232 if( mode === "hide" ) {
11235 $.effects.restore( el, props );
11238 // we need to calculate our new positioning based on the scaling
11239 if ( position === "static" ) {
11241 position: "relative",
11246 $.each([ "top", "left" ], function( idx, pos ) {
11247 el.css( pos, function( _, str ) {
11248 var val = parseInt( str, 10 ),
11249 toRef = idx ? el.to.left : el.to.top;
11251 // if original was "auto", recalculate the new value from wrapper
11252 if ( str === "auto" ) {
11253 return toRef + "px";
11256 return val + toRef + "px";
11262 $.effects.removeWrapper( el );
11271 (function( $, undefined ) {
11273 $.effects.effect.shake = function( o, done ) {
11275 var el = $( this ),
11276 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
11277 mode = $.effects.setMode( el, o.mode || "effect" ),
11278 direction = o.direction || "left",
11279 distance = o.distance || 20,
11280 times = o.times || 3,
11281 anims = times * 2 + 1,
11282 speed = Math.round(o.duration/anims),
11283 ref = (direction === "up" || direction === "down") ? "top" : "left",
11284 positiveMotion = (direction === "up" || direction === "left"),
11290 // we will need to re-assemble the queue to stack our animations in place
11291 queue = el.queue(),
11292 queuelen = queue.length;
11294 $.effects.save( el, props );
11296 $.effects.createWrapper( el );
11299 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
11300 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
11301 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
11304 el.animate( animation, speed, o.easing );
11307 for ( i = 1; i < times; i++ ) {
11308 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
11311 .animate( animation1, speed, o.easing )
11312 .animate( animation, speed / 2, o.easing )
11313 .queue(function() {
11314 if ( mode === "hide" ) {
11317 $.effects.restore( el, props );
11318 $.effects.removeWrapper( el );
11322 // inject all the animations we just queued to be first in line (after "inprogress")
11323 if ( queuelen > 1) {
11324 queue.splice.apply( queue,
11325 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
11333 (function( $, undefined ) {
11335 $.effects.effect.slide = function( o, done ) {
11338 var el = $( this ),
11339 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
11340 mode = $.effects.setMode( el, o.mode || "show" ),
11341 show = mode === "show",
11342 direction = o.direction || "left",
11343 ref = (direction === "up" || direction === "down") ? "top" : "left",
11344 positiveMotion = (direction === "up" || direction === "left"),
11349 $.effects.save( el, props );
11351 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
11353 $.effects.createWrapper( el ).css({
11358 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
11362 animation[ ref ] = ( show ?
11363 ( positiveMotion ? "+=" : "-=") :
11364 ( positiveMotion ? "-=" : "+=")) +
11368 el.animate( animation, {
11370 duration: o.duration,
11372 complete: function() {
11373 if ( mode === "hide" ) {
11376 $.effects.restore( el, props );
11377 $.effects.removeWrapper( el );
11385 (function( $, undefined ) {
11387 $.effects.effect.transfer = function( o, done ) {
11388 var elem = $( this ),
11389 target = $( o.to ),
11390 targetFixed = target.css( "position" ) === "fixed",
11392 fixTop = targetFixed ? body.scrollTop() : 0,
11393 fixLeft = targetFixed ? body.scrollLeft() : 0,
11394 endPosition = target.offset(),
11396 top: endPosition.top - fixTop ,
11397 left: endPosition.left - fixLeft ,
11398 height: target.innerHeight(),
11399 width: target.innerWidth()
11401 startPosition = elem.offset(),
11402 transfer = $( "<div class='ui-effects-transfer'></div>" )
11403 .appendTo( document.body )
11404 .addClass( o.className )
11406 top: startPosition.top - fixTop ,
11407 left: startPosition.left - fixLeft ,
11408 height: elem.innerHeight(),
11409 width: elem.innerWidth(),
11410 position: targetFixed ? "fixed" : "absolute"
11412 .animate( animation, o.duration, o.easing, function() {
11420 (function( $, undefined ) {
11422 $.widget( "ui.menu", {
11424 defaultElement: "<ul>",
11428 submenu: "ui-icon-carat-1-e"
11443 _create: function() {
11444 this.activeMenu = this.element;
11445 // flag used to prevent firing of the click handler
11446 // as the event bubbles up through nested menus
11447 this.mouseHandled = false;
11450 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11451 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
11453 role: this.options.role,
11456 // need to catch all clicks on disabled menu
11457 // not possible through _on
11458 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
11459 if ( this.options.disabled ) {
11460 event.preventDefault();
11464 if ( this.options.disabled ) {
11466 .addClass( "ui-state-disabled" )
11467 .attr( "aria-disabled", "true" );
11471 // Prevent focus from sticking to links inside menu after clicking
11472 // them (focus should always stay on UL during navigation).
11473 "mousedown .ui-menu-item > a": function( event ) {
11474 event.preventDefault();
11476 "click .ui-state-disabled > a": function( event ) {
11477 event.preventDefault();
11479 "click .ui-menu-item:has(a)": function( event ) {
11480 var target = $( event.target ).closest( ".ui-menu-item" );
11481 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
11482 this.mouseHandled = true;
11484 this.select( event );
11485 // Open submenu on click
11486 if ( target.has( ".ui-menu" ).length ) {
11487 this.expand( event );
11488 } else if ( !this.element.is( ":focus" ) ) {
11489 // Redirect focus to the menu
11490 this.element.trigger( "focus", [ true ] );
11492 // If the active item is on the top level, let it stay active.
11493 // Otherwise, blur the active item since it is no longer visible.
11494 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
11495 clearTimeout( this.timer );
11500 "mouseenter .ui-menu-item": function( event ) {
11501 var target = $( event.currentTarget );
11502 // Remove ui-state-active class from siblings of the newly focused menu item
11503 // to avoid a jump caused by adjacent elements both having a class with a border
11504 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
11505 this.focus( event, target );
11507 mouseleave: "collapseAll",
11508 "mouseleave .ui-menu": "collapseAll",
11509 focus: function( event, keepActiveItem ) {
11510 // If there's already an active item, keep it active
11511 // If not, activate the first item
11512 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
11514 if ( !keepActiveItem ) {
11515 this.focus( event, item );
11518 blur: function( event ) {
11519 this._delay(function() {
11520 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
11521 this.collapseAll( event );
11525 keydown: "_keydown"
11530 // Clicks outside of a menu collapse any open menus
11531 this._on( this.document, {
11532 click: function( event ) {
11533 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
11534 this.collapseAll( event );
11537 // Reset the mouseHandled flag
11538 this.mouseHandled = false;
11543 _destroy: function() {
11544 // Destroy (sub)menus
11546 .removeAttr( "aria-activedescendant" )
11547 .find( ".ui-menu" ).addBack()
11548 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
11549 .removeAttr( "role" )
11550 .removeAttr( "tabIndex" )
11551 .removeAttr( "aria-labelledby" )
11552 .removeAttr( "aria-expanded" )
11553 .removeAttr( "aria-hidden" )
11554 .removeAttr( "aria-disabled" )
11558 // Destroy menu items
11559 this.element.find( ".ui-menu-item" )
11560 .removeClass( "ui-menu-item" )
11561 .removeAttr( "role" )
11562 .removeAttr( "aria-disabled" )
11565 .removeClass( "ui-corner-all ui-state-hover" )
11566 .removeAttr( "tabIndex" )
11567 .removeAttr( "role" )
11568 .removeAttr( "aria-haspopup" )
11569 .children().each( function() {
11570 var elem = $( this );
11571 if ( elem.data( "ui-menu-submenu-carat" ) ) {
11576 // Destroy menu dividers
11577 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
11580 _keydown: function( event ) {
11581 /*jshint maxcomplexity:20*/
11582 var match, prev, character, skip, regex,
11583 preventDefault = true;
11585 function escape( value ) {
11586 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
11589 switch ( event.keyCode ) {
11590 case $.ui.keyCode.PAGE_UP:
11591 this.previousPage( event );
11593 case $.ui.keyCode.PAGE_DOWN:
11594 this.nextPage( event );
11596 case $.ui.keyCode.HOME:
11597 this._move( "first", "first", event );
11599 case $.ui.keyCode.END:
11600 this._move( "last", "last", event );
11602 case $.ui.keyCode.UP:
11603 this.previous( event );
11605 case $.ui.keyCode.DOWN:
11606 this.next( event );
11608 case $.ui.keyCode.LEFT:
11609 this.collapse( event );
11611 case $.ui.keyCode.RIGHT:
11612 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
11613 this.expand( event );
11616 case $.ui.keyCode.ENTER:
11617 case $.ui.keyCode.SPACE:
11618 this._activate( event );
11620 case $.ui.keyCode.ESCAPE:
11621 this.collapse( event );
11624 preventDefault = false;
11625 prev = this.previousFilter || "";
11626 character = String.fromCharCode( event.keyCode );
11629 clearTimeout( this.filterTimer );
11631 if ( character === prev ) {
11634 character = prev + character;
11637 regex = new RegExp( "^" + escape( character ), "i" );
11638 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11639 return regex.test( $( this ).children( "a" ).text() );
11641 match = skip && match.index( this.active.next() ) !== -1 ?
11642 this.active.nextAll( ".ui-menu-item" ) :
11645 // If no matches on the current filter, reset to the last character pressed
11646 // to move down the menu to the first item that starts with that character
11647 if ( !match.length ) {
11648 character = String.fromCharCode( event.keyCode );
11649 regex = new RegExp( "^" + escape( character ), "i" );
11650 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11651 return regex.test( $( this ).children( "a" ).text() );
11655 if ( match.length ) {
11656 this.focus( event, match );
11657 if ( match.length > 1 ) {
11658 this.previousFilter = character;
11659 this.filterTimer = this._delay(function() {
11660 delete this.previousFilter;
11663 delete this.previousFilter;
11666 delete this.previousFilter;
11670 if ( preventDefault ) {
11671 event.preventDefault();
11675 _activate: function( event ) {
11676 if ( !this.active.is( ".ui-state-disabled" ) ) {
11677 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
11678 this.expand( event );
11680 this.select( event );
11685 refresh: function() {
11687 icon = this.options.icons.submenu,
11688 submenus = this.element.find( this.options.menus );
11690 // Initialize nested menus
11691 submenus.filter( ":not(.ui-menu)" )
11692 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11695 role: this.options.role,
11696 "aria-hidden": "true",
11697 "aria-expanded": "false"
11700 var menu = $( this ),
11701 item = menu.prev( "a" ),
11702 submenuCarat = $( "<span>" )
11703 .addClass( "ui-menu-icon ui-icon " + icon )
11704 .data( "ui-menu-submenu-carat", true );
11707 .attr( "aria-haspopup", "true" )
11708 .prepend( submenuCarat );
11709 menu.attr( "aria-labelledby", item.attr( "id" ) );
11712 menus = submenus.add( this.element );
11714 // Don't refresh list items that are already adapted
11715 menus.children( ":not(.ui-menu-item):has(a)" )
11716 .addClass( "ui-menu-item" )
11717 .attr( "role", "presentation" )
11720 .addClass( "ui-corner-all" )
11723 role: this._itemRole()
11726 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
11727 menus.children( ":not(.ui-menu-item)" ).each(function() {
11728 var item = $( this );
11729 // hyphen, em dash, en dash
11730 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
11731 item.addClass( "ui-widget-content ui-menu-divider" );
11735 // Add aria-disabled attribute to any disabled menu item
11736 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
11738 // If the active item has been removed, blur the menu
11739 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
11744 _itemRole: function() {
11748 }[ this.options.role ];
11751 _setOption: function( key, value ) {
11752 if ( key === "icons" ) {
11753 this.element.find( ".ui-menu-icon" )
11754 .removeClass( this.options.icons.submenu )
11755 .addClass( value.submenu );
11757 this._super( key, value );
11760 focus: function( event, item ) {
11761 var nested, focused;
11762 this.blur( event, event && event.type === "focus" );
11764 this._scrollIntoView( item );
11766 this.active = item.first();
11767 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
11768 // Only update aria-activedescendant if there's a role
11769 // otherwise we assume focus is managed elsewhere
11770 if ( this.options.role ) {
11771 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
11774 // Highlight active parent menu item, if any
11777 .closest( ".ui-menu-item" )
11778 .children( "a:first" )
11779 .addClass( "ui-state-active" );
11781 if ( event && event.type === "keydown" ) {
11784 this.timer = this._delay(function() {
11789 nested = item.children( ".ui-menu" );
11790 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
11791 this._startOpening(nested);
11793 this.activeMenu = item.parent();
11795 this._trigger( "focus", event, { item: item } );
11798 _scrollIntoView: function( item ) {
11799 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
11800 if ( this._hasScroll() ) {
11801 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
11802 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
11803 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
11804 scroll = this.activeMenu.scrollTop();
11805 elementHeight = this.activeMenu.height();
11806 itemHeight = item.height();
11808 if ( offset < 0 ) {
11809 this.activeMenu.scrollTop( scroll + offset );
11810 } else if ( offset + itemHeight > elementHeight ) {
11811 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
11816 blur: function( event, fromFocus ) {
11817 if ( !fromFocus ) {
11818 clearTimeout( this.timer );
11821 if ( !this.active ) {
11825 this.active.children( "a" ).removeClass( "ui-state-focus" );
11826 this.active = null;
11828 this._trigger( "blur", event, { item: this.active } );
11831 _startOpening: function( submenu ) {
11832 clearTimeout( this.timer );
11834 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
11835 // shift in the submenu position when mousing over the carat icon
11836 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
11840 this.timer = this._delay(function() {
11842 this._open( submenu );
11846 _open: function( submenu ) {
11847 var position = $.extend({
11849 }, this.options.position );
11851 clearTimeout( this.timer );
11852 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
11854 .attr( "aria-hidden", "true" );
11858 .removeAttr( "aria-hidden" )
11859 .attr( "aria-expanded", "true" )
11860 .position( position );
11863 collapseAll: function( event, all ) {
11864 clearTimeout( this.timer );
11865 this.timer = this._delay(function() {
11866 // If we were passed an event, look for the submenu that contains the event
11867 var currentMenu = all ? this.element :
11868 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
11870 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
11871 if ( !currentMenu.length ) {
11872 currentMenu = this.element;
11875 this._close( currentMenu );
11877 this.blur( event );
11878 this.activeMenu = currentMenu;
11882 // With no arguments, closes the currently active menu - if nothing is active
11883 // it closes all menus. If passed an argument, it will search for menus BELOW
11884 _close: function( startMenu ) {
11885 if ( !startMenu ) {
11886 startMenu = this.active ? this.active.parent() : this.element;
11890 .find( ".ui-menu" )
11892 .attr( "aria-hidden", "true" )
11893 .attr( "aria-expanded", "false" )
11895 .find( "a.ui-state-active" )
11896 .removeClass( "ui-state-active" );
11899 collapse: function( event ) {
11900 var newItem = this.active &&
11901 this.active.parent().closest( ".ui-menu-item", this.element );
11902 if ( newItem && newItem.length ) {
11904 this.focus( event, newItem );
11908 expand: function( event ) {
11909 var newItem = this.active &&
11911 .children( ".ui-menu " )
11912 .children( ".ui-menu-item" )
11915 if ( newItem && newItem.length ) {
11916 this._open( newItem.parent() );
11918 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
11919 this._delay(function() {
11920 this.focus( event, newItem );
11925 next: function( event ) {
11926 this._move( "next", "first", event );
11929 previous: function( event ) {
11930 this._move( "prev", "last", event );
11933 isFirstItem: function() {
11934 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
11937 isLastItem: function() {
11938 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
11941 _move: function( direction, filter, event ) {
11943 if ( this.active ) {
11944 if ( direction === "first" || direction === "last" ) {
11946 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
11950 [ direction + "All" ]( ".ui-menu-item" )
11954 if ( !next || !next.length || !this.active ) {
11955 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
11958 this.focus( event, next );
11961 nextPage: function( event ) {
11962 var item, base, height;
11964 if ( !this.active ) {
11965 this.next( event );
11968 if ( this.isLastItem() ) {
11971 if ( this._hasScroll() ) {
11972 base = this.active.offset().top;
11973 height = this.element.height();
11974 this.active.nextAll( ".ui-menu-item" ).each(function() {
11976 return item.offset().top - base - height < 0;
11979 this.focus( event, item );
11981 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
11982 [ !this.active ? "first" : "last" ]() );
11986 previousPage: function( event ) {
11987 var item, base, height;
11988 if ( !this.active ) {
11989 this.next( event );
11992 if ( this.isFirstItem() ) {
11995 if ( this._hasScroll() ) {
11996 base = this.active.offset().top;
11997 height = this.element.height();
11998 this.active.prevAll( ".ui-menu-item" ).each(function() {
12000 return item.offset().top - base + height > 0;
12003 this.focus( event, item );
12005 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
12009 _hasScroll: function() {
12010 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
12013 select: function( event ) {
12014 // TODO: It should never be possible to not have an active item at this
12015 // point, but the tests don't trigger mouseenter before click.
12016 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
12017 var ui = { item: this.active };
12018 if ( !this.active.has( ".ui-menu" ).length ) {
12019 this.collapseAll( event, true );
12021 this._trigger( "select", event, ui );
12027 (function( $, undefined ) {
12031 var cachedScrollbarWidth,
12034 round = Math.round,
12035 rhorizontal = /left|center|right/,
12036 rvertical = /top|center|bottom/,
12037 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
12038 rposition = /^\w+/,
12040 _position = $.fn.position;
12042 function getOffsets( offsets, width, height ) {
12044 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
12045 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
12049 function parseCss( element, property ) {
12050 return parseInt( $.css( element, property ), 10 ) || 0;
12053 function getDimensions( elem ) {
12055 if ( raw.nodeType === 9 ) {
12057 width: elem.width(),
12058 height: elem.height(),
12059 offset: { top: 0, left: 0 }
12062 if ( $.isWindow( raw ) ) {
12064 width: elem.width(),
12065 height: elem.height(),
12066 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
12069 if ( raw.preventDefault ) {
12073 offset: { top: raw.pageY, left: raw.pageX }
12077 width: elem.outerWidth(),
12078 height: elem.outerHeight(),
12079 offset: elem.offset()
12084 scrollbarWidth: function() {
12085 if ( cachedScrollbarWidth !== undefined ) {
12086 return cachedScrollbarWidth;
12089 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
12090 innerDiv = div.children()[0];
12092 $( "body" ).append( div );
12093 w1 = innerDiv.offsetWidth;
12094 div.css( "overflow", "scroll" );
12096 w2 = innerDiv.offsetWidth;
12099 w2 = div[0].clientWidth;
12104 return (cachedScrollbarWidth = w1 - w2);
12106 getScrollInfo: function( within ) {
12107 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
12108 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
12109 hasOverflowX = overflowX === "scroll" ||
12110 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
12111 hasOverflowY = overflowY === "scroll" ||
12112 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
12114 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
12115 height: hasOverflowX ? $.position.scrollbarWidth() : 0
12118 getWithinInfo: function( element ) {
12119 var withinElement = $( element || window ),
12120 isWindow = $.isWindow( withinElement[0] );
12122 element: withinElement,
12123 isWindow: isWindow,
12124 offset: withinElement.offset() || { left: 0, top: 0 },
12125 scrollLeft: withinElement.scrollLeft(),
12126 scrollTop: withinElement.scrollTop(),
12127 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
12128 height: isWindow ? withinElement.height() : withinElement.outerHeight()
12133 $.fn.position = function( options ) {
12134 if ( !options || !options.of ) {
12135 return _position.apply( this, arguments );
12138 // make a copy, we don't want to modify arguments
12139 options = $.extend( {}, options );
12141 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
12142 target = $( options.of ),
12143 within = $.position.getWithinInfo( options.within ),
12144 scrollInfo = $.position.getScrollInfo( within ),
12145 collision = ( options.collision || "flip" ).split( " " ),
12148 dimensions = getDimensions( target );
12149 if ( target[0].preventDefault ) {
12150 // force left top to allow flipping
12151 options.at = "left top";
12153 targetWidth = dimensions.width;
12154 targetHeight = dimensions.height;
12155 targetOffset = dimensions.offset;
12156 // clone to reuse original targetOffset later
12157 basePosition = $.extend( {}, targetOffset );
12159 // force my and at to have valid horizontal and vertical positions
12160 // if a value is missing or invalid, it will be converted to center
12161 $.each( [ "my", "at" ], function() {
12162 var pos = ( options[ this ] || "" ).split( " " ),
12166 if ( pos.length === 1) {
12167 pos = rhorizontal.test( pos[ 0 ] ) ?
12168 pos.concat( [ "center" ] ) :
12169 rvertical.test( pos[ 0 ] ) ?
12170 [ "center" ].concat( pos ) :
12171 [ "center", "center" ];
12173 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
12174 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
12176 // calculate offsets
12177 horizontalOffset = roffset.exec( pos[ 0 ] );
12178 verticalOffset = roffset.exec( pos[ 1 ] );
12179 offsets[ this ] = [
12180 horizontalOffset ? horizontalOffset[ 0 ] : 0,
12181 verticalOffset ? verticalOffset[ 0 ] : 0
12184 // reduce to just the positions without the offsets
12185 options[ this ] = [
12186 rposition.exec( pos[ 0 ] )[ 0 ],
12187 rposition.exec( pos[ 1 ] )[ 0 ]
12191 // normalize collision option
12192 if ( collision.length === 1 ) {
12193 collision[ 1 ] = collision[ 0 ];
12196 if ( options.at[ 0 ] === "right" ) {
12197 basePosition.left += targetWidth;
12198 } else if ( options.at[ 0 ] === "center" ) {
12199 basePosition.left += targetWidth / 2;
12202 if ( options.at[ 1 ] === "bottom" ) {
12203 basePosition.top += targetHeight;
12204 } else if ( options.at[ 1 ] === "center" ) {
12205 basePosition.top += targetHeight / 2;
12208 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
12209 basePosition.left += atOffset[ 0 ];
12210 basePosition.top += atOffset[ 1 ];
12212 return this.each(function() {
12213 var collisionPosition, using,
12215 elemWidth = elem.outerWidth(),
12216 elemHeight = elem.outerHeight(),
12217 marginLeft = parseCss( this, "marginLeft" ),
12218 marginTop = parseCss( this, "marginTop" ),
12219 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
12220 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
12221 position = $.extend( {}, basePosition ),
12222 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
12224 if ( options.my[ 0 ] === "right" ) {
12225 position.left -= elemWidth;
12226 } else if ( options.my[ 0 ] === "center" ) {
12227 position.left -= elemWidth / 2;
12230 if ( options.my[ 1 ] === "bottom" ) {
12231 position.top -= elemHeight;
12232 } else if ( options.my[ 1 ] === "center" ) {
12233 position.top -= elemHeight / 2;
12236 position.left += myOffset[ 0 ];
12237 position.top += myOffset[ 1 ];
12239 // if the browser doesn't support fractions, then round for consistent results
12240 if ( !$.support.offsetFractions ) {
12241 position.left = round( position.left );
12242 position.top = round( position.top );
12245 collisionPosition = {
12246 marginLeft: marginLeft,
12247 marginTop: marginTop
12250 $.each( [ "left", "top" ], function( i, dir ) {
12251 if ( $.ui.position[ collision[ i ] ] ) {
12252 $.ui.position[ collision[ i ] ][ dir ]( position, {
12253 targetWidth: targetWidth,
12254 targetHeight: targetHeight,
12255 elemWidth: elemWidth,
12256 elemHeight: elemHeight,
12257 collisionPosition: collisionPosition,
12258 collisionWidth: collisionWidth,
12259 collisionHeight: collisionHeight,
12260 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
12269 if ( options.using ) {
12270 // adds feedback as second argument to using callback, if present
12271 using = function( props ) {
12272 var left = targetOffset.left - position.left,
12273 right = left + targetWidth - elemWidth,
12274 top = targetOffset.top - position.top,
12275 bottom = top + targetHeight - elemHeight,
12279 left: targetOffset.left,
12280 top: targetOffset.top,
12281 width: targetWidth,
12282 height: targetHeight
12286 left: position.left,
12291 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
12292 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
12294 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
12295 feedback.horizontal = "center";
12297 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
12298 feedback.vertical = "middle";
12300 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
12301 feedback.important = "horizontal";
12303 feedback.important = "vertical";
12305 options.using.call( this, props, feedback );
12309 elem.offset( $.extend( position, { using: using } ) );
12315 left: function( position, data ) {
12316 var within = data.within,
12317 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
12318 outerWidth = within.width,
12319 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12320 overLeft = withinOffset - collisionPosLeft,
12321 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
12324 // element is wider than within
12325 if ( data.collisionWidth > outerWidth ) {
12326 // element is initially over the left side of within
12327 if ( overLeft > 0 && overRight <= 0 ) {
12328 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
12329 position.left += overLeft - newOverRight;
12330 // element is initially over right side of within
12331 } else if ( overRight > 0 && overLeft <= 0 ) {
12332 position.left = withinOffset;
12333 // element is initially over both left and right sides of within
12335 if ( overLeft > overRight ) {
12336 position.left = withinOffset + outerWidth - data.collisionWidth;
12338 position.left = withinOffset;
12341 // too far left -> align with left edge
12342 } else if ( overLeft > 0 ) {
12343 position.left += overLeft;
12344 // too far right -> align with right edge
12345 } else if ( overRight > 0 ) {
12346 position.left -= overRight;
12347 // adjust based on position and margin
12349 position.left = max( position.left - collisionPosLeft, position.left );
12352 top: function( position, data ) {
12353 var within = data.within,
12354 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
12355 outerHeight = data.within.height,
12356 collisionPosTop = position.top - data.collisionPosition.marginTop,
12357 overTop = withinOffset - collisionPosTop,
12358 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
12361 // element is taller than within
12362 if ( data.collisionHeight > outerHeight ) {
12363 // element is initially over the top of within
12364 if ( overTop > 0 && overBottom <= 0 ) {
12365 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
12366 position.top += overTop - newOverBottom;
12367 // element is initially over bottom of within
12368 } else if ( overBottom > 0 && overTop <= 0 ) {
12369 position.top = withinOffset;
12370 // element is initially over both top and bottom of within
12372 if ( overTop > overBottom ) {
12373 position.top = withinOffset + outerHeight - data.collisionHeight;
12375 position.top = withinOffset;
12378 // too far up -> align with top
12379 } else if ( overTop > 0 ) {
12380 position.top += overTop;
12381 // too far down -> align with bottom edge
12382 } else if ( overBottom > 0 ) {
12383 position.top -= overBottom;
12384 // adjust based on position and margin
12386 position.top = max( position.top - collisionPosTop, position.top );
12391 left: function( position, data ) {
12392 var within = data.within,
12393 withinOffset = within.offset.left + within.scrollLeft,
12394 outerWidth = within.width,
12395 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
12396 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12397 overLeft = collisionPosLeft - offsetLeft,
12398 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
12399 myOffset = data.my[ 0 ] === "left" ?
12401 data.my[ 0 ] === "right" ?
12404 atOffset = data.at[ 0 ] === "left" ?
12406 data.at[ 0 ] === "right" ?
12407 -data.targetWidth :
12409 offset = -2 * data.offset[ 0 ],
12413 if ( overLeft < 0 ) {
12414 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
12415 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
12416 position.left += myOffset + atOffset + offset;
12419 else if ( overRight > 0 ) {
12420 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
12421 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
12422 position.left += myOffset + atOffset + offset;
12426 top: function( position, data ) {
12427 var within = data.within,
12428 withinOffset = within.offset.top + within.scrollTop,
12429 outerHeight = within.height,
12430 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
12431 collisionPosTop = position.top - data.collisionPosition.marginTop,
12432 overTop = collisionPosTop - offsetTop,
12433 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
12434 top = data.my[ 1 ] === "top",
12437 data.my[ 1 ] === "bottom" ?
12440 atOffset = data.at[ 1 ] === "top" ?
12441 data.targetHeight :
12442 data.at[ 1 ] === "bottom" ?
12443 -data.targetHeight :
12445 offset = -2 * data.offset[ 1 ],
12448 if ( overTop < 0 ) {
12449 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
12450 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
12451 position.top += myOffset + atOffset + offset;
12454 else if ( overBottom > 0 ) {
12455 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
12456 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
12457 position.top += myOffset + atOffset + offset;
12464 $.ui.position.flip.left.apply( this, arguments );
12465 $.ui.position.fit.left.apply( this, arguments );
12468 $.ui.position.flip.top.apply( this, arguments );
12469 $.ui.position.fit.top.apply( this, arguments );
12474 // fraction support test
12476 var testElement, testElementParent, testElementStyle, offsetLeft, i,
12477 body = document.getElementsByTagName( "body" )[ 0 ],
12478 div = document.createElement( "div" );
12480 //Create a "fake body" for testing based on method used in jQuery.support
12481 testElement = document.createElement( body ? "div" : "body" );
12482 testElementStyle = {
12483 visibility: "hidden",
12491 $.extend( testElementStyle, {
12492 position: "absolute",
12497 for ( i in testElementStyle ) {
12498 testElement.style[ i ] = testElementStyle[ i ];
12500 testElement.appendChild( div );
12501 testElementParent = body || document.documentElement;
12502 testElementParent.insertBefore( testElement, testElementParent.firstChild );
12504 div.style.cssText = "position: absolute; left: 10.7432222px;";
12506 offsetLeft = $( div ).offset().left;
12507 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
12509 testElement.innerHTML = "";
12510 testElementParent.removeChild( testElement );
12515 (function( $, undefined ) {
12517 $.widget( "ui.progressbar", {
12529 _create: function() {
12530 // Constrain initial value
12531 this.oldValue = this.options.value = this._constrainedValue();
12534 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12536 // Only set static values, aria-valuenow and aria-valuemax are
12537 // set inside _refreshValue()
12538 role: "progressbar",
12539 "aria-valuemin": this.min
12542 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
12543 .appendTo( this.element );
12545 this._refreshValue();
12548 _destroy: function() {
12550 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12551 .removeAttr( "role" )
12552 .removeAttr( "aria-valuemin" )
12553 .removeAttr( "aria-valuemax" )
12554 .removeAttr( "aria-valuenow" );
12556 this.valueDiv.remove();
12559 value: function( newValue ) {
12560 if ( newValue === undefined ) {
12561 return this.options.value;
12564 this.options.value = this._constrainedValue( newValue );
12565 this._refreshValue();
12568 _constrainedValue: function( newValue ) {
12569 if ( newValue === undefined ) {
12570 newValue = this.options.value;
12573 this.indeterminate = newValue === false;
12576 if ( typeof newValue !== "number" ) {
12580 return this.indeterminate ? false :
12581 Math.min( this.options.max, Math.max( this.min, newValue ) );
12584 _setOptions: function( options ) {
12585 // Ensure "value" option is set after other values (like max)
12586 var value = options.value;
12587 delete options.value;
12589 this._super( options );
12591 this.options.value = this._constrainedValue( value );
12592 this._refreshValue();
12595 _setOption: function( key, value ) {
12596 if ( key === "max" ) {
12597 // Don't allow a max less than min
12598 value = Math.max( this.min, value );
12601 this._super( key, value );
12604 _percentage: function() {
12605 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
12608 _refreshValue: function() {
12609 var value = this.options.value,
12610 percentage = this._percentage();
12613 .toggle( this.indeterminate || value > this.min )
12614 .toggleClass( "ui-corner-right", value === this.options.max )
12615 .width( percentage.toFixed(0) + "%" );
12617 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
12619 if ( this.indeterminate ) {
12620 this.element.removeAttr( "aria-valuenow" );
12621 if ( !this.overlayDiv ) {
12622 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
12625 this.element.attr({
12626 "aria-valuemax": this.options.max,
12627 "aria-valuenow": value
12629 if ( this.overlayDiv ) {
12630 this.overlayDiv.remove();
12631 this.overlayDiv = null;
12635 if ( this.oldValue !== value ) {
12636 this.oldValue = value;
12637 this._trigger( "change" );
12639 if ( value === this.options.max ) {
12640 this._trigger( "complete" );
12647 (function( $, undefined ) {
12649 // number of pages in a slider
12650 // (how many times can you page up/down to go through the whole range)
12653 $.widget( "ui.slider", $.ui.mouse, {
12655 widgetEventPrefix: "slide",
12662 orientation: "horizontal",
12675 _create: function() {
12676 this._keySliding = false;
12677 this._mouseSliding = false;
12678 this._animateOff = true;
12679 this._handleIndex = null;
12680 this._detectOrientation();
12684 .addClass( "ui-slider" +
12685 " ui-slider-" + this.orientation +
12687 " ui-widget-content" +
12691 this._setOption( "disabled", this.options.disabled );
12693 this._animateOff = false;
12696 _refresh: function() {
12697 this._createRange();
12698 this._createHandles();
12699 this._setupEvents();
12700 this._refreshValue();
12703 _createHandles: function() {
12704 var i, handleCount,
12705 options = this.options,
12706 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
12707 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
12710 handleCount = ( options.values && options.values.length ) || 1;
12712 if ( existingHandles.length > handleCount ) {
12713 existingHandles.slice( handleCount ).remove();
12714 existingHandles = existingHandles.slice( 0, handleCount );
12717 for ( i = existingHandles.length; i < handleCount; i++ ) {
12718 handles.push( handle );
12721 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
12723 this.handle = this.handles.eq( 0 );
12725 this.handles.each(function( i ) {
12726 $( this ).data( "ui-slider-handle-index", i );
12730 _createRange: function() {
12731 var options = this.options,
12734 if ( options.range ) {
12735 if ( options.range === true ) {
12736 if ( !options.values ) {
12737 options.values = [ this._valueMin(), this._valueMin() ];
12738 } else if ( options.values.length && options.values.length !== 2 ) {
12739 options.values = [ options.values[0], options.values[0] ];
12740 } else if ( $.isArray( options.values ) ) {
12741 options.values = options.values.slice(0);
12745 if ( !this.range || !this.range.length ) {
12746 this.range = $( "<div></div>" )
12747 .appendTo( this.element );
12749 classes = "ui-slider-range" +
12750 // note: this isn't the most fittingly semantic framework class for this element,
12751 // but worked best visually with a variety of themes
12752 " ui-widget-header ui-corner-all";
12754 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
12755 // Handle range switching from true to min/max
12762 this.range.addClass( classes +
12763 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
12765 this.range = $([]);
12769 _setupEvents: function() {
12770 var elements = this.handles.add( this.range ).filter( "a" );
12771 this._off( elements );
12772 this._on( elements, this._handleEvents );
12773 this._hoverable( elements );
12774 this._focusable( elements );
12777 _destroy: function() {
12778 this.handles.remove();
12779 this.range.remove();
12782 .removeClass( "ui-slider" +
12783 " ui-slider-horizontal" +
12784 " ui-slider-vertical" +
12786 " ui-widget-content" +
12787 " ui-corner-all" );
12789 this._mouseDestroy();
12792 _mouseCapture: function( event ) {
12793 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
12797 if ( o.disabled ) {
12801 this.elementSize = {
12802 width: this.element.outerWidth(),
12803 height: this.element.outerHeight()
12805 this.elementOffset = this.element.offset();
12807 position = { x: event.pageX, y: event.pageY };
12808 normValue = this._normValueFromMouse( position );
12809 distance = this._valueMax() - this._valueMin() + 1;
12810 this.handles.each(function( i ) {
12811 var thisDistance = Math.abs( normValue - that.values(i) );
12812 if (( distance > thisDistance ) ||
12813 ( distance === thisDistance &&
12814 (i === that._lastChangedValue || that.values(i) === o.min ))) {
12815 distance = thisDistance;
12816 closestHandle = $( this );
12821 allowed = this._start( event, index );
12822 if ( allowed === false ) {
12825 this._mouseSliding = true;
12827 this._handleIndex = index;
12830 .addClass( "ui-state-active" )
12833 offset = closestHandle.offset();
12834 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
12835 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
12836 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
12837 top: event.pageY - offset.top -
12838 ( closestHandle.height() / 2 ) -
12839 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
12840 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
12841 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
12844 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
12845 this._slide( event, index, normValue );
12847 this._animateOff = true;
12851 _mouseStart: function() {
12855 _mouseDrag: function( event ) {
12856 var position = { x: event.pageX, y: event.pageY },
12857 normValue = this._normValueFromMouse( position );
12859 this._slide( event, this._handleIndex, normValue );
12864 _mouseStop: function( event ) {
12865 this.handles.removeClass( "ui-state-active" );
12866 this._mouseSliding = false;
12868 this._stop( event, this._handleIndex );
12869 this._change( event, this._handleIndex );
12871 this._handleIndex = null;
12872 this._clickOffset = null;
12873 this._animateOff = false;
12878 _detectOrientation: function() {
12879 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
12882 _normValueFromMouse: function( position ) {
12889 if ( this.orientation === "horizontal" ) {
12890 pixelTotal = this.elementSize.width;
12891 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
12893 pixelTotal = this.elementSize.height;
12894 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
12897 percentMouse = ( pixelMouse / pixelTotal );
12898 if ( percentMouse > 1 ) {
12901 if ( percentMouse < 0 ) {
12904 if ( this.orientation === "vertical" ) {
12905 percentMouse = 1 - percentMouse;
12908 valueTotal = this._valueMax() - this._valueMin();
12909 valueMouse = this._valueMin() + percentMouse * valueTotal;
12911 return this._trimAlignValue( valueMouse );
12914 _start: function( event, index ) {
12916 handle: this.handles[ index ],
12917 value: this.value()
12919 if ( this.options.values && this.options.values.length ) {
12920 uiHash.value = this.values( index );
12921 uiHash.values = this.values();
12923 return this._trigger( "start", event, uiHash );
12926 _slide: function( event, index, newVal ) {
12931 if ( this.options.values && this.options.values.length ) {
12932 otherVal = this.values( index ? 0 : 1 );
12934 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
12935 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
12940 if ( newVal !== this.values( index ) ) {
12941 newValues = this.values();
12942 newValues[ index ] = newVal;
12943 // A slide can be canceled by returning false from the slide callback
12944 allowed = this._trigger( "slide", event, {
12945 handle: this.handles[ index ],
12949 otherVal = this.values( index ? 0 : 1 );
12950 if ( allowed !== false ) {
12951 this.values( index, newVal, true );
12955 if ( newVal !== this.value() ) {
12956 // A slide can be canceled by returning false from the slide callback
12957 allowed = this._trigger( "slide", event, {
12958 handle: this.handles[ index ],
12961 if ( allowed !== false ) {
12962 this.value( newVal );
12968 _stop: function( event, index ) {
12970 handle: this.handles[ index ],
12971 value: this.value()
12973 if ( this.options.values && this.options.values.length ) {
12974 uiHash.value = this.values( index );
12975 uiHash.values = this.values();
12978 this._trigger( "stop", event, uiHash );
12981 _change: function( event, index ) {
12982 if ( !this._keySliding && !this._mouseSliding ) {
12984 handle: this.handles[ index ],
12985 value: this.value()
12987 if ( this.options.values && this.options.values.length ) {
12988 uiHash.value = this.values( index );
12989 uiHash.values = this.values();
12992 //store the last changed value index for reference when handles overlap
12993 this._lastChangedValue = index;
12995 this._trigger( "change", event, uiHash );
12999 value: function( newValue ) {
13000 if ( arguments.length ) {
13001 this.options.value = this._trimAlignValue( newValue );
13002 this._refreshValue();
13003 this._change( null, 0 );
13007 return this._value();
13010 values: function( index, newValue ) {
13015 if ( arguments.length > 1 ) {
13016 this.options.values[ index ] = this._trimAlignValue( newValue );
13017 this._refreshValue();
13018 this._change( null, index );
13022 if ( arguments.length ) {
13023 if ( $.isArray( arguments[ 0 ] ) ) {
13024 vals = this.options.values;
13025 newValues = arguments[ 0 ];
13026 for ( i = 0; i < vals.length; i += 1 ) {
13027 vals[ i ] = this._trimAlignValue( newValues[ i ] );
13028 this._change( null, i );
13030 this._refreshValue();
13032 if ( this.options.values && this.options.values.length ) {
13033 return this._values( index );
13035 return this.value();
13039 return this._values();
13043 _setOption: function( key, value ) {
13047 if ( key === "range" && this.options.range === true ) {
13048 if ( value === "min" ) {
13049 this.options.value = this._values( 0 );
13050 this.options.values = null;
13051 } else if ( value === "max" ) {
13052 this.options.value = this._values( this.options.values.length-1 );
13053 this.options.values = null;
13057 if ( $.isArray( this.options.values ) ) {
13058 valsLength = this.options.values.length;
13061 $.Widget.prototype._setOption.apply( this, arguments );
13064 case "orientation":
13065 this._detectOrientation();
13067 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
13068 .addClass( "ui-slider-" + this.orientation );
13069 this._refreshValue();
13072 this._animateOff = true;
13073 this._refreshValue();
13074 this._change( null, 0 );
13075 this._animateOff = false;
13078 this._animateOff = true;
13079 this._refreshValue();
13080 for ( i = 0; i < valsLength; i += 1 ) {
13081 this._change( null, i );
13083 this._animateOff = false;
13087 this._animateOff = true;
13088 this._refreshValue();
13089 this._animateOff = false;
13092 this._animateOff = true;
13094 this._animateOff = false;
13099 //internal value getter
13100 // _value() returns value trimmed by min and max, aligned by step
13101 _value: function() {
13102 var val = this.options.value;
13103 val = this._trimAlignValue( val );
13108 //internal values getter
13109 // _values() returns array of values trimmed by min and max, aligned by step
13110 // _values( index ) returns single value trimmed by min and max, aligned by step
13111 _values: function( index ) {
13116 if ( arguments.length ) {
13117 val = this.options.values[ index ];
13118 val = this._trimAlignValue( val );
13121 } else if ( this.options.values && this.options.values.length ) {
13122 // .slice() creates a copy of the array
13123 // this copy gets trimmed by min and max and then returned
13124 vals = this.options.values.slice();
13125 for ( i = 0; i < vals.length; i+= 1) {
13126 vals[ i ] = this._trimAlignValue( vals[ i ] );
13135 // returns the step-aligned value that val is closest to, between (inclusive) min and max
13136 _trimAlignValue: function( val ) {
13137 if ( val <= this._valueMin() ) {
13138 return this._valueMin();
13140 if ( val >= this._valueMax() ) {
13141 return this._valueMax();
13143 var step = ( this.options.step > 0 ) ? this.options.step : 1,
13144 valModStep = (val - this._valueMin()) % step,
13145 alignValue = val - valModStep;
13147 if ( Math.abs(valModStep) * 2 >= step ) {
13148 alignValue += ( valModStep > 0 ) ? step : ( -step );
13151 // Since JavaScript has problems with large floats, round
13152 // the final value to 5 digits after the decimal point (see #4124)
13153 return parseFloat( alignValue.toFixed(5) );
13156 _valueMin: function() {
13157 return this.options.min;
13160 _valueMax: function() {
13161 return this.options.max;
13164 _refreshValue: function() {
13165 var lastValPercent, valPercent, value, valueMin, valueMax,
13166 oRange = this.options.range,
13169 animate = ( !this._animateOff ) ? o.animate : false,
13172 if ( this.options.values && this.options.values.length ) {
13173 this.handles.each(function( i ) {
13174 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
13175 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13176 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13177 if ( that.options.range === true ) {
13178 if ( that.orientation === "horizontal" ) {
13180 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
13183 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13187 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
13190 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13194 lastValPercent = valPercent;
13197 value = this.value();
13198 valueMin = this._valueMin();
13199 valueMax = this._valueMax();
13200 valPercent = ( valueMax !== valueMin ) ?
13201 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
13203 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13204 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13206 if ( oRange === "min" && this.orientation === "horizontal" ) {
13207 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
13209 if ( oRange === "max" && this.orientation === "horizontal" ) {
13210 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13212 if ( oRange === "min" && this.orientation === "vertical" ) {
13213 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
13215 if ( oRange === "max" && this.orientation === "vertical" ) {
13216 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13222 keydown: function( event ) {
13223 /*jshint maxcomplexity:25*/
13224 var allowed, curVal, newVal, step,
13225 index = $( event.target ).data( "ui-slider-handle-index" );
13227 switch ( event.keyCode ) {
13228 case $.ui.keyCode.HOME:
13229 case $.ui.keyCode.END:
13230 case $.ui.keyCode.PAGE_UP:
13231 case $.ui.keyCode.PAGE_DOWN:
13232 case $.ui.keyCode.UP:
13233 case $.ui.keyCode.RIGHT:
13234 case $.ui.keyCode.DOWN:
13235 case $.ui.keyCode.LEFT:
13236 event.preventDefault();
13237 if ( !this._keySliding ) {
13238 this._keySliding = true;
13239 $( event.target ).addClass( "ui-state-active" );
13240 allowed = this._start( event, index );
13241 if ( allowed === false ) {
13248 step = this.options.step;
13249 if ( this.options.values && this.options.values.length ) {
13250 curVal = newVal = this.values( index );
13252 curVal = newVal = this.value();
13255 switch ( event.keyCode ) {
13256 case $.ui.keyCode.HOME:
13257 newVal = this._valueMin();
13259 case $.ui.keyCode.END:
13260 newVal = this._valueMax();
13262 case $.ui.keyCode.PAGE_UP:
13263 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
13265 case $.ui.keyCode.PAGE_DOWN:
13266 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
13268 case $.ui.keyCode.UP:
13269 case $.ui.keyCode.RIGHT:
13270 if ( curVal === this._valueMax() ) {
13273 newVal = this._trimAlignValue( curVal + step );
13275 case $.ui.keyCode.DOWN:
13276 case $.ui.keyCode.LEFT:
13277 if ( curVal === this._valueMin() ) {
13280 newVal = this._trimAlignValue( curVal - step );
13284 this._slide( event, index, newVal );
13286 click: function( event ) {
13287 event.preventDefault();
13289 keyup: function( event ) {
13290 var index = $( event.target ).data( "ui-slider-handle-index" );
13292 if ( this._keySliding ) {
13293 this._keySliding = false;
13294 this._stop( event, index );
13295 this._change( event, index );
13296 $( event.target ).removeClass( "ui-state-active" );
13307 function modifier( fn ) {
13308 return function() {
13309 var previous = this.element.val();
13310 fn.apply( this, arguments );
13312 if ( previous !== this.element.val() ) {
13313 this._trigger( "change" );
13318 $.widget( "ui.spinner", {
13320 defaultElement: "<input>",
13321 widgetEventPrefix: "spin",
13325 down: "ui-icon-triangle-1-s",
13326 up: "ui-icon-triangle-1-n"
13331 numberFormat: null,
13341 _create: function() {
13342 // handle string values that need to be parsed
13343 this._setOption( "max", this.options.max );
13344 this._setOption( "min", this.options.min );
13345 this._setOption( "step", this.options.step );
13347 // format the value, but don't constrain
13348 this._value( this.element.val(), true );
13351 this._on( this._events );
13354 // turning off autocomplete prevents the browser from remembering the
13355 // value when navigating through history, so we re-enable autocomplete
13356 // if the page is unloaded before the widget is destroyed. #7790
13357 this._on( this.window, {
13358 beforeunload: function() {
13359 this.element.removeAttr( "autocomplete" );
13364 _getCreateOptions: function() {
13366 element = this.element;
13368 $.each( [ "min", "max", "step" ], function( i, option ) {
13369 var value = element.attr( option );
13370 if ( value !== undefined && value.length ) {
13371 options[ option ] = value;
13379 keydown: function( event ) {
13380 if ( this._start( event ) && this._keydown( event ) ) {
13381 event.preventDefault();
13385 focus: function() {
13386 this.previous = this.element.val();
13388 blur: function( event ) {
13389 if ( this.cancelBlur ) {
13390 delete this.cancelBlur;
13396 if ( this.previous !== this.element.val() ) {
13397 this._trigger( "change", event );
13400 mousewheel: function( event, delta ) {
13404 if ( !this.spinning && !this._start( event ) ) {
13408 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
13409 clearTimeout( this.mousewheelTimer );
13410 this.mousewheelTimer = this._delay(function() {
13411 if ( this.spinning ) {
13412 this._stop( event );
13415 event.preventDefault();
13417 "mousedown .ui-spinner-button": function( event ) {
13420 // We never want the buttons to have focus; whenever the user is
13421 // interacting with the spinner, the focus should be on the input.
13422 // If the input is focused then this.previous is properly set from
13423 // when the input first received focus. If the input is not focused
13424 // then we need to set this.previous based on the value before spinning.
13425 previous = this.element[0] === this.document[0].activeElement ?
13426 this.previous : this.element.val();
13427 function checkFocus() {
13428 var isActive = this.element[0] === this.document[0].activeElement;
13430 this.element.focus();
13431 this.previous = previous;
13433 // IE sets focus asynchronously, so we need to check if focus
13434 // moved off of the input because the user clicked on the button.
13435 this._delay(function() {
13436 this.previous = previous;
13441 // ensure focus is on (or stays on) the text field
13442 event.preventDefault();
13443 checkFocus.call( this );
13446 // IE doesn't prevent moving focus even with event.preventDefault()
13447 // so we set a flag to know when we should ignore the blur event
13448 // and check (again) if focus moved off of the input.
13449 this.cancelBlur = true;
13450 this._delay(function() {
13451 delete this.cancelBlur;
13452 checkFocus.call( this );
13455 if ( this._start( event ) === false ) {
13459 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13461 "mouseup .ui-spinner-button": "_stop",
13462 "mouseenter .ui-spinner-button": function( event ) {
13463 // button will add ui-state-active if mouse was down while mouseleave and kept down
13464 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
13468 if ( this._start( event ) === false ) {
13471 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13473 // TODO: do we really want to consider this a stop?
13474 // shouldn't we just stop the repeater and wait until mouseup before
13475 // we trigger the stop event?
13476 "mouseleave .ui-spinner-button": "_stop"
13479 _draw: function() {
13480 var uiSpinner = this.uiSpinner = this.element
13481 .addClass( "ui-spinner-input" )
13482 .attr( "autocomplete", "off" )
13483 .wrap( this._uiSpinnerHtml() )
13486 .append( this._buttonHtml() );
13488 this.element.attr( "role", "spinbutton" );
13491 this.buttons = uiSpinner.find( ".ui-spinner-button" )
13492 .attr( "tabIndex", -1 )
13494 .removeClass( "ui-corner-all" );
13496 // IE 6 doesn't understand height: 50% for the buttons
13497 // unless the wrapper has an explicit height
13498 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
13499 uiSpinner.height() > 0 ) {
13500 uiSpinner.height( uiSpinner.height() );
13503 // disable spinner if element was already disabled
13504 if ( this.options.disabled ) {
13509 _keydown: function( event ) {
13510 var options = this.options,
13511 keyCode = $.ui.keyCode;
13513 switch ( event.keyCode ) {
13515 this._repeat( null, 1, event );
13518 this._repeat( null, -1, event );
13520 case keyCode.PAGE_UP:
13521 this._repeat( null, options.page, event );
13523 case keyCode.PAGE_DOWN:
13524 this._repeat( null, -options.page, event );
13531 _uiSpinnerHtml: function() {
13532 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
13535 _buttonHtml: function() {
13537 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
13538 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
13540 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
13541 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
13545 _start: function( event ) {
13546 if ( !this.spinning && this._trigger( "start", event ) === false ) {
13550 if ( !this.counter ) {
13553 this.spinning = true;
13557 _repeat: function( i, steps, event ) {
13560 clearTimeout( this.timer );
13561 this.timer = this._delay(function() {
13562 this._repeat( 40, steps, event );
13565 this._spin( steps * this.options.step, event );
13568 _spin: function( step, event ) {
13569 var value = this.value() || 0;
13571 if ( !this.counter ) {
13575 value = this._adjustValue( value + step * this._increment( this.counter ) );
13577 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
13578 this._value( value );
13583 _increment: function( i ) {
13584 var incremental = this.options.incremental;
13586 if ( incremental ) {
13587 return $.isFunction( incremental ) ?
13589 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
13595 _precision: function() {
13596 var precision = this._precisionOf( this.options.step );
13597 if ( this.options.min !== null ) {
13598 precision = Math.max( precision, this._precisionOf( this.options.min ) );
13603 _precisionOf: function( num ) {
13604 var str = num.toString(),
13605 decimal = str.indexOf( "." );
13606 return decimal === -1 ? 0 : str.length - decimal - 1;
13609 _adjustValue: function( value ) {
13610 var base, aboveMin,
13611 options = this.options;
13613 // make sure we're at a valid step
13614 // - find out where we are relative to the base (min or 0)
13615 base = options.min !== null ? options.min : 0;
13616 aboveMin = value - base;
13617 // - round to the nearest step
13618 aboveMin = Math.round(aboveMin / options.step) * options.step;
13619 // - rounding is based on 0, so adjust back to our base
13620 value = base + aboveMin;
13622 // fix precision from bad JS floating point math
13623 value = parseFloat( value.toFixed( this._precision() ) );
13626 if ( options.max !== null && value > options.max) {
13627 return options.max;
13629 if ( options.min !== null && value < options.min ) {
13630 return options.min;
13636 _stop: function( event ) {
13637 if ( !this.spinning ) {
13641 clearTimeout( this.timer );
13642 clearTimeout( this.mousewheelTimer );
13644 this.spinning = false;
13645 this._trigger( "stop", event );
13648 _setOption: function( key, value ) {
13649 if ( key === "culture" || key === "numberFormat" ) {
13650 var prevValue = this._parse( this.element.val() );
13651 this.options[ key ] = value;
13652 this.element.val( this._format( prevValue ) );
13656 if ( key === "max" || key === "min" || key === "step" ) {
13657 if ( typeof value === "string" ) {
13658 value = this._parse( value );
13661 if ( key === "icons" ) {
13662 this.buttons.first().find( ".ui-icon" )
13663 .removeClass( this.options.icons.up )
13664 .addClass( value.up );
13665 this.buttons.last().find( ".ui-icon" )
13666 .removeClass( this.options.icons.down )
13667 .addClass( value.down );
13670 this._super( key, value );
13672 if ( key === "disabled" ) {
13674 this.element.prop( "disabled", true );
13675 this.buttons.button( "disable" );
13677 this.element.prop( "disabled", false );
13678 this.buttons.button( "enable" );
13683 _setOptions: modifier(function( options ) {
13684 this._super( options );
13685 this._value( this.element.val() );
13688 _parse: function( val ) {
13689 if ( typeof val === "string" && val !== "" ) {
13690 val = window.Globalize && this.options.numberFormat ?
13691 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
13693 return val === "" || isNaN( val ) ? null : val;
13696 _format: function( value ) {
13697 if ( value === "" ) {
13700 return window.Globalize && this.options.numberFormat ?
13701 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
13705 _refresh: function() {
13706 this.element.attr({
13707 "aria-valuemin": this.options.min,
13708 "aria-valuemax": this.options.max,
13709 // TODO: what should we do with values that can't be parsed?
13710 "aria-valuenow": this._parse( this.element.val() )
13714 // update the value without triggering change
13715 _value: function( value, allowAny ) {
13717 if ( value !== "" ) {
13718 parsed = this._parse( value );
13719 if ( parsed !== null ) {
13721 parsed = this._adjustValue( parsed );
13723 value = this._format( parsed );
13726 this.element.val( value );
13730 _destroy: function() {
13732 .removeClass( "ui-spinner-input" )
13733 .prop( "disabled", false )
13734 .removeAttr( "autocomplete" )
13735 .removeAttr( "role" )
13736 .removeAttr( "aria-valuemin" )
13737 .removeAttr( "aria-valuemax" )
13738 .removeAttr( "aria-valuenow" );
13739 this.uiSpinner.replaceWith( this.element );
13742 stepUp: modifier(function( steps ) {
13743 this._stepUp( steps );
13745 _stepUp: function( steps ) {
13746 if ( this._start() ) {
13747 this._spin( (steps || 1) * this.options.step );
13752 stepDown: modifier(function( steps ) {
13753 this._stepDown( steps );
13755 _stepDown: function( steps ) {
13756 if ( this._start() ) {
13757 this._spin( (steps || 1) * -this.options.step );
13762 pageUp: modifier(function( pages ) {
13763 this._stepUp( (pages || 1) * this.options.page );
13766 pageDown: modifier(function( pages ) {
13767 this._stepDown( (pages || 1) * this.options.page );
13770 value: function( newVal ) {
13771 if ( !arguments.length ) {
13772 return this._parse( this.element.val() );
13774 modifier( this._value ).call( this, newVal );
13777 widget: function() {
13778 return this.uiSpinner;
13784 (function( $, undefined ) {
13789 function getNextTabId() {
13793 function isLocal( anchor ) {
13794 return anchor.hash.length > 1 &&
13795 decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
13796 decodeURIComponent( location.href.replace( rhash, "" ) );
13799 $.widget( "ui.tabs", {
13804 collapsible: false,
13806 heightStyle: "content",
13812 beforeActivate: null,
13817 _create: function() {
13819 options = this.options;
13821 this.running = false;
13824 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
13825 .toggleClass( "ui-tabs-collapsible", options.collapsible )
13826 // Prevent users from focusing disabled tabs via click
13827 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
13828 if ( $( this ).is( ".ui-state-disabled" ) ) {
13829 event.preventDefault();
13833 // Preventing the default action in mousedown doesn't prevent IE
13834 // from focusing the element, so if the anchor gets focused, blur.
13835 // We don't have to worry about focusing the previously focused
13836 // element since clicking on a non-focusable element should focus
13837 // the body anyway.
13838 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
13839 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
13844 this._processTabs();
13845 options.active = this._initialActive();
13847 // Take disabling tabs via class attribute from HTML
13848 // into account and update option properly.
13849 if ( $.isArray( options.disabled ) ) {
13850 options.disabled = $.unique( options.disabled.concat(
13851 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
13852 return that.tabs.index( li );
13857 // check for length avoids error when initializing empty list
13858 if ( this.options.active !== false && this.anchors.length ) {
13859 this.active = this._findActive( options.active );
13866 if ( this.active.length ) {
13867 this.load( options.active );
13871 _initialActive: function() {
13872 var active = this.options.active,
13873 collapsible = this.options.collapsible,
13874 locationHash = location.hash.substring( 1 );
13876 if ( active === null ) {
13877 // check the fragment identifier in the URL
13878 if ( locationHash ) {
13879 this.tabs.each(function( i, tab ) {
13880 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
13887 // check for a tab marked active via a class
13888 if ( active === null ) {
13889 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
13892 // no active tab, set to false
13893 if ( active === null || active === -1 ) {
13894 active = this.tabs.length ? 0 : false;
13898 // handle numbers: negative, out of range
13899 if ( active !== false ) {
13900 active = this.tabs.index( this.tabs.eq( active ) );
13901 if ( active === -1 ) {
13902 active = collapsible ? false : 0;
13906 // don't allow collapsible: false and active: false
13907 if ( !collapsible && active === false && this.anchors.length ) {
13914 _getCreateEventData: function() {
13917 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
13921 _tabKeydown: function( event ) {
13922 /*jshint maxcomplexity:15*/
13923 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
13924 selectedIndex = this.tabs.index( focusedTab ),
13925 goingForward = true;
13927 if ( this._handlePageNav( event ) ) {
13931 switch ( event.keyCode ) {
13932 case $.ui.keyCode.RIGHT:
13933 case $.ui.keyCode.DOWN:
13936 case $.ui.keyCode.UP:
13937 case $.ui.keyCode.LEFT:
13938 goingForward = false;
13941 case $.ui.keyCode.END:
13942 selectedIndex = this.anchors.length - 1;
13944 case $.ui.keyCode.HOME:
13947 case $.ui.keyCode.SPACE:
13948 // Activate only, no collapsing
13949 event.preventDefault();
13950 clearTimeout( this.activating );
13951 this._activate( selectedIndex );
13953 case $.ui.keyCode.ENTER:
13954 // Toggle (cancel delayed activation, allow collapsing)
13955 event.preventDefault();
13956 clearTimeout( this.activating );
13957 // Determine if we should collapse or activate
13958 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
13964 // Focus the appropriate tab, based on which key was pressed
13965 event.preventDefault();
13966 clearTimeout( this.activating );
13967 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
13969 // Navigating with control key will prevent automatic activation
13970 if ( !event.ctrlKey ) {
13971 // Update aria-selected immediately so that AT think the tab is already selected.
13972 // Otherwise AT may confuse the user by stating that they need to activate the tab,
13973 // but the tab will already be activated by the time the announcement finishes.
13974 focusedTab.attr( "aria-selected", "false" );
13975 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
13977 this.activating = this._delay(function() {
13978 this.option( "active", selectedIndex );
13983 _panelKeydown: function( event ) {
13984 if ( this._handlePageNav( event ) ) {
13988 // Ctrl+up moves focus to the current tab
13989 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
13990 event.preventDefault();
13991 this.active.focus();
13995 // Alt+page up/down moves focus to the previous/next tab (and activates)
13996 _handlePageNav: function( event ) {
13997 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
13998 this._activate( this._focusNextTab( this.options.active - 1, false ) );
14001 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
14002 this._activate( this._focusNextTab( this.options.active + 1, true ) );
14007 _findNextTab: function( index, goingForward ) {
14008 var lastTabIndex = this.tabs.length - 1;
14010 function constrain() {
14011 if ( index > lastTabIndex ) {
14015 index = lastTabIndex;
14020 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
14021 index = goingForward ? index + 1 : index - 1;
14027 _focusNextTab: function( index, goingForward ) {
14028 index = this._findNextTab( index, goingForward );
14029 this.tabs.eq( index ).focus();
14033 _setOption: function( key, value ) {
14034 if ( key === "active" ) {
14035 // _activate() will handle invalid values and update this.options
14036 this._activate( value );
14040 if ( key === "disabled" ) {
14041 // don't use the widget factory's disabled handling
14042 this._setupDisabled( value );
14046 this._super( key, value);
14048 if ( key === "collapsible" ) {
14049 this.element.toggleClass( "ui-tabs-collapsible", value );
14050 // Setting collapsible: false while collapsed; open first panel
14051 if ( !value && this.options.active === false ) {
14052 this._activate( 0 );
14056 if ( key === "event" ) {
14057 this._setupEvents( value );
14060 if ( key === "heightStyle" ) {
14061 this._setupHeightStyle( value );
14065 _tabId: function( tab ) {
14066 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
14069 _sanitizeSelector: function( hash ) {
14070 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
14073 refresh: function() {
14074 var options = this.options,
14075 lis = this.tablist.children( ":has(a[href])" );
14077 // get disabled tabs from class attribute from HTML
14078 // this will get converted to a boolean if needed in _refresh()
14079 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
14080 return lis.index( tab );
14083 this._processTabs();
14085 // was collapsed or no tabs
14086 if ( options.active === false || !this.anchors.length ) {
14087 options.active = false;
14089 // was active, but active tab is gone
14090 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
14091 // all remaining tabs are disabled
14092 if ( this.tabs.length === options.disabled.length ) {
14093 options.active = false;
14095 // activate previous tab
14097 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
14099 // was active, active tab still exists
14101 // make sure active index is correct
14102 options.active = this.tabs.index( this.active );
14108 _refresh: function() {
14109 this._setupDisabled( this.options.disabled );
14110 this._setupEvents( this.options.event );
14111 this._setupHeightStyle( this.options.heightStyle );
14113 this.tabs.not( this.active ).attr({
14114 "aria-selected": "false",
14117 this.panels.not( this._getPanelForTab( this.active ) )
14120 "aria-expanded": "false",
14121 "aria-hidden": "true"
14124 // Make sure one tab is in the tab order
14125 if ( !this.active.length ) {
14126 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
14129 .addClass( "ui-tabs-active ui-state-active" )
14131 "aria-selected": "true",
14134 this._getPanelForTab( this.active )
14137 "aria-expanded": "true",
14138 "aria-hidden": "false"
14143 _processTabs: function() {
14146 this.tablist = this._getList()
14147 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14148 .attr( "role", "tablist" );
14150 this.tabs = this.tablist.find( "> li:has(a[href])" )
14151 .addClass( "ui-state-default ui-corner-top" )
14157 this.anchors = this.tabs.map(function() {
14158 return $( "a", this )[ 0 ];
14160 .addClass( "ui-tabs-anchor" )
14162 role: "presentation",
14168 this.anchors.each(function( i, anchor ) {
14169 var selector, panel, panelId,
14170 anchorId = $( anchor ).uniqueId().attr( "id" ),
14171 tab = $( anchor ).closest( "li" ),
14172 originalAriaControls = tab.attr( "aria-controls" );
14175 if ( isLocal( anchor ) ) {
14176 selector = anchor.hash;
14177 panel = that.element.find( that._sanitizeSelector( selector ) );
14180 panelId = that._tabId( tab );
14181 selector = "#" + panelId;
14182 panel = that.element.find( selector );
14183 if ( !panel.length ) {
14184 panel = that._createPanel( panelId );
14185 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
14187 panel.attr( "aria-live", "polite" );
14190 if ( panel.length) {
14191 that.panels = that.panels.add( panel );
14193 if ( originalAriaControls ) {
14194 tab.data( "ui-tabs-aria-controls", originalAriaControls );
14197 "aria-controls": selector.substring( 1 ),
14198 "aria-labelledby": anchorId
14200 panel.attr( "aria-labelledby", anchorId );
14204 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14205 .attr( "role", "tabpanel" );
14208 // allow overriding how to find the list for rare usage scenarios (#7715)
14209 _getList: function() {
14210 return this.element.find( "ol,ul" ).eq( 0 );
14213 _createPanel: function( id ) {
14214 return $( "<div>" )
14216 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14217 .data( "ui-tabs-destroy", true );
14220 _setupDisabled: function( disabled ) {
14221 if ( $.isArray( disabled ) ) {
14222 if ( !disabled.length ) {
14224 } else if ( disabled.length === this.anchors.length ) {
14230 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
14231 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
14233 .addClass( "ui-state-disabled" )
14234 .attr( "aria-disabled", "true" );
14237 .removeClass( "ui-state-disabled" )
14238 .removeAttr( "aria-disabled" );
14242 this.options.disabled = disabled;
14245 _setupEvents: function( event ) {
14247 click: function( event ) {
14248 event.preventDefault();
14252 $.each( event.split(" "), function( index, eventName ) {
14253 events[ eventName ] = "_eventHandler";
14257 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
14258 this._on( this.anchors, events );
14259 this._on( this.tabs, { keydown: "_tabKeydown" } );
14260 this._on( this.panels, { keydown: "_panelKeydown" } );
14262 this._focusable( this.tabs );
14263 this._hoverable( this.tabs );
14266 _setupHeightStyle: function( heightStyle ) {
14268 parent = this.element.parent();
14270 if ( heightStyle === "fill" ) {
14271 maxHeight = parent.height();
14272 maxHeight -= this.element.outerHeight() - this.element.height();
14274 this.element.siblings( ":visible" ).each(function() {
14275 var elem = $( this ),
14276 position = elem.css( "position" );
14278 if ( position === "absolute" || position === "fixed" ) {
14281 maxHeight -= elem.outerHeight( true );
14284 this.element.children().not( this.panels ).each(function() {
14285 maxHeight -= $( this ).outerHeight( true );
14288 this.panels.each(function() {
14289 $( this ).height( Math.max( 0, maxHeight -
14290 $( this ).innerHeight() + $( this ).height() ) );
14292 .css( "overflow", "auto" );
14293 } else if ( heightStyle === "auto" ) {
14295 this.panels.each(function() {
14296 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
14297 }).height( maxHeight );
14301 _eventHandler: function( event ) {
14302 var options = this.options,
14303 active = this.active,
14304 anchor = $( event.currentTarget ),
14305 tab = anchor.closest( "li" ),
14306 clickedIsActive = tab[ 0 ] === active[ 0 ],
14307 collapsing = clickedIsActive && options.collapsible,
14308 toShow = collapsing ? $() : this._getPanelForTab( tab ),
14309 toHide = !active.length ? $() : this._getPanelForTab( active ),
14313 newTab: collapsing ? $() : tab,
14317 event.preventDefault();
14319 if ( tab.hasClass( "ui-state-disabled" ) ||
14320 // tab is already loading
14321 tab.hasClass( "ui-tabs-loading" ) ||
14322 // can't switch durning an animation
14324 // click on active header, but not collapsible
14325 ( clickedIsActive && !options.collapsible ) ||
14326 // allow canceling activation
14327 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
14331 options.active = collapsing ? false : this.tabs.index( tab );
14333 this.active = clickedIsActive ? $() : tab;
14338 if ( !toHide.length && !toShow.length ) {
14339 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
14342 if ( toShow.length ) {
14343 this.load( this.tabs.index( tab ), event );
14345 this._toggle( event, eventData );
14348 // handles show/hide for selecting tabs
14349 _toggle: function( event, eventData ) {
14351 toShow = eventData.newPanel,
14352 toHide = eventData.oldPanel;
14354 this.running = true;
14356 function complete() {
14357 that.running = false;
14358 that._trigger( "activate", event, eventData );
14362 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
14364 if ( toShow.length && that.options.show ) {
14365 that._show( toShow, that.options.show, complete );
14372 // start out by hiding, then showing, then completing
14373 if ( toHide.length && this.options.hide ) {
14374 this._hide( toHide, this.options.hide, function() {
14375 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14379 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14385 "aria-expanded": "false",
14386 "aria-hidden": "true"
14388 eventData.oldTab.attr( "aria-selected", "false" );
14389 // If we're switching tabs, remove the old tab from the tab order.
14390 // If we're opening from collapsed state, remove the previous tab from the tab order.
14391 // If we're collapsing, then keep the collapsing tab in the tab order.
14392 if ( toShow.length && toHide.length ) {
14393 eventData.oldTab.attr( "tabIndex", -1 );
14394 } else if ( toShow.length ) {
14395 this.tabs.filter(function() {
14396 return $( this ).attr( "tabIndex" ) === 0;
14398 .attr( "tabIndex", -1 );
14402 "aria-expanded": "true",
14403 "aria-hidden": "false"
14405 eventData.newTab.attr({
14406 "aria-selected": "true",
14411 _activate: function( index ) {
14413 active = this._findActive( index );
14415 // trying to activate the already active panel
14416 if ( active[ 0 ] === this.active[ 0 ] ) {
14420 // trying to collapse, simulate a click on the current active header
14421 if ( !active.length ) {
14422 active = this.active;
14425 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
14426 this._eventHandler({
14428 currentTarget: anchor,
14429 preventDefault: $.noop
14433 _findActive: function( index ) {
14434 return index === false ? $() : this.tabs.eq( index );
14437 _getIndex: function( index ) {
14438 // meta-function to give users option to provide a href string instead of a numerical index.
14439 if ( typeof index === "string" ) {
14440 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
14446 _destroy: function() {
14451 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
14454 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14455 .removeAttr( "role" );
14458 .removeClass( "ui-tabs-anchor" )
14459 .removeAttr( "role" )
14460 .removeAttr( "tabIndex" )
14463 this.tabs.add( this.panels ).each(function() {
14464 if ( $.data( this, "ui-tabs-destroy" ) ) {
14465 $( this ).remove();
14468 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
14469 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
14470 .removeAttr( "tabIndex" )
14471 .removeAttr( "aria-live" )
14472 .removeAttr( "aria-busy" )
14473 .removeAttr( "aria-selected" )
14474 .removeAttr( "aria-labelledby" )
14475 .removeAttr( "aria-hidden" )
14476 .removeAttr( "aria-expanded" )
14477 .removeAttr( "role" );
14481 this.tabs.each(function() {
14482 var li = $( this ),
14483 prev = li.data( "ui-tabs-aria-controls" );
14486 .attr( "aria-controls", prev )
14487 .removeData( "ui-tabs-aria-controls" );
14489 li.removeAttr( "aria-controls" );
14493 this.panels.show();
14495 if ( this.options.heightStyle !== "content" ) {
14496 this.panels.css( "height", "" );
14500 enable: function( index ) {
14501 var disabled = this.options.disabled;
14502 if ( disabled === false ) {
14506 if ( index === undefined ) {
14509 index = this._getIndex( index );
14510 if ( $.isArray( disabled ) ) {
14511 disabled = $.map( disabled, function( num ) {
14512 return num !== index ? num : null;
14515 disabled = $.map( this.tabs, function( li, num ) {
14516 return num !== index ? num : null;
14520 this._setupDisabled( disabled );
14523 disable: function( index ) {
14524 var disabled = this.options.disabled;
14525 if ( disabled === true ) {
14529 if ( index === undefined ) {
14532 index = this._getIndex( index );
14533 if ( $.inArray( index, disabled ) !== -1 ) {
14536 if ( $.isArray( disabled ) ) {
14537 disabled = $.merge( [ index ], disabled ).sort();
14539 disabled = [ index ];
14542 this._setupDisabled( disabled );
14545 load: function( index, event ) {
14546 index = this._getIndex( index );
14548 tab = this.tabs.eq( index ),
14549 anchor = tab.find( ".ui-tabs-anchor" ),
14550 panel = this._getPanelForTab( tab ),
14557 if ( isLocal( anchor[ 0 ] ) ) {
14561 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
14563 // support: jQuery <1.8
14564 // jQuery <1.8 returns false if the request is canceled in beforeSend,
14565 // but as of 1.8, $.ajax() always returns a jqXHR object.
14566 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
14567 tab.addClass( "ui-tabs-loading" );
14568 panel.attr( "aria-busy", "true" );
14571 .success(function( response ) {
14572 // support: jQuery <1.8
14573 // http://bugs.jquery.com/ticket/11778
14574 setTimeout(function() {
14575 panel.html( response );
14576 that._trigger( "load", event, eventData );
14579 .complete(function( jqXHR, status ) {
14580 // support: jQuery <1.8
14581 // http://bugs.jquery.com/ticket/11778
14582 setTimeout(function() {
14583 if ( status === "abort" ) {
14584 that.panels.stop( false, true );
14587 tab.removeClass( "ui-tabs-loading" );
14588 panel.removeAttr( "aria-busy" );
14590 if ( jqXHR === that.xhr ) {
14598 _ajaxSettings: function( anchor, event, eventData ) {
14601 url: anchor.attr( "href" ),
14602 beforeSend: function( jqXHR, settings ) {
14603 return that._trigger( "beforeLoad", event,
14604 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
14609 _getPanelForTab: function( tab ) {
14610 var id = $( tab ).attr( "aria-controls" );
14611 return this.element.find( this._sanitizeSelector( "#" + id ) );
14619 var increments = 0;
14621 function addDescribedBy( elem, id ) {
14622 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
14623 describedby.push( id );
14625 .data( "ui-tooltip-id", id )
14626 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
14629 function removeDescribedBy( elem ) {
14630 var id = elem.data( "ui-tooltip-id" ),
14631 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
14632 index = $.inArray( id, describedby );
14633 if ( index !== -1 ) {
14634 describedby.splice( index, 1 );
14637 elem.removeData( "ui-tooltip-id" );
14638 describedby = $.trim( describedby.join( " " ) );
14639 if ( describedby ) {
14640 elem.attr( "aria-describedby", describedby );
14642 elem.removeAttr( "aria-describedby" );
14646 $.widget( "ui.tooltip", {
14649 content: function() {
14650 // support: IE<9, Opera in jQuery <1.7
14651 // .text() can't accept undefined, so coerce to a string
14652 var title = $( this ).attr( "title" ) || "";
14653 // Escape title, since we're going from an attribute to raw HTML
14654 return $( "<a>" ).text( title ).html();
14657 // Disabled elements have inconsistent behavior across browsers (#8661)
14658 items: "[title]:not([disabled])",
14662 collision: "flipfit flip"
14665 tooltipClass: null,
14673 _create: function() {
14679 // IDs of generated tooltips, needed for destroy
14680 this.tooltips = {};
14681 // IDs of parent tooltips where we removed the title attribute
14684 if ( this.options.disabled ) {
14689 _setOption: function( key, value ) {
14692 if ( key === "disabled" ) {
14693 this[ value ? "_disable" : "_enable" ]();
14694 this.options[ key ] = value;
14695 // disable element style changes
14699 this._super( key, value );
14701 if ( key === "content" ) {
14702 $.each( this.tooltips, function( id, element ) {
14703 that._updateContent( element );
14708 _disable: function() {
14711 // close open tooltips
14712 $.each( this.tooltips, function( id, element ) {
14713 var event = $.Event( "blur" );
14714 event.target = event.currentTarget = element[0];
14715 that.close( event, true );
14718 // remove title attributes to prevent native tooltips
14719 this.element.find( this.options.items ).addBack().each(function() {
14720 var element = $( this );
14721 if ( element.is( "[title]" ) ) {
14723 .data( "ui-tooltip-title", element.attr( "title" ) )
14724 .attr( "title", "" );
14729 _enable: function() {
14730 // restore title attributes
14731 this.element.find( this.options.items ).addBack().each(function() {
14732 var element = $( this );
14733 if ( element.data( "ui-tooltip-title" ) ) {
14734 element.attr( "title", element.data( "ui-tooltip-title" ) );
14739 open: function( event ) {
14741 target = $( event ? event.target : this.element )
14742 // we need closest here due to mouseover bubbling,
14743 // but always pointing at the same event target
14744 .closest( this.options.items );
14746 // No element to show a tooltip for or the tooltip is already open
14747 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
14751 if ( target.attr( "title" ) ) {
14752 target.data( "ui-tooltip-title", target.attr( "title" ) );
14755 target.data( "ui-tooltip-open", true );
14757 // kill parent tooltips, custom or native, for hover
14758 if ( event && event.type === "mouseover" ) {
14759 target.parents().each(function() {
14760 var parent = $( this ),
14762 if ( parent.data( "ui-tooltip-open" ) ) {
14763 blurEvent = $.Event( "blur" );
14764 blurEvent.target = blurEvent.currentTarget = this;
14765 that.close( blurEvent, true );
14767 if ( parent.attr( "title" ) ) {
14769 that.parents[ this.id ] = {
14771 title: parent.attr( "title" )
14773 parent.attr( "title", "" );
14778 this._updateContent( target, event );
14781 _updateContent: function( target, event ) {
14783 contentOption = this.options.content,
14785 eventType = event ? event.type : null;
14787 if ( typeof contentOption === "string" ) {
14788 return this._open( event, target, contentOption );
14791 content = contentOption.call( target[0], function( response ) {
14792 // ignore async response if tooltip was closed already
14793 if ( !target.data( "ui-tooltip-open" ) ) {
14796 // IE may instantly serve a cached response for ajax requests
14797 // delay this call to _open so the other call to _open runs first
14798 that._delay(function() {
14799 // jQuery creates a special event for focusin when it doesn't
14800 // exist natively. To improve performance, the native event
14801 // object is reused and the type is changed. Therefore, we can't
14802 // rely on the type being correct after the event finished
14803 // bubbling, so we set it back to the previous value. (#8740)
14805 event.type = eventType;
14807 this._open( event, target, response );
14811 this._open( event, target, content );
14815 _open: function( event, target, content ) {
14816 var tooltip, events, delayedShow,
14817 positionOption = $.extend( {}, this.options.position );
14823 // Content can be updated multiple times. If the tooltip already
14824 // exists, then just update the content and bail.
14825 tooltip = this._find( target );
14826 if ( tooltip.length ) {
14827 tooltip.find( ".ui-tooltip-content" ).html( content );
14831 // if we have a title, clear it to prevent the native tooltip
14832 // we have to check first to avoid defining a title if none exists
14833 // (we don't want to cause an element to start matching [title])
14835 // We use removeAttr only for key events, to allow IE to export the correct
14836 // accessible attributes. For mouse events, set to empty string to avoid
14837 // native tooltip showing up (happens only when removing inside mouseover).
14838 if ( target.is( "[title]" ) ) {
14839 if ( event && event.type === "mouseover" ) {
14840 target.attr( "title", "" );
14842 target.removeAttr( "title" );
14846 tooltip = this._tooltip( target );
14847 addDescribedBy( target, tooltip.attr( "id" ) );
14848 tooltip.find( ".ui-tooltip-content" ).html( content );
14850 function position( event ) {
14851 positionOption.of = event;
14852 if ( tooltip.is( ":hidden" ) ) {
14855 tooltip.position( positionOption );
14857 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
14858 this._on( this.document, {
14859 mousemove: position
14861 // trigger once to override element-relative positioning
14864 tooltip.position( $.extend({
14866 }, this.options.position ) );
14871 this._show( tooltip, this.options.show );
14872 // Handle tracking tooltips that are shown with a delay (#8644). As soon
14873 // as the tooltip is visible, position the tooltip using the most recent
14875 if ( this.options.show && this.options.show.delay ) {
14876 delayedShow = this.delayedShow = setInterval(function() {
14877 if ( tooltip.is( ":visible" ) ) {
14878 position( positionOption.of );
14879 clearInterval( delayedShow );
14881 }, $.fx.interval );
14884 this._trigger( "open", event, { tooltip: tooltip } );
14887 keyup: function( event ) {
14888 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
14889 var fakeEvent = $.Event(event);
14890 fakeEvent.currentTarget = target[0];
14891 this.close( fakeEvent, true );
14894 remove: function() {
14895 this._removeTooltip( tooltip );
14898 if ( !event || event.type === "mouseover" ) {
14899 events.mouseleave = "close";
14901 if ( !event || event.type === "focusin" ) {
14902 events.focusout = "close";
14904 this._on( true, target, events );
14907 close: function( event ) {
14909 target = $( event ? event.currentTarget : this.element ),
14910 tooltip = this._find( target );
14912 // disabling closes the tooltip, so we need to track when we're closing
14913 // to avoid an infinite loop in case the tooltip becomes disabled on close
14914 if ( this.closing ) {
14918 // Clear the interval for delayed tracking tooltips
14919 clearInterval( this.delayedShow );
14921 // only set title if we had one before (see comment in _open())
14922 if ( target.data( "ui-tooltip-title" ) ) {
14923 target.attr( "title", target.data( "ui-tooltip-title" ) );
14926 removeDescribedBy( target );
14928 tooltip.stop( true );
14929 this._hide( tooltip, this.options.hide, function() {
14930 that._removeTooltip( $( this ) );
14933 target.removeData( "ui-tooltip-open" );
14934 this._off( target, "mouseleave focusout keyup" );
14935 // Remove 'remove' binding only on delegated targets
14936 if ( target[0] !== this.element[0] ) {
14937 this._off( target, "remove" );
14939 this._off( this.document, "mousemove" );
14941 if ( event && event.type === "mouseleave" ) {
14942 $.each( this.parents, function( id, parent ) {
14943 $( parent.element ).attr( "title", parent.title );
14944 delete that.parents[ id ];
14948 this.closing = true;
14949 this._trigger( "close", event, { tooltip: tooltip } );
14950 this.closing = false;
14953 _tooltip: function( element ) {
14954 var id = "ui-tooltip-" + increments++,
14955 tooltip = $( "<div>" )
14960 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
14961 ( this.options.tooltipClass || "" ) );
14963 .addClass( "ui-tooltip-content" )
14964 .appendTo( tooltip );
14965 tooltip.appendTo( this.document[0].body );
14966 this.tooltips[ id ] = element;
14970 _find: function( target ) {
14971 var id = target.data( "ui-tooltip-id" );
14972 return id ? $( "#" + id ) : $();
14975 _removeTooltip: function( tooltip ) {
14977 delete this.tooltips[ tooltip.attr( "id" ) ];
14980 _destroy: function() {
14983 // close open tooltips
14984 $.each( this.tooltips, function( id, element ) {
14985 // Delegate to close method to handle common cleanup
14986 var event = $.Event( "blur" );
14987 event.target = event.currentTarget = element[0];
14988 that.close( event, true );
14990 // Remove immediately; destroying an open tooltip doesn't use the
14992 $( "#" + id ).remove();
14994 // Restore the title
14995 if ( element.data( "ui-tooltip-title" ) ) {
14996 element.attr( "title", element.data( "ui-tooltip-title" ) );
14997 element.removeData( "ui-tooltip-title" );