1 /*! jQuery UI - v1.9.2 - 2012-11-23
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 (c) 2012 jQuery Foundation and other contributors Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // prevent duplicate loading
12 // this is only a problem because we proxy existing functions
13 // and we don't want to double proxy them
51 focus: function( delay, fn ) {
52 return typeof delay === "number" ?
53 this.each(function() {
55 setTimeout(function() {
62 this._focus.apply( this, arguments );
65 scrollParent: function() {
67 if (($.ui.ie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
68 scrollParent = this.parents().filter(function() {
69 return (/(relative|absolute|fixed)/).test($.css(this,'position')) && (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
72 scrollParent = this.parents().filter(function() {
73 return (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
77 return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
80 zIndex: function( zIndex ) {
81 if ( zIndex !== undefined ) {
82 return this.css( "zIndex", zIndex );
86 var elem = $( this[ 0 ] ), position, value;
87 while ( elem.length && elem[ 0 ] !== document ) {
88 // Ignore z-index if position is set to a value where z-index is ignored by the browser
89 // This makes behavior of this function consistent across browsers
90 // WebKit always returns auto if the element is positioned
91 position = elem.css( "position" );
92 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
93 // IE returns 0 when zIndex is not specified
94 // other browsers return a string
95 // we ignore the case of nested elements with an explicit value of 0
96 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
97 value = parseInt( elem.css( "zIndex" ), 10 );
98 if ( !isNaN( value ) && value !== 0 ) {
102 elem = elem.parent();
109 uniqueId: function() {
110 return this.each(function() {
112 this.id = "ui-id-" + (++uuid);
117 removeUniqueId: function() {
118 return this.each(function() {
119 if ( runiqueId.test( this.id ) ) {
120 $( this ).removeAttr( "id" );
127 function focusable( element, isTabIndexNotNaN ) {
128 var map, mapName, img,
129 nodeName = element.nodeName.toLowerCase();
130 if ( "area" === nodeName ) {
131 map = element.parentNode;
133 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
136 img = $( "img[usemap=#" + mapName + "]" )[0];
137 return !!img && visible( img );
139 return ( /input|select|textarea|button|object/.test( nodeName ) ?
142 element.href || isTabIndexNotNaN :
144 // the element and all of its ancestors must be visible
148 function visible( element ) {
149 return $.expr.filters.visible( element ) &&
150 !$( element ).parents().andSelf().filter(function() {
151 return $.css( this, "visibility" ) === "hidden";
155 $.extend( $.expr[ ":" ], {
156 data: $.expr.createPseudo ?
157 $.expr.createPseudo(function( dataName ) {
158 return function( elem ) {
159 return !!$.data( elem, dataName );
162 // support: jQuery <1.8
163 function( elem, i, match ) {
164 return !!$.data( elem, match[ 3 ] );
167 focusable: function( element ) {
168 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
171 tabbable: function( element ) {
172 var tabIndex = $.attr( element, "tabindex" ),
173 isTabIndexNaN = isNaN( tabIndex );
174 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
180 var body = document.body,
181 div = body.appendChild( div = document.createElement( "div" ) );
183 // access offsetHeight before setting the style to prevent a layout bug
184 // in IE 9 which causes the element to continue to take up space even
185 // after it is removed from the DOM (#8026)
188 $.extend( div.style, {
195 $.support.minHeight = div.offsetHeight === 100;
196 $.support.selectstart = "onselectstart" in div;
198 // set display to none to avoid a layout bug in IE
199 // http://dev.jquery.com/ticket/4014
200 body.removeChild( div ).style.display = "none";
203 // support: jQuery <1.8
204 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
205 $.each( [ "Width", "Height" ], function( i, name ) {
206 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
207 type = name.toLowerCase(),
209 innerWidth: $.fn.innerWidth,
210 innerHeight: $.fn.innerHeight,
211 outerWidth: $.fn.outerWidth,
212 outerHeight: $.fn.outerHeight
215 function reduce( elem, size, border, margin ) {
216 $.each( side, function() {
217 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
219 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
222 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
228 $.fn[ "inner" + name ] = function( size ) {
229 if ( size === undefined ) {
230 return orig[ "inner" + name ].call( this );
233 return this.each(function() {
234 $( this ).css( type, reduce( this, size ) + "px" );
238 $.fn[ "outer" + name] = function( size, margin ) {
239 if ( typeof size !== "number" ) {
240 return orig[ "outer" + name ].call( this, size );
243 return this.each(function() {
244 $( this).css( type, reduce( this, size, true, margin ) + "px" );
250 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
251 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
252 $.fn.removeData = (function( removeData ) {
253 return function( key ) {
254 if ( arguments.length ) {
255 return removeData.call( this, $.camelCase( key ) );
257 return removeData.call( this );
260 })( $.fn.removeData );
270 var uaMatch = /msie ([\w.]+)/.exec( navigator.userAgent.toLowerCase() ) || [];
271 $.ui.ie = uaMatch.length ? true : false;
272 $.ui.ie6 = parseFloat( uaMatch[ 1 ], 10 ) === 6;
276 disableSelection: function() {
277 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
278 ".ui-disableSelection", function( event ) {
279 event.preventDefault();
283 enableSelection: function() {
284 return this.unbind( ".ui-disableSelection" );
289 // $.ui.plugin is deprecated. Use the proxy pattern instead.
291 add: function( module, option, set ) {
293 proto = $.ui[ module ].prototype;
295 proto.plugins[ i ] = proto.plugins[ i ] || [];
296 proto.plugins[ i ].push( [ option, set[ i ] ] );
299 call: function( instance, name, args ) {
301 set = instance.plugins[ name ];
302 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
306 for ( i = 0; i < set.length; i++ ) {
307 if ( instance.options[ set[ i ][ 0 ] ] ) {
308 set[ i ][ 1 ].apply( instance.element, args );
314 contains: $.contains,
316 // only used by resizable
317 hasScroll: function( el, a ) {
319 //If overflow is hidden, the element might have extra content, but the user wants to hide it
320 if ( $( el ).css( "overflow" ) === "hidden") {
324 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
327 if ( el[ scroll ] > 0 ) {
331 // TODO: determine which cases actually cause this to happen
332 // if the element doesn't have the scroll set, see if it's possible to
335 has = ( el[ scroll ] > 0 );
340 // these are odd functions, fix the API or move into individual plugins
341 isOverAxis: function( x, reference, size ) {
342 //Determines when x coordinate is over "b" element axis
343 return ( x > reference ) && ( x < ( reference + size ) );
345 isOver: function( y, x, top, left, height, width ) {
346 //Determines when x, y coordinates is over "b" element
347 return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
352 (function( $, undefined ) {
355 slice = Array.prototype.slice,
356 _cleanData = $.cleanData;
357 $.cleanData = function( elems ) {
358 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
360 $( elem ).triggerHandler( "remove" );
361 // http://bugs.jquery.com/ticket/8235
367 $.widget = function( name, base, prototype ) {
368 var fullName, existingConstructor, constructor, basePrototype,
369 namespace = name.split( "." )[ 0 ];
371 name = name.split( "." )[ 1 ];
372 fullName = namespace + "-" + name;
379 // create selector for plugin
380 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
381 return !!$.data( elem, fullName );
384 $[ namespace ] = $[ namespace ] || {};
385 existingConstructor = $[ namespace ][ name ];
386 constructor = $[ namespace ][ name ] = function( options, element ) {
387 // allow instantiation without "new" keyword
388 if ( !this._createWidget ) {
389 return new constructor( options, element );
392 // allow instantiation without initializing for simple inheritance
393 // must use "new" keyword (the code above always passes args)
394 if ( arguments.length ) {
395 this._createWidget( options, element );
398 // extend with the existing constructor to carry over any static properties
399 $.extend( constructor, existingConstructor, {
400 version: prototype.version,
401 // copy the object used to create the prototype in case we need to
402 // redefine the widget later
403 _proto: $.extend( {}, prototype ),
404 // track widgets that inherit from this widget in case this widget is
405 // redefined after a widget inherits from it
406 _childConstructors: []
409 basePrototype = new base();
410 // we need to make the options hash a property directly on the new instance
411 // otherwise we'll modify the options hash on the prototype that we're
413 basePrototype.options = $.widget.extend( {}, basePrototype.options );
414 $.each( prototype, function( prop, value ) {
415 if ( $.isFunction( value ) ) {
416 prototype[ prop ] = (function() {
417 var _super = function() {
418 return base.prototype[ prop ].apply( this, arguments );
420 _superApply = function( args ) {
421 return base.prototype[ prop ].apply( this, args );
424 var __super = this._super,
425 __superApply = this._superApply,
428 this._super = _super;
429 this._superApply = _superApply;
431 returnValue = value.apply( this, arguments );
433 this._super = __super;
434 this._superApply = __superApply;
441 constructor.prototype = $.widget.extend( basePrototype, {
442 // TODO: remove support for widgetEventPrefix
443 // always use the name + a colon as the prefix, e.g., draggable:start
444 // don't prefix for widgets that aren't DOM-based
445 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
447 constructor: constructor,
448 namespace: namespace,
450 // TODO remove widgetBaseClass, see #8155
451 widgetBaseClass: fullName,
452 widgetFullName: fullName
455 // If this widget is being redefined then we need to find all widgets that
456 // are inheriting from it and redefine all of them so that they inherit from
457 // the new version of this widget. We're essentially trying to replace one
458 // level in the prototype chain.
459 if ( existingConstructor ) {
460 $.each( existingConstructor._childConstructors, function( i, child ) {
461 var childPrototype = child.prototype;
463 // redefine the child widget using the same prototype that was
464 // originally used, but inherit from the new version of the base
465 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
467 // remove the list of existing child constructors from the old constructor
468 // so the old child constructors can be garbage collected
469 delete existingConstructor._childConstructors;
471 base._childConstructors.push( constructor );
474 $.widget.bridge( name, constructor );
477 $.widget.extend = function( target ) {
478 var input = slice.call( arguments, 1 ),
480 inputLength = input.length,
483 for ( ; inputIndex < inputLength; inputIndex++ ) {
484 for ( key in input[ inputIndex ] ) {
485 value = input[ inputIndex ][ key ];
486 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
488 if ( $.isPlainObject( value ) ) {
489 target[ key ] = $.isPlainObject( target[ key ] ) ?
490 $.widget.extend( {}, target[ key ], value ) :
491 // Don't extend strings, arrays, etc. with objects
492 $.widget.extend( {}, value );
493 // Copy everything else by reference
495 target[ key ] = value;
503 $.widget.bridge = function( name, object ) {
504 var fullName = object.prototype.widgetFullName || name;
505 $.fn[ name ] = function( options ) {
506 var isMethodCall = typeof options === "string",
507 args = slice.call( arguments, 1 ),
510 // allow multiple hashes to be passed on init
511 options = !isMethodCall && args.length ?
512 $.widget.extend.apply( null, [ options ].concat(args) ) :
515 if ( isMethodCall ) {
516 this.each(function() {
518 instance = $.data( this, fullName );
520 return $.error( "cannot call methods on " + name + " prior to initialization; " +
521 "attempted to call method '" + options + "'" );
523 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
524 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
526 methodValue = instance[ options ].apply( instance, args );
527 if ( methodValue !== instance && methodValue !== undefined ) {
528 returnValue = methodValue && methodValue.jquery ?
529 returnValue.pushStack( methodValue.get() ) :
535 this.each(function() {
536 var instance = $.data( this, fullName );
538 instance.option( options || {} )._init();
540 $.data( this, fullName, new object( options, this ) );
549 $.Widget = function( /* options, element */ ) {};
550 $.Widget._childConstructors = [];
552 $.Widget.prototype = {
553 widgetName: "widget",
554 widgetEventPrefix: "",
555 defaultElement: "<div>",
562 _createWidget: function( options, element ) {
563 element = $( element || this.defaultElement || this )[ 0 ];
564 this.element = $( element );
566 this.eventNamespace = "." + this.widgetName + this.uuid;
567 this.options = $.widget.extend( {},
569 this._getCreateOptions(),
573 this.hoverable = $();
574 this.focusable = $();
576 if ( element !== this ) {
578 // TODO remove dual storage
579 $.data( element, this.widgetName, this );
580 $.data( element, this.widgetFullName, this );
581 this._on( true, this.element, {
582 remove: function( event ) {
583 if ( event.target === element ) {
588 this.document = $( element.style ?
589 // element within the document
590 element.ownerDocument :
591 // element is window or document
592 element.document || element );
593 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
597 this._trigger( "create", null, this._getCreateEventData() );
600 _getCreateOptions: $.noop,
601 _getCreateEventData: $.noop,
605 destroy: function() {
607 // we can probably remove the unbind calls in 2.0
608 // all event bindings should go through this._on()
610 .unbind( this.eventNamespace )
612 // TODO remove dual storage
613 .removeData( this.widgetName )
614 .removeData( this.widgetFullName )
615 // support: jquery <1.6.3
616 // http://bugs.jquery.com/ticket/9413
617 .removeData( $.camelCase( this.widgetFullName ) );
619 .unbind( this.eventNamespace )
620 .removeAttr( "aria-disabled" )
622 this.widgetFullName + "-disabled " +
623 "ui-state-disabled" );
625 // clean up events and states
626 this.bindings.unbind( this.eventNamespace );
627 this.hoverable.removeClass( "ui-state-hover" );
628 this.focusable.removeClass( "ui-state-focus" );
636 option: function( key, value ) {
642 if ( arguments.length === 0 ) {
643 // don't return a reference to the internal hash
644 return $.widget.extend( {}, this.options );
647 if ( typeof key === "string" ) {
648 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
650 parts = key.split( "." );
652 if ( parts.length ) {
653 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
654 for ( i = 0; i < parts.length - 1; i++ ) {
655 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
656 curOption = curOption[ parts[ i ] ];
659 if ( value === undefined ) {
660 return curOption[ key ] === undefined ? null : curOption[ key ];
662 curOption[ key ] = value;
664 if ( value === undefined ) {
665 return this.options[ key ] === undefined ? null : this.options[ key ];
667 options[ key ] = value;
671 this._setOptions( options );
675 _setOptions: function( options ) {
678 for ( key in options ) {
679 this._setOption( key, options[ key ] );
684 _setOption: function( key, value ) {
685 this.options[ key ] = value;
687 if ( key === "disabled" ) {
689 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
690 .attr( "aria-disabled", value );
691 this.hoverable.removeClass( "ui-state-hover" );
692 this.focusable.removeClass( "ui-state-focus" );
699 return this._setOption( "disabled", false );
701 disable: function() {
702 return this._setOption( "disabled", true );
705 _on: function( suppressDisabledCheck, element, handlers ) {
709 // no suppressDisabledCheck flag, shuffle arguments
710 if ( typeof suppressDisabledCheck !== "boolean" ) {
712 element = suppressDisabledCheck;
713 suppressDisabledCheck = false;
716 // no element argument, shuffle and use this.element
719 element = this.element;
720 delegateElement = this.widget();
722 // accept selectors, DOM elements
723 element = delegateElement = $( element );
724 this.bindings = this.bindings.add( element );
727 $.each( handlers, function( event, handler ) {
728 function handlerProxy() {
729 // allow widgets to customize the disabled handling
730 // - disabled as an array instead of boolean
731 // - disabled class as method for disabling individual parts
732 if ( !suppressDisabledCheck &&
733 ( instance.options.disabled === true ||
734 $( this ).hasClass( "ui-state-disabled" ) ) ) {
737 return ( typeof handler === "string" ? instance[ handler ] : handler )
738 .apply( instance, arguments );
741 // copy the guid so direct unbinding works
742 if ( typeof handler !== "string" ) {
743 handlerProxy.guid = handler.guid =
744 handler.guid || handlerProxy.guid || $.guid++;
747 var match = event.match( /^(\w+)\s*(.*)$/ ),
748 eventName = match[1] + instance.eventNamespace,
751 delegateElement.delegate( selector, eventName, handlerProxy );
753 element.bind( eventName, handlerProxy );
758 _off: function( element, eventName ) {
759 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
760 element.unbind( eventName ).undelegate( eventName );
763 _delay: function( handler, delay ) {
764 function handlerProxy() {
765 return ( typeof handler === "string" ? instance[ handler ] : handler )
766 .apply( instance, arguments );
769 return setTimeout( handlerProxy, delay || 0 );
772 _hoverable: function( element ) {
773 this.hoverable = this.hoverable.add( element );
775 mouseenter: function( event ) {
776 $( event.currentTarget ).addClass( "ui-state-hover" );
778 mouseleave: function( event ) {
779 $( event.currentTarget ).removeClass( "ui-state-hover" );
784 _focusable: function( element ) {
785 this.focusable = this.focusable.add( element );
787 focusin: function( event ) {
788 $( event.currentTarget ).addClass( "ui-state-focus" );
790 focusout: function( event ) {
791 $( event.currentTarget ).removeClass( "ui-state-focus" );
796 _trigger: function( type, event, data ) {
798 callback = this.options[ type ];
801 event = $.Event( event );
802 event.type = ( type === this.widgetEventPrefix ?
804 this.widgetEventPrefix + type ).toLowerCase();
805 // the original event may come from any element
806 // so we need to reset the target on the new event
807 event.target = this.element[ 0 ];
809 // copy original event properties over to the new event
810 orig = event.originalEvent;
812 for ( prop in orig ) {
813 if ( !( prop in event ) ) {
814 event[ prop ] = orig[ prop ];
819 this.element.trigger( event, data );
820 return !( $.isFunction( callback ) &&
821 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
822 event.isDefaultPrevented() );
826 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
827 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
828 if ( typeof options === "string" ) {
829 options = { effect: options };
832 effectName = !options ?
834 options === true || typeof options === "number" ?
836 options.effect || defaultEffect;
837 options = options || {};
838 if ( typeof options === "number" ) {
839 options = { duration: options };
841 hasOptions = !$.isEmptyObject( options );
842 options.complete = callback;
843 if ( options.delay ) {
844 element.delay( options.delay );
846 if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) {
847 element[ method ]( options );
848 } else if ( effectName !== method && element[ effectName ] ) {
849 element[ effectName ]( options.duration, options.easing, callback );
851 element.queue(function( next ) {
852 $( this )[ method ]();
854 callback.call( element[ 0 ] );
863 if ( $.uiBackCompat !== false ) {
864 $.Widget.prototype._getCreateOptions = function() {
865 return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
870 (function( $, undefined ) {
872 var mouseHandled = false;
873 $( document ).mouseup( function( e ) {
874 mouseHandled = false;
877 $.widget("ui.mouse", {
880 cancel: 'input,textarea,button,select,option',
884 _mouseInit: function() {
888 .bind('mousedown.'+this.widgetName, function(event) {
889 return that._mouseDown(event);
891 .bind('click.'+this.widgetName, function(event) {
892 if (true === $.data(event.target, that.widgetName + '.preventClickEvent')) {
893 $.removeData(event.target, that.widgetName + '.preventClickEvent');
894 event.stopImmediatePropagation();
899 this.started = false;
902 // TODO: make sure destroying one instance of mouse doesn't mess with
903 // other instances of mouse
904 _mouseDestroy: function() {
905 this.element.unbind('.'+this.widgetName);
906 if ( this._mouseMoveDelegate ) {
908 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
909 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
913 _mouseDown: function(event) {
914 // don't let more than one widget handle mouseStart
915 if( mouseHandled ) { return; }
917 // we may have missed mouseup (out of window)
918 (this._mouseStarted && this._mouseUp(event));
920 this._mouseDownEvent = event;
923 btnIsLeft = (event.which === 1),
924 // event.target.nodeName works around a bug in IE 8 with
925 // disabled inputs (#7620)
926 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
927 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
931 this.mouseDelayMet = !this.options.delay;
932 if (!this.mouseDelayMet) {
933 this._mouseDelayTimer = setTimeout(function() {
934 that.mouseDelayMet = true;
935 }, this.options.delay);
938 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
939 this._mouseStarted = (this._mouseStart(event) !== false);
940 if (!this._mouseStarted) {
941 event.preventDefault();
946 // Click event may never have fired (Gecko & Opera)
947 if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
948 $.removeData(event.target, this.widgetName + '.preventClickEvent');
951 // these delegates are required to keep context
952 this._mouseMoveDelegate = function(event) {
953 return that._mouseMove(event);
955 this._mouseUpDelegate = function(event) {
956 return that._mouseUp(event);
959 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
960 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
962 event.preventDefault();
968 _mouseMove: function(event) {
969 // IE mouseup check - mouseup happened when mouse was out of window
970 if ($.ui.ie && !(document.documentMode >= 9) && !event.button) {
971 return this._mouseUp(event);
974 if (this._mouseStarted) {
975 this._mouseDrag(event);
976 return event.preventDefault();
979 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
981 (this._mouseStart(this._mouseDownEvent, event) !== false);
982 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
985 return !this._mouseStarted;
988 _mouseUp: function(event) {
990 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
991 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
993 if (this._mouseStarted) {
994 this._mouseStarted = false;
996 if (event.target === this._mouseDownEvent.target) {
997 $.data(event.target, this.widgetName + '.preventClickEvent', true);
1000 this._mouseStop(event);
1006 _mouseDistanceMet: function(event) {
1008 Math.abs(this._mouseDownEvent.pageX - event.pageX),
1009 Math.abs(this._mouseDownEvent.pageY - event.pageY)
1010 ) >= this.options.distance
1014 _mouseDelayMet: function(event) {
1015 return this.mouseDelayMet;
1018 // These are placeholder methods, to be overriden by extending plugin
1019 _mouseStart: function(event) {},
1020 _mouseDrag: function(event) {},
1021 _mouseStop: function(event) {},
1022 _mouseCapture: function(event) { return true; }
1026 (function( $, undefined ) {
1030 var cachedScrollbarWidth,
1034 rhorizontal = /left|center|right/,
1035 rvertical = /top|center|bottom/,
1036 roffset = /[\+\-]\d+%?/,
1039 _position = $.fn.position;
1041 function getOffsets( offsets, width, height ) {
1043 parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1044 parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1047 function parseCss( element, property ) {
1048 return parseInt( $.css( element, property ), 10 ) || 0;
1052 scrollbarWidth: function() {
1053 if ( cachedScrollbarWidth !== undefined ) {
1054 return cachedScrollbarWidth;
1057 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1058 innerDiv = div.children()[0];
1060 $( "body" ).append( div );
1061 w1 = innerDiv.offsetWidth;
1062 div.css( "overflow", "scroll" );
1064 w2 = innerDiv.offsetWidth;
1067 w2 = div[0].clientWidth;
1072 return (cachedScrollbarWidth = w1 - w2);
1074 getScrollInfo: function( within ) {
1075 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
1076 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
1077 hasOverflowX = overflowX === "scroll" ||
1078 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1079 hasOverflowY = overflowY === "scroll" ||
1080 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1082 width: hasOverflowX ? $.position.scrollbarWidth() : 0,
1083 height: hasOverflowY ? $.position.scrollbarWidth() : 0
1086 getWithinInfo: function( element ) {
1087 var withinElement = $( element || window ),
1088 isWindow = $.isWindow( withinElement[0] );
1090 element: withinElement,
1092 offset: withinElement.offset() || { left: 0, top: 0 },
1093 scrollLeft: withinElement.scrollLeft(),
1094 scrollTop: withinElement.scrollTop(),
1095 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1096 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1101 $.fn.position = function( options ) {
1102 if ( !options || !options.of ) {
1103 return _position.apply( this, arguments );
1106 // make a copy, we don't want to modify arguments
1107 options = $.extend( {}, options );
1109 var atOffset, targetWidth, targetHeight, targetOffset, basePosition,
1110 target = $( options.of ),
1111 within = $.position.getWithinInfo( options.within ),
1112 scrollInfo = $.position.getScrollInfo( within ),
1113 targetElem = target[0],
1114 collision = ( options.collision || "flip" ).split( " " ),
1117 if ( targetElem.nodeType === 9 ) {
1118 targetWidth = target.width();
1119 targetHeight = target.height();
1120 targetOffset = { top: 0, left: 0 };
1121 } else if ( $.isWindow( targetElem ) ) {
1122 targetWidth = target.width();
1123 targetHeight = target.height();
1124 targetOffset = { top: target.scrollTop(), left: target.scrollLeft() };
1125 } else if ( targetElem.preventDefault ) {
1126 // force left top to allow flipping
1127 options.at = "left top";
1128 targetWidth = targetHeight = 0;
1129 targetOffset = { top: targetElem.pageY, left: targetElem.pageX };
1131 targetWidth = target.outerWidth();
1132 targetHeight = target.outerHeight();
1133 targetOffset = target.offset();
1135 // clone to reuse original targetOffset later
1136 basePosition = $.extend( {}, targetOffset );
1138 // force my and at to have valid horizontal and vertical positions
1139 // if a value is missing or invalid, it will be converted to center
1140 $.each( [ "my", "at" ], function() {
1141 var pos = ( options[ this ] || "" ).split( " " ),
1145 if ( pos.length === 1) {
1146 pos = rhorizontal.test( pos[ 0 ] ) ?
1147 pos.concat( [ "center" ] ) :
1148 rvertical.test( pos[ 0 ] ) ?
1149 [ "center" ].concat( pos ) :
1150 [ "center", "center" ];
1152 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1153 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1155 // calculate offsets
1156 horizontalOffset = roffset.exec( pos[ 0 ] );
1157 verticalOffset = roffset.exec( pos[ 1 ] );
1159 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1160 verticalOffset ? verticalOffset[ 0 ] : 0
1163 // reduce to just the positions without the offsets
1165 rposition.exec( pos[ 0 ] )[ 0 ],
1166 rposition.exec( pos[ 1 ] )[ 0 ]
1170 // normalize collision option
1171 if ( collision.length === 1 ) {
1172 collision[ 1 ] = collision[ 0 ];
1175 if ( options.at[ 0 ] === "right" ) {
1176 basePosition.left += targetWidth;
1177 } else if ( options.at[ 0 ] === "center" ) {
1178 basePosition.left += targetWidth / 2;
1181 if ( options.at[ 1 ] === "bottom" ) {
1182 basePosition.top += targetHeight;
1183 } else if ( options.at[ 1 ] === "center" ) {
1184 basePosition.top += targetHeight / 2;
1187 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1188 basePosition.left += atOffset[ 0 ];
1189 basePosition.top += atOffset[ 1 ];
1191 return this.each(function() {
1192 var collisionPosition, using,
1194 elemWidth = elem.outerWidth(),
1195 elemHeight = elem.outerHeight(),
1196 marginLeft = parseCss( this, "marginLeft" ),
1197 marginTop = parseCss( this, "marginTop" ),
1198 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1199 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1200 position = $.extend( {}, basePosition ),
1201 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1203 if ( options.my[ 0 ] === "right" ) {
1204 position.left -= elemWidth;
1205 } else if ( options.my[ 0 ] === "center" ) {
1206 position.left -= elemWidth / 2;
1209 if ( options.my[ 1 ] === "bottom" ) {
1210 position.top -= elemHeight;
1211 } else if ( options.my[ 1 ] === "center" ) {
1212 position.top -= elemHeight / 2;
1215 position.left += myOffset[ 0 ];
1216 position.top += myOffset[ 1 ];
1218 // if the browser doesn't support fractions, then round for consistent results
1219 if ( !$.support.offsetFractions ) {
1220 position.left = round( position.left );
1221 position.top = round( position.top );
1224 collisionPosition = {
1225 marginLeft: marginLeft,
1226 marginTop: marginTop
1229 $.each( [ "left", "top" ], function( i, dir ) {
1230 if ( $.ui.position[ collision[ i ] ] ) {
1231 $.ui.position[ collision[ i ] ][ dir ]( position, {
1232 targetWidth: targetWidth,
1233 targetHeight: targetHeight,
1234 elemWidth: elemWidth,
1235 elemHeight: elemHeight,
1236 collisionPosition: collisionPosition,
1237 collisionWidth: collisionWidth,
1238 collisionHeight: collisionHeight,
1239 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1248 if ( $.fn.bgiframe ) {
1252 if ( options.using ) {
1253 // adds feedback as second argument to using callback, if present
1254 using = function( props ) {
1255 var left = targetOffset.left - position.left,
1256 right = left + targetWidth - elemWidth,
1257 top = targetOffset.top - position.top,
1258 bottom = top + targetHeight - elemHeight,
1262 left: targetOffset.left,
1263 top: targetOffset.top,
1265 height: targetHeight
1269 left: position.left,
1274 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1275 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1277 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1278 feedback.horizontal = "center";
1280 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1281 feedback.vertical = "middle";
1283 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1284 feedback.important = "horizontal";
1286 feedback.important = "vertical";
1288 options.using.call( this, props, feedback );
1292 elem.offset( $.extend( position, { using: using } ) );
1298 left: function( position, data ) {
1299 var within = data.within,
1300 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1301 outerWidth = within.width,
1302 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1303 overLeft = withinOffset - collisionPosLeft,
1304 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1307 // element is wider than within
1308 if ( data.collisionWidth > outerWidth ) {
1309 // element is initially over the left side of within
1310 if ( overLeft > 0 && overRight <= 0 ) {
1311 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1312 position.left += overLeft - newOverRight;
1313 // element is initially over right side of within
1314 } else if ( overRight > 0 && overLeft <= 0 ) {
1315 position.left = withinOffset;
1316 // element is initially over both left and right sides of within
1318 if ( overLeft > overRight ) {
1319 position.left = withinOffset + outerWidth - data.collisionWidth;
1321 position.left = withinOffset;
1324 // too far left -> align with left edge
1325 } else if ( overLeft > 0 ) {
1326 position.left += overLeft;
1327 // too far right -> align with right edge
1328 } else if ( overRight > 0 ) {
1329 position.left -= overRight;
1330 // adjust based on position and margin
1332 position.left = max( position.left - collisionPosLeft, position.left );
1335 top: function( position, data ) {
1336 var within = data.within,
1337 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1338 outerHeight = data.within.height,
1339 collisionPosTop = position.top - data.collisionPosition.marginTop,
1340 overTop = withinOffset - collisionPosTop,
1341 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1344 // element is taller than within
1345 if ( data.collisionHeight > outerHeight ) {
1346 // element is initially over the top of within
1347 if ( overTop > 0 && overBottom <= 0 ) {
1348 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1349 position.top += overTop - newOverBottom;
1350 // element is initially over bottom of within
1351 } else if ( overBottom > 0 && overTop <= 0 ) {
1352 position.top = withinOffset;
1353 // element is initially over both top and bottom of within
1355 if ( overTop > overBottom ) {
1356 position.top = withinOffset + outerHeight - data.collisionHeight;
1358 position.top = withinOffset;
1361 // too far up -> align with top
1362 } else if ( overTop > 0 ) {
1363 position.top += overTop;
1364 // too far down -> align with bottom edge
1365 } else if ( overBottom > 0 ) {
1366 position.top -= overBottom;
1367 // adjust based on position and margin
1369 position.top = max( position.top - collisionPosTop, position.top );
1374 left: function( position, data ) {
1375 var within = data.within,
1376 withinOffset = within.offset.left + within.scrollLeft,
1377 outerWidth = within.width,
1378 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1379 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1380 overLeft = collisionPosLeft - offsetLeft,
1381 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1382 myOffset = data.my[ 0 ] === "left" ?
1384 data.my[ 0 ] === "right" ?
1387 atOffset = data.at[ 0 ] === "left" ?
1389 data.at[ 0 ] === "right" ?
1392 offset = -2 * data.offset[ 0 ],
1396 if ( overLeft < 0 ) {
1397 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1398 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1399 position.left += myOffset + atOffset + offset;
1402 else if ( overRight > 0 ) {
1403 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1404 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1405 position.left += myOffset + atOffset + offset;
1409 top: function( position, data ) {
1410 var within = data.within,
1411 withinOffset = within.offset.top + within.scrollTop,
1412 outerHeight = within.height,
1413 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1414 collisionPosTop = position.top - data.collisionPosition.marginTop,
1415 overTop = collisionPosTop - offsetTop,
1416 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1417 top = data.my[ 1 ] === "top",
1420 data.my[ 1 ] === "bottom" ?
1423 atOffset = data.at[ 1 ] === "top" ?
1425 data.at[ 1 ] === "bottom" ?
1426 -data.targetHeight :
1428 offset = -2 * data.offset[ 1 ],
1431 if ( overTop < 0 ) {
1432 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1433 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1434 position.top += myOffset + atOffset + offset;
1437 else if ( overBottom > 0 ) {
1438 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1439 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1440 position.top += myOffset + atOffset + offset;
1447 $.ui.position.flip.left.apply( this, arguments );
1448 $.ui.position.fit.left.apply( this, arguments );
1451 $.ui.position.flip.top.apply( this, arguments );
1452 $.ui.position.fit.top.apply( this, arguments );
1457 // fraction support test
1459 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1460 body = document.getElementsByTagName( "body" )[ 0 ],
1461 div = document.createElement( "div" );
1463 //Create a "fake body" for testing based on method used in jQuery.support
1464 testElement = document.createElement( body ? "div" : "body" );
1465 testElementStyle = {
1466 visibility: "hidden",
1474 $.extend( testElementStyle, {
1475 position: "absolute",
1480 for ( i in testElementStyle ) {
1481 testElement.style[ i ] = testElementStyle[ i ];
1483 testElement.appendChild( div );
1484 testElementParent = body || document.documentElement;
1485 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1487 div.style.cssText = "position: absolute; left: 10.7432222px;";
1489 offsetLeft = $( div ).offset().left;
1490 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1492 testElement.innerHTML = "";
1493 testElementParent.removeChild( testElement );
1497 if ( $.uiBackCompat !== false ) {
1500 var _position = $.fn.position;
1501 $.fn.position = function( options ) {
1502 if ( !options || !options.offset ) {
1503 return _position.call( this, options );
1505 var offset = options.offset.split( " " ),
1506 at = options.at.split( " " );
1507 if ( offset.length === 1 ) {
1508 offset[ 1 ] = offset[ 0 ];
1510 if ( /^\d/.test( offset[ 0 ] ) ) {
1511 offset[ 0 ] = "+" + offset[ 0 ];
1513 if ( /^\d/.test( offset[ 1 ] ) ) {
1514 offset[ 1 ] = "+" + offset[ 1 ];
1516 if ( at.length === 1 ) {
1517 if ( /left|center|right/.test( at[ 0 ] ) ) {
1524 return _position.call( this, $.extend( options, {
1525 at: at[ 0 ] + offset[ 0 ] + " " + at[ 1 ] + offset[ 1 ],
1533 (function( $, undefined ) {
1539 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
1540 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
1541 showProps.height = showProps.paddingTop = showProps.paddingBottom =
1542 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
1544 $.widget( "ui.accordion", {
1551 header: "> li > :first-child,> :not(li):even",
1552 heightStyle: "auto",
1554 activeHeader: "ui-icon-triangle-1-s",
1555 header: "ui-icon-triangle-1-e"
1560 beforeActivate: null
1563 _create: function() {
1564 var accordionId = this.accordionId = "ui-accordion-" +
1565 (this.element.attr( "id" ) || ++uid),
1566 options = this.options;
1568 this.prevShow = this.prevHide = $();
1569 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
1571 this.headers = this.element.find( options.header )
1572 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
1573 this._hoverable( this.headers );
1574 this._focusable( this.headers );
1577 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
1580 // don't allow collapsible: false and active: false / null
1581 if ( !options.collapsible && (options.active === false || options.active == null) ) {
1584 // handle negative values
1585 if ( options.active < 0 ) {
1586 options.active += this.headers.length;
1588 this.active = this._findActive( options.active )
1589 .addClass( "ui-accordion-header-active ui-state-active" )
1590 .toggleClass( "ui-corner-all ui-corner-top" );
1592 .addClass( "ui-accordion-content-active" )
1595 this._createIcons();
1599 this.element.attr( "role", "tablist" );
1602 .attr( "role", "tab" )
1603 .each(function( i ) {
1604 var header = $( this ),
1605 headerId = header.attr( "id" ),
1606 panel = header.next(),
1607 panelId = panel.attr( "id" );
1609 headerId = accordionId + "-header-" + i;
1610 header.attr( "id", headerId );
1613 panelId = accordionId + "-panel-" + i;
1614 panel.attr( "id", panelId );
1616 header.attr( "aria-controls", panelId );
1617 panel.attr( "aria-labelledby", headerId );
1620 .attr( "role", "tabpanel" );
1625 "aria-selected": "false",
1630 "aria-expanded": "false",
1631 "aria-hidden": "true"
1635 // make sure at least one header is in the tab order
1636 if ( !this.active.length ) {
1637 this.headers.eq( 0 ).attr( "tabIndex", 0 );
1640 "aria-selected": "true",
1645 "aria-expanded": "true",
1646 "aria-hidden": "false"
1650 this._on( this.headers, { keydown: "_keydown" });
1651 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
1652 this._setupEvents( options.event );
1655 _getCreateEventData: function() {
1657 header: this.active,
1658 content: !this.active.length ? $() : this.active.next()
1662 _createIcons: function() {
1663 var icons = this.options.icons;
1666 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
1667 .prependTo( this.headers );
1668 this.active.children( ".ui-accordion-header-icon" )
1669 .removeClass( icons.header )
1670 .addClass( icons.activeHeader );
1671 this.headers.addClass( "ui-accordion-icons" );
1675 _destroyIcons: function() {
1677 .removeClass( "ui-accordion-icons" )
1678 .children( ".ui-accordion-header-icon" )
1682 _destroy: function() {
1685 // clean up main element
1687 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
1688 .removeAttr( "role" );
1692 .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" )
1693 .removeAttr( "role" )
1694 .removeAttr( "aria-selected" )
1695 .removeAttr( "aria-controls" )
1696 .removeAttr( "tabIndex" )
1698 if ( /^ui-accordion/.test( this.id ) ) {
1699 this.removeAttribute( "id" );
1702 this._destroyIcons();
1704 // clean up content panels
1705 contents = this.headers.next()
1706 .css( "display", "" )
1707 .removeAttr( "role" )
1708 .removeAttr( "aria-expanded" )
1709 .removeAttr( "aria-hidden" )
1710 .removeAttr( "aria-labelledby" )
1711 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
1713 if ( /^ui-accordion/.test( this.id ) ) {
1714 this.removeAttribute( "id" );
1717 if ( this.options.heightStyle !== "content" ) {
1718 contents.css( "height", "" );
1722 _setOption: function( key, value ) {
1723 if ( key === "active" ) {
1724 // _activate() will handle invalid values and update this.options
1725 this._activate( value );
1729 if ( key === "event" ) {
1730 if ( this.options.event ) {
1731 this._off( this.headers, this.options.event );
1733 this._setupEvents( value );
1736 this._super( key, value );
1738 // setting collapsible: false while collapsed; open first panel
1739 if ( key === "collapsible" && !value && this.options.active === false ) {
1740 this._activate( 0 );
1743 if ( key === "icons" ) {
1744 this._destroyIcons();
1746 this._createIcons();
1750 // #5332 - opacity doesn't cascade to positioned elements in IE
1751 // so we need to add the disabled class to the headers and panels
1752 if ( key === "disabled" ) {
1753 this.headers.add( this.headers.next() )
1754 .toggleClass( "ui-state-disabled", !!value );
1758 _keydown: function( event ) {
1759 if ( event.altKey || event.ctrlKey ) {
1763 var keyCode = $.ui.keyCode,
1764 length = this.headers.length,
1765 currentIndex = this.headers.index( event.target ),
1768 switch ( event.keyCode ) {
1771 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
1775 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
1779 this._eventHandler( event );
1782 toFocus = this.headers[ 0 ];
1785 toFocus = this.headers[ length - 1 ];
1790 $( event.target ).attr( "tabIndex", -1 );
1791 $( toFocus ).attr( "tabIndex", 0 );
1793 event.preventDefault();
1797 _panelKeyDown : function( event ) {
1798 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
1799 $( event.currentTarget ).prev().focus();
1803 refresh: function() {
1804 var maxHeight, overflow,
1805 heightStyle = this.options.heightStyle,
1806 parent = this.element.parent();
1809 if ( heightStyle === "fill" ) {
1810 // IE 6 treats height like minHeight, so we need to turn off overflow
1811 // in order to get a reliable height
1812 // we use the minHeight support test because we assume that only
1813 // browsers that don't support minHeight will treat height as minHeight
1814 if ( !$.support.minHeight ) {
1815 overflow = parent.css( "overflow" );
1816 parent.css( "overflow", "hidden");
1818 maxHeight = parent.height();
1819 this.element.siblings( ":visible" ).each(function() {
1820 var elem = $( this ),
1821 position = elem.css( "position" );
1823 if ( position === "absolute" || position === "fixed" ) {
1826 maxHeight -= elem.outerHeight( true );
1829 parent.css( "overflow", overflow );
1832 this.headers.each(function() {
1833 maxHeight -= $( this ).outerHeight( true );
1838 $( this ).height( Math.max( 0, maxHeight -
1839 $( this ).innerHeight() + $( this ).height() ) );
1841 .css( "overflow", "auto" );
1842 } else if ( heightStyle === "auto" ) {
1846 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
1848 .height( maxHeight );
1852 _activate: function( index ) {
1853 var active = this._findActive( index )[ 0 ];
1855 // trying to activate the already active panel
1856 if ( active === this.active[ 0 ] ) {
1860 // trying to collapse, simulate a click on the currently active header
1861 active = active || this.active[ 0 ];
1863 this._eventHandler({
1865 currentTarget: active,
1866 preventDefault: $.noop
1870 _findActive: function( selector ) {
1871 return typeof selector === "number" ? this.headers.eq( selector ) : $();
1874 _setupEvents: function( event ) {
1879 $.each( event.split(" "), function( index, eventName ) {
1880 events[ eventName ] = "_eventHandler";
1882 this._on( this.headers, events );
1885 _eventHandler: function( event ) {
1886 var options = this.options,
1887 active = this.active,
1888 clicked = $( event.currentTarget ),
1889 clickedIsActive = clicked[ 0 ] === active[ 0 ],
1890 collapsing = clickedIsActive && options.collapsible,
1891 toShow = collapsing ? $() : clicked.next(),
1892 toHide = active.next(),
1896 newHeader: collapsing ? $() : clicked,
1900 event.preventDefault();
1903 // click on active header, but not collapsible
1904 ( clickedIsActive && !options.collapsible ) ||
1905 // allow canceling activation
1906 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
1910 options.active = collapsing ? false : this.headers.index( clicked );
1912 // when the call to ._toggle() comes after the class changes
1913 // it causes a very odd bug in IE 8 (see #6720)
1914 this.active = clickedIsActive ? $() : clicked;
1915 this._toggle( eventData );
1918 // corner classes on the previously active header stay after the animation
1919 active.removeClass( "ui-accordion-header-active ui-state-active" );
1920 if ( options.icons ) {
1921 active.children( ".ui-accordion-header-icon" )
1922 .removeClass( options.icons.activeHeader )
1923 .addClass( options.icons.header );
1926 if ( !clickedIsActive ) {
1928 .removeClass( "ui-corner-all" )
1929 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
1930 if ( options.icons ) {
1931 clicked.children( ".ui-accordion-header-icon" )
1932 .removeClass( options.icons.header )
1933 .addClass( options.icons.activeHeader );
1938 .addClass( "ui-accordion-content-active" );
1942 _toggle: function( data ) {
1943 var toShow = data.newPanel,
1944 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
1946 // handle activating a panel during the animation for another activation
1947 this.prevShow.add( this.prevHide ).stop( true, true );
1948 this.prevShow = toShow;
1949 this.prevHide = toHide;
1951 if ( this.options.animate ) {
1952 this._animate( toShow, toHide, data );
1956 this._toggleComplete( data );
1960 "aria-expanded": "false",
1961 "aria-hidden": "true"
1963 toHide.prev().attr( "aria-selected", "false" );
1964 // if we're switching panels, remove the old header from the tab order
1965 // if we're opening from collapsed state, remove the previous header from the tab order
1966 // if we're collapsing, then keep the collapsing header in the tab order
1967 if ( toShow.length && toHide.length ) {
1968 toHide.prev().attr( "tabIndex", -1 );
1969 } else if ( toShow.length ) {
1970 this.headers.filter(function() {
1971 return $( this ).attr( "tabIndex" ) === 0;
1973 .attr( "tabIndex", -1 );
1978 "aria-expanded": "true",
1979 "aria-hidden": "false"
1983 "aria-selected": "true",
1988 _animate: function( toShow, toHide, data ) {
1989 var total, easing, duration,
1992 down = toShow.length &&
1993 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
1994 animate = this.options.animate || {},
1995 options = down && animate.down || animate,
1996 complete = function() {
1997 that._toggleComplete( data );
2000 if ( typeof options === "number" ) {
2003 if ( typeof options === "string" ) {
2006 // fall back from options to animation in case of partial down settings
2007 easing = easing || options.easing || animate.easing;
2008 duration = duration || options.duration || animate.duration;
2010 if ( !toHide.length ) {
2011 return toShow.animate( showProps, duration, easing, complete );
2013 if ( !toShow.length ) {
2014 return toHide.animate( hideProps, duration, easing, complete );
2017 total = toShow.show().outerHeight();
2018 toHide.animate( hideProps, {
2021 step: function( now, fx ) {
2022 fx.now = Math.round( now );
2027 .animate( showProps, {
2031 step: function( now, fx ) {
2032 fx.now = Math.round( now );
2033 if ( fx.prop !== "height" ) {
2035 } else if ( that.options.heightStyle !== "content" ) {
2036 fx.now = Math.round( total - toHide.outerHeight() - adjust );
2043 _toggleComplete: function( data ) {
2044 var toHide = data.oldPanel;
2047 .removeClass( "ui-accordion-content-active" )
2049 .removeClass( "ui-corner-top" )
2050 .addClass( "ui-corner-all" );
2052 // Work around for rendering bug in IE (#5421)
2053 if ( toHide.length ) {
2054 toHide.parent()[0].className = toHide.parent()[0].className;
2057 this._trigger( "activate", null, data );
2064 if ( $.uiBackCompat !== false ) {
2065 // navigation options
2066 (function( $, prototype ) {
2067 $.extend( prototype.options, {
2069 navigationFilter: function() {
2070 return this.href.toLowerCase() === location.href.toLowerCase();
2074 var _create = prototype._create;
2075 prototype._create = function() {
2076 if ( this.options.navigation ) {
2078 headers = this.element.find( this.options.header ),
2079 content = headers.next(),
2080 current = headers.add( content )
2082 .filter( this.options.navigationFilter )
2085 headers.add( content ).each( function( index ) {
2086 if ( $.contains( this, current ) ) {
2087 that.options.active = Math.floor( index / 2 );
2093 _create.call( this );
2095 }( jQuery, jQuery.ui.accordion.prototype ) );
2098 (function( $, prototype ) {
2099 $.extend( prototype.options, {
2100 heightStyle: null, // remove default so we fall back to old values
2101 autoHeight: true, // use heightStyle: "auto"
2102 clearStyle: false, // use heightStyle: "content"
2103 fillSpace: false // use heightStyle: "fill"
2106 var _create = prototype._create,
2107 _setOption = prototype._setOption;
2109 $.extend( prototype, {
2110 _create: function() {
2111 this.options.heightStyle = this.options.heightStyle ||
2112 this._mergeHeightStyle();
2114 _create.call( this );
2117 _setOption: function( key ) {
2118 if ( key === "autoHeight" || key === "clearStyle" || key === "fillSpace" ) {
2119 this.options.heightStyle = this._mergeHeightStyle();
2121 _setOption.apply( this, arguments );
2124 _mergeHeightStyle: function() {
2125 var options = this.options;
2127 if ( options.fillSpace ) {
2131 if ( options.clearStyle ) {
2135 if ( options.autoHeight ) {
2140 }( jQuery, jQuery.ui.accordion.prototype ) );
2143 (function( $, prototype ) {
2144 $.extend( prototype.options.icons, {
2145 activeHeader: null, // remove default so we fall back to old values
2146 headerSelected: "ui-icon-triangle-1-s"
2149 var _createIcons = prototype._createIcons;
2150 prototype._createIcons = function() {
2151 if ( this.options.icons ) {
2152 this.options.icons.activeHeader = this.options.icons.activeHeader ||
2153 this.options.icons.headerSelected;
2155 _createIcons.call( this );
2157 }( jQuery, jQuery.ui.accordion.prototype ) );
2159 // expanded active option, activate method
2160 (function( $, prototype ) {
2161 prototype.activate = prototype._activate;
2163 var _findActive = prototype._findActive;
2164 prototype._findActive = function( index ) {
2165 if ( index === -1 ) {
2168 if ( index && typeof index !== "number" ) {
2169 index = this.headers.index( this.headers.filter( index ) );
2170 if ( index === -1 ) {
2174 return _findActive.call( this, index );
2176 }( jQuery, jQuery.ui.accordion.prototype ) );
2179 jQuery.ui.accordion.prototype.resize = jQuery.ui.accordion.prototype.refresh;
2182 (function( $, prototype ) {
2183 $.extend( prototype.options, {
2188 var _trigger = prototype._trigger;
2189 prototype._trigger = function( type, event, data ) {
2190 var ret = _trigger.apply( this, arguments );
2195 if ( type === "beforeActivate" ) {
2196 ret = _trigger.call( this, "changestart", event, {
2197 oldHeader: data.oldHeader,
2198 oldContent: data.oldPanel,
2199 newHeader: data.newHeader,
2200 newContent: data.newPanel
2202 } else if ( type === "activate" ) {
2203 ret = _trigger.call( this, "change", event, {
2204 oldHeader: data.oldHeader,
2205 oldContent: data.oldPanel,
2206 newHeader: data.newHeader,
2207 newContent: data.newPanel
2212 }( jQuery, jQuery.ui.accordion.prototype ) );
2215 // NOTE: this only provides support for "slide", "bounceslide", and easings
2216 // not the full $.ui.accordion.animations API
2217 (function( $, prototype ) {
2218 $.extend( prototype.options, {
2223 var _create = prototype._create;
2224 prototype._create = function() {
2225 var options = this.options;
2226 if ( options.animate === null ) {
2227 if ( !options.animated ) {
2228 options.animate = false;
2229 } else if ( options.animated === "slide" ) {
2230 options.animate = 300;
2231 } else if ( options.animated === "bounceslide" ) {
2235 easing: "easeOutBounce",
2240 options.animate = options.animated;
2244 _create.call( this );
2246 }( jQuery, jQuery.ui.accordion.prototype ) );
2250 (function( $, undefined ) {
2252 // used to prevent race conditions with remote data sources
2253 var requestIndex = 0;
2255 $.widget( "ui.autocomplete", {
2257 defaultElement: "<input>",
2282 _create: function() {
2283 // Some browsers only repeat keydown events, not keypress events,
2284 // so we use the suppressKeyPress flag to determine if we've already
2285 // handled the keydown event. #7269
2286 // Unfortunately the code for & in keypress is the same as the up arrow,
2287 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
2288 // events when we know the keydown event was used to modify the
2289 // search term. #7799
2290 var suppressKeyPress, suppressKeyPressRepeat, suppressInput;
2292 this.isMultiLine = this._isMultiLine();
2293 this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];
2294 this.isNewMenu = true;
2297 .addClass( "ui-autocomplete-input" )
2298 .attr( "autocomplete", "off" );
2300 this._on( this.element, {
2301 keydown: function( event ) {
2302 if ( this.element.prop( "readOnly" ) ) {
2303 suppressKeyPress = true;
2304 suppressInput = true;
2305 suppressKeyPressRepeat = true;
2309 suppressKeyPress = false;
2310 suppressInput = false;
2311 suppressKeyPressRepeat = false;
2312 var keyCode = $.ui.keyCode;
2313 switch( event.keyCode ) {
2314 case keyCode.PAGE_UP:
2315 suppressKeyPress = true;
2316 this._move( "previousPage", event );
2318 case keyCode.PAGE_DOWN:
2319 suppressKeyPress = true;
2320 this._move( "nextPage", event );
2323 suppressKeyPress = true;
2324 this._keyEvent( "previous", event );
2327 suppressKeyPress = true;
2328 this._keyEvent( "next", event );
2331 case keyCode.NUMPAD_ENTER:
2332 // when menu is open and has focus
2333 if ( this.menu.active ) {
2334 // #6055 - Opera still allows the keypress to occur
2335 // which causes forms to submit
2336 suppressKeyPress = true;
2337 event.preventDefault();
2338 this.menu.select( event );
2342 if ( this.menu.active ) {
2343 this.menu.select( event );
2346 case keyCode.ESCAPE:
2347 if ( this.menu.element.is( ":visible" ) ) {
2348 this._value( this.term );
2349 this.close( event );
2350 // Different browsers have different default behavior for escape
2351 // Single press can mean undo or clear
2352 // Double press in IE means clear the whole form
2353 event.preventDefault();
2357 suppressKeyPressRepeat = true;
2358 // search timeout should be triggered before the input value is changed
2359 this._searchTimeout( event );
2363 keypress: function( event ) {
2364 if ( suppressKeyPress ) {
2365 suppressKeyPress = false;
2366 event.preventDefault();
2369 if ( suppressKeyPressRepeat ) {
2373 // replicate some key handlers to allow them to repeat in Firefox and Opera
2374 var keyCode = $.ui.keyCode;
2375 switch( event.keyCode ) {
2376 case keyCode.PAGE_UP:
2377 this._move( "previousPage", event );
2379 case keyCode.PAGE_DOWN:
2380 this._move( "nextPage", event );
2383 this._keyEvent( "previous", event );
2386 this._keyEvent( "next", event );
2390 input: function( event ) {
2391 if ( suppressInput ) {
2392 suppressInput = false;
2393 event.preventDefault();
2396 this._searchTimeout( event );
2399 this.selectedItem = null;
2400 this.previous = this._value();
2402 blur: function( event ) {
2403 if ( this.cancelBlur ) {
2404 delete this.cancelBlur;
2408 clearTimeout( this.searching );
2409 this.close( event );
2410 this._change( event );
2415 this.menu = $( "<ul>" )
2416 .addClass( "ui-autocomplete" )
2417 .appendTo( this.document.find( this.options.appendTo || "body" )[ 0 ] )
2419 // custom key handling for now
2421 // disable ARIA support, the live region takes care of that
2424 .zIndex( this.element.zIndex() + 1 )
2428 this._on( this.menu.element, {
2429 mousedown: function( event ) {
2430 // prevent moving focus out of the text field
2431 event.preventDefault();
2433 // IE doesn't prevent moving focus even with event.preventDefault()
2434 // so we set a flag to know when we should ignore the blur event
2435 this.cancelBlur = true;
2436 this._delay(function() {
2437 delete this.cancelBlur;
2440 // clicking on the scrollbar causes focus to shift to the body
2441 // but we can't detect a mouseup or a click immediately afterward
2442 // so we have to track the next mousedown and close the menu if
2443 // the user clicks somewhere outside of the autocomplete
2444 var menuElement = this.menu.element[ 0 ];
2445 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
2446 this._delay(function() {
2448 this.document.one( "mousedown", function( event ) {
2449 if ( event.target !== that.element[ 0 ] &&
2450 event.target !== menuElement &&
2451 !$.contains( menuElement, event.target ) ) {
2458 menufocus: function( event, ui ) {
2459 // #7024 - Prevent accidental activation of menu items in Firefox
2460 if ( this.isNewMenu ) {
2461 this.isNewMenu = false;
2462 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
2465 this.document.one( "mousemove", function() {
2466 $( event.target ).trigger( event.originalEvent );
2473 // back compat for _renderItem using item.autocomplete, via #7810
2474 // TODO remove the fallback, see #8156
2475 var item = ui.item.data( "ui-autocomplete-item" ) || ui.item.data( "item.autocomplete" );
2476 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
2477 // use value to match what will end up in the input, if it was a key event
2478 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
2479 this._value( item.value );
2482 // Normally the input is populated with the item's value as the
2483 // menu is navigated, causing screen readers to notice a change and
2484 // announce the item. Since the focus event was canceled, this doesn't
2485 // happen, so we update the live region so that screen readers can
2486 // still notice the change and announce it.
2487 this.liveRegion.text( item.value );
2490 menuselect: function( event, ui ) {
2491 // back compat for _renderItem using item.autocomplete, via #7810
2492 // TODO remove the fallback, see #8156
2493 var item = ui.item.data( "ui-autocomplete-item" ) || ui.item.data( "item.autocomplete" ),
2494 previous = this.previous;
2496 // only trigger when focus was lost (click on menu)
2497 if ( this.element[0] !== this.document[0].activeElement ) {
2498 this.element.focus();
2499 this.previous = previous;
2500 // #6109 - IE triggers two focus events and the second
2501 // is asynchronous, so we need to reset the previous
2502 // term synchronously and asynchronously :-(
2503 this._delay(function() {
2504 this.previous = previous;
2505 this.selectedItem = item;
2509 if ( false !== this._trigger( "select", event, { item: item } ) ) {
2510 this._value( item.value );
2512 // reset the term after the select event
2513 // this allows custom select handling to work properly
2514 this.term = this._value();
2516 this.close( event );
2517 this.selectedItem = item;
2521 this.liveRegion = $( "<span>", {
2523 "aria-live": "polite"
2525 .addClass( "ui-helper-hidden-accessible" )
2526 .insertAfter( this.element );
2528 if ( $.fn.bgiframe ) {
2529 this.menu.element.bgiframe();
2532 // turning off autocomplete prevents the browser from remembering the
2533 // value when navigating through history, so we re-enable autocomplete
2534 // if the page is unloaded before the widget is destroyed. #7790
2535 this._on( this.window, {
2536 beforeunload: function() {
2537 this.element.removeAttr( "autocomplete" );
2542 _destroy: function() {
2543 clearTimeout( this.searching );
2545 .removeClass( "ui-autocomplete-input" )
2546 .removeAttr( "autocomplete" );
2547 this.menu.element.remove();
2548 this.liveRegion.remove();
2551 _setOption: function( key, value ) {
2552 this._super( key, value );
2553 if ( key === "source" ) {
2556 if ( key === "appendTo" ) {
2557 this.menu.element.appendTo( this.document.find( value || "body" )[0] );
2559 if ( key === "disabled" && value && this.xhr ) {
2564 _isMultiLine: function() {
2565 // Textareas are always multi-line
2566 if ( this.element.is( "textarea" ) ) {
2569 // Inputs are always single-line, even if inside a contentEditable element
2570 // IE also treats inputs as contentEditable
2571 if ( this.element.is( "input" ) ) {
2574 // All other element types are determined by whether or not they're contentEditable
2575 return this.element.prop( "isContentEditable" );
2578 _initSource: function() {
2581 if ( $.isArray(this.options.source) ) {
2582 array = this.options.source;
2583 this.source = function( request, response ) {
2584 response( $.ui.autocomplete.filter( array, request.term ) );
2586 } else if ( typeof this.options.source === "string" ) {
2587 url = this.options.source;
2588 this.source = function( request, response ) {
2596 success: function( data ) {
2605 this.source = this.options.source;
2609 _searchTimeout: function( event ) {
2610 clearTimeout( this.searching );
2611 this.searching = this._delay(function() {
2612 // only search if the value has changed
2613 if ( this.term !== this._value() ) {
2614 this.selectedItem = null;
2615 this.search( null, event );
2617 }, this.options.delay );
2620 search: function( value, event ) {
2621 value = value != null ? value : this._value();
2623 // always save the actual value, not the one passed as an argument
2624 this.term = this._value();
2626 if ( value.length < this.options.minLength ) {
2627 return this.close( event );
2630 if ( this._trigger( "search", event ) === false ) {
2634 return this._search( value );
2637 _search: function( value ) {
2639 this.element.addClass( "ui-autocomplete-loading" );
2640 this.cancelSearch = false;
2642 this.source( { term: value }, this._response() );
2645 _response: function() {
2647 index = ++requestIndex;
2649 return function( content ) {
2650 if ( index === requestIndex ) {
2651 that.__response( content );
2655 if ( !that.pending ) {
2656 that.element.removeClass( "ui-autocomplete-loading" );
2661 __response: function( content ) {
2663 content = this._normalize( content );
2665 this._trigger( "response", null, { content: content } );
2666 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
2667 this._suggest( content );
2668 this._trigger( "open" );
2670 // use ._close() instead of .close() so we don't cancel future searches
2675 close: function( event ) {
2676 this.cancelSearch = true;
2677 this._close( event );
2680 _close: function( event ) {
2681 if ( this.menu.element.is( ":visible" ) ) {
2682 this.menu.element.hide();
2684 this.isNewMenu = true;
2685 this._trigger( "close", event );
2689 _change: function( event ) {
2690 if ( this.previous !== this._value() ) {
2691 this._trigger( "change", event, { item: this.selectedItem } );
2695 _normalize: function( items ) {
2696 // assume all items have the right format when the first item is complete
2697 if ( items.length && items[0].label && items[0].value ) {
2700 return $.map( items, function( item ) {
2701 if ( typeof item === "string" ) {
2708 label: item.label || item.value,
2709 value: item.value || item.label
2714 _suggest: function( items ) {
2715 var ul = this.menu.element
2717 .zIndex( this.element.zIndex() + 1 );
2718 this._renderMenu( ul, items );
2719 this.menu.refresh();
2721 // size and position menu
2724 ul.position( $.extend({
2726 }, this.options.position ));
2728 if ( this.options.autoFocus ) {
2733 _resizeMenu: function() {
2734 var ul = this.menu.element;
2735 ul.outerWidth( Math.max(
2736 // Firefox wraps long text (possibly a rounding bug)
2737 // so we add 1px to avoid the wrapping (#7513)
2738 ul.width( "" ).outerWidth() + 1,
2739 this.element.outerWidth()
2743 _renderMenu: function( ul, items ) {
2745 $.each( items, function( index, item ) {
2746 that._renderItemData( ul, item );
2750 _renderItemData: function( ul, item ) {
2751 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
2754 _renderItem: function( ul, item ) {
2756 .append( $( "<a>" ).text( item.label ) )
2760 _move: function( direction, event ) {
2761 if ( !this.menu.element.is( ":visible" ) ) {
2762 this.search( null, event );
2765 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
2766 this.menu.isLastItem() && /^next/.test( direction ) ) {
2767 this._value( this.term );
2771 this.menu[ direction ]( event );
2774 widget: function() {
2775 return this.menu.element;
2778 _value: function() {
2779 return this.valueMethod.apply( this.element, arguments );
2782 _keyEvent: function( keyEvent, event ) {
2783 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2784 this._move( keyEvent, event );
2786 // prevents moving cursor to beginning/end of the text field in some browsers
2787 event.preventDefault();
2792 $.extend( $.ui.autocomplete, {
2793 escapeRegex: function( value ) {
2794 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
2796 filter: function(array, term) {
2797 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
2798 return $.grep( array, function(value) {
2799 return matcher.test( value.label || value.value || value );
2805 // live region extension, adding a `messages` option
2806 // NOTE: This is an experimental API. We are still investigating
2807 // a full solution for string manipulation and internationalization.
2808 $.widget( "ui.autocomplete", $.ui.autocomplete, {
2811 noResults: "No search results.",
2812 results: function( amount ) {
2813 return amount + ( amount > 1 ? " results are" : " result is" ) +
2814 " available, use up and down arrow keys to navigate.";
2819 __response: function( content ) {
2821 this._superApply( arguments );
2822 if ( this.options.disabled || this.cancelSearch ) {
2825 if ( content && content.length ) {
2826 message = this.options.messages.results( content.length );
2828 message = this.options.messages.noResults;
2830 this.liveRegion.text( message );
2836 (function( $, undefined ) {
2838 var lastActive, startXPos, startYPos, clickDragged,
2839 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
2840 stateClasses = "ui-state-hover ui-state-active ",
2841 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",
2842 formResetHandler = function() {
2843 var buttons = $( this ).find( ":ui-button" );
2844 setTimeout(function() {
2845 buttons.button( "refresh" );
2848 radioGroup = function( radio ) {
2849 var name = radio.name,
2854 radios = $( form ).find( "[name='" + name + "']" );
2856 radios = $( "[name='" + name + "']", radio.ownerDocument )
2857 .filter(function() {
2865 $.widget( "ui.button", {
2867 defaultElement: "<button>",
2877 _create: function() {
2878 this.element.closest( "form" )
2879 .unbind( "reset" + this.eventNamespace )
2880 .bind( "reset" + this.eventNamespace, formResetHandler );
2882 if ( typeof this.options.disabled !== "boolean" ) {
2883 this.options.disabled = !!this.element.prop( "disabled" );
2885 this.element.prop( "disabled", this.options.disabled );
2888 this._determineButtonType();
2889 this.hasTitle = !!this.buttonElement.attr( "title" );
2892 options = this.options,
2893 toggleButton = this.type === "checkbox" || this.type === "radio",
2894 activeClass = !toggleButton ? "ui-state-active" : "",
2895 focusClass = "ui-state-focus";
2897 if ( options.label === null ) {
2898 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
2901 this._hoverable( this.buttonElement );
2904 .addClass( baseClasses )
2905 .attr( "role", "button" )
2906 .bind( "mouseenter" + this.eventNamespace, function() {
2907 if ( options.disabled ) {
2910 if ( this === lastActive ) {
2911 $( this ).addClass( "ui-state-active" );
2914 .bind( "mouseleave" + this.eventNamespace, function() {
2915 if ( options.disabled ) {
2918 $( this ).removeClass( activeClass );
2920 .bind( "click" + this.eventNamespace, function( event ) {
2921 if ( options.disabled ) {
2922 event.preventDefault();
2923 event.stopImmediatePropagation();
2928 .bind( "focus" + this.eventNamespace, function() {
2929 // no need to check disabled, focus won't be triggered anyway
2930 that.buttonElement.addClass( focusClass );
2932 .bind( "blur" + this.eventNamespace, function() {
2933 that.buttonElement.removeClass( focusClass );
2936 if ( toggleButton ) {
2937 this.element.bind( "change" + this.eventNamespace, function() {
2938 if ( clickDragged ) {
2943 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
2944 // prevents issue where button state changes but checkbox/radio checked state
2945 // does not in Firefox (see ticket #6970)
2947 .bind( "mousedown" + this.eventNamespace, function( event ) {
2948 if ( options.disabled ) {
2951 clickDragged = false;
2952 startXPos = event.pageX;
2953 startYPos = event.pageY;
2955 .bind( "mouseup" + this.eventNamespace, function( event ) {
2956 if ( options.disabled ) {
2959 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
2960 clickDragged = true;
2965 if ( this.type === "checkbox" ) {
2966 this.buttonElement.bind( "click" + this.eventNamespace, function() {
2967 if ( options.disabled || clickDragged ) {
2970 $( this ).toggleClass( "ui-state-active" );
2971 that.buttonElement.attr( "aria-pressed", that.element[0].checked );
2973 } else if ( this.type === "radio" ) {
2974 this.buttonElement.bind( "click" + this.eventNamespace, function() {
2975 if ( options.disabled || clickDragged ) {
2978 $( this ).addClass( "ui-state-active" );
2979 that.buttonElement.attr( "aria-pressed", "true" );
2981 var radio = that.element[ 0 ];
2985 return $( this ).button( "widget" )[ 0 ];
2987 .removeClass( "ui-state-active" )
2988 .attr( "aria-pressed", "false" );
2992 .bind( "mousedown" + this.eventNamespace, function() {
2993 if ( options.disabled ) {
2996 $( this ).addClass( "ui-state-active" );
2998 that.document.one( "mouseup", function() {
3002 .bind( "mouseup" + this.eventNamespace, function() {
3003 if ( options.disabled ) {
3006 $( this ).removeClass( "ui-state-active" );
3008 .bind( "keydown" + this.eventNamespace, function(event) {
3009 if ( options.disabled ) {
3012 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
3013 $( this ).addClass( "ui-state-active" );
3016 .bind( "keyup" + this.eventNamespace, function() {
3017 $( this ).removeClass( "ui-state-active" );
3020 if ( this.buttonElement.is("a") ) {
3021 this.buttonElement.keyup(function(event) {
3022 if ( event.keyCode === $.ui.keyCode.SPACE ) {
3023 // TODO pass through original event correctly (just as 2nd argument doesn't work)
3030 // TODO: pull out $.Widget's handling for the disabled option into
3031 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
3032 // be overridden by individual plugins
3033 this._setOption( "disabled", options.disabled );
3034 this._resetButton();
3037 _determineButtonType: function() {
3038 var ancestor, labelSelector, checked;
3040 if ( this.element.is("[type=checkbox]") ) {
3041 this.type = "checkbox";
3042 } else if ( this.element.is("[type=radio]") ) {
3043 this.type = "radio";
3044 } else if ( this.element.is("input") ) {
3045 this.type = "input";
3047 this.type = "button";
3050 if ( this.type === "checkbox" || this.type === "radio" ) {
3051 // we don't search against the document in case the element
3052 // is disconnected from the DOM
3053 ancestor = this.element.parents().last();
3054 labelSelector = "label[for='" + this.element.attr("id") + "']";
3055 this.buttonElement = ancestor.find( labelSelector );
3056 if ( !this.buttonElement.length ) {
3057 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
3058 this.buttonElement = ancestor.filter( labelSelector );
3059 if ( !this.buttonElement.length ) {
3060 this.buttonElement = ancestor.find( labelSelector );
3063 this.element.addClass( "ui-helper-hidden-accessible" );
3065 checked = this.element.is( ":checked" );
3067 this.buttonElement.addClass( "ui-state-active" );
3069 this.buttonElement.prop( "aria-pressed", checked );
3071 this.buttonElement = this.element;
3075 widget: function() {
3076 return this.buttonElement;
3079 _destroy: function() {
3081 .removeClass( "ui-helper-hidden-accessible" );
3083 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
3084 .removeAttr( "role" )
3085 .removeAttr( "aria-pressed" )
3086 .html( this.buttonElement.find(".ui-button-text").html() );
3088 if ( !this.hasTitle ) {
3089 this.buttonElement.removeAttr( "title" );
3093 _setOption: function( key, value ) {
3094 this._super( key, value );
3095 if ( key === "disabled" ) {
3097 this.element.prop( "disabled", true );
3099 this.element.prop( "disabled", false );
3103 this._resetButton();
3106 refresh: function() {
3108 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
3110 if ( isDisabled !== this.options.disabled ) {
3111 this._setOption( "disabled", isDisabled );
3113 if ( this.type === "radio" ) {
3114 radioGroup( this.element[0] ).each(function() {
3115 if ( $( this ).is( ":checked" ) ) {
3116 $( this ).button( "widget" )
3117 .addClass( "ui-state-active" )
3118 .attr( "aria-pressed", "true" );
3120 $( this ).button( "widget" )
3121 .removeClass( "ui-state-active" )
3122 .attr( "aria-pressed", "false" );
3125 } else if ( this.type === "checkbox" ) {
3126 if ( this.element.is( ":checked" ) ) {
3128 .addClass( "ui-state-active" )
3129 .attr( "aria-pressed", "true" );
3132 .removeClass( "ui-state-active" )
3133 .attr( "aria-pressed", "false" );
3138 _resetButton: function() {
3139 if ( this.type === "input" ) {
3140 if ( this.options.label ) {
3141 this.element.val( this.options.label );
3145 var buttonElement = this.buttonElement.removeClass( typeClasses ),
3146 buttonText = $( "<span></span>", this.document[0] )
3147 .addClass( "ui-button-text" )
3148 .html( this.options.label )
3149 .appendTo( buttonElement.empty() )
3151 icons = this.options.icons,
3152 multipleIcons = icons.primary && icons.secondary,
3155 if ( icons.primary || icons.secondary ) {
3156 if ( this.options.text ) {
3157 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
3160 if ( icons.primary ) {
3161 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
3164 if ( icons.secondary ) {
3165 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
3168 if ( !this.options.text ) {
3169 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
3171 if ( !this.hasTitle ) {
3172 buttonElement.attr( "title", $.trim( buttonText ) );
3176 buttonClasses.push( "ui-button-text-only" );
3178 buttonElement.addClass( buttonClasses.join( " " ) );
3182 $.widget( "ui.buttonset", {
3185 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(button)"
3188 _create: function() {
3189 this.element.addClass( "ui-buttonset" );
3196 _setOption: function( key, value ) {
3197 if ( key === "disabled" ) {
3198 this.buttons.button( "option", key, value );
3201 this._super( key, value );
3204 refresh: function() {
3205 var rtl = this.element.css( "direction" ) === "rtl";
3207 this.buttons = this.element.find( this.options.items )
3208 .filter( ":ui-button" )
3209 .button( "refresh" )
3211 .not( ":ui-button" )
3215 return $( this ).button( "widget" )[ 0 ];
3217 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
3219 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
3222 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
3227 _destroy: function() {
3228 this.element.removeClass( "ui-buttonset" );
3231 return $( this ).button( "widget" )[ 0 ];
3233 .removeClass( "ui-corner-left ui-corner-right" )
3235 .button( "destroy" );
3240 (function( $, undefined ) {
3242 $.extend($.ui, { datepicker: { version: "1.9.2" } });
3244 var PROP_NAME = 'datepicker';
3245 var dpuuid = new Date().getTime();
3248 /* Date picker manager.
3249 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
3250 Settings for (groups of) date pickers are maintained in an instance object,
3251 allowing multiple different settings on the same page. */
3253 function Datepicker() {
3254 this.debug = false; // Change this to true to start debugging
3255 this._curInst = null; // The current instance in use
3256 this._keyEvent = false; // If the last event was a key event
3257 this._disabledInputs = []; // List of date picker inputs that have been disabled
3258 this._datepickerShowing = false; // True if the popup picker is showing , false if not
3259 this._inDialog = false; // True if showing within a "dialog", false if not
3260 this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
3261 this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
3262 this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
3263 this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
3264 this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
3265 this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
3266 this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
3267 this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
3268 this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
3269 this.regional = []; // Available regional settings, indexed by language code
3270 this.regional[''] = { // Default regional settings
3271 closeText: 'Done', // Display text for close link
3272 prevText: 'Prev', // Display text for previous month link
3273 nextText: 'Next', // Display text for next month link
3274 currentText: 'Today', // Display text for current month link
3275 monthNames: ['January','February','March','April','May','June',
3276 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
3277 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
3278 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
3279 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
3280 dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
3281 weekHeader: 'Wk', // Column header for week of the year
3282 dateFormat: 'mm/dd/yy', // See format options on parseDate
3283 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
3284 isRTL: false, // True if right-to-left language, false if left-to-right
3285 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
3286 yearSuffix: '' // Additional text to append to the year in the month headers
3288 this._defaults = { // Global defaults for all the date picker instances
3289 showOn: 'focus', // 'focus' for popup on focus,
3290 // 'button' for trigger button, or 'both' for either
3291 showAnim: 'fadeIn', // Name of jQuery animation for popup
3292 showOptions: {}, // Options for enhanced animations
3293 defaultDate: null, // Used when field is blank: actual date,
3294 // +/-number for offset from today, null for today
3295 appendText: '', // Display text following the input box, e.g. showing the format
3296 buttonText: '...', // Text for trigger button
3297 buttonImage: '', // URL for trigger button image
3298 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
3299 hideIfNoPrevNext: false, // True to hide next/previous month links
3300 // if not applicable, false to just disable them
3301 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
3302 gotoCurrent: false, // True if today link goes back to current selection instead
3303 changeMonth: false, // True if month can be selected directly, false if only prev/next
3304 changeYear: false, // True if year can be selected directly, false if only prev/next
3305 yearRange: 'c-10:c+10', // Range of years to display in drop-down,
3306 // either relative to today's year (-nn:+nn), relative to currently displayed year
3307 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
3308 showOtherMonths: false, // True to show dates in other months, false to leave blank
3309 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
3310 showWeek: false, // True to show week of the year, false to not show it
3311 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
3312 // takes a Date and returns the number of the week for it
3313 shortYearCutoff: '+10', // Short year values < this are in the current century,
3314 // > this are in the previous century,
3315 // string value starting with '+' for current year + value
3316 minDate: null, // The earliest selectable date, or null for no limit
3317 maxDate: null, // The latest selectable date, or null for no limit
3318 duration: 'fast', // Duration of display/closure
3319 beforeShowDay: null, // Function that takes a date and returns an array with
3320 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
3321 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
3322 beforeShow: null, // Function that takes an input field and
3323 // returns a set of custom settings for the date picker
3324 onSelect: null, // Define a callback function when a date is selected
3325 onChangeMonthYear: null, // Define a callback function when the month or year is changed
3326 onClose: null, // Define a callback function when the datepicker is closed
3327 numberOfMonths: 1, // Number of months to show at a time
3328 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
3329 stepMonths: 1, // Number of months to step back/forward
3330 stepBigMonths: 12, // Number of months to step back/forward for the big links
3331 altField: '', // Selector for an alternate field to store selected dates into
3332 altFormat: '', // The date format to use for the alternate field
3333 constrainInput: true, // The input is constrained by the current date format
3334 showButtonPanel: false, // True to show button panel, false to not show it
3335 autoSize: false, // True to size the input for the date format, false to leave as is
3336 disabled: false // The initial disabled state
3338 $.extend(this._defaults, this.regional['']);
3339 this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
3342 $.extend(Datepicker.prototype, {
3343 /* Class name added to elements to indicate already configured with a date picker. */
3344 markerClassName: 'hasDatepicker',
3346 //Keep track of the maximum number of rows displayed (see #7043)
3349 /* Debug logging (if enabled). */
3352 console.log.apply('', arguments);
3355 // TODO rename to "widget" when switching to widget factory
3356 _widgetDatepicker: function() {
3360 /* Override the default settings for all instances of the date picker.
3361 @param settings object - the new settings to use as defaults (anonymous object)
3362 @return the manager object */
3363 setDefaults: function(settings) {
3364 extendRemove(this._defaults, settings || {});
3368 /* Attach the date picker to a jQuery selection.
3369 @param target element - the target input field or division or span
3370 @param settings object - the new settings to use for this date picker instance (anonymous) */
3371 _attachDatepicker: function(target, settings) {
3372 // check for settings on the control itself - in namespace 'date:'
3373 var inlineSettings = null;
3374 for (var attrName in this._defaults) {
3375 var attrValue = target.getAttribute('date:' + attrName);
3377 inlineSettings = inlineSettings || {};
3379 inlineSettings[attrName] = eval(attrValue);
3381 inlineSettings[attrName] = attrValue;
3385 var nodeName = target.nodeName.toLowerCase();
3386 var inline = (nodeName == 'div' || nodeName == 'span');
3389 target.id = 'dp' + this.uuid;
3391 var inst = this._newInst($(target), inline);
3392 inst.settings = $.extend({}, settings || {}, inlineSettings || {});
3393 if (nodeName == 'input') {
3394 this._connectDatepicker(target, inst);
3395 } else if (inline) {
3396 this._inlineDatepicker(target, inst);
3400 /* Create a new instance object. */
3401 _newInst: function(target, inline) {
3402 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
3403 return {id: id, input: target, // associated target
3404 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
3405 drawMonth: 0, drawYear: 0, // month being drawn
3406 inline: inline, // is datepicker inline or not
3407 dpDiv: (!inline ? this.dpDiv : // presentation div
3408 bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
3411 /* Attach the date picker to an input field. */
3412 _connectDatepicker: function(target, inst) {
3413 var input = $(target);
3414 inst.append = $([]);
3415 inst.trigger = $([]);
3416 if (input.hasClass(this.markerClassName))
3418 this._attachments(input, inst);
3419 input.addClass(this.markerClassName).keydown(this._doKeyDown).
3420 keypress(this._doKeyPress).keyup(this._doKeyUp).
3421 bind("setData.datepicker", function(event, key, value) {
3422 inst.settings[key] = value;
3423 }).bind("getData.datepicker", function(event, key) {
3424 return this._get(inst, key);
3426 this._autoSize(inst);
3427 $.data(target, PROP_NAME, inst);
3428 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
3429 if( inst.settings.disabled ) {
3430 this._disableDatepicker( target );
3434 /* Make attachments based on settings. */
3435 _attachments: function(input, inst) {
3436 var appendText = this._get(inst, 'appendText');
3437 var isRTL = this._get(inst, 'isRTL');
3439 inst.append.remove();
3441 inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
3442 input[isRTL ? 'before' : 'after'](inst.append);
3444 input.unbind('focus', this._showDatepicker);
3446 inst.trigger.remove();
3447 var showOn = this._get(inst, 'showOn');
3448 if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
3449 input.focus(this._showDatepicker);
3450 if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
3451 var buttonText = this._get(inst, 'buttonText');
3452 var buttonImage = this._get(inst, 'buttonImage');
3453 inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
3454 $('<img/>').addClass(this._triggerClass).
3455 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
3456 $('<button type="button"></button>').addClass(this._triggerClass).
3457 html(buttonImage == '' ? buttonText : $('<img/>').attr(
3458 { src:buttonImage, alt:buttonText, title:buttonText })));
3459 input[isRTL ? 'before' : 'after'](inst.trigger);
3460 inst.trigger.click(function() {
3461 if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
3462 $.datepicker._hideDatepicker();
3463 else if ($.datepicker._datepickerShowing && $.datepicker._lastInput != input[0]) {
3464 $.datepicker._hideDatepicker();
3465 $.datepicker._showDatepicker(input[0]);
3467 $.datepicker._showDatepicker(input[0]);
3473 /* Apply the maximum length for the date format. */
3474 _autoSize: function(inst) {
3475 if (this._get(inst, 'autoSize') && !inst.inline) {
3476 var date = new Date(2009, 12 - 1, 20); // Ensure double digits
3477 var dateFormat = this._get(inst, 'dateFormat');
3478 if (dateFormat.match(/[DM]/)) {
3479 var findMax = function(names) {
3482 for (var i = 0; i < names.length; i++) {
3483 if (names[i].length > max) {
3484 max = names[i].length;
3490 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
3491 'monthNames' : 'monthNamesShort'))));
3492 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
3493 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
3495 inst.input.attr('size', this._formatDate(inst, date).length);
3499 /* Attach an inline date picker to a div. */
3500 _inlineDatepicker: function(target, inst) {
3501 var divSpan = $(target);
3502 if (divSpan.hasClass(this.markerClassName))
3504 divSpan.addClass(this.markerClassName).append(inst.dpDiv).
3505 bind("setData.datepicker", function(event, key, value){
3506 inst.settings[key] = value;
3507 }).bind("getData.datepicker", function(event, key){
3508 return this._get(inst, key);
3510 $.data(target, PROP_NAME, inst);
3511 this._setDate(inst, this._getDefaultDate(inst), true);
3512 this._updateDatepicker(inst);
3513 this._updateAlternate(inst);
3514 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
3515 if( inst.settings.disabled ) {
3516 this._disableDatepicker( target );
3518 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
3519 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
3520 inst.dpDiv.css( "display", "block" );
3523 /* Pop-up the date picker in a "dialog" box.
3524 @param input element - ignored
3525 @param date string or Date - the initial date to display
3526 @param onSelect function - the function to call when a date is selected
3527 @param settings object - update the dialog date picker instance's settings (anonymous object)
3528 @param pos int[2] - coordinates for the dialog's position within the screen or
3529 event - with x/y coordinates or
3530 leave empty for default (screen centre)
3531 @return the manager object */
3532 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
3533 var inst = this._dialogInst; // internal instance
3536 var id = 'dp' + this.uuid;
3537 this._dialogInput = $('<input type="text" id="' + id +
3538 '" style="position: absolute; top: -100px; width: 0px;"/>');
3539 this._dialogInput.keydown(this._doKeyDown);
3540 $('body').append(this._dialogInput);
3541 inst = this._dialogInst = this._newInst(this._dialogInput, false);
3543 $.data(this._dialogInput[0], PROP_NAME, inst);
3545 extendRemove(inst.settings, settings || {});
3546 date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
3547 this._dialogInput.val(date);
3549 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
3551 var browserWidth = document.documentElement.clientWidth;
3552 var browserHeight = document.documentElement.clientHeight;
3553 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
3554 var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
3555 this._pos = // should use actual width/height below
3556 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
3559 // move input on screen for focus, but hidden behind dialog
3560 this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
3561 inst.settings.onSelect = onSelect;
3562 this._inDialog = true;
3563 this.dpDiv.addClass(this._dialogClass);
3564 this._showDatepicker(this._dialogInput[0]);
3566 $.blockUI(this.dpDiv);
3567 $.data(this._dialogInput[0], PROP_NAME, inst);
3571 /* Detach a datepicker from its control.
3572 @param target element - the target input field or division or span */
3573 _destroyDatepicker: function(target) {
3574 var $target = $(target);
3575 var inst = $.data(target, PROP_NAME);
3576 if (!$target.hasClass(this.markerClassName)) {
3579 var nodeName = target.nodeName.toLowerCase();
3580 $.removeData(target, PROP_NAME);
3581 if (nodeName == 'input') {
3582 inst.append.remove();
3583 inst.trigger.remove();
3584 $target.removeClass(this.markerClassName).
3585 unbind('focus', this._showDatepicker).
3586 unbind('keydown', this._doKeyDown).
3587 unbind('keypress', this._doKeyPress).
3588 unbind('keyup', this._doKeyUp);
3589 } else if (nodeName == 'div' || nodeName == 'span')
3590 $target.removeClass(this.markerClassName).empty();
3593 /* Enable the date picker to a jQuery selection.
3594 @param target element - the target input field or division or span */
3595 _enableDatepicker: function(target) {
3596 var $target = $(target);
3597 var inst = $.data(target, PROP_NAME);
3598 if (!$target.hasClass(this.markerClassName)) {
3601 var nodeName = target.nodeName.toLowerCase();
3602 if (nodeName == 'input') {
3603 target.disabled = false;
3604 inst.trigger.filter('button').
3605 each(function() { this.disabled = false; }).end().
3606 filter('img').css({opacity: '1.0', cursor: ''});
3608 else if (nodeName == 'div' || nodeName == 'span') {
3609 var inline = $target.children('.' + this._inlineClass);
3610 inline.children().removeClass('ui-state-disabled');
3611 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
3612 prop("disabled", false);
3614 this._disabledInputs = $.map(this._disabledInputs,
3615 function(value) { return (value == target ? null : value); }); // delete entry
3618 /* Disable the date picker to a jQuery selection.
3619 @param target element - the target input field or division or span */
3620 _disableDatepicker: function(target) {
3621 var $target = $(target);
3622 var inst = $.data(target, PROP_NAME);
3623 if (!$target.hasClass(this.markerClassName)) {
3626 var nodeName = target.nodeName.toLowerCase();
3627 if (nodeName == 'input') {
3628 target.disabled = true;
3629 inst.trigger.filter('button').
3630 each(function() { this.disabled = true; }).end().
3631 filter('img').css({opacity: '0.5', cursor: 'default'});
3633 else if (nodeName == 'div' || nodeName == 'span') {
3634 var inline = $target.children('.' + this._inlineClass);
3635 inline.children().addClass('ui-state-disabled');
3636 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
3637 prop("disabled", true);
3639 this._disabledInputs = $.map(this._disabledInputs,
3640 function(value) { return (value == target ? null : value); }); // delete entry
3641 this._disabledInputs[this._disabledInputs.length] = target;
3644 /* Is the first field in a jQuery collection disabled as a datepicker?
3645 @param target element - the target input field or division or span
3646 @return boolean - true if disabled, false if enabled */
3647 _isDisabledDatepicker: function(target) {
3651 for (var i = 0; i < this._disabledInputs.length; i++) {
3652 if (this._disabledInputs[i] == target)
3658 /* Retrieve the instance data for the target control.
3659 @param target element - the target input field or division or span
3660 @return object - the associated instance data
3661 @throws error if a jQuery problem getting data */
3662 _getInst: function(target) {
3664 return $.data(target, PROP_NAME);
3667 throw 'Missing instance data for this datepicker';
3671 /* Update or retrieve the settings for a date picker attached to an input field or division.
3672 @param target element - the target input field or division or span
3673 @param name object - the new settings to update or
3674 string - the name of the setting to change or retrieve,
3675 when retrieving also 'all' for all instance settings or
3676 'defaults' for all global defaults
3677 @param value any - the new value for the setting
3678 (omit if above is an object or to retrieve a value) */
3679 _optionDatepicker: function(target, name, value) {
3680 var inst = this._getInst(target);
3681 if (arguments.length == 2 && typeof name == 'string') {
3682 return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
3683 (inst ? (name == 'all' ? $.extend({}, inst.settings) :
3684 this._get(inst, name)) : null));
3686 var settings = name || {};
3687 if (typeof name == 'string') {
3689 settings[name] = value;
3692 if (this._curInst == inst) {
3693 this._hideDatepicker();
3695 var date = this._getDateDatepicker(target, true);
3696 var minDate = this._getMinMaxDate(inst, 'min');
3697 var maxDate = this._getMinMaxDate(inst, 'max');
3698 extendRemove(inst.settings, settings);
3699 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
3700 if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
3701 inst.settings.minDate = this._formatDate(inst, minDate);
3702 if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
3703 inst.settings.maxDate = this._formatDate(inst, maxDate);
3704 this._attachments($(target), inst);
3705 this._autoSize(inst);
3706 this._setDate(inst, date);
3707 this._updateAlternate(inst);
3708 this._updateDatepicker(inst);
3712 // change method deprecated
3713 _changeDatepicker: function(target, name, value) {
3714 this._optionDatepicker(target, name, value);
3717 /* Redraw the date picker attached to an input field or division.
3718 @param target element - the target input field or division or span */
3719 _refreshDatepicker: function(target) {
3720 var inst = this._getInst(target);
3722 this._updateDatepicker(inst);
3726 /* Set the dates for a jQuery selection.
3727 @param target element - the target input field or division or span
3728 @param date Date - the new date */
3729 _setDateDatepicker: function(target, date) {
3730 var inst = this._getInst(target);
3732 this._setDate(inst, date);
3733 this._updateDatepicker(inst);
3734 this._updateAlternate(inst);
3738 /* Get the date(s) for the first entry in a jQuery selection.
3739 @param target element - the target input field or division or span
3740 @param noDefault boolean - true if no default date is to be used
3741 @return Date - the current date */
3742 _getDateDatepicker: function(target, noDefault) {
3743 var inst = this._getInst(target);
3744 if (inst && !inst.inline)
3745 this._setDateFromField(inst, noDefault);
3746 return (inst ? this._getDate(inst) : null);
3749 /* Handle keystrokes. */
3750 _doKeyDown: function(event) {
3751 var inst = $.datepicker._getInst(event.target);
3753 var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
3754 inst._keyEvent = true;
3755 if ($.datepicker._datepickerShowing)
3756 switch (event.keyCode) {
3757 case 9: $.datepicker._hideDatepicker();
3759 break; // hide on tab out
3760 case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
3761 $.datepicker._currentClass + ')', inst.dpDiv);
3763 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
3764 var onSelect = $.datepicker._get(inst, 'onSelect');
3766 var dateStr = $.datepicker._formatDate(inst);
3768 // trigger custom callback
3769 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
3772 $.datepicker._hideDatepicker();
3773 return false; // don't submit the form
3774 break; // select the value on enter
3775 case 27: $.datepicker._hideDatepicker();
3776 break; // hide on escape
3777 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3778 -$.datepicker._get(inst, 'stepBigMonths') :
3779 -$.datepicker._get(inst, 'stepMonths')), 'M');
3780 break; // previous month/year on page up/+ ctrl
3781 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3782 +$.datepicker._get(inst, 'stepBigMonths') :
3783 +$.datepicker._get(inst, 'stepMonths')), 'M');
3784 break; // next month/year on page down/+ ctrl
3785 case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
3786 handled = event.ctrlKey || event.metaKey;
3787 break; // clear on ctrl or command +end
3788 case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
3789 handled = event.ctrlKey || event.metaKey;
3790 break; // current on ctrl or command +home
3791 case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
3792 handled = event.ctrlKey || event.metaKey;
3793 // -1 day on ctrl or command +left
3794 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3795 -$.datepicker._get(inst, 'stepBigMonths') :
3796 -$.datepicker._get(inst, 'stepMonths')), 'M');
3797 // next month/year on alt +left on Mac
3799 case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
3800 handled = event.ctrlKey || event.metaKey;
3801 break; // -1 week on ctrl or command +up
3802 case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
3803 handled = event.ctrlKey || event.metaKey;
3804 // +1 day on ctrl or command +right
3805 if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
3806 +$.datepicker._get(inst, 'stepBigMonths') :
3807 +$.datepicker._get(inst, 'stepMonths')), 'M');
3808 // next month/year on alt +right
3810 case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
3811 handled = event.ctrlKey || event.metaKey;
3812 break; // +1 week on ctrl or command +down
3813 default: handled = false;
3815 else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
3816 $.datepicker._showDatepicker(this);
3821 event.preventDefault();
3822 event.stopPropagation();
3826 /* Filter entered characters - based on date format. */
3827 _doKeyPress: function(event) {
3828 var inst = $.datepicker._getInst(event.target);
3829 if ($.datepicker._get(inst, 'constrainInput')) {
3830 var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
3831 var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
3832 return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
3836 /* Synchronise manual entry and field/alternate field. */
3837 _doKeyUp: function(event) {
3838 var inst = $.datepicker._getInst(event.target);
3839 if (inst.input.val() != inst.lastVal) {
3841 var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
3842 (inst.input ? inst.input.val() : null),
3843 $.datepicker._getFormatConfig(inst));
3844 if (date) { // only if valid
3845 $.datepicker._setDateFromField(inst);
3846 $.datepicker._updateAlternate(inst);
3847 $.datepicker._updateDatepicker(inst);
3851 $.datepicker.log(err);
3857 /* Pop-up the date picker for a given input field.
3858 If false returned from beforeShow event handler do not show.
3859 @param input element - the input field attached to the date picker or
3860 event - if triggered by focus */
3861 _showDatepicker: function(input) {
3862 input = input.target || input;
3863 if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
3864 input = $('input', input.parentNode)[0];
3865 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
3867 var inst = $.datepicker._getInst(input);
3868 if ($.datepicker._curInst && $.datepicker._curInst != inst) {
3869 $.datepicker._curInst.dpDiv.stop(true, true);
3870 if ( inst && $.datepicker._datepickerShowing ) {
3871 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
3874 var beforeShow = $.datepicker._get(inst, 'beforeShow');
3875 var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
3876 if(beforeShowSettings === false){
3880 extendRemove(inst.settings, beforeShowSettings);
3881 inst.lastVal = null;
3882 $.datepicker._lastInput = input;
3883 $.datepicker._setDateFromField(inst);
3884 if ($.datepicker._inDialog) // hide cursor
3886 if (!$.datepicker._pos) { // position below input
3887 $.datepicker._pos = $.datepicker._findPos(input);
3888 $.datepicker._pos[1] += input.offsetHeight; // add the height
3890 var isFixed = false;
3891 $(input).parents().each(function() {
3892 isFixed |= $(this).css('position') == 'fixed';
3895 var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
3896 $.datepicker._pos = null;
3897 //to avoid flashes on Firefox
3899 // determine sizing offscreen
3900 inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
3901 $.datepicker._updateDatepicker(inst);
3902 // fix width for dynamic number of date pickers
3903 // and adjust position before showing
3904 offset = $.datepicker._checkOffset(inst, offset, isFixed);
3905 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
3906 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
3907 left: offset.left + 'px', top: offset.top + 'px'});
3909 var showAnim = $.datepicker._get(inst, 'showAnim');
3910 var duration = $.datepicker._get(inst, 'duration');
3911 var postProcess = function() {
3912 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
3913 if( !! cover.length ){
3914 var borders = $.datepicker._getBorders(inst.dpDiv);
3915 cover.css({left: -borders[0], top: -borders[1],
3916 width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
3919 inst.dpDiv.zIndex($(input).zIndex()+1);
3920 $.datepicker._datepickerShowing = true;
3922 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
3923 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
3924 inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
3926 inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
3927 if (!showAnim || !duration)
3929 if (inst.input.is(':visible') && !inst.input.is(':disabled'))
3931 $.datepicker._curInst = inst;
3935 /* Generate the date picker content. */
3936 _updateDatepicker: function(inst) {
3937 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
3938 var borders = $.datepicker._getBorders(inst.dpDiv);
3939 instActive = inst; // for delegate hover events
3940 inst.dpDiv.empty().append(this._generateHTML(inst));
3941 this._attachHandlers(inst);
3942 var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
3943 if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
3944 cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
3946 inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
3947 var numMonths = this._getNumberOfMonths(inst);
3948 var cols = numMonths[1];
3950 inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
3952 inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
3953 inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
3954 'Class']('ui-datepicker-multi');
3955 inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
3956 'Class']('ui-datepicker-rtl');
3957 if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
3958 // #6694 - don't focus the input if it's already focused
3959 // this breaks the change event in IE
3960 inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
3962 // deffered render of the years select (to avoid flashes on Firefox)
3963 if( inst.yearshtml ){
3964 var origyearshtml = inst.yearshtml;
3965 setTimeout(function(){
3966 //assure that inst.yearshtml didn't change.
3967 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
3968 inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
3970 origyearshtml = inst.yearshtml = null;
3975 /* Retrieve the size of left and top borders for an element.
3976 @param elem (jQuery object) the element of interest
3977 @return (number[2]) the left and top borders */
3978 _getBorders: function(elem) {
3979 var convert = function(value) {
3980 return {thin: 1, medium: 2, thick: 3}[value] || value;
3982 return [parseFloat(convert(elem.css('border-left-width'))),
3983 parseFloat(convert(elem.css('border-top-width')))];
3986 /* Check positioning to remain on screen. */
3987 _checkOffset: function(inst, offset, isFixed) {
3988 var dpWidth = inst.dpDiv.outerWidth();
3989 var dpHeight = inst.dpDiv.outerHeight();
3990 var inputWidth = inst.input ? inst.input.outerWidth() : 0;
3991 var inputHeight = inst.input ? inst.input.outerHeight() : 0;
3992 var viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft());
3993 var viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
3995 offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
3996 offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
3997 offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
3999 // now check if datepicker is showing outside window viewport - move to a better place if so.
4000 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
4001 Math.abs(offset.left + dpWidth - viewWidth) : 0);
4002 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
4003 Math.abs(dpHeight + inputHeight) : 0);
4008 /* Find an object's position on the screen. */
4009 _findPos: function(obj) {
4010 var inst = this._getInst(obj);
4011 var isRTL = this._get(inst, 'isRTL');
4012 while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
4013 obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
4015 var position = $(obj).offset();
4016 return [position.left, position.top];
4019 /* Hide the date picker from view.
4020 @param input element - the input field attached to the date picker */
4021 _hideDatepicker: function(input) {
4022 var inst = this._curInst;
4023 if (!inst || (input && inst != $.data(input, PROP_NAME)))
4025 if (this._datepickerShowing) {
4026 var showAnim = this._get(inst, 'showAnim');
4027 var duration = this._get(inst, 'duration');
4028 var postProcess = function() {
4029 $.datepicker._tidyDialog(inst);
4032 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
4033 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
4034 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
4036 inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
4037 (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
4040 this._datepickerShowing = false;
4041 var onClose = this._get(inst, 'onClose');
4043 onClose.apply((inst.input ? inst.input[0] : null),
4044 [(inst.input ? inst.input.val() : ''), inst]);
4045 this._lastInput = null;
4046 if (this._inDialog) {
4047 this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
4050 $('body').append(this.dpDiv);
4053 this._inDialog = false;
4057 /* Tidy up after a dialog display. */
4058 _tidyDialog: function(inst) {
4059 inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
4062 /* Close date picker if clicked elsewhere. */
4063 _checkExternalClick: function(event) {
4064 if (!$.datepicker._curInst)
4067 var $target = $(event.target),
4068 inst = $.datepicker._getInst($target[0]);
4070 if ( ( ( $target[0].id != $.datepicker._mainDivId &&
4071 $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
4072 !$target.hasClass($.datepicker.markerClassName) &&
4073 !$target.closest("." + $.datepicker._triggerClass).length &&
4074 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
4075 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
4076 $.datepicker._hideDatepicker();
4079 /* Adjust one of the date sub-fields. */
4080 _adjustDate: function(id, offset, period) {
4082 var inst = this._getInst(target[0]);
4083 if (this._isDisabledDatepicker(target[0])) {
4086 this._adjustInstDate(inst, offset +
4087 (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
4089 this._updateDatepicker(inst);
4092 /* Action for current link. */
4093 _gotoToday: function(id) {
4095 var inst = this._getInst(target[0]);
4096 if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
4097 inst.selectedDay = inst.currentDay;
4098 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
4099 inst.drawYear = inst.selectedYear = inst.currentYear;
4102 var date = new Date();
4103 inst.selectedDay = date.getDate();
4104 inst.drawMonth = inst.selectedMonth = date.getMonth();
4105 inst.drawYear = inst.selectedYear = date.getFullYear();
4107 this._notifyChange(inst);
4108 this._adjustDate(target);
4111 /* Action for selecting a new month/year. */
4112 _selectMonthYear: function(id, select, period) {
4114 var inst = this._getInst(target[0]);
4115 inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
4116 inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
4117 parseInt(select.options[select.selectedIndex].value,10);
4118 this._notifyChange(inst);
4119 this._adjustDate(target);
4122 /* Action for selecting a day. */
4123 _selectDay: function(id, month, year, td) {
4125 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
4128 var inst = this._getInst(target[0]);
4129 inst.selectedDay = inst.currentDay = $('a', td).html();
4130 inst.selectedMonth = inst.currentMonth = month;
4131 inst.selectedYear = inst.currentYear = year;
4132 this._selectDate(id, this._formatDate(inst,
4133 inst.currentDay, inst.currentMonth, inst.currentYear));
4136 /* Erase the input field and hide the date picker. */
4137 _clearDate: function(id) {
4139 var inst = this._getInst(target[0]);
4140 this._selectDate(target, '');
4143 /* Update the input field with the selected date. */
4144 _selectDate: function(id, dateStr) {
4146 var inst = this._getInst(target[0]);
4147 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
4149 inst.input.val(dateStr);
4150 this._updateAlternate(inst);
4151 var onSelect = this._get(inst, 'onSelect');
4153 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
4154 else if (inst.input)
4155 inst.input.trigger('change'); // fire the change event
4157 this._updateDatepicker(inst);
4159 this._hideDatepicker();
4160 this._lastInput = inst.input[0];
4161 if (typeof(inst.input[0]) != 'object')
4162 inst.input.focus(); // restore focus
4163 this._lastInput = null;
4167 /* Update any alternate field to synchronise with the main field. */
4168 _updateAlternate: function(inst) {
4169 var altField = this._get(inst, 'altField');
4170 if (altField) { // update alternate field too
4171 var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
4172 var date = this._getDate(inst);
4173 var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
4174 $(altField).each(function() { $(this).val(dateStr); });
4178 /* Set as beforeShowDay function to prevent selection of weekends.
4179 @param date Date - the date to customise
4180 @return [boolean, string] - is this date selectable?, what is its CSS class? */
4181 noWeekends: function(date) {
4182 var day = date.getDay();
4183 return [(day > 0 && day < 6), ''];
4186 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
4187 @param date Date - the date to get the week for
4188 @return number - the number of the week within the year that contains this date */
4189 iso8601Week: function(date) {
4190 var checkDate = new Date(date.getTime());
4191 // Find Thursday of this week starting on Monday
4192 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
4193 var time = checkDate.getTime();
4194 checkDate.setMonth(0); // Compare with Jan 1
4195 checkDate.setDate(1);
4196 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
4199 /* Parse a string value into a date object.
4200 See formatDate below for the possible formats.
4202 @param format string - the expected format of the date
4203 @param value string - the date in the above format
4204 @param settings Object - attributes include:
4205 shortYearCutoff number - the cutoff year for determining the century (optional)
4206 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
4207 dayNames string[7] - names of the days from Sunday (optional)
4208 monthNamesShort string[12] - abbreviated names of the months (optional)
4209 monthNames string[12] - names of the months (optional)
4210 @return Date - the extracted date value or null if value is blank */
4211 parseDate: function (format, value, settings) {
4212 if (format == null || value == null)
4213 throw 'Invalid arguments';
4214 value = (typeof value == 'object' ? value.toString() : value + '');
4217 var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
4218 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
4219 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
4220 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
4221 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
4222 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
4223 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
4228 var literal = false;
4229 // Check whether a format character is doubled
4230 var lookAhead = function(match) {
4231 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
4236 // Extract a number from the string value
4237 var getNumber = function(match) {
4238 var isDoubled = lookAhead(match);
4239 var size = (match == '@' ? 14 : (match == '!' ? 20 :
4240 (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
4241 var digits = new RegExp('^\\d{1,' + size + '}');
4242 var num = value.substring(iValue).match(digits);
4244 throw 'Missing number at position ' + iValue;
4245 iValue += num[0].length;
4246 return parseInt(num[0], 10);
4248 // Extract a name from the string value and convert to an index
4249 var getName = function(match, shortNames, longNames) {
4250 var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
4252 }).sort(function (a, b) {
4253 return -(a[1].length - b[1].length);
4256 $.each(names, function (i, pair) {
4258 if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
4260 iValue += name.length;
4267 throw 'Unknown name at position ' + iValue;
4269 // Confirm that a literal character matches the string value
4270 var checkLiteral = function() {
4271 if (value.charAt(iValue) != format.charAt(iFormat))
4272 throw 'Unexpected literal at position ' + iValue;
4276 for (var iFormat = 0; iFormat < format.length; iFormat++) {
4278 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
4283 switch (format.charAt(iFormat)) {
4285 day = getNumber('d');
4288 getName('D', dayNamesShort, dayNames);
4291 doy = getNumber('o');
4294 month = getNumber('m');
4297 month = getName('M', monthNamesShort, monthNames);
4300 year = getNumber('y');
4303 var date = new Date(getNumber('@'));
4304 year = date.getFullYear();
4305 month = date.getMonth() + 1;
4306 day = date.getDate();
4309 var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
4310 year = date.getFullYear();
4311 month = date.getMonth() + 1;
4312 day = date.getDate();
4324 if (iValue < value.length){
4325 var extra = value.substr(iValue);
4326 if (!/^\s+/.test(extra)) {
4327 throw "Extra/unparsed characters found in date: " + extra;
4331 year = new Date().getFullYear();
4332 else if (year < 100)
4333 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
4334 (year <= shortYearCutoff ? 0 : -100);
4339 var dim = this._getDaysInMonth(year, month - 1);
4346 var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
4347 if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
4348 throw 'Invalid date'; // E.g. 31/02/00
4352 /* Standard date formats. */
4353 ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
4354 COOKIE: 'D, dd M yy',
4355 ISO_8601: 'yy-mm-dd',
4356 RFC_822: 'D, d M y',
4357 RFC_850: 'DD, dd-M-y',
4358 RFC_1036: 'D, d M y',
4359 RFC_1123: 'D, d M yy',
4360 RFC_2822: 'D, d M yy',
4361 RSS: 'D, d M y', // RFC 822
4364 W3C: 'yy-mm-dd', // ISO 8601
4366 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
4367 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
4369 /* Format a date object into a string value.
4370 The format can be combinations of the following:
4371 d - day of month (no leading zero)
4372 dd - day of month (two digit)
4373 o - day of year (no leading zeros)
4374 oo - day of year (three digit)
4377 m - month of year (no leading zero)
4378 mm - month of year (two digit)
4379 M - month name short
4380 MM - month name long
4381 y - year (two digit)
4382 yy - year (four digit)
4383 @ - Unix timestamp (ms since 01/01/1970)
4384 ! - Windows ticks (100ns since 01/01/0001)
4385 '...' - literal text
4388 @param format string - the desired format of the date
4389 @param date Date - the date value to format
4390 @param settings Object - attributes include:
4391 dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
4392 dayNames string[7] - names of the days from Sunday (optional)
4393 monthNamesShort string[12] - abbreviated names of the months (optional)
4394 monthNames string[12] - names of the months (optional)
4395 @return string - the date in the above format */
4396 formatDate: function (format, date, settings) {
4399 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
4400 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
4401 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
4402 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
4403 // Check whether a format character is doubled
4404 var lookAhead = function(match) {
4405 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
4410 // Format a number, with leading zero if necessary
4411 var formatNumber = function(match, value, len) {
4412 var num = '' + value;
4413 if (lookAhead(match))
4414 while (num.length < len)
4418 // Format a name, short or long as requested
4419 var formatName = function(match, value, shortNames, longNames) {
4420 return (lookAhead(match) ? longNames[value] : shortNames[value]);
4423 var literal = false;
4425 for (var iFormat = 0; iFormat < format.length; iFormat++) {
4427 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
4430 output += format.charAt(iFormat);
4432 switch (format.charAt(iFormat)) {
4434 output += formatNumber('d', date.getDate(), 2);
4437 output += formatName('D', date.getDay(), dayNamesShort, dayNames);
4440 output += formatNumber('o',
4441 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
4444 output += formatNumber('m', date.getMonth() + 1, 2);
4447 output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
4450 output += (lookAhead('y') ? date.getFullYear() :
4451 (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
4454 output += date.getTime();
4457 output += date.getTime() * 10000 + this._ticksTo1970;
4466 output += format.charAt(iFormat);
4472 /* Extract all possible characters from the date format. */
4473 _possibleChars: function (format) {
4475 var literal = false;
4476 // Check whether a format character is doubled
4477 var lookAhead = function(match) {
4478 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
4483 for (var iFormat = 0; iFormat < format.length; iFormat++)
4485 if (format.charAt(iFormat) == "'" && !lookAhead("'"))
4488 chars += format.charAt(iFormat);
4490 switch (format.charAt(iFormat)) {
4491 case 'd': case 'm': case 'y': case '@':
4492 chars += '0123456789';
4495 return null; // Accept anything
4503 chars += format.charAt(iFormat);
4508 /* Get a setting value, defaulting if necessary. */
4509 _get: function(inst, name) {
4510 return inst.settings[name] !== undefined ?
4511 inst.settings[name] : this._defaults[name];
4514 /* Parse existing date and initialise date picker. */
4515 _setDateFromField: function(inst, noDefault) {
4516 if (inst.input.val() == inst.lastVal) {
4519 var dateFormat = this._get(inst, 'dateFormat');
4520 var dates = inst.lastVal = inst.input ? inst.input.val() : null;
4521 var date, defaultDate;
4522 date = defaultDate = this._getDefaultDate(inst);
4523 var settings = this._getFormatConfig(inst);
4525 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
4528 dates = (noDefault ? '' : dates);
4530 inst.selectedDay = date.getDate();
4531 inst.drawMonth = inst.selectedMonth = date.getMonth();
4532 inst.drawYear = inst.selectedYear = date.getFullYear();
4533 inst.currentDay = (dates ? date.getDate() : 0);
4534 inst.currentMonth = (dates ? date.getMonth() : 0);
4535 inst.currentYear = (dates ? date.getFullYear() : 0);
4536 this._adjustInstDate(inst);
4539 /* Retrieve the default date shown on opening. */
4540 _getDefaultDate: function(inst) {
4541 return this._restrictMinMax(inst,
4542 this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
4545 /* A date may be specified as an exact value or a relative one. */
4546 _determineDate: function(inst, date, defaultDate) {
4547 var offsetNumeric = function(offset) {
4548 var date = new Date();
4549 date.setDate(date.getDate() + offset);
4552 var offsetString = function(offset) {
4554 return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
4555 offset, $.datepicker._getFormatConfig(inst));
4560 var date = (offset.toLowerCase().match(/^c/) ?
4561 $.datepicker._getDate(inst) : null) || new Date();
4562 var year = date.getFullYear();
4563 var month = date.getMonth();
4564 var day = date.getDate();
4565 var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
4566 var matches = pattern.exec(offset);
4568 switch (matches[2] || 'd') {
4569 case 'd' : case 'D' :
4570 day += parseInt(matches[1],10); break;
4571 case 'w' : case 'W' :
4572 day += parseInt(matches[1],10) * 7; break;
4573 case 'm' : case 'M' :
4574 month += parseInt(matches[1],10);
4575 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
4577 case 'y': case 'Y' :
4578 year += parseInt(matches[1],10);
4579 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
4582 matches = pattern.exec(offset);
4584 return new Date(year, month, day);
4586 var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
4587 (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
4588 newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
4590 newDate.setHours(0);
4591 newDate.setMinutes(0);
4592 newDate.setSeconds(0);
4593 newDate.setMilliseconds(0);
4595 return this._daylightSavingAdjust(newDate);
4598 /* Handle switch to/from daylight saving.
4599 Hours may be non-zero on daylight saving cut-over:
4600 > 12 when midnight changeover, but then cannot generate
4601 midnight datetime, so jump to 1AM, otherwise reset.
4602 @param date (Date) the date to check
4603 @return (Date) the corrected date */
4604 _daylightSavingAdjust: function(date) {
4605 if (!date) return null;
4606 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
4610 /* Set the date(s) directly. */
4611 _setDate: function(inst, date, noChange) {
4613 var origMonth = inst.selectedMonth;
4614 var origYear = inst.selectedYear;
4615 var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
4616 inst.selectedDay = inst.currentDay = newDate.getDate();
4617 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
4618 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
4619 if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
4620 this._notifyChange(inst);
4621 this._adjustInstDate(inst);
4623 inst.input.val(clear ? '' : this._formatDate(inst));
4627 /* Retrieve the date(s) directly. */
4628 _getDate: function(inst) {
4629 var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
4630 this._daylightSavingAdjust(new Date(
4631 inst.currentYear, inst.currentMonth, inst.currentDay)));
4635 /* Attach the onxxx handlers. These are declared statically so
4636 * they work with static code transformers like Caja.
4638 _attachHandlers: function(inst) {
4639 var stepMonths = this._get(inst, 'stepMonths');
4640 var id = '#' + inst.id.replace( /\\\\/g, "\\" );
4641 inst.dpDiv.find('[data-handler]').map(function () {
4644 window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, -stepMonths, 'M');
4647 window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, +stepMonths, 'M');
4650 window['DP_jQuery_' + dpuuid].datepicker._hideDatepicker();
4652 today: function () {
4653 window['DP_jQuery_' + dpuuid].datepicker._gotoToday(id);
4655 selectDay: function () {
4656 window['DP_jQuery_' + dpuuid].datepicker._selectDay(id, +this.getAttribute('data-month'), +this.getAttribute('data-year'), this);
4659 selectMonth: function () {
4660 window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'M');
4663 selectYear: function () {
4664 window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'Y');
4668 $(this).bind(this.getAttribute('data-event'), handler[this.getAttribute('data-handler')]);
4672 /* Generate the HTML for the current state of the date picker. */
4673 _generateHTML: function(inst) {
4674 var today = new Date();
4675 today = this._daylightSavingAdjust(
4676 new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
4677 var isRTL = this._get(inst, 'isRTL');
4678 var showButtonPanel = this._get(inst, 'showButtonPanel');
4679 var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
4680 var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
4681 var numMonths = this._getNumberOfMonths(inst);
4682 var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
4683 var stepMonths = this._get(inst, 'stepMonths');
4684 var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
4685 var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
4686 new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
4687 var minDate = this._getMinMaxDate(inst, 'min');
4688 var maxDate = this._getMinMaxDate(inst, 'max');
4689 var drawMonth = inst.drawMonth - showCurrentAtPos;
4690 var drawYear = inst.drawYear;
4691 if (drawMonth < 0) {
4696 var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
4697 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
4698 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
4699 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
4701 if (drawMonth < 0) {
4707 inst.drawMonth = drawMonth;
4708 inst.drawYear = drawYear;
4709 var prevText = this._get(inst, 'prevText');
4710 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
4711 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
4712 this._getFormatConfig(inst)));
4713 var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
4714 '<a class="ui-datepicker-prev ui-corner-all" data-handler="prev" data-event="click"' +
4715 ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
4716 (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>'));
4717 var nextText = this._get(inst, 'nextText');
4718 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
4719 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
4720 this._getFormatConfig(inst)));
4721 var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
4722 '<a class="ui-datepicker-next ui-corner-all" data-handler="next" data-event="click"' +
4723 ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
4724 (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>'));
4725 var currentText = this._get(inst, 'currentText');
4726 var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
4727 currentText = (!navigationAsDateFormat ? currentText :
4728 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
4729 var 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">' +
4730 this._get(inst, 'closeText') + '</button>' : '');
4731 var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
4732 (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"' +
4733 '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
4734 var firstDay = parseInt(this._get(inst, 'firstDay'),10);
4735 firstDay = (isNaN(firstDay) ? 0 : firstDay);
4736 var showWeek = this._get(inst, 'showWeek');
4737 var dayNames = this._get(inst, 'dayNames');
4738 var dayNamesShort = this._get(inst, 'dayNamesShort');
4739 var dayNamesMin = this._get(inst, 'dayNamesMin');
4740 var monthNames = this._get(inst, 'monthNames');
4741 var monthNamesShort = this._get(inst, 'monthNamesShort');
4742 var beforeShowDay = this._get(inst, 'beforeShowDay');
4743 var showOtherMonths = this._get(inst, 'showOtherMonths');
4744 var selectOtherMonths = this._get(inst, 'selectOtherMonths');
4745 var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
4746 var defaultDate = this._getDefaultDate(inst);
4748 for (var row = 0; row < numMonths[0]; row++) {
4751 for (var col = 0; col < numMonths[1]; col++) {
4752 var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
4753 var cornerClass = ' ui-corner-all';
4756 calender += '<div class="ui-datepicker-group';
4757 if (numMonths[1] > 1)
4759 case 0: calender += ' ui-datepicker-group-first';
4760 cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
4761 case numMonths[1]-1: calender += ' ui-datepicker-group-last';
4762 cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
4763 default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
4767 calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
4768 (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
4769 (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
4770 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
4771 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
4772 '</div><table class="ui-datepicker-calendar"><thead>' +
4774 var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
4775 for (var dow = 0; dow < 7; dow++) { // days of the week
4776 var day = (dow + firstDay) % 7;
4777 thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
4778 '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
4780 calender += thead + '</tr></thead><tbody>';
4781 var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
4782 if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
4783 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
4784 var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
4785 var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
4786 var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
4787 this.maxRows = numRows;
4788 var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
4789 for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
4791 var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
4792 this._get(inst, 'calculateWeek')(printDate) + '</td>');
4793 for (var dow = 0; dow < 7; dow++) { // create date picker days
4794 var daySettings = (beforeShowDay ?
4795 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
4796 var otherMonth = (printDate.getMonth() != drawMonth);
4797 var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
4798 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
4799 tbody += '<td class="' +
4800 ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
4801 (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
4802 ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
4803 (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
4804 // or defaultDate is current printedDate and defaultDate is selectedDate
4805 ' ' + this._dayOverClass : '') + // highlight selected day
4806 (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
4807 (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
4808 (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
4809 (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
4810 ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
4811 (unselectable ? '' : ' data-handler="selectDay" data-event="click" data-month="' + printDate.getMonth() + '" data-year="' + printDate.getFullYear() + '"') + '>' + // actions
4812 (otherMonth && !showOtherMonths ? ' ' : // display for other months
4813 (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
4814 (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
4815 (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
4816 (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
4817 '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
4818 printDate.setDate(printDate.getDate() + 1);
4819 printDate = this._daylightSavingAdjust(printDate);
4821 calender += tbody + '</tr>';
4824 if (drawMonth > 11) {
4828 calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
4829 ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
4834 html += buttonPanel + ($.ui.ie6 && !inst.inline ?
4835 '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
4836 inst._keyEvent = false;
4840 /* Generate the month and year header. */
4841 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
4842 secondary, monthNames, monthNamesShort) {
4843 var changeMonth = this._get(inst, 'changeMonth');
4844 var changeYear = this._get(inst, 'changeYear');
4845 var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
4846 var html = '<div class="ui-datepicker-title">';
4849 if (secondary || !changeMonth)
4850 monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
4852 var inMinYear = (minDate && minDate.getFullYear() == drawYear);
4853 var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
4854 monthHtml += '<select class="ui-datepicker-month" data-handler="selectMonth" data-event="change">';
4855 for (var month = 0; month < 12; month++) {
4856 if ((!inMinYear || month >= minDate.getMonth()) &&
4857 (!inMaxYear || month <= maxDate.getMonth()))
4858 monthHtml += '<option value="' + month + '"' +
4859 (month == drawMonth ? ' selected="selected"' : '') +
4860 '>' + monthNamesShort[month] + '</option>';
4862 monthHtml += '</select>';
4864 if (!showMonthAfterYear)
4865 html += monthHtml + (secondary || !(changeMonth && changeYear) ? ' ' : '');
4867 if ( !inst.yearshtml ) {
4868 inst.yearshtml = '';
4869 if (secondary || !changeYear)
4870 html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
4872 // determine range of years to display
4873 var years = this._get(inst, 'yearRange').split(':');
4874 var thisYear = new Date().getFullYear();
4875 var determineYear = function(value) {
4876 var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
4877 (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
4878 parseInt(value, 10)));
4879 return (isNaN(year) ? thisYear : year);
4881 var year = determineYear(years[0]);
4882 var endYear = Math.max(year, determineYear(years[1] || ''));
4883 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
4884 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
4885 inst.yearshtml += '<select class="ui-datepicker-year" data-handler="selectYear" data-event="change">';
4886 for (; year <= endYear; year++) {
4887 inst.yearshtml += '<option value="' + year + '"' +
4888 (year == drawYear ? ' selected="selected"' : '') +
4889 '>' + year + '</option>';
4891 inst.yearshtml += '</select>';
4893 html += inst.yearshtml;
4894 inst.yearshtml = null;
4897 html += this._get(inst, 'yearSuffix');
4898 if (showMonthAfterYear)
4899 html += (secondary || !(changeMonth && changeYear) ? ' ' : '') + monthHtml;
4900 html += '</div>'; // Close datepicker_header
4904 /* Adjust one of the date sub-fields. */
4905 _adjustInstDate: function(inst, offset, period) {
4906 var year = inst.drawYear + (period == 'Y' ? offset : 0);
4907 var month = inst.drawMonth + (period == 'M' ? offset : 0);
4908 var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
4909 (period == 'D' ? offset : 0);
4910 var date = this._restrictMinMax(inst,
4911 this._daylightSavingAdjust(new Date(year, month, day)));
4912 inst.selectedDay = date.getDate();
4913 inst.drawMonth = inst.selectedMonth = date.getMonth();
4914 inst.drawYear = inst.selectedYear = date.getFullYear();
4915 if (period == 'M' || period == 'Y')
4916 this._notifyChange(inst);
4919 /* Ensure a date is within any min/max bounds. */
4920 _restrictMinMax: function(inst, date) {
4921 var minDate = this._getMinMaxDate(inst, 'min');
4922 var maxDate = this._getMinMaxDate(inst, 'max');
4923 var newDate = (minDate && date < minDate ? minDate : date);
4924 newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
4928 /* Notify change of month/year. */
4929 _notifyChange: function(inst) {
4930 var onChange = this._get(inst, 'onChangeMonthYear');
4932 onChange.apply((inst.input ? inst.input[0] : null),
4933 [inst.selectedYear, inst.selectedMonth + 1, inst]);
4936 /* Determine the number of months to show. */
4937 _getNumberOfMonths: function(inst) {
4938 var numMonths = this._get(inst, 'numberOfMonths');
4939 return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
4942 /* Determine the current maximum date - ensure no time components are set. */
4943 _getMinMaxDate: function(inst, minMax) {
4944 return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
4947 /* Find the number of days in a given month. */
4948 _getDaysInMonth: function(year, month) {
4949 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
4952 /* Find the day of the week of the first of a month. */
4953 _getFirstDayOfMonth: function(year, month) {
4954 return new Date(year, month, 1).getDay();
4957 /* Determines if we should allow a "next/prev" month display change. */
4958 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
4959 var numMonths = this._getNumberOfMonths(inst);
4960 var date = this._daylightSavingAdjust(new Date(curYear,
4961 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
4963 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
4964 return this._isInRange(inst, date);
4967 /* Is the given date in the accepted range? */
4968 _isInRange: function(inst, date) {
4969 var minDate = this._getMinMaxDate(inst, 'min');
4970 var maxDate = this._getMinMaxDate(inst, 'max');
4971 return ((!minDate || date.getTime() >= minDate.getTime()) &&
4972 (!maxDate || date.getTime() <= maxDate.getTime()));
4975 /* Provide the configuration settings for formatting/parsing. */
4976 _getFormatConfig: function(inst) {
4977 var shortYearCutoff = this._get(inst, 'shortYearCutoff');
4978 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
4979 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
4980 return {shortYearCutoff: shortYearCutoff,
4981 dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
4982 monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
4985 /* Format the given date for display. */
4986 _formatDate: function(inst, day, month, year) {
4988 inst.currentDay = inst.selectedDay;
4989 inst.currentMonth = inst.selectedMonth;
4990 inst.currentYear = inst.selectedYear;
4992 var date = (day ? (typeof day == 'object' ? day :
4993 this._daylightSavingAdjust(new Date(year, month, day))) :
4994 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
4995 return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
5000 * Bind hover events for datepicker elements.
5001 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
5002 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
5004 function bindHover(dpDiv) {
5005 var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
5006 return dpDiv.delegate(selector, 'mouseout', function() {
5007 $(this).removeClass('ui-state-hover');
5008 if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
5009 if (this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
5011 .delegate(selector, 'mouseover', function(){
5012 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
5013 $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
5014 $(this).addClass('ui-state-hover');
5015 if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
5016 if (this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
5021 /* jQuery extend now ignores nulls! */
5022 function extendRemove(target, props) {
5023 $.extend(target, props);
5024 for (var name in props)
5025 if (props[name] == null || props[name] == undefined)
5026 target[name] = props[name];
5030 /* Invoke the datepicker functionality.
5031 @param options string - a command, optionally followed by additional parameters or
5032 Object - settings for attaching new datepicker functionality
5033 @return jQuery object */
5034 $.fn.datepicker = function(options){
5036 /* Verify an empty collection wasn't passed - Fixes #6976 */
5037 if ( !this.length ) {
5041 /* Initialise the date picker. */
5042 if (!$.datepicker.initialized) {
5043 $(document).mousedown($.datepicker._checkExternalClick).
5044 find(document.body).append($.datepicker.dpDiv);
5045 $.datepicker.initialized = true;
5048 var otherArgs = Array.prototype.slice.call(arguments, 1);
5049 if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
5050 return $.datepicker['_' + options + 'Datepicker'].
5051 apply($.datepicker, [this[0]].concat(otherArgs));
5052 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
5053 return $.datepicker['_' + options + 'Datepicker'].
5054 apply($.datepicker, [this[0]].concat(otherArgs));
5055 return this.each(function() {
5056 typeof options == 'string' ?
5057 $.datepicker['_' + options + 'Datepicker'].
5058 apply($.datepicker, [this].concat(otherArgs)) :
5059 $.datepicker._attachDatepicker(this, options);
5063 $.datepicker = new Datepicker(); // singleton instance
5064 $.datepicker.initialized = false;
5065 $.datepicker.uuid = new Date().getTime();
5066 $.datepicker.version = "1.9.2";
5068 // Workaround for #4055
5069 // Add another global to avoid noConflict issues with inline event handlers
5070 window['DP_jQuery_' + dpuuid] = $;
5073 (function( $, undefined ) {
5075 var uiDialogClasses = "ui-dialog ui-widget ui-widget-content ui-corner-all ",
5076 sizeRelatedOptions = {
5085 resizableRelatedOptions = {
5092 $.widget("ui.dialog", {
5097 closeOnEscape: true,
5113 // ensure that the titlebar is never outside the document
5114 using: function( pos ) {
5115 var topOffset = $( this ).css( pos ).offset().top;
5116 if ( topOffset < 0 ) {
5117 $( this ).css( "top", pos.top - topOffset );
5129 _create: function() {
5130 this.originalTitle = this.element.attr( "title" );
5131 // #5742 - .attr() might return a DOMElement
5132 if ( typeof this.originalTitle !== "string" ) {
5133 this.originalTitle = "";
5135 this.oldPosition = {
5136 parent: this.element.parent(),
5137 index: this.element.parent().children().index( this.element )
5139 this.options.title = this.options.title || this.originalTitle;
5141 options = this.options,
5143 title = options.title || " ",
5146 uiDialogTitlebarClose,
5150 uiDialog = ( this.uiDialog = $( "<div>" ) )
5151 .addClass( uiDialogClasses + options.dialogClass )
5154 outline: 0, // TODO: move to stylesheet
5155 zIndex: options.zIndex
5157 // setting tabIndex makes the div focusable
5158 .attr( "tabIndex", -1)
5159 .keydown(function( event ) {
5160 if ( options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
5161 event.keyCode === $.ui.keyCode.ESCAPE ) {
5162 that.close( event );
5163 event.preventDefault();
5166 .mousedown(function( event ) {
5167 that.moveToTop( false, event );
5169 .appendTo( "body" );
5173 .removeAttr( "title" )
5174 .addClass( "ui-dialog-content ui-widget-content" )
5175 .appendTo( uiDialog );
5177 uiDialogTitlebar = ( this.uiDialogTitlebar = $( "<div>" ) )
5178 .addClass( "ui-dialog-titlebar ui-widget-header " +
5179 "ui-corner-all ui-helper-clearfix" )
5180 .bind( "mousedown", function() {
5181 // Dialog isn't getting focus when dragging (#8063)
5184 .prependTo( uiDialog );
5186 uiDialogTitlebarClose = $( "<a href='#'></a>" )
5187 .addClass( "ui-dialog-titlebar-close ui-corner-all" )
5188 .attr( "role", "button" )
5189 .click(function( event ) {
5190 event.preventDefault();
5191 that.close( event );
5193 .appendTo( uiDialogTitlebar );
5195 ( this.uiDialogTitlebarCloseText = $( "<span>" ) )
5196 .addClass( "ui-icon ui-icon-closethick" )
5197 .text( options.closeText )
5198 .appendTo( uiDialogTitlebarClose );
5200 uiDialogTitle = $( "<span>" )
5202 .addClass( "ui-dialog-title" )
5204 .prependTo( uiDialogTitlebar );
5206 uiDialogButtonPane = ( this.uiDialogButtonPane = $( "<div>" ) )
5207 .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" );
5209 ( this.uiButtonSet = $( "<div>" ) )
5210 .addClass( "ui-dialog-buttonset" )
5211 .appendTo( uiDialogButtonPane );
5215 "aria-labelledby": uiDialogTitle.attr( "id" )
5218 uiDialogTitlebar.find( "*" ).add( uiDialogTitlebar ).disableSelection();
5219 this._hoverable( uiDialogTitlebarClose );
5220 this._focusable( uiDialogTitlebarClose );
5222 if ( options.draggable && $.fn.draggable ) {
5223 this._makeDraggable();
5225 if ( options.resizable && $.fn.resizable ) {
5226 this._makeResizable();
5229 this._createButtons( options.buttons );
5230 this._isOpen = false;
5232 if ( $.fn.bgiframe ) {
5233 uiDialog.bgiframe();
5236 // prevent tabbing out of modal dialogs
5237 this._on( uiDialog, { keydown: function( event ) {
5238 if ( !options.modal || event.keyCode !== $.ui.keyCode.TAB ) {
5242 var tabbables = $( ":tabbable", uiDialog ),
5243 first = tabbables.filter( ":first" ),
5244 last = tabbables.filter( ":last" );
5246 if ( event.target === last[0] && !event.shiftKey ) {
5249 } else if ( event.target === first[0] && event.shiftKey ) {
5257 if ( this.options.autoOpen ) {
5262 _destroy: function() {
5264 oldPosition = this.oldPosition;
5266 if ( this.overlay ) {
5267 this.overlay.destroy();
5269 this.uiDialog.hide();
5271 .removeClass( "ui-dialog-content ui-widget-content" )
5273 .appendTo( "body" );
5274 this.uiDialog.remove();
5276 if ( this.originalTitle ) {
5277 this.element.attr( "title", this.originalTitle );
5280 next = oldPosition.parent.children().eq( oldPosition.index );
5281 // Don't try to place the dialog next to itself (#8613)
5282 if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
5283 next.before( this.element );
5285 oldPosition.parent.append( this.element );
5289 widget: function() {
5290 return this.uiDialog;
5293 close: function( event ) {
5297 if ( !this._isOpen ) {
5301 if ( false === this._trigger( "beforeClose", event ) ) {
5305 this._isOpen = false;
5307 if ( this.overlay ) {
5308 this.overlay.destroy();
5311 if ( this.options.hide ) {
5312 this._hide( this.uiDialog, this.options.hide, function() {
5313 that._trigger( "close", event );
5316 this.uiDialog.hide();
5317 this._trigger( "close", event );
5320 $.ui.dialog.overlay.resize();
5322 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
5323 if ( this.options.modal ) {
5325 $( ".ui-dialog" ).each(function() {
5326 if ( this !== that.uiDialog[0] ) {
5327 thisZ = $( this ).css( "z-index" );
5328 if ( !isNaN( thisZ ) ) {
5329 maxZ = Math.max( maxZ, thisZ );
5333 $.ui.dialog.maxZ = maxZ;
5339 isOpen: function() {
5340 return this._isOpen;
5343 // the force parameter allows us to move modal dialogs to their correct
5345 moveToTop: function( force, event ) {
5346 var options = this.options,
5349 if ( ( options.modal && !force ) ||
5350 ( !options.stack && !options.modal ) ) {
5351 return this._trigger( "focus", event );
5354 if ( options.zIndex > $.ui.dialog.maxZ ) {
5355 $.ui.dialog.maxZ = options.zIndex;
5357 if ( this.overlay ) {
5358 $.ui.dialog.maxZ += 1;
5359 $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ;
5360 this.overlay.$el.css( "z-index", $.ui.dialog.overlay.maxZ );
5363 // Save and then restore scroll
5364 // Opera 9.5+ resets when parent z-index is changed.
5365 // http://bugs.jqueryui.com/ticket/3193
5367 scrollTop: this.element.scrollTop(),
5368 scrollLeft: this.element.scrollLeft()
5370 $.ui.dialog.maxZ += 1;
5371 this.uiDialog.css( "z-index", $.ui.dialog.maxZ );
5372 this.element.attr( saveScroll );
5373 this._trigger( "focus", event );
5379 if ( this._isOpen ) {
5384 options = this.options,
5385 uiDialog = this.uiDialog;
5388 this._position( options.position );
5389 uiDialog.show( options.show );
5390 this.overlay = options.modal ? new $.ui.dialog.overlay( this ) : null;
5391 this.moveToTop( true );
5393 // set focus to the first tabbable element in the content area or the first button
5394 // if there are no tabbable elements, set focus on the dialog itself
5395 hasFocus = this.element.find( ":tabbable" );
5396 if ( !hasFocus.length ) {
5397 hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
5398 if ( !hasFocus.length ) {
5399 hasFocus = uiDialog;
5402 hasFocus.eq( 0 ).focus();
5404 this._isOpen = true;
5405 this._trigger( "open" );
5410 _createButtons: function( buttons ) {
5414 // if we already have a button pane, remove it
5415 this.uiDialogButtonPane.remove();
5416 this.uiButtonSet.empty();
5418 if ( typeof buttons === "object" && buttons !== null ) {
5419 $.each( buttons, function() {
5420 return !(hasButtons = true);
5424 $.each( buttons, function( name, props ) {
5426 props = $.isFunction( props ) ?
5427 { click: props, text: name } :
5429 // Default to a non-submitting button
5430 props = $.extend( { type: "button" }, props );
5431 // Change the context for the click callback to be the main element
5432 click = props.click;
5433 props.click = function() {
5434 click.apply( that.element[0], arguments );
5436 button = $( "<button></button>", props )
5437 .appendTo( that.uiButtonSet );
5438 if ( $.fn.button ) {
5442 this.uiDialog.addClass( "ui-dialog-buttons" );
5443 this.uiDialogButtonPane.appendTo( this.uiDialog );
5445 this.uiDialog.removeClass( "ui-dialog-buttons" );
5449 _makeDraggable: function() {
5451 options = this.options;
5453 function filteredUi( ui ) {
5455 position: ui.position,
5460 this.uiDialog.draggable({
5461 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
5462 handle: ".ui-dialog-titlebar",
5463 containment: "document",
5464 start: function( event, ui ) {
5466 .addClass( "ui-dialog-dragging" );
5467 that._trigger( "dragStart", event, filteredUi( ui ) );
5469 drag: function( event, ui ) {
5470 that._trigger( "drag", event, filteredUi( ui ) );
5472 stop: function( event, ui ) {
5473 options.position = [
5474 ui.position.left - that.document.scrollLeft(),
5475 ui.position.top - that.document.scrollTop()
5478 .removeClass( "ui-dialog-dragging" );
5479 that._trigger( "dragStop", event, filteredUi( ui ) );
5480 $.ui.dialog.overlay.resize();
5485 _makeResizable: function( handles ) {
5486 handles = (handles === undefined ? this.options.resizable : handles);
5488 options = this.options,
5489 // .ui-resizable has position: relative defined in the stylesheet
5490 // but dialogs have to use absolute or fixed positioning
5491 position = this.uiDialog.css( "position" ),
5492 resizeHandles = typeof handles === 'string' ?
5494 "n,e,s,w,se,sw,ne,nw";
5496 function filteredUi( ui ) {
5498 originalPosition: ui.originalPosition,
5499 originalSize: ui.originalSize,
5500 position: ui.position,
5505 this.uiDialog.resizable({
5506 cancel: ".ui-dialog-content",
5507 containment: "document",
5508 alsoResize: this.element,
5509 maxWidth: options.maxWidth,
5510 maxHeight: options.maxHeight,
5511 minWidth: options.minWidth,
5512 minHeight: this._minHeight(),
5513 handles: resizeHandles,
5514 start: function( event, ui ) {
5515 $( this ).addClass( "ui-dialog-resizing" );
5516 that._trigger( "resizeStart", event, filteredUi( ui ) );
5518 resize: function( event, ui ) {
5519 that._trigger( "resize", event, filteredUi( ui ) );
5521 stop: function( event, ui ) {
5522 $( this ).removeClass( "ui-dialog-resizing" );
5523 options.height = $( this ).height();
5524 options.width = $( this ).width();
5525 that._trigger( "resizeStop", event, filteredUi( ui ) );
5526 $.ui.dialog.overlay.resize();
5529 .css( "position", position )
5530 .find( ".ui-resizable-se" )
5531 .addClass( "ui-icon ui-icon-grip-diagonal-se" );
5534 _minHeight: function() {
5535 var options = this.options;
5537 if ( options.height === "auto" ) {
5538 return options.minHeight;
5540 return Math.min( options.minHeight, options.height );
5544 _position: function( position ) {
5550 // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
5551 // if (typeof position == 'string' || $.isArray(position)) {
5552 // myAt = $.isArray(position) ? position : position.split(' ');
5554 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
5555 myAt = position.split ? position.split( " " ) : [ position[ 0 ], position[ 1 ] ];
5556 if ( myAt.length === 1 ) {
5557 myAt[ 1 ] = myAt[ 0 ];
5560 $.each( [ "left", "top" ], function( i, offsetPosition ) {
5561 if ( +myAt[ i ] === myAt[ i ] ) {
5562 offset[ i ] = myAt[ i ];
5563 myAt[ i ] = offsetPosition;
5568 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
5569 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
5570 at: myAt.join( " " )
5574 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
5576 position = $.ui.dialog.prototype.options.position;
5579 // need to show the dialog to get the actual offset in the position plugin
5580 isVisible = this.uiDialog.is( ":visible" );
5582 this.uiDialog.show();
5584 this.uiDialog.position( position );
5586 this.uiDialog.hide();
5590 _setOptions: function( options ) {
5592 resizableOptions = {},
5595 $.each( options, function( key, value ) {
5596 that._setOption( key, value );
5598 if ( key in sizeRelatedOptions ) {
5601 if ( key in resizableRelatedOptions ) {
5602 resizableOptions[ key ] = value;
5609 if ( this.uiDialog.is( ":data(resizable)" ) ) {
5610 this.uiDialog.resizable( "option", resizableOptions );
5614 _setOption: function( key, value ) {
5615 var isDraggable, isResizable,
5616 uiDialog = this.uiDialog;
5620 this._createButtons( value );
5623 // ensure that we always pass a string
5624 this.uiDialogTitlebarCloseText.text( "" + value );
5628 .removeClass( this.options.dialogClass )
5629 .addClass( uiDialogClasses + value );
5633 uiDialog.addClass( "ui-dialog-disabled" );
5635 uiDialog.removeClass( "ui-dialog-disabled" );
5639 isDraggable = uiDialog.is( ":data(draggable)" );
5640 if ( isDraggable && !value ) {
5641 uiDialog.draggable( "destroy" );
5644 if ( !isDraggable && value ) {
5645 this._makeDraggable();
5649 this._position( value );
5652 // currently resizable, becoming non-resizable
5653 isResizable = uiDialog.is( ":data(resizable)" );
5654 if ( isResizable && !value ) {
5655 uiDialog.resizable( "destroy" );
5658 // currently resizable, changing handles
5659 if ( isResizable && typeof value === "string" ) {
5660 uiDialog.resizable( "option", "handles", value );
5663 // currently non-resizable, becoming resizable
5664 if ( !isResizable && value !== false ) {
5665 this._makeResizable( value );
5669 // convert whatever was passed in o a string, for html() to not throw up
5670 $( ".ui-dialog-title", this.uiDialogTitlebar )
5671 .html( "" + ( value || " " ) );
5675 this._super( key, value );
5679 /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
5680 * divs will both have width and height set, so we need to reset them
5682 var nonContentHeight, minContentHeight, autoHeight,
5683 options = this.options,
5684 isVisible = this.uiDialog.is( ":visible" );
5686 // reset content sizing
5687 this.element.show().css({
5693 if ( options.minWidth > options.width ) {
5694 options.width = options.minWidth;
5697 // reset wrapper sizing
5698 // determine the height of all the non-content elements
5699 nonContentHeight = this.uiDialog.css({
5701 width: options.width
5704 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
5706 if ( options.height === "auto" ) {
5707 // only needed for IE6 support
5708 if ( $.support.minHeight ) {
5710 minHeight: minContentHeight,
5714 this.uiDialog.show();
5715 autoHeight = this.element.css( "height", "auto" ).height();
5717 this.uiDialog.hide();
5719 this.element.height( Math.max( autoHeight, minContentHeight ) );
5722 this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
5725 if (this.uiDialog.is( ":data(resizable)" ) ) {
5726 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
5731 $.extend($.ui.dialog, {
5735 getTitleId: function($el) {
5736 var id = $el.attr( "id" );
5741 return "ui-dialog-title-" + id;
5744 overlay: function( dialog ) {
5745 this.$el = $.ui.dialog.overlay.create( dialog );
5749 $.extend( $.ui.dialog.overlay, {
5751 // reuse old instances due to IE memory leak with alpha transparency (see #5185)
5755 "focus,mousedown,mouseup,keydown,keypress,click".split( "," ),
5757 return event + ".dialog-overlay";
5760 create: function( dialog ) {
5761 if ( this.instances.length === 0 ) {
5762 // prevent use of anchors and inputs
5763 // we use a setTimeout in case the overlay is created from an
5764 // event that we're going to be cancelling (see #2804)
5765 setTimeout(function() {
5766 // handle $(el).dialog().dialog('close') (see #4065)
5767 if ( $.ui.dialog.overlay.instances.length ) {
5768 $( document ).bind( $.ui.dialog.overlay.events, function( event ) {
5769 // stop events if the z-index of the target is < the z-index of the overlay
5770 // we cannot return true when we don't want to cancel the event (#3523)
5771 if ( $( event.target ).zIndex() < $.ui.dialog.overlay.maxZ ) {
5778 // handle window resize
5779 $( window ).bind( "resize.dialog-overlay", $.ui.dialog.overlay.resize );
5782 var $el = ( this.oldInstances.pop() || $( "<div>" ).addClass( "ui-widget-overlay" ) );
5784 // allow closing by pressing the escape key
5785 $( document ).bind( "keydown.dialog-overlay", function( event ) {
5786 var instances = $.ui.dialog.overlay.instances;
5787 // only react to the event if we're the top overlay
5788 if ( instances.length !== 0 && instances[ instances.length - 1 ] === $el &&
5789 dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
5790 event.keyCode === $.ui.keyCode.ESCAPE ) {
5792 dialog.close( event );
5793 event.preventDefault();
5797 $el.appendTo( document.body ).css({
5798 width: this.width(),
5799 height: this.height()
5802 if ( $.fn.bgiframe ) {
5806 this.instances.push( $el );
5810 destroy: function( $el ) {
5811 var indexOf = $.inArray( $el, this.instances ),
5814 if ( indexOf !== -1 ) {
5815 this.oldInstances.push( this.instances.splice( indexOf, 1 )[ 0 ] );
5818 if ( this.instances.length === 0 ) {
5819 $( [ document, window ] ).unbind( ".dialog-overlay" );
5822 $el.height( 0 ).width( 0 ).remove();
5824 // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
5825 $.each( this.instances, function() {
5826 maxZ = Math.max( maxZ, this.css( "z-index" ) );
5831 height: function() {
5836 scrollHeight = Math.max(
5837 document.documentElement.scrollHeight,
5838 document.body.scrollHeight
5840 offsetHeight = Math.max(
5841 document.documentElement.offsetHeight,
5842 document.body.offsetHeight
5845 if ( scrollHeight < offsetHeight ) {
5846 return $( window ).height() + "px";
5848 return scrollHeight + "px";
5850 // handle "good" browsers
5852 return $( document ).height() + "px";
5861 scrollWidth = Math.max(
5862 document.documentElement.scrollWidth,
5863 document.body.scrollWidth
5865 offsetWidth = Math.max(
5866 document.documentElement.offsetWidth,
5867 document.body.offsetWidth
5870 if ( scrollWidth < offsetWidth ) {
5871 return $( window ).width() + "px";
5873 return scrollWidth + "px";
5875 // handle "good" browsers
5877 return $( document ).width() + "px";
5881 resize: function() {
5882 /* If the dialog is draggable and the user drags it past the
5883 * right edge of the window, the document becomes wider so we
5884 * need to stretch the overlay. If the user then drags the
5885 * dialog back to the left, the document will become narrower,
5886 * so we need to shrink the overlay to the appropriate size.
5887 * This is handled by shrinking the overlay before setting it
5888 * to the full document size.
5890 var $overlays = $( [] );
5891 $.each( $.ui.dialog.overlay.instances, function() {
5892 $overlays = $overlays.add( this );
5899 width: $.ui.dialog.overlay.width(),
5900 height: $.ui.dialog.overlay.height()
5905 $.extend( $.ui.dialog.overlay.prototype, {
5906 destroy: function() {
5907 $.ui.dialog.overlay.destroy( this.$el );
5912 (function( $, undefined ) {
5914 $.widget("ui.draggable", $.ui.mouse, {
5916 widgetEventPrefix: "drag",
5921 connectToSortable: false,
5930 refreshPositions: false,
5932 revertDuration: 500,
5935 scrollSensitivity: 20,
5943 _create: function() {
5945 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
5946 this.element[0].style.position = 'relative';
5948 (this.options.addClasses && this.element.addClass("ui-draggable"));
5949 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
5955 _destroy: function() {
5956 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
5957 this._mouseDestroy();
5960 _mouseCapture: function(event) {
5962 var o = this.options;
5964 // among others, prevent a drag on a resizable-handle
5965 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
5968 //Quit if we're not on a valid handle
5969 this.handle = this._getHandle(event);
5973 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
5974 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
5976 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
5977 position: "absolute", opacity: "0.001", zIndex: 1000
5979 .css($(this).offset())
5987 _mouseStart: function(event) {
5989 var o = this.options;
5991 //Create and append the visible helper
5992 this.helper = this._createHelper(event);
5994 this.helper.addClass("ui-draggable-dragging");
5996 //Cache the helper size
5997 this._cacheHelperProportions();
5999 //If ddmanager is used for droppables, set the global draggable
6001 $.ui.ddmanager.current = this;
6004 * - Position generation -
6005 * This block generates everything position related - it's the core of draggables.
6008 //Cache the margins of the original element
6009 this._cacheMargins();
6011 //Store the helper's css position
6012 this.cssPosition = this.helper.css("position");
6013 this.scrollParent = this.helper.scrollParent();
6015 //The element's absolute position on the page minus margins
6016 this.offset = this.positionAbs = this.element.offset();
6018 top: this.offset.top - this.margins.top,
6019 left: this.offset.left - this.margins.left
6022 $.extend(this.offset, {
6023 click: { //Where the click happened, relative to the element
6024 left: event.pageX - this.offset.left,
6025 top: event.pageY - this.offset.top
6027 parent: this._getParentOffset(),
6028 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
6031 //Generate the original position
6032 this.originalPosition = this.position = this._generatePosition(event);
6033 this.originalPageX = event.pageX;
6034 this.originalPageY = event.pageY;
6036 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
6037 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
6039 //Set a containment if given in the options
6041 this._setContainment();
6043 //Trigger event + callbacks
6044 if(this._trigger("start", event) === false) {
6049 //Recache the helper size
6050 this._cacheHelperProportions();
6052 //Prepare the droppable offsets
6053 if ($.ui.ddmanager && !o.dropBehaviour)
6054 $.ui.ddmanager.prepareOffsets(this, event);
6057 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
6059 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
6060 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
6065 _mouseDrag: function(event, noPropagation) {
6067 //Compute the helpers position
6068 this.position = this._generatePosition(event);
6069 this.positionAbs = this._convertPositionTo("absolute");
6071 //Call plugins and callbacks and use the resulting position if something is returned
6072 if (!noPropagation) {
6073 var ui = this._uiHash();
6074 if(this._trigger('drag', event, ui) === false) {
6078 this.position = ui.position;
6081 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
6082 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
6083 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
6088 _mouseStop: function(event) {
6090 //If we are using droppables, inform the manager about the drop
6091 var dropped = false;
6092 if ($.ui.ddmanager && !this.options.dropBehaviour)
6093 dropped = $.ui.ddmanager.drop(this, event);
6095 //if a drop comes from outside (a sortable)
6097 dropped = this.dropped;
6098 this.dropped = false;
6101 //if the original element is no longer in the DOM don't bother to continue (see #8269)
6102 var element = this.element[0], elementInDom = false;
6103 while ( element && (element = element.parentNode) ) {
6104 if (element == document ) {
6105 elementInDom = true;
6108 if ( !elementInDom && this.options.helper === "original" )
6111 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))) {
6113 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
6114 if(that._trigger("stop", event) !== false) {
6119 if(this._trigger("stop", event) !== false) {
6127 _mouseUp: function(event) {
6128 //Remove frame helpers
6129 $("div.ui-draggable-iframeFix").each(function() {
6130 this.parentNode.removeChild(this);
6133 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
6134 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
6136 return $.ui.mouse.prototype._mouseUp.call(this, event);
6139 cancel: function() {
6141 if(this.helper.is(".ui-draggable-dragging")) {
6151 _getHandle: function(event) {
6153 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
6154 $(this.options.handle, this.element)
6158 if(this == event.target) handle = true;
6165 _createHelper: function(event) {
6167 var o = this.options;
6168 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
6170 if(!helper.parents('body').length)
6171 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
6173 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
6174 helper.css("position", "absolute");
6180 _adjustOffsetFromHelper: function(obj) {
6181 if (typeof obj == 'string') {
6182 obj = obj.split(' ');
6184 if ($.isArray(obj)) {
6185 obj = {left: +obj[0], top: +obj[1] || 0};
6187 if ('left' in obj) {
6188 this.offset.click.left = obj.left + this.margins.left;
6190 if ('right' in obj) {
6191 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
6194 this.offset.click.top = obj.top + this.margins.top;
6196 if ('bottom' in obj) {
6197 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
6201 _getParentOffset: function() {
6203 //Get the offsetParent and cache its position
6204 this.offsetParent = this.helper.offsetParent();
6205 var po = this.offsetParent.offset();
6207 // This is a special case where we need to modify a offset calculated on start, since the following happened:
6208 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
6209 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
6210 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
6211 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
6212 po.left += this.scrollParent.scrollLeft();
6213 po.top += this.scrollParent.scrollTop();
6216 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
6217 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
6218 po = { top: 0, left: 0 };
6221 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
6222 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
6227 _getRelativeOffset: function() {
6229 if(this.cssPosition == "relative") {
6230 var p = this.element.position();
6232 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
6233 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
6236 return { top: 0, left: 0 };
6241 _cacheMargins: function() {
6243 left: (parseInt(this.element.css("marginLeft"),10) || 0),
6244 top: (parseInt(this.element.css("marginTop"),10) || 0),
6245 right: (parseInt(this.element.css("marginRight"),10) || 0),
6246 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
6250 _cacheHelperProportions: function() {
6251 this.helperProportions = {
6252 width: this.helper.outerWidth(),
6253 height: this.helper.outerHeight()
6257 _setContainment: function() {
6259 var o = this.options;
6260 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
6261 if(o.containment == 'document' || o.containment == 'window') this.containment = [
6262 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
6263 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
6264 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
6265 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
6268 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
6269 var c = $(o.containment);
6270 var ce = c[0]; if(!ce) return;
6271 var co = c.offset();
6272 var over = ($(ce).css("overflow") != 'hidden');
6274 this.containment = [
6275 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
6276 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
6277 (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 - this.margins.right,
6278 (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 - this.margins.bottom
6280 this.relative_container = c;
6282 } else if(o.containment.constructor == Array) {
6283 this.containment = o.containment;
6288 _convertPositionTo: function(d, pos) {
6290 if(!pos) pos = this.position;
6291 var mod = d == "absolute" ? 1 : -1;
6292 var o = this.options, 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);
6296 pos.top // The absolute mouse position
6297 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
6298 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
6299 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
6302 pos.left // The absolute mouse position
6303 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
6304 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
6305 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
6311 _generatePosition: function(event) {
6313 var o = this.options, 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);
6314 var pageX = event.pageX;
6315 var pageY = event.pageY;
6318 * - Position constraining -
6319 * Constrain the position to a mix of grid, containment.
6322 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
6324 if(this.containment) {
6325 if (this.relative_container){
6326 var co = this.relative_container.offset();
6327 containment = [ this.containment[0] + co.left,
6328 this.containment[1] + co.top,
6329 this.containment[2] + co.left,
6330 this.containment[3] + co.top ];
6333 containment = this.containment;
6336 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
6337 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
6338 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
6339 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
6343 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
6344 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
6345 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;
6347 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
6348 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;
6355 pageY // The absolute mouse position
6356 - this.offset.click.top // Click offset (relative to the element)
6357 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
6358 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
6359 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
6362 pageX // The absolute mouse position
6363 - this.offset.click.left // Click offset (relative to the element)
6364 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
6365 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
6366 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
6372 _clear: function() {
6373 this.helper.removeClass("ui-draggable-dragging");
6374 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
6375 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
6377 this.cancelHelperRemoval = false;
6380 // From now on bulk stuff - mainly helpers
6382 _trigger: function(type, event, ui) {
6383 ui = ui || this._uiHash();
6384 $.ui.plugin.call(this, type, [event, ui]);
6385 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
6386 return $.Widget.prototype._trigger.call(this, type, event, ui);
6391 _uiHash: function(event) {
6393 helper: this.helper,
6394 position: this.position,
6395 originalPosition: this.originalPosition,
6396 offset: this.positionAbs
6402 $.ui.plugin.add("draggable", "connectToSortable", {
6403 start: function(event, ui) {
6405 var inst = $(this).data("draggable"), o = inst.options,
6406 uiSortable = $.extend({}, ui, { item: inst.element });
6407 inst.sortables = [];
6408 $(o.connectToSortable).each(function() {
6409 var sortable = $.data(this, 'sortable');
6410 if (sortable && !sortable.options.disabled) {
6411 inst.sortables.push({
6413 shouldRevert: sortable.options.revert
6415 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).
6416 sortable._trigger("activate", event, uiSortable);
6421 stop: function(event, ui) {
6423 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
6424 var inst = $(this).data("draggable"),
6425 uiSortable = $.extend({}, ui, { item: inst.element });
6427 $.each(inst.sortables, function() {
6428 if(this.instance.isOver) {
6430 this.instance.isOver = 0;
6432 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
6433 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
6435 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
6436 if(this.shouldRevert) this.instance.options.revert = true;
6438 //Trigger the stop of the sortable
6439 this.instance._mouseStop(event);
6441 this.instance.options.helper = this.instance.options._helper;
6443 //If the helper has been the original item, restore properties in the sortable
6444 if(inst.options.helper == 'original')
6445 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
6448 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
6449 this.instance._trigger("deactivate", event, uiSortable);
6455 drag: function(event, ui) {
6457 var inst = $(this).data("draggable"), that = this;
6459 var checkPos = function(o) {
6460 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
6461 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
6462 var itemHeight = o.height, itemWidth = o.width;
6463 var itemTop = o.top, itemLeft = o.left;
6465 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
6468 $.each(inst.sortables, function(i) {
6470 var innermostIntersecting = false;
6471 var thisSortable = this;
6472 //Copy over some variables to allow calling the sortable's native _intersectsWith
6473 this.instance.positionAbs = inst.positionAbs;
6474 this.instance.helperProportions = inst.helperProportions;
6475 this.instance.offset.click = inst.offset.click;
6477 if(this.instance._intersectsWith(this.instance.containerCache)) {
6478 innermostIntersecting = true;
6479 $.each(inst.sortables, function () {
6480 this.instance.positionAbs = inst.positionAbs;
6481 this.instance.helperProportions = inst.helperProportions;
6482 this.instance.offset.click = inst.offset.click;
6483 if (this != thisSortable
6484 && this.instance._intersectsWith(this.instance.containerCache)
6485 && $.ui.contains(thisSortable.instance.element[0], this.instance.element[0]))
6486 innermostIntersecting = false;
6487 return innermostIntersecting;
6492 if(innermostIntersecting) {
6493 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
6494 if(!this.instance.isOver) {
6496 this.instance.isOver = 1;
6497 //Now we fake the start of dragging for the sortable instance,
6498 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
6499 //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)
6500 this.instance.currentItem = $(that).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
6501 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
6502 this.instance.options.helper = function() { return ui.helper[0]; };
6504 event.target = this.instance.currentItem[0];
6505 this.instance._mouseCapture(event, true);
6506 this.instance._mouseStart(event, true, true);
6508 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
6509 this.instance.offset.click.top = inst.offset.click.top;
6510 this.instance.offset.click.left = inst.offset.click.left;
6511 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
6512 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
6514 inst._trigger("toSortable", event);
6515 inst.dropped = this.instance.element; //draggable revert needs that
6516 //hack so receive/update callbacks work (mostly)
6517 inst.currentItem = inst.element;
6518 this.instance.fromOutside = inst;
6522 //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
6523 if(this.instance.currentItem) this.instance._mouseDrag(event);
6527 //If it doesn't intersect with the sortable, and it intersected before,
6528 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
6529 if(this.instance.isOver) {
6531 this.instance.isOver = 0;
6532 this.instance.cancelHelperRemoval = true;
6534 //Prevent reverting on this forced stop
6535 this.instance.options.revert = false;
6537 // The out event needs to be triggered independently
6538 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
6540 this.instance._mouseStop(event, true);
6541 this.instance.options.helper = this.instance.options._helper;
6543 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
6544 this.instance.currentItem.remove();
6545 if(this.instance.placeholder) this.instance.placeholder.remove();
6547 inst._trigger("fromSortable", event);
6548 inst.dropped = false; //draggable revert needs that
6558 $.ui.plugin.add("draggable", "cursor", {
6559 start: function(event, ui) {
6560 var t = $('body'), o = $(this).data('draggable').options;
6561 if (t.css("cursor")) o._cursor = t.css("cursor");
6562 t.css("cursor", o.cursor);
6564 stop: function(event, ui) {
6565 var o = $(this).data('draggable').options;
6566 if (o._cursor) $('body').css("cursor", o._cursor);
6570 $.ui.plugin.add("draggable", "opacity", {
6571 start: function(event, ui) {
6572 var t = $(ui.helper), o = $(this).data('draggable').options;
6573 if(t.css("opacity")) o._opacity = t.css("opacity");
6574 t.css('opacity', o.opacity);
6576 stop: function(event, ui) {
6577 var o = $(this).data('draggable').options;
6578 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
6582 $.ui.plugin.add("draggable", "scroll", {
6583 start: function(event, ui) {
6584 var i = $(this).data("draggable");
6585 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
6587 drag: function(event, ui) {
6589 var i = $(this).data("draggable"), o = i.options, scrolled = false;
6591 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
6593 if(!o.axis || o.axis != 'x') {
6594 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
6595 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
6596 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
6597 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
6600 if(!o.axis || o.axis != 'y') {
6601 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
6602 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
6603 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
6604 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
6609 if(!o.axis || o.axis != 'x') {
6610 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
6611 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
6612 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
6613 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
6616 if(!o.axis || o.axis != 'y') {
6617 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
6618 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
6619 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
6620 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
6625 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
6626 $.ui.ddmanager.prepareOffsets(i, event);
6631 $.ui.plugin.add("draggable", "snap", {
6632 start: function(event, ui) {
6634 var i = $(this).data("draggable"), o = i.options;
6635 i.snapElements = [];
6637 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
6638 var $t = $(this); var $o = $t.offset();
6639 if(this != i.element[0]) i.snapElements.push({
6641 width: $t.outerWidth(), height: $t.outerHeight(),
6642 top: $o.top, left: $o.left
6647 drag: function(event, ui) {
6649 var inst = $(this).data("draggable"), o = inst.options;
6650 var d = o.snapTolerance;
6652 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
6653 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
6655 for (var i = inst.snapElements.length - 1; i >= 0; i--){
6657 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
6658 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
6660 //Yes, I know, this is insane ;)
6661 if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
6662 if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
6663 inst.snapElements[i].snapping = false;
6667 if(o.snapMode != 'inner') {
6668 var ts = Math.abs(t - y2) <= d;
6669 var bs = Math.abs(b - y1) <= d;
6670 var ls = Math.abs(l - x2) <= d;
6671 var rs = Math.abs(r - x1) <= d;
6672 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
6673 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
6674 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
6675 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
6678 var first = (ts || bs || ls || rs);
6680 if(o.snapMode != 'outer') {
6681 var ts = Math.abs(t - y1) <= d;
6682 var bs = Math.abs(b - y2) <= d;
6683 var ls = Math.abs(l - x1) <= d;
6684 var rs = Math.abs(r - x2) <= d;
6685 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
6686 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
6687 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
6688 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
6691 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
6692 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
6693 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
6700 $.ui.plugin.add("draggable", "stack", {
6701 start: function(event, ui) {
6703 var o = $(this).data("draggable").options;
6705 var group = $.makeArray($(o.stack)).sort(function(a,b) {
6706 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
6708 if (!group.length) { return; }
6710 var min = parseInt(group[0].style.zIndex) || 0;
6711 $(group).each(function(i) {
6712 this.style.zIndex = min + i;
6715 this[0].style.zIndex = min + group.length;
6720 $.ui.plugin.add("draggable", "zIndex", {
6721 start: function(event, ui) {
6722 var t = $(ui.helper), o = $(this).data("draggable").options;
6723 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
6724 t.css('zIndex', o.zIndex);
6726 stop: function(event, ui) {
6727 var o = $(this).data("draggable").options;
6728 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
6733 (function( $, undefined ) {
6735 $.widget("ui.droppable", {
6737 widgetEventPrefix: "drop",
6745 tolerance: 'intersect'
6747 _create: function() {
6749 var o = this.options, accept = o.accept;
6750 this.isover = 0; this.isout = 1;
6752 this.accept = $.isFunction(accept) ? accept : function(d) {
6753 return d.is(accept);
6756 //Store the droppable's proportions
6757 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
6759 // Add the reference and positions to the manager
6760 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
6761 $.ui.ddmanager.droppables[o.scope].push(this);
6763 (o.addClasses && this.element.addClass("ui-droppable"));
6767 _destroy: function() {
6768 var drop = $.ui.ddmanager.droppables[this.options.scope];
6769 for ( var i = 0; i < drop.length; i++ )
6770 if ( drop[i] == this )
6773 this.element.removeClass("ui-droppable ui-droppable-disabled");
6776 _setOption: function(key, value) {
6778 if(key == 'accept') {
6779 this.accept = $.isFunction(value) ? value : function(d) {
6783 $.Widget.prototype._setOption.apply(this, arguments);
6786 _activate: function(event) {
6787 var draggable = $.ui.ddmanager.current;
6788 if(this.options.activeClass) this.element.addClass(this.options.activeClass);
6789 (draggable && this._trigger('activate', event, this.ui(draggable)));
6792 _deactivate: function(event) {
6793 var draggable = $.ui.ddmanager.current;
6794 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
6795 (draggable && this._trigger('deactivate', event, this.ui(draggable)));
6798 _over: function(event) {
6800 var draggable = $.ui.ddmanager.current;
6801 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
6803 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6804 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
6805 this._trigger('over', event, this.ui(draggable));
6810 _out: function(event) {
6812 var draggable = $.ui.ddmanager.current;
6813 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
6815 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6816 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
6817 this._trigger('out', event, this.ui(draggable));
6822 _drop: function(event,custom) {
6824 var draggable = custom || $.ui.ddmanager.current;
6825 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
6827 var childrenIntersection = false;
6828 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
6829 var inst = $.data(this, 'droppable');
6832 && !inst.options.disabled
6833 && inst.options.scope == draggable.options.scope
6834 && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
6835 && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
6836 ) { childrenIntersection = true; return false; }
6838 if(childrenIntersection) return false;
6840 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6841 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
6842 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
6843 this._trigger('drop', event, this.ui(draggable));
6844 return this.element;
6853 draggable: (c.currentItem || c.element),
6855 position: c.position,
6856 offset: c.positionAbs
6862 $.ui.intersect = function(draggable, droppable, toleranceMode) {
6864 if (!droppable.offset) return false;
6866 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
6867 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
6868 var l = droppable.offset.left, r = l + droppable.proportions.width,
6869 t = droppable.offset.top, b = t + droppable.proportions.height;
6871 switch (toleranceMode) {
6873 return (l <= x1 && x2 <= r
6874 && t <= y1 && y2 <= b);
6877 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
6878 && x2 - (draggable.helperProportions.width / 2) < r // Left Half
6879 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
6880 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
6883 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
6884 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
6885 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
6890 (y1 >= t && y1 <= b) || // Top edge touching
6891 (y2 >= t && y2 <= b) || // Bottom edge touching
6892 (y1 < t && y2 > b) // Surrounded vertically
6894 (x1 >= l && x1 <= r) || // Left edge touching
6895 (x2 >= l && x2 <= r) || // Right edge touching
6896 (x1 < l && x2 > r) // Surrounded horizontally
6907 This manager tracks offsets of draggables and droppables
6911 droppables: { 'default': [] },
6912 prepareOffsets: function(t, event) {
6914 var m = $.ui.ddmanager.droppables[t.options.scope] || [];
6915 var type = event ? event.type : null; // workaround for #2317
6916 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
6918 droppablesLoop: for (var i = 0; i < m.length; i++) {
6920 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
6921 for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
6922 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
6924 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
6926 m[i].offset = m[i].element.offset();
6927 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
6932 drop: function(draggable, event) {
6934 var dropped = false;
6935 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
6937 if(!this.options) return;
6938 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
6939 dropped = this._drop.call(this, event) || dropped;
6941 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
6942 this.isout = 1; this.isover = 0;
6943 this._deactivate.call(this, event);
6950 dragStart: function( draggable, event ) {
6951 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
6952 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
6953 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
6956 drag: function(draggable, event) {
6958 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
6959 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
6961 //Run through all droppables and check their positions based on specific tolerance options
6962 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
6964 if(this.options.disabled || this.greedyChild || !this.visible) return;
6965 var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
6967 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
6971 if (this.options.greedy) {
6972 // find droppable parents with same scope
6973 var scope = this.options.scope;
6974 var parent = this.element.parents(':data(droppable)').filter(function () {
6975 return $.data(this, 'droppable').options.scope === scope;
6978 if (parent.length) {
6979 parentInstance = $.data(parent[0], 'droppable');
6980 parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
6984 // we just moved into a greedy child
6985 if (parentInstance && c == 'isover') {
6986 parentInstance['isover'] = 0;
6987 parentInstance['isout'] = 1;
6988 parentInstance._out.call(parentInstance, event);
6991 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
6992 this[c == "isover" ? "_over" : "_out"].call(this, event);
6994 // we just moved out of a greedy child
6995 if (parentInstance && c == 'isout') {
6996 parentInstance['isout'] = 0;
6997 parentInstance['isover'] = 1;
6998 parentInstance._over.call(parentInstance, event);
7003 dragStop: function( draggable, event ) {
7004 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
7005 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
7006 if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
7011 ;(jQuery.effects || (function($, undefined) {
7013 var backCompat = $.uiBackCompat !== false,
7014 // prefix used for storing data on .data()
7015 dataSpace = "ui-effects-";
7022 * jQuery Color Animations v2.0.0
7023 * http://jquery.com/
7025 * Copyright 2012 jQuery Foundation and other contributors
7026 * Released under the MIT license.
7027 * http://jquery.org/license
7029 * Date: Mon Aug 13 13:41:02 2012 -0500
7031 (function( jQuery, undefined ) {
7033 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor".split(" "),
7035 // plusequals test for += 100 -= 100
7036 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
7037 // a set of RE's that can match strings and generate color tuples.
7039 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
7040 parse: function( execResult ) {
7049 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
7050 parse: function( execResult ) {
7052 execResult[ 1 ] * 2.55,
7053 execResult[ 2 ] * 2.55,
7054 execResult[ 3 ] * 2.55,
7059 // this regex ignores A-F because it's compared against an already lowercased string
7060 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
7061 parse: function( execResult ) {
7063 parseInt( execResult[ 1 ], 16 ),
7064 parseInt( execResult[ 2 ], 16 ),
7065 parseInt( execResult[ 3 ], 16 )
7069 // this regex ignores A-F because it's compared against an already lowercased string
7070 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
7071 parse: function( execResult ) {
7073 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
7074 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
7075 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
7079 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
7081 parse: function( execResult ) {
7084 execResult[ 2 ] / 100,
7085 execResult[ 3 ] / 100,
7092 color = jQuery.Color = function( color, green, blue, alpha ) {
7093 return new jQuery.Color.fn.parse( color, green, blue, alpha );
7143 support = color.support = {},
7145 // element for support tests
7146 supportElem = jQuery( "<p>" )[ 0 ],
7148 // colors = jQuery.Color.names
7151 // local aliases of functions called often
7154 // determine rgba support immediately
7155 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
7156 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
7158 // define cache name and alpha properties
7159 // for rgba and hsla spaces
7160 each( spaces, function( spaceName, space ) {
7161 space.cache = "_" + spaceName;
7162 space.props.alpha = {
7169 function clamp( value, prop, allowEmpty ) {
7170 var type = propTypes[ prop.type ] || {};
7172 if ( value == null ) {
7173 return (allowEmpty || !prop.def) ? null : prop.def;
7176 // ~~ is an short way of doing floor for positive numbers
7177 value = type.floor ? ~~value : parseFloat( value );
7179 // IE will pass in empty strings as value for alpha,
7180 // which will hit this case
7181 if ( isNaN( value ) ) {
7186 // we add mod before modding to make sure that negatives values
7187 // get converted properly: -10 -> 350
7188 return (value + type.mod) % type.mod;
7191 // for now all property types without mod have min and max
7192 return 0 > value ? 0 : type.max < value ? type.max : value;
7195 function stringParse( string ) {
7197 rgba = inst._rgba = [];
7199 string = string.toLowerCase();
7201 each( stringParsers, function( i, parser ) {
7203 match = parser.re.exec( string ),
7204 values = match && parser.parse( match ),
7205 spaceName = parser.space || "rgba";
7208 parsed = inst[ spaceName ]( values );
7210 // if this was an rgba parse the assignment might happen twice
7212 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
7213 rgba = inst._rgba = parsed._rgba;
7215 // exit each( stringParsers ) here because we matched
7220 // Found a stringParser that handled it
7221 if ( rgba.length ) {
7223 // if this came from a parsed string, force "transparent" when alpha is 0
7224 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
7225 if ( rgba.join() === "0,0,0,0" ) {
7226 jQuery.extend( rgba, colors.transparent );
7232 return colors[ string ];
7235 color.fn = jQuery.extend( color.prototype, {
7236 parse: function( red, green, blue, alpha ) {
7237 if ( red === undefined ) {
7238 this._rgba = [ null, null, null, null ];
7241 if ( red.jquery || red.nodeType ) {
7242 red = jQuery( red ).css( green );
7247 type = jQuery.type( red ),
7248 rgba = this._rgba = [];
7250 // more than 1 argument specified - assume ( red, green, blue, alpha )
7251 if ( green !== undefined ) {
7252 red = [ red, green, blue, alpha ];
7256 if ( type === "string" ) {
7257 return this.parse( stringParse( red ) || colors._default );
7260 if ( type === "array" ) {
7261 each( spaces.rgba.props, function( key, prop ) {
7262 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
7267 if ( type === "object" ) {
7268 if ( red instanceof color ) {
7269 each( spaces, function( spaceName, space ) {
7270 if ( red[ space.cache ] ) {
7271 inst[ space.cache ] = red[ space.cache ].slice();
7275 each( spaces, function( spaceName, space ) {
7276 var cache = space.cache;
7277 each( space.props, function( key, prop ) {
7279 // if the cache doesn't exist, and we know how to convert
7280 if ( !inst[ cache ] && space.to ) {
7282 // if the value was null, we don't need to copy it
7283 // if the key was alpha, we don't need to copy it either
7284 if ( key === "alpha" || red[ key ] == null ) {
7287 inst[ cache ] = space.to( inst._rgba );
7290 // this is the only case where we allow nulls for ALL properties.
7291 // call clamp with alwaysAllowEmpty
7292 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
7295 // everything defined but alpha?
7296 if ( inst[ cache ] && $.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
7297 // use the default of 1
7298 inst[ cache ][ 3 ] = 1;
7300 inst._rgba = space.from( inst[ cache ] );
7308 is: function( compare ) {
7309 var is = color( compare ),
7313 each( spaces, function( _, space ) {
7315 isCache = is[ space.cache ];
7317 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
7318 each( space.props, function( _, prop ) {
7319 if ( isCache[ prop.idx ] != null ) {
7320 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
7329 _space: function() {
7332 each( spaces, function( spaceName, space ) {
7333 if ( inst[ space.cache ] ) {
7334 used.push( spaceName );
7339 transition: function( other, distance ) {
7340 var end = color( other ),
7341 spaceName = end._space(),
7342 space = spaces[ spaceName ],
7343 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
7344 start = startColor[ space.cache ] || space.to( startColor._rgba ),
7345 result = start.slice();
7347 end = end[ space.cache ];
7348 each( space.props, function( key, prop ) {
7349 var index = prop.idx,
7350 startValue = start[ index ],
7351 endValue = end[ index ],
7352 type = propTypes[ prop.type ] || {};
7354 // if null, don't override start value
7355 if ( endValue === null ) {
7358 // if null - use end
7359 if ( startValue === null ) {
7360 result[ index ] = endValue;
7363 if ( endValue - startValue > type.mod / 2 ) {
7364 startValue += type.mod;
7365 } else if ( startValue - endValue > type.mod / 2 ) {
7366 startValue -= type.mod;
7369 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
7372 return this[ spaceName ]( result );
7374 blend: function( opaque ) {
7375 // if we are already opaque - return ourself
7376 if ( this._rgba[ 3 ] === 1 ) {
7380 var rgb = this._rgba.slice(),
7382 blend = color( opaque )._rgba;
7384 return color( jQuery.map( rgb, function( v, i ) {
7385 return ( 1 - a ) * blend[ i ] + a * v;
7388 toRgbaString: function() {
7389 var prefix = "rgba(",
7390 rgba = jQuery.map( this._rgba, function( v, i ) {
7391 return v == null ? ( i > 2 ? 1 : 0 ) : v;
7394 if ( rgba[ 3 ] === 1 ) {
7399 return prefix + rgba.join() + ")";
7401 toHslaString: function() {
7402 var prefix = "hsla(",
7403 hsla = jQuery.map( this.hsla(), function( v, i ) {
7410 v = Math.round( v * 100 ) + "%";
7415 if ( hsla[ 3 ] === 1 ) {
7419 return prefix + hsla.join() + ")";
7421 toHexString: function( includeAlpha ) {
7422 var rgba = this._rgba.slice(),
7425 if ( includeAlpha ) {
7426 rgba.push( ~~( alpha * 255 ) );
7429 return "#" + jQuery.map( rgba, function( v ) {
7431 // default to 0 when nulls exist
7432 v = ( v || 0 ).toString( 16 );
7433 return v.length === 1 ? "0" + v : v;
7436 toString: function() {
7437 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
7440 color.fn.parse.prototype = color.fn;
7442 // hsla conversions adapted from:
7443 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
7445 function hue2rgb( p, q, h ) {
7448 return p + (q - p) * h * 6;
7454 return p + (q - p) * ((2/3) - h) * 6;
7459 spaces.hsla.to = function ( rgba ) {
7460 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
7461 return [ null, null, null, rgba[ 3 ] ];
7463 var r = rgba[ 0 ] / 255,
7464 g = rgba[ 1 ] / 255,
7465 b = rgba[ 2 ] / 255,
7467 max = Math.max( r, g, b ),
7468 min = Math.min( r, g, b ),
7474 if ( min === max ) {
7476 } else if ( r === max ) {
7477 h = ( 60 * ( g - b ) / diff ) + 360;
7478 } else if ( g === max ) {
7479 h = ( 60 * ( b - r ) / diff ) + 120;
7481 h = ( 60 * ( r - g ) / diff ) + 240;
7484 if ( l === 0 || l === 1 ) {
7486 } else if ( l <= 0.5 ) {
7489 s = diff / ( 2 - add );
7491 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
7494 spaces.hsla.from = function ( hsla ) {
7495 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
7496 return [ null, null, null, hsla[ 3 ] ];
7498 var h = hsla[ 0 ] / 360,
7502 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
7506 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
7507 Math.round( hue2rgb( p, q, h ) * 255 ),
7508 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
7514 each( spaces, function( spaceName, space ) {
7515 var props = space.props,
7516 cache = space.cache,
7520 // makes rgba() and hsla()
7521 color.fn[ spaceName ] = function( value ) {
7523 // generate a cache for this space if it doesn't exist
7524 if ( to && !this[ cache ] ) {
7525 this[ cache ] = to( this._rgba );
7527 if ( value === undefined ) {
7528 return this[ cache ].slice();
7532 type = jQuery.type( value ),
7533 arr = ( type === "array" || type === "object" ) ? value : arguments,
7534 local = this[ cache ].slice();
7536 each( props, function( key, prop ) {
7537 var val = arr[ type === "object" ? key : prop.idx ];
7538 if ( val == null ) {
7539 val = local[ prop.idx ];
7541 local[ prop.idx ] = clamp( val, prop );
7545 ret = color( from( local ) );
7546 ret[ cache ] = local;
7549 return color( local );
7553 // makes red() green() blue() alpha() hue() saturation() lightness()
7554 each( props, function( key, prop ) {
7555 // alpha is included in more than one space
7556 if ( color.fn[ key ] ) {
7559 color.fn[ key ] = function( value ) {
7560 var vtype = jQuery.type( value ),
7561 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
7562 local = this[ fn ](),
7563 cur = local[ prop.idx ],
7566 if ( vtype === "undefined" ) {
7570 if ( vtype === "function" ) {
7571 value = value.call( this, cur );
7572 vtype = jQuery.type( value );
7574 if ( value == null && prop.empty ) {
7577 if ( vtype === "string" ) {
7578 match = rplusequals.exec( value );
7580 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
7583 local[ prop.idx ] = value;
7584 return this[ fn ]( local );
7589 // add .fx.step functions
7590 each( stepHooks, function( i, hook ) {
7591 jQuery.cssHooks[ hook ] = {
7592 set: function( elem, value ) {
7593 var parsed, curElem,
7594 backgroundColor = "";
7596 if ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) {
7597 value = color( parsed || value );
7598 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
7599 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
7601 (backgroundColor === "" || backgroundColor === "transparent") &&
7602 curElem && curElem.style
7605 backgroundColor = jQuery.css( curElem, "backgroundColor" );
7606 curElem = curElem.parentNode;
7611 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
7616 value = value.toRgbaString();
7619 elem.style[ hook ] = value;
7621 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
7625 jQuery.fx.step[ hook ] = function( fx ) {
7626 if ( !fx.colorInit ) {
7627 fx.start = color( fx.elem, hook );
7628 fx.end = color( fx.end );
7629 fx.colorInit = true;
7631 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
7635 jQuery.cssHooks.borderColor = {
7636 expand: function( value ) {
7639 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
7640 expanded[ "border" + part + "Color" ] = value;
7646 // Basic color names only.
7647 // Usage of any of the other color names requires adding yourself or including
7648 // jquery.color.svg-names.js.
7649 colors = jQuery.Color.names = {
7650 // 4.1. Basic color keywords
7668 // 4.2.3. "transparent" color keyword
7669 transparent: [ null, null, null, 0 ],
7678 /******************************************************************************/
7679 /****************************** CLASS ANIMATIONS ******************************/
7680 /******************************************************************************/
7683 var classAnimationActions = [ "add", "remove", "toggle" ],
7696 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
7697 $.fx.step[ prop ] = function( fx ) {
7698 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
7699 jQuery.style( fx.elem, prop, fx.end );
7705 function getElementStyles() {
7706 var style = this.ownerDocument.defaultView ?
7707 this.ownerDocument.defaultView.getComputedStyle( this, null ) :
7713 // webkit enumerates style porperties
7714 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
7718 if ( typeof style[ key ] === "string" ) {
7719 newStyle[ $.camelCase( key ) ] = style[ key ];
7723 for ( key in style ) {
7724 if ( typeof style[ key ] === "string" ) {
7725 newStyle[ key ] = style[ key ];
7734 function styleDifference( oldStyle, newStyle ) {
7738 for ( name in newStyle ) {
7739 value = newStyle[ name ];
7740 if ( oldStyle[ name ] !== value ) {
7741 if ( !shorthandStyles[ name ] ) {
7742 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
7743 diff[ name ] = value;
7752 $.effects.animateClass = function( value, duration, easing, callback ) {
7753 var o = $.speed( duration, easing, callback );
7755 return this.queue( function() {
7756 var animated = $( this ),
7757 baseClass = animated.attr( "class" ) || "",
7759 allAnimations = o.children ? animated.find( "*" ).andSelf() : animated;
7761 // map the animated objects to store the original styles.
7762 allAnimations = allAnimations.map(function() {
7766 start: getElementStyles.call( this )
7770 // apply class change
7771 applyClassChange = function() {
7772 $.each( classAnimationActions, function(i, action) {
7773 if ( value[ action ] ) {
7774 animated[ action + "Class" ]( value[ action ] );
7780 // map all animated objects again - calculate new styles and diff
7781 allAnimations = allAnimations.map(function() {
7782 this.end = getElementStyles.call( this.el[ 0 ] );
7783 this.diff = styleDifference( this.start, this.end );
7787 // apply original class
7788 animated.attr( "class", baseClass );
7790 // map all animated objects again - this time collecting a promise
7791 allAnimations = allAnimations.map(function() {
7792 var styleInfo = this,
7794 opts = jQuery.extend({}, o, {
7796 complete: function() {
7797 dfd.resolve( styleInfo );
7801 this.el.animate( this.diff, opts );
7802 return dfd.promise();
7805 // once all animations have completed:
7806 $.when.apply( $, allAnimations.get() ).done(function() {
7808 // set the final class
7811 // for each animated element,
7812 // clear all css properties that were animated
7813 $.each( arguments, function() {
7815 $.each( this.diff, function(key) {
7820 // this is guarnteed to be there if you use jQuery.speed()
7821 // it also handles dequeuing the next anim...
7822 o.complete.call( animated[ 0 ] );
7828 _addClass: $.fn.addClass,
7829 addClass: function( classNames, speed, easing, callback ) {
7831 $.effects.animateClass.call( this,
7832 { add: classNames }, speed, easing, callback ) :
7833 this._addClass( classNames );
7836 _removeClass: $.fn.removeClass,
7837 removeClass: function( classNames, speed, easing, callback ) {
7839 $.effects.animateClass.call( this,
7840 { remove: classNames }, speed, easing, callback ) :
7841 this._removeClass( classNames );
7844 _toggleClass: $.fn.toggleClass,
7845 toggleClass: function( classNames, force, speed, easing, callback ) {
7846 if ( typeof force === "boolean" || force === undefined ) {
7848 // without speed parameter
7849 return this._toggleClass( classNames, force );
7851 return $.effects.animateClass.call( this,
7852 (force ? { add: classNames } : { remove: classNames }),
7853 speed, easing, callback );
7856 // without force parameter
7857 return $.effects.animateClass.call( this,
7858 { toggle: classNames }, force, speed, easing );
7862 switchClass: function( remove, add, speed, easing, callback) {
7863 return $.effects.animateClass.call( this, {
7866 }, speed, easing, callback );
7872 /******************************************************************************/
7873 /*********************************** EFFECTS **********************************/
7874 /******************************************************************************/
7878 $.extend( $.effects, {
7881 // Saves a set of properties in a data storage
7882 save: function( element, set ) {
7883 for( var i=0; i < set.length; i++ ) {
7884 if ( set[ i ] !== null ) {
7885 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
7890 // Restores a set of previously saved properties from a data storage
7891 restore: function( element, set ) {
7893 for( i=0; i < set.length; i++ ) {
7894 if ( set[ i ] !== null ) {
7895 val = element.data( dataSpace + set[ i ] );
7896 // support: jQuery 1.6.2
7897 // http://bugs.jquery.com/ticket/9917
7898 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
7899 // We can't differentiate between "" and 0 here, so we just assume
7900 // empty string since it's likely to be a more common value...
7901 if ( val === undefined ) {
7904 element.css( set[ i ], val );
7909 setMode: function( el, mode ) {
7910 if (mode === "toggle") {
7911 mode = el.is( ":hidden" ) ? "show" : "hide";
7916 // Translates a [top,left] array into a baseline value
7917 // this should be a little more flexible in the future to handle a string & hash
7918 getBaseline: function( origin, original ) {
7920 switch ( origin[ 0 ] ) {
7921 case "top": y = 0; break;
7922 case "middle": y = 0.5; break;
7923 case "bottom": y = 1; break;
7924 default: y = origin[ 0 ] / original.height;
7926 switch ( origin[ 1 ] ) {
7927 case "left": x = 0; break;
7928 case "center": x = 0.5; break;
7929 case "right": x = 1; break;
7930 default: x = origin[ 1 ] / original.width;
7938 // Wraps the element around a wrapper that copies position properties
7939 createWrapper: function( element ) {
7941 // if the element is already wrapped, return it
7942 if ( element.parent().is( ".ui-effects-wrapper" )) {
7943 return element.parent();
7948 width: element.outerWidth(true),
7949 height: element.outerHeight(true),
7950 "float": element.css( "float" )
7952 wrapper = $( "<div></div>" )
7953 .addClass( "ui-effects-wrapper" )
7956 background: "transparent",
7961 // Store the size in case width/height are defined in % - Fixes #5245
7963 width: element.width(),
7964 height: element.height()
7966 active = document.activeElement;
7969 // Firefox incorrectly exposes anonymous content
7970 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
7974 active = document.body;
7977 element.wrap( wrapper );
7979 // Fixes #7595 - Elements lose focus when wrapped.
7980 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
7981 $( active ).focus();
7984 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
7986 // transfer positioning properties to the wrapper
7987 if ( element.css( "position" ) === "static" ) {
7988 wrapper.css({ position: "relative" });
7989 element.css({ position: "relative" });
7992 position: element.css( "position" ),
7993 zIndex: element.css( "z-index" )
7995 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
7996 props[ pos ] = element.css( pos );
7997 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
7998 props[ pos ] = "auto";
8002 position: "relative",
8011 return wrapper.css( props ).show();
8014 removeWrapper: function( element ) {
8015 var active = document.activeElement;
8017 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
8018 element.parent().replaceWith( element );
8020 // Fixes #7595 - Elements lose focus when wrapped.
8021 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
8022 $( active ).focus();
8030 setTransition: function( element, list, factor, value ) {
8031 value = value || {};
8032 $.each( list, function( i, x ) {
8033 var unit = element.cssUnit( x );
8034 if ( unit[ 0 ] > 0 ) {
8035 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
8042 // return an effect options object for the given parameters:
8043 function _normalizeArguments( effect, options, speed, callback ) {
8045 // allow passing all options as the first parameter
8046 if ( $.isPlainObject( effect ) ) {
8048 effect = effect.effect;
8051 // convert to an object
8052 effect = { effect: effect };
8054 // catch (effect, null, ...)
8055 if ( options == null ) {
8059 // catch (effect, callback)
8060 if ( $.isFunction( options ) ) {
8066 // catch (effect, speed, ?)
8067 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
8073 // catch (effect, options, callback)
8074 if ( $.isFunction( speed ) ) {
8079 // add options to effect
8081 $.extend( effect, options );
8084 speed = speed || options.duration;
8085 effect.duration = $.fx.off ? 0 :
8086 typeof speed === "number" ? speed :
8087 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
8088 $.fx.speeds._default;
8090 effect.complete = callback || options.complete;
8095 function standardSpeed( speed ) {
8096 // valid standard speeds
8097 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
8101 // invalid strings - treat as "normal" speed
8102 if ( typeof speed === "string" && !$.effects.effect[ speed ] ) {
8103 // TODO: remove in 2.0 (#7115)
8104 if ( backCompat && $.effects[ speed ] ) {
8114 effect: function( /* effect, options, speed, callback */ ) {
8115 var args = _normalizeArguments.apply( this, arguments ),
8118 effectMethod = $.effects.effect[ args.effect ],
8120 // DEPRECATED: remove in 2.0 (#7115)
8121 oldEffectMethod = !effectMethod && backCompat && $.effects[ args.effect ];
8123 if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
8124 // delegate to the original method (e.g., .show()) if possible
8126 return this[ mode ]( args.duration, args.complete );
8128 return this.each( function() {
8129 if ( args.complete ) {
8130 args.complete.call( this );
8136 function run( next ) {
8137 var elem = $( this ),
8138 complete = args.complete,
8142 if ( $.isFunction( complete ) ) {
8143 complete.call( elem[0] );
8145 if ( $.isFunction( next ) ) {
8150 // if the element is hiddden and mode is hide,
8151 // or element is visible and mode is show
8152 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
8155 effectMethod.call( elem[0], args, done );
8159 // TODO: remove this check in 2.0, effectMethod will always be true
8160 if ( effectMethod ) {
8161 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
8163 // DEPRECATED: remove in 2.0 (#7115)
8164 return oldEffectMethod.call(this, {
8166 duration: args.duration,
8167 callback: args.complete,
8174 show: function( speed ) {
8175 if ( standardSpeed( speed ) ) {
8176 return this._show.apply( this, arguments );
8178 var args = _normalizeArguments.apply( this, arguments );
8180 return this.effect.call( this, args );
8185 hide: function( speed ) {
8186 if ( standardSpeed( speed ) ) {
8187 return this._hide.apply( this, arguments );
8189 var args = _normalizeArguments.apply( this, arguments );
8191 return this.effect.call( this, args );
8195 // jQuery core overloads toggle and creates _toggle
8196 __toggle: $.fn.toggle,
8197 toggle: function( speed ) {
8198 if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
8199 return this.__toggle.apply( this, arguments );
8201 var args = _normalizeArguments.apply( this, arguments );
8202 args.mode = "toggle";
8203 return this.effect.call( this, args );
8208 cssUnit: function(key) {
8209 var style = this.css( key ),
8212 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
8213 if ( style.indexOf( unit ) > 0 ) {
8214 val = [ parseFloat( style ), unit ];
8223 /******************************************************************************/
8224 /*********************************** EASING ***********************************/
8225 /******************************************************************************/
8229 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
8231 var baseEasings = {};
8233 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
8234 baseEasings[ name ] = function( p ) {
8235 return Math.pow( p, i + 2 );
8239 $.extend( baseEasings, {
8240 Sine: function ( p ) {
8241 return 1 - Math.cos( p * Math.PI / 2 );
8243 Circ: function ( p ) {
8244 return 1 - Math.sqrt( 1 - p * p );
8246 Elastic: function( p ) {
8247 return p === 0 || p === 1 ? p :
8248 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
8250 Back: function( p ) {
8251 return p * p * ( 3 * p - 2 );
8253 Bounce: function ( p ) {
8257 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
8258 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
8262 $.each( baseEasings, function( name, easeIn ) {
8263 $.easing[ "easeIn" + name ] = easeIn;
8264 $.easing[ "easeOut" + name ] = function( p ) {
8265 return 1 - easeIn( 1 - p );
8267 $.easing[ "easeInOut" + name ] = function( p ) {
8269 easeIn( p * 2 ) / 2 :
8270 1 - easeIn( p * -2 + 2 ) / 2;
8277 (function( $, undefined ) {
8279 var rvertical = /up|down|vertical/,
8280 rpositivemotion = /up|left|vertical|horizontal/;
8282 $.effects.effect.blind = function( o, done ) {
8285 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8286 mode = $.effects.setMode( el, o.mode || "hide" ),
8287 direction = o.direction || "up",
8288 vertical = rvertical.test( direction ),
8289 ref = vertical ? "height" : "width",
8290 ref2 = vertical ? "top" : "left",
8291 motion = rpositivemotion.test( direction ),
8293 show = mode === "show",
8294 wrapper, distance, margin;
8296 // if already wrapped, the wrapper's properties are my property. #6245
8297 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
8298 $.effects.save( el.parent(), props );
8300 $.effects.save( el, props );
8303 wrapper = $.effects.createWrapper( el ).css({
8307 distance = wrapper[ ref ]();
8308 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
8310 animation[ ref ] = show ? distance : 0;
8313 .css( vertical ? "bottom" : "right", 0 )
8314 .css( vertical ? "top" : "left", "auto" )
8315 .css({ position: "absolute" });
8317 animation[ ref2 ] = show ? margin : distance + margin;
8320 // start at 0 if we are showing
8322 wrapper.css( ref, 0 );
8324 wrapper.css( ref2, margin + distance );
8329 wrapper.animate( animation, {
8330 duration: o.duration,
8333 complete: function() {
8334 if ( mode === "hide" ) {
8337 $.effects.restore( el, props );
8338 $.effects.removeWrapper( el );
8346 (function( $, undefined ) {
8348 $.effects.effect.bounce = function( o, done ) {
8350 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8353 mode = $.effects.setMode( el, o.mode || "effect" ),
8354 hide = mode === "hide",
8355 show = mode === "show",
8356 direction = o.direction || "up",
8357 distance = o.distance,
8358 times = o.times || 5,
8360 // number of internal animations
8361 anims = times * 2 + ( show || hide ? 1 : 0 ),
8362 speed = o.duration / anims,
8366 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
8367 motion = ( direction === "up" || direction === "left" ),
8372 // we will need to re-assemble the queue to stack our animations in place
8374 queuelen = queue.length;
8376 // Avoid touching opacity to prevent clearType and PNG issues in IE
8377 if ( show || hide ) {
8378 props.push( "opacity" );
8381 $.effects.save( el, props );
8383 $.effects.createWrapper( el ); // Create Wrapper
8385 // default distance for the BIGGEST bounce is the outer Distance / 3
8387 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
8391 downAnim = { opacity: 1 };
8392 downAnim[ ref ] = 0;
8394 // if we are showing, force opacity 0 and set the initial position
8395 // then do the "first" animation
8396 el.css( "opacity", 0 )
8397 .css( ref, motion ? -distance * 2 : distance * 2 )
8398 .animate( downAnim, speed, easing );
8401 // start at the smallest distance if we are hiding
8403 distance = distance / Math.pow( 2, times - 1 );
8407 downAnim[ ref ] = 0;
8408 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
8409 for ( i = 0; i < times; i++ ) {
8411 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
8413 el.animate( upAnim, speed, easing )
8414 .animate( downAnim, speed, easing );
8416 distance = hide ? distance * 2 : distance / 2;
8419 // Last Bounce when Hiding
8421 upAnim = { opacity: 0 };
8422 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
8424 el.animate( upAnim, speed, easing );
8427 el.queue(function() {
8431 $.effects.restore( el, props );
8432 $.effects.removeWrapper( el );
8436 // inject all the animations we just queued to be first in line (after "inprogress")
8437 if ( queuelen > 1) {
8438 queue.splice.apply( queue,
8439 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8446 (function( $, undefined ) {
8448 $.effects.effect.clip = function( o, done ) {
8451 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8452 mode = $.effects.setMode( el, o.mode || "hide" ),
8453 show = mode === "show",
8454 direction = o.direction || "vertical",
8455 vert = direction === "vertical",
8456 size = vert ? "height" : "width",
8457 position = vert ? "top" : "left",
8459 wrapper, animate, distance;
8462 $.effects.save( el, props );
8466 wrapper = $.effects.createWrapper( el ).css({
8469 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
8470 distance = animate[ size ]();
8474 animate.css( size, 0 );
8475 animate.css( position, distance / 2 );
8478 // Create Animation Object:
8479 animation[ size ] = show ? distance : 0;
8480 animation[ position ] = show ? 0 : distance / 2;
8483 animate.animate( animation, {
8485 duration: o.duration,
8487 complete: function() {
8491 $.effects.restore( el, props );
8492 $.effects.removeWrapper( el );
8500 (function( $, undefined ) {
8502 $.effects.effect.drop = function( o, done ) {
8505 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
8506 mode = $.effects.setMode( el, o.mode || "hide" ),
8507 show = mode === "show",
8508 direction = o.direction || "left",
8509 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
8510 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
8512 opacity: show ? 1 : 0
8517 $.effects.save( el, props );
8519 $.effects.createWrapper( el );
8521 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
8525 .css( "opacity", 0 )
8526 .css( ref, motion === "pos" ? -distance : distance );
8530 animation[ ref ] = ( show ?
8531 ( motion === "pos" ? "+=" : "-=" ) :
8532 ( motion === "pos" ? "-=" : "+=" ) ) +
8536 el.animate( animation, {
8538 duration: o.duration,
8540 complete: function() {
8541 if ( mode === "hide" ) {
8544 $.effects.restore( el, props );
8545 $.effects.removeWrapper( el );
8552 (function( $, undefined ) {
8554 $.effects.effect.explode = function( o, done ) {
8556 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
8559 mode = $.effects.setMode( el, o.mode || "hide" ),
8560 show = mode === "show",
8562 // show and then visibility:hidden the element before calculating offset
8563 offset = el.show().css( "visibility", "hidden" ).offset(),
8565 // width and height of a piece
8566 width = Math.ceil( el.outerWidth() / cells ),
8567 height = Math.ceil( el.outerHeight() / rows ),
8571 i, j, left, top, mx, my;
8573 // children animate complete:
8574 function childComplete() {
8575 pieces.push( this );
8576 if ( pieces.length === rows * cells ) {
8581 // clone the element for each row and cell.
8582 for( i = 0; i < rows ; i++ ) { // ===>
8583 top = offset.top + i * height;
8584 my = i - ( rows - 1 ) / 2 ;
8586 for( j = 0; j < cells ; j++ ) { // |||
8587 left = offset.left + j * width;
8588 mx = j - ( cells - 1 ) / 2 ;
8590 // Create a clone of the now hidden main element that will be absolute positioned
8591 // within a wrapper div off the -left and -top equal to size of our pieces
8595 .wrap( "<div></div>" )
8597 position: "absolute",
8598 visibility: "visible",
8603 // select the wrapper - make it overflow: hidden and absolute positioned based on
8604 // where the original was located +left and +top equal to the size of pieces
8606 .addClass( "ui-effects-explode" )
8608 position: "absolute",
8612 left: left + ( show ? mx * width : 0 ),
8613 top: top + ( show ? my * height : 0 ),
8614 opacity: show ? 0 : 1
8616 left: left + ( show ? 0 : mx * width ),
8617 top: top + ( show ? 0 : my * height ),
8618 opacity: show ? 1 : 0
8619 }, o.duration || 500, o.easing, childComplete );
8623 function animComplete() {
8625 visibility: "visible"
8627 $( pieces ).remove();
8636 (function( $, undefined ) {
8638 $.effects.effect.fade = function( o, done ) {
8640 mode = $.effects.setMode( el, o.mode || "toggle" );
8646 duration: o.duration,
8653 (function( $, undefined ) {
8655 $.effects.effect.fold = function( o, done ) {
8659 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
8660 mode = $.effects.setMode( el, o.mode || "hide" ),
8661 show = mode === "show",
8662 hide = mode === "hide",
8663 size = o.size || 15,
8664 percent = /([0-9]+)%/.exec( size ),
8665 horizFirst = !!o.horizFirst,
8666 widthFirst = show !== horizFirst,
8667 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
8668 duration = o.duration / 2,
8673 $.effects.save( el, props );
8677 wrapper = $.effects.createWrapper( el ).css({
8680 distance = widthFirst ?
8681 [ wrapper.width(), wrapper.height() ] :
8682 [ wrapper.height(), wrapper.width() ];
8685 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
8688 wrapper.css( horizFirst ? {
8698 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
8699 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
8703 .animate( animation1, duration, o.easing )
8704 .animate( animation2, duration, o.easing, function() {
8708 $.effects.restore( el, props );
8709 $.effects.removeWrapper( el );
8716 (function( $, undefined ) {
8718 $.effects.effect.highlight = function( o, done ) {
8719 var elem = $( this ),
8720 props = [ "backgroundImage", "backgroundColor", "opacity" ],
8721 mode = $.effects.setMode( elem, o.mode || "show" ),
8723 backgroundColor: elem.css( "backgroundColor" )
8726 if (mode === "hide") {
8727 animation.opacity = 0;
8730 $.effects.save( elem, props );
8735 backgroundImage: "none",
8736 backgroundColor: o.color || "#ffff99"
8738 .animate( animation, {
8740 duration: o.duration,
8742 complete: function() {
8743 if ( mode === "hide" ) {
8746 $.effects.restore( elem, props );
8753 (function( $, undefined ) {
8755 $.effects.effect.pulsate = function( o, done ) {
8756 var elem = $( this ),
8757 mode = $.effects.setMode( elem, o.mode || "show" ),
8758 show = mode === "show",
8759 hide = mode === "hide",
8760 showhide = ( show || mode === "hide" ),
8762 // showing or hiding leaves of the "last" animation
8763 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
8764 duration = o.duration / anims,
8766 queue = elem.queue(),
8767 queuelen = queue.length,
8770 if ( show || !elem.is(":visible")) {
8771 elem.css( "opacity", 0 ).show();
8775 // anims - 1 opacity "toggles"
8776 for ( i = 1; i < anims; i++ ) {
8779 }, duration, o.easing );
8780 animateTo = 1 - animateTo;
8785 }, duration, o.easing);
8787 elem.queue(function() {
8794 // We just queued up "anims" animations, we need to put them next in the queue
8795 if ( queuelen > 1 ) {
8796 queue.splice.apply( queue,
8797 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
8803 (function( $, undefined ) {
8805 $.effects.effect.puff = function( o, done ) {
8806 var elem = $( this ),
8807 mode = $.effects.setMode( elem, o.mode || "hide" ),
8808 hide = mode === "hide",
8809 percent = parseInt( o.percent, 10 ) || 150,
8810 factor = percent / 100,
8812 height: elem.height(),
8813 width: elem.width(),
8814 outerHeight: elem.outerHeight(),
8815 outerWidth: elem.outerWidth()
8824 percent: hide ? percent : 100,
8828 height: original.height * factor,
8829 width: original.width * factor,
8830 outerHeight: original.outerHeight * factor,
8831 outerWidth: original.outerWidth * factor
8838 $.effects.effect.scale = function( o, done ) {
8842 options = $.extend( true, {}, o ),
8843 mode = $.effects.setMode( el, o.mode || "effect" ),
8844 percent = parseInt( o.percent, 10 ) ||
8845 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
8846 direction = o.direction || "both",
8849 height: el.height(),
8851 outerHeight: el.outerHeight(),
8852 outerWidth: el.outerWidth()
8855 y: direction !== "horizontal" ? (percent / 100) : 1,
8856 x: direction !== "vertical" ? (percent / 100) : 1
8859 // We are going to pass this effect to the size effect:
8860 options.effect = "size";
8861 options.queue = false;
8862 options.complete = done;
8864 // Set default origin and restore for show/hide
8865 if ( mode !== "effect" ) {
8866 options.origin = origin || ["middle","center"];
8867 options.restore = true;
8870 options.from = o.from || ( mode === "show" ? {
8877 height: original.height * factor.y,
8878 width: original.width * factor.x,
8879 outerHeight: original.outerHeight * factor.y,
8880 outerWidth: original.outerWidth * factor.x
8883 // Fade option to support puff
8884 if ( options.fade ) {
8885 if ( mode === "show" ) {
8886 options.from.opacity = 0;
8887 options.to.opacity = 1;
8889 if ( mode === "hide" ) {
8890 options.from.opacity = 1;
8891 options.to.opacity = 0;
8896 el.effect( options );
8900 $.effects.effect.size = function( o, done ) {
8903 var original, baseline, factor,
8905 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
8908 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
8910 // Copy for children
8911 props2 = [ "width", "height", "overflow" ],
8912 cProps = [ "fontSize" ],
8913 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
8914 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
8917 mode = $.effects.setMode( el, o.mode || "effect" ),
8918 restore = o.restore || mode !== "effect",
8919 scale = o.scale || "both",
8920 origin = o.origin || [ "middle", "center" ],
8921 position = el.css( "position" ),
8922 props = restore ? props0 : props1,
8930 if ( mode === "show" ) {
8934 height: el.height(),
8936 outerHeight: el.outerHeight(),
8937 outerWidth: el.outerWidth()
8940 if ( o.mode === "toggle" && mode === "show" ) {
8941 el.from = o.to || zero;
8942 el.to = o.from || original;
8944 el.from = o.from || ( mode === "show" ? zero : original );
8945 el.to = o.to || ( mode === "hide" ? zero : original );
8948 // Set scaling factor
8951 y: el.from.height / original.height,
8952 x: el.from.width / original.width
8955 y: el.to.height / original.height,
8956 x: el.to.width / original.width
8960 // Scale the css box
8961 if ( scale === "box" || scale === "both" ) {
8963 // Vertical props scaling
8964 if ( factor.from.y !== factor.to.y ) {
8965 props = props.concat( vProps );
8966 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
8967 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
8970 // Horizontal props scaling
8971 if ( factor.from.x !== factor.to.x ) {
8972 props = props.concat( hProps );
8973 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
8974 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
8978 // Scale the content
8979 if ( scale === "content" || scale === "both" ) {
8981 // Vertical props scaling
8982 if ( factor.from.y !== factor.to.y ) {
8983 props = props.concat( cProps ).concat( props2 );
8984 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
8985 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
8989 $.effects.save( el, props );
8991 $.effects.createWrapper( el );
8992 el.css( "overflow", "hidden" ).css( el.from );
8995 if (origin) { // Calculate baseline shifts
8996 baseline = $.effects.getBaseline( origin, original );
8997 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
8998 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
8999 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
9000 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
9002 el.css( el.from ); // set top & left
9005 if ( scale === "content" || scale === "both" ) { // Scale the children
9007 // Add margins/font-size
9008 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
9009 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
9010 props2 = props0.concat(vProps).concat(hProps);
9012 el.find( "*[width]" ).each( function(){
9013 var child = $( this ),
9015 height: child.height(),
9016 width: child.width(),
9017 outerHeight: child.outerHeight(),
9018 outerWidth: child.outerWidth()
9021 $.effects.save(child, props2);
9025 height: c_original.height * factor.from.y,
9026 width: c_original.width * factor.from.x,
9027 outerHeight: c_original.outerHeight * factor.from.y,
9028 outerWidth: c_original.outerWidth * factor.from.x
9031 height: c_original.height * factor.to.y,
9032 width: c_original.width * factor.to.x,
9033 outerHeight: c_original.height * factor.to.y,
9034 outerWidth: c_original.width * factor.to.x
9037 // Vertical props scaling
9038 if ( factor.from.y !== factor.to.y ) {
9039 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
9040 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
9043 // Horizontal props scaling
9044 if ( factor.from.x !== factor.to.x ) {
9045 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
9046 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
9050 child.css( child.from );
9051 child.animate( child.to, o.duration, o.easing, function() {
9055 $.effects.restore( child, props2 );
9062 el.animate( el.to, {
9064 duration: o.duration,
9066 complete: function() {
9067 if ( el.to.opacity === 0 ) {
9068 el.css( "opacity", el.from.opacity );
9070 if( mode === "hide" ) {
9073 $.effects.restore( el, props );
9076 // we need to calculate our new positioning based on the scaling
9077 if ( position === "static" ) {
9079 position: "relative",
9084 $.each([ "top", "left" ], function( idx, pos ) {
9085 el.css( pos, function( _, str ) {
9086 var val = parseInt( str, 10 ),
9087 toRef = idx ? el.to.left : el.to.top;
9089 // if original was "auto", recalculate the new value from wrapper
9090 if ( str === "auto" ) {
9091 return toRef + "px";
9094 return val + toRef + "px";
9100 $.effects.removeWrapper( el );
9108 (function( $, undefined ) {
9110 $.effects.effect.shake = function( o, done ) {
9113 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
9114 mode = $.effects.setMode( el, o.mode || "effect" ),
9115 direction = o.direction || "left",
9116 distance = o.distance || 20,
9117 times = o.times || 3,
9118 anims = times * 2 + 1,
9119 speed = Math.round(o.duration/anims),
9120 ref = (direction === "up" || direction === "down") ? "top" : "left",
9121 positiveMotion = (direction === "up" || direction === "left"),
9127 // we will need to re-assemble the queue to stack our animations in place
9129 queuelen = queue.length;
9131 $.effects.save( el, props );
9133 $.effects.createWrapper( el );
9136 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
9137 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
9138 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
9141 el.animate( animation, speed, o.easing );
9144 for ( i = 1; i < times; i++ ) {
9145 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
9148 .animate( animation1, speed, o.easing )
9149 .animate( animation, speed / 2, o.easing )
9151 if ( mode === "hide" ) {
9154 $.effects.restore( el, props );
9155 $.effects.removeWrapper( el );
9159 // inject all the animations we just queued to be first in line (after "inprogress")
9160 if ( queuelen > 1) {
9161 queue.splice.apply( queue,
9162 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
9169 (function( $, undefined ) {
9171 $.effects.effect.slide = function( o, done ) {
9175 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
9176 mode = $.effects.setMode( el, o.mode || "show" ),
9177 show = mode === "show",
9178 direction = o.direction || "left",
9179 ref = (direction === "up" || direction === "down") ? "top" : "left",
9180 positiveMotion = (direction === "up" || direction === "left"),
9185 $.effects.save( el, props );
9187 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
9189 $.effects.createWrapper( el ).css({
9194 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
9198 animation[ ref ] = ( show ?
9199 ( positiveMotion ? "+=" : "-=") :
9200 ( positiveMotion ? "-=" : "+=")) +
9204 el.animate( animation, {
9206 duration: o.duration,
9208 complete: function() {
9209 if ( mode === "hide" ) {
9212 $.effects.restore( el, props );
9213 $.effects.removeWrapper( el );
9220 (function( $, undefined ) {
9222 $.effects.effect.transfer = function( o, done ) {
9223 var elem = $( this ),
9225 targetFixed = target.css( "position" ) === "fixed",
9227 fixTop = targetFixed ? body.scrollTop() : 0,
9228 fixLeft = targetFixed ? body.scrollLeft() : 0,
9229 endPosition = target.offset(),
9231 top: endPosition.top - fixTop ,
9232 left: endPosition.left - fixLeft ,
9233 height: target.innerHeight(),
9234 width: target.innerWidth()
9236 startPosition = elem.offset(),
9237 transfer = $( '<div class="ui-effects-transfer"></div>' )
9238 .appendTo( document.body )
9239 .addClass( o.className )
9241 top: startPosition.top - fixTop ,
9242 left: startPosition.left - fixLeft ,
9243 height: elem.innerHeight(),
9244 width: elem.innerWidth(),
9245 position: targetFixed ? "fixed" : "absolute"
9247 .animate( animation, o.duration, o.easing, function() {
9254 (function( $, undefined ) {
9256 var mouseHandled = false;
9258 $.widget( "ui.menu", {
9260 defaultElement: "<ul>",
9264 submenu: "ui-icon-carat-1-e"
9279 _create: function() {
9280 this.activeMenu = this.element;
9283 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9284 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
9286 role: this.options.role,
9289 // need to catch all clicks on disabled menu
9290 // not possible through _on
9291 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
9292 if ( this.options.disabled ) {
9293 event.preventDefault();
9297 if ( this.options.disabled ) {
9299 .addClass( "ui-state-disabled" )
9300 .attr( "aria-disabled", "true" );
9304 // Prevent focus from sticking to links inside menu after clicking
9305 // them (focus should always stay on UL during navigation).
9306 "mousedown .ui-menu-item > a": function( event ) {
9307 event.preventDefault();
9309 "click .ui-state-disabled > a": function( event ) {
9310 event.preventDefault();
9312 "click .ui-menu-item:has(a)": function( event ) {
9313 var target = $( event.target ).closest( ".ui-menu-item" );
9314 if ( !mouseHandled && target.not( ".ui-state-disabled" ).length ) {
9315 mouseHandled = true;
9317 this.select( event );
9318 // Open submenu on click
9319 if ( target.has( ".ui-menu" ).length ) {
9320 this.expand( event );
9321 } else if ( !this.element.is( ":focus" ) ) {
9322 // Redirect focus to the menu
9323 this.element.trigger( "focus", [ true ] );
9325 // If the active item is on the top level, let it stay active.
9326 // Otherwise, blur the active item since it is no longer visible.
9327 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
9328 clearTimeout( this.timer );
9333 "mouseenter .ui-menu-item": function( event ) {
9334 var target = $( event.currentTarget );
9335 // Remove ui-state-active class from siblings of the newly focused menu item
9336 // to avoid a jump caused by adjacent elements both having a class with a border
9337 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
9338 this.focus( event, target );
9340 mouseleave: "collapseAll",
9341 "mouseleave .ui-menu": "collapseAll",
9342 focus: function( event, keepActiveItem ) {
9343 // If there's already an active item, keep it active
9344 // If not, activate the first item
9345 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
9347 if ( !keepActiveItem ) {
9348 this.focus( event, item );
9351 blur: function( event ) {
9352 this._delay(function() {
9353 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
9354 this.collapseAll( event );
9363 // Clicks outside of a menu collapse any open menus
9364 this._on( this.document, {
9365 click: function( event ) {
9366 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
9367 this.collapseAll( event );
9370 // Reset the mouseHandled flag
9371 mouseHandled = false;
9376 _destroy: function() {
9377 // Destroy (sub)menus
9379 .removeAttr( "aria-activedescendant" )
9380 .find( ".ui-menu" ).andSelf()
9381 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
9382 .removeAttr( "role" )
9383 .removeAttr( "tabIndex" )
9384 .removeAttr( "aria-labelledby" )
9385 .removeAttr( "aria-expanded" )
9386 .removeAttr( "aria-hidden" )
9387 .removeAttr( "aria-disabled" )
9391 // Destroy menu items
9392 this.element.find( ".ui-menu-item" )
9393 .removeClass( "ui-menu-item" )
9394 .removeAttr( "role" )
9395 .removeAttr( "aria-disabled" )
9398 .removeClass( "ui-corner-all ui-state-hover" )
9399 .removeAttr( "tabIndex" )
9400 .removeAttr( "role" )
9401 .removeAttr( "aria-haspopup" )
9402 .children().each( function() {
9403 var elem = $( this );
9404 if ( elem.data( "ui-menu-submenu-carat" ) ) {
9409 // Destroy menu dividers
9410 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
9413 _keydown: function( event ) {
9414 var match, prev, character, skip, regex,
9415 preventDefault = true;
9417 function escape( value ) {
9418 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
9421 switch ( event.keyCode ) {
9422 case $.ui.keyCode.PAGE_UP:
9423 this.previousPage( event );
9425 case $.ui.keyCode.PAGE_DOWN:
9426 this.nextPage( event );
9428 case $.ui.keyCode.HOME:
9429 this._move( "first", "first", event );
9431 case $.ui.keyCode.END:
9432 this._move( "last", "last", event );
9434 case $.ui.keyCode.UP:
9435 this.previous( event );
9437 case $.ui.keyCode.DOWN:
9440 case $.ui.keyCode.LEFT:
9441 this.collapse( event );
9443 case $.ui.keyCode.RIGHT:
9444 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
9445 this.expand( event );
9448 case $.ui.keyCode.ENTER:
9449 case $.ui.keyCode.SPACE:
9450 this._activate( event );
9452 case $.ui.keyCode.ESCAPE:
9453 this.collapse( event );
9456 preventDefault = false;
9457 prev = this.previousFilter || "";
9458 character = String.fromCharCode( event.keyCode );
9461 clearTimeout( this.filterTimer );
9463 if ( character === prev ) {
9466 character = prev + character;
9469 regex = new RegExp( "^" + escape( character ), "i" );
9470 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9471 return regex.test( $( this ).children( "a" ).text() );
9473 match = skip && match.index( this.active.next() ) !== -1 ?
9474 this.active.nextAll( ".ui-menu-item" ) :
9477 // If no matches on the current filter, reset to the last character pressed
9478 // to move down the menu to the first item that starts with that character
9479 if ( !match.length ) {
9480 character = String.fromCharCode( event.keyCode );
9481 regex = new RegExp( "^" + escape( character ), "i" );
9482 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
9483 return regex.test( $( this ).children( "a" ).text() );
9487 if ( match.length ) {
9488 this.focus( event, match );
9489 if ( match.length > 1 ) {
9490 this.previousFilter = character;
9491 this.filterTimer = this._delay(function() {
9492 delete this.previousFilter;
9495 delete this.previousFilter;
9498 delete this.previousFilter;
9502 if ( preventDefault ) {
9503 event.preventDefault();
9507 _activate: function( event ) {
9508 if ( !this.active.is( ".ui-state-disabled" ) ) {
9509 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
9510 this.expand( event );
9512 this.select( event );
9517 refresh: function() {
9519 icon = this.options.icons.submenu,
9520 submenus = this.element.find( this.options.menus );
9522 // Initialize nested menus
9523 submenus.filter( ":not(.ui-menu)" )
9524 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
9527 role: this.options.role,
9528 "aria-hidden": "true",
9529 "aria-expanded": "false"
9532 var menu = $( this ),
9533 item = menu.prev( "a" ),
9534 submenuCarat = $( "<span>" )
9535 .addClass( "ui-menu-icon ui-icon " + icon )
9536 .data( "ui-menu-submenu-carat", true );
9539 .attr( "aria-haspopup", "true" )
9540 .prepend( submenuCarat );
9541 menu.attr( "aria-labelledby", item.attr( "id" ) );
9544 menus = submenus.add( this.element );
9546 // Don't refresh list items that are already adapted
9547 menus.children( ":not(.ui-menu-item):has(a)" )
9548 .addClass( "ui-menu-item" )
9549 .attr( "role", "presentation" )
9552 .addClass( "ui-corner-all" )
9555 role: this._itemRole()
9558 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
9559 menus.children( ":not(.ui-menu-item)" ).each(function() {
9560 var item = $( this );
9561 // hyphen, em dash, en dash
9562 if ( !/[^\-—–\s]/.test( item.text() ) ) {
9563 item.addClass( "ui-widget-content ui-menu-divider" );
9567 // Add aria-disabled attribute to any disabled menu item
9568 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
9570 // If the active item has been removed, blur the menu
9571 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
9576 _itemRole: function() {
9580 }[ this.options.role ];
9583 focus: function( event, item ) {
9584 var nested, focused;
9585 this.blur( event, event && event.type === "focus" );
9587 this._scrollIntoView( item );
9589 this.active = item.first();
9590 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
9591 // Only update aria-activedescendant if there's a role
9592 // otherwise we assume focus is managed elsewhere
9593 if ( this.options.role ) {
9594 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
9597 // Highlight active parent menu item, if any
9600 .closest( ".ui-menu-item" )
9601 .children( "a:first" )
9602 .addClass( "ui-state-active" );
9604 if ( event && event.type === "keydown" ) {
9607 this.timer = this._delay(function() {
9612 nested = item.children( ".ui-menu" );
9613 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
9614 this._startOpening(nested);
9616 this.activeMenu = item.parent();
9618 this._trigger( "focus", event, { item: item } );
9621 _scrollIntoView: function( item ) {
9622 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
9623 if ( this._hasScroll() ) {
9624 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
9625 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
9626 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
9627 scroll = this.activeMenu.scrollTop();
9628 elementHeight = this.activeMenu.height();
9629 itemHeight = item.height();
9632 this.activeMenu.scrollTop( scroll + offset );
9633 } else if ( offset + itemHeight > elementHeight ) {
9634 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
9639 blur: function( event, fromFocus ) {
9641 clearTimeout( this.timer );
9644 if ( !this.active ) {
9648 this.active.children( "a" ).removeClass( "ui-state-focus" );
9651 this._trigger( "blur", event, { item: this.active } );
9654 _startOpening: function( submenu ) {
9655 clearTimeout( this.timer );
9657 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
9658 // shift in the submenu position when mousing over the carat icon
9659 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
9663 this.timer = this._delay(function() {
9665 this._open( submenu );
9669 _open: function( submenu ) {
9670 var position = $.extend({
9672 }, this.options.position );
9674 clearTimeout( this.timer );
9675 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
9677 .attr( "aria-hidden", "true" );
9681 .removeAttr( "aria-hidden" )
9682 .attr( "aria-expanded", "true" )
9683 .position( position );
9686 collapseAll: function( event, all ) {
9687 clearTimeout( this.timer );
9688 this.timer = this._delay(function() {
9689 // If we were passed an event, look for the submenu that contains the event
9690 var currentMenu = all ? this.element :
9691 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
9693 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
9694 if ( !currentMenu.length ) {
9695 currentMenu = this.element;
9698 this._close( currentMenu );
9701 this.activeMenu = currentMenu;
9705 // With no arguments, closes the currently active menu - if nothing is active
9706 // it closes all menus. If passed an argument, it will search for menus BELOW
9707 _close: function( startMenu ) {
9709 startMenu = this.active ? this.active.parent() : this.element;
9715 .attr( "aria-hidden", "true" )
9716 .attr( "aria-expanded", "false" )
9718 .find( "a.ui-state-active" )
9719 .removeClass( "ui-state-active" );
9722 collapse: function( event ) {
9723 var newItem = this.active &&
9724 this.active.parent().closest( ".ui-menu-item", this.element );
9725 if ( newItem && newItem.length ) {
9727 this.focus( event, newItem );
9731 expand: function( event ) {
9732 var newItem = this.active &&
9734 .children( ".ui-menu " )
9735 .children( ".ui-menu-item" )
9738 if ( newItem && newItem.length ) {
9739 this._open( newItem.parent() );
9741 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
9742 this._delay(function() {
9743 this.focus( event, newItem );
9748 next: function( event ) {
9749 this._move( "next", "first", event );
9752 previous: function( event ) {
9753 this._move( "prev", "last", event );
9756 isFirstItem: function() {
9757 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
9760 isLastItem: function() {
9761 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
9764 _move: function( direction, filter, event ) {
9766 if ( this.active ) {
9767 if ( direction === "first" || direction === "last" ) {
9769 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
9773 [ direction + "All" ]( ".ui-menu-item" )
9777 if ( !next || !next.length || !this.active ) {
9778 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
9781 this.focus( event, next );
9784 nextPage: function( event ) {
9785 var item, base, height;
9787 if ( !this.active ) {
9791 if ( this.isLastItem() ) {
9794 if ( this._hasScroll() ) {
9795 base = this.active.offset().top;
9796 height = this.element.height();
9797 this.active.nextAll( ".ui-menu-item" ).each(function() {
9799 return item.offset().top - base - height < 0;
9802 this.focus( event, item );
9804 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
9805 [ !this.active ? "first" : "last" ]() );
9809 previousPage: function( event ) {
9810 var item, base, height;
9811 if ( !this.active ) {
9815 if ( this.isFirstItem() ) {
9818 if ( this._hasScroll() ) {
9819 base = this.active.offset().top;
9820 height = this.element.height();
9821 this.active.prevAll( ".ui-menu-item" ).each(function() {
9823 return item.offset().top - base + height > 0;
9826 this.focus( event, item );
9828 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
9832 _hasScroll: function() {
9833 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
9836 select: function( event ) {
9837 // TODO: It should never be possible to not have an active item at this
9838 // point, but the tests don't trigger mouseenter before click.
9839 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
9840 var ui = { item: this.active };
9841 if ( !this.active.has( ".ui-menu" ).length ) {
9842 this.collapseAll( event, true );
9844 this._trigger( "select", event, ui );
9849 (function( $, undefined ) {
9851 $.widget( "ui.progressbar", {
9860 _create: function() {
9862 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
9864 role: "progressbar",
9865 "aria-valuemin": this.min,
9866 "aria-valuemax": this.options.max,
9867 "aria-valuenow": this._value()
9870 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
9871 .appendTo( this.element );
9873 this.oldValue = this._value();
9874 this._refreshValue();
9877 _destroy: function() {
9879 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
9880 .removeAttr( "role" )
9881 .removeAttr( "aria-valuemin" )
9882 .removeAttr( "aria-valuemax" )
9883 .removeAttr( "aria-valuenow" );
9885 this.valueDiv.remove();
9888 value: function( newValue ) {
9889 if ( newValue === undefined ) {
9890 return this._value();
9893 this._setOption( "value", newValue );
9897 _setOption: function( key, value ) {
9898 if ( key === "value" ) {
9899 this.options.value = value;
9900 this._refreshValue();
9901 if ( this._value() === this.options.max ) {
9902 this._trigger( "complete" );
9906 this._super( key, value );
9909 _value: function() {
9910 var val = this.options.value;
9911 // normalize invalid value
9912 if ( typeof val !== "number" ) {
9915 return Math.min( this.options.max, Math.max( this.min, val ) );
9918 _percentage: function() {
9919 return 100 * this._value() / this.options.max;
9922 _refreshValue: function() {
9923 var value = this.value(),
9924 percentage = this._percentage();
9926 if ( this.oldValue !== value ) {
9927 this.oldValue = value;
9928 this._trigger( "change" );
9932 .toggle( value > this.min )
9933 .toggleClass( "ui-corner-right", value === this.options.max )
9934 .width( percentage.toFixed(0) + "%" );
9935 this.element.attr( "aria-valuenow", value );
9940 (function( $, undefined ) {
9942 $.widget("ui.resizable", $.ui.mouse, {
9944 widgetEventPrefix: "resize",
9948 animateDuration: "slow",
9949 animateEasing: "swing",
9963 _create: function() {
9965 var that = this, o = this.options;
9966 this.element.addClass("ui-resizable");
9969 _aspectRatio: !!(o.aspectRatio),
9970 aspectRatio: o.aspectRatio,
9971 originalElement: this.element,
9972 _proportionallyResizeElements: [],
9973 _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
9976 //Wrap the element if it cannot hold child nodes
9977 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
9979 //Create a wrapper element and set the wrapper to the new current internal element
9981 $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
9982 position: this.element.css('position'),
9983 width: this.element.outerWidth(),
9984 height: this.element.outerHeight(),
9985 top: this.element.css('top'),
9986 left: this.element.css('left')
9990 //Overwrite the original this.element
9991 this.element = this.element.parent().data(
9992 "resizable", this.element.data('resizable')
9995 this.elementIsWrapper = true;
9997 //Move margins to the wrapper
9998 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
9999 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
10001 //Prevent Safari textarea resize
10002 this.originalResizeStyle = this.originalElement.css('resize');
10003 this.originalElement.css('resize', 'none');
10005 //Push the actual element to our proportionallyResize internal array
10006 this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
10008 // avoid IE jump (hard set the margin)
10009 this.originalElement.css({ margin: this.originalElement.css('margin') });
10011 // fix handlers offset
10012 this._proportionallyResize();
10016 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' });
10017 if(this.handles.constructor == String) {
10019 if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
10020 var n = this.handles.split(","); this.handles = {};
10022 for(var i = 0; i < n.length; i++) {
10024 var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
10025 var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
10027 // Apply zIndex to all handles - see #7960
10028 axis.css({ zIndex: o.zIndex });
10030 //TODO : What's going on here?
10031 if ('se' == handle) {
10032 axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
10035 //Insert into internal handles object and append to element
10036 this.handles[handle] = '.ui-resizable-'+handle;
10037 this.element.append(axis);
10042 this._renderAxis = function(target) {
10044 target = target || this.element;
10046 for(var i in this.handles) {
10048 if(this.handles[i].constructor == String)
10049 this.handles[i] = $(this.handles[i], this.element).show();
10051 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
10052 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
10054 var axis = $(this.handles[i], this.element), padWrapper = 0;
10056 //Checking the correct pad and border
10057 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
10059 //The padding type i have to apply...
10060 var padPos = [ 'padding',
10061 /ne|nw|n/.test(i) ? 'Top' :
10062 /se|sw|s/.test(i) ? 'Bottom' :
10063 /^e$/.test(i) ? 'Right' : 'Left' ].join("");
10065 target.css(padPos, padWrapper);
10067 this._proportionallyResize();
10071 //TODO: What's that good for? There's not anything to be executed left
10072 if(!$(this.handles[i]).length)
10078 //TODO: make renderAxis a prototype function
10079 this._renderAxis(this.element);
10081 this._handles = $('.ui-resizable-handle', this.element)
10082 .disableSelection();
10084 //Matching axis name
10085 this._handles.mouseover(function() {
10086 if (!that.resizing) {
10087 if (this.className)
10088 var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
10089 //Axis, default = se
10090 that.axis = axis && axis[1] ? axis[1] : 'se';
10094 //If we want to auto hide the elements
10096 this._handles.hide();
10098 .addClass("ui-resizable-autohide")
10099 .mouseenter(function() {
10100 if (o.disabled) return;
10101 $(this).removeClass("ui-resizable-autohide");
10102 that._handles.show();
10104 .mouseleave(function(){
10105 if (o.disabled) return;
10106 if (!that.resizing) {
10107 $(this).addClass("ui-resizable-autohide");
10108 that._handles.hide();
10113 //Initialize the mouse interaction
10118 _destroy: function() {
10120 this._mouseDestroy();
10122 var _destroy = function(exp) {
10123 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
10124 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
10127 //TODO: Unwrap at same DOM position
10128 if (this.elementIsWrapper) {
10129 _destroy(this.element);
10130 var wrapper = this.element;
10131 this.originalElement.css({
10132 position: wrapper.css('position'),
10133 width: wrapper.outerWidth(),
10134 height: wrapper.outerHeight(),
10135 top: wrapper.css('top'),
10136 left: wrapper.css('left')
10137 }).insertAfter( wrapper );
10141 this.originalElement.css('resize', this.originalResizeStyle);
10142 _destroy(this.originalElement);
10147 _mouseCapture: function(event) {
10148 var handle = false;
10149 for (var i in this.handles) {
10150 if ($(this.handles[i])[0] == event.target) {
10155 return !this.options.disabled && handle;
10158 _mouseStart: function(event) {
10160 var o = this.options, iniPos = this.element.position(), el = this.element;
10162 this.resizing = true;
10163 this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
10165 // bugfix for http://dev.jquery.com/ticket/1749
10166 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
10167 el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
10170 this._renderProxy();
10172 var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
10174 if (o.containment) {
10175 curleft += $(o.containment).scrollLeft() || 0;
10176 curtop += $(o.containment).scrollTop() || 0;
10179 //Store needed variables
10180 this.offset = this.helper.offset();
10181 this.position = { left: curleft, top: curtop };
10182 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
10183 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
10184 this.originalPosition = { left: curleft, top: curtop };
10185 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
10186 this.originalMousePosition = { left: event.pageX, top: event.pageY };
10189 this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
10191 var cursor = $('.ui-resizable-' + this.axis).css('cursor');
10192 $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
10194 el.addClass("ui-resizable-resizing");
10195 this._propagate("start", event);
10199 _mouseDrag: function(event) {
10201 //Increase performance, avoid regex
10202 var el = this.helper, o = this.options, props = {},
10203 that = this, smp = this.originalMousePosition, a = this.axis;
10205 var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
10206 var trigger = this._change[a];
10207 if (!trigger) return false;
10209 // Calculate the attrs that will be change
10210 var data = trigger.apply(this, [event, dx, dy]);
10212 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
10213 this._updateVirtualBoundaries(event.shiftKey);
10214 if (this._aspectRatio || event.shiftKey)
10215 data = this._updateRatio(data, event);
10217 data = this._respectSize(data, event);
10219 // plugins callbacks need to be called first
10220 this._propagate("resize", event);
10223 top: this.position.top + "px", left: this.position.left + "px",
10224 width: this.size.width + "px", height: this.size.height + "px"
10227 if (!this._helper && this._proportionallyResizeElements.length)
10228 this._proportionallyResize();
10230 this._updateCache(data);
10232 // calling the user callback at the end
10233 this._trigger('resize', event, this.ui());
10238 _mouseStop: function(event) {
10240 this.resizing = false;
10241 var o = this.options, that = this;
10244 var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
10245 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
10246 soffsetw = ista ? 0 : that.sizeDiff.width;
10248 var s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) },
10249 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
10250 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
10253 this.element.css($.extend(s, { top: top, left: left }));
10255 that.helper.height(that.size.height);
10256 that.helper.width(that.size.width);
10258 if (this._helper && !o.animate) this._proportionallyResize();
10261 $('body').css('cursor', 'auto');
10263 this.element.removeClass("ui-resizable-resizing");
10265 this._propagate("stop", event);
10267 if (this._helper) this.helper.remove();
10272 _updateVirtualBoundaries: function(forceAspectRatio) {
10273 var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
10276 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
10277 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
10278 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
10279 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
10282 if(this._aspectRatio || forceAspectRatio) {
10283 // We want to create an enclosing box whose aspect ration is the requested one
10284 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
10285 pMinWidth = b.minHeight * this.aspectRatio;
10286 pMinHeight = b.minWidth / this.aspectRatio;
10287 pMaxWidth = b.maxHeight * this.aspectRatio;
10288 pMaxHeight = b.maxWidth / this.aspectRatio;
10290 if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
10291 if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
10292 if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
10293 if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
10295 this._vBoundaries = b;
10298 _updateCache: function(data) {
10299 var o = this.options;
10300 this.offset = this.helper.offset();
10301 if (isNumber(data.left)) this.position.left = data.left;
10302 if (isNumber(data.top)) this.position.top = data.top;
10303 if (isNumber(data.height)) this.size.height = data.height;
10304 if (isNumber(data.width)) this.size.width = data.width;
10307 _updateRatio: function(data, event) {
10309 var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
10311 if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
10312 else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
10315 data.left = cpos.left + (csize.width - data.width);
10319 data.top = cpos.top + (csize.height - data.height);
10320 data.left = cpos.left + (csize.width - data.width);
10326 _respectSize: function(data, event) {
10328 var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
10329 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
10330 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
10332 if (isminw) data.width = o.minWidth;
10333 if (isminh) data.height = o.minHeight;
10334 if (ismaxw) data.width = o.maxWidth;
10335 if (ismaxh) data.height = o.maxHeight;
10337 var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
10338 var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
10340 if (isminw && cw) data.left = dw - o.minWidth;
10341 if (ismaxw && cw) data.left = dw - o.maxWidth;
10342 if (isminh && ch) data.top = dh - o.minHeight;
10343 if (ismaxh && ch) data.top = dh - o.maxHeight;
10345 // fixing jump error on top/left - bug #2330
10346 var isNotwh = !data.width && !data.height;
10347 if (isNotwh && !data.left && data.top) data.top = null;
10348 else if (isNotwh && !data.top && data.left) data.left = null;
10353 _proportionallyResize: function() {
10355 var o = this.options;
10356 if (!this._proportionallyResizeElements.length) return;
10357 var element = this.helper || this.element;
10359 for (var i=0; i < this._proportionallyResizeElements.length; i++) {
10361 var prel = this._proportionallyResizeElements[i];
10363 if (!this.borderDif) {
10364 var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
10365 p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
10367 this.borderDif = $.map(b, function(v, i) {
10368 var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
10369 return border + padding;
10374 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
10375 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
10382 _renderProxy: function() {
10384 var el = this.element, o = this.options;
10385 this.elementOffset = el.offset();
10389 this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
10391 // fix ie6 offset TODO: This seems broken
10392 var ie6offset = ($.ui.ie6 ? 1 : 0),
10393 pxyoffset = ( $.ui.ie6 ? 2 : -1 );
10395 this.helper.addClass(this._helper).css({
10396 width: this.element.outerWidth() + pxyoffset,
10397 height: this.element.outerHeight() + pxyoffset,
10398 position: 'absolute',
10399 left: this.elementOffset.left - ie6offset +'px',
10400 top: this.elementOffset.top - ie6offset +'px',
10401 zIndex: ++o.zIndex //TODO: Don't modify option
10406 .disableSelection();
10409 this.helper = this.element;
10415 e: function(event, dx, dy) {
10416 return { width: this.originalSize.width + dx };
10418 w: function(event, dx, dy) {
10419 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
10420 return { left: sp.left + dx, width: cs.width - dx };
10422 n: function(event, dx, dy) {
10423 var o = this.options, cs = this.originalSize, sp = this.originalPosition;
10424 return { top: sp.top + dy, height: cs.height - dy };
10426 s: function(event, dx, dy) {
10427 return { height: this.originalSize.height + dy };
10429 se: function(event, dx, dy) {
10430 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
10432 sw: function(event, dx, dy) {
10433 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
10435 ne: function(event, dx, dy) {
10436 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
10438 nw: function(event, dx, dy) {
10439 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
10443 _propagate: function(n, event) {
10444 $.ui.plugin.call(this, n, [event, this.ui()]);
10445 (n != "resize" && this._trigger(n, event, this.ui()));
10452 originalElement: this.originalElement,
10453 element: this.element,
10454 helper: this.helper,
10455 position: this.position,
10457 originalSize: this.originalSize,
10458 originalPosition: this.originalPosition
10465 * Resizable Extensions
10468 $.ui.plugin.add("resizable", "alsoResize", {
10470 start: function (event, ui) {
10471 var that = $(this).data("resizable"), o = that.options;
10473 var _store = function (exp) {
10474 $(exp).each(function() {
10476 el.data("resizable-alsoresize", {
10477 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
10478 left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10)
10483 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
10484 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
10485 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
10487 _store(o.alsoResize);
10491 resize: function (event, ui) {
10492 var that = $(this).data("resizable"), o = that.options, os = that.originalSize, op = that.originalPosition;
10495 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
10496 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
10499 _alsoResize = function (exp, c) {
10500 $(exp).each(function() {
10501 var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
10502 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
10504 $.each(css, function (i, prop) {
10505 var sum = (start[prop]||0) + (delta[prop]||0);
10506 if (sum && sum >= 0)
10507 style[prop] = sum || null;
10514 if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
10515 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
10517 _alsoResize(o.alsoResize);
10521 stop: function (event, ui) {
10522 $(this).removeData("resizable-alsoresize");
10526 $.ui.plugin.add("resizable", "animate", {
10528 stop: function(event, ui) {
10529 var that = $(this).data("resizable"), o = that.options;
10531 var pr = that._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
10532 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : that.sizeDiff.height,
10533 soffsetw = ista ? 0 : that.sizeDiff.width;
10535 var style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
10536 left = (parseInt(that.element.css('left'), 10) + (that.position.left - that.originalPosition.left)) || null,
10537 top = (parseInt(that.element.css('top'), 10) + (that.position.top - that.originalPosition.top)) || null;
10539 that.element.animate(
10540 $.extend(style, top && left ? { top: top, left: left } : {}), {
10541 duration: o.animateDuration,
10542 easing: o.animateEasing,
10546 width: parseInt(that.element.css('width'), 10),
10547 height: parseInt(that.element.css('height'), 10),
10548 top: parseInt(that.element.css('top'), 10),
10549 left: parseInt(that.element.css('left'), 10)
10552 if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
10554 // propagating resize, and updating values for each animation step
10555 that._updateCache(data);
10556 that._propagate("resize", event);
10565 $.ui.plugin.add("resizable", "containment", {
10567 start: function(event, ui) {
10568 var that = $(this).data("resizable"), o = that.options, el = that.element;
10569 var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
10572 that.containerElement = $(ce);
10574 if (/document/.test(oc) || oc == document) {
10575 that.containerOffset = { left: 0, top: 0 };
10576 that.containerPosition = { left: 0, top: 0 };
10578 that.parentData = {
10579 element: $(document), left: 0, top: 0,
10580 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
10584 // i'm a node, so compute top, left, right, bottom
10586 var element = $(ce), p = [];
10587 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
10589 that.containerOffset = element.offset();
10590 that.containerPosition = element.position();
10591 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
10593 var co = that.containerOffset, ch = that.containerSize.height, cw = that.containerSize.width,
10594 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
10596 that.parentData = {
10597 element: ce, left: co.left, top: co.top, width: width, height: height
10602 resize: function(event, ui) {
10603 var that = $(this).data("resizable"), o = that.options,
10604 ps = that.containerSize, co = that.containerOffset, cs = that.size, cp = that.position,
10605 pRatio = that._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = that.containerElement;
10607 if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
10609 if (cp.left < (that._helper ? co.left : 0)) {
10610 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
10611 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
10612 that.position.left = o.helper ? co.left : 0;
10615 if (cp.top < (that._helper ? co.top : 0)) {
10616 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
10617 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
10618 that.position.top = that._helper ? co.top : 0;
10621 that.offset.left = that.parentData.left+that.position.left;
10622 that.offset.top = that.parentData.top+that.position.top;
10624 var woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ),
10625 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
10627 var isParent = that.containerElement.get(0) == that.element.parent().get(0),
10628 isOffsetRelative = /relative|absolute/.test(that.containerElement.css('position'));
10630 if(isParent && isOffsetRelative) woset -= that.parentData.left;
10632 if (woset + that.size.width >= that.parentData.width) {
10633 that.size.width = that.parentData.width - woset;
10634 if (pRatio) that.size.height = that.size.width / that.aspectRatio;
10637 if (hoset + that.size.height >= that.parentData.height) {
10638 that.size.height = that.parentData.height - hoset;
10639 if (pRatio) that.size.width = that.size.height * that.aspectRatio;
10643 stop: function(event, ui){
10644 var that = $(this).data("resizable"), o = that.options, cp = that.position,
10645 co = that.containerOffset, cop = that.containerPosition, ce = that.containerElement;
10647 var helper = $(that.helper), ho = helper.offset(), w = helper.outerWidth() - that.sizeDiff.width, h = helper.outerHeight() - that.sizeDiff.height;
10649 if (that._helper && !o.animate && (/relative/).test(ce.css('position')))
10650 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
10652 if (that._helper && !o.animate && (/static/).test(ce.css('position')))
10653 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
10658 $.ui.plugin.add("resizable", "ghost", {
10660 start: function(event, ui) {
10662 var that = $(this).data("resizable"), o = that.options, cs = that.size;
10664 that.ghost = that.originalElement.clone();
10666 .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
10667 .addClass('ui-resizable-ghost')
10668 .addClass(typeof o.ghost == 'string' ? o.ghost : '');
10670 that.ghost.appendTo(that.helper);
10674 resize: function(event, ui){
10675 var that = $(this).data("resizable"), o = that.options;
10676 if (that.ghost) that.ghost.css({ position: 'relative', height: that.size.height, width: that.size.width });
10679 stop: function(event, ui){
10680 var that = $(this).data("resizable"), o = that.options;
10681 if (that.ghost && that.helper) that.helper.get(0).removeChild(that.ghost.get(0));
10686 $.ui.plugin.add("resizable", "grid", {
10688 resize: function(event, ui) {
10689 var that = $(this).data("resizable"), o = that.options, cs = that.size, os = that.originalSize, op = that.originalPosition, a = that.axis, ratio = o._aspectRatio || event.shiftKey;
10690 o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
10691 var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
10693 if (/^(se|s|e)$/.test(a)) {
10694 that.size.width = os.width + ox;
10695 that.size.height = os.height + oy;
10697 else if (/^(ne)$/.test(a)) {
10698 that.size.width = os.width + ox;
10699 that.size.height = os.height + oy;
10700 that.position.top = op.top - oy;
10702 else if (/^(sw)$/.test(a)) {
10703 that.size.width = os.width + ox;
10704 that.size.height = os.height + oy;
10705 that.position.left = op.left - ox;
10708 that.size.width = os.width + ox;
10709 that.size.height = os.height + oy;
10710 that.position.top = op.top - oy;
10711 that.position.left = op.left - ox;
10717 var num = function(v) {
10718 return parseInt(v, 10) || 0;
10721 var isNumber = function(value) {
10722 return !isNaN(parseInt(value, 10));
10726 (function( $, undefined ) {
10728 $.widget("ui.selectable", $.ui.mouse, {
10737 _create: function() {
10740 this.element.addClass("ui-selectable");
10742 this.dragged = false;
10744 // cache selectee children based on filter
10746 this.refresh = function() {
10747 selectees = $(that.options.filter, that.element[0]);
10748 selectees.addClass("ui-selectee");
10749 selectees.each(function() {
10750 var $this = $(this);
10751 var pos = $this.offset();
10752 $.data(this, "selectable-item", {
10757 right: pos.left + $this.outerWidth(),
10758 bottom: pos.top + $this.outerHeight(),
10759 startselected: false,
10760 selected: $this.hasClass('ui-selected'),
10761 selecting: $this.hasClass('ui-selecting'),
10762 unselecting: $this.hasClass('ui-unselecting')
10768 this.selectees = selectees.addClass("ui-selectee");
10772 this.helper = $("<div class='ui-selectable-helper'></div>");
10775 _destroy: function() {
10777 .removeClass("ui-selectee")
10778 .removeData("selectable-item");
10780 .removeClass("ui-selectable ui-selectable-disabled");
10781 this._mouseDestroy();
10784 _mouseStart: function(event) {
10787 this.opos = [event.pageX, event.pageY];
10789 if (this.options.disabled)
10792 var options = this.options;
10794 this.selectees = $(options.filter, this.element[0]);
10796 this._trigger("start", event);
10798 $(options.appendTo).append(this.helper);
10799 // position helper (lasso)
10801 "left": event.clientX,
10802 "top": event.clientY,
10807 if (options.autoRefresh) {
10811 this.selectees.filter('.ui-selected').each(function() {
10812 var selectee = $.data(this, "selectable-item");
10813 selectee.startselected = true;
10814 if (!event.metaKey && !event.ctrlKey) {
10815 selectee.$element.removeClass('ui-selected');
10816 selectee.selected = false;
10817 selectee.$element.addClass('ui-unselecting');
10818 selectee.unselecting = true;
10819 // selectable UNSELECTING callback
10820 that._trigger("unselecting", event, {
10821 unselecting: selectee.element
10826 $(event.target).parents().andSelf().each(function() {
10827 var selectee = $.data(this, "selectable-item");
10829 var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
10831 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
10832 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
10833 selectee.unselecting = !doSelect;
10834 selectee.selecting = doSelect;
10835 selectee.selected = doSelect;
10836 // selectable (UN)SELECTING callback
10838 that._trigger("selecting", event, {
10839 selecting: selectee.element
10842 that._trigger("unselecting", event, {
10843 unselecting: selectee.element
10852 _mouseDrag: function(event) {
10854 this.dragged = true;
10856 if (this.options.disabled)
10859 var options = this.options;
10861 var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
10862 if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
10863 if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
10864 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
10866 this.selectees.each(function() {
10867 var selectee = $.data(this, "selectable-item");
10868 //prevent helper from being selected if appendTo: selectable
10869 if (!selectee || selectee.element == that.element[0])
10872 if (options.tolerance == 'touch') {
10873 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
10874 } else if (options.tolerance == 'fit') {
10875 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
10880 if (selectee.selected) {
10881 selectee.$element.removeClass('ui-selected');
10882 selectee.selected = false;
10884 if (selectee.unselecting) {
10885 selectee.$element.removeClass('ui-unselecting');
10886 selectee.unselecting = false;
10888 if (!selectee.selecting) {
10889 selectee.$element.addClass('ui-selecting');
10890 selectee.selecting = true;
10891 // selectable SELECTING callback
10892 that._trigger("selecting", event, {
10893 selecting: selectee.element
10898 if (selectee.selecting) {
10899 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
10900 selectee.$element.removeClass('ui-selecting');
10901 selectee.selecting = false;
10902 selectee.$element.addClass('ui-selected');
10903 selectee.selected = true;
10905 selectee.$element.removeClass('ui-selecting');
10906 selectee.selecting = false;
10907 if (selectee.startselected) {
10908 selectee.$element.addClass('ui-unselecting');
10909 selectee.unselecting = true;
10911 // selectable UNSELECTING callback
10912 that._trigger("unselecting", event, {
10913 unselecting: selectee.element
10917 if (selectee.selected) {
10918 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
10919 selectee.$element.removeClass('ui-selected');
10920 selectee.selected = false;
10922 selectee.$element.addClass('ui-unselecting');
10923 selectee.unselecting = true;
10924 // selectable UNSELECTING callback
10925 that._trigger("unselecting", event, {
10926 unselecting: selectee.element
10936 _mouseStop: function(event) {
10939 this.dragged = false;
10941 var options = this.options;
10943 $('.ui-unselecting', this.element[0]).each(function() {
10944 var selectee = $.data(this, "selectable-item");
10945 selectee.$element.removeClass('ui-unselecting');
10946 selectee.unselecting = false;
10947 selectee.startselected = false;
10948 that._trigger("unselected", event, {
10949 unselected: selectee.element
10952 $('.ui-selecting', this.element[0]).each(function() {
10953 var selectee = $.data(this, "selectable-item");
10954 selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
10955 selectee.selecting = false;
10956 selectee.selected = true;
10957 selectee.startselected = true;
10958 that._trigger("selected", event, {
10959 selected: selectee.element
10962 this._trigger("stop", event);
10964 this.helper.remove();
10972 (function( $, undefined ) {
10974 // number of pages in a slider
10975 // (how many times can you page up/down to go through the whole range)
10978 $.widget( "ui.slider", $.ui.mouse, {
10980 widgetEventPrefix: "slide",
10987 orientation: "horizontal",
10994 _create: function() {
10995 var i, handleCount,
10997 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
10998 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
11001 this._keySliding = false;
11002 this._mouseSliding = false;
11003 this._animateOff = true;
11004 this._handleIndex = null;
11005 this._detectOrientation();
11009 .addClass( "ui-slider" +
11010 " ui-slider-" + this.orientation +
11012 " ui-widget-content" +
11014 ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
11016 this.range = $([]);
11019 if ( o.range === true ) {
11021 o.values = [ this._valueMin(), this._valueMin() ];
11023 if ( o.values.length && o.values.length !== 2 ) {
11024 o.values = [ o.values[0], o.values[0] ];
11028 this.range = $( "<div></div>" )
11029 .appendTo( this.element )
11030 .addClass( "ui-slider-range" +
11031 // note: this isn't the most fittingly semantic framework class for this element,
11032 // but worked best visually with a variety of themes
11033 " ui-widget-header" +
11034 ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
11037 handleCount = ( o.values && o.values.length ) || 1;
11039 for ( i = existingHandles.length; i < handleCount; i++ ) {
11040 handles.push( handle );
11043 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
11045 this.handle = this.handles.eq( 0 );
11047 this.handles.add( this.range ).filter( "a" )
11048 .click(function( event ) {
11049 event.preventDefault();
11051 .mouseenter(function() {
11052 if ( !o.disabled ) {
11053 $( this ).addClass( "ui-state-hover" );
11056 .mouseleave(function() {
11057 $( this ).removeClass( "ui-state-hover" );
11059 .focus(function() {
11060 if ( !o.disabled ) {
11061 $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
11062 $( this ).addClass( "ui-state-focus" );
11068 $( this ).removeClass( "ui-state-focus" );
11071 this.handles.each(function( i ) {
11072 $( this ).data( "ui-slider-handle-index", i );
11075 this._on( this.handles, {
11076 keydown: function( event ) {
11077 var allowed, curVal, newVal, step,
11078 index = $( event.target ).data( "ui-slider-handle-index" );
11080 switch ( event.keyCode ) {
11081 case $.ui.keyCode.HOME:
11082 case $.ui.keyCode.END:
11083 case $.ui.keyCode.PAGE_UP:
11084 case $.ui.keyCode.PAGE_DOWN:
11085 case $.ui.keyCode.UP:
11086 case $.ui.keyCode.RIGHT:
11087 case $.ui.keyCode.DOWN:
11088 case $.ui.keyCode.LEFT:
11089 event.preventDefault();
11090 if ( !this._keySliding ) {
11091 this._keySliding = true;
11092 $( event.target ).addClass( "ui-state-active" );
11093 allowed = this._start( event, index );
11094 if ( allowed === false ) {
11101 step = this.options.step;
11102 if ( this.options.values && this.options.values.length ) {
11103 curVal = newVal = this.values( index );
11105 curVal = newVal = this.value();
11108 switch ( event.keyCode ) {
11109 case $.ui.keyCode.HOME:
11110 newVal = this._valueMin();
11112 case $.ui.keyCode.END:
11113 newVal = this._valueMax();
11115 case $.ui.keyCode.PAGE_UP:
11116 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
11118 case $.ui.keyCode.PAGE_DOWN:
11119 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
11121 case $.ui.keyCode.UP:
11122 case $.ui.keyCode.RIGHT:
11123 if ( curVal === this._valueMax() ) {
11126 newVal = this._trimAlignValue( curVal + step );
11128 case $.ui.keyCode.DOWN:
11129 case $.ui.keyCode.LEFT:
11130 if ( curVal === this._valueMin() ) {
11133 newVal = this._trimAlignValue( curVal - step );
11137 this._slide( event, index, newVal );
11139 keyup: function( event ) {
11140 var index = $( event.target ).data( "ui-slider-handle-index" );
11142 if ( this._keySliding ) {
11143 this._keySliding = false;
11144 this._stop( event, index );
11145 this._change( event, index );
11146 $( event.target ).removeClass( "ui-state-active" );
11151 this._refreshValue();
11153 this._animateOff = false;
11156 _destroy: function() {
11157 this.handles.remove();
11158 this.range.remove();
11161 .removeClass( "ui-slider" +
11162 " ui-slider-horizontal" +
11163 " ui-slider-vertical" +
11164 " ui-slider-disabled" +
11166 " ui-widget-content" +
11167 " ui-corner-all" );
11169 this._mouseDestroy();
11172 _mouseCapture: function( event ) {
11173 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
11177 if ( o.disabled ) {
11181 this.elementSize = {
11182 width: this.element.outerWidth(),
11183 height: this.element.outerHeight()
11185 this.elementOffset = this.element.offset();
11187 position = { x: event.pageX, y: event.pageY };
11188 normValue = this._normValueFromMouse( position );
11189 distance = this._valueMax() - this._valueMin() + 1;
11190 this.handles.each(function( i ) {
11191 var thisDistance = Math.abs( normValue - that.values(i) );
11192 if ( distance > thisDistance ) {
11193 distance = thisDistance;
11194 closestHandle = $( this );
11199 // workaround for bug #3736 (if both handles of a range are at 0,
11200 // the first is always used as the one with least distance,
11201 // and moving it is obviously prevented by preventing negative ranges)
11202 if( o.range === true && this.values(1) === o.min ) {
11204 closestHandle = $( this.handles[index] );
11207 allowed = this._start( event, index );
11208 if ( allowed === false ) {
11211 this._mouseSliding = true;
11213 this._handleIndex = index;
11216 .addClass( "ui-state-active" )
11219 offset = closestHandle.offset();
11220 mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
11221 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
11222 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
11223 top: event.pageY - offset.top -
11224 ( closestHandle.height() / 2 ) -
11225 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
11226 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
11227 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
11230 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
11231 this._slide( event, index, normValue );
11233 this._animateOff = true;
11237 _mouseStart: function() {
11241 _mouseDrag: function( event ) {
11242 var position = { x: event.pageX, y: event.pageY },
11243 normValue = this._normValueFromMouse( position );
11245 this._slide( event, this._handleIndex, normValue );
11250 _mouseStop: function( event ) {
11251 this.handles.removeClass( "ui-state-active" );
11252 this._mouseSliding = false;
11254 this._stop( event, this._handleIndex );
11255 this._change( event, this._handleIndex );
11257 this._handleIndex = null;
11258 this._clickOffset = null;
11259 this._animateOff = false;
11264 _detectOrientation: function() {
11265 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
11268 _normValueFromMouse: function( position ) {
11275 if ( this.orientation === "horizontal" ) {
11276 pixelTotal = this.elementSize.width;
11277 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
11279 pixelTotal = this.elementSize.height;
11280 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
11283 percentMouse = ( pixelMouse / pixelTotal );
11284 if ( percentMouse > 1 ) {
11287 if ( percentMouse < 0 ) {
11290 if ( this.orientation === "vertical" ) {
11291 percentMouse = 1 - percentMouse;
11294 valueTotal = this._valueMax() - this._valueMin();
11295 valueMouse = this._valueMin() + percentMouse * valueTotal;
11297 return this._trimAlignValue( valueMouse );
11300 _start: function( event, index ) {
11302 handle: this.handles[ index ],
11303 value: this.value()
11305 if ( this.options.values && this.options.values.length ) {
11306 uiHash.value = this.values( index );
11307 uiHash.values = this.values();
11309 return this._trigger( "start", event, uiHash );
11312 _slide: function( event, index, newVal ) {
11317 if ( this.options.values && this.options.values.length ) {
11318 otherVal = this.values( index ? 0 : 1 );
11320 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
11321 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
11326 if ( newVal !== this.values( index ) ) {
11327 newValues = this.values();
11328 newValues[ index ] = newVal;
11329 // A slide can be canceled by returning false from the slide callback
11330 allowed = this._trigger( "slide", event, {
11331 handle: this.handles[ index ],
11335 otherVal = this.values( index ? 0 : 1 );
11336 if ( allowed !== false ) {
11337 this.values( index, newVal, true );
11341 if ( newVal !== this.value() ) {
11342 // A slide can be canceled by returning false from the slide callback
11343 allowed = this._trigger( "slide", event, {
11344 handle: this.handles[ index ],
11347 if ( allowed !== false ) {
11348 this.value( newVal );
11354 _stop: function( event, index ) {
11356 handle: this.handles[ index ],
11357 value: this.value()
11359 if ( this.options.values && this.options.values.length ) {
11360 uiHash.value = this.values( index );
11361 uiHash.values = this.values();
11364 this._trigger( "stop", event, uiHash );
11367 _change: function( event, index ) {
11368 if ( !this._keySliding && !this._mouseSliding ) {
11370 handle: this.handles[ index ],
11371 value: this.value()
11373 if ( this.options.values && this.options.values.length ) {
11374 uiHash.value = this.values( index );
11375 uiHash.values = this.values();
11378 this._trigger( "change", event, uiHash );
11382 value: function( newValue ) {
11383 if ( arguments.length ) {
11384 this.options.value = this._trimAlignValue( newValue );
11385 this._refreshValue();
11386 this._change( null, 0 );
11390 return this._value();
11393 values: function( index, newValue ) {
11398 if ( arguments.length > 1 ) {
11399 this.options.values[ index ] = this._trimAlignValue( newValue );
11400 this._refreshValue();
11401 this._change( null, index );
11405 if ( arguments.length ) {
11406 if ( $.isArray( arguments[ 0 ] ) ) {
11407 vals = this.options.values;
11408 newValues = arguments[ 0 ];
11409 for ( i = 0; i < vals.length; i += 1 ) {
11410 vals[ i ] = this._trimAlignValue( newValues[ i ] );
11411 this._change( null, i );
11413 this._refreshValue();
11415 if ( this.options.values && this.options.values.length ) {
11416 return this._values( index );
11418 return this.value();
11422 return this._values();
11426 _setOption: function( key, value ) {
11430 if ( $.isArray( this.options.values ) ) {
11431 valsLength = this.options.values.length;
11434 $.Widget.prototype._setOption.apply( this, arguments );
11439 this.handles.filter( ".ui-state-focus" ).blur();
11440 this.handles.removeClass( "ui-state-hover" );
11441 this.handles.prop( "disabled", true );
11442 this.element.addClass( "ui-disabled" );
11444 this.handles.prop( "disabled", false );
11445 this.element.removeClass( "ui-disabled" );
11448 case "orientation":
11449 this._detectOrientation();
11451 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
11452 .addClass( "ui-slider-" + this.orientation );
11453 this._refreshValue();
11456 this._animateOff = true;
11457 this._refreshValue();
11458 this._change( null, 0 );
11459 this._animateOff = false;
11462 this._animateOff = true;
11463 this._refreshValue();
11464 for ( i = 0; i < valsLength; i += 1 ) {
11465 this._change( null, i );
11467 this._animateOff = false;
11471 this._animateOff = true;
11472 this._refreshValue();
11473 this._animateOff = false;
11478 //internal value getter
11479 // _value() returns value trimmed by min and max, aligned by step
11480 _value: function() {
11481 var val = this.options.value;
11482 val = this._trimAlignValue( val );
11487 //internal values getter
11488 // _values() returns array of values trimmed by min and max, aligned by step
11489 // _values( index ) returns single value trimmed by min and max, aligned by step
11490 _values: function( index ) {
11495 if ( arguments.length ) {
11496 val = this.options.values[ index ];
11497 val = this._trimAlignValue( val );
11501 // .slice() creates a copy of the array
11502 // this copy gets trimmed by min and max and then returned
11503 vals = this.options.values.slice();
11504 for ( i = 0; i < vals.length; i+= 1) {
11505 vals[ i ] = this._trimAlignValue( vals[ i ] );
11512 // returns the step-aligned value that val is closest to, between (inclusive) min and max
11513 _trimAlignValue: function( val ) {
11514 if ( val <= this._valueMin() ) {
11515 return this._valueMin();
11517 if ( val >= this._valueMax() ) {
11518 return this._valueMax();
11520 var step = ( this.options.step > 0 ) ? this.options.step : 1,
11521 valModStep = (val - this._valueMin()) % step,
11522 alignValue = val - valModStep;
11524 if ( Math.abs(valModStep) * 2 >= step ) {
11525 alignValue += ( valModStep > 0 ) ? step : ( -step );
11528 // Since JavaScript has problems with large floats, round
11529 // the final value to 5 digits after the decimal point (see #4124)
11530 return parseFloat( alignValue.toFixed(5) );
11533 _valueMin: function() {
11534 return this.options.min;
11537 _valueMax: function() {
11538 return this.options.max;
11541 _refreshValue: function() {
11542 var lastValPercent, valPercent, value, valueMin, valueMax,
11543 oRange = this.options.range,
11546 animate = ( !this._animateOff ) ? o.animate : false,
11549 if ( this.options.values && this.options.values.length ) {
11550 this.handles.each(function( i ) {
11551 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
11552 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
11553 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
11554 if ( that.options.range === true ) {
11555 if ( that.orientation === "horizontal" ) {
11557 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
11560 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
11564 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
11567 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
11571 lastValPercent = valPercent;
11574 value = this.value();
11575 valueMin = this._valueMin();
11576 valueMax = this._valueMax();
11577 valPercent = ( valueMax !== valueMin ) ?
11578 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
11580 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
11581 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
11583 if ( oRange === "min" && this.orientation === "horizontal" ) {
11584 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
11586 if ( oRange === "max" && this.orientation === "horizontal" ) {
11587 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
11589 if ( oRange === "min" && this.orientation === "vertical" ) {
11590 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
11592 if ( oRange === "max" && this.orientation === "vertical" ) {
11593 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
11601 (function( $, undefined ) {
11603 $.widget("ui.sortable", $.ui.mouse, {
11605 widgetEventPrefix: "sort",
11608 appendTo: "parent",
11610 connectWith: false,
11611 containment: false,
11615 forcePlaceholderSize: false,
11616 forceHelperSize: false,
11619 helper: "original",
11622 placeholder: false,
11625 scrollSensitivity: 20,
11628 tolerance: "intersect",
11631 _create: function() {
11633 var o = this.options;
11634 this.containerCache = {};
11635 this.element.addClass("ui-sortable");
11640 //Let's determine if the items are being displayed horizontally
11641 this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
11643 //Let's determine the parent's offset
11644 this.offset = this.element.offset();
11646 //Initialize mouse events for interaction
11649 //We're ready to go
11654 _destroy: function() {
11656 .removeClass("ui-sortable ui-sortable-disabled");
11657 this._mouseDestroy();
11659 for ( var i = this.items.length - 1; i >= 0; i-- )
11660 this.items[i].item.removeData(this.widgetName + "-item");
11665 _setOption: function(key, value){
11666 if ( key === "disabled" ) {
11667 this.options[ key ] = value;
11669 this.widget().toggleClass( "ui-sortable-disabled", !!value );
11671 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
11672 $.Widget.prototype._setOption.apply(this, arguments);
11676 _mouseCapture: function(event, overrideHandle) {
11679 if (this.reverting) {
11683 if(this.options.disabled || this.options.type == 'static') return false;
11685 //We have to refresh the items data once first
11686 this._refreshItems(event);
11688 //Find out if the clicked node (or one of its parents) is a actual item in this.items
11689 var currentItem = null, nodes = $(event.target).parents().each(function() {
11690 if($.data(this, that.widgetName + '-item') == that) {
11691 currentItem = $(this);
11695 if($.data(event.target, that.widgetName + '-item') == that) currentItem = $(event.target);
11697 if(!currentItem) return false;
11698 if(this.options.handle && !overrideHandle) {
11699 var validHandle = false;
11701 $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
11702 if(!validHandle) return false;
11705 this.currentItem = currentItem;
11706 this._removeCurrentsFromItems();
11711 _mouseStart: function(event, overrideHandle, noActivation) {
11713 var o = this.options;
11714 this.currentContainer = this;
11716 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
11717 this.refreshPositions();
11719 //Create and append the visible helper
11720 this.helper = this._createHelper(event);
11722 //Cache the helper size
11723 this._cacheHelperProportions();
11726 * - Position generation -
11727 * This block generates everything position related - it's the core of draggables.
11730 //Cache the margins of the original element
11731 this._cacheMargins();
11733 //Get the next scrolling parent
11734 this.scrollParent = this.helper.scrollParent();
11736 //The element's absolute position on the page minus margins
11737 this.offset = this.currentItem.offset();
11739 top: this.offset.top - this.margins.top,
11740 left: this.offset.left - this.margins.left
11743 $.extend(this.offset, {
11744 click: { //Where the click happened, relative to the element
11745 left: event.pageX - this.offset.left,
11746 top: event.pageY - this.offset.top
11748 parent: this._getParentOffset(),
11749 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
11752 // Only after we got the offset, we can change the helper's position to absolute
11753 // TODO: Still need to figure out a way to make relative sorting possible
11754 this.helper.css("position", "absolute");
11755 this.cssPosition = this.helper.css("position");
11757 //Generate the original position
11758 this.originalPosition = this._generatePosition(event);
11759 this.originalPageX = event.pageX;
11760 this.originalPageY = event.pageY;
11762 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
11763 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
11765 //Cache the former DOM position
11766 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
11768 //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
11769 if(this.helper[0] != this.currentItem[0]) {
11770 this.currentItem.hide();
11773 //Create the placeholder
11774 this._createPlaceholder();
11776 //Set a containment if given in the options
11778 this._setContainment();
11780 if(o.cursor) { // cursor option
11781 if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
11782 $('body').css("cursor", o.cursor);
11785 if(o.opacity) { // opacity option
11786 if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
11787 this.helper.css("opacity", o.opacity);
11790 if(o.zIndex) { // zIndex option
11791 if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
11792 this.helper.css("zIndex", o.zIndex);
11795 //Prepare scrolling
11796 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
11797 this.overflowOffset = this.scrollParent.offset();
11800 this._trigger("start", event, this._uiHash());
11802 //Recache the helper size
11803 if(!this._preserveHelperProportions)
11804 this._cacheHelperProportions();
11807 //Post 'activate' events to possible containers
11808 if(!noActivation) {
11809 for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, this._uiHash(this)); }
11812 //Prepare possible droppables
11814 $.ui.ddmanager.current = this;
11816 if ($.ui.ddmanager && !o.dropBehaviour)
11817 $.ui.ddmanager.prepareOffsets(this, event);
11819 this.dragging = true;
11821 this.helper.addClass("ui-sortable-helper");
11822 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
11827 _mouseDrag: function(event) {
11829 //Compute the helpers position
11830 this.position = this._generatePosition(event);
11831 this.positionAbs = this._convertPositionTo("absolute");
11833 if (!this.lastPositionAbs) {
11834 this.lastPositionAbs = this.positionAbs;
11838 if(this.options.scroll) {
11839 var o = this.options, scrolled = false;
11840 if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
11842 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
11843 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
11844 else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
11845 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
11847 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
11848 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
11849 else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
11850 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
11854 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
11855 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
11856 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
11857 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
11859 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
11860 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
11861 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
11862 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
11866 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
11867 $.ui.ddmanager.prepareOffsets(this, event);
11870 //Regenerate the absolute position used for position checks
11871 this.positionAbs = this._convertPositionTo("absolute");
11873 //Set the helper position
11874 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
11875 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
11878 for (var i = this.items.length - 1; i >= 0; i--) {
11880 //Cache variables and intersection, continue if no intersection
11881 var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
11882 if (!intersection) continue;
11884 // Only put the placeholder inside the current Container, skip all
11885 // items form other containers. This works because when moving
11886 // an item from one container to another the
11887 // currentContainer is switched before the placeholder is moved.
11889 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
11890 // beetween the outer and inner container.
11891 if (item.instance !== this.currentContainer) continue;
11893 if (itemElement != this.currentItem[0] //cannot intersect with itself
11894 && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
11895 && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
11896 && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
11897 //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
11900 this.direction = intersection == 1 ? "down" : "up";
11902 if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
11903 this._rearrange(event, item);
11908 this._trigger("change", event, this._uiHash());
11913 //Post events to containers
11914 this._contactContainers(event);
11916 //Interconnect with droppables
11917 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
11920 this._trigger('sort', event, this._uiHash());
11922 this.lastPositionAbs = this.positionAbs;
11927 _mouseStop: function(event, noPropagation) {
11931 //If we are using droppables, inform the manager about the drop
11932 if ($.ui.ddmanager && !this.options.dropBehaviour)
11933 $.ui.ddmanager.drop(this, event);
11935 if(this.options.revert) {
11937 var cur = this.placeholder.offset();
11939 this.reverting = true;
11941 $(this.helper).animate({
11942 left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
11943 top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
11944 }, parseInt(this.options.revert, 10) || 500, function() {
11945 that._clear(event);
11948 this._clear(event, noPropagation);
11955 cancel: function() {
11957 if(this.dragging) {
11959 this._mouseUp({ target: null });
11961 if(this.options.helper == "original")
11962 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
11964 this.currentItem.show();
11966 //Post deactivating events to containers
11967 for (var i = this.containers.length - 1; i >= 0; i--){
11968 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
11969 if(this.containers[i].containerCache.over) {
11970 this.containers[i]._trigger("out", null, this._uiHash(this));
11971 this.containers[i].containerCache.over = 0;
11977 if (this.placeholder) {
11978 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
11979 if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
11980 if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
11989 if(this.domPosition.prev) {
11990 $(this.domPosition.prev).after(this.currentItem);
11992 $(this.domPosition.parent).prepend(this.currentItem);
12000 serialize: function(o) {
12002 var items = this._getItemsAsjQuery(o && o.connected);
12003 var str = []; o = o || {};
12005 $(items).each(function() {
12006 var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
12007 if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
12010 if(!str.length && o.key) {
12011 str.push(o.key + '=');
12014 return str.join('&');
12018 toArray: function(o) {
12020 var items = this._getItemsAsjQuery(o && o.connected);
12021 var ret = []; o = o || {};
12023 items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
12028 /* Be careful with the following core functions */
12029 _intersectsWith: function(item) {
12031 var x1 = this.positionAbs.left,
12032 x2 = x1 + this.helperProportions.width,
12033 y1 = this.positionAbs.top,
12034 y2 = y1 + this.helperProportions.height;
12037 r = l + item.width,
12039 b = t + item.height;
12041 var dyClick = this.offset.click.top,
12042 dxClick = this.offset.click.left;
12044 var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
12046 if( this.options.tolerance == "pointer"
12047 || this.options.forcePointerForContainers
12048 || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
12050 return isOverElement;
12053 return (l < x1 + (this.helperProportions.width / 2) // Right Half
12054 && x2 - (this.helperProportions.width / 2) < r // Left Half
12055 && t < y1 + (this.helperProportions.height / 2) // Bottom Half
12056 && y2 - (this.helperProportions.height / 2) < b ); // Top Half
12061 _intersectsWithPointer: function(item) {
12063 var isOverElementHeight = (this.options.axis === 'x') || $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
12064 isOverElementWidth = (this.options.axis === 'y') || $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
12065 isOverElement = isOverElementHeight && isOverElementWidth,
12066 verticalDirection = this._getDragVerticalDirection(),
12067 horizontalDirection = this._getDragHorizontalDirection();
12069 if (!isOverElement)
12072 return this.floating ?
12073 ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
12074 : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
12078 _intersectsWithSides: function(item) {
12080 var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
12081 isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
12082 verticalDirection = this._getDragVerticalDirection(),
12083 horizontalDirection = this._getDragHorizontalDirection();
12085 if (this.floating && horizontalDirection) {
12086 return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
12088 return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
12093 _getDragVerticalDirection: function() {
12094 var delta = this.positionAbs.top - this.lastPositionAbs.top;
12095 return delta != 0 && (delta > 0 ? "down" : "up");
12098 _getDragHorizontalDirection: function() {
12099 var delta = this.positionAbs.left - this.lastPositionAbs.left;
12100 return delta != 0 && (delta > 0 ? "right" : "left");
12103 refresh: function(event) {
12104 this._refreshItems(event);
12105 this.refreshPositions();
12109 _connectWith: function() {
12110 var options = this.options;
12111 return options.connectWith.constructor == String
12112 ? [options.connectWith]
12113 : options.connectWith;
12116 _getItemsAsjQuery: function(connected) {
12120 var connectWith = this._connectWith();
12122 if(connectWith && connected) {
12123 for (var i = connectWith.length - 1; i >= 0; i--){
12124 var cur = $(connectWith[i]);
12125 for (var j = cur.length - 1; j >= 0; j--){
12126 var inst = $.data(cur[j], this.widgetName);
12127 if(inst && inst != this && !inst.options.disabled) {
12128 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]);
12134 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]);
12136 for (var i = queries.length - 1; i >= 0; i--){
12137 queries[i][0].each(function() {
12146 _removeCurrentsFromItems: function() {
12148 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
12150 this.items = $.grep(this.items, function (item) {
12151 for (var j=0; j < list.length; j++) {
12152 if(list[j] == item.item[0])
12160 _refreshItems: function(event) {
12163 this.containers = [this];
12164 var items = this.items;
12165 var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
12166 var connectWith = this._connectWith();
12168 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
12169 for (var i = connectWith.length - 1; i >= 0; i--){
12170 var cur = $(connectWith[i]);
12171 for (var j = cur.length - 1; j >= 0; j--){
12172 var inst = $.data(cur[j], this.widgetName);
12173 if(inst && inst != this && !inst.options.disabled) {
12174 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
12175 this.containers.push(inst);
12181 for (var i = queries.length - 1; i >= 0; i--) {
12182 var targetData = queries[i][1];
12183 var _queries = queries[i][0];
12185 for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
12186 var item = $(_queries[j]);
12188 item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
12192 instance: targetData,
12193 width: 0, height: 0,
12201 refreshPositions: function(fast) {
12203 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
12204 if(this.offsetParent && this.helper) {
12205 this.offset.parent = this._getParentOffset();
12208 for (var i = this.items.length - 1; i >= 0; i--){
12209 var item = this.items[i];
12211 //We ignore calculating positions of all connected containers when we're not over them
12212 if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
12215 var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
12218 item.width = t.outerWidth();
12219 item.height = t.outerHeight();
12222 var p = t.offset();
12223 item.left = p.left;
12227 if(this.options.custom && this.options.custom.refreshContainers) {
12228 this.options.custom.refreshContainers.call(this);
12230 for (var i = this.containers.length - 1; i >= 0; i--){
12231 var p = this.containers[i].element.offset();
12232 this.containers[i].containerCache.left = p.left;
12233 this.containers[i].containerCache.top = p.top;
12234 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
12235 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
12242 _createPlaceholder: function(that) {
12243 that = that || this;
12244 var o = that.options;
12246 if(!o.placeholder || o.placeholder.constructor == String) {
12247 var className = o.placeholder;
12249 element: function() {
12251 var el = $(document.createElement(that.currentItem[0].nodeName))
12252 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
12253 .removeClass("ui-sortable-helper")[0];
12256 el.style.visibility = "hidden";
12260 update: function(container, p) {
12262 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
12263 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
12264 if(className && !o.forcePlaceholderSize) return;
12266 //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
12267 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css('paddingTop')||0, 10) - parseInt(that.currentItem.css('paddingBottom')||0, 10)); };
12268 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css('paddingLeft')||0, 10) - parseInt(that.currentItem.css('paddingRight')||0, 10)); };
12273 //Create the placeholder
12274 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
12276 //Append it after the actual current item
12277 that.currentItem.after(that.placeholder);
12279 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
12280 o.placeholder.update(that, that.placeholder);
12284 _contactContainers: function(event) {
12286 // get innermost container that intersects with item
12287 var innermostContainer = null, innermostIndex = null;
12290 for (var i = this.containers.length - 1; i >= 0; i--){
12292 // never consider a container that's located within the item itself
12293 if($.contains(this.currentItem[0], this.containers[i].element[0]))
12296 if(this._intersectsWith(this.containers[i].containerCache)) {
12298 // if we've already found a container and it's more "inner" than this, then continue
12299 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0]))
12302 innermostContainer = this.containers[i];
12303 innermostIndex = i;
12306 // container doesn't intersect. trigger "out" event if necessary
12307 if(this.containers[i].containerCache.over) {
12308 this.containers[i]._trigger("out", event, this._uiHash(this));
12309 this.containers[i].containerCache.over = 0;
12315 // if no intersecting containers found, return
12316 if(!innermostContainer) return;
12318 // move the item into the container if it's not there already
12319 if(this.containers.length === 1) {
12320 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
12321 this.containers[innermostIndex].containerCache.over = 1;
12324 //When entering a new container, we will find the item with the least distance and append our item near it
12325 var dist = 10000; var itemWithLeastDistance = null;
12326 var posProperty = this.containers[innermostIndex].floating ? 'left' : 'top';
12327 var sizeProperty = this.containers[innermostIndex].floating ? 'width' : 'height';
12328 var base = this.positionAbs[posProperty] + this.offset.click[posProperty];
12329 for (var j = this.items.length - 1; j >= 0; j--) {
12330 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
12331 if(this.items[j].item[0] == this.currentItem[0]) continue;
12332 var cur = this.items[j].item.offset()[posProperty];
12333 var nearBottom = false;
12334 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
12336 cur += this.items[j][sizeProperty];
12339 if(Math.abs(cur - base) < dist) {
12340 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
12341 this.direction = nearBottom ? "up": "down";
12345 if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
12348 this.currentContainer = this.containers[innermostIndex];
12349 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
12350 this._trigger("change", event, this._uiHash());
12351 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
12353 //Update the placeholder
12354 this.options.placeholder.update(this.currentContainer, this.placeholder);
12356 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
12357 this.containers[innermostIndex].containerCache.over = 1;
12363 _createHelper: function(event) {
12365 var o = this.options;
12366 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
12368 if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
12369 $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
12371 if(helper[0] == this.currentItem[0])
12372 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") };
12374 if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
12375 if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
12381 _adjustOffsetFromHelper: function(obj) {
12382 if (typeof obj == 'string') {
12383 obj = obj.split(' ');
12385 if ($.isArray(obj)) {
12386 obj = {left: +obj[0], top: +obj[1] || 0};
12388 if ('left' in obj) {
12389 this.offset.click.left = obj.left + this.margins.left;
12391 if ('right' in obj) {
12392 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
12394 if ('top' in obj) {
12395 this.offset.click.top = obj.top + this.margins.top;
12397 if ('bottom' in obj) {
12398 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
12402 _getParentOffset: function() {
12405 //Get the offsetParent and cache its position
12406 this.offsetParent = this.helper.offsetParent();
12407 var po = this.offsetParent.offset();
12409 // This is a special case where we need to modify a offset calculated on start, since the following happened:
12410 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
12411 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
12412 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
12413 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
12414 po.left += this.scrollParent.scrollLeft();
12415 po.top += this.scrollParent.scrollTop();
12418 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
12419 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
12420 po = { top: 0, left: 0 };
12423 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
12424 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
12429 _getRelativeOffset: function() {
12431 if(this.cssPosition == "relative") {
12432 var p = this.currentItem.position();
12434 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
12435 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
12438 return { top: 0, left: 0 };
12443 _cacheMargins: function() {
12445 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
12446 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
12450 _cacheHelperProportions: function() {
12451 this.helperProportions = {
12452 width: this.helper.outerWidth(),
12453 height: this.helper.outerHeight()
12457 _setContainment: function() {
12459 var o = this.options;
12460 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
12461 if(o.containment == 'document' || o.containment == 'window') this.containment = [
12462 0 - this.offset.relative.left - this.offset.parent.left,
12463 0 - this.offset.relative.top - this.offset.parent.top,
12464 $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
12465 ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
12468 if(!(/^(document|window|parent)$/).test(o.containment)) {
12469 var ce = $(o.containment)[0];
12470 var co = $(o.containment).offset();
12471 var over = ($(ce).css("overflow") != 'hidden');
12473 this.containment = [
12474 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
12475 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
12476 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,
12477 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
12483 _convertPositionTo: function(d, pos) {
12485 if(!pos) pos = this.position;
12486 var mod = d == "absolute" ? 1 : -1;
12487 var o = this.options, 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);
12491 pos.top // The absolute mouse position
12492 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
12493 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
12494 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
12497 pos.left // The absolute mouse position
12498 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
12499 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
12500 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
12506 _generatePosition: function(event) {
12508 var o = this.options, 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);
12510 // This is another very weird special case that only happens for relative elements:
12511 // 1. If the css position is relative
12512 // 2. and the scroll parent is the document or similar to the offset parent
12513 // we have to refresh the relative offset during the scroll so there are no jumps
12514 if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
12515 this.offset.relative = this._getRelativeOffset();
12518 var pageX = event.pageX;
12519 var pageY = event.pageY;
12522 * - Position constraining -
12523 * Constrain the position to a mix of grid, containment.
12526 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
12528 if(this.containment) {
12529 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
12530 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
12531 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
12532 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
12536 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
12537 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;
12539 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
12540 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;
12547 pageY // The absolute mouse position
12548 - this.offset.click.top // Click offset (relative to the element)
12549 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
12550 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
12551 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
12554 pageX // The absolute mouse position
12555 - this.offset.click.left // Click offset (relative to the element)
12556 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
12557 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
12558 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
12564 _rearrange: function(event, i, a, hardRefresh) {
12566 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));
12568 //Various things done here to improve the performance:
12569 // 1. we create a setTimeout, that calls refreshPositions
12570 // 2. on the instance, we have a counter variable, that get's higher after every append
12571 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
12572 // 4. this lets only the last addition to the timeout stack through
12573 this.counter = this.counter ? ++this.counter : 1;
12574 var counter = this.counter;
12576 this._delay(function() {
12577 if(counter == this.counter) this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
12582 _clear: function(event, noPropagation) {
12584 this.reverting = false;
12585 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
12586 // everything else normalized again
12587 var delayedTriggers = [];
12589 // We first have to update the dom position of the actual currentItem
12590 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
12591 if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
12592 this._noFinalSort = null;
12594 if(this.helper[0] == this.currentItem[0]) {
12595 for(var i in this._storedCSS) {
12596 if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
12598 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
12600 this.currentItem.show();
12603 if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
12604 if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
12606 // Check if the items Container has Changed and trigger appropriate
12608 if (this !== this.currentContainer) {
12609 if(!noPropagation) {
12610 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
12611 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
12612 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
12617 //Post events to containers
12618 for (var i = this.containers.length - 1; i >= 0; i--){
12619 if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
12620 if(this.containers[i].containerCache.over) {
12621 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
12622 this.containers[i].containerCache.over = 0;
12626 //Do what was originally in plugins
12627 if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
12628 if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
12629 if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
12631 this.dragging = false;
12632 if(this.cancelHelperRemoval) {
12633 if(!noPropagation) {
12634 this._trigger("beforeStop", event, this._uiHash());
12635 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
12636 this._trigger("stop", event, this._uiHash());
12639 this.fromOutside = false;
12643 if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
12645 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
12646 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
12648 if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
12650 if(!noPropagation) {
12651 for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
12652 this._trigger("stop", event, this._uiHash());
12655 this.fromOutside = false;
12660 _trigger: function() {
12661 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
12666 _uiHash: function(_inst) {
12667 var inst = _inst || this;
12669 helper: inst.helper,
12670 placeholder: inst.placeholder || $([]),
12671 position: inst.position,
12672 originalPosition: inst.originalPosition,
12673 offset: inst.positionAbs,
12674 item: inst.currentItem,
12675 sender: _inst ? _inst.element : null
12684 function modifier( fn ) {
12685 return function() {
12686 var previous = this.element.val();
12687 fn.apply( this, arguments );
12689 if ( previous !== this.element.val() ) {
12690 this._trigger( "change" );
12695 $.widget( "ui.spinner", {
12697 defaultElement: "<input>",
12698 widgetEventPrefix: "spin",
12702 down: "ui-icon-triangle-1-s",
12703 up: "ui-icon-triangle-1-n"
12708 numberFormat: null,
12718 _create: function() {
12719 // handle string values that need to be parsed
12720 this._setOption( "max", this.options.max );
12721 this._setOption( "min", this.options.min );
12722 this._setOption( "step", this.options.step );
12724 // format the value, but don't constrain
12725 this._value( this.element.val(), true );
12728 this._on( this._events );
12731 // turning off autocomplete prevents the browser from remembering the
12732 // value when navigating through history, so we re-enable autocomplete
12733 // if the page is unloaded before the widget is destroyed. #7790
12734 this._on( this.window, {
12735 beforeunload: function() {
12736 this.element.removeAttr( "autocomplete" );
12741 _getCreateOptions: function() {
12743 element = this.element;
12745 $.each( [ "min", "max", "step" ], function( i, option ) {
12746 var value = element.attr( option );
12747 if ( value !== undefined && value.length ) {
12748 options[ option ] = value;
12756 keydown: function( event ) {
12757 if ( this._start( event ) && this._keydown( event ) ) {
12758 event.preventDefault();
12762 focus: function() {
12763 this.previous = this.element.val();
12765 blur: function( event ) {
12766 if ( this.cancelBlur ) {
12767 delete this.cancelBlur;
12772 if ( this.previous !== this.element.val() ) {
12773 this._trigger( "change", event );
12776 mousewheel: function( event, delta ) {
12780 if ( !this.spinning && !this._start( event ) ) {
12784 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
12785 clearTimeout( this.mousewheelTimer );
12786 this.mousewheelTimer = this._delay(function() {
12787 if ( this.spinning ) {
12788 this._stop( event );
12791 event.preventDefault();
12793 "mousedown .ui-spinner-button": function( event ) {
12796 // We never want the buttons to have focus; whenever the user is
12797 // interacting with the spinner, the focus should be on the input.
12798 // If the input is focused then this.previous is properly set from
12799 // when the input first received focus. If the input is not focused
12800 // then we need to set this.previous based on the value before spinning.
12801 previous = this.element[0] === this.document[0].activeElement ?
12802 this.previous : this.element.val();
12803 function checkFocus() {
12804 var isActive = this.element[0] === this.document[0].activeElement;
12806 this.element.focus();
12807 this.previous = previous;
12809 // IE sets focus asynchronously, so we need to check if focus
12810 // moved off of the input because the user clicked on the button.
12811 this._delay(function() {
12812 this.previous = previous;
12817 // ensure focus is on (or stays on) the text field
12818 event.preventDefault();
12819 checkFocus.call( this );
12822 // IE doesn't prevent moving focus even with event.preventDefault()
12823 // so we set a flag to know when we should ignore the blur event
12824 // and check (again) if focus moved off of the input.
12825 this.cancelBlur = true;
12826 this._delay(function() {
12827 delete this.cancelBlur;
12828 checkFocus.call( this );
12831 if ( this._start( event ) === false ) {
12835 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
12837 "mouseup .ui-spinner-button": "_stop",
12838 "mouseenter .ui-spinner-button": function( event ) {
12839 // button will add ui-state-active if mouse was down while mouseleave and kept down
12840 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
12844 if ( this._start( event ) === false ) {
12847 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
12849 // TODO: do we really want to consider this a stop?
12850 // shouldn't we just stop the repeater and wait until mouseup before
12851 // we trigger the stop event?
12852 "mouseleave .ui-spinner-button": "_stop"
12855 _draw: function() {
12856 var uiSpinner = this.uiSpinner = this.element
12857 .addClass( "ui-spinner-input" )
12858 .attr( "autocomplete", "off" )
12859 .wrap( this._uiSpinnerHtml() )
12862 .append( this._buttonHtml() );
12864 this.element.attr( "role", "spinbutton" );
12867 this.buttons = uiSpinner.find( ".ui-spinner-button" )
12868 .attr( "tabIndex", -1 )
12870 .removeClass( "ui-corner-all" );
12872 // IE 6 doesn't understand height: 50% for the buttons
12873 // unless the wrapper has an explicit height
12874 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
12875 uiSpinner.height() > 0 ) {
12876 uiSpinner.height( uiSpinner.height() );
12879 // disable spinner if element was already disabled
12880 if ( this.options.disabled ) {
12885 _keydown: function( event ) {
12886 var options = this.options,
12887 keyCode = $.ui.keyCode;
12889 switch ( event.keyCode ) {
12891 this._repeat( null, 1, event );
12894 this._repeat( null, -1, event );
12896 case keyCode.PAGE_UP:
12897 this._repeat( null, options.page, event );
12899 case keyCode.PAGE_DOWN:
12900 this._repeat( null, -options.page, event );
12907 _uiSpinnerHtml: function() {
12908 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
12911 _buttonHtml: function() {
12913 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
12914 "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
12916 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
12917 "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
12921 _start: function( event ) {
12922 if ( !this.spinning && this._trigger( "start", event ) === false ) {
12926 if ( !this.counter ) {
12929 this.spinning = true;
12933 _repeat: function( i, steps, event ) {
12936 clearTimeout( this.timer );
12937 this.timer = this._delay(function() {
12938 this._repeat( 40, steps, event );
12941 this._spin( steps * this.options.step, event );
12944 _spin: function( step, event ) {
12945 var value = this.value() || 0;
12947 if ( !this.counter ) {
12951 value = this._adjustValue( value + step * this._increment( this.counter ) );
12953 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
12954 this._value( value );
12959 _increment: function( i ) {
12960 var incremental = this.options.incremental;
12962 if ( incremental ) {
12963 return $.isFunction( incremental ) ?
12965 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
12971 _precision: function() {
12972 var precision = this._precisionOf( this.options.step );
12973 if ( this.options.min !== null ) {
12974 precision = Math.max( precision, this._precisionOf( this.options.min ) );
12979 _precisionOf: function( num ) {
12980 var str = num.toString(),
12981 decimal = str.indexOf( "." );
12982 return decimal === -1 ? 0 : str.length - decimal - 1;
12985 _adjustValue: function( value ) {
12986 var base, aboveMin,
12987 options = this.options;
12989 // make sure we're at a valid step
12990 // - find out where we are relative to the base (min or 0)
12991 base = options.min !== null ? options.min : 0;
12992 aboveMin = value - base;
12993 // - round to the nearest step
12994 aboveMin = Math.round(aboveMin / options.step) * options.step;
12995 // - rounding is based on 0, so adjust back to our base
12996 value = base + aboveMin;
12998 // fix precision from bad JS floating point math
12999 value = parseFloat( value.toFixed( this._precision() ) );
13002 if ( options.max !== null && value > options.max) {
13003 return options.max;
13005 if ( options.min !== null && value < options.min ) {
13006 return options.min;
13012 _stop: function( event ) {
13013 if ( !this.spinning ) {
13017 clearTimeout( this.timer );
13018 clearTimeout( this.mousewheelTimer );
13020 this.spinning = false;
13021 this._trigger( "stop", event );
13024 _setOption: function( key, value ) {
13025 if ( key === "culture" || key === "numberFormat" ) {
13026 var prevValue = this._parse( this.element.val() );
13027 this.options[ key ] = value;
13028 this.element.val( this._format( prevValue ) );
13032 if ( key === "max" || key === "min" || key === "step" ) {
13033 if ( typeof value === "string" ) {
13034 value = this._parse( value );
13038 this._super( key, value );
13040 if ( key === "disabled" ) {
13042 this.element.prop( "disabled", true );
13043 this.buttons.button( "disable" );
13045 this.element.prop( "disabled", false );
13046 this.buttons.button( "enable" );
13051 _setOptions: modifier(function( options ) {
13052 this._super( options );
13053 this._value( this.element.val() );
13056 _parse: function( val ) {
13057 if ( typeof val === "string" && val !== "" ) {
13058 val = window.Globalize && this.options.numberFormat ?
13059 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
13061 return val === "" || isNaN( val ) ? null : val;
13064 _format: function( value ) {
13065 if ( value === "" ) {
13068 return window.Globalize && this.options.numberFormat ?
13069 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
13073 _refresh: function() {
13074 this.element.attr({
13075 "aria-valuemin": this.options.min,
13076 "aria-valuemax": this.options.max,
13077 // TODO: what should we do with values that can't be parsed?
13078 "aria-valuenow": this._parse( this.element.val() )
13082 // update the value without triggering change
13083 _value: function( value, allowAny ) {
13085 if ( value !== "" ) {
13086 parsed = this._parse( value );
13087 if ( parsed !== null ) {
13089 parsed = this._adjustValue( parsed );
13091 value = this._format( parsed );
13094 this.element.val( value );
13098 _destroy: function() {
13100 .removeClass( "ui-spinner-input" )
13101 .prop( "disabled", false )
13102 .removeAttr( "autocomplete" )
13103 .removeAttr( "role" )
13104 .removeAttr( "aria-valuemin" )
13105 .removeAttr( "aria-valuemax" )
13106 .removeAttr( "aria-valuenow" );
13107 this.uiSpinner.replaceWith( this.element );
13110 stepUp: modifier(function( steps ) {
13111 this._stepUp( steps );
13113 _stepUp: function( steps ) {
13114 this._spin( (steps || 1) * this.options.step );
13117 stepDown: modifier(function( steps ) {
13118 this._stepDown( steps );
13120 _stepDown: function( steps ) {
13121 this._spin( (steps || 1) * -this.options.step );
13124 pageUp: modifier(function( pages ) {
13125 this._stepUp( (pages || 1) * this.options.page );
13128 pageDown: modifier(function( pages ) {
13129 this._stepDown( (pages || 1) * this.options.page );
13132 value: function( newVal ) {
13133 if ( !arguments.length ) {
13134 return this._parse( this.element.val() );
13136 modifier( this._value ).call( this, newVal );
13139 widget: function() {
13140 return this.uiSpinner;
13145 (function( $, undefined ) {
13150 function getNextTabId() {
13154 function isLocal( anchor ) {
13155 return anchor.hash.length > 1 &&
13156 anchor.href.replace( rhash, "" ) ===
13157 location.href.replace( rhash, "" )
13158 // support: Safari 5.1
13159 // Safari 5.1 doesn't encode spaces in window.location
13160 // but it does encode spaces from anchors (#8777)
13161 .replace( /\s/g, "%20" );
13164 $.widget( "ui.tabs", {
13169 collapsible: false,
13171 heightStyle: "content",
13177 beforeActivate: null,
13182 _create: function() {
13184 options = this.options,
13185 active = options.active,
13186 locationHash = location.hash.substring( 1 );
13188 this.running = false;
13191 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
13192 .toggleClass( "ui-tabs-collapsible", options.collapsible )
13193 // Prevent users from focusing disabled tabs via click
13194 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
13195 if ( $( this ).is( ".ui-state-disabled" ) ) {
13196 event.preventDefault();
13200 // Preventing the default action in mousedown doesn't prevent IE
13201 // from focusing the element, so if the anchor gets focused, blur.
13202 // We don't have to worry about focusing the previously focused
13203 // element since clicking on a non-focusable element should focus
13204 // the body anyway.
13205 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
13206 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
13211 this._processTabs();
13213 if ( active === null ) {
13214 // check the fragment identifier in the URL
13215 if ( locationHash ) {
13216 this.tabs.each(function( i, tab ) {
13217 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
13224 // check for a tab marked active via a class
13225 if ( active === null ) {
13226 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
13229 // no active tab, set to false
13230 if ( active === null || active === -1 ) {
13231 active = this.tabs.length ? 0 : false;
13235 // handle numbers: negative, out of range
13236 if ( active !== false ) {
13237 active = this.tabs.index( this.tabs.eq( active ) );
13238 if ( active === -1 ) {
13239 active = options.collapsible ? false : 0;
13242 options.active = active;
13244 // don't allow collapsible: false and active: false
13245 if ( !options.collapsible && options.active === false && this.anchors.length ) {
13246 options.active = 0;
13249 // Take disabling tabs via class attribute from HTML
13250 // into account and update option properly.
13251 if ( $.isArray( options.disabled ) ) {
13252 options.disabled = $.unique( options.disabled.concat(
13253 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
13254 return that.tabs.index( li );
13259 // check for length avoids error when initializing empty list
13260 if ( this.options.active !== false && this.anchors.length ) {
13261 this.active = this._findActive( this.options.active );
13268 if ( this.active.length ) {
13269 this.load( options.active );
13273 _getCreateEventData: function() {
13276 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
13280 _tabKeydown: function( event ) {
13281 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
13282 selectedIndex = this.tabs.index( focusedTab ),
13283 goingForward = true;
13285 if ( this._handlePageNav( event ) ) {
13289 switch ( event.keyCode ) {
13290 case $.ui.keyCode.RIGHT:
13291 case $.ui.keyCode.DOWN:
13294 case $.ui.keyCode.UP:
13295 case $.ui.keyCode.LEFT:
13296 goingForward = false;
13299 case $.ui.keyCode.END:
13300 selectedIndex = this.anchors.length - 1;
13302 case $.ui.keyCode.HOME:
13305 case $.ui.keyCode.SPACE:
13306 // Activate only, no collapsing
13307 event.preventDefault();
13308 clearTimeout( this.activating );
13309 this._activate( selectedIndex );
13311 case $.ui.keyCode.ENTER:
13312 // Toggle (cancel delayed activation, allow collapsing)
13313 event.preventDefault();
13314 clearTimeout( this.activating );
13315 // Determine if we should collapse or activate
13316 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
13322 // Focus the appropriate tab, based on which key was pressed
13323 event.preventDefault();
13324 clearTimeout( this.activating );
13325 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
13327 // Navigating with control key will prevent automatic activation
13328 if ( !event.ctrlKey ) {
13329 // Update aria-selected immediately so that AT think the tab is already selected.
13330 // Otherwise AT may confuse the user by stating that they need to activate the tab,
13331 // but the tab will already be activated by the time the announcement finishes.
13332 focusedTab.attr( "aria-selected", "false" );
13333 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
13335 this.activating = this._delay(function() {
13336 this.option( "active", selectedIndex );
13341 _panelKeydown: function( event ) {
13342 if ( this._handlePageNav( event ) ) {
13346 // Ctrl+up moves focus to the current tab
13347 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
13348 event.preventDefault();
13349 this.active.focus();
13353 // Alt+page up/down moves focus to the previous/next tab (and activates)
13354 _handlePageNav: function( event ) {
13355 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
13356 this._activate( this._focusNextTab( this.options.active - 1, false ) );
13359 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
13360 this._activate( this._focusNextTab( this.options.active + 1, true ) );
13365 _findNextTab: function( index, goingForward ) {
13366 var lastTabIndex = this.tabs.length - 1;
13368 function constrain() {
13369 if ( index > lastTabIndex ) {
13373 index = lastTabIndex;
13378 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
13379 index = goingForward ? index + 1 : index - 1;
13385 _focusNextTab: function( index, goingForward ) {
13386 index = this._findNextTab( index, goingForward );
13387 this.tabs.eq( index ).focus();
13391 _setOption: function( key, value ) {
13392 if ( key === "active" ) {
13393 // _activate() will handle invalid values and update this.options
13394 this._activate( value );
13398 if ( key === "disabled" ) {
13399 // don't use the widget factory's disabled handling
13400 this._setupDisabled( value );
13404 this._super( key, value);
13406 if ( key === "collapsible" ) {
13407 this.element.toggleClass( "ui-tabs-collapsible", value );
13408 // Setting collapsible: false while collapsed; open first panel
13409 if ( !value && this.options.active === false ) {
13410 this._activate( 0 );
13414 if ( key === "event" ) {
13415 this._setupEvents( value );
13418 if ( key === "heightStyle" ) {
13419 this._setupHeightStyle( value );
13423 _tabId: function( tab ) {
13424 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
13427 _sanitizeSelector: function( hash ) {
13428 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
13431 refresh: function() {
13432 var options = this.options,
13433 lis = this.tablist.children( ":has(a[href])" );
13435 // get disabled tabs from class attribute from HTML
13436 // this will get converted to a boolean if needed in _refresh()
13437 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
13438 return lis.index( tab );
13441 this._processTabs();
13443 // was collapsed or no tabs
13444 if ( options.active === false || !this.anchors.length ) {
13445 options.active = false;
13447 // was active, but active tab is gone
13448 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
13449 // all remaining tabs are disabled
13450 if ( this.tabs.length === options.disabled.length ) {
13451 options.active = false;
13453 // activate previous tab
13455 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
13457 // was active, active tab still exists
13459 // make sure active index is correct
13460 options.active = this.tabs.index( this.active );
13466 _refresh: function() {
13467 this._setupDisabled( this.options.disabled );
13468 this._setupEvents( this.options.event );
13469 this._setupHeightStyle( this.options.heightStyle );
13471 this.tabs.not( this.active ).attr({
13472 "aria-selected": "false",
13475 this.panels.not( this._getPanelForTab( this.active ) )
13478 "aria-expanded": "false",
13479 "aria-hidden": "true"
13482 // Make sure one tab is in the tab order
13483 if ( !this.active.length ) {
13484 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
13487 .addClass( "ui-tabs-active ui-state-active" )
13489 "aria-selected": "true",
13492 this._getPanelForTab( this.active )
13495 "aria-expanded": "true",
13496 "aria-hidden": "false"
13501 _processTabs: function() {
13504 this.tablist = this._getList()
13505 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
13506 .attr( "role", "tablist" );
13508 this.tabs = this.tablist.find( "> li:has(a[href])" )
13509 .addClass( "ui-state-default ui-corner-top" )
13515 this.anchors = this.tabs.map(function() {
13516 return $( "a", this )[ 0 ];
13518 .addClass( "ui-tabs-anchor" )
13520 role: "presentation",
13526 this.anchors.each(function( i, anchor ) {
13527 var selector, panel, panelId,
13528 anchorId = $( anchor ).uniqueId().attr( "id" ),
13529 tab = $( anchor ).closest( "li" ),
13530 originalAriaControls = tab.attr( "aria-controls" );
13533 if ( isLocal( anchor ) ) {
13534 selector = anchor.hash;
13535 panel = that.element.find( that._sanitizeSelector( selector ) );
13538 panelId = that._tabId( tab );
13539 selector = "#" + panelId;
13540 panel = that.element.find( selector );
13541 if ( !panel.length ) {
13542 panel = that._createPanel( panelId );
13543 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
13545 panel.attr( "aria-live", "polite" );
13548 if ( panel.length) {
13549 that.panels = that.panels.add( panel );
13551 if ( originalAriaControls ) {
13552 tab.data( "ui-tabs-aria-controls", originalAriaControls );
13555 "aria-controls": selector.substring( 1 ),
13556 "aria-labelledby": anchorId
13558 panel.attr( "aria-labelledby", anchorId );
13562 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
13563 .attr( "role", "tabpanel" );
13566 // allow overriding how to find the list for rare usage scenarios (#7715)
13567 _getList: function() {
13568 return this.element.find( "ol,ul" ).eq( 0 );
13571 _createPanel: function( id ) {
13572 return $( "<div>" )
13574 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
13575 .data( "ui-tabs-destroy", true );
13578 _setupDisabled: function( disabled ) {
13579 if ( $.isArray( disabled ) ) {
13580 if ( !disabled.length ) {
13582 } else if ( disabled.length === this.anchors.length ) {
13588 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
13589 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
13591 .addClass( "ui-state-disabled" )
13592 .attr( "aria-disabled", "true" );
13595 .removeClass( "ui-state-disabled" )
13596 .removeAttr( "aria-disabled" );
13600 this.options.disabled = disabled;
13603 _setupEvents: function( event ) {
13605 click: function( event ) {
13606 event.preventDefault();
13610 $.each( event.split(" "), function( index, eventName ) {
13611 events[ eventName ] = "_eventHandler";
13615 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
13616 this._on( this.anchors, events );
13617 this._on( this.tabs, { keydown: "_tabKeydown" } );
13618 this._on( this.panels, { keydown: "_panelKeydown" } );
13620 this._focusable( this.tabs );
13621 this._hoverable( this.tabs );
13624 _setupHeightStyle: function( heightStyle ) {
13625 var maxHeight, overflow,
13626 parent = this.element.parent();
13628 if ( heightStyle === "fill" ) {
13629 // IE 6 treats height like minHeight, so we need to turn off overflow
13630 // in order to get a reliable height
13631 // we use the minHeight support test because we assume that only
13632 // browsers that don't support minHeight will treat height as minHeight
13633 if ( !$.support.minHeight ) {
13634 overflow = parent.css( "overflow" );
13635 parent.css( "overflow", "hidden");
13637 maxHeight = parent.height();
13638 this.element.siblings( ":visible" ).each(function() {
13639 var elem = $( this ),
13640 position = elem.css( "position" );
13642 if ( position === "absolute" || position === "fixed" ) {
13645 maxHeight -= elem.outerHeight( true );
13648 parent.css( "overflow", overflow );
13651 this.element.children().not( this.panels ).each(function() {
13652 maxHeight -= $( this ).outerHeight( true );
13655 this.panels.each(function() {
13656 $( this ).height( Math.max( 0, maxHeight -
13657 $( this ).innerHeight() + $( this ).height() ) );
13659 .css( "overflow", "auto" );
13660 } else if ( heightStyle === "auto" ) {
13662 this.panels.each(function() {
13663 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
13664 }).height( maxHeight );
13668 _eventHandler: function( event ) {
13669 var options = this.options,
13670 active = this.active,
13671 anchor = $( event.currentTarget ),
13672 tab = anchor.closest( "li" ),
13673 clickedIsActive = tab[ 0 ] === active[ 0 ],
13674 collapsing = clickedIsActive && options.collapsible,
13675 toShow = collapsing ? $() : this._getPanelForTab( tab ),
13676 toHide = !active.length ? $() : this._getPanelForTab( active ),
13680 newTab: collapsing ? $() : tab,
13684 event.preventDefault();
13686 if ( tab.hasClass( "ui-state-disabled" ) ||
13687 // tab is already loading
13688 tab.hasClass( "ui-tabs-loading" ) ||
13689 // can't switch durning an animation
13691 // click on active header, but not collapsible
13692 ( clickedIsActive && !options.collapsible ) ||
13693 // allow canceling activation
13694 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
13698 options.active = collapsing ? false : this.tabs.index( tab );
13700 this.active = clickedIsActive ? $() : tab;
13705 if ( !toHide.length && !toShow.length ) {
13706 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
13709 if ( toShow.length ) {
13710 this.load( this.tabs.index( tab ), event );
13712 this._toggle( event, eventData );
13715 // handles show/hide for selecting tabs
13716 _toggle: function( event, eventData ) {
13718 toShow = eventData.newPanel,
13719 toHide = eventData.oldPanel;
13721 this.running = true;
13723 function complete() {
13724 that.running = false;
13725 that._trigger( "activate", event, eventData );
13729 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
13731 if ( toShow.length && that.options.show ) {
13732 that._show( toShow, that.options.show, complete );
13739 // start out by hiding, then showing, then completing
13740 if ( toHide.length && this.options.hide ) {
13741 this._hide( toHide, this.options.hide, function() {
13742 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
13746 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
13752 "aria-expanded": "false",
13753 "aria-hidden": "true"
13755 eventData.oldTab.attr( "aria-selected", "false" );
13756 // If we're switching tabs, remove the old tab from the tab order.
13757 // If we're opening from collapsed state, remove the previous tab from the tab order.
13758 // If we're collapsing, then keep the collapsing tab in the tab order.
13759 if ( toShow.length && toHide.length ) {
13760 eventData.oldTab.attr( "tabIndex", -1 );
13761 } else if ( toShow.length ) {
13762 this.tabs.filter(function() {
13763 return $( this ).attr( "tabIndex" ) === 0;
13765 .attr( "tabIndex", -1 );
13769 "aria-expanded": "true",
13770 "aria-hidden": "false"
13772 eventData.newTab.attr({
13773 "aria-selected": "true",
13778 _activate: function( index ) {
13780 active = this._findActive( index );
13782 // trying to activate the already active panel
13783 if ( active[ 0 ] === this.active[ 0 ] ) {
13787 // trying to collapse, simulate a click on the current active header
13788 if ( !active.length ) {
13789 active = this.active;
13792 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
13793 this._eventHandler({
13795 currentTarget: anchor,
13796 preventDefault: $.noop
13800 _findActive: function( index ) {
13801 return index === false ? $() : this.tabs.eq( index );
13804 _getIndex: function( index ) {
13805 // meta-function to give users option to provide a href string instead of a numerical index.
13806 if ( typeof index === "string" ) {
13807 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
13813 _destroy: function() {
13818 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
13821 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
13822 .removeAttr( "role" );
13825 .removeClass( "ui-tabs-anchor" )
13826 .removeAttr( "role" )
13827 .removeAttr( "tabIndex" )
13828 .removeData( "href.tabs" )
13829 .removeData( "load.tabs" )
13832 this.tabs.add( this.panels ).each(function() {
13833 if ( $.data( this, "ui-tabs-destroy" ) ) {
13834 $( this ).remove();
13837 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
13838 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
13839 .removeAttr( "tabIndex" )
13840 .removeAttr( "aria-live" )
13841 .removeAttr( "aria-busy" )
13842 .removeAttr( "aria-selected" )
13843 .removeAttr( "aria-labelledby" )
13844 .removeAttr( "aria-hidden" )
13845 .removeAttr( "aria-expanded" )
13846 .removeAttr( "role" );
13850 this.tabs.each(function() {
13851 var li = $( this ),
13852 prev = li.data( "ui-tabs-aria-controls" );
13854 li.attr( "aria-controls", prev );
13856 li.removeAttr( "aria-controls" );
13860 this.panels.show();
13862 if ( this.options.heightStyle !== "content" ) {
13863 this.panels.css( "height", "" );
13867 enable: function( index ) {
13868 var disabled = this.options.disabled;
13869 if ( disabled === false ) {
13873 if ( index === undefined ) {
13876 index = this._getIndex( index );
13877 if ( $.isArray( disabled ) ) {
13878 disabled = $.map( disabled, function( num ) {
13879 return num !== index ? num : null;
13882 disabled = $.map( this.tabs, function( li, num ) {
13883 return num !== index ? num : null;
13887 this._setupDisabled( disabled );
13890 disable: function( index ) {
13891 var disabled = this.options.disabled;
13892 if ( disabled === true ) {
13896 if ( index === undefined ) {
13899 index = this._getIndex( index );
13900 if ( $.inArray( index, disabled ) !== -1 ) {
13903 if ( $.isArray( disabled ) ) {
13904 disabled = $.merge( [ index ], disabled ).sort();
13906 disabled = [ index ];
13909 this._setupDisabled( disabled );
13912 load: function( index, event ) {
13913 index = this._getIndex( index );
13915 tab = this.tabs.eq( index ),
13916 anchor = tab.find( ".ui-tabs-anchor" ),
13917 panel = this._getPanelForTab( tab ),
13924 if ( isLocal( anchor[ 0 ] ) ) {
13928 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
13930 // support: jQuery <1.8
13931 // jQuery <1.8 returns false if the request is canceled in beforeSend,
13932 // but as of 1.8, $.ajax() always returns a jqXHR object.
13933 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
13934 tab.addClass( "ui-tabs-loading" );
13935 panel.attr( "aria-busy", "true" );
13938 .success(function( response ) {
13939 // support: jQuery <1.8
13940 // http://bugs.jquery.com/ticket/11778
13941 setTimeout(function() {
13942 panel.html( response );
13943 that._trigger( "load", event, eventData );
13946 .complete(function( jqXHR, status ) {
13947 // support: jQuery <1.8
13948 // http://bugs.jquery.com/ticket/11778
13949 setTimeout(function() {
13950 if ( status === "abort" ) {
13951 that.panels.stop( false, true );
13954 tab.removeClass( "ui-tabs-loading" );
13955 panel.removeAttr( "aria-busy" );
13957 if ( jqXHR === that.xhr ) {
13965 // TODO: Remove this function in 1.10 when ajaxOptions is removed
13966 _ajaxSettings: function( anchor, event, eventData ) {
13969 url: anchor.attr( "href" ),
13970 beforeSend: function( jqXHR, settings ) {
13971 return that._trigger( "beforeLoad", event,
13972 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
13977 _getPanelForTab: function( tab ) {
13978 var id = $( tab ).attr( "aria-controls" );
13979 return this.element.find( this._sanitizeSelector( "#" + id ) );
13984 if ( $.uiBackCompat !== false ) {
13986 // helper method for a lot of the back compat extensions
13987 $.ui.tabs.prototype._ui = function( tab, panel ) {
13991 index: this.anchors.index( tab )
13996 $.widget( "ui.tabs", $.ui.tabs, {
13997 url: function( index, url ) {
13998 this.anchors.eq( index ).attr( "href", url );
14002 // TODO: Remove _ajaxSettings() method when removing this extension
14003 // ajaxOptions and cache options
14004 $.widget( "ui.tabs", $.ui.tabs, {
14010 _create: function() {
14015 this._on({ tabsbeforeload: function( event, ui ) {
14016 // tab is already cached
14017 if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) {
14018 event.preventDefault();
14022 ui.jqXHR.success(function() {
14023 if ( that.options.cache ) {
14024 $.data( ui.tab[ 0 ], "cache.tabs", true );
14030 _ajaxSettings: function( anchor, event, ui ) {
14031 var ajaxOptions = this.options.ajaxOptions;
14032 return $.extend( {}, ajaxOptions, {
14033 error: function( xhr, status ) {
14035 // Passing index avoid a race condition when this method is
14036 // called after the user has selected another tab.
14037 // Pass the anchor that initiated this request allows
14038 // loadError to manipulate the tab content panel via $(a.hash)
14040 xhr, status, ui.tab.closest( "li" ).index(), ui.tab[ 0 ] );
14044 }, this._superApply( arguments ) );
14047 _setOption: function( key, value ) {
14048 // reset cache if switching from cached to not cached
14049 if ( key === "cache" && value === false ) {
14050 this.anchors.removeData( "cache.tabs" );
14052 this._super( key, value );
14055 _destroy: function() {
14056 this.anchors.removeData( "cache.tabs" );
14060 url: function( index ){
14061 this.anchors.eq( index ).removeData( "cache.tabs" );
14062 this._superApply( arguments );
14067 $.widget( "ui.tabs", $.ui.tabs, {
14068 abort: function() {
14076 $.widget( "ui.tabs", $.ui.tabs, {
14078 spinner: "<em>Loading…</em>"
14080 _create: function() {
14083 tabsbeforeload: function( event, ui ) {
14084 // Don't react to nested tabs or tabs that don't use a spinner
14085 if ( event.target !== this.element[ 0 ] ||
14086 !this.options.spinner ) {
14090 var span = ui.tab.find( "span" ),
14091 html = span.html();
14092 span.html( this.options.spinner );
14093 ui.jqXHR.complete(function() {
14101 // enable/disable events
14102 $.widget( "ui.tabs", $.ui.tabs, {
14108 enable: function( index ) {
14109 var options = this.options,
14112 if ( index && options.disabled === true ||
14113 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) !== -1 ) ) {
14117 this._superApply( arguments );
14120 this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
14124 disable: function( index ) {
14125 var options = this.options,
14128 if ( index && options.disabled === false ||
14129 ( $.isArray( options.disabled ) && $.inArray( index, options.disabled ) === -1 ) ) {
14133 this._superApply( arguments );
14136 this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
14141 // add/remove methods and events
14142 $.widget( "ui.tabs", $.ui.tabs, {
14146 tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
14149 add: function( url, label, index ) {
14150 if ( index === undefined ) {
14151 index = this.anchors.length;
14154 var doInsertAfter, panel,
14155 options = this.options,
14156 li = $( options.tabTemplate
14157 .replace( /#\{href\}/g, url )
14158 .replace( /#\{label\}/g, label ) ),
14159 id = !url.indexOf( "#" ) ?
14160 url.replace( "#", "" ) :
14163 li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
14164 li.attr( "aria-controls", id );
14166 doInsertAfter = index >= this.tabs.length;
14168 // try to find an existing element before creating a new one
14169 panel = this.element.find( "#" + id );
14170 if ( !panel.length ) {
14171 panel = this._createPanel( id );
14172 if ( doInsertAfter ) {
14174 panel.insertAfter( this.panels.eq( -1 ) );
14176 panel.appendTo( this.element );
14179 panel.insertBefore( this.panels[ index ] );
14182 panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide();
14184 if ( doInsertAfter ) {
14185 li.appendTo( this.tablist );
14187 li.insertBefore( this.tabs[ index ] );
14190 options.disabled = $.map( options.disabled, function( n ) {
14191 return n >= index ? ++n : n;
14195 if ( this.tabs.length === 1 && options.active === false ) {
14196 this.option( "active", 0 );
14199 this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
14203 remove: function( index ) {
14204 index = this._getIndex( index );
14205 var options = this.options,
14206 tab = this.tabs.eq( index ).remove(),
14207 panel = this._getPanelForTab( tab ).remove();
14209 // If selected tab was removed focus tab to the right or
14210 // in case the last tab was removed the tab to the left.
14211 // We check for more than 2 tabs, because if there are only 2,
14212 // then when we remove this tab, there will only be one tab left
14213 // so we don't need to detect which tab to activate.
14214 if ( tab.hasClass( "ui-tabs-active" ) && this.anchors.length > 2 ) {
14215 this._activate( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
14218 options.disabled = $.map(
14219 $.grep( options.disabled, function( n ) {
14220 return n !== index;
14223 return n >= index ? --n : n;
14228 this._trigger( "remove", null, this._ui( tab.find( "a" )[ 0 ], panel[ 0 ] ) );
14234 $.widget( "ui.tabs", $.ui.tabs, {
14235 length: function() {
14236 return this.anchors.length;
14240 // panel ids (idPrefix option + title attribute)
14241 $.widget( "ui.tabs", $.ui.tabs, {
14243 idPrefix: "ui-tabs-"
14246 _tabId: function( tab ) {
14247 var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
14249 return $( a ).closest( "li" ).attr( "aria-controls" ) ||
14250 a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
14251 this.options.idPrefix + getNextTabId();
14255 // _createPanel method
14256 $.widget( "ui.tabs", $.ui.tabs, {
14258 panelTemplate: "<div></div>"
14261 _createPanel: function( id ) {
14262 return $( this.options.panelTemplate )
14264 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14265 .data( "ui-tabs-destroy", true );
14270 $.widget( "ui.tabs", $.ui.tabs, {
14271 _create: function() {
14272 var options = this.options;
14273 if ( options.active === null && options.selected !== undefined ) {
14274 options.active = options.selected === -1 ? false : options.selected;
14277 options.selected = options.active;
14278 if ( options.selected === false ) {
14279 options.selected = -1;
14283 _setOption: function( key, value ) {
14284 if ( key !== "selected" ) {
14285 return this._super( key, value );
14288 var options = this.options;
14289 this._super( "active", value === -1 ? false : value );
14290 options.selected = options.active;
14291 if ( options.selected === false ) {
14292 options.selected = -1;
14296 _eventHandler: function() {
14297 this._superApply( arguments );
14298 this.options.selected = this.options.active;
14299 if ( this.options.selected === false ) {
14300 this.options.selected = -1;
14305 // show and select event
14306 $.widget( "ui.tabs", $.ui.tabs, {
14311 _create: function() {
14313 if ( this.options.active !== false ) {
14314 this._trigger( "show", null, this._ui(
14315 this.active.find( ".ui-tabs-anchor" )[ 0 ],
14316 this._getPanelForTab( this.active )[ 0 ] ) );
14319 _trigger: function( type, event, data ) {
14321 ret = this._superApply( arguments );
14327 if ( type === "beforeActivate" ) {
14328 tab = data.newTab.length ? data.newTab : data.oldTab;
14329 panel = data.newPanel.length ? data.newPanel : data.oldPanel;
14330 ret = this._super( "select", event, {
14331 tab: tab.find( ".ui-tabs-anchor" )[ 0],
14333 index: tab.closest( "li" ).index()
14335 } else if ( type === "activate" && data.newTab.length ) {
14336 ret = this._super( "show", event, {
14337 tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
14338 panel: data.newPanel[ 0 ],
14339 index: data.newTab.closest( "li" ).index()
14347 $.widget( "ui.tabs", $.ui.tabs, {
14348 select: function( index ) {
14349 index = this._getIndex( index );
14350 if ( index === -1 ) {
14351 if ( this.options.collapsible && this.options.selected !== -1 ) {
14352 index = this.options.selected;
14357 this.anchors.eq( index ).trigger( this.options.event + this.eventNamespace );
14366 $.widget( "ui.tabs", $.ui.tabs, {
14368 cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
14370 _create: function() {
14371 var options = this.options,
14373 if ( options.active == null && options.cookie ) {
14374 active = parseInt( this._cookie(), 10 );
14375 if ( active === -1 ) {
14378 options.active = active;
14382 _cookie: function( active ) {
14383 var cookie = [ this.cookie ||
14384 ( this.cookie = this.options.cookie.name || "ui-tabs-" + (++listId) ) ];
14385 if ( arguments.length ) {
14386 cookie.push( active === false ? -1 : active );
14387 cookie.push( this.options.cookie );
14389 return $.cookie.apply( null, cookie );
14391 _refresh: function() {
14393 if ( this.options.cookie ) {
14394 this._cookie( this.options.active, this.options.cookie );
14397 _eventHandler: function() {
14398 this._superApply( arguments );
14399 if ( this.options.cookie ) {
14400 this._cookie( this.options.active, this.options.cookie );
14403 _destroy: function() {
14405 if ( this.options.cookie ) {
14406 this._cookie( null, this.options.cookie );
14414 $.widget( "ui.tabs", $.ui.tabs, {
14415 _trigger: function( type, event, data ) {
14416 var _data = $.extend( {}, data );
14417 if ( type === "load" ) {
14418 _data.panel = _data.panel[ 0 ];
14419 _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
14421 return this._super( type, event, _data );
14426 // The new animation options (show, hide) conflict with the old show callback.
14427 // The old fx option wins over show/hide anyway (always favor back-compat).
14428 // If a user wants to use the new animation API, they must give up the old API.
14429 $.widget( "ui.tabs", $.ui.tabs, {
14431 fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
14434 _getFx: function() {
14436 fx = this.options.fx;
14439 if ( $.isArray( fx ) ) {
14447 return fx ? { show: show, hide: hide } : null;
14450 _toggle: function( event, eventData ) {
14452 toShow = eventData.newPanel,
14453 toHide = eventData.oldPanel,
14454 fx = this._getFx();
14457 return this._super( event, eventData );
14460 that.running = true;
14462 function complete() {
14463 that.running = false;
14464 that._trigger( "activate", event, eventData );
14468 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
14470 if ( toShow.length && fx.show ) {
14472 .animate( fx.show, fx.show.duration, function() {
14481 // start out by hiding, then showing, then completing
14482 if ( toHide.length && fx.hide ) {
14483 toHide.animate( fx.hide, fx.hide.duration, function() {
14484 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14488 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14499 var increments = 0;
14501 function addDescribedBy( elem, id ) {
14502 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
14503 describedby.push( id );
14505 .data( "ui-tooltip-id", id )
14506 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
14509 function removeDescribedBy( elem ) {
14510 var id = elem.data( "ui-tooltip-id" ),
14511 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
14512 index = $.inArray( id, describedby );
14513 if ( index !== -1 ) {
14514 describedby.splice( index, 1 );
14517 elem.removeData( "ui-tooltip-id" );
14518 describedby = $.trim( describedby.join( " " ) );
14519 if ( describedby ) {
14520 elem.attr( "aria-describedby", describedby );
14522 elem.removeAttr( "aria-describedby" );
14526 $.widget( "ui.tooltip", {
14529 content: function() {
14530 return $( this ).attr( "title" );
14533 // Disabled elements have inconsistent behavior across browsers (#8661)
14534 items: "[title]:not([disabled])",
14538 collision: "flipfit flip"
14541 tooltipClass: null,
14549 _create: function() {
14555 // IDs of generated tooltips, needed for destroy
14556 this.tooltips = {};
14557 // IDs of parent tooltips where we removed the title attribute
14560 if ( this.options.disabled ) {
14565 _setOption: function( key, value ) {
14568 if ( key === "disabled" ) {
14569 this[ value ? "_disable" : "_enable" ]();
14570 this.options[ key ] = value;
14571 // disable element style changes
14575 this._super( key, value );
14577 if ( key === "content" ) {
14578 $.each( this.tooltips, function( id, element ) {
14579 that._updateContent( element );
14584 _disable: function() {
14587 // close open tooltips
14588 $.each( this.tooltips, function( id, element ) {
14589 var event = $.Event( "blur" );
14590 event.target = event.currentTarget = element[0];
14591 that.close( event, true );
14594 // remove title attributes to prevent native tooltips
14595 this.element.find( this.options.items ).andSelf().each(function() {
14596 var element = $( this );
14597 if ( element.is( "[title]" ) ) {
14599 .data( "ui-tooltip-title", element.attr( "title" ) )
14600 .attr( "title", "" );
14605 _enable: function() {
14606 // restore title attributes
14607 this.element.find( this.options.items ).andSelf().each(function() {
14608 var element = $( this );
14609 if ( element.data( "ui-tooltip-title" ) ) {
14610 element.attr( "title", element.data( "ui-tooltip-title" ) );
14615 open: function( event ) {
14617 target = $( event ? event.target : this.element )
14618 // we need closest here due to mouseover bubbling,
14619 // but always pointing at the same event target
14620 .closest( this.options.items );
14622 // No element to show a tooltip for or the tooltip is already open
14623 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
14627 if ( target.attr( "title" ) ) {
14628 target.data( "ui-tooltip-title", target.attr( "title" ) );
14631 target.data( "ui-tooltip-open", true );
14633 // kill parent tooltips, custom or native, for hover
14634 if ( event && event.type === "mouseover" ) {
14635 target.parents().each(function() {
14636 var parent = $( this ),
14638 if ( parent.data( "ui-tooltip-open" ) ) {
14639 blurEvent = $.Event( "blur" );
14640 blurEvent.target = blurEvent.currentTarget = this;
14641 that.close( blurEvent, true );
14643 if ( parent.attr( "title" ) ) {
14645 that.parents[ this.id ] = {
14647 title: parent.attr( "title" )
14649 parent.attr( "title", "" );
14654 this._updateContent( target, event );
14657 _updateContent: function( target, event ) {
14659 contentOption = this.options.content,
14661 eventType = event ? event.type : null;
14663 if ( typeof contentOption === "string" ) {
14664 return this._open( event, target, contentOption );
14667 content = contentOption.call( target[0], function( response ) {
14668 // ignore async response if tooltip was closed already
14669 if ( !target.data( "ui-tooltip-open" ) ) {
14672 // IE may instantly serve a cached response for ajax requests
14673 // delay this call to _open so the other call to _open runs first
14674 that._delay(function() {
14675 // jQuery creates a special event for focusin when it doesn't
14676 // exist natively. To improve performance, the native event
14677 // object is reused and the type is changed. Therefore, we can't
14678 // rely on the type being correct after the event finished
14679 // bubbling, so we set it back to the previous value. (#8740)
14681 event.type = eventType;
14683 this._open( event, target, response );
14687 this._open( event, target, content );
14691 _open: function( event, target, content ) {
14692 var tooltip, events, delayedShow,
14693 positionOption = $.extend( {}, this.options.position );
14699 // Content can be updated multiple times. If the tooltip already
14700 // exists, then just update the content and bail.
14701 tooltip = this._find( target );
14702 if ( tooltip.length ) {
14703 tooltip.find( ".ui-tooltip-content" ).html( content );
14707 // if we have a title, clear it to prevent the native tooltip
14708 // we have to check first to avoid defining a title if none exists
14709 // (we don't want to cause an element to start matching [title])
14711 // We use removeAttr only for key events, to allow IE to export the correct
14712 // accessible attributes. For mouse events, set to empty string to avoid
14713 // native tooltip showing up (happens only when removing inside mouseover).
14714 if ( target.is( "[title]" ) ) {
14715 if ( event && event.type === "mouseover" ) {
14716 target.attr( "title", "" );
14718 target.removeAttr( "title" );
14722 tooltip = this._tooltip( target );
14723 addDescribedBy( target, tooltip.attr( "id" ) );
14724 tooltip.find( ".ui-tooltip-content" ).html( content );
14726 function position( event ) {
14727 positionOption.of = event;
14728 if ( tooltip.is( ":hidden" ) ) {
14731 tooltip.position( positionOption );
14733 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
14734 this._on( this.document, {
14735 mousemove: position
14737 // trigger once to override element-relative positioning
14740 tooltip.position( $.extend({
14742 }, this.options.position ) );
14747 this._show( tooltip, this.options.show );
14748 // Handle tracking tooltips that are shown with a delay (#8644). As soon
14749 // as the tooltip is visible, position the tooltip using the most recent
14751 if ( this.options.show && this.options.show.delay ) {
14752 delayedShow = setInterval(function() {
14753 if ( tooltip.is( ":visible" ) ) {
14754 position( positionOption.of );
14755 clearInterval( delayedShow );
14757 }, $.fx.interval );
14760 this._trigger( "open", event, { tooltip: tooltip } );
14763 keyup: function( event ) {
14764 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
14765 var fakeEvent = $.Event(event);
14766 fakeEvent.currentTarget = target[0];
14767 this.close( fakeEvent, true );
14770 remove: function() {
14771 this._removeTooltip( tooltip );
14774 if ( !event || event.type === "mouseover" ) {
14775 events.mouseleave = "close";
14777 if ( !event || event.type === "focusin" ) {
14778 events.focusout = "close";
14780 this._on( true, target, events );
14783 close: function( event ) {
14785 target = $( event ? event.currentTarget : this.element ),
14786 tooltip = this._find( target );
14788 // disabling closes the tooltip, so we need to track when we're closing
14789 // to avoid an infinite loop in case the tooltip becomes disabled on close
14790 if ( this.closing ) {
14794 // only set title if we had one before (see comment in _open())
14795 if ( target.data( "ui-tooltip-title" ) ) {
14796 target.attr( "title", target.data( "ui-tooltip-title" ) );
14799 removeDescribedBy( target );
14801 tooltip.stop( true );
14802 this._hide( tooltip, this.options.hide, function() {
14803 that._removeTooltip( $( this ) );
14806 target.removeData( "ui-tooltip-open" );
14807 this._off( target, "mouseleave focusout keyup" );
14808 // Remove 'remove' binding only on delegated targets
14809 if ( target[0] !== this.element[0] ) {
14810 this._off( target, "remove" );
14812 this._off( this.document, "mousemove" );
14814 if ( event && event.type === "mouseleave" ) {
14815 $.each( this.parents, function( id, parent ) {
14816 $( parent.element ).attr( "title", parent.title );
14817 delete that.parents[ id ];
14821 this.closing = true;
14822 this._trigger( "close", event, { tooltip: tooltip } );
14823 this.closing = false;
14826 _tooltip: function( element ) {
14827 var id = "ui-tooltip-" + increments++,
14828 tooltip = $( "<div>" )
14833 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
14834 ( this.options.tooltipClass || "" ) );
14836 .addClass( "ui-tooltip-content" )
14837 .appendTo( tooltip );
14838 tooltip.appendTo( this.document[0].body );
14839 if ( $.fn.bgiframe ) {
14840 tooltip.bgiframe();
14842 this.tooltips[ id ] = element;
14846 _find: function( target ) {
14847 var id = target.data( "ui-tooltip-id" );
14848 return id ? $( "#" + id ) : $();
14851 _removeTooltip: function( tooltip ) {
14853 delete this.tooltips[ tooltip.attr( "id" ) ];
14856 _destroy: function() {
14859 // close open tooltips
14860 $.each( this.tooltips, function( id, element ) {
14861 // Delegate to close method to handle common cleanup
14862 var event = $.Event( "blur" );
14863 event.target = event.currentTarget = element[0];
14864 that.close( event, true );
14866 // Remove immediately; destroying an open tooltip doesn't use the
14868 $( "#" + id ).remove();
14870 // Restore the title
14871 if ( element.data( "ui-tooltip-title" ) ) {
14872 element.attr( "title", element.data( "ui-tooltip-title" ) );
14873 element.removeData( "ui-tooltip-title" );