1 /*! jQuery UI - v1.10.4 - 2014-01-17
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.slider.js, jquery.ui.sortable.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
4 * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // $.ui might exist from components with no dependencies, e.g., $.ui.position
45 focus: (function( orig ) {
46 return function( delay, fn ) {
47 return typeof delay === "number" ?
48 this.each(function() {
50 setTimeout(function() {
57 orig.apply( this, arguments );
61 scrollParent: function() {
63 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
64 scrollParent = this.parents().filter(function() {
65 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
68 scrollParent = this.parents().filter(function() {
69 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
73 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
76 zIndex: function( zIndex ) {
77 if ( zIndex !== undefined ) {
78 return this.css( "zIndex", zIndex );
82 var elem = $( this[ 0 ] ), position, value;
83 while ( elem.length && elem[ 0 ] !== document ) {
84 // Ignore z-index if position is set to a value where z-index is ignored by the browser
85 // This makes behavior of this function consistent across browsers
86 // WebKit always returns auto if the element is positioned
87 position = elem.css( "position" );
88 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
89 // IE returns 0 when zIndex is not specified
90 // other browsers return a string
91 // we ignore the case of nested elements with an explicit value of 0
92 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
93 value = parseInt( elem.css( "zIndex" ), 10 );
94 if ( !isNaN( value ) && value !== 0 ) {
105 uniqueId: function() {
106 return this.each(function() {
108 this.id = "ui-id-" + (++uuid);
113 removeUniqueId: function() {
114 return this.each(function() {
115 if ( runiqueId.test( this.id ) ) {
116 $( this ).removeAttr( "id" );
123 function focusable( element, isTabIndexNotNaN ) {
124 var map, mapName, img,
125 nodeName = element.nodeName.toLowerCase();
126 if ( "area" === nodeName ) {
127 map = element.parentNode;
129 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
132 img = $( "img[usemap=#" + mapName + "]" )[0];
133 return !!img && visible( img );
135 return ( /input|select|textarea|button|object/.test( nodeName ) ?
138 element.href || isTabIndexNotNaN :
140 // the element and all of its ancestors must be visible
144 function visible( element ) {
145 return $.expr.filters.visible( element ) &&
146 !$( element ).parents().addBack().filter(function() {
147 return $.css( this, "visibility" ) === "hidden";
151 $.extend( $.expr[ ":" ], {
152 data: $.expr.createPseudo ?
153 $.expr.createPseudo(function( dataName ) {
154 return function( elem ) {
155 return !!$.data( elem, dataName );
158 // support: jQuery <1.8
159 function( elem, i, match ) {
160 return !!$.data( elem, match[ 3 ] );
163 focusable: function( element ) {
164 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
167 tabbable: function( element ) {
168 var tabIndex = $.attr( element, "tabindex" ),
169 isTabIndexNaN = isNaN( tabIndex );
170 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
174 // support: jQuery <1.8
175 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
176 $.each( [ "Width", "Height" ], function( i, name ) {
177 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
178 type = name.toLowerCase(),
180 innerWidth: $.fn.innerWidth,
181 innerHeight: $.fn.innerHeight,
182 outerWidth: $.fn.outerWidth,
183 outerHeight: $.fn.outerHeight
186 function reduce( elem, size, border, margin ) {
187 $.each( side, function() {
188 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
190 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
193 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
199 $.fn[ "inner" + name ] = function( size ) {
200 if ( size === undefined ) {
201 return orig[ "inner" + name ].call( this );
204 return this.each(function() {
205 $( this ).css( type, reduce( this, size ) + "px" );
209 $.fn[ "outer" + name] = function( size, margin ) {
210 if ( typeof size !== "number" ) {
211 return orig[ "outer" + name ].call( this, size );
214 return this.each(function() {
215 $( this).css( type, reduce( this, size, true, margin ) + "px" );
221 // support: jQuery <1.8
222 if ( !$.fn.addBack ) {
223 $.fn.addBack = function( selector ) {
224 return this.add( selector == null ?
225 this.prevObject : this.prevObject.filter( selector )
230 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
231 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
232 $.fn.removeData = (function( removeData ) {
233 return function( key ) {
234 if ( arguments.length ) {
235 return removeData.call( this, $.camelCase( key ) );
237 return removeData.call( this );
240 })( $.fn.removeData );
248 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
250 $.support.selectstart = "onselectstart" in document.createElement( "div" );
252 disableSelection: function() {
253 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
254 ".ui-disableSelection", function( event ) {
255 event.preventDefault();
259 enableSelection: function() {
260 return this.unbind( ".ui-disableSelection" );
265 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
267 add: function( module, option, set ) {
269 proto = $.ui[ module ].prototype;
271 proto.plugins[ i ] = proto.plugins[ i ] || [];
272 proto.plugins[ i ].push( [ option, set[ i ] ] );
275 call: function( instance, name, args ) {
277 set = instance.plugins[ name ];
278 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
282 for ( i = 0; i < set.length; i++ ) {
283 if ( instance.options[ set[ i ][ 0 ] ] ) {
284 set[ i ][ 1 ].apply( instance.element, args );
290 // only used by resizable
291 hasScroll: function( el, a ) {
293 //If overflow is hidden, the element might have extra content, but the user wants to hide it
294 if ( $( el ).css( "overflow" ) === "hidden") {
298 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
301 if ( el[ scroll ] > 0 ) {
305 // TODO: determine which cases actually cause this to happen
306 // if the element doesn't have the scroll set, see if it's possible to
309 has = ( el[ scroll ] > 0 );
316 (function( $, undefined ) {
319 slice = Array.prototype.slice,
320 _cleanData = $.cleanData;
321 $.cleanData = function( elems ) {
322 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
324 $( elem ).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
331 $.widget = function( name, base, prototype ) {
332 var fullName, existingConstructor, constructor, basePrototype,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype = {},
336 namespace = name.split( "." )[ 0 ];
338 name = name.split( "." )[ 1 ];
339 fullName = namespace + "-" + name;
346 // create selector for plugin
347 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348 return !!$.data( elem, fullName );
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor = $[ namespace ][ name ];
353 constructor = $[ namespace ][ name ] = function( options, element ) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget ) {
356 return new constructor( options, element );
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments.length ) {
362 this._createWidget( options, element );
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor, {
367 version: prototype.version,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype = new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
380 basePrototype.options = $.widget.extend( {}, basePrototype.options );
381 $.each( prototype, function( prop, value ) {
382 if ( !$.isFunction( value ) ) {
383 proxiedPrototype[ prop ] = value;
386 proxiedPrototype[ prop ] = (function() {
387 var _super = function() {
388 return base.prototype[ prop ].apply( this, arguments );
390 _superApply = function( args ) {
391 return base.prototype[ prop ].apply( this, args );
394 var __super = this._super,
395 __superApply = this._superApply,
398 this._super = _super;
399 this._superApply = _superApply;
401 returnValue = value.apply( this, arguments );
403 this._super = __super;
404 this._superApply = __superApply;
410 constructor.prototype = $.widget.extend( basePrototype, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : 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 ( arguments.length === 1 ) {
624 return curOption[ key ] === undefined ? null : curOption[ key ];
626 curOption[ key ] = value;
628 if ( arguments.length === 1 ) {
629 return this.options[ key ] === undefined ? null : this.options[ key ];
631 options[ key ] = value;
635 this._setOptions( options );
639 _setOptions: function( options ) {
642 for ( key in options ) {
643 this._setOption( key, options[ key ] );
648 _setOption: function( key, value ) {
649 this.options[ key ] = value;
651 if ( key === "disabled" ) {
653 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
654 .attr( "aria-disabled", value );
655 this.hoverable.removeClass( "ui-state-hover" );
656 this.focusable.removeClass( "ui-state-focus" );
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck, element, handlers ) {
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck !== "boolean" ) {
676 element = suppressDisabledCheck;
677 suppressDisabledCheck = false;
680 // no element argument, shuffle and use this.element
683 element = this.element;
684 delegateElement = this.widget();
686 // accept selectors, DOM elements
687 element = delegateElement = $( element );
688 this.bindings = this.bindings.add( element );
691 $.each( handlers, function( event, handler ) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck &&
697 ( instance.options.disabled === true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
701 return ( typeof handler === "string" ? instance[ handler ] : handler )
702 .apply( instance, arguments );
705 // copy the guid so direct unbinding works
706 if ( typeof handler !== "string" ) {
707 handlerProxy.guid = handler.guid =
708 handler.guid || handlerProxy.guid || $.guid++;
711 var match = event.match( /^(\w+)\s*(.*)$/ ),
712 eventName = match[1] + instance.eventNamespace,
715 delegateElement.delegate( selector, eventName, handlerProxy );
717 element.bind( eventName, handlerProxy );
722 _off: function( element, eventName ) {
723 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724 element.unbind( eventName ).undelegate( eventName );
727 _delay: function( handler, delay ) {
728 function handlerProxy() {
729 return ( typeof handler === "string" ? instance[ handler ] : handler )
730 .apply( instance, arguments );
733 return setTimeout( handlerProxy, delay || 0 );
736 _hoverable: function( element ) {
737 this.hoverable = this.hoverable.add( element );
739 mouseenter: function( event ) {
740 $( event.currentTarget ).addClass( "ui-state-hover" );
742 mouseleave: function( event ) {
743 $( event.currentTarget ).removeClass( "ui-state-hover" );
748 _focusable: function( element ) {
749 this.focusable = this.focusable.add( element );
751 focusin: function( event ) {
752 $( event.currentTarget ).addClass( "ui-state-focus" );
754 focusout: function( event ) {
755 $( event.currentTarget ).removeClass( "ui-state-focus" );
760 _trigger: function( type, event, data ) {
762 callback = this.options[ type ];
765 event = $.Event( event );
766 event.type = ( type === this.widgetEventPrefix ?
768 this.widgetEventPrefix + type ).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event.target = this.element[ 0 ];
773 // copy original event properties over to the new event
774 orig = event.originalEvent;
776 for ( prop in orig ) {
777 if ( !( prop in event ) ) {
778 event[ prop ] = orig[ prop ];
783 this.element.trigger( event, data );
784 return !( $.isFunction( callback ) &&
785 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
786 event.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792 if ( typeof options === "string" ) {
793 options = { effect: options };
796 effectName = !options ?
798 options === true || typeof options === "number" ?
800 options.effect || defaultEffect;
801 options = options || {};
802 if ( typeof options === "number" ) {
803 options = { duration: options };
805 hasOptions = !$.isEmptyObject( options );
806 options.complete = callback;
807 if ( options.delay ) {
808 element.delay( options.delay );
810 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811 element[ method ]( options );
812 } else if ( effectName !== method && element[ effectName ] ) {
813 element[ effectName ]( options.duration, options.easing, callback );
815 element.queue(function( next ) {
816 $( this )[ method ]();
818 callback.call( element[ 0 ] );
827 (function( $, undefined ) {
829 var mouseHandled = false;
830 $( document ).mouseup( function() {
831 mouseHandled = false;
834 $.widget("ui.mouse", {
837 cancel: "input,textarea,button,select,option",
841 _mouseInit: function() {
845 .bind("mousedown."+this.widgetName, function(event) {
846 return that._mouseDown(event);
848 .bind("click."+this.widgetName, function(event) {
849 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
850 $.removeData(event.target, that.widgetName + ".preventClickEvent");
851 event.stopImmediatePropagation();
856 this.started = false;
859 // TODO: make sure destroying one instance of mouse doesn't mess with
860 // other instances of mouse
861 _mouseDestroy: function() {
862 this.element.unbind("."+this.widgetName);
863 if ( this._mouseMoveDelegate ) {
865 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
866 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
870 _mouseDown: function(event) {
871 // don't let more than one widget handle mouseStart
872 if( mouseHandled ) { return; }
874 // we may have missed mouseup (out of window)
875 (this._mouseStarted && this._mouseUp(event));
877 this._mouseDownEvent = event;
880 btnIsLeft = (event.which === 1),
881 // event.target.nodeName works around a bug in IE 8 with
882 // disabled inputs (#7620)
883 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
884 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
888 this.mouseDelayMet = !this.options.delay;
889 if (!this.mouseDelayMet) {
890 this._mouseDelayTimer = setTimeout(function() {
891 that.mouseDelayMet = true;
892 }, this.options.delay);
895 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
896 this._mouseStarted = (this._mouseStart(event) !== false);
897 if (!this._mouseStarted) {
898 event.preventDefault();
903 // Click event may never have fired (Gecko & Opera)
904 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
905 $.removeData(event.target, this.widgetName + ".preventClickEvent");
908 // these delegates are required to keep context
909 this._mouseMoveDelegate = function(event) {
910 return that._mouseMove(event);
912 this._mouseUpDelegate = function(event) {
913 return that._mouseUp(event);
916 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
917 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
919 event.preventDefault();
925 _mouseMove: function(event) {
926 // IE mouseup check - mouseup happened when mouse was out of window
927 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
928 return this._mouseUp(event);
931 if (this._mouseStarted) {
932 this._mouseDrag(event);
933 return event.preventDefault();
936 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
938 (this._mouseStart(this._mouseDownEvent, event) !== false);
939 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
942 return !this._mouseStarted;
945 _mouseUp: function(event) {
947 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
948 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
950 if (this._mouseStarted) {
951 this._mouseStarted = false;
953 if (event.target === this._mouseDownEvent.target) {
954 $.data(event.target, this.widgetName + ".preventClickEvent", true);
957 this._mouseStop(event);
963 _mouseDistanceMet: function(event) {
965 Math.abs(this._mouseDownEvent.pageX - event.pageX),
966 Math.abs(this._mouseDownEvent.pageY - event.pageY)
967 ) >= this.options.distance
971 _mouseDelayMet: function(/* event */) {
972 return this.mouseDelayMet;
975 // These are placeholder methods, to be overriden by extending plugin
976 _mouseStart: function(/* event */) {},
977 _mouseDrag: function(/* event */) {},
978 _mouseStop: function(/* event */) {},
979 _mouseCapture: function(/* event */) { return true; }
983 (function( $, undefined ) {
987 var cachedScrollbarWidth,
991 rhorizontal = /left|center|right/,
992 rvertical = /top|center|bottom/,
993 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
996 _position = $.fn.position;
998 function getOffsets( offsets, width, height ) {
1000 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1001 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1005 function parseCss( element, property ) {
1006 return parseInt( $.css( element, property ), 10 ) || 0;
1009 function getDimensions( elem ) {
1011 if ( raw.nodeType === 9 ) {
1013 width: elem.width(),
1014 height: elem.height(),
1015 offset: { top: 0, left: 0 }
1018 if ( $.isWindow( raw ) ) {
1020 width: elem.width(),
1021 height: elem.height(),
1022 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
1025 if ( raw.preventDefault ) {
1029 offset: { top: raw.pageY, left: raw.pageX }
1033 width: elem.outerWidth(),
1034 height: elem.outerHeight(),
1035 offset: elem.offset()
1040 scrollbarWidth: function() {
1041 if ( cachedScrollbarWidth !== undefined ) {
1042 return cachedScrollbarWidth;
1045 div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1046 innerDiv = div.children()[0];
1048 $( "body" ).append( div );
1049 w1 = innerDiv.offsetWidth;
1050 div.css( "overflow", "scroll" );
1052 w2 = innerDiv.offsetWidth;
1055 w2 = div[0].clientWidth;
1060 return (cachedScrollbarWidth = w1 - w2);
1062 getScrollInfo: function( within ) {
1063 var overflowX = within.isWindow || within.isDocument ? "" :
1064 within.element.css( "overflow-x" ),
1065 overflowY = within.isWindow || within.isDocument ? "" :
1066 within.element.css( "overflow-y" ),
1067 hasOverflowX = overflowX === "scroll" ||
1068 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1069 hasOverflowY = overflowY === "scroll" ||
1070 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1072 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
1073 height: hasOverflowX ? $.position.scrollbarWidth() : 0
1076 getWithinInfo: function( element ) {
1077 var withinElement = $( element || window ),
1078 isWindow = $.isWindow( withinElement[0] ),
1079 isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
1081 element: withinElement,
1083 isDocument: isDocument,
1084 offset: withinElement.offset() || { left: 0, top: 0 },
1085 scrollLeft: withinElement.scrollLeft(),
1086 scrollTop: withinElement.scrollTop(),
1087 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1088 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1093 $.fn.position = function( options ) {
1094 if ( !options || !options.of ) {
1095 return _position.apply( this, arguments );
1098 // make a copy, we don't want to modify arguments
1099 options = $.extend( {}, options );
1101 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
1102 target = $( options.of ),
1103 within = $.position.getWithinInfo( options.within ),
1104 scrollInfo = $.position.getScrollInfo( within ),
1105 collision = ( options.collision || "flip" ).split( " " ),
1108 dimensions = getDimensions( target );
1109 if ( target[0].preventDefault ) {
1110 // force left top to allow flipping
1111 options.at = "left top";
1113 targetWidth = dimensions.width;
1114 targetHeight = dimensions.height;
1115 targetOffset = dimensions.offset;
1116 // clone to reuse original targetOffset later
1117 basePosition = $.extend( {}, targetOffset );
1119 // force my and at to have valid horizontal and vertical positions
1120 // if a value is missing or invalid, it will be converted to center
1121 $.each( [ "my", "at" ], function() {
1122 var pos = ( options[ this ] || "" ).split( " " ),
1126 if ( pos.length === 1) {
1127 pos = rhorizontal.test( pos[ 0 ] ) ?
1128 pos.concat( [ "center" ] ) :
1129 rvertical.test( pos[ 0 ] ) ?
1130 [ "center" ].concat( pos ) :
1131 [ "center", "center" ];
1133 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1134 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1136 // calculate offsets
1137 horizontalOffset = roffset.exec( pos[ 0 ] );
1138 verticalOffset = roffset.exec( pos[ 1 ] );
1140 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1141 verticalOffset ? verticalOffset[ 0 ] : 0
1144 // reduce to just the positions without the offsets
1146 rposition.exec( pos[ 0 ] )[ 0 ],
1147 rposition.exec( pos[ 1 ] )[ 0 ]
1151 // normalize collision option
1152 if ( collision.length === 1 ) {
1153 collision[ 1 ] = collision[ 0 ];
1156 if ( options.at[ 0 ] === "right" ) {
1157 basePosition.left += targetWidth;
1158 } else if ( options.at[ 0 ] === "center" ) {
1159 basePosition.left += targetWidth / 2;
1162 if ( options.at[ 1 ] === "bottom" ) {
1163 basePosition.top += targetHeight;
1164 } else if ( options.at[ 1 ] === "center" ) {
1165 basePosition.top += targetHeight / 2;
1168 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1169 basePosition.left += atOffset[ 0 ];
1170 basePosition.top += atOffset[ 1 ];
1172 return this.each(function() {
1173 var collisionPosition, using,
1175 elemWidth = elem.outerWidth(),
1176 elemHeight = elem.outerHeight(),
1177 marginLeft = parseCss( this, "marginLeft" ),
1178 marginTop = parseCss( this, "marginTop" ),
1179 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1180 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1181 position = $.extend( {}, basePosition ),
1182 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1184 if ( options.my[ 0 ] === "right" ) {
1185 position.left -= elemWidth;
1186 } else if ( options.my[ 0 ] === "center" ) {
1187 position.left -= elemWidth / 2;
1190 if ( options.my[ 1 ] === "bottom" ) {
1191 position.top -= elemHeight;
1192 } else if ( options.my[ 1 ] === "center" ) {
1193 position.top -= elemHeight / 2;
1196 position.left += myOffset[ 0 ];
1197 position.top += myOffset[ 1 ];
1199 // if the browser doesn't support fractions, then round for consistent results
1200 if ( !$.support.offsetFractions ) {
1201 position.left = round( position.left );
1202 position.top = round( position.top );
1205 collisionPosition = {
1206 marginLeft: marginLeft,
1207 marginTop: marginTop
1210 $.each( [ "left", "top" ], function( i, dir ) {
1211 if ( $.ui.position[ collision[ i ] ] ) {
1212 $.ui.position[ collision[ i ] ][ dir ]( position, {
1213 targetWidth: targetWidth,
1214 targetHeight: targetHeight,
1215 elemWidth: elemWidth,
1216 elemHeight: elemHeight,
1217 collisionPosition: collisionPosition,
1218 collisionWidth: collisionWidth,
1219 collisionHeight: collisionHeight,
1220 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1229 if ( options.using ) {
1230 // adds feedback as second argument to using callback, if present
1231 using = function( props ) {
1232 var left = targetOffset.left - position.left,
1233 right = left + targetWidth - elemWidth,
1234 top = targetOffset.top - position.top,
1235 bottom = top + targetHeight - elemHeight,
1239 left: targetOffset.left,
1240 top: targetOffset.top,
1242 height: targetHeight
1246 left: position.left,
1251 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1252 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1254 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1255 feedback.horizontal = "center";
1257 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1258 feedback.vertical = "middle";
1260 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1261 feedback.important = "horizontal";
1263 feedback.important = "vertical";
1265 options.using.call( this, props, feedback );
1269 elem.offset( $.extend( position, { using: using } ) );
1275 left: function( position, data ) {
1276 var within = data.within,
1277 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1278 outerWidth = within.width,
1279 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1280 overLeft = withinOffset - collisionPosLeft,
1281 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1284 // element is wider than within
1285 if ( data.collisionWidth > outerWidth ) {
1286 // element is initially over the left side of within
1287 if ( overLeft > 0 && overRight <= 0 ) {
1288 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1289 position.left += overLeft - newOverRight;
1290 // element is initially over right side of within
1291 } else if ( overRight > 0 && overLeft <= 0 ) {
1292 position.left = withinOffset;
1293 // element is initially over both left and right sides of within
1295 if ( overLeft > overRight ) {
1296 position.left = withinOffset + outerWidth - data.collisionWidth;
1298 position.left = withinOffset;
1301 // too far left -> align with left edge
1302 } else if ( overLeft > 0 ) {
1303 position.left += overLeft;
1304 // too far right -> align with right edge
1305 } else if ( overRight > 0 ) {
1306 position.left -= overRight;
1307 // adjust based on position and margin
1309 position.left = max( position.left - collisionPosLeft, position.left );
1312 top: function( position, data ) {
1313 var within = data.within,
1314 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1315 outerHeight = data.within.height,
1316 collisionPosTop = position.top - data.collisionPosition.marginTop,
1317 overTop = withinOffset - collisionPosTop,
1318 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1321 // element is taller than within
1322 if ( data.collisionHeight > outerHeight ) {
1323 // element is initially over the top of within
1324 if ( overTop > 0 && overBottom <= 0 ) {
1325 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1326 position.top += overTop - newOverBottom;
1327 // element is initially over bottom of within
1328 } else if ( overBottom > 0 && overTop <= 0 ) {
1329 position.top = withinOffset;
1330 // element is initially over both top and bottom of within
1332 if ( overTop > overBottom ) {
1333 position.top = withinOffset + outerHeight - data.collisionHeight;
1335 position.top = withinOffset;
1338 // too far up -> align with top
1339 } else if ( overTop > 0 ) {
1340 position.top += overTop;
1341 // too far down -> align with bottom edge
1342 } else if ( overBottom > 0 ) {
1343 position.top -= overBottom;
1344 // adjust based on position and margin
1346 position.top = max( position.top - collisionPosTop, position.top );
1351 left: function( position, data ) {
1352 var within = data.within,
1353 withinOffset = within.offset.left + within.scrollLeft,
1354 outerWidth = within.width,
1355 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1356 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1357 overLeft = collisionPosLeft - offsetLeft,
1358 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1359 myOffset = data.my[ 0 ] === "left" ?
1361 data.my[ 0 ] === "right" ?
1364 atOffset = data.at[ 0 ] === "left" ?
1366 data.at[ 0 ] === "right" ?
1369 offset = -2 * data.offset[ 0 ],
1373 if ( overLeft < 0 ) {
1374 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1375 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1376 position.left += myOffset + atOffset + offset;
1379 else if ( overRight > 0 ) {
1380 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1381 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1382 position.left += myOffset + atOffset + offset;
1386 top: function( position, data ) {
1387 var within = data.within,
1388 withinOffset = within.offset.top + within.scrollTop,
1389 outerHeight = within.height,
1390 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1391 collisionPosTop = position.top - data.collisionPosition.marginTop,
1392 overTop = collisionPosTop - offsetTop,
1393 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1394 top = data.my[ 1 ] === "top",
1397 data.my[ 1 ] === "bottom" ?
1400 atOffset = data.at[ 1 ] === "top" ?
1402 data.at[ 1 ] === "bottom" ?
1403 -data.targetHeight :
1405 offset = -2 * data.offset[ 1 ],
1408 if ( overTop < 0 ) {
1409 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1410 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1411 position.top += myOffset + atOffset + offset;
1414 else if ( overBottom > 0 ) {
1415 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1416 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1417 position.top += myOffset + atOffset + offset;
1424 $.ui.position.flip.left.apply( this, arguments );
1425 $.ui.position.fit.left.apply( this, arguments );
1428 $.ui.position.flip.top.apply( this, arguments );
1429 $.ui.position.fit.top.apply( this, arguments );
1434 // fraction support test
1436 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1437 body = document.getElementsByTagName( "body" )[ 0 ],
1438 div = document.createElement( "div" );
1440 //Create a "fake body" for testing based on method used in jQuery.support
1441 testElement = document.createElement( body ? "div" : "body" );
1442 testElementStyle = {
1443 visibility: "hidden",
1451 $.extend( testElementStyle, {
1452 position: "absolute",
1457 for ( i in testElementStyle ) {
1458 testElement.style[ i ] = testElementStyle[ i ];
1460 testElement.appendChild( div );
1461 testElementParent = body || document.documentElement;
1462 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1464 div.style.cssText = "position: absolute; left: 10.7432222px;";
1466 offsetLeft = $( div ).offset().left;
1467 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1469 testElement.innerHTML = "";
1470 testElementParent.removeChild( testElement );
1474 (function( $, undefined ) {
1480 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
1481 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
1482 showProps.height = showProps.paddingTop = showProps.paddingBottom =
1483 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
1485 $.widget( "ui.accordion", {
1492 header: "> li > :first-child,> :not(li):even",
1493 heightStyle: "auto",
1495 activeHeader: "ui-icon-triangle-1-s",
1496 header: "ui-icon-triangle-1-e"
1501 beforeActivate: null
1504 _create: function() {
1505 var options = this.options;
1506 this.prevShow = this.prevHide = $();
1507 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
1509 .attr( "role", "tablist" );
1511 // don't allow collapsible: false and active: false / null
1512 if ( !options.collapsible && (options.active === false || options.active == null) ) {
1516 this._processPanels();
1517 // handle negative values
1518 if ( options.active < 0 ) {
1519 options.active += this.headers.length;
1524 _getCreateEventData: function() {
1526 header: this.active,
1527 panel: !this.active.length ? $() : this.active.next(),
1528 content: !this.active.length ? $() : this.active.next()
1532 _createIcons: function() {
1533 var icons = this.options.icons;
1536 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
1537 .prependTo( this.headers );
1538 this.active.children( ".ui-accordion-header-icon" )
1539 .removeClass( icons.header )
1540 .addClass( icons.activeHeader );
1541 this.headers.addClass( "ui-accordion-icons" );
1545 _destroyIcons: function() {
1547 .removeClass( "ui-accordion-icons" )
1548 .children( ".ui-accordion-header-icon" )
1552 _destroy: function() {
1555 // clean up main element
1557 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
1558 .removeAttr( "role" );
1562 .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" )
1563 .removeAttr( "role" )
1564 .removeAttr( "aria-expanded" )
1565 .removeAttr( "aria-selected" )
1566 .removeAttr( "aria-controls" )
1567 .removeAttr( "tabIndex" )
1569 if ( /^ui-accordion/.test( this.id ) ) {
1570 this.removeAttribute( "id" );
1573 this._destroyIcons();
1575 // clean up content panels
1576 contents = this.headers.next()
1577 .css( "display", "" )
1578 .removeAttr( "role" )
1579 .removeAttr( "aria-hidden" )
1580 .removeAttr( "aria-labelledby" )
1581 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
1583 if ( /^ui-accordion/.test( this.id ) ) {
1584 this.removeAttribute( "id" );
1587 if ( this.options.heightStyle !== "content" ) {
1588 contents.css( "height", "" );
1592 _setOption: function( key, value ) {
1593 if ( key === "active" ) {
1594 // _activate() will handle invalid values and update this.options
1595 this._activate( value );
1599 if ( key === "event" ) {
1600 if ( this.options.event ) {
1601 this._off( this.headers, this.options.event );
1603 this._setupEvents( value );
1606 this._super( key, value );
1608 // setting collapsible: false while collapsed; open first panel
1609 if ( key === "collapsible" && !value && this.options.active === false ) {
1610 this._activate( 0 );
1613 if ( key === "icons" ) {
1614 this._destroyIcons();
1616 this._createIcons();
1620 // #5332 - opacity doesn't cascade to positioned elements in IE
1621 // so we need to add the disabled class to the headers and panels
1622 if ( key === "disabled" ) {
1623 this.headers.add( this.headers.next() )
1624 .toggleClass( "ui-state-disabled", !!value );
1628 _keydown: function( event ) {
1629 if ( event.altKey || event.ctrlKey ) {
1633 var keyCode = $.ui.keyCode,
1634 length = this.headers.length,
1635 currentIndex = this.headers.index( event.target ),
1638 switch ( event.keyCode ) {
1641 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
1645 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
1649 this._eventHandler( event );
1652 toFocus = this.headers[ 0 ];
1655 toFocus = this.headers[ length - 1 ];
1660 $( event.target ).attr( "tabIndex", -1 );
1661 $( toFocus ).attr( "tabIndex", 0 );
1663 event.preventDefault();
1667 _panelKeyDown : function( event ) {
1668 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
1669 $( event.currentTarget ).prev().focus();
1673 refresh: function() {
1674 var options = this.options;
1675 this._processPanels();
1677 // was collapsed or no panel
1678 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
1679 options.active = false;
1681 // active false only when collapsible is true
1682 } else if ( options.active === false ) {
1683 this._activate( 0 );
1684 // was active, but active panel is gone
1685 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
1686 // all remaining panel are disabled
1687 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
1688 options.active = false;
1690 // activate previous panel
1692 this._activate( Math.max( 0, options.active - 1 ) );
1694 // was active, active panel still exists
1696 // make sure active index is correct
1697 options.active = this.headers.index( this.active );
1700 this._destroyIcons();
1705 _processPanels: function() {
1706 this.headers = this.element.find( this.options.header )
1707 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
1710 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
1711 .filter(":not(.ui-accordion-content-active)")
1715 _refresh: function() {
1717 options = this.options,
1718 heightStyle = options.heightStyle,
1719 parent = this.element.parent(),
1720 accordionId = this.accordionId = "ui-accordion-" +
1721 (this.element.attr( "id" ) || ++uid);
1723 this.active = this._findActive( options.active )
1724 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
1725 .removeClass( "ui-corner-all" );
1727 .addClass( "ui-accordion-content-active" )
1731 .attr( "role", "tab" )
1732 .each(function( i ) {
1733 var header = $( this ),
1734 headerId = header.attr( "id" ),
1735 panel = header.next(),
1736 panelId = panel.attr( "id" );
1738 headerId = accordionId + "-header-" + i;
1739 header.attr( "id", headerId );
1742 panelId = accordionId + "-panel-" + i;
1743 panel.attr( "id", panelId );
1745 header.attr( "aria-controls", panelId );
1746 panel.attr( "aria-labelledby", headerId );
1749 .attr( "role", "tabpanel" );
1754 "aria-selected": "false",
1755 "aria-expanded": "false",
1760 "aria-hidden": "true"
1764 // make sure at least one header is in the tab order
1765 if ( !this.active.length ) {
1766 this.headers.eq( 0 ).attr( "tabIndex", 0 );
1769 "aria-selected": "true",
1770 "aria-expanded": "true",
1775 "aria-hidden": "false"
1779 this._createIcons();
1781 this._setupEvents( options.event );
1783 if ( heightStyle === "fill" ) {
1784 maxHeight = parent.height();
1785 this.element.siblings( ":visible" ).each(function() {
1786 var elem = $( this ),
1787 position = elem.css( "position" );
1789 if ( position === "absolute" || position === "fixed" ) {
1792 maxHeight -= elem.outerHeight( true );
1795 this.headers.each(function() {
1796 maxHeight -= $( this ).outerHeight( true );
1801 $( this ).height( Math.max( 0, maxHeight -
1802 $( this ).innerHeight() + $( this ).height() ) );
1804 .css( "overflow", "auto" );
1805 } else if ( heightStyle === "auto" ) {
1809 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
1811 .height( maxHeight );
1815 _activate: function( index ) {
1816 var active = this._findActive( index )[ 0 ];
1818 // trying to activate the already active panel
1819 if ( active === this.active[ 0 ] ) {
1823 // trying to collapse, simulate a click on the currently active header
1824 active = active || this.active[ 0 ];
1826 this._eventHandler({
1828 currentTarget: active,
1829 preventDefault: $.noop
1833 _findActive: function( selector ) {
1834 return typeof selector === "number" ? this.headers.eq( selector ) : $();
1837 _setupEvents: function( event ) {
1842 $.each( event.split(" "), function( index, eventName ) {
1843 events[ eventName ] = "_eventHandler";
1847 this._off( this.headers.add( this.headers.next() ) );
1848 this._on( this.headers, events );
1849 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
1850 this._hoverable( this.headers );
1851 this._focusable( this.headers );
1854 _eventHandler: function( event ) {
1855 var options = this.options,
1856 active = this.active,
1857 clicked = $( event.currentTarget ),
1858 clickedIsActive = clicked[ 0 ] === active[ 0 ],
1859 collapsing = clickedIsActive && options.collapsible,
1860 toShow = collapsing ? $() : clicked.next(),
1861 toHide = active.next(),
1865 newHeader: collapsing ? $() : clicked,
1869 event.preventDefault();
1872 // click on active header, but not collapsible
1873 ( clickedIsActive && !options.collapsible ) ||
1874 // allow canceling activation
1875 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
1879 options.active = collapsing ? false : this.headers.index( clicked );
1881 // when the call to ._toggle() comes after the class changes
1882 // it causes a very odd bug in IE 8 (see #6720)
1883 this.active = clickedIsActive ? $() : clicked;
1884 this._toggle( eventData );
1887 // corner classes on the previously active header stay after the animation
1888 active.removeClass( "ui-accordion-header-active ui-state-active" );
1889 if ( options.icons ) {
1890 active.children( ".ui-accordion-header-icon" )
1891 .removeClass( options.icons.activeHeader )
1892 .addClass( options.icons.header );
1895 if ( !clickedIsActive ) {
1897 .removeClass( "ui-corner-all" )
1898 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
1899 if ( options.icons ) {
1900 clicked.children( ".ui-accordion-header-icon" )
1901 .removeClass( options.icons.header )
1902 .addClass( options.icons.activeHeader );
1907 .addClass( "ui-accordion-content-active" );
1911 _toggle: function( data ) {
1912 var toShow = data.newPanel,
1913 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
1915 // handle activating a panel during the animation for another activation
1916 this.prevShow.add( this.prevHide ).stop( true, true );
1917 this.prevShow = toShow;
1918 this.prevHide = toHide;
1920 if ( this.options.animate ) {
1921 this._animate( toShow, toHide, data );
1925 this._toggleComplete( data );
1929 "aria-hidden": "true"
1931 toHide.prev().attr( "aria-selected", "false" );
1932 // if we're switching panels, remove the old header from the tab order
1933 // if we're opening from collapsed state, remove the previous header from the tab order
1934 // if we're collapsing, then keep the collapsing header in the tab order
1935 if ( toShow.length && toHide.length ) {
1936 toHide.prev().attr({
1938 "aria-expanded": "false"
1940 } else if ( toShow.length ) {
1941 this.headers.filter(function() {
1942 return $( this ).attr( "tabIndex" ) === 0;
1944 .attr( "tabIndex", -1 );
1948 .attr( "aria-hidden", "false" )
1951 "aria-selected": "true",
1953 "aria-expanded": "true"
1957 _animate: function( toShow, toHide, data ) {
1958 var total, easing, duration,
1961 down = toShow.length &&
1962 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
1963 animate = this.options.animate || {},
1964 options = down && animate.down || animate,
1965 complete = function() {
1966 that._toggleComplete( data );
1969 if ( typeof options === "number" ) {
1972 if ( typeof options === "string" ) {
1975 // fall back from options to animation in case of partial down settings
1976 easing = easing || options.easing || animate.easing;
1977 duration = duration || options.duration || animate.duration;
1979 if ( !toHide.length ) {
1980 return toShow.animate( showProps, duration, easing, complete );
1982 if ( !toShow.length ) {
1983 return toHide.animate( hideProps, duration, easing, complete );
1986 total = toShow.show().outerHeight();
1987 toHide.animate( hideProps, {
1990 step: function( now, fx ) {
1991 fx.now = Math.round( now );
1996 .animate( showProps, {
2000 step: function( now, fx ) {
2001 fx.now = Math.round( now );
2002 if ( fx.prop !== "height" ) {
2004 } else if ( that.options.heightStyle !== "content" ) {
2005 fx.now = Math.round( total - toHide.outerHeight() - adjust );
2012 _toggleComplete: function( data ) {
2013 var toHide = data.oldPanel;
2016 .removeClass( "ui-accordion-content-active" )
2018 .removeClass( "ui-corner-top" )
2019 .addClass( "ui-corner-all" );
2021 // Work around for rendering bug in IE (#5421)
2022 if ( toHide.length ) {
2023 toHide.parent()[0].className = toHide.parent()[0].className;
2025 this._trigger( "activate", null, data );
2030 (function( $, undefined ) {
2032 $.widget( "ui.autocomplete", {
2034 defaultElement: "<input>",
2060 _create: function() {
2061 // Some browsers only repeat keydown events, not keypress events,
2062 // so we use the suppressKeyPress flag to determine if we've already
2063 // handled the keydown event. #7269
2064 // Unfortunately the code for & in keypress is the same as the up arrow,
2065 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
2066 // events when we know the keydown event was used to modify the
2067 // search term. #7799
2068 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
2069 nodeName = this.element[0].nodeName.toLowerCase(),
2070 isTextarea = nodeName === "textarea",
2071 isInput = nodeName === "input";
2074 // Textareas are always multi-line
2076 // Inputs are always single-line, even if inside a contentEditable element
2077 // IE also treats inputs as contentEditable
2079 // All other element types are determined by whether or not they're contentEditable
2080 this.element.prop( "isContentEditable" );
2082 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
2083 this.isNewMenu = true;
2086 .addClass( "ui-autocomplete-input" )
2087 .attr( "autocomplete", "off" );
2089 this._on( this.element, {
2090 keydown: function( event ) {
2091 if ( this.element.prop( "readOnly" ) ) {
2092 suppressKeyPress = true;
2093 suppressInput = true;
2094 suppressKeyPressRepeat = true;
2098 suppressKeyPress = false;
2099 suppressInput = false;
2100 suppressKeyPressRepeat = false;
2101 var keyCode = $.ui.keyCode;
2102 switch( event.keyCode ) {
2103 case keyCode.PAGE_UP:
2104 suppressKeyPress = true;
2105 this._move( "previousPage", event );
2107 case keyCode.PAGE_DOWN:
2108 suppressKeyPress = true;
2109 this._move( "nextPage", event );
2112 suppressKeyPress = true;
2113 this._keyEvent( "previous", event );
2116 suppressKeyPress = true;
2117 this._keyEvent( "next", event );
2120 case keyCode.NUMPAD_ENTER:
2121 // when menu is open and has focus
2122 if ( this.menu.active ) {
2123 // #6055 - Opera still allows the keypress to occur
2124 // which causes forms to submit
2125 suppressKeyPress = true;
2126 event.preventDefault();
2127 this.menu.select( event );
2131 if ( this.menu.active ) {
2132 this.menu.select( event );
2135 case keyCode.ESCAPE:
2136 if ( this.menu.element.is( ":visible" ) ) {
2137 this._value( this.term );
2138 this.close( event );
2139 // Different browsers have different default behavior for escape
2140 // Single press can mean undo or clear
2141 // Double press in IE means clear the whole form
2142 event.preventDefault();
2146 suppressKeyPressRepeat = true;
2147 // search timeout should be triggered before the input value is changed
2148 this._searchTimeout( event );
2152 keypress: function( event ) {
2153 if ( suppressKeyPress ) {
2154 suppressKeyPress = false;
2155 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2156 event.preventDefault();
2160 if ( suppressKeyPressRepeat ) {
2164 // replicate some key handlers to allow them to repeat in Firefox and Opera
2165 var keyCode = $.ui.keyCode;
2166 switch( event.keyCode ) {
2167 case keyCode.PAGE_UP:
2168 this._move( "previousPage", event );
2170 case keyCode.PAGE_DOWN:
2171 this._move( "nextPage", event );
2174 this._keyEvent( "previous", event );
2177 this._keyEvent( "next", event );
2181 input: function( event ) {
2182 if ( suppressInput ) {
2183 suppressInput = false;
2184 event.preventDefault();
2187 this._searchTimeout( event );
2190 this.selectedItem = null;
2191 this.previous = this._value();
2193 blur: function( event ) {
2194 if ( this.cancelBlur ) {
2195 delete this.cancelBlur;
2199 clearTimeout( this.searching );
2200 this.close( event );
2201 this._change( event );
2206 this.menu = $( "<ul>" )
2207 .addClass( "ui-autocomplete ui-front" )
2208 .appendTo( this._appendTo() )
2210 // disable ARIA support, the live region takes care of that
2216 this._on( this.menu.element, {
2217 mousedown: function( event ) {
2218 // prevent moving focus out of the text field
2219 event.preventDefault();
2221 // IE doesn't prevent moving focus even with event.preventDefault()
2222 // so we set a flag to know when we should ignore the blur event
2223 this.cancelBlur = true;
2224 this._delay(function() {
2225 delete this.cancelBlur;
2228 // clicking on the scrollbar causes focus to shift to the body
2229 // but we can't detect a mouseup or a click immediately afterward
2230 // so we have to track the next mousedown and close the menu if
2231 // the user clicks somewhere outside of the autocomplete
2232 var menuElement = this.menu.element[ 0 ];
2233 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
2234 this._delay(function() {
2236 this.document.one( "mousedown", function( event ) {
2237 if ( event.target !== that.element[ 0 ] &&
2238 event.target !== menuElement &&
2239 !$.contains( menuElement, event.target ) ) {
2246 menufocus: function( event, ui ) {
2248 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
2249 if ( this.isNewMenu ) {
2250 this.isNewMenu = false;
2251 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
2254 this.document.one( "mousemove", function() {
2255 $( event.target ).trigger( event.originalEvent );
2262 var item = ui.item.data( "ui-autocomplete-item" );
2263 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
2264 // use value to match what will end up in the input, if it was a key event
2265 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
2266 this._value( item.value );
2269 // Normally the input is populated with the item's value as the
2270 // menu is navigated, causing screen readers to notice a change and
2271 // announce the item. Since the focus event was canceled, this doesn't
2272 // happen, so we update the live region so that screen readers can
2273 // still notice the change and announce it.
2274 this.liveRegion.text( item.value );
2277 menuselect: function( event, ui ) {
2278 var item = ui.item.data( "ui-autocomplete-item" ),
2279 previous = this.previous;
2281 // only trigger when focus was lost (click on menu)
2282 if ( this.element[0] !== this.document[0].activeElement ) {
2283 this.element.focus();
2284 this.previous = previous;
2285 // #6109 - IE triggers two focus events and the second
2286 // is asynchronous, so we need to reset the previous
2287 // term synchronously and asynchronously :-(
2288 this._delay(function() {
2289 this.previous = previous;
2290 this.selectedItem = item;
2294 if ( false !== this._trigger( "select", event, { item: item } ) ) {
2295 this._value( item.value );
2297 // reset the term after the select event
2298 // this allows custom select handling to work properly
2299 this.term = this._value();
2301 this.close( event );
2302 this.selectedItem = item;
2306 this.liveRegion = $( "<span>", {
2308 "aria-live": "polite"
2310 .addClass( "ui-helper-hidden-accessible" )
2311 .insertBefore( this.element );
2313 // turning off autocomplete prevents the browser from remembering the
2314 // value when navigating through history, so we re-enable autocomplete
2315 // if the page is unloaded before the widget is destroyed. #7790
2316 this._on( this.window, {
2317 beforeunload: function() {
2318 this.element.removeAttr( "autocomplete" );
2323 _destroy: function() {
2324 clearTimeout( this.searching );
2326 .removeClass( "ui-autocomplete-input" )
2327 .removeAttr( "autocomplete" );
2328 this.menu.element.remove();
2329 this.liveRegion.remove();
2332 _setOption: function( key, value ) {
2333 this._super( key, value );
2334 if ( key === "source" ) {
2337 if ( key === "appendTo" ) {
2338 this.menu.element.appendTo( this._appendTo() );
2340 if ( key === "disabled" && value && this.xhr ) {
2345 _appendTo: function() {
2346 var element = this.options.appendTo;
2349 element = element.jquery || element.nodeType ?
2351 this.document.find( element ).eq( 0 );
2355 element = this.element.closest( ".ui-front" );
2358 if ( !element.length ) {
2359 element = this.document[0].body;
2365 _initSource: function() {
2368 if ( $.isArray(this.options.source) ) {
2369 array = this.options.source;
2370 this.source = function( request, response ) {
2371 response( $.ui.autocomplete.filter( array, request.term ) );
2373 } else if ( typeof this.options.source === "string" ) {
2374 url = this.options.source;
2375 this.source = function( request, response ) {
2383 success: function( data ) {
2392 this.source = this.options.source;
2396 _searchTimeout: function( event ) {
2397 clearTimeout( this.searching );
2398 this.searching = this._delay(function() {
2399 // only search if the value has changed
2400 if ( this.term !== this._value() ) {
2401 this.selectedItem = null;
2402 this.search( null, event );
2404 }, this.options.delay );
2407 search: function( value, event ) {
2408 value = value != null ? value : this._value();
2410 // always save the actual value, not the one passed as an argument
2411 this.term = this._value();
2413 if ( value.length < this.options.minLength ) {
2414 return this.close( event );
2417 if ( this._trigger( "search", event ) === false ) {
2421 return this._search( value );
2424 _search: function( value ) {
2426 this.element.addClass( "ui-autocomplete-loading" );
2427 this.cancelSearch = false;
2429 this.source( { term: value }, this._response() );
2432 _response: function() {
2433 var index = ++this.requestIndex;
2435 return $.proxy(function( content ) {
2436 if ( index === this.requestIndex ) {
2437 this.__response( content );
2441 if ( !this.pending ) {
2442 this.element.removeClass( "ui-autocomplete-loading" );
2447 __response: function( content ) {
2449 content = this._normalize( content );
2451 this._trigger( "response", null, { content: content } );
2452 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
2453 this._suggest( content );
2454 this._trigger( "open" );
2456 // use ._close() instead of .close() so we don't cancel future searches
2461 close: function( event ) {
2462 this.cancelSearch = true;
2463 this._close( event );
2466 _close: function( event ) {
2467 if ( this.menu.element.is( ":visible" ) ) {
2468 this.menu.element.hide();
2470 this.isNewMenu = true;
2471 this._trigger( "close", event );
2475 _change: function( event ) {
2476 if ( this.previous !== this._value() ) {
2477 this._trigger( "change", event, { item: this.selectedItem } );
2481 _normalize: function( items ) {
2482 // assume all items have the right format when the first item is complete
2483 if ( items.length && items[0].label && items[0].value ) {
2486 return $.map( items, function( item ) {
2487 if ( typeof item === "string" ) {
2494 label: item.label || item.value,
2495 value: item.value || item.label
2500 _suggest: function( items ) {
2501 var ul = this.menu.element.empty();
2502 this._renderMenu( ul, items );
2503 this.isNewMenu = true;
2504 this.menu.refresh();
2506 // size and position menu
2509 ul.position( $.extend({
2511 }, this.options.position ));
2513 if ( this.options.autoFocus ) {
2518 _resizeMenu: function() {
2519 var ul = this.menu.element;
2520 ul.outerWidth( Math.max(
2521 // Firefox wraps long text (possibly a rounding bug)
2522 // so we add 1px to avoid the wrapping (#7513)
2523 ul.width( "" ).outerWidth() + 1,
2524 this.element.outerWidth()
2528 _renderMenu: function( ul, items ) {
2530 $.each( items, function( index, item ) {
2531 that._renderItemData( ul, item );
2535 _renderItemData: function( ul, item ) {
2536 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
2539 _renderItem: function( ul, item ) {
2541 .append( $( "<a>" ).text( item.label ) )
2545 _move: function( direction, event ) {
2546 if ( !this.menu.element.is( ":visible" ) ) {
2547 this.search( null, event );
2550 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
2551 this.menu.isLastItem() && /^next/.test( direction ) ) {
2552 this._value( this.term );
2556 this.menu[ direction ]( event );
2559 widget: function() {
2560 return this.menu.element;
2563 _value: function() {
2564 return this.valueMethod.apply( this.element, arguments );
2567 _keyEvent: function( keyEvent, event ) {
2568 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2569 this._move( keyEvent, event );
2571 // prevents moving cursor to beginning/end of the text field in some browsers
2572 event.preventDefault();
2577 $.extend( $.ui.autocomplete, {
2578 escapeRegex: function( value ) {
2579 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
2581 filter: function(array, term) {
2582 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
2583 return $.grep( array, function(value) {
2584 return matcher.test( value.label || value.value || value );
2590 // live region extension, adding a `messages` option
2591 // NOTE: This is an experimental API. We are still investigating
2592 // a full solution for string manipulation and internationalization.
2593 $.widget( "ui.autocomplete", $.ui.autocomplete, {
2596 noResults: "No search results.",
2597 results: function( amount ) {
2598 return amount + ( amount > 1 ? " results are" : " result is" ) +
2599 " available, use up and down arrow keys to navigate.";
2604 __response: function( content ) {
2606 this._superApply( arguments );
2607 if ( this.options.disabled || this.cancelSearch ) {
2610 if ( content && content.length ) {
2611 message = this.options.messages.results( content.length );
2613 message = this.options.messages.noResults;
2615 this.liveRegion.text( message );
2620 (function( $, undefined ) {
2623 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
2624 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",
2625 formResetHandler = function() {
2626 var form = $( this );
2627 setTimeout(function() {
2628 form.find( ":ui-button" ).button( "refresh" );
2631 radioGroup = function( radio ) {
2632 var name = radio.name,
2636 name = name.replace( /'/g, "\\'" );
2638 radios = $( form ).find( "[name='" + name + "']" );
2640 radios = $( "[name='" + name + "']", radio.ownerDocument )
2641 .filter(function() {
2649 $.widget( "ui.button", {
2651 defaultElement: "<button>",
2661 _create: function() {
2662 this.element.closest( "form" )
2663 .unbind( "reset" + this.eventNamespace )
2664 .bind( "reset" + this.eventNamespace, formResetHandler );
2666 if ( typeof this.options.disabled !== "boolean" ) {
2667 this.options.disabled = !!this.element.prop( "disabled" );
2669 this.element.prop( "disabled", this.options.disabled );
2672 this._determineButtonType();
2673 this.hasTitle = !!this.buttonElement.attr( "title" );
2676 options = this.options,
2677 toggleButton = this.type === "checkbox" || this.type === "radio",
2678 activeClass = !toggleButton ? "ui-state-active" : "";
2680 if ( options.label === null ) {
2681 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
2684 this._hoverable( this.buttonElement );
2687 .addClass( baseClasses )
2688 .attr( "role", "button" )
2689 .bind( "mouseenter" + this.eventNamespace, function() {
2690 if ( options.disabled ) {
2693 if ( this === lastActive ) {
2694 $( this ).addClass( "ui-state-active" );
2697 .bind( "mouseleave" + this.eventNamespace, function() {
2698 if ( options.disabled ) {
2701 $( this ).removeClass( activeClass );
2703 .bind( "click" + this.eventNamespace, function( event ) {
2704 if ( options.disabled ) {
2705 event.preventDefault();
2706 event.stopImmediatePropagation();
2710 // Can't use _focusable() because the element that receives focus
2711 // and the element that gets the ui-state-focus class are different
2714 this.buttonElement.addClass( "ui-state-focus" );
2717 this.buttonElement.removeClass( "ui-state-focus" );
2721 if ( toggleButton ) {
2722 this.element.bind( "change" + this.eventNamespace, function() {
2727 if ( this.type === "checkbox" ) {
2728 this.buttonElement.bind( "click" + this.eventNamespace, function() {
2729 if ( options.disabled ) {
2733 } else if ( this.type === "radio" ) {
2734 this.buttonElement.bind( "click" + this.eventNamespace, function() {
2735 if ( options.disabled ) {
2738 $( this ).addClass( "ui-state-active" );
2739 that.buttonElement.attr( "aria-pressed", "true" );
2741 var radio = that.element[ 0 ];
2745 return $( this ).button( "widget" )[ 0 ];
2747 .removeClass( "ui-state-active" )
2748 .attr( "aria-pressed", "false" );
2752 .bind( "mousedown" + this.eventNamespace, function() {
2753 if ( options.disabled ) {
2756 $( this ).addClass( "ui-state-active" );
2758 that.document.one( "mouseup", function() {
2762 .bind( "mouseup" + this.eventNamespace, function() {
2763 if ( options.disabled ) {
2766 $( this ).removeClass( "ui-state-active" );
2768 .bind( "keydown" + this.eventNamespace, function(event) {
2769 if ( options.disabled ) {
2772 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
2773 $( this ).addClass( "ui-state-active" );
2776 // see #8559, we bind to blur here in case the button element loses
2777 // focus between keydown and keyup, it would be left in an "active" state
2778 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
2779 $( this ).removeClass( "ui-state-active" );
2782 if ( this.buttonElement.is("a") ) {
2783 this.buttonElement.keyup(function(event) {
2784 if ( event.keyCode === $.ui.keyCode.SPACE ) {
2785 // TODO pass through original event correctly (just as 2nd argument doesn't work)
2792 // TODO: pull out $.Widget's handling for the disabled option into
2793 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
2794 // be overridden by individual plugins
2795 this._setOption( "disabled", options.disabled );
2796 this._resetButton();
2799 _determineButtonType: function() {
2800 var ancestor, labelSelector, checked;
2802 if ( this.element.is("[type=checkbox]") ) {
2803 this.type = "checkbox";
2804 } else if ( this.element.is("[type=radio]") ) {
2805 this.type = "radio";
2806 } else if ( this.element.is("input") ) {
2807 this.type = "input";
2809 this.type = "button";
2812 if ( this.type === "checkbox" || this.type === "radio" ) {
2813 // we don't search against the document in case the element
2814 // is disconnected from the DOM
2815 ancestor = this.element.parents().last();
2816 labelSelector = "label[for='" + this.element.attr("id") + "']";
2817 this.buttonElement = ancestor.find( labelSelector );
2818 if ( !this.buttonElement.length ) {
2819 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
2820 this.buttonElement = ancestor.filter( labelSelector );
2821 if ( !this.buttonElement.length ) {
2822 this.buttonElement = ancestor.find( labelSelector );
2825 this.element.addClass( "ui-helper-hidden-accessible" );
2827 checked = this.element.is( ":checked" );
2829 this.buttonElement.addClass( "ui-state-active" );
2831 this.buttonElement.prop( "aria-pressed", checked );
2833 this.buttonElement = this.element;
2837 widget: function() {
2838 return this.buttonElement;
2841 _destroy: function() {
2843 .removeClass( "ui-helper-hidden-accessible" );
2845 .removeClass( baseClasses + " ui-state-active " + typeClasses )
2846 .removeAttr( "role" )
2847 .removeAttr( "aria-pressed" )
2848 .html( this.buttonElement.find(".ui-button-text").html() );
2850 if ( !this.hasTitle ) {
2851 this.buttonElement.removeAttr( "title" );
2855 _setOption: function( key, value ) {
2856 this._super( key, value );
2857 if ( key === "disabled" ) {
2858 this.element.prop( "disabled", !!value );
2860 this.buttonElement.removeClass( "ui-state-focus" );
2864 this._resetButton();
2867 refresh: function() {
2869 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
2871 if ( isDisabled !== this.options.disabled ) {
2872 this._setOption( "disabled", isDisabled );
2874 if ( this.type === "radio" ) {
2875 radioGroup( this.element[0] ).each(function() {
2876 if ( $( this ).is( ":checked" ) ) {
2877 $( this ).button( "widget" )
2878 .addClass( "ui-state-active" )
2879 .attr( "aria-pressed", "true" );
2881 $( this ).button( "widget" )
2882 .removeClass( "ui-state-active" )
2883 .attr( "aria-pressed", "false" );
2886 } else if ( this.type === "checkbox" ) {
2887 if ( this.element.is( ":checked" ) ) {
2889 .addClass( "ui-state-active" )
2890 .attr( "aria-pressed", "true" );
2893 .removeClass( "ui-state-active" )
2894 .attr( "aria-pressed", "false" );
2899 _resetButton: function() {
2900 if ( this.type === "input" ) {
2901 if ( this.options.label ) {
2902 this.element.val( this.options.label );
2906 var buttonElement = this.buttonElement.removeClass( typeClasses ),
2907 buttonText = $( "<span></span>", this.document[0] )
2908 .addClass( "ui-button-text" )
2909 .html( this.options.label )
2910 .appendTo( buttonElement.empty() )
2912 icons = this.options.icons,
2913 multipleIcons = icons.primary && icons.secondary,
2916 if ( icons.primary || icons.secondary ) {
2917 if ( this.options.text ) {
2918 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
2921 if ( icons.primary ) {
2922 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
2925 if ( icons.secondary ) {
2926 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
2929 if ( !this.options.text ) {
2930 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
2932 if ( !this.hasTitle ) {
2933 buttonElement.attr( "title", $.trim( buttonText ) );
2937 buttonClasses.push( "ui-button-text-only" );
2939 buttonElement.addClass( buttonClasses.join( " " ) );
2943 $.widget( "ui.buttonset", {
2946 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
2949 _create: function() {
2950 this.element.addClass( "ui-buttonset" );
2957 _setOption: function( key, value ) {
2958 if ( key === "disabled" ) {
2959 this.buttons.button( "option", key, value );
2962 this._super( key, value );
2965 refresh: function() {
2966 var rtl = this.element.css( "direction" ) === "rtl";
2968 this.buttons = this.element.find( this.options.items )
2969 .filter( ":ui-button" )
2970 .button( "refresh" )
2972 .not( ":ui-button" )
2976 return $( this ).button( "widget" )[ 0 ];
2978 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
2980 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
2983 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
2988 _destroy: function() {
2989 this.element.removeClass( "ui-buttonset" );
2992 return $( this ).button( "widget" )[ 0 ];
2994 .removeClass( "ui-corner-left ui-corner-right" )
2996 .button( "destroy" );
3001 (function( $, undefined ) {
3003 $.extend($.ui, { datepicker: { version: "1.10.4" } });
3005 var PROP_NAME = "datepicker",
3008 /* Date picker manager.
3009 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
3010 Settings for (groups of) date pickers are maintained in an instance object,
3011 allowing multiple different settings on the same page. */
3013 function Datepicker() {
3014 this._curInst = null; // The current instance in use
3015 this._keyEvent = false; // If the last event was a key event
3016 this._disabledInputs = []; // List of date picker inputs that have been disabled
3017 this._datepickerShowing = false; // True if the popup picker is showing , false if not
3018 this._inDialog = false; // True if showing within a "dialog", false if not
3019 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
3020 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
3021 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
3022 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
3023 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
3024 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
3025 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
3026 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
3027 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
3028 this.regional = []; // Available regional settings, indexed by language code
3029 this.regional[""] = { // Default regional settings
3030 closeText: "Done", // Display text for close link
3031 prevText: "Prev", // Display text for previous month link
3032 nextText: "Next", // Display text for next month link
3033 currentText: "Today", // Display text for current month link
3034 monthNames: ["January","February","March","April","May","June",
3035 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
3036 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
3037 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
3038 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
3039 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
3040 weekHeader: "Wk", // Column header for week of the year
3041 dateFormat: "mm/dd/yy", // See format options on parseDate
3042 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
3043 isRTL: false, // True if right-to-left language, false if left-to-right
3044 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
3045 yearSuffix: "" // Additional text to append to the year in the month headers
3047 this._defaults = { // Global defaults for all the date picker instances
3048 showOn: "focus", // "focus" for popup on focus,
3049 // "button" for trigger button, or "both" for either
3050 showAnim: "fadeIn", // Name of jQuery animation for popup
3051 showOptions: {}, // Options for enhanced animations
3052 defaultDate: null, // Used when field is blank: actual date,
3053 // +/-number for offset from today, null for today
3054 appendText: "", // Display text following the input box, e.g. showing the format
3055 buttonText: "...", // Text for trigger button
3056 buttonImage: "", // URL for trigger button image
3057 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
3058 hideIfNoPrevNext: false, // True to hide next/previous month links
3059 // if not applicable, false to just disable them
3060 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
3061 gotoCurrent: false, // True if today link goes back to current selection instead
3062 changeMonth: false, // True if month can be selected directly, false if only prev/next
3063 changeYear: false, // True if year can be selected directly, false if only prev/next
3064 yearRange: "c-10:c+10", // Range of years to display in drop-down,
3065 // either relative to today's year (-nn:+nn), relative to currently displayed year
3066 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
3067 showOtherMonths: false, // True to show dates in other months, false to leave blank
3068 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
3069 showWeek: false, // True to show week of the year, false to not show it
3070 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
3071 // takes a Date and returns the number of the week for it
3072 shortYearCutoff: "+10", // Short year values < this are in the current century,
3073 // > this are in the previous century,
3074 // string value starting with "+" for current year + value
3075 minDate: null, // The earliest selectable date, or null for no limit
3076 maxDate: null, // The latest selectable date, or null for no limit
3077 duration: "fast", // Duration of display/closure
3078 beforeShowDay: null, // Function that takes a date and returns an array with
3079 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
3080 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
3081 beforeShow: null, // Function that takes an input field and
3082 // returns a set of custom settings for the date picker
3083 onSelect: null, // Define a callback function when a date is selected
3084 onChangeMonthYear: null, // Define a callback function when the month or year is changed
3085 onClose: null, // Define a callback function when the datepicker is closed
3086 numberOfMonths: 1, // Number of months to show at a time
3087 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
3088 stepMonths: 1, // Number of months to step back/forward
3089 stepBigMonths: 12, // Number of months to step back/forward for the big links
3090 altField: "", // Selector for an alternate field to store selected dates into
3091 altFormat: "", // The date format to use for the alternate field
3092 constrainInput: true, // The input is constrained by the current date format
3093 showButtonPanel: false, // True to show button panel, false to not show it
3094 autoSize: false, // True to size the input for the date format, false to leave as is
3095 disabled: false // The initial disabled state
3097 $.extend(this._defaults, this.regional[""]);
3098 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
3101 $.extend(Datepicker.prototype, {
3102 /* Class name added to elements to indicate already configured with a date picker. */
3103 markerClassName: "hasDatepicker",
3105 //Keep track of the maximum number of rows displayed (see #7043)
3108 // TODO rename to "widget" when switching to widget factory
3109 _widgetDatepicker: function() {
3113 /* Override the default settings for all instances of the date picker.
3114 * @param settings object - the new settings to use as defaults (anonymous object)
3115 * @return the manager object
3117 setDefaults: function(settings) {
3118 extendRemove(this._defaults, settings || {});
3122 /* Attach the date picker to a jQuery selection.
3123 * @param target element - the target input field or division or span
3124 * @param settings object - the new settings to use for this date picker instance (anonymous)
3126 _attachDatepicker: function(target, settings) {
3127 var nodeName, inline, inst;
3128 nodeName = target.nodeName.toLowerCase();
3129 inline = (nodeName === "div" || nodeName === "span");
3132 target.id = "dp" + this.uuid;
3134 inst = this._newInst($(target), inline);
3135 inst.settings = $.extend({}, settings || {});
3136 if (nodeName === "input") {
3137 this._connectDatepicker(target, inst);
3138 } else if (inline) {
3139 this._inlineDatepicker(target, inst);
3143 /* Create a new instance object. */
3144 _newInst: function(target, inline) {
3145 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
3146 return {id: id, input: target, // associated target
3147 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
3148 drawMonth: 0, drawYear: 0, // month being drawn
3149 inline: inline, // is datepicker inline or not
3150 dpDiv: (!inline ? this.dpDiv : // presentation div
3151 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
3154 /* Attach the date picker to an input field. */
3155 _connectDatepicker: function(target, inst) {
3156 var input = $(target);
3157 inst.append = $([]);
3158 inst.trigger = $([]);
3159 if (input.hasClass(this.markerClassName)) {
3162 this._attachments(input, inst);
3163 input.addClass(this.markerClassName).keydown(this._doKeyDown).
3164 keypress(this._doKeyPress).keyup(this._doKeyUp);
3165 this._autoSize(inst);
3166 $.data(target, PROP_NAME, inst);
3167 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
3168 if( inst.settings.disabled ) {
3169 this._disableDatepicker( target );
3173 /* Make attachments based on settings. */
3174 _attachments: function(input, inst) {
3175 var showOn, buttonText, buttonImage,
3176 appendText = this._get(inst, "appendText"),
3177 isRTL = this._get(inst, "isRTL");
3180 inst.append.remove();
3183 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
3184 input[isRTL ? "before" : "after"](inst.append);
3187 input.unbind("focus", this._showDatepicker);
3190 inst.trigger.remove();
3193 showOn = this._get(inst, "showOn");
3194 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
3195 input.focus(this._showDatepicker);
3197 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
3198 buttonText = this._get(inst, "buttonText");
3199 buttonImage = this._get(inst, "buttonImage");
3200 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
3201 $("<img/>").addClass(this._triggerClass).
3202 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
3203 $("<button type='button'></button>").addClass(this._triggerClass).
3204 html(!buttonImage ? buttonText : $("<img/>").attr(
3205 { src:buttonImage, alt:buttonText, title:buttonText })));
3206 input[isRTL ? "before" : "after"](inst.trigger);
3207 inst.trigger.click(function() {
3208 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
3209 $.datepicker._hideDatepicker();
3210 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
3211 $.datepicker._hideDatepicker();
3212 $.datepicker._showDatepicker(input[0]);
3214 $.datepicker._showDatepicker(input[0]);
3221 /* Apply the maximum length for the date format. */
3222 _autoSize: function(inst) {
3223 if (this._get(inst, "autoSize") && !inst.inline) {
3224 var findMax, max, maxI, i,
3225 date = new Date(2009, 12 - 1, 20), // Ensure double digits
3226 dateFormat = this._get(inst, "dateFormat");
3228 if (dateFormat.match(/[DM]/)) {
3229 findMax = function(names) {
3232 for (i = 0; i < names.length; i++) {
3233 if (names[i].length > max) {
3234 max = names[i].length;
3240 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
3241 "monthNames" : "monthNamesShort"))));
3242 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
3243 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
3245 inst.input.attr("size", this._formatDate(inst, date).length);
3249 /* Attach an inline date picker to a div. */
3250 _inlineDatepicker: function(target, inst) {
3251 var divSpan = $(target);
3252 if (divSpan.hasClass(this.markerClassName)) {
3255 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
3256 $.data(target, PROP_NAME, inst);
3257 this._setDate(inst, this._getDefaultDate(inst), true);
3258 this._updateDatepicker(inst);
3259 this._updateAlternate(inst);
3260 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
3261 if( inst.settings.disabled ) {
3262 this._disableDatepicker( target );
3264 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
3265 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
3266 inst.dpDiv.css( "display", "block" );
3269 /* Pop-up the date picker in a "dialog" box.
3270 * @param input element - ignored
3271 * @param date string or Date - the initial date to display
3272 * @param onSelect function - the function to call when a date is selected
3273 * @param settings object - update the dialog date picker instance's settings (anonymous object)
3274 * @param pos int[2] - coordinates for the dialog's position within the screen or
3275 * event - with x/y coordinates or
3276 * leave empty for default (screen centre)
3277 * @return the manager object
3279 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
3280 var id, browserWidth, browserHeight, scrollX, scrollY,
3281 inst = this._dialogInst; // internal instance
3285 id = "dp" + this.uuid;
3286 this._dialogInput = $("<input type='text' id='" + id +
3287 "' style='position: absolute; top: -100px; width: 0px;'/>");
3288 this._dialogInput.keydown(this._doKeyDown);
3289 $("body").append(this._dialogInput);
3290 inst = this._dialogInst = this._newInst(this._dialogInput, false);
3292 $.data(this._dialogInput[0], PROP_NAME, inst);
3294 extendRemove(inst.settings, settings || {});
3295 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
3296 this._dialogInput.val(date);
3298 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
3300 browserWidth = document.documentElement.clientWidth;
3301 browserHeight = document.documentElement.clientHeight;
3302 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
3303 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
3304 this._pos = // should use actual width/height below
3305 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
3308 // move input on screen for focus, but hidden behind dialog
3309 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
3310 inst.settings.onSelect = onSelect;
3311 this._inDialog = true;
3312 this.dpDiv.addClass(this._dialogClass);
3313 this._showDatepicker(this._dialogInput[0]);
3315 $.blockUI(this.dpDiv);
3317 $.data(this._dialogInput[0], PROP_NAME, inst);
3321 /* Detach a datepicker from its control.
3322 * @param target element - the target input field or division or span
3324 _destroyDatepicker: function(target) {
3326 $target = $(target),
3327 inst = $.data(target, PROP_NAME);
3329 if (!$target.hasClass(this.markerClassName)) {
3333 nodeName = target.nodeName.toLowerCase();
3334 $.removeData(target, PROP_NAME);
3335 if (nodeName === "input") {
3336 inst.append.remove();
3337 inst.trigger.remove();
3338 $target.removeClass(this.markerClassName).
3339 unbind("focus", this._showDatepicker).
3340 unbind("keydown", this._doKeyDown).
3341 unbind("keypress", this._doKeyPress).
3342 unbind("keyup", this._doKeyUp);
3343 } else if (nodeName === "div" || nodeName === "span") {
3344 $target.removeClass(this.markerClassName).empty();
3348 /* Enable the date picker to a jQuery selection.
3349 * @param target element - the target input field or division or span
3351 _enableDatepicker: function(target) {
3352 var nodeName, inline,
3353 $target = $(target),
3354 inst = $.data(target, PROP_NAME);
3356 if (!$target.hasClass(this.markerClassName)) {
3360 nodeName = target.nodeName.toLowerCase();
3361 if (nodeName === "input") {
3362 target.disabled = false;
3363 inst.trigger.filter("button").
3364 each(function() { this.disabled = false; }).end().
3365 filter("img").css({opacity: "1.0", cursor: ""});
3366 } else if (nodeName === "div" || nodeName === "span") {
3367 inline = $target.children("." + this._inlineClass);
3368 inline.children().removeClass("ui-state-disabled");
3369 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
3370 prop("disabled", false);
3372 this._disabledInputs = $.map(this._disabledInputs,
3373 function(value) { return (value === target ? null : value); }); // delete entry
3376 /* Disable the date picker to a jQuery selection.
3377 * @param target element - the target input field or division or span
3379 _disableDatepicker: function(target) {
3380 var nodeName, inline,
3381 $target = $(target),
3382 inst = $.data(target, PROP_NAME);
3384 if (!$target.hasClass(this.markerClassName)) {
3388 nodeName = target.nodeName.toLowerCase();
3389 if (nodeName === "input") {
3390 target.disabled = true;
3391 inst.trigger.filter("button").
3392 each(function() { this.disabled = true; }).end().
3393 filter("img").css({opacity: "0.5", cursor: "default"});
3394 } else if (nodeName === "div" || nodeName === "span") {
3395 inline = $target.children("." + this._inlineClass);
3396 inline.children().addClass("ui-state-disabled");
3397 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
3398 prop("disabled", true);
3400 this._disabledInputs = $.map(this._disabledInputs,
3401 function(value) { return (value === target ? null : value); }); // delete entry
3402 this._disabledInputs[this._disabledInputs.length] = target;
3405 /* Is the first field in a jQuery collection disabled as a datepicker?
3406 * @param target element - the target input field or division or span
3407 * @return boolean - true if disabled, false if enabled
3409 _isDisabledDatepicker: function(target) {
3413 for (var i = 0; i < this._disabledInputs.length; i++) {
3414 if (this._disabledInputs[i] === target) {
3421 /* Retrieve the instance data for the target control.
3422 * @param target element - the target input field or division or span
3423 * @return object - the associated instance data
3424 * @throws error if a jQuery problem getting data
3426 _getInst: function(target) {
3428 return $.data(target, PROP_NAME);
3431 throw "Missing instance data for this datepicker";
3435 /* Update or retrieve the settings for a date picker attached to an input field or division.
3436 * @param target element - the target input field or division or span
3437 * @param name object - the new settings to update or
3438 * string - the name of the setting to change or retrieve,
3439 * when retrieving also "all" for all instance settings or
3440 * "defaults" for all global defaults
3441 * @param value any - the new value for the setting
3442 * (omit if above is an object or to retrieve a value)
3444 _optionDatepicker: function(target, name, value) {
3445 var settings, date, minDate, maxDate,
3446 inst = this._getInst(target);
3448 if (arguments.length === 2 && typeof name === "string") {
3449 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
3450 (inst ? (name === "all" ? $.extend({}, inst.settings) :
3451 this._get(inst, name)) : null));
3454 settings = name || {};
3455 if (typeof name === "string") {
3457 settings[name] = value;
3461 if (this._curInst === inst) {
3462 this._hideDatepicker();
3465 date = this._getDateDatepicker(target, true);
3466 minDate = this._getMinMaxDate(inst, "min");
3467 maxDate = this._getMinMaxDate(inst, "max");
3468 extendRemove(inst.settings, settings);
3469 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
3470 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
3471 inst.settings.minDate = this._formatDate(inst, minDate);
3473 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
3474 inst.settings.maxDate = this._formatDate(inst, maxDate);
3476 if ( "disabled" in settings ) {
3477 if ( settings.disabled ) {
3478 this._disableDatepicker(target);
3480 this._enableDatepicker(target);
3483 this._attachments($(target), inst);
3484 this._autoSize(inst);
3485 this._setDate(inst, date);
3486 this._updateAlternate(inst);
3487 this._updateDatepicker(inst);
3491 // change method deprecated
3492 _changeDatepicker: function(target, name, value) {
3493 this._optionDatepicker(target, name, value);
3496 /* Redraw the date picker attached to an input field or division.
3497 * @param target element - the target input field or division or span
3499 _refreshDatepicker: function(target) {
3500 var inst = this._getInst(target);
3502 this._updateDatepicker(inst);
3506 /* Set the dates for a jQuery selection.
3507 * @param target element - the target input field or division or span
3508 * @param date Date - the new date
3510 _setDateDatepicker: function(target, date) {
3511 var inst = this._getInst(target);
3513 this._setDate(inst, date);
3514 this._updateDatepicker(inst);
3515 this._updateAlternate(inst);
3519 /* Get the date(s) for the first entry in a jQuery selection.
3520 * @param target element - the target input field or division or span
3521 * @param noDefault boolean - true if no default date is to be used
3522 * @return Date - the current date
3524 _getDateDatepicker: function(target, noDefault) {
3525 var inst = this._getInst(target);
3526 if (inst && !inst.inline) {
3527 this._setDateFromField(inst, noDefault);
3529 return (inst ? this._getDate(inst) : null);
3532 /* Handle keystrokes. */
3533 _doKeyDown: function(event) {
3534 var onSelect, dateStr, sel,
3535 inst = $.datepicker._getInst(event.target),
3537 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
3539 inst._keyEvent = true;
3540 if ($.datepicker._datepickerShowing) {
3541 switch (event.keyCode) {
3542 case 9: $.datepicker._hideDatepicker();
3544 break; // hide on tab out
3545 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
3546 $.datepicker._currentClass + ")", inst.dpDiv);
3548 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
3551 onSelect = $.datepicker._get(inst, "onSelect");
3553 dateStr = $.datepicker._formatDate(inst);
3555 // trigger custom callback
3556 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
3558 $.datepicker._hideDatepicker();
3561 return false; // don't submit the form
3562 case 27: $.datepicker._hideDatepicker();
3563 break; // hide on escape
3564 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3565 -$.datepicker._get(inst, "stepBigMonths") :
3566 -$.datepicker._get(inst, "stepMonths")), "M");
3567 break; // previous month/year on page up/+ ctrl
3568 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3569 +$.datepicker._get(inst, "stepBigMonths") :
3570 +$.datepicker._get(inst, "stepMonths")), "M");
3571 break; // next month/year on page down/+ ctrl
3572 case 35: if (event.ctrlKey || event.metaKey) {
3573 $.datepicker._clearDate(event.target);
3575 handled = event.ctrlKey || event.metaKey;
3576 break; // clear on ctrl or command +end
3577 case 36: if (event.ctrlKey || event.metaKey) {
3578 $.datepicker._gotoToday(event.target);
3580 handled = event.ctrlKey || event.metaKey;
3581 break; // current on ctrl or command +home
3582 case 37: if (event.ctrlKey || event.metaKey) {
3583 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
3585 handled = event.ctrlKey || event.metaKey;
3586 // -1 day on ctrl or command +left
3587 if (event.originalEvent.altKey) {
3588 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3589 -$.datepicker._get(inst, "stepBigMonths") :
3590 -$.datepicker._get(inst, "stepMonths")), "M");
3592 // next month/year on alt +left on Mac
3594 case 38: if (event.ctrlKey || event.metaKey) {
3595 $.datepicker._adjustDate(event.target, -7, "D");
3597 handled = event.ctrlKey || event.metaKey;
3598 break; // -1 week on ctrl or command +up
3599 case 39: if (event.ctrlKey || event.metaKey) {
3600 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
3602 handled = event.ctrlKey || event.metaKey;
3603 // +1 day on ctrl or command +right
3604 if (event.originalEvent.altKey) {
3605 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3606 +$.datepicker._get(inst, "stepBigMonths") :
3607 +$.datepicker._get(inst, "stepMonths")), "M");
3609 // next month/year on alt +right
3611 case 40: if (event.ctrlKey || event.metaKey) {
3612 $.datepicker._adjustDate(event.target, +7, "D");
3614 handled = event.ctrlKey || event.metaKey;
3615 break; // +1 week on ctrl or command +down
3616 default: handled = false;
3618 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
3619 $.datepicker._showDatepicker(this);
3625 event.preventDefault();
3626 event.stopPropagation();
3630 /* Filter entered characters - based on date format. */
3631 _doKeyPress: function(event) {
3633 inst = $.datepicker._getInst(event.target);
3635 if ($.datepicker._get(inst, "constrainInput")) {
3636 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
3637 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
3638 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
3642 /* Synchronise manual entry and field/alternate field. */
3643 _doKeyUp: function(event) {
3645 inst = $.datepicker._getInst(event.target);
3647 if (inst.input.val() !== inst.lastVal) {
3649 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
3650 (inst.input ? inst.input.val() : null),
3651 $.datepicker._getFormatConfig(inst));
3653 if (date) { // only if valid
3654 $.datepicker._setDateFromField(inst);
3655 $.datepicker._updateAlternate(inst);
3656 $.datepicker._updateDatepicker(inst);
3665 /* Pop-up the date picker for a given input field.
3666 * If false returned from beforeShow event handler do not show.
3667 * @param input element - the input field attached to the date picker or
3668 * event - if triggered by focus
3670 _showDatepicker: function(input) {
3671 input = input.target || input;
3672 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
3673 input = $("input", input.parentNode)[0];
3676 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
3680 var inst, beforeShow, beforeShowSettings, isFixed,
3681 offset, showAnim, duration;
3683 inst = $.datepicker._getInst(input);
3684 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
3685 $.datepicker._curInst.dpDiv.stop(true, true);
3686 if ( inst && $.datepicker._datepickerShowing ) {
3687 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
3691 beforeShow = $.datepicker._get(inst, "beforeShow");
3692 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
3693 if(beforeShowSettings === false){
3696 extendRemove(inst.settings, beforeShowSettings);
3698 inst.lastVal = null;
3699 $.datepicker._lastInput = input;
3700 $.datepicker._setDateFromField(inst);
3702 if ($.datepicker._inDialog) { // hide cursor
3705 if (!$.datepicker._pos) { // position below input
3706 $.datepicker._pos = $.datepicker._findPos(input);
3707 $.datepicker._pos[1] += input.offsetHeight; // add the height
3711 $(input).parents().each(function() {
3712 isFixed |= $(this).css("position") === "fixed";
3716 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
3717 $.datepicker._pos = null;
3718 //to avoid flashes on Firefox
3720 // determine sizing offscreen
3721 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
3722 $.datepicker._updateDatepicker(inst);
3723 // fix width for dynamic number of date pickers
3724 // and adjust position before showing
3725 offset = $.datepicker._checkOffset(inst, offset, isFixed);
3726 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
3727 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
3728 left: offset.left + "px", top: offset.top + "px"});
3731 showAnim = $.datepicker._get(inst, "showAnim");
3732 duration = $.datepicker._get(inst, "duration");
3733 inst.dpDiv.zIndex($(input).zIndex()+1);
3734 $.datepicker._datepickerShowing = true;
3736 if ( $.effects && $.effects.effect[ showAnim ] ) {
3737 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
3739 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
3742 if ( $.datepicker._shouldFocusInput( inst ) ) {
3746 $.datepicker._curInst = inst;
3750 /* Generate the date picker content. */
3751 _updateDatepicker: function(inst) {
3752 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
3753 instActive = inst; // for delegate hover events
3754 inst.dpDiv.empty().append(this._generateHTML(inst));
3755 this._attachHandlers(inst);
3756 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
3759 numMonths = this._getNumberOfMonths(inst),
3760 cols = numMonths[1],
3763 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
3765 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
3767 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
3768 "Class"]("ui-datepicker-multi");
3769 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
3770 "Class"]("ui-datepicker-rtl");
3772 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
3776 // deffered render of the years select (to avoid flashes on Firefox)
3777 if( inst.yearshtml ){
3778 origyearshtml = inst.yearshtml;
3779 setTimeout(function(){
3780 //assure that inst.yearshtml didn't change.
3781 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
3782 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
3784 origyearshtml = inst.yearshtml = null;
3789 // #6694 - don't focus the input if it's already focused
3790 // this breaks the change event in IE
3791 // Support: IE and jQuery <1.9
3792 _shouldFocusInput: function( inst ) {
3793 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
3796 /* Check positioning to remain on screen. */
3797 _checkOffset: function(inst, offset, isFixed) {
3798 var dpWidth = inst.dpDiv.outerWidth(),
3799 dpHeight = inst.dpDiv.outerHeight(),
3800 inputWidth = inst.input ? inst.input.outerWidth() : 0,
3801 inputHeight = inst.input ? inst.input.outerHeight() : 0,
3802 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
3803 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
3805 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
3806 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
3807 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
3809 // now check if datepicker is showing outside window viewport - move to a better place if so.
3810 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
3811 Math.abs(offset.left + dpWidth - viewWidth) : 0);
3812 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
3813 Math.abs(dpHeight + inputHeight) : 0);
3818 /* Find an object's position on the screen. */
3819 _findPos: function(obj) {
3821 inst = this._getInst(obj),
3822 isRTL = this._get(inst, "isRTL");
3824 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
3825 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
3828 position = $(obj).offset();
3829 return [position.left, position.top];
3832 /* Hide the date picker from view.
3833 * @param input element - the input field attached to the date picker
3835 _hideDatepicker: function(input) {
3836 var showAnim, duration, postProcess, onClose,
3837 inst = this._curInst;
3839 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
3843 if (this._datepickerShowing) {
3844 showAnim = this._get(inst, "showAnim");
3845 duration = this._get(inst, "duration");
3846 postProcess = function() {
3847 $.datepicker._tidyDialog(inst);
3850 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
3851 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
3852 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
3854 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
3855 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
3861 this._datepickerShowing = false;
3863 onClose = this._get(inst, "onClose");
3865 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
3868 this._lastInput = null;
3869 if (this._inDialog) {
3870 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
3873 $("body").append(this.dpDiv);
3876 this._inDialog = false;
3880 /* Tidy up after a dialog display. */
3881 _tidyDialog: function(inst) {
3882 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
3885 /* Close date picker if clicked elsewhere. */
3886 _checkExternalClick: function(event) {
3887 if (!$.datepicker._curInst) {
3891 var $target = $(event.target),
3892 inst = $.datepicker._getInst($target[0]);
3894 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
3895 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
3896 !$target.hasClass($.datepicker.markerClassName) &&
3897 !$target.closest("." + $.datepicker._triggerClass).length &&
3898 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
3899 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
3900 $.datepicker._hideDatepicker();
3904 /* Adjust one of the date sub-fields. */
3905 _adjustDate: function(id, offset, period) {
3907 inst = this._getInst(target[0]);
3909 if (this._isDisabledDatepicker(target[0])) {
3912 this._adjustInstDate(inst, offset +
3913 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
3915 this._updateDatepicker(inst);
3918 /* Action for current link. */
3919 _gotoToday: function(id) {
3922 inst = this._getInst(target[0]);
3924 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
3925 inst.selectedDay = inst.currentDay;
3926 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
3927 inst.drawYear = inst.selectedYear = inst.currentYear;
3930 inst.selectedDay = date.getDate();
3931 inst.drawMonth = inst.selectedMonth = date.getMonth();
3932 inst.drawYear = inst.selectedYear = date.getFullYear();
3934 this._notifyChange(inst);
3935 this._adjustDate(target);
3938 /* Action for selecting a new month/year. */
3939 _selectMonthYear: function(id, select, period) {
3941 inst = this._getInst(target[0]);
3943 inst["selected" + (period === "M" ? "Month" : "Year")] =
3944 inst["draw" + (period === "M" ? "Month" : "Year")] =
3945 parseInt(select.options[select.selectedIndex].value,10);
3947 this._notifyChange(inst);
3948 this._adjustDate(target);
3951 /* Action for selecting a day. */
3952 _selectDay: function(id, month, year, td) {
3956 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
3960 inst = this._getInst(target[0]);
3961 inst.selectedDay = inst.currentDay = $("a", td).html();
3962 inst.selectedMonth = inst.currentMonth = month;
3963 inst.selectedYear = inst.currentYear = year;
3964 this._selectDate(id, this._formatDate(inst,
3965 inst.currentDay, inst.currentMonth, inst.currentYear));
3968 /* Erase the input field and hide the date picker. */
3969 _clearDate: function(id) {
3971 this._selectDate(target, "");
3974 /* Update the input field with the selected date. */
3975 _selectDate: function(id, dateStr) {
3978 inst = this._getInst(target[0]);
3980 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
3982 inst.input.val(dateStr);
3984 this._updateAlternate(inst);
3986 onSelect = this._get(inst, "onSelect");
3988 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
3989 } else if (inst.input) {
3990 inst.input.trigger("change"); // fire the change event
3994 this._updateDatepicker(inst);
3996 this._hideDatepicker();
3997 this._lastInput = inst.input[0];
3998 if (typeof(inst.input[0]) !== "object") {
3999 inst.input.focus(); // restore focus
4001 this._lastInput = null;
4005 /* Update any alternate field to synchronise with the main field. */
4006 _updateAlternate: function(inst) {
4007 var altFormat, date, dateStr,
4008 altField = this._get(inst, "altField");
4010 if (altField) { // update alternate field too
4011 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
4012 date = this._getDate(inst);
4013 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
4014 $(altField).each(function() { $(this).val(dateStr); });
4018 /* Set as beforeShowDay function to prevent selection of weekends.
4019 * @param date Date - the date to customise
4020 * @return [boolean, string] - is this date selectable?, what is its CSS class?
4022 noWeekends: function(date) {
4023 var day = date.getDay();
4024 return [(day > 0 && day < 6), ""];
4027 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
4028 * @param date Date - the date to get the week for
4029 * @return number - the number of the week within the year that contains this date
4031 iso8601Week: function(date) {
4033 checkDate = new Date(date.getTime());
4035 // Find Thursday of this week starting on Monday
4036 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
4038 time = checkDate.getTime();
4039 checkDate.setMonth(0); // Compare with Jan 1
4040 checkDate.setDate(1);
4041 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
4044 /* Parse a string value into a date object.
4045 * See formatDate below for the possible formats.
4047 * @param format string - the expected format of the date
4048 * @param value string - the date in the above format
4049 * @param settings Object - attributes include:
4050 * shortYearCutoff number - the cutoff year for determining the century (optional)
4051 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
4052 * dayNames string[7] - names of the days from Sunday (optional)
4053 * monthNamesShort string[12] - abbreviated names of the months (optional)
4054 * monthNames string[12] - names of the months (optional)
4055 * @return Date - the extracted date value or null if value is blank
4057 parseDate: function (format, value, settings) {
4058 if (format == null || value == null) {
4059 throw "Invalid arguments";
4062 value = (typeof value === "object" ? value.toString() : value + "");
4067 var iFormat, dim, extra,
4069 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
4070 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
4071 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
4072 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
4073 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
4074 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
4075 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
4082 // Check whether a format character is doubled
4083 lookAhead = function(match) {
4084 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
4090 // Extract a number from the string value
4091 getNumber = function(match) {
4092 var isDoubled = lookAhead(match),
4093 size = (match === "@" ? 14 : (match === "!" ? 20 :
4094 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
4095 digits = new RegExp("^\\d{1," + size + "}"),
4096 num = value.substring(iValue).match(digits);
4098 throw "Missing number at position " + iValue;
4100 iValue += num[0].length;
4101 return parseInt(num[0], 10);
4103 // Extract a name from the string value and convert to an index
4104 getName = function(match, shortNames, longNames) {
4106 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
4108 }).sort(function (a, b) {
4109 return -(a[1].length - b[1].length);
4112 $.each(names, function (i, pair) {
4114 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
4116 iValue += name.length;
4123 throw "Unknown name at position " + iValue;
4126 // Confirm that a literal character matches the string value
4127 checkLiteral = function() {
4128 if (value.charAt(iValue) !== format.charAt(iFormat)) {
4129 throw "Unexpected literal at position " + iValue;
4134 for (iFormat = 0; iFormat < format.length; iFormat++) {
4136 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
4142 switch (format.charAt(iFormat)) {
4144 day = getNumber("d");
4147 getName("D", dayNamesShort, dayNames);
4150 doy = getNumber("o");
4153 month = getNumber("m");
4156 month = getName("M", monthNamesShort, monthNames);
4159 year = getNumber("y");
4162 date = new Date(getNumber("@"));
4163 year = date.getFullYear();
4164 month = date.getMonth() + 1;
4165 day = date.getDate();
4168 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
4169 year = date.getFullYear();
4170 month = date.getMonth() + 1;
4171 day = date.getDate();
4174 if (lookAhead("'")){
4186 if (iValue < value.length){
4187 extra = value.substr(iValue);
4188 if (!/^\s+/.test(extra)) {
4189 throw "Extra/unparsed characters found in date: " + extra;
4194 year = new Date().getFullYear();
4195 } else if (year < 100) {
4196 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
4197 (year <= shortYearCutoff ? 0 : -100);
4204 dim = this._getDaysInMonth(year, month - 1);
4213 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
4214 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
4215 throw "Invalid date"; // E.g. 31/02/00
4220 /* Standard date formats. */
4221 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
4222 COOKIE: "D, dd M yy",
4223 ISO_8601: "yy-mm-dd",
4224 RFC_822: "D, d M y",
4225 RFC_850: "DD, dd-M-y",
4226 RFC_1036: "D, d M y",
4227 RFC_1123: "D, d M yy",
4228 RFC_2822: "D, d M yy",
4229 RSS: "D, d M y", // RFC 822
4232 W3C: "yy-mm-dd", // ISO 8601
4234 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
4235 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
4237 /* Format a date object into a string value.
4238 * The format can be combinations of the following:
4239 * d - day of month (no leading zero)
4240 * dd - day of month (two digit)
4241 * o - day of year (no leading zeros)
4242 * oo - day of year (three digit)
4243 * D - day name short
4244 * DD - day name long
4245 * m - month of year (no leading zero)
4246 * mm - month of year (two digit)
4247 * M - month name short
4248 * MM - month name long
4249 * y - year (two digit)
4250 * yy - year (four digit)
4251 * @ - Unix timestamp (ms since 01/01/1970)
4252 * ! - Windows ticks (100ns since 01/01/0001)
4253 * "..." - literal text
4256 * @param format string - the desired format of the date
4257 * @param date Date - the date value to format
4258 * @param settings Object - attributes include:
4259 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
4260 * dayNames string[7] - names of the days from Sunday (optional)
4261 * monthNamesShort string[12] - abbreviated names of the months (optional)
4262 * monthNames string[12] - names of the months (optional)
4263 * @return string - the date in the above format
4265 formatDate: function (format, date, settings) {
4271 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
4272 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
4273 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
4274 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
4275 // Check whether a format character is doubled
4276 lookAhead = function(match) {
4277 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
4283 // Format a number, with leading zero if necessary
4284 formatNumber = function(match, value, len) {
4285 var num = "" + value;
4286 if (lookAhead(match)) {
4287 while (num.length < len) {
4293 // Format a name, short or long as requested
4294 formatName = function(match, value, shortNames, longNames) {
4295 return (lookAhead(match) ? longNames[value] : shortNames[value]);
4301 for (iFormat = 0; iFormat < format.length; iFormat++) {
4303 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
4306 output += format.charAt(iFormat);
4309 switch (format.charAt(iFormat)) {
4311 output += formatNumber("d", date.getDate(), 2);
4314 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
4317 output += formatNumber("o",
4318 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
4321 output += formatNumber("m", date.getMonth() + 1, 2);
4324 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
4327 output += (lookAhead("y") ? date.getFullYear() :
4328 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
4331 output += date.getTime();
4334 output += date.getTime() * 10000 + this._ticksTo1970;
4337 if (lookAhead("'")) {
4344 output += format.charAt(iFormat);
4352 /* Extract all possible characters from the date format. */
4353 _possibleChars: function (format) {
4357 // Check whether a format character is doubled
4358 lookAhead = function(match) {
4359 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
4366 for (iFormat = 0; iFormat < format.length; iFormat++) {
4368 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
4371 chars += format.charAt(iFormat);
4374 switch (format.charAt(iFormat)) {
4375 case "d": case "m": case "y": case "@":
4376 chars += "0123456789";
4379 return null; // Accept anything
4381 if (lookAhead("'")) {
4388 chars += format.charAt(iFormat);
4395 /* Get a setting value, defaulting if necessary. */
4396 _get: function(inst, name) {
4397 return inst.settings[name] !== undefined ?
4398 inst.settings[name] : this._defaults[name];
4401 /* Parse existing date and initialise date picker. */
4402 _setDateFromField: function(inst, noDefault) {
4403 if (inst.input.val() === inst.lastVal) {
4407 var dateFormat = this._get(inst, "dateFormat"),
4408 dates = inst.lastVal = inst.input ? inst.input.val() : null,
4409 defaultDate = this._getDefaultDate(inst),
4411 settings = this._getFormatConfig(inst);
4414 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
4416 dates = (noDefault ? "" : dates);
4418 inst.selectedDay = date.getDate();
4419 inst.drawMonth = inst.selectedMonth = date.getMonth();
4420 inst.drawYear = inst.selectedYear = date.getFullYear();
4421 inst.currentDay = (dates ? date.getDate() : 0);
4422 inst.currentMonth = (dates ? date.getMonth() : 0);
4423 inst.currentYear = (dates ? date.getFullYear() : 0);
4424 this._adjustInstDate(inst);
4427 /* Retrieve the default date shown on opening. */
4428 _getDefaultDate: function(inst) {
4429 return this._restrictMinMax(inst,
4430 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
4433 /* A date may be specified as an exact value or a relative one. */
4434 _determineDate: function(inst, date, defaultDate) {
4435 var offsetNumeric = function(offset) {
4436 var date = new Date();
4437 date.setDate(date.getDate() + offset);
4440 offsetString = function(offset) {
4442 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
4443 offset, $.datepicker._getFormatConfig(inst));
4449 var date = (offset.toLowerCase().match(/^c/) ?
4450 $.datepicker._getDate(inst) : null) || new Date(),
4451 year = date.getFullYear(),
4452 month = date.getMonth(),
4453 day = date.getDate(),
4454 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
4455 matches = pattern.exec(offset);
4458 switch (matches[2] || "d") {
4459 case "d" : case "D" :
4460 day += parseInt(matches[1],10); break;
4461 case "w" : case "W" :
4462 day += parseInt(matches[1],10) * 7; break;
4463 case "m" : case "M" :
4464 month += parseInt(matches[1],10);
4465 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
4467 case "y": case "Y" :
4468 year += parseInt(matches[1],10);
4469 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
4472 matches = pattern.exec(offset);
4474 return new Date(year, month, day);
4476 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
4477 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
4479 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
4481 newDate.setHours(0);
4482 newDate.setMinutes(0);
4483 newDate.setSeconds(0);
4484 newDate.setMilliseconds(0);
4486 return this._daylightSavingAdjust(newDate);
4489 /* Handle switch to/from daylight saving.
4490 * Hours may be non-zero on daylight saving cut-over:
4491 * > 12 when midnight changeover, but then cannot generate
4492 * midnight datetime, so jump to 1AM, otherwise reset.
4493 * @param date (Date) the date to check
4494 * @return (Date) the corrected date
4496 _daylightSavingAdjust: function(date) {
4500 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
4504 /* Set the date(s) directly. */
4505 _setDate: function(inst, date, noChange) {
4507 origMonth = inst.selectedMonth,
4508 origYear = inst.selectedYear,
4509 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
4511 inst.selectedDay = inst.currentDay = newDate.getDate();
4512 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
4513 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
4514 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
4515 this._notifyChange(inst);
4517 this._adjustInstDate(inst);
4519 inst.input.val(clear ? "" : this._formatDate(inst));
4523 /* Retrieve the date(s) directly. */
4524 _getDate: function(inst) {
4525 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
4526 this._daylightSavingAdjust(new Date(
4527 inst.currentYear, inst.currentMonth, inst.currentDay)));
4531 /* Attach the onxxx handlers. These are declared statically so
4532 * they work with static code transformers like Caja.
4534 _attachHandlers: function(inst) {
4535 var stepMonths = this._get(inst, "stepMonths"),
4536 id = "#" + inst.id.replace( /\\\\/g, "\\" );
4537 inst.dpDiv.find("[data-handler]").map(function () {
4540 $.datepicker._adjustDate(id, -stepMonths, "M");
4543 $.datepicker._adjustDate(id, +stepMonths, "M");
4546 $.datepicker._hideDatepicker();
4548 today: function () {
4549 $.datepicker._gotoToday(id);
4551 selectDay: function () {
4552 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
4555 selectMonth: function () {
4556 $.datepicker._selectMonthYear(id, this, "M");
4559 selectYear: function () {
4560 $.datepicker._selectMonthYear(id, this, "Y");
4564 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
4568 /* Generate the HTML for the current state of the date picker. */
4569 _generateHTML: function(inst) {
4570 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
4571 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
4572 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
4573 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
4574 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
4575 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
4576 tempDate = new Date(),
4577 today = this._daylightSavingAdjust(
4578 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
4579 isRTL = this._get(inst, "isRTL"),
4580 showButtonPanel = this._get(inst, "showButtonPanel"),
4581 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
4582 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
4583 numMonths = this._getNumberOfMonths(inst),
4584 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
4585 stepMonths = this._get(inst, "stepMonths"),
4586 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
4587 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
4588 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
4589 minDate = this._getMinMaxDate(inst, "min"),
4590 maxDate = this._getMinMaxDate(inst, "max"),
4591 drawMonth = inst.drawMonth - showCurrentAtPos,
4592 drawYear = inst.drawYear;
4594 if (drawMonth < 0) {
4599 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
4600 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
4601 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
4602 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
4604 if (drawMonth < 0) {
4610 inst.drawMonth = drawMonth;
4611 inst.drawYear = drawYear;
4613 prevText = this._get(inst, "prevText");
4614 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
4615 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
4616 this._getFormatConfig(inst)));
4618 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
4619 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
4620 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
4621 (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>"));
4623 nextText = this._get(inst, "nextText");
4624 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
4625 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
4626 this._getFormatConfig(inst)));
4628 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
4629 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
4630 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
4631 (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>"));
4633 currentText = this._get(inst, "currentText");
4634 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
4635 currentText = (!navigationAsDateFormat ? currentText :
4636 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
4638 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'>" +
4639 this._get(inst, "closeText") + "</button>" : "");
4641 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
4642 (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'" +
4643 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
4645 firstDay = parseInt(this._get(inst, "firstDay"),10);
4646 firstDay = (isNaN(firstDay) ? 0 : firstDay);
4648 showWeek = this._get(inst, "showWeek");
4649 dayNames = this._get(inst, "dayNames");
4650 dayNamesMin = this._get(inst, "dayNamesMin");
4651 monthNames = this._get(inst, "monthNames");
4652 monthNamesShort = this._get(inst, "monthNamesShort");
4653 beforeShowDay = this._get(inst, "beforeShowDay");
4654 showOtherMonths = this._get(inst, "showOtherMonths");
4655 selectOtherMonths = this._get(inst, "selectOtherMonths");
4656 defaultDate = this._getDefaultDate(inst);
4659 for (row = 0; row < numMonths[0]; row++) {
4662 for (col = 0; col < numMonths[1]; col++) {
4663 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
4664 cornerClass = " ui-corner-all";
4667 calender += "<div class='ui-datepicker-group";
4668 if (numMonths[1] > 1) {
4670 case 0: calender += " ui-datepicker-group-first";
4671 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
4672 case numMonths[1]-1: calender += " ui-datepicker-group-last";
4673 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
4674 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
4679 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
4680 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
4681 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
4682 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
4683 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
4684 "</div><table class='ui-datepicker-calendar'><thead>" +
4686 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
4687 for (dow = 0; dow < 7; dow++) { // days of the week
4688 day = (dow + firstDay) % 7;
4689 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
4690 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
4692 calender += thead + "</tr></thead><tbody>";
4693 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
4694 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
4695 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
4697 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
4698 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
4699 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
4700 this.maxRows = numRows;
4701 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
4702 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
4704 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
4705 this._get(inst, "calculateWeek")(printDate) + "</td>");
4706 for (dow = 0; dow < 7; dow++) { // create date picker days
4707 daySettings = (beforeShowDay ?
4708 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
4709 otherMonth = (printDate.getMonth() !== drawMonth);
4710 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
4711 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
4712 tbody += "<td class='" +
4713 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
4714 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
4715 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
4716 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
4717 // or defaultDate is current printedDate and defaultDate is selectedDate
4718 " " + this._dayOverClass : "") + // highlight selected day
4719 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
4720 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
4721 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
4722 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
4723 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
4724 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
4725 (otherMonth && !showOtherMonths ? " " : // display for other months
4726 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
4727 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
4728 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
4729 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
4730 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
4731 printDate.setDate(printDate.getDate() + 1);
4732 printDate = this._daylightSavingAdjust(printDate);
4734 calender += tbody + "</tr>";
4737 if (drawMonth > 11) {
4741 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
4742 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
4747 html += buttonPanel;
4748 inst._keyEvent = false;
4752 /* Generate the month and year header. */
4753 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
4754 secondary, monthNames, monthNamesShort) {
4756 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
4757 changeMonth = this._get(inst, "changeMonth"),
4758 changeYear = this._get(inst, "changeYear"),
4759 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
4760 html = "<div class='ui-datepicker-title'>",
4764 if (secondary || !changeMonth) {
4765 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
4767 inMinYear = (minDate && minDate.getFullYear() === drawYear);
4768 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
4769 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
4770 for ( month = 0; month < 12; month++) {
4771 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
4772 monthHtml += "<option value='" + month + "'" +
4773 (month === drawMonth ? " selected='selected'" : "") +
4774 ">" + monthNamesShort[month] + "</option>";
4777 monthHtml += "</select>";
4780 if (!showMonthAfterYear) {
4781 html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
4785 if ( !inst.yearshtml ) {
4786 inst.yearshtml = "";
4787 if (secondary || !changeYear) {
4788 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
4790 // determine range of years to display
4791 years = this._get(inst, "yearRange").split(":");
4792 thisYear = new Date().getFullYear();
4793 determineYear = function(value) {
4794 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
4795 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
4796 parseInt(value, 10)));
4797 return (isNaN(year) ? thisYear : year);
4799 year = determineYear(years[0]);
4800 endYear = Math.max(year, determineYear(years[1] || ""));
4801 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
4802 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
4803 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
4804 for (; year <= endYear; year++) {
4805 inst.yearshtml += "<option value='" + year + "'" +
4806 (year === drawYear ? " selected='selected'" : "") +
4807 ">" + year + "</option>";
4809 inst.yearshtml += "</select>";
4811 html += inst.yearshtml;
4812 inst.yearshtml = null;
4816 html += this._get(inst, "yearSuffix");
4817 if (showMonthAfterYear) {
4818 html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
4820 html += "</div>"; // Close datepicker_header
4824 /* Adjust one of the date sub-fields. */
4825 _adjustInstDate: function(inst, offset, period) {
4826 var year = inst.drawYear + (period === "Y" ? offset : 0),
4827 month = inst.drawMonth + (period === "M" ? offset : 0),
4828 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
4829 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
4831 inst.selectedDay = date.getDate();
4832 inst.drawMonth = inst.selectedMonth = date.getMonth();
4833 inst.drawYear = inst.selectedYear = date.getFullYear();
4834 if (period === "M" || period === "Y") {
4835 this._notifyChange(inst);
4839 /* Ensure a date is within any min/max bounds. */
4840 _restrictMinMax: function(inst, date) {
4841 var minDate = this._getMinMaxDate(inst, "min"),
4842 maxDate = this._getMinMaxDate(inst, "max"),
4843 newDate = (minDate && date < minDate ? minDate : date);
4844 return (maxDate && newDate > maxDate ? maxDate : newDate);
4847 /* Notify change of month/year. */
4848 _notifyChange: function(inst) {
4849 var onChange = this._get(inst, "onChangeMonthYear");
4851 onChange.apply((inst.input ? inst.input[0] : null),
4852 [inst.selectedYear, inst.selectedMonth + 1, inst]);
4856 /* Determine the number of months to show. */
4857 _getNumberOfMonths: function(inst) {
4858 var numMonths = this._get(inst, "numberOfMonths");
4859 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
4862 /* Determine the current maximum date - ensure no time components are set. */
4863 _getMinMaxDate: function(inst, minMax) {
4864 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
4867 /* Find the number of days in a given month. */
4868 _getDaysInMonth: function(year, month) {
4869 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
4872 /* Find the day of the week of the first of a month. */
4873 _getFirstDayOfMonth: function(year, month) {
4874 return new Date(year, month, 1).getDay();
4877 /* Determines if we should allow a "next/prev" month display change. */
4878 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
4879 var numMonths = this._getNumberOfMonths(inst),
4880 date = this._daylightSavingAdjust(new Date(curYear,
4881 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
4884 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
4886 return this._isInRange(inst, date);
4889 /* Is the given date in the accepted range? */
4890 _isInRange: function(inst, date) {
4891 var yearSplit, currentYear,
4892 minDate = this._getMinMaxDate(inst, "min"),
4893 maxDate = this._getMinMaxDate(inst, "max"),
4896 years = this._get(inst, "yearRange");
4898 yearSplit = years.split(":");
4899 currentYear = new Date().getFullYear();
4900 minYear = parseInt(yearSplit[0], 10);
4901 maxYear = parseInt(yearSplit[1], 10);
4902 if ( yearSplit[0].match(/[+\-].*/) ) {
4903 minYear += currentYear;
4905 if ( yearSplit[1].match(/[+\-].*/) ) {
4906 maxYear += currentYear;
4910 return ((!minDate || date.getTime() >= minDate.getTime()) &&
4911 (!maxDate || date.getTime() <= maxDate.getTime()) &&
4912 (!minYear || date.getFullYear() >= minYear) &&
4913 (!maxYear || date.getFullYear() <= maxYear));
4916 /* Provide the configuration settings for formatting/parsing. */
4917 _getFormatConfig: function(inst) {
4918 var shortYearCutoff = this._get(inst, "shortYearCutoff");
4919 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
4920 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
4921 return {shortYearCutoff: shortYearCutoff,
4922 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
4923 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
4926 /* Format the given date for display. */
4927 _formatDate: function(inst, day, month, year) {
4929 inst.currentDay = inst.selectedDay;
4930 inst.currentMonth = inst.selectedMonth;
4931 inst.currentYear = inst.selectedYear;
4933 var date = (day ? (typeof day === "object" ? day :
4934 this._daylightSavingAdjust(new Date(year, month, day))) :
4935 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
4936 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
4941 * Bind hover events for datepicker elements.
4942 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
4943 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
4945 function bindHover(dpDiv) {
4946 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
4947 return dpDiv.delegate(selector, "mouseout", function() {
4948 $(this).removeClass("ui-state-hover");
4949 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
4950 $(this).removeClass("ui-datepicker-prev-hover");
4952 if (this.className.indexOf("ui-datepicker-next") !== -1) {
4953 $(this).removeClass("ui-datepicker-next-hover");
4956 .delegate(selector, "mouseover", function(){
4957 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
4958 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
4959 $(this).addClass("ui-state-hover");
4960 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
4961 $(this).addClass("ui-datepicker-prev-hover");
4963 if (this.className.indexOf("ui-datepicker-next") !== -1) {
4964 $(this).addClass("ui-datepicker-next-hover");
4970 /* jQuery extend now ignores nulls! */
4971 function extendRemove(target, props) {
4972 $.extend(target, props);
4973 for (var name in props) {
4974 if (props[name] == null) {
4975 target[name] = props[name];
4981 /* Invoke the datepicker functionality.
4982 @param options string - a command, optionally followed by additional parameters or
4983 Object - settings for attaching new datepicker functionality
4984 @return jQuery object */
4985 $.fn.datepicker = function(options){
4987 /* Verify an empty collection wasn't passed - Fixes #6976 */
4988 if ( !this.length ) {
4992 /* Initialise the date picker. */
4993 if (!$.datepicker.initialized) {
4994 $(document).mousedown($.datepicker._checkExternalClick);
4995 $.datepicker.initialized = true;
4998 /* Append datepicker main container to body if not exist. */
4999 if ($("#"+$.datepicker._mainDivId).length === 0) {
5000 $("body").append($.datepicker.dpDiv);
5003 var otherArgs = Array.prototype.slice.call(arguments, 1);
5004 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
5005 return $.datepicker["_" + options + "Datepicker"].
5006 apply($.datepicker, [this[0]].concat(otherArgs));
5008 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
5009 return $.datepicker["_" + options + "Datepicker"].
5010 apply($.datepicker, [this[0]].concat(otherArgs));
5012 return this.each(function() {
5013 typeof options === "string" ?
5014 $.datepicker["_" + options + "Datepicker"].
5015 apply($.datepicker, [this].concat(otherArgs)) :
5016 $.datepicker._attachDatepicker(this, options);
5020 $.datepicker = new Datepicker(); // singleton instance
5021 $.datepicker.initialized = false;
5022 $.datepicker.uuid = new Date().getTime();
5023 $.datepicker.version = "1.10.4";
5026 (function( $, undefined ) {
5028 var sizeRelatedOptions = {
5037 resizableRelatedOptions = {
5044 $.widget( "ui.dialog", {
5050 closeOnEscape: true,
5066 // Ensure the titlebar is always visible
5067 using: function( pos ) {
5068 var topOffset = $( this ).css( pos ).offset().top;
5069 if ( topOffset < 0 ) {
5070 $( this ).css( "top", pos.top - topOffset );
5092 _create: function() {
5093 this.originalCss = {
5094 display: this.element[0].style.display,
5095 width: this.element[0].style.width,
5096 minHeight: this.element[0].style.minHeight,
5097 maxHeight: this.element[0].style.maxHeight,
5098 height: this.element[0].style.height
5100 this.originalPosition = {
5101 parent: this.element.parent(),
5102 index: this.element.parent().children().index( this.element )
5104 this.originalTitle = this.element.attr("title");
5105 this.options.title = this.options.title || this.originalTitle;
5107 this._createWrapper();
5111 .removeAttr("title")
5112 .addClass("ui-dialog-content ui-widget-content")
5113 .appendTo( this.uiDialog );
5115 this._createTitlebar();
5116 this._createButtonPane();
5118 if ( this.options.draggable && $.fn.draggable ) {
5119 this._makeDraggable();
5121 if ( this.options.resizable && $.fn.resizable ) {
5122 this._makeResizable();
5125 this._isOpen = false;
5129 if ( this.options.autoOpen ) {
5134 _appendTo: function() {
5135 var element = this.options.appendTo;
5136 if ( element && (element.jquery || element.nodeType) ) {
5137 return $( element );
5139 return this.document.find( element || "body" ).eq( 0 );
5142 _destroy: function() {
5144 originalPosition = this.originalPosition;
5146 this._destroyOverlay();
5150 .removeClass("ui-dialog-content ui-widget-content")
5151 .css( this.originalCss )
5152 // Without detaching first, the following becomes really slow
5155 this.uiDialog.stop( true, true ).remove();
5157 if ( this.originalTitle ) {
5158 this.element.attr( "title", this.originalTitle );
5161 next = originalPosition.parent.children().eq( originalPosition.index );
5162 // Don't try to place the dialog next to itself (#8613)
5163 if ( next.length && next[0] !== this.element[0] ) {
5164 next.before( this.element );
5166 originalPosition.parent.append( this.element );
5170 widget: function() {
5171 return this.uiDialog;
5177 close: function( event ) {
5181 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
5185 this._isOpen = false;
5186 this._destroyOverlay();
5188 if ( !this.opener.filter(":focusable").focus().length ) {
5191 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
5193 activeElement = this.document[ 0 ].activeElement;
5195 // Support: IE9, IE10
5196 // If the <body> is blurred, IE will switch windows, see #4520
5197 if ( activeElement && activeElement.nodeName.toLowerCase() !== "body" ) {
5199 // Hiding a focused element doesn't trigger blur in WebKit
5200 // so in case we have nothing to focus on, explicitly blur the active element
5201 // https://bugs.webkit.org/show_bug.cgi?id=47182
5202 $( activeElement ).blur();
5204 } catch ( error ) {}
5207 this._hide( this.uiDialog, this.options.hide, function() {
5208 that._trigger( "close", event );
5212 isOpen: function() {
5213 return this._isOpen;
5216 moveToTop: function() {
5220 _moveToTop: function( event, silent ) {
5221 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
5222 if ( moved && !silent ) {
5223 this._trigger( "focus", event );
5230 if ( this._isOpen ) {
5231 if ( this._moveToTop() ) {
5232 this._focusTabbable();
5237 this._isOpen = true;
5238 this.opener = $( this.document[0].activeElement );
5242 this._createOverlay();
5243 this._moveToTop( null, true );
5244 this._show( this.uiDialog, this.options.show, function() {
5245 that._focusTabbable();
5246 that._trigger("focus");
5249 this._trigger("open");
5252 _focusTabbable: function() {
5253 // Set focus to the first match:
5254 // 1. First element inside the dialog matching [autofocus]
5255 // 2. Tabbable element inside the content element
5256 // 3. Tabbable element inside the buttonpane
5257 // 4. The close button
5258 // 5. The dialog itself
5259 var hasFocus = this.element.find("[autofocus]");
5260 if ( !hasFocus.length ) {
5261 hasFocus = this.element.find(":tabbable");
5263 if ( !hasFocus.length ) {
5264 hasFocus = this.uiDialogButtonPane.find(":tabbable");
5266 if ( !hasFocus.length ) {
5267 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
5269 if ( !hasFocus.length ) {
5270 hasFocus = this.uiDialog;
5272 hasFocus.eq( 0 ).focus();
5275 _keepFocus: function( event ) {
5276 function checkFocus() {
5277 var activeElement = this.document[0].activeElement,
5278 isActive = this.uiDialog[0] === activeElement ||
5279 $.contains( this.uiDialog[0], activeElement );
5281 this._focusTabbable();
5284 event.preventDefault();
5285 checkFocus.call( this );
5287 // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
5288 // so we check again later
5289 this._delay( checkFocus );
5292 _createWrapper: function() {
5293 this.uiDialog = $("<div>")
5294 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
5295 this.options.dialogClass )
5298 // Setting tabIndex makes the div focusable
5302 .appendTo( this._appendTo() );
5304 this._on( this.uiDialog, {
5305 keydown: function( event ) {
5306 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
5307 event.keyCode === $.ui.keyCode.ESCAPE ) {
5308 event.preventDefault();
5309 this.close( event );
5313 // prevent tabbing out of dialogs
5314 if ( event.keyCode !== $.ui.keyCode.TAB ) {
5317 var tabbables = this.uiDialog.find(":tabbable"),
5318 first = tabbables.filter(":first"),
5319 last = tabbables.filter(":last");
5321 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
5323 event.preventDefault();
5324 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
5326 event.preventDefault();
5329 mousedown: function( event ) {
5330 if ( this._moveToTop( event ) ) {
5331 this._focusTabbable();
5336 // We assume that any existing aria-describedby attribute means
5337 // that the dialog content is marked up properly
5338 // otherwise we brute force the content as the description
5339 if ( !this.element.find("[aria-describedby]").length ) {
5340 this.uiDialog.attr({
5341 "aria-describedby": this.element.uniqueId().attr("id")
5346 _createTitlebar: function() {
5349 this.uiDialogTitlebar = $("<div>")
5350 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
5351 .prependTo( this.uiDialog );
5352 this._on( this.uiDialogTitlebar, {
5353 mousedown: function( event ) {
5354 // Don't prevent click on close button (#8838)
5355 // Focusing a dialog that is partially scrolled out of view
5356 // causes the browser to scroll it into view, preventing the click event
5357 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
5358 // Dialog isn't getting focus when dragging (#8063)
5359 this.uiDialog.focus();
5365 // Use type="button" to prevent enter keypresses in textboxes from closing the
5366 // dialog in IE (#9312)
5367 this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
5369 label: this.options.closeText,
5371 primary: "ui-icon-closethick"
5375 .addClass("ui-dialog-titlebar-close")
5376 .appendTo( this.uiDialogTitlebar );
5377 this._on( this.uiDialogTitlebarClose, {
5378 click: function( event ) {
5379 event.preventDefault();
5380 this.close( event );
5384 uiDialogTitle = $("<span>")
5386 .addClass("ui-dialog-title")
5387 .prependTo( this.uiDialogTitlebar );
5388 this._title( uiDialogTitle );
5390 this.uiDialog.attr({
5391 "aria-labelledby": uiDialogTitle.attr("id")
5395 _title: function( title ) {
5396 if ( !this.options.title ) {
5397 title.html(" ");
5399 title.text( this.options.title );
5402 _createButtonPane: function() {
5403 this.uiDialogButtonPane = $("<div>")
5404 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
5406 this.uiButtonSet = $("<div>")
5407 .addClass("ui-dialog-buttonset")
5408 .appendTo( this.uiDialogButtonPane );
5410 this._createButtons();
5413 _createButtons: function() {
5415 buttons = this.options.buttons;
5417 // if we already have a button pane, remove it
5418 this.uiDialogButtonPane.remove();
5419 this.uiButtonSet.empty();
5421 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
5422 this.uiDialog.removeClass("ui-dialog-buttons");
5426 $.each( buttons, function( name, props ) {
5427 var click, buttonOptions;
5428 props = $.isFunction( props ) ?
5429 { click: props, text: name } :
5431 // Default to a non-submitting button
5432 props = $.extend( { type: "button" }, props );
5433 // Change the context for the click callback to be the main element
5434 click = props.click;
5435 props.click = function() {
5436 click.apply( that.element[0], arguments );
5440 text: props.showText
5443 delete props.showText;
5444 $( "<button></button>", props )
5445 .button( buttonOptions )
5446 .appendTo( that.uiButtonSet );
5448 this.uiDialog.addClass("ui-dialog-buttons");
5449 this.uiDialogButtonPane.appendTo( this.uiDialog );
5452 _makeDraggable: function() {
5454 options = this.options;
5456 function filteredUi( ui ) {
5458 position: ui.position,
5463 this.uiDialog.draggable({
5464 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
5465 handle: ".ui-dialog-titlebar",
5466 containment: "document",
5467 start: function( event, ui ) {
5468 $( this ).addClass("ui-dialog-dragging");
5469 that._blockFrames();
5470 that._trigger( "dragStart", event, filteredUi( ui ) );
5472 drag: function( event, ui ) {
5473 that._trigger( "drag", event, filteredUi( ui ) );
5475 stop: function( event, ui ) {
5476 options.position = [
5477 ui.position.left - that.document.scrollLeft(),
5478 ui.position.top - that.document.scrollTop()
5480 $( this ).removeClass("ui-dialog-dragging");
5481 that._unblockFrames();
5482 that._trigger( "dragStop", event, filteredUi( ui ) );
5487 _makeResizable: function() {
5489 options = this.options,
5490 handles = options.resizable,
5491 // .ui-resizable has position: relative defined in the stylesheet
5492 // but dialogs have to use absolute or fixed positioning
5493 position = this.uiDialog.css("position"),
5494 resizeHandles = typeof handles === "string" ?
5496 "n,e,s,w,se,sw,ne,nw";
5498 function filteredUi( ui ) {
5500 originalPosition: ui.originalPosition,
5501 originalSize: ui.originalSize,
5502 position: ui.position,
5507 this.uiDialog.resizable({
5508 cancel: ".ui-dialog-content",
5509 containment: "document",
5510 alsoResize: this.element,
5511 maxWidth: options.maxWidth,
5512 maxHeight: options.maxHeight,
5513 minWidth: options.minWidth,
5514 minHeight: this._minHeight(),
5515 handles: resizeHandles,
5516 start: function( event, ui ) {
5517 $( this ).addClass("ui-dialog-resizing");
5518 that._blockFrames();
5519 that._trigger( "resizeStart", event, filteredUi( ui ) );
5521 resize: function( event, ui ) {
5522 that._trigger( "resize", event, filteredUi( ui ) );
5524 stop: function( event, ui ) {
5525 options.height = $( this ).height();
5526 options.width = $( this ).width();
5527 $( this ).removeClass("ui-dialog-resizing");
5528 that._unblockFrames();
5529 that._trigger( "resizeStop", event, filteredUi( ui ) );
5532 .css( "position", position );
5535 _minHeight: function() {
5536 var options = this.options;
5538 return options.height === "auto" ?
5540 Math.min( options.minHeight, options.height );
5543 _position: function() {
5544 // Need to show the dialog to get the actual offset in the position plugin
5545 var isVisible = this.uiDialog.is(":visible");
5547 this.uiDialog.show();
5549 this.uiDialog.position( this.options.position );
5551 this.uiDialog.hide();
5555 _setOptions: function( options ) {
5558 resizableOptions = {};
5560 $.each( options, function( key, value ) {
5561 that._setOption( key, value );
5563 if ( key in sizeRelatedOptions ) {
5566 if ( key in resizableRelatedOptions ) {
5567 resizableOptions[ key ] = value;
5575 if ( this.uiDialog.is(":data(ui-resizable)") ) {
5576 this.uiDialog.resizable( "option", resizableOptions );
5580 _setOption: function( key, value ) {
5581 var isDraggable, isResizable,
5582 uiDialog = this.uiDialog;
5584 if ( key === "dialogClass" ) {
5586 .removeClass( this.options.dialogClass )
5590 if ( key === "disabled" ) {
5594 this._super( key, value );
5596 if ( key === "appendTo" ) {
5597 this.uiDialog.appendTo( this._appendTo() );
5600 if ( key === "buttons" ) {
5601 this._createButtons();
5604 if ( key === "closeText" ) {
5605 this.uiDialogTitlebarClose.button({
5606 // Ensure that we always pass a string
5611 if ( key === "draggable" ) {
5612 isDraggable = uiDialog.is(":data(ui-draggable)");
5613 if ( isDraggable && !value ) {
5614 uiDialog.draggable("destroy");
5617 if ( !isDraggable && value ) {
5618 this._makeDraggable();
5622 if ( key === "position" ) {
5626 if ( key === "resizable" ) {
5627 // currently resizable, becoming non-resizable
5628 isResizable = uiDialog.is(":data(ui-resizable)");
5629 if ( isResizable && !value ) {
5630 uiDialog.resizable("destroy");
5633 // currently resizable, changing handles
5634 if ( isResizable && typeof value === "string" ) {
5635 uiDialog.resizable( "option", "handles", value );
5638 // currently non-resizable, becoming resizable
5639 if ( !isResizable && value !== false ) {
5640 this._makeResizable();
5644 if ( key === "title" ) {
5645 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
5650 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
5651 // divs will both have width and height set, so we need to reset them
5652 var nonContentHeight, minContentHeight, maxContentHeight,
5653 options = this.options;
5655 // Reset content sizing
5656 this.element.show().css({
5663 if ( options.minWidth > options.width ) {
5664 options.width = options.minWidth;
5667 // reset wrapper sizing
5668 // determine the height of all the non-content elements
5669 nonContentHeight = this.uiDialog.css({
5671 width: options.width
5674 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
5675 maxContentHeight = typeof options.maxHeight === "number" ?
5676 Math.max( 0, options.maxHeight - nonContentHeight ) :
5679 if ( options.height === "auto" ) {
5681 minHeight: minContentHeight,
5682 maxHeight: maxContentHeight,
5686 this.element.height( Math.max( 0, options.height - nonContentHeight ) );
5689 if (this.uiDialog.is(":data(ui-resizable)") ) {
5690 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
5694 _blockFrames: function() {
5695 this.iframeBlocks = this.document.find( "iframe" ).map(function() {
5696 var iframe = $( this );
5700 position: "absolute",
5701 width: iframe.outerWidth(),
5702 height: iframe.outerHeight()
5704 .appendTo( iframe.parent() )
5705 .offset( iframe.offset() )[0];
5709 _unblockFrames: function() {
5710 if ( this.iframeBlocks ) {
5711 this.iframeBlocks.remove();
5712 delete this.iframeBlocks;
5716 _allowInteraction: function( event ) {
5717 if ( $( event.target ).closest(".ui-dialog").length ) {
5721 // TODO: Remove hack when datepicker implements
5722 // the .ui-front logic (#8989)
5723 return !!$( event.target ).closest(".ui-datepicker").length;
5726 _createOverlay: function() {
5727 if ( !this.options.modal ) {
5732 widgetFullName = this.widgetFullName;
5733 if ( !$.ui.dialog.overlayInstances ) {
5734 // Prevent use of anchors and inputs.
5735 // We use a delay in case the overlay is created from an
5736 // event that we're going to be cancelling. (#2804)
5737 this._delay(function() {
5738 // Handle .dialog().dialog("close") (#4065)
5739 if ( $.ui.dialog.overlayInstances ) {
5740 this.document.bind( "focusin.dialog", function( event ) {
5741 if ( !that._allowInteraction( event ) ) {
5742 event.preventDefault();
5743 $(".ui-dialog:visible:last .ui-dialog-content")
5744 .data( widgetFullName )._focusTabbable();
5751 this.overlay = $("<div>")
5752 .addClass("ui-widget-overlay ui-front")
5753 .appendTo( this._appendTo() );
5754 this._on( this.overlay, {
5755 mousedown: "_keepFocus"
5757 $.ui.dialog.overlayInstances++;
5760 _destroyOverlay: function() {
5761 if ( !this.options.modal ) {
5765 if ( this.overlay ) {
5766 $.ui.dialog.overlayInstances--;
5768 if ( !$.ui.dialog.overlayInstances ) {
5769 this.document.unbind( "focusin.dialog" );
5771 this.overlay.remove();
5772 this.overlay = null;
5777 $.ui.dialog.overlayInstances = 0;
5780 if ( $.uiBackCompat !== false ) {
5781 // position option with array notation
5782 // just override with old implementation
5783 $.widget( "ui.dialog", $.ui.dialog, {
5784 _position: function() {
5785 var position = this.options.position,
5791 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
5792 myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
5793 if ( myAt.length === 1 ) {
5797 $.each( [ "left", "top" ], function( i, offsetPosition ) {
5798 if ( +myAt[ i ] === myAt[ i ] ) {
5799 offset[ i ] = myAt[ i ];
5800 myAt[ i ] = offsetPosition;
5805 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
5806 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
5811 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
5813 position = $.ui.dialog.prototype.options.position;
5816 // need to show the dialog to get the actual offset in the position plugin
5817 isVisible = this.uiDialog.is(":visible");
5819 this.uiDialog.show();
5821 this.uiDialog.position( position );
5823 this.uiDialog.hide();
5830 (function( $, undefined ) {
5832 $.widget("ui.draggable", $.ui.mouse, {
5834 widgetEventPrefix: "drag",
5839 connectToSortable: false,
5848 refreshPositions: false,
5850 revertDuration: 500,
5853 scrollSensitivity: 20,
5866 _create: function() {
5868 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
5869 this.element[0].style.position = "relative";
5871 if (this.options.addClasses){
5872 this.element.addClass("ui-draggable");
5874 if (this.options.disabled){
5875 this.element.addClass("ui-draggable-disabled");
5882 _destroy: function() {
5883 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
5884 this._mouseDestroy();
5887 _mouseCapture: function(event) {
5889 var o = this.options;
5891 // among others, prevent a drag on a resizable-handle
5892 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
5896 //Quit if we're not on a valid handle
5897 this.handle = this._getHandle(event);
5902 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
5903 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
5905 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
5906 position: "absolute", opacity: "0.001", zIndex: 1000
5908 .css($(this).offset())
5916 _mouseStart: function(event) {
5918 var o = this.options;
5920 //Create and append the visible helper
5921 this.helper = this._createHelper(event);
5923 this.helper.addClass("ui-draggable-dragging");
5925 //Cache the helper size
5926 this._cacheHelperProportions();
5928 //If ddmanager is used for droppables, set the global draggable
5929 if($.ui.ddmanager) {
5930 $.ui.ddmanager.current = this;
5934 * - Position generation -
5935 * This block generates everything position related - it's the core of draggables.
5938 //Cache the margins of the original element
5939 this._cacheMargins();
5941 //Store the helper's css position
5942 this.cssPosition = this.helper.css( "position" );
5943 this.scrollParent = this.helper.scrollParent();
5944 this.offsetParent = this.helper.offsetParent();
5945 this.offsetParentCssPosition = this.offsetParent.css( "position" );
5947 //The element's absolute position on the page minus margins
5948 this.offset = this.positionAbs = this.element.offset();
5950 top: this.offset.top - this.margins.top,
5951 left: this.offset.left - this.margins.left
5954 //Reset scroll cache
5955 this.offset.scroll = false;
5957 $.extend(this.offset, {
5958 click: { //Where the click happened, relative to the element
5959 left: event.pageX - this.offset.left,
5960 top: event.pageY - this.offset.top
5962 parent: this._getParentOffset(),
5963 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
5966 //Generate the original position
5967 this.originalPosition = this.position = this._generatePosition(event);
5968 this.originalPageX = event.pageX;
5969 this.originalPageY = event.pageY;
5971 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
5972 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
5974 //Set a containment if given in the options
5975 this._setContainment();
5977 //Trigger event + callbacks
5978 if(this._trigger("start", event) === false) {
5983 //Recache the helper size
5984 this._cacheHelperProportions();
5986 //Prepare the droppable offsets
5987 if ($.ui.ddmanager && !o.dropBehaviour) {
5988 $.ui.ddmanager.prepareOffsets(this, event);
5992 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
5994 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
5995 if ( $.ui.ddmanager ) {
5996 $.ui.ddmanager.dragStart(this, event);
6002 _mouseDrag: function(event, noPropagation) {
6003 // reset any necessary cached properties (see #5009)
6004 if ( this.offsetParentCssPosition === "fixed" ) {
6005 this.offset.parent = this._getParentOffset();
6008 //Compute the helpers position
6009 this.position = this._generatePosition(event);
6010 this.positionAbs = this._convertPositionTo("absolute");
6012 //Call plugins and callbacks and use the resulting position if something is returned
6013 if (!noPropagation) {
6014 var ui = this._uiHash();
6015 if(this._trigger("drag", event, ui) === false) {
6019 this.position = ui.position;
6022 if(!this.options.axis || this.options.axis !== "y") {
6023 this.helper[0].style.left = this.position.left+"px";
6025 if(!this.options.axis || this.options.axis !== "x") {
6026 this.helper[0].style.top = this.position.top+"px";
6028 if($.ui.ddmanager) {
6029 $.ui.ddmanager.drag(this, event);
6035 _mouseStop: function(event) {
6037 //If we are using droppables, inform the manager about the drop
6040 if ($.ui.ddmanager && !this.options.dropBehaviour) {
6041 dropped = $.ui.ddmanager.drop(this, event);
6044 //if a drop comes from outside (a sortable)
6046 dropped = this.dropped;
6047 this.dropped = false;
6050 //if the original element is no longer in the DOM don't bother to continue (see #8269)
6051 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
6055 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))) {
6056 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
6057 if(that._trigger("stop", event) !== false) {
6062 if(this._trigger("stop", event) !== false) {
6070 _mouseUp: function(event) {
6071 //Remove frame helpers
6072 $("div.ui-draggable-iframeFix").each(function() {
6073 this.parentNode.removeChild(this);
6076 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
6077 if( $.ui.ddmanager ) {
6078 $.ui.ddmanager.dragStop(this, event);
6081 return $.ui.mouse.prototype._mouseUp.call(this, event);
6084 cancel: function() {
6086 if(this.helper.is(".ui-draggable-dragging")) {
6096 _getHandle: function(event) {
6097 return this.options.handle ?
6098 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
6102 _createHelper: function(event) {
6104 var o = this.options,
6105 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
6107 if(!helper.parents("body").length) {
6108 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
6111 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
6112 helper.css("position", "absolute");
6119 _adjustOffsetFromHelper: function(obj) {
6120 if (typeof obj === "string") {
6121 obj = obj.split(" ");
6123 if ($.isArray(obj)) {
6124 obj = {left: +obj[0], top: +obj[1] || 0};
6126 if ("left" in obj) {
6127 this.offset.click.left = obj.left + this.margins.left;
6129 if ("right" in obj) {
6130 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
6133 this.offset.click.top = obj.top + this.margins.top;
6135 if ("bottom" in obj) {
6136 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
6140 _getParentOffset: function() {
6142 //Get the offsetParent and cache its position
6143 var po = this.offsetParent.offset();
6145 // This is a special case where we need to modify a offset calculated on start, since the following happened:
6146 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
6147 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
6148 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
6149 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
6150 po.left += this.scrollParent.scrollLeft();
6151 po.top += this.scrollParent.scrollTop();
6154 //This needs to be actually done for all browsers, since pageX/pageY includes this information
6156 if((this.offsetParent[0] === document.body) ||
6157 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
6158 po = { top: 0, left: 0 };
6162 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
6163 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
6168 _getRelativeOffset: function() {
6170 if(this.cssPosition === "relative") {
6171 var p = this.element.position();
6173 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
6174 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
6177 return { top: 0, left: 0 };
6182 _cacheMargins: function() {
6184 left: (parseInt(this.element.css("marginLeft"),10) || 0),
6185 top: (parseInt(this.element.css("marginTop"),10) || 0),
6186 right: (parseInt(this.element.css("marginRight"),10) || 0),
6187 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
6191 _cacheHelperProportions: function() {
6192 this.helperProportions = {
6193 width: this.helper.outerWidth(),
6194 height: this.helper.outerHeight()
6198 _setContainment: function() {
6203 if ( !o.containment ) {
6204 this.containment = null;
6208 if ( o.containment === "window" ) {
6209 this.containment = [
6210 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
6211 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
6212 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
6213 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
6218 if ( o.containment === "document") {
6219 this.containment = [
6222 $( document ).width() - this.helperProportions.width - this.margins.left,
6223 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
6228 if ( o.containment.constructor === Array ) {
6229 this.containment = o.containment;
6233 if ( o.containment === "parent" ) {
6234 o.containment = this.helper[ 0 ].parentNode;
6237 c = $( o.containment );
6244 over = c.css( "overflow" ) !== "hidden";
6246 this.containment = [
6247 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
6248 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
6249 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
6250 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
6252 this.relative_container = c;
6255 _convertPositionTo: function(d, pos) {
6258 pos = this.position;
6261 var mod = d === "absolute" ? 1 : -1,
6262 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
6265 if (!this.offset.scroll) {
6266 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
6271 pos.top + // The absolute mouse position
6272 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
6273 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
6274 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
6277 pos.left + // The absolute mouse position
6278 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
6279 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
6280 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
6286 _generatePosition: function(event) {
6288 var containment, co, top, left,
6290 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
6291 pageX = event.pageX,
6292 pageY = event.pageY;
6295 if (!this.offset.scroll) {
6296 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
6300 * - Position constraining -
6301 * Constrain the position to a mix of grid, containment.
6304 // If we are not dragging yet, we won't check for options
6305 if ( this.originalPosition ) {
6306 if ( this.containment ) {
6307 if ( this.relative_container ){
6308 co = this.relative_container.offset();
6310 this.containment[ 0 ] + co.left,
6311 this.containment[ 1 ] + co.top,
6312 this.containment[ 2 ] + co.left,
6313 this.containment[ 3 ] + co.top
6317 containment = this.containment;
6320 if(event.pageX - this.offset.click.left < containment[0]) {
6321 pageX = containment[0] + this.offset.click.left;
6323 if(event.pageY - this.offset.click.top < containment[1]) {
6324 pageY = containment[1] + this.offset.click.top;
6326 if(event.pageX - this.offset.click.left > containment[2]) {
6327 pageX = containment[2] + this.offset.click.left;
6329 if(event.pageY - this.offset.click.top > containment[3]) {
6330 pageY = containment[3] + this.offset.click.top;
6335 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
6336 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
6337 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;
6339 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
6340 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;
6347 pageY - // The absolute mouse position
6348 this.offset.click.top - // Click offset (relative to the element)
6349 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
6350 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
6351 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
6354 pageX - // The absolute mouse position
6355 this.offset.click.left - // Click offset (relative to the element)
6356 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
6357 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
6358 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
6364 _clear: function() {
6365 this.helper.removeClass("ui-draggable-dragging");
6366 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
6367 this.helper.remove();
6370 this.cancelHelperRemoval = false;
6373 // From now on bulk stuff - mainly helpers
6375 _trigger: function(type, event, ui) {
6376 ui = ui || this._uiHash();
6377 $.ui.plugin.call(this, type, [event, ui]);
6378 //The absolute position has to be recalculated after plugins
6379 if(type === "drag") {
6380 this.positionAbs = this._convertPositionTo("absolute");
6382 return $.Widget.prototype._trigger.call(this, type, event, ui);
6387 _uiHash: function() {
6389 helper: this.helper,
6390 position: this.position,
6391 originalPosition: this.originalPosition,
6392 offset: this.positionAbs
6398 $.ui.plugin.add("draggable", "connectToSortable", {
6399 start: function(event, ui) {
6401 var inst = $(this).data("ui-draggable"), o = inst.options,
6402 uiSortable = $.extend({}, ui, { item: inst.element });
6403 inst.sortables = [];
6404 $(o.connectToSortable).each(function() {
6405 var sortable = $.data(this, "ui-sortable");
6406 if (sortable && !sortable.options.disabled) {
6407 inst.sortables.push({
6409 shouldRevert: sortable.options.revert
6411 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).
6412 sortable._trigger("activate", event, uiSortable);
6417 stop: function(event, ui) {
6419 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
6420 var inst = $(this).data("ui-draggable"),
6421 uiSortable = $.extend({}, ui, { item: inst.element });
6423 $.each(inst.sortables, function() {
6424 if(this.instance.isOver) {
6426 this.instance.isOver = 0;
6428 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
6429 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
6431 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
6432 if(this.shouldRevert) {
6433 this.instance.options.revert = this.shouldRevert;
6436 //Trigger the stop of the sortable
6437 this.instance._mouseStop(event);
6439 this.instance.options.helper = this.instance.options._helper;
6441 //If the helper has been the original item, restore properties in the sortable
6442 if(inst.options.helper === "original") {
6443 this.instance.currentItem.css({ top: "auto", left: "auto" });
6447 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
6448 this.instance._trigger("deactivate", event, uiSortable);
6454 drag: function(event, ui) {
6456 var inst = $(this).data("ui-draggable"), that = this;
6458 $.each(inst.sortables, function() {
6460 var innermostIntersecting = false,
6461 thisSortable = this;
6463 //Copy over some variables to allow calling the sortable's native _intersectsWith
6464 this.instance.positionAbs = inst.positionAbs;
6465 this.instance.helperProportions = inst.helperProportions;
6466 this.instance.offset.click = inst.offset.click;
6468 if(this.instance._intersectsWith(this.instance.containerCache)) {
6469 innermostIntersecting = true;
6470 $.each(inst.sortables, function () {
6471 this.instance.positionAbs = inst.positionAbs;
6472 this.instance.helperProportions = inst.helperProportions;
6473 this.instance.offset.click = inst.offset.click;
6474 if (this !== thisSortable &&
6475 this.instance._intersectsWith(this.instance.containerCache) &&
6476 $.contains(thisSortable.instance.element[0], this.instance.element[0])
6478 innermostIntersecting = false;
6480 return innermostIntersecting;
6485 if(innermostIntersecting) {
6486 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
6487 if(!this.instance.isOver) {
6489 this.instance.isOver = 1;
6490 //Now we fake the start of dragging for the sortable instance,
6491 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
6492 //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)
6493 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
6494 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
6495 this.instance.options.helper = function() { return ui.helper[0]; };
6497 event.target = this.instance.currentItem[0];
6498 this.instance._mouseCapture(event, true);
6499 this.instance._mouseStart(event, true, true);
6501 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
6502 this.instance.offset.click.top = inst.offset.click.top;
6503 this.instance.offset.click.left = inst.offset.click.left;
6504 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
6505 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
6507 inst._trigger("toSortable", event);
6508 inst.dropped = this.instance.element; //draggable revert needs that
6509 //hack so receive/update callbacks work (mostly)
6510 inst.currentItem = inst.element;
6511 this.instance.fromOutside = inst;
6515 //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
6516 if(this.instance.currentItem) {
6517 this.instance._mouseDrag(event);
6522 //If it doesn't intersect with the sortable, and it intersected before,
6523 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
6524 if(this.instance.isOver) {
6526 this.instance.isOver = 0;
6527 this.instance.cancelHelperRemoval = true;
6529 //Prevent reverting on this forced stop
6530 this.instance.options.revert = false;
6532 // The out event needs to be triggered independently
6533 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
6535 this.instance._mouseStop(event, true);
6536 this.instance.options.helper = this.instance.options._helper;
6538 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
6539 this.instance.currentItem.remove();
6540 if(this.instance.placeholder) {
6541 this.instance.placeholder.remove();
6544 inst._trigger("fromSortable", event);
6545 inst.dropped = false; //draggable revert needs that
6555 $.ui.plugin.add("draggable", "cursor", {
6557 var t = $("body"), o = $(this).data("ui-draggable").options;
6558 if (t.css("cursor")) {
6559 o._cursor = t.css("cursor");
6561 t.css("cursor", o.cursor);
6564 var o = $(this).data("ui-draggable").options;
6566 $("body").css("cursor", o._cursor);
6571 $.ui.plugin.add("draggable", "opacity", {
6572 start: function(event, ui) {
6573 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
6574 if(t.css("opacity")) {
6575 o._opacity = t.css("opacity");
6577 t.css("opacity", o.opacity);
6579 stop: function(event, ui) {
6580 var o = $(this).data("ui-draggable").options;
6582 $(ui.helper).css("opacity", o._opacity);
6587 $.ui.plugin.add("draggable", "scroll", {
6589 var i = $(this).data("ui-draggable");
6590 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
6591 i.overflowOffset = i.scrollParent.offset();
6594 drag: function( event ) {
6596 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
6598 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
6600 if(!o.axis || o.axis !== "x") {
6601 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
6602 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
6603 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
6604 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
6608 if(!o.axis || o.axis !== "y") {
6609 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
6610 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
6611 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
6612 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
6618 if(!o.axis || o.axis !== "x") {
6619 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
6620 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
6621 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
6622 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
6626 if(!o.axis || o.axis !== "y") {
6627 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
6628 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
6629 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
6630 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
6636 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
6637 $.ui.ddmanager.prepareOffsets(i, event);
6643 $.ui.plugin.add("draggable", "snap", {
6646 var i = $(this).data("ui-draggable"),
6649 i.snapElements = [];
6651 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
6654 if(this !== i.element[0]) {
6655 i.snapElements.push({
6657 width: $t.outerWidth(), height: $t.outerHeight(),
6658 top: $o.top, left: $o.left
6664 drag: function(event, ui) {
6666 var ts, bs, ls, rs, l, r, t, b, i, first,
6667 inst = $(this).data("ui-draggable"),
6669 d = o.snapTolerance,
6670 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
6671 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
6673 for (i = inst.snapElements.length - 1; i >= 0; i--){
6675 l = inst.snapElements[i].left;
6676 r = l + inst.snapElements[i].width;
6677 t = inst.snapElements[i].top;
6678 b = t + inst.snapElements[i].height;
6680 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
6681 if(inst.snapElements[i].snapping) {
6682 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
6684 inst.snapElements[i].snapping = false;
6688 if(o.snapMode !== "inner") {
6689 ts = Math.abs(t - y2) <= d;
6690 bs = Math.abs(b - y1) <= d;
6691 ls = Math.abs(l - x2) <= d;
6692 rs = Math.abs(r - x1) <= d;
6694 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
6697 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
6700 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
6703 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
6707 first = (ts || bs || ls || rs);
6709 if(o.snapMode !== "outer") {
6710 ts = Math.abs(t - y1) <= d;
6711 bs = Math.abs(b - y2) <= d;
6712 ls = Math.abs(l - x1) <= d;
6713 rs = Math.abs(r - x2) <= d;
6715 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
6718 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
6721 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
6724 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
6728 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
6729 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
6731 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
6738 $.ui.plugin.add("draggable", "stack", {
6741 o = this.data("ui-draggable").options,
6742 group = $.makeArray($(o.stack)).sort(function(a,b) {
6743 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
6746 if (!group.length) { return; }
6748 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
6749 $(group).each(function(i) {
6750 $(this).css("zIndex", min + i);
6752 this.css("zIndex", (min + group.length));
6756 $.ui.plugin.add("draggable", "zIndex", {
6757 start: function(event, ui) {
6758 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
6759 if(t.css("zIndex")) {
6760 o._zIndex = t.css("zIndex");
6762 t.css("zIndex", o.zIndex);
6764 stop: function(event, ui) {
6765 var o = $(this).data("ui-draggable").options;
6767 $(ui.helper).css("zIndex", o._zIndex);
6773 (function( $, undefined ) {
6775 function isOverAxis( x, reference, size ) {
6776 return ( x > reference ) && ( x < ( reference + size ) );
6779 $.widget("ui.droppable", {
6781 widgetEventPrefix: "drop",
6789 tolerance: "intersect",
6798 _create: function() {
6804 this.isover = false;
6807 this.accept = $.isFunction(accept) ? accept : function(d) {
6808 return d.is(accept);
6811 this.proportions = function( /* valueToWrite */ ) {
6812 if ( arguments.length ) {
6813 // Store the droppable's proportions
6814 proportions = arguments[ 0 ];
6816 // Retrieve or derive the droppable's proportions
6817 return proportions ?
6820 width: this.element[ 0 ].offsetWidth,
6821 height: this.element[ 0 ].offsetHeight
6826 // Add the reference and positions to the manager
6827 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
6828 $.ui.ddmanager.droppables[o.scope].push(this);
6830 (o.addClasses && this.element.addClass("ui-droppable"));
6834 _destroy: function() {
6836 drop = $.ui.ddmanager.droppables[this.options.scope];
6838 for ( ; i < drop.length; i++ ) {
6839 if ( drop[i] === this ) {
6844 this.element.removeClass("ui-droppable ui-droppable-disabled");
6847 _setOption: function(key, value) {
6849 if(key === "accept") {
6850 this.accept = $.isFunction(value) ? value : function(d) {
6854 $.Widget.prototype._setOption.apply(this, arguments);
6857 _activate: function(event) {
6858 var draggable = $.ui.ddmanager.current;
6859 if(this.options.activeClass) {
6860 this.element.addClass(this.options.activeClass);
6863 this._trigger("activate", event, this.ui(draggable));
6867 _deactivate: function(event) {
6868 var draggable = $.ui.ddmanager.current;
6869 if(this.options.activeClass) {
6870 this.element.removeClass(this.options.activeClass);
6873 this._trigger("deactivate", event, this.ui(draggable));
6877 _over: function(event) {
6879 var draggable = $.ui.ddmanager.current;
6881 // Bail if draggable and droppable are same element
6882 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
6886 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6887 if(this.options.hoverClass) {
6888 this.element.addClass(this.options.hoverClass);
6890 this._trigger("over", event, this.ui(draggable));
6895 _out: function(event) {
6897 var draggable = $.ui.ddmanager.current;
6899 // Bail if draggable and droppable are same element
6900 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
6904 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6905 if(this.options.hoverClass) {
6906 this.element.removeClass(this.options.hoverClass);
6908 this._trigger("out", event, this.ui(draggable));
6913 _drop: function(event,custom) {
6915 var draggable = custom || $.ui.ddmanager.current,
6916 childrenIntersection = false;
6918 // Bail if draggable and droppable are same element
6919 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
6923 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
6924 var inst = $.data(this, "ui-droppable");
6926 inst.options.greedy &&
6927 !inst.options.disabled &&
6928 inst.options.scope === draggable.options.scope &&
6929 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
6930 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
6931 ) { childrenIntersection = true; return false; }
6933 if(childrenIntersection) {
6937 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6938 if(this.options.activeClass) {
6939 this.element.removeClass(this.options.activeClass);
6941 if(this.options.hoverClass) {
6942 this.element.removeClass(this.options.hoverClass);
6944 this._trigger("drop", event, this.ui(draggable));
6945 return this.element;
6954 draggable: (c.currentItem || c.element),
6956 position: c.position,
6957 offset: c.positionAbs
6963 $.ui.intersect = function(draggable, droppable, toleranceMode) {
6965 if (!droppable.offset) {
6969 var draggableLeft, draggableTop,
6970 x1 = (draggable.positionAbs || draggable.position.absolute).left,
6971 y1 = (draggable.positionAbs || draggable.position.absolute).top,
6972 x2 = x1 + draggable.helperProportions.width,
6973 y2 = y1 + draggable.helperProportions.height,
6974 l = droppable.offset.left,
6975 t = droppable.offset.top,
6976 r = l + droppable.proportions().width,
6977 b = t + droppable.proportions().height;
6979 switch (toleranceMode) {
6981 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
6983 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
6984 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
6985 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
6986 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
6988 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
6989 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
6990 return isOverAxis( draggableTop, t, droppable.proportions().height ) && isOverAxis( draggableLeft, l, droppable.proportions().width );
6993 (y1 >= t && y1 <= b) || // Top edge touching
6994 (y2 >= t && y2 <= b) || // Bottom edge touching
6995 (y1 < t && y2 > b) // Surrounded vertically
6997 (x1 >= l && x1 <= r) || // Left edge touching
6998 (x2 >= l && x2 <= r) || // Right edge touching
6999 (x1 < l && x2 > r) // Surrounded horizontally
7008 This manager tracks offsets of draggables and droppables
7012 droppables: { "default": [] },
7013 prepareOffsets: function(t, event) {
7016 m = $.ui.ddmanager.droppables[t.options.scope] || [],
7017 type = event ? event.type : null, // workaround for #2317
7018 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
7020 droppablesLoop: for (i = 0; i < m.length; i++) {
7022 //No disabled and non-accepted
7023 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
7027 // Filter out elements in the current dragged item
7028 for (j=0; j < list.length; j++) {
7029 if(list[j] === m[i].element[0]) {
7030 m[i].proportions().height = 0;
7031 continue droppablesLoop;
7035 m[i].visible = m[i].element.css("display") !== "none";
7040 //Activate the droppable if used directly from draggables
7041 if(type === "mousedown") {
7042 m[i]._activate.call(m[i], event);
7045 m[ i ].offset = m[ i ].element.offset();
7046 m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth, height: m[ i ].element[ 0 ].offsetHeight });
7051 drop: function(draggable, event) {
7053 var dropped = false;
7054 // Create a copy of the droppables in case the list changes during the drop (#9116)
7055 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
7060 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
7061 dropped = this._drop.call(this, event) || dropped;
7064 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
7066 this.isover = false;
7067 this._deactivate.call(this, event);
7074 dragStart: function( draggable, event ) {
7075 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
7076 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
7077 if( !draggable.options.refreshPositions ) {
7078 $.ui.ddmanager.prepareOffsets( draggable, event );
7082 drag: function(draggable, event) {
7084 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
7085 if(draggable.options.refreshPositions) {
7086 $.ui.ddmanager.prepareOffsets(draggable, event);
7089 //Run through all droppables and check their positions based on specific tolerance options
7090 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
7092 if(this.options.disabled || this.greedyChild || !this.visible) {
7096 var parentInstance, scope, parent,
7097 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
7098 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
7103 if (this.options.greedy) {
7104 // find droppable parents with same scope
7105 scope = this.options.scope;
7106 parent = this.element.parents(":data(ui-droppable)").filter(function () {
7107 return $.data(this, "ui-droppable").options.scope === scope;
7110 if (parent.length) {
7111 parentInstance = $.data(parent[0], "ui-droppable");
7112 parentInstance.greedyChild = (c === "isover");
7116 // we just moved into a greedy child
7117 if (parentInstance && c === "isover") {
7118 parentInstance.isover = false;
7119 parentInstance.isout = true;
7120 parentInstance._out.call(parentInstance, event);
7124 this[c === "isout" ? "isover" : "isout"] = false;
7125 this[c === "isover" ? "_over" : "_out"].call(this, event);
7127 // we just moved out of a greedy child
7128 if (parentInstance && c === "isout") {
7129 parentInstance.isout = false;
7130 parentInstance.isover = true;
7131 parentInstance._over.call(parentInstance, event);
7136 dragStop: function( draggable, event ) {
7137 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
7138 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
7139 if( !draggable.options.refreshPositions ) {
7140 $.ui.ddmanager.prepareOffsets( draggable, event );
7146 (function($, undefined) {
7148 var dataSpace = "ui-effects-";
7155 * jQuery Color Animations v2.1.2
7156 * https://github.com/jquery/jquery-color
7158 * Copyright 2013 jQuery Foundation and other contributors
7159 * Released under the MIT license.
7160 * http://jquery.org/license
7162 * Date: Wed Jan 16 08:47:09 2013 -0600
7164 (function( jQuery, undefined ) {
7166 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
7168 // plusequals test for += 100 -= 100
7169 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
7170 // a set of RE's that can match strings and generate color tuples.
7172 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7173 parse: function( execResult ) {
7182 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7183 parse: function( execResult ) {
7185 execResult[ 1 ] * 2.55,
7186 execResult[ 2 ] * 2.55,
7187 execResult[ 3 ] * 2.55,
7192 // this regex ignores A-F because it's compared against an already lowercased string
7193 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
7194 parse: function( execResult ) {
7196 parseInt( execResult[ 1 ], 16 ),
7197 parseInt( execResult[ 2 ], 16 ),
7198 parseInt( execResult[ 3 ], 16 )
7202 // this regex ignores A-F because it's compared against an already lowercased string
7203 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
7204 parse: function( execResult ) {
7206 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
7207 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
7208 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
7212 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7214 parse: function( execResult ) {
7217 execResult[ 2 ] / 100,
7218 execResult[ 3 ] / 100,
7225 color = jQuery.Color = function( color, green, blue, alpha ) {
7226 return new jQuery.Color.fn.parse( color, green, blue, alpha );
7276 support = color.support = {},
7278 // element for support tests
7279 supportElem = jQuery( "<p>" )[ 0 ],
7281 // colors = jQuery.Color.names
7284 // local aliases of functions called often
7287 // determine rgba support immediately
7288 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
7289 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
7291 // define cache name and alpha properties
7292 // for rgba and hsla spaces
7293 each( spaces, function( spaceName, space ) {
7294 space.cache = "_" + spaceName;
7295 space.props.alpha = {
7302 function clamp( value, prop, allowEmpty ) {
7303 var type = propTypes[ prop.type ] || {};
7305 if ( value == null ) {
7306 return (allowEmpty || !prop.def) ? null : prop.def;
7309 // ~~ is an short way of doing floor for positive numbers
7310 value = type.floor ? ~~value : parseFloat( value );
7312 // IE will pass in empty strings as value for alpha,
7313 // which will hit this case
7314 if ( isNaN( value ) ) {
7319 // we add mod before modding to make sure that negatives values
7320 // get converted properly: -10 -> 350
7321 return (value + type.mod) % type.mod;
7324 // for now all property types without mod have min and max
7325 return 0 > value ? 0 : type.max < value ? type.max : value;
7328 function stringParse( string ) {
7330 rgba = inst._rgba = [];
7332 string = string.toLowerCase();
7334 each( stringParsers, function( i, parser ) {
7336 match = parser.re.exec( string ),
7337 values = match && parser.parse( match ),
7338 spaceName = parser.space || "rgba";
7341 parsed = inst[ spaceName ]( values );
7343 // if this was an rgba parse the assignment might happen twice
7345 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
7346 rgba = inst._rgba = parsed._rgba;
7348 // exit each( stringParsers ) here because we matched
7353 // Found a stringParser that handled it
7354 if ( rgba.length ) {
7356 // if this came from a parsed string, force "transparent" when alpha is 0
7357 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
7358 if ( rgba.join() === "0,0,0,0" ) {
7359 jQuery.extend( rgba, colors.transparent );
7365 return colors[ string ];
7368 color.fn = jQuery.extend( color.prototype, {
7369 parse: function( red, green, blue, alpha ) {
7370 if ( red === undefined ) {
7371 this._rgba = [ null, null, null, null ];
7374 if ( red.jquery || red.nodeType ) {
7375 red = jQuery( red ).css( green );
7380 type = jQuery.type( red ),
7381 rgba = this._rgba = [];
7383 // more than 1 argument specified - assume ( red, green, blue, alpha )
7384 if ( green !== undefined ) {
7385 red = [ red, green, blue, alpha ];
7389 if ( type === "string" ) {
7390 return this.parse( stringParse( red ) || colors._default );
7393 if ( type === "array" ) {
7394 each( spaces.rgba.props, function( key, prop ) {
7395 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
7400 if ( type === "object" ) {
7401 if ( red instanceof color ) {
7402 each( spaces, function( spaceName, space ) {
7403 if ( red[ space.cache ] ) {
7404 inst[ space.cache ] = red[ space.cache ].slice();
7408 each( spaces, function( spaceName, space ) {
7409 var cache = space.cache;
7410 each( space.props, function( key, prop ) {
7412 // if the cache doesn't exist, and we know how to convert
7413 if ( !inst[ cache ] && space.to ) {
7415 // if the value was null, we don't need to copy it
7416 // if the key was alpha, we don't need to copy it either
7417 if ( key === "alpha" || red[ key ] == null ) {
7420 inst[ cache ] = space.to( inst._rgba );
7423 // this is the only case where we allow nulls for ALL properties.
7424 // call clamp with alwaysAllowEmpty
7425 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
7428 // everything defined but alpha?
7429 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
7430 // use the default of 1
7431 inst[ cache ][ 3 ] = 1;
7433 inst._rgba = space.from( inst[ cache ] );
7441 is: function( compare ) {
7442 var is = color( compare ),
7446 each( spaces, function( _, space ) {
7448 isCache = is[ space.cache ];
7450 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
7451 each( space.props, function( _, prop ) {
7452 if ( isCache[ prop.idx ] != null ) {
7453 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
7462 _space: function() {
7465 each( spaces, function( spaceName, space ) {
7466 if ( inst[ space.cache ] ) {
7467 used.push( spaceName );
7472 transition: function( other, distance ) {
7473 var end = color( other ),
7474 spaceName = end._space(),
7475 space = spaces[ spaceName ],
7476 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
7477 start = startColor[ space.cache ] || space.to( startColor._rgba ),
7478 result = start.slice();
7480 end = end[ space.cache ];
7481 each( space.props, function( key, prop ) {
7482 var index = prop.idx,
7483 startValue = start[ index ],
7484 endValue = end[ index ],
7485 type = propTypes[ prop.type ] || {};
7487 // if null, don't override start value
7488 if ( endValue === null ) {
7491 // if null - use end
7492 if ( startValue === null ) {
7493 result[ index ] = endValue;
7496 if ( endValue - startValue > type.mod / 2 ) {
7497 startValue += type.mod;
7498 } else if ( startValue - endValue > type.mod / 2 ) {
7499 startValue -= type.mod;
7502 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
7505 return this[ spaceName ]( result );
7507 blend: function( opaque ) {
7508 // if we are already opaque - return ourself
7509 if ( this._rgba[ 3 ] === 1 ) {
7513 var rgb = this._rgba.slice(),
7515 blend = color( opaque )._rgba;
7517 return color( jQuery.map( rgb, function( v, i ) {
7518 return ( 1 - a ) * blend[ i ] + a * v;
7521 toRgbaString: function() {
7522 var prefix = "rgba(",
7523 rgba = jQuery.map( this._rgba, function( v, i ) {
7524 return v == null ? ( i > 2 ? 1 : 0 ) : v;
7527 if ( rgba[ 3 ] === 1 ) {
7532 return prefix + rgba.join() + ")";
7534 toHslaString: function() {
7535 var prefix = "hsla(",
7536 hsla = jQuery.map( this.hsla(), function( v, i ) {
7543 v = Math.round( v * 100 ) + "%";
7548 if ( hsla[ 3 ] === 1 ) {
7552 return prefix + hsla.join() + ")";
7554 toHexString: function( includeAlpha ) {
7555 var rgba = this._rgba.slice(),
7558 if ( includeAlpha ) {
7559 rgba.push( ~~( alpha * 255 ) );
7562 return "#" + jQuery.map( rgba, function( v ) {
7564 // default to 0 when nulls exist
7565 v = ( v || 0 ).toString( 16 );
7566 return v.length === 1 ? "0" + v : v;
7569 toString: function() {
7570 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
7573 color.fn.parse.prototype = color.fn;
7575 // hsla conversions adapted from:
7576 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
7578 function hue2rgb( p, q, h ) {
7581 return p + (q - p) * h * 6;
7587 return p + (q - p) * ((2/3) - h) * 6;
7592 spaces.hsla.to = function ( rgba ) {
7593 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
7594 return [ null, null, null, rgba[ 3 ] ];
7596 var r = rgba[ 0 ] / 255,
7597 g = rgba[ 1 ] / 255,
7598 b = rgba[ 2 ] / 255,
7600 max = Math.max( r, g, b ),
7601 min = Math.min( r, g, b ),
7607 if ( min === max ) {
7609 } else if ( r === max ) {
7610 h = ( 60 * ( g - b ) / diff ) + 360;
7611 } else if ( g === max ) {
7612 h = ( 60 * ( b - r ) / diff ) + 120;
7614 h = ( 60 * ( r - g ) / diff ) + 240;
7617 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
7618 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
7621 } else if ( l <= 0.5 ) {
7624 s = diff / ( 2 - add );
7626 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
7629 spaces.hsla.from = function ( hsla ) {
7630 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
7631 return [ null, null, null, hsla[ 3 ] ];
7633 var h = hsla[ 0 ] / 360,
7637 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
7641 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
7642 Math.round( hue2rgb( p, q, h ) * 255 ),
7643 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
7649 each( spaces, function( spaceName, space ) {
7650 var props = space.props,
7651 cache = space.cache,
7655 // makes rgba() and hsla()
7656 color.fn[ spaceName ] = function( value ) {
7658 // generate a cache for this space if it doesn't exist
7659 if ( to && !this[ cache ] ) {
7660 this[ cache ] = to( this._rgba );
7662 if ( value === undefined ) {
7663 return this[ cache ].slice();
7667 type = jQuery.type( value ),
7668 arr = ( type === "array" || type === "object" ) ? value : arguments,
7669 local = this[ cache ].slice();
7671 each( props, function( key, prop ) {
7672 var val = arr[ type === "object" ? key : prop.idx ];
7673 if ( val == null ) {
7674 val = local[ prop.idx ];
7676 local[ prop.idx ] = clamp( val, prop );
7680 ret = color( from( local ) );
7681 ret[ cache ] = local;
7684 return color( local );
7688 // makes red() green() blue() alpha() hue() saturation() lightness()
7689 each( props, function( key, prop ) {
7690 // alpha is included in more than one space
7691 if ( color.fn[ key ] ) {
7694 color.fn[ key ] = function( value ) {
7695 var vtype = jQuery.type( value ),
7696 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
7697 local = this[ fn ](),
7698 cur = local[ prop.idx ],
7701 if ( vtype === "undefined" ) {
7705 if ( vtype === "function" ) {
7706 value = value.call( this, cur );
7707 vtype = jQuery.type( value );
7709 if ( value == null && prop.empty ) {
7712 if ( vtype === "string" ) {
7713 match = rplusequals.exec( value );
7715 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
7718 local[ prop.idx ] = value;
7719 return this[ fn ]( local );
7724 // add cssHook and .fx.step function for each named hook.
7725 // accept a space separated string of properties
7726 color.hook = function( hook ) {
7727 var hooks = hook.split( " " );
7728 each( hooks, function( i, hook ) {
7729 jQuery.cssHooks[ hook ] = {
7730 set: function( elem, value ) {
7731 var parsed, curElem,
7732 backgroundColor = "";
7734 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
7735 value = color( parsed || value );
7736 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
7737 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
7739 (backgroundColor === "" || backgroundColor === "transparent") &&
7740 curElem && curElem.style
7743 backgroundColor = jQuery.css( curElem, "backgroundColor" );
7744 curElem = curElem.parentNode;
7749 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
7754 value = value.toRgbaString();
7757 elem.style[ hook ] = value;
7759 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
7763 jQuery.fx.step[ hook ] = function( fx ) {
7764 if ( !fx.colorInit ) {
7765 fx.start = color( fx.elem, hook );
7766 fx.end = color( fx.end );
7767 fx.colorInit = true;
7769 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
7775 color.hook( stepHooks );
7777 jQuery.cssHooks.borderColor = {
7778 expand: function( value ) {
7781 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
7782 expanded[ "border" + part + "Color" ] = value;
7788 // Basic color names only.
7789 // Usage of any of the other color names requires adding yourself or including
7790 // jquery.color.svg-names.js.
7791 colors = jQuery.Color.names = {
7792 // 4.1. Basic color keywords
7810 // 4.2.3. "transparent" color keyword
7811 transparent: [ null, null, null, 0 ],
7819 /******************************************************************************/
7820 /****************************** CLASS ANIMATIONS ******************************/
7821 /******************************************************************************/
7824 var classAnimationActions = [ "add", "remove", "toggle" ],
7837 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
7838 $.fx.step[ prop ] = function( fx ) {
7839 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
7840 jQuery.style( fx.elem, prop, fx.end );
7846 function getElementStyles( elem ) {
7848 style = elem.ownerDocument.defaultView ?
7849 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
7853 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
7857 if ( typeof style[ key ] === "string" ) {
7858 styles[ $.camelCase( key ) ] = style[ key ];
7861 // support: Opera, IE <9
7863 for ( key in style ) {
7864 if ( typeof style[ key ] === "string" ) {
7865 styles[ key ] = style[ key ];
7874 function styleDifference( oldStyle, newStyle ) {
7878 for ( name in newStyle ) {
7879 value = newStyle[ name ];
7880 if ( oldStyle[ name ] !== value ) {
7881 if ( !shorthandStyles[ name ] ) {
7882 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
7883 diff[ name ] = value;
7892 // support: jQuery <1.8
7893 if ( !$.fn.addBack ) {
7894 $.fn.addBack = function( selector ) {
7895 return this.add( selector == null ?
7896 this.prevObject : this.prevObject.filter( selector )
7901 $.effects.animateClass = function( value, duration, easing, callback ) {
7902 var o = $.speed( duration, easing, callback );
7904 return this.queue( function() {
7905 var animated = $( this ),
7906 baseClass = animated.attr( "class" ) || "",
7908 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
7910 // map the animated objects to store the original styles.
7911 allAnimations = allAnimations.map(function() {
7915 start: getElementStyles( this )
7919 // apply class change
7920 applyClassChange = function() {
7921 $.each( classAnimationActions, function(i, action) {
7922 if ( value[ action ] ) {
7923 animated[ action + "Class" ]( value[ action ] );
7929 // map all animated objects again - calculate new styles and diff
7930 allAnimations = allAnimations.map(function() {
7931 this.end = getElementStyles( this.el[ 0 ] );
7932 this.diff = styleDifference( this.start, this.end );
7936 // apply original class
7937 animated.attr( "class", baseClass );
7939 // map all animated objects again - this time collecting a promise
7940 allAnimations = allAnimations.map(function() {
7941 var styleInfo = this,
7943 opts = $.extend({}, o, {
7945 complete: function() {
7946 dfd.resolve( styleInfo );
7950 this.el.animate( this.diff, opts );
7951 return dfd.promise();
7954 // once all animations have completed:
7955 $.when.apply( $, allAnimations.get() ).done(function() {
7957 // set the final class
7960 // for each animated element,
7961 // clear all css properties that were animated
7962 $.each( arguments, function() {
7964 $.each( this.diff, function(key) {
7969 // this is guarnteed to be there if you use jQuery.speed()
7970 // it also handles dequeuing the next anim...
7971 o.complete.call( animated[ 0 ] );
7977 addClass: (function( orig ) {
7978 return function( classNames, speed, easing, callback ) {
7980 $.effects.animateClass.call( this,
7981 { add: classNames }, speed, easing, callback ) :
7982 orig.apply( this, arguments );
7984 })( $.fn.addClass ),
7986 removeClass: (function( orig ) {
7987 return function( classNames, speed, easing, callback ) {
7988 return arguments.length > 1 ?
7989 $.effects.animateClass.call( this,
7990 { remove: classNames }, speed, easing, callback ) :
7991 orig.apply( this, arguments );
7993 })( $.fn.removeClass ),
7995 toggleClass: (function( orig ) {
7996 return function( classNames, force, speed, easing, callback ) {
7997 if ( typeof force === "boolean" || force === undefined ) {
7999 // without speed parameter
8000 return orig.apply( this, arguments );
8002 return $.effects.animateClass.call( this,
8003 (force ? { add: classNames } : { remove: classNames }),
8004 speed, easing, callback );
8007 // without force parameter
8008 return $.effects.animateClass.call( this,
8009 { toggle: classNames }, force, speed, easing );
8012 })( $.fn.toggleClass ),
8014 switchClass: function( remove, add, speed, easing, callback) {
8015 return $.effects.animateClass.call( this, {
8018 }, speed, easing, callback );
8024 /******************************************************************************/
8025 /*********************************** EFFECTS **********************************/
8026 /******************************************************************************/
8030 $.extend( $.effects, {
8033 // Saves a set of properties in a data storage
8034 save: function( element, set ) {
8035 for( var i=0; i < set.length; i++ ) {
8036 if ( set[ i ] !== null ) {
8037 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
8042 // Restores a set of previously saved properties from a data storage
8043 restore: function( element, set ) {
8045 for( i=0; i < set.length; i++ ) {
8046 if ( set[ i ] !== null ) {
8047 val = element.data( dataSpace + set[ i ] );
8048 // support: jQuery 1.6.2
8049 // http://bugs.jquery.com/ticket/9917
8050 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
8051 // We can't differentiate between "" and 0 here, so we just assume
8052 // empty string since it's likely to be a more common value...
8053 if ( val === undefined ) {
8056 element.css( set[ i ], val );
8061 setMode: function( el, mode ) {
8062 if (mode === "toggle") {
8063 mode = el.is( ":hidden" ) ? "show" : "hide";
8068 // Translates a [top,left] array into a baseline value
8069 // this should be a little more flexible in the future to handle a string & hash
8070 getBaseline: function( origin, original ) {
8072 switch ( origin[ 0 ] ) {
8073 case "top": y = 0; break;
8074 case "middle": y = 0.5; break;
8075 case "bottom": y = 1; break;
8076 default: y = origin[ 0 ] / original.height;
8078 switch ( origin[ 1 ] ) {
8079 case "left": x = 0; break;
8080 case "center": x = 0.5; break;
8081 case "right": x = 1; break;
8082 default: x = origin[ 1 ] / original.width;
8090 // Wraps the element around a wrapper that copies position properties
8091 createWrapper: function( element ) {
8093 // if the element is already wrapped, return it
8094 if ( element.parent().is( ".ui-effects-wrapper" )) {
8095 return element.parent();
8100 width: element.outerWidth(true),
8101 height: element.outerHeight(true),
8102 "float": element.css( "float" )
8104 wrapper = $( "<div></div>" )
8105 .addClass( "ui-effects-wrapper" )
8108 background: "transparent",
8113 // Store the size in case width/height are defined in % - Fixes #5245
8115 width: element.width(),
8116 height: element.height()
8118 active = document.activeElement;
8121 // Firefox incorrectly exposes anonymous content
8122 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
8126 active = document.body;
8129 element.wrap( wrapper );
8131 // Fixes #7595 - Elements lose focus when wrapped.
8132 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
8133 $( active ).focus();
8136 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
8138 // transfer positioning properties to the wrapper
8139 if ( element.css( "position" ) === "static" ) {
8140 wrapper.css({ position: "relative" });
8141 element.css({ position: "relative" });
8144 position: element.css( "position" ),
8145 zIndex: element.css( "z-index" )
8147 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
8148 props[ pos ] = element.css( pos );
8149 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
8150 props[ pos ] = "auto";
8154 position: "relative",
8163 return wrapper.css( props ).show();
8166 removeWrapper: function( element ) {
8167 var active = document.activeElement;
8169 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
8170 element.parent().replaceWith( element );
8172 // Fixes #7595 - Elements lose focus when wrapped.
8173 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
8174 $( active ).focus();
8182 setTransition: function( element, list, factor, value ) {
8183 value = value || {};
8184 $.each( list, function( i, x ) {
8185 var unit = element.cssUnit( x );
8186 if ( unit[ 0 ] > 0 ) {
8187 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
8194 // return an effect options object for the given parameters:
8195 function _normalizeArguments( effect, options, speed, callback ) {
8197 // allow passing all options as the first parameter
8198 if ( $.isPlainObject( effect ) ) {
8200 effect = effect.effect;
8203 // convert to an object
8204 effect = { effect: effect };
8206 // catch (effect, null, ...)
8207 if ( options == null ) {
8211 // catch (effect, callback)
8212 if ( $.isFunction( options ) ) {
8218 // catch (effect, speed, ?)
8219 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
8225 // catch (effect, options, callback)
8226 if ( $.isFunction( speed ) ) {
8231 // add options to effect
8233 $.extend( effect, options );
8236 speed = speed || options.duration;
8237 effect.duration = $.fx.off ? 0 :
8238 typeof speed === "number" ? speed :
8239 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
8240 $.fx.speeds._default;
8242 effect.complete = callback || options.complete;
8247 function standardAnimationOption( option ) {
8248 // Valid standard speeds (nothing, number, named speed)
8249 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
8253 // Invalid strings - treat as "normal" speed
8254 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
8258 // Complete callback
8259 if ( $.isFunction( option ) ) {
8263 // Options hash (but not naming an effect)
8264 if ( typeof option === "object" && !option.effect ) {
8268 // Didn't match any standard API
8273 effect: function( /* effect, options, speed, callback */ ) {
8274 var args = _normalizeArguments.apply( this, arguments ),
8277 effectMethod = $.effects.effect[ args.effect ];
8279 if ( $.fx.off || !effectMethod ) {
8280 // delegate to the original method (e.g., .show()) if possible
8282 return this[ mode ]( args.duration, args.complete );
8284 return this.each( function() {
8285 if ( args.complete ) {
8286 args.complete.call( this );
8292 function run( next ) {
8293 var elem = $( this ),
8294 complete = args.complete,
8298 if ( $.isFunction( complete ) ) {
8299 complete.call( elem[0] );
8301 if ( $.isFunction( next ) ) {
8306 // If the element already has the correct final state, delegate to
8307 // the core methods so the internal tracking of "olddisplay" works.
8308 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
8312 effectMethod.call( elem[0], args, done );
8316 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
8319 show: (function( orig ) {
8320 return function( option ) {
8321 if ( standardAnimationOption( option ) ) {
8322 return orig.apply( this, arguments );
8324 var args = _normalizeArguments.apply( this, arguments );
8326 return this.effect.call( this, args );
8331 hide: (function( orig ) {
8332 return function( option ) {
8333 if ( standardAnimationOption( option ) ) {
8334 return orig.apply( this, arguments );
8336 var args = _normalizeArguments.apply( this, arguments );
8338 return this.effect.call( this, args );
8343 toggle: (function( orig ) {
8344 return function( option ) {
8345 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
8346 return orig.apply( this, arguments );
8348 var args = _normalizeArguments.apply( this, arguments );
8349 args.mode = "toggle";
8350 return this.effect.call( this, args );
8356 cssUnit: function(key) {
8357 var style = this.css( key ),
8360 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
8361 if ( style.indexOf( unit ) > 0 ) {
8362 val = [ parseFloat( style ), unit ];
8371 /******************************************************************************/
8372 /*********************************** EASING ***********************************/
8373 /******************************************************************************/
8377 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
8379 var baseEasings = {};
8381 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
8382 baseEasings[ name ] = function( p ) {
8383 return Math.pow( p, i + 2 );
8387 $.extend( baseEasings, {
8388 Sine: function ( p ) {
8389 return 1 - Math.cos( p * Math.PI / 2 );
8391 Circ: function ( p ) {
8392 return 1 - Math.sqrt( 1 - p * p );
8394 Elastic: function( p ) {
8395 return p === 0 || p === 1 ? p :
8396 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
8398 Back: function( p ) {
8399 return p * p * ( 3 * p - 2 );
8401 Bounce: function ( p ) {
8405 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
8406 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
8410 $.each( baseEasings, function( name, easeIn ) {
8411 $.easing[ "easeIn" + name ] = easeIn;
8412 $.easing[ "easeOut" + name ] = function( p ) {
8413 return 1 - easeIn( 1 - p );
8415 $.easing[ "easeInOut" + name ] = function( p ) {
8417 easeIn( p * 2 ) / 2 :
8418 1 - easeIn( p * -2 + 2 ) / 2;
8425 (function( $, undefined ) {
8427 var rvertical = /up|down|vertical/,
8428 rpositivemotion = /up|left|vertical|horizontal/;
8430 $.effects.effect.blind = function( o, done ) {
8433 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8434 mode = $.effects.setMode( el, o.mode || "hide" ),
8435 direction = o.direction || "up",
8436 vertical = rvertical.test( direction ),
8437 ref = vertical ? "height" : "width",
8438 ref2 = vertical ? "top" : "left",
8439 motion = rpositivemotion.test( direction ),
8441 show = mode === "show",
8442 wrapper, distance, margin;
8444 // if already wrapped, the wrapper's properties are my property. #6245
8445 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
8446 $.effects.save( el.parent(), props );
8448 $.effects.save( el, props );
8451 wrapper = $.effects.createWrapper( el ).css({
8455 distance = wrapper[ ref ]();
8456 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
8458 animation[ ref ] = show ? distance : 0;
8461 .css( vertical ? "bottom" : "right", 0 )
8462 .css( vertical ? "top" : "left", "auto" )
8463 .css({ position: "absolute" });
8465 animation[ ref2 ] = show ? margin : distance + margin;
8468 // start at 0 if we are showing
8470 wrapper.css( ref, 0 );
8472 wrapper.css( ref2, margin + distance );
8477 wrapper.animate( animation, {
8478 duration: o.duration,
8481 complete: function() {
8482 if ( mode === "hide" ) {
8485 $.effects.restore( el, props );
8486 $.effects.removeWrapper( el );
8494 (function( $, undefined ) {
8496 $.effects.effect.bounce = function( o, done ) {
8498 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8501 mode = $.effects.setMode( el, o.mode || "effect" ),
8502 hide = mode === "hide",
8503 show = mode === "show",
8504 direction = o.direction || "up",
8505 distance = o.distance,
8506 times = o.times || 5,
8508 // number of internal animations
8509 anims = times * 2 + ( show || hide ? 1 : 0 ),
8510 speed = o.duration / anims,
8514 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
8515 motion = ( direction === "up" || direction === "left" ),
8520 // we will need to re-assemble the queue to stack our animations in place
8522 queuelen = queue.length;
8524 // Avoid touching opacity to prevent clearType and PNG issues in IE
8525 if ( show || hide ) {
8526 props.push( "opacity" );
8529 $.effects.save( el, props );
8531 $.effects.createWrapper( el ); // Create Wrapper
8533 // default distance for the BIGGEST bounce is the outer Distance / 3
8535 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
8539 downAnim = { opacity: 1 };
8540 downAnim[ ref ] = 0;
8542 // if we are showing, force opacity 0 and set the initial position
8543 // then do the "first" animation
8544 el.css( "opacity", 0 )
8545 .css( ref, motion ? -distance * 2 : distance * 2 )
8546 .animate( downAnim, speed, easing );
8549 // start at the smallest distance if we are hiding
8551 distance = distance / Math.pow( 2, times - 1 );
8555 downAnim[ ref ] = 0;
8556 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
8557 for ( i = 0; i < times; i++ ) {
8559 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
8561 el.animate( upAnim, speed, easing )
8562 .animate( downAnim, speed, easing );
8564 distance = hide ? distance * 2 : distance / 2;
8567 // Last Bounce when Hiding
8569 upAnim = { opacity: 0 };
8570 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
8572 el.animate( upAnim, speed, easing );
8575 el.queue(function() {
8579 $.effects.restore( el, props );
8580 $.effects.removeWrapper( el );
8584 // inject all the animations we just queued to be first in line (after "inprogress")
8585 if ( queuelen > 1) {
8586 queue.splice.apply( queue,
8587 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8594 (function( $, undefined ) {
8596 $.effects.effect.clip = function( o, done ) {
8599 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8600 mode = $.effects.setMode( el, o.mode || "hide" ),
8601 show = mode === "show",
8602 direction = o.direction || "vertical",
8603 vert = direction === "vertical",
8604 size = vert ? "height" : "width",
8605 position = vert ? "top" : "left",
8607 wrapper, animate, distance;
8610 $.effects.save( el, props );
8614 wrapper = $.effects.createWrapper( el ).css({
8617 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
8618 distance = animate[ size ]();
8622 animate.css( size, 0 );
8623 animate.css( position, distance / 2 );
8626 // Create Animation Object:
8627 animation[ size ] = show ? distance : 0;
8628 animation[ position ] = show ? 0 : distance / 2;
8631 animate.animate( animation, {
8633 duration: o.duration,
8635 complete: function() {
8639 $.effects.restore( el, props );
8640 $.effects.removeWrapper( el );
8648 (function( $, undefined ) {
8650 $.effects.effect.drop = function( o, done ) {
8653 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
8654 mode = $.effects.setMode( el, o.mode || "hide" ),
8655 show = mode === "show",
8656 direction = o.direction || "left",
8657 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
8658 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
8660 opacity: show ? 1 : 0
8665 $.effects.save( el, props );
8667 $.effects.createWrapper( el );
8669 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
8673 .css( "opacity", 0 )
8674 .css( ref, motion === "pos" ? -distance : distance );
8678 animation[ ref ] = ( show ?
8679 ( motion === "pos" ? "+=" : "-=" ) :
8680 ( motion === "pos" ? "-=" : "+=" ) ) +
8684 el.animate( animation, {
8686 duration: o.duration,
8688 complete: function() {
8689 if ( mode === "hide" ) {
8692 $.effects.restore( el, props );
8693 $.effects.removeWrapper( el );
8700 (function( $, undefined ) {
8702 $.effects.effect.explode = function( o, done ) {
8704 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
8707 mode = $.effects.setMode( el, o.mode || "hide" ),
8708 show = mode === "show",
8710 // show and then visibility:hidden the element before calculating offset
8711 offset = el.show().css( "visibility", "hidden" ).offset(),
8713 // width and height of a piece
8714 width = Math.ceil( el.outerWidth() / cells ),
8715 height = Math.ceil( el.outerHeight() / rows ),
8719 i, j, left, top, mx, my;
8721 // children animate complete:
8722 function childComplete() {
8723 pieces.push( this );
8724 if ( pieces.length === rows * cells ) {
8729 // clone the element for each row and cell.
8730 for( i = 0; i < rows ; i++ ) { // ===>
8731 top = offset.top + i * height;
8732 my = i - ( rows - 1 ) / 2 ;
8734 for( j = 0; j < cells ; j++ ) { // |||
8735 left = offset.left + j * width;
8736 mx = j - ( cells - 1 ) / 2 ;
8738 // Create a clone of the now hidden main element that will be absolute positioned
8739 // within a wrapper div off the -left and -top equal to size of our pieces
8743 .wrap( "<div></div>" )
8745 position: "absolute",
8746 visibility: "visible",
8751 // select the wrapper - make it overflow: hidden and absolute positioned based on
8752 // where the original was located +left and +top equal to the size of pieces
8754 .addClass( "ui-effects-explode" )
8756 position: "absolute",
8760 left: left + ( show ? mx * width : 0 ),
8761 top: top + ( show ? my * height : 0 ),
8762 opacity: show ? 0 : 1
8764 left: left + ( show ? 0 : mx * width ),
8765 top: top + ( show ? 0 : my * height ),
8766 opacity: show ? 1 : 0
8767 }, o.duration || 500, o.easing, childComplete );
8771 function animComplete() {
8773 visibility: "visible"
8775 $( pieces ).remove();
8784 (function( $, undefined ) {
8786 $.effects.effect.fade = function( o, done ) {
8788 mode = $.effects.setMode( el, o.mode || "toggle" );
8794 duration: o.duration,
8801 (function( $, undefined ) {
8803 $.effects.effect.fold = function( o, done ) {
8807 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8808 mode = $.effects.setMode( el, o.mode || "hide" ),
8809 show = mode === "show",
8810 hide = mode === "hide",
8811 size = o.size || 15,
8812 percent = /([0-9]+)%/.exec( size ),
8813 horizFirst = !!o.horizFirst,
8814 widthFirst = show !== horizFirst,
8815 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
8816 duration = o.duration / 2,
8821 $.effects.save( el, props );
8825 wrapper = $.effects.createWrapper( el ).css({
8828 distance = widthFirst ?
8829 [ wrapper.width(), wrapper.height() ] :
8830 [ wrapper.height(), wrapper.width() ];
8833 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
8836 wrapper.css( horizFirst ? {
8846 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
8847 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
8851 .animate( animation1, duration, o.easing )
8852 .animate( animation2, duration, o.easing, function() {
8856 $.effects.restore( el, props );
8857 $.effects.removeWrapper( el );
8864 (function( $, undefined ) {
8866 $.effects.effect.highlight = function( o, done ) {
8867 var elem = $( this ),
8868 props = [ "backgroundImage", "backgroundColor", "opacity" ],
8869 mode = $.effects.setMode( elem, o.mode || "show" ),
8871 backgroundColor: elem.css( "backgroundColor" )
8874 if (mode === "hide") {
8875 animation.opacity = 0;
8878 $.effects.save( elem, props );
8883 backgroundImage: "none",
8884 backgroundColor: o.color || "#ffff99"
8886 .animate( animation, {
8888 duration: o.duration,
8890 complete: function() {
8891 if ( mode === "hide" ) {
8894 $.effects.restore( elem, props );
8901 (function( $, undefined ) {
8903 $.effects.effect.pulsate = function( o, done ) {
8904 var elem = $( this ),
8905 mode = $.effects.setMode( elem, o.mode || "show" ),
8906 show = mode === "show",
8907 hide = mode === "hide",
8908 showhide = ( show || mode === "hide" ),
8910 // showing or hiding leaves of the "last" animation
8911 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
8912 duration = o.duration / anims,
8914 queue = elem.queue(),
8915 queuelen = queue.length,
8918 if ( show || !elem.is(":visible")) {
8919 elem.css( "opacity", 0 ).show();
8923 // anims - 1 opacity "toggles"
8924 for ( i = 1; i < anims; i++ ) {
8927 }, duration, o.easing );
8928 animateTo = 1 - animateTo;
8933 }, duration, o.easing);
8935 elem.queue(function() {
8942 // We just queued up "anims" animations, we need to put them next in the queue
8943 if ( queuelen > 1 ) {
8944 queue.splice.apply( queue,
8945 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8951 (function( $, undefined ) {
8953 $.effects.effect.puff = function( o, done ) {
8954 var elem = $( this ),
8955 mode = $.effects.setMode( elem, o.mode || "hide" ),
8956 hide = mode === "hide",
8957 percent = parseInt( o.percent, 10 ) || 150,
8958 factor = percent / 100,
8960 height: elem.height(),
8961 width: elem.width(),
8962 outerHeight: elem.outerHeight(),
8963 outerWidth: elem.outerWidth()
8972 percent: hide ? percent : 100,
8976 height: original.height * factor,
8977 width: original.width * factor,
8978 outerHeight: original.outerHeight * factor,
8979 outerWidth: original.outerWidth * factor
8986 $.effects.effect.scale = function( o, done ) {
8990 options = $.extend( true, {}, o ),
8991 mode = $.effects.setMode( el, o.mode || "effect" ),
8992 percent = parseInt( o.percent, 10 ) ||
8993 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
8994 direction = o.direction || "both",
8997 height: el.height(),
8999 outerHeight: el.outerHeight(),
9000 outerWidth: el.outerWidth()
9003 y: direction !== "horizontal" ? (percent / 100) : 1,
9004 x: direction !== "vertical" ? (percent / 100) : 1
9007 // We are going to pass this effect to the size effect:
9008 options.effect = "size";
9009 options.queue = false;
9010 options.complete = done;
9012 // Set default origin and restore for show/hide
9013 if ( mode !== "effect" ) {
9014 options.origin = origin || ["middle","center"];
9015 options.restore = true;
9018 options.from = o.from || ( mode === "show" ? {
9025 height: original.height * factor.y,
9026 width: original.width * factor.x,
9027 outerHeight: original.outerHeight * factor.y,
9028 outerWidth: original.outerWidth * factor.x
9031 // Fade option to support puff
9032 if ( options.fade ) {
9033 if ( mode === "show" ) {
9034 options.from.opacity = 0;
9035 options.to.opacity = 1;
9037 if ( mode === "hide" ) {
9038 options.from.opacity = 1;
9039 options.to.opacity = 0;
9044 el.effect( options );
9048 $.effects.effect.size = function( o, done ) {
9051 var original, baseline, factor,
9053 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
9056 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
9058 // Copy for children
9059 props2 = [ "width", "height", "overflow" ],
9060 cProps = [ "fontSize" ],
9061 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
9062 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
9065 mode = $.effects.setMode( el, o.mode || "effect" ),
9066 restore = o.restore || mode !== "effect",
9067 scale = o.scale || "both",
9068 origin = o.origin || [ "middle", "center" ],
9069 position = el.css( "position" ),
9070 props = restore ? props0 : props1,
9078 if ( mode === "show" ) {
9082 height: el.height(),
9084 outerHeight: el.outerHeight(),
9085 outerWidth: el.outerWidth()
9088 if ( o.mode === "toggle" && mode === "show" ) {
9089 el.from = o.to || zero;
9090 el.to = o.from || original;
9092 el.from = o.from || ( mode === "show" ? zero : original );
9093 el.to = o.to || ( mode === "hide" ? zero : original );
9096 // Set scaling factor
9099 y: el.from.height / original.height,
9100 x: el.from.width / original.width
9103 y: el.to.height / original.height,
9104 x: el.to.width / original.width
9108 // Scale the css box
9109 if ( scale === "box" || scale === "both" ) {
9111 // Vertical props scaling
9112 if ( factor.from.y !== factor.to.y ) {
9113 props = props.concat( vProps );
9114 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
9115 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
9118 // Horizontal props scaling
9119 if ( factor.from.x !== factor.to.x ) {
9120 props = props.concat( hProps );
9121 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
9122 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
9126 // Scale the content
9127 if ( scale === "content" || scale === "both" ) {
9129 // Vertical props scaling
9130 if ( factor.from.y !== factor.to.y ) {
9131 props = props.concat( cProps ).concat( props2 );
9132 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
9133 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
9137 $.effects.save( el, props );
9139 $.effects.createWrapper( el );
9140 el.css( "overflow", "hidden" ).css( el.from );
9143 if (origin) { // Calculate baseline shifts
9144 baseline = $.effects.getBaseline( origin, original );
9145 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
9146 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
9147 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
9148 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
9150 el.css( el.from ); // set top & left
9153 if ( scale === "content" || scale === "both" ) { // Scale the children
9155 // Add margins/font-size
9156 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
9157 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
9158 props2 = props0.concat(vProps).concat(hProps);
9160 el.find( "*[width]" ).each( function(){
9161 var child = $( this ),
9163 height: child.height(),
9164 width: child.width(),
9165 outerHeight: child.outerHeight(),
9166 outerWidth: child.outerWidth()
9169 $.effects.save(child, props2);
9173 height: c_original.height * factor.from.y,
9174 width: c_original.width * factor.from.x,
9175 outerHeight: c_original.outerHeight * factor.from.y,
9176 outerWidth: c_original.outerWidth * factor.from.x
9179 height: c_original.height * factor.to.y,
9180 width: c_original.width * factor.to.x,
9181 outerHeight: c_original.height * factor.to.y,
9182 outerWidth: c_original.width * factor.to.x
9185 // Vertical props scaling
9186 if ( factor.from.y !== factor.to.y ) {
9187 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
9188 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
9191 // Horizontal props scaling
9192 if ( factor.from.x !== factor.to.x ) {
9193 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
9194 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
9198 child.css( child.from );
9199 child.animate( child.to, o.duration, o.easing, function() {
9203 $.effects.restore( child, props2 );
9210 el.animate( el.to, {
9212 duration: o.duration,
9214 complete: function() {
9215 if ( el.to.opacity === 0 ) {
9216 el.css( "opacity", el.from.opacity );
9218 if( mode === "hide" ) {
9221 $.effects.restore( el, props );
9224 // we need to calculate our new positioning based on the scaling
9225 if ( position === "static" ) {
9227 position: "relative",
9232 $.each([ "top", "left" ], function( idx, pos ) {
9233 el.css( pos, function( _, str ) {
9234 var val = parseInt( str, 10 ),
9235 toRef = idx ? el.to.left : el.to.top;
9237 // if original was "auto", recalculate the new value from wrapper
9238 if ( str === "auto" ) {
9239 return toRef + "px";
9242 return val + toRef + "px";
9248 $.effects.removeWrapper( el );
9256 (function( $, undefined ) {
9258 $.effects.effect.shake = function( o, done ) {
9261 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
9262 mode = $.effects.setMode( el, o.mode || "effect" ),
9263 direction = o.direction || "left",
9264 distance = o.distance || 20,
9265 times = o.times || 3,
9266 anims = times * 2 + 1,
9267 speed = Math.round(o.duration/anims),
9268 ref = (direction === "up" || direction === "down") ? "top" : "left",
9269 positiveMotion = (direction === "up" || direction === "left"),
9275 // we will need to re-assemble the queue to stack our animations in place
9277 queuelen = queue.length;
9279 $.effects.save( el, props );
9281 $.effects.createWrapper( el );
9284 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
9285 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
9286 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
9289 el.animate( animation, speed, o.easing );
9292 for ( i = 1; i < times; i++ ) {
9293 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
9296 .animate( animation1, speed, o.easing )
9297 .animate( animation, speed / 2, o.easing )
9299 if ( mode === "hide" ) {
9302 $.effects.restore( el, props );
9303 $.effects.removeWrapper( el );
9307 // inject all the animations we just queued to be first in line (after "inprogress")
9308 if ( queuelen > 1) {
9309 queue.splice.apply( queue,
9310 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
9317 (function( $, undefined ) {
9319 $.effects.effect.slide = function( o, done ) {
9323 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
9324 mode = $.effects.setMode( el, o.mode || "show" ),
9325 show = mode === "show",
9326 direction = o.direction || "left",
9327 ref = (direction === "up" || direction === "down") ? "top" : "left",
9328 positiveMotion = (direction === "up" || direction === "left"),
9333 $.effects.save( el, props );
9335 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
9337 $.effects.createWrapper( el ).css({
9342 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
9346 animation[ ref ] = ( show ?
9347 ( positiveMotion ? "+=" : "-=") :
9348 ( positiveMotion ? "-=" : "+=")) +
9352 el.animate( animation, {
9354 duration: o.duration,
9356 complete: function() {
9357 if ( mode === "hide" ) {
9360 $.effects.restore( el, props );
9361 $.effects.removeWrapper( el );
9368 (function( $, undefined ) {
9370 $.effects.effect.transfer = function( o, done ) {
9371 var elem = $( this ),
9373 targetFixed = target.css( "position" ) === "fixed",
9375 fixTop = targetFixed ? body.scrollTop() : 0,
9376 fixLeft = targetFixed ? body.scrollLeft() : 0,
9377 endPosition = target.offset(),
9379 top: endPosition.top - fixTop ,
9380 left: endPosition.left - fixLeft ,
9381 height: target.innerHeight(),
9382 width: target.innerWidth()
9384 startPosition = elem.offset(),
9385 transfer = $( "<div class='ui-effects-transfer'></div>" )
9386 .appendTo( document.body )
9387 .addClass( o.className )
9389 top: startPosition.top - fixTop ,
9390 left: startPosition.left - fixLeft ,
9391 height: elem.innerHeight(),
9392 width: elem.innerWidth(),
9393 position: targetFixed ? "fixed" : "absolute"
9395 .animate( animation, o.duration, o.easing, function() {
9402 (function( $, undefined ) {
9404 $.widget( "ui.menu", {
9406 defaultElement: "<ul>",
9410 submenu: "ui-icon-carat-1-e"
9425 _create: function() {
9426 this.activeMenu = this.element;
9427 // flag used to prevent firing of the click handler
9428 // as the event bubbles up through nested menus
9429 this.mouseHandled = false;
9432 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9433 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
9435 role: this.options.role,
9438 // need to catch all clicks on disabled menu
9439 // not possible through _on
9440 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
9441 if ( this.options.disabled ) {
9442 event.preventDefault();
9446 if ( this.options.disabled ) {
9448 .addClass( "ui-state-disabled" )
9449 .attr( "aria-disabled", "true" );
9453 // Prevent focus from sticking to links inside menu after clicking
9454 // them (focus should always stay on UL during navigation).
9455 "mousedown .ui-menu-item > a": function( event ) {
9456 event.preventDefault();
9458 "click .ui-state-disabled > a": function( event ) {
9459 event.preventDefault();
9461 "click .ui-menu-item:has(a)": function( event ) {
9462 var target = $( event.target ).closest( ".ui-menu-item" );
9463 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
9464 this.select( event );
9466 // Only set the mouseHandled flag if the event will bubble, see #9469.
9467 if ( !event.isPropagationStopped() ) {
9468 this.mouseHandled = true;
9471 // Open submenu on click
9472 if ( target.has( ".ui-menu" ).length ) {
9473 this.expand( event );
9474 } else if ( !this.element.is( ":focus" ) && $( this.document[ 0 ].activeElement ).closest( ".ui-menu" ).length ) {
9476 // Redirect focus to the menu
9477 this.element.trigger( "focus", [ true ] );
9479 // If the active item is on the top level, let it stay active.
9480 // Otherwise, blur the active item since it is no longer visible.
9481 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
9482 clearTimeout( this.timer );
9487 "mouseenter .ui-menu-item": function( event ) {
9488 var target = $( event.currentTarget );
9489 // Remove ui-state-active class from siblings of the newly focused menu item
9490 // to avoid a jump caused by adjacent elements both having a class with a border
9491 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
9492 this.focus( event, target );
9494 mouseleave: "collapseAll",
9495 "mouseleave .ui-menu": "collapseAll",
9496 focus: function( event, keepActiveItem ) {
9497 // If there's already an active item, keep it active
9498 // If not, activate the first item
9499 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
9501 if ( !keepActiveItem ) {
9502 this.focus( event, item );
9505 blur: function( event ) {
9506 this._delay(function() {
9507 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
9508 this.collapseAll( event );
9517 // Clicks outside of a menu collapse any open menus
9518 this._on( this.document, {
9519 click: function( event ) {
9520 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
9521 this.collapseAll( event );
9524 // Reset the mouseHandled flag
9525 this.mouseHandled = false;
9530 _destroy: function() {
9531 // Destroy (sub)menus
9533 .removeAttr( "aria-activedescendant" )
9534 .find( ".ui-menu" ).addBack()
9535 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
9536 .removeAttr( "role" )
9537 .removeAttr( "tabIndex" )
9538 .removeAttr( "aria-labelledby" )
9539 .removeAttr( "aria-expanded" )
9540 .removeAttr( "aria-hidden" )
9541 .removeAttr( "aria-disabled" )
9545 // Destroy menu items
9546 this.element.find( ".ui-menu-item" )
9547 .removeClass( "ui-menu-item" )
9548 .removeAttr( "role" )
9549 .removeAttr( "aria-disabled" )
9552 .removeClass( "ui-corner-all ui-state-hover" )
9553 .removeAttr( "tabIndex" )
9554 .removeAttr( "role" )
9555 .removeAttr( "aria-haspopup" )
9556 .children().each( function() {
9557 var elem = $( this );
9558 if ( elem.data( "ui-menu-submenu-carat" ) ) {
9563 // Destroy menu dividers
9564 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
9567 _keydown: function( event ) {
9568 var match, prev, character, skip, regex,
9569 preventDefault = true;
9571 function escape( value ) {
9572 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
9575 switch ( event.keyCode ) {
9576 case $.ui.keyCode.PAGE_UP:
9577 this.previousPage( event );
9579 case $.ui.keyCode.PAGE_DOWN:
9580 this.nextPage( event );
9582 case $.ui.keyCode.HOME:
9583 this._move( "first", "first", event );
9585 case $.ui.keyCode.END:
9586 this._move( "last", "last", event );
9588 case $.ui.keyCode.UP:
9589 this.previous( event );
9591 case $.ui.keyCode.DOWN:
9594 case $.ui.keyCode.LEFT:
9595 this.collapse( event );
9597 case $.ui.keyCode.RIGHT:
9598 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
9599 this.expand( event );
9602 case $.ui.keyCode.ENTER:
9603 case $.ui.keyCode.SPACE:
9604 this._activate( event );
9606 case $.ui.keyCode.ESCAPE:
9607 this.collapse( event );
9610 preventDefault = false;
9611 prev = this.previousFilter || "";
9612 character = String.fromCharCode( event.keyCode );
9615 clearTimeout( this.filterTimer );
9617 if ( character === prev ) {
9620 character = prev + character;
9623 regex = new RegExp( "^" + escape( character ), "i" );
9624 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9625 return regex.test( $( this ).children( "a" ).text() );
9627 match = skip && match.index( this.active.next() ) !== -1 ?
9628 this.active.nextAll( ".ui-menu-item" ) :
9631 // If no matches on the current filter, reset to the last character pressed
9632 // to move down the menu to the first item that starts with that character
9633 if ( !match.length ) {
9634 character = String.fromCharCode( event.keyCode );
9635 regex = new RegExp( "^" + escape( character ), "i" );
9636 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9637 return regex.test( $( this ).children( "a" ).text() );
9641 if ( match.length ) {
9642 this.focus( event, match );
9643 if ( match.length > 1 ) {
9644 this.previousFilter = character;
9645 this.filterTimer = this._delay(function() {
9646 delete this.previousFilter;
9649 delete this.previousFilter;
9652 delete this.previousFilter;
9656 if ( preventDefault ) {
9657 event.preventDefault();
9661 _activate: function( event ) {
9662 if ( !this.active.is( ".ui-state-disabled" ) ) {
9663 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
9664 this.expand( event );
9666 this.select( event );
9671 refresh: function() {
9673 icon = this.options.icons.submenu,
9674 submenus = this.element.find( this.options.menus );
9676 this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length );
9678 // Initialize nested menus
9679 submenus.filter( ":not(.ui-menu)" )
9680 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9683 role: this.options.role,
9684 "aria-hidden": "true",
9685 "aria-expanded": "false"
9688 var menu = $( this ),
9689 item = menu.prev( "a" ),
9690 submenuCarat = $( "<span>" )
9691 .addClass( "ui-menu-icon ui-icon " + icon )
9692 .data( "ui-menu-submenu-carat", true );
9695 .attr( "aria-haspopup", "true" )
9696 .prepend( submenuCarat );
9697 menu.attr( "aria-labelledby", item.attr( "id" ) );
9700 menus = submenus.add( this.element );
9702 // Don't refresh list items that are already adapted
9703 menus.children( ":not(.ui-menu-item):has(a)" )
9704 .addClass( "ui-menu-item" )
9705 .attr( "role", "presentation" )
9708 .addClass( "ui-corner-all" )
9711 role: this._itemRole()
9714 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
9715 menus.children( ":not(.ui-menu-item)" ).each(function() {
9716 var item = $( this );
9717 // hyphen, em dash, en dash
9718 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
9719 item.addClass( "ui-widget-content ui-menu-divider" );
9723 // Add aria-disabled attribute to any disabled menu item
9724 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
9726 // If the active item has been removed, blur the menu
9727 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
9732 _itemRole: function() {
9736 }[ this.options.role ];
9739 _setOption: function( key, value ) {
9740 if ( key === "icons" ) {
9741 this.element.find( ".ui-menu-icon" )
9742 .removeClass( this.options.icons.submenu )
9743 .addClass( value.submenu );
9745 this._super( key, value );
9748 focus: function( event, item ) {
9749 var nested, focused;
9750 this.blur( event, event && event.type === "focus" );
9752 this._scrollIntoView( item );
9754 this.active = item.first();
9755 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
9756 // Only update aria-activedescendant if there's a role
9757 // otherwise we assume focus is managed elsewhere
9758 if ( this.options.role ) {
9759 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
9762 // Highlight active parent menu item, if any
9765 .closest( ".ui-menu-item" )
9766 .children( "a:first" )
9767 .addClass( "ui-state-active" );
9769 if ( event && event.type === "keydown" ) {
9772 this.timer = this._delay(function() {
9777 nested = item.children( ".ui-menu" );
9778 if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
9779 this._startOpening(nested);
9781 this.activeMenu = item.parent();
9783 this._trigger( "focus", event, { item: item } );
9786 _scrollIntoView: function( item ) {
9787 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
9788 if ( this._hasScroll() ) {
9789 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
9790 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
9791 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
9792 scroll = this.activeMenu.scrollTop();
9793 elementHeight = this.activeMenu.height();
9794 itemHeight = item.height();
9797 this.activeMenu.scrollTop( scroll + offset );
9798 } else if ( offset + itemHeight > elementHeight ) {
9799 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
9804 blur: function( event, fromFocus ) {
9806 clearTimeout( this.timer );
9809 if ( !this.active ) {
9813 this.active.children( "a" ).removeClass( "ui-state-focus" );
9816 this._trigger( "blur", event, { item: this.active } );
9819 _startOpening: function( submenu ) {
9820 clearTimeout( this.timer );
9822 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
9823 // shift in the submenu position when mousing over the carat icon
9824 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
9828 this.timer = this._delay(function() {
9830 this._open( submenu );
9834 _open: function( submenu ) {
9835 var position = $.extend({
9837 }, this.options.position );
9839 clearTimeout( this.timer );
9840 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
9842 .attr( "aria-hidden", "true" );
9846 .removeAttr( "aria-hidden" )
9847 .attr( "aria-expanded", "true" )
9848 .position( position );
9851 collapseAll: function( event, all ) {
9852 clearTimeout( this.timer );
9853 this.timer = this._delay(function() {
9854 // If we were passed an event, look for the submenu that contains the event
9855 var currentMenu = all ? this.element :
9856 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
9858 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
9859 if ( !currentMenu.length ) {
9860 currentMenu = this.element;
9863 this._close( currentMenu );
9866 this.activeMenu = currentMenu;
9870 // With no arguments, closes the currently active menu - if nothing is active
9871 // it closes all menus. If passed an argument, it will search for menus BELOW
9872 _close: function( startMenu ) {
9874 startMenu = this.active ? this.active.parent() : this.element;
9880 .attr( "aria-hidden", "true" )
9881 .attr( "aria-expanded", "false" )
9883 .find( "a.ui-state-active" )
9884 .removeClass( "ui-state-active" );
9887 collapse: function( event ) {
9888 var newItem = this.active &&
9889 this.active.parent().closest( ".ui-menu-item", this.element );
9890 if ( newItem && newItem.length ) {
9892 this.focus( event, newItem );
9896 expand: function( event ) {
9897 var newItem = this.active &&
9899 .children( ".ui-menu " )
9900 .children( ".ui-menu-item" )
9903 if ( newItem && newItem.length ) {
9904 this._open( newItem.parent() );
9906 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
9907 this._delay(function() {
9908 this.focus( event, newItem );
9913 next: function( event ) {
9914 this._move( "next", "first", event );
9917 previous: function( event ) {
9918 this._move( "prev", "last", event );
9921 isFirstItem: function() {
9922 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
9925 isLastItem: function() {
9926 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
9929 _move: function( direction, filter, event ) {
9931 if ( this.active ) {
9932 if ( direction === "first" || direction === "last" ) {
9934 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
9938 [ direction + "All" ]( ".ui-menu-item" )
9942 if ( !next || !next.length || !this.active ) {
9943 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
9946 this.focus( event, next );
9949 nextPage: function( event ) {
9950 var item, base, height;
9952 if ( !this.active ) {
9956 if ( this.isLastItem() ) {
9959 if ( this._hasScroll() ) {
9960 base = this.active.offset().top;
9961 height = this.element.height();
9962 this.active.nextAll( ".ui-menu-item" ).each(function() {
9964 return item.offset().top - base - height < 0;
9967 this.focus( event, item );
9969 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
9970 [ !this.active ? "first" : "last" ]() );
9974 previousPage: function( event ) {
9975 var item, base, height;
9976 if ( !this.active ) {
9980 if ( this.isFirstItem() ) {
9983 if ( this._hasScroll() ) {
9984 base = this.active.offset().top;
9985 height = this.element.height();
9986 this.active.prevAll( ".ui-menu-item" ).each(function() {
9988 return item.offset().top - base + height > 0;
9991 this.focus( event, item );
9993 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
9997 _hasScroll: function() {
9998 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
10001 select: function( event ) {
10002 // TODO: It should never be possible to not have an active item at this
10003 // point, but the tests don't trigger mouseenter before click.
10004 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
10005 var ui = { item: this.active };
10006 if ( !this.active.has( ".ui-menu" ).length ) {
10007 this.collapseAll( event, true );
10009 this._trigger( "select", event, ui );
10014 (function( $, undefined ) {
10016 $.widget( "ui.progressbar", {
10028 _create: function() {
10029 // Constrain initial value
10030 this.oldValue = this.options.value = this._constrainedValue();
10033 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10035 // Only set static values, aria-valuenow and aria-valuemax are
10036 // set inside _refreshValue()
10037 role: "progressbar",
10038 "aria-valuemin": this.min
10041 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
10042 .appendTo( this.element );
10044 this._refreshValue();
10047 _destroy: function() {
10049 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
10050 .removeAttr( "role" )
10051 .removeAttr( "aria-valuemin" )
10052 .removeAttr( "aria-valuemax" )
10053 .removeAttr( "aria-valuenow" );
10055 this.valueDiv.remove();
10058 value: function( newValue ) {
10059 if ( newValue === undefined ) {
10060 return this.options.value;
10063 this.options.value = this._constrainedValue( newValue );
10064 this._refreshValue();
10067 _constrainedValue: function( newValue ) {
10068 if ( newValue === undefined ) {
10069 newValue = this.options.value;
10072 this.indeterminate = newValue === false;
10075 if ( typeof newValue !== "number" ) {
10079 return this.indeterminate ? false :
10080 Math.min( this.options.max, Math.max( this.min, newValue ) );
10083 _setOptions: function( options ) {
10084 // Ensure "value" option is set after other values (like max)
10085 var value = options.value;
10086 delete options.value;
10088 this._super( options );
10090 this.options.value = this._constrainedValue( value );
10091 this._refreshValue();
10094 _setOption: function( key, value ) {
10095 if ( key === "max" ) {
10096 // Don't allow a max less than min
10097 value = Math.max( this.min, value );
10100 this._super( key, value );
10103 _percentage: function() {
10104 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
10107 _refreshValue: function() {
10108 var value = this.options.value,
10109 percentage = this._percentage();
10112 .toggle( this.indeterminate || value > this.min )
10113 .toggleClass( "ui-corner-right", value === this.options.max )
10114 .width( percentage.toFixed(0) + "%" );
10116 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
10118 if ( this.indeterminate ) {
10119 this.element.removeAttr( "aria-valuenow" );
10120 if ( !this.overlayDiv ) {
10121 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
10124 this.element.attr({
10125 "aria-valuemax": this.options.max,
10126 "aria-valuenow": value
10128 if ( this.overlayDiv ) {
10129 this.overlayDiv.remove();
10130 this.overlayDiv = null;
10134 if ( this.oldValue !== value ) {
10135 this.oldValue = value;
10136 this._trigger( "change" );
10138 if ( value === this.options.max ) {
10139 this._trigger( "complete" );
10145 (function( $, undefined ) {
10148 return parseInt(v, 10) || 0;
10151 function isNumber(value) {
10152 return !isNaN(parseInt(value, 10));
10155 $.widget("ui.resizable", $.ui.mouse, {
10157 widgetEventPrefix: "resize",
10161 animateDuration: "slow",
10162 animateEasing: "swing",
10163 aspectRatio: false,
10165 containment: false,
10182 _create: function() {
10184 var n, i, handle, axis, hname,
10187 this.element.addClass("ui-resizable");
10190 _aspectRatio: !!(o.aspectRatio),
10191 aspectRatio: o.aspectRatio,
10192 originalElement: this.element,
10193 _proportionallyResizeElements: [],
10194 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
10197 //Wrap the element if it cannot hold child nodes
10198 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
10200 //Create a wrapper element and set the wrapper to the new current internal element
10202 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
10203 position: this.element.css("position"),
10204 width: this.element.outerWidth(),
10205 height: this.element.outerHeight(),
10206 top: this.element.css("top"),
10207 left: this.element.css("left")
10211 //Overwrite the original this.element
10212 this.element = this.element.parent().data(
10213 "ui-resizable", this.element.data("ui-resizable")
10216 this.elementIsWrapper = true;
10218 //Move margins to the wrapper
10219 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
10220 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
10222 //Prevent Safari textarea resize
10223 this.originalResizeStyle = this.originalElement.css("resize");
10224 this.originalElement.css("resize", "none");
10226 //Push the actual element to our proportionallyResize internal array
10227 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
10229 // avoid IE jump (hard set the margin)
10230 this.originalElement.css({ margin: this.originalElement.css("margin") });
10232 // fix handlers offset
10233 this._proportionallyResize();
10237 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" });
10238 if(this.handles.constructor === String) {
10240 if ( this.handles === "all") {
10241 this.handles = "n,e,s,w,se,sw,ne,nw";
10244 n = this.handles.split(",");
10247 for(i = 0; i < n.length; i++) {
10249 handle = $.trim(n[i]);
10250 hname = "ui-resizable-"+handle;
10251 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
10253 // Apply zIndex to all handles - see #7960
10254 axis.css({ zIndex: o.zIndex });
10256 //TODO : What's going on here?
10257 if ("se" === handle) {
10258 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
10261 //Insert into internal handles object and append to element
10262 this.handles[handle] = ".ui-resizable-"+handle;
10263 this.element.append(axis);
10268 this._renderAxis = function(target) {
10270 var i, axis, padPos, padWrapper;
10272 target = target || this.element;
10274 for(i in this.handles) {
10276 if(this.handles[i].constructor === String) {
10277 this.handles[i] = $(this.handles[i], this.element).show();
10280 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
10281 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
10283 axis = $(this.handles[i], this.element);
10285 //Checking the correct pad and border
10286 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
10288 //The padding type i have to apply...
10289 padPos = [ "padding",
10290 /ne|nw|n/.test(i) ? "Top" :
10291 /se|sw|s/.test(i) ? "Bottom" :
10292 /^e$/.test(i) ? "Right" : "Left" ].join("");
10294 target.css(padPos, padWrapper);
10296 this._proportionallyResize();
10300 //TODO: What's that good for? There's not anything to be executed left
10301 if(!$(this.handles[i]).length) {
10307 //TODO: make renderAxis a prototype function
10308 this._renderAxis(this.element);
10310 this._handles = $(".ui-resizable-handle", this.element)
10311 .disableSelection();
10313 //Matching axis name
10314 this._handles.mouseover(function() {
10315 if (!that.resizing) {
10316 if (this.className) {
10317 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
10319 //Axis, default = se
10320 that.axis = axis && axis[1] ? axis[1] : "se";
10324 //If we want to auto hide the elements
10326 this._handles.hide();
10328 .addClass("ui-resizable-autohide")
10329 .mouseenter(function() {
10333 $(this).removeClass("ui-resizable-autohide");
10334 that._handles.show();
10336 .mouseleave(function(){
10340 if (!that.resizing) {
10341 $(this).addClass("ui-resizable-autohide");
10342 that._handles.hide();
10347 //Initialize the mouse interaction
10352 _destroy: function() {
10354 this._mouseDestroy();
10357 _destroy = function(exp) {
10358 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
10359 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
10362 //TODO: Unwrap at same DOM position
10363 if (this.elementIsWrapper) {
10364 _destroy(this.element);
10365 wrapper = this.element;
10366 this.originalElement.css({
10367 position: wrapper.css("position"),
10368 width: wrapper.outerWidth(),
10369 height: wrapper.outerHeight(),
10370 top: wrapper.css("top"),
10371 left: wrapper.css("left")
10372 }).insertAfter( wrapper );
10376 this.originalElement.css("resize", this.originalResizeStyle);
10377 _destroy(this.originalElement);
10382 _mouseCapture: function(event) {
10386 for (i in this.handles) {
10387 handle = $(this.handles[i])[0];
10388 if (handle === event.target || $.contains(handle, event.target)) {
10393 return !this.options.disabled && capture;
10396 _mouseStart: function(event) {
10398 var curleft, curtop, cursor,
10400 iniPos = this.element.position(),
10403 this.resizing = true;
10405 // bugfix for http://dev.jquery.com/ticket/1749
10406 if ( (/absolute/).test( el.css("position") ) ) {
10407 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
10408 } else if (el.is(".ui-draggable")) {
10409 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
10412 this._renderProxy();
10414 curleft = num(this.helper.css("left"));
10415 curtop = num(this.helper.css("top"));
10417 if (o.containment) {
10418 curleft += $(o.containment).scrollLeft() || 0;
10419 curtop += $(o.containment).scrollTop() || 0;
10422 //Store needed variables
10423 this.offset = this.helper.offset();
10424 this.position = { left: curleft, top: curtop };
10425 this.size = this._helper ? { width: this.helper.width(), height: this.helper.height() } : { width: el.width(), height: el.height() };
10426 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
10427 this.originalPosition = { left: curleft, top: curtop };
10428 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
10429 this.originalMousePosition = { left: event.pageX, top: event.pageY };
10432 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
10434 cursor = $(".ui-resizable-" + this.axis).css("cursor");
10435 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
10437 el.addClass("ui-resizable-resizing");
10438 this._propagate("start", event);
10442 _mouseDrag: function(event) {
10444 //Increase performance, avoid regex
10446 el = this.helper, props = {},
10447 smp = this.originalMousePosition,
10449 prevTop = this.position.top,
10450 prevLeft = this.position.left,
10451 prevWidth = this.size.width,
10452 prevHeight = this.size.height,
10453 dx = (event.pageX-smp.left)||0,
10454 dy = (event.pageY-smp.top)||0,
10455 trigger = this._change[a];
10461 // Calculate the attrs that will be change
10462 data = trigger.apply(this, [event, dx, dy]);
10464 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
10465 this._updateVirtualBoundaries(event.shiftKey);
10466 if (this._aspectRatio || event.shiftKey) {
10467 data = this._updateRatio(data, event);
10470 data = this._respectSize(data, event);
10472 this._updateCache(data);
10474 // plugins callbacks need to be called first
10475 this._propagate("resize", event);
10477 if (this.position.top !== prevTop) {
10478 props.top = this.position.top + "px";
10480 if (this.position.left !== prevLeft) {
10481 props.left = this.position.left + "px";
10483 if (this.size.width !== prevWidth) {
10484 props.width = this.size.width + "px";
10486 if (this.size.height !== prevHeight) {
10487 props.height = this.size.height + "px";
10491 if (!this._helper && this._proportionallyResizeElements.length) {
10492 this._proportionallyResize();
10495 // Call the user callback if the element was resized
10496 if ( ! $.isEmptyObject(props) ) {
10497 this._trigger("resize", event, this.ui());
10503 _mouseStop: function(event) {
10505 this.resizing = false;
10506 var pr, ista, soffseth, soffsetw, s, left, top,
10507 o = this.options, that = this;
10511 pr = this._proportionallyResizeElements;
10512 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
10513 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
10514 soffsetw = ista ? 0 : that.sizeDiff.width;
10516 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
10517 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
10518 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
10521 this.element.css($.extend(s, { top: top, left: left }));
10524 that.helper.height(that.size.height);
10525 that.helper.width(that.size.width);
10527 if (this._helper && !o.animate) {
10528 this._proportionallyResize();
10532 $("body").css("cursor", "auto");
10534 this.element.removeClass("ui-resizable-resizing");
10536 this._propagate("stop", event);
10538 if (this._helper) {
10539 this.helper.remove();
10546 _updateVirtualBoundaries: function(forceAspectRatio) {
10547 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
10551 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
10552 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
10553 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
10554 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
10557 if(this._aspectRatio || forceAspectRatio) {
10558 // We want to create an enclosing box whose aspect ration is the requested one
10559 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
10560 pMinWidth = b.minHeight * this.aspectRatio;
10561 pMinHeight = b.minWidth / this.aspectRatio;
10562 pMaxWidth = b.maxHeight * this.aspectRatio;
10563 pMaxHeight = b.maxWidth / this.aspectRatio;
10565 if(pMinWidth > b.minWidth) {
10566 b.minWidth = pMinWidth;
10568 if(pMinHeight > b.minHeight) {
10569 b.minHeight = pMinHeight;
10571 if(pMaxWidth < b.maxWidth) {
10572 b.maxWidth = pMaxWidth;
10574 if(pMaxHeight < b.maxHeight) {
10575 b.maxHeight = pMaxHeight;
10578 this._vBoundaries = b;
10581 _updateCache: function(data) {
10582 this.offset = this.helper.offset();
10583 if (isNumber(data.left)) {
10584 this.position.left = data.left;
10586 if (isNumber(data.top)) {
10587 this.position.top = data.top;
10589 if (isNumber(data.height)) {
10590 this.size.height = data.height;
10592 if (isNumber(data.width)) {
10593 this.size.width = data.width;
10597 _updateRatio: function( data ) {
10599 var cpos = this.position,
10603 if (isNumber(data.height)) {
10604 data.width = (data.height * this.aspectRatio);
10605 } else if (isNumber(data.width)) {
10606 data.height = (data.width / this.aspectRatio);
10610 data.left = cpos.left + (csize.width - data.width);
10614 data.top = cpos.top + (csize.height - data.height);
10615 data.left = cpos.left + (csize.width - data.width);
10621 _respectSize: function( data ) {
10623 var o = this._vBoundaries,
10625 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
10626 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
10627 dw = this.originalPosition.left + this.originalSize.width,
10628 dh = this.position.top + this.size.height,
10629 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
10631 data.width = o.minWidth;
10634 data.height = o.minHeight;
10637 data.width = o.maxWidth;
10640 data.height = o.maxHeight;
10643 if (isminw && cw) {
10644 data.left = dw - o.minWidth;
10646 if (ismaxw && cw) {
10647 data.left = dw - o.maxWidth;
10649 if (isminh && ch) {
10650 data.top = dh - o.minHeight;
10652 if (ismaxh && ch) {
10653 data.top = dh - o.maxHeight;
10656 // fixing jump error on top/left - bug #2330
10657 if (!data.width && !data.height && !data.left && data.top) {
10659 } else if (!data.width && !data.height && !data.top && data.left) {
10666 _proportionallyResize: function() {
10668 if (!this._proportionallyResizeElements.length) {
10672 var i, j, borders, paddings, prel,
10673 element = this.helper || this.element;
10675 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
10677 prel = this._proportionallyResizeElements[i];
10679 if (!this.borderDif) {
10680 this.borderDif = [];
10681 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
10682 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
10684 for ( j = 0; j < borders.length; j++ ) {
10685 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
10690 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
10691 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
10698 _renderProxy: function() {
10700 var el = this.element, o = this.options;
10701 this.elementOffset = el.offset();
10705 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
10707 this.helper.addClass(this._helper).css({
10708 width: this.element.outerWidth() - 1,
10709 height: this.element.outerHeight() - 1,
10710 position: "absolute",
10711 left: this.elementOffset.left +"px",
10712 top: this.elementOffset.top +"px",
10713 zIndex: ++o.zIndex //TODO: Don't modify option
10718 .disableSelection();
10721 this.helper = this.element;
10727 e: function(event, dx) {
10728 return { width: this.originalSize.width + dx };
10730 w: function(event, dx) {
10731 var cs = this.originalSize, sp = this.originalPosition;
10732 return { left: sp.left + dx, width: cs.width - dx };
10734 n: function(event, dx, dy) {
10735 var cs = this.originalSize, sp = this.originalPosition;
10736 return { top: sp.top + dy, height: cs.height - dy };
10738 s: function(event, dx, dy) {
10739 return { height: this.originalSize.height + dy };
10741 se: function(event, dx, dy) {
10742 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
10744 sw: function(event, dx, dy) {
10745 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
10747 ne: function(event, dx, dy) {
10748 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
10750 nw: function(event, dx, dy) {
10751 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
10755 _propagate: function(n, event) {
10756 $.ui.plugin.call(this, n, [event, this.ui()]);
10757 (n !== "resize" && this._trigger(n, event, this.ui()));
10764 originalElement: this.originalElement,
10765 element: this.element,
10766 helper: this.helper,
10767 position: this.position,
10769 originalSize: this.originalSize,
10770 originalPosition: this.originalPosition
10777 * Resizable Extensions
10780 $.ui.plugin.add("resizable", "animate", {
10782 stop: function( event ) {
10783 var that = $(this).data("ui-resizable"),
10785 pr = that._proportionallyResizeElements,
10786 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
10787 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
10788 soffsetw = ista ? 0 : that.sizeDiff.width,
10789 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
10790 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
10791 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
10793 that.element.animate(
10794 $.extend(style, top && left ? { top: top, left: left } : {}), {
10795 duration: o.animateDuration,
10796 easing: o.animateEasing,
10800 width: parseInt(that.element.css("width"), 10),
10801 height: parseInt(that.element.css("height"), 10),
10802 top: parseInt(that.element.css("top"), 10),
10803 left: parseInt(that.element.css("left"), 10)
10806 if (pr && pr.length) {
10807 $(pr[0]).css({ width: data.width, height: data.height });
10810 // propagating resize, and updating values for each animation step
10811 that._updateCache(data);
10812 that._propagate("resize", event);
10821 $.ui.plugin.add("resizable", "containment", {
10823 start: function() {
10824 var element, p, co, ch, cw, width, height,
10825 that = $(this).data("ui-resizable"),
10828 oc = o.containment,
10829 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
10835 that.containerElement = $(ce);
10837 if (/document/.test(oc) || oc === document) {
10838 that.containerOffset = { left: 0, top: 0 };
10839 that.containerPosition = { left: 0, top: 0 };
10841 that.parentData = {
10842 element: $(document), left: 0, top: 0,
10843 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
10847 // i'm a node, so compute top, left, right, bottom
10851 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
10853 that.containerOffset = element.offset();
10854 that.containerPosition = element.position();
10855 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
10857 co = that.containerOffset;
10858 ch = that.containerSize.height;
10859 cw = that.containerSize.width;
10860 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
10861 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
10863 that.parentData = {
10864 element: ce, left: co.left, top: co.top, width: width, height: height
10869 resize: function( event ) {
10870 var woset, hoset, isParent, isOffsetRelative,
10871 that = $(this).data("ui-resizable"),
10873 co = that.containerOffset, cp = that.position,
10874 pRatio = that._aspectRatio || event.shiftKey,
10875 cop = { top:0, left:0 }, ce = that.containerElement;
10877 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
10881 if (cp.left < (that._helper ? co.left : 0)) {
10882 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
10884 that.size.height = that.size.width / that.aspectRatio;
10886 that.position.left = o.helper ? co.left : 0;
10889 if (cp.top < (that._helper ? co.top : 0)) {
10890 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
10892 that.size.width = that.size.height * that.aspectRatio;
10894 that.position.top = that._helper ? co.top : 0;
10897 that.offset.left = that.parentData.left+that.position.left;
10898 that.offset.top = that.parentData.top+that.position.top;
10900 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
10901 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
10903 isParent = that.containerElement.get(0) === that.element.parent().get(0);
10904 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
10906 if ( isParent && isOffsetRelative ) {
10907 woset -= Math.abs( that.parentData.left );
10910 if (woset + that.size.width >= that.parentData.width) {
10911 that.size.width = that.parentData.width - woset;
10913 that.size.height = that.size.width / that.aspectRatio;
10917 if (hoset + that.size.height >= that.parentData.height) {
10918 that.size.height = that.parentData.height - hoset;
10920 that.size.width = that.size.height * that.aspectRatio;
10926 var that = $(this).data("ui-resizable"),
10928 co = that.containerOffset,
10929 cop = that.containerPosition,
10930 ce = that.containerElement,
10931 helper = $(that.helper),
10932 ho = helper.offset(),
10933 w = helper.outerWidth() - that.sizeDiff.width,
10934 h = helper.outerHeight() - that.sizeDiff.height;
10936 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
10937 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
10940 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
10941 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
10947 $.ui.plugin.add("resizable", "alsoResize", {
10949 start: function () {
10950 var that = $(this).data("ui-resizable"),
10952 _store = function (exp) {
10953 $(exp).each(function() {
10955 el.data("ui-resizable-alsoresize", {
10956 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
10957 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
10962 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
10963 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
10964 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
10966 _store(o.alsoResize);
10970 resize: function (event, ui) {
10971 var that = $(this).data("ui-resizable"),
10973 os = that.originalSize,
10974 op = that.originalPosition,
10976 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
10977 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
10980 _alsoResize = function (exp, c) {
10981 $(exp).each(function() {
10982 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
10983 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
10985 $.each(css, function (i, prop) {
10986 var sum = (start[prop]||0) + (delta[prop]||0);
10987 if (sum && sum >= 0) {
10988 style[prop] = sum || null;
10996 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
10997 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
10999 _alsoResize(o.alsoResize);
11003 stop: function () {
11004 $(this).removeData("resizable-alsoresize");
11008 $.ui.plugin.add("resizable", "ghost", {
11010 start: function() {
11012 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
11014 that.ghost = that.originalElement.clone();
11016 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
11017 .addClass("ui-resizable-ghost")
11018 .addClass(typeof o.ghost === "string" ? o.ghost : "");
11020 that.ghost.appendTo(that.helper);
11024 resize: function(){
11025 var that = $(this).data("ui-resizable");
11027 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
11032 var that = $(this).data("ui-resizable");
11033 if (that.ghost && that.helper) {
11034 that.helper.get(0).removeChild(that.ghost.get(0));
11040 $.ui.plugin.add("resizable", "grid", {
11042 resize: function() {
11043 var that = $(this).data("ui-resizable"),
11046 os = that.originalSize,
11047 op = that.originalPosition,
11049 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
11050 gridX = (grid[0]||1),
11051 gridY = (grid[1]||1),
11052 ox = Math.round((cs.width - os.width) / gridX) * gridX,
11053 oy = Math.round((cs.height - os.height) / gridY) * gridY,
11054 newWidth = os.width + ox,
11055 newHeight = os.height + oy,
11056 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
11057 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
11058 isMinWidth = o.minWidth && (o.minWidth > newWidth),
11059 isMinHeight = o.minHeight && (o.minHeight > newHeight);
11064 newWidth = newWidth + gridX;
11067 newHeight = newHeight + gridY;
11070 newWidth = newWidth - gridX;
11073 newHeight = newHeight - gridY;
11076 if (/^(se|s|e)$/.test(a)) {
11077 that.size.width = newWidth;
11078 that.size.height = newHeight;
11079 } else if (/^(ne)$/.test(a)) {
11080 that.size.width = newWidth;
11081 that.size.height = newHeight;
11082 that.position.top = op.top - oy;
11083 } else if (/^(sw)$/.test(a)) {
11084 that.size.width = newWidth;
11085 that.size.height = newHeight;
11086 that.position.left = op.left - ox;
11088 if ( newHeight - gridY > 0 ) {
11089 that.size.height = newHeight;
11090 that.position.top = op.top - oy;
11092 that.size.height = gridY;
11093 that.position.top = op.top + os.height - gridY;
11095 if ( newWidth - gridX > 0 ) {
11096 that.size.width = newWidth;
11097 that.position.left = op.left - ox;
11099 that.size.width = gridX;
11100 that.position.left = op.left + os.width - gridX;
11108 (function( $, undefined ) {
11110 $.widget("ui.selectable", $.ui.mouse, {
11117 tolerance: "touch",
11127 _create: function() {
11131 this.element.addClass("ui-selectable");
11133 this.dragged = false;
11135 // cache selectee children based on filter
11136 this.refresh = function() {
11137 selectees = $(that.options.filter, that.element[0]);
11138 selectees.addClass("ui-selectee");
11139 selectees.each(function() {
11140 var $this = $(this),
11141 pos = $this.offset();
11142 $.data(this, "selectable-item", {
11147 right: pos.left + $this.outerWidth(),
11148 bottom: pos.top + $this.outerHeight(),
11149 startselected: false,
11150 selected: $this.hasClass("ui-selected"),
11151 selecting: $this.hasClass("ui-selecting"),
11152 unselecting: $this.hasClass("ui-unselecting")
11158 this.selectees = selectees.addClass("ui-selectee");
11162 this.helper = $("<div class='ui-selectable-helper'></div>");
11165 _destroy: function() {
11167 .removeClass("ui-selectee")
11168 .removeData("selectable-item");
11170 .removeClass("ui-selectable ui-selectable-disabled");
11171 this._mouseDestroy();
11174 _mouseStart: function(event) {
11176 options = this.options;
11178 this.opos = [event.pageX, event.pageY];
11180 if (this.options.disabled) {
11184 this.selectees = $(options.filter, this.element[0]);
11186 this._trigger("start", event);
11188 $(options.appendTo).append(this.helper);
11189 // position helper (lasso)
11191 "left": event.pageX,
11192 "top": event.pageY,
11197 if (options.autoRefresh) {
11201 this.selectees.filter(".ui-selected").each(function() {
11202 var selectee = $.data(this, "selectable-item");
11203 selectee.startselected = true;
11204 if (!event.metaKey && !event.ctrlKey) {
11205 selectee.$element.removeClass("ui-selected");
11206 selectee.selected = false;
11207 selectee.$element.addClass("ui-unselecting");
11208 selectee.unselecting = true;
11209 // selectable UNSELECTING callback
11210 that._trigger("unselecting", event, {
11211 unselecting: selectee.element
11216 $(event.target).parents().addBack().each(function() {
11218 selectee = $.data(this, "selectable-item");
11220 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
11222 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
11223 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
11224 selectee.unselecting = !doSelect;
11225 selectee.selecting = doSelect;
11226 selectee.selected = doSelect;
11227 // selectable (UN)SELECTING callback
11229 that._trigger("selecting", event, {
11230 selecting: selectee.element
11233 that._trigger("unselecting", event, {
11234 unselecting: selectee.element
11243 _mouseDrag: function(event) {
11245 this.dragged = true;
11247 if (this.options.disabled) {
11253 options = this.options,
11259 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
11260 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
11261 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
11263 this.selectees.each(function() {
11264 var selectee = $.data(this, "selectable-item"),
11267 //prevent helper from being selected if appendTo: selectable
11268 if (!selectee || selectee.element === that.element[0]) {
11272 if (options.tolerance === "touch") {
11273 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
11274 } else if (options.tolerance === "fit") {
11275 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
11280 if (selectee.selected) {
11281 selectee.$element.removeClass("ui-selected");
11282 selectee.selected = false;
11284 if (selectee.unselecting) {
11285 selectee.$element.removeClass("ui-unselecting");
11286 selectee.unselecting = false;
11288 if (!selectee.selecting) {
11289 selectee.$element.addClass("ui-selecting");
11290 selectee.selecting = true;
11291 // selectable SELECTING callback
11292 that._trigger("selecting", event, {
11293 selecting: selectee.element
11298 if (selectee.selecting) {
11299 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
11300 selectee.$element.removeClass("ui-selecting");
11301 selectee.selecting = false;
11302 selectee.$element.addClass("ui-selected");
11303 selectee.selected = true;
11305 selectee.$element.removeClass("ui-selecting");
11306 selectee.selecting = false;
11307 if (selectee.startselected) {
11308 selectee.$element.addClass("ui-unselecting");
11309 selectee.unselecting = true;
11311 // selectable UNSELECTING callback
11312 that._trigger("unselecting", event, {
11313 unselecting: selectee.element
11317 if (selectee.selected) {
11318 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
11319 selectee.$element.removeClass("ui-selected");
11320 selectee.selected = false;
11322 selectee.$element.addClass("ui-unselecting");
11323 selectee.unselecting = true;
11324 // selectable UNSELECTING callback
11325 that._trigger("unselecting", event, {
11326 unselecting: selectee.element
11336 _mouseStop: function(event) {
11339 this.dragged = false;
11341 $(".ui-unselecting", this.element[0]).each(function() {
11342 var selectee = $.data(this, "selectable-item");
11343 selectee.$element.removeClass("ui-unselecting");
11344 selectee.unselecting = false;
11345 selectee.startselected = false;
11346 that._trigger("unselected", event, {
11347 unselected: selectee.element
11350 $(".ui-selecting", this.element[0]).each(function() {
11351 var selectee = $.data(this, "selectable-item");
11352 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
11353 selectee.selecting = false;
11354 selectee.selected = true;
11355 selectee.startselected = true;
11356 that._trigger("selected", event, {
11357 selected: selectee.element
11360 this._trigger("stop", event);
11362 this.helper.remove();
11370 (function( $, undefined ) {
11372 // number of pages in a slider
11373 // (how many times can you page up/down to go through the whole range)
11376 $.widget( "ui.slider", $.ui.mouse, {
11378 widgetEventPrefix: "slide",
11385 orientation: "horizontal",
11398 _create: function() {
11399 this._keySliding = false;
11400 this._mouseSliding = false;
11401 this._animateOff = true;
11402 this._handleIndex = null;
11403 this._detectOrientation();
11407 .addClass( "ui-slider" +
11408 " ui-slider-" + this.orientation +
11410 " ui-widget-content" +
11414 this._setOption( "disabled", this.options.disabled );
11416 this._animateOff = false;
11419 _refresh: function() {
11420 this._createRange();
11421 this._createHandles();
11422 this._setupEvents();
11423 this._refreshValue();
11426 _createHandles: function() {
11427 var i, handleCount,
11428 options = this.options,
11429 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
11430 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
11433 handleCount = ( options.values && options.values.length ) || 1;
11435 if ( existingHandles.length > handleCount ) {
11436 existingHandles.slice( handleCount ).remove();
11437 existingHandles = existingHandles.slice( 0, handleCount );
11440 for ( i = existingHandles.length; i < handleCount; i++ ) {
11441 handles.push( handle );
11444 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
11446 this.handle = this.handles.eq( 0 );
11448 this.handles.each(function( i ) {
11449 $( this ).data( "ui-slider-handle-index", i );
11453 _createRange: function() {
11454 var options = this.options,
11457 if ( options.range ) {
11458 if ( options.range === true ) {
11459 if ( !options.values ) {
11460 options.values = [ this._valueMin(), this._valueMin() ];
11461 } else if ( options.values.length && options.values.length !== 2 ) {
11462 options.values = [ options.values[0], options.values[0] ];
11463 } else if ( $.isArray( options.values ) ) {
11464 options.values = options.values.slice(0);
11468 if ( !this.range || !this.range.length ) {
11469 this.range = $( "<div></div>" )
11470 .appendTo( this.element );
11472 classes = "ui-slider-range" +
11473 // note: this isn't the most fittingly semantic framework class for this element,
11474 // but worked best visually with a variety of themes
11475 " ui-widget-header ui-corner-all";
11477 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
11478 // Handle range switching from true to min/max
11485 this.range.addClass( classes +
11486 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
11488 if ( this.range ) {
11489 this.range.remove();
11495 _setupEvents: function() {
11496 var elements = this.handles.add( this.range ).filter( "a" );
11497 this._off( elements );
11498 this._on( elements, this._handleEvents );
11499 this._hoverable( elements );
11500 this._focusable( elements );
11503 _destroy: function() {
11504 this.handles.remove();
11505 if ( this.range ) {
11506 this.range.remove();
11510 .removeClass( "ui-slider" +
11511 " ui-slider-horizontal" +
11512 " ui-slider-vertical" +
11514 " ui-widget-content" +
11515 " ui-corner-all" );
11517 this._mouseDestroy();
11520 _mouseCapture: function( event ) {
11521 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
11525 if ( o.disabled ) {
11529 this.elementSize = {
11530 width: this.element.outerWidth(),
11531 height: this.element.outerHeight()
11533 this.elementOffset = this.element.offset();
11535 position = { x: event.pageX, y: event.pageY };
11536 normValue = this._normValueFromMouse( position );
11537 distance = this._valueMax() - this._valueMin() + 1;
11538 this.handles.each(function( i ) {
11539 var thisDistance = Math.abs( normValue - that.values(i) );
11540 if (( distance > thisDistance ) ||
11541 ( distance === thisDistance &&
11542 (i === that._lastChangedValue || that.values(i) === o.min ))) {
11543 distance = thisDistance;
11544 closestHandle = $( this );
11549 allowed = this._start( event, index );
11550 if ( allowed === false ) {
11553 this._mouseSliding = true;
11555 this._handleIndex = index;
11558 .addClass( "ui-state-active" )
11561 offset = closestHandle.offset();
11562 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
11563 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
11564 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
11565 top: event.pageY - offset.top -
11566 ( closestHandle.height() / 2 ) -
11567 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
11568 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
11569 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
11572 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
11573 this._slide( event, index, normValue );
11575 this._animateOff = true;
11579 _mouseStart: function() {
11583 _mouseDrag: function( event ) {
11584 var position = { x: event.pageX, y: event.pageY },
11585 normValue = this._normValueFromMouse( position );
11587 this._slide( event, this._handleIndex, normValue );
11592 _mouseStop: function( event ) {
11593 this.handles.removeClass( "ui-state-active" );
11594 this._mouseSliding = false;
11596 this._stop( event, this._handleIndex );
11597 this._change( event, this._handleIndex );
11599 this._handleIndex = null;
11600 this._clickOffset = null;
11601 this._animateOff = false;
11606 _detectOrientation: function() {
11607 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
11610 _normValueFromMouse: function( position ) {
11617 if ( this.orientation === "horizontal" ) {
11618 pixelTotal = this.elementSize.width;
11619 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
11621 pixelTotal = this.elementSize.height;
11622 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
11625 percentMouse = ( pixelMouse / pixelTotal );
11626 if ( percentMouse > 1 ) {
11629 if ( percentMouse < 0 ) {
11632 if ( this.orientation === "vertical" ) {
11633 percentMouse = 1 - percentMouse;
11636 valueTotal = this._valueMax() - this._valueMin();
11637 valueMouse = this._valueMin() + percentMouse * valueTotal;
11639 return this._trimAlignValue( valueMouse );
11642 _start: function( event, index ) {
11644 handle: this.handles[ index ],
11645 value: this.value()
11647 if ( this.options.values && this.options.values.length ) {
11648 uiHash.value = this.values( index );
11649 uiHash.values = this.values();
11651 return this._trigger( "start", event, uiHash );
11654 _slide: function( event, index, newVal ) {
11659 if ( this.options.values && this.options.values.length ) {
11660 otherVal = this.values( index ? 0 : 1 );
11662 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
11663 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
11668 if ( newVal !== this.values( index ) ) {
11669 newValues = this.values();
11670 newValues[ index ] = newVal;
11671 // A slide can be canceled by returning false from the slide callback
11672 allowed = this._trigger( "slide", event, {
11673 handle: this.handles[ index ],
11677 otherVal = this.values( index ? 0 : 1 );
11678 if ( allowed !== false ) {
11679 this.values( index, newVal );
11683 if ( newVal !== this.value() ) {
11684 // A slide can be canceled by returning false from the slide callback
11685 allowed = this._trigger( "slide", event, {
11686 handle: this.handles[ index ],
11689 if ( allowed !== false ) {
11690 this.value( newVal );
11696 _stop: function( event, index ) {
11698 handle: this.handles[ index ],
11699 value: this.value()
11701 if ( this.options.values && this.options.values.length ) {
11702 uiHash.value = this.values( index );
11703 uiHash.values = this.values();
11706 this._trigger( "stop", event, uiHash );
11709 _change: function( event, index ) {
11710 if ( !this._keySliding && !this._mouseSliding ) {
11712 handle: this.handles[ index ],
11713 value: this.value()
11715 if ( this.options.values && this.options.values.length ) {
11716 uiHash.value = this.values( index );
11717 uiHash.values = this.values();
11720 //store the last changed value index for reference when handles overlap
11721 this._lastChangedValue = index;
11723 this._trigger( "change", event, uiHash );
11727 value: function( newValue ) {
11728 if ( arguments.length ) {
11729 this.options.value = this._trimAlignValue( newValue );
11730 this._refreshValue();
11731 this._change( null, 0 );
11735 return this._value();
11738 values: function( index, newValue ) {
11743 if ( arguments.length > 1 ) {
11744 this.options.values[ index ] = this._trimAlignValue( newValue );
11745 this._refreshValue();
11746 this._change( null, index );
11750 if ( arguments.length ) {
11751 if ( $.isArray( arguments[ 0 ] ) ) {
11752 vals = this.options.values;
11753 newValues = arguments[ 0 ];
11754 for ( i = 0; i < vals.length; i += 1 ) {
11755 vals[ i ] = this._trimAlignValue( newValues[ i ] );
11756 this._change( null, i );
11758 this._refreshValue();
11760 if ( this.options.values && this.options.values.length ) {
11761 return this._values( index );
11763 return this.value();
11767 return this._values();
11771 _setOption: function( key, value ) {
11775 if ( key === "range" && this.options.range === true ) {
11776 if ( value === "min" ) {
11777 this.options.value = this._values( 0 );
11778 this.options.values = null;
11779 } else if ( value === "max" ) {
11780 this.options.value = this._values( this.options.values.length-1 );
11781 this.options.values = null;
11785 if ( $.isArray( this.options.values ) ) {
11786 valsLength = this.options.values.length;
11789 $.Widget.prototype._setOption.apply( this, arguments );
11792 case "orientation":
11793 this._detectOrientation();
11795 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
11796 .addClass( "ui-slider-" + this.orientation );
11797 this._refreshValue();
11800 this._animateOff = true;
11801 this._refreshValue();
11802 this._change( null, 0 );
11803 this._animateOff = false;
11806 this._animateOff = true;
11807 this._refreshValue();
11808 for ( i = 0; i < valsLength; i += 1 ) {
11809 this._change( null, i );
11811 this._animateOff = false;
11815 this._animateOff = true;
11816 this._refreshValue();
11817 this._animateOff = false;
11820 this._animateOff = true;
11822 this._animateOff = false;
11827 //internal value getter
11828 // _value() returns value trimmed by min and max, aligned by step
11829 _value: function() {
11830 var val = this.options.value;
11831 val = this._trimAlignValue( val );
11836 //internal values getter
11837 // _values() returns array of values trimmed by min and max, aligned by step
11838 // _values( index ) returns single value trimmed by min and max, aligned by step
11839 _values: function( index ) {
11844 if ( arguments.length ) {
11845 val = this.options.values[ index ];
11846 val = this._trimAlignValue( val );
11849 } else if ( this.options.values && this.options.values.length ) {
11850 // .slice() creates a copy of the array
11851 // this copy gets trimmed by min and max and then returned
11852 vals = this.options.values.slice();
11853 for ( i = 0; i < vals.length; i+= 1) {
11854 vals[ i ] = this._trimAlignValue( vals[ i ] );
11863 // returns the step-aligned value that val is closest to, between (inclusive) min and max
11864 _trimAlignValue: function( val ) {
11865 if ( val <= this._valueMin() ) {
11866 return this._valueMin();
11868 if ( val >= this._valueMax() ) {
11869 return this._valueMax();
11871 var step = ( this.options.step > 0 ) ? this.options.step : 1,
11872 valModStep = (val - this._valueMin()) % step,
11873 alignValue = val - valModStep;
11875 if ( Math.abs(valModStep) * 2 >= step ) {
11876 alignValue += ( valModStep > 0 ) ? step : ( -step );
11879 // Since JavaScript has problems with large floats, round
11880 // the final value to 5 digits after the decimal point (see #4124)
11881 return parseFloat( alignValue.toFixed(5) );
11884 _valueMin: function() {
11885 return this.options.min;
11888 _valueMax: function() {
11889 return this.options.max;
11892 _refreshValue: function() {
11893 var lastValPercent, valPercent, value, valueMin, valueMax,
11894 oRange = this.options.range,
11897 animate = ( !this._animateOff ) ? o.animate : false,
11900 if ( this.options.values && this.options.values.length ) {
11901 this.handles.each(function( i ) {
11902 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
11903 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
11904 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
11905 if ( that.options.range === true ) {
11906 if ( that.orientation === "horizontal" ) {
11908 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
11911 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
11915 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
11918 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
11922 lastValPercent = valPercent;
11925 value = this.value();
11926 valueMin = this._valueMin();
11927 valueMax = this._valueMax();
11928 valPercent = ( valueMax !== valueMin ) ?
11929 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
11931 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
11932 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
11934 if ( oRange === "min" && this.orientation === "horizontal" ) {
11935 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
11937 if ( oRange === "max" && this.orientation === "horizontal" ) {
11938 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
11940 if ( oRange === "min" && this.orientation === "vertical" ) {
11941 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
11943 if ( oRange === "max" && this.orientation === "vertical" ) {
11944 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
11950 keydown: function( event ) {
11951 var allowed, curVal, newVal, step,
11952 index = $( event.target ).data( "ui-slider-handle-index" );
11954 switch ( event.keyCode ) {
11955 case $.ui.keyCode.HOME:
11956 case $.ui.keyCode.END:
11957 case $.ui.keyCode.PAGE_UP:
11958 case $.ui.keyCode.PAGE_DOWN:
11959 case $.ui.keyCode.UP:
11960 case $.ui.keyCode.RIGHT:
11961 case $.ui.keyCode.DOWN:
11962 case $.ui.keyCode.LEFT:
11963 event.preventDefault();
11964 if ( !this._keySliding ) {
11965 this._keySliding = true;
11966 $( event.target ).addClass( "ui-state-active" );
11967 allowed = this._start( event, index );
11968 if ( allowed === false ) {
11975 step = this.options.step;
11976 if ( this.options.values && this.options.values.length ) {
11977 curVal = newVal = this.values( index );
11979 curVal = newVal = this.value();
11982 switch ( event.keyCode ) {
11983 case $.ui.keyCode.HOME:
11984 newVal = this._valueMin();
11986 case $.ui.keyCode.END:
11987 newVal = this._valueMax();
11989 case $.ui.keyCode.PAGE_UP:
11990 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
11992 case $.ui.keyCode.PAGE_DOWN:
11993 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
11995 case $.ui.keyCode.UP:
11996 case $.ui.keyCode.RIGHT:
11997 if ( curVal === this._valueMax() ) {
12000 newVal = this._trimAlignValue( curVal + step );
12002 case $.ui.keyCode.DOWN:
12003 case $.ui.keyCode.LEFT:
12004 if ( curVal === this._valueMin() ) {
12007 newVal = this._trimAlignValue( curVal - step );
12011 this._slide( event, index, newVal );
12013 click: function( event ) {
12014 event.preventDefault();
12016 keyup: function( event ) {
12017 var index = $( event.target ).data( "ui-slider-handle-index" );
12019 if ( this._keySliding ) {
12020 this._keySliding = false;
12021 this._stop( event, index );
12022 this._change( event, index );
12023 $( event.target ).removeClass( "ui-state-active" );
12031 (function( $, undefined ) {
12033 function isOverAxis( x, reference, size ) {
12034 return ( x > reference ) && ( x < ( reference + size ) );
12037 function isFloating(item) {
12038 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
12041 $.widget("ui.sortable", $.ui.mouse, {
12043 widgetEventPrefix: "sort",
12046 appendTo: "parent",
12048 connectWith: false,
12049 containment: false,
12053 forcePlaceholderSize: false,
12054 forceHelperSize: false,
12057 helper: "original",
12060 placeholder: false,
12063 scrollSensitivity: 20,
12066 tolerance: "intersect",
12083 _create: function() {
12085 var o = this.options;
12086 this.containerCache = {};
12087 this.element.addClass("ui-sortable");
12092 //Let's determine if the items are being displayed horizontally
12093 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
12095 //Let's determine the parent's offset
12096 this.offset = this.element.offset();
12098 //Initialize mouse events for interaction
12101 //We're ready to go
12106 _destroy: function() {
12108 .removeClass("ui-sortable ui-sortable-disabled");
12109 this._mouseDestroy();
12111 for ( var i = this.items.length - 1; i >= 0; i-- ) {
12112 this.items[i].item.removeData(this.widgetName + "-item");
12118 _setOption: function(key, value){
12119 if ( key === "disabled" ) {
12120 this.options[ key ] = value;
12122 this.widget().toggleClass( "ui-sortable-disabled", !!value );
12124 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
12125 $.Widget.prototype._setOption.apply(this, arguments);
12129 _mouseCapture: function(event, overrideHandle) {
12130 var currentItem = null,
12131 validHandle = false,
12134 if (this.reverting) {
12138 if(this.options.disabled || this.options.type === "static") {
12142 //We have to refresh the items data once first
12143 this._refreshItems(event);
12145 //Find out if the clicked node (or one of its parents) is a actual item in this.items
12146 $(event.target).parents().each(function() {
12147 if($.data(this, that.widgetName + "-item") === that) {
12148 currentItem = $(this);
12152 if($.data(event.target, that.widgetName + "-item") === that) {
12153 currentItem = $(event.target);
12159 if(this.options.handle && !overrideHandle) {
12160 $(this.options.handle, currentItem).find("*").addBack().each(function() {
12161 if(this === event.target) {
12162 validHandle = true;
12170 this.currentItem = currentItem;
12171 this._removeCurrentsFromItems();
12176 _mouseStart: function(event, overrideHandle, noActivation) {
12181 this.currentContainer = this;
12183 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
12184 this.refreshPositions();
12186 //Create and append the visible helper
12187 this.helper = this._createHelper(event);
12189 //Cache the helper size
12190 this._cacheHelperProportions();
12193 * - Position generation -
12194 * This block generates everything position related - it's the core of draggables.
12197 //Cache the margins of the original element
12198 this._cacheMargins();
12200 //Get the next scrolling parent
12201 this.scrollParent = this.helper.scrollParent();
12203 //The element's absolute position on the page minus margins
12204 this.offset = this.currentItem.offset();
12206 top: this.offset.top - this.margins.top,
12207 left: this.offset.left - this.margins.left
12210 $.extend(this.offset, {
12211 click: { //Where the click happened, relative to the element
12212 left: event.pageX - this.offset.left,
12213 top: event.pageY - this.offset.top
12215 parent: this._getParentOffset(),
12216 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
12219 // Only after we got the offset, we can change the helper's position to absolute
12220 // TODO: Still need to figure out a way to make relative sorting possible
12221 this.helper.css("position", "absolute");
12222 this.cssPosition = this.helper.css("position");
12224 //Generate the original position
12225 this.originalPosition = this._generatePosition(event);
12226 this.originalPageX = event.pageX;
12227 this.originalPageY = event.pageY;
12229 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
12230 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
12232 //Cache the former DOM position
12233 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
12235 //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
12236 if(this.helper[0] !== this.currentItem[0]) {
12237 this.currentItem.hide();
12240 //Create the placeholder
12241 this._createPlaceholder();
12243 //Set a containment if given in the options
12244 if(o.containment) {
12245 this._setContainment();
12248 if( o.cursor && o.cursor !== "auto" ) { // cursor option
12249 body = this.document.find( "body" );
12252 this.storedCursor = body.css( "cursor" );
12253 body.css( "cursor", o.cursor );
12255 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
12258 if(o.opacity) { // opacity option
12259 if (this.helper.css("opacity")) {
12260 this._storedOpacity = this.helper.css("opacity");
12262 this.helper.css("opacity", o.opacity);
12265 if(o.zIndex) { // zIndex option
12266 if (this.helper.css("zIndex")) {
12267 this._storedZIndex = this.helper.css("zIndex");
12269 this.helper.css("zIndex", o.zIndex);
12272 //Prepare scrolling
12273 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
12274 this.overflowOffset = this.scrollParent.offset();
12278 this._trigger("start", event, this._uiHash());
12280 //Recache the helper size
12281 if(!this._preserveHelperProportions) {
12282 this._cacheHelperProportions();
12286 //Post "activate" events to possible containers
12287 if( !noActivation ) {
12288 for ( i = this.containers.length - 1; i >= 0; i-- ) {
12289 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
12293 //Prepare possible droppables
12294 if($.ui.ddmanager) {
12295 $.ui.ddmanager.current = this;
12298 if ($.ui.ddmanager && !o.dropBehaviour) {
12299 $.ui.ddmanager.prepareOffsets(this, event);
12302 this.dragging = true;
12304 this.helper.addClass("ui-sortable-helper");
12305 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
12310 _mouseDrag: function(event) {
12311 var i, item, itemElement, intersection,
12315 //Compute the helpers position
12316 this.position = this._generatePosition(event);
12317 this.positionAbs = this._convertPositionTo("absolute");
12319 if (!this.lastPositionAbs) {
12320 this.lastPositionAbs = this.positionAbs;
12324 if(this.options.scroll) {
12325 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
12327 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
12328 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
12329 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
12330 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
12333 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
12334 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
12335 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
12336 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
12341 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
12342 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
12343 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
12344 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
12347 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
12348 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
12349 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
12350 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
12355 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
12356 $.ui.ddmanager.prepareOffsets(this, event);
12360 //Regenerate the absolute position used for position checks
12361 this.positionAbs = this._convertPositionTo("absolute");
12363 //Set the helper position
12364 if(!this.options.axis || this.options.axis !== "y") {
12365 this.helper[0].style.left = this.position.left+"px";
12367 if(!this.options.axis || this.options.axis !== "x") {
12368 this.helper[0].style.top = this.position.top+"px";
12372 for (i = this.items.length - 1; i >= 0; i--) {
12374 //Cache variables and intersection, continue if no intersection
12375 item = this.items[i];
12376 itemElement = item.item[0];
12377 intersection = this._intersectsWithPointer(item);
12378 if (!intersection) {
12382 // Only put the placeholder inside the current Container, skip all
12383 // items from other containers. This works because when moving
12384 // an item from one container to another the
12385 // currentContainer is switched before the placeholder is moved.
12387 // Without this, moving items in "sub-sortables" can cause
12388 // the placeholder to jitter beetween the outer and inner container.
12389 if (item.instance !== this.currentContainer) {
12393 // cannot intersect with itself
12394 // no useless actions that have been done before
12395 // no action if the item moved is the parent of the item checked
12396 if (itemElement !== this.currentItem[0] &&
12397 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
12398 !$.contains(this.placeholder[0], itemElement) &&
12399 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
12402 this.direction = intersection === 1 ? "down" : "up";
12404 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
12405 this._rearrange(event, item);
12410 this._trigger("change", event, this._uiHash());
12415 //Post events to containers
12416 this._contactContainers(event);
12418 //Interconnect with droppables
12419 if($.ui.ddmanager) {
12420 $.ui.ddmanager.drag(this, event);
12424 this._trigger("sort", event, this._uiHash());
12426 this.lastPositionAbs = this.positionAbs;
12431 _mouseStop: function(event, noPropagation) {
12437 //If we are using droppables, inform the manager about the drop
12438 if ($.ui.ddmanager && !this.options.dropBehaviour) {
12439 $.ui.ddmanager.drop(this, event);
12442 if(this.options.revert) {
12444 cur = this.placeholder.offset(),
12445 axis = this.options.axis,
12448 if ( !axis || axis === "x" ) {
12449 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
12451 if ( !axis || axis === "y" ) {
12452 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
12454 this.reverting = true;
12455 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
12456 that._clear(event);
12459 this._clear(event, noPropagation);
12466 cancel: function() {
12468 if(this.dragging) {
12470 this._mouseUp({ target: null });
12472 if(this.options.helper === "original") {
12473 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
12475 this.currentItem.show();
12478 //Post deactivating events to containers
12479 for (var i = this.containers.length - 1; i >= 0; i--){
12480 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
12481 if(this.containers[i].containerCache.over) {
12482 this.containers[i]._trigger("out", null, this._uiHash(this));
12483 this.containers[i].containerCache.over = 0;
12489 if (this.placeholder) {
12490 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
12491 if(this.placeholder[0].parentNode) {
12492 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
12494 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
12495 this.helper.remove();
12505 if(this.domPosition.prev) {
12506 $(this.domPosition.prev).after(this.currentItem);
12508 $(this.domPosition.parent).prepend(this.currentItem);
12516 serialize: function(o) {
12518 var items = this._getItemsAsjQuery(o && o.connected),
12522 $(items).each(function() {
12523 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
12525 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
12529 if(!str.length && o.key) {
12530 str.push(o.key + "=");
12533 return str.join("&");
12537 toArray: function(o) {
12539 var items = this._getItemsAsjQuery(o && o.connected),
12544 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
12549 /* Be careful with the following core functions */
12550 _intersectsWith: function(item) {
12552 var x1 = this.positionAbs.left,
12553 x2 = x1 + this.helperProportions.width,
12554 y1 = this.positionAbs.top,
12555 y2 = y1 + this.helperProportions.height,
12557 r = l + item.width,
12559 b = t + item.height,
12560 dyClick = this.offset.click.top,
12561 dxClick = this.offset.click.left,
12562 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
12563 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
12564 isOverElement = isOverElementHeight && isOverElementWidth;
12566 if ( this.options.tolerance === "pointer" ||
12567 this.options.forcePointerForContainers ||
12568 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
12570 return isOverElement;
12573 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
12574 x2 - (this.helperProportions.width / 2) < r && // Left Half
12575 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
12576 y2 - (this.helperProportions.height / 2) < b ); // Top Half
12581 _intersectsWithPointer: function(item) {
12583 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
12584 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
12585 isOverElement = isOverElementHeight && isOverElementWidth,
12586 verticalDirection = this._getDragVerticalDirection(),
12587 horizontalDirection = this._getDragHorizontalDirection();
12589 if (!isOverElement) {
12593 return this.floating ?
12594 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
12595 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
12599 _intersectsWithSides: function(item) {
12601 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
12602 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
12603 verticalDirection = this._getDragVerticalDirection(),
12604 horizontalDirection = this._getDragHorizontalDirection();
12606 if (this.floating && horizontalDirection) {
12607 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
12609 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
12614 _getDragVerticalDirection: function() {
12615 var delta = this.positionAbs.top - this.lastPositionAbs.top;
12616 return delta !== 0 && (delta > 0 ? "down" : "up");
12619 _getDragHorizontalDirection: function() {
12620 var delta = this.positionAbs.left - this.lastPositionAbs.left;
12621 return delta !== 0 && (delta > 0 ? "right" : "left");
12624 refresh: function(event) {
12625 this._refreshItems(event);
12626 this.refreshPositions();
12630 _connectWith: function() {
12631 var options = this.options;
12632 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
12635 _getItemsAsjQuery: function(connected) {
12637 var i, j, cur, inst,
12640 connectWith = this._connectWith();
12642 if(connectWith && connected) {
12643 for (i = connectWith.length - 1; i >= 0; i--){
12644 cur = $(connectWith[i]);
12645 for ( j = cur.length - 1; j >= 0; j--){
12646 inst = $.data(cur[j], this.widgetFullName);
12647 if(inst && inst !== this && !inst.options.disabled) {
12648 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]);
12654 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]);
12656 function addItems() {
12657 items.push( this );
12659 for (i = queries.length - 1; i >= 0; i--){
12660 queries[i][0].each( addItems );
12667 _removeCurrentsFromItems: function() {
12669 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
12671 this.items = $.grep(this.items, function (item) {
12672 for (var j=0; j < list.length; j++) {
12673 if(list[j] === item.item[0]) {
12682 _refreshItems: function(event) {
12685 this.containers = [this];
12687 var i, j, cur, inst, targetData, _queries, item, queriesLength,
12688 items = this.items,
12689 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
12690 connectWith = this._connectWith();
12692 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
12693 for (i = connectWith.length - 1; i >= 0; i--){
12694 cur = $(connectWith[i]);
12695 for (j = cur.length - 1; j >= 0; j--){
12696 inst = $.data(cur[j], this.widgetFullName);
12697 if(inst && inst !== this && !inst.options.disabled) {
12698 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
12699 this.containers.push(inst);
12705 for (i = queries.length - 1; i >= 0; i--) {
12706 targetData = queries[i][1];
12707 _queries = queries[i][0];
12709 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
12710 item = $(_queries[j]);
12712 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
12716 instance: targetData,
12717 width: 0, height: 0,
12725 refreshPositions: function(fast) {
12727 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
12728 if(this.offsetParent && this.helper) {
12729 this.offset.parent = this._getParentOffset();
12734 for (i = this.items.length - 1; i >= 0; i--){
12735 item = this.items[i];
12737 //We ignore calculating positions of all connected containers when we're not over them
12738 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
12742 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
12745 item.width = t.outerWidth();
12746 item.height = t.outerHeight();
12750 item.left = p.left;
12754 if(this.options.custom && this.options.custom.refreshContainers) {
12755 this.options.custom.refreshContainers.call(this);
12757 for (i = this.containers.length - 1; i >= 0; i--){
12758 p = this.containers[i].element.offset();
12759 this.containers[i].containerCache.left = p.left;
12760 this.containers[i].containerCache.top = p.top;
12761 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
12762 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
12769 _createPlaceholder: function(that) {
12770 that = that || this;
12774 if(!o.placeholder || o.placeholder.constructor === String) {
12775 className = o.placeholder;
12777 element: function() {
12779 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
12780 element = $( "<" + nodeName + ">", that.document[0] )
12781 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
12782 .removeClass("ui-sortable-helper");
12784 if ( nodeName === "tr" ) {
12785 that.currentItem.children().each(function() {
12786 $( "<td> </td>", that.document[0] )
12787 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
12788 .appendTo( element );
12790 } else if ( nodeName === "img" ) {
12791 element.attr( "src", that.currentItem.attr( "src" ) );
12794 if ( !className ) {
12795 element.css( "visibility", "hidden" );
12800 update: function(container, p) {
12802 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
12803 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
12804 if(className && !o.forcePlaceholderSize) {
12808 //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
12809 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
12810 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
12815 //Create the placeholder
12816 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
12818 //Append it after the actual current item
12819 that.currentItem.after(that.placeholder);
12821 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
12822 o.placeholder.update(that, that.placeholder);
12826 _contactContainers: function(event) {
12827 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
12828 innermostContainer = null,
12829 innermostIndex = null;
12831 // get innermost container that intersects with item
12832 for (i = this.containers.length - 1; i >= 0; i--) {
12834 // never consider a container that's located within the item itself
12835 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
12839 if(this._intersectsWith(this.containers[i].containerCache)) {
12841 // if we've already found a container and it's more "inner" than this, then continue
12842 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
12846 innermostContainer = this.containers[i];
12847 innermostIndex = i;
12850 // container doesn't intersect. trigger "out" event if necessary
12851 if(this.containers[i].containerCache.over) {
12852 this.containers[i]._trigger("out", event, this._uiHash(this));
12853 this.containers[i].containerCache.over = 0;
12859 // if no intersecting containers found, return
12860 if(!innermostContainer) {
12864 // move the item into the container if it's not there already
12865 if(this.containers.length === 1) {
12866 if (!this.containers[innermostIndex].containerCache.over) {
12867 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
12868 this.containers[innermostIndex].containerCache.over = 1;
12872 //When entering a new container, we will find the item with the least distance and append our item near it
12874 itemWithLeastDistance = null;
12875 floating = innermostContainer.floating || isFloating(this.currentItem);
12876 posProperty = floating ? "left" : "top";
12877 sizeProperty = floating ? "width" : "height";
12878 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
12879 for (j = this.items.length - 1; j >= 0; j--) {
12880 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
12883 if(this.items[j].item[0] === this.currentItem[0]) {
12886 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
12889 cur = this.items[j].item.offset()[posProperty];
12890 nearBottom = false;
12891 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
12893 cur += this.items[j][sizeProperty];
12896 if(Math.abs(cur - base) < dist) {
12897 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
12898 this.direction = nearBottom ? "up": "down";
12902 //Check if dropOnEmpty is enabled
12903 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
12907 if(this.currentContainer === this.containers[innermostIndex]) {
12911 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
12912 this._trigger("change", event, this._uiHash());
12913 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
12914 this.currentContainer = this.containers[innermostIndex];
12916 //Update the placeholder
12917 this.options.placeholder.update(this.currentContainer, this.placeholder);
12919 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
12920 this.containers[innermostIndex].containerCache.over = 1;
12926 _createHelper: function(event) {
12928 var o = this.options,
12929 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
12931 //Add the helper to the DOM if that didn't happen already
12932 if(!helper.parents("body").length) {
12933 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
12936 if(helper[0] === this.currentItem[0]) {
12937 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") };
12940 if(!helper[0].style.width || o.forceHelperSize) {
12941 helper.width(this.currentItem.width());
12943 if(!helper[0].style.height || o.forceHelperSize) {
12944 helper.height(this.currentItem.height());
12951 _adjustOffsetFromHelper: function(obj) {
12952 if (typeof obj === "string") {
12953 obj = obj.split(" ");
12955 if ($.isArray(obj)) {
12956 obj = {left: +obj[0], top: +obj[1] || 0};
12958 if ("left" in obj) {
12959 this.offset.click.left = obj.left + this.margins.left;
12961 if ("right" in obj) {
12962 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
12964 if ("top" in obj) {
12965 this.offset.click.top = obj.top + this.margins.top;
12967 if ("bottom" in obj) {
12968 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
12972 _getParentOffset: function() {
12975 //Get the offsetParent and cache its position
12976 this.offsetParent = this.helper.offsetParent();
12977 var po = this.offsetParent.offset();
12979 // This is a special case where we need to modify a offset calculated on start, since the following happened:
12980 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
12981 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
12982 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
12983 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
12984 po.left += this.scrollParent.scrollLeft();
12985 po.top += this.scrollParent.scrollTop();
12988 // This needs to be actually done for all browsers, since pageX/pageY includes this information
12989 // with an ugly IE fix
12990 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
12991 po = { top: 0, left: 0 };
12995 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
12996 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
13001 _getRelativeOffset: function() {
13003 if(this.cssPosition === "relative") {
13004 var p = this.currentItem.position();
13006 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
13007 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
13010 return { top: 0, left: 0 };
13015 _cacheMargins: function() {
13017 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
13018 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
13022 _cacheHelperProportions: function() {
13023 this.helperProportions = {
13024 width: this.helper.outerWidth(),
13025 height: this.helper.outerHeight()
13029 _setContainment: function() {
13033 if(o.containment === "parent") {
13034 o.containment = this.helper[0].parentNode;
13036 if(o.containment === "document" || o.containment === "window") {
13037 this.containment = [
13038 0 - this.offset.relative.left - this.offset.parent.left,
13039 0 - this.offset.relative.top - this.offset.parent.top,
13040 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
13041 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
13045 if(!(/^(document|window|parent)$/).test(o.containment)) {
13046 ce = $(o.containment)[0];
13047 co = $(o.containment).offset();
13048 over = ($(ce).css("overflow") !== "hidden");
13050 this.containment = [
13051 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
13052 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
13053 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,
13054 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
13060 _convertPositionTo: function(d, pos) {
13063 pos = this.position;
13065 var mod = d === "absolute" ? 1 : -1,
13066 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
13067 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
13071 pos.top + // The absolute mouse position
13072 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
13073 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
13074 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
13077 pos.left + // The absolute mouse position
13078 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
13079 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
13080 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
13086 _generatePosition: function(event) {
13090 pageX = event.pageX,
13091 pageY = event.pageY,
13092 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);
13094 // This is another very weird special case that only happens for relative elements:
13095 // 1. If the css position is relative
13096 // 2. and the scroll parent is the document or similar to the offset parent
13097 // we have to refresh the relative offset during the scroll so there are no jumps
13098 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
13099 this.offset.relative = this._getRelativeOffset();
13103 * - Position constraining -
13104 * Constrain the position to a mix of grid, containment.
13107 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
13109 if(this.containment) {
13110 if(event.pageX - this.offset.click.left < this.containment[0]) {
13111 pageX = this.containment[0] + this.offset.click.left;
13113 if(event.pageY - this.offset.click.top < this.containment[1]) {
13114 pageY = this.containment[1] + this.offset.click.top;
13116 if(event.pageX - this.offset.click.left > this.containment[2]) {
13117 pageX = this.containment[2] + this.offset.click.left;
13119 if(event.pageY - this.offset.click.top > this.containment[3]) {
13120 pageY = this.containment[3] + this.offset.click.top;
13125 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
13126 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;
13128 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
13129 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;
13136 pageY - // The absolute mouse position
13137 this.offset.click.top - // Click offset (relative to the element)
13138 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
13139 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
13140 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
13143 pageX - // The absolute mouse position
13144 this.offset.click.left - // Click offset (relative to the element)
13145 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
13146 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
13147 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
13153 _rearrange: function(event, i, a, hardRefresh) {
13155 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));
13157 //Various things done here to improve the performance:
13158 // 1. we create a setTimeout, that calls refreshPositions
13159 // 2. on the instance, we have a counter variable, that get's higher after every append
13160 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
13161 // 4. this lets only the last addition to the timeout stack through
13162 this.counter = this.counter ? ++this.counter : 1;
13163 var counter = this.counter;
13165 this._delay(function() {
13166 if(counter === this.counter) {
13167 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
13173 _clear: function(event, noPropagation) {
13175 this.reverting = false;
13176 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
13177 // everything else normalized again
13179 delayedTriggers = [];
13181 // We first have to update the dom position of the actual currentItem
13182 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
13183 if(!this._noFinalSort && this.currentItem.parent().length) {
13184 this.placeholder.before(this.currentItem);
13186 this._noFinalSort = null;
13188 if(this.helper[0] === this.currentItem[0]) {
13189 for(i in this._storedCSS) {
13190 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
13191 this._storedCSS[i] = "";
13194 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
13196 this.currentItem.show();
13199 if(this.fromOutside && !noPropagation) {
13200 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
13202 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
13203 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
13206 // Check if the items Container has Changed and trigger appropriate
13208 if (this !== this.currentContainer) {
13209 if(!noPropagation) {
13210 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
13211 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
13212 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
13217 //Post events to containers
13218 function delayEvent( type, instance, container ) {
13219 return function( event ) {
13220 container._trigger( type, event, instance._uiHash( instance ) );
13223 for (i = this.containers.length - 1; i >= 0; i--){
13224 if (!noPropagation) {
13225 delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
13227 if(this.containers[i].containerCache.over) {
13228 delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
13229 this.containers[i].containerCache.over = 0;
13233 //Do what was originally in plugins
13234 if ( this.storedCursor ) {
13235 this.document.find( "body" ).css( "cursor", this.storedCursor );
13236 this.storedStylesheet.remove();
13238 if(this._storedOpacity) {
13239 this.helper.css("opacity", this._storedOpacity);
13241 if(this._storedZIndex) {
13242 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
13245 this.dragging = false;
13246 if(this.cancelHelperRemoval) {
13247 if(!noPropagation) {
13248 this._trigger("beforeStop", event, this._uiHash());
13249 for (i=0; i < delayedTriggers.length; i++) {
13250 delayedTriggers[i].call(this, event);
13251 } //Trigger all delayed events
13252 this._trigger("stop", event, this._uiHash());
13255 this.fromOutside = false;
13259 if(!noPropagation) {
13260 this._trigger("beforeStop", event, this._uiHash());
13263 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
13264 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
13266 if(this.helper[0] !== this.currentItem[0]) {
13267 this.helper.remove();
13269 this.helper = null;
13271 if(!noPropagation) {
13272 for (i=0; i < delayedTriggers.length; i++) {
13273 delayedTriggers[i].call(this, event);
13274 } //Trigger all delayed events
13275 this._trigger("stop", event, this._uiHash());
13278 this.fromOutside = false;
13283 _trigger: function() {
13284 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
13289 _uiHash: function(_inst) {
13290 var inst = _inst || this;
13292 helper: inst.helper,
13293 placeholder: inst.placeholder || $([]),
13294 position: inst.position,
13295 originalPosition: inst.originalPosition,
13296 offset: inst.positionAbs,
13297 item: inst.currentItem,
13298 sender: _inst ? _inst.element : null
13307 function modifier( fn ) {
13308 return function() {
13309 var previous = this.element.val();
13310 fn.apply( this, arguments );
13312 if ( previous !== this.element.val() ) {
13313 this._trigger( "change" );
13318 $.widget( "ui.spinner", {
13320 defaultElement: "<input>",
13321 widgetEventPrefix: "spin",
13325 down: "ui-icon-triangle-1-s",
13326 up: "ui-icon-triangle-1-n"
13331 numberFormat: null,
13341 _create: function() {
13342 // handle string values that need to be parsed
13343 this._setOption( "max", this.options.max );
13344 this._setOption( "min", this.options.min );
13345 this._setOption( "step", this.options.step );
13347 // Only format if there is a value, prevents the field from being marked
13348 // as invalid in Firefox, see #9573.
13349 if ( this.value() !== "" ) {
13350 // Format the value, but don't constrain.
13351 this._value( this.element.val(), true );
13355 this._on( this._events );
13358 // turning off autocomplete prevents the browser from remembering the
13359 // value when navigating through history, so we re-enable autocomplete
13360 // if the page is unloaded before the widget is destroyed. #7790
13361 this._on( this.window, {
13362 beforeunload: function() {
13363 this.element.removeAttr( "autocomplete" );
13368 _getCreateOptions: function() {
13370 element = this.element;
13372 $.each( [ "min", "max", "step" ], function( i, option ) {
13373 var value = element.attr( option );
13374 if ( value !== undefined && value.length ) {
13375 options[ option ] = value;
13383 keydown: function( event ) {
13384 if ( this._start( event ) && this._keydown( event ) ) {
13385 event.preventDefault();
13389 focus: function() {
13390 this.previous = this.element.val();
13392 blur: function( event ) {
13393 if ( this.cancelBlur ) {
13394 delete this.cancelBlur;
13400 if ( this.previous !== this.element.val() ) {
13401 this._trigger( "change", event );
13404 mousewheel: function( event, delta ) {
13408 if ( !this.spinning && !this._start( event ) ) {
13412 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
13413 clearTimeout( this.mousewheelTimer );
13414 this.mousewheelTimer = this._delay(function() {
13415 if ( this.spinning ) {
13416 this._stop( event );
13419 event.preventDefault();
13421 "mousedown .ui-spinner-button": function( event ) {
13424 // We never want the buttons to have focus; whenever the user is
13425 // interacting with the spinner, the focus should be on the input.
13426 // If the input is focused then this.previous is properly set from
13427 // when the input first received focus. If the input is not focused
13428 // then we need to set this.previous based on the value before spinning.
13429 previous = this.element[0] === this.document[0].activeElement ?
13430 this.previous : this.element.val();
13431 function checkFocus() {
13432 var isActive = this.element[0] === this.document[0].activeElement;
13434 this.element.focus();
13435 this.previous = previous;
13437 // IE sets focus asynchronously, so we need to check if focus
13438 // moved off of the input because the user clicked on the button.
13439 this._delay(function() {
13440 this.previous = previous;
13445 // ensure focus is on (or stays on) the text field
13446 event.preventDefault();
13447 checkFocus.call( this );
13450 // IE doesn't prevent moving focus even with event.preventDefault()
13451 // so we set a flag to know when we should ignore the blur event
13452 // and check (again) if focus moved off of the input.
13453 this.cancelBlur = true;
13454 this._delay(function() {
13455 delete this.cancelBlur;
13456 checkFocus.call( this );
13459 if ( this._start( event ) === false ) {
13463 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13465 "mouseup .ui-spinner-button": "_stop",
13466 "mouseenter .ui-spinner-button": function( event ) {
13467 // button will add ui-state-active if mouse was down while mouseleave and kept down
13468 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
13472 if ( this._start( event ) === false ) {
13475 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13477 // TODO: do we really want to consider this a stop?
13478 // shouldn't we just stop the repeater and wait until mouseup before
13479 // we trigger the stop event?
13480 "mouseleave .ui-spinner-button": "_stop"
13483 _draw: function() {
13484 var uiSpinner = this.uiSpinner = this.element
13485 .addClass( "ui-spinner-input" )
13486 .attr( "autocomplete", "off" )
13487 .wrap( this._uiSpinnerHtml() )
13490 .append( this._buttonHtml() );
13492 this.element.attr( "role", "spinbutton" );
13495 this.buttons = uiSpinner.find( ".ui-spinner-button" )
13496 .attr( "tabIndex", -1 )
13498 .removeClass( "ui-corner-all" );
13500 // IE 6 doesn't understand height: 50% for the buttons
13501 // unless the wrapper has an explicit height
13502 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
13503 uiSpinner.height() > 0 ) {
13504 uiSpinner.height( uiSpinner.height() );
13507 // disable spinner if element was already disabled
13508 if ( this.options.disabled ) {
13513 _keydown: function( event ) {
13514 var options = this.options,
13515 keyCode = $.ui.keyCode;
13517 switch ( event.keyCode ) {
13519 this._repeat( null, 1, event );
13522 this._repeat( null, -1, event );
13524 case keyCode.PAGE_UP:
13525 this._repeat( null, options.page, event );
13527 case keyCode.PAGE_DOWN:
13528 this._repeat( null, -options.page, event );
13535 _uiSpinnerHtml: function() {
13536 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
13539 _buttonHtml: function() {
13541 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
13542 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
13544 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
13545 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
13549 _start: function( event ) {
13550 if ( !this.spinning && this._trigger( "start", event ) === false ) {
13554 if ( !this.counter ) {
13557 this.spinning = true;
13561 _repeat: function( i, steps, event ) {
13564 clearTimeout( this.timer );
13565 this.timer = this._delay(function() {
13566 this._repeat( 40, steps, event );
13569 this._spin( steps * this.options.step, event );
13572 _spin: function( step, event ) {
13573 var value = this.value() || 0;
13575 if ( !this.counter ) {
13579 value = this._adjustValue( value + step * this._increment( this.counter ) );
13581 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
13582 this._value( value );
13587 _increment: function( i ) {
13588 var incremental = this.options.incremental;
13590 if ( incremental ) {
13591 return $.isFunction( incremental ) ?
13593 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
13599 _precision: function() {
13600 var precision = this._precisionOf( this.options.step );
13601 if ( this.options.min !== null ) {
13602 precision = Math.max( precision, this._precisionOf( this.options.min ) );
13607 _precisionOf: function( num ) {
13608 var str = num.toString(),
13609 decimal = str.indexOf( "." );
13610 return decimal === -1 ? 0 : str.length - decimal - 1;
13613 _adjustValue: function( value ) {
13614 var base, aboveMin,
13615 options = this.options;
13617 // make sure we're at a valid step
13618 // - find out where we are relative to the base (min or 0)
13619 base = options.min !== null ? options.min : 0;
13620 aboveMin = value - base;
13621 // - round to the nearest step
13622 aboveMin = Math.round(aboveMin / options.step) * options.step;
13623 // - rounding is based on 0, so adjust back to our base
13624 value = base + aboveMin;
13626 // fix precision from bad JS floating point math
13627 value = parseFloat( value.toFixed( this._precision() ) );
13630 if ( options.max !== null && value > options.max) {
13631 return options.max;
13633 if ( options.min !== null && value < options.min ) {
13634 return options.min;
13640 _stop: function( event ) {
13641 if ( !this.spinning ) {
13645 clearTimeout( this.timer );
13646 clearTimeout( this.mousewheelTimer );
13648 this.spinning = false;
13649 this._trigger( "stop", event );
13652 _setOption: function( key, value ) {
13653 if ( key === "culture" || key === "numberFormat" ) {
13654 var prevValue = this._parse( this.element.val() );
13655 this.options[ key ] = value;
13656 this.element.val( this._format( prevValue ) );
13660 if ( key === "max" || key === "min" || key === "step" ) {
13661 if ( typeof value === "string" ) {
13662 value = this._parse( value );
13665 if ( key === "icons" ) {
13666 this.buttons.first().find( ".ui-icon" )
13667 .removeClass( this.options.icons.up )
13668 .addClass( value.up );
13669 this.buttons.last().find( ".ui-icon" )
13670 .removeClass( this.options.icons.down )
13671 .addClass( value.down );
13674 this._super( key, value );
13676 if ( key === "disabled" ) {
13678 this.element.prop( "disabled", true );
13679 this.buttons.button( "disable" );
13681 this.element.prop( "disabled", false );
13682 this.buttons.button( "enable" );
13687 _setOptions: modifier(function( options ) {
13688 this._super( options );
13689 this._value( this.element.val() );
13692 _parse: function( val ) {
13693 if ( typeof val === "string" && val !== "" ) {
13694 val = window.Globalize && this.options.numberFormat ?
13695 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
13697 return val === "" || isNaN( val ) ? null : val;
13700 _format: function( value ) {
13701 if ( value === "" ) {
13704 return window.Globalize && this.options.numberFormat ?
13705 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
13709 _refresh: function() {
13710 this.element.attr({
13711 "aria-valuemin": this.options.min,
13712 "aria-valuemax": this.options.max,
13713 // TODO: what should we do with values that can't be parsed?
13714 "aria-valuenow": this._parse( this.element.val() )
13718 // update the value without triggering change
13719 _value: function( value, allowAny ) {
13721 if ( value !== "" ) {
13722 parsed = this._parse( value );
13723 if ( parsed !== null ) {
13725 parsed = this._adjustValue( parsed );
13727 value = this._format( parsed );
13730 this.element.val( value );
13734 _destroy: function() {
13736 .removeClass( "ui-spinner-input" )
13737 .prop( "disabled", false )
13738 .removeAttr( "autocomplete" )
13739 .removeAttr( "role" )
13740 .removeAttr( "aria-valuemin" )
13741 .removeAttr( "aria-valuemax" )
13742 .removeAttr( "aria-valuenow" );
13743 this.uiSpinner.replaceWith( this.element );
13746 stepUp: modifier(function( steps ) {
13747 this._stepUp( steps );
13749 _stepUp: function( steps ) {
13750 if ( this._start() ) {
13751 this._spin( (steps || 1) * this.options.step );
13756 stepDown: modifier(function( steps ) {
13757 this._stepDown( steps );
13759 _stepDown: function( steps ) {
13760 if ( this._start() ) {
13761 this._spin( (steps || 1) * -this.options.step );
13766 pageUp: modifier(function( pages ) {
13767 this._stepUp( (pages || 1) * this.options.page );
13770 pageDown: modifier(function( pages ) {
13771 this._stepDown( (pages || 1) * this.options.page );
13774 value: function( newVal ) {
13775 if ( !arguments.length ) {
13776 return this._parse( this.element.val() );
13778 modifier( this._value ).call( this, newVal );
13781 widget: function() {
13782 return this.uiSpinner;
13787 (function( $, undefined ) {
13792 function getNextTabId() {
13796 function isLocal( anchor ) {
13798 // IE7 doesn't normalize the href property when set via script (#9317)
13799 anchor = anchor.cloneNode( false );
13801 return anchor.hash.length > 1 &&
13802 decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
13803 decodeURIComponent( location.href.replace( rhash, "" ) );
13806 $.widget( "ui.tabs", {
13811 collapsible: false,
13813 heightStyle: "content",
13819 beforeActivate: null,
13824 _create: function() {
13826 options = this.options;
13828 this.running = false;
13831 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
13832 .toggleClass( "ui-tabs-collapsible", options.collapsible )
13833 // Prevent users from focusing disabled tabs via click
13834 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
13835 if ( $( this ).is( ".ui-state-disabled" ) ) {
13836 event.preventDefault();
13840 // Preventing the default action in mousedown doesn't prevent IE
13841 // from focusing the element, so if the anchor gets focused, blur.
13842 // We don't have to worry about focusing the previously focused
13843 // element since clicking on a non-focusable element should focus
13844 // the body anyway.
13845 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
13846 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
13851 this._processTabs();
13852 options.active = this._initialActive();
13854 // Take disabling tabs via class attribute from HTML
13855 // into account and update option properly.
13856 if ( $.isArray( options.disabled ) ) {
13857 options.disabled = $.unique( options.disabled.concat(
13858 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
13859 return that.tabs.index( li );
13864 // check for length avoids error when initializing empty list
13865 if ( this.options.active !== false && this.anchors.length ) {
13866 this.active = this._findActive( options.active );
13873 if ( this.active.length ) {
13874 this.load( options.active );
13878 _initialActive: function() {
13879 var active = this.options.active,
13880 collapsible = this.options.collapsible,
13881 locationHash = location.hash.substring( 1 );
13883 if ( active === null ) {
13884 // check the fragment identifier in the URL
13885 if ( locationHash ) {
13886 this.tabs.each(function( i, tab ) {
13887 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
13894 // check for a tab marked active via a class
13895 if ( active === null ) {
13896 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
13899 // no active tab, set to false
13900 if ( active === null || active === -1 ) {
13901 active = this.tabs.length ? 0 : false;
13905 // handle numbers: negative, out of range
13906 if ( active !== false ) {
13907 active = this.tabs.index( this.tabs.eq( active ) );
13908 if ( active === -1 ) {
13909 active = collapsible ? false : 0;
13913 // don't allow collapsible: false and active: false
13914 if ( !collapsible && active === false && this.anchors.length ) {
13921 _getCreateEventData: function() {
13924 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
13928 _tabKeydown: function( event ) {
13929 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
13930 selectedIndex = this.tabs.index( focusedTab ),
13931 goingForward = true;
13933 if ( this._handlePageNav( event ) ) {
13937 switch ( event.keyCode ) {
13938 case $.ui.keyCode.RIGHT:
13939 case $.ui.keyCode.DOWN:
13942 case $.ui.keyCode.UP:
13943 case $.ui.keyCode.LEFT:
13944 goingForward = false;
13947 case $.ui.keyCode.END:
13948 selectedIndex = this.anchors.length - 1;
13950 case $.ui.keyCode.HOME:
13953 case $.ui.keyCode.SPACE:
13954 // Activate only, no collapsing
13955 event.preventDefault();
13956 clearTimeout( this.activating );
13957 this._activate( selectedIndex );
13959 case $.ui.keyCode.ENTER:
13960 // Toggle (cancel delayed activation, allow collapsing)
13961 event.preventDefault();
13962 clearTimeout( this.activating );
13963 // Determine if we should collapse or activate
13964 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
13970 // Focus the appropriate tab, based on which key was pressed
13971 event.preventDefault();
13972 clearTimeout( this.activating );
13973 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
13975 // Navigating with control key will prevent automatic activation
13976 if ( !event.ctrlKey ) {
13977 // Update aria-selected immediately so that AT think the tab is already selected.
13978 // Otherwise AT may confuse the user by stating that they need to activate the tab,
13979 // but the tab will already be activated by the time the announcement finishes.
13980 focusedTab.attr( "aria-selected", "false" );
13981 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
13983 this.activating = this._delay(function() {
13984 this.option( "active", selectedIndex );
13989 _panelKeydown: function( event ) {
13990 if ( this._handlePageNav( event ) ) {
13994 // Ctrl+up moves focus to the current tab
13995 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
13996 event.preventDefault();
13997 this.active.focus();
14001 // Alt+page up/down moves focus to the previous/next tab (and activates)
14002 _handlePageNav: function( event ) {
14003 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
14004 this._activate( this._focusNextTab( this.options.active - 1, false ) );
14007 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
14008 this._activate( this._focusNextTab( this.options.active + 1, true ) );
14013 _findNextTab: function( index, goingForward ) {
14014 var lastTabIndex = this.tabs.length - 1;
14016 function constrain() {
14017 if ( index > lastTabIndex ) {
14021 index = lastTabIndex;
14026 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
14027 index = goingForward ? index + 1 : index - 1;
14033 _focusNextTab: function( index, goingForward ) {
14034 index = this._findNextTab( index, goingForward );
14035 this.tabs.eq( index ).focus();
14039 _setOption: function( key, value ) {
14040 if ( key === "active" ) {
14041 // _activate() will handle invalid values and update this.options
14042 this._activate( value );
14046 if ( key === "disabled" ) {
14047 // don't use the widget factory's disabled handling
14048 this._setupDisabled( value );
14052 this._super( key, value);
14054 if ( key === "collapsible" ) {
14055 this.element.toggleClass( "ui-tabs-collapsible", value );
14056 // Setting collapsible: false while collapsed; open first panel
14057 if ( !value && this.options.active === false ) {
14058 this._activate( 0 );
14062 if ( key === "event" ) {
14063 this._setupEvents( value );
14066 if ( key === "heightStyle" ) {
14067 this._setupHeightStyle( value );
14071 _tabId: function( tab ) {
14072 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
14075 _sanitizeSelector: function( hash ) {
14076 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
14079 refresh: function() {
14080 var options = this.options,
14081 lis = this.tablist.children( ":has(a[href])" );
14083 // get disabled tabs from class attribute from HTML
14084 // this will get converted to a boolean if needed in _refresh()
14085 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
14086 return lis.index( tab );
14089 this._processTabs();
14091 // was collapsed or no tabs
14092 if ( options.active === false || !this.anchors.length ) {
14093 options.active = false;
14095 // was active, but active tab is gone
14096 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
14097 // all remaining tabs are disabled
14098 if ( this.tabs.length === options.disabled.length ) {
14099 options.active = false;
14101 // activate previous tab
14103 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
14105 // was active, active tab still exists
14107 // make sure active index is correct
14108 options.active = this.tabs.index( this.active );
14114 _refresh: function() {
14115 this._setupDisabled( this.options.disabled );
14116 this._setupEvents( this.options.event );
14117 this._setupHeightStyle( this.options.heightStyle );
14119 this.tabs.not( this.active ).attr({
14120 "aria-selected": "false",
14123 this.panels.not( this._getPanelForTab( this.active ) )
14126 "aria-expanded": "false",
14127 "aria-hidden": "true"
14130 // Make sure one tab is in the tab order
14131 if ( !this.active.length ) {
14132 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
14135 .addClass( "ui-tabs-active ui-state-active" )
14137 "aria-selected": "true",
14140 this._getPanelForTab( this.active )
14143 "aria-expanded": "true",
14144 "aria-hidden": "false"
14149 _processTabs: function() {
14152 this.tablist = this._getList()
14153 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14154 .attr( "role", "tablist" );
14156 this.tabs = this.tablist.find( "> li:has(a[href])" )
14157 .addClass( "ui-state-default ui-corner-top" )
14163 this.anchors = this.tabs.map(function() {
14164 return $( "a", this )[ 0 ];
14166 .addClass( "ui-tabs-anchor" )
14168 role: "presentation",
14174 this.anchors.each(function( i, anchor ) {
14175 var selector, panel, panelId,
14176 anchorId = $( anchor ).uniqueId().attr( "id" ),
14177 tab = $( anchor ).closest( "li" ),
14178 originalAriaControls = tab.attr( "aria-controls" );
14181 if ( isLocal( anchor ) ) {
14182 selector = anchor.hash;
14183 panel = that.element.find( that._sanitizeSelector( selector ) );
14186 panelId = that._tabId( tab );
14187 selector = "#" + panelId;
14188 panel = that.element.find( selector );
14189 if ( !panel.length ) {
14190 panel = that._createPanel( panelId );
14191 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
14193 panel.attr( "aria-live", "polite" );
14196 if ( panel.length) {
14197 that.panels = that.panels.add( panel );
14199 if ( originalAriaControls ) {
14200 tab.data( "ui-tabs-aria-controls", originalAriaControls );
14203 "aria-controls": selector.substring( 1 ),
14204 "aria-labelledby": anchorId
14206 panel.attr( "aria-labelledby", anchorId );
14210 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14211 .attr( "role", "tabpanel" );
14214 // allow overriding how to find the list for rare usage scenarios (#7715)
14215 _getList: function() {
14216 return this.tablist || this.element.find( "ol,ul" ).eq( 0 );
14219 _createPanel: function( id ) {
14220 return $( "<div>" )
14222 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14223 .data( "ui-tabs-destroy", true );
14226 _setupDisabled: function( disabled ) {
14227 if ( $.isArray( disabled ) ) {
14228 if ( !disabled.length ) {
14230 } else if ( disabled.length === this.anchors.length ) {
14236 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
14237 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
14239 .addClass( "ui-state-disabled" )
14240 .attr( "aria-disabled", "true" );
14243 .removeClass( "ui-state-disabled" )
14244 .removeAttr( "aria-disabled" );
14248 this.options.disabled = disabled;
14251 _setupEvents: function( event ) {
14253 click: function( event ) {
14254 event.preventDefault();
14258 $.each( event.split(" "), function( index, eventName ) {
14259 events[ eventName ] = "_eventHandler";
14263 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
14264 this._on( this.anchors, events );
14265 this._on( this.tabs, { keydown: "_tabKeydown" } );
14266 this._on( this.panels, { keydown: "_panelKeydown" } );
14268 this._focusable( this.tabs );
14269 this._hoverable( this.tabs );
14272 _setupHeightStyle: function( heightStyle ) {
14274 parent = this.element.parent();
14276 if ( heightStyle === "fill" ) {
14277 maxHeight = parent.height();
14278 maxHeight -= this.element.outerHeight() - this.element.height();
14280 this.element.siblings( ":visible" ).each(function() {
14281 var elem = $( this ),
14282 position = elem.css( "position" );
14284 if ( position === "absolute" || position === "fixed" ) {
14287 maxHeight -= elem.outerHeight( true );
14290 this.element.children().not( this.panels ).each(function() {
14291 maxHeight -= $( this ).outerHeight( true );
14294 this.panels.each(function() {
14295 $( this ).height( Math.max( 0, maxHeight -
14296 $( this ).innerHeight() + $( this ).height() ) );
14298 .css( "overflow", "auto" );
14299 } else if ( heightStyle === "auto" ) {
14301 this.panels.each(function() {
14302 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
14303 }).height( maxHeight );
14307 _eventHandler: function( event ) {
14308 var options = this.options,
14309 active = this.active,
14310 anchor = $( event.currentTarget ),
14311 tab = anchor.closest( "li" ),
14312 clickedIsActive = tab[ 0 ] === active[ 0 ],
14313 collapsing = clickedIsActive && options.collapsible,
14314 toShow = collapsing ? $() : this._getPanelForTab( tab ),
14315 toHide = !active.length ? $() : this._getPanelForTab( active ),
14319 newTab: collapsing ? $() : tab,
14323 event.preventDefault();
14325 if ( tab.hasClass( "ui-state-disabled" ) ||
14326 // tab is already loading
14327 tab.hasClass( "ui-tabs-loading" ) ||
14328 // can't switch durning an animation
14330 // click on active header, but not collapsible
14331 ( clickedIsActive && !options.collapsible ) ||
14332 // allow canceling activation
14333 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
14337 options.active = collapsing ? false : this.tabs.index( tab );
14339 this.active = clickedIsActive ? $() : tab;
14344 if ( !toHide.length && !toShow.length ) {
14345 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
14348 if ( toShow.length ) {
14349 this.load( this.tabs.index( tab ), event );
14351 this._toggle( event, eventData );
14354 // handles show/hide for selecting tabs
14355 _toggle: function( event, eventData ) {
14357 toShow = eventData.newPanel,
14358 toHide = eventData.oldPanel;
14360 this.running = true;
14362 function complete() {
14363 that.running = false;
14364 that._trigger( "activate", event, eventData );
14368 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
14370 if ( toShow.length && that.options.show ) {
14371 that._show( toShow, that.options.show, complete );
14378 // start out by hiding, then showing, then completing
14379 if ( toHide.length && this.options.hide ) {
14380 this._hide( toHide, this.options.hide, function() {
14381 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14385 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14391 "aria-expanded": "false",
14392 "aria-hidden": "true"
14394 eventData.oldTab.attr( "aria-selected", "false" );
14395 // If we're switching tabs, remove the old tab from the tab order.
14396 // If we're opening from collapsed state, remove the previous tab from the tab order.
14397 // If we're collapsing, then keep the collapsing tab in the tab order.
14398 if ( toShow.length && toHide.length ) {
14399 eventData.oldTab.attr( "tabIndex", -1 );
14400 } else if ( toShow.length ) {
14401 this.tabs.filter(function() {
14402 return $( this ).attr( "tabIndex" ) === 0;
14404 .attr( "tabIndex", -1 );
14408 "aria-expanded": "true",
14409 "aria-hidden": "false"
14411 eventData.newTab.attr({
14412 "aria-selected": "true",
14417 _activate: function( index ) {
14419 active = this._findActive( index );
14421 // trying to activate the already active panel
14422 if ( active[ 0 ] === this.active[ 0 ] ) {
14426 // trying to collapse, simulate a click on the current active header
14427 if ( !active.length ) {
14428 active = this.active;
14431 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
14432 this._eventHandler({
14434 currentTarget: anchor,
14435 preventDefault: $.noop
14439 _findActive: function( index ) {
14440 return index === false ? $() : this.tabs.eq( index );
14443 _getIndex: function( index ) {
14444 // meta-function to give users option to provide a href string instead of a numerical index.
14445 if ( typeof index === "string" ) {
14446 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
14452 _destroy: function() {
14457 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
14460 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14461 .removeAttr( "role" );
14464 .removeClass( "ui-tabs-anchor" )
14465 .removeAttr( "role" )
14466 .removeAttr( "tabIndex" )
14469 this.tabs.add( this.panels ).each(function() {
14470 if ( $.data( this, "ui-tabs-destroy" ) ) {
14471 $( this ).remove();
14474 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
14475 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
14476 .removeAttr( "tabIndex" )
14477 .removeAttr( "aria-live" )
14478 .removeAttr( "aria-busy" )
14479 .removeAttr( "aria-selected" )
14480 .removeAttr( "aria-labelledby" )
14481 .removeAttr( "aria-hidden" )
14482 .removeAttr( "aria-expanded" )
14483 .removeAttr( "role" );
14487 this.tabs.each(function() {
14488 var li = $( this ),
14489 prev = li.data( "ui-tabs-aria-controls" );
14492 .attr( "aria-controls", prev )
14493 .removeData( "ui-tabs-aria-controls" );
14495 li.removeAttr( "aria-controls" );
14499 this.panels.show();
14501 if ( this.options.heightStyle !== "content" ) {
14502 this.panels.css( "height", "" );
14506 enable: function( index ) {
14507 var disabled = this.options.disabled;
14508 if ( disabled === false ) {
14512 if ( index === undefined ) {
14515 index = this._getIndex( index );
14516 if ( $.isArray( disabled ) ) {
14517 disabled = $.map( disabled, function( num ) {
14518 return num !== index ? num : null;
14521 disabled = $.map( this.tabs, function( li, num ) {
14522 return num !== index ? num : null;
14526 this._setupDisabled( disabled );
14529 disable: function( index ) {
14530 var disabled = this.options.disabled;
14531 if ( disabled === true ) {
14535 if ( index === undefined ) {
14538 index = this._getIndex( index );
14539 if ( $.inArray( index, disabled ) !== -1 ) {
14542 if ( $.isArray( disabled ) ) {
14543 disabled = $.merge( [ index ], disabled ).sort();
14545 disabled = [ index ];
14548 this._setupDisabled( disabled );
14551 load: function( index, event ) {
14552 index = this._getIndex( index );
14554 tab = this.tabs.eq( index ),
14555 anchor = tab.find( ".ui-tabs-anchor" ),
14556 panel = this._getPanelForTab( tab ),
14563 if ( isLocal( anchor[ 0 ] ) ) {
14567 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
14569 // support: jQuery <1.8
14570 // jQuery <1.8 returns false if the request is canceled in beforeSend,
14571 // but as of 1.8, $.ajax() always returns a jqXHR object.
14572 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
14573 tab.addClass( "ui-tabs-loading" );
14574 panel.attr( "aria-busy", "true" );
14577 .success(function( response ) {
14578 // support: jQuery <1.8
14579 // http://bugs.jquery.com/ticket/11778
14580 setTimeout(function() {
14581 panel.html( response );
14582 that._trigger( "load", event, eventData );
14585 .complete(function( jqXHR, status ) {
14586 // support: jQuery <1.8
14587 // http://bugs.jquery.com/ticket/11778
14588 setTimeout(function() {
14589 if ( status === "abort" ) {
14590 that.panels.stop( false, true );
14593 tab.removeClass( "ui-tabs-loading" );
14594 panel.removeAttr( "aria-busy" );
14596 if ( jqXHR === that.xhr ) {
14604 _ajaxSettings: function( anchor, event, eventData ) {
14607 url: anchor.attr( "href" ),
14608 beforeSend: function( jqXHR, settings ) {
14609 return that._trigger( "beforeLoad", event,
14610 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
14615 _getPanelForTab: function( tab ) {
14616 var id = $( tab ).attr( "aria-controls" );
14617 return this.element.find( this._sanitizeSelector( "#" + id ) );
14624 var increments = 0;
14626 function addDescribedBy( elem, id ) {
14627 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
14628 describedby.push( id );
14630 .data( "ui-tooltip-id", id )
14631 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
14634 function removeDescribedBy( elem ) {
14635 var id = elem.data( "ui-tooltip-id" ),
14636 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
14637 index = $.inArray( id, describedby );
14638 if ( index !== -1 ) {
14639 describedby.splice( index, 1 );
14642 elem.removeData( "ui-tooltip-id" );
14643 describedby = $.trim( describedby.join( " " ) );
14644 if ( describedby ) {
14645 elem.attr( "aria-describedby", describedby );
14647 elem.removeAttr( "aria-describedby" );
14651 $.widget( "ui.tooltip", {
14654 content: function() {
14655 // support: IE<9, Opera in jQuery <1.7
14656 // .text() can't accept undefined, so coerce to a string
14657 var title = $( this ).attr( "title" ) || "";
14658 // Escape title, since we're going from an attribute to raw HTML
14659 return $( "<a>" ).text( title ).html();
14662 // Disabled elements have inconsistent behavior across browsers (#8661)
14663 items: "[title]:not([disabled])",
14667 collision: "flipfit flip"
14670 tooltipClass: null,
14678 _create: function() {
14684 // IDs of generated tooltips, needed for destroy
14685 this.tooltips = {};
14686 // IDs of parent tooltips where we removed the title attribute
14689 if ( this.options.disabled ) {
14694 _setOption: function( key, value ) {
14697 if ( key === "disabled" ) {
14698 this[ value ? "_disable" : "_enable" ]();
14699 this.options[ key ] = value;
14700 // disable element style changes
14704 this._super( key, value );
14706 if ( key === "content" ) {
14707 $.each( this.tooltips, function( id, element ) {
14708 that._updateContent( element );
14713 _disable: function() {
14716 // close open tooltips
14717 $.each( this.tooltips, function( id, element ) {
14718 var event = $.Event( "blur" );
14719 event.target = event.currentTarget = element[0];
14720 that.close( event, true );
14723 // remove title attributes to prevent native tooltips
14724 this.element.find( this.options.items ).addBack().each(function() {
14725 var element = $( this );
14726 if ( element.is( "[title]" ) ) {
14728 .data( "ui-tooltip-title", element.attr( "title" ) )
14729 .attr( "title", "" );
14734 _enable: function() {
14735 // restore title attributes
14736 this.element.find( this.options.items ).addBack().each(function() {
14737 var element = $( this );
14738 if ( element.data( "ui-tooltip-title" ) ) {
14739 element.attr( "title", element.data( "ui-tooltip-title" ) );
14744 open: function( event ) {
14746 target = $( event ? event.target : this.element )
14747 // we need closest here due to mouseover bubbling,
14748 // but always pointing at the same event target
14749 .closest( this.options.items );
14751 // No element to show a tooltip for or the tooltip is already open
14752 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
14756 if ( target.attr( "title" ) ) {
14757 target.data( "ui-tooltip-title", target.attr( "title" ) );
14760 target.data( "ui-tooltip-open", true );
14762 // kill parent tooltips, custom or native, for hover
14763 if ( event && event.type === "mouseover" ) {
14764 target.parents().each(function() {
14765 var parent = $( this ),
14767 if ( parent.data( "ui-tooltip-open" ) ) {
14768 blurEvent = $.Event( "blur" );
14769 blurEvent.target = blurEvent.currentTarget = this;
14770 that.close( blurEvent, true );
14772 if ( parent.attr( "title" ) ) {
14774 that.parents[ this.id ] = {
14776 title: parent.attr( "title" )
14778 parent.attr( "title", "" );
14783 this._updateContent( target, event );
14786 _updateContent: function( target, event ) {
14788 contentOption = this.options.content,
14790 eventType = event ? event.type : null;
14792 if ( typeof contentOption === "string" ) {
14793 return this._open( event, target, contentOption );
14796 content = contentOption.call( target[0], function( response ) {
14797 // ignore async response if tooltip was closed already
14798 if ( !target.data( "ui-tooltip-open" ) ) {
14801 // IE may instantly serve a cached response for ajax requests
14802 // delay this call to _open so the other call to _open runs first
14803 that._delay(function() {
14804 // jQuery creates a special event for focusin when it doesn't
14805 // exist natively. To improve performance, the native event
14806 // object is reused and the type is changed. Therefore, we can't
14807 // rely on the type being correct after the event finished
14808 // bubbling, so we set it back to the previous value. (#8740)
14810 event.type = eventType;
14812 this._open( event, target, response );
14816 this._open( event, target, content );
14820 _open: function( event, target, content ) {
14821 var tooltip, events, delayedShow,
14822 positionOption = $.extend( {}, this.options.position );
14828 // Content can be updated multiple times. If the tooltip already
14829 // exists, then just update the content and bail.
14830 tooltip = this._find( target );
14831 if ( tooltip.length ) {
14832 tooltip.find( ".ui-tooltip-content" ).html( content );
14836 // if we have a title, clear it to prevent the native tooltip
14837 // we have to check first to avoid defining a title if none exists
14838 // (we don't want to cause an element to start matching [title])
14840 // We use removeAttr only for key events, to allow IE to export the correct
14841 // accessible attributes. For mouse events, set to empty string to avoid
14842 // native tooltip showing up (happens only when removing inside mouseover).
14843 if ( target.is( "[title]" ) ) {
14844 if ( event && event.type === "mouseover" ) {
14845 target.attr( "title", "" );
14847 target.removeAttr( "title" );
14851 tooltip = this._tooltip( target );
14852 addDescribedBy( target, tooltip.attr( "id" ) );
14853 tooltip.find( ".ui-tooltip-content" ).html( content );
14855 function position( event ) {
14856 positionOption.of = event;
14857 if ( tooltip.is( ":hidden" ) ) {
14860 tooltip.position( positionOption );
14862 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
14863 this._on( this.document, {
14864 mousemove: position
14866 // trigger once to override element-relative positioning
14869 tooltip.position( $.extend({
14871 }, this.options.position ) );
14876 this._show( tooltip, this.options.show );
14877 // Handle tracking tooltips that are shown with a delay (#8644). As soon
14878 // as the tooltip is visible, position the tooltip using the most recent
14880 if ( this.options.show && this.options.show.delay ) {
14881 delayedShow = this.delayedShow = setInterval(function() {
14882 if ( tooltip.is( ":visible" ) ) {
14883 position( positionOption.of );
14884 clearInterval( delayedShow );
14886 }, $.fx.interval );
14889 this._trigger( "open", event, { tooltip: tooltip } );
14892 keyup: function( event ) {
14893 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
14894 var fakeEvent = $.Event(event);
14895 fakeEvent.currentTarget = target[0];
14896 this.close( fakeEvent, true );
14899 remove: function() {
14900 this._removeTooltip( tooltip );
14903 if ( !event || event.type === "mouseover" ) {
14904 events.mouseleave = "close";
14906 if ( !event || event.type === "focusin" ) {
14907 events.focusout = "close";
14909 this._on( true, target, events );
14912 close: function( event ) {
14914 target = $( event ? event.currentTarget : this.element ),
14915 tooltip = this._find( target );
14917 // disabling closes the tooltip, so we need to track when we're closing
14918 // to avoid an infinite loop in case the tooltip becomes disabled on close
14919 if ( this.closing ) {
14923 // Clear the interval for delayed tracking tooltips
14924 clearInterval( this.delayedShow );
14926 // only set title if we had one before (see comment in _open())
14927 if ( target.data( "ui-tooltip-title" ) ) {
14928 target.attr( "title", target.data( "ui-tooltip-title" ) );
14931 removeDescribedBy( target );
14933 tooltip.stop( true );
14934 this._hide( tooltip, this.options.hide, function() {
14935 that._removeTooltip( $( this ) );
14938 target.removeData( "ui-tooltip-open" );
14939 this._off( target, "mouseleave focusout keyup" );
14940 // Remove 'remove' binding only on delegated targets
14941 if ( target[0] !== this.element[0] ) {
14942 this._off( target, "remove" );
14944 this._off( this.document, "mousemove" );
14946 if ( event && event.type === "mouseleave" ) {
14947 $.each( this.parents, function( id, parent ) {
14948 $( parent.element ).attr( "title", parent.title );
14949 delete that.parents[ id ];
14953 this.closing = true;
14954 this._trigger( "close", event, { tooltip: tooltip } );
14955 this.closing = false;
14958 _tooltip: function( element ) {
14959 var id = "ui-tooltip-" + increments++,
14960 tooltip = $( "<div>" )
14965 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
14966 ( this.options.tooltipClass || "" ) );
14968 .addClass( "ui-tooltip-content" )
14969 .appendTo( tooltip );
14970 tooltip.appendTo( this.document[0].body );
14971 this.tooltips[ id ] = element;
14975 _find: function( target ) {
14976 var id = target.data( "ui-tooltip-id" );
14977 return id ? $( "#" + id ) : $();
14980 _removeTooltip: function( tooltip ) {
14982 delete this.tooltips[ tooltip.attr( "id" ) ];
14985 _destroy: function() {
14988 // close open tooltips
14989 $.each( this.tooltips, function( id, element ) {
14990 // Delegate to close method to handle common cleanup
14991 var event = $.Event( "blur" );
14992 event.target = event.currentTarget = element[0];
14993 that.close( event, true );
14995 // Remove immediately; destroying an open tooltip doesn't use the
14997 $( "#" + id ).remove();
14999 // Restore the title
15000 if ( element.data( "ui-tooltip-title" ) ) {
15001 element.attr( "title", element.data( "ui-tooltip-title" ) );
15002 element.removeData( "ui-tooltip-title" );