1 /*! jQuery UI - v1.10.2 - 2013-03-14
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 the proxy pattern 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();
1100 //The element's absolute position on the page minus margins
1101 this.offset = this.positionAbs = this.element.offset();
1103 top: this.offset.top - this.margins.top,
1104 left: this.offset.left - this.margins.left
1107 $.extend(this.offset, {
1108 click: { //Where the click happened, relative to the element
1109 left: event.pageX - this.offset.left,
1110 top: event.pageY - this.offset.top
1112 parent: this._getParentOffset(),
1113 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1116 //Generate the original position
1117 this.originalPosition = this.position = this._generatePosition(event);
1118 this.originalPageX = event.pageX;
1119 this.originalPageY = event.pageY;
1121 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1122 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1124 //Set a containment if given in the options
1126 this._setContainment();
1129 //Trigger event + callbacks
1130 if(this._trigger("start", event) === false) {
1135 //Recache the helper size
1136 this._cacheHelperProportions();
1138 //Prepare the droppable offsets
1139 if ($.ui.ddmanager && !o.dropBehaviour) {
1140 $.ui.ddmanager.prepareOffsets(this, event);
1144 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1146 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1147 if ( $.ui.ddmanager ) {
1148 $.ui.ddmanager.dragStart(this, event);
1154 _mouseDrag: function(event, noPropagation) {
1156 //Compute the helpers position
1157 this.position = this._generatePosition(event);
1158 this.positionAbs = this._convertPositionTo("absolute");
1160 //Call plugins and callbacks and use the resulting position if something is returned
1161 if (!noPropagation) {
1162 var ui = this._uiHash();
1163 if(this._trigger("drag", event, ui) === false) {
1167 this.position = ui.position;
1170 if(!this.options.axis || this.options.axis !== "y") {
1171 this.helper[0].style.left = this.position.left+"px";
1173 if(!this.options.axis || this.options.axis !== "x") {
1174 this.helper[0].style.top = this.position.top+"px";
1176 if($.ui.ddmanager) {
1177 $.ui.ddmanager.drag(this, event);
1183 _mouseStop: function(event) {
1185 //If we are using droppables, inform the manager about the drop
1188 elementInDom = false,
1190 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1191 dropped = $.ui.ddmanager.drop(this, event);
1194 //if a drop comes from outside (a sortable)
1196 dropped = this.dropped;
1197 this.dropped = false;
1200 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1201 element = this.element[0];
1202 while ( element && (element = element.parentNode) ) {
1203 if (element === document ) {
1204 elementInDom = true;
1207 if ( !elementInDom && this.options.helper === "original" ) {
1211 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))) {
1212 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1213 if(that._trigger("stop", event) !== false) {
1218 if(this._trigger("stop", event) !== false) {
1226 _mouseUp: function(event) {
1227 //Remove frame helpers
1228 $("div.ui-draggable-iframeFix").each(function() {
1229 this.parentNode.removeChild(this);
1232 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1233 if( $.ui.ddmanager ) {
1234 $.ui.ddmanager.dragStop(this, event);
1237 return $.ui.mouse.prototype._mouseUp.call(this, event);
1240 cancel: function() {
1242 if(this.helper.is(".ui-draggable-dragging")) {
1252 _getHandle: function(event) {
1253 return this.options.handle ?
1254 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1258 _createHelper: function(event) {
1260 var o = this.options,
1261 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1263 if(!helper.parents("body").length) {
1264 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1267 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1268 helper.css("position", "absolute");
1275 _adjustOffsetFromHelper: function(obj) {
1276 if (typeof obj === "string") {
1277 obj = obj.split(" ");
1279 if ($.isArray(obj)) {
1280 obj = {left: +obj[0], top: +obj[1] || 0};
1282 if ("left" in obj) {
1283 this.offset.click.left = obj.left + this.margins.left;
1285 if ("right" in obj) {
1286 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1289 this.offset.click.top = obj.top + this.margins.top;
1291 if ("bottom" in obj) {
1292 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1296 _getParentOffset: function() {
1298 //Get the offsetParent and cache its position
1299 this.offsetParent = this.helper.offsetParent();
1300 var po = this.offsetParent.offset();
1302 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1303 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1304 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1305 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1306 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1307 po.left += this.scrollParent.scrollLeft();
1308 po.top += this.scrollParent.scrollTop();
1311 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1313 if((this.offsetParent[0] === document.body) ||
1314 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1315 po = { top: 0, left: 0 };
1319 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1320 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1325 _getRelativeOffset: function() {
1327 if(this.cssPosition === "relative") {
1328 var p = this.element.position();
1330 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1331 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1334 return { top: 0, left: 0 };
1339 _cacheMargins: function() {
1341 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1342 top: (parseInt(this.element.css("marginTop"),10) || 0),
1343 right: (parseInt(this.element.css("marginRight"),10) || 0),
1344 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1348 _cacheHelperProportions: function() {
1349 this.helperProportions = {
1350 width: this.helper.outerWidth(),
1351 height: this.helper.outerHeight()
1355 _setContainment: function() {
1360 if(o.containment === "parent") {
1361 o.containment = this.helper[0].parentNode;
1363 if(o.containment === "document" || o.containment === "window") {
1364 this.containment = [
1365 o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1366 o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1367 (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
1368 (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1372 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) {
1373 c = $(o.containment);
1380 over = ($(ce).css("overflow") !== "hidden");
1382 this.containment = [
1383 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1384 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1385 (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderRightWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
1386 (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderBottomWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
1388 this.relative_container = c;
1390 } else if(o.containment.constructor === Array) {
1391 this.containment = o.containment;
1396 _convertPositionTo: function(d, pos) {
1399 pos = this.position;
1402 var mod = d === "absolute" ? 1 : -1,
1403 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);
1407 pos.top + // The absolute mouse position
1408 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1409 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1410 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1413 pos.left + // The absolute mouse position
1414 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1415 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1416 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1422 _generatePosition: function(event) {
1424 var containment, co, top, left,
1426 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
1427 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName),
1428 pageX = event.pageX,
1429 pageY = event.pageY;
1432 * - Position constraining -
1433 * Constrain the position to a mix of grid, containment.
1436 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1437 if(this.containment) {
1438 if (this.relative_container){
1439 co = this.relative_container.offset();
1440 containment = [ this.containment[0] + co.left,
1441 this.containment[1] + co.top,
1442 this.containment[2] + co.left,
1443 this.containment[3] + co.top ];
1446 containment = this.containment;
1449 if(event.pageX - this.offset.click.left < containment[0]) {
1450 pageX = containment[0] + this.offset.click.left;
1452 if(event.pageY - this.offset.click.top < containment[1]) {
1453 pageY = containment[1] + this.offset.click.top;
1455 if(event.pageX - this.offset.click.left > containment[2]) {
1456 pageX = containment[2] + this.offset.click.left;
1458 if(event.pageY - this.offset.click.top > containment[3]) {
1459 pageY = containment[3] + this.offset.click.top;
1464 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1465 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1466 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;
1468 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1469 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;
1476 pageY - // The absolute mouse position
1477 this.offset.click.top - // Click offset (relative to the element)
1478 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1479 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1480 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1483 pageX - // The absolute mouse position
1484 this.offset.click.left - // Click offset (relative to the element)
1485 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1486 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1487 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1493 _clear: function() {
1494 this.helper.removeClass("ui-draggable-dragging");
1495 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1496 this.helper.remove();
1499 this.cancelHelperRemoval = false;
1502 // From now on bulk stuff - mainly helpers
1504 _trigger: function(type, event, ui) {
1505 ui = ui || this._uiHash();
1506 $.ui.plugin.call(this, type, [event, ui]);
1507 //The absolute position has to be recalculated after plugins
1508 if(type === "drag") {
1509 this.positionAbs = this._convertPositionTo("absolute");
1511 return $.Widget.prototype._trigger.call(this, type, event, ui);
1516 _uiHash: function() {
1518 helper: this.helper,
1519 position: this.position,
1520 originalPosition: this.originalPosition,
1521 offset: this.positionAbs
1527 $.ui.plugin.add("draggable", "connectToSortable", {
1528 start: function(event, ui) {
1530 var inst = $(this).data("ui-draggable"), o = inst.options,
1531 uiSortable = $.extend({}, ui, { item: inst.element });
1532 inst.sortables = [];
1533 $(o.connectToSortable).each(function() {
1534 var sortable = $.data(this, "ui-sortable");
1535 if (sortable && !sortable.options.disabled) {
1536 inst.sortables.push({
1538 shouldRevert: sortable.options.revert
1540 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).
1541 sortable._trigger("activate", event, uiSortable);
1546 stop: function(event, ui) {
1548 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1549 var inst = $(this).data("ui-draggable"),
1550 uiSortable = $.extend({}, ui, { item: inst.element });
1552 $.each(inst.sortables, function() {
1553 if(this.instance.isOver) {
1555 this.instance.isOver = 0;
1557 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1558 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1560 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
1561 if(this.shouldRevert) {
1562 this.instance.options.revert = this.shouldRevert;
1565 //Trigger the stop of the sortable
1566 this.instance._mouseStop(event);
1568 this.instance.options.helper = this.instance.options._helper;
1570 //If the helper has been the original item, restore properties in the sortable
1571 if(inst.options.helper === "original") {
1572 this.instance.currentItem.css({ top: "auto", left: "auto" });
1576 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1577 this.instance._trigger("deactivate", event, uiSortable);
1583 drag: function(event, ui) {
1585 var inst = $(this).data("ui-draggable"), that = this;
1587 $.each(inst.sortables, function() {
1589 var innermostIntersecting = false,
1590 thisSortable = this;
1592 //Copy over some variables to allow calling the sortable's native _intersectsWith
1593 this.instance.positionAbs = inst.positionAbs;
1594 this.instance.helperProportions = inst.helperProportions;
1595 this.instance.offset.click = inst.offset.click;
1597 if(this.instance._intersectsWith(this.instance.containerCache)) {
1598 innermostIntersecting = true;
1599 $.each(inst.sortables, function () {
1600 this.instance.positionAbs = inst.positionAbs;
1601 this.instance.helperProportions = inst.helperProportions;
1602 this.instance.offset.click = inst.offset.click;
1603 if (this !== thisSortable &&
1604 this.instance._intersectsWith(this.instance.containerCache) &&
1605 $.contains(thisSortable.instance.element[0], this.instance.element[0])
1607 innermostIntersecting = false;
1609 return innermostIntersecting;
1614 if(innermostIntersecting) {
1615 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1616 if(!this.instance.isOver) {
1618 this.instance.isOver = 1;
1619 //Now we fake the start of dragging for the sortable instance,
1620 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1621 //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)
1622 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
1623 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1624 this.instance.options.helper = function() { return ui.helper[0]; };
1626 event.target = this.instance.currentItem[0];
1627 this.instance._mouseCapture(event, true);
1628 this.instance._mouseStart(event, true, true);
1630 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1631 this.instance.offset.click.top = inst.offset.click.top;
1632 this.instance.offset.click.left = inst.offset.click.left;
1633 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1634 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1636 inst._trigger("toSortable", event);
1637 inst.dropped = this.instance.element; //draggable revert needs that
1638 //hack so receive/update callbacks work (mostly)
1639 inst.currentItem = inst.element;
1640 this.instance.fromOutside = inst;
1644 //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
1645 if(this.instance.currentItem) {
1646 this.instance._mouseDrag(event);
1651 //If it doesn't intersect with the sortable, and it intersected before,
1652 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1653 if(this.instance.isOver) {
1655 this.instance.isOver = 0;
1656 this.instance.cancelHelperRemoval = true;
1658 //Prevent reverting on this forced stop
1659 this.instance.options.revert = false;
1661 // The out event needs to be triggered independently
1662 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
1664 this.instance._mouseStop(event, true);
1665 this.instance.options.helper = this.instance.options._helper;
1667 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1668 this.instance.currentItem.remove();
1669 if(this.instance.placeholder) {
1670 this.instance.placeholder.remove();
1673 inst._trigger("fromSortable", event);
1674 inst.dropped = false; //draggable revert needs that
1684 $.ui.plugin.add("draggable", "cursor", {
1686 var t = $("body"), o = $(this).data("ui-draggable").options;
1687 if (t.css("cursor")) {
1688 o._cursor = t.css("cursor");
1690 t.css("cursor", o.cursor);
1693 var o = $(this).data("ui-draggable").options;
1695 $("body").css("cursor", o._cursor);
1700 $.ui.plugin.add("draggable", "opacity", {
1701 start: function(event, ui) {
1702 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1703 if(t.css("opacity")) {
1704 o._opacity = t.css("opacity");
1706 t.css("opacity", o.opacity);
1708 stop: function(event, ui) {
1709 var o = $(this).data("ui-draggable").options;
1711 $(ui.helper).css("opacity", o._opacity);
1716 $.ui.plugin.add("draggable", "scroll", {
1718 var i = $(this).data("ui-draggable");
1719 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1720 i.overflowOffset = i.scrollParent.offset();
1723 drag: function( event ) {
1725 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
1727 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1729 if(!o.axis || o.axis !== "x") {
1730 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
1731 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1732 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
1733 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1737 if(!o.axis || o.axis !== "y") {
1738 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
1739 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1740 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
1741 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1747 if(!o.axis || o.axis !== "x") {
1748 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
1749 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1750 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
1751 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1755 if(!o.axis || o.axis !== "y") {
1756 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
1757 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1758 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
1759 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1765 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
1766 $.ui.ddmanager.prepareOffsets(i, event);
1772 $.ui.plugin.add("draggable", "snap", {
1775 var i = $(this).data("ui-draggable"),
1778 i.snapElements = [];
1780 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
1783 if(this !== i.element[0]) {
1784 i.snapElements.push({
1786 width: $t.outerWidth(), height: $t.outerHeight(),
1787 top: $o.top, left: $o.left
1793 drag: function(event, ui) {
1795 var ts, bs, ls, rs, l, r, t, b, i, first,
1796 inst = $(this).data("ui-draggable"),
1798 d = o.snapTolerance,
1799 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1800 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1802 for (i = inst.snapElements.length - 1; i >= 0; i--){
1804 l = inst.snapElements[i].left;
1805 r = l + inst.snapElements[i].width;
1806 t = inst.snapElements[i].top;
1807 b = t + inst.snapElements[i].height;
1809 //Yes, I know, this is insane ;)
1810 if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
1811 if(inst.snapElements[i].snapping) {
1812 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1814 inst.snapElements[i].snapping = false;
1818 if(o.snapMode !== "inner") {
1819 ts = Math.abs(t - y2) <= d;
1820 bs = Math.abs(b - y1) <= d;
1821 ls = Math.abs(l - x2) <= d;
1822 rs = Math.abs(r - x1) <= d;
1824 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1827 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1830 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1833 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1837 first = (ts || bs || ls || rs);
1839 if(o.snapMode !== "outer") {
1840 ts = Math.abs(t - y1) <= d;
1841 bs = Math.abs(b - y2) <= d;
1842 ls = Math.abs(l - x1) <= d;
1843 rs = Math.abs(r - x2) <= d;
1845 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1848 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1851 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1854 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1858 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
1859 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1861 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1868 $.ui.plugin.add("draggable", "stack", {
1871 o = this.data("ui-draggable").options,
1872 group = $.makeArray($(o.stack)).sort(function(a,b) {
1873 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1876 if (!group.length) { return; }
1878 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
1879 $(group).each(function(i) {
1880 $(this).css("zIndex", min + i);
1882 this.css("zIndex", (min + group.length));
1886 $.ui.plugin.add("draggable", "zIndex", {
1887 start: function(event, ui) {
1888 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1889 if(t.css("zIndex")) {
1890 o._zIndex = t.css("zIndex");
1892 t.css("zIndex", o.zIndex);
1894 stop: function(event, ui) {
1895 var o = $(this).data("ui-draggable").options;
1897 $(ui.helper).css("zIndex", o._zIndex);
1904 (function( $, undefined ) {
1906 function isOverAxis( x, reference, size ) {
1907 return ( x > reference ) && ( x < ( reference + size ) );
1910 $.widget("ui.droppable", {
1912 widgetEventPrefix: "drop",
1920 tolerance: "intersect",
1929 _create: function() {
1931 var o = this.options,
1934 this.isover = false;
1937 this.accept = $.isFunction(accept) ? accept : function(d) {
1938 return d.is(accept);
1941 //Store the droppable's proportions
1942 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1944 // Add the reference and positions to the manager
1945 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1946 $.ui.ddmanager.droppables[o.scope].push(this);
1948 (o.addClasses && this.element.addClass("ui-droppable"));
1952 _destroy: function() {
1954 drop = $.ui.ddmanager.droppables[this.options.scope];
1956 for ( ; i < drop.length; i++ ) {
1957 if ( drop[i] === this ) {
1962 this.element.removeClass("ui-droppable ui-droppable-disabled");
1965 _setOption: function(key, value) {
1967 if(key === "accept") {
1968 this.accept = $.isFunction(value) ? value : function(d) {
1972 $.Widget.prototype._setOption.apply(this, arguments);
1975 _activate: function(event) {
1976 var draggable = $.ui.ddmanager.current;
1977 if(this.options.activeClass) {
1978 this.element.addClass(this.options.activeClass);
1981 this._trigger("activate", event, this.ui(draggable));
1985 _deactivate: function(event) {
1986 var draggable = $.ui.ddmanager.current;
1987 if(this.options.activeClass) {
1988 this.element.removeClass(this.options.activeClass);
1991 this._trigger("deactivate", event, this.ui(draggable));
1995 _over: function(event) {
1997 var draggable = $.ui.ddmanager.current;
1999 // Bail if draggable and droppable are same element
2000 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2004 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2005 if(this.options.hoverClass) {
2006 this.element.addClass(this.options.hoverClass);
2008 this._trigger("over", event, this.ui(draggable));
2013 _out: function(event) {
2015 var draggable = $.ui.ddmanager.current;
2017 // Bail if draggable and droppable are same element
2018 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2022 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2023 if(this.options.hoverClass) {
2024 this.element.removeClass(this.options.hoverClass);
2026 this._trigger("out", event, this.ui(draggable));
2031 _drop: function(event,custom) {
2033 var draggable = custom || $.ui.ddmanager.current,
2034 childrenIntersection = false;
2036 // Bail if draggable and droppable are same element
2037 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2041 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2042 var inst = $.data(this, "ui-droppable");
2044 inst.options.greedy &&
2045 !inst.options.disabled &&
2046 inst.options.scope === draggable.options.scope &&
2047 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2048 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2049 ) { childrenIntersection = true; return false; }
2051 if(childrenIntersection) {
2055 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2056 if(this.options.activeClass) {
2057 this.element.removeClass(this.options.activeClass);
2059 if(this.options.hoverClass) {
2060 this.element.removeClass(this.options.hoverClass);
2062 this._trigger("drop", event, this.ui(draggable));
2063 return this.element;
2072 draggable: (c.currentItem || c.element),
2074 position: c.position,
2075 offset: c.positionAbs
2081 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2083 if (!droppable.offset) {
2087 var draggableLeft, draggableTop,
2088 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2089 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2090 l = droppable.offset.left, r = l + droppable.proportions.width,
2091 t = droppable.offset.top, b = t + droppable.proportions.height;
2093 switch (toleranceMode) {
2095 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2097 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2098 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2099 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2100 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2102 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2103 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2104 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2107 (y1 >= t && y1 <= b) || // Top edge touching
2108 (y2 >= t && y2 <= b) || // Bottom edge touching
2109 (y1 < t && y2 > b) // Surrounded vertically
2111 (x1 >= l && x1 <= r) || // Left edge touching
2112 (x2 >= l && x2 <= r) || // Right edge touching
2113 (x1 < l && x2 > r) // Surrounded horizontally
2122 This manager tracks offsets of draggables and droppables
2126 droppables: { "default": [] },
2127 prepareOffsets: function(t, event) {
2130 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2131 type = event ? event.type : null, // workaround for #2317
2132 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2134 droppablesLoop: for (i = 0; i < m.length; i++) {
2136 //No disabled and non-accepted
2137 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2141 // Filter out elements in the current dragged item
2142 for (j=0; j < list.length; j++) {
2143 if(list[j] === m[i].element[0]) {
2144 m[i].proportions.height = 0;
2145 continue droppablesLoop;
2149 m[i].visible = m[i].element.css("display") !== "none";
2154 //Activate the droppable if used directly from draggables
2155 if(type === "mousedown") {
2156 m[i]._activate.call(m[i], event);
2159 m[i].offset = m[i].element.offset();
2160 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2165 drop: function(draggable, event) {
2167 var dropped = false;
2168 // Create a copy of the droppables in case the list changes during the drop (#9116)
2169 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2174 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2175 dropped = this._drop.call(this, event) || dropped;
2178 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2180 this.isover = false;
2181 this._deactivate.call(this, event);
2188 dragStart: function( draggable, event ) {
2189 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2190 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2191 if( !draggable.options.refreshPositions ) {
2192 $.ui.ddmanager.prepareOffsets( draggable, event );
2196 drag: function(draggable, event) {
2198 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2199 if(draggable.options.refreshPositions) {
2200 $.ui.ddmanager.prepareOffsets(draggable, event);
2203 //Run through all droppables and check their positions based on specific tolerance options
2204 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2206 if(this.options.disabled || this.greedyChild || !this.visible) {
2210 var parentInstance, scope, parent,
2211 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2212 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2217 if (this.options.greedy) {
2218 // find droppable parents with same scope
2219 scope = this.options.scope;
2220 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2221 return $.data(this, "ui-droppable").options.scope === scope;
2224 if (parent.length) {
2225 parentInstance = $.data(parent[0], "ui-droppable");
2226 parentInstance.greedyChild = (c === "isover");
2230 // we just moved into a greedy child
2231 if (parentInstance && c === "isover") {
2232 parentInstance.isover = false;
2233 parentInstance.isout = true;
2234 parentInstance._out.call(parentInstance, event);
2238 this[c === "isout" ? "isover" : "isout"] = false;
2239 this[c === "isover" ? "_over" : "_out"].call(this, event);
2241 // we just moved out of a greedy child
2242 if (parentInstance && c === "isout") {
2243 parentInstance.isout = false;
2244 parentInstance.isover = true;
2245 parentInstance._over.call(parentInstance, event);
2250 dragStop: function( draggable, event ) {
2251 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2252 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2253 if( !draggable.options.refreshPositions ) {
2254 $.ui.ddmanager.prepareOffsets( draggable, event );
2261 (function( $, undefined ) {
2264 return parseInt(v, 10) || 0;
2267 function isNumber(value) {
2268 return !isNaN(parseInt(value, 10));
2271 $.widget("ui.resizable", $.ui.mouse, {
2273 widgetEventPrefix: "resize",
2277 animateDuration: "slow",
2278 animateEasing: "swing",
2298 _create: function() {
2300 var n, i, handle, axis, hname,
2303 this.element.addClass("ui-resizable");
2306 _aspectRatio: !!(o.aspectRatio),
2307 aspectRatio: o.aspectRatio,
2308 originalElement: this.element,
2309 _proportionallyResizeElements: [],
2310 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2313 //Wrap the element if it cannot hold child nodes
2314 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2316 //Create a wrapper element and set the wrapper to the new current internal element
2318 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2319 position: this.element.css("position"),
2320 width: this.element.outerWidth(),
2321 height: this.element.outerHeight(),
2322 top: this.element.css("top"),
2323 left: this.element.css("left")
2327 //Overwrite the original this.element
2328 this.element = this.element.parent().data(
2329 "ui-resizable", this.element.data("ui-resizable")
2332 this.elementIsWrapper = true;
2334 //Move margins to the wrapper
2335 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2336 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2338 //Prevent Safari textarea resize
2339 this.originalResizeStyle = this.originalElement.css("resize");
2340 this.originalElement.css("resize", "none");
2342 //Push the actual element to our proportionallyResize internal array
2343 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2345 // avoid IE jump (hard set the margin)
2346 this.originalElement.css({ margin: this.originalElement.css("margin") });
2348 // fix handlers offset
2349 this._proportionallyResize();
2353 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" });
2354 if(this.handles.constructor === String) {
2356 if ( this.handles === "all") {
2357 this.handles = "n,e,s,w,se,sw,ne,nw";
2360 n = this.handles.split(",");
2363 for(i = 0; i < n.length; i++) {
2365 handle = $.trim(n[i]);
2366 hname = "ui-resizable-"+handle;
2367 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2369 // Apply zIndex to all handles - see #7960
2370 axis.css({ zIndex: o.zIndex });
2372 //TODO : What's going on here?
2373 if ("se" === handle) {
2374 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2377 //Insert into internal handles object and append to element
2378 this.handles[handle] = ".ui-resizable-"+handle;
2379 this.element.append(axis);
2384 this._renderAxis = function(target) {
2386 var i, axis, padPos, padWrapper;
2388 target = target || this.element;
2390 for(i in this.handles) {
2392 if(this.handles[i].constructor === String) {
2393 this.handles[i] = $(this.handles[i], this.element).show();
2396 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2397 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2399 axis = $(this.handles[i], this.element);
2401 //Checking the correct pad and border
2402 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2404 //The padding type i have to apply...
2405 padPos = [ "padding",
2406 /ne|nw|n/.test(i) ? "Top" :
2407 /se|sw|s/.test(i) ? "Bottom" :
2408 /^e$/.test(i) ? "Right" : "Left" ].join("");
2410 target.css(padPos, padWrapper);
2412 this._proportionallyResize();
2416 //TODO: What's that good for? There's not anything to be executed left
2417 if(!$(this.handles[i]).length) {
2423 //TODO: make renderAxis a prototype function
2424 this._renderAxis(this.element);
2426 this._handles = $(".ui-resizable-handle", this.element)
2427 .disableSelection();
2429 //Matching axis name
2430 this._handles.mouseover(function() {
2431 if (!that.resizing) {
2432 if (this.className) {
2433 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2435 //Axis, default = se
2436 that.axis = axis && axis[1] ? axis[1] : "se";
2440 //If we want to auto hide the elements
2442 this._handles.hide();
2444 .addClass("ui-resizable-autohide")
2445 .mouseenter(function() {
2449 $(this).removeClass("ui-resizable-autohide");
2450 that._handles.show();
2452 .mouseleave(function(){
2456 if (!that.resizing) {
2457 $(this).addClass("ui-resizable-autohide");
2458 that._handles.hide();
2463 //Initialize the mouse interaction
2468 _destroy: function() {
2470 this._mouseDestroy();
2473 _destroy = function(exp) {
2474 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2475 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2478 //TODO: Unwrap at same DOM position
2479 if (this.elementIsWrapper) {
2480 _destroy(this.element);
2481 wrapper = this.element;
2482 this.originalElement.css({
2483 position: wrapper.css("position"),
2484 width: wrapper.outerWidth(),
2485 height: wrapper.outerHeight(),
2486 top: wrapper.css("top"),
2487 left: wrapper.css("left")
2488 }).insertAfter( wrapper );
2492 this.originalElement.css("resize", this.originalResizeStyle);
2493 _destroy(this.originalElement);
2498 _mouseCapture: function(event) {
2502 for (i in this.handles) {
2503 handle = $(this.handles[i])[0];
2504 if (handle === event.target || $.contains(handle, event.target)) {
2509 return !this.options.disabled && capture;
2512 _mouseStart: function(event) {
2514 var curleft, curtop, cursor,
2516 iniPos = this.element.position(),
2519 this.resizing = true;
2521 // bugfix for http://dev.jquery.com/ticket/1749
2522 if ( (/absolute/).test( el.css("position") ) ) {
2523 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
2524 } else if (el.is(".ui-draggable")) {
2525 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
2528 this._renderProxy();
2530 curleft = num(this.helper.css("left"));
2531 curtop = num(this.helper.css("top"));
2533 if (o.containment) {
2534 curleft += $(o.containment).scrollLeft() || 0;
2535 curtop += $(o.containment).scrollTop() || 0;
2538 //Store needed variables
2539 this.offset = this.helper.offset();
2540 this.position = { left: curleft, top: curtop };
2541 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2542 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2543 this.originalPosition = { left: curleft, top: curtop };
2544 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2545 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2548 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2550 cursor = $(".ui-resizable-" + this.axis).css("cursor");
2551 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
2553 el.addClass("ui-resizable-resizing");
2554 this._propagate("start", event);
2558 _mouseDrag: function(event) {
2560 //Increase performance, avoid regex
2562 el = this.helper, props = {},
2563 smp = this.originalMousePosition,
2565 prevTop = this.position.top,
2566 prevLeft = this.position.left,
2567 prevWidth = this.size.width,
2568 prevHeight = this.size.height,
2569 dx = (event.pageX-smp.left)||0,
2570 dy = (event.pageY-smp.top)||0,
2571 trigger = this._change[a];
2577 // Calculate the attrs that will be change
2578 data = trigger.apply(this, [event, dx, dy]);
2580 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2581 this._updateVirtualBoundaries(event.shiftKey);
2582 if (this._aspectRatio || event.shiftKey) {
2583 data = this._updateRatio(data, event);
2586 data = this._respectSize(data, event);
2588 this._updateCache(data);
2590 // plugins callbacks need to be called first
2591 this._propagate("resize", event);
2593 if (this.position.top !== prevTop) {
2594 props.top = this.position.top + "px";
2596 if (this.position.left !== prevLeft) {
2597 props.left = this.position.left + "px";
2599 if (this.size.width !== prevWidth) {
2600 props.width = this.size.width + "px";
2602 if (this.size.height !== prevHeight) {
2603 props.height = this.size.height + "px";
2607 if (!this._helper && this._proportionallyResizeElements.length) {
2608 this._proportionallyResize();
2611 // Call the user callback if the element was resized
2612 if ( ! $.isEmptyObject(props) ) {
2613 this._trigger("resize", event, this.ui());
2619 _mouseStop: function(event) {
2621 this.resizing = false;
2622 var pr, ista, soffseth, soffsetw, s, left, top,
2623 o = this.options, that = this;
2627 pr = this._proportionallyResizeElements;
2628 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
2629 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
2630 soffsetw = ista ? 0 : that.sizeDiff.width;
2632 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
2633 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
2634 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2637 this.element.css($.extend(s, { top: top, left: left }));
2640 that.helper.height(that.size.height);
2641 that.helper.width(that.size.width);
2643 if (this._helper && !o.animate) {
2644 this._proportionallyResize();
2648 $("body").css("cursor", "auto");
2650 this.element.removeClass("ui-resizable-resizing");
2652 this._propagate("stop", event);
2655 this.helper.remove();
2662 _updateVirtualBoundaries: function(forceAspectRatio) {
2663 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
2667 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2668 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2669 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2670 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2673 if(this._aspectRatio || forceAspectRatio) {
2674 // We want to create an enclosing box whose aspect ration is the requested one
2675 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2676 pMinWidth = b.minHeight * this.aspectRatio;
2677 pMinHeight = b.minWidth / this.aspectRatio;
2678 pMaxWidth = b.maxHeight * this.aspectRatio;
2679 pMaxHeight = b.maxWidth / this.aspectRatio;
2681 if(pMinWidth > b.minWidth) {
2682 b.minWidth = pMinWidth;
2684 if(pMinHeight > b.minHeight) {
2685 b.minHeight = pMinHeight;
2687 if(pMaxWidth < b.maxWidth) {
2688 b.maxWidth = pMaxWidth;
2690 if(pMaxHeight < b.maxHeight) {
2691 b.maxHeight = pMaxHeight;
2694 this._vBoundaries = b;
2697 _updateCache: function(data) {
2698 this.offset = this.helper.offset();
2699 if (isNumber(data.left)) {
2700 this.position.left = data.left;
2702 if (isNumber(data.top)) {
2703 this.position.top = data.top;
2705 if (isNumber(data.height)) {
2706 this.size.height = data.height;
2708 if (isNumber(data.width)) {
2709 this.size.width = data.width;
2713 _updateRatio: function( data ) {
2715 var cpos = this.position,
2719 if (isNumber(data.height)) {
2720 data.width = (data.height * this.aspectRatio);
2721 } else if (isNumber(data.width)) {
2722 data.height = (data.width / this.aspectRatio);
2726 data.left = cpos.left + (csize.width - data.width);
2730 data.top = cpos.top + (csize.height - data.height);
2731 data.left = cpos.left + (csize.width - data.width);
2737 _respectSize: function( data ) {
2739 var o = this._vBoundaries,
2741 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2742 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
2743 dw = this.originalPosition.left + this.originalSize.width,
2744 dh = this.position.top + this.size.height,
2745 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2747 data.width = o.minWidth;
2750 data.height = o.minHeight;
2753 data.width = o.maxWidth;
2756 data.height = o.maxHeight;
2760 data.left = dw - o.minWidth;
2763 data.left = dw - o.maxWidth;
2766 data.top = dh - o.minHeight;
2769 data.top = dh - o.maxHeight;
2772 // fixing jump error on top/left - bug #2330
2773 if (!data.width && !data.height && !data.left && data.top) {
2775 } else if (!data.width && !data.height && !data.top && data.left) {
2782 _proportionallyResize: function() {
2784 if (!this._proportionallyResizeElements.length) {
2788 var i, j, borders, paddings, prel,
2789 element = this.helper || this.element;
2791 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
2793 prel = this._proportionallyResizeElements[i];
2795 if (!this.borderDif) {
2796 this.borderDif = [];
2797 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
2798 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
2800 for ( j = 0; j < borders.length; j++ ) {
2801 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
2806 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2807 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2814 _renderProxy: function() {
2816 var el = this.element, o = this.options;
2817 this.elementOffset = el.offset();
2821 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
2823 this.helper.addClass(this._helper).css({
2824 width: this.element.outerWidth() - 1,
2825 height: this.element.outerHeight() - 1,
2826 position: "absolute",
2827 left: this.elementOffset.left +"px",
2828 top: this.elementOffset.top +"px",
2829 zIndex: ++o.zIndex //TODO: Don't modify option
2834 .disableSelection();
2837 this.helper = this.element;
2843 e: function(event, dx) {
2844 return { width: this.originalSize.width + dx };
2846 w: function(event, dx) {
2847 var cs = this.originalSize, sp = this.originalPosition;
2848 return { left: sp.left + dx, width: cs.width - dx };
2850 n: function(event, dx, dy) {
2851 var cs = this.originalSize, sp = this.originalPosition;
2852 return { top: sp.top + dy, height: cs.height - dy };
2854 s: function(event, dx, dy) {
2855 return { height: this.originalSize.height + dy };
2857 se: function(event, dx, dy) {
2858 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2860 sw: function(event, dx, dy) {
2861 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2863 ne: function(event, dx, dy) {
2864 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2866 nw: function(event, dx, dy) {
2867 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2871 _propagate: function(n, event) {
2872 $.ui.plugin.call(this, n, [event, this.ui()]);
2873 (n !== "resize" && this._trigger(n, event, this.ui()));
2880 originalElement: this.originalElement,
2881 element: this.element,
2882 helper: this.helper,
2883 position: this.position,
2885 originalSize: this.originalSize,
2886 originalPosition: this.originalPosition
2893 * Resizable Extensions
2896 $.ui.plugin.add("resizable", "animate", {
2898 stop: function( event ) {
2899 var that = $(this).data("ui-resizable"),
2901 pr = that._proportionallyResizeElements,
2902 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2903 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2904 soffsetw = ista ? 0 : that.sizeDiff.width,
2905 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
2906 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
2907 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2909 that.element.animate(
2910 $.extend(style, top && left ? { top: top, left: left } : {}), {
2911 duration: o.animateDuration,
2912 easing: o.animateEasing,
2916 width: parseInt(that.element.css("width"), 10),
2917 height: parseInt(that.element.css("height"), 10),
2918 top: parseInt(that.element.css("top"), 10),
2919 left: parseInt(that.element.css("left"), 10)
2922 if (pr && pr.length) {
2923 $(pr[0]).css({ width: data.width, height: data.height });
2926 // propagating resize, and updating values for each animation step
2927 that._updateCache(data);
2928 that._propagate("resize", event);
2937 $.ui.plugin.add("resizable", "containment", {
2940 var element, p, co, ch, cw, width, height,
2941 that = $(this).data("ui-resizable"),
2945 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2951 that.containerElement = $(ce);
2953 if (/document/.test(oc) || oc === document) {
2954 that.containerOffset = { left: 0, top: 0 };
2955 that.containerPosition = { left: 0, top: 0 };
2958 element: $(document), left: 0, top: 0,
2959 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2963 // i'm a node, so compute top, left, right, bottom
2967 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2969 that.containerOffset = element.offset();
2970 that.containerPosition = element.position();
2971 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2973 co = that.containerOffset;
2974 ch = that.containerSize.height;
2975 cw = that.containerSize.width;
2976 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
2977 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
2980 element: ce, left: co.left, top: co.top, width: width, height: height
2985 resize: function( event ) {
2986 var woset, hoset, isParent, isOffsetRelative,
2987 that = $(this).data("ui-resizable"),
2989 co = that.containerOffset, cp = that.position,
2990 pRatio = that._aspectRatio || event.shiftKey,
2991 cop = { top:0, left:0 }, ce = that.containerElement;
2993 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
2997 if (cp.left < (that._helper ? co.left : 0)) {
2998 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3000 that.size.height = that.size.width / that.aspectRatio;
3002 that.position.left = o.helper ? co.left : 0;
3005 if (cp.top < (that._helper ? co.top : 0)) {
3006 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3008 that.size.width = that.size.height * that.aspectRatio;
3010 that.position.top = that._helper ? co.top : 0;
3013 that.offset.left = that.parentData.left+that.position.left;
3014 that.offset.top = that.parentData.top+that.position.top;
3016 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3017 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3019 isParent = that.containerElement.get(0) === that.element.parent().get(0);
3020 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3022 if(isParent && isOffsetRelative) {
3023 woset -= that.parentData.left;
3026 if (woset + that.size.width >= that.parentData.width) {
3027 that.size.width = that.parentData.width - woset;
3029 that.size.height = that.size.width / that.aspectRatio;
3033 if (hoset + that.size.height >= that.parentData.height) {
3034 that.size.height = that.parentData.height - hoset;
3036 that.size.width = that.size.height * that.aspectRatio;
3042 var that = $(this).data("ui-resizable"),
3044 co = that.containerOffset,
3045 cop = that.containerPosition,
3046 ce = that.containerElement,
3047 helper = $(that.helper),
3048 ho = helper.offset(),
3049 w = helper.outerWidth() - that.sizeDiff.width,
3050 h = helper.outerHeight() - that.sizeDiff.height;
3052 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3053 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3056 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3057 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3063 $.ui.plugin.add("resizable", "alsoResize", {
3065 start: function () {
3066 var that = $(this).data("ui-resizable"),
3068 _store = function (exp) {
3069 $(exp).each(function() {
3071 el.data("ui-resizable-alsoresize", {
3072 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3073 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3078 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3079 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3080 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3082 _store(o.alsoResize);
3086 resize: function (event, ui) {
3087 var that = $(this).data("ui-resizable"),
3089 os = that.originalSize,
3090 op = that.originalPosition,
3092 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3093 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3096 _alsoResize = function (exp, c) {
3097 $(exp).each(function() {
3098 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3099 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3101 $.each(css, function (i, prop) {
3102 var sum = (start[prop]||0) + (delta[prop]||0);
3103 if (sum && sum >= 0) {
3104 style[prop] = sum || null;
3112 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3113 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3115 _alsoResize(o.alsoResize);
3120 $(this).removeData("resizable-alsoresize");
3124 $.ui.plugin.add("resizable", "ghost", {
3128 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3130 that.ghost = that.originalElement.clone();
3132 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3133 .addClass("ui-resizable-ghost")
3134 .addClass(typeof o.ghost === "string" ? o.ghost : "");
3136 that.ghost.appendTo(that.helper);
3141 var that = $(this).data("ui-resizable");
3143 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3148 var that = $(this).data("ui-resizable");
3149 if (that.ghost && that.helper) {
3150 that.helper.get(0).removeChild(that.ghost.get(0));
3156 $.ui.plugin.add("resizable", "grid", {
3158 resize: function() {
3159 var that = $(this).data("ui-resizable"),
3162 os = that.originalSize,
3163 op = that.originalPosition,
3165 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3166 gridX = (grid[0]||1),
3167 gridY = (grid[1]||1),
3168 ox = Math.round((cs.width - os.width) / gridX) * gridX,
3169 oy = Math.round((cs.height - os.height) / gridY) * gridY,
3170 newWidth = os.width + ox,
3171 newHeight = os.height + oy,
3172 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3173 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3174 isMinWidth = o.minWidth && (o.minWidth > newWidth),
3175 isMinHeight = o.minHeight && (o.minHeight > newHeight);
3180 newWidth = newWidth + gridX;
3183 newHeight = newHeight + gridY;
3186 newWidth = newWidth - gridX;
3189 newHeight = newHeight - gridY;
3192 if (/^(se|s|e)$/.test(a)) {
3193 that.size.width = newWidth;
3194 that.size.height = newHeight;
3195 } else if (/^(ne)$/.test(a)) {
3196 that.size.width = newWidth;
3197 that.size.height = newHeight;
3198 that.position.top = op.top - oy;
3199 } else if (/^(sw)$/.test(a)) {
3200 that.size.width = newWidth;
3201 that.size.height = newHeight;
3202 that.position.left = op.left - ox;
3204 that.size.width = newWidth;
3205 that.size.height = newHeight;
3206 that.position.top = op.top - oy;
3207 that.position.left = op.left - ox;
3215 (function( $, undefined ) {
3217 $.widget("ui.selectable", $.ui.mouse, {
3234 _create: function() {
3238 this.element.addClass("ui-selectable");
3240 this.dragged = false;
3242 // cache selectee children based on filter
3243 this.refresh = function() {
3244 selectees = $(that.options.filter, that.element[0]);
3245 selectees.addClass("ui-selectee");
3246 selectees.each(function() {
3247 var $this = $(this),
3248 pos = $this.offset();
3249 $.data(this, "selectable-item", {
3254 right: pos.left + $this.outerWidth(),
3255 bottom: pos.top + $this.outerHeight(),
3256 startselected: false,
3257 selected: $this.hasClass("ui-selected"),
3258 selecting: $this.hasClass("ui-selecting"),
3259 unselecting: $this.hasClass("ui-unselecting")
3265 this.selectees = selectees.addClass("ui-selectee");
3269 this.helper = $("<div class='ui-selectable-helper'></div>");
3272 _destroy: function() {
3274 .removeClass("ui-selectee")
3275 .removeData("selectable-item");
3277 .removeClass("ui-selectable ui-selectable-disabled");
3278 this._mouseDestroy();
3281 _mouseStart: function(event) {
3283 options = this.options;
3285 this.opos = [event.pageX, event.pageY];
3287 if (this.options.disabled) {
3291 this.selectees = $(options.filter, this.element[0]);
3293 this._trigger("start", event);
3295 $(options.appendTo).append(this.helper);
3296 // position helper (lasso)
3298 "left": event.pageX,
3304 if (options.autoRefresh) {
3308 this.selectees.filter(".ui-selected").each(function() {
3309 var selectee = $.data(this, "selectable-item");
3310 selectee.startselected = true;
3311 if (!event.metaKey && !event.ctrlKey) {
3312 selectee.$element.removeClass("ui-selected");
3313 selectee.selected = false;
3314 selectee.$element.addClass("ui-unselecting");
3315 selectee.unselecting = true;
3316 // selectable UNSELECTING callback
3317 that._trigger("unselecting", event, {
3318 unselecting: selectee.element
3323 $(event.target).parents().addBack().each(function() {
3325 selectee = $.data(this, "selectable-item");
3327 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3329 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3330 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3331 selectee.unselecting = !doSelect;
3332 selectee.selecting = doSelect;
3333 selectee.selected = doSelect;
3334 // selectable (UN)SELECTING callback
3336 that._trigger("selecting", event, {
3337 selecting: selectee.element
3340 that._trigger("unselecting", event, {
3341 unselecting: selectee.element
3350 _mouseDrag: function(event) {
3352 this.dragged = true;
3354 if (this.options.disabled) {
3360 options = this.options,
3366 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3367 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
3368 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3370 this.selectees.each(function() {
3371 var selectee = $.data(this, "selectable-item"),
3374 //prevent helper from being selected if appendTo: selectable
3375 if (!selectee || selectee.element === that.element[0]) {
3379 if (options.tolerance === "touch") {
3380 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3381 } else if (options.tolerance === "fit") {
3382 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3387 if (selectee.selected) {
3388 selectee.$element.removeClass("ui-selected");
3389 selectee.selected = false;
3391 if (selectee.unselecting) {
3392 selectee.$element.removeClass("ui-unselecting");
3393 selectee.unselecting = false;
3395 if (!selectee.selecting) {
3396 selectee.$element.addClass("ui-selecting");
3397 selectee.selecting = true;
3398 // selectable SELECTING callback
3399 that._trigger("selecting", event, {
3400 selecting: selectee.element
3405 if (selectee.selecting) {
3406 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3407 selectee.$element.removeClass("ui-selecting");
3408 selectee.selecting = false;
3409 selectee.$element.addClass("ui-selected");
3410 selectee.selected = true;
3412 selectee.$element.removeClass("ui-selecting");
3413 selectee.selecting = false;
3414 if (selectee.startselected) {
3415 selectee.$element.addClass("ui-unselecting");
3416 selectee.unselecting = true;
3418 // selectable UNSELECTING callback
3419 that._trigger("unselecting", event, {
3420 unselecting: selectee.element
3424 if (selectee.selected) {
3425 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3426 selectee.$element.removeClass("ui-selected");
3427 selectee.selected = false;
3429 selectee.$element.addClass("ui-unselecting");
3430 selectee.unselecting = true;
3431 // selectable UNSELECTING callback
3432 that._trigger("unselecting", event, {
3433 unselecting: selectee.element
3443 _mouseStop: function(event) {
3446 this.dragged = false;
3448 $(".ui-unselecting", this.element[0]).each(function() {
3449 var selectee = $.data(this, "selectable-item");
3450 selectee.$element.removeClass("ui-unselecting");
3451 selectee.unselecting = false;
3452 selectee.startselected = false;
3453 that._trigger("unselected", event, {
3454 unselected: selectee.element
3457 $(".ui-selecting", this.element[0]).each(function() {
3458 var selectee = $.data(this, "selectable-item");
3459 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3460 selectee.selecting = false;
3461 selectee.selected = true;
3462 selectee.startselected = true;
3463 that._trigger("selected", event, {
3464 selected: selectee.element
3467 this._trigger("stop", event);
3469 this.helper.remove();
3478 (function( $, undefined ) {
3480 /*jshint loopfunc: true */
3482 function isOverAxis( x, reference, size ) {
3483 return ( x > reference ) && ( x < ( reference + size ) );
3486 function isFloating(item) {
3487 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3490 $.widget("ui.sortable", $.ui.mouse, {
3492 widgetEventPrefix: "sort",
3502 forcePlaceholderSize: false,
3503 forceHelperSize: false,
3512 scrollSensitivity: 20,
3515 tolerance: "intersect",
3532 _create: function() {
3534 var o = this.options;
3535 this.containerCache = {};
3536 this.element.addClass("ui-sortable");
3541 //Let's determine if the items are being displayed horizontally
3542 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
3544 //Let's determine the parent's offset
3545 this.offset = this.element.offset();
3547 //Initialize mouse events for interaction
3555 _destroy: function() {
3557 .removeClass("ui-sortable ui-sortable-disabled");
3558 this._mouseDestroy();
3560 for ( var i = this.items.length - 1; i >= 0; i-- ) {
3561 this.items[i].item.removeData(this.widgetName + "-item");
3567 _setOption: function(key, value){
3568 if ( key === "disabled" ) {
3569 this.options[ key ] = value;
3571 this.widget().toggleClass( "ui-sortable-disabled", !!value );
3573 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3574 $.Widget.prototype._setOption.apply(this, arguments);
3578 _mouseCapture: function(event, overrideHandle) {
3579 var currentItem = null,
3580 validHandle = false,
3583 if (this.reverting) {
3587 if(this.options.disabled || this.options.type === "static") {
3591 //We have to refresh the items data once first
3592 this._refreshItems(event);
3594 //Find out if the clicked node (or one of its parents) is a actual item in this.items
3595 $(event.target).parents().each(function() {
3596 if($.data(this, that.widgetName + "-item") === that) {
3597 currentItem = $(this);
3601 if($.data(event.target, that.widgetName + "-item") === that) {
3602 currentItem = $(event.target);
3608 if(this.options.handle && !overrideHandle) {
3609 $(this.options.handle, currentItem).find("*").addBack().each(function() {
3610 if(this === event.target) {
3619 this.currentItem = currentItem;
3620 this._removeCurrentsFromItems();
3625 _mouseStart: function(event, overrideHandle, noActivation) {
3630 this.currentContainer = this;
3632 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3633 this.refreshPositions();
3635 //Create and append the visible helper
3636 this.helper = this._createHelper(event);
3638 //Cache the helper size
3639 this._cacheHelperProportions();
3642 * - Position generation -
3643 * This block generates everything position related - it's the core of draggables.
3646 //Cache the margins of the original element
3647 this._cacheMargins();
3649 //Get the next scrolling parent
3650 this.scrollParent = this.helper.scrollParent();
3652 //The element's absolute position on the page minus margins
3653 this.offset = this.currentItem.offset();
3655 top: this.offset.top - this.margins.top,
3656 left: this.offset.left - this.margins.left
3659 $.extend(this.offset, {
3660 click: { //Where the click happened, relative to the element
3661 left: event.pageX - this.offset.left,
3662 top: event.pageY - this.offset.top
3664 parent: this._getParentOffset(),
3665 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3668 // Only after we got the offset, we can change the helper's position to absolute
3669 // TODO: Still need to figure out a way to make relative sorting possible
3670 this.helper.css("position", "absolute");
3671 this.cssPosition = this.helper.css("position");
3673 //Generate the original position
3674 this.originalPosition = this._generatePosition(event);
3675 this.originalPageX = event.pageX;
3676 this.originalPageY = event.pageY;
3678 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
3679 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3681 //Cache the former DOM position
3682 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3684 //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
3685 if(this.helper[0] !== this.currentItem[0]) {
3686 this.currentItem.hide();
3689 //Create the placeholder
3690 this._createPlaceholder();
3692 //Set a containment if given in the options
3694 this._setContainment();
3697 if( o.cursor && o.cursor !== "auto" ) { // cursor option
3698 body = this.document.find( "body" );
3701 this.storedCursor = body.css( "cursor" );
3702 body.css( "cursor", o.cursor );
3704 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
3707 if(o.opacity) { // opacity option
3708 if (this.helper.css("opacity")) {
3709 this._storedOpacity = this.helper.css("opacity");
3711 this.helper.css("opacity", o.opacity);
3714 if(o.zIndex) { // zIndex option
3715 if (this.helper.css("zIndex")) {
3716 this._storedZIndex = this.helper.css("zIndex");
3718 this.helper.css("zIndex", o.zIndex);
3722 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3723 this.overflowOffset = this.scrollParent.offset();
3727 this._trigger("start", event, this._uiHash());
3729 //Recache the helper size
3730 if(!this._preserveHelperProportions) {
3731 this._cacheHelperProportions();
3735 //Post "activate" events to possible containers
3736 if( !noActivation ) {
3737 for ( i = this.containers.length - 1; i >= 0; i-- ) {
3738 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
3742 //Prepare possible droppables
3743 if($.ui.ddmanager) {
3744 $.ui.ddmanager.current = this;
3747 if ($.ui.ddmanager && !o.dropBehaviour) {
3748 $.ui.ddmanager.prepareOffsets(this, event);
3751 this.dragging = true;
3753 this.helper.addClass("ui-sortable-helper");
3754 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3759 _mouseDrag: function(event) {
3760 var i, item, itemElement, intersection,
3764 //Compute the helpers position
3765 this.position = this._generatePosition(event);
3766 this.positionAbs = this._convertPositionTo("absolute");
3768 if (!this.lastPositionAbs) {
3769 this.lastPositionAbs = this.positionAbs;
3773 if(this.options.scroll) {
3774 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3776 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
3777 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3778 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
3779 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3782 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
3783 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3784 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
3785 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3790 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
3791 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3792 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
3793 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3796 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
3797 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3798 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
3799 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3804 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
3805 $.ui.ddmanager.prepareOffsets(this, event);
3809 //Regenerate the absolute position used for position checks
3810 this.positionAbs = this._convertPositionTo("absolute");
3812 //Set the helper position
3813 if(!this.options.axis || this.options.axis !== "y") {
3814 this.helper[0].style.left = this.position.left+"px";
3816 if(!this.options.axis || this.options.axis !== "x") {
3817 this.helper[0].style.top = this.position.top+"px";
3821 for (i = this.items.length - 1; i >= 0; i--) {
3823 //Cache variables and intersection, continue if no intersection
3824 item = this.items[i];
3825 itemElement = item.item[0];
3826 intersection = this._intersectsWithPointer(item);
3827 if (!intersection) {
3831 // Only put the placeholder inside the current Container, skip all
3832 // items form other containers. This works because when moving
3833 // an item from one container to another the
3834 // currentContainer is switched before the placeholder is moved.
3836 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3837 // beetween the outer and inner container.
3838 if (item.instance !== this.currentContainer) {
3842 // cannot intersect with itself
3843 // no useless actions that have been done before
3844 // no action if the item moved is the parent of the item checked
3845 if (itemElement !== this.currentItem[0] &&
3846 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
3847 !$.contains(this.placeholder[0], itemElement) &&
3848 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
3851 this.direction = intersection === 1 ? "down" : "up";
3853 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
3854 this._rearrange(event, item);
3859 this._trigger("change", event, this._uiHash());
3864 //Post events to containers
3865 this._contactContainers(event);
3867 //Interconnect with droppables
3868 if($.ui.ddmanager) {
3869 $.ui.ddmanager.drag(this, event);
3873 this._trigger("sort", event, this._uiHash());
3875 this.lastPositionAbs = this.positionAbs;
3880 _mouseStop: function(event, noPropagation) {
3886 //If we are using droppables, inform the manager about the drop
3887 if ($.ui.ddmanager && !this.options.dropBehaviour) {
3888 $.ui.ddmanager.drop(this, event);
3891 if(this.options.revert) {
3893 cur = this.placeholder.offset(),
3894 axis = this.options.axis,
3897 if ( !axis || axis === "x" ) {
3898 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
3900 if ( !axis || axis === "y" ) {
3901 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
3903 this.reverting = true;
3904 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
3908 this._clear(event, noPropagation);
3915 cancel: function() {
3919 this._mouseUp({ target: null });
3921 if(this.options.helper === "original") {
3922 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3924 this.currentItem.show();
3927 //Post deactivating events to containers
3928 for (var i = this.containers.length - 1; i >= 0; i--){
3929 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3930 if(this.containers[i].containerCache.over) {
3931 this.containers[i]._trigger("out", null, this._uiHash(this));
3932 this.containers[i].containerCache.over = 0;
3938 if (this.placeholder) {
3939 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3940 if(this.placeholder[0].parentNode) {
3941 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3943 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
3944 this.helper.remove();
3954 if(this.domPosition.prev) {
3955 $(this.domPosition.prev).after(this.currentItem);
3957 $(this.domPosition.parent).prepend(this.currentItem);
3965 serialize: function(o) {
3967 var items = this._getItemsAsjQuery(o && o.connected),
3971 $(items).each(function() {
3972 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
3974 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
3978 if(!str.length && o.key) {
3979 str.push(o.key + "=");
3982 return str.join("&");
3986 toArray: function(o) {
3988 var items = this._getItemsAsjQuery(o && o.connected),
3993 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
3998 /* Be careful with the following core functions */
3999 _intersectsWith: function(item) {
4001 var x1 = this.positionAbs.left,
4002 x2 = x1 + this.helperProportions.width,
4003 y1 = this.positionAbs.top,
4004 y2 = y1 + this.helperProportions.height,
4008 b = t + item.height,
4009 dyClick = this.offset.click.top,
4010 dxClick = this.offset.click.left,
4011 isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
4013 if ( this.options.tolerance === "pointer" ||
4014 this.options.forcePointerForContainers ||
4015 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4017 return isOverElement;
4020 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4021 x2 - (this.helperProportions.width / 2) < r && // Left Half
4022 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4023 y2 - (this.helperProportions.height / 2) < b ); // Top Half
4028 _intersectsWithPointer: function(item) {
4030 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4031 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4032 isOverElement = isOverElementHeight && isOverElementWidth,
4033 verticalDirection = this._getDragVerticalDirection(),
4034 horizontalDirection = this._getDragHorizontalDirection();
4036 if (!isOverElement) {
4040 return this.floating ?
4041 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4042 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4046 _intersectsWithSides: function(item) {
4048 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4049 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4050 verticalDirection = this._getDragVerticalDirection(),
4051 horizontalDirection = this._getDragHorizontalDirection();
4053 if (this.floating && horizontalDirection) {
4054 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4056 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4061 _getDragVerticalDirection: function() {
4062 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4063 return delta !== 0 && (delta > 0 ? "down" : "up");
4066 _getDragHorizontalDirection: function() {
4067 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4068 return delta !== 0 && (delta > 0 ? "right" : "left");
4071 refresh: function(event) {
4072 this._refreshItems(event);
4073 this.refreshPositions();
4077 _connectWith: function() {
4078 var options = this.options;
4079 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4082 _getItemsAsjQuery: function(connected) {
4084 var i, j, cur, inst,
4087 connectWith = this._connectWith();
4089 if(connectWith && connected) {
4090 for (i = connectWith.length - 1; i >= 0; i--){
4091 cur = $(connectWith[i]);
4092 for ( j = cur.length - 1; j >= 0; j--){
4093 inst = $.data(cur[j], this.widgetFullName);
4094 if(inst && inst !== this && !inst.options.disabled) {
4095 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]);
4101 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]);
4103 for (i = queries.length - 1; i >= 0; i--){
4104 queries[i][0].each(function() {
4113 _removeCurrentsFromItems: function() {
4115 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4117 this.items = $.grep(this.items, function (item) {
4118 for (var j=0; j < list.length; j++) {
4119 if(list[j] === item.item[0]) {
4128 _refreshItems: function(event) {
4131 this.containers = [this];
4133 var i, j, cur, inst, targetData, _queries, item, queriesLength,
4135 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4136 connectWith = this._connectWith();
4138 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4139 for (i = connectWith.length - 1; i >= 0; i--){
4140 cur = $(connectWith[i]);
4141 for (j = cur.length - 1; j >= 0; j--){
4142 inst = $.data(cur[j], this.widgetFullName);
4143 if(inst && inst !== this && !inst.options.disabled) {
4144 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4145 this.containers.push(inst);
4151 for (i = queries.length - 1; i >= 0; i--) {
4152 targetData = queries[i][1];
4153 _queries = queries[i][0];
4155 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4156 item = $(_queries[j]);
4158 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4162 instance: targetData,
4163 width: 0, height: 0,
4171 refreshPositions: function(fast) {
4173 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4174 if(this.offsetParent && this.helper) {
4175 this.offset.parent = this._getParentOffset();
4180 for (i = this.items.length - 1; i >= 0; i--){
4181 item = this.items[i];
4183 //We ignore calculating positions of all connected containers when we're not over them
4184 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4188 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4191 item.width = t.outerWidth();
4192 item.height = t.outerHeight();
4200 if(this.options.custom && this.options.custom.refreshContainers) {
4201 this.options.custom.refreshContainers.call(this);
4203 for (i = this.containers.length - 1; i >= 0; i--){
4204 p = this.containers[i].element.offset();
4205 this.containers[i].containerCache.left = p.left;
4206 this.containers[i].containerCache.top = p.top;
4207 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4208 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4215 _createPlaceholder: function(that) {
4216 that = that || this;
4220 if(!o.placeholder || o.placeholder.constructor === String) {
4221 className = o.placeholder;
4223 element: function() {
4225 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4226 element = $( that.document[0].createElement( nodeName ) )
4227 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4228 .removeClass("ui-sortable-helper");
4230 if ( nodeName === "tr" ) {
4231 // Use a high colspan to force the td to expand the full
4232 // width of the table (browsers are smart enough to
4233 // handle this properly)
4234 element.append( "<td colspan='99'> </td>" );
4235 } else if ( nodeName === "img" ) {
4236 element.attr( "src", that.currentItem.attr( "src" ) );
4240 element.css( "visibility", "hidden" );
4245 update: function(container, p) {
4247 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4248 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4249 if(className && !o.forcePlaceholderSize) {
4253 //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
4254 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4255 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4260 //Create the placeholder
4261 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4263 //Append it after the actual current item
4264 that.currentItem.after(that.placeholder);
4266 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4267 o.placeholder.update(that, that.placeholder);
4271 _contactContainers: function(event) {
4272 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4273 innermostContainer = null,
4274 innermostIndex = null;
4276 // get innermost container that intersects with item
4277 for (i = this.containers.length - 1; i >= 0; i--) {
4279 // never consider a container that's located within the item itself
4280 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4284 if(this._intersectsWith(this.containers[i].containerCache)) {
4286 // if we've already found a container and it's more "inner" than this, then continue
4287 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4291 innermostContainer = this.containers[i];
4295 // container doesn't intersect. trigger "out" event if necessary
4296 if(this.containers[i].containerCache.over) {
4297 this.containers[i]._trigger("out", event, this._uiHash(this));
4298 this.containers[i].containerCache.over = 0;
4304 // if no intersecting containers found, return
4305 if(!innermostContainer) {
4309 // move the item into the container if it's not there already
4310 if(this.containers.length === 1) {
4311 if (!this.containers[innermostIndex].containerCache.over) {
4312 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4313 this.containers[innermostIndex].containerCache.over = 1;
4317 //When entering a new container, we will find the item with the least distance and append our item near it
4319 itemWithLeastDistance = null;
4320 floating = innermostContainer.floating || isFloating(this.currentItem);
4321 posProperty = floating ? "left" : "top";
4322 sizeProperty = floating ? "width" : "height";
4323 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4324 for (j = this.items.length - 1; j >= 0; j--) {
4325 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4328 if(this.items[j].item[0] === this.currentItem[0]) {
4331 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4334 cur = this.items[j].item.offset()[posProperty];
4336 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4338 cur += this.items[j][sizeProperty];
4341 if(Math.abs(cur - base) < dist) {
4342 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4343 this.direction = nearBottom ? "up": "down";
4347 //Check if dropOnEmpty is enabled
4348 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4352 if(this.currentContainer === this.containers[innermostIndex]) {
4356 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4357 this._trigger("change", event, this._uiHash());
4358 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4359 this.currentContainer = this.containers[innermostIndex];
4361 //Update the placeholder
4362 this.options.placeholder.update(this.currentContainer, this.placeholder);
4364 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4365 this.containers[innermostIndex].containerCache.over = 1;
4371 _createHelper: function(event) {
4373 var o = this.options,
4374 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4376 //Add the helper to the DOM if that didn't happen already
4377 if(!helper.parents("body").length) {
4378 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4381 if(helper[0] === this.currentItem[0]) {
4382 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") };
4385 if(!helper[0].style.width || o.forceHelperSize) {
4386 helper.width(this.currentItem.width());
4388 if(!helper[0].style.height || o.forceHelperSize) {
4389 helper.height(this.currentItem.height());
4396 _adjustOffsetFromHelper: function(obj) {
4397 if (typeof obj === "string") {
4398 obj = obj.split(" ");
4400 if ($.isArray(obj)) {
4401 obj = {left: +obj[0], top: +obj[1] || 0};
4403 if ("left" in obj) {
4404 this.offset.click.left = obj.left + this.margins.left;
4406 if ("right" in obj) {
4407 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4410 this.offset.click.top = obj.top + this.margins.top;
4412 if ("bottom" in obj) {
4413 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4417 _getParentOffset: function() {
4420 //Get the offsetParent and cache its position
4421 this.offsetParent = this.helper.offsetParent();
4422 var po = this.offsetParent.offset();
4424 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4425 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4426 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4427 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4428 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4429 po.left += this.scrollParent.scrollLeft();
4430 po.top += this.scrollParent.scrollTop();
4433 // This needs to be actually done for all browsers, since pageX/pageY includes this information
4434 // with an ugly IE fix
4435 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4436 po = { top: 0, left: 0 };
4440 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4441 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4446 _getRelativeOffset: function() {
4448 if(this.cssPosition === "relative") {
4449 var p = this.currentItem.position();
4451 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4452 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4455 return { top: 0, left: 0 };
4460 _cacheMargins: function() {
4462 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4463 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4467 _cacheHelperProportions: function() {
4468 this.helperProportions = {
4469 width: this.helper.outerWidth(),
4470 height: this.helper.outerHeight()
4474 _setContainment: function() {
4478 if(o.containment === "parent") {
4479 o.containment = this.helper[0].parentNode;
4481 if(o.containment === "document" || o.containment === "window") {
4482 this.containment = [
4483 0 - this.offset.relative.left - this.offset.parent.left,
4484 0 - this.offset.relative.top - this.offset.parent.top,
4485 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4486 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4490 if(!(/^(document|window|parent)$/).test(o.containment)) {
4491 ce = $(o.containment)[0];
4492 co = $(o.containment).offset();
4493 over = ($(ce).css("overflow") !== "hidden");
4495 this.containment = [
4496 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4497 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4498 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,
4499 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
4505 _convertPositionTo: function(d, pos) {
4508 pos = this.position;
4510 var mod = d === "absolute" ? 1 : -1,
4511 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
4512 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4516 pos.top + // The absolute mouse position
4517 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4518 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
4519 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4522 pos.left + // The absolute mouse position
4523 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4524 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
4525 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4531 _generatePosition: function(event) {
4535 pageX = event.pageX,
4536 pageY = event.pageY,
4537 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);
4539 // This is another very weird special case that only happens for relative elements:
4540 // 1. If the css position is relative
4541 // 2. and the scroll parent is the document or similar to the offset parent
4542 // we have to refresh the relative offset during the scroll so there are no jumps
4543 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
4544 this.offset.relative = this._getRelativeOffset();
4548 * - Position constraining -
4549 * Constrain the position to a mix of grid, containment.
4552 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4554 if(this.containment) {
4555 if(event.pageX - this.offset.click.left < this.containment[0]) {
4556 pageX = this.containment[0] + this.offset.click.left;
4558 if(event.pageY - this.offset.click.top < this.containment[1]) {
4559 pageY = this.containment[1] + this.offset.click.top;
4561 if(event.pageX - this.offset.click.left > this.containment[2]) {
4562 pageX = this.containment[2] + this.offset.click.left;
4564 if(event.pageY - this.offset.click.top > this.containment[3]) {
4565 pageY = this.containment[3] + this.offset.click.top;
4570 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4571 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;
4573 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4574 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;
4581 pageY - // The absolute mouse position
4582 this.offset.click.top - // Click offset (relative to the element)
4583 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
4584 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
4585 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4588 pageX - // The absolute mouse position
4589 this.offset.click.left - // Click offset (relative to the element)
4590 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
4591 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
4592 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4598 _rearrange: function(event, i, a, hardRefresh) {
4600 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));
4602 //Various things done here to improve the performance:
4603 // 1. we create a setTimeout, that calls refreshPositions
4604 // 2. on the instance, we have a counter variable, that get's higher after every append
4605 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4606 // 4. this lets only the last addition to the timeout stack through
4607 this.counter = this.counter ? ++this.counter : 1;
4608 var counter = this.counter;
4610 this._delay(function() {
4611 if(counter === this.counter) {
4612 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4618 _clear: function(event, noPropagation) {
4620 this.reverting = false;
4621 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4622 // everything else normalized again
4624 delayedTriggers = [];
4626 // We first have to update the dom position of the actual currentItem
4627 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4628 if(!this._noFinalSort && this.currentItem.parent().length) {
4629 this.placeholder.before(this.currentItem);
4631 this._noFinalSort = null;
4633 if(this.helper[0] === this.currentItem[0]) {
4634 for(i in this._storedCSS) {
4635 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
4636 this._storedCSS[i] = "";
4639 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4641 this.currentItem.show();
4644 if(this.fromOutside && !noPropagation) {
4645 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4647 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
4648 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4651 // Check if the items Container has Changed and trigger appropriate
4653 if (this !== this.currentContainer) {
4654 if(!noPropagation) {
4655 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4656 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4657 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4662 //Post events to containers
4663 for (i = this.containers.length - 1; i >= 0; i--){
4664 if(!noPropagation) {
4665 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4667 if(this.containers[i].containerCache.over) {
4668 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4669 this.containers[i].containerCache.over = 0;
4673 //Do what was originally in plugins
4674 if ( this.storedCursor ) {
4675 this.document.find( "body" ).css( "cursor", this.storedCursor );
4676 this.storedStylesheet.remove();
4678 if(this._storedOpacity) {
4679 this.helper.css("opacity", this._storedOpacity);
4681 if(this._storedZIndex) {
4682 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
4685 this.dragging = false;
4686 if(this.cancelHelperRemoval) {
4687 if(!noPropagation) {
4688 this._trigger("beforeStop", event, this._uiHash());
4689 for (i=0; i < delayedTriggers.length; i++) {
4690 delayedTriggers[i].call(this, event);
4691 } //Trigger all delayed events
4692 this._trigger("stop", event, this._uiHash());
4695 this.fromOutside = false;
4699 if(!noPropagation) {
4700 this._trigger("beforeStop", event, this._uiHash());
4703 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4704 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4706 if(this.helper[0] !== this.currentItem[0]) {
4707 this.helper.remove();
4711 if(!noPropagation) {
4712 for (i=0; i < delayedTriggers.length; i++) {
4713 delayedTriggers[i].call(this, event);
4714 } //Trigger all delayed events
4715 this._trigger("stop", event, this._uiHash());
4718 this.fromOutside = false;
4723 _trigger: function() {
4724 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4729 _uiHash: function(_inst) {
4730 var inst = _inst || this;
4732 helper: inst.helper,
4733 placeholder: inst.placeholder || $([]),
4734 position: inst.position,
4735 originalPosition: inst.originalPosition,
4736 offset: inst.positionAbs,
4737 item: inst.currentItem,
4738 sender: _inst ? _inst.element : null
4746 (function($, undefined) {
4748 var dataSpace = "ui-effects-";
4755 * jQuery Color Animations v2.1.2
4756 * https://github.com/jquery/jquery-color
4758 * Copyright 2013 jQuery Foundation and other contributors
4759 * Released under the MIT license.
4760 * http://jquery.org/license
4762 * Date: Wed Jan 16 08:47:09 2013 -0600
4764 (function( jQuery, undefined ) {
4766 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
4768 // plusequals test for += 100 -= 100
4769 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
4770 // a set of RE's that can match strings and generate color tuples.
4772 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4773 parse: function( execResult ) {
4782 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4783 parse: function( execResult ) {
4785 execResult[ 1 ] * 2.55,
4786 execResult[ 2 ] * 2.55,
4787 execResult[ 3 ] * 2.55,
4792 // this regex ignores A-F because it's compared against an already lowercased string
4793 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
4794 parse: function( execResult ) {
4796 parseInt( execResult[ 1 ], 16 ),
4797 parseInt( execResult[ 2 ], 16 ),
4798 parseInt( execResult[ 3 ], 16 )
4802 // this regex ignores A-F because it's compared against an already lowercased string
4803 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
4804 parse: function( execResult ) {
4806 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
4807 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
4808 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
4812 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4814 parse: function( execResult ) {
4817 execResult[ 2 ] / 100,
4818 execResult[ 3 ] / 100,
4825 color = jQuery.Color = function( color, green, blue, alpha ) {
4826 return new jQuery.Color.fn.parse( color, green, blue, alpha );
4876 support = color.support = {},
4878 // element for support tests
4879 supportElem = jQuery( "<p>" )[ 0 ],
4881 // colors = jQuery.Color.names
4884 // local aliases of functions called often
4887 // determine rgba support immediately
4888 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
4889 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
4891 // define cache name and alpha properties
4892 // for rgba and hsla spaces
4893 each( spaces, function( spaceName, space ) {
4894 space.cache = "_" + spaceName;
4895 space.props.alpha = {
4902 function clamp( value, prop, allowEmpty ) {
4903 var type = propTypes[ prop.type ] || {};
4905 if ( value == null ) {
4906 return (allowEmpty || !prop.def) ? null : prop.def;
4909 // ~~ is an short way of doing floor for positive numbers
4910 value = type.floor ? ~~value : parseFloat( value );
4912 // IE will pass in empty strings as value for alpha,
4913 // which will hit this case
4914 if ( isNaN( value ) ) {
4919 // we add mod before modding to make sure that negatives values
4920 // get converted properly: -10 -> 350
4921 return (value + type.mod) % type.mod;
4924 // for now all property types without mod have min and max
4925 return 0 > value ? 0 : type.max < value ? type.max : value;
4928 function stringParse( string ) {
4930 rgba = inst._rgba = [];
4932 string = string.toLowerCase();
4934 each( stringParsers, function( i, parser ) {
4936 match = parser.re.exec( string ),
4937 values = match && parser.parse( match ),
4938 spaceName = parser.space || "rgba";
4941 parsed = inst[ spaceName ]( values );
4943 // if this was an rgba parse the assignment might happen twice
4945 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
4946 rgba = inst._rgba = parsed._rgba;
4948 // exit each( stringParsers ) here because we matched
4953 // Found a stringParser that handled it
4954 if ( rgba.length ) {
4956 // if this came from a parsed string, force "transparent" when alpha is 0
4957 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
4958 if ( rgba.join() === "0,0,0,0" ) {
4959 jQuery.extend( rgba, colors.transparent );
4965 return colors[ string ];
4968 color.fn = jQuery.extend( color.prototype, {
4969 parse: function( red, green, blue, alpha ) {
4970 if ( red === undefined ) {
4971 this._rgba = [ null, null, null, null ];
4974 if ( red.jquery || red.nodeType ) {
4975 red = jQuery( red ).css( green );
4980 type = jQuery.type( red ),
4981 rgba = this._rgba = [];
4983 // more than 1 argument specified - assume ( red, green, blue, alpha )
4984 if ( green !== undefined ) {
4985 red = [ red, green, blue, alpha ];
4989 if ( type === "string" ) {
4990 return this.parse( stringParse( red ) || colors._default );
4993 if ( type === "array" ) {
4994 each( spaces.rgba.props, function( key, prop ) {
4995 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
5000 if ( type === "object" ) {
5001 if ( red instanceof color ) {
5002 each( spaces, function( spaceName, space ) {
5003 if ( red[ space.cache ] ) {
5004 inst[ space.cache ] = red[ space.cache ].slice();
5008 each( spaces, function( spaceName, space ) {
5009 var cache = space.cache;
5010 each( space.props, function( key, prop ) {
5012 // if the cache doesn't exist, and we know how to convert
5013 if ( !inst[ cache ] && space.to ) {
5015 // if the value was null, we don't need to copy it
5016 // if the key was alpha, we don't need to copy it either
5017 if ( key === "alpha" || red[ key ] == null ) {
5020 inst[ cache ] = space.to( inst._rgba );
5023 // this is the only case where we allow nulls for ALL properties.
5024 // call clamp with alwaysAllowEmpty
5025 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
5028 // everything defined but alpha?
5029 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
5030 // use the default of 1
5031 inst[ cache ][ 3 ] = 1;
5033 inst._rgba = space.from( inst[ cache ] );
5041 is: function( compare ) {
5042 var is = color( compare ),
5046 each( spaces, function( _, space ) {
5048 isCache = is[ space.cache ];
5050 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
5051 each( space.props, function( _, prop ) {
5052 if ( isCache[ prop.idx ] != null ) {
5053 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
5062 _space: function() {
5065 each( spaces, function( spaceName, space ) {
5066 if ( inst[ space.cache ] ) {
5067 used.push( spaceName );
5072 transition: function( other, distance ) {
5073 var end = color( other ),
5074 spaceName = end._space(),
5075 space = spaces[ spaceName ],
5076 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
5077 start = startColor[ space.cache ] || space.to( startColor._rgba ),
5078 result = start.slice();
5080 end = end[ space.cache ];
5081 each( space.props, function( key, prop ) {
5082 var index = prop.idx,
5083 startValue = start[ index ],
5084 endValue = end[ index ],
5085 type = propTypes[ prop.type ] || {};
5087 // if null, don't override start value
5088 if ( endValue === null ) {
5091 // if null - use end
5092 if ( startValue === null ) {
5093 result[ index ] = endValue;
5096 if ( endValue - startValue > type.mod / 2 ) {
5097 startValue += type.mod;
5098 } else if ( startValue - endValue > type.mod / 2 ) {
5099 startValue -= type.mod;
5102 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
5105 return this[ spaceName ]( result );
5107 blend: function( opaque ) {
5108 // if we are already opaque - return ourself
5109 if ( this._rgba[ 3 ] === 1 ) {
5113 var rgb = this._rgba.slice(),
5115 blend = color( opaque )._rgba;
5117 return color( jQuery.map( rgb, function( v, i ) {
5118 return ( 1 - a ) * blend[ i ] + a * v;
5121 toRgbaString: function() {
5122 var prefix = "rgba(",
5123 rgba = jQuery.map( this._rgba, function( v, i ) {
5124 return v == null ? ( i > 2 ? 1 : 0 ) : v;
5127 if ( rgba[ 3 ] === 1 ) {
5132 return prefix + rgba.join() + ")";
5134 toHslaString: function() {
5135 var prefix = "hsla(",
5136 hsla = jQuery.map( this.hsla(), function( v, i ) {
5143 v = Math.round( v * 100 ) + "%";
5148 if ( hsla[ 3 ] === 1 ) {
5152 return prefix + hsla.join() + ")";
5154 toHexString: function( includeAlpha ) {
5155 var rgba = this._rgba.slice(),
5158 if ( includeAlpha ) {
5159 rgba.push( ~~( alpha * 255 ) );
5162 return "#" + jQuery.map( rgba, function( v ) {
5164 // default to 0 when nulls exist
5165 v = ( v || 0 ).toString( 16 );
5166 return v.length === 1 ? "0" + v : v;
5169 toString: function() {
5170 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
5173 color.fn.parse.prototype = color.fn;
5175 // hsla conversions adapted from:
5176 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
5178 function hue2rgb( p, q, h ) {
5181 return p + (q - p) * h * 6;
5187 return p + (q - p) * ((2/3) - h) * 6;
5192 spaces.hsla.to = function ( rgba ) {
5193 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
5194 return [ null, null, null, rgba[ 3 ] ];
5196 var r = rgba[ 0 ] / 255,
5197 g = rgba[ 1 ] / 255,
5198 b = rgba[ 2 ] / 255,
5200 max = Math.max( r, g, b ),
5201 min = Math.min( r, g, b ),
5207 if ( min === max ) {
5209 } else if ( r === max ) {
5210 h = ( 60 * ( g - b ) / diff ) + 360;
5211 } else if ( g === max ) {
5212 h = ( 60 * ( b - r ) / diff ) + 120;
5214 h = ( 60 * ( r - g ) / diff ) + 240;
5217 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
5218 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
5221 } else if ( l <= 0.5 ) {
5224 s = diff / ( 2 - add );
5226 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
5229 spaces.hsla.from = function ( hsla ) {
5230 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
5231 return [ null, null, null, hsla[ 3 ] ];
5233 var h = hsla[ 0 ] / 360,
5237 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
5241 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
5242 Math.round( hue2rgb( p, q, h ) * 255 ),
5243 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
5249 each( spaces, function( spaceName, space ) {
5250 var props = space.props,
5251 cache = space.cache,
5255 // makes rgba() and hsla()
5256 color.fn[ spaceName ] = function( value ) {
5258 // generate a cache for this space if it doesn't exist
5259 if ( to && !this[ cache ] ) {
5260 this[ cache ] = to( this._rgba );
5262 if ( value === undefined ) {
5263 return this[ cache ].slice();
5267 type = jQuery.type( value ),
5268 arr = ( type === "array" || type === "object" ) ? value : arguments,
5269 local = this[ cache ].slice();
5271 each( props, function( key, prop ) {
5272 var val = arr[ type === "object" ? key : prop.idx ];
5273 if ( val == null ) {
5274 val = local[ prop.idx ];
5276 local[ prop.idx ] = clamp( val, prop );
5280 ret = color( from( local ) );
5281 ret[ cache ] = local;
5284 return color( local );
5288 // makes red() green() blue() alpha() hue() saturation() lightness()
5289 each( props, function( key, prop ) {
5290 // alpha is included in more than one space
5291 if ( color.fn[ key ] ) {
5294 color.fn[ key ] = function( value ) {
5295 var vtype = jQuery.type( value ),
5296 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
5297 local = this[ fn ](),
5298 cur = local[ prop.idx ],
5301 if ( vtype === "undefined" ) {
5305 if ( vtype === "function" ) {
5306 value = value.call( this, cur );
5307 vtype = jQuery.type( value );
5309 if ( value == null && prop.empty ) {
5312 if ( vtype === "string" ) {
5313 match = rplusequals.exec( value );
5315 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
5318 local[ prop.idx ] = value;
5319 return this[ fn ]( local );
5324 // add cssHook and .fx.step function for each named hook.
5325 // accept a space separated string of properties
5326 color.hook = function( hook ) {
5327 var hooks = hook.split( " " );
5328 each( hooks, function( i, hook ) {
5329 jQuery.cssHooks[ hook ] = {
5330 set: function( elem, value ) {
5331 var parsed, curElem,
5332 backgroundColor = "";
5334 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
5335 value = color( parsed || value );
5336 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
5337 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
5339 (backgroundColor === "" || backgroundColor === "transparent") &&
5340 curElem && curElem.style
5343 backgroundColor = jQuery.css( curElem, "backgroundColor" );
5344 curElem = curElem.parentNode;
5349 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
5354 value = value.toRgbaString();
5357 elem.style[ hook ] = value;
5359 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
5363 jQuery.fx.step[ hook ] = function( fx ) {
5364 if ( !fx.colorInit ) {
5365 fx.start = color( fx.elem, hook );
5366 fx.end = color( fx.end );
5367 fx.colorInit = true;
5369 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
5375 color.hook( stepHooks );
5377 jQuery.cssHooks.borderColor = {
5378 expand: function( value ) {
5381 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
5382 expanded[ "border" + part + "Color" ] = value;
5388 // Basic color names only.
5389 // Usage of any of the other color names requires adding yourself or including
5390 // jquery.color.svg-names.js.
5391 colors = jQuery.Color.names = {
5392 // 4.1. Basic color keywords
5410 // 4.2.3. "transparent" color keyword
5411 transparent: [ null, null, null, 0 ],
5419 /******************************************************************************/
5420 /****************************** CLASS ANIMATIONS ******************************/
5421 /******************************************************************************/
5424 var classAnimationActions = [ "add", "remove", "toggle" ],
5437 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
5438 $.fx.step[ prop ] = function( fx ) {
5439 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
5440 jQuery.style( fx.elem, prop, fx.end );
5446 function getElementStyles( elem ) {
5448 style = elem.ownerDocument.defaultView ?
5449 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
5453 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
5457 if ( typeof style[ key ] === "string" ) {
5458 styles[ $.camelCase( key ) ] = style[ key ];
5461 // support: Opera, IE <9
5463 for ( key in style ) {
5464 if ( typeof style[ key ] === "string" ) {
5465 styles[ key ] = style[ key ];
5474 function styleDifference( oldStyle, newStyle ) {
5478 for ( name in newStyle ) {
5479 value = newStyle[ name ];
5480 if ( oldStyle[ name ] !== value ) {
5481 if ( !shorthandStyles[ name ] ) {
5482 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
5483 diff[ name ] = value;
5492 // support: jQuery <1.8
5493 if ( !$.fn.addBack ) {
5494 $.fn.addBack = function( selector ) {
5495 return this.add( selector == null ?
5496 this.prevObject : this.prevObject.filter( selector )
5501 $.effects.animateClass = function( value, duration, easing, callback ) {
5502 var o = $.speed( duration, easing, callback );
5504 return this.queue( function() {
5505 var animated = $( this ),
5506 baseClass = animated.attr( "class" ) || "",
5508 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
5510 // map the animated objects to store the original styles.
5511 allAnimations = allAnimations.map(function() {
5515 start: getElementStyles( this )
5519 // apply class change
5520 applyClassChange = function() {
5521 $.each( classAnimationActions, function(i, action) {
5522 if ( value[ action ] ) {
5523 animated[ action + "Class" ]( value[ action ] );
5529 // map all animated objects again - calculate new styles and diff
5530 allAnimations = allAnimations.map(function() {
5531 this.end = getElementStyles( this.el[ 0 ] );
5532 this.diff = styleDifference( this.start, this.end );
5536 // apply original class
5537 animated.attr( "class", baseClass );
5539 // map all animated objects again - this time collecting a promise
5540 allAnimations = allAnimations.map(function() {
5541 var styleInfo = this,
5543 opts = $.extend({}, o, {
5545 complete: function() {
5546 dfd.resolve( styleInfo );
5550 this.el.animate( this.diff, opts );
5551 return dfd.promise();
5554 // once all animations have completed:
5555 $.when.apply( $, allAnimations.get() ).done(function() {
5557 // set the final class
5560 // for each animated element,
5561 // clear all css properties that were animated
5562 $.each( arguments, function() {
5564 $.each( this.diff, function(key) {
5569 // this is guarnteed to be there if you use jQuery.speed()
5570 // it also handles dequeuing the next anim...
5571 o.complete.call( animated[ 0 ] );
5577 addClass: (function( orig ) {
5578 return function( classNames, speed, easing, callback ) {
5580 $.effects.animateClass.call( this,
5581 { add: classNames }, speed, easing, callback ) :
5582 orig.apply( this, arguments );
5584 })( $.fn.addClass ),
5586 removeClass: (function( orig ) {
5587 return function( classNames, speed, easing, callback ) {
5588 return arguments.length > 1 ?
5589 $.effects.animateClass.call( this,
5590 { remove: classNames }, speed, easing, callback ) :
5591 orig.apply( this, arguments );
5593 })( $.fn.removeClass ),
5595 toggleClass: (function( orig ) {
5596 return function( classNames, force, speed, easing, callback ) {
5597 if ( typeof force === "boolean" || force === undefined ) {
5599 // without speed parameter
5600 return orig.apply( this, arguments );
5602 return $.effects.animateClass.call( this,
5603 (force ? { add: classNames } : { remove: classNames }),
5604 speed, easing, callback );
5607 // without force parameter
5608 return $.effects.animateClass.call( this,
5609 { toggle: classNames }, force, speed, easing );
5612 })( $.fn.toggleClass ),
5614 switchClass: function( remove, add, speed, easing, callback) {
5615 return $.effects.animateClass.call( this, {
5618 }, speed, easing, callback );
5624 /******************************************************************************/
5625 /*********************************** EFFECTS **********************************/
5626 /******************************************************************************/
5630 $.extend( $.effects, {
5633 // Saves a set of properties in a data storage
5634 save: function( element, set ) {
5635 for( var i=0; i < set.length; i++ ) {
5636 if ( set[ i ] !== null ) {
5637 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
5642 // Restores a set of previously saved properties from a data storage
5643 restore: function( element, set ) {
5645 for( i=0; i < set.length; i++ ) {
5646 if ( set[ i ] !== null ) {
5647 val = element.data( dataSpace + set[ i ] );
5648 // support: jQuery 1.6.2
5649 // http://bugs.jquery.com/ticket/9917
5650 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
5651 // We can't differentiate between "" and 0 here, so we just assume
5652 // empty string since it's likely to be a more common value...
5653 if ( val === undefined ) {
5656 element.css( set[ i ], val );
5661 setMode: function( el, mode ) {
5662 if (mode === "toggle") {
5663 mode = el.is( ":hidden" ) ? "show" : "hide";
5668 // Translates a [top,left] array into a baseline value
5669 // this should be a little more flexible in the future to handle a string & hash
5670 getBaseline: function( origin, original ) {
5672 switch ( origin[ 0 ] ) {
5673 case "top": y = 0; break;
5674 case "middle": y = 0.5; break;
5675 case "bottom": y = 1; break;
5676 default: y = origin[ 0 ] / original.height;
5678 switch ( origin[ 1 ] ) {
5679 case "left": x = 0; break;
5680 case "center": x = 0.5; break;
5681 case "right": x = 1; break;
5682 default: x = origin[ 1 ] / original.width;
5690 // Wraps the element around a wrapper that copies position properties
5691 createWrapper: function( element ) {
5693 // if the element is already wrapped, return it
5694 if ( element.parent().is( ".ui-effects-wrapper" )) {
5695 return element.parent();
5700 width: element.outerWidth(true),
5701 height: element.outerHeight(true),
5702 "float": element.css( "float" )
5704 wrapper = $( "<div></div>" )
5705 .addClass( "ui-effects-wrapper" )
5708 background: "transparent",
5713 // Store the size in case width/height are defined in % - Fixes #5245
5715 width: element.width(),
5716 height: element.height()
5718 active = document.activeElement;
5721 // Firefox incorrectly exposes anonymous content
5722 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
5726 active = document.body;
5729 element.wrap( wrapper );
5731 // Fixes #7595 - Elements lose focus when wrapped.
5732 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5733 $( active ).focus();
5736 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
5738 // transfer positioning properties to the wrapper
5739 if ( element.css( "position" ) === "static" ) {
5740 wrapper.css({ position: "relative" });
5741 element.css({ position: "relative" });
5744 position: element.css( "position" ),
5745 zIndex: element.css( "z-index" )
5747 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
5748 props[ pos ] = element.css( pos );
5749 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
5750 props[ pos ] = "auto";
5754 position: "relative",
5763 return wrapper.css( props ).show();
5766 removeWrapper: function( element ) {
5767 var active = document.activeElement;
5769 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
5770 element.parent().replaceWith( element );
5772 // Fixes #7595 - Elements lose focus when wrapped.
5773 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5774 $( active ).focus();
5782 setTransition: function( element, list, factor, value ) {
5783 value = value || {};
5784 $.each( list, function( i, x ) {
5785 var unit = element.cssUnit( x );
5786 if ( unit[ 0 ] > 0 ) {
5787 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
5794 // return an effect options object for the given parameters:
5795 function _normalizeArguments( effect, options, speed, callback ) {
5797 // allow passing all options as the first parameter
5798 if ( $.isPlainObject( effect ) ) {
5800 effect = effect.effect;
5803 // convert to an object
5804 effect = { effect: effect };
5806 // catch (effect, null, ...)
5807 if ( options == null ) {
5811 // catch (effect, callback)
5812 if ( $.isFunction( options ) ) {
5818 // catch (effect, speed, ?)
5819 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
5825 // catch (effect, options, callback)
5826 if ( $.isFunction( speed ) ) {
5831 // add options to effect
5833 $.extend( effect, options );
5836 speed = speed || options.duration;
5837 effect.duration = $.fx.off ? 0 :
5838 typeof speed === "number" ? speed :
5839 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
5840 $.fx.speeds._default;
5842 effect.complete = callback || options.complete;
5847 function standardAnimationOption( option ) {
5848 // Valid standard speeds (nothing, number, named speed)
5849 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
5853 // Invalid strings - treat as "normal" speed
5854 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
5858 // Complete callback
5859 if ( $.isFunction( option ) ) {
5863 // Options hash (but not naming an effect)
5864 if ( typeof option === "object" && !option.effect ) {
5868 // Didn't match any standard API
5873 effect: function( /* effect, options, speed, callback */ ) {
5874 var args = _normalizeArguments.apply( this, arguments ),
5877 effectMethod = $.effects.effect[ args.effect ];
5879 if ( $.fx.off || !effectMethod ) {
5880 // delegate to the original method (e.g., .show()) if possible
5882 return this[ mode ]( args.duration, args.complete );
5884 return this.each( function() {
5885 if ( args.complete ) {
5886 args.complete.call( this );
5892 function run( next ) {
5893 var elem = $( this ),
5894 complete = args.complete,
5898 if ( $.isFunction( complete ) ) {
5899 complete.call( elem[0] );
5901 if ( $.isFunction( next ) ) {
5906 // If the element already has the correct final state, delegate to
5907 // the core methods so the internal tracking of "olddisplay" works.
5908 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
5912 effectMethod.call( elem[0], args, done );
5916 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
5919 show: (function( orig ) {
5920 return function( option ) {
5921 if ( standardAnimationOption( option ) ) {
5922 return orig.apply( this, arguments );
5924 var args = _normalizeArguments.apply( this, arguments );
5926 return this.effect.call( this, args );
5931 hide: (function( orig ) {
5932 return function( option ) {
5933 if ( standardAnimationOption( option ) ) {
5934 return orig.apply( this, arguments );
5936 var args = _normalizeArguments.apply( this, arguments );
5938 return this.effect.call( this, args );
5943 toggle: (function( orig ) {
5944 return function( option ) {
5945 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
5946 return orig.apply( this, arguments );
5948 var args = _normalizeArguments.apply( this, arguments );
5949 args.mode = "toggle";
5950 return this.effect.call( this, args );
5956 cssUnit: function(key) {
5957 var style = this.css( key ),
5960 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
5961 if ( style.indexOf( unit ) > 0 ) {
5962 val = [ parseFloat( style ), unit ];
5971 /******************************************************************************/
5972 /*********************************** EASING ***********************************/
5973 /******************************************************************************/
5977 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
5979 var baseEasings = {};
5981 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
5982 baseEasings[ name ] = function( p ) {
5983 return Math.pow( p, i + 2 );
5987 $.extend( baseEasings, {
5988 Sine: function ( p ) {
5989 return 1 - Math.cos( p * Math.PI / 2 );
5991 Circ: function ( p ) {
5992 return 1 - Math.sqrt( 1 - p * p );
5994 Elastic: function( p ) {
5995 return p === 0 || p === 1 ? p :
5996 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
5998 Back: function( p ) {
5999 return p * p * ( 3 * p - 2 );
6001 Bounce: function ( p ) {
6005 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
6006 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
6010 $.each( baseEasings, function( name, easeIn ) {
6011 $.easing[ "easeIn" + name ] = easeIn;
6012 $.easing[ "easeOut" + name ] = function( p ) {
6013 return 1 - easeIn( 1 - p );
6015 $.easing[ "easeInOut" + name ] = function( p ) {
6017 easeIn( p * 2 ) / 2 :
6018 1 - easeIn( p * -2 + 2 ) / 2;
6026 (function( $, undefined ) {
6032 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
6033 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
6034 showProps.height = showProps.paddingTop = showProps.paddingBottom =
6035 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
6037 $.widget( "ui.accordion", {
6044 header: "> li > :first-child,> :not(li):even",
6045 heightStyle: "auto",
6047 activeHeader: "ui-icon-triangle-1-s",
6048 header: "ui-icon-triangle-1-e"
6053 beforeActivate: null
6056 _create: function() {
6057 var options = this.options;
6058 this.prevShow = this.prevHide = $();
6059 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
6061 .attr( "role", "tablist" );
6063 // don't allow collapsible: false and active: false / null
6064 if ( !options.collapsible && (options.active === false || options.active == null) ) {
6068 this._processPanels();
6069 // handle negative values
6070 if ( options.active < 0 ) {
6071 options.active += this.headers.length;
6076 _getCreateEventData: function() {
6078 header: this.active,
6079 panel: !this.active.length ? $() : this.active.next(),
6080 content: !this.active.length ? $() : this.active.next()
6084 _createIcons: function() {
6085 var icons = this.options.icons;
6088 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
6089 .prependTo( this.headers );
6090 this.active.children( ".ui-accordion-header-icon" )
6091 .removeClass( icons.header )
6092 .addClass( icons.activeHeader );
6093 this.headers.addClass( "ui-accordion-icons" );
6097 _destroyIcons: function() {
6099 .removeClass( "ui-accordion-icons" )
6100 .children( ".ui-accordion-header-icon" )
6104 _destroy: function() {
6107 // clean up main element
6109 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
6110 .removeAttr( "role" );
6114 .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" )
6115 .removeAttr( "role" )
6116 .removeAttr( "aria-selected" )
6117 .removeAttr( "aria-controls" )
6118 .removeAttr( "tabIndex" )
6120 if ( /^ui-accordion/.test( this.id ) ) {
6121 this.removeAttribute( "id" );
6124 this._destroyIcons();
6126 // clean up content panels
6127 contents = this.headers.next()
6128 .css( "display", "" )
6129 .removeAttr( "role" )
6130 .removeAttr( "aria-expanded" )
6131 .removeAttr( "aria-hidden" )
6132 .removeAttr( "aria-labelledby" )
6133 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
6135 if ( /^ui-accordion/.test( this.id ) ) {
6136 this.removeAttribute( "id" );
6139 if ( this.options.heightStyle !== "content" ) {
6140 contents.css( "height", "" );
6144 _setOption: function( key, value ) {
6145 if ( key === "active" ) {
6146 // _activate() will handle invalid values and update this.options
6147 this._activate( value );
6151 if ( key === "event" ) {
6152 if ( this.options.event ) {
6153 this._off( this.headers, this.options.event );
6155 this._setupEvents( value );
6158 this._super( key, value );
6160 // setting collapsible: false while collapsed; open first panel
6161 if ( key === "collapsible" && !value && this.options.active === false ) {
6162 this._activate( 0 );
6165 if ( key === "icons" ) {
6166 this._destroyIcons();
6168 this._createIcons();
6172 // #5332 - opacity doesn't cascade to positioned elements in IE
6173 // so we need to add the disabled class to the headers and panels
6174 if ( key === "disabled" ) {
6175 this.headers.add( this.headers.next() )
6176 .toggleClass( "ui-state-disabled", !!value );
6180 _keydown: function( event ) {
6181 /*jshint maxcomplexity:15*/
6182 if ( event.altKey || event.ctrlKey ) {
6186 var keyCode = $.ui.keyCode,
6187 length = this.headers.length,
6188 currentIndex = this.headers.index( event.target ),
6191 switch ( event.keyCode ) {
6194 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
6198 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
6202 this._eventHandler( event );
6205 toFocus = this.headers[ 0 ];
6208 toFocus = this.headers[ length - 1 ];
6213 $( event.target ).attr( "tabIndex", -1 );
6214 $( toFocus ).attr( "tabIndex", 0 );
6216 event.preventDefault();
6220 _panelKeyDown : function( event ) {
6221 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
6222 $( event.currentTarget ).prev().focus();
6226 refresh: function() {
6227 var options = this.options;
6228 this._processPanels();
6230 // was collapsed or no panel
6231 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
6232 options.active = false;
6234 // active false only when collapsible is true
6235 } if ( options.active === false ) {
6236 this._activate( 0 );
6237 // was active, but active panel is gone
6238 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
6239 // all remaining panel are disabled
6240 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
6241 options.active = false;
6243 // activate previous panel
6245 this._activate( Math.max( 0, options.active - 1 ) );
6247 // was active, active panel still exists
6249 // make sure active index is correct
6250 options.active = this.headers.index( this.active );
6253 this._destroyIcons();
6258 _processPanels: function() {
6259 this.headers = this.element.find( this.options.header )
6260 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
6263 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
6264 .filter(":not(.ui-accordion-content-active)")
6268 _refresh: function() {
6270 options = this.options,
6271 heightStyle = options.heightStyle,
6272 parent = this.element.parent(),
6273 accordionId = this.accordionId = "ui-accordion-" +
6274 (this.element.attr( "id" ) || ++uid);
6276 this.active = this._findActive( options.active )
6277 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
6278 .removeClass( "ui-corner-all" );
6280 .addClass( "ui-accordion-content-active" )
6284 .attr( "role", "tab" )
6285 .each(function( i ) {
6286 var header = $( this ),
6287 headerId = header.attr( "id" ),
6288 panel = header.next(),
6289 panelId = panel.attr( "id" );
6291 headerId = accordionId + "-header-" + i;
6292 header.attr( "id", headerId );
6295 panelId = accordionId + "-panel-" + i;
6296 panel.attr( "id", panelId );
6298 header.attr( "aria-controls", panelId );
6299 panel.attr( "aria-labelledby", headerId );
6302 .attr( "role", "tabpanel" );
6307 "aria-selected": "false",
6312 "aria-expanded": "false",
6313 "aria-hidden": "true"
6317 // make sure at least one header is in the tab order
6318 if ( !this.active.length ) {
6319 this.headers.eq( 0 ).attr( "tabIndex", 0 );
6322 "aria-selected": "true",
6327 "aria-expanded": "true",
6328 "aria-hidden": "false"
6332 this._createIcons();
6334 this._setupEvents( options.event );
6336 if ( heightStyle === "fill" ) {
6337 maxHeight = parent.height();
6338 this.element.siblings( ":visible" ).each(function() {
6339 var elem = $( this ),
6340 position = elem.css( "position" );
6342 if ( position === "absolute" || position === "fixed" ) {
6345 maxHeight -= elem.outerHeight( true );
6348 this.headers.each(function() {
6349 maxHeight -= $( this ).outerHeight( true );
6354 $( this ).height( Math.max( 0, maxHeight -
6355 $( this ).innerHeight() + $( this ).height() ) );
6357 .css( "overflow", "auto" );
6358 } else if ( heightStyle === "auto" ) {
6362 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
6364 .height( maxHeight );
6368 _activate: function( index ) {
6369 var active = this._findActive( index )[ 0 ];
6371 // trying to activate the already active panel
6372 if ( active === this.active[ 0 ] ) {
6376 // trying to collapse, simulate a click on the currently active header
6377 active = active || this.active[ 0 ];
6379 this._eventHandler({
6381 currentTarget: active,
6382 preventDefault: $.noop
6386 _findActive: function( selector ) {
6387 return typeof selector === "number" ? this.headers.eq( selector ) : $();
6390 _setupEvents: function( event ) {
6395 $.each( event.split(" "), function( index, eventName ) {
6396 events[ eventName ] = "_eventHandler";
6400 this._off( this.headers.add( this.headers.next() ) );
6401 this._on( this.headers, events );
6402 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
6403 this._hoverable( this.headers );
6404 this._focusable( this.headers );
6407 _eventHandler: function( event ) {
6408 var options = this.options,
6409 active = this.active,
6410 clicked = $( event.currentTarget ),
6411 clickedIsActive = clicked[ 0 ] === active[ 0 ],
6412 collapsing = clickedIsActive && options.collapsible,
6413 toShow = collapsing ? $() : clicked.next(),
6414 toHide = active.next(),
6418 newHeader: collapsing ? $() : clicked,
6422 event.preventDefault();
6425 // click on active header, but not collapsible
6426 ( clickedIsActive && !options.collapsible ) ||
6427 // allow canceling activation
6428 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
6432 options.active = collapsing ? false : this.headers.index( clicked );
6434 // when the call to ._toggle() comes after the class changes
6435 // it causes a very odd bug in IE 8 (see #6720)
6436 this.active = clickedIsActive ? $() : clicked;
6437 this._toggle( eventData );
6440 // corner classes on the previously active header stay after the animation
6441 active.removeClass( "ui-accordion-header-active ui-state-active" );
6442 if ( options.icons ) {
6443 active.children( ".ui-accordion-header-icon" )
6444 .removeClass( options.icons.activeHeader )
6445 .addClass( options.icons.header );
6448 if ( !clickedIsActive ) {
6450 .removeClass( "ui-corner-all" )
6451 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
6452 if ( options.icons ) {
6453 clicked.children( ".ui-accordion-header-icon" )
6454 .removeClass( options.icons.header )
6455 .addClass( options.icons.activeHeader );
6460 .addClass( "ui-accordion-content-active" );
6464 _toggle: function( data ) {
6465 var toShow = data.newPanel,
6466 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
6468 // handle activating a panel during the animation for another activation
6469 this.prevShow.add( this.prevHide ).stop( true, true );
6470 this.prevShow = toShow;
6471 this.prevHide = toHide;
6473 if ( this.options.animate ) {
6474 this._animate( toShow, toHide, data );
6478 this._toggleComplete( data );
6482 "aria-expanded": "false",
6483 "aria-hidden": "true"
6485 toHide.prev().attr( "aria-selected", "false" );
6486 // if we're switching panels, remove the old header from the tab order
6487 // if we're opening from collapsed state, remove the previous header from the tab order
6488 // if we're collapsing, then keep the collapsing header in the tab order
6489 if ( toShow.length && toHide.length ) {
6490 toHide.prev().attr( "tabIndex", -1 );
6491 } else if ( toShow.length ) {
6492 this.headers.filter(function() {
6493 return $( this ).attr( "tabIndex" ) === 0;
6495 .attr( "tabIndex", -1 );
6500 "aria-expanded": "true",
6501 "aria-hidden": "false"
6505 "aria-selected": "true",
6510 _animate: function( toShow, toHide, data ) {
6511 var total, easing, duration,
6514 down = toShow.length &&
6515 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
6516 animate = this.options.animate || {},
6517 options = down && animate.down || animate,
6518 complete = function() {
6519 that._toggleComplete( data );
6522 if ( typeof options === "number" ) {
6525 if ( typeof options === "string" ) {
6528 // fall back from options to animation in case of partial down settings
6529 easing = easing || options.easing || animate.easing;
6530 duration = duration || options.duration || animate.duration;
6532 if ( !toHide.length ) {
6533 return toShow.animate( showProps, duration, easing, complete );
6535 if ( !toShow.length ) {
6536 return toHide.animate( hideProps, duration, easing, complete );
6539 total = toShow.show().outerHeight();
6540 toHide.animate( hideProps, {
6543 step: function( now, fx ) {
6544 fx.now = Math.round( now );
6549 .animate( showProps, {
6553 step: function( now, fx ) {
6554 fx.now = Math.round( now );
6555 if ( fx.prop !== "height" ) {
6557 } else if ( that.options.heightStyle !== "content" ) {
6558 fx.now = Math.round( total - toHide.outerHeight() - adjust );
6565 _toggleComplete: function( data ) {
6566 var toHide = data.oldPanel;
6569 .removeClass( "ui-accordion-content-active" )
6571 .removeClass( "ui-corner-top" )
6572 .addClass( "ui-corner-all" );
6574 // Work around for rendering bug in IE (#5421)
6575 if ( toHide.length ) {
6576 toHide.parent()[0].className = toHide.parent()[0].className;
6579 this._trigger( "activate", null, data );
6585 (function( $, undefined ) {
6587 // used to prevent race conditions with remote data sources
6588 var requestIndex = 0;
6590 $.widget( "ui.autocomplete", {
6592 defaultElement: "<input>",
6617 _create: function() {
6618 // Some browsers only repeat keydown events, not keypress events,
6619 // so we use the suppressKeyPress flag to determine if we've already
6620 // handled the keydown event. #7269
6621 // Unfortunately the code for & in keypress is the same as the up arrow,
6622 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
6623 // events when we know the keydown event was used to modify the
6624 // search term. #7799
6625 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
6626 nodeName = this.element[0].nodeName.toLowerCase(),
6627 isTextarea = nodeName === "textarea",
6628 isInput = nodeName === "input";
6631 // Textareas are always multi-line
6633 // Inputs are always single-line, even if inside a contentEditable element
6634 // IE also treats inputs as contentEditable
6636 // All other element types are determined by whether or not they're contentEditable
6637 this.element.prop( "isContentEditable" );
6639 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
6640 this.isNewMenu = true;
6643 .addClass( "ui-autocomplete-input" )
6644 .attr( "autocomplete", "off" );
6646 this._on( this.element, {
6647 keydown: function( event ) {
6648 /*jshint maxcomplexity:15*/
6649 if ( this.element.prop( "readOnly" ) ) {
6650 suppressKeyPress = true;
6651 suppressInput = true;
6652 suppressKeyPressRepeat = true;
6656 suppressKeyPress = false;
6657 suppressInput = false;
6658 suppressKeyPressRepeat = false;
6659 var keyCode = $.ui.keyCode;
6660 switch( event.keyCode ) {
6661 case keyCode.PAGE_UP:
6662 suppressKeyPress = true;
6663 this._move( "previousPage", event );
6665 case keyCode.PAGE_DOWN:
6666 suppressKeyPress = true;
6667 this._move( "nextPage", event );
6670 suppressKeyPress = true;
6671 this._keyEvent( "previous", event );
6674 suppressKeyPress = true;
6675 this._keyEvent( "next", event );
6678 case keyCode.NUMPAD_ENTER:
6679 // when menu is open and has focus
6680 if ( this.menu.active ) {
6681 // #6055 - Opera still allows the keypress to occur
6682 // which causes forms to submit
6683 suppressKeyPress = true;
6684 event.preventDefault();
6685 this.menu.select( event );
6689 if ( this.menu.active ) {
6690 this.menu.select( event );
6693 case keyCode.ESCAPE:
6694 if ( this.menu.element.is( ":visible" ) ) {
6695 this._value( this.term );
6696 this.close( event );
6697 // Different browsers have different default behavior for escape
6698 // Single press can mean undo or clear
6699 // Double press in IE means clear the whole form
6700 event.preventDefault();
6704 suppressKeyPressRepeat = true;
6705 // search timeout should be triggered before the input value is changed
6706 this._searchTimeout( event );
6710 keypress: function( event ) {
6711 if ( suppressKeyPress ) {
6712 suppressKeyPress = false;
6713 event.preventDefault();
6716 if ( suppressKeyPressRepeat ) {
6720 // replicate some key handlers to allow them to repeat in Firefox and Opera
6721 var keyCode = $.ui.keyCode;
6722 switch( event.keyCode ) {
6723 case keyCode.PAGE_UP:
6724 this._move( "previousPage", event );
6726 case keyCode.PAGE_DOWN:
6727 this._move( "nextPage", event );
6730 this._keyEvent( "previous", event );
6733 this._keyEvent( "next", event );
6737 input: function( event ) {
6738 if ( suppressInput ) {
6739 suppressInput = false;
6740 event.preventDefault();
6743 this._searchTimeout( event );
6746 this.selectedItem = null;
6747 this.previous = this._value();
6749 blur: function( event ) {
6750 if ( this.cancelBlur ) {
6751 delete this.cancelBlur;
6755 clearTimeout( this.searching );
6756 this.close( event );
6757 this._change( event );
6762 this.menu = $( "<ul>" )
6763 .addClass( "ui-autocomplete ui-front" )
6764 .appendTo( this._appendTo() )
6766 // custom key handling for now
6768 // disable ARIA support, the live region takes care of that
6774 this._on( this.menu.element, {
6775 mousedown: function( event ) {
6776 // prevent moving focus out of the text field
6777 event.preventDefault();
6779 // IE doesn't prevent moving focus even with event.preventDefault()
6780 // so we set a flag to know when we should ignore the blur event
6781 this.cancelBlur = true;
6782 this._delay(function() {
6783 delete this.cancelBlur;
6786 // clicking on the scrollbar causes focus to shift to the body
6787 // but we can't detect a mouseup or a click immediately afterward
6788 // so we have to track the next mousedown and close the menu if
6789 // the user clicks somewhere outside of the autocomplete
6790 var menuElement = this.menu.element[ 0 ];
6791 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
6792 this._delay(function() {
6794 this.document.one( "mousedown", function( event ) {
6795 if ( event.target !== that.element[ 0 ] &&
6796 event.target !== menuElement &&
6797 !$.contains( menuElement, event.target ) ) {
6804 menufocus: function( event, ui ) {
6806 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
6807 if ( this.isNewMenu ) {
6808 this.isNewMenu = false;
6809 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
6812 this.document.one( "mousemove", function() {
6813 $( event.target ).trigger( event.originalEvent );
6820 var item = ui.item.data( "ui-autocomplete-item" );
6821 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
6822 // use value to match what will end up in the input, if it was a key event
6823 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
6824 this._value( item.value );
6827 // Normally the input is populated with the item's value as the
6828 // menu is navigated, causing screen readers to notice a change and
6829 // announce the item. Since the focus event was canceled, this doesn't
6830 // happen, so we update the live region so that screen readers can
6831 // still notice the change and announce it.
6832 this.liveRegion.text( item.value );
6835 menuselect: function( event, ui ) {
6836 var item = ui.item.data( "ui-autocomplete-item" ),
6837 previous = this.previous;
6839 // only trigger when focus was lost (click on menu)
6840 if ( this.element[0] !== this.document[0].activeElement ) {
6841 this.element.focus();
6842 this.previous = previous;
6843 // #6109 - IE triggers two focus events and the second
6844 // is asynchronous, so we need to reset the previous
6845 // term synchronously and asynchronously :-(
6846 this._delay(function() {
6847 this.previous = previous;
6848 this.selectedItem = item;
6852 if ( false !== this._trigger( "select", event, { item: item } ) ) {
6853 this._value( item.value );
6855 // reset the term after the select event
6856 // this allows custom select handling to work properly
6857 this.term = this._value();
6859 this.close( event );
6860 this.selectedItem = item;
6864 this.liveRegion = $( "<span>", {
6866 "aria-live": "polite"
6868 .addClass( "ui-helper-hidden-accessible" )
6869 .insertAfter( this.element );
6871 // turning off autocomplete prevents the browser from remembering the
6872 // value when navigating through history, so we re-enable autocomplete
6873 // if the page is unloaded before the widget is destroyed. #7790
6874 this._on( this.window, {
6875 beforeunload: function() {
6876 this.element.removeAttr( "autocomplete" );
6881 _destroy: function() {
6882 clearTimeout( this.searching );
6884 .removeClass( "ui-autocomplete-input" )
6885 .removeAttr( "autocomplete" );
6886 this.menu.element.remove();
6887 this.liveRegion.remove();
6890 _setOption: function( key, value ) {
6891 this._super( key, value );
6892 if ( key === "source" ) {
6895 if ( key === "appendTo" ) {
6896 this.menu.element.appendTo( this._appendTo() );
6898 if ( key === "disabled" && value && this.xhr ) {
6903 _appendTo: function() {
6904 var element = this.options.appendTo;
6907 element = element.jquery || element.nodeType ?
6909 this.document.find( element ).eq( 0 );
6913 element = this.element.closest( ".ui-front" );
6916 if ( !element.length ) {
6917 element = this.document[0].body;
6923 _initSource: function() {
6926 if ( $.isArray(this.options.source) ) {
6927 array = this.options.source;
6928 this.source = function( request, response ) {
6929 response( $.ui.autocomplete.filter( array, request.term ) );
6931 } else if ( typeof this.options.source === "string" ) {
6932 url = this.options.source;
6933 this.source = function( request, response ) {
6941 success: function( data ) {
6950 this.source = this.options.source;
6954 _searchTimeout: function( event ) {
6955 clearTimeout( this.searching );
6956 this.searching = this._delay(function() {
6957 // only search if the value has changed
6958 if ( this.term !== this._value() ) {
6959 this.selectedItem = null;
6960 this.search( null, event );
6962 }, this.options.delay );
6965 search: function( value, event ) {
6966 value = value != null ? value : this._value();
6968 // always save the actual value, not the one passed as an argument
6969 this.term = this._value();
6971 if ( value.length < this.options.minLength ) {
6972 return this.close( event );
6975 if ( this._trigger( "search", event ) === false ) {
6979 return this._search( value );
6982 _search: function( value ) {
6984 this.element.addClass( "ui-autocomplete-loading" );
6985 this.cancelSearch = false;
6987 this.source( { term: value }, this._response() );
6990 _response: function() {
6992 index = ++requestIndex;
6994 return function( content ) {
6995 if ( index === requestIndex ) {
6996 that.__response( content );
7000 if ( !that.pending ) {
7001 that.element.removeClass( "ui-autocomplete-loading" );
7006 __response: function( content ) {
7008 content = this._normalize( content );
7010 this._trigger( "response", null, { content: content } );
7011 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
7012 this._suggest( content );
7013 this._trigger( "open" );
7015 // use ._close() instead of .close() so we don't cancel future searches
7020 close: function( event ) {
7021 this.cancelSearch = true;
7022 this._close( event );
7025 _close: function( event ) {
7026 if ( this.menu.element.is( ":visible" ) ) {
7027 this.menu.element.hide();
7029 this.isNewMenu = true;
7030 this._trigger( "close", event );
7034 _change: function( event ) {
7035 if ( this.previous !== this._value() ) {
7036 this._trigger( "change", event, { item: this.selectedItem } );
7040 _normalize: function( items ) {
7041 // assume all items have the right format when the first item is complete
7042 if ( items.length && items[0].label && items[0].value ) {
7045 return $.map( items, function( item ) {
7046 if ( typeof item === "string" ) {
7053 label: item.label || item.value,
7054 value: item.value || item.label
7059 _suggest: function( items ) {
7060 var ul = this.menu.element.empty();
7061 this._renderMenu( ul, items );
7062 this.isNewMenu = true;
7063 this.menu.refresh();
7065 // size and position menu
7068 ul.position( $.extend({
7070 }, this.options.position ));
7072 if ( this.options.autoFocus ) {
7077 _resizeMenu: function() {
7078 var ul = this.menu.element;
7079 ul.outerWidth( Math.max(
7080 // Firefox wraps long text (possibly a rounding bug)
7081 // so we add 1px to avoid the wrapping (#7513)
7082 ul.width( "" ).outerWidth() + 1,
7083 this.element.outerWidth()
7087 _renderMenu: function( ul, items ) {
7089 $.each( items, function( index, item ) {
7090 that._renderItemData( ul, item );
7094 _renderItemData: function( ul, item ) {
7095 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
7098 _renderItem: function( ul, item ) {
7100 .append( $( "<a>" ).text( item.label ) )
7104 _move: function( direction, event ) {
7105 if ( !this.menu.element.is( ":visible" ) ) {
7106 this.search( null, event );
7109 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
7110 this.menu.isLastItem() && /^next/.test( direction ) ) {
7111 this._value( this.term );
7115 this.menu[ direction ]( event );
7118 widget: function() {
7119 return this.menu.element;
7122 _value: function() {
7123 return this.valueMethod.apply( this.element, arguments );
7126 _keyEvent: function( keyEvent, event ) {
7127 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
7128 this._move( keyEvent, event );
7130 // prevents moving cursor to beginning/end of the text field in some browsers
7131 event.preventDefault();
7136 $.extend( $.ui.autocomplete, {
7137 escapeRegex: function( value ) {
7138 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
7140 filter: function(array, term) {
7141 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
7142 return $.grep( array, function(value) {
7143 return matcher.test( value.label || value.value || value );
7149 // live region extension, adding a `messages` option
7150 // NOTE: This is an experimental API. We are still investigating
7151 // a full solution for string manipulation and internationalization.
7152 $.widget( "ui.autocomplete", $.ui.autocomplete, {
7155 noResults: "No search results.",
7156 results: function( amount ) {
7157 return amount + ( amount > 1 ? " results are" : " result is" ) +
7158 " available, use up and down arrow keys to navigate.";
7163 __response: function( content ) {
7165 this._superApply( arguments );
7166 if ( this.options.disabled || this.cancelSearch ) {
7169 if ( content && content.length ) {
7170 message = this.options.messages.results( content.length );
7172 message = this.options.messages.noResults;
7174 this.liveRegion.text( message );
7180 (function( $, undefined ) {
7182 var lastActive, startXPos, startYPos, clickDragged,
7183 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
7184 stateClasses = "ui-state-hover ui-state-active ",
7185 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",
7186 formResetHandler = function() {
7187 var buttons = $( this ).find( ":ui-button" );
7188 setTimeout(function() {
7189 buttons.button( "refresh" );
7192 radioGroup = function( radio ) {
7193 var name = radio.name,
7197 name = name.replace( /'/g, "\\'" );
7199 radios = $( form ).find( "[name='" + name + "']" );
7201 radios = $( "[name='" + name + "']", radio.ownerDocument )
7202 .filter(function() {
7210 $.widget( "ui.button", {
7212 defaultElement: "<button>",
7222 _create: function() {
7223 this.element.closest( "form" )
7224 .unbind( "reset" + this.eventNamespace )
7225 .bind( "reset" + this.eventNamespace, formResetHandler );
7227 if ( typeof this.options.disabled !== "boolean" ) {
7228 this.options.disabled = !!this.element.prop( "disabled" );
7230 this.element.prop( "disabled", this.options.disabled );
7233 this._determineButtonType();
7234 this.hasTitle = !!this.buttonElement.attr( "title" );
7237 options = this.options,
7238 toggleButton = this.type === "checkbox" || this.type === "radio",
7239 activeClass = !toggleButton ? "ui-state-active" : "",
7240 focusClass = "ui-state-focus";
7242 if ( options.label === null ) {
7243 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
7246 this._hoverable( this.buttonElement );
7249 .addClass( baseClasses )
7250 .attr( "role", "button" )
7251 .bind( "mouseenter" + this.eventNamespace, function() {
7252 if ( options.disabled ) {
7255 if ( this === lastActive ) {
7256 $( this ).addClass( "ui-state-active" );
7259 .bind( "mouseleave" + this.eventNamespace, function() {
7260 if ( options.disabled ) {
7263 $( this ).removeClass( activeClass );
7265 .bind( "click" + this.eventNamespace, function( event ) {
7266 if ( options.disabled ) {
7267 event.preventDefault();
7268 event.stopImmediatePropagation();
7273 .bind( "focus" + this.eventNamespace, function() {
7274 // no need to check disabled, focus won't be triggered anyway
7275 that.buttonElement.addClass( focusClass );
7277 .bind( "blur" + this.eventNamespace, function() {
7278 that.buttonElement.removeClass( focusClass );
7281 if ( toggleButton ) {
7282 this.element.bind( "change" + this.eventNamespace, function() {
7283 if ( clickDragged ) {
7288 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
7289 // prevents issue where button state changes but checkbox/radio checked state
7290 // does not in Firefox (see ticket #6970)
7292 .bind( "mousedown" + this.eventNamespace, function( event ) {
7293 if ( options.disabled ) {
7296 clickDragged = false;
7297 startXPos = event.pageX;
7298 startYPos = event.pageY;
7300 .bind( "mouseup" + this.eventNamespace, function( event ) {
7301 if ( options.disabled ) {
7304 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
7305 clickDragged = true;
7310 if ( this.type === "checkbox" ) {
7311 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7312 if ( options.disabled || clickDragged ) {
7316 } else if ( this.type === "radio" ) {
7317 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7318 if ( options.disabled || clickDragged ) {
7321 $( this ).addClass( "ui-state-active" );
7322 that.buttonElement.attr( "aria-pressed", "true" );
7324 var radio = that.element[ 0 ];
7328 return $( this ).button( "widget" )[ 0 ];
7330 .removeClass( "ui-state-active" )
7331 .attr( "aria-pressed", "false" );
7335 .bind( "mousedown" + this.eventNamespace, function() {
7336 if ( options.disabled ) {
7339 $( this ).addClass( "ui-state-active" );
7341 that.document.one( "mouseup", function() {
7345 .bind( "mouseup" + this.eventNamespace, function() {
7346 if ( options.disabled ) {
7349 $( this ).removeClass( "ui-state-active" );
7351 .bind( "keydown" + this.eventNamespace, function(event) {
7352 if ( options.disabled ) {
7355 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
7356 $( this ).addClass( "ui-state-active" );
7359 // see #8559, we bind to blur here in case the button element loses
7360 // focus between keydown and keyup, it would be left in an "active" state
7361 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
7362 $( this ).removeClass( "ui-state-active" );
7365 if ( this.buttonElement.is("a") ) {
7366 this.buttonElement.keyup(function(event) {
7367 if ( event.keyCode === $.ui.keyCode.SPACE ) {
7368 // TODO pass through original event correctly (just as 2nd argument doesn't work)
7375 // TODO: pull out $.Widget's handling for the disabled option into
7376 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
7377 // be overridden by individual plugins
7378 this._setOption( "disabled", options.disabled );
7379 this._resetButton();
7382 _determineButtonType: function() {
7383 var ancestor, labelSelector, checked;
7385 if ( this.element.is("[type=checkbox]") ) {
7386 this.type = "checkbox";
7387 } else if ( this.element.is("[type=radio]") ) {
7388 this.type = "radio";
7389 } else if ( this.element.is("input") ) {
7390 this.type = "input";
7392 this.type = "button";
7395 if ( this.type === "checkbox" || this.type === "radio" ) {
7396 // we don't search against the document in case the element
7397 // is disconnected from the DOM
7398 ancestor = this.element.parents().last();
7399 labelSelector = "label[for='" + this.element.attr("id") + "']";
7400 this.buttonElement = ancestor.find( labelSelector );
7401 if ( !this.buttonElement.length ) {
7402 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
7403 this.buttonElement = ancestor.filter( labelSelector );
7404 if ( !this.buttonElement.length ) {
7405 this.buttonElement = ancestor.find( labelSelector );
7408 this.element.addClass( "ui-helper-hidden-accessible" );
7410 checked = this.element.is( ":checked" );
7412 this.buttonElement.addClass( "ui-state-active" );
7414 this.buttonElement.prop( "aria-pressed", checked );
7416 this.buttonElement = this.element;
7420 widget: function() {
7421 return this.buttonElement;
7424 _destroy: function() {
7426 .removeClass( "ui-helper-hidden-accessible" );
7428 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
7429 .removeAttr( "role" )
7430 .removeAttr( "aria-pressed" )
7431 .html( this.buttonElement.find(".ui-button-text").html() );
7433 if ( !this.hasTitle ) {
7434 this.buttonElement.removeAttr( "title" );
7438 _setOption: function( key, value ) {
7439 this._super( key, value );
7440 if ( key === "disabled" ) {
7442 this.element.prop( "disabled", true );
7444 this.element.prop( "disabled", false );
7448 this._resetButton();
7451 refresh: function() {
7453 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
7455 if ( isDisabled !== this.options.disabled ) {
7456 this._setOption( "disabled", isDisabled );
7458 if ( this.type === "radio" ) {
7459 radioGroup( this.element[0] ).each(function() {
7460 if ( $( this ).is( ":checked" ) ) {
7461 $( this ).button( "widget" )
7462 .addClass( "ui-state-active" )
7463 .attr( "aria-pressed", "true" );
7465 $( this ).button( "widget" )
7466 .removeClass( "ui-state-active" )
7467 .attr( "aria-pressed", "false" );
7470 } else if ( this.type === "checkbox" ) {
7471 if ( this.element.is( ":checked" ) ) {
7473 .addClass( "ui-state-active" )
7474 .attr( "aria-pressed", "true" );
7477 .removeClass( "ui-state-active" )
7478 .attr( "aria-pressed", "false" );
7483 _resetButton: function() {
7484 if ( this.type === "input" ) {
7485 if ( this.options.label ) {
7486 this.element.val( this.options.label );
7490 var buttonElement = this.buttonElement.removeClass( typeClasses ),
7491 buttonText = $( "<span></span>", this.document[0] )
7492 .addClass( "ui-button-text" )
7493 .html( this.options.label )
7494 .appendTo( buttonElement.empty() )
7496 icons = this.options.icons,
7497 multipleIcons = icons.primary && icons.secondary,
7500 if ( icons.primary || icons.secondary ) {
7501 if ( this.options.text ) {
7502 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
7505 if ( icons.primary ) {
7506 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
7509 if ( icons.secondary ) {
7510 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
7513 if ( !this.options.text ) {
7514 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
7516 if ( !this.hasTitle ) {
7517 buttonElement.attr( "title", $.trim( buttonText ) );
7521 buttonClasses.push( "ui-button-text-only" );
7523 buttonElement.addClass( buttonClasses.join( " " ) );
7527 $.widget( "ui.buttonset", {
7530 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
7533 _create: function() {
7534 this.element.addClass( "ui-buttonset" );
7541 _setOption: function( key, value ) {
7542 if ( key === "disabled" ) {
7543 this.buttons.button( "option", key, value );
7546 this._super( key, value );
7549 refresh: function() {
7550 var rtl = this.element.css( "direction" ) === "rtl";
7552 this.buttons = this.element.find( this.options.items )
7553 .filter( ":ui-button" )
7554 .button( "refresh" )
7556 .not( ":ui-button" )
7560 return $( this ).button( "widget" )[ 0 ];
7562 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
7564 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
7567 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
7572 _destroy: function() {
7573 this.element.removeClass( "ui-buttonset" );
7576 return $( this ).button( "widget" )[ 0 ];
7578 .removeClass( "ui-corner-left ui-corner-right" )
7580 .button( "destroy" );
7586 (function( $, undefined ) {
7588 $.extend($.ui, { datepicker: { version: "1.10.2" } });
7590 var PROP_NAME = "datepicker",
7591 dpuuid = new Date().getTime(),
7594 /* Date picker manager.
7595 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
7596 Settings for (groups of) date pickers are maintained in an instance object,
7597 allowing multiple different settings on the same page. */
7599 function Datepicker() {
7600 this._curInst = null; // The current instance in use
7601 this._keyEvent = false; // If the last event was a key event
7602 this._disabledInputs = []; // List of date picker inputs that have been disabled
7603 this._datepickerShowing = false; // True if the popup picker is showing , false if not
7604 this._inDialog = false; // True if showing within a "dialog", false if not
7605 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
7606 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
7607 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
7608 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
7609 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
7610 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
7611 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
7612 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
7613 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
7614 this.regional = []; // Available regional settings, indexed by language code
7615 this.regional[""] = { // Default regional settings
7616 closeText: "Done", // Display text for close link
7617 prevText: "Prev", // Display text for previous month link
7618 nextText: "Next", // Display text for next month link
7619 currentText: "Today", // Display text for current month link
7620 monthNames: ["January","February","March","April","May","June",
7621 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
7622 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
7623 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
7624 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
7625 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
7626 weekHeader: "Wk", // Column header for week of the year
7627 dateFormat: "mm/dd/yy", // See format options on parseDate
7628 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
7629 isRTL: false, // True if right-to-left language, false if left-to-right
7630 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
7631 yearSuffix: "" // Additional text to append to the year in the month headers
7633 this._defaults = { // Global defaults for all the date picker instances
7634 showOn: "focus", // "focus" for popup on focus,
7635 // "button" for trigger button, or "both" for either
7636 showAnim: "fadeIn", // Name of jQuery animation for popup
7637 showOptions: {}, // Options for enhanced animations
7638 defaultDate: null, // Used when field is blank: actual date,
7639 // +/-number for offset from today, null for today
7640 appendText: "", // Display text following the input box, e.g. showing the format
7641 buttonText: "...", // Text for trigger button
7642 buttonImage: "", // URL for trigger button image
7643 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
7644 hideIfNoPrevNext: false, // True to hide next/previous month links
7645 // if not applicable, false to just disable them
7646 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
7647 gotoCurrent: false, // True if today link goes back to current selection instead
7648 changeMonth: false, // True if month can be selected directly, false if only prev/next
7649 changeYear: false, // True if year can be selected directly, false if only prev/next
7650 yearRange: "c-10:c+10", // Range of years to display in drop-down,
7651 // either relative to today's year (-nn:+nn), relative to currently displayed year
7652 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
7653 showOtherMonths: false, // True to show dates in other months, false to leave blank
7654 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
7655 showWeek: false, // True to show week of the year, false to not show it
7656 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
7657 // takes a Date and returns the number of the week for it
7658 shortYearCutoff: "+10", // Short year values < this are in the current century,
7659 // > this are in the previous century,
7660 // string value starting with "+" for current year + value
7661 minDate: null, // The earliest selectable date, or null for no limit
7662 maxDate: null, // The latest selectable date, or null for no limit
7663 duration: "fast", // Duration of display/closure
7664 beforeShowDay: null, // Function that takes a date and returns an array with
7665 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
7666 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
7667 beforeShow: null, // Function that takes an input field and
7668 // returns a set of custom settings for the date picker
7669 onSelect: null, // Define a callback function when a date is selected
7670 onChangeMonthYear: null, // Define a callback function when the month or year is changed
7671 onClose: null, // Define a callback function when the datepicker is closed
7672 numberOfMonths: 1, // Number of months to show at a time
7673 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
7674 stepMonths: 1, // Number of months to step back/forward
7675 stepBigMonths: 12, // Number of months to step back/forward for the big links
7676 altField: "", // Selector for an alternate field to store selected dates into
7677 altFormat: "", // The date format to use for the alternate field
7678 constrainInput: true, // The input is constrained by the current date format
7679 showButtonPanel: false, // True to show button panel, false to not show it
7680 autoSize: false, // True to size the input for the date format, false to leave as is
7681 disabled: false // The initial disabled state
7683 $.extend(this._defaults, this.regional[""]);
7684 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
7687 $.extend(Datepicker.prototype, {
7688 /* Class name added to elements to indicate already configured with a date picker. */
7689 markerClassName: "hasDatepicker",
7691 //Keep track of the maximum number of rows displayed (see #7043)
7694 // TODO rename to "widget" when switching to widget factory
7695 _widgetDatepicker: function() {
7699 /* Override the default settings for all instances of the date picker.
7700 * @param settings object - the new settings to use as defaults (anonymous object)
7701 * @return the manager object
7703 setDefaults: function(settings) {
7704 extendRemove(this._defaults, settings || {});
7708 /* Attach the date picker to a jQuery selection.
7709 * @param target element - the target input field or division or span
7710 * @param settings object - the new settings to use for this date picker instance (anonymous)
7712 _attachDatepicker: function(target, settings) {
7713 var nodeName, inline, inst;
7714 nodeName = target.nodeName.toLowerCase();
7715 inline = (nodeName === "div" || nodeName === "span");
7718 target.id = "dp" + this.uuid;
7720 inst = this._newInst($(target), inline);
7721 inst.settings = $.extend({}, settings || {});
7722 if (nodeName === "input") {
7723 this._connectDatepicker(target, inst);
7724 } else if (inline) {
7725 this._inlineDatepicker(target, inst);
7729 /* Create a new instance object. */
7730 _newInst: function(target, inline) {
7731 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
7732 return {id: id, input: target, // associated target
7733 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
7734 drawMonth: 0, drawYear: 0, // month being drawn
7735 inline: inline, // is datepicker inline or not
7736 dpDiv: (!inline ? this.dpDiv : // presentation div
7737 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
7740 /* Attach the date picker to an input field. */
7741 _connectDatepicker: function(target, inst) {
7742 var input = $(target);
7743 inst.append = $([]);
7744 inst.trigger = $([]);
7745 if (input.hasClass(this.markerClassName)) {
7748 this._attachments(input, inst);
7749 input.addClass(this.markerClassName).keydown(this._doKeyDown).
7750 keypress(this._doKeyPress).keyup(this._doKeyUp);
7751 this._autoSize(inst);
7752 $.data(target, PROP_NAME, inst);
7753 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
7754 if( inst.settings.disabled ) {
7755 this._disableDatepicker( target );
7759 /* Make attachments based on settings. */
7760 _attachments: function(input, inst) {
7761 var showOn, buttonText, buttonImage,
7762 appendText = this._get(inst, "appendText"),
7763 isRTL = this._get(inst, "isRTL");
7766 inst.append.remove();
7769 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
7770 input[isRTL ? "before" : "after"](inst.append);
7773 input.unbind("focus", this._showDatepicker);
7776 inst.trigger.remove();
7779 showOn = this._get(inst, "showOn");
7780 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
7781 input.focus(this._showDatepicker);
7783 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
7784 buttonText = this._get(inst, "buttonText");
7785 buttonImage = this._get(inst, "buttonImage");
7786 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
7787 $("<img/>").addClass(this._triggerClass).
7788 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
7789 $("<button type='button'></button>").addClass(this._triggerClass).
7790 html(!buttonImage ? buttonText : $("<img/>").attr(
7791 { src:buttonImage, alt:buttonText, title:buttonText })));
7792 input[isRTL ? "before" : "after"](inst.trigger);
7793 inst.trigger.click(function() {
7794 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
7795 $.datepicker._hideDatepicker();
7796 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
7797 $.datepicker._hideDatepicker();
7798 $.datepicker._showDatepicker(input[0]);
7800 $.datepicker._showDatepicker(input[0]);
7807 /* Apply the maximum length for the date format. */
7808 _autoSize: function(inst) {
7809 if (this._get(inst, "autoSize") && !inst.inline) {
7810 var findMax, max, maxI, i,
7811 date = new Date(2009, 12 - 1, 20), // Ensure double digits
7812 dateFormat = this._get(inst, "dateFormat");
7814 if (dateFormat.match(/[DM]/)) {
7815 findMax = function(names) {
7818 for (i = 0; i < names.length; i++) {
7819 if (names[i].length > max) {
7820 max = names[i].length;
7826 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
7827 "monthNames" : "monthNamesShort"))));
7828 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
7829 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
7831 inst.input.attr("size", this._formatDate(inst, date).length);
7835 /* Attach an inline date picker to a div. */
7836 _inlineDatepicker: function(target, inst) {
7837 var divSpan = $(target);
7838 if (divSpan.hasClass(this.markerClassName)) {
7841 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
7842 $.data(target, PROP_NAME, inst);
7843 this._setDate(inst, this._getDefaultDate(inst), true);
7844 this._updateDatepicker(inst);
7845 this._updateAlternate(inst);
7846 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
7847 if( inst.settings.disabled ) {
7848 this._disableDatepicker( target );
7850 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
7851 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
7852 inst.dpDiv.css( "display", "block" );
7855 /* Pop-up the date picker in a "dialog" box.
7856 * @param input element - ignored
7857 * @param date string or Date - the initial date to display
7858 * @param onSelect function - the function to call when a date is selected
7859 * @param settings object - update the dialog date picker instance's settings (anonymous object)
7860 * @param pos int[2] - coordinates for the dialog's position within the screen or
7861 * event - with x/y coordinates or
7862 * leave empty for default (screen centre)
7863 * @return the manager object
7865 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
7866 var id, browserWidth, browserHeight, scrollX, scrollY,
7867 inst = this._dialogInst; // internal instance
7871 id = "dp" + this.uuid;
7872 this._dialogInput = $("<input type='text' id='" + id +
7873 "' style='position: absolute; top: -100px; width: 0px;'/>");
7874 this._dialogInput.keydown(this._doKeyDown);
7875 $("body").append(this._dialogInput);
7876 inst = this._dialogInst = this._newInst(this._dialogInput, false);
7878 $.data(this._dialogInput[0], PROP_NAME, inst);
7880 extendRemove(inst.settings, settings || {});
7881 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
7882 this._dialogInput.val(date);
7884 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
7886 browserWidth = document.documentElement.clientWidth;
7887 browserHeight = document.documentElement.clientHeight;
7888 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
7889 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
7890 this._pos = // should use actual width/height below
7891 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
7894 // move input on screen for focus, but hidden behind dialog
7895 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
7896 inst.settings.onSelect = onSelect;
7897 this._inDialog = true;
7898 this.dpDiv.addClass(this._dialogClass);
7899 this._showDatepicker(this._dialogInput[0]);
7901 $.blockUI(this.dpDiv);
7903 $.data(this._dialogInput[0], PROP_NAME, inst);
7907 /* Detach a datepicker from its control.
7908 * @param target element - the target input field or division or span
7910 _destroyDatepicker: function(target) {
7912 $target = $(target),
7913 inst = $.data(target, PROP_NAME);
7915 if (!$target.hasClass(this.markerClassName)) {
7919 nodeName = target.nodeName.toLowerCase();
7920 $.removeData(target, PROP_NAME);
7921 if (nodeName === "input") {
7922 inst.append.remove();
7923 inst.trigger.remove();
7924 $target.removeClass(this.markerClassName).
7925 unbind("focus", this._showDatepicker).
7926 unbind("keydown", this._doKeyDown).
7927 unbind("keypress", this._doKeyPress).
7928 unbind("keyup", this._doKeyUp);
7929 } else if (nodeName === "div" || nodeName === "span") {
7930 $target.removeClass(this.markerClassName).empty();
7934 /* Enable the date picker to a jQuery selection.
7935 * @param target element - the target input field or division or span
7937 _enableDatepicker: function(target) {
7938 var nodeName, inline,
7939 $target = $(target),
7940 inst = $.data(target, PROP_NAME);
7942 if (!$target.hasClass(this.markerClassName)) {
7946 nodeName = target.nodeName.toLowerCase();
7947 if (nodeName === "input") {
7948 target.disabled = false;
7949 inst.trigger.filter("button").
7950 each(function() { this.disabled = false; }).end().
7951 filter("img").css({opacity: "1.0", cursor: ""});
7952 } else if (nodeName === "div" || nodeName === "span") {
7953 inline = $target.children("." + this._inlineClass);
7954 inline.children().removeClass("ui-state-disabled");
7955 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7956 prop("disabled", false);
7958 this._disabledInputs = $.map(this._disabledInputs,
7959 function(value) { return (value === target ? null : value); }); // delete entry
7962 /* Disable the date picker to a jQuery selection.
7963 * @param target element - the target input field or division or span
7965 _disableDatepicker: function(target) {
7966 var nodeName, inline,
7967 $target = $(target),
7968 inst = $.data(target, PROP_NAME);
7970 if (!$target.hasClass(this.markerClassName)) {
7974 nodeName = target.nodeName.toLowerCase();
7975 if (nodeName === "input") {
7976 target.disabled = true;
7977 inst.trigger.filter("button").
7978 each(function() { this.disabled = true; }).end().
7979 filter("img").css({opacity: "0.5", cursor: "default"});
7980 } else if (nodeName === "div" || nodeName === "span") {
7981 inline = $target.children("." + this._inlineClass);
7982 inline.children().addClass("ui-state-disabled");
7983 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7984 prop("disabled", true);
7986 this._disabledInputs = $.map(this._disabledInputs,
7987 function(value) { return (value === target ? null : value); }); // delete entry
7988 this._disabledInputs[this._disabledInputs.length] = target;
7991 /* Is the first field in a jQuery collection disabled as a datepicker?
7992 * @param target element - the target input field or division or span
7993 * @return boolean - true if disabled, false if enabled
7995 _isDisabledDatepicker: function(target) {
7999 for (var i = 0; i < this._disabledInputs.length; i++) {
8000 if (this._disabledInputs[i] === target) {
8007 /* Retrieve the instance data for the target control.
8008 * @param target element - the target input field or division or span
8009 * @return object - the associated instance data
8010 * @throws error if a jQuery problem getting data
8012 _getInst: function(target) {
8014 return $.data(target, PROP_NAME);
8017 throw "Missing instance data for this datepicker";
8021 /* Update or retrieve the settings for a date picker attached to an input field or division.
8022 * @param target element - the target input field or division or span
8023 * @param name object - the new settings to update or
8024 * string - the name of the setting to change or retrieve,
8025 * when retrieving also "all" for all instance settings or
8026 * "defaults" for all global defaults
8027 * @param value any - the new value for the setting
8028 * (omit if above is an object or to retrieve a value)
8030 _optionDatepicker: function(target, name, value) {
8031 var settings, date, minDate, maxDate,
8032 inst = this._getInst(target);
8034 if (arguments.length === 2 && typeof name === "string") {
8035 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
8036 (inst ? (name === "all" ? $.extend({}, inst.settings) :
8037 this._get(inst, name)) : null));
8040 settings = name || {};
8041 if (typeof name === "string") {
8043 settings[name] = value;
8047 if (this._curInst === inst) {
8048 this._hideDatepicker();
8051 date = this._getDateDatepicker(target, true);
8052 minDate = this._getMinMaxDate(inst, "min");
8053 maxDate = this._getMinMaxDate(inst, "max");
8054 extendRemove(inst.settings, settings);
8055 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
8056 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
8057 inst.settings.minDate = this._formatDate(inst, minDate);
8059 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
8060 inst.settings.maxDate = this._formatDate(inst, maxDate);
8062 if ( "disabled" in settings ) {
8063 if ( settings.disabled ) {
8064 this._disableDatepicker(target);
8066 this._enableDatepicker(target);
8069 this._attachments($(target), inst);
8070 this._autoSize(inst);
8071 this._setDate(inst, date);
8072 this._updateAlternate(inst);
8073 this._updateDatepicker(inst);
8077 // change method deprecated
8078 _changeDatepicker: function(target, name, value) {
8079 this._optionDatepicker(target, name, value);
8082 /* Redraw the date picker attached to an input field or division.
8083 * @param target element - the target input field or division or span
8085 _refreshDatepicker: function(target) {
8086 var inst = this._getInst(target);
8088 this._updateDatepicker(inst);
8092 /* Set the dates for a jQuery selection.
8093 * @param target element - the target input field or division or span
8094 * @param date Date - the new date
8096 _setDateDatepicker: function(target, date) {
8097 var inst = this._getInst(target);
8099 this._setDate(inst, date);
8100 this._updateDatepicker(inst);
8101 this._updateAlternate(inst);
8105 /* Get the date(s) for the first entry in a jQuery selection.
8106 * @param target element - the target input field or division or span
8107 * @param noDefault boolean - true if no default date is to be used
8108 * @return Date - the current date
8110 _getDateDatepicker: function(target, noDefault) {
8111 var inst = this._getInst(target);
8112 if (inst && !inst.inline) {
8113 this._setDateFromField(inst, noDefault);
8115 return (inst ? this._getDate(inst) : null);
8118 /* Handle keystrokes. */
8119 _doKeyDown: function(event) {
8120 var onSelect, dateStr, sel,
8121 inst = $.datepicker._getInst(event.target),
8123 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
8125 inst._keyEvent = true;
8126 if ($.datepicker._datepickerShowing) {
8127 switch (event.keyCode) {
8128 case 9: $.datepicker._hideDatepicker();
8130 break; // hide on tab out
8131 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
8132 $.datepicker._currentClass + ")", inst.dpDiv);
8134 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
8137 onSelect = $.datepicker._get(inst, "onSelect");
8139 dateStr = $.datepicker._formatDate(inst);
8141 // trigger custom callback
8142 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
8144 $.datepicker._hideDatepicker();
8147 return false; // don't submit the form
8148 case 27: $.datepicker._hideDatepicker();
8149 break; // hide on escape
8150 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8151 -$.datepicker._get(inst, "stepBigMonths") :
8152 -$.datepicker._get(inst, "stepMonths")), "M");
8153 break; // previous month/year on page up/+ ctrl
8154 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8155 +$.datepicker._get(inst, "stepBigMonths") :
8156 +$.datepicker._get(inst, "stepMonths")), "M");
8157 break; // next month/year on page down/+ ctrl
8158 case 35: if (event.ctrlKey || event.metaKey) {
8159 $.datepicker._clearDate(event.target);
8161 handled = event.ctrlKey || event.metaKey;
8162 break; // clear on ctrl or command +end
8163 case 36: if (event.ctrlKey || event.metaKey) {
8164 $.datepicker._gotoToday(event.target);
8166 handled = event.ctrlKey || event.metaKey;
8167 break; // current on ctrl or command +home
8168 case 37: if (event.ctrlKey || event.metaKey) {
8169 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
8171 handled = event.ctrlKey || event.metaKey;
8172 // -1 day on ctrl or command +left
8173 if (event.originalEvent.altKey) {
8174 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8175 -$.datepicker._get(inst, "stepBigMonths") :
8176 -$.datepicker._get(inst, "stepMonths")), "M");
8178 // next month/year on alt +left on Mac
8180 case 38: if (event.ctrlKey || event.metaKey) {
8181 $.datepicker._adjustDate(event.target, -7, "D");
8183 handled = event.ctrlKey || event.metaKey;
8184 break; // -1 week on ctrl or command +up
8185 case 39: if (event.ctrlKey || event.metaKey) {
8186 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
8188 handled = event.ctrlKey || event.metaKey;
8189 // +1 day on ctrl or command +right
8190 if (event.originalEvent.altKey) {
8191 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8192 +$.datepicker._get(inst, "stepBigMonths") :
8193 +$.datepicker._get(inst, "stepMonths")), "M");
8195 // next month/year on alt +right
8197 case 40: if (event.ctrlKey || event.metaKey) {
8198 $.datepicker._adjustDate(event.target, +7, "D");
8200 handled = event.ctrlKey || event.metaKey;
8201 break; // +1 week on ctrl or command +down
8202 default: handled = false;
8204 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
8205 $.datepicker._showDatepicker(this);
8211 event.preventDefault();
8212 event.stopPropagation();
8216 /* Filter entered characters - based on date format. */
8217 _doKeyPress: function(event) {
8219 inst = $.datepicker._getInst(event.target);
8221 if ($.datepicker._get(inst, "constrainInput")) {
8222 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
8223 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
8224 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
8228 /* Synchronise manual entry and field/alternate field. */
8229 _doKeyUp: function(event) {
8231 inst = $.datepicker._getInst(event.target);
8233 if (inst.input.val() !== inst.lastVal) {
8235 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
8236 (inst.input ? inst.input.val() : null),
8237 $.datepicker._getFormatConfig(inst));
8239 if (date) { // only if valid
8240 $.datepicker._setDateFromField(inst);
8241 $.datepicker._updateAlternate(inst);
8242 $.datepicker._updateDatepicker(inst);
8251 /* Pop-up the date picker for a given input field.
8252 * If false returned from beforeShow event handler do not show.
8253 * @param input element - the input field attached to the date picker or
8254 * event - if triggered by focus
8256 _showDatepicker: function(input) {
8257 input = input.target || input;
8258 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
8259 input = $("input", input.parentNode)[0];
8262 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
8266 var inst, beforeShow, beforeShowSettings, isFixed,
8267 offset, showAnim, duration;
8269 inst = $.datepicker._getInst(input);
8270 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
8271 $.datepicker._curInst.dpDiv.stop(true, true);
8272 if ( inst && $.datepicker._datepickerShowing ) {
8273 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
8277 beforeShow = $.datepicker._get(inst, "beforeShow");
8278 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
8279 if(beforeShowSettings === false){
8282 extendRemove(inst.settings, beforeShowSettings);
8284 inst.lastVal = null;
8285 $.datepicker._lastInput = input;
8286 $.datepicker._setDateFromField(inst);
8288 if ($.datepicker._inDialog) { // hide cursor
8291 if (!$.datepicker._pos) { // position below input
8292 $.datepicker._pos = $.datepicker._findPos(input);
8293 $.datepicker._pos[1] += input.offsetHeight; // add the height
8297 $(input).parents().each(function() {
8298 isFixed |= $(this).css("position") === "fixed";
8302 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
8303 $.datepicker._pos = null;
8304 //to avoid flashes on Firefox
8306 // determine sizing offscreen
8307 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
8308 $.datepicker._updateDatepicker(inst);
8309 // fix width for dynamic number of date pickers
8310 // and adjust position before showing
8311 offset = $.datepicker._checkOffset(inst, offset, isFixed);
8312 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
8313 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
8314 left: offset.left + "px", top: offset.top + "px"});
8317 showAnim = $.datepicker._get(inst, "showAnim");
8318 duration = $.datepicker._get(inst, "duration");
8319 inst.dpDiv.zIndex($(input).zIndex()+1);
8320 $.datepicker._datepickerShowing = true;
8322 if ( $.effects && $.effects.effect[ showAnim ] ) {
8323 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
8325 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
8328 if (inst.input.is(":visible") && !inst.input.is(":disabled")) {
8331 $.datepicker._curInst = inst;
8335 /* Generate the date picker content. */
8336 _updateDatepicker: function(inst) {
8337 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
8338 instActive = inst; // for delegate hover events
8339 inst.dpDiv.empty().append(this._generateHTML(inst));
8340 this._attachHandlers(inst);
8341 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
8344 numMonths = this._getNumberOfMonths(inst),
8345 cols = numMonths[1],
8348 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
8350 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
8352 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
8353 "Class"]("ui-datepicker-multi");
8354 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
8355 "Class"]("ui-datepicker-rtl");
8357 // #6694 - don't focus the input if it's already focused
8358 // this breaks the change event in IE
8359 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
8360 inst.input.is(":visible") && !inst.input.is(":disabled") && inst.input[0] !== document.activeElement) {
8364 // deffered render of the years select (to avoid flashes on Firefox)
8365 if( inst.yearshtml ){
8366 origyearshtml = inst.yearshtml;
8367 setTimeout(function(){
8368 //assure that inst.yearshtml didn't change.
8369 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
8370 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
8372 origyearshtml = inst.yearshtml = null;
8377 /* Retrieve the size of left and top borders for an element.
8378 * @param elem (jQuery object) the element of interest
8379 * @return (number[2]) the left and top borders
8381 _getBorders: function(elem) {
8382 var convert = function(value) {
8383 return {thin: 1, medium: 2, thick: 3}[value] || value;
8385 return [parseFloat(convert(elem.css("border-left-width"))),
8386 parseFloat(convert(elem.css("border-top-width")))];
8389 /* Check positioning to remain on screen. */
8390 _checkOffset: function(inst, offset, isFixed) {
8391 var dpWidth = inst.dpDiv.outerWidth(),
8392 dpHeight = inst.dpDiv.outerHeight(),
8393 inputWidth = inst.input ? inst.input.outerWidth() : 0,
8394 inputHeight = inst.input ? inst.input.outerHeight() : 0,
8395 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
8396 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
8398 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
8399 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
8400 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
8402 // now check if datepicker is showing outside window viewport - move to a better place if so.
8403 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
8404 Math.abs(offset.left + dpWidth - viewWidth) : 0);
8405 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
8406 Math.abs(dpHeight + inputHeight) : 0);
8411 /* Find an object's position on the screen. */
8412 _findPos: function(obj) {
8414 inst = this._getInst(obj),
8415 isRTL = this._get(inst, "isRTL");
8417 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
8418 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
8421 position = $(obj).offset();
8422 return [position.left, position.top];
8425 /* Hide the date picker from view.
8426 * @param input element - the input field attached to the date picker
8428 _hideDatepicker: function(input) {
8429 var showAnim, duration, postProcess, onClose,
8430 inst = this._curInst;
8432 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
8436 if (this._datepickerShowing) {
8437 showAnim = this._get(inst, "showAnim");
8438 duration = this._get(inst, "duration");
8439 postProcess = function() {
8440 $.datepicker._tidyDialog(inst);
8443 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
8444 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
8445 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
8447 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
8448 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
8454 this._datepickerShowing = false;
8456 onClose = this._get(inst, "onClose");
8458 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
8461 this._lastInput = null;
8462 if (this._inDialog) {
8463 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
8466 $("body").append(this.dpDiv);
8469 this._inDialog = false;
8473 /* Tidy up after a dialog display. */
8474 _tidyDialog: function(inst) {
8475 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
8478 /* Close date picker if clicked elsewhere. */
8479 _checkExternalClick: function(event) {
8480 if (!$.datepicker._curInst) {
8484 var $target = $(event.target),
8485 inst = $.datepicker._getInst($target[0]);
8487 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
8488 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
8489 !$target.hasClass($.datepicker.markerClassName) &&
8490 !$target.closest("." + $.datepicker._triggerClass).length &&
8491 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
8492 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
8493 $.datepicker._hideDatepicker();
8497 /* Adjust one of the date sub-fields. */
8498 _adjustDate: function(id, offset, period) {
8500 inst = this._getInst(target[0]);
8502 if (this._isDisabledDatepicker(target[0])) {
8505 this._adjustInstDate(inst, offset +
8506 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
8508 this._updateDatepicker(inst);
8511 /* Action for current link. */
8512 _gotoToday: function(id) {
8515 inst = this._getInst(target[0]);
8517 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
8518 inst.selectedDay = inst.currentDay;
8519 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
8520 inst.drawYear = inst.selectedYear = inst.currentYear;
8523 inst.selectedDay = date.getDate();
8524 inst.drawMonth = inst.selectedMonth = date.getMonth();
8525 inst.drawYear = inst.selectedYear = date.getFullYear();
8527 this._notifyChange(inst);
8528 this._adjustDate(target);
8531 /* Action for selecting a new month/year. */
8532 _selectMonthYear: function(id, select, period) {
8534 inst = this._getInst(target[0]);
8536 inst["selected" + (period === "M" ? "Month" : "Year")] =
8537 inst["draw" + (period === "M" ? "Month" : "Year")] =
8538 parseInt(select.options[select.selectedIndex].value,10);
8540 this._notifyChange(inst);
8541 this._adjustDate(target);
8544 /* Action for selecting a day. */
8545 _selectDay: function(id, month, year, td) {
8549 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
8553 inst = this._getInst(target[0]);
8554 inst.selectedDay = inst.currentDay = $("a", td).html();
8555 inst.selectedMonth = inst.currentMonth = month;
8556 inst.selectedYear = inst.currentYear = year;
8557 this._selectDate(id, this._formatDate(inst,
8558 inst.currentDay, inst.currentMonth, inst.currentYear));
8561 /* Erase the input field and hide the date picker. */
8562 _clearDate: function(id) {
8564 this._selectDate(target, "");
8567 /* Update the input field with the selected date. */
8568 _selectDate: function(id, dateStr) {
8571 inst = this._getInst(target[0]);
8573 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
8575 inst.input.val(dateStr);
8577 this._updateAlternate(inst);
8579 onSelect = this._get(inst, "onSelect");
8581 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
8582 } else if (inst.input) {
8583 inst.input.trigger("change"); // fire the change event
8587 this._updateDatepicker(inst);
8589 this._hideDatepicker();
8590 this._lastInput = inst.input[0];
8591 if (typeof(inst.input[0]) !== "object") {
8592 inst.input.focus(); // restore focus
8594 this._lastInput = null;
8598 /* Update any alternate field to synchronise with the main field. */
8599 _updateAlternate: function(inst) {
8600 var altFormat, date, dateStr,
8601 altField = this._get(inst, "altField");
8603 if (altField) { // update alternate field too
8604 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
8605 date = this._getDate(inst);
8606 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
8607 $(altField).each(function() { $(this).val(dateStr); });
8611 /* Set as beforeShowDay function to prevent selection of weekends.
8612 * @param date Date - the date to customise
8613 * @return [boolean, string] - is this date selectable?, what is its CSS class?
8615 noWeekends: function(date) {
8616 var day = date.getDay();
8617 return [(day > 0 && day < 6), ""];
8620 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
8621 * @param date Date - the date to get the week for
8622 * @return number - the number of the week within the year that contains this date
8624 iso8601Week: function(date) {
8626 checkDate = new Date(date.getTime());
8628 // Find Thursday of this week starting on Monday
8629 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
8631 time = checkDate.getTime();
8632 checkDate.setMonth(0); // Compare with Jan 1
8633 checkDate.setDate(1);
8634 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
8637 /* Parse a string value into a date object.
8638 * See formatDate below for the possible formats.
8640 * @param format string - the expected format of the date
8641 * @param value string - the date in the above format
8642 * @param settings Object - attributes include:
8643 * shortYearCutoff number - the cutoff year for determining the century (optional)
8644 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8645 * dayNames string[7] - names of the days from Sunday (optional)
8646 * monthNamesShort string[12] - abbreviated names of the months (optional)
8647 * monthNames string[12] - names of the months (optional)
8648 * @return Date - the extracted date value or null if value is blank
8650 parseDate: function (format, value, settings) {
8651 if (format == null || value == null) {
8652 throw "Invalid arguments";
8655 value = (typeof value === "object" ? value.toString() : value + "");
8660 var iFormat, dim, extra,
8662 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
8663 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
8664 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
8665 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8666 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8667 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8668 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8675 // Check whether a format character is doubled
8676 lookAhead = function(match) {
8677 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8683 // Extract a number from the string value
8684 getNumber = function(match) {
8685 var isDoubled = lookAhead(match),
8686 size = (match === "@" ? 14 : (match === "!" ? 20 :
8687 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
8688 digits = new RegExp("^\\d{1," + size + "}"),
8689 num = value.substring(iValue).match(digits);
8691 throw "Missing number at position " + iValue;
8693 iValue += num[0].length;
8694 return parseInt(num[0], 10);
8696 // Extract a name from the string value and convert to an index
8697 getName = function(match, shortNames, longNames) {
8699 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
8701 }).sort(function (a, b) {
8702 return -(a[1].length - b[1].length);
8705 $.each(names, function (i, pair) {
8707 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
8709 iValue += name.length;
8716 throw "Unknown name at position " + iValue;
8719 // Confirm that a literal character matches the string value
8720 checkLiteral = function() {
8721 if (value.charAt(iValue) !== format.charAt(iFormat)) {
8722 throw "Unexpected literal at position " + iValue;
8727 for (iFormat = 0; iFormat < format.length; iFormat++) {
8729 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8735 switch (format.charAt(iFormat)) {
8737 day = getNumber("d");
8740 getName("D", dayNamesShort, dayNames);
8743 doy = getNumber("o");
8746 month = getNumber("m");
8749 month = getName("M", monthNamesShort, monthNames);
8752 year = getNumber("y");
8755 date = new Date(getNumber("@"));
8756 year = date.getFullYear();
8757 month = date.getMonth() + 1;
8758 day = date.getDate();
8761 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
8762 year = date.getFullYear();
8763 month = date.getMonth() + 1;
8764 day = date.getDate();
8767 if (lookAhead("'")){
8779 if (iValue < value.length){
8780 extra = value.substr(iValue);
8781 if (!/^\s+/.test(extra)) {
8782 throw "Extra/unparsed characters found in date: " + extra;
8787 year = new Date().getFullYear();
8788 } else if (year < 100) {
8789 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
8790 (year <= shortYearCutoff ? 0 : -100);
8797 dim = this._getDaysInMonth(year, month - 1);
8806 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
8807 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
8808 throw "Invalid date"; // E.g. 31/02/00
8813 /* Standard date formats. */
8814 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
8815 COOKIE: "D, dd M yy",
8816 ISO_8601: "yy-mm-dd",
8817 RFC_822: "D, d M y",
8818 RFC_850: "DD, dd-M-y",
8819 RFC_1036: "D, d M y",
8820 RFC_1123: "D, d M yy",
8821 RFC_2822: "D, d M yy",
8822 RSS: "D, d M y", // RFC 822
8825 W3C: "yy-mm-dd", // ISO 8601
8827 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
8828 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
8830 /* Format a date object into a string value.
8831 * The format can be combinations of the following:
8832 * d - day of month (no leading zero)
8833 * dd - day of month (two digit)
8834 * o - day of year (no leading zeros)
8835 * oo - day of year (three digit)
8836 * D - day name short
8837 * DD - day name long
8838 * m - month of year (no leading zero)
8839 * mm - month of year (two digit)
8840 * M - month name short
8841 * MM - month name long
8842 * y - year (two digit)
8843 * yy - year (four digit)
8844 * @ - Unix timestamp (ms since 01/01/1970)
8845 * ! - Windows ticks (100ns since 01/01/0001)
8846 * "..." - literal text
8849 * @param format string - the desired format of the date
8850 * @param date Date - the date value to format
8851 * @param settings Object - attributes include:
8852 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8853 * dayNames string[7] - names of the days from Sunday (optional)
8854 * monthNamesShort string[12] - abbreviated names of the months (optional)
8855 * monthNames string[12] - names of the months (optional)
8856 * @return string - the date in the above format
8858 formatDate: function (format, date, settings) {
8864 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8865 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8866 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8867 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8868 // Check whether a format character is doubled
8869 lookAhead = function(match) {
8870 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8876 // Format a number, with leading zero if necessary
8877 formatNumber = function(match, value, len) {
8878 var num = "" + value;
8879 if (lookAhead(match)) {
8880 while (num.length < len) {
8886 // Format a name, short or long as requested
8887 formatName = function(match, value, shortNames, longNames) {
8888 return (lookAhead(match) ? longNames[value] : shortNames[value]);
8894 for (iFormat = 0; iFormat < format.length; iFormat++) {
8896 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8899 output += format.charAt(iFormat);
8902 switch (format.charAt(iFormat)) {
8904 output += formatNumber("d", date.getDate(), 2);
8907 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
8910 output += formatNumber("o",
8911 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
8914 output += formatNumber("m", date.getMonth() + 1, 2);
8917 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
8920 output += (lookAhead("y") ? date.getFullYear() :
8921 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
8924 output += date.getTime();
8927 output += date.getTime() * 10000 + this._ticksTo1970;
8930 if (lookAhead("'")) {
8937 output += format.charAt(iFormat);
8945 /* Extract all possible characters from the date format. */
8946 _possibleChars: function (format) {
8950 // Check whether a format character is doubled
8951 lookAhead = function(match) {
8952 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8959 for (iFormat = 0; iFormat < format.length; iFormat++) {
8961 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8964 chars += format.charAt(iFormat);
8967 switch (format.charAt(iFormat)) {
8968 case "d": case "m": case "y": case "@":
8969 chars += "0123456789";
8972 return null; // Accept anything
8974 if (lookAhead("'")) {
8981 chars += format.charAt(iFormat);
8988 /* Get a setting value, defaulting if necessary. */
8989 _get: function(inst, name) {
8990 return inst.settings[name] !== undefined ?
8991 inst.settings[name] : this._defaults[name];
8994 /* Parse existing date and initialise date picker. */
8995 _setDateFromField: function(inst, noDefault) {
8996 if (inst.input.val() === inst.lastVal) {
9000 var dateFormat = this._get(inst, "dateFormat"),
9001 dates = inst.lastVal = inst.input ? inst.input.val() : null,
9002 defaultDate = this._getDefaultDate(inst),
9004 settings = this._getFormatConfig(inst);
9007 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
9009 dates = (noDefault ? "" : dates);
9011 inst.selectedDay = date.getDate();
9012 inst.drawMonth = inst.selectedMonth = date.getMonth();
9013 inst.drawYear = inst.selectedYear = date.getFullYear();
9014 inst.currentDay = (dates ? date.getDate() : 0);
9015 inst.currentMonth = (dates ? date.getMonth() : 0);
9016 inst.currentYear = (dates ? date.getFullYear() : 0);
9017 this._adjustInstDate(inst);
9020 /* Retrieve the default date shown on opening. */
9021 _getDefaultDate: function(inst) {
9022 return this._restrictMinMax(inst,
9023 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
9026 /* A date may be specified as an exact value or a relative one. */
9027 _determineDate: function(inst, date, defaultDate) {
9028 var offsetNumeric = function(offset) {
9029 var date = new Date();
9030 date.setDate(date.getDate() + offset);
9033 offsetString = function(offset) {
9035 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
9036 offset, $.datepicker._getFormatConfig(inst));
9042 var date = (offset.toLowerCase().match(/^c/) ?
9043 $.datepicker._getDate(inst) : null) || new Date(),
9044 year = date.getFullYear(),
9045 month = date.getMonth(),
9046 day = date.getDate(),
9047 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
9048 matches = pattern.exec(offset);
9051 switch (matches[2] || "d") {
9052 case "d" : case "D" :
9053 day += parseInt(matches[1],10); break;
9054 case "w" : case "W" :
9055 day += parseInt(matches[1],10) * 7; break;
9056 case "m" : case "M" :
9057 month += parseInt(matches[1],10);
9058 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9060 case "y": case "Y" :
9061 year += parseInt(matches[1],10);
9062 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9065 matches = pattern.exec(offset);
9067 return new Date(year, month, day);
9069 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
9070 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
9072 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
9074 newDate.setHours(0);
9075 newDate.setMinutes(0);
9076 newDate.setSeconds(0);
9077 newDate.setMilliseconds(0);
9079 return this._daylightSavingAdjust(newDate);
9082 /* Handle switch to/from daylight saving.
9083 * Hours may be non-zero on daylight saving cut-over:
9084 * > 12 when midnight changeover, but then cannot generate
9085 * midnight datetime, so jump to 1AM, otherwise reset.
9086 * @param date (Date) the date to check
9087 * @return (Date) the corrected date
9089 _daylightSavingAdjust: function(date) {
9093 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
9097 /* Set the date(s) directly. */
9098 _setDate: function(inst, date, noChange) {
9100 origMonth = inst.selectedMonth,
9101 origYear = inst.selectedYear,
9102 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
9104 inst.selectedDay = inst.currentDay = newDate.getDate();
9105 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
9106 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
9107 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
9108 this._notifyChange(inst);
9110 this._adjustInstDate(inst);
9112 inst.input.val(clear ? "" : this._formatDate(inst));
9116 /* Retrieve the date(s) directly. */
9117 _getDate: function(inst) {
9118 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
9119 this._daylightSavingAdjust(new Date(
9120 inst.currentYear, inst.currentMonth, inst.currentDay)));
9124 /* Attach the onxxx handlers. These are declared statically so
9125 * they work with static code transformers like Caja.
9127 _attachHandlers: function(inst) {
9128 var stepMonths = this._get(inst, "stepMonths"),
9129 id = "#" + inst.id.replace( /\\\\/g, "\\" );
9130 inst.dpDiv.find("[data-handler]").map(function () {
9133 window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, -stepMonths, "M");
9136 window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, +stepMonths, "M");
9139 window["DP_jQuery_" + dpuuid].datepicker._hideDatepicker();
9141 today: function () {
9142 window["DP_jQuery_" + dpuuid].datepicker._gotoToday(id);
9144 selectDay: function () {
9145 window["DP_jQuery_" + dpuuid].datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
9148 selectMonth: function () {
9149 window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "M");
9152 selectYear: function () {
9153 window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "Y");
9157 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
9161 /* Generate the HTML for the current state of the date picker. */
9162 _generateHTML: function(inst) {
9163 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
9164 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
9165 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
9166 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
9167 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
9168 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
9169 tempDate = new Date(),
9170 today = this._daylightSavingAdjust(
9171 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
9172 isRTL = this._get(inst, "isRTL"),
9173 showButtonPanel = this._get(inst, "showButtonPanel"),
9174 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
9175 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
9176 numMonths = this._getNumberOfMonths(inst),
9177 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
9178 stepMonths = this._get(inst, "stepMonths"),
9179 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
9180 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
9181 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
9182 minDate = this._getMinMaxDate(inst, "min"),
9183 maxDate = this._getMinMaxDate(inst, "max"),
9184 drawMonth = inst.drawMonth - showCurrentAtPos,
9185 drawYear = inst.drawYear;
9187 if (drawMonth < 0) {
9192 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
9193 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
9194 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
9195 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
9197 if (drawMonth < 0) {
9203 inst.drawMonth = drawMonth;
9204 inst.drawYear = drawYear;
9206 prevText = this._get(inst, "prevText");
9207 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
9208 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
9209 this._getFormatConfig(inst)));
9211 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
9212 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
9213 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
9214 (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>"));
9216 nextText = this._get(inst, "nextText");
9217 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
9218 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
9219 this._getFormatConfig(inst)));
9221 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
9222 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
9223 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
9224 (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>"));
9226 currentText = this._get(inst, "currentText");
9227 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
9228 currentText = (!navigationAsDateFormat ? currentText :
9229 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
9231 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'>" +
9232 this._get(inst, "closeText") + "</button>" : "");
9234 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
9235 (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'" +
9236 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
9238 firstDay = parseInt(this._get(inst, "firstDay"),10);
9239 firstDay = (isNaN(firstDay) ? 0 : firstDay);
9241 showWeek = this._get(inst, "showWeek");
9242 dayNames = this._get(inst, "dayNames");
9243 dayNamesMin = this._get(inst, "dayNamesMin");
9244 monthNames = this._get(inst, "monthNames");
9245 monthNamesShort = this._get(inst, "monthNamesShort");
9246 beforeShowDay = this._get(inst, "beforeShowDay");
9247 showOtherMonths = this._get(inst, "showOtherMonths");
9248 selectOtherMonths = this._get(inst, "selectOtherMonths");
9249 defaultDate = this._getDefaultDate(inst);
9252 for (row = 0; row < numMonths[0]; row++) {
9255 for (col = 0; col < numMonths[1]; col++) {
9256 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
9257 cornerClass = " ui-corner-all";
9260 calender += "<div class='ui-datepicker-group";
9261 if (numMonths[1] > 1) {
9263 case 0: calender += " ui-datepicker-group-first";
9264 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
9265 case numMonths[1]-1: calender += " ui-datepicker-group-last";
9266 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
9267 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
9272 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
9273 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
9274 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
9275 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
9276 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
9277 "</div><table class='ui-datepicker-calendar'><thead>" +
9279 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
9280 for (dow = 0; dow < 7; dow++) { // days of the week
9281 day = (dow + firstDay) % 7;
9282 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
9283 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
9285 calender += thead + "</tr></thead><tbody>";
9286 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
9287 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
9288 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
9290 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
9291 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
9292 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
9293 this.maxRows = numRows;
9294 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
9295 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
9297 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
9298 this._get(inst, "calculateWeek")(printDate) + "</td>");
9299 for (dow = 0; dow < 7; dow++) { // create date picker days
9300 daySettings = (beforeShowDay ?
9301 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
9302 otherMonth = (printDate.getMonth() !== drawMonth);
9303 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
9304 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
9305 tbody += "<td class='" +
9306 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
9307 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
9308 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
9309 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
9310 // or defaultDate is current printedDate and defaultDate is selectedDate
9311 " " + this._dayOverClass : "") + // highlight selected day
9312 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
9313 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
9314 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
9315 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
9316 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
9317 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
9318 (otherMonth && !showOtherMonths ? " " : // display for other months
9319 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
9320 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
9321 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
9322 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
9323 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
9324 printDate.setDate(printDate.getDate() + 1);
9325 printDate = this._daylightSavingAdjust(printDate);
9327 calender += tbody + "</tr>";
9330 if (drawMonth > 11) {
9334 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
9335 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
9340 html += buttonPanel;
9341 inst._keyEvent = false;
9345 /* Generate the month and year header. */
9346 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
9347 secondary, monthNames, monthNamesShort) {
9349 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
9350 changeMonth = this._get(inst, "changeMonth"),
9351 changeYear = this._get(inst, "changeYear"),
9352 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
9353 html = "<div class='ui-datepicker-title'>",
9357 if (secondary || !changeMonth) {
9358 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
9360 inMinYear = (minDate && minDate.getFullYear() === drawYear);
9361 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
9362 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
9363 for ( month = 0; month < 12; month++) {
9364 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
9365 monthHtml += "<option value='" + month + "'" +
9366 (month === drawMonth ? " selected='selected'" : "") +
9367 ">" + monthNamesShort[month] + "</option>";
9370 monthHtml += "</select>";
9373 if (!showMonthAfterYear) {
9374 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
9378 if ( !inst.yearshtml ) {
9379 inst.yearshtml = "";
9380 if (secondary || !changeYear) {
9381 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
9383 // determine range of years to display
9384 years = this._get(inst, "yearRange").split(":");
9385 thisYear = new Date().getFullYear();
9386 determineYear = function(value) {
9387 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
9388 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
9389 parseInt(value, 10)));
9390 return (isNaN(year) ? thisYear : year);
9392 year = determineYear(years[0]);
9393 endYear = Math.max(year, determineYear(years[1] || ""));
9394 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
9395 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
9396 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
9397 for (; year <= endYear; year++) {
9398 inst.yearshtml += "<option value='" + year + "'" +
9399 (year === drawYear ? " selected='selected'" : "") +
9400 ">" + year + "</option>";
9402 inst.yearshtml += "</select>";
9404 html += inst.yearshtml;
9405 inst.yearshtml = null;
9409 html += this._get(inst, "yearSuffix");
9410 if (showMonthAfterYear) {
9411 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
9413 html += "</div>"; // Close datepicker_header
9417 /* Adjust one of the date sub-fields. */
9418 _adjustInstDate: function(inst, offset, period) {
9419 var year = inst.drawYear + (period === "Y" ? offset : 0),
9420 month = inst.drawMonth + (period === "M" ? offset : 0),
9421 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
9422 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
9424 inst.selectedDay = date.getDate();
9425 inst.drawMonth = inst.selectedMonth = date.getMonth();
9426 inst.drawYear = inst.selectedYear = date.getFullYear();
9427 if (period === "M" || period === "Y") {
9428 this._notifyChange(inst);
9432 /* Ensure a date is within any min/max bounds. */
9433 _restrictMinMax: function(inst, date) {
9434 var minDate = this._getMinMaxDate(inst, "min"),
9435 maxDate = this._getMinMaxDate(inst, "max"),
9436 newDate = (minDate && date < minDate ? minDate : date);
9437 return (maxDate && newDate > maxDate ? maxDate : newDate);
9440 /* Notify change of month/year. */
9441 _notifyChange: function(inst) {
9442 var onChange = this._get(inst, "onChangeMonthYear");
9444 onChange.apply((inst.input ? inst.input[0] : null),
9445 [inst.selectedYear, inst.selectedMonth + 1, inst]);
9449 /* Determine the number of months to show. */
9450 _getNumberOfMonths: function(inst) {
9451 var numMonths = this._get(inst, "numberOfMonths");
9452 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
9455 /* Determine the current maximum date - ensure no time components are set. */
9456 _getMinMaxDate: function(inst, minMax) {
9457 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
9460 /* Find the number of days in a given month. */
9461 _getDaysInMonth: function(year, month) {
9462 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
9465 /* Find the day of the week of the first of a month. */
9466 _getFirstDayOfMonth: function(year, month) {
9467 return new Date(year, month, 1).getDay();
9470 /* Determines if we should allow a "next/prev" month display change. */
9471 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
9472 var numMonths = this._getNumberOfMonths(inst),
9473 date = this._daylightSavingAdjust(new Date(curYear,
9474 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
9477 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
9479 return this._isInRange(inst, date);
9482 /* Is the given date in the accepted range? */
9483 _isInRange: function(inst, date) {
9484 var yearSplit, currentYear,
9485 minDate = this._getMinMaxDate(inst, "min"),
9486 maxDate = this._getMinMaxDate(inst, "max"),
9489 years = this._get(inst, "yearRange");
9491 yearSplit = years.split(":");
9492 currentYear = new Date().getFullYear();
9493 minYear = parseInt(yearSplit[0], 10);
9494 maxYear = parseInt(yearSplit[1], 10);
9495 if ( yearSplit[0].match(/[+\-].*/) ) {
9496 minYear += currentYear;
9498 if ( yearSplit[1].match(/[+\-].*/) ) {
9499 maxYear += currentYear;
9503 return ((!minDate || date.getTime() >= minDate.getTime()) &&
9504 (!maxDate || date.getTime() <= maxDate.getTime()) &&
9505 (!minYear || date.getFullYear() >= minYear) &&
9506 (!maxYear || date.getFullYear() <= maxYear));
9509 /* Provide the configuration settings for formatting/parsing. */
9510 _getFormatConfig: function(inst) {
9511 var shortYearCutoff = this._get(inst, "shortYearCutoff");
9512 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
9513 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
9514 return {shortYearCutoff: shortYearCutoff,
9515 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
9516 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
9519 /* Format the given date for display. */
9520 _formatDate: function(inst, day, month, year) {
9522 inst.currentDay = inst.selectedDay;
9523 inst.currentMonth = inst.selectedMonth;
9524 inst.currentYear = inst.selectedYear;
9526 var date = (day ? (typeof day === "object" ? day :
9527 this._daylightSavingAdjust(new Date(year, month, day))) :
9528 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
9529 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
9534 * Bind hover events for datepicker elements.
9535 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
9536 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
9538 function bindHover(dpDiv) {
9539 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
9540 return dpDiv.delegate(selector, "mouseout", function() {
9541 $(this).removeClass("ui-state-hover");
9542 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9543 $(this).removeClass("ui-datepicker-prev-hover");
9545 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9546 $(this).removeClass("ui-datepicker-next-hover");
9549 .delegate(selector, "mouseover", function(){
9550 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
9551 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
9552 $(this).addClass("ui-state-hover");
9553 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9554 $(this).addClass("ui-datepicker-prev-hover");
9556 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9557 $(this).addClass("ui-datepicker-next-hover");
9563 /* jQuery extend now ignores nulls! */
9564 function extendRemove(target, props) {
9565 $.extend(target, props);
9566 for (var name in props) {
9567 if (props[name] == null) {
9568 target[name] = props[name];
9574 /* Invoke the datepicker functionality.
9575 @param options string - a command, optionally followed by additional parameters or
9576 Object - settings for attaching new datepicker functionality
9577 @return jQuery object */
9578 $.fn.datepicker = function(options){
9580 /* Verify an empty collection wasn't passed - Fixes #6976 */
9581 if ( !this.length ) {
9585 /* Initialise the date picker. */
9586 if (!$.datepicker.initialized) {
9587 $(document).mousedown($.datepicker._checkExternalClick);
9588 $.datepicker.initialized = true;
9591 /* Append datepicker main container to body if not exist. */
9592 if ($("#"+$.datepicker._mainDivId).length === 0) {
9593 $("body").append($.datepicker.dpDiv);
9596 var otherArgs = Array.prototype.slice.call(arguments, 1);
9597 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
9598 return $.datepicker["_" + options + "Datepicker"].
9599 apply($.datepicker, [this[0]].concat(otherArgs));
9601 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
9602 return $.datepicker["_" + options + "Datepicker"].
9603 apply($.datepicker, [this[0]].concat(otherArgs));
9605 return this.each(function() {
9606 typeof options === "string" ?
9607 $.datepicker["_" + options + "Datepicker"].
9608 apply($.datepicker, [this].concat(otherArgs)) :
9609 $.datepicker._attachDatepicker(this, options);
9613 $.datepicker = new Datepicker(); // singleton instance
9614 $.datepicker.initialized = false;
9615 $.datepicker.uuid = new Date().getTime();
9616 $.datepicker.version = "1.10.2";
9618 // Workaround for #4055
9619 // Add another global to avoid noConflict issues with inline event handlers
9620 window["DP_jQuery_" + dpuuid] = $;
9624 (function( $, undefined ) {
9626 var sizeRelatedOptions = {
9635 resizableRelatedOptions = {
9642 $.widget( "ui.dialog", {
9648 closeOnEscape: true,
9664 // Ensure the titlebar is always visible
9665 using: function( pos ) {
9666 var topOffset = $( this ).css( pos ).offset().top;
9667 if ( topOffset < 0 ) {
9668 $( this ).css( "top", pos.top - topOffset );
9690 _create: function() {
9691 this.originalCss = {
9692 display: this.element[0].style.display,
9693 width: this.element[0].style.width,
9694 minHeight: this.element[0].style.minHeight,
9695 maxHeight: this.element[0].style.maxHeight,
9696 height: this.element[0].style.height
9698 this.originalPosition = {
9699 parent: this.element.parent(),
9700 index: this.element.parent().children().index( this.element )
9702 this.originalTitle = this.element.attr("title");
9703 this.options.title = this.options.title || this.originalTitle;
9705 this._createWrapper();
9709 .removeAttr("title")
9710 .addClass("ui-dialog-content ui-widget-content")
9711 .appendTo( this.uiDialog );
9713 this._createTitlebar();
9714 this._createButtonPane();
9716 if ( this.options.draggable && $.fn.draggable ) {
9717 this._makeDraggable();
9719 if ( this.options.resizable && $.fn.resizable ) {
9720 this._makeResizable();
9723 this._isOpen = false;
9727 if ( this.options.autoOpen ) {
9732 _appendTo: function() {
9733 var element = this.options.appendTo;
9734 if ( element && (element.jquery || element.nodeType) ) {
9735 return $( element );
9737 return this.document.find( element || "body" ).eq( 0 );
9740 _destroy: function() {
9742 originalPosition = this.originalPosition;
9744 this._destroyOverlay();
9748 .removeClass("ui-dialog-content ui-widget-content")
9749 .css( this.originalCss )
9750 // Without detaching first, the following becomes really slow
9753 this.uiDialog.stop( true, true ).remove();
9755 if ( this.originalTitle ) {
9756 this.element.attr( "title", this.originalTitle );
9759 next = originalPosition.parent.children().eq( originalPosition.index );
9760 // Don't try to place the dialog next to itself (#8613)
9761 if ( next.length && next[0] !== this.element[0] ) {
9762 next.before( this.element );
9764 originalPosition.parent.append( this.element );
9768 widget: function() {
9769 return this.uiDialog;
9775 close: function( event ) {
9778 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
9782 this._isOpen = false;
9783 this._destroyOverlay();
9785 if ( !this.opener.filter(":focusable").focus().length ) {
9786 // Hiding a focused element doesn't trigger blur in WebKit
9787 // so in case we have nothing to focus on, explicitly blur the active element
9788 // https://bugs.webkit.org/show_bug.cgi?id=47182
9789 $( this.document[0].activeElement ).blur();
9792 this._hide( this.uiDialog, this.options.hide, function() {
9793 that._trigger( "close", event );
9797 isOpen: function() {
9798 return this._isOpen;
9801 moveToTop: function() {
9805 _moveToTop: function( event, silent ) {
9806 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
9807 if ( moved && !silent ) {
9808 this._trigger( "focus", event );
9815 if ( this._isOpen ) {
9816 if ( this._moveToTop() ) {
9817 this._focusTabbable();
9822 this._isOpen = true;
9823 this.opener = $( this.document[0].activeElement );
9827 this._createOverlay();
9828 this._moveToTop( null, true );
9829 this._show( this.uiDialog, this.options.show, function() {
9830 that._focusTabbable();
9831 that._trigger("focus");
9834 this._trigger("open");
9837 _focusTabbable: function() {
9838 // Set focus to the first match:
9839 // 1. First element inside the dialog matching [autofocus]
9840 // 2. Tabbable element inside the content element
9841 // 3. Tabbable element inside the buttonpane
9842 // 4. The close button
9843 // 5. The dialog itself
9844 var hasFocus = this.element.find("[autofocus]");
9845 if ( !hasFocus.length ) {
9846 hasFocus = this.element.find(":tabbable");
9848 if ( !hasFocus.length ) {
9849 hasFocus = this.uiDialogButtonPane.find(":tabbable");
9851 if ( !hasFocus.length ) {
9852 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
9854 if ( !hasFocus.length ) {
9855 hasFocus = this.uiDialog;
9857 hasFocus.eq( 0 ).focus();
9860 _keepFocus: function( event ) {
9861 function checkFocus() {
9862 var activeElement = this.document[0].activeElement,
9863 isActive = this.uiDialog[0] === activeElement ||
9864 $.contains( this.uiDialog[0], activeElement );
9866 this._focusTabbable();
9869 event.preventDefault();
9870 checkFocus.call( this );
9872 // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
9873 // so we check again later
9874 this._delay( checkFocus );
9877 _createWrapper: function() {
9878 this.uiDialog = $("<div>")
9879 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
9880 this.options.dialogClass )
9883 // Setting tabIndex makes the div focusable
9887 .appendTo( this._appendTo() );
9889 this._on( this.uiDialog, {
9890 keydown: function( event ) {
9891 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
9892 event.keyCode === $.ui.keyCode.ESCAPE ) {
9893 event.preventDefault();
9894 this.close( event );
9898 // prevent tabbing out of dialogs
9899 if ( event.keyCode !== $.ui.keyCode.TAB ) {
9902 var tabbables = this.uiDialog.find(":tabbable"),
9903 first = tabbables.filter(":first"),
9904 last = tabbables.filter(":last");
9906 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
9908 event.preventDefault();
9909 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
9911 event.preventDefault();
9914 mousedown: function( event ) {
9915 if ( this._moveToTop( event ) ) {
9916 this._focusTabbable();
9921 // We assume that any existing aria-describedby attribute means
9922 // that the dialog content is marked up properly
9923 // otherwise we brute force the content as the description
9924 if ( !this.element.find("[aria-describedby]").length ) {
9925 this.uiDialog.attr({
9926 "aria-describedby": this.element.uniqueId().attr("id")
9931 _createTitlebar: function() {
9934 this.uiDialogTitlebar = $("<div>")
9935 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
9936 .prependTo( this.uiDialog );
9937 this._on( this.uiDialogTitlebar, {
9938 mousedown: function( event ) {
9939 // Don't prevent click on close button (#8838)
9940 // Focusing a dialog that is partially scrolled out of view
9941 // causes the browser to scroll it into view, preventing the click event
9942 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
9943 // Dialog isn't getting focus when dragging (#8063)
9944 this.uiDialog.focus();
9949 this.uiDialogTitlebarClose = $("<button></button>")
9951 label: this.options.closeText,
9953 primary: "ui-icon-closethick"
9957 .addClass("ui-dialog-titlebar-close")
9958 .appendTo( this.uiDialogTitlebar );
9959 this._on( this.uiDialogTitlebarClose, {
9960 click: function( event ) {
9961 event.preventDefault();
9962 this.close( event );
9966 uiDialogTitle = $("<span>")
9968 .addClass("ui-dialog-title")
9969 .prependTo( this.uiDialogTitlebar );
9970 this._title( uiDialogTitle );
9972 this.uiDialog.attr({
9973 "aria-labelledby": uiDialogTitle.attr("id")
9977 _title: function( title ) {
9978 if ( !this.options.title ) {
9979 title.html(" ");
9981 title.text( this.options.title );
9984 _createButtonPane: function() {
9985 this.uiDialogButtonPane = $("<div>")
9986 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
9988 this.uiButtonSet = $("<div>")
9989 .addClass("ui-dialog-buttonset")
9990 .appendTo( this.uiDialogButtonPane );
9992 this._createButtons();
9995 _createButtons: function() {
9997 buttons = this.options.buttons;
9999 // if we already have a button pane, remove it
10000 this.uiDialogButtonPane.remove();
10001 this.uiButtonSet.empty();
10003 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
10004 this.uiDialog.removeClass("ui-dialog-buttons");
10008 $.each( buttons, function( name, props ) {
10009 var click, buttonOptions;
10010 props = $.isFunction( props ) ?
10011 { click: props, text: name } :
10013 // Default to a non-submitting button
10014 props = $.extend( { type: "button" }, props );
10015 // Change the context for the click callback to be the main element
10016 click = props.click;
10017 props.click = function() {
10018 click.apply( that.element[0], arguments );
10021 icons: props.icons,
10022 text: props.showText
10024 delete props.icons;
10025 delete props.showText;
10026 $( "<button></button>", props )
10027 .button( buttonOptions )
10028 .appendTo( that.uiButtonSet );
10030 this.uiDialog.addClass("ui-dialog-buttons");
10031 this.uiDialogButtonPane.appendTo( this.uiDialog );
10034 _makeDraggable: function() {
10036 options = this.options;
10038 function filteredUi( ui ) {
10040 position: ui.position,
10045 this.uiDialog.draggable({
10046 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
10047 handle: ".ui-dialog-titlebar",
10048 containment: "document",
10049 start: function( event, ui ) {
10050 $( this ).addClass("ui-dialog-dragging");
10051 that._blockFrames();
10052 that._trigger( "dragStart", event, filteredUi( ui ) );
10054 drag: function( event, ui ) {
10055 that._trigger( "drag", event, filteredUi( ui ) );
10057 stop: function( event, ui ) {
10058 options.position = [
10059 ui.position.left - that.document.scrollLeft(),
10060 ui.position.top - that.document.scrollTop()
10062 $( this ).removeClass("ui-dialog-dragging");
10063 that._unblockFrames();
10064 that._trigger( "dragStop", event, filteredUi( ui ) );
10069 _makeResizable: function() {
10071 options = this.options,
10072 handles = options.resizable,
10073 // .ui-resizable has position: relative defined in the stylesheet
10074 // but dialogs have to use absolute or fixed positioning
10075 position = this.uiDialog.css("position"),
10076 resizeHandles = typeof handles === "string" ?
10078 "n,e,s,w,se,sw,ne,nw";
10080 function filteredUi( ui ) {
10082 originalPosition: ui.originalPosition,
10083 originalSize: ui.originalSize,
10084 position: ui.position,
10089 this.uiDialog.resizable({
10090 cancel: ".ui-dialog-content",
10091 containment: "document",
10092 alsoResize: this.element,
10093 maxWidth: options.maxWidth,
10094 maxHeight: options.maxHeight,
10095 minWidth: options.minWidth,
10096 minHeight: this._minHeight(),
10097 handles: resizeHandles,
10098 start: function( event, ui ) {
10099 $( this ).addClass("ui-dialog-resizing");
10100 that._blockFrames();
10101 that._trigger( "resizeStart", event, filteredUi( ui ) );
10103 resize: function( event, ui ) {
10104 that._trigger( "resize", event, filteredUi( ui ) );
10106 stop: function( event, ui ) {
10107 options.height = $( this ).height();
10108 options.width = $( this ).width();
10109 $( this ).removeClass("ui-dialog-resizing");
10110 that._unblockFrames();
10111 that._trigger( "resizeStop", event, filteredUi( ui ) );
10114 .css( "position", position );
10117 _minHeight: function() {
10118 var options = this.options;
10120 return options.height === "auto" ?
10121 options.minHeight :
10122 Math.min( options.minHeight, options.height );
10125 _position: function() {
10126 // Need to show the dialog to get the actual offset in the position plugin
10127 var isVisible = this.uiDialog.is(":visible");
10128 if ( !isVisible ) {
10129 this.uiDialog.show();
10131 this.uiDialog.position( this.options.position );
10132 if ( !isVisible ) {
10133 this.uiDialog.hide();
10137 _setOptions: function( options ) {
10140 resizableOptions = {};
10142 $.each( options, function( key, value ) {
10143 that._setOption( key, value );
10145 if ( key in sizeRelatedOptions ) {
10148 if ( key in resizableRelatedOptions ) {
10149 resizableOptions[ key ] = value;
10157 if ( this.uiDialog.is(":data(ui-resizable)") ) {
10158 this.uiDialog.resizable( "option", resizableOptions );
10162 _setOption: function( key, value ) {
10163 /*jshint maxcomplexity:15*/
10164 var isDraggable, isResizable,
10165 uiDialog = this.uiDialog;
10167 if ( key === "dialogClass" ) {
10169 .removeClass( this.options.dialogClass )
10170 .addClass( value );
10173 if ( key === "disabled" ) {
10177 this._super( key, value );
10179 if ( key === "appendTo" ) {
10180 this.uiDialog.appendTo( this._appendTo() );
10183 if ( key === "buttons" ) {
10184 this._createButtons();
10187 if ( key === "closeText" ) {
10188 this.uiDialogTitlebarClose.button({
10189 // Ensure that we always pass a string
10194 if ( key === "draggable" ) {
10195 isDraggable = uiDialog.is(":data(ui-draggable)");
10196 if ( isDraggable && !value ) {
10197 uiDialog.draggable("destroy");
10200 if ( !isDraggable && value ) {
10201 this._makeDraggable();
10205 if ( key === "position" ) {
10209 if ( key === "resizable" ) {
10210 // currently resizable, becoming non-resizable
10211 isResizable = uiDialog.is(":data(ui-resizable)");
10212 if ( isResizable && !value ) {
10213 uiDialog.resizable("destroy");
10216 // currently resizable, changing handles
10217 if ( isResizable && typeof value === "string" ) {
10218 uiDialog.resizable( "option", "handles", value );
10221 // currently non-resizable, becoming resizable
10222 if ( !isResizable && value !== false ) {
10223 this._makeResizable();
10227 if ( key === "title" ) {
10228 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
10232 _size: function() {
10233 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
10234 // divs will both have width and height set, so we need to reset them
10235 var nonContentHeight, minContentHeight, maxContentHeight,
10236 options = this.options;
10238 // Reset content sizing
10239 this.element.show().css({
10246 if ( options.minWidth > options.width ) {
10247 options.width = options.minWidth;
10250 // reset wrapper sizing
10251 // determine the height of all the non-content elements
10252 nonContentHeight = this.uiDialog.css({
10254 width: options.width
10257 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
10258 maxContentHeight = typeof options.maxHeight === "number" ?
10259 Math.max( 0, options.maxHeight - nonContentHeight ) :
10262 if ( options.height === "auto" ) {
10264 minHeight: minContentHeight,
10265 maxHeight: maxContentHeight,
10269 this.element.height( Math.max( 0, options.height - nonContentHeight ) );
10272 if (this.uiDialog.is(":data(ui-resizable)") ) {
10273 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
10277 _blockFrames: function() {
10278 this.iframeBlocks = this.document.find( "iframe" ).map(function() {
10279 var iframe = $( this );
10281 return $( "<div>" )
10283 position: "absolute",
10284 width: iframe.outerWidth(),
10285 height: iframe.outerHeight()
10287 .appendTo( iframe.parent() )
10288 .offset( iframe.offset() )[0];
10292 _unblockFrames: function() {
10293 if ( this.iframeBlocks ) {
10294 this.iframeBlocks.remove();
10295 delete this.iframeBlocks;
10299 _allowInteraction: function( event ) {
10300 if ( $( event.target ).closest(".ui-dialog").length ) {
10304 // TODO: Remove hack when datepicker implements
10305 // the .ui-front logic (#8989)
10306 return !!$( event.target ).closest(".ui-datepicker").length;
10309 _createOverlay: function() {
10310 if ( !this.options.modal ) {
10315 widgetFullName = this.widgetFullName;
10316 if ( !$.ui.dialog.overlayInstances ) {
10317 // Prevent use of anchors and inputs.
10318 // We use a delay in case the overlay is created from an
10319 // event that we're going to be cancelling. (#2804)
10320 this._delay(function() {
10321 // Handle .dialog().dialog("close") (#4065)
10322 if ( $.ui.dialog.overlayInstances ) {
10323 this.document.bind( "focusin.dialog", function( event ) {
10324 if ( !that._allowInteraction( event ) ) {
10325 event.preventDefault();
10326 $(".ui-dialog:visible:last .ui-dialog-content")
10327 .data( widgetFullName )._focusTabbable();
10334 this.overlay = $("<div>")
10335 .addClass("ui-widget-overlay ui-front")
10336 .appendTo( this._appendTo() );
10337 this._on( this.overlay, {
10338 mousedown: "_keepFocus"
10340 $.ui.dialog.overlayInstances++;
10343 _destroyOverlay: function() {
10344 if ( !this.options.modal ) {
10348 if ( this.overlay ) {
10349 $.ui.dialog.overlayInstances--;
10351 if ( !$.ui.dialog.overlayInstances ) {
10352 this.document.unbind( "focusin.dialog" );
10354 this.overlay.remove();
10355 this.overlay = null;
10360 $.ui.dialog.overlayInstances = 0;
10363 if ( $.uiBackCompat !== false ) {
10364 // position option with array notation
10365 // just override with old implementation
10366 $.widget( "ui.dialog", $.ui.dialog, {
10367 _position: function() {
10368 var position = this.options.position,
10374 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
10375 myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
10376 if ( myAt.length === 1 ) {
10380 $.each( [ "left", "top" ], function( i, offsetPosition ) {
10381 if ( +myAt[ i ] === myAt[ i ] ) {
10382 offset[ i ] = myAt[ i ];
10383 myAt[ i ] = offsetPosition;
10388 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
10389 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
10394 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
10396 position = $.ui.dialog.prototype.options.position;
10399 // need to show the dialog to get the actual offset in the position plugin
10400 isVisible = this.uiDialog.is(":visible");
10401 if ( !isVisible ) {
10402 this.uiDialog.show();
10404 this.uiDialog.position( position );
10405 if ( !isVisible ) {
10406 this.uiDialog.hide();
10414 (function( $, undefined ) {
10416 var rvertical = /up|down|vertical/,
10417 rpositivemotion = /up|left|vertical|horizontal/;
10419 $.effects.effect.blind = function( o, done ) {
10421 var el = $( this ),
10422 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10423 mode = $.effects.setMode( el, o.mode || "hide" ),
10424 direction = o.direction || "up",
10425 vertical = rvertical.test( direction ),
10426 ref = vertical ? "height" : "width",
10427 ref2 = vertical ? "top" : "left",
10428 motion = rpositivemotion.test( direction ),
10430 show = mode === "show",
10431 wrapper, distance, margin;
10433 // if already wrapped, the wrapper's properties are my property. #6245
10434 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
10435 $.effects.save( el.parent(), props );
10437 $.effects.save( el, props );
10440 wrapper = $.effects.createWrapper( el ).css({
10444 distance = wrapper[ ref ]();
10445 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
10447 animation[ ref ] = show ? distance : 0;
10450 .css( vertical ? "bottom" : "right", 0 )
10451 .css( vertical ? "top" : "left", "auto" )
10452 .css({ position: "absolute" });
10454 animation[ ref2 ] = show ? margin : distance + margin;
10457 // start at 0 if we are showing
10459 wrapper.css( ref, 0 );
10461 wrapper.css( ref2, margin + distance );
10466 wrapper.animate( animation, {
10467 duration: o.duration,
10470 complete: function() {
10471 if ( mode === "hide" ) {
10474 $.effects.restore( el, props );
10475 $.effects.removeWrapper( el );
10484 (function( $, undefined ) {
10486 $.effects.effect.bounce = function( o, done ) {
10487 var el = $( this ),
10488 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10491 mode = $.effects.setMode( el, o.mode || "effect" ),
10492 hide = mode === "hide",
10493 show = mode === "show",
10494 direction = o.direction || "up",
10495 distance = o.distance,
10496 times = o.times || 5,
10498 // number of internal animations
10499 anims = times * 2 + ( show || hide ? 1 : 0 ),
10500 speed = o.duration / anims,
10504 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10505 motion = ( direction === "up" || direction === "left" ),
10510 // we will need to re-assemble the queue to stack our animations in place
10511 queue = el.queue(),
10512 queuelen = queue.length;
10514 // Avoid touching opacity to prevent clearType and PNG issues in IE
10515 if ( show || hide ) {
10516 props.push( "opacity" );
10519 $.effects.save( el, props );
10521 $.effects.createWrapper( el ); // Create Wrapper
10523 // default distance for the BIGGEST bounce is the outer Distance / 3
10525 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
10529 downAnim = { opacity: 1 };
10530 downAnim[ ref ] = 0;
10532 // if we are showing, force opacity 0 and set the initial position
10533 // then do the "first" animation
10534 el.css( "opacity", 0 )
10535 .css( ref, motion ? -distance * 2 : distance * 2 )
10536 .animate( downAnim, speed, easing );
10539 // start at the smallest distance if we are hiding
10541 distance = distance / Math.pow( 2, times - 1 );
10545 downAnim[ ref ] = 0;
10546 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
10547 for ( i = 0; i < times; i++ ) {
10549 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10551 el.animate( upAnim, speed, easing )
10552 .animate( downAnim, speed, easing );
10554 distance = hide ? distance * 2 : distance / 2;
10557 // Last Bounce when Hiding
10559 upAnim = { opacity: 0 };
10560 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10562 el.animate( upAnim, speed, easing );
10565 el.queue(function() {
10569 $.effects.restore( el, props );
10570 $.effects.removeWrapper( el );
10574 // inject all the animations we just queued to be first in line (after "inprogress")
10575 if ( queuelen > 1) {
10576 queue.splice.apply( queue,
10577 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10585 (function( $, undefined ) {
10587 $.effects.effect.clip = function( o, done ) {
10589 var el = $( this ),
10590 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10591 mode = $.effects.setMode( el, o.mode || "hide" ),
10592 show = mode === "show",
10593 direction = o.direction || "vertical",
10594 vert = direction === "vertical",
10595 size = vert ? "height" : "width",
10596 position = vert ? "top" : "left",
10598 wrapper, animate, distance;
10601 $.effects.save( el, props );
10605 wrapper = $.effects.createWrapper( el ).css({
10608 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
10609 distance = animate[ size ]();
10613 animate.css( size, 0 );
10614 animate.css( position, distance / 2 );
10617 // Create Animation Object:
10618 animation[ size ] = show ? distance : 0;
10619 animation[ position ] = show ? 0 : distance / 2;
10622 animate.animate( animation, {
10624 duration: o.duration,
10626 complete: function() {
10630 $.effects.restore( el, props );
10631 $.effects.removeWrapper( el );
10640 (function( $, undefined ) {
10642 $.effects.effect.drop = function( o, done ) {
10644 var el = $( this ),
10645 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
10646 mode = $.effects.setMode( el, o.mode || "hide" ),
10647 show = mode === "show",
10648 direction = o.direction || "left",
10649 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10650 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
10652 opacity: show ? 1 : 0
10657 $.effects.save( el, props );
10659 $.effects.createWrapper( el );
10661 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
10665 .css( "opacity", 0 )
10666 .css( ref, motion === "pos" ? -distance : distance );
10670 animation[ ref ] = ( show ?
10671 ( motion === "pos" ? "+=" : "-=" ) :
10672 ( motion === "pos" ? "-=" : "+=" ) ) +
10676 el.animate( animation, {
10678 duration: o.duration,
10680 complete: function() {
10681 if ( mode === "hide" ) {
10684 $.effects.restore( el, props );
10685 $.effects.removeWrapper( el );
10693 (function( $, undefined ) {
10695 $.effects.effect.explode = function( o, done ) {
10697 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
10700 mode = $.effects.setMode( el, o.mode || "hide" ),
10701 show = mode === "show",
10703 // show and then visibility:hidden the element before calculating offset
10704 offset = el.show().css( "visibility", "hidden" ).offset(),
10706 // width and height of a piece
10707 width = Math.ceil( el.outerWidth() / cells ),
10708 height = Math.ceil( el.outerHeight() / rows ),
10712 i, j, left, top, mx, my;
10714 // children animate complete:
10715 function childComplete() {
10716 pieces.push( this );
10717 if ( pieces.length === rows * cells ) {
10722 // clone the element for each row and cell.
10723 for( i = 0; i < rows ; i++ ) { // ===>
10724 top = offset.top + i * height;
10725 my = i - ( rows - 1 ) / 2 ;
10727 for( j = 0; j < cells ; j++ ) { // |||
10728 left = offset.left + j * width;
10729 mx = j - ( cells - 1 ) / 2 ;
10731 // Create a clone of the now hidden main element that will be absolute positioned
10732 // within a wrapper div off the -left and -top equal to size of our pieces
10735 .appendTo( "body" )
10736 .wrap( "<div></div>" )
10738 position: "absolute",
10739 visibility: "visible",
10744 // select the wrapper - make it overflow: hidden and absolute positioned based on
10745 // where the original was located +left and +top equal to the size of pieces
10747 .addClass( "ui-effects-explode" )
10749 position: "absolute",
10750 overflow: "hidden",
10753 left: left + ( show ? mx * width : 0 ),
10754 top: top + ( show ? my * height : 0 ),
10755 opacity: show ? 0 : 1
10757 left: left + ( show ? 0 : mx * width ),
10758 top: top + ( show ? 0 : my * height ),
10759 opacity: show ? 1 : 0
10760 }, o.duration || 500, o.easing, childComplete );
10764 function animComplete() {
10766 visibility: "visible"
10768 $( pieces ).remove();
10778 (function( $, undefined ) {
10780 $.effects.effect.fade = function( o, done ) {
10781 var el = $( this ),
10782 mode = $.effects.setMode( el, o.mode || "toggle" );
10788 duration: o.duration,
10796 (function( $, undefined ) {
10798 $.effects.effect.fold = function( o, done ) {
10801 var el = $( this ),
10802 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10803 mode = $.effects.setMode( el, o.mode || "hide" ),
10804 show = mode === "show",
10805 hide = mode === "hide",
10806 size = o.size || 15,
10807 percent = /([0-9]+)%/.exec( size ),
10808 horizFirst = !!o.horizFirst,
10809 widthFirst = show !== horizFirst,
10810 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
10811 duration = o.duration / 2,
10816 $.effects.save( el, props );
10820 wrapper = $.effects.createWrapper( el ).css({
10823 distance = widthFirst ?
10824 [ wrapper.width(), wrapper.height() ] :
10825 [ wrapper.height(), wrapper.width() ];
10828 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
10831 wrapper.css( horizFirst ? {
10841 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
10842 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
10846 .animate( animation1, duration, o.easing )
10847 .animate( animation2, duration, o.easing, function() {
10851 $.effects.restore( el, props );
10852 $.effects.removeWrapper( el );
10860 (function( $, undefined ) {
10862 $.effects.effect.highlight = function( o, done ) {
10863 var elem = $( this ),
10864 props = [ "backgroundImage", "backgroundColor", "opacity" ],
10865 mode = $.effects.setMode( elem, o.mode || "show" ),
10867 backgroundColor: elem.css( "backgroundColor" )
10870 if (mode === "hide") {
10871 animation.opacity = 0;
10874 $.effects.save( elem, props );
10879 backgroundImage: "none",
10880 backgroundColor: o.color || "#ffff99"
10882 .animate( animation, {
10884 duration: o.duration,
10886 complete: function() {
10887 if ( mode === "hide" ) {
10890 $.effects.restore( elem, props );
10898 (function( $, undefined ) {
10900 $.effects.effect.pulsate = function( o, done ) {
10901 var elem = $( this ),
10902 mode = $.effects.setMode( elem, o.mode || "show" ),
10903 show = mode === "show",
10904 hide = mode === "hide",
10905 showhide = ( show || mode === "hide" ),
10907 // showing or hiding leaves of the "last" animation
10908 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
10909 duration = o.duration / anims,
10911 queue = elem.queue(),
10912 queuelen = queue.length,
10915 if ( show || !elem.is(":visible")) {
10916 elem.css( "opacity", 0 ).show();
10920 // anims - 1 opacity "toggles"
10921 for ( i = 1; i < anims; i++ ) {
10924 }, duration, o.easing );
10925 animateTo = 1 - animateTo;
10930 }, duration, o.easing);
10932 elem.queue(function() {
10939 // We just queued up "anims" animations, we need to put them next in the queue
10940 if ( queuelen > 1 ) {
10941 queue.splice.apply( queue,
10942 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10949 (function( $, undefined ) {
10951 $.effects.effect.puff = function( o, done ) {
10952 var elem = $( this ),
10953 mode = $.effects.setMode( elem, o.mode || "hide" ),
10954 hide = mode === "hide",
10955 percent = parseInt( o.percent, 10 ) || 150,
10956 factor = percent / 100,
10958 height: elem.height(),
10959 width: elem.width(),
10960 outerHeight: elem.outerHeight(),
10961 outerWidth: elem.outerWidth()
10970 percent: hide ? percent : 100,
10974 height: original.height * factor,
10975 width: original.width * factor,
10976 outerHeight: original.outerHeight * factor,
10977 outerWidth: original.outerWidth * factor
10984 $.effects.effect.scale = function( o, done ) {
10987 var el = $( this ),
10988 options = $.extend( true, {}, o ),
10989 mode = $.effects.setMode( el, o.mode || "effect" ),
10990 percent = parseInt( o.percent, 10 ) ||
10991 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
10992 direction = o.direction || "both",
10995 height: el.height(),
10997 outerHeight: el.outerHeight(),
10998 outerWidth: el.outerWidth()
11001 y: direction !== "horizontal" ? (percent / 100) : 1,
11002 x: direction !== "vertical" ? (percent / 100) : 1
11005 // We are going to pass this effect to the size effect:
11006 options.effect = "size";
11007 options.queue = false;
11008 options.complete = done;
11010 // Set default origin and restore for show/hide
11011 if ( mode !== "effect" ) {
11012 options.origin = origin || ["middle","center"];
11013 options.restore = true;
11016 options.from = o.from || ( mode === "show" ? {
11023 height: original.height * factor.y,
11024 width: original.width * factor.x,
11025 outerHeight: original.outerHeight * factor.y,
11026 outerWidth: original.outerWidth * factor.x
11029 // Fade option to support puff
11030 if ( options.fade ) {
11031 if ( mode === "show" ) {
11032 options.from.opacity = 0;
11033 options.to.opacity = 1;
11035 if ( mode === "hide" ) {
11036 options.from.opacity = 1;
11037 options.to.opacity = 0;
11042 el.effect( options );
11046 $.effects.effect.size = function( o, done ) {
11049 var original, baseline, factor,
11051 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
11054 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
11056 // Copy for children
11057 props2 = [ "width", "height", "overflow" ],
11058 cProps = [ "fontSize" ],
11059 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
11060 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
11063 mode = $.effects.setMode( el, o.mode || "effect" ),
11064 restore = o.restore || mode !== "effect",
11065 scale = o.scale || "both",
11066 origin = o.origin || [ "middle", "center" ],
11067 position = el.css( "position" ),
11068 props = restore ? props0 : props1,
11076 if ( mode === "show" ) {
11080 height: el.height(),
11082 outerHeight: el.outerHeight(),
11083 outerWidth: el.outerWidth()
11086 if ( o.mode === "toggle" && mode === "show" ) {
11087 el.from = o.to || zero;
11088 el.to = o.from || original;
11090 el.from = o.from || ( mode === "show" ? zero : original );
11091 el.to = o.to || ( mode === "hide" ? zero : original );
11094 // Set scaling factor
11097 y: el.from.height / original.height,
11098 x: el.from.width / original.width
11101 y: el.to.height / original.height,
11102 x: el.to.width / original.width
11106 // Scale the css box
11107 if ( scale === "box" || scale === "both" ) {
11109 // Vertical props scaling
11110 if ( factor.from.y !== factor.to.y ) {
11111 props = props.concat( vProps );
11112 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
11113 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
11116 // Horizontal props scaling
11117 if ( factor.from.x !== factor.to.x ) {
11118 props = props.concat( hProps );
11119 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
11120 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
11124 // Scale the content
11125 if ( scale === "content" || scale === "both" ) {
11127 // Vertical props scaling
11128 if ( factor.from.y !== factor.to.y ) {
11129 props = props.concat( cProps ).concat( props2 );
11130 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
11131 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
11135 $.effects.save( el, props );
11137 $.effects.createWrapper( el );
11138 el.css( "overflow", "hidden" ).css( el.from );
11141 if (origin) { // Calculate baseline shifts
11142 baseline = $.effects.getBaseline( origin, original );
11143 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
11144 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
11145 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
11146 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
11148 el.css( el.from ); // set top & left
11151 if ( scale === "content" || scale === "both" ) { // Scale the children
11153 // Add margins/font-size
11154 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
11155 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
11156 props2 = props0.concat(vProps).concat(hProps);
11158 el.find( "*[width]" ).each( function(){
11159 var child = $( this ),
11161 height: child.height(),
11162 width: child.width(),
11163 outerHeight: child.outerHeight(),
11164 outerWidth: child.outerWidth()
11167 $.effects.save(child, props2);
11171 height: c_original.height * factor.from.y,
11172 width: c_original.width * factor.from.x,
11173 outerHeight: c_original.outerHeight * factor.from.y,
11174 outerWidth: c_original.outerWidth * factor.from.x
11177 height: c_original.height * factor.to.y,
11178 width: c_original.width * factor.to.x,
11179 outerHeight: c_original.height * factor.to.y,
11180 outerWidth: c_original.width * factor.to.x
11183 // Vertical props scaling
11184 if ( factor.from.y !== factor.to.y ) {
11185 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
11186 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
11189 // Horizontal props scaling
11190 if ( factor.from.x !== factor.to.x ) {
11191 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
11192 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
11195 // Animate children
11196 child.css( child.from );
11197 child.animate( child.to, o.duration, o.easing, function() {
11199 // Restore children
11201 $.effects.restore( child, props2 );
11208 el.animate( el.to, {
11210 duration: o.duration,
11212 complete: function() {
11213 if ( el.to.opacity === 0 ) {
11214 el.css( "opacity", el.from.opacity );
11216 if( mode === "hide" ) {
11219 $.effects.restore( el, props );
11222 // we need to calculate our new positioning based on the scaling
11223 if ( position === "static" ) {
11225 position: "relative",
11230 $.each([ "top", "left" ], function( idx, pos ) {
11231 el.css( pos, function( _, str ) {
11232 var val = parseInt( str, 10 ),
11233 toRef = idx ? el.to.left : el.to.top;
11235 // if original was "auto", recalculate the new value from wrapper
11236 if ( str === "auto" ) {
11237 return toRef + "px";
11240 return val + toRef + "px";
11246 $.effects.removeWrapper( el );
11255 (function( $, undefined ) {
11257 $.effects.effect.shake = function( o, done ) {
11259 var el = $( this ),
11260 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
11261 mode = $.effects.setMode( el, o.mode || "effect" ),
11262 direction = o.direction || "left",
11263 distance = o.distance || 20,
11264 times = o.times || 3,
11265 anims = times * 2 + 1,
11266 speed = Math.round(o.duration/anims),
11267 ref = (direction === "up" || direction === "down") ? "top" : "left",
11268 positiveMotion = (direction === "up" || direction === "left"),
11274 // we will need to re-assemble the queue to stack our animations in place
11275 queue = el.queue(),
11276 queuelen = queue.length;
11278 $.effects.save( el, props );
11280 $.effects.createWrapper( el );
11283 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
11284 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
11285 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
11288 el.animate( animation, speed, o.easing );
11291 for ( i = 1; i < times; i++ ) {
11292 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
11295 .animate( animation1, speed, o.easing )
11296 .animate( animation, speed / 2, o.easing )
11297 .queue(function() {
11298 if ( mode === "hide" ) {
11301 $.effects.restore( el, props );
11302 $.effects.removeWrapper( el );
11306 // inject all the animations we just queued to be first in line (after "inprogress")
11307 if ( queuelen > 1) {
11308 queue.splice.apply( queue,
11309 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
11317 (function( $, undefined ) {
11319 $.effects.effect.slide = function( o, done ) {
11322 var el = $( this ),
11323 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
11324 mode = $.effects.setMode( el, o.mode || "show" ),
11325 show = mode === "show",
11326 direction = o.direction || "left",
11327 ref = (direction === "up" || direction === "down") ? "top" : "left",
11328 positiveMotion = (direction === "up" || direction === "left"),
11333 $.effects.save( el, props );
11335 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
11337 $.effects.createWrapper( el ).css({
11342 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
11346 animation[ ref ] = ( show ?
11347 ( positiveMotion ? "+=" : "-=") :
11348 ( positiveMotion ? "-=" : "+=")) +
11352 el.animate( animation, {
11354 duration: o.duration,
11356 complete: function() {
11357 if ( mode === "hide" ) {
11360 $.effects.restore( el, props );
11361 $.effects.removeWrapper( el );
11369 (function( $, undefined ) {
11371 $.effects.effect.transfer = function( o, done ) {
11372 var elem = $( this ),
11373 target = $( o.to ),
11374 targetFixed = target.css( "position" ) === "fixed",
11376 fixTop = targetFixed ? body.scrollTop() : 0,
11377 fixLeft = targetFixed ? body.scrollLeft() : 0,
11378 endPosition = target.offset(),
11380 top: endPosition.top - fixTop ,
11381 left: endPosition.left - fixLeft ,
11382 height: target.innerHeight(),
11383 width: target.innerWidth()
11385 startPosition = elem.offset(),
11386 transfer = $( "<div class='ui-effects-transfer'></div>" )
11387 .appendTo( document.body )
11388 .addClass( o.className )
11390 top: startPosition.top - fixTop ,
11391 left: startPosition.left - fixLeft ,
11392 height: elem.innerHeight(),
11393 width: elem.innerWidth(),
11394 position: targetFixed ? "fixed" : "absolute"
11396 .animate( animation, o.duration, o.easing, function() {
11404 (function( $, undefined ) {
11406 $.widget( "ui.menu", {
11408 defaultElement: "<ul>",
11412 submenu: "ui-icon-carat-1-e"
11427 _create: function() {
11428 this.activeMenu = this.element;
11429 // flag used to prevent firing of the click handler
11430 // as the event bubbles up through nested menus
11431 this.mouseHandled = false;
11434 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11435 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
11437 role: this.options.role,
11440 // need to catch all clicks on disabled menu
11441 // not possible through _on
11442 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
11443 if ( this.options.disabled ) {
11444 event.preventDefault();
11448 if ( this.options.disabled ) {
11450 .addClass( "ui-state-disabled" )
11451 .attr( "aria-disabled", "true" );
11455 // Prevent focus from sticking to links inside menu after clicking
11456 // them (focus should always stay on UL during navigation).
11457 "mousedown .ui-menu-item > a": function( event ) {
11458 event.preventDefault();
11460 "click .ui-state-disabled > a": function( event ) {
11461 event.preventDefault();
11463 "click .ui-menu-item:has(a)": function( event ) {
11464 var target = $( event.target ).closest( ".ui-menu-item" );
11465 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
11466 this.mouseHandled = true;
11468 this.select( event );
11469 // Open submenu on click
11470 if ( target.has( ".ui-menu" ).length ) {
11471 this.expand( event );
11472 } else if ( !this.element.is( ":focus" ) ) {
11473 // Redirect focus to the menu
11474 this.element.trigger( "focus", [ true ] );
11476 // If the active item is on the top level, let it stay active.
11477 // Otherwise, blur the active item since it is no longer visible.
11478 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
11479 clearTimeout( this.timer );
11484 "mouseenter .ui-menu-item": function( event ) {
11485 var target = $( event.currentTarget );
11486 // Remove ui-state-active class from siblings of the newly focused menu item
11487 // to avoid a jump caused by adjacent elements both having a class with a border
11488 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
11489 this.focus( event, target );
11491 mouseleave: "collapseAll",
11492 "mouseleave .ui-menu": "collapseAll",
11493 focus: function( event, keepActiveItem ) {
11494 // If there's already an active item, keep it active
11495 // If not, activate the first item
11496 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
11498 if ( !keepActiveItem ) {
11499 this.focus( event, item );
11502 blur: function( event ) {
11503 this._delay(function() {
11504 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
11505 this.collapseAll( event );
11509 keydown: "_keydown"
11514 // Clicks outside of a menu collapse any open menus
11515 this._on( this.document, {
11516 click: function( event ) {
11517 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
11518 this.collapseAll( event );
11521 // Reset the mouseHandled flag
11522 this.mouseHandled = false;
11527 _destroy: function() {
11528 // Destroy (sub)menus
11530 .removeAttr( "aria-activedescendant" )
11531 .find( ".ui-menu" ).addBack()
11532 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
11533 .removeAttr( "role" )
11534 .removeAttr( "tabIndex" )
11535 .removeAttr( "aria-labelledby" )
11536 .removeAttr( "aria-expanded" )
11537 .removeAttr( "aria-hidden" )
11538 .removeAttr( "aria-disabled" )
11542 // Destroy menu items
11543 this.element.find( ".ui-menu-item" )
11544 .removeClass( "ui-menu-item" )
11545 .removeAttr( "role" )
11546 .removeAttr( "aria-disabled" )
11549 .removeClass( "ui-corner-all ui-state-hover" )
11550 .removeAttr( "tabIndex" )
11551 .removeAttr( "role" )
11552 .removeAttr( "aria-haspopup" )
11553 .children().each( function() {
11554 var elem = $( this );
11555 if ( elem.data( "ui-menu-submenu-carat" ) ) {
11560 // Destroy menu dividers
11561 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
11564 _keydown: function( event ) {
11565 /*jshint maxcomplexity:20*/
11566 var match, prev, character, skip, regex,
11567 preventDefault = true;
11569 function escape( value ) {
11570 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
11573 switch ( event.keyCode ) {
11574 case $.ui.keyCode.PAGE_UP:
11575 this.previousPage( event );
11577 case $.ui.keyCode.PAGE_DOWN:
11578 this.nextPage( event );
11580 case $.ui.keyCode.HOME:
11581 this._move( "first", "first", event );
11583 case $.ui.keyCode.END:
11584 this._move( "last", "last", event );
11586 case $.ui.keyCode.UP:
11587 this.previous( event );
11589 case $.ui.keyCode.DOWN:
11590 this.next( event );
11592 case $.ui.keyCode.LEFT:
11593 this.collapse( event );
11595 case $.ui.keyCode.RIGHT:
11596 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
11597 this.expand( event );
11600 case $.ui.keyCode.ENTER:
11601 case $.ui.keyCode.SPACE:
11602 this._activate( event );
11604 case $.ui.keyCode.ESCAPE:
11605 this.collapse( event );
11608 preventDefault = false;
11609 prev = this.previousFilter || "";
11610 character = String.fromCharCode( event.keyCode );
11613 clearTimeout( this.filterTimer );
11615 if ( character === prev ) {
11618 character = prev + character;
11621 regex = new RegExp( "^" + escape( character ), "i" );
11622 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11623 return regex.test( $( this ).children( "a" ).text() );
11625 match = skip && match.index( this.active.next() ) !== -1 ?
11626 this.active.nextAll( ".ui-menu-item" ) :
11629 // If no matches on the current filter, reset to the last character pressed
11630 // to move down the menu to the first item that starts with that character
11631 if ( !match.length ) {
11632 character = String.fromCharCode( event.keyCode );
11633 regex = new RegExp( "^" + escape( character ), "i" );
11634 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11635 return regex.test( $( this ).children( "a" ).text() );
11639 if ( match.length ) {
11640 this.focus( event, match );
11641 if ( match.length > 1 ) {
11642 this.previousFilter = character;
11643 this.filterTimer = this._delay(function() {
11644 delete this.previousFilter;
11647 delete this.previousFilter;
11650 delete this.previousFilter;
11654 if ( preventDefault ) {
11655 event.preventDefault();
11659 _activate: function( event ) {
11660 if ( !this.active.is( ".ui-state-disabled" ) ) {
11661 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
11662 this.expand( event );
11664 this.select( event );
11669 refresh: function() {
11671 icon = this.options.icons.submenu,
11672 submenus = this.element.find( this.options.menus );
11674 // Initialize nested menus
11675 submenus.filter( ":not(.ui-menu)" )
11676 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11679 role: this.options.role,
11680 "aria-hidden": "true",
11681 "aria-expanded": "false"
11684 var menu = $( this ),
11685 item = menu.prev( "a" ),
11686 submenuCarat = $( "<span>" )
11687 .addClass( "ui-menu-icon ui-icon " + icon )
11688 .data( "ui-menu-submenu-carat", true );
11691 .attr( "aria-haspopup", "true" )
11692 .prepend( submenuCarat );
11693 menu.attr( "aria-labelledby", item.attr( "id" ) );
11696 menus = submenus.add( this.element );
11698 // Don't refresh list items that are already adapted
11699 menus.children( ":not(.ui-menu-item):has(a)" )
11700 .addClass( "ui-menu-item" )
11701 .attr( "role", "presentation" )
11704 .addClass( "ui-corner-all" )
11707 role: this._itemRole()
11710 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
11711 menus.children( ":not(.ui-menu-item)" ).each(function() {
11712 var item = $( this );
11713 // hyphen, em dash, en dash
11714 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
11715 item.addClass( "ui-widget-content ui-menu-divider" );
11719 // Add aria-disabled attribute to any disabled menu item
11720 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
11722 // If the active item has been removed, blur the menu
11723 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
11728 _itemRole: function() {
11732 }[ this.options.role ];
11735 _setOption: function( key, value ) {
11736 if ( key === "icons" ) {
11737 this.element.find( ".ui-menu-icon" )
11738 .removeClass( this.options.icons.submenu )
11739 .addClass( value.submenu );
11741 this._super( key, value );
11744 focus: function( event, item ) {
11745 var nested, focused;
11746 this.blur( event, event && event.type === "focus" );
11748 this._scrollIntoView( item );
11750 this.active = item.first();
11751 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
11752 // Only update aria-activedescendant if there's a role
11753 // otherwise we assume focus is managed elsewhere
11754 if ( this.options.role ) {
11755 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
11758 // Highlight active parent menu item, if any
11761 .closest( ".ui-menu-item" )
11762 .children( "a:first" )
11763 .addClass( "ui-state-active" );
11765 if ( event && event.type === "keydown" ) {
11768 this.timer = this._delay(function() {
11773 nested = item.children( ".ui-menu" );
11774 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
11775 this._startOpening(nested);
11777 this.activeMenu = item.parent();
11779 this._trigger( "focus", event, { item: item } );
11782 _scrollIntoView: function( item ) {
11783 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
11784 if ( this._hasScroll() ) {
11785 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
11786 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
11787 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
11788 scroll = this.activeMenu.scrollTop();
11789 elementHeight = this.activeMenu.height();
11790 itemHeight = item.height();
11792 if ( offset < 0 ) {
11793 this.activeMenu.scrollTop( scroll + offset );
11794 } else if ( offset + itemHeight > elementHeight ) {
11795 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
11800 blur: function( event, fromFocus ) {
11801 if ( !fromFocus ) {
11802 clearTimeout( this.timer );
11805 if ( !this.active ) {
11809 this.active.children( "a" ).removeClass( "ui-state-focus" );
11810 this.active = null;
11812 this._trigger( "blur", event, { item: this.active } );
11815 _startOpening: function( submenu ) {
11816 clearTimeout( this.timer );
11818 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
11819 // shift in the submenu position when mousing over the carat icon
11820 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
11824 this.timer = this._delay(function() {
11826 this._open( submenu );
11830 _open: function( submenu ) {
11831 var position = $.extend({
11833 }, this.options.position );
11835 clearTimeout( this.timer );
11836 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
11838 .attr( "aria-hidden", "true" );
11842 .removeAttr( "aria-hidden" )
11843 .attr( "aria-expanded", "true" )
11844 .position( position );
11847 collapseAll: function( event, all ) {
11848 clearTimeout( this.timer );
11849 this.timer = this._delay(function() {
11850 // If we were passed an event, look for the submenu that contains the event
11851 var currentMenu = all ? this.element :
11852 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
11854 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
11855 if ( !currentMenu.length ) {
11856 currentMenu = this.element;
11859 this._close( currentMenu );
11861 this.blur( event );
11862 this.activeMenu = currentMenu;
11866 // With no arguments, closes the currently active menu - if nothing is active
11867 // it closes all menus. If passed an argument, it will search for menus BELOW
11868 _close: function( startMenu ) {
11869 if ( !startMenu ) {
11870 startMenu = this.active ? this.active.parent() : this.element;
11874 .find( ".ui-menu" )
11876 .attr( "aria-hidden", "true" )
11877 .attr( "aria-expanded", "false" )
11879 .find( "a.ui-state-active" )
11880 .removeClass( "ui-state-active" );
11883 collapse: function( event ) {
11884 var newItem = this.active &&
11885 this.active.parent().closest( ".ui-menu-item", this.element );
11886 if ( newItem && newItem.length ) {
11888 this.focus( event, newItem );
11892 expand: function( event ) {
11893 var newItem = this.active &&
11895 .children( ".ui-menu " )
11896 .children( ".ui-menu-item" )
11899 if ( newItem && newItem.length ) {
11900 this._open( newItem.parent() );
11902 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
11903 this._delay(function() {
11904 this.focus( event, newItem );
11909 next: function( event ) {
11910 this._move( "next", "first", event );
11913 previous: function( event ) {
11914 this._move( "prev", "last", event );
11917 isFirstItem: function() {
11918 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
11921 isLastItem: function() {
11922 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
11925 _move: function( direction, filter, event ) {
11927 if ( this.active ) {
11928 if ( direction === "first" || direction === "last" ) {
11930 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
11934 [ direction + "All" ]( ".ui-menu-item" )
11938 if ( !next || !next.length || !this.active ) {
11939 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
11942 this.focus( event, next );
11945 nextPage: function( event ) {
11946 var item, base, height;
11948 if ( !this.active ) {
11949 this.next( event );
11952 if ( this.isLastItem() ) {
11955 if ( this._hasScroll() ) {
11956 base = this.active.offset().top;
11957 height = this.element.height();
11958 this.active.nextAll( ".ui-menu-item" ).each(function() {
11960 return item.offset().top - base - height < 0;
11963 this.focus( event, item );
11965 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
11966 [ !this.active ? "first" : "last" ]() );
11970 previousPage: function( event ) {
11971 var item, base, height;
11972 if ( !this.active ) {
11973 this.next( event );
11976 if ( this.isFirstItem() ) {
11979 if ( this._hasScroll() ) {
11980 base = this.active.offset().top;
11981 height = this.element.height();
11982 this.active.prevAll( ".ui-menu-item" ).each(function() {
11984 return item.offset().top - base + height > 0;
11987 this.focus( event, item );
11989 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
11993 _hasScroll: function() {
11994 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
11997 select: function( event ) {
11998 // TODO: It should never be possible to not have an active item at this
11999 // point, but the tests don't trigger mouseenter before click.
12000 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
12001 var ui = { item: this.active };
12002 if ( !this.active.has( ".ui-menu" ).length ) {
12003 this.collapseAll( event, true );
12005 this._trigger( "select", event, ui );
12011 (function( $, undefined ) {
12015 var cachedScrollbarWidth,
12018 round = Math.round,
12019 rhorizontal = /left|center|right/,
12020 rvertical = /top|center|bottom/,
12021 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
12022 rposition = /^\w+/,
12024 _position = $.fn.position;
12026 function getOffsets( offsets, width, height ) {
12028 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
12029 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
12033 function parseCss( element, property ) {
12034 return parseInt( $.css( element, property ), 10 ) || 0;
12037 function getDimensions( elem ) {
12039 if ( raw.nodeType === 9 ) {
12041 width: elem.width(),
12042 height: elem.height(),
12043 offset: { top: 0, left: 0 }
12046 if ( $.isWindow( raw ) ) {
12048 width: elem.width(),
12049 height: elem.height(),
12050 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
12053 if ( raw.preventDefault ) {
12057 offset: { top: raw.pageY, left: raw.pageX }
12061 width: elem.outerWidth(),
12062 height: elem.outerHeight(),
12063 offset: elem.offset()
12068 scrollbarWidth: function() {
12069 if ( cachedScrollbarWidth !== undefined ) {
12070 return cachedScrollbarWidth;
12073 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
12074 innerDiv = div.children()[0];
12076 $( "body" ).append( div );
12077 w1 = innerDiv.offsetWidth;
12078 div.css( "overflow", "scroll" );
12080 w2 = innerDiv.offsetWidth;
12083 w2 = div[0].clientWidth;
12088 return (cachedScrollbarWidth = w1 - w2);
12090 getScrollInfo: function( within ) {
12091 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
12092 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
12093 hasOverflowX = overflowX === "scroll" ||
12094 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
12095 hasOverflowY = overflowY === "scroll" ||
12096 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
12098 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
12099 height: hasOverflowX ? $.position.scrollbarWidth() : 0
12102 getWithinInfo: function( element ) {
12103 var withinElement = $( element || window ),
12104 isWindow = $.isWindow( withinElement[0] );
12106 element: withinElement,
12107 isWindow: isWindow,
12108 offset: withinElement.offset() || { left: 0, top: 0 },
12109 scrollLeft: withinElement.scrollLeft(),
12110 scrollTop: withinElement.scrollTop(),
12111 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
12112 height: isWindow ? withinElement.height() : withinElement.outerHeight()
12117 $.fn.position = function( options ) {
12118 if ( !options || !options.of ) {
12119 return _position.apply( this, arguments );
12122 // make a copy, we don't want to modify arguments
12123 options = $.extend( {}, options );
12125 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
12126 target = $( options.of ),
12127 within = $.position.getWithinInfo( options.within ),
12128 scrollInfo = $.position.getScrollInfo( within ),
12129 collision = ( options.collision || "flip" ).split( " " ),
12132 dimensions = getDimensions( target );
12133 if ( target[0].preventDefault ) {
12134 // force left top to allow flipping
12135 options.at = "left top";
12137 targetWidth = dimensions.width;
12138 targetHeight = dimensions.height;
12139 targetOffset = dimensions.offset;
12140 // clone to reuse original targetOffset later
12141 basePosition = $.extend( {}, targetOffset );
12143 // force my and at to have valid horizontal and vertical positions
12144 // if a value is missing or invalid, it will be converted to center
12145 $.each( [ "my", "at" ], function() {
12146 var pos = ( options[ this ] || "" ).split( " " ),
12150 if ( pos.length === 1) {
12151 pos = rhorizontal.test( pos[ 0 ] ) ?
12152 pos.concat( [ "center" ] ) :
12153 rvertical.test( pos[ 0 ] ) ?
12154 [ "center" ].concat( pos ) :
12155 [ "center", "center" ];
12157 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
12158 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
12160 // calculate offsets
12161 horizontalOffset = roffset.exec( pos[ 0 ] );
12162 verticalOffset = roffset.exec( pos[ 1 ] );
12163 offsets[ this ] = [
12164 horizontalOffset ? horizontalOffset[ 0 ] : 0,
12165 verticalOffset ? verticalOffset[ 0 ] : 0
12168 // reduce to just the positions without the offsets
12169 options[ this ] = [
12170 rposition.exec( pos[ 0 ] )[ 0 ],
12171 rposition.exec( pos[ 1 ] )[ 0 ]
12175 // normalize collision option
12176 if ( collision.length === 1 ) {
12177 collision[ 1 ] = collision[ 0 ];
12180 if ( options.at[ 0 ] === "right" ) {
12181 basePosition.left += targetWidth;
12182 } else if ( options.at[ 0 ] === "center" ) {
12183 basePosition.left += targetWidth / 2;
12186 if ( options.at[ 1 ] === "bottom" ) {
12187 basePosition.top += targetHeight;
12188 } else if ( options.at[ 1 ] === "center" ) {
12189 basePosition.top += targetHeight / 2;
12192 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
12193 basePosition.left += atOffset[ 0 ];
12194 basePosition.top += atOffset[ 1 ];
12196 return this.each(function() {
12197 var collisionPosition, using,
12199 elemWidth = elem.outerWidth(),
12200 elemHeight = elem.outerHeight(),
12201 marginLeft = parseCss( this, "marginLeft" ),
12202 marginTop = parseCss( this, "marginTop" ),
12203 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
12204 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
12205 position = $.extend( {}, basePosition ),
12206 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
12208 if ( options.my[ 0 ] === "right" ) {
12209 position.left -= elemWidth;
12210 } else if ( options.my[ 0 ] === "center" ) {
12211 position.left -= elemWidth / 2;
12214 if ( options.my[ 1 ] === "bottom" ) {
12215 position.top -= elemHeight;
12216 } else if ( options.my[ 1 ] === "center" ) {
12217 position.top -= elemHeight / 2;
12220 position.left += myOffset[ 0 ];
12221 position.top += myOffset[ 1 ];
12223 // if the browser doesn't support fractions, then round for consistent results
12224 if ( !$.support.offsetFractions ) {
12225 position.left = round( position.left );
12226 position.top = round( position.top );
12229 collisionPosition = {
12230 marginLeft: marginLeft,
12231 marginTop: marginTop
12234 $.each( [ "left", "top" ], function( i, dir ) {
12235 if ( $.ui.position[ collision[ i ] ] ) {
12236 $.ui.position[ collision[ i ] ][ dir ]( position, {
12237 targetWidth: targetWidth,
12238 targetHeight: targetHeight,
12239 elemWidth: elemWidth,
12240 elemHeight: elemHeight,
12241 collisionPosition: collisionPosition,
12242 collisionWidth: collisionWidth,
12243 collisionHeight: collisionHeight,
12244 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
12253 if ( options.using ) {
12254 // adds feedback as second argument to using callback, if present
12255 using = function( props ) {
12256 var left = targetOffset.left - position.left,
12257 right = left + targetWidth - elemWidth,
12258 top = targetOffset.top - position.top,
12259 bottom = top + targetHeight - elemHeight,
12263 left: targetOffset.left,
12264 top: targetOffset.top,
12265 width: targetWidth,
12266 height: targetHeight
12270 left: position.left,
12275 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
12276 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
12278 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
12279 feedback.horizontal = "center";
12281 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
12282 feedback.vertical = "middle";
12284 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
12285 feedback.important = "horizontal";
12287 feedback.important = "vertical";
12289 options.using.call( this, props, feedback );
12293 elem.offset( $.extend( position, { using: using } ) );
12299 left: function( position, data ) {
12300 var within = data.within,
12301 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
12302 outerWidth = within.width,
12303 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12304 overLeft = withinOffset - collisionPosLeft,
12305 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
12308 // element is wider than within
12309 if ( data.collisionWidth > outerWidth ) {
12310 // element is initially over the left side of within
12311 if ( overLeft > 0 && overRight <= 0 ) {
12312 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
12313 position.left += overLeft - newOverRight;
12314 // element is initially over right side of within
12315 } else if ( overRight > 0 && overLeft <= 0 ) {
12316 position.left = withinOffset;
12317 // element is initially over both left and right sides of within
12319 if ( overLeft > overRight ) {
12320 position.left = withinOffset + outerWidth - data.collisionWidth;
12322 position.left = withinOffset;
12325 // too far left -> align with left edge
12326 } else if ( overLeft > 0 ) {
12327 position.left += overLeft;
12328 // too far right -> align with right edge
12329 } else if ( overRight > 0 ) {
12330 position.left -= overRight;
12331 // adjust based on position and margin
12333 position.left = max( position.left - collisionPosLeft, position.left );
12336 top: function( position, data ) {
12337 var within = data.within,
12338 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
12339 outerHeight = data.within.height,
12340 collisionPosTop = position.top - data.collisionPosition.marginTop,
12341 overTop = withinOffset - collisionPosTop,
12342 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
12345 // element is taller than within
12346 if ( data.collisionHeight > outerHeight ) {
12347 // element is initially over the top of within
12348 if ( overTop > 0 && overBottom <= 0 ) {
12349 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
12350 position.top += overTop - newOverBottom;
12351 // element is initially over bottom of within
12352 } else if ( overBottom > 0 && overTop <= 0 ) {
12353 position.top = withinOffset;
12354 // element is initially over both top and bottom of within
12356 if ( overTop > overBottom ) {
12357 position.top = withinOffset + outerHeight - data.collisionHeight;
12359 position.top = withinOffset;
12362 // too far up -> align with top
12363 } else if ( overTop > 0 ) {
12364 position.top += overTop;
12365 // too far down -> align with bottom edge
12366 } else if ( overBottom > 0 ) {
12367 position.top -= overBottom;
12368 // adjust based on position and margin
12370 position.top = max( position.top - collisionPosTop, position.top );
12375 left: function( position, data ) {
12376 var within = data.within,
12377 withinOffset = within.offset.left + within.scrollLeft,
12378 outerWidth = within.width,
12379 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
12380 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12381 overLeft = collisionPosLeft - offsetLeft,
12382 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
12383 myOffset = data.my[ 0 ] === "left" ?
12385 data.my[ 0 ] === "right" ?
12388 atOffset = data.at[ 0 ] === "left" ?
12390 data.at[ 0 ] === "right" ?
12391 -data.targetWidth :
12393 offset = -2 * data.offset[ 0 ],
12397 if ( overLeft < 0 ) {
12398 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
12399 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
12400 position.left += myOffset + atOffset + offset;
12403 else if ( overRight > 0 ) {
12404 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
12405 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
12406 position.left += myOffset + atOffset + offset;
12410 top: function( position, data ) {
12411 var within = data.within,
12412 withinOffset = within.offset.top + within.scrollTop,
12413 outerHeight = within.height,
12414 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
12415 collisionPosTop = position.top - data.collisionPosition.marginTop,
12416 overTop = collisionPosTop - offsetTop,
12417 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
12418 top = data.my[ 1 ] === "top",
12421 data.my[ 1 ] === "bottom" ?
12424 atOffset = data.at[ 1 ] === "top" ?
12425 data.targetHeight :
12426 data.at[ 1 ] === "bottom" ?
12427 -data.targetHeight :
12429 offset = -2 * data.offset[ 1 ],
12432 if ( overTop < 0 ) {
12433 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
12434 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
12435 position.top += myOffset + atOffset + offset;
12438 else if ( overBottom > 0 ) {
12439 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
12440 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
12441 position.top += myOffset + atOffset + offset;
12448 $.ui.position.flip.left.apply( this, arguments );
12449 $.ui.position.fit.left.apply( this, arguments );
12452 $.ui.position.flip.top.apply( this, arguments );
12453 $.ui.position.fit.top.apply( this, arguments );
12458 // fraction support test
12460 var testElement, testElementParent, testElementStyle, offsetLeft, i,
12461 body = document.getElementsByTagName( "body" )[ 0 ],
12462 div = document.createElement( "div" );
12464 //Create a "fake body" for testing based on method used in jQuery.support
12465 testElement = document.createElement( body ? "div" : "body" );
12466 testElementStyle = {
12467 visibility: "hidden",
12475 $.extend( testElementStyle, {
12476 position: "absolute",
12481 for ( i in testElementStyle ) {
12482 testElement.style[ i ] = testElementStyle[ i ];
12484 testElement.appendChild( div );
12485 testElementParent = body || document.documentElement;
12486 testElementParent.insertBefore( testElement, testElementParent.firstChild );
12488 div.style.cssText = "position: absolute; left: 10.7432222px;";
12490 offsetLeft = $( div ).offset().left;
12491 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
12493 testElement.innerHTML = "";
12494 testElementParent.removeChild( testElement );
12499 (function( $, undefined ) {
12501 $.widget( "ui.progressbar", {
12513 _create: function() {
12514 // Constrain initial value
12515 this.oldValue = this.options.value = this._constrainedValue();
12518 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12520 // Only set static values, aria-valuenow and aria-valuemax are
12521 // set inside _refreshValue()
12522 role: "progressbar",
12523 "aria-valuemin": this.min
12526 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
12527 .appendTo( this.element );
12529 this._refreshValue();
12532 _destroy: function() {
12534 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12535 .removeAttr( "role" )
12536 .removeAttr( "aria-valuemin" )
12537 .removeAttr( "aria-valuemax" )
12538 .removeAttr( "aria-valuenow" );
12540 this.valueDiv.remove();
12543 value: function( newValue ) {
12544 if ( newValue === undefined ) {
12545 return this.options.value;
12548 this.options.value = this._constrainedValue( newValue );
12549 this._refreshValue();
12552 _constrainedValue: function( newValue ) {
12553 if ( newValue === undefined ) {
12554 newValue = this.options.value;
12557 this.indeterminate = newValue === false;
12560 if ( typeof newValue !== "number" ) {
12564 return this.indeterminate ? false :
12565 Math.min( this.options.max, Math.max( this.min, newValue ) );
12568 _setOptions: function( options ) {
12569 // Ensure "value" option is set after other values (like max)
12570 var value = options.value;
12571 delete options.value;
12573 this._super( options );
12575 this.options.value = this._constrainedValue( value );
12576 this._refreshValue();
12579 _setOption: function( key, value ) {
12580 if ( key === "max" ) {
12581 // Don't allow a max less than min
12582 value = Math.max( this.min, value );
12585 this._super( key, value );
12588 _percentage: function() {
12589 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
12592 _refreshValue: function() {
12593 var value = this.options.value,
12594 percentage = this._percentage();
12597 .toggle( this.indeterminate || value > this.min )
12598 .toggleClass( "ui-corner-right", value === this.options.max )
12599 .width( percentage.toFixed(0) + "%" );
12601 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
12603 if ( this.indeterminate ) {
12604 this.element.removeAttr( "aria-valuenow" );
12605 if ( !this.overlayDiv ) {
12606 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
12609 this.element.attr({
12610 "aria-valuemax": this.options.max,
12611 "aria-valuenow": value
12613 if ( this.overlayDiv ) {
12614 this.overlayDiv.remove();
12615 this.overlayDiv = null;
12619 if ( this.oldValue !== value ) {
12620 this.oldValue = value;
12621 this._trigger( "change" );
12623 if ( value === this.options.max ) {
12624 this._trigger( "complete" );
12631 (function( $, undefined ) {
12633 // number of pages in a slider
12634 // (how many times can you page up/down to go through the whole range)
12637 $.widget( "ui.slider", $.ui.mouse, {
12639 widgetEventPrefix: "slide",
12646 orientation: "horizontal",
12659 _create: function() {
12660 this._keySliding = false;
12661 this._mouseSliding = false;
12662 this._animateOff = true;
12663 this._handleIndex = null;
12664 this._detectOrientation();
12668 .addClass( "ui-slider" +
12669 " ui-slider-" + this.orientation +
12671 " ui-widget-content" +
12675 this._setOption( "disabled", this.options.disabled );
12677 this._animateOff = false;
12680 _refresh: function() {
12681 this._createRange();
12682 this._createHandles();
12683 this._setupEvents();
12684 this._refreshValue();
12687 _createHandles: function() {
12688 var i, handleCount,
12689 options = this.options,
12690 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
12691 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
12694 handleCount = ( options.values && options.values.length ) || 1;
12696 if ( existingHandles.length > handleCount ) {
12697 existingHandles.slice( handleCount ).remove();
12698 existingHandles = existingHandles.slice( 0, handleCount );
12701 for ( i = existingHandles.length; i < handleCount; i++ ) {
12702 handles.push( handle );
12705 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
12707 this.handle = this.handles.eq( 0 );
12709 this.handles.each(function( i ) {
12710 $( this ).data( "ui-slider-handle-index", i );
12714 _createRange: function() {
12715 var options = this.options,
12718 if ( options.range ) {
12719 if ( options.range === true ) {
12720 if ( !options.values ) {
12721 options.values = [ this._valueMin(), this._valueMin() ];
12722 } else if ( options.values.length && options.values.length !== 2 ) {
12723 options.values = [ options.values[0], options.values[0] ];
12724 } else if ( $.isArray( options.values ) ) {
12725 options.values = options.values.slice(0);
12729 if ( !this.range || !this.range.length ) {
12730 this.range = $( "<div></div>" )
12731 .appendTo( this.element );
12733 classes = "ui-slider-range" +
12734 // note: this isn't the most fittingly semantic framework class for this element,
12735 // but worked best visually with a variety of themes
12736 " ui-widget-header ui-corner-all";
12738 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
12739 // Handle range switching from true to min/max
12746 this.range.addClass( classes +
12747 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
12749 this.range = $([]);
12753 _setupEvents: function() {
12754 var elements = this.handles.add( this.range ).filter( "a" );
12755 this._off( elements );
12756 this._on( elements, this._handleEvents );
12757 this._hoverable( elements );
12758 this._focusable( elements );
12761 _destroy: function() {
12762 this.handles.remove();
12763 this.range.remove();
12766 .removeClass( "ui-slider" +
12767 " ui-slider-horizontal" +
12768 " ui-slider-vertical" +
12770 " ui-widget-content" +
12771 " ui-corner-all" );
12773 this._mouseDestroy();
12776 _mouseCapture: function( event ) {
12777 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
12781 if ( o.disabled ) {
12785 this.elementSize = {
12786 width: this.element.outerWidth(),
12787 height: this.element.outerHeight()
12789 this.elementOffset = this.element.offset();
12791 position = { x: event.pageX, y: event.pageY };
12792 normValue = this._normValueFromMouse( position );
12793 distance = this._valueMax() - this._valueMin() + 1;
12794 this.handles.each(function( i ) {
12795 var thisDistance = Math.abs( normValue - that.values(i) );
12796 if (( distance > thisDistance ) ||
12797 ( distance === thisDistance &&
12798 (i === that._lastChangedValue || that.values(i) === o.min ))) {
12799 distance = thisDistance;
12800 closestHandle = $( this );
12805 allowed = this._start( event, index );
12806 if ( allowed === false ) {
12809 this._mouseSliding = true;
12811 this._handleIndex = index;
12814 .addClass( "ui-state-active" )
12817 offset = closestHandle.offset();
12818 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
12819 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
12820 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
12821 top: event.pageY - offset.top -
12822 ( closestHandle.height() / 2 ) -
12823 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
12824 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
12825 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
12828 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
12829 this._slide( event, index, normValue );
12831 this._animateOff = true;
12835 _mouseStart: function() {
12839 _mouseDrag: function( event ) {
12840 var position = { x: event.pageX, y: event.pageY },
12841 normValue = this._normValueFromMouse( position );
12843 this._slide( event, this._handleIndex, normValue );
12848 _mouseStop: function( event ) {
12849 this.handles.removeClass( "ui-state-active" );
12850 this._mouseSliding = false;
12852 this._stop( event, this._handleIndex );
12853 this._change( event, this._handleIndex );
12855 this._handleIndex = null;
12856 this._clickOffset = null;
12857 this._animateOff = false;
12862 _detectOrientation: function() {
12863 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
12866 _normValueFromMouse: function( position ) {
12873 if ( this.orientation === "horizontal" ) {
12874 pixelTotal = this.elementSize.width;
12875 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
12877 pixelTotal = this.elementSize.height;
12878 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
12881 percentMouse = ( pixelMouse / pixelTotal );
12882 if ( percentMouse > 1 ) {
12885 if ( percentMouse < 0 ) {
12888 if ( this.orientation === "vertical" ) {
12889 percentMouse = 1 - percentMouse;
12892 valueTotal = this._valueMax() - this._valueMin();
12893 valueMouse = this._valueMin() + percentMouse * valueTotal;
12895 return this._trimAlignValue( valueMouse );
12898 _start: function( event, index ) {
12900 handle: this.handles[ index ],
12901 value: this.value()
12903 if ( this.options.values && this.options.values.length ) {
12904 uiHash.value = this.values( index );
12905 uiHash.values = this.values();
12907 return this._trigger( "start", event, uiHash );
12910 _slide: function( event, index, newVal ) {
12915 if ( this.options.values && this.options.values.length ) {
12916 otherVal = this.values( index ? 0 : 1 );
12918 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
12919 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
12924 if ( newVal !== this.values( index ) ) {
12925 newValues = this.values();
12926 newValues[ index ] = newVal;
12927 // A slide can be canceled by returning false from the slide callback
12928 allowed = this._trigger( "slide", event, {
12929 handle: this.handles[ index ],
12933 otherVal = this.values( index ? 0 : 1 );
12934 if ( allowed !== false ) {
12935 this.values( index, newVal, true );
12939 if ( newVal !== this.value() ) {
12940 // A slide can be canceled by returning false from the slide callback
12941 allowed = this._trigger( "slide", event, {
12942 handle: this.handles[ index ],
12945 if ( allowed !== false ) {
12946 this.value( newVal );
12952 _stop: function( event, index ) {
12954 handle: this.handles[ index ],
12955 value: this.value()
12957 if ( this.options.values && this.options.values.length ) {
12958 uiHash.value = this.values( index );
12959 uiHash.values = this.values();
12962 this._trigger( "stop", event, uiHash );
12965 _change: function( event, index ) {
12966 if ( !this._keySliding && !this._mouseSliding ) {
12968 handle: this.handles[ index ],
12969 value: this.value()
12971 if ( this.options.values && this.options.values.length ) {
12972 uiHash.value = this.values( index );
12973 uiHash.values = this.values();
12976 //store the last changed value index for reference when handles overlap
12977 this._lastChangedValue = index;
12979 this._trigger( "change", event, uiHash );
12983 value: function( newValue ) {
12984 if ( arguments.length ) {
12985 this.options.value = this._trimAlignValue( newValue );
12986 this._refreshValue();
12987 this._change( null, 0 );
12991 return this._value();
12994 values: function( index, newValue ) {
12999 if ( arguments.length > 1 ) {
13000 this.options.values[ index ] = this._trimAlignValue( newValue );
13001 this._refreshValue();
13002 this._change( null, index );
13006 if ( arguments.length ) {
13007 if ( $.isArray( arguments[ 0 ] ) ) {
13008 vals = this.options.values;
13009 newValues = arguments[ 0 ];
13010 for ( i = 0; i < vals.length; i += 1 ) {
13011 vals[ i ] = this._trimAlignValue( newValues[ i ] );
13012 this._change( null, i );
13014 this._refreshValue();
13016 if ( this.options.values && this.options.values.length ) {
13017 return this._values( index );
13019 return this.value();
13023 return this._values();
13027 _setOption: function( key, value ) {
13031 if ( key === "range" && this.options.range === true ) {
13032 if ( value === "min" ) {
13033 this.options.value = this._values( 0 );
13034 this.options.values = null;
13035 } else if ( value === "max" ) {
13036 this.options.value = this._values( this.options.values.length-1 );
13037 this.options.values = null;
13041 if ( $.isArray( this.options.values ) ) {
13042 valsLength = this.options.values.length;
13045 $.Widget.prototype._setOption.apply( this, arguments );
13048 case "orientation":
13049 this._detectOrientation();
13051 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
13052 .addClass( "ui-slider-" + this.orientation );
13053 this._refreshValue();
13056 this._animateOff = true;
13057 this._refreshValue();
13058 this._change( null, 0 );
13059 this._animateOff = false;
13062 this._animateOff = true;
13063 this._refreshValue();
13064 for ( i = 0; i < valsLength; i += 1 ) {
13065 this._change( null, i );
13067 this._animateOff = false;
13071 this._animateOff = true;
13072 this._refreshValue();
13073 this._animateOff = false;
13076 this._animateOff = true;
13078 this._animateOff = false;
13083 //internal value getter
13084 // _value() returns value trimmed by min and max, aligned by step
13085 _value: function() {
13086 var val = this.options.value;
13087 val = this._trimAlignValue( val );
13092 //internal values getter
13093 // _values() returns array of values trimmed by min and max, aligned by step
13094 // _values( index ) returns single value trimmed by min and max, aligned by step
13095 _values: function( index ) {
13100 if ( arguments.length ) {
13101 val = this.options.values[ index ];
13102 val = this._trimAlignValue( val );
13105 } else if ( this.options.values && this.options.values.length ) {
13106 // .slice() creates a copy of the array
13107 // this copy gets trimmed by min and max and then returned
13108 vals = this.options.values.slice();
13109 for ( i = 0; i < vals.length; i+= 1) {
13110 vals[ i ] = this._trimAlignValue( vals[ i ] );
13119 // returns the step-aligned value that val is closest to, between (inclusive) min and max
13120 _trimAlignValue: function( val ) {
13121 if ( val <= this._valueMin() ) {
13122 return this._valueMin();
13124 if ( val >= this._valueMax() ) {
13125 return this._valueMax();
13127 var step = ( this.options.step > 0 ) ? this.options.step : 1,
13128 valModStep = (val - this._valueMin()) % step,
13129 alignValue = val - valModStep;
13131 if ( Math.abs(valModStep) * 2 >= step ) {
13132 alignValue += ( valModStep > 0 ) ? step : ( -step );
13135 // Since JavaScript has problems with large floats, round
13136 // the final value to 5 digits after the decimal point (see #4124)
13137 return parseFloat( alignValue.toFixed(5) );
13140 _valueMin: function() {
13141 return this.options.min;
13144 _valueMax: function() {
13145 return this.options.max;
13148 _refreshValue: function() {
13149 var lastValPercent, valPercent, value, valueMin, valueMax,
13150 oRange = this.options.range,
13153 animate = ( !this._animateOff ) ? o.animate : false,
13156 if ( this.options.values && this.options.values.length ) {
13157 this.handles.each(function( i ) {
13158 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
13159 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13160 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13161 if ( that.options.range === true ) {
13162 if ( that.orientation === "horizontal" ) {
13164 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
13167 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13171 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
13174 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13178 lastValPercent = valPercent;
13181 value = this.value();
13182 valueMin = this._valueMin();
13183 valueMax = this._valueMax();
13184 valPercent = ( valueMax !== valueMin ) ?
13185 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
13187 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13188 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13190 if ( oRange === "min" && this.orientation === "horizontal" ) {
13191 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
13193 if ( oRange === "max" && this.orientation === "horizontal" ) {
13194 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13196 if ( oRange === "min" && this.orientation === "vertical" ) {
13197 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
13199 if ( oRange === "max" && this.orientation === "vertical" ) {
13200 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13206 keydown: function( event ) {
13207 /*jshint maxcomplexity:25*/
13208 var allowed, curVal, newVal, step,
13209 index = $( event.target ).data( "ui-slider-handle-index" );
13211 switch ( event.keyCode ) {
13212 case $.ui.keyCode.HOME:
13213 case $.ui.keyCode.END:
13214 case $.ui.keyCode.PAGE_UP:
13215 case $.ui.keyCode.PAGE_DOWN:
13216 case $.ui.keyCode.UP:
13217 case $.ui.keyCode.RIGHT:
13218 case $.ui.keyCode.DOWN:
13219 case $.ui.keyCode.LEFT:
13220 event.preventDefault();
13221 if ( !this._keySliding ) {
13222 this._keySliding = true;
13223 $( event.target ).addClass( "ui-state-active" );
13224 allowed = this._start( event, index );
13225 if ( allowed === false ) {
13232 step = this.options.step;
13233 if ( this.options.values && this.options.values.length ) {
13234 curVal = newVal = this.values( index );
13236 curVal = newVal = this.value();
13239 switch ( event.keyCode ) {
13240 case $.ui.keyCode.HOME:
13241 newVal = this._valueMin();
13243 case $.ui.keyCode.END:
13244 newVal = this._valueMax();
13246 case $.ui.keyCode.PAGE_UP:
13247 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
13249 case $.ui.keyCode.PAGE_DOWN:
13250 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
13252 case $.ui.keyCode.UP:
13253 case $.ui.keyCode.RIGHT:
13254 if ( curVal === this._valueMax() ) {
13257 newVal = this._trimAlignValue( curVal + step );
13259 case $.ui.keyCode.DOWN:
13260 case $.ui.keyCode.LEFT:
13261 if ( curVal === this._valueMin() ) {
13264 newVal = this._trimAlignValue( curVal - step );
13268 this._slide( event, index, newVal );
13270 click: function( event ) {
13271 event.preventDefault();
13273 keyup: function( event ) {
13274 var index = $( event.target ).data( "ui-slider-handle-index" );
13276 if ( this._keySliding ) {
13277 this._keySliding = false;
13278 this._stop( event, index );
13279 this._change( event, index );
13280 $( event.target ).removeClass( "ui-state-active" );
13291 function modifier( fn ) {
13292 return function() {
13293 var previous = this.element.val();
13294 fn.apply( this, arguments );
13296 if ( previous !== this.element.val() ) {
13297 this._trigger( "change" );
13302 $.widget( "ui.spinner", {
13304 defaultElement: "<input>",
13305 widgetEventPrefix: "spin",
13309 down: "ui-icon-triangle-1-s",
13310 up: "ui-icon-triangle-1-n"
13315 numberFormat: null,
13325 _create: function() {
13326 // handle string values that need to be parsed
13327 this._setOption( "max", this.options.max );
13328 this._setOption( "min", this.options.min );
13329 this._setOption( "step", this.options.step );
13331 // format the value, but don't constrain
13332 this._value( this.element.val(), true );
13335 this._on( this._events );
13338 // turning off autocomplete prevents the browser from remembering the
13339 // value when navigating through history, so we re-enable autocomplete
13340 // if the page is unloaded before the widget is destroyed. #7790
13341 this._on( this.window, {
13342 beforeunload: function() {
13343 this.element.removeAttr( "autocomplete" );
13348 _getCreateOptions: function() {
13350 element = this.element;
13352 $.each( [ "min", "max", "step" ], function( i, option ) {
13353 var value = element.attr( option );
13354 if ( value !== undefined && value.length ) {
13355 options[ option ] = value;
13363 keydown: function( event ) {
13364 if ( this._start( event ) && this._keydown( event ) ) {
13365 event.preventDefault();
13369 focus: function() {
13370 this.previous = this.element.val();
13372 blur: function( event ) {
13373 if ( this.cancelBlur ) {
13374 delete this.cancelBlur;
13380 if ( this.previous !== this.element.val() ) {
13381 this._trigger( "change", event );
13384 mousewheel: function( event, delta ) {
13388 if ( !this.spinning && !this._start( event ) ) {
13392 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
13393 clearTimeout( this.mousewheelTimer );
13394 this.mousewheelTimer = this._delay(function() {
13395 if ( this.spinning ) {
13396 this._stop( event );
13399 event.preventDefault();
13401 "mousedown .ui-spinner-button": function( event ) {
13404 // We never want the buttons to have focus; whenever the user is
13405 // interacting with the spinner, the focus should be on the input.
13406 // If the input is focused then this.previous is properly set from
13407 // when the input first received focus. If the input is not focused
13408 // then we need to set this.previous based on the value before spinning.
13409 previous = this.element[0] === this.document[0].activeElement ?
13410 this.previous : this.element.val();
13411 function checkFocus() {
13412 var isActive = this.element[0] === this.document[0].activeElement;
13414 this.element.focus();
13415 this.previous = previous;
13417 // IE sets focus asynchronously, so we need to check if focus
13418 // moved off of the input because the user clicked on the button.
13419 this._delay(function() {
13420 this.previous = previous;
13425 // ensure focus is on (or stays on) the text field
13426 event.preventDefault();
13427 checkFocus.call( this );
13430 // IE doesn't prevent moving focus even with event.preventDefault()
13431 // so we set a flag to know when we should ignore the blur event
13432 // and check (again) if focus moved off of the input.
13433 this.cancelBlur = true;
13434 this._delay(function() {
13435 delete this.cancelBlur;
13436 checkFocus.call( this );
13439 if ( this._start( event ) === false ) {
13443 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13445 "mouseup .ui-spinner-button": "_stop",
13446 "mouseenter .ui-spinner-button": function( event ) {
13447 // button will add ui-state-active if mouse was down while mouseleave and kept down
13448 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
13452 if ( this._start( event ) === false ) {
13455 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13457 // TODO: do we really want to consider this a stop?
13458 // shouldn't we just stop the repeater and wait until mouseup before
13459 // we trigger the stop event?
13460 "mouseleave .ui-spinner-button": "_stop"
13463 _draw: function() {
13464 var uiSpinner = this.uiSpinner = this.element
13465 .addClass( "ui-spinner-input" )
13466 .attr( "autocomplete", "off" )
13467 .wrap( this._uiSpinnerHtml() )
13470 .append( this._buttonHtml() );
13472 this.element.attr( "role", "spinbutton" );
13475 this.buttons = uiSpinner.find( ".ui-spinner-button" )
13476 .attr( "tabIndex", -1 )
13478 .removeClass( "ui-corner-all" );
13480 // IE 6 doesn't understand height: 50% for the buttons
13481 // unless the wrapper has an explicit height
13482 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
13483 uiSpinner.height() > 0 ) {
13484 uiSpinner.height( uiSpinner.height() );
13487 // disable spinner if element was already disabled
13488 if ( this.options.disabled ) {
13493 _keydown: function( event ) {
13494 var options = this.options,
13495 keyCode = $.ui.keyCode;
13497 switch ( event.keyCode ) {
13499 this._repeat( null, 1, event );
13502 this._repeat( null, -1, event );
13504 case keyCode.PAGE_UP:
13505 this._repeat( null, options.page, event );
13507 case keyCode.PAGE_DOWN:
13508 this._repeat( null, -options.page, event );
13515 _uiSpinnerHtml: function() {
13516 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
13519 _buttonHtml: function() {
13521 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
13522 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
13524 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
13525 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
13529 _start: function( event ) {
13530 if ( !this.spinning && this._trigger( "start", event ) === false ) {
13534 if ( !this.counter ) {
13537 this.spinning = true;
13541 _repeat: function( i, steps, event ) {
13544 clearTimeout( this.timer );
13545 this.timer = this._delay(function() {
13546 this._repeat( 40, steps, event );
13549 this._spin( steps * this.options.step, event );
13552 _spin: function( step, event ) {
13553 var value = this.value() || 0;
13555 if ( !this.counter ) {
13559 value = this._adjustValue( value + step * this._increment( this.counter ) );
13561 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
13562 this._value( value );
13567 _increment: function( i ) {
13568 var incremental = this.options.incremental;
13570 if ( incremental ) {
13571 return $.isFunction( incremental ) ?
13573 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
13579 _precision: function() {
13580 var precision = this._precisionOf( this.options.step );
13581 if ( this.options.min !== null ) {
13582 precision = Math.max( precision, this._precisionOf( this.options.min ) );
13587 _precisionOf: function( num ) {
13588 var str = num.toString(),
13589 decimal = str.indexOf( "." );
13590 return decimal === -1 ? 0 : str.length - decimal - 1;
13593 _adjustValue: function( value ) {
13594 var base, aboveMin,
13595 options = this.options;
13597 // make sure we're at a valid step
13598 // - find out where we are relative to the base (min or 0)
13599 base = options.min !== null ? options.min : 0;
13600 aboveMin = value - base;
13601 // - round to the nearest step
13602 aboveMin = Math.round(aboveMin / options.step) * options.step;
13603 // - rounding is based on 0, so adjust back to our base
13604 value = base + aboveMin;
13606 // fix precision from bad JS floating point math
13607 value = parseFloat( value.toFixed( this._precision() ) );
13610 if ( options.max !== null && value > options.max) {
13611 return options.max;
13613 if ( options.min !== null && value < options.min ) {
13614 return options.min;
13620 _stop: function( event ) {
13621 if ( !this.spinning ) {
13625 clearTimeout( this.timer );
13626 clearTimeout( this.mousewheelTimer );
13628 this.spinning = false;
13629 this._trigger( "stop", event );
13632 _setOption: function( key, value ) {
13633 if ( key === "culture" || key === "numberFormat" ) {
13634 var prevValue = this._parse( this.element.val() );
13635 this.options[ key ] = value;
13636 this.element.val( this._format( prevValue ) );
13640 if ( key === "max" || key === "min" || key === "step" ) {
13641 if ( typeof value === "string" ) {
13642 value = this._parse( value );
13645 if ( key === "icons" ) {
13646 this.buttons.first().find( ".ui-icon" )
13647 .removeClass( this.options.icons.up )
13648 .addClass( value.up );
13649 this.buttons.last().find( ".ui-icon" )
13650 .removeClass( this.options.icons.down )
13651 .addClass( value.down );
13654 this._super( key, value );
13656 if ( key === "disabled" ) {
13658 this.element.prop( "disabled", true );
13659 this.buttons.button( "disable" );
13661 this.element.prop( "disabled", false );
13662 this.buttons.button( "enable" );
13667 _setOptions: modifier(function( options ) {
13668 this._super( options );
13669 this._value( this.element.val() );
13672 _parse: function( val ) {
13673 if ( typeof val === "string" && val !== "" ) {
13674 val = window.Globalize && this.options.numberFormat ?
13675 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
13677 return val === "" || isNaN( val ) ? null : val;
13680 _format: function( value ) {
13681 if ( value === "" ) {
13684 return window.Globalize && this.options.numberFormat ?
13685 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
13689 _refresh: function() {
13690 this.element.attr({
13691 "aria-valuemin": this.options.min,
13692 "aria-valuemax": this.options.max,
13693 // TODO: what should we do with values that can't be parsed?
13694 "aria-valuenow": this._parse( this.element.val() )
13698 // update the value without triggering change
13699 _value: function( value, allowAny ) {
13701 if ( value !== "" ) {
13702 parsed = this._parse( value );
13703 if ( parsed !== null ) {
13705 parsed = this._adjustValue( parsed );
13707 value = this._format( parsed );
13710 this.element.val( value );
13714 _destroy: function() {
13716 .removeClass( "ui-spinner-input" )
13717 .prop( "disabled", false )
13718 .removeAttr( "autocomplete" )
13719 .removeAttr( "role" )
13720 .removeAttr( "aria-valuemin" )
13721 .removeAttr( "aria-valuemax" )
13722 .removeAttr( "aria-valuenow" );
13723 this.uiSpinner.replaceWith( this.element );
13726 stepUp: modifier(function( steps ) {
13727 this._stepUp( steps );
13729 _stepUp: function( steps ) {
13730 if ( this._start() ) {
13731 this._spin( (steps || 1) * this.options.step );
13736 stepDown: modifier(function( steps ) {
13737 this._stepDown( steps );
13739 _stepDown: function( steps ) {
13740 if ( this._start() ) {
13741 this._spin( (steps || 1) * -this.options.step );
13746 pageUp: modifier(function( pages ) {
13747 this._stepUp( (pages || 1) * this.options.page );
13750 pageDown: modifier(function( pages ) {
13751 this._stepDown( (pages || 1) * this.options.page );
13754 value: function( newVal ) {
13755 if ( !arguments.length ) {
13756 return this._parse( this.element.val() );
13758 modifier( this._value ).call( this, newVal );
13761 widget: function() {
13762 return this.uiSpinner;
13768 (function( $, undefined ) {
13773 function getNextTabId() {
13777 function isLocal( anchor ) {
13778 return anchor.hash.length > 1 &&
13779 decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
13780 decodeURIComponent( location.href.replace( rhash, "" ) );
13783 $.widget( "ui.tabs", {
13788 collapsible: false,
13790 heightStyle: "content",
13796 beforeActivate: null,
13801 _create: function() {
13803 options = this.options;
13805 this.running = false;
13808 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
13809 .toggleClass( "ui-tabs-collapsible", options.collapsible )
13810 // Prevent users from focusing disabled tabs via click
13811 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
13812 if ( $( this ).is( ".ui-state-disabled" ) ) {
13813 event.preventDefault();
13817 // Preventing the default action in mousedown doesn't prevent IE
13818 // from focusing the element, so if the anchor gets focused, blur.
13819 // We don't have to worry about focusing the previously focused
13820 // element since clicking on a non-focusable element should focus
13821 // the body anyway.
13822 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
13823 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
13828 this._processTabs();
13829 options.active = this._initialActive();
13831 // Take disabling tabs via class attribute from HTML
13832 // into account and update option properly.
13833 if ( $.isArray( options.disabled ) ) {
13834 options.disabled = $.unique( options.disabled.concat(
13835 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
13836 return that.tabs.index( li );
13841 // check for length avoids error when initializing empty list
13842 if ( this.options.active !== false && this.anchors.length ) {
13843 this.active = this._findActive( options.active );
13850 if ( this.active.length ) {
13851 this.load( options.active );
13855 _initialActive: function() {
13856 var active = this.options.active,
13857 collapsible = this.options.collapsible,
13858 locationHash = location.hash.substring( 1 );
13860 if ( active === null ) {
13861 // check the fragment identifier in the URL
13862 if ( locationHash ) {
13863 this.tabs.each(function( i, tab ) {
13864 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
13871 // check for a tab marked active via a class
13872 if ( active === null ) {
13873 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
13876 // no active tab, set to false
13877 if ( active === null || active === -1 ) {
13878 active = this.tabs.length ? 0 : false;
13882 // handle numbers: negative, out of range
13883 if ( active !== false ) {
13884 active = this.tabs.index( this.tabs.eq( active ) );
13885 if ( active === -1 ) {
13886 active = collapsible ? false : 0;
13890 // don't allow collapsible: false and active: false
13891 if ( !collapsible && active === false && this.anchors.length ) {
13898 _getCreateEventData: function() {
13901 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
13905 _tabKeydown: function( event ) {
13906 /*jshint maxcomplexity:15*/
13907 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
13908 selectedIndex = this.tabs.index( focusedTab ),
13909 goingForward = true;
13911 if ( this._handlePageNav( event ) ) {
13915 switch ( event.keyCode ) {
13916 case $.ui.keyCode.RIGHT:
13917 case $.ui.keyCode.DOWN:
13920 case $.ui.keyCode.UP:
13921 case $.ui.keyCode.LEFT:
13922 goingForward = false;
13925 case $.ui.keyCode.END:
13926 selectedIndex = this.anchors.length - 1;
13928 case $.ui.keyCode.HOME:
13931 case $.ui.keyCode.SPACE:
13932 // Activate only, no collapsing
13933 event.preventDefault();
13934 clearTimeout( this.activating );
13935 this._activate( selectedIndex );
13937 case $.ui.keyCode.ENTER:
13938 // Toggle (cancel delayed activation, allow collapsing)
13939 event.preventDefault();
13940 clearTimeout( this.activating );
13941 // Determine if we should collapse or activate
13942 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
13948 // Focus the appropriate tab, based on which key was pressed
13949 event.preventDefault();
13950 clearTimeout( this.activating );
13951 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
13953 // Navigating with control key will prevent automatic activation
13954 if ( !event.ctrlKey ) {
13955 // Update aria-selected immediately so that AT think the tab is already selected.
13956 // Otherwise AT may confuse the user by stating that they need to activate the tab,
13957 // but the tab will already be activated by the time the announcement finishes.
13958 focusedTab.attr( "aria-selected", "false" );
13959 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
13961 this.activating = this._delay(function() {
13962 this.option( "active", selectedIndex );
13967 _panelKeydown: function( event ) {
13968 if ( this._handlePageNav( event ) ) {
13972 // Ctrl+up moves focus to the current tab
13973 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
13974 event.preventDefault();
13975 this.active.focus();
13979 // Alt+page up/down moves focus to the previous/next tab (and activates)
13980 _handlePageNav: function( event ) {
13981 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
13982 this._activate( this._focusNextTab( this.options.active - 1, false ) );
13985 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
13986 this._activate( this._focusNextTab( this.options.active + 1, true ) );
13991 _findNextTab: function( index, goingForward ) {
13992 var lastTabIndex = this.tabs.length - 1;
13994 function constrain() {
13995 if ( index > lastTabIndex ) {
13999 index = lastTabIndex;
14004 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
14005 index = goingForward ? index + 1 : index - 1;
14011 _focusNextTab: function( index, goingForward ) {
14012 index = this._findNextTab( index, goingForward );
14013 this.tabs.eq( index ).focus();
14017 _setOption: function( key, value ) {
14018 if ( key === "active" ) {
14019 // _activate() will handle invalid values and update this.options
14020 this._activate( value );
14024 if ( key === "disabled" ) {
14025 // don't use the widget factory's disabled handling
14026 this._setupDisabled( value );
14030 this._super( key, value);
14032 if ( key === "collapsible" ) {
14033 this.element.toggleClass( "ui-tabs-collapsible", value );
14034 // Setting collapsible: false while collapsed; open first panel
14035 if ( !value && this.options.active === false ) {
14036 this._activate( 0 );
14040 if ( key === "event" ) {
14041 this._setupEvents( value );
14044 if ( key === "heightStyle" ) {
14045 this._setupHeightStyle( value );
14049 _tabId: function( tab ) {
14050 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
14053 _sanitizeSelector: function( hash ) {
14054 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
14057 refresh: function() {
14058 var options = this.options,
14059 lis = this.tablist.children( ":has(a[href])" );
14061 // get disabled tabs from class attribute from HTML
14062 // this will get converted to a boolean if needed in _refresh()
14063 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
14064 return lis.index( tab );
14067 this._processTabs();
14069 // was collapsed or no tabs
14070 if ( options.active === false || !this.anchors.length ) {
14071 options.active = false;
14073 // was active, but active tab is gone
14074 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
14075 // all remaining tabs are disabled
14076 if ( this.tabs.length === options.disabled.length ) {
14077 options.active = false;
14079 // activate previous tab
14081 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
14083 // was active, active tab still exists
14085 // make sure active index is correct
14086 options.active = this.tabs.index( this.active );
14092 _refresh: function() {
14093 this._setupDisabled( this.options.disabled );
14094 this._setupEvents( this.options.event );
14095 this._setupHeightStyle( this.options.heightStyle );
14097 this.tabs.not( this.active ).attr({
14098 "aria-selected": "false",
14101 this.panels.not( this._getPanelForTab( this.active ) )
14104 "aria-expanded": "false",
14105 "aria-hidden": "true"
14108 // Make sure one tab is in the tab order
14109 if ( !this.active.length ) {
14110 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
14113 .addClass( "ui-tabs-active ui-state-active" )
14115 "aria-selected": "true",
14118 this._getPanelForTab( this.active )
14121 "aria-expanded": "true",
14122 "aria-hidden": "false"
14127 _processTabs: function() {
14130 this.tablist = this._getList()
14131 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14132 .attr( "role", "tablist" );
14134 this.tabs = this.tablist.find( "> li:has(a[href])" )
14135 .addClass( "ui-state-default ui-corner-top" )
14141 this.anchors = this.tabs.map(function() {
14142 return $( "a", this )[ 0 ];
14144 .addClass( "ui-tabs-anchor" )
14146 role: "presentation",
14152 this.anchors.each(function( i, anchor ) {
14153 var selector, panel, panelId,
14154 anchorId = $( anchor ).uniqueId().attr( "id" ),
14155 tab = $( anchor ).closest( "li" ),
14156 originalAriaControls = tab.attr( "aria-controls" );
14159 if ( isLocal( anchor ) ) {
14160 selector = anchor.hash;
14161 panel = that.element.find( that._sanitizeSelector( selector ) );
14164 panelId = that._tabId( tab );
14165 selector = "#" + panelId;
14166 panel = that.element.find( selector );
14167 if ( !panel.length ) {
14168 panel = that._createPanel( panelId );
14169 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
14171 panel.attr( "aria-live", "polite" );
14174 if ( panel.length) {
14175 that.panels = that.panels.add( panel );
14177 if ( originalAriaControls ) {
14178 tab.data( "ui-tabs-aria-controls", originalAriaControls );
14181 "aria-controls": selector.substring( 1 ),
14182 "aria-labelledby": anchorId
14184 panel.attr( "aria-labelledby", anchorId );
14188 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14189 .attr( "role", "tabpanel" );
14192 // allow overriding how to find the list for rare usage scenarios (#7715)
14193 _getList: function() {
14194 return this.element.find( "ol,ul" ).eq( 0 );
14197 _createPanel: function( id ) {
14198 return $( "<div>" )
14200 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14201 .data( "ui-tabs-destroy", true );
14204 _setupDisabled: function( disabled ) {
14205 if ( $.isArray( disabled ) ) {
14206 if ( !disabled.length ) {
14208 } else if ( disabled.length === this.anchors.length ) {
14214 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
14215 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
14217 .addClass( "ui-state-disabled" )
14218 .attr( "aria-disabled", "true" );
14221 .removeClass( "ui-state-disabled" )
14222 .removeAttr( "aria-disabled" );
14226 this.options.disabled = disabled;
14229 _setupEvents: function( event ) {
14231 click: function( event ) {
14232 event.preventDefault();
14236 $.each( event.split(" "), function( index, eventName ) {
14237 events[ eventName ] = "_eventHandler";
14241 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
14242 this._on( this.anchors, events );
14243 this._on( this.tabs, { keydown: "_tabKeydown" } );
14244 this._on( this.panels, { keydown: "_panelKeydown" } );
14246 this._focusable( this.tabs );
14247 this._hoverable( this.tabs );
14250 _setupHeightStyle: function( heightStyle ) {
14252 parent = this.element.parent();
14254 if ( heightStyle === "fill" ) {
14255 maxHeight = parent.height();
14256 maxHeight -= this.element.outerHeight() - this.element.height();
14258 this.element.siblings( ":visible" ).each(function() {
14259 var elem = $( this ),
14260 position = elem.css( "position" );
14262 if ( position === "absolute" || position === "fixed" ) {
14265 maxHeight -= elem.outerHeight( true );
14268 this.element.children().not( this.panels ).each(function() {
14269 maxHeight -= $( this ).outerHeight( true );
14272 this.panels.each(function() {
14273 $( this ).height( Math.max( 0, maxHeight -
14274 $( this ).innerHeight() + $( this ).height() ) );
14276 .css( "overflow", "auto" );
14277 } else if ( heightStyle === "auto" ) {
14279 this.panels.each(function() {
14280 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
14281 }).height( maxHeight );
14285 _eventHandler: function( event ) {
14286 var options = this.options,
14287 active = this.active,
14288 anchor = $( event.currentTarget ),
14289 tab = anchor.closest( "li" ),
14290 clickedIsActive = tab[ 0 ] === active[ 0 ],
14291 collapsing = clickedIsActive && options.collapsible,
14292 toShow = collapsing ? $() : this._getPanelForTab( tab ),
14293 toHide = !active.length ? $() : this._getPanelForTab( active ),
14297 newTab: collapsing ? $() : tab,
14301 event.preventDefault();
14303 if ( tab.hasClass( "ui-state-disabled" ) ||
14304 // tab is already loading
14305 tab.hasClass( "ui-tabs-loading" ) ||
14306 // can't switch durning an animation
14308 // click on active header, but not collapsible
14309 ( clickedIsActive && !options.collapsible ) ||
14310 // allow canceling activation
14311 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
14315 options.active = collapsing ? false : this.tabs.index( tab );
14317 this.active = clickedIsActive ? $() : tab;
14322 if ( !toHide.length && !toShow.length ) {
14323 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
14326 if ( toShow.length ) {
14327 this.load( this.tabs.index( tab ), event );
14329 this._toggle( event, eventData );
14332 // handles show/hide for selecting tabs
14333 _toggle: function( event, eventData ) {
14335 toShow = eventData.newPanel,
14336 toHide = eventData.oldPanel;
14338 this.running = true;
14340 function complete() {
14341 that.running = false;
14342 that._trigger( "activate", event, eventData );
14346 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
14348 if ( toShow.length && that.options.show ) {
14349 that._show( toShow, that.options.show, complete );
14356 // start out by hiding, then showing, then completing
14357 if ( toHide.length && this.options.hide ) {
14358 this._hide( toHide, this.options.hide, function() {
14359 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14363 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14369 "aria-expanded": "false",
14370 "aria-hidden": "true"
14372 eventData.oldTab.attr( "aria-selected", "false" );
14373 // If we're switching tabs, remove the old tab from the tab order.
14374 // If we're opening from collapsed state, remove the previous tab from the tab order.
14375 // If we're collapsing, then keep the collapsing tab in the tab order.
14376 if ( toShow.length && toHide.length ) {
14377 eventData.oldTab.attr( "tabIndex", -1 );
14378 } else if ( toShow.length ) {
14379 this.tabs.filter(function() {
14380 return $( this ).attr( "tabIndex" ) === 0;
14382 .attr( "tabIndex", -1 );
14386 "aria-expanded": "true",
14387 "aria-hidden": "false"
14389 eventData.newTab.attr({
14390 "aria-selected": "true",
14395 _activate: function( index ) {
14397 active = this._findActive( index );
14399 // trying to activate the already active panel
14400 if ( active[ 0 ] === this.active[ 0 ] ) {
14404 // trying to collapse, simulate a click on the current active header
14405 if ( !active.length ) {
14406 active = this.active;
14409 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
14410 this._eventHandler({
14412 currentTarget: anchor,
14413 preventDefault: $.noop
14417 _findActive: function( index ) {
14418 return index === false ? $() : this.tabs.eq( index );
14421 _getIndex: function( index ) {
14422 // meta-function to give users option to provide a href string instead of a numerical index.
14423 if ( typeof index === "string" ) {
14424 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
14430 _destroy: function() {
14435 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
14438 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14439 .removeAttr( "role" );
14442 .removeClass( "ui-tabs-anchor" )
14443 .removeAttr( "role" )
14444 .removeAttr( "tabIndex" )
14447 this.tabs.add( this.panels ).each(function() {
14448 if ( $.data( this, "ui-tabs-destroy" ) ) {
14449 $( this ).remove();
14452 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
14453 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
14454 .removeAttr( "tabIndex" )
14455 .removeAttr( "aria-live" )
14456 .removeAttr( "aria-busy" )
14457 .removeAttr( "aria-selected" )
14458 .removeAttr( "aria-labelledby" )
14459 .removeAttr( "aria-hidden" )
14460 .removeAttr( "aria-expanded" )
14461 .removeAttr( "role" );
14465 this.tabs.each(function() {
14466 var li = $( this ),
14467 prev = li.data( "ui-tabs-aria-controls" );
14470 .attr( "aria-controls", prev )
14471 .removeData( "ui-tabs-aria-controls" );
14473 li.removeAttr( "aria-controls" );
14477 this.panels.show();
14479 if ( this.options.heightStyle !== "content" ) {
14480 this.panels.css( "height", "" );
14484 enable: function( index ) {
14485 var disabled = this.options.disabled;
14486 if ( disabled === false ) {
14490 if ( index === undefined ) {
14493 index = this._getIndex( index );
14494 if ( $.isArray( disabled ) ) {
14495 disabled = $.map( disabled, function( num ) {
14496 return num !== index ? num : null;
14499 disabled = $.map( this.tabs, function( li, num ) {
14500 return num !== index ? num : null;
14504 this._setupDisabled( disabled );
14507 disable: function( index ) {
14508 var disabled = this.options.disabled;
14509 if ( disabled === true ) {
14513 if ( index === undefined ) {
14516 index = this._getIndex( index );
14517 if ( $.inArray( index, disabled ) !== -1 ) {
14520 if ( $.isArray( disabled ) ) {
14521 disabled = $.merge( [ index ], disabled ).sort();
14523 disabled = [ index ];
14526 this._setupDisabled( disabled );
14529 load: function( index, event ) {
14530 index = this._getIndex( index );
14532 tab = this.tabs.eq( index ),
14533 anchor = tab.find( ".ui-tabs-anchor" ),
14534 panel = this._getPanelForTab( tab ),
14541 if ( isLocal( anchor[ 0 ] ) ) {
14545 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
14547 // support: jQuery <1.8
14548 // jQuery <1.8 returns false if the request is canceled in beforeSend,
14549 // but as of 1.8, $.ajax() always returns a jqXHR object.
14550 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
14551 tab.addClass( "ui-tabs-loading" );
14552 panel.attr( "aria-busy", "true" );
14555 .success(function( response ) {
14556 // support: jQuery <1.8
14557 // http://bugs.jquery.com/ticket/11778
14558 setTimeout(function() {
14559 panel.html( response );
14560 that._trigger( "load", event, eventData );
14563 .complete(function( jqXHR, status ) {
14564 // support: jQuery <1.8
14565 // http://bugs.jquery.com/ticket/11778
14566 setTimeout(function() {
14567 if ( status === "abort" ) {
14568 that.panels.stop( false, true );
14571 tab.removeClass( "ui-tabs-loading" );
14572 panel.removeAttr( "aria-busy" );
14574 if ( jqXHR === that.xhr ) {
14582 _ajaxSettings: function( anchor, event, eventData ) {
14585 url: anchor.attr( "href" ),
14586 beforeSend: function( jqXHR, settings ) {
14587 return that._trigger( "beforeLoad", event,
14588 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
14593 _getPanelForTab: function( tab ) {
14594 var id = $( tab ).attr( "aria-controls" );
14595 return this.element.find( this._sanitizeSelector( "#" + id ) );
14603 var increments = 0;
14605 function addDescribedBy( elem, id ) {
14606 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
14607 describedby.push( id );
14609 .data( "ui-tooltip-id", id )
14610 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
14613 function removeDescribedBy( elem ) {
14614 var id = elem.data( "ui-tooltip-id" ),
14615 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
14616 index = $.inArray( id, describedby );
14617 if ( index !== -1 ) {
14618 describedby.splice( index, 1 );
14621 elem.removeData( "ui-tooltip-id" );
14622 describedby = $.trim( describedby.join( " " ) );
14623 if ( describedby ) {
14624 elem.attr( "aria-describedby", describedby );
14626 elem.removeAttr( "aria-describedby" );
14630 $.widget( "ui.tooltip", {
14633 content: function() {
14634 // support: IE<9, Opera in jQuery <1.7
14635 // .text() can't accept undefined, so coerce to a string
14636 var title = $( this ).attr( "title" ) || "";
14637 // Escape title, since we're going from an attribute to raw HTML
14638 return $( "<a>" ).text( title ).html();
14641 // Disabled elements have inconsistent behavior across browsers (#8661)
14642 items: "[title]:not([disabled])",
14646 collision: "flipfit flip"
14649 tooltipClass: null,
14657 _create: function() {
14663 // IDs of generated tooltips, needed for destroy
14664 this.tooltips = {};
14665 // IDs of parent tooltips where we removed the title attribute
14668 if ( this.options.disabled ) {
14673 _setOption: function( key, value ) {
14676 if ( key === "disabled" ) {
14677 this[ value ? "_disable" : "_enable" ]();
14678 this.options[ key ] = value;
14679 // disable element style changes
14683 this._super( key, value );
14685 if ( key === "content" ) {
14686 $.each( this.tooltips, function( id, element ) {
14687 that._updateContent( element );
14692 _disable: function() {
14695 // close open tooltips
14696 $.each( this.tooltips, function( id, element ) {
14697 var event = $.Event( "blur" );
14698 event.target = event.currentTarget = element[0];
14699 that.close( event, true );
14702 // remove title attributes to prevent native tooltips
14703 this.element.find( this.options.items ).addBack().each(function() {
14704 var element = $( this );
14705 if ( element.is( "[title]" ) ) {
14707 .data( "ui-tooltip-title", element.attr( "title" ) )
14708 .attr( "title", "" );
14713 _enable: function() {
14714 // restore title attributes
14715 this.element.find( this.options.items ).addBack().each(function() {
14716 var element = $( this );
14717 if ( element.data( "ui-tooltip-title" ) ) {
14718 element.attr( "title", element.data( "ui-tooltip-title" ) );
14723 open: function( event ) {
14725 target = $( event ? event.target : this.element )
14726 // we need closest here due to mouseover bubbling,
14727 // but always pointing at the same event target
14728 .closest( this.options.items );
14730 // No element to show a tooltip for or the tooltip is already open
14731 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
14735 if ( target.attr( "title" ) ) {
14736 target.data( "ui-tooltip-title", target.attr( "title" ) );
14739 target.data( "ui-tooltip-open", true );
14741 // kill parent tooltips, custom or native, for hover
14742 if ( event && event.type === "mouseover" ) {
14743 target.parents().each(function() {
14744 var parent = $( this ),
14746 if ( parent.data( "ui-tooltip-open" ) ) {
14747 blurEvent = $.Event( "blur" );
14748 blurEvent.target = blurEvent.currentTarget = this;
14749 that.close( blurEvent, true );
14751 if ( parent.attr( "title" ) ) {
14753 that.parents[ this.id ] = {
14755 title: parent.attr( "title" )
14757 parent.attr( "title", "" );
14762 this._updateContent( target, event );
14765 _updateContent: function( target, event ) {
14767 contentOption = this.options.content,
14769 eventType = event ? event.type : null;
14771 if ( typeof contentOption === "string" ) {
14772 return this._open( event, target, contentOption );
14775 content = contentOption.call( target[0], function( response ) {
14776 // ignore async response if tooltip was closed already
14777 if ( !target.data( "ui-tooltip-open" ) ) {
14780 // IE may instantly serve a cached response for ajax requests
14781 // delay this call to _open so the other call to _open runs first
14782 that._delay(function() {
14783 // jQuery creates a special event for focusin when it doesn't
14784 // exist natively. To improve performance, the native event
14785 // object is reused and the type is changed. Therefore, we can't
14786 // rely on the type being correct after the event finished
14787 // bubbling, so we set it back to the previous value. (#8740)
14789 event.type = eventType;
14791 this._open( event, target, response );
14795 this._open( event, target, content );
14799 _open: function( event, target, content ) {
14800 var tooltip, events, delayedShow,
14801 positionOption = $.extend( {}, this.options.position );
14807 // Content can be updated multiple times. If the tooltip already
14808 // exists, then just update the content and bail.
14809 tooltip = this._find( target );
14810 if ( tooltip.length ) {
14811 tooltip.find( ".ui-tooltip-content" ).html( content );
14815 // if we have a title, clear it to prevent the native tooltip
14816 // we have to check first to avoid defining a title if none exists
14817 // (we don't want to cause an element to start matching [title])
14819 // We use removeAttr only for key events, to allow IE to export the correct
14820 // accessible attributes. For mouse events, set to empty string to avoid
14821 // native tooltip showing up (happens only when removing inside mouseover).
14822 if ( target.is( "[title]" ) ) {
14823 if ( event && event.type === "mouseover" ) {
14824 target.attr( "title", "" );
14826 target.removeAttr( "title" );
14830 tooltip = this._tooltip( target );
14831 addDescribedBy( target, tooltip.attr( "id" ) );
14832 tooltip.find( ".ui-tooltip-content" ).html( content );
14834 function position( event ) {
14835 positionOption.of = event;
14836 if ( tooltip.is( ":hidden" ) ) {
14839 tooltip.position( positionOption );
14841 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
14842 this._on( this.document, {
14843 mousemove: position
14845 // trigger once to override element-relative positioning
14848 tooltip.position( $.extend({
14850 }, this.options.position ) );
14855 this._show( tooltip, this.options.show );
14856 // Handle tracking tooltips that are shown with a delay (#8644). As soon
14857 // as the tooltip is visible, position the tooltip using the most recent
14859 if ( this.options.show && this.options.show.delay ) {
14860 delayedShow = this.delayedShow = setInterval(function() {
14861 if ( tooltip.is( ":visible" ) ) {
14862 position( positionOption.of );
14863 clearInterval( delayedShow );
14865 }, $.fx.interval );
14868 this._trigger( "open", event, { tooltip: tooltip } );
14871 keyup: function( event ) {
14872 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
14873 var fakeEvent = $.Event(event);
14874 fakeEvent.currentTarget = target[0];
14875 this.close( fakeEvent, true );
14878 remove: function() {
14879 this._removeTooltip( tooltip );
14882 if ( !event || event.type === "mouseover" ) {
14883 events.mouseleave = "close";
14885 if ( !event || event.type === "focusin" ) {
14886 events.focusout = "close";
14888 this._on( true, target, events );
14891 close: function( event ) {
14893 target = $( event ? event.currentTarget : this.element ),
14894 tooltip = this._find( target );
14896 // disabling closes the tooltip, so we need to track when we're closing
14897 // to avoid an infinite loop in case the tooltip becomes disabled on close
14898 if ( this.closing ) {
14902 // Clear the interval for delayed tracking tooltips
14903 clearInterval( this.delayedShow );
14905 // only set title if we had one before (see comment in _open())
14906 if ( target.data( "ui-tooltip-title" ) ) {
14907 target.attr( "title", target.data( "ui-tooltip-title" ) );
14910 removeDescribedBy( target );
14912 tooltip.stop( true );
14913 this._hide( tooltip, this.options.hide, function() {
14914 that._removeTooltip( $( this ) );
14917 target.removeData( "ui-tooltip-open" );
14918 this._off( target, "mouseleave focusout keyup" );
14919 // Remove 'remove' binding only on delegated targets
14920 if ( target[0] !== this.element[0] ) {
14921 this._off( target, "remove" );
14923 this._off( this.document, "mousemove" );
14925 if ( event && event.type === "mouseleave" ) {
14926 $.each( this.parents, function( id, parent ) {
14927 $( parent.element ).attr( "title", parent.title );
14928 delete that.parents[ id ];
14932 this.closing = true;
14933 this._trigger( "close", event, { tooltip: tooltip } );
14934 this.closing = false;
14937 _tooltip: function( element ) {
14938 var id = "ui-tooltip-" + increments++,
14939 tooltip = $( "<div>" )
14944 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
14945 ( this.options.tooltipClass || "" ) );
14947 .addClass( "ui-tooltip-content" )
14948 .appendTo( tooltip );
14949 tooltip.appendTo( this.document[0].body );
14950 this.tooltips[ id ] = element;
14954 _find: function( target ) {
14955 var id = target.data( "ui-tooltip-id" );
14956 return id ? $( "#" + id ) : $();
14959 _removeTooltip: function( tooltip ) {
14961 delete this.tooltips[ tooltip.attr( "id" ) ];
14964 _destroy: function() {
14967 // close open tooltips
14968 $.each( this.tooltips, function( id, element ) {
14969 // Delegate to close method to handle common cleanup
14970 var event = $.Event( "blur" );
14971 event.target = event.currentTarget = element[0];
14972 that.close( event, true );
14974 // Remove immediately; destroying an open tooltip doesn't use the
14976 $( "#" + id ).remove();
14978 // Restore the title
14979 if ( element.data( "ui-tooltip-title" ) ) {
14980 element.attr( "title", element.data( "ui-tooltip-title" ) );
14981 element.removeData( "ui-tooltip-title" );