Merge branch 'master' into topic/dt_feature_search
[sgn.git] / js / jqueryui.js
blob7b727e74350cbef7d079ea93d8da23a511eaa961
1 /*! jQuery UI - v1.10.3 - 2013-05-03
2 * http://jqueryui.com
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.position.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
4 * Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
5 (function( $, undefined ) {
7 var uuid = 0,
8 runiqueId = /^ui-id-\d+$/;
10 // $.ui might exist from components with no dependencies, e.g., $.ui.position
11 $.ui = $.ui || {};
13 $.extend( $.ui, {
14 version: "1.10.3",
16 keyCode: {
17 BACKSPACE: 8,
18 COMMA: 188,
19 DELETE: 46,
20 DOWN: 40,
21 END: 35,
22 ENTER: 13,
23 ESCAPE: 27,
24 HOME: 36,
25 LEFT: 37,
26 NUMPAD_ADD: 107,
27 NUMPAD_DECIMAL: 110,
28 NUMPAD_DIVIDE: 111,
29 NUMPAD_ENTER: 108,
30 NUMPAD_MULTIPLY: 106,
31 NUMPAD_SUBTRACT: 109,
32 PAGE_DOWN: 34,
33 PAGE_UP: 33,
34 PERIOD: 190,
35 RIGHT: 39,
36 SPACE: 32,
37 TAB: 9,
38 UP: 38
40 });
42 // plugins
43 $.fn.extend({
44 focus: (function( orig ) {
45 return function( delay, fn ) {
46 return typeof delay === "number" ?
47 this.each(function() {
48 var elem = this;
49 setTimeout(function() {
50 $( elem ).focus();
51 if ( fn ) {
52 fn.call( elem );
54 }, delay );
55 }) :
56 orig.apply( this, arguments );
58 })( $.fn.focus ),
60 scrollParent: function() {
61 var scrollParent;
62 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
63 scrollParent = this.parents().filter(function() {
64 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
65 }).eq(0);
66 } else {
67 scrollParent = this.parents().filter(function() {
68 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
69 }).eq(0);
72 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
75 zIndex: function( zIndex ) {
76 if ( zIndex !== undefined ) {
77 return this.css( "zIndex", zIndex );
80 if ( this.length ) {
81 var elem = $( this[ 0 ] ), position, value;
82 while ( elem.length && elem[ 0 ] !== document ) {
83 // Ignore z-index if position is set to a value where z-index is ignored by the browser
84 // This makes behavior of this function consistent across browsers
85 // WebKit always returns auto if the element is positioned
86 position = elem.css( "position" );
87 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
88 // IE returns 0 when zIndex is not specified
89 // other browsers return a string
90 // we ignore the case of nested elements with an explicit value of 0
91 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
92 value = parseInt( elem.css( "zIndex" ), 10 );
93 if ( !isNaN( value ) && value !== 0 ) {
94 return value;
97 elem = elem.parent();
101 return 0;
104 uniqueId: function() {
105 return this.each(function() {
106 if ( !this.id ) {
107 this.id = "ui-id-" + (++uuid);
112 removeUniqueId: function() {
113 return this.each(function() {
114 if ( runiqueId.test( this.id ) ) {
115 $( this ).removeAttr( "id" );
121 // selectors
122 function focusable( element, isTabIndexNotNaN ) {
123 var map, mapName, img,
124 nodeName = element.nodeName.toLowerCase();
125 if ( "area" === nodeName ) {
126 map = element.parentNode;
127 mapName = map.name;
128 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
129 return false;
131 img = $( "img[usemap=#" + mapName + "]" )[0];
132 return !!img && visible( img );
134 return ( /input|select|textarea|button|object/.test( nodeName ) ?
135 !element.disabled :
136 "a" === nodeName ?
137 element.href || isTabIndexNotNaN :
138 isTabIndexNotNaN) &&
139 // the element and all of its ancestors must be visible
140 visible( element );
143 function visible( element ) {
144 return $.expr.filters.visible( element ) &&
145 !$( element ).parents().addBack().filter(function() {
146 return $.css( this, "visibility" ) === "hidden";
147 }).length;
150 $.extend( $.expr[ ":" ], {
151 data: $.expr.createPseudo ?
152 $.expr.createPseudo(function( dataName ) {
153 return function( elem ) {
154 return !!$.data( elem, dataName );
156 }) :
157 // support: jQuery <1.8
158 function( elem, i, match ) {
159 return !!$.data( elem, match[ 3 ] );
162 focusable: function( element ) {
163 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
166 tabbable: function( element ) {
167 var tabIndex = $.attr( element, "tabindex" ),
168 isTabIndexNaN = isNaN( tabIndex );
169 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
173 // support: jQuery <1.8
174 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
175 $.each( [ "Width", "Height" ], function( i, name ) {
176 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
177 type = name.toLowerCase(),
178 orig = {
179 innerWidth: $.fn.innerWidth,
180 innerHeight: $.fn.innerHeight,
181 outerWidth: $.fn.outerWidth,
182 outerHeight: $.fn.outerHeight
185 function reduce( elem, size, border, margin ) {
186 $.each( side, function() {
187 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
188 if ( border ) {
189 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
191 if ( margin ) {
192 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
195 return size;
198 $.fn[ "inner" + name ] = function( size ) {
199 if ( size === undefined ) {
200 return orig[ "inner" + name ].call( this );
203 return this.each(function() {
204 $( this ).css( type, reduce( this, size ) + "px" );
208 $.fn[ "outer" + name] = function( size, margin ) {
209 if ( typeof size !== "number" ) {
210 return orig[ "outer" + name ].call( this, size );
213 return this.each(function() {
214 $( this).css( type, reduce( this, size, true, margin ) + "px" );
220 // support: jQuery <1.8
221 if ( !$.fn.addBack ) {
222 $.fn.addBack = function( selector ) {
223 return this.add( selector == null ?
224 this.prevObject : this.prevObject.filter( selector )
229 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
230 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
231 $.fn.removeData = (function( removeData ) {
232 return function( key ) {
233 if ( arguments.length ) {
234 return removeData.call( this, $.camelCase( key ) );
235 } else {
236 return removeData.call( this );
239 })( $.fn.removeData );
246 // deprecated
247 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
249 $.support.selectstart = "onselectstart" in document.createElement( "div" );
250 $.fn.extend({
251 disableSelection: function() {
252 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
253 ".ui-disableSelection", function( event ) {
254 event.preventDefault();
258 enableSelection: function() {
259 return this.unbind( ".ui-disableSelection" );
263 $.extend( $.ui, {
264 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
265 plugin: {
266 add: function( module, option, set ) {
267 var i,
268 proto = $.ui[ module ].prototype;
269 for ( i in set ) {
270 proto.plugins[ i ] = proto.plugins[ i ] || [];
271 proto.plugins[ i ].push( [ option, set[ i ] ] );
274 call: function( instance, name, args ) {
275 var i,
276 set = instance.plugins[ name ];
277 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
278 return;
281 for ( i = 0; i < set.length; i++ ) {
282 if ( instance.options[ set[ i ][ 0 ] ] ) {
283 set[ i ][ 1 ].apply( instance.element, args );
289 // only used by resizable
290 hasScroll: function( el, a ) {
292 //If overflow is hidden, the element might have extra content, but the user wants to hide it
293 if ( $( el ).css( "overflow" ) === "hidden") {
294 return false;
297 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
298 has = false;
300 if ( el[ scroll ] > 0 ) {
301 return true;
304 // TODO: determine which cases actually cause this to happen
305 // if the element doesn't have the scroll set, see if it's possible to
306 // set the scroll
307 el[ scroll ] = 1;
308 has = ( el[ scroll ] > 0 );
309 el[ scroll ] = 0;
310 return has;
314 })( jQuery );
316 (function( $, undefined ) {
318 var uuid = 0,
319 slice = Array.prototype.slice,
320 _cleanData = $.cleanData;
321 $.cleanData = function( elems ) {
322 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
323 try {
324 $( elem ).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
326 } catch( e ) {}
328 _cleanData( elems );
331 $.widget = function( name, base, prototype ) {
332 var fullName, existingConstructor, constructor, basePrototype,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype = {},
336 namespace = name.split( "." )[ 0 ];
338 name = name.split( "." )[ 1 ];
339 fullName = namespace + "-" + name;
341 if ( !prototype ) {
342 prototype = base;
343 base = $.Widget;
346 // create selector for plugin
347 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
348 return !!$.data( elem, fullName );
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor = $[ namespace ][ name ];
353 constructor = $[ namespace ][ name ] = function( options, element ) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget ) {
356 return new constructor( options, element );
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments.length ) {
362 this._createWidget( options, element );
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor, {
367 version: prototype.version,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype = new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
379 // inheriting from
380 basePrototype.options = $.widget.extend( {}, basePrototype.options );
381 $.each( prototype, function( prop, value ) {
382 if ( !$.isFunction( value ) ) {
383 proxiedPrototype[ prop ] = value;
384 return;
386 proxiedPrototype[ prop ] = (function() {
387 var _super = function() {
388 return base.prototype[ prop ].apply( this, arguments );
390 _superApply = function( args ) {
391 return base.prototype[ prop ].apply( this, args );
393 return function() {
394 var __super = this._super,
395 __superApply = this._superApply,
396 returnValue;
398 this._super = _super;
399 this._superApply = _superApply;
401 returnValue = value.apply( this, arguments );
403 this._super = __super;
404 this._superApply = __superApply;
406 return returnValue;
408 })();
410 constructor.prototype = $.widget.extend( basePrototype, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
415 }, proxiedPrototype, {
416 constructor: constructor,
417 namespace: namespace,
418 widgetName: name,
419 widgetFullName: fullName
422 // If this widget is being redefined then we need to find all widgets that
423 // are inheriting from it and redefine all of them so that they inherit from
424 // the new version of this widget. We're essentially trying to replace one
425 // level in the prototype chain.
426 if ( existingConstructor ) {
427 $.each( existingConstructor._childConstructors, function( i, child ) {
428 var childPrototype = child.prototype;
430 // redefine the child widget using the same prototype that was
431 // originally used, but inherit from the new version of the base
432 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
434 // remove the list of existing child constructors from the old constructor
435 // so the old child constructors can be garbage collected
436 delete existingConstructor._childConstructors;
437 } else {
438 base._childConstructors.push( constructor );
441 $.widget.bridge( name, constructor );
444 $.widget.extend = function( target ) {
445 var input = slice.call( arguments, 1 ),
446 inputIndex = 0,
447 inputLength = input.length,
448 key,
449 value;
450 for ( ; inputIndex < inputLength; inputIndex++ ) {
451 for ( key in input[ inputIndex ] ) {
452 value = input[ inputIndex ][ key ];
453 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
454 // Clone objects
455 if ( $.isPlainObject( value ) ) {
456 target[ key ] = $.isPlainObject( target[ key ] ) ?
457 $.widget.extend( {}, target[ key ], value ) :
458 // Don't extend strings, arrays, etc. with objects
459 $.widget.extend( {}, value );
460 // Copy everything else by reference
461 } else {
462 target[ key ] = value;
467 return target;
470 $.widget.bridge = function( name, object ) {
471 var fullName = object.prototype.widgetFullName || name;
472 $.fn[ name ] = function( options ) {
473 var isMethodCall = typeof options === "string",
474 args = slice.call( arguments, 1 ),
475 returnValue = this;
477 // allow multiple hashes to be passed on init
478 options = !isMethodCall && args.length ?
479 $.widget.extend.apply( null, [ options ].concat(args) ) :
480 options;
482 if ( isMethodCall ) {
483 this.each(function() {
484 var methodValue,
485 instance = $.data( this, fullName );
486 if ( !instance ) {
487 return $.error( "cannot call methods on " + name + " prior to initialization; " +
488 "attempted to call method '" + options + "'" );
490 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
491 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
493 methodValue = instance[ options ].apply( instance, args );
494 if ( methodValue !== instance && methodValue !== undefined ) {
495 returnValue = methodValue && methodValue.jquery ?
496 returnValue.pushStack( methodValue.get() ) :
497 methodValue;
498 return false;
501 } else {
502 this.each(function() {
503 var instance = $.data( this, fullName );
504 if ( instance ) {
505 instance.option( options || {} )._init();
506 } else {
507 $.data( this, fullName, new object( options, this ) );
512 return returnValue;
516 $.Widget = function( /* options, element */ ) {};
517 $.Widget._childConstructors = [];
519 $.Widget.prototype = {
520 widgetName: "widget",
521 widgetEventPrefix: "",
522 defaultElement: "<div>",
523 options: {
524 disabled: false,
526 // callbacks
527 create: null
529 _createWidget: function( options, element ) {
530 element = $( element || this.defaultElement || this )[ 0 ];
531 this.element = $( element );
532 this.uuid = uuid++;
533 this.eventNamespace = "." + this.widgetName + this.uuid;
534 this.options = $.widget.extend( {},
535 this.options,
536 this._getCreateOptions(),
537 options );
539 this.bindings = $();
540 this.hoverable = $();
541 this.focusable = $();
543 if ( element !== this ) {
544 $.data( element, this.widgetFullName, this );
545 this._on( true, this.element, {
546 remove: function( event ) {
547 if ( event.target === element ) {
548 this.destroy();
552 this.document = $( element.style ?
553 // element within the document
554 element.ownerDocument :
555 // element is window or document
556 element.document || element );
557 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
560 this._create();
561 this._trigger( "create", null, this._getCreateEventData() );
562 this._init();
564 _getCreateOptions: $.noop,
565 _getCreateEventData: $.noop,
566 _create: $.noop,
567 _init: $.noop,
569 destroy: function() {
570 this._destroy();
571 // we can probably remove the unbind calls in 2.0
572 // all event bindings should go through this._on()
573 this.element
574 .unbind( this.eventNamespace )
575 // 1.9 BC for #7810
576 // TODO remove dual storage
577 .removeData( this.widgetName )
578 .removeData( this.widgetFullName )
579 // support: jquery <1.6.3
580 // http://bugs.jquery.com/ticket/9413
581 .removeData( $.camelCase( this.widgetFullName ) );
582 this.widget()
583 .unbind( this.eventNamespace )
584 .removeAttr( "aria-disabled" )
585 .removeClass(
586 this.widgetFullName + "-disabled " +
587 "ui-state-disabled" );
589 // clean up events and states
590 this.bindings.unbind( this.eventNamespace );
591 this.hoverable.removeClass( "ui-state-hover" );
592 this.focusable.removeClass( "ui-state-focus" );
594 _destroy: $.noop,
596 widget: function() {
597 return this.element;
600 option: function( key, value ) {
601 var options = key,
602 parts,
603 curOption,
606 if ( arguments.length === 0 ) {
607 // don't return a reference to the internal hash
608 return $.widget.extend( {}, this.options );
611 if ( typeof key === "string" ) {
612 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
613 options = {};
614 parts = key.split( "." );
615 key = parts.shift();
616 if ( parts.length ) {
617 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
618 for ( i = 0; i < parts.length - 1; i++ ) {
619 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
620 curOption = curOption[ parts[ i ] ];
622 key = parts.pop();
623 if ( value === undefined ) {
624 return curOption[ key ] === undefined ? null : curOption[ key ];
626 curOption[ key ] = value;
627 } else {
628 if ( value === undefined ) {
629 return this.options[ key ] === undefined ? null : this.options[ key ];
631 options[ key ] = value;
635 this._setOptions( options );
637 return this;
639 _setOptions: function( options ) {
640 var key;
642 for ( key in options ) {
643 this._setOption( key, options[ key ] );
646 return this;
648 _setOption: function( key, value ) {
649 this.options[ key ] = value;
651 if ( key === "disabled" ) {
652 this.widget()
653 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
654 .attr( "aria-disabled", value );
655 this.hoverable.removeClass( "ui-state-hover" );
656 this.focusable.removeClass( "ui-state-focus" );
659 return this;
662 enable: function() {
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck, element, handlers ) {
670 var delegateElement,
671 instance = this;
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck !== "boolean" ) {
675 handlers = element;
676 element = suppressDisabledCheck;
677 suppressDisabledCheck = false;
680 // no element argument, shuffle and use this.element
681 if ( !handlers ) {
682 handlers = element;
683 element = this.element;
684 delegateElement = this.widget();
685 } else {
686 // accept selectors, DOM elements
687 element = delegateElement = $( element );
688 this.bindings = this.bindings.add( element );
691 $.each( handlers, function( event, handler ) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck &&
697 ( instance.options.disabled === true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
699 return;
701 return ( typeof handler === "string" ? instance[ handler ] : handler )
702 .apply( instance, arguments );
705 // copy the guid so direct unbinding works
706 if ( typeof handler !== "string" ) {
707 handlerProxy.guid = handler.guid =
708 handler.guid || handlerProxy.guid || $.guid++;
711 var match = event.match( /^(\w+)\s*(.*)$/ ),
712 eventName = match[1] + instance.eventNamespace,
713 selector = match[2];
714 if ( selector ) {
715 delegateElement.delegate( selector, eventName, handlerProxy );
716 } else {
717 element.bind( eventName, handlerProxy );
722 _off: function( element, eventName ) {
723 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
724 element.unbind( eventName ).undelegate( eventName );
727 _delay: function( handler, delay ) {
728 function handlerProxy() {
729 return ( typeof handler === "string" ? instance[ handler ] : handler )
730 .apply( instance, arguments );
732 var instance = this;
733 return setTimeout( handlerProxy, delay || 0 );
736 _hoverable: function( element ) {
737 this.hoverable = this.hoverable.add( element );
738 this._on( element, {
739 mouseenter: function( event ) {
740 $( event.currentTarget ).addClass( "ui-state-hover" );
742 mouseleave: function( event ) {
743 $( event.currentTarget ).removeClass( "ui-state-hover" );
748 _focusable: function( element ) {
749 this.focusable = this.focusable.add( element );
750 this._on( element, {
751 focusin: function( event ) {
752 $( event.currentTarget ).addClass( "ui-state-focus" );
754 focusout: function( event ) {
755 $( event.currentTarget ).removeClass( "ui-state-focus" );
760 _trigger: function( type, event, data ) {
761 var prop, orig,
762 callback = this.options[ type ];
764 data = data || {};
765 event = $.Event( event );
766 event.type = ( type === this.widgetEventPrefix ?
767 type :
768 this.widgetEventPrefix + type ).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event.target = this.element[ 0 ];
773 // copy original event properties over to the new event
774 orig = event.originalEvent;
775 if ( orig ) {
776 for ( prop in orig ) {
777 if ( !( prop in event ) ) {
778 event[ prop ] = orig[ prop ];
783 this.element.trigger( event, data );
784 return !( $.isFunction( callback ) &&
785 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
786 event.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
791 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
792 if ( typeof options === "string" ) {
793 options = { effect: options };
795 var hasOptions,
796 effectName = !options ?
797 method :
798 options === true || typeof options === "number" ?
799 defaultEffect :
800 options.effect || defaultEffect;
801 options = options || {};
802 if ( typeof options === "number" ) {
803 options = { duration: options };
805 hasOptions = !$.isEmptyObject( options );
806 options.complete = callback;
807 if ( options.delay ) {
808 element.delay( options.delay );
810 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
811 element[ method ]( options );
812 } else if ( effectName !== method && element[ effectName ] ) {
813 element[ effectName ]( options.duration, options.easing, callback );
814 } else {
815 element.queue(function( next ) {
816 $( this )[ method ]();
817 if ( callback ) {
818 callback.call( element[ 0 ] );
820 next();
826 })( jQuery );
828 (function( $, undefined ) {
830 var mouseHandled = false;
831 $( document ).mouseup( function() {
832 mouseHandled = false;
835 $.widget("ui.mouse", {
836 version: "1.10.3",
837 options: {
838 cancel: "input,textarea,button,select,option",
839 distance: 1,
840 delay: 0
842 _mouseInit: function() {
843 var that = this;
845 this.element
846 .bind("mousedown."+this.widgetName, function(event) {
847 return that._mouseDown(event);
849 .bind("click."+this.widgetName, function(event) {
850 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
851 $.removeData(event.target, that.widgetName + ".preventClickEvent");
852 event.stopImmediatePropagation();
853 return false;
857 this.started = false;
860 // TODO: make sure destroying one instance of mouse doesn't mess with
861 // other instances of mouse
862 _mouseDestroy: function() {
863 this.element.unbind("."+this.widgetName);
864 if ( this._mouseMoveDelegate ) {
865 $(document)
866 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
867 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
871 _mouseDown: function(event) {
872 // don't let more than one widget handle mouseStart
873 if( mouseHandled ) { return; }
875 // we may have missed mouseup (out of window)
876 (this._mouseStarted && this._mouseUp(event));
878 this._mouseDownEvent = event;
880 var that = this,
881 btnIsLeft = (event.which === 1),
882 // event.target.nodeName works around a bug in IE 8 with
883 // disabled inputs (#7620)
884 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
885 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
886 return true;
889 this.mouseDelayMet = !this.options.delay;
890 if (!this.mouseDelayMet) {
891 this._mouseDelayTimer = setTimeout(function() {
892 that.mouseDelayMet = true;
893 }, this.options.delay);
896 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
897 this._mouseStarted = (this._mouseStart(event) !== false);
898 if (!this._mouseStarted) {
899 event.preventDefault();
900 return true;
904 // Click event may never have fired (Gecko & Opera)
905 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
906 $.removeData(event.target, this.widgetName + ".preventClickEvent");
909 // these delegates are required to keep context
910 this._mouseMoveDelegate = function(event) {
911 return that._mouseMove(event);
913 this._mouseUpDelegate = function(event) {
914 return that._mouseUp(event);
916 $(document)
917 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
918 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
920 event.preventDefault();
922 mouseHandled = true;
923 return true;
926 _mouseMove: function(event) {
927 // IE mouseup check - mouseup happened when mouse was out of window
928 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
929 return this._mouseUp(event);
932 if (this._mouseStarted) {
933 this._mouseDrag(event);
934 return event.preventDefault();
937 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
938 this._mouseStarted =
939 (this._mouseStart(this._mouseDownEvent, event) !== false);
940 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
943 return !this._mouseStarted;
946 _mouseUp: function(event) {
947 $(document)
948 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
949 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
951 if (this._mouseStarted) {
952 this._mouseStarted = false;
954 if (event.target === this._mouseDownEvent.target) {
955 $.data(event.target, this.widgetName + ".preventClickEvent", true);
958 this._mouseStop(event);
961 return false;
964 _mouseDistanceMet: function(event) {
965 return (Math.max(
966 Math.abs(this._mouseDownEvent.pageX - event.pageX),
967 Math.abs(this._mouseDownEvent.pageY - event.pageY)
968 ) >= this.options.distance
972 _mouseDelayMet: function(/* event */) {
973 return this.mouseDelayMet;
976 // These are placeholder methods, to be overriden by extending plugin
977 _mouseStart: function(/* event */) {},
978 _mouseDrag: function(/* event */) {},
979 _mouseStop: function(/* event */) {},
980 _mouseCapture: function(/* event */) { return true; }
983 })(jQuery);
985 (function( $, undefined ) {
987 $.widget("ui.draggable", $.ui.mouse, {
988 version: "1.10.3",
989 widgetEventPrefix: "drag",
990 options: {
991 addClasses: true,
992 appendTo: "parent",
993 axis: false,
994 connectToSortable: false,
995 containment: false,
996 cursor: "auto",
997 cursorAt: false,
998 grid: false,
999 handle: false,
1000 helper: "original",
1001 iframeFix: false,
1002 opacity: false,
1003 refreshPositions: false,
1004 revert: false,
1005 revertDuration: 500,
1006 scope: "default",
1007 scroll: true,
1008 scrollSensitivity: 20,
1009 scrollSpeed: 20,
1010 snap: false,
1011 snapMode: "both",
1012 snapTolerance: 20,
1013 stack: false,
1014 zIndex: false,
1016 // callbacks
1017 drag: null,
1018 start: null,
1019 stop: null
1021 _create: function() {
1023 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1024 this.element[0].style.position = "relative";
1026 if (this.options.addClasses){
1027 this.element.addClass("ui-draggable");
1029 if (this.options.disabled){
1030 this.element.addClass("ui-draggable-disabled");
1033 this._mouseInit();
1037 _destroy: function() {
1038 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1039 this._mouseDestroy();
1042 _mouseCapture: function(event) {
1044 var o = this.options;
1046 // among others, prevent a drag on a resizable-handle
1047 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1048 return false;
1051 //Quit if we're not on a valid handle
1052 this.handle = this._getHandle(event);
1053 if (!this.handle) {
1054 return false;
1057 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1058 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1059 .css({
1060 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1061 position: "absolute", opacity: "0.001", zIndex: 1000
1063 .css($(this).offset())
1064 .appendTo("body");
1067 return true;
1071 _mouseStart: function(event) {
1073 var o = this.options;
1075 //Create and append the visible helper
1076 this.helper = this._createHelper(event);
1078 this.helper.addClass("ui-draggable-dragging");
1080 //Cache the helper size
1081 this._cacheHelperProportions();
1083 //If ddmanager is used for droppables, set the global draggable
1084 if($.ui.ddmanager) {
1085 $.ui.ddmanager.current = this;
1089 * - Position generation -
1090 * This block generates everything position related - it's the core of draggables.
1093 //Cache the margins of the original element
1094 this._cacheMargins();
1096 //Store the helper's css position
1097 this.cssPosition = this.helper.css( "position" );
1098 this.scrollParent = this.helper.scrollParent();
1099 this.offsetParent = this.helper.offsetParent();
1100 this.offsetParentCssPosition = this.offsetParent.css( "position" );
1102 //The element's absolute position on the page minus margins
1103 this.offset = this.positionAbs = this.element.offset();
1104 this.offset = {
1105 top: this.offset.top - this.margins.top,
1106 left: this.offset.left - this.margins.left
1109 //Reset scroll cache
1110 this.offset.scroll = false;
1112 $.extend(this.offset, {
1113 click: { //Where the click happened, relative to the element
1114 left: event.pageX - this.offset.left,
1115 top: event.pageY - this.offset.top
1117 parent: this._getParentOffset(),
1118 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1121 //Generate the original position
1122 this.originalPosition = this.position = this._generatePosition(event);
1123 this.originalPageX = event.pageX;
1124 this.originalPageY = event.pageY;
1126 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1127 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1129 //Set a containment if given in the options
1130 this._setContainment();
1132 //Trigger event + callbacks
1133 if(this._trigger("start", event) === false) {
1134 this._clear();
1135 return false;
1138 //Recache the helper size
1139 this._cacheHelperProportions();
1141 //Prepare the droppable offsets
1142 if ($.ui.ddmanager && !o.dropBehaviour) {
1143 $.ui.ddmanager.prepareOffsets(this, event);
1147 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1149 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1150 if ( $.ui.ddmanager ) {
1151 $.ui.ddmanager.dragStart(this, event);
1154 return true;
1157 _mouseDrag: function(event, noPropagation) {
1158 // reset any necessary cached properties (see #5009)
1159 if ( this.offsetParentCssPosition === "fixed" ) {
1160 this.offset.parent = this._getParentOffset();
1163 //Compute the helpers position
1164 this.position = this._generatePosition(event);
1165 this.positionAbs = this._convertPositionTo("absolute");
1167 //Call plugins and callbacks and use the resulting position if something is returned
1168 if (!noPropagation) {
1169 var ui = this._uiHash();
1170 if(this._trigger("drag", event, ui) === false) {
1171 this._mouseUp({});
1172 return false;
1174 this.position = ui.position;
1177 if(!this.options.axis || this.options.axis !== "y") {
1178 this.helper[0].style.left = this.position.left+"px";
1180 if(!this.options.axis || this.options.axis !== "x") {
1181 this.helper[0].style.top = this.position.top+"px";
1183 if($.ui.ddmanager) {
1184 $.ui.ddmanager.drag(this, event);
1187 return false;
1190 _mouseStop: function(event) {
1192 //If we are using droppables, inform the manager about the drop
1193 var that = this,
1194 dropped = false;
1195 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1196 dropped = $.ui.ddmanager.drop(this, event);
1199 //if a drop comes from outside (a sortable)
1200 if(this.dropped) {
1201 dropped = this.dropped;
1202 this.dropped = false;
1205 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1206 if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
1207 return false;
1210 if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1211 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1212 if(that._trigger("stop", event) !== false) {
1213 that._clear();
1216 } else {
1217 if(this._trigger("stop", event) !== false) {
1218 this._clear();
1222 return false;
1225 _mouseUp: function(event) {
1226 //Remove frame helpers
1227 $("div.ui-draggable-iframeFix").each(function() {
1228 this.parentNode.removeChild(this);
1231 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1232 if( $.ui.ddmanager ) {
1233 $.ui.ddmanager.dragStop(this, event);
1236 return $.ui.mouse.prototype._mouseUp.call(this, event);
1239 cancel: function() {
1241 if(this.helper.is(".ui-draggable-dragging")) {
1242 this._mouseUp({});
1243 } else {
1244 this._clear();
1247 return this;
1251 _getHandle: function(event) {
1252 return this.options.handle ?
1253 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1254 true;
1257 _createHelper: function(event) {
1259 var o = this.options,
1260 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1262 if(!helper.parents("body").length) {
1263 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1266 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1267 helper.css("position", "absolute");
1270 return helper;
1274 _adjustOffsetFromHelper: function(obj) {
1275 if (typeof obj === "string") {
1276 obj = obj.split(" ");
1278 if ($.isArray(obj)) {
1279 obj = {left: +obj[0], top: +obj[1] || 0};
1281 if ("left" in obj) {
1282 this.offset.click.left = obj.left + this.margins.left;
1284 if ("right" in obj) {
1285 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1287 if ("top" in obj) {
1288 this.offset.click.top = obj.top + this.margins.top;
1290 if ("bottom" in obj) {
1291 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1295 _getParentOffset: function() {
1297 //Get the offsetParent and cache its position
1298 var po = this.offsetParent.offset();
1300 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1301 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1302 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1303 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1304 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1305 po.left += this.scrollParent.scrollLeft();
1306 po.top += this.scrollParent.scrollTop();
1309 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1310 //Ugly IE fix
1311 if((this.offsetParent[0] === document.body) ||
1312 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1313 po = { top: 0, left: 0 };
1316 return {
1317 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1318 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1323 _getRelativeOffset: function() {
1325 if(this.cssPosition === "relative") {
1326 var p = this.element.position();
1327 return {
1328 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1329 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1331 } else {
1332 return { top: 0, left: 0 };
1337 _cacheMargins: function() {
1338 this.margins = {
1339 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1340 top: (parseInt(this.element.css("marginTop"),10) || 0),
1341 right: (parseInt(this.element.css("marginRight"),10) || 0),
1342 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1346 _cacheHelperProportions: function() {
1347 this.helperProportions = {
1348 width: this.helper.outerWidth(),
1349 height: this.helper.outerHeight()
1353 _setContainment: function() {
1355 var over, c, ce,
1356 o = this.options;
1358 if ( !o.containment ) {
1359 this.containment = null;
1360 return;
1363 if ( o.containment === "window" ) {
1364 this.containment = [
1365 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1366 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1367 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
1368 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1370 return;
1373 if ( o.containment === "document") {
1374 this.containment = [
1377 $( document ).width() - this.helperProportions.width - this.margins.left,
1378 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
1380 return;
1383 if ( o.containment.constructor === Array ) {
1384 this.containment = o.containment;
1385 return;
1388 if ( o.containment === "parent" ) {
1389 o.containment = this.helper[ 0 ].parentNode;
1392 c = $( o.containment );
1393 ce = c[ 0 ];
1395 if( !ce ) {
1396 return;
1399 over = c.css( "overflow" ) !== "hidden";
1401 this.containment = [
1402 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
1403 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
1404 ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
1405 ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
1407 this.relative_container = c;
1410 _convertPositionTo: function(d, pos) {
1412 if(!pos) {
1413 pos = this.position;
1416 var mod = d === "absolute" ? 1 : -1,
1417 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
1419 //Cache the scroll
1420 if (!this.offset.scroll) {
1421 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1424 return {
1425 top: (
1426 pos.top + // The absolute mouse position
1427 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1428 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1429 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
1431 left: (
1432 pos.left + // The absolute mouse position
1433 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1434 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1435 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
1441 _generatePosition: function(event) {
1443 var containment, co, top, left,
1444 o = this.options,
1445 scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
1446 pageX = event.pageX,
1447 pageY = event.pageY;
1449 //Cache the scroll
1450 if (!this.offset.scroll) {
1451 this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
1455 * - Position constraining -
1456 * Constrain the position to a mix of grid, containment.
1459 // If we are not dragging yet, we won't check for options
1460 if ( this.originalPosition ) {
1461 if ( this.containment ) {
1462 if ( this.relative_container ){
1463 co = this.relative_container.offset();
1464 containment = [
1465 this.containment[ 0 ] + co.left,
1466 this.containment[ 1 ] + co.top,
1467 this.containment[ 2 ] + co.left,
1468 this.containment[ 3 ] + co.top
1471 else {
1472 containment = this.containment;
1475 if(event.pageX - this.offset.click.left < containment[0]) {
1476 pageX = containment[0] + this.offset.click.left;
1478 if(event.pageY - this.offset.click.top < containment[1]) {
1479 pageY = containment[1] + this.offset.click.top;
1481 if(event.pageX - this.offset.click.left > containment[2]) {
1482 pageX = containment[2] + this.offset.click.left;
1484 if(event.pageY - this.offset.click.top > containment[3]) {
1485 pageY = containment[3] + this.offset.click.top;
1489 if(o.grid) {
1490 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1491 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1492 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
1494 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1495 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
1500 return {
1501 top: (
1502 pageY - // The absolute mouse position
1503 this.offset.click.top - // Click offset (relative to the element)
1504 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1505 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1506 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
1508 left: (
1509 pageX - // The absolute mouse position
1510 this.offset.click.left - // Click offset (relative to the element)
1511 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1512 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1513 ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
1519 _clear: function() {
1520 this.helper.removeClass("ui-draggable-dragging");
1521 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1522 this.helper.remove();
1524 this.helper = null;
1525 this.cancelHelperRemoval = false;
1528 // From now on bulk stuff - mainly helpers
1530 _trigger: function(type, event, ui) {
1531 ui = ui || this._uiHash();
1532 $.ui.plugin.call(this, type, [event, ui]);
1533 //The absolute position has to be recalculated after plugins
1534 if(type === "drag") {
1535 this.positionAbs = this._convertPositionTo("absolute");
1537 return $.Widget.prototype._trigger.call(this, type, event, ui);
1540 plugins: {},
1542 _uiHash: function() {
1543 return {
1544 helper: this.helper,
1545 position: this.position,
1546 originalPosition: this.originalPosition,
1547 offset: this.positionAbs
1553 $.ui.plugin.add("draggable", "connectToSortable", {
1554 start: function(event, ui) {
1556 var inst = $(this).data("ui-draggable"), o = inst.options,
1557 uiSortable = $.extend({}, ui, { item: inst.element });
1558 inst.sortables = [];
1559 $(o.connectToSortable).each(function() {
1560 var sortable = $.data(this, "ui-sortable");
1561 if (sortable && !sortable.options.disabled) {
1562 inst.sortables.push({
1563 instance: sortable,
1564 shouldRevert: sortable.options.revert
1566 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
1567 sortable._trigger("activate", event, uiSortable);
1572 stop: function(event, ui) {
1574 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1575 var inst = $(this).data("ui-draggable"),
1576 uiSortable = $.extend({}, ui, { item: inst.element });
1578 $.each(inst.sortables, function() {
1579 if(this.instance.isOver) {
1581 this.instance.isOver = 0;
1583 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1584 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1586 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
1587 if(this.shouldRevert) {
1588 this.instance.options.revert = this.shouldRevert;
1591 //Trigger the stop of the sortable
1592 this.instance._mouseStop(event);
1594 this.instance.options.helper = this.instance.options._helper;
1596 //If the helper has been the original item, restore properties in the sortable
1597 if(inst.options.helper === "original") {
1598 this.instance.currentItem.css({ top: "auto", left: "auto" });
1601 } else {
1602 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1603 this.instance._trigger("deactivate", event, uiSortable);
1609 drag: function(event, ui) {
1611 var inst = $(this).data("ui-draggable"), that = this;
1613 $.each(inst.sortables, function() {
1615 var innermostIntersecting = false,
1616 thisSortable = this;
1618 //Copy over some variables to allow calling the sortable's native _intersectsWith
1619 this.instance.positionAbs = inst.positionAbs;
1620 this.instance.helperProportions = inst.helperProportions;
1621 this.instance.offset.click = inst.offset.click;
1623 if(this.instance._intersectsWith(this.instance.containerCache)) {
1624 innermostIntersecting = true;
1625 $.each(inst.sortables, function () {
1626 this.instance.positionAbs = inst.positionAbs;
1627 this.instance.helperProportions = inst.helperProportions;
1628 this.instance.offset.click = inst.offset.click;
1629 if (this !== thisSortable &&
1630 this.instance._intersectsWith(this.instance.containerCache) &&
1631 $.contains(thisSortable.instance.element[0], this.instance.element[0])
1633 innermostIntersecting = false;
1635 return innermostIntersecting;
1640 if(innermostIntersecting) {
1641 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1642 if(!this.instance.isOver) {
1644 this.instance.isOver = 1;
1645 //Now we fake the start of dragging for the sortable instance,
1646 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1647 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
1648 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
1649 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1650 this.instance.options.helper = function() { return ui.helper[0]; };
1652 event.target = this.instance.currentItem[0];
1653 this.instance._mouseCapture(event, true);
1654 this.instance._mouseStart(event, true, true);
1656 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1657 this.instance.offset.click.top = inst.offset.click.top;
1658 this.instance.offset.click.left = inst.offset.click.left;
1659 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1660 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1662 inst._trigger("toSortable", event);
1663 inst.dropped = this.instance.element; //draggable revert needs that
1664 //hack so receive/update callbacks work (mostly)
1665 inst.currentItem = inst.element;
1666 this.instance.fromOutside = inst;
1670 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1671 if(this.instance.currentItem) {
1672 this.instance._mouseDrag(event);
1675 } else {
1677 //If it doesn't intersect with the sortable, and it intersected before,
1678 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1679 if(this.instance.isOver) {
1681 this.instance.isOver = 0;
1682 this.instance.cancelHelperRemoval = true;
1684 //Prevent reverting on this forced stop
1685 this.instance.options.revert = false;
1687 // The out event needs to be triggered independently
1688 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
1690 this.instance._mouseStop(event, true);
1691 this.instance.options.helper = this.instance.options._helper;
1693 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1694 this.instance.currentItem.remove();
1695 if(this.instance.placeholder) {
1696 this.instance.placeholder.remove();
1699 inst._trigger("fromSortable", event);
1700 inst.dropped = false; //draggable revert needs that
1710 $.ui.plugin.add("draggable", "cursor", {
1711 start: function() {
1712 var t = $("body"), o = $(this).data("ui-draggable").options;
1713 if (t.css("cursor")) {
1714 o._cursor = t.css("cursor");
1716 t.css("cursor", o.cursor);
1718 stop: function() {
1719 var o = $(this).data("ui-draggable").options;
1720 if (o._cursor) {
1721 $("body").css("cursor", o._cursor);
1726 $.ui.plugin.add("draggable", "opacity", {
1727 start: function(event, ui) {
1728 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1729 if(t.css("opacity")) {
1730 o._opacity = t.css("opacity");
1732 t.css("opacity", o.opacity);
1734 stop: function(event, ui) {
1735 var o = $(this).data("ui-draggable").options;
1736 if(o._opacity) {
1737 $(ui.helper).css("opacity", o._opacity);
1742 $.ui.plugin.add("draggable", "scroll", {
1743 start: function() {
1744 var i = $(this).data("ui-draggable");
1745 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1746 i.overflowOffset = i.scrollParent.offset();
1749 drag: function( event ) {
1751 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
1753 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1755 if(!o.axis || o.axis !== "x") {
1756 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
1757 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1758 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
1759 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1763 if(!o.axis || o.axis !== "y") {
1764 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
1765 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1766 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
1767 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1771 } else {
1773 if(!o.axis || o.axis !== "x") {
1774 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
1775 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1776 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
1777 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1781 if(!o.axis || o.axis !== "y") {
1782 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
1783 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1784 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
1785 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1791 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
1792 $.ui.ddmanager.prepareOffsets(i, event);
1798 $.ui.plugin.add("draggable", "snap", {
1799 start: function() {
1801 var i = $(this).data("ui-draggable"),
1802 o = i.options;
1804 i.snapElements = [];
1806 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
1807 var $t = $(this),
1808 $o = $t.offset();
1809 if(this !== i.element[0]) {
1810 i.snapElements.push({
1811 item: this,
1812 width: $t.outerWidth(), height: $t.outerHeight(),
1813 top: $o.top, left: $o.left
1819 drag: function(event, ui) {
1821 var ts, bs, ls, rs, l, r, t, b, i, first,
1822 inst = $(this).data("ui-draggable"),
1823 o = inst.options,
1824 d = o.snapTolerance,
1825 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1826 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1828 for (i = inst.snapElements.length - 1; i >= 0; i--){
1830 l = inst.snapElements[i].left;
1831 r = l + inst.snapElements[i].width;
1832 t = inst.snapElements[i].top;
1833 b = t + inst.snapElements[i].height;
1835 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
1836 if(inst.snapElements[i].snapping) {
1837 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1839 inst.snapElements[i].snapping = false;
1840 continue;
1843 if(o.snapMode !== "inner") {
1844 ts = Math.abs(t - y2) <= d;
1845 bs = Math.abs(b - y1) <= d;
1846 ls = Math.abs(l - x2) <= d;
1847 rs = Math.abs(r - x1) <= d;
1848 if(ts) {
1849 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1851 if(bs) {
1852 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1854 if(ls) {
1855 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1857 if(rs) {
1858 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1862 first = (ts || bs || ls || rs);
1864 if(o.snapMode !== "outer") {
1865 ts = Math.abs(t - y1) <= d;
1866 bs = Math.abs(b - y2) <= d;
1867 ls = Math.abs(l - x1) <= d;
1868 rs = Math.abs(r - x2) <= d;
1869 if(ts) {
1870 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1872 if(bs) {
1873 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1875 if(ls) {
1876 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1878 if(rs) {
1879 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1883 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
1884 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1886 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1893 $.ui.plugin.add("draggable", "stack", {
1894 start: function() {
1895 var min,
1896 o = this.data("ui-draggable").options,
1897 group = $.makeArray($(o.stack)).sort(function(a,b) {
1898 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1901 if (!group.length) { return; }
1903 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
1904 $(group).each(function(i) {
1905 $(this).css("zIndex", min + i);
1907 this.css("zIndex", (min + group.length));
1911 $.ui.plugin.add("draggable", "zIndex", {
1912 start: function(event, ui) {
1913 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1914 if(t.css("zIndex")) {
1915 o._zIndex = t.css("zIndex");
1917 t.css("zIndex", o.zIndex);
1919 stop: function(event, ui) {
1920 var o = $(this).data("ui-draggable").options;
1921 if(o._zIndex) {
1922 $(ui.helper).css("zIndex", o._zIndex);
1927 })(jQuery);
1929 (function( $, undefined ) {
1931 function isOverAxis( x, reference, size ) {
1932 return ( x > reference ) && ( x < ( reference + size ) );
1935 $.widget("ui.droppable", {
1936 version: "1.10.3",
1937 widgetEventPrefix: "drop",
1938 options: {
1939 accept: "*",
1940 activeClass: false,
1941 addClasses: true,
1942 greedy: false,
1943 hoverClass: false,
1944 scope: "default",
1945 tolerance: "intersect",
1947 // callbacks
1948 activate: null,
1949 deactivate: null,
1950 drop: null,
1951 out: null,
1952 over: null
1954 _create: function() {
1956 var o = this.options,
1957 accept = o.accept;
1959 this.isover = false;
1960 this.isout = true;
1962 this.accept = $.isFunction(accept) ? accept : function(d) {
1963 return d.is(accept);
1966 //Store the droppable's proportions
1967 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1969 // Add the reference and positions to the manager
1970 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1971 $.ui.ddmanager.droppables[o.scope].push(this);
1973 (o.addClasses && this.element.addClass("ui-droppable"));
1977 _destroy: function() {
1978 var i = 0,
1979 drop = $.ui.ddmanager.droppables[this.options.scope];
1981 for ( ; i < drop.length; i++ ) {
1982 if ( drop[i] === this ) {
1983 drop.splice(i, 1);
1987 this.element.removeClass("ui-droppable ui-droppable-disabled");
1990 _setOption: function(key, value) {
1992 if(key === "accept") {
1993 this.accept = $.isFunction(value) ? value : function(d) {
1994 return d.is(value);
1997 $.Widget.prototype._setOption.apply(this, arguments);
2000 _activate: function(event) {
2001 var draggable = $.ui.ddmanager.current;
2002 if(this.options.activeClass) {
2003 this.element.addClass(this.options.activeClass);
2005 if(draggable){
2006 this._trigger("activate", event, this.ui(draggable));
2010 _deactivate: function(event) {
2011 var draggable = $.ui.ddmanager.current;
2012 if(this.options.activeClass) {
2013 this.element.removeClass(this.options.activeClass);
2015 if(draggable){
2016 this._trigger("deactivate", event, this.ui(draggable));
2020 _over: function(event) {
2022 var draggable = $.ui.ddmanager.current;
2024 // Bail if draggable and droppable are same element
2025 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2026 return;
2029 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2030 if(this.options.hoverClass) {
2031 this.element.addClass(this.options.hoverClass);
2033 this._trigger("over", event, this.ui(draggable));
2038 _out: function(event) {
2040 var draggable = $.ui.ddmanager.current;
2042 // Bail if draggable and droppable are same element
2043 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2044 return;
2047 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2048 if(this.options.hoverClass) {
2049 this.element.removeClass(this.options.hoverClass);
2051 this._trigger("out", event, this.ui(draggable));
2056 _drop: function(event,custom) {
2058 var draggable = custom || $.ui.ddmanager.current,
2059 childrenIntersection = false;
2061 // Bail if draggable and droppable are same element
2062 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2063 return false;
2066 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2067 var inst = $.data(this, "ui-droppable");
2069 inst.options.greedy &&
2070 !inst.options.disabled &&
2071 inst.options.scope === draggable.options.scope &&
2072 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2073 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2074 ) { childrenIntersection = true; return false; }
2076 if(childrenIntersection) {
2077 return false;
2080 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2081 if(this.options.activeClass) {
2082 this.element.removeClass(this.options.activeClass);
2084 if(this.options.hoverClass) {
2085 this.element.removeClass(this.options.hoverClass);
2087 this._trigger("drop", event, this.ui(draggable));
2088 return this.element;
2091 return false;
2095 ui: function(c) {
2096 return {
2097 draggable: (c.currentItem || c.element),
2098 helper: c.helper,
2099 position: c.position,
2100 offset: c.positionAbs
2106 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2108 if (!droppable.offset) {
2109 return false;
2112 var draggableLeft, draggableTop,
2113 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2114 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2115 l = droppable.offset.left, r = l + droppable.proportions.width,
2116 t = droppable.offset.top, b = t + droppable.proportions.height;
2118 switch (toleranceMode) {
2119 case "fit":
2120 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2121 case "intersect":
2122 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2123 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2124 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2125 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2126 case "pointer":
2127 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2128 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2129 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2130 case "touch":
2131 return (
2132 (y1 >= t && y1 <= b) || // Top edge touching
2133 (y2 >= t && y2 <= b) || // Bottom edge touching
2134 (y1 < t && y2 > b) // Surrounded vertically
2135 ) && (
2136 (x1 >= l && x1 <= r) || // Left edge touching
2137 (x2 >= l && x2 <= r) || // Right edge touching
2138 (x1 < l && x2 > r) // Surrounded horizontally
2140 default:
2141 return false;
2147 This manager tracks offsets of draggables and droppables
2149 $.ui.ddmanager = {
2150 current: null,
2151 droppables: { "default": [] },
2152 prepareOffsets: function(t, event) {
2154 var i, j,
2155 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2156 type = event ? event.type : null, // workaround for #2317
2157 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2159 droppablesLoop: for (i = 0; i < m.length; i++) {
2161 //No disabled and non-accepted
2162 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2163 continue;
2166 // Filter out elements in the current dragged item
2167 for (j=0; j < list.length; j++) {
2168 if(list[j] === m[i].element[0]) {
2169 m[i].proportions.height = 0;
2170 continue droppablesLoop;
2174 m[i].visible = m[i].element.css("display") !== "none";
2175 if(!m[i].visible) {
2176 continue;
2179 //Activate the droppable if used directly from draggables
2180 if(type === "mousedown") {
2181 m[i]._activate.call(m[i], event);
2184 m[i].offset = m[i].element.offset();
2185 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2190 drop: function(draggable, event) {
2192 var dropped = false;
2193 // Create a copy of the droppables in case the list changes during the drop (#9116)
2194 $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
2196 if(!this.options) {
2197 return;
2199 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2200 dropped = this._drop.call(this, event) || dropped;
2203 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2204 this.isout = true;
2205 this.isover = false;
2206 this._deactivate.call(this, event);
2210 return dropped;
2213 dragStart: function( draggable, event ) {
2214 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2215 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2216 if( !draggable.options.refreshPositions ) {
2217 $.ui.ddmanager.prepareOffsets( draggable, event );
2221 drag: function(draggable, event) {
2223 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2224 if(draggable.options.refreshPositions) {
2225 $.ui.ddmanager.prepareOffsets(draggable, event);
2228 //Run through all droppables and check their positions based on specific tolerance options
2229 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2231 if(this.options.disabled || this.greedyChild || !this.visible) {
2232 return;
2235 var parentInstance, scope, parent,
2236 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2237 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2238 if(!c) {
2239 return;
2242 if (this.options.greedy) {
2243 // find droppable parents with same scope
2244 scope = this.options.scope;
2245 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2246 return $.data(this, "ui-droppable").options.scope === scope;
2249 if (parent.length) {
2250 parentInstance = $.data(parent[0], "ui-droppable");
2251 parentInstance.greedyChild = (c === "isover");
2255 // we just moved into a greedy child
2256 if (parentInstance && c === "isover") {
2257 parentInstance.isover = false;
2258 parentInstance.isout = true;
2259 parentInstance._out.call(parentInstance, event);
2262 this[c] = true;
2263 this[c === "isout" ? "isover" : "isout"] = false;
2264 this[c === "isover" ? "_over" : "_out"].call(this, event);
2266 // we just moved out of a greedy child
2267 if (parentInstance && c === "isout") {
2268 parentInstance.isout = false;
2269 parentInstance.isover = true;
2270 parentInstance._over.call(parentInstance, event);
2275 dragStop: function( draggable, event ) {
2276 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2277 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2278 if( !draggable.options.refreshPositions ) {
2279 $.ui.ddmanager.prepareOffsets( draggable, event );
2284 })(jQuery);
2286 (function( $, undefined ) {
2288 function num(v) {
2289 return parseInt(v, 10) || 0;
2292 function isNumber(value) {
2293 return !isNaN(parseInt(value, 10));
2296 $.widget("ui.resizable", $.ui.mouse, {
2297 version: "1.10.3",
2298 widgetEventPrefix: "resize",
2299 options: {
2300 alsoResize: false,
2301 animate: false,
2302 animateDuration: "slow",
2303 animateEasing: "swing",
2304 aspectRatio: false,
2305 autoHide: false,
2306 containment: false,
2307 ghost: false,
2308 grid: false,
2309 handles: "e,s,se",
2310 helper: false,
2311 maxHeight: null,
2312 maxWidth: null,
2313 minHeight: 10,
2314 minWidth: 10,
2315 // See #7960
2316 zIndex: 90,
2318 // callbacks
2319 resize: null,
2320 start: null,
2321 stop: null
2323 _create: function() {
2325 var n, i, handle, axis, hname,
2326 that = this,
2327 o = this.options;
2328 this.element.addClass("ui-resizable");
2330 $.extend(this, {
2331 _aspectRatio: !!(o.aspectRatio),
2332 aspectRatio: o.aspectRatio,
2333 originalElement: this.element,
2334 _proportionallyResizeElements: [],
2335 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
2338 //Wrap the element if it cannot hold child nodes
2339 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2341 //Create a wrapper element and set the wrapper to the new current internal element
2342 this.element.wrap(
2343 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
2344 position: this.element.css("position"),
2345 width: this.element.outerWidth(),
2346 height: this.element.outerHeight(),
2347 top: this.element.css("top"),
2348 left: this.element.css("left")
2352 //Overwrite the original this.element
2353 this.element = this.element.parent().data(
2354 "ui-resizable", this.element.data("ui-resizable")
2357 this.elementIsWrapper = true;
2359 //Move margins to the wrapper
2360 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2361 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2363 //Prevent Safari textarea resize
2364 this.originalResizeStyle = this.originalElement.css("resize");
2365 this.originalElement.css("resize", "none");
2367 //Push the actual element to our proportionallyResize internal array
2368 this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
2370 // avoid IE jump (hard set the margin)
2371 this.originalElement.css({ margin: this.originalElement.css("margin") });
2373 // fix handlers offset
2374 this._proportionallyResize();
2378 this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
2379 if(this.handles.constructor === String) {
2381 if ( this.handles === "all") {
2382 this.handles = "n,e,s,w,se,sw,ne,nw";
2385 n = this.handles.split(",");
2386 this.handles = {};
2388 for(i = 0; i < n.length; i++) {
2390 handle = $.trim(n[i]);
2391 hname = "ui-resizable-"+handle;
2392 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
2394 // Apply zIndex to all handles - see #7960
2395 axis.css({ zIndex: o.zIndex });
2397 //TODO : What's going on here?
2398 if ("se" === handle) {
2399 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
2402 //Insert into internal handles object and append to element
2403 this.handles[handle] = ".ui-resizable-"+handle;
2404 this.element.append(axis);
2409 this._renderAxis = function(target) {
2411 var i, axis, padPos, padWrapper;
2413 target = target || this.element;
2415 for(i in this.handles) {
2417 if(this.handles[i].constructor === String) {
2418 this.handles[i] = $(this.handles[i], this.element).show();
2421 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2422 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2424 axis = $(this.handles[i], this.element);
2426 //Checking the correct pad and border
2427 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2429 //The padding type i have to apply...
2430 padPos = [ "padding",
2431 /ne|nw|n/.test(i) ? "Top" :
2432 /se|sw|s/.test(i) ? "Bottom" :
2433 /^e$/.test(i) ? "Right" : "Left" ].join("");
2435 target.css(padPos, padWrapper);
2437 this._proportionallyResize();
2441 //TODO: What's that good for? There's not anything to be executed left
2442 if(!$(this.handles[i]).length) {
2443 continue;
2448 //TODO: make renderAxis a prototype function
2449 this._renderAxis(this.element);
2451 this._handles = $(".ui-resizable-handle", this.element)
2452 .disableSelection();
2454 //Matching axis name
2455 this._handles.mouseover(function() {
2456 if (!that.resizing) {
2457 if (this.className) {
2458 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2460 //Axis, default = se
2461 that.axis = axis && axis[1] ? axis[1] : "se";
2465 //If we want to auto hide the elements
2466 if (o.autoHide) {
2467 this._handles.hide();
2468 $(this.element)
2469 .addClass("ui-resizable-autohide")
2470 .mouseenter(function() {
2471 if (o.disabled) {
2472 return;
2474 $(this).removeClass("ui-resizable-autohide");
2475 that._handles.show();
2477 .mouseleave(function(){
2478 if (o.disabled) {
2479 return;
2481 if (!that.resizing) {
2482 $(this).addClass("ui-resizable-autohide");
2483 that._handles.hide();
2488 //Initialize the mouse interaction
2489 this._mouseInit();
2493 _destroy: function() {
2495 this._mouseDestroy();
2497 var wrapper,
2498 _destroy = function(exp) {
2499 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2500 .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
2503 //TODO: Unwrap at same DOM position
2504 if (this.elementIsWrapper) {
2505 _destroy(this.element);
2506 wrapper = this.element;
2507 this.originalElement.css({
2508 position: wrapper.css("position"),
2509 width: wrapper.outerWidth(),
2510 height: wrapper.outerHeight(),
2511 top: wrapper.css("top"),
2512 left: wrapper.css("left")
2513 }).insertAfter( wrapper );
2514 wrapper.remove();
2517 this.originalElement.css("resize", this.originalResizeStyle);
2518 _destroy(this.originalElement);
2520 return this;
2523 _mouseCapture: function(event) {
2524 var i, handle,
2525 capture = false;
2527 for (i in this.handles) {
2528 handle = $(this.handles[i])[0];
2529 if (handle === event.target || $.contains(handle, event.target)) {
2530 capture = true;
2534 return !this.options.disabled && capture;
2537 _mouseStart: function(event) {
2539 var curleft, curtop, cursor,
2540 o = this.options,
2541 iniPos = this.element.position(),
2542 el = this.element;
2544 this.resizing = true;
2546 // bugfix for http://dev.jquery.com/ticket/1749
2547 if ( (/absolute/).test( el.css("position") ) ) {
2548 el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
2549 } else if (el.is(".ui-draggable")) {
2550 el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
2553 this._renderProxy();
2555 curleft = num(this.helper.css("left"));
2556 curtop = num(this.helper.css("top"));
2558 if (o.containment) {
2559 curleft += $(o.containment).scrollLeft() || 0;
2560 curtop += $(o.containment).scrollTop() || 0;
2563 //Store needed variables
2564 this.offset = this.helper.offset();
2565 this.position = { left: curleft, top: curtop };
2566 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2567 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2568 this.originalPosition = { left: curleft, top: curtop };
2569 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2570 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2572 //Aspect Ratio
2573 this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2575 cursor = $(".ui-resizable-" + this.axis).css("cursor");
2576 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
2578 el.addClass("ui-resizable-resizing");
2579 this._propagate("start", event);
2580 return true;
2583 _mouseDrag: function(event) {
2585 //Increase performance, avoid regex
2586 var data,
2587 el = this.helper, props = {},
2588 smp = this.originalMousePosition,
2589 a = this.axis,
2590 prevTop = this.position.top,
2591 prevLeft = this.position.left,
2592 prevWidth = this.size.width,
2593 prevHeight = this.size.height,
2594 dx = (event.pageX-smp.left)||0,
2595 dy = (event.pageY-smp.top)||0,
2596 trigger = this._change[a];
2598 if (!trigger) {
2599 return false;
2602 // Calculate the attrs that will be change
2603 data = trigger.apply(this, [event, dx, dy]);
2605 // Put this in the mouseDrag handler since the user can start pressing shift while resizing
2606 this._updateVirtualBoundaries(event.shiftKey);
2607 if (this._aspectRatio || event.shiftKey) {
2608 data = this._updateRatio(data, event);
2611 data = this._respectSize(data, event);
2613 this._updateCache(data);
2615 // plugins callbacks need to be called first
2616 this._propagate("resize", event);
2618 if (this.position.top !== prevTop) {
2619 props.top = this.position.top + "px";
2621 if (this.position.left !== prevLeft) {
2622 props.left = this.position.left + "px";
2624 if (this.size.width !== prevWidth) {
2625 props.width = this.size.width + "px";
2627 if (this.size.height !== prevHeight) {
2628 props.height = this.size.height + "px";
2630 el.css(props);
2632 if (!this._helper && this._proportionallyResizeElements.length) {
2633 this._proportionallyResize();
2636 // Call the user callback if the element was resized
2637 if ( ! $.isEmptyObject(props) ) {
2638 this._trigger("resize", event, this.ui());
2641 return false;
2644 _mouseStop: function(event) {
2646 this.resizing = false;
2647 var pr, ista, soffseth, soffsetw, s, left, top,
2648 o = this.options, that = this;
2650 if(this._helper) {
2652 pr = this._proportionallyResizeElements;
2653 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
2654 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
2655 soffsetw = ista ? 0 : that.sizeDiff.width;
2657 s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
2658 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
2659 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2661 if (!o.animate) {
2662 this.element.css($.extend(s, { top: top, left: left }));
2665 that.helper.height(that.size.height);
2666 that.helper.width(that.size.width);
2668 if (this._helper && !o.animate) {
2669 this._proportionallyResize();
2673 $("body").css("cursor", "auto");
2675 this.element.removeClass("ui-resizable-resizing");
2677 this._propagate("stop", event);
2679 if (this._helper) {
2680 this.helper.remove();
2683 return false;
2687 _updateVirtualBoundaries: function(forceAspectRatio) {
2688 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
2689 o = this.options;
2691 b = {
2692 minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
2693 maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
2694 minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
2695 maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
2698 if(this._aspectRatio || forceAspectRatio) {
2699 // We want to create an enclosing box whose aspect ration is the requested one
2700 // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
2701 pMinWidth = b.minHeight * this.aspectRatio;
2702 pMinHeight = b.minWidth / this.aspectRatio;
2703 pMaxWidth = b.maxHeight * this.aspectRatio;
2704 pMaxHeight = b.maxWidth / this.aspectRatio;
2706 if(pMinWidth > b.minWidth) {
2707 b.minWidth = pMinWidth;
2709 if(pMinHeight > b.minHeight) {
2710 b.minHeight = pMinHeight;
2712 if(pMaxWidth < b.maxWidth) {
2713 b.maxWidth = pMaxWidth;
2715 if(pMaxHeight < b.maxHeight) {
2716 b.maxHeight = pMaxHeight;
2719 this._vBoundaries = b;
2722 _updateCache: function(data) {
2723 this.offset = this.helper.offset();
2724 if (isNumber(data.left)) {
2725 this.position.left = data.left;
2727 if (isNumber(data.top)) {
2728 this.position.top = data.top;
2730 if (isNumber(data.height)) {
2731 this.size.height = data.height;
2733 if (isNumber(data.width)) {
2734 this.size.width = data.width;
2738 _updateRatio: function( data ) {
2740 var cpos = this.position,
2741 csize = this.size,
2742 a = this.axis;
2744 if (isNumber(data.height)) {
2745 data.width = (data.height * this.aspectRatio);
2746 } else if (isNumber(data.width)) {
2747 data.height = (data.width / this.aspectRatio);
2750 if (a === "sw") {
2751 data.left = cpos.left + (csize.width - data.width);
2752 data.top = null;
2754 if (a === "nw") {
2755 data.top = cpos.top + (csize.height - data.height);
2756 data.left = cpos.left + (csize.width - data.width);
2759 return data;
2762 _respectSize: function( data ) {
2764 var o = this._vBoundaries,
2765 a = this.axis,
2766 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2767 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
2768 dw = this.originalPosition.left + this.originalSize.width,
2769 dh = this.position.top + this.size.height,
2770 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2771 if (isminw) {
2772 data.width = o.minWidth;
2774 if (isminh) {
2775 data.height = o.minHeight;
2777 if (ismaxw) {
2778 data.width = o.maxWidth;
2780 if (ismaxh) {
2781 data.height = o.maxHeight;
2784 if (isminw && cw) {
2785 data.left = dw - o.minWidth;
2787 if (ismaxw && cw) {
2788 data.left = dw - o.maxWidth;
2790 if (isminh && ch) {
2791 data.top = dh - o.minHeight;
2793 if (ismaxh && ch) {
2794 data.top = dh - o.maxHeight;
2797 // fixing jump error on top/left - bug #2330
2798 if (!data.width && !data.height && !data.left && data.top) {
2799 data.top = null;
2800 } else if (!data.width && !data.height && !data.top && data.left) {
2801 data.left = null;
2804 return data;
2807 _proportionallyResize: function() {
2809 if (!this._proportionallyResizeElements.length) {
2810 return;
2813 var i, j, borders, paddings, prel,
2814 element = this.helper || this.element;
2816 for ( i=0; i < this._proportionallyResizeElements.length; i++) {
2818 prel = this._proportionallyResizeElements[i];
2820 if (!this.borderDif) {
2821 this.borderDif = [];
2822 borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
2823 paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
2825 for ( j = 0; j < borders.length; j++ ) {
2826 this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
2830 prel.css({
2831 height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
2832 width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
2839 _renderProxy: function() {
2841 var el = this.element, o = this.options;
2842 this.elementOffset = el.offset();
2844 if(this._helper) {
2846 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
2848 this.helper.addClass(this._helper).css({
2849 width: this.element.outerWidth() - 1,
2850 height: this.element.outerHeight() - 1,
2851 position: "absolute",
2852 left: this.elementOffset.left +"px",
2853 top: this.elementOffset.top +"px",
2854 zIndex: ++o.zIndex //TODO: Don't modify option
2857 this.helper
2858 .appendTo("body")
2859 .disableSelection();
2861 } else {
2862 this.helper = this.element;
2867 _change: {
2868 e: function(event, dx) {
2869 return { width: this.originalSize.width + dx };
2871 w: function(event, dx) {
2872 var cs = this.originalSize, sp = this.originalPosition;
2873 return { left: sp.left + dx, width: cs.width - dx };
2875 n: function(event, dx, dy) {
2876 var cs = this.originalSize, sp = this.originalPosition;
2877 return { top: sp.top + dy, height: cs.height - dy };
2879 s: function(event, dx, dy) {
2880 return { height: this.originalSize.height + dy };
2882 se: function(event, dx, dy) {
2883 return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2885 sw: function(event, dx, dy) {
2886 return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2888 ne: function(event, dx, dy) {
2889 return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
2891 nw: function(event, dx, dy) {
2892 return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
2896 _propagate: function(n, event) {
2897 $.ui.plugin.call(this, n, [event, this.ui()]);
2898 (n !== "resize" && this._trigger(n, event, this.ui()));
2901 plugins: {},
2903 ui: function() {
2904 return {
2905 originalElement: this.originalElement,
2906 element: this.element,
2907 helper: this.helper,
2908 position: this.position,
2909 size: this.size,
2910 originalSize: this.originalSize,
2911 originalPosition: this.originalPosition
2918 * Resizable Extensions
2921 $.ui.plugin.add("resizable", "animate", {
2923 stop: function( event ) {
2924 var that = $(this).data("ui-resizable"),
2925 o = that.options,
2926 pr = that._proportionallyResizeElements,
2927 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2928 soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
2929 soffsetw = ista ? 0 : that.sizeDiff.width,
2930 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
2931 left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
2932 top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
2934 that.element.animate(
2935 $.extend(style, top && left ? { top: top, left: left } : {}), {
2936 duration: o.animateDuration,
2937 easing: o.animateEasing,
2938 step: function() {
2940 var data = {
2941 width: parseInt(that.element.css("width"), 10),
2942 height: parseInt(that.element.css("height"), 10),
2943 top: parseInt(that.element.css("top"), 10),
2944 left: parseInt(that.element.css("left"), 10)
2947 if (pr && pr.length) {
2948 $(pr[0]).css({ width: data.width, height: data.height });
2951 // propagating resize, and updating values for each animation step
2952 that._updateCache(data);
2953 that._propagate("resize", event);
2962 $.ui.plugin.add("resizable", "containment", {
2964 start: function() {
2965 var element, p, co, ch, cw, width, height,
2966 that = $(this).data("ui-resizable"),
2967 o = that.options,
2968 el = that.element,
2969 oc = o.containment,
2970 ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
2972 if (!ce) {
2973 return;
2976 that.containerElement = $(ce);
2978 if (/document/.test(oc) || oc === document) {
2979 that.containerOffset = { left: 0, top: 0 };
2980 that.containerPosition = { left: 0, top: 0 };
2982 that.parentData = {
2983 element: $(document), left: 0, top: 0,
2984 width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
2988 // i'm a node, so compute top, left, right, bottom
2989 else {
2990 element = $(ce);
2991 p = [];
2992 $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
2994 that.containerOffset = element.offset();
2995 that.containerPosition = element.position();
2996 that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
2998 co = that.containerOffset;
2999 ch = that.containerSize.height;
3000 cw = that.containerSize.width;
3001 width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
3002 height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3004 that.parentData = {
3005 element: ce, left: co.left, top: co.top, width: width, height: height
3010 resize: function( event ) {
3011 var woset, hoset, isParent, isOffsetRelative,
3012 that = $(this).data("ui-resizable"),
3013 o = that.options,
3014 co = that.containerOffset, cp = that.position,
3015 pRatio = that._aspectRatio || event.shiftKey,
3016 cop = { top:0, left:0 }, ce = that.containerElement;
3018 if (ce[0] !== document && (/static/).test(ce.css("position"))) {
3019 cop = co;
3022 if (cp.left < (that._helper ? co.left : 0)) {
3023 that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
3024 if (pRatio) {
3025 that.size.height = that.size.width / that.aspectRatio;
3027 that.position.left = o.helper ? co.left : 0;
3030 if (cp.top < (that._helper ? co.top : 0)) {
3031 that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
3032 if (pRatio) {
3033 that.size.width = that.size.height * that.aspectRatio;
3035 that.position.top = that._helper ? co.top : 0;
3038 that.offset.left = that.parentData.left+that.position.left;
3039 that.offset.top = that.parentData.top+that.position.top;
3041 woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
3042 hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
3044 isParent = that.containerElement.get(0) === that.element.parent().get(0);
3045 isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
3047 if(isParent && isOffsetRelative) {
3048 woset -= that.parentData.left;
3051 if (woset + that.size.width >= that.parentData.width) {
3052 that.size.width = that.parentData.width - woset;
3053 if (pRatio) {
3054 that.size.height = that.size.width / that.aspectRatio;
3058 if (hoset + that.size.height >= that.parentData.height) {
3059 that.size.height = that.parentData.height - hoset;
3060 if (pRatio) {
3061 that.size.width = that.size.height * that.aspectRatio;
3066 stop: function(){
3067 var that = $(this).data("ui-resizable"),
3068 o = that.options,
3069 co = that.containerOffset,
3070 cop = that.containerPosition,
3071 ce = that.containerElement,
3072 helper = $(that.helper),
3073 ho = helper.offset(),
3074 w = helper.outerWidth() - that.sizeDiff.width,
3075 h = helper.outerHeight() - that.sizeDiff.height;
3077 if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
3078 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3081 if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
3082 $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3088 $.ui.plugin.add("resizable", "alsoResize", {
3090 start: function () {
3091 var that = $(this).data("ui-resizable"),
3092 o = that.options,
3093 _store = function (exp) {
3094 $(exp).each(function() {
3095 var el = $(this);
3096 el.data("ui-resizable-alsoresize", {
3097 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3098 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
3103 if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
3104 if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3105 else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3106 }else{
3107 _store(o.alsoResize);
3111 resize: function (event, ui) {
3112 var that = $(this).data("ui-resizable"),
3113 o = that.options,
3114 os = that.originalSize,
3115 op = that.originalPosition,
3116 delta = {
3117 height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
3118 top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
3121 _alsoResize = function (exp, c) {
3122 $(exp).each(function() {
3123 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
3124 css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
3126 $.each(css, function (i, prop) {
3127 var sum = (start[prop]||0) + (delta[prop]||0);
3128 if (sum && sum >= 0) {
3129 style[prop] = sum || null;
3133 el.css(style);
3137 if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
3138 $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3139 }else{
3140 _alsoResize(o.alsoResize);
3144 stop: function () {
3145 $(this).removeData("resizable-alsoresize");
3149 $.ui.plugin.add("resizable", "ghost", {
3151 start: function() {
3153 var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
3155 that.ghost = that.originalElement.clone();
3156 that.ghost
3157 .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
3158 .addClass("ui-resizable-ghost")
3159 .addClass(typeof o.ghost === "string" ? o.ghost : "");
3161 that.ghost.appendTo(that.helper);
3165 resize: function(){
3166 var that = $(this).data("ui-resizable");
3167 if (that.ghost) {
3168 that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
3172 stop: function() {
3173 var that = $(this).data("ui-resizable");
3174 if (that.ghost && that.helper) {
3175 that.helper.get(0).removeChild(that.ghost.get(0));
3181 $.ui.plugin.add("resizable", "grid", {
3183 resize: function() {
3184 var that = $(this).data("ui-resizable"),
3185 o = that.options,
3186 cs = that.size,
3187 os = that.originalSize,
3188 op = that.originalPosition,
3189 a = that.axis,
3190 grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
3191 gridX = (grid[0]||1),
3192 gridY = (grid[1]||1),
3193 ox = Math.round((cs.width - os.width) / gridX) * gridX,
3194 oy = Math.round((cs.height - os.height) / gridY) * gridY,
3195 newWidth = os.width + ox,
3196 newHeight = os.height + oy,
3197 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
3198 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
3199 isMinWidth = o.minWidth && (o.minWidth > newWidth),
3200 isMinHeight = o.minHeight && (o.minHeight > newHeight);
3202 o.grid = grid;
3204 if (isMinWidth) {
3205 newWidth = newWidth + gridX;
3207 if (isMinHeight) {
3208 newHeight = newHeight + gridY;
3210 if (isMaxWidth) {
3211 newWidth = newWidth - gridX;
3213 if (isMaxHeight) {
3214 newHeight = newHeight - gridY;
3217 if (/^(se|s|e)$/.test(a)) {
3218 that.size.width = newWidth;
3219 that.size.height = newHeight;
3220 } else if (/^(ne)$/.test(a)) {
3221 that.size.width = newWidth;
3222 that.size.height = newHeight;
3223 that.position.top = op.top - oy;
3224 } else if (/^(sw)$/.test(a)) {
3225 that.size.width = newWidth;
3226 that.size.height = newHeight;
3227 that.position.left = op.left - ox;
3228 } else {
3229 that.size.width = newWidth;
3230 that.size.height = newHeight;
3231 that.position.top = op.top - oy;
3232 that.position.left = op.left - ox;
3238 })(jQuery);
3240 (function( $, undefined ) {
3242 $.widget("ui.selectable", $.ui.mouse, {
3243 version: "1.10.3",
3244 options: {
3245 appendTo: "body",
3246 autoRefresh: true,
3247 distance: 0,
3248 filter: "*",
3249 tolerance: "touch",
3251 // callbacks
3252 selected: null,
3253 selecting: null,
3254 start: null,
3255 stop: null,
3256 unselected: null,
3257 unselecting: null
3259 _create: function() {
3260 var selectees,
3261 that = this;
3263 this.element.addClass("ui-selectable");
3265 this.dragged = false;
3267 // cache selectee children based on filter
3268 this.refresh = function() {
3269 selectees = $(that.options.filter, that.element[0]);
3270 selectees.addClass("ui-selectee");
3271 selectees.each(function() {
3272 var $this = $(this),
3273 pos = $this.offset();
3274 $.data(this, "selectable-item", {
3275 element: this,
3276 $element: $this,
3277 left: pos.left,
3278 top: pos.top,
3279 right: pos.left + $this.outerWidth(),
3280 bottom: pos.top + $this.outerHeight(),
3281 startselected: false,
3282 selected: $this.hasClass("ui-selected"),
3283 selecting: $this.hasClass("ui-selecting"),
3284 unselecting: $this.hasClass("ui-unselecting")
3288 this.refresh();
3290 this.selectees = selectees.addClass("ui-selectee");
3292 this._mouseInit();
3294 this.helper = $("<div class='ui-selectable-helper'></div>");
3297 _destroy: function() {
3298 this.selectees
3299 .removeClass("ui-selectee")
3300 .removeData("selectable-item");
3301 this.element
3302 .removeClass("ui-selectable ui-selectable-disabled");
3303 this._mouseDestroy();
3306 _mouseStart: function(event) {
3307 var that = this,
3308 options = this.options;
3310 this.opos = [event.pageX, event.pageY];
3312 if (this.options.disabled) {
3313 return;
3316 this.selectees = $(options.filter, this.element[0]);
3318 this._trigger("start", event);
3320 $(options.appendTo).append(this.helper);
3321 // position helper (lasso)
3322 this.helper.css({
3323 "left": event.pageX,
3324 "top": event.pageY,
3325 "width": 0,
3326 "height": 0
3329 if (options.autoRefresh) {
3330 this.refresh();
3333 this.selectees.filter(".ui-selected").each(function() {
3334 var selectee = $.data(this, "selectable-item");
3335 selectee.startselected = true;
3336 if (!event.metaKey && !event.ctrlKey) {
3337 selectee.$element.removeClass("ui-selected");
3338 selectee.selected = false;
3339 selectee.$element.addClass("ui-unselecting");
3340 selectee.unselecting = true;
3341 // selectable UNSELECTING callback
3342 that._trigger("unselecting", event, {
3343 unselecting: selectee.element
3348 $(event.target).parents().addBack().each(function() {
3349 var doSelect,
3350 selectee = $.data(this, "selectable-item");
3351 if (selectee) {
3352 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
3353 selectee.$element
3354 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
3355 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
3356 selectee.unselecting = !doSelect;
3357 selectee.selecting = doSelect;
3358 selectee.selected = doSelect;
3359 // selectable (UN)SELECTING callback
3360 if (doSelect) {
3361 that._trigger("selecting", event, {
3362 selecting: selectee.element
3364 } else {
3365 that._trigger("unselecting", event, {
3366 unselecting: selectee.element
3369 return false;
3375 _mouseDrag: function(event) {
3377 this.dragged = true;
3379 if (this.options.disabled) {
3380 return;
3383 var tmp,
3384 that = this,
3385 options = this.options,
3386 x1 = this.opos[0],
3387 y1 = this.opos[1],
3388 x2 = event.pageX,
3389 y2 = event.pageY;
3391 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
3392 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
3393 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
3395 this.selectees.each(function() {
3396 var selectee = $.data(this, "selectable-item"),
3397 hit = false;
3399 //prevent helper from being selected if appendTo: selectable
3400 if (!selectee || selectee.element === that.element[0]) {
3401 return;
3404 if (options.tolerance === "touch") {
3405 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
3406 } else if (options.tolerance === "fit") {
3407 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
3410 if (hit) {
3411 // SELECT
3412 if (selectee.selected) {
3413 selectee.$element.removeClass("ui-selected");
3414 selectee.selected = false;
3416 if (selectee.unselecting) {
3417 selectee.$element.removeClass("ui-unselecting");
3418 selectee.unselecting = false;
3420 if (!selectee.selecting) {
3421 selectee.$element.addClass("ui-selecting");
3422 selectee.selecting = true;
3423 // selectable SELECTING callback
3424 that._trigger("selecting", event, {
3425 selecting: selectee.element
3428 } else {
3429 // UNSELECT
3430 if (selectee.selecting) {
3431 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
3432 selectee.$element.removeClass("ui-selecting");
3433 selectee.selecting = false;
3434 selectee.$element.addClass("ui-selected");
3435 selectee.selected = true;
3436 } else {
3437 selectee.$element.removeClass("ui-selecting");
3438 selectee.selecting = false;
3439 if (selectee.startselected) {
3440 selectee.$element.addClass("ui-unselecting");
3441 selectee.unselecting = true;
3443 // selectable UNSELECTING callback
3444 that._trigger("unselecting", event, {
3445 unselecting: selectee.element
3449 if (selectee.selected) {
3450 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
3451 selectee.$element.removeClass("ui-selected");
3452 selectee.selected = false;
3454 selectee.$element.addClass("ui-unselecting");
3455 selectee.unselecting = true;
3456 // selectable UNSELECTING callback
3457 that._trigger("unselecting", event, {
3458 unselecting: selectee.element
3465 return false;
3468 _mouseStop: function(event) {
3469 var that = this;
3471 this.dragged = false;
3473 $(".ui-unselecting", this.element[0]).each(function() {
3474 var selectee = $.data(this, "selectable-item");
3475 selectee.$element.removeClass("ui-unselecting");
3476 selectee.unselecting = false;
3477 selectee.startselected = false;
3478 that._trigger("unselected", event, {
3479 unselected: selectee.element
3482 $(".ui-selecting", this.element[0]).each(function() {
3483 var selectee = $.data(this, "selectable-item");
3484 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
3485 selectee.selecting = false;
3486 selectee.selected = true;
3487 selectee.startselected = true;
3488 that._trigger("selected", event, {
3489 selected: selectee.element
3492 this._trigger("stop", event);
3494 this.helper.remove();
3496 return false;
3501 })(jQuery);
3503 (function( $, undefined ) {
3505 /*jshint loopfunc: true */
3507 function isOverAxis( x, reference, size ) {
3508 return ( x > reference ) && ( x < ( reference + size ) );
3511 function isFloating(item) {
3512 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
3515 $.widget("ui.sortable", $.ui.mouse, {
3516 version: "1.10.3",
3517 widgetEventPrefix: "sort",
3518 ready: false,
3519 options: {
3520 appendTo: "parent",
3521 axis: false,
3522 connectWith: false,
3523 containment: false,
3524 cursor: "auto",
3525 cursorAt: false,
3526 dropOnEmpty: true,
3527 forcePlaceholderSize: false,
3528 forceHelperSize: false,
3529 grid: false,
3530 handle: false,
3531 helper: "original",
3532 items: "> *",
3533 opacity: false,
3534 placeholder: false,
3535 revert: false,
3536 scroll: true,
3537 scrollSensitivity: 20,
3538 scrollSpeed: 20,
3539 scope: "default",
3540 tolerance: "intersect",
3541 zIndex: 1000,
3543 // callbacks
3544 activate: null,
3545 beforeStop: null,
3546 change: null,
3547 deactivate: null,
3548 out: null,
3549 over: null,
3550 receive: null,
3551 remove: null,
3552 sort: null,
3553 start: null,
3554 stop: null,
3555 update: null
3557 _create: function() {
3559 var o = this.options;
3560 this.containerCache = {};
3561 this.element.addClass("ui-sortable");
3563 //Get the items
3564 this.refresh();
3566 //Let's determine if the items are being displayed horizontally
3567 this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
3569 //Let's determine the parent's offset
3570 this.offset = this.element.offset();
3572 //Initialize mouse events for interaction
3573 this._mouseInit();
3575 //We're ready to go
3576 this.ready = true;
3580 _destroy: function() {
3581 this.element
3582 .removeClass("ui-sortable ui-sortable-disabled");
3583 this._mouseDestroy();
3585 for ( var i = this.items.length - 1; i >= 0; i-- ) {
3586 this.items[i].item.removeData(this.widgetName + "-item");
3589 return this;
3592 _setOption: function(key, value){
3593 if ( key === "disabled" ) {
3594 this.options[ key ] = value;
3596 this.widget().toggleClass( "ui-sortable-disabled", !!value );
3597 } else {
3598 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
3599 $.Widget.prototype._setOption.apply(this, arguments);
3603 _mouseCapture: function(event, overrideHandle) {
3604 var currentItem = null,
3605 validHandle = false,
3606 that = this;
3608 if (this.reverting) {
3609 return false;
3612 if(this.options.disabled || this.options.type === "static") {
3613 return false;
3616 //We have to refresh the items data once first
3617 this._refreshItems(event);
3619 //Find out if the clicked node (or one of its parents) is a actual item in this.items
3620 $(event.target).parents().each(function() {
3621 if($.data(this, that.widgetName + "-item") === that) {
3622 currentItem = $(this);
3623 return false;
3626 if($.data(event.target, that.widgetName + "-item") === that) {
3627 currentItem = $(event.target);
3630 if(!currentItem) {
3631 return false;
3633 if(this.options.handle && !overrideHandle) {
3634 $(this.options.handle, currentItem).find("*").addBack().each(function() {
3635 if(this === event.target) {
3636 validHandle = true;
3639 if(!validHandle) {
3640 return false;
3644 this.currentItem = currentItem;
3645 this._removeCurrentsFromItems();
3646 return true;
3650 _mouseStart: function(event, overrideHandle, noActivation) {
3652 var i, body,
3653 o = this.options;
3655 this.currentContainer = this;
3657 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
3658 this.refreshPositions();
3660 //Create and append the visible helper
3661 this.helper = this._createHelper(event);
3663 //Cache the helper size
3664 this._cacheHelperProportions();
3667 * - Position generation -
3668 * This block generates everything position related - it's the core of draggables.
3671 //Cache the margins of the original element
3672 this._cacheMargins();
3674 //Get the next scrolling parent
3675 this.scrollParent = this.helper.scrollParent();
3677 //The element's absolute position on the page minus margins
3678 this.offset = this.currentItem.offset();
3679 this.offset = {
3680 top: this.offset.top - this.margins.top,
3681 left: this.offset.left - this.margins.left
3684 $.extend(this.offset, {
3685 click: { //Where the click happened, relative to the element
3686 left: event.pageX - this.offset.left,
3687 top: event.pageY - this.offset.top
3689 parent: this._getParentOffset(),
3690 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
3693 // Only after we got the offset, we can change the helper's position to absolute
3694 // TODO: Still need to figure out a way to make relative sorting possible
3695 this.helper.css("position", "absolute");
3696 this.cssPosition = this.helper.css("position");
3698 //Generate the original position
3699 this.originalPosition = this._generatePosition(event);
3700 this.originalPageX = event.pageX;
3701 this.originalPageY = event.pageY;
3703 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
3704 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
3706 //Cache the former DOM position
3707 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
3709 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
3710 if(this.helper[0] !== this.currentItem[0]) {
3711 this.currentItem.hide();
3714 //Create the placeholder
3715 this._createPlaceholder();
3717 //Set a containment if given in the options
3718 if(o.containment) {
3719 this._setContainment();
3722 if( o.cursor && o.cursor !== "auto" ) { // cursor option
3723 body = this.document.find( "body" );
3725 // support: IE
3726 this.storedCursor = body.css( "cursor" );
3727 body.css( "cursor", o.cursor );
3729 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
3732 if(o.opacity) { // opacity option
3733 if (this.helper.css("opacity")) {
3734 this._storedOpacity = this.helper.css("opacity");
3736 this.helper.css("opacity", o.opacity);
3739 if(o.zIndex) { // zIndex option
3740 if (this.helper.css("zIndex")) {
3741 this._storedZIndex = this.helper.css("zIndex");
3743 this.helper.css("zIndex", o.zIndex);
3746 //Prepare scrolling
3747 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3748 this.overflowOffset = this.scrollParent.offset();
3751 //Call callbacks
3752 this._trigger("start", event, this._uiHash());
3754 //Recache the helper size
3755 if(!this._preserveHelperProportions) {
3756 this._cacheHelperProportions();
3760 //Post "activate" events to possible containers
3761 if( !noActivation ) {
3762 for ( i = this.containers.length - 1; i >= 0; i-- ) {
3763 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
3767 //Prepare possible droppables
3768 if($.ui.ddmanager) {
3769 $.ui.ddmanager.current = this;
3772 if ($.ui.ddmanager && !o.dropBehaviour) {
3773 $.ui.ddmanager.prepareOffsets(this, event);
3776 this.dragging = true;
3778 this.helper.addClass("ui-sortable-helper");
3779 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
3780 return true;
3784 _mouseDrag: function(event) {
3785 var i, item, itemElement, intersection,
3786 o = this.options,
3787 scrolled = false;
3789 //Compute the helpers position
3790 this.position = this._generatePosition(event);
3791 this.positionAbs = this._convertPositionTo("absolute");
3793 if (!this.lastPositionAbs) {
3794 this.lastPositionAbs = this.positionAbs;
3797 //Do scrolling
3798 if(this.options.scroll) {
3799 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
3801 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
3802 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
3803 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
3804 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
3807 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
3808 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
3809 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
3810 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
3813 } else {
3815 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
3816 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
3817 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
3818 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
3821 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
3822 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
3823 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
3824 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
3829 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
3830 $.ui.ddmanager.prepareOffsets(this, event);
3834 //Regenerate the absolute position used for position checks
3835 this.positionAbs = this._convertPositionTo("absolute");
3837 //Set the helper position
3838 if(!this.options.axis || this.options.axis !== "y") {
3839 this.helper[0].style.left = this.position.left+"px";
3841 if(!this.options.axis || this.options.axis !== "x") {
3842 this.helper[0].style.top = this.position.top+"px";
3845 //Rearrange
3846 for (i = this.items.length - 1; i >= 0; i--) {
3848 //Cache variables and intersection, continue if no intersection
3849 item = this.items[i];
3850 itemElement = item.item[0];
3851 intersection = this._intersectsWithPointer(item);
3852 if (!intersection) {
3853 continue;
3856 // Only put the placeholder inside the current Container, skip all
3857 // items form other containers. This works because when moving
3858 // an item from one container to another the
3859 // currentContainer is switched before the placeholder is moved.
3861 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
3862 // beetween the outer and inner container.
3863 if (item.instance !== this.currentContainer) {
3864 continue;
3867 // cannot intersect with itself
3868 // no useless actions that have been done before
3869 // no action if the item moved is the parent of the item checked
3870 if (itemElement !== this.currentItem[0] &&
3871 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
3872 !$.contains(this.placeholder[0], itemElement) &&
3873 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
3876 this.direction = intersection === 1 ? "down" : "up";
3878 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
3879 this._rearrange(event, item);
3880 } else {
3881 break;
3884 this._trigger("change", event, this._uiHash());
3885 break;
3889 //Post events to containers
3890 this._contactContainers(event);
3892 //Interconnect with droppables
3893 if($.ui.ddmanager) {
3894 $.ui.ddmanager.drag(this, event);
3897 //Call callbacks
3898 this._trigger("sort", event, this._uiHash());
3900 this.lastPositionAbs = this.positionAbs;
3901 return false;
3905 _mouseStop: function(event, noPropagation) {
3907 if(!event) {
3908 return;
3911 //If we are using droppables, inform the manager about the drop
3912 if ($.ui.ddmanager && !this.options.dropBehaviour) {
3913 $.ui.ddmanager.drop(this, event);
3916 if(this.options.revert) {
3917 var that = this,
3918 cur = this.placeholder.offset(),
3919 axis = this.options.axis,
3920 animation = {};
3922 if ( !axis || axis === "x" ) {
3923 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
3925 if ( !axis || axis === "y" ) {
3926 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
3928 this.reverting = true;
3929 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
3930 that._clear(event);
3932 } else {
3933 this._clear(event, noPropagation);
3936 return false;
3940 cancel: function() {
3942 if(this.dragging) {
3944 this._mouseUp({ target: null });
3946 if(this.options.helper === "original") {
3947 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3948 } else {
3949 this.currentItem.show();
3952 //Post deactivating events to containers
3953 for (var i = this.containers.length - 1; i >= 0; i--){
3954 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
3955 if(this.containers[i].containerCache.over) {
3956 this.containers[i]._trigger("out", null, this._uiHash(this));
3957 this.containers[i].containerCache.over = 0;
3963 if (this.placeholder) {
3964 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3965 if(this.placeholder[0].parentNode) {
3966 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3968 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
3969 this.helper.remove();
3972 $.extend(this, {
3973 helper: null,
3974 dragging: false,
3975 reverting: false,
3976 _noFinalSort: null
3979 if(this.domPosition.prev) {
3980 $(this.domPosition.prev).after(this.currentItem);
3981 } else {
3982 $(this.domPosition.parent).prepend(this.currentItem);
3986 return this;
3990 serialize: function(o) {
3992 var items = this._getItemsAsjQuery(o && o.connected),
3993 str = [];
3994 o = o || {};
3996 $(items).each(function() {
3997 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
3998 if (res) {
3999 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4003 if(!str.length && o.key) {
4004 str.push(o.key + "=");
4007 return str.join("&");
4011 toArray: function(o) {
4013 var items = this._getItemsAsjQuery(o && o.connected),
4014 ret = [];
4016 o = o || {};
4018 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
4019 return ret;
4023 /* Be careful with the following core functions */
4024 _intersectsWith: function(item) {
4026 var x1 = this.positionAbs.left,
4027 x2 = x1 + this.helperProportions.width,
4028 y1 = this.positionAbs.top,
4029 y2 = y1 + this.helperProportions.height,
4030 l = item.left,
4031 r = l + item.width,
4032 t = item.top,
4033 b = t + item.height,
4034 dyClick = this.offset.click.top,
4035 dxClick = this.offset.click.left,
4036 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
4037 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
4038 isOverElement = isOverElementHeight && isOverElementWidth;
4040 if ( this.options.tolerance === "pointer" ||
4041 this.options.forcePointerForContainers ||
4042 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
4044 return isOverElement;
4045 } else {
4047 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
4048 x2 - (this.helperProportions.width / 2) < r && // Left Half
4049 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
4050 y2 - (this.helperProportions.height / 2) < b ); // Top Half
4055 _intersectsWithPointer: function(item) {
4057 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4058 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4059 isOverElement = isOverElementHeight && isOverElementWidth,
4060 verticalDirection = this._getDragVerticalDirection(),
4061 horizontalDirection = this._getDragHorizontalDirection();
4063 if (!isOverElement) {
4064 return false;
4067 return this.floating ?
4068 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
4069 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
4073 _intersectsWithSides: function(item) {
4075 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4076 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4077 verticalDirection = this._getDragVerticalDirection(),
4078 horizontalDirection = this._getDragHorizontalDirection();
4080 if (this.floating && horizontalDirection) {
4081 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
4082 } else {
4083 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
4088 _getDragVerticalDirection: function() {
4089 var delta = this.positionAbs.top - this.lastPositionAbs.top;
4090 return delta !== 0 && (delta > 0 ? "down" : "up");
4093 _getDragHorizontalDirection: function() {
4094 var delta = this.positionAbs.left - this.lastPositionAbs.left;
4095 return delta !== 0 && (delta > 0 ? "right" : "left");
4098 refresh: function(event) {
4099 this._refreshItems(event);
4100 this.refreshPositions();
4101 return this;
4104 _connectWith: function() {
4105 var options = this.options;
4106 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
4109 _getItemsAsjQuery: function(connected) {
4111 var i, j, cur, inst,
4112 items = [],
4113 queries = [],
4114 connectWith = this._connectWith();
4116 if(connectWith && connected) {
4117 for (i = connectWith.length - 1; i >= 0; i--){
4118 cur = $(connectWith[i]);
4119 for ( j = cur.length - 1; j >= 0; j--){
4120 inst = $.data(cur[j], this.widgetFullName);
4121 if(inst && inst !== this && !inst.options.disabled) {
4122 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
4128 queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
4130 for (i = queries.length - 1; i >= 0; i--){
4131 queries[i][0].each(function() {
4132 items.push(this);
4136 return $(items);
4140 _removeCurrentsFromItems: function() {
4142 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
4144 this.items = $.grep(this.items, function (item) {
4145 for (var j=0; j < list.length; j++) {
4146 if(list[j] === item.item[0]) {
4147 return false;
4150 return true;
4155 _refreshItems: function(event) {
4157 this.items = [];
4158 this.containers = [this];
4160 var i, j, cur, inst, targetData, _queries, item, queriesLength,
4161 items = this.items,
4162 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
4163 connectWith = this._connectWith();
4165 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
4166 for (i = connectWith.length - 1; i >= 0; i--){
4167 cur = $(connectWith[i]);
4168 for (j = cur.length - 1; j >= 0; j--){
4169 inst = $.data(cur[j], this.widgetFullName);
4170 if(inst && inst !== this && !inst.options.disabled) {
4171 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4172 this.containers.push(inst);
4178 for (i = queries.length - 1; i >= 0; i--) {
4179 targetData = queries[i][1];
4180 _queries = queries[i][0];
4182 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4183 item = $(_queries[j]);
4185 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
4187 items.push({
4188 item: item,
4189 instance: targetData,
4190 width: 0, height: 0,
4191 left: 0, top: 0
4198 refreshPositions: function(fast) {
4200 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4201 if(this.offsetParent && this.helper) {
4202 this.offset.parent = this._getParentOffset();
4205 var i, item, t, p;
4207 for (i = this.items.length - 1; i >= 0; i--){
4208 item = this.items[i];
4210 //We ignore calculating positions of all connected containers when we're not over them
4211 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
4212 continue;
4215 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4217 if (!fast) {
4218 item.width = t.outerWidth();
4219 item.height = t.outerHeight();
4222 p = t.offset();
4223 item.left = p.left;
4224 item.top = p.top;
4227 if(this.options.custom && this.options.custom.refreshContainers) {
4228 this.options.custom.refreshContainers.call(this);
4229 } else {
4230 for (i = this.containers.length - 1; i >= 0; i--){
4231 p = this.containers[i].element.offset();
4232 this.containers[i].containerCache.left = p.left;
4233 this.containers[i].containerCache.top = p.top;
4234 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4235 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4239 return this;
4242 _createPlaceholder: function(that) {
4243 that = that || this;
4244 var className,
4245 o = that.options;
4247 if(!o.placeholder || o.placeholder.constructor === String) {
4248 className = o.placeholder;
4249 o.placeholder = {
4250 element: function() {
4252 var nodeName = that.currentItem[0].nodeName.toLowerCase(),
4253 element = $( "<" + nodeName + ">", that.document[0] )
4254 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
4255 .removeClass("ui-sortable-helper");
4257 if ( nodeName === "tr" ) {
4258 that.currentItem.children().each(function() {
4259 $( "<td>&#160;</td>", that.document[0] )
4260 .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
4261 .appendTo( element );
4263 } else if ( nodeName === "img" ) {
4264 element.attr( "src", that.currentItem.attr( "src" ) );
4267 if ( !className ) {
4268 element.css( "visibility", "hidden" );
4271 return element;
4273 update: function(container, p) {
4275 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4276 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4277 if(className && !o.forcePlaceholderSize) {
4278 return;
4281 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4282 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
4283 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
4288 //Create the placeholder
4289 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
4291 //Append it after the actual current item
4292 that.currentItem.after(that.placeholder);
4294 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
4295 o.placeholder.update(that, that.placeholder);
4299 _contactContainers: function(event) {
4300 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
4301 innermostContainer = null,
4302 innermostIndex = null;
4304 // get innermost container that intersects with item
4305 for (i = this.containers.length - 1; i >= 0; i--) {
4307 // never consider a container that's located within the item itself
4308 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
4309 continue;
4312 if(this._intersectsWith(this.containers[i].containerCache)) {
4314 // if we've already found a container and it's more "inner" than this, then continue
4315 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
4316 continue;
4319 innermostContainer = this.containers[i];
4320 innermostIndex = i;
4322 } else {
4323 // container doesn't intersect. trigger "out" event if necessary
4324 if(this.containers[i].containerCache.over) {
4325 this.containers[i]._trigger("out", event, this._uiHash(this));
4326 this.containers[i].containerCache.over = 0;
4332 // if no intersecting containers found, return
4333 if(!innermostContainer) {
4334 return;
4337 // move the item into the container if it's not there already
4338 if(this.containers.length === 1) {
4339 if (!this.containers[innermostIndex].containerCache.over) {
4340 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4341 this.containers[innermostIndex].containerCache.over = 1;
4343 } else {
4345 //When entering a new container, we will find the item with the least distance and append our item near it
4346 dist = 10000;
4347 itemWithLeastDistance = null;
4348 floating = innermostContainer.floating || isFloating(this.currentItem);
4349 posProperty = floating ? "left" : "top";
4350 sizeProperty = floating ? "width" : "height";
4351 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
4352 for (j = this.items.length - 1; j >= 0; j--) {
4353 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
4354 continue;
4356 if(this.items[j].item[0] === this.currentItem[0]) {
4357 continue;
4359 if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
4360 continue;
4362 cur = this.items[j].item.offset()[posProperty];
4363 nearBottom = false;
4364 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
4365 nearBottom = true;
4366 cur += this.items[j][sizeProperty];
4369 if(Math.abs(cur - base) < dist) {
4370 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
4371 this.direction = nearBottom ? "up": "down";
4375 //Check if dropOnEmpty is enabled
4376 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
4377 return;
4380 if(this.currentContainer === this.containers[innermostIndex]) {
4381 return;
4384 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
4385 this._trigger("change", event, this._uiHash());
4386 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
4387 this.currentContainer = this.containers[innermostIndex];
4389 //Update the placeholder
4390 this.options.placeholder.update(this.currentContainer, this.placeholder);
4392 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
4393 this.containers[innermostIndex].containerCache.over = 1;
4399 _createHelper: function(event) {
4401 var o = this.options,
4402 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
4404 //Add the helper to the DOM if that didn't happen already
4405 if(!helper.parents("body").length) {
4406 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
4409 if(helper[0] === this.currentItem[0]) {
4410 this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
4413 if(!helper[0].style.width || o.forceHelperSize) {
4414 helper.width(this.currentItem.width());
4416 if(!helper[0].style.height || o.forceHelperSize) {
4417 helper.height(this.currentItem.height());
4420 return helper;
4424 _adjustOffsetFromHelper: function(obj) {
4425 if (typeof obj === "string") {
4426 obj = obj.split(" ");
4428 if ($.isArray(obj)) {
4429 obj = {left: +obj[0], top: +obj[1] || 0};
4431 if ("left" in obj) {
4432 this.offset.click.left = obj.left + this.margins.left;
4434 if ("right" in obj) {
4435 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
4437 if ("top" in obj) {
4438 this.offset.click.top = obj.top + this.margins.top;
4440 if ("bottom" in obj) {
4441 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
4445 _getParentOffset: function() {
4448 //Get the offsetParent and cache its position
4449 this.offsetParent = this.helper.offsetParent();
4450 var po = this.offsetParent.offset();
4452 // This is a special case where we need to modify a offset calculated on start, since the following happened:
4453 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
4454 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
4455 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
4456 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
4457 po.left += this.scrollParent.scrollLeft();
4458 po.top += this.scrollParent.scrollTop();
4461 // This needs to be actually done for all browsers, since pageX/pageY includes this information
4462 // with an ugly IE fix
4463 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
4464 po = { top: 0, left: 0 };
4467 return {
4468 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
4469 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
4474 _getRelativeOffset: function() {
4476 if(this.cssPosition === "relative") {
4477 var p = this.currentItem.position();
4478 return {
4479 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
4480 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
4482 } else {
4483 return { top: 0, left: 0 };
4488 _cacheMargins: function() {
4489 this.margins = {
4490 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
4491 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
4495 _cacheHelperProportions: function() {
4496 this.helperProportions = {
4497 width: this.helper.outerWidth(),
4498 height: this.helper.outerHeight()
4502 _setContainment: function() {
4504 var ce, co, over,
4505 o = this.options;
4506 if(o.containment === "parent") {
4507 o.containment = this.helper[0].parentNode;
4509 if(o.containment === "document" || o.containment === "window") {
4510 this.containment = [
4511 0 - this.offset.relative.left - this.offset.parent.left,
4512 0 - this.offset.relative.top - this.offset.parent.top,
4513 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
4514 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
4518 if(!(/^(document|window|parent)$/).test(o.containment)) {
4519 ce = $(o.containment)[0];
4520 co = $(o.containment).offset();
4521 over = ($(ce).css("overflow") !== "hidden");
4523 this.containment = [
4524 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
4525 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
4526 co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
4527 co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
4533 _convertPositionTo: function(d, pos) {
4535 if(!pos) {
4536 pos = this.position;
4538 var mod = d === "absolute" ? 1 : -1,
4539 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
4540 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4542 return {
4543 top: (
4544 pos.top + // The absolute mouse position
4545 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4546 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
4547 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
4549 left: (
4550 pos.left + // The absolute mouse position
4551 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
4552 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
4553 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
4559 _generatePosition: function(event) {
4561 var top, left,
4562 o = this.options,
4563 pageX = event.pageX,
4564 pageY = event.pageY,
4565 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
4567 // This is another very weird special case that only happens for relative elements:
4568 // 1. If the css position is relative
4569 // 2. and the scroll parent is the document or similar to the offset parent
4570 // we have to refresh the relative offset during the scroll so there are no jumps
4571 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
4572 this.offset.relative = this._getRelativeOffset();
4576 * - Position constraining -
4577 * Constrain the position to a mix of grid, containment.
4580 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
4582 if(this.containment) {
4583 if(event.pageX - this.offset.click.left < this.containment[0]) {
4584 pageX = this.containment[0] + this.offset.click.left;
4586 if(event.pageY - this.offset.click.top < this.containment[1]) {
4587 pageY = this.containment[1] + this.offset.click.top;
4589 if(event.pageX - this.offset.click.left > this.containment[2]) {
4590 pageX = this.containment[2] + this.offset.click.left;
4592 if(event.pageY - this.offset.click.top > this.containment[3]) {
4593 pageY = this.containment[3] + this.offset.click.top;
4597 if(o.grid) {
4598 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
4599 pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
4601 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
4602 pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
4607 return {
4608 top: (
4609 pageY - // The absolute mouse position
4610 this.offset.click.top - // Click offset (relative to the element)
4611 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
4612 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
4613 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
4615 left: (
4616 pageX - // The absolute mouse position
4617 this.offset.click.left - // Click offset (relative to the element)
4618 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
4619 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
4620 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
4626 _rearrange: function(event, i, a, hardRefresh) {
4628 a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
4630 //Various things done here to improve the performance:
4631 // 1. we create a setTimeout, that calls refreshPositions
4632 // 2. on the instance, we have a counter variable, that get's higher after every append
4633 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
4634 // 4. this lets only the last addition to the timeout stack through
4635 this.counter = this.counter ? ++this.counter : 1;
4636 var counter = this.counter;
4638 this._delay(function() {
4639 if(counter === this.counter) {
4640 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
4646 _clear: function(event, noPropagation) {
4648 this.reverting = false;
4649 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
4650 // everything else normalized again
4651 var i,
4652 delayedTriggers = [];
4654 // We first have to update the dom position of the actual currentItem
4655 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
4656 if(!this._noFinalSort && this.currentItem.parent().length) {
4657 this.placeholder.before(this.currentItem);
4659 this._noFinalSort = null;
4661 if(this.helper[0] === this.currentItem[0]) {
4662 for(i in this._storedCSS) {
4663 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
4664 this._storedCSS[i] = "";
4667 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4668 } else {
4669 this.currentItem.show();
4672 if(this.fromOutside && !noPropagation) {
4673 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
4675 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
4676 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
4679 // Check if the items Container has Changed and trigger appropriate
4680 // events.
4681 if (this !== this.currentContainer) {
4682 if(!noPropagation) {
4683 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
4684 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4685 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
4690 //Post events to containers
4691 for (i = this.containers.length - 1; i >= 0; i--){
4692 if(!noPropagation) {
4693 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4695 if(this.containers[i].containerCache.over) {
4696 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
4697 this.containers[i].containerCache.over = 0;
4701 //Do what was originally in plugins
4702 if ( this.storedCursor ) {
4703 this.document.find( "body" ).css( "cursor", this.storedCursor );
4704 this.storedStylesheet.remove();
4706 if(this._storedOpacity) {
4707 this.helper.css("opacity", this._storedOpacity);
4709 if(this._storedZIndex) {
4710 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
4713 this.dragging = false;
4714 if(this.cancelHelperRemoval) {
4715 if(!noPropagation) {
4716 this._trigger("beforeStop", event, this._uiHash());
4717 for (i=0; i < delayedTriggers.length; i++) {
4718 delayedTriggers[i].call(this, event);
4719 } //Trigger all delayed events
4720 this._trigger("stop", event, this._uiHash());
4723 this.fromOutside = false;
4724 return false;
4727 if(!noPropagation) {
4728 this._trigger("beforeStop", event, this._uiHash());
4731 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4732 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4734 if(this.helper[0] !== this.currentItem[0]) {
4735 this.helper.remove();
4737 this.helper = null;
4739 if(!noPropagation) {
4740 for (i=0; i < delayedTriggers.length; i++) {
4741 delayedTriggers[i].call(this, event);
4742 } //Trigger all delayed events
4743 this._trigger("stop", event, this._uiHash());
4746 this.fromOutside = false;
4747 return true;
4751 _trigger: function() {
4752 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
4753 this.cancel();
4757 _uiHash: function(_inst) {
4758 var inst = _inst || this;
4759 return {
4760 helper: inst.helper,
4761 placeholder: inst.placeholder || $([]),
4762 position: inst.position,
4763 originalPosition: inst.originalPosition,
4764 offset: inst.positionAbs,
4765 item: inst.currentItem,
4766 sender: _inst ? _inst.element : null
4772 })(jQuery);
4774 (function($, undefined) {
4776 var dataSpace = "ui-effects-";
4778 $.effects = {
4779 effect: {}
4783 * jQuery Color Animations v2.1.2
4784 * https://github.com/jquery/jquery-color
4786 * Copyright 2013 jQuery Foundation and other contributors
4787 * Released under the MIT license.
4788 * http://jquery.org/license
4790 * Date: Wed Jan 16 08:47:09 2013 -0600
4792 (function( jQuery, undefined ) {
4794 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
4796 // plusequals test for += 100 -= 100
4797 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
4798 // a set of RE's that can match strings and generate color tuples.
4799 stringParsers = [{
4800 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4801 parse: function( execResult ) {
4802 return [
4803 execResult[ 1 ],
4804 execResult[ 2 ],
4805 execResult[ 3 ],
4806 execResult[ 4 ]
4809 }, {
4810 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4811 parse: function( execResult ) {
4812 return [
4813 execResult[ 1 ] * 2.55,
4814 execResult[ 2 ] * 2.55,
4815 execResult[ 3 ] * 2.55,
4816 execResult[ 4 ]
4819 }, {
4820 // this regex ignores A-F because it's compared against an already lowercased string
4821 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
4822 parse: function( execResult ) {
4823 return [
4824 parseInt( execResult[ 1 ], 16 ),
4825 parseInt( execResult[ 2 ], 16 ),
4826 parseInt( execResult[ 3 ], 16 )
4829 }, {
4830 // this regex ignores A-F because it's compared against an already lowercased string
4831 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
4832 parse: function( execResult ) {
4833 return [
4834 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
4835 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
4836 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
4839 }, {
4840 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
4841 space: "hsla",
4842 parse: function( execResult ) {
4843 return [
4844 execResult[ 1 ],
4845 execResult[ 2 ] / 100,
4846 execResult[ 3 ] / 100,
4847 execResult[ 4 ]
4852 // jQuery.Color( )
4853 color = jQuery.Color = function( color, green, blue, alpha ) {
4854 return new jQuery.Color.fn.parse( color, green, blue, alpha );
4856 spaces = {
4857 rgba: {
4858 props: {
4859 red: {
4860 idx: 0,
4861 type: "byte"
4863 green: {
4864 idx: 1,
4865 type: "byte"
4867 blue: {
4868 idx: 2,
4869 type: "byte"
4874 hsla: {
4875 props: {
4876 hue: {
4877 idx: 0,
4878 type: "degrees"
4880 saturation: {
4881 idx: 1,
4882 type: "percent"
4884 lightness: {
4885 idx: 2,
4886 type: "percent"
4891 propTypes = {
4892 "byte": {
4893 floor: true,
4894 max: 255
4896 "percent": {
4897 max: 1
4899 "degrees": {
4900 mod: 360,
4901 floor: true
4904 support = color.support = {},
4906 // element for support tests
4907 supportElem = jQuery( "<p>" )[ 0 ],
4909 // colors = jQuery.Color.names
4910 colors,
4912 // local aliases of functions called often
4913 each = jQuery.each;
4915 // determine rgba support immediately
4916 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
4917 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
4919 // define cache name and alpha properties
4920 // for rgba and hsla spaces
4921 each( spaces, function( spaceName, space ) {
4922 space.cache = "_" + spaceName;
4923 space.props.alpha = {
4924 idx: 3,
4925 type: "percent",
4926 def: 1
4930 function clamp( value, prop, allowEmpty ) {
4931 var type = propTypes[ prop.type ] || {};
4933 if ( value == null ) {
4934 return (allowEmpty || !prop.def) ? null : prop.def;
4937 // ~~ is an short way of doing floor for positive numbers
4938 value = type.floor ? ~~value : parseFloat( value );
4940 // IE will pass in empty strings as value for alpha,
4941 // which will hit this case
4942 if ( isNaN( value ) ) {
4943 return prop.def;
4946 if ( type.mod ) {
4947 // we add mod before modding to make sure that negatives values
4948 // get converted properly: -10 -> 350
4949 return (value + type.mod) % type.mod;
4952 // for now all property types without mod have min and max
4953 return 0 > value ? 0 : type.max < value ? type.max : value;
4956 function stringParse( string ) {
4957 var inst = color(),
4958 rgba = inst._rgba = [];
4960 string = string.toLowerCase();
4962 each( stringParsers, function( i, parser ) {
4963 var parsed,
4964 match = parser.re.exec( string ),
4965 values = match && parser.parse( match ),
4966 spaceName = parser.space || "rgba";
4968 if ( values ) {
4969 parsed = inst[ spaceName ]( values );
4971 // if this was an rgba parse the assignment might happen twice
4972 // oh well....
4973 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
4974 rgba = inst._rgba = parsed._rgba;
4976 // exit each( stringParsers ) here because we matched
4977 return false;
4981 // Found a stringParser that handled it
4982 if ( rgba.length ) {
4984 // if this came from a parsed string, force "transparent" when alpha is 0
4985 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
4986 if ( rgba.join() === "0,0,0,0" ) {
4987 jQuery.extend( rgba, colors.transparent );
4989 return inst;
4992 // named colors
4993 return colors[ string ];
4996 color.fn = jQuery.extend( color.prototype, {
4997 parse: function( red, green, blue, alpha ) {
4998 if ( red === undefined ) {
4999 this._rgba = [ null, null, null, null ];
5000 return this;
5002 if ( red.jquery || red.nodeType ) {
5003 red = jQuery( red ).css( green );
5004 green = undefined;
5007 var inst = this,
5008 type = jQuery.type( red ),
5009 rgba = this._rgba = [];
5011 // more than 1 argument specified - assume ( red, green, blue, alpha )
5012 if ( green !== undefined ) {
5013 red = [ red, green, blue, alpha ];
5014 type = "array";
5017 if ( type === "string" ) {
5018 return this.parse( stringParse( red ) || colors._default );
5021 if ( type === "array" ) {
5022 each( spaces.rgba.props, function( key, prop ) {
5023 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
5025 return this;
5028 if ( type === "object" ) {
5029 if ( red instanceof color ) {
5030 each( spaces, function( spaceName, space ) {
5031 if ( red[ space.cache ] ) {
5032 inst[ space.cache ] = red[ space.cache ].slice();
5035 } else {
5036 each( spaces, function( spaceName, space ) {
5037 var cache = space.cache;
5038 each( space.props, function( key, prop ) {
5040 // if the cache doesn't exist, and we know how to convert
5041 if ( !inst[ cache ] && space.to ) {
5043 // if the value was null, we don't need to copy it
5044 // if the key was alpha, we don't need to copy it either
5045 if ( key === "alpha" || red[ key ] == null ) {
5046 return;
5048 inst[ cache ] = space.to( inst._rgba );
5051 // this is the only case where we allow nulls for ALL properties.
5052 // call clamp with alwaysAllowEmpty
5053 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
5056 // everything defined but alpha?
5057 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
5058 // use the default of 1
5059 inst[ cache ][ 3 ] = 1;
5060 if ( space.from ) {
5061 inst._rgba = space.from( inst[ cache ] );
5066 return this;
5069 is: function( compare ) {
5070 var is = color( compare ),
5071 same = true,
5072 inst = this;
5074 each( spaces, function( _, space ) {
5075 var localCache,
5076 isCache = is[ space.cache ];
5077 if (isCache) {
5078 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
5079 each( space.props, function( _, prop ) {
5080 if ( isCache[ prop.idx ] != null ) {
5081 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
5082 return same;
5086 return same;
5088 return same;
5090 _space: function() {
5091 var used = [],
5092 inst = this;
5093 each( spaces, function( spaceName, space ) {
5094 if ( inst[ space.cache ] ) {
5095 used.push( spaceName );
5098 return used.pop();
5100 transition: function( other, distance ) {
5101 var end = color( other ),
5102 spaceName = end._space(),
5103 space = spaces[ spaceName ],
5104 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
5105 start = startColor[ space.cache ] || space.to( startColor._rgba ),
5106 result = start.slice();
5108 end = end[ space.cache ];
5109 each( space.props, function( key, prop ) {
5110 var index = prop.idx,
5111 startValue = start[ index ],
5112 endValue = end[ index ],
5113 type = propTypes[ prop.type ] || {};
5115 // if null, don't override start value
5116 if ( endValue === null ) {
5117 return;
5119 // if null - use end
5120 if ( startValue === null ) {
5121 result[ index ] = endValue;
5122 } else {
5123 if ( type.mod ) {
5124 if ( endValue - startValue > type.mod / 2 ) {
5125 startValue += type.mod;
5126 } else if ( startValue - endValue > type.mod / 2 ) {
5127 startValue -= type.mod;
5130 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
5133 return this[ spaceName ]( result );
5135 blend: function( opaque ) {
5136 // if we are already opaque - return ourself
5137 if ( this._rgba[ 3 ] === 1 ) {
5138 return this;
5141 var rgb = this._rgba.slice(),
5142 a = rgb.pop(),
5143 blend = color( opaque )._rgba;
5145 return color( jQuery.map( rgb, function( v, i ) {
5146 return ( 1 - a ) * blend[ i ] + a * v;
5147 }));
5149 toRgbaString: function() {
5150 var prefix = "rgba(",
5151 rgba = jQuery.map( this._rgba, function( v, i ) {
5152 return v == null ? ( i > 2 ? 1 : 0 ) : v;
5155 if ( rgba[ 3 ] === 1 ) {
5156 rgba.pop();
5157 prefix = "rgb(";
5160 return prefix + rgba.join() + ")";
5162 toHslaString: function() {
5163 var prefix = "hsla(",
5164 hsla = jQuery.map( this.hsla(), function( v, i ) {
5165 if ( v == null ) {
5166 v = i > 2 ? 1 : 0;
5169 // catch 1 and 2
5170 if ( i && i < 3 ) {
5171 v = Math.round( v * 100 ) + "%";
5173 return v;
5176 if ( hsla[ 3 ] === 1 ) {
5177 hsla.pop();
5178 prefix = "hsl(";
5180 return prefix + hsla.join() + ")";
5182 toHexString: function( includeAlpha ) {
5183 var rgba = this._rgba.slice(),
5184 alpha = rgba.pop();
5186 if ( includeAlpha ) {
5187 rgba.push( ~~( alpha * 255 ) );
5190 return "#" + jQuery.map( rgba, function( v ) {
5192 // default to 0 when nulls exist
5193 v = ( v || 0 ).toString( 16 );
5194 return v.length === 1 ? "0" + v : v;
5195 }).join("");
5197 toString: function() {
5198 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
5201 color.fn.parse.prototype = color.fn;
5203 // hsla conversions adapted from:
5204 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
5206 function hue2rgb( p, q, h ) {
5207 h = ( h + 1 ) % 1;
5208 if ( h * 6 < 1 ) {
5209 return p + (q - p) * h * 6;
5211 if ( h * 2 < 1) {
5212 return q;
5214 if ( h * 3 < 2 ) {
5215 return p + (q - p) * ((2/3) - h) * 6;
5217 return p;
5220 spaces.hsla.to = function ( rgba ) {
5221 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
5222 return [ null, null, null, rgba[ 3 ] ];
5224 var r = rgba[ 0 ] / 255,
5225 g = rgba[ 1 ] / 255,
5226 b = rgba[ 2 ] / 255,
5227 a = rgba[ 3 ],
5228 max = Math.max( r, g, b ),
5229 min = Math.min( r, g, b ),
5230 diff = max - min,
5231 add = max + min,
5232 l = add * 0.5,
5233 h, s;
5235 if ( min === max ) {
5236 h = 0;
5237 } else if ( r === max ) {
5238 h = ( 60 * ( g - b ) / diff ) + 360;
5239 } else if ( g === max ) {
5240 h = ( 60 * ( b - r ) / diff ) + 120;
5241 } else {
5242 h = ( 60 * ( r - g ) / diff ) + 240;
5245 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
5246 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
5247 if ( diff === 0 ) {
5248 s = 0;
5249 } else if ( l <= 0.5 ) {
5250 s = diff / add;
5251 } else {
5252 s = diff / ( 2 - add );
5254 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
5257 spaces.hsla.from = function ( hsla ) {
5258 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
5259 return [ null, null, null, hsla[ 3 ] ];
5261 var h = hsla[ 0 ] / 360,
5262 s = hsla[ 1 ],
5263 l = hsla[ 2 ],
5264 a = hsla[ 3 ],
5265 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
5266 p = 2 * l - q;
5268 return [
5269 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
5270 Math.round( hue2rgb( p, q, h ) * 255 ),
5271 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
5277 each( spaces, function( spaceName, space ) {
5278 var props = space.props,
5279 cache = space.cache,
5280 to = space.to,
5281 from = space.from;
5283 // makes rgba() and hsla()
5284 color.fn[ spaceName ] = function( value ) {
5286 // generate a cache for this space if it doesn't exist
5287 if ( to && !this[ cache ] ) {
5288 this[ cache ] = to( this._rgba );
5290 if ( value === undefined ) {
5291 return this[ cache ].slice();
5294 var ret,
5295 type = jQuery.type( value ),
5296 arr = ( type === "array" || type === "object" ) ? value : arguments,
5297 local = this[ cache ].slice();
5299 each( props, function( key, prop ) {
5300 var val = arr[ type === "object" ? key : prop.idx ];
5301 if ( val == null ) {
5302 val = local[ prop.idx ];
5304 local[ prop.idx ] = clamp( val, prop );
5307 if ( from ) {
5308 ret = color( from( local ) );
5309 ret[ cache ] = local;
5310 return ret;
5311 } else {
5312 return color( local );
5316 // makes red() green() blue() alpha() hue() saturation() lightness()
5317 each( props, function( key, prop ) {
5318 // alpha is included in more than one space
5319 if ( color.fn[ key ] ) {
5320 return;
5322 color.fn[ key ] = function( value ) {
5323 var vtype = jQuery.type( value ),
5324 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
5325 local = this[ fn ](),
5326 cur = local[ prop.idx ],
5327 match;
5329 if ( vtype === "undefined" ) {
5330 return cur;
5333 if ( vtype === "function" ) {
5334 value = value.call( this, cur );
5335 vtype = jQuery.type( value );
5337 if ( value == null && prop.empty ) {
5338 return this;
5340 if ( vtype === "string" ) {
5341 match = rplusequals.exec( value );
5342 if ( match ) {
5343 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
5346 local[ prop.idx ] = value;
5347 return this[ fn ]( local );
5352 // add cssHook and .fx.step function for each named hook.
5353 // accept a space separated string of properties
5354 color.hook = function( hook ) {
5355 var hooks = hook.split( " " );
5356 each( hooks, function( i, hook ) {
5357 jQuery.cssHooks[ hook ] = {
5358 set: function( elem, value ) {
5359 var parsed, curElem,
5360 backgroundColor = "";
5362 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
5363 value = color( parsed || value );
5364 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
5365 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
5366 while (
5367 (backgroundColor === "" || backgroundColor === "transparent") &&
5368 curElem && curElem.style
5370 try {
5371 backgroundColor = jQuery.css( curElem, "backgroundColor" );
5372 curElem = curElem.parentNode;
5373 } catch ( e ) {
5377 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
5378 backgroundColor :
5379 "_default" );
5382 value = value.toRgbaString();
5384 try {
5385 elem.style[ hook ] = value;
5386 } catch( e ) {
5387 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
5391 jQuery.fx.step[ hook ] = function( fx ) {
5392 if ( !fx.colorInit ) {
5393 fx.start = color( fx.elem, hook );
5394 fx.end = color( fx.end );
5395 fx.colorInit = true;
5397 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
5403 color.hook( stepHooks );
5405 jQuery.cssHooks.borderColor = {
5406 expand: function( value ) {
5407 var expanded = {};
5409 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
5410 expanded[ "border" + part + "Color" ] = value;
5412 return expanded;
5416 // Basic color names only.
5417 // Usage of any of the other color names requires adding yourself or including
5418 // jquery.color.svg-names.js.
5419 colors = jQuery.Color.names = {
5420 // 4.1. Basic color keywords
5421 aqua: "#00ffff",
5422 black: "#000000",
5423 blue: "#0000ff",
5424 fuchsia: "#ff00ff",
5425 gray: "#808080",
5426 green: "#008000",
5427 lime: "#00ff00",
5428 maroon: "#800000",
5429 navy: "#000080",
5430 olive: "#808000",
5431 purple: "#800080",
5432 red: "#ff0000",
5433 silver: "#c0c0c0",
5434 teal: "#008080",
5435 white: "#ffffff",
5436 yellow: "#ffff00",
5438 // 4.2.3. "transparent" color keyword
5439 transparent: [ null, null, null, 0 ],
5441 _default: "#ffffff"
5444 })( jQuery );
5447 /******************************************************************************/
5448 /****************************** CLASS ANIMATIONS ******************************/
5449 /******************************************************************************/
5450 (function() {
5452 var classAnimationActions = [ "add", "remove", "toggle" ],
5453 shorthandStyles = {
5454 border: 1,
5455 borderBottom: 1,
5456 borderColor: 1,
5457 borderLeft: 1,
5458 borderRight: 1,
5459 borderTop: 1,
5460 borderWidth: 1,
5461 margin: 1,
5462 padding: 1
5465 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
5466 $.fx.step[ prop ] = function( fx ) {
5467 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
5468 jQuery.style( fx.elem, prop, fx.end );
5469 fx.setAttr = true;
5474 function getElementStyles( elem ) {
5475 var key, len,
5476 style = elem.ownerDocument.defaultView ?
5477 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
5478 elem.currentStyle,
5479 styles = {};
5481 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
5482 len = style.length;
5483 while ( len-- ) {
5484 key = style[ len ];
5485 if ( typeof style[ key ] === "string" ) {
5486 styles[ $.camelCase( key ) ] = style[ key ];
5489 // support: Opera, IE <9
5490 } else {
5491 for ( key in style ) {
5492 if ( typeof style[ key ] === "string" ) {
5493 styles[ key ] = style[ key ];
5498 return styles;
5502 function styleDifference( oldStyle, newStyle ) {
5503 var diff = {},
5504 name, value;
5506 for ( name in newStyle ) {
5507 value = newStyle[ name ];
5508 if ( oldStyle[ name ] !== value ) {
5509 if ( !shorthandStyles[ name ] ) {
5510 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
5511 diff[ name ] = value;
5517 return diff;
5520 // support: jQuery <1.8
5521 if ( !$.fn.addBack ) {
5522 $.fn.addBack = function( selector ) {
5523 return this.add( selector == null ?
5524 this.prevObject : this.prevObject.filter( selector )
5529 $.effects.animateClass = function( value, duration, easing, callback ) {
5530 var o = $.speed( duration, easing, callback );
5532 return this.queue( function() {
5533 var animated = $( this ),
5534 baseClass = animated.attr( "class" ) || "",
5535 applyClassChange,
5536 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
5538 // map the animated objects to store the original styles.
5539 allAnimations = allAnimations.map(function() {
5540 var el = $( this );
5541 return {
5542 el: el,
5543 start: getElementStyles( this )
5547 // apply class change
5548 applyClassChange = function() {
5549 $.each( classAnimationActions, function(i, action) {
5550 if ( value[ action ] ) {
5551 animated[ action + "Class" ]( value[ action ] );
5555 applyClassChange();
5557 // map all animated objects again - calculate new styles and diff
5558 allAnimations = allAnimations.map(function() {
5559 this.end = getElementStyles( this.el[ 0 ] );
5560 this.diff = styleDifference( this.start, this.end );
5561 return this;
5564 // apply original class
5565 animated.attr( "class", baseClass );
5567 // map all animated objects again - this time collecting a promise
5568 allAnimations = allAnimations.map(function() {
5569 var styleInfo = this,
5570 dfd = $.Deferred(),
5571 opts = $.extend({}, o, {
5572 queue: false,
5573 complete: function() {
5574 dfd.resolve( styleInfo );
5578 this.el.animate( this.diff, opts );
5579 return dfd.promise();
5582 // once all animations have completed:
5583 $.when.apply( $, allAnimations.get() ).done(function() {
5585 // set the final class
5586 applyClassChange();
5588 // for each animated element,
5589 // clear all css properties that were animated
5590 $.each( arguments, function() {
5591 var el = this.el;
5592 $.each( this.diff, function(key) {
5593 el.css( key, "" );
5597 // this is guarnteed to be there if you use jQuery.speed()
5598 // it also handles dequeuing the next anim...
5599 o.complete.call( animated[ 0 ] );
5604 $.fn.extend({
5605 addClass: (function( orig ) {
5606 return function( classNames, speed, easing, callback ) {
5607 return speed ?
5608 $.effects.animateClass.call( this,
5609 { add: classNames }, speed, easing, callback ) :
5610 orig.apply( this, arguments );
5612 })( $.fn.addClass ),
5614 removeClass: (function( orig ) {
5615 return function( classNames, speed, easing, callback ) {
5616 return arguments.length > 1 ?
5617 $.effects.animateClass.call( this,
5618 { remove: classNames }, speed, easing, callback ) :
5619 orig.apply( this, arguments );
5621 })( $.fn.removeClass ),
5623 toggleClass: (function( orig ) {
5624 return function( classNames, force, speed, easing, callback ) {
5625 if ( typeof force === "boolean" || force === undefined ) {
5626 if ( !speed ) {
5627 // without speed parameter
5628 return orig.apply( this, arguments );
5629 } else {
5630 return $.effects.animateClass.call( this,
5631 (force ? { add: classNames } : { remove: classNames }),
5632 speed, easing, callback );
5634 } else {
5635 // without force parameter
5636 return $.effects.animateClass.call( this,
5637 { toggle: classNames }, force, speed, easing );
5640 })( $.fn.toggleClass ),
5642 switchClass: function( remove, add, speed, easing, callback) {
5643 return $.effects.animateClass.call( this, {
5644 add: add,
5645 remove: remove
5646 }, speed, easing, callback );
5650 })();
5652 /******************************************************************************/
5653 /*********************************** EFFECTS **********************************/
5654 /******************************************************************************/
5656 (function() {
5658 $.extend( $.effects, {
5659 version: "1.10.3",
5661 // Saves a set of properties in a data storage
5662 save: function( element, set ) {
5663 for( var i=0; i < set.length; i++ ) {
5664 if ( set[ i ] !== null ) {
5665 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
5670 // Restores a set of previously saved properties from a data storage
5671 restore: function( element, set ) {
5672 var val, i;
5673 for( i=0; i < set.length; i++ ) {
5674 if ( set[ i ] !== null ) {
5675 val = element.data( dataSpace + set[ i ] );
5676 // support: jQuery 1.6.2
5677 // http://bugs.jquery.com/ticket/9917
5678 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
5679 // We can't differentiate between "" and 0 here, so we just assume
5680 // empty string since it's likely to be a more common value...
5681 if ( val === undefined ) {
5682 val = "";
5684 element.css( set[ i ], val );
5689 setMode: function( el, mode ) {
5690 if (mode === "toggle") {
5691 mode = el.is( ":hidden" ) ? "show" : "hide";
5693 return mode;
5696 // Translates a [top,left] array into a baseline value
5697 // this should be a little more flexible in the future to handle a string & hash
5698 getBaseline: function( origin, original ) {
5699 var y, x;
5700 switch ( origin[ 0 ] ) {
5701 case "top": y = 0; break;
5702 case "middle": y = 0.5; break;
5703 case "bottom": y = 1; break;
5704 default: y = origin[ 0 ] / original.height;
5706 switch ( origin[ 1 ] ) {
5707 case "left": x = 0; break;
5708 case "center": x = 0.5; break;
5709 case "right": x = 1; break;
5710 default: x = origin[ 1 ] / original.width;
5712 return {
5713 x: x,
5714 y: y
5718 // Wraps the element around a wrapper that copies position properties
5719 createWrapper: function( element ) {
5721 // if the element is already wrapped, return it
5722 if ( element.parent().is( ".ui-effects-wrapper" )) {
5723 return element.parent();
5726 // wrap the element
5727 var props = {
5728 width: element.outerWidth(true),
5729 height: element.outerHeight(true),
5730 "float": element.css( "float" )
5732 wrapper = $( "<div></div>" )
5733 .addClass( "ui-effects-wrapper" )
5734 .css({
5735 fontSize: "100%",
5736 background: "transparent",
5737 border: "none",
5738 margin: 0,
5739 padding: 0
5741 // Store the size in case width/height are defined in % - Fixes #5245
5742 size = {
5743 width: element.width(),
5744 height: element.height()
5746 active = document.activeElement;
5748 // support: Firefox
5749 // Firefox incorrectly exposes anonymous content
5750 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
5751 try {
5752 active.id;
5753 } catch( e ) {
5754 active = document.body;
5757 element.wrap( wrapper );
5759 // Fixes #7595 - Elements lose focus when wrapped.
5760 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5761 $( active ).focus();
5764 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
5766 // transfer positioning properties to the wrapper
5767 if ( element.css( "position" ) === "static" ) {
5768 wrapper.css({ position: "relative" });
5769 element.css({ position: "relative" });
5770 } else {
5771 $.extend( props, {
5772 position: element.css( "position" ),
5773 zIndex: element.css( "z-index" )
5775 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
5776 props[ pos ] = element.css( pos );
5777 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
5778 props[ pos ] = "auto";
5781 element.css({
5782 position: "relative",
5783 top: 0,
5784 left: 0,
5785 right: "auto",
5786 bottom: "auto"
5789 element.css(size);
5791 return wrapper.css( props ).show();
5794 removeWrapper: function( element ) {
5795 var active = document.activeElement;
5797 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
5798 element.parent().replaceWith( element );
5800 // Fixes #7595 - Elements lose focus when wrapped.
5801 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
5802 $( active ).focus();
5807 return element;
5810 setTransition: function( element, list, factor, value ) {
5811 value = value || {};
5812 $.each( list, function( i, x ) {
5813 var unit = element.cssUnit( x );
5814 if ( unit[ 0 ] > 0 ) {
5815 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
5818 return value;
5822 // return an effect options object for the given parameters:
5823 function _normalizeArguments( effect, options, speed, callback ) {
5825 // allow passing all options as the first parameter
5826 if ( $.isPlainObject( effect ) ) {
5827 options = effect;
5828 effect = effect.effect;
5831 // convert to an object
5832 effect = { effect: effect };
5834 // catch (effect, null, ...)
5835 if ( options == null ) {
5836 options = {};
5839 // catch (effect, callback)
5840 if ( $.isFunction( options ) ) {
5841 callback = options;
5842 speed = null;
5843 options = {};
5846 // catch (effect, speed, ?)
5847 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
5848 callback = speed;
5849 speed = options;
5850 options = {};
5853 // catch (effect, options, callback)
5854 if ( $.isFunction( speed ) ) {
5855 callback = speed;
5856 speed = null;
5859 // add options to effect
5860 if ( options ) {
5861 $.extend( effect, options );
5864 speed = speed || options.duration;
5865 effect.duration = $.fx.off ? 0 :
5866 typeof speed === "number" ? speed :
5867 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
5868 $.fx.speeds._default;
5870 effect.complete = callback || options.complete;
5872 return effect;
5875 function standardAnimationOption( option ) {
5876 // Valid standard speeds (nothing, number, named speed)
5877 if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
5878 return true;
5881 // Invalid strings - treat as "normal" speed
5882 if ( typeof option === "string" && !$.effects.effect[ option ] ) {
5883 return true;
5886 // Complete callback
5887 if ( $.isFunction( option ) ) {
5888 return true;
5891 // Options hash (but not naming an effect)
5892 if ( typeof option === "object" && !option.effect ) {
5893 return true;
5896 // Didn't match any standard API
5897 return false;
5900 $.fn.extend({
5901 effect: function( /* effect, options, speed, callback */ ) {
5902 var args = _normalizeArguments.apply( this, arguments ),
5903 mode = args.mode,
5904 queue = args.queue,
5905 effectMethod = $.effects.effect[ args.effect ];
5907 if ( $.fx.off || !effectMethod ) {
5908 // delegate to the original method (e.g., .show()) if possible
5909 if ( mode ) {
5910 return this[ mode ]( args.duration, args.complete );
5911 } else {
5912 return this.each( function() {
5913 if ( args.complete ) {
5914 args.complete.call( this );
5920 function run( next ) {
5921 var elem = $( this ),
5922 complete = args.complete,
5923 mode = args.mode;
5925 function done() {
5926 if ( $.isFunction( complete ) ) {
5927 complete.call( elem[0] );
5929 if ( $.isFunction( next ) ) {
5930 next();
5934 // If the element already has the correct final state, delegate to
5935 // the core methods so the internal tracking of "olddisplay" works.
5936 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
5937 elem[ mode ]();
5938 done();
5939 } else {
5940 effectMethod.call( elem[0], args, done );
5944 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
5947 show: (function( orig ) {
5948 return function( option ) {
5949 if ( standardAnimationOption( option ) ) {
5950 return orig.apply( this, arguments );
5951 } else {
5952 var args = _normalizeArguments.apply( this, arguments );
5953 args.mode = "show";
5954 return this.effect.call( this, args );
5957 })( $.fn.show ),
5959 hide: (function( orig ) {
5960 return function( option ) {
5961 if ( standardAnimationOption( option ) ) {
5962 return orig.apply( this, arguments );
5963 } else {
5964 var args = _normalizeArguments.apply( this, arguments );
5965 args.mode = "hide";
5966 return this.effect.call( this, args );
5969 })( $.fn.hide ),
5971 toggle: (function( orig ) {
5972 return function( option ) {
5973 if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
5974 return orig.apply( this, arguments );
5975 } else {
5976 var args = _normalizeArguments.apply( this, arguments );
5977 args.mode = "toggle";
5978 return this.effect.call( this, args );
5981 })( $.fn.toggle ),
5983 // helper functions
5984 cssUnit: function(key) {
5985 var style = this.css( key ),
5986 val = [];
5988 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
5989 if ( style.indexOf( unit ) > 0 ) {
5990 val = [ parseFloat( style ), unit ];
5993 return val;
5997 })();
5999 /******************************************************************************/
6000 /*********************************** EASING ***********************************/
6001 /******************************************************************************/
6003 (function() {
6005 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
6007 var baseEasings = {};
6009 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
6010 baseEasings[ name ] = function( p ) {
6011 return Math.pow( p, i + 2 );
6015 $.extend( baseEasings, {
6016 Sine: function ( p ) {
6017 return 1 - Math.cos( p * Math.PI / 2 );
6019 Circ: function ( p ) {
6020 return 1 - Math.sqrt( 1 - p * p );
6022 Elastic: function( p ) {
6023 return p === 0 || p === 1 ? p :
6024 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
6026 Back: function( p ) {
6027 return p * p * ( 3 * p - 2 );
6029 Bounce: function ( p ) {
6030 var pow2,
6031 bounce = 4;
6033 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
6034 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
6038 $.each( baseEasings, function( name, easeIn ) {
6039 $.easing[ "easeIn" + name ] = easeIn;
6040 $.easing[ "easeOut" + name ] = function( p ) {
6041 return 1 - easeIn( 1 - p );
6043 $.easing[ "easeInOut" + name ] = function( p ) {
6044 return p < 0.5 ?
6045 easeIn( p * 2 ) / 2 :
6046 1 - easeIn( p * -2 + 2 ) / 2;
6050 })();
6052 })(jQuery);
6054 (function( $, undefined ) {
6056 var uid = 0,
6057 hideProps = {},
6058 showProps = {};
6060 hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
6061 hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
6062 showProps.height = showProps.paddingTop = showProps.paddingBottom =
6063 showProps.borderTopWidth = showProps.borderBottomWidth = "show";
6065 $.widget( "ui.accordion", {
6066 version: "1.10.3",
6067 options: {
6068 active: 0,
6069 animate: {},
6070 collapsible: false,
6071 event: "click",
6072 header: "> li > :first-child,> :not(li):even",
6073 heightStyle: "auto",
6074 icons: {
6075 activeHeader: "ui-icon-triangle-1-s",
6076 header: "ui-icon-triangle-1-e"
6079 // callbacks
6080 activate: null,
6081 beforeActivate: null
6084 _create: function() {
6085 var options = this.options;
6086 this.prevShow = this.prevHide = $();
6087 this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
6088 // ARIA
6089 .attr( "role", "tablist" );
6091 // don't allow collapsible: false and active: false / null
6092 if ( !options.collapsible && (options.active === false || options.active == null) ) {
6093 options.active = 0;
6096 this._processPanels();
6097 // handle negative values
6098 if ( options.active < 0 ) {
6099 options.active += this.headers.length;
6101 this._refresh();
6104 _getCreateEventData: function() {
6105 return {
6106 header: this.active,
6107 panel: !this.active.length ? $() : this.active.next(),
6108 content: !this.active.length ? $() : this.active.next()
6112 _createIcons: function() {
6113 var icons = this.options.icons;
6114 if ( icons ) {
6115 $( "<span>" )
6116 .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
6117 .prependTo( this.headers );
6118 this.active.children( ".ui-accordion-header-icon" )
6119 .removeClass( icons.header )
6120 .addClass( icons.activeHeader );
6121 this.headers.addClass( "ui-accordion-icons" );
6125 _destroyIcons: function() {
6126 this.headers
6127 .removeClass( "ui-accordion-icons" )
6128 .children( ".ui-accordion-header-icon" )
6129 .remove();
6132 _destroy: function() {
6133 var contents;
6135 // clean up main element
6136 this.element
6137 .removeClass( "ui-accordion ui-widget ui-helper-reset" )
6138 .removeAttr( "role" );
6140 // clean up headers
6141 this.headers
6142 .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
6143 .removeAttr( "role" )
6144 .removeAttr( "aria-selected" )
6145 .removeAttr( "aria-controls" )
6146 .removeAttr( "tabIndex" )
6147 .each(function() {
6148 if ( /^ui-accordion/.test( this.id ) ) {
6149 this.removeAttribute( "id" );
6152 this._destroyIcons();
6154 // clean up content panels
6155 contents = this.headers.next()
6156 .css( "display", "" )
6157 .removeAttr( "role" )
6158 .removeAttr( "aria-expanded" )
6159 .removeAttr( "aria-hidden" )
6160 .removeAttr( "aria-labelledby" )
6161 .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
6162 .each(function() {
6163 if ( /^ui-accordion/.test( this.id ) ) {
6164 this.removeAttribute( "id" );
6167 if ( this.options.heightStyle !== "content" ) {
6168 contents.css( "height", "" );
6172 _setOption: function( key, value ) {
6173 if ( key === "active" ) {
6174 // _activate() will handle invalid values and update this.options
6175 this._activate( value );
6176 return;
6179 if ( key === "event" ) {
6180 if ( this.options.event ) {
6181 this._off( this.headers, this.options.event );
6183 this._setupEvents( value );
6186 this._super( key, value );
6188 // setting collapsible: false while collapsed; open first panel
6189 if ( key === "collapsible" && !value && this.options.active === false ) {
6190 this._activate( 0 );
6193 if ( key === "icons" ) {
6194 this._destroyIcons();
6195 if ( value ) {
6196 this._createIcons();
6200 // #5332 - opacity doesn't cascade to positioned elements in IE
6201 // so we need to add the disabled class to the headers and panels
6202 if ( key === "disabled" ) {
6203 this.headers.add( this.headers.next() )
6204 .toggleClass( "ui-state-disabled", !!value );
6208 _keydown: function( event ) {
6209 /*jshint maxcomplexity:15*/
6210 if ( event.altKey || event.ctrlKey ) {
6211 return;
6214 var keyCode = $.ui.keyCode,
6215 length = this.headers.length,
6216 currentIndex = this.headers.index( event.target ),
6217 toFocus = false;
6219 switch ( event.keyCode ) {
6220 case keyCode.RIGHT:
6221 case keyCode.DOWN:
6222 toFocus = this.headers[ ( currentIndex + 1 ) % length ];
6223 break;
6224 case keyCode.LEFT:
6225 case keyCode.UP:
6226 toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
6227 break;
6228 case keyCode.SPACE:
6229 case keyCode.ENTER:
6230 this._eventHandler( event );
6231 break;
6232 case keyCode.HOME:
6233 toFocus = this.headers[ 0 ];
6234 break;
6235 case keyCode.END:
6236 toFocus = this.headers[ length - 1 ];
6237 break;
6240 if ( toFocus ) {
6241 $( event.target ).attr( "tabIndex", -1 );
6242 $( toFocus ).attr( "tabIndex", 0 );
6243 toFocus.focus();
6244 event.preventDefault();
6248 _panelKeyDown : function( event ) {
6249 if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
6250 $( event.currentTarget ).prev().focus();
6254 refresh: function() {
6255 var options = this.options;
6256 this._processPanels();
6258 // was collapsed or no panel
6259 if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
6260 options.active = false;
6261 this.active = $();
6262 // active false only when collapsible is true
6263 } else if ( options.active === false ) {
6264 this._activate( 0 );
6265 // was active, but active panel is gone
6266 } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
6267 // all remaining panel are disabled
6268 if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
6269 options.active = false;
6270 this.active = $();
6271 // activate previous panel
6272 } else {
6273 this._activate( Math.max( 0, options.active - 1 ) );
6275 // was active, active panel still exists
6276 } else {
6277 // make sure active index is correct
6278 options.active = this.headers.index( this.active );
6281 this._destroyIcons();
6283 this._refresh();
6286 _processPanels: function() {
6287 this.headers = this.element.find( this.options.header )
6288 .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
6290 this.headers.next()
6291 .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
6292 .filter(":not(.ui-accordion-content-active)")
6293 .hide();
6296 _refresh: function() {
6297 var maxHeight,
6298 options = this.options,
6299 heightStyle = options.heightStyle,
6300 parent = this.element.parent(),
6301 accordionId = this.accordionId = "ui-accordion-" +
6302 (this.element.attr( "id" ) || ++uid);
6304 this.active = this._findActive( options.active )
6305 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
6306 .removeClass( "ui-corner-all" );
6307 this.active.next()
6308 .addClass( "ui-accordion-content-active" )
6309 .show();
6311 this.headers
6312 .attr( "role", "tab" )
6313 .each(function( i ) {
6314 var header = $( this ),
6315 headerId = header.attr( "id" ),
6316 panel = header.next(),
6317 panelId = panel.attr( "id" );
6318 if ( !headerId ) {
6319 headerId = accordionId + "-header-" + i;
6320 header.attr( "id", headerId );
6322 if ( !panelId ) {
6323 panelId = accordionId + "-panel-" + i;
6324 panel.attr( "id", panelId );
6326 header.attr( "aria-controls", panelId );
6327 panel.attr( "aria-labelledby", headerId );
6329 .next()
6330 .attr( "role", "tabpanel" );
6332 this.headers
6333 .not( this.active )
6334 .attr({
6335 "aria-selected": "false",
6336 tabIndex: -1
6338 .next()
6339 .attr({
6340 "aria-expanded": "false",
6341 "aria-hidden": "true"
6343 .hide();
6345 // make sure at least one header is in the tab order
6346 if ( !this.active.length ) {
6347 this.headers.eq( 0 ).attr( "tabIndex", 0 );
6348 } else {
6349 this.active.attr({
6350 "aria-selected": "true",
6351 tabIndex: 0
6353 .next()
6354 .attr({
6355 "aria-expanded": "true",
6356 "aria-hidden": "false"
6360 this._createIcons();
6362 this._setupEvents( options.event );
6364 if ( heightStyle === "fill" ) {
6365 maxHeight = parent.height();
6366 this.element.siblings( ":visible" ).each(function() {
6367 var elem = $( this ),
6368 position = elem.css( "position" );
6370 if ( position === "absolute" || position === "fixed" ) {
6371 return;
6373 maxHeight -= elem.outerHeight( true );
6376 this.headers.each(function() {
6377 maxHeight -= $( this ).outerHeight( true );
6380 this.headers.next()
6381 .each(function() {
6382 $( this ).height( Math.max( 0, maxHeight -
6383 $( this ).innerHeight() + $( this ).height() ) );
6385 .css( "overflow", "auto" );
6386 } else if ( heightStyle === "auto" ) {
6387 maxHeight = 0;
6388 this.headers.next()
6389 .each(function() {
6390 maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
6392 .height( maxHeight );
6396 _activate: function( index ) {
6397 var active = this._findActive( index )[ 0 ];
6399 // trying to activate the already active panel
6400 if ( active === this.active[ 0 ] ) {
6401 return;
6404 // trying to collapse, simulate a click on the currently active header
6405 active = active || this.active[ 0 ];
6407 this._eventHandler({
6408 target: active,
6409 currentTarget: active,
6410 preventDefault: $.noop
6414 _findActive: function( selector ) {
6415 return typeof selector === "number" ? this.headers.eq( selector ) : $();
6418 _setupEvents: function( event ) {
6419 var events = {
6420 keydown: "_keydown"
6422 if ( event ) {
6423 $.each( event.split(" "), function( index, eventName ) {
6424 events[ eventName ] = "_eventHandler";
6428 this._off( this.headers.add( this.headers.next() ) );
6429 this._on( this.headers, events );
6430 this._on( this.headers.next(), { keydown: "_panelKeyDown" });
6431 this._hoverable( this.headers );
6432 this._focusable( this.headers );
6435 _eventHandler: function( event ) {
6436 var options = this.options,
6437 active = this.active,
6438 clicked = $( event.currentTarget ),
6439 clickedIsActive = clicked[ 0 ] === active[ 0 ],
6440 collapsing = clickedIsActive && options.collapsible,
6441 toShow = collapsing ? $() : clicked.next(),
6442 toHide = active.next(),
6443 eventData = {
6444 oldHeader: active,
6445 oldPanel: toHide,
6446 newHeader: collapsing ? $() : clicked,
6447 newPanel: toShow
6450 event.preventDefault();
6452 if (
6453 // click on active header, but not collapsible
6454 ( clickedIsActive && !options.collapsible ) ||
6455 // allow canceling activation
6456 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
6457 return;
6460 options.active = collapsing ? false : this.headers.index( clicked );
6462 // when the call to ._toggle() comes after the class changes
6463 // it causes a very odd bug in IE 8 (see #6720)
6464 this.active = clickedIsActive ? $() : clicked;
6465 this._toggle( eventData );
6467 // switch classes
6468 // corner classes on the previously active header stay after the animation
6469 active.removeClass( "ui-accordion-header-active ui-state-active" );
6470 if ( options.icons ) {
6471 active.children( ".ui-accordion-header-icon" )
6472 .removeClass( options.icons.activeHeader )
6473 .addClass( options.icons.header );
6476 if ( !clickedIsActive ) {
6477 clicked
6478 .removeClass( "ui-corner-all" )
6479 .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
6480 if ( options.icons ) {
6481 clicked.children( ".ui-accordion-header-icon" )
6482 .removeClass( options.icons.header )
6483 .addClass( options.icons.activeHeader );
6486 clicked
6487 .next()
6488 .addClass( "ui-accordion-content-active" );
6492 _toggle: function( data ) {
6493 var toShow = data.newPanel,
6494 toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
6496 // handle activating a panel during the animation for another activation
6497 this.prevShow.add( this.prevHide ).stop( true, true );
6498 this.prevShow = toShow;
6499 this.prevHide = toHide;
6501 if ( this.options.animate ) {
6502 this._animate( toShow, toHide, data );
6503 } else {
6504 toHide.hide();
6505 toShow.show();
6506 this._toggleComplete( data );
6509 toHide.attr({
6510 "aria-expanded": "false",
6511 "aria-hidden": "true"
6513 toHide.prev().attr( "aria-selected", "false" );
6514 // if we're switching panels, remove the old header from the tab order
6515 // if we're opening from collapsed state, remove the previous header from the tab order
6516 // if we're collapsing, then keep the collapsing header in the tab order
6517 if ( toShow.length && toHide.length ) {
6518 toHide.prev().attr( "tabIndex", -1 );
6519 } else if ( toShow.length ) {
6520 this.headers.filter(function() {
6521 return $( this ).attr( "tabIndex" ) === 0;
6523 .attr( "tabIndex", -1 );
6526 toShow
6527 .attr({
6528 "aria-expanded": "true",
6529 "aria-hidden": "false"
6531 .prev()
6532 .attr({
6533 "aria-selected": "true",
6534 tabIndex: 0
6538 _animate: function( toShow, toHide, data ) {
6539 var total, easing, duration,
6540 that = this,
6541 adjust = 0,
6542 down = toShow.length &&
6543 ( !toHide.length || ( toShow.index() < toHide.index() ) ),
6544 animate = this.options.animate || {},
6545 options = down && animate.down || animate,
6546 complete = function() {
6547 that._toggleComplete( data );
6550 if ( typeof options === "number" ) {
6551 duration = options;
6553 if ( typeof options === "string" ) {
6554 easing = options;
6556 // fall back from options to animation in case of partial down settings
6557 easing = easing || options.easing || animate.easing;
6558 duration = duration || options.duration || animate.duration;
6560 if ( !toHide.length ) {
6561 return toShow.animate( showProps, duration, easing, complete );
6563 if ( !toShow.length ) {
6564 return toHide.animate( hideProps, duration, easing, complete );
6567 total = toShow.show().outerHeight();
6568 toHide.animate( hideProps, {
6569 duration: duration,
6570 easing: easing,
6571 step: function( now, fx ) {
6572 fx.now = Math.round( now );
6575 toShow
6576 .hide()
6577 .animate( showProps, {
6578 duration: duration,
6579 easing: easing,
6580 complete: complete,
6581 step: function( now, fx ) {
6582 fx.now = Math.round( now );
6583 if ( fx.prop !== "height" ) {
6584 adjust += fx.now;
6585 } else if ( that.options.heightStyle !== "content" ) {
6586 fx.now = Math.round( total - toHide.outerHeight() - adjust );
6587 adjust = 0;
6593 _toggleComplete: function( data ) {
6594 var toHide = data.oldPanel;
6596 toHide
6597 .removeClass( "ui-accordion-content-active" )
6598 .prev()
6599 .removeClass( "ui-corner-top" )
6600 .addClass( "ui-corner-all" );
6602 // Work around for rendering bug in IE (#5421)
6603 if ( toHide.length ) {
6604 toHide.parent()[0].className = toHide.parent()[0].className;
6607 this._trigger( "activate", null, data );
6611 })( jQuery );
6613 (function( $, undefined ) {
6615 // used to prevent race conditions with remote data sources
6616 var requestIndex = 0;
6618 $.widget( "ui.autocomplete", {
6619 version: "1.10.3",
6620 defaultElement: "<input>",
6621 options: {
6622 appendTo: null,
6623 autoFocus: false,
6624 delay: 300,
6625 minLength: 1,
6626 position: {
6627 my: "left top",
6628 at: "left bottom",
6629 collision: "none"
6631 source: null,
6633 // callbacks
6634 change: null,
6635 close: null,
6636 focus: null,
6637 open: null,
6638 response: null,
6639 search: null,
6640 select: null
6643 pending: 0,
6645 _create: function() {
6646 // Some browsers only repeat keydown events, not keypress events,
6647 // so we use the suppressKeyPress flag to determine if we've already
6648 // handled the keydown event. #7269
6649 // Unfortunately the code for & in keypress is the same as the up arrow,
6650 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
6651 // events when we know the keydown event was used to modify the
6652 // search term. #7799
6653 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
6654 nodeName = this.element[0].nodeName.toLowerCase(),
6655 isTextarea = nodeName === "textarea",
6656 isInput = nodeName === "input";
6658 this.isMultiLine =
6659 // Textareas are always multi-line
6660 isTextarea ? true :
6661 // Inputs are always single-line, even if inside a contentEditable element
6662 // IE also treats inputs as contentEditable
6663 isInput ? false :
6664 // All other element types are determined by whether or not they're contentEditable
6665 this.element.prop( "isContentEditable" );
6667 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
6668 this.isNewMenu = true;
6670 this.element
6671 .addClass( "ui-autocomplete-input" )
6672 .attr( "autocomplete", "off" );
6674 this._on( this.element, {
6675 keydown: function( event ) {
6676 /*jshint maxcomplexity:15*/
6677 if ( this.element.prop( "readOnly" ) ) {
6678 suppressKeyPress = true;
6679 suppressInput = true;
6680 suppressKeyPressRepeat = true;
6681 return;
6684 suppressKeyPress = false;
6685 suppressInput = false;
6686 suppressKeyPressRepeat = false;
6687 var keyCode = $.ui.keyCode;
6688 switch( event.keyCode ) {
6689 case keyCode.PAGE_UP:
6690 suppressKeyPress = true;
6691 this._move( "previousPage", event );
6692 break;
6693 case keyCode.PAGE_DOWN:
6694 suppressKeyPress = true;
6695 this._move( "nextPage", event );
6696 break;
6697 case keyCode.UP:
6698 suppressKeyPress = true;
6699 this._keyEvent( "previous", event );
6700 break;
6701 case keyCode.DOWN:
6702 suppressKeyPress = true;
6703 this._keyEvent( "next", event );
6704 break;
6705 case keyCode.ENTER:
6706 case keyCode.NUMPAD_ENTER:
6707 // when menu is open and has focus
6708 if ( this.menu.active ) {
6709 // #6055 - Opera still allows the keypress to occur
6710 // which causes forms to submit
6711 suppressKeyPress = true;
6712 event.preventDefault();
6713 this.menu.select( event );
6715 break;
6716 case keyCode.TAB:
6717 if ( this.menu.active ) {
6718 this.menu.select( event );
6720 break;
6721 case keyCode.ESCAPE:
6722 if ( this.menu.element.is( ":visible" ) ) {
6723 this._value( this.term );
6724 this.close( event );
6725 // Different browsers have different default behavior for escape
6726 // Single press can mean undo or clear
6727 // Double press in IE means clear the whole form
6728 event.preventDefault();
6730 break;
6731 default:
6732 suppressKeyPressRepeat = true;
6733 // search timeout should be triggered before the input value is changed
6734 this._searchTimeout( event );
6735 break;
6738 keypress: function( event ) {
6739 if ( suppressKeyPress ) {
6740 suppressKeyPress = false;
6741 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
6742 event.preventDefault();
6744 return;
6746 if ( suppressKeyPressRepeat ) {
6747 return;
6750 // replicate some key handlers to allow them to repeat in Firefox and Opera
6751 var keyCode = $.ui.keyCode;
6752 switch( event.keyCode ) {
6753 case keyCode.PAGE_UP:
6754 this._move( "previousPage", event );
6755 break;
6756 case keyCode.PAGE_DOWN:
6757 this._move( "nextPage", event );
6758 break;
6759 case keyCode.UP:
6760 this._keyEvent( "previous", event );
6761 break;
6762 case keyCode.DOWN:
6763 this._keyEvent( "next", event );
6764 break;
6767 input: function( event ) {
6768 if ( suppressInput ) {
6769 suppressInput = false;
6770 event.preventDefault();
6771 return;
6773 this._searchTimeout( event );
6775 focus: function() {
6776 this.selectedItem = null;
6777 this.previous = this._value();
6779 blur: function( event ) {
6780 if ( this.cancelBlur ) {
6781 delete this.cancelBlur;
6782 return;
6785 clearTimeout( this.searching );
6786 this.close( event );
6787 this._change( event );
6791 this._initSource();
6792 this.menu = $( "<ul>" )
6793 .addClass( "ui-autocomplete ui-front" )
6794 .appendTo( this._appendTo() )
6795 .menu({
6796 // disable ARIA support, the live region takes care of that
6797 role: null
6799 .hide()
6800 .data( "ui-menu" );
6802 this._on( this.menu.element, {
6803 mousedown: function( event ) {
6804 // prevent moving focus out of the text field
6805 event.preventDefault();
6807 // IE doesn't prevent moving focus even with event.preventDefault()
6808 // so we set a flag to know when we should ignore the blur event
6809 this.cancelBlur = true;
6810 this._delay(function() {
6811 delete this.cancelBlur;
6814 // clicking on the scrollbar causes focus to shift to the body
6815 // but we can't detect a mouseup or a click immediately afterward
6816 // so we have to track the next mousedown and close the menu if
6817 // the user clicks somewhere outside of the autocomplete
6818 var menuElement = this.menu.element[ 0 ];
6819 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
6820 this._delay(function() {
6821 var that = this;
6822 this.document.one( "mousedown", function( event ) {
6823 if ( event.target !== that.element[ 0 ] &&
6824 event.target !== menuElement &&
6825 !$.contains( menuElement, event.target ) ) {
6826 that.close();
6832 menufocus: function( event, ui ) {
6833 // support: Firefox
6834 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
6835 if ( this.isNewMenu ) {
6836 this.isNewMenu = false;
6837 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
6838 this.menu.blur();
6840 this.document.one( "mousemove", function() {
6841 $( event.target ).trigger( event.originalEvent );
6844 return;
6848 var item = ui.item.data( "ui-autocomplete-item" );
6849 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
6850 // use value to match what will end up in the input, if it was a key event
6851 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
6852 this._value( item.value );
6854 } else {
6855 // Normally the input is populated with the item's value as the
6856 // menu is navigated, causing screen readers to notice a change and
6857 // announce the item. Since the focus event was canceled, this doesn't
6858 // happen, so we update the live region so that screen readers can
6859 // still notice the change and announce it.
6860 this.liveRegion.text( item.value );
6863 menuselect: function( event, ui ) {
6864 var item = ui.item.data( "ui-autocomplete-item" ),
6865 previous = this.previous;
6867 // only trigger when focus was lost (click on menu)
6868 if ( this.element[0] !== this.document[0].activeElement ) {
6869 this.element.focus();
6870 this.previous = previous;
6871 // #6109 - IE triggers two focus events and the second
6872 // is asynchronous, so we need to reset the previous
6873 // term synchronously and asynchronously :-(
6874 this._delay(function() {
6875 this.previous = previous;
6876 this.selectedItem = item;
6880 if ( false !== this._trigger( "select", event, { item: item } ) ) {
6881 this._value( item.value );
6883 // reset the term after the select event
6884 // this allows custom select handling to work properly
6885 this.term = this._value();
6887 this.close( event );
6888 this.selectedItem = item;
6892 this.liveRegion = $( "<span>", {
6893 role: "status",
6894 "aria-live": "polite"
6896 .addClass( "ui-helper-hidden-accessible" )
6897 .insertBefore( this.element );
6899 // turning off autocomplete prevents the browser from remembering the
6900 // value when navigating through history, so we re-enable autocomplete
6901 // if the page is unloaded before the widget is destroyed. #7790
6902 this._on( this.window, {
6903 beforeunload: function() {
6904 this.element.removeAttr( "autocomplete" );
6909 _destroy: function() {
6910 clearTimeout( this.searching );
6911 this.element
6912 .removeClass( "ui-autocomplete-input" )
6913 .removeAttr( "autocomplete" );
6914 this.menu.element.remove();
6915 this.liveRegion.remove();
6918 _setOption: function( key, value ) {
6919 this._super( key, value );
6920 if ( key === "source" ) {
6921 this._initSource();
6923 if ( key === "appendTo" ) {
6924 this.menu.element.appendTo( this._appendTo() );
6926 if ( key === "disabled" && value && this.xhr ) {
6927 this.xhr.abort();
6931 _appendTo: function() {
6932 var element = this.options.appendTo;
6934 if ( element ) {
6935 element = element.jquery || element.nodeType ?
6936 $( element ) :
6937 this.document.find( element ).eq( 0 );
6940 if ( !element ) {
6941 element = this.element.closest( ".ui-front" );
6944 if ( !element.length ) {
6945 element = this.document[0].body;
6948 return element;
6951 _initSource: function() {
6952 var array, url,
6953 that = this;
6954 if ( $.isArray(this.options.source) ) {
6955 array = this.options.source;
6956 this.source = function( request, response ) {
6957 response( $.ui.autocomplete.filter( array, request.term ) );
6959 } else if ( typeof this.options.source === "string" ) {
6960 url = this.options.source;
6961 this.source = function( request, response ) {
6962 if ( that.xhr ) {
6963 that.xhr.abort();
6965 that.xhr = $.ajax({
6966 url: url,
6967 data: request,
6968 dataType: "json",
6969 success: function( data ) {
6970 response( data );
6972 error: function() {
6973 response( [] );
6977 } else {
6978 this.source = this.options.source;
6982 _searchTimeout: function( event ) {
6983 clearTimeout( this.searching );
6984 this.searching = this._delay(function() {
6985 // only search if the value has changed
6986 if ( this.term !== this._value() ) {
6987 this.selectedItem = null;
6988 this.search( null, event );
6990 }, this.options.delay );
6993 search: function( value, event ) {
6994 value = value != null ? value : this._value();
6996 // always save the actual value, not the one passed as an argument
6997 this.term = this._value();
6999 if ( value.length < this.options.minLength ) {
7000 return this.close( event );
7003 if ( this._trigger( "search", event ) === false ) {
7004 return;
7007 return this._search( value );
7010 _search: function( value ) {
7011 this.pending++;
7012 this.element.addClass( "ui-autocomplete-loading" );
7013 this.cancelSearch = false;
7015 this.source( { term: value }, this._response() );
7018 _response: function() {
7019 var that = this,
7020 index = ++requestIndex;
7022 return function( content ) {
7023 if ( index === requestIndex ) {
7024 that.__response( content );
7027 that.pending--;
7028 if ( !that.pending ) {
7029 that.element.removeClass( "ui-autocomplete-loading" );
7034 __response: function( content ) {
7035 if ( content ) {
7036 content = this._normalize( content );
7038 this._trigger( "response", null, { content: content } );
7039 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
7040 this._suggest( content );
7041 this._trigger( "open" );
7042 } else {
7043 // use ._close() instead of .close() so we don't cancel future searches
7044 this._close();
7048 close: function( event ) {
7049 this.cancelSearch = true;
7050 this._close( event );
7053 _close: function( event ) {
7054 if ( this.menu.element.is( ":visible" ) ) {
7055 this.menu.element.hide();
7056 this.menu.blur();
7057 this.isNewMenu = true;
7058 this._trigger( "close", event );
7062 _change: function( event ) {
7063 if ( this.previous !== this._value() ) {
7064 this._trigger( "change", event, { item: this.selectedItem } );
7068 _normalize: function( items ) {
7069 // assume all items have the right format when the first item is complete
7070 if ( items.length && items[0].label && items[0].value ) {
7071 return items;
7073 return $.map( items, function( item ) {
7074 if ( typeof item === "string" ) {
7075 return {
7076 label: item,
7077 value: item
7080 return $.extend({
7081 label: item.label || item.value,
7082 value: item.value || item.label
7083 }, item );
7087 _suggest: function( items ) {
7088 var ul = this.menu.element.empty();
7089 this._renderMenu( ul, items );
7090 this.isNewMenu = true;
7091 this.menu.refresh();
7093 // size and position menu
7094 ul.show();
7095 this._resizeMenu();
7096 ul.position( $.extend({
7097 of: this.element
7098 }, this.options.position ));
7100 if ( this.options.autoFocus ) {
7101 this.menu.next();
7105 _resizeMenu: function() {
7106 var ul = this.menu.element;
7107 ul.outerWidth( Math.max(
7108 // Firefox wraps long text (possibly a rounding bug)
7109 // so we add 1px to avoid the wrapping (#7513)
7110 ul.width( "" ).outerWidth() + 1,
7111 this.element.outerWidth()
7112 ) );
7115 _renderMenu: function( ul, items ) {
7116 var that = this;
7117 $.each( items, function( index, item ) {
7118 that._renderItemData( ul, item );
7122 _renderItemData: function( ul, item ) {
7123 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
7126 _renderItem: function( ul, item ) {
7127 return $( "<li>" )
7128 .append( $( "<a>" ).text( item.label ) )
7129 .appendTo( ul );
7132 _move: function( direction, event ) {
7133 if ( !this.menu.element.is( ":visible" ) ) {
7134 this.search( null, event );
7135 return;
7137 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
7138 this.menu.isLastItem() && /^next/.test( direction ) ) {
7139 this._value( this.term );
7140 this.menu.blur();
7141 return;
7143 this.menu[ direction ]( event );
7146 widget: function() {
7147 return this.menu.element;
7150 _value: function() {
7151 return this.valueMethod.apply( this.element, arguments );
7154 _keyEvent: function( keyEvent, event ) {
7155 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
7156 this._move( keyEvent, event );
7158 // prevents moving cursor to beginning/end of the text field in some browsers
7159 event.preventDefault();
7164 $.extend( $.ui.autocomplete, {
7165 escapeRegex: function( value ) {
7166 return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
7168 filter: function(array, term) {
7169 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
7170 return $.grep( array, function(value) {
7171 return matcher.test( value.label || value.value || value );
7177 // live region extension, adding a `messages` option
7178 // NOTE: This is an experimental API. We are still investigating
7179 // a full solution for string manipulation and internationalization.
7180 $.widget( "ui.autocomplete", $.ui.autocomplete, {
7181 options: {
7182 messages: {
7183 noResults: "No search results.",
7184 results: function( amount ) {
7185 return amount + ( amount > 1 ? " results are" : " result is" ) +
7186 " available, use up and down arrow keys to navigate.";
7191 __response: function( content ) {
7192 var message;
7193 this._superApply( arguments );
7194 if ( this.options.disabled || this.cancelSearch ) {
7195 return;
7197 if ( content && content.length ) {
7198 message = this.options.messages.results( content.length );
7199 } else {
7200 message = this.options.messages.noResults;
7202 this.liveRegion.text( message );
7206 }( jQuery ));
7208 (function( $, undefined ) {
7210 var lastActive, startXPos, startYPos, clickDragged,
7211 baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
7212 stateClasses = "ui-state-hover ui-state-active ",
7213 typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
7214 formResetHandler = function() {
7215 var form = $( this );
7216 setTimeout(function() {
7217 form.find( ":ui-button" ).button( "refresh" );
7218 }, 1 );
7220 radioGroup = function( radio ) {
7221 var name = radio.name,
7222 form = radio.form,
7223 radios = $( [] );
7224 if ( name ) {
7225 name = name.replace( /'/g, "\\'" );
7226 if ( form ) {
7227 radios = $( form ).find( "[name='" + name + "']" );
7228 } else {
7229 radios = $( "[name='" + name + "']", radio.ownerDocument )
7230 .filter(function() {
7231 return !this.form;
7235 return radios;
7238 $.widget( "ui.button", {
7239 version: "1.10.3",
7240 defaultElement: "<button>",
7241 options: {
7242 disabled: null,
7243 text: true,
7244 label: null,
7245 icons: {
7246 primary: null,
7247 secondary: null
7250 _create: function() {
7251 this.element.closest( "form" )
7252 .unbind( "reset" + this.eventNamespace )
7253 .bind( "reset" + this.eventNamespace, formResetHandler );
7255 if ( typeof this.options.disabled !== "boolean" ) {
7256 this.options.disabled = !!this.element.prop( "disabled" );
7257 } else {
7258 this.element.prop( "disabled", this.options.disabled );
7261 this._determineButtonType();
7262 this.hasTitle = !!this.buttonElement.attr( "title" );
7264 var that = this,
7265 options = this.options,
7266 toggleButton = this.type === "checkbox" || this.type === "radio",
7267 activeClass = !toggleButton ? "ui-state-active" : "",
7268 focusClass = "ui-state-focus";
7270 if ( options.label === null ) {
7271 options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
7274 this._hoverable( this.buttonElement );
7276 this.buttonElement
7277 .addClass( baseClasses )
7278 .attr( "role", "button" )
7279 .bind( "mouseenter" + this.eventNamespace, function() {
7280 if ( options.disabled ) {
7281 return;
7283 if ( this === lastActive ) {
7284 $( this ).addClass( "ui-state-active" );
7287 .bind( "mouseleave" + this.eventNamespace, function() {
7288 if ( options.disabled ) {
7289 return;
7291 $( this ).removeClass( activeClass );
7293 .bind( "click" + this.eventNamespace, function( event ) {
7294 if ( options.disabled ) {
7295 event.preventDefault();
7296 event.stopImmediatePropagation();
7300 this.element
7301 .bind( "focus" + this.eventNamespace, function() {
7302 // no need to check disabled, focus won't be triggered anyway
7303 that.buttonElement.addClass( focusClass );
7305 .bind( "blur" + this.eventNamespace, function() {
7306 that.buttonElement.removeClass( focusClass );
7309 if ( toggleButton ) {
7310 this.element.bind( "change" + this.eventNamespace, function() {
7311 if ( clickDragged ) {
7312 return;
7314 that.refresh();
7316 // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
7317 // prevents issue where button state changes but checkbox/radio checked state
7318 // does not in Firefox (see ticket #6970)
7319 this.buttonElement
7320 .bind( "mousedown" + this.eventNamespace, function( event ) {
7321 if ( options.disabled ) {
7322 return;
7324 clickDragged = false;
7325 startXPos = event.pageX;
7326 startYPos = event.pageY;
7328 .bind( "mouseup" + this.eventNamespace, function( event ) {
7329 if ( options.disabled ) {
7330 return;
7332 if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
7333 clickDragged = true;
7338 if ( this.type === "checkbox" ) {
7339 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7340 if ( options.disabled || clickDragged ) {
7341 return false;
7344 } else if ( this.type === "radio" ) {
7345 this.buttonElement.bind( "click" + this.eventNamespace, function() {
7346 if ( options.disabled || clickDragged ) {
7347 return false;
7349 $( this ).addClass( "ui-state-active" );
7350 that.buttonElement.attr( "aria-pressed", "true" );
7352 var radio = that.element[ 0 ];
7353 radioGroup( radio )
7354 .not( radio )
7355 .map(function() {
7356 return $( this ).button( "widget" )[ 0 ];
7358 .removeClass( "ui-state-active" )
7359 .attr( "aria-pressed", "false" );
7361 } else {
7362 this.buttonElement
7363 .bind( "mousedown" + this.eventNamespace, function() {
7364 if ( options.disabled ) {
7365 return false;
7367 $( this ).addClass( "ui-state-active" );
7368 lastActive = this;
7369 that.document.one( "mouseup", function() {
7370 lastActive = null;
7373 .bind( "mouseup" + this.eventNamespace, function() {
7374 if ( options.disabled ) {
7375 return false;
7377 $( this ).removeClass( "ui-state-active" );
7379 .bind( "keydown" + this.eventNamespace, function(event) {
7380 if ( options.disabled ) {
7381 return false;
7383 if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
7384 $( this ).addClass( "ui-state-active" );
7387 // see #8559, we bind to blur here in case the button element loses
7388 // focus between keydown and keyup, it would be left in an "active" state
7389 .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
7390 $( this ).removeClass( "ui-state-active" );
7393 if ( this.buttonElement.is("a") ) {
7394 this.buttonElement.keyup(function(event) {
7395 if ( event.keyCode === $.ui.keyCode.SPACE ) {
7396 // TODO pass through original event correctly (just as 2nd argument doesn't work)
7397 $( this ).click();
7403 // TODO: pull out $.Widget's handling for the disabled option into
7404 // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
7405 // be overridden by individual plugins
7406 this._setOption( "disabled", options.disabled );
7407 this._resetButton();
7410 _determineButtonType: function() {
7411 var ancestor, labelSelector, checked;
7413 if ( this.element.is("[type=checkbox]") ) {
7414 this.type = "checkbox";
7415 } else if ( this.element.is("[type=radio]") ) {
7416 this.type = "radio";
7417 } else if ( this.element.is("input") ) {
7418 this.type = "input";
7419 } else {
7420 this.type = "button";
7423 if ( this.type === "checkbox" || this.type === "radio" ) {
7424 // we don't search against the document in case the element
7425 // is disconnected from the DOM
7426 ancestor = this.element.parents().last();
7427 labelSelector = "label[for='" + this.element.attr("id") + "']";
7428 this.buttonElement = ancestor.find( labelSelector );
7429 if ( !this.buttonElement.length ) {
7430 ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
7431 this.buttonElement = ancestor.filter( labelSelector );
7432 if ( !this.buttonElement.length ) {
7433 this.buttonElement = ancestor.find( labelSelector );
7436 this.element.addClass( "ui-helper-hidden-accessible" );
7438 checked = this.element.is( ":checked" );
7439 if ( checked ) {
7440 this.buttonElement.addClass( "ui-state-active" );
7442 this.buttonElement.prop( "aria-pressed", checked );
7443 } else {
7444 this.buttonElement = this.element;
7448 widget: function() {
7449 return this.buttonElement;
7452 _destroy: function() {
7453 this.element
7454 .removeClass( "ui-helper-hidden-accessible" );
7455 this.buttonElement
7456 .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
7457 .removeAttr( "role" )
7458 .removeAttr( "aria-pressed" )
7459 .html( this.buttonElement.find(".ui-button-text").html() );
7461 if ( !this.hasTitle ) {
7462 this.buttonElement.removeAttr( "title" );
7466 _setOption: function( key, value ) {
7467 this._super( key, value );
7468 if ( key === "disabled" ) {
7469 if ( value ) {
7470 this.element.prop( "disabled", true );
7471 } else {
7472 this.element.prop( "disabled", false );
7474 return;
7476 this._resetButton();
7479 refresh: function() {
7480 //See #8237 & #8828
7481 var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
7483 if ( isDisabled !== this.options.disabled ) {
7484 this._setOption( "disabled", isDisabled );
7486 if ( this.type === "radio" ) {
7487 radioGroup( this.element[0] ).each(function() {
7488 if ( $( this ).is( ":checked" ) ) {
7489 $( this ).button( "widget" )
7490 .addClass( "ui-state-active" )
7491 .attr( "aria-pressed", "true" );
7492 } else {
7493 $( this ).button( "widget" )
7494 .removeClass( "ui-state-active" )
7495 .attr( "aria-pressed", "false" );
7498 } else if ( this.type === "checkbox" ) {
7499 if ( this.element.is( ":checked" ) ) {
7500 this.buttonElement
7501 .addClass( "ui-state-active" )
7502 .attr( "aria-pressed", "true" );
7503 } else {
7504 this.buttonElement
7505 .removeClass( "ui-state-active" )
7506 .attr( "aria-pressed", "false" );
7511 _resetButton: function() {
7512 if ( this.type === "input" ) {
7513 if ( this.options.label ) {
7514 this.element.val( this.options.label );
7516 return;
7518 var buttonElement = this.buttonElement.removeClass( typeClasses ),
7519 buttonText = $( "<span></span>", this.document[0] )
7520 .addClass( "ui-button-text" )
7521 .html( this.options.label )
7522 .appendTo( buttonElement.empty() )
7523 .text(),
7524 icons = this.options.icons,
7525 multipleIcons = icons.primary && icons.secondary,
7526 buttonClasses = [];
7528 if ( icons.primary || icons.secondary ) {
7529 if ( this.options.text ) {
7530 buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
7533 if ( icons.primary ) {
7534 buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
7537 if ( icons.secondary ) {
7538 buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
7541 if ( !this.options.text ) {
7542 buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
7544 if ( !this.hasTitle ) {
7545 buttonElement.attr( "title", $.trim( buttonText ) );
7548 } else {
7549 buttonClasses.push( "ui-button-text-only" );
7551 buttonElement.addClass( buttonClasses.join( " " ) );
7555 $.widget( "ui.buttonset", {
7556 version: "1.10.3",
7557 options: {
7558 items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
7561 _create: function() {
7562 this.element.addClass( "ui-buttonset" );
7565 _init: function() {
7566 this.refresh();
7569 _setOption: function( key, value ) {
7570 if ( key === "disabled" ) {
7571 this.buttons.button( "option", key, value );
7574 this._super( key, value );
7577 refresh: function() {
7578 var rtl = this.element.css( "direction" ) === "rtl";
7580 this.buttons = this.element.find( this.options.items )
7581 .filter( ":ui-button" )
7582 .button( "refresh" )
7583 .end()
7584 .not( ":ui-button" )
7585 .button()
7586 .end()
7587 .map(function() {
7588 return $( this ).button( "widget" )[ 0 ];
7590 .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
7591 .filter( ":first" )
7592 .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
7593 .end()
7594 .filter( ":last" )
7595 .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
7596 .end()
7597 .end();
7600 _destroy: function() {
7601 this.element.removeClass( "ui-buttonset" );
7602 this.buttons
7603 .map(function() {
7604 return $( this ).button( "widget" )[ 0 ];
7606 .removeClass( "ui-corner-left ui-corner-right" )
7607 .end()
7608 .button( "destroy" );
7612 }( jQuery ) );
7614 (function( $, undefined ) {
7616 $.extend($.ui, { datepicker: { version: "1.10.3" } });
7618 var PROP_NAME = "datepicker",
7619 instActive;
7621 /* Date picker manager.
7622 Use the singleton instance of this class, $.datepicker, to interact with the date picker.
7623 Settings for (groups of) date pickers are maintained in an instance object,
7624 allowing multiple different settings on the same page. */
7626 function Datepicker() {
7627 this._curInst = null; // The current instance in use
7628 this._keyEvent = false; // If the last event was a key event
7629 this._disabledInputs = []; // List of date picker inputs that have been disabled
7630 this._datepickerShowing = false; // True if the popup picker is showing , false if not
7631 this._inDialog = false; // True if showing within a "dialog", false if not
7632 this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
7633 this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
7634 this._appendClass = "ui-datepicker-append"; // The name of the append marker class
7635 this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
7636 this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
7637 this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
7638 this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
7639 this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
7640 this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
7641 this.regional = []; // Available regional settings, indexed by language code
7642 this.regional[""] = { // Default regional settings
7643 closeText: "Done", // Display text for close link
7644 prevText: "Prev", // Display text for previous month link
7645 nextText: "Next", // Display text for next month link
7646 currentText: "Today", // Display text for current month link
7647 monthNames: ["January","February","March","April","May","June",
7648 "July","August","September","October","November","December"], // Names of months for drop-down and formatting
7649 monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
7650 dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
7651 dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
7652 dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
7653 weekHeader: "Wk", // Column header for week of the year
7654 dateFormat: "mm/dd/yy", // See format options on parseDate
7655 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
7656 isRTL: false, // True if right-to-left language, false if left-to-right
7657 showMonthAfterYear: false, // True if the year select precedes month, false for month then year
7658 yearSuffix: "" // Additional text to append to the year in the month headers
7660 this._defaults = { // Global defaults for all the date picker instances
7661 showOn: "focus", // "focus" for popup on focus,
7662 // "button" for trigger button, or "both" for either
7663 showAnim: "fadeIn", // Name of jQuery animation for popup
7664 showOptions: {}, // Options for enhanced animations
7665 defaultDate: null, // Used when field is blank: actual date,
7666 // +/-number for offset from today, null for today
7667 appendText: "", // Display text following the input box, e.g. showing the format
7668 buttonText: "...", // Text for trigger button
7669 buttonImage: "", // URL for trigger button image
7670 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
7671 hideIfNoPrevNext: false, // True to hide next/previous month links
7672 // if not applicable, false to just disable them
7673 navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
7674 gotoCurrent: false, // True if today link goes back to current selection instead
7675 changeMonth: false, // True if month can be selected directly, false if only prev/next
7676 changeYear: false, // True if year can be selected directly, false if only prev/next
7677 yearRange: "c-10:c+10", // Range of years to display in drop-down,
7678 // either relative to today's year (-nn:+nn), relative to currently displayed year
7679 // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
7680 showOtherMonths: false, // True to show dates in other months, false to leave blank
7681 selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
7682 showWeek: false, // True to show week of the year, false to not show it
7683 calculateWeek: this.iso8601Week, // How to calculate the week of the year,
7684 // takes a Date and returns the number of the week for it
7685 shortYearCutoff: "+10", // Short year values < this are in the current century,
7686 // > this are in the previous century,
7687 // string value starting with "+" for current year + value
7688 minDate: null, // The earliest selectable date, or null for no limit
7689 maxDate: null, // The latest selectable date, or null for no limit
7690 duration: "fast", // Duration of display/closure
7691 beforeShowDay: null, // Function that takes a date and returns an array with
7692 // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
7693 // [2] = cell title (optional), e.g. $.datepicker.noWeekends
7694 beforeShow: null, // Function that takes an input field and
7695 // returns a set of custom settings for the date picker
7696 onSelect: null, // Define a callback function when a date is selected
7697 onChangeMonthYear: null, // Define a callback function when the month or year is changed
7698 onClose: null, // Define a callback function when the datepicker is closed
7699 numberOfMonths: 1, // Number of months to show at a time
7700 showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
7701 stepMonths: 1, // Number of months to step back/forward
7702 stepBigMonths: 12, // Number of months to step back/forward for the big links
7703 altField: "", // Selector for an alternate field to store selected dates into
7704 altFormat: "", // The date format to use for the alternate field
7705 constrainInput: true, // The input is constrained by the current date format
7706 showButtonPanel: false, // True to show button panel, false to not show it
7707 autoSize: false, // True to size the input for the date format, false to leave as is
7708 disabled: false // The initial disabled state
7710 $.extend(this._defaults, this.regional[""]);
7711 this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
7714 $.extend(Datepicker.prototype, {
7715 /* Class name added to elements to indicate already configured with a date picker. */
7716 markerClassName: "hasDatepicker",
7718 //Keep track of the maximum number of rows displayed (see #7043)
7719 maxRows: 4,
7721 // TODO rename to "widget" when switching to widget factory
7722 _widgetDatepicker: function() {
7723 return this.dpDiv;
7726 /* Override the default settings for all instances of the date picker.
7727 * @param settings object - the new settings to use as defaults (anonymous object)
7728 * @return the manager object
7730 setDefaults: function(settings) {
7731 extendRemove(this._defaults, settings || {});
7732 return this;
7735 /* Attach the date picker to a jQuery selection.
7736 * @param target element - the target input field or division or span
7737 * @param settings object - the new settings to use for this date picker instance (anonymous)
7739 _attachDatepicker: function(target, settings) {
7740 var nodeName, inline, inst;
7741 nodeName = target.nodeName.toLowerCase();
7742 inline = (nodeName === "div" || nodeName === "span");
7743 if (!target.id) {
7744 this.uuid += 1;
7745 target.id = "dp" + this.uuid;
7747 inst = this._newInst($(target), inline);
7748 inst.settings = $.extend({}, settings || {});
7749 if (nodeName === "input") {
7750 this._connectDatepicker(target, inst);
7751 } else if (inline) {
7752 this._inlineDatepicker(target, inst);
7756 /* Create a new instance object. */
7757 _newInst: function(target, inline) {
7758 var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
7759 return {id: id, input: target, // associated target
7760 selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
7761 drawMonth: 0, drawYear: 0, // month being drawn
7762 inline: inline, // is datepicker inline or not
7763 dpDiv: (!inline ? this.dpDiv : // presentation div
7764 bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
7767 /* Attach the date picker to an input field. */
7768 _connectDatepicker: function(target, inst) {
7769 var input = $(target);
7770 inst.append = $([]);
7771 inst.trigger = $([]);
7772 if (input.hasClass(this.markerClassName)) {
7773 return;
7775 this._attachments(input, inst);
7776 input.addClass(this.markerClassName).keydown(this._doKeyDown).
7777 keypress(this._doKeyPress).keyup(this._doKeyUp);
7778 this._autoSize(inst);
7779 $.data(target, PROP_NAME, inst);
7780 //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
7781 if( inst.settings.disabled ) {
7782 this._disableDatepicker( target );
7786 /* Make attachments based on settings. */
7787 _attachments: function(input, inst) {
7788 var showOn, buttonText, buttonImage,
7789 appendText = this._get(inst, "appendText"),
7790 isRTL = this._get(inst, "isRTL");
7792 if (inst.append) {
7793 inst.append.remove();
7795 if (appendText) {
7796 inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
7797 input[isRTL ? "before" : "after"](inst.append);
7800 input.unbind("focus", this._showDatepicker);
7802 if (inst.trigger) {
7803 inst.trigger.remove();
7806 showOn = this._get(inst, "showOn");
7807 if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
7808 input.focus(this._showDatepicker);
7810 if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
7811 buttonText = this._get(inst, "buttonText");
7812 buttonImage = this._get(inst, "buttonImage");
7813 inst.trigger = $(this._get(inst, "buttonImageOnly") ?
7814 $("<img/>").addClass(this._triggerClass).
7815 attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
7816 $("<button type='button'></button>").addClass(this._triggerClass).
7817 html(!buttonImage ? buttonText : $("<img/>").attr(
7818 { src:buttonImage, alt:buttonText, title:buttonText })));
7819 input[isRTL ? "before" : "after"](inst.trigger);
7820 inst.trigger.click(function() {
7821 if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
7822 $.datepicker._hideDatepicker();
7823 } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
7824 $.datepicker._hideDatepicker();
7825 $.datepicker._showDatepicker(input[0]);
7826 } else {
7827 $.datepicker._showDatepicker(input[0]);
7829 return false;
7834 /* Apply the maximum length for the date format. */
7835 _autoSize: function(inst) {
7836 if (this._get(inst, "autoSize") && !inst.inline) {
7837 var findMax, max, maxI, i,
7838 date = new Date(2009, 12 - 1, 20), // Ensure double digits
7839 dateFormat = this._get(inst, "dateFormat");
7841 if (dateFormat.match(/[DM]/)) {
7842 findMax = function(names) {
7843 max = 0;
7844 maxI = 0;
7845 for (i = 0; i < names.length; i++) {
7846 if (names[i].length > max) {
7847 max = names[i].length;
7848 maxI = i;
7851 return maxI;
7853 date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
7854 "monthNames" : "monthNamesShort"))));
7855 date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
7856 "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
7858 inst.input.attr("size", this._formatDate(inst, date).length);
7862 /* Attach an inline date picker to a div. */
7863 _inlineDatepicker: function(target, inst) {
7864 var divSpan = $(target);
7865 if (divSpan.hasClass(this.markerClassName)) {
7866 return;
7868 divSpan.addClass(this.markerClassName).append(inst.dpDiv);
7869 $.data(target, PROP_NAME, inst);
7870 this._setDate(inst, this._getDefaultDate(inst), true);
7871 this._updateDatepicker(inst);
7872 this._updateAlternate(inst);
7873 //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
7874 if( inst.settings.disabled ) {
7875 this._disableDatepicker( target );
7877 // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
7878 // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
7879 inst.dpDiv.css( "display", "block" );
7882 /* Pop-up the date picker in a "dialog" box.
7883 * @param input element - ignored
7884 * @param date string or Date - the initial date to display
7885 * @param onSelect function - the function to call when a date is selected
7886 * @param settings object - update the dialog date picker instance's settings (anonymous object)
7887 * @param pos int[2] - coordinates for the dialog's position within the screen or
7888 * event - with x/y coordinates or
7889 * leave empty for default (screen centre)
7890 * @return the manager object
7892 _dialogDatepicker: function(input, date, onSelect, settings, pos) {
7893 var id, browserWidth, browserHeight, scrollX, scrollY,
7894 inst = this._dialogInst; // internal instance
7896 if (!inst) {
7897 this.uuid += 1;
7898 id = "dp" + this.uuid;
7899 this._dialogInput = $("<input type='text' id='" + id +
7900 "' style='position: absolute; top: -100px; width: 0px;'/>");
7901 this._dialogInput.keydown(this._doKeyDown);
7902 $("body").append(this._dialogInput);
7903 inst = this._dialogInst = this._newInst(this._dialogInput, false);
7904 inst.settings = {};
7905 $.data(this._dialogInput[0], PROP_NAME, inst);
7907 extendRemove(inst.settings, settings || {});
7908 date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
7909 this._dialogInput.val(date);
7911 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
7912 if (!this._pos) {
7913 browserWidth = document.documentElement.clientWidth;
7914 browserHeight = document.documentElement.clientHeight;
7915 scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
7916 scrollY = document.documentElement.scrollTop || document.body.scrollTop;
7917 this._pos = // should use actual width/height below
7918 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
7921 // move input on screen for focus, but hidden behind dialog
7922 this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
7923 inst.settings.onSelect = onSelect;
7924 this._inDialog = true;
7925 this.dpDiv.addClass(this._dialogClass);
7926 this._showDatepicker(this._dialogInput[0]);
7927 if ($.blockUI) {
7928 $.blockUI(this.dpDiv);
7930 $.data(this._dialogInput[0], PROP_NAME, inst);
7931 return this;
7934 /* Detach a datepicker from its control.
7935 * @param target element - the target input field or division or span
7937 _destroyDatepicker: function(target) {
7938 var nodeName,
7939 $target = $(target),
7940 inst = $.data(target, PROP_NAME);
7942 if (!$target.hasClass(this.markerClassName)) {
7943 return;
7946 nodeName = target.nodeName.toLowerCase();
7947 $.removeData(target, PROP_NAME);
7948 if (nodeName === "input") {
7949 inst.append.remove();
7950 inst.trigger.remove();
7951 $target.removeClass(this.markerClassName).
7952 unbind("focus", this._showDatepicker).
7953 unbind("keydown", this._doKeyDown).
7954 unbind("keypress", this._doKeyPress).
7955 unbind("keyup", this._doKeyUp);
7956 } else if (nodeName === "div" || nodeName === "span") {
7957 $target.removeClass(this.markerClassName).empty();
7961 /* Enable the date picker to a jQuery selection.
7962 * @param target element - the target input field or division or span
7964 _enableDatepicker: function(target) {
7965 var nodeName, inline,
7966 $target = $(target),
7967 inst = $.data(target, PROP_NAME);
7969 if (!$target.hasClass(this.markerClassName)) {
7970 return;
7973 nodeName = target.nodeName.toLowerCase();
7974 if (nodeName === "input") {
7975 target.disabled = false;
7976 inst.trigger.filter("button").
7977 each(function() { this.disabled = false; }).end().
7978 filter("img").css({opacity: "1.0", cursor: ""});
7979 } else if (nodeName === "div" || nodeName === "span") {
7980 inline = $target.children("." + this._inlineClass);
7981 inline.children().removeClass("ui-state-disabled");
7982 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
7983 prop("disabled", false);
7985 this._disabledInputs = $.map(this._disabledInputs,
7986 function(value) { return (value === target ? null : value); }); // delete entry
7989 /* Disable the date picker to a jQuery selection.
7990 * @param target element - the target input field or division or span
7992 _disableDatepicker: function(target) {
7993 var nodeName, inline,
7994 $target = $(target),
7995 inst = $.data(target, PROP_NAME);
7997 if (!$target.hasClass(this.markerClassName)) {
7998 return;
8001 nodeName = target.nodeName.toLowerCase();
8002 if (nodeName === "input") {
8003 target.disabled = true;
8004 inst.trigger.filter("button").
8005 each(function() { this.disabled = true; }).end().
8006 filter("img").css({opacity: "0.5", cursor: "default"});
8007 } else if (nodeName === "div" || nodeName === "span") {
8008 inline = $target.children("." + this._inlineClass);
8009 inline.children().addClass("ui-state-disabled");
8010 inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
8011 prop("disabled", true);
8013 this._disabledInputs = $.map(this._disabledInputs,
8014 function(value) { return (value === target ? null : value); }); // delete entry
8015 this._disabledInputs[this._disabledInputs.length] = target;
8018 /* Is the first field in a jQuery collection disabled as a datepicker?
8019 * @param target element - the target input field or division or span
8020 * @return boolean - true if disabled, false if enabled
8022 _isDisabledDatepicker: function(target) {
8023 if (!target) {
8024 return false;
8026 for (var i = 0; i < this._disabledInputs.length; i++) {
8027 if (this._disabledInputs[i] === target) {
8028 return true;
8031 return false;
8034 /* Retrieve the instance data for the target control.
8035 * @param target element - the target input field or division or span
8036 * @return object - the associated instance data
8037 * @throws error if a jQuery problem getting data
8039 _getInst: function(target) {
8040 try {
8041 return $.data(target, PROP_NAME);
8043 catch (err) {
8044 throw "Missing instance data for this datepicker";
8048 /* Update or retrieve the settings for a date picker attached to an input field or division.
8049 * @param target element - the target input field or division or span
8050 * @param name object - the new settings to update or
8051 * string - the name of the setting to change or retrieve,
8052 * when retrieving also "all" for all instance settings or
8053 * "defaults" for all global defaults
8054 * @param value any - the new value for the setting
8055 * (omit if above is an object or to retrieve a value)
8057 _optionDatepicker: function(target, name, value) {
8058 var settings, date, minDate, maxDate,
8059 inst = this._getInst(target);
8061 if (arguments.length === 2 && typeof name === "string") {
8062 return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
8063 (inst ? (name === "all" ? $.extend({}, inst.settings) :
8064 this._get(inst, name)) : null));
8067 settings = name || {};
8068 if (typeof name === "string") {
8069 settings = {};
8070 settings[name] = value;
8073 if (inst) {
8074 if (this._curInst === inst) {
8075 this._hideDatepicker();
8078 date = this._getDateDatepicker(target, true);
8079 minDate = this._getMinMaxDate(inst, "min");
8080 maxDate = this._getMinMaxDate(inst, "max");
8081 extendRemove(inst.settings, settings);
8082 // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
8083 if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
8084 inst.settings.minDate = this._formatDate(inst, minDate);
8086 if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
8087 inst.settings.maxDate = this._formatDate(inst, maxDate);
8089 if ( "disabled" in settings ) {
8090 if ( settings.disabled ) {
8091 this._disableDatepicker(target);
8092 } else {
8093 this._enableDatepicker(target);
8096 this._attachments($(target), inst);
8097 this._autoSize(inst);
8098 this._setDate(inst, date);
8099 this._updateAlternate(inst);
8100 this._updateDatepicker(inst);
8104 // change method deprecated
8105 _changeDatepicker: function(target, name, value) {
8106 this._optionDatepicker(target, name, value);
8109 /* Redraw the date picker attached to an input field or division.
8110 * @param target element - the target input field or division or span
8112 _refreshDatepicker: function(target) {
8113 var inst = this._getInst(target);
8114 if (inst) {
8115 this._updateDatepicker(inst);
8119 /* Set the dates for a jQuery selection.
8120 * @param target element - the target input field or division or span
8121 * @param date Date - the new date
8123 _setDateDatepicker: function(target, date) {
8124 var inst = this._getInst(target);
8125 if (inst) {
8126 this._setDate(inst, date);
8127 this._updateDatepicker(inst);
8128 this._updateAlternate(inst);
8132 /* Get the date(s) for the first entry in a jQuery selection.
8133 * @param target element - the target input field or division or span
8134 * @param noDefault boolean - true if no default date is to be used
8135 * @return Date - the current date
8137 _getDateDatepicker: function(target, noDefault) {
8138 var inst = this._getInst(target);
8139 if (inst && !inst.inline) {
8140 this._setDateFromField(inst, noDefault);
8142 return (inst ? this._getDate(inst) : null);
8145 /* Handle keystrokes. */
8146 _doKeyDown: function(event) {
8147 var onSelect, dateStr, sel,
8148 inst = $.datepicker._getInst(event.target),
8149 handled = true,
8150 isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
8152 inst._keyEvent = true;
8153 if ($.datepicker._datepickerShowing) {
8154 switch (event.keyCode) {
8155 case 9: $.datepicker._hideDatepicker();
8156 handled = false;
8157 break; // hide on tab out
8158 case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
8159 $.datepicker._currentClass + ")", inst.dpDiv);
8160 if (sel[0]) {
8161 $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
8164 onSelect = $.datepicker._get(inst, "onSelect");
8165 if (onSelect) {
8166 dateStr = $.datepicker._formatDate(inst);
8168 // trigger custom callback
8169 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
8170 } else {
8171 $.datepicker._hideDatepicker();
8174 return false; // don't submit the form
8175 case 27: $.datepicker._hideDatepicker();
8176 break; // hide on escape
8177 case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8178 -$.datepicker._get(inst, "stepBigMonths") :
8179 -$.datepicker._get(inst, "stepMonths")), "M");
8180 break; // previous month/year on page up/+ ctrl
8181 case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8182 +$.datepicker._get(inst, "stepBigMonths") :
8183 +$.datepicker._get(inst, "stepMonths")), "M");
8184 break; // next month/year on page down/+ ctrl
8185 case 35: if (event.ctrlKey || event.metaKey) {
8186 $.datepicker._clearDate(event.target);
8188 handled = event.ctrlKey || event.metaKey;
8189 break; // clear on ctrl or command +end
8190 case 36: if (event.ctrlKey || event.metaKey) {
8191 $.datepicker._gotoToday(event.target);
8193 handled = event.ctrlKey || event.metaKey;
8194 break; // current on ctrl or command +home
8195 case 37: if (event.ctrlKey || event.metaKey) {
8196 $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
8198 handled = event.ctrlKey || event.metaKey;
8199 // -1 day on ctrl or command +left
8200 if (event.originalEvent.altKey) {
8201 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8202 -$.datepicker._get(inst, "stepBigMonths") :
8203 -$.datepicker._get(inst, "stepMonths")), "M");
8205 // next month/year on alt +left on Mac
8206 break;
8207 case 38: if (event.ctrlKey || event.metaKey) {
8208 $.datepicker._adjustDate(event.target, -7, "D");
8210 handled = event.ctrlKey || event.metaKey;
8211 break; // -1 week on ctrl or command +up
8212 case 39: if (event.ctrlKey || event.metaKey) {
8213 $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
8215 handled = event.ctrlKey || event.metaKey;
8216 // +1 day on ctrl or command +right
8217 if (event.originalEvent.altKey) {
8218 $.datepicker._adjustDate(event.target, (event.ctrlKey ?
8219 +$.datepicker._get(inst, "stepBigMonths") :
8220 +$.datepicker._get(inst, "stepMonths")), "M");
8222 // next month/year on alt +right
8223 break;
8224 case 40: if (event.ctrlKey || event.metaKey) {
8225 $.datepicker._adjustDate(event.target, +7, "D");
8227 handled = event.ctrlKey || event.metaKey;
8228 break; // +1 week on ctrl or command +down
8229 default: handled = false;
8231 } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
8232 $.datepicker._showDatepicker(this);
8233 } else {
8234 handled = false;
8237 if (handled) {
8238 event.preventDefault();
8239 event.stopPropagation();
8243 /* Filter entered characters - based on date format. */
8244 _doKeyPress: function(event) {
8245 var chars, chr,
8246 inst = $.datepicker._getInst(event.target);
8248 if ($.datepicker._get(inst, "constrainInput")) {
8249 chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
8250 chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
8251 return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
8255 /* Synchronise manual entry and field/alternate field. */
8256 _doKeyUp: function(event) {
8257 var date,
8258 inst = $.datepicker._getInst(event.target);
8260 if (inst.input.val() !== inst.lastVal) {
8261 try {
8262 date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
8263 (inst.input ? inst.input.val() : null),
8264 $.datepicker._getFormatConfig(inst));
8266 if (date) { // only if valid
8267 $.datepicker._setDateFromField(inst);
8268 $.datepicker._updateAlternate(inst);
8269 $.datepicker._updateDatepicker(inst);
8272 catch (err) {
8275 return true;
8278 /* Pop-up the date picker for a given input field.
8279 * If false returned from beforeShow event handler do not show.
8280 * @param input element - the input field attached to the date picker or
8281 * event - if triggered by focus
8283 _showDatepicker: function(input) {
8284 input = input.target || input;
8285 if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
8286 input = $("input", input.parentNode)[0];
8289 if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
8290 return;
8293 var inst, beforeShow, beforeShowSettings, isFixed,
8294 offset, showAnim, duration;
8296 inst = $.datepicker._getInst(input);
8297 if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
8298 $.datepicker._curInst.dpDiv.stop(true, true);
8299 if ( inst && $.datepicker._datepickerShowing ) {
8300 $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
8304 beforeShow = $.datepicker._get(inst, "beforeShow");
8305 beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
8306 if(beforeShowSettings === false){
8307 return;
8309 extendRemove(inst.settings, beforeShowSettings);
8311 inst.lastVal = null;
8312 $.datepicker._lastInput = input;
8313 $.datepicker._setDateFromField(inst);
8315 if ($.datepicker._inDialog) { // hide cursor
8316 input.value = "";
8318 if (!$.datepicker._pos) { // position below input
8319 $.datepicker._pos = $.datepicker._findPos(input);
8320 $.datepicker._pos[1] += input.offsetHeight; // add the height
8323 isFixed = false;
8324 $(input).parents().each(function() {
8325 isFixed |= $(this).css("position") === "fixed";
8326 return !isFixed;
8329 offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
8330 $.datepicker._pos = null;
8331 //to avoid flashes on Firefox
8332 inst.dpDiv.empty();
8333 // determine sizing offscreen
8334 inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
8335 $.datepicker._updateDatepicker(inst);
8336 // fix width for dynamic number of date pickers
8337 // and adjust position before showing
8338 offset = $.datepicker._checkOffset(inst, offset, isFixed);
8339 inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
8340 "static" : (isFixed ? "fixed" : "absolute")), display: "none",
8341 left: offset.left + "px", top: offset.top + "px"});
8343 if (!inst.inline) {
8344 showAnim = $.datepicker._get(inst, "showAnim");
8345 duration = $.datepicker._get(inst, "duration");
8346 inst.dpDiv.zIndex($(input).zIndex()+1);
8347 $.datepicker._datepickerShowing = true;
8349 if ( $.effects && $.effects.effect[ showAnim ] ) {
8350 inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
8351 } else {
8352 inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
8355 if ( $.datepicker._shouldFocusInput( inst ) ) {
8356 inst.input.focus();
8359 $.datepicker._curInst = inst;
8363 /* Generate the date picker content. */
8364 _updateDatepicker: function(inst) {
8365 this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
8366 instActive = inst; // for delegate hover events
8367 inst.dpDiv.empty().append(this._generateHTML(inst));
8368 this._attachHandlers(inst);
8369 inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
8371 var origyearshtml,
8372 numMonths = this._getNumberOfMonths(inst),
8373 cols = numMonths[1],
8374 width = 17;
8376 inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
8377 if (cols > 1) {
8378 inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
8380 inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
8381 "Class"]("ui-datepicker-multi");
8382 inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
8383 "Class"]("ui-datepicker-rtl");
8385 if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
8386 inst.input.focus();
8389 // deffered render of the years select (to avoid flashes on Firefox)
8390 if( inst.yearshtml ){
8391 origyearshtml = inst.yearshtml;
8392 setTimeout(function(){
8393 //assure that inst.yearshtml didn't change.
8394 if( origyearshtml === inst.yearshtml && inst.yearshtml ){
8395 inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
8397 origyearshtml = inst.yearshtml = null;
8398 }, 0);
8402 // #6694 - don't focus the input if it's already focused
8403 // this breaks the change event in IE
8404 // Support: IE and jQuery <1.9
8405 _shouldFocusInput: function( inst ) {
8406 return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
8409 /* Check positioning to remain on screen. */
8410 _checkOffset: function(inst, offset, isFixed) {
8411 var dpWidth = inst.dpDiv.outerWidth(),
8412 dpHeight = inst.dpDiv.outerHeight(),
8413 inputWidth = inst.input ? inst.input.outerWidth() : 0,
8414 inputHeight = inst.input ? inst.input.outerHeight() : 0,
8415 viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
8416 viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
8418 offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
8419 offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
8420 offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
8422 // now check if datepicker is showing outside window viewport - move to a better place if so.
8423 offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
8424 Math.abs(offset.left + dpWidth - viewWidth) : 0);
8425 offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
8426 Math.abs(dpHeight + inputHeight) : 0);
8428 return offset;
8431 /* Find an object's position on the screen. */
8432 _findPos: function(obj) {
8433 var position,
8434 inst = this._getInst(obj),
8435 isRTL = this._get(inst, "isRTL");
8437 while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
8438 obj = obj[isRTL ? "previousSibling" : "nextSibling"];
8441 position = $(obj).offset();
8442 return [position.left, position.top];
8445 /* Hide the date picker from view.
8446 * @param input element - the input field attached to the date picker
8448 _hideDatepicker: function(input) {
8449 var showAnim, duration, postProcess, onClose,
8450 inst = this._curInst;
8452 if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
8453 return;
8456 if (this._datepickerShowing) {
8457 showAnim = this._get(inst, "showAnim");
8458 duration = this._get(inst, "duration");
8459 postProcess = function() {
8460 $.datepicker._tidyDialog(inst);
8463 // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
8464 if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
8465 inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
8466 } else {
8467 inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
8468 (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
8471 if (!showAnim) {
8472 postProcess();
8474 this._datepickerShowing = false;
8476 onClose = this._get(inst, "onClose");
8477 if (onClose) {
8478 onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
8481 this._lastInput = null;
8482 if (this._inDialog) {
8483 this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
8484 if ($.blockUI) {
8485 $.unblockUI();
8486 $("body").append(this.dpDiv);
8489 this._inDialog = false;
8493 /* Tidy up after a dialog display. */
8494 _tidyDialog: function(inst) {
8495 inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
8498 /* Close date picker if clicked elsewhere. */
8499 _checkExternalClick: function(event) {
8500 if (!$.datepicker._curInst) {
8501 return;
8504 var $target = $(event.target),
8505 inst = $.datepicker._getInst($target[0]);
8507 if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
8508 $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
8509 !$target.hasClass($.datepicker.markerClassName) &&
8510 !$target.closest("." + $.datepicker._triggerClass).length &&
8511 $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
8512 ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
8513 $.datepicker._hideDatepicker();
8517 /* Adjust one of the date sub-fields. */
8518 _adjustDate: function(id, offset, period) {
8519 var target = $(id),
8520 inst = this._getInst(target[0]);
8522 if (this._isDisabledDatepicker(target[0])) {
8523 return;
8525 this._adjustInstDate(inst, offset +
8526 (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
8527 period);
8528 this._updateDatepicker(inst);
8531 /* Action for current link. */
8532 _gotoToday: function(id) {
8533 var date,
8534 target = $(id),
8535 inst = this._getInst(target[0]);
8537 if (this._get(inst, "gotoCurrent") && inst.currentDay) {
8538 inst.selectedDay = inst.currentDay;
8539 inst.drawMonth = inst.selectedMonth = inst.currentMonth;
8540 inst.drawYear = inst.selectedYear = inst.currentYear;
8541 } else {
8542 date = new Date();
8543 inst.selectedDay = date.getDate();
8544 inst.drawMonth = inst.selectedMonth = date.getMonth();
8545 inst.drawYear = inst.selectedYear = date.getFullYear();
8547 this._notifyChange(inst);
8548 this._adjustDate(target);
8551 /* Action for selecting a new month/year. */
8552 _selectMonthYear: function(id, select, period) {
8553 var target = $(id),
8554 inst = this._getInst(target[0]);
8556 inst["selected" + (period === "M" ? "Month" : "Year")] =
8557 inst["draw" + (period === "M" ? "Month" : "Year")] =
8558 parseInt(select.options[select.selectedIndex].value,10);
8560 this._notifyChange(inst);
8561 this._adjustDate(target);
8564 /* Action for selecting a day. */
8565 _selectDay: function(id, month, year, td) {
8566 var inst,
8567 target = $(id);
8569 if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
8570 return;
8573 inst = this._getInst(target[0]);
8574 inst.selectedDay = inst.currentDay = $("a", td).html();
8575 inst.selectedMonth = inst.currentMonth = month;
8576 inst.selectedYear = inst.currentYear = year;
8577 this._selectDate(id, this._formatDate(inst,
8578 inst.currentDay, inst.currentMonth, inst.currentYear));
8581 /* Erase the input field and hide the date picker. */
8582 _clearDate: function(id) {
8583 var target = $(id);
8584 this._selectDate(target, "");
8587 /* Update the input field with the selected date. */
8588 _selectDate: function(id, dateStr) {
8589 var onSelect,
8590 target = $(id),
8591 inst = this._getInst(target[0]);
8593 dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
8594 if (inst.input) {
8595 inst.input.val(dateStr);
8597 this._updateAlternate(inst);
8599 onSelect = this._get(inst, "onSelect");
8600 if (onSelect) {
8601 onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
8602 } else if (inst.input) {
8603 inst.input.trigger("change"); // fire the change event
8606 if (inst.inline){
8607 this._updateDatepicker(inst);
8608 } else {
8609 this._hideDatepicker();
8610 this._lastInput = inst.input[0];
8611 if (typeof(inst.input[0]) !== "object") {
8612 inst.input.focus(); // restore focus
8614 this._lastInput = null;
8618 /* Update any alternate field to synchronise with the main field. */
8619 _updateAlternate: function(inst) {
8620 var altFormat, date, dateStr,
8621 altField = this._get(inst, "altField");
8623 if (altField) { // update alternate field too
8624 altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
8625 date = this._getDate(inst);
8626 dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
8627 $(altField).each(function() { $(this).val(dateStr); });
8631 /* Set as beforeShowDay function to prevent selection of weekends.
8632 * @param date Date - the date to customise
8633 * @return [boolean, string] - is this date selectable?, what is its CSS class?
8635 noWeekends: function(date) {
8636 var day = date.getDay();
8637 return [(day > 0 && day < 6), ""];
8640 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
8641 * @param date Date - the date to get the week for
8642 * @return number - the number of the week within the year that contains this date
8644 iso8601Week: function(date) {
8645 var time,
8646 checkDate = new Date(date.getTime());
8648 // Find Thursday of this week starting on Monday
8649 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
8651 time = checkDate.getTime();
8652 checkDate.setMonth(0); // Compare with Jan 1
8653 checkDate.setDate(1);
8654 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
8657 /* Parse a string value into a date object.
8658 * See formatDate below for the possible formats.
8660 * @param format string - the expected format of the date
8661 * @param value string - the date in the above format
8662 * @param settings Object - attributes include:
8663 * shortYearCutoff number - the cutoff year for determining the century (optional)
8664 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8665 * dayNames string[7] - names of the days from Sunday (optional)
8666 * monthNamesShort string[12] - abbreviated names of the months (optional)
8667 * monthNames string[12] - names of the months (optional)
8668 * @return Date - the extracted date value or null if value is blank
8670 parseDate: function (format, value, settings) {
8671 if (format == null || value == null) {
8672 throw "Invalid arguments";
8675 value = (typeof value === "object" ? value.toString() : value + "");
8676 if (value === "") {
8677 return null;
8680 var iFormat, dim, extra,
8681 iValue = 0,
8682 shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
8683 shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
8684 new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
8685 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8686 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8687 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8688 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8689 year = -1,
8690 month = -1,
8691 day = -1,
8692 doy = -1,
8693 literal = false,
8694 date,
8695 // Check whether a format character is doubled
8696 lookAhead = function(match) {
8697 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8698 if (matches) {
8699 iFormat++;
8701 return matches;
8703 // Extract a number from the string value
8704 getNumber = function(match) {
8705 var isDoubled = lookAhead(match),
8706 size = (match === "@" ? 14 : (match === "!" ? 20 :
8707 (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
8708 digits = new RegExp("^\\d{1," + size + "}"),
8709 num = value.substring(iValue).match(digits);
8710 if (!num) {
8711 throw "Missing number at position " + iValue;
8713 iValue += num[0].length;
8714 return parseInt(num[0], 10);
8716 // Extract a name from the string value and convert to an index
8717 getName = function(match, shortNames, longNames) {
8718 var index = -1,
8719 names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
8720 return [ [k, v] ];
8721 }).sort(function (a, b) {
8722 return -(a[1].length - b[1].length);
8725 $.each(names, function (i, pair) {
8726 var name = pair[1];
8727 if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
8728 index = pair[0];
8729 iValue += name.length;
8730 return false;
8733 if (index !== -1) {
8734 return index + 1;
8735 } else {
8736 throw "Unknown name at position " + iValue;
8739 // Confirm that a literal character matches the string value
8740 checkLiteral = function() {
8741 if (value.charAt(iValue) !== format.charAt(iFormat)) {
8742 throw "Unexpected literal at position " + iValue;
8744 iValue++;
8747 for (iFormat = 0; iFormat < format.length; iFormat++) {
8748 if (literal) {
8749 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8750 literal = false;
8751 } else {
8752 checkLiteral();
8754 } else {
8755 switch (format.charAt(iFormat)) {
8756 case "d":
8757 day = getNumber("d");
8758 break;
8759 case "D":
8760 getName("D", dayNamesShort, dayNames);
8761 break;
8762 case "o":
8763 doy = getNumber("o");
8764 break;
8765 case "m":
8766 month = getNumber("m");
8767 break;
8768 case "M":
8769 month = getName("M", monthNamesShort, monthNames);
8770 break;
8771 case "y":
8772 year = getNumber("y");
8773 break;
8774 case "@":
8775 date = new Date(getNumber("@"));
8776 year = date.getFullYear();
8777 month = date.getMonth() + 1;
8778 day = date.getDate();
8779 break;
8780 case "!":
8781 date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
8782 year = date.getFullYear();
8783 month = date.getMonth() + 1;
8784 day = date.getDate();
8785 break;
8786 case "'":
8787 if (lookAhead("'")){
8788 checkLiteral();
8789 } else {
8790 literal = true;
8792 break;
8793 default:
8794 checkLiteral();
8799 if (iValue < value.length){
8800 extra = value.substr(iValue);
8801 if (!/^\s+/.test(extra)) {
8802 throw "Extra/unparsed characters found in date: " + extra;
8806 if (year === -1) {
8807 year = new Date().getFullYear();
8808 } else if (year < 100) {
8809 year += new Date().getFullYear() - new Date().getFullYear() % 100 +
8810 (year <= shortYearCutoff ? 0 : -100);
8813 if (doy > -1) {
8814 month = 1;
8815 day = doy;
8816 do {
8817 dim = this._getDaysInMonth(year, month - 1);
8818 if (day <= dim) {
8819 break;
8821 month++;
8822 day -= dim;
8823 } while (true);
8826 date = this._daylightSavingAdjust(new Date(year, month - 1, day));
8827 if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
8828 throw "Invalid date"; // E.g. 31/02/00
8830 return date;
8833 /* Standard date formats. */
8834 ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
8835 COOKIE: "D, dd M yy",
8836 ISO_8601: "yy-mm-dd",
8837 RFC_822: "D, d M y",
8838 RFC_850: "DD, dd-M-y",
8839 RFC_1036: "D, d M y",
8840 RFC_1123: "D, d M yy",
8841 RFC_2822: "D, d M yy",
8842 RSS: "D, d M y", // RFC 822
8843 TICKS: "!",
8844 TIMESTAMP: "@",
8845 W3C: "yy-mm-dd", // ISO 8601
8847 _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
8848 Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
8850 /* Format a date object into a string value.
8851 * The format can be combinations of the following:
8852 * d - day of month (no leading zero)
8853 * dd - day of month (two digit)
8854 * o - day of year (no leading zeros)
8855 * oo - day of year (three digit)
8856 * D - day name short
8857 * DD - day name long
8858 * m - month of year (no leading zero)
8859 * mm - month of year (two digit)
8860 * M - month name short
8861 * MM - month name long
8862 * y - year (two digit)
8863 * yy - year (four digit)
8864 * @ - Unix timestamp (ms since 01/01/1970)
8865 * ! - Windows ticks (100ns since 01/01/0001)
8866 * "..." - literal text
8867 * '' - single quote
8869 * @param format string - the desired format of the date
8870 * @param date Date - the date value to format
8871 * @param settings Object - attributes include:
8872 * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
8873 * dayNames string[7] - names of the days from Sunday (optional)
8874 * monthNamesShort string[12] - abbreviated names of the months (optional)
8875 * monthNames string[12] - names of the months (optional)
8876 * @return string - the date in the above format
8878 formatDate: function (format, date, settings) {
8879 if (!date) {
8880 return "";
8883 var iFormat,
8884 dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
8885 dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
8886 monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
8887 monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
8888 // Check whether a format character is doubled
8889 lookAhead = function(match) {
8890 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8891 if (matches) {
8892 iFormat++;
8894 return matches;
8896 // Format a number, with leading zero if necessary
8897 formatNumber = function(match, value, len) {
8898 var num = "" + value;
8899 if (lookAhead(match)) {
8900 while (num.length < len) {
8901 num = "0" + num;
8904 return num;
8906 // Format a name, short or long as requested
8907 formatName = function(match, value, shortNames, longNames) {
8908 return (lookAhead(match) ? longNames[value] : shortNames[value]);
8910 output = "",
8911 literal = false;
8913 if (date) {
8914 for (iFormat = 0; iFormat < format.length; iFormat++) {
8915 if (literal) {
8916 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8917 literal = false;
8918 } else {
8919 output += format.charAt(iFormat);
8921 } else {
8922 switch (format.charAt(iFormat)) {
8923 case "d":
8924 output += formatNumber("d", date.getDate(), 2);
8925 break;
8926 case "D":
8927 output += formatName("D", date.getDay(), dayNamesShort, dayNames);
8928 break;
8929 case "o":
8930 output += formatNumber("o",
8931 Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
8932 break;
8933 case "m":
8934 output += formatNumber("m", date.getMonth() + 1, 2);
8935 break;
8936 case "M":
8937 output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
8938 break;
8939 case "y":
8940 output += (lookAhead("y") ? date.getFullYear() :
8941 (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
8942 break;
8943 case "@":
8944 output += date.getTime();
8945 break;
8946 case "!":
8947 output += date.getTime() * 10000 + this._ticksTo1970;
8948 break;
8949 case "'":
8950 if (lookAhead("'")) {
8951 output += "'";
8952 } else {
8953 literal = true;
8955 break;
8956 default:
8957 output += format.charAt(iFormat);
8962 return output;
8965 /* Extract all possible characters from the date format. */
8966 _possibleChars: function (format) {
8967 var iFormat,
8968 chars = "",
8969 literal = false,
8970 // Check whether a format character is doubled
8971 lookAhead = function(match) {
8972 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
8973 if (matches) {
8974 iFormat++;
8976 return matches;
8979 for (iFormat = 0; iFormat < format.length; iFormat++) {
8980 if (literal) {
8981 if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
8982 literal = false;
8983 } else {
8984 chars += format.charAt(iFormat);
8986 } else {
8987 switch (format.charAt(iFormat)) {
8988 case "d": case "m": case "y": case "@":
8989 chars += "0123456789";
8990 break;
8991 case "D": case "M":
8992 return null; // Accept anything
8993 case "'":
8994 if (lookAhead("'")) {
8995 chars += "'";
8996 } else {
8997 literal = true;
8999 break;
9000 default:
9001 chars += format.charAt(iFormat);
9005 return chars;
9008 /* Get a setting value, defaulting if necessary. */
9009 _get: function(inst, name) {
9010 return inst.settings[name] !== undefined ?
9011 inst.settings[name] : this._defaults[name];
9014 /* Parse existing date and initialise date picker. */
9015 _setDateFromField: function(inst, noDefault) {
9016 if (inst.input.val() === inst.lastVal) {
9017 return;
9020 var dateFormat = this._get(inst, "dateFormat"),
9021 dates = inst.lastVal = inst.input ? inst.input.val() : null,
9022 defaultDate = this._getDefaultDate(inst),
9023 date = defaultDate,
9024 settings = this._getFormatConfig(inst);
9026 try {
9027 date = this.parseDate(dateFormat, dates, settings) || defaultDate;
9028 } catch (event) {
9029 dates = (noDefault ? "" : dates);
9031 inst.selectedDay = date.getDate();
9032 inst.drawMonth = inst.selectedMonth = date.getMonth();
9033 inst.drawYear = inst.selectedYear = date.getFullYear();
9034 inst.currentDay = (dates ? date.getDate() : 0);
9035 inst.currentMonth = (dates ? date.getMonth() : 0);
9036 inst.currentYear = (dates ? date.getFullYear() : 0);
9037 this._adjustInstDate(inst);
9040 /* Retrieve the default date shown on opening. */
9041 _getDefaultDate: function(inst) {
9042 return this._restrictMinMax(inst,
9043 this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
9046 /* A date may be specified as an exact value or a relative one. */
9047 _determineDate: function(inst, date, defaultDate) {
9048 var offsetNumeric = function(offset) {
9049 var date = new Date();
9050 date.setDate(date.getDate() + offset);
9051 return date;
9053 offsetString = function(offset) {
9054 try {
9055 return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
9056 offset, $.datepicker._getFormatConfig(inst));
9058 catch (e) {
9059 // Ignore
9062 var date = (offset.toLowerCase().match(/^c/) ?
9063 $.datepicker._getDate(inst) : null) || new Date(),
9064 year = date.getFullYear(),
9065 month = date.getMonth(),
9066 day = date.getDate(),
9067 pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
9068 matches = pattern.exec(offset);
9070 while (matches) {
9071 switch (matches[2] || "d") {
9072 case "d" : case "D" :
9073 day += parseInt(matches[1],10); break;
9074 case "w" : case "W" :
9075 day += parseInt(matches[1],10) * 7; break;
9076 case "m" : case "M" :
9077 month += parseInt(matches[1],10);
9078 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9079 break;
9080 case "y": case "Y" :
9081 year += parseInt(matches[1],10);
9082 day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
9083 break;
9085 matches = pattern.exec(offset);
9087 return new Date(year, month, day);
9089 newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
9090 (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
9092 newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
9093 if (newDate) {
9094 newDate.setHours(0);
9095 newDate.setMinutes(0);
9096 newDate.setSeconds(0);
9097 newDate.setMilliseconds(0);
9099 return this._daylightSavingAdjust(newDate);
9102 /* Handle switch to/from daylight saving.
9103 * Hours may be non-zero on daylight saving cut-over:
9104 * > 12 when midnight changeover, but then cannot generate
9105 * midnight datetime, so jump to 1AM, otherwise reset.
9106 * @param date (Date) the date to check
9107 * @return (Date) the corrected date
9109 _daylightSavingAdjust: function(date) {
9110 if (!date) {
9111 return null;
9113 date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
9114 return date;
9117 /* Set the date(s) directly. */
9118 _setDate: function(inst, date, noChange) {
9119 var clear = !date,
9120 origMonth = inst.selectedMonth,
9121 origYear = inst.selectedYear,
9122 newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
9124 inst.selectedDay = inst.currentDay = newDate.getDate();
9125 inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
9126 inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
9127 if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
9128 this._notifyChange(inst);
9130 this._adjustInstDate(inst);
9131 if (inst.input) {
9132 inst.input.val(clear ? "" : this._formatDate(inst));
9136 /* Retrieve the date(s) directly. */
9137 _getDate: function(inst) {
9138 var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
9139 this._daylightSavingAdjust(new Date(
9140 inst.currentYear, inst.currentMonth, inst.currentDay)));
9141 return startDate;
9144 /* Attach the onxxx handlers. These are declared statically so
9145 * they work with static code transformers like Caja.
9147 _attachHandlers: function(inst) {
9148 var stepMonths = this._get(inst, "stepMonths"),
9149 id = "#" + inst.id.replace( /\\\\/g, "\\" );
9150 inst.dpDiv.find("[data-handler]").map(function () {
9151 var handler = {
9152 prev: function () {
9153 $.datepicker._adjustDate(id, -stepMonths, "M");
9155 next: function () {
9156 $.datepicker._adjustDate(id, +stepMonths, "M");
9158 hide: function () {
9159 $.datepicker._hideDatepicker();
9161 today: function () {
9162 $.datepicker._gotoToday(id);
9164 selectDay: function () {
9165 $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
9166 return false;
9168 selectMonth: function () {
9169 $.datepicker._selectMonthYear(id, this, "M");
9170 return false;
9172 selectYear: function () {
9173 $.datepicker._selectMonthYear(id, this, "Y");
9174 return false;
9177 $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
9181 /* Generate the HTML for the current state of the date picker. */
9182 _generateHTML: function(inst) {
9183 var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
9184 controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
9185 monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
9186 selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
9187 cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
9188 printDate, dRow, tbody, daySettings, otherMonth, unselectable,
9189 tempDate = new Date(),
9190 today = this._daylightSavingAdjust(
9191 new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
9192 isRTL = this._get(inst, "isRTL"),
9193 showButtonPanel = this._get(inst, "showButtonPanel"),
9194 hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
9195 navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
9196 numMonths = this._getNumberOfMonths(inst),
9197 showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
9198 stepMonths = this._get(inst, "stepMonths"),
9199 isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
9200 currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
9201 new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
9202 minDate = this._getMinMaxDate(inst, "min"),
9203 maxDate = this._getMinMaxDate(inst, "max"),
9204 drawMonth = inst.drawMonth - showCurrentAtPos,
9205 drawYear = inst.drawYear;
9207 if (drawMonth < 0) {
9208 drawMonth += 12;
9209 drawYear--;
9211 if (maxDate) {
9212 maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
9213 maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
9214 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
9215 while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
9216 drawMonth--;
9217 if (drawMonth < 0) {
9218 drawMonth = 11;
9219 drawYear--;
9223 inst.drawMonth = drawMonth;
9224 inst.drawYear = drawYear;
9226 prevText = this._get(inst, "prevText");
9227 prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
9228 this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
9229 this._getFormatConfig(inst)));
9231 prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
9232 "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
9233 " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
9234 (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>"));
9236 nextText = this._get(inst, "nextText");
9237 nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
9238 this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
9239 this._getFormatConfig(inst)));
9241 next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
9242 "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
9243 " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
9244 (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>"));
9246 currentText = this._get(inst, "currentText");
9247 gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
9248 currentText = (!navigationAsDateFormat ? currentText :
9249 this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
9251 controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
9252 this._get(inst, "closeText") + "</button>" : "");
9254 buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
9255 (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
9256 ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
9258 firstDay = parseInt(this._get(inst, "firstDay"),10);
9259 firstDay = (isNaN(firstDay) ? 0 : firstDay);
9261 showWeek = this._get(inst, "showWeek");
9262 dayNames = this._get(inst, "dayNames");
9263 dayNamesMin = this._get(inst, "dayNamesMin");
9264 monthNames = this._get(inst, "monthNames");
9265 monthNamesShort = this._get(inst, "monthNamesShort");
9266 beforeShowDay = this._get(inst, "beforeShowDay");
9267 showOtherMonths = this._get(inst, "showOtherMonths");
9268 selectOtherMonths = this._get(inst, "selectOtherMonths");
9269 defaultDate = this._getDefaultDate(inst);
9270 html = "";
9271 dow;
9272 for (row = 0; row < numMonths[0]; row++) {
9273 group = "";
9274 this.maxRows = 4;
9275 for (col = 0; col < numMonths[1]; col++) {
9276 selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
9277 cornerClass = " ui-corner-all";
9278 calender = "";
9279 if (isMultiMonth) {
9280 calender += "<div class='ui-datepicker-group";
9281 if (numMonths[1] > 1) {
9282 switch (col) {
9283 case 0: calender += " ui-datepicker-group-first";
9284 cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
9285 case numMonths[1]-1: calender += " ui-datepicker-group-last";
9286 cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
9287 default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
9290 calender += "'>";
9292 calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
9293 (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
9294 (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
9295 this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
9296 row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
9297 "</div><table class='ui-datepicker-calendar'><thead>" +
9298 "<tr>";
9299 thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
9300 for (dow = 0; dow < 7; dow++) { // days of the week
9301 day = (dow + firstDay) % 7;
9302 thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
9303 "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
9305 calender += thead + "</tr></thead><tbody>";
9306 daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
9307 if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
9308 inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
9310 leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
9311 curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
9312 numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
9313 this.maxRows = numRows;
9314 printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
9315 for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
9316 calender += "<tr>";
9317 tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
9318 this._get(inst, "calculateWeek")(printDate) + "</td>");
9319 for (dow = 0; dow < 7; dow++) { // create date picker days
9320 daySettings = (beforeShowDay ?
9321 beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
9322 otherMonth = (printDate.getMonth() !== drawMonth);
9323 unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
9324 (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
9325 tbody += "<td class='" +
9326 ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
9327 (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
9328 ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
9329 (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
9330 // or defaultDate is current printedDate and defaultDate is selectedDate
9331 " " + this._dayOverClass : "") + // highlight selected day
9332 (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
9333 (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
9334 (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
9335 (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
9336 ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "&#39;") + "'" : "") + // cell title
9337 (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
9338 (otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
9339 (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
9340 (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
9341 (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
9342 (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
9343 "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
9344 printDate.setDate(printDate.getDate() + 1);
9345 printDate = this._daylightSavingAdjust(printDate);
9347 calender += tbody + "</tr>";
9349 drawMonth++;
9350 if (drawMonth > 11) {
9351 drawMonth = 0;
9352 drawYear++;
9354 calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
9355 ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
9356 group += calender;
9358 html += group;
9360 html += buttonPanel;
9361 inst._keyEvent = false;
9362 return html;
9365 /* Generate the month and year header. */
9366 _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
9367 secondary, monthNames, monthNamesShort) {
9369 var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
9370 changeMonth = this._get(inst, "changeMonth"),
9371 changeYear = this._get(inst, "changeYear"),
9372 showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
9373 html = "<div class='ui-datepicker-title'>",
9374 monthHtml = "";
9376 // month selection
9377 if (secondary || !changeMonth) {
9378 monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
9379 } else {
9380 inMinYear = (minDate && minDate.getFullYear() === drawYear);
9381 inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
9382 monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
9383 for ( month = 0; month < 12; month++) {
9384 if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
9385 monthHtml += "<option value='" + month + "'" +
9386 (month === drawMonth ? " selected='selected'" : "") +
9387 ">" + monthNamesShort[month] + "</option>";
9390 monthHtml += "</select>";
9393 if (!showMonthAfterYear) {
9394 html += monthHtml + (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "");
9397 // year selection
9398 if ( !inst.yearshtml ) {
9399 inst.yearshtml = "";
9400 if (secondary || !changeYear) {
9401 html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
9402 } else {
9403 // determine range of years to display
9404 years = this._get(inst, "yearRange").split(":");
9405 thisYear = new Date().getFullYear();
9406 determineYear = function(value) {
9407 var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
9408 (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
9409 parseInt(value, 10)));
9410 return (isNaN(year) ? thisYear : year);
9412 year = determineYear(years[0]);
9413 endYear = Math.max(year, determineYear(years[1] || ""));
9414 year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
9415 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
9416 inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
9417 for (; year <= endYear; year++) {
9418 inst.yearshtml += "<option value='" + year + "'" +
9419 (year === drawYear ? " selected='selected'" : "") +
9420 ">" + year + "</option>";
9422 inst.yearshtml += "</select>";
9424 html += inst.yearshtml;
9425 inst.yearshtml = null;
9429 html += this._get(inst, "yearSuffix");
9430 if (showMonthAfterYear) {
9431 html += (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "") + monthHtml;
9433 html += "</div>"; // Close datepicker_header
9434 return html;
9437 /* Adjust one of the date sub-fields. */
9438 _adjustInstDate: function(inst, offset, period) {
9439 var year = inst.drawYear + (period === "Y" ? offset : 0),
9440 month = inst.drawMonth + (period === "M" ? offset : 0),
9441 day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
9442 date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
9444 inst.selectedDay = date.getDate();
9445 inst.drawMonth = inst.selectedMonth = date.getMonth();
9446 inst.drawYear = inst.selectedYear = date.getFullYear();
9447 if (period === "M" || period === "Y") {
9448 this._notifyChange(inst);
9452 /* Ensure a date is within any min/max bounds. */
9453 _restrictMinMax: function(inst, date) {
9454 var minDate = this._getMinMaxDate(inst, "min"),
9455 maxDate = this._getMinMaxDate(inst, "max"),
9456 newDate = (minDate && date < minDate ? minDate : date);
9457 return (maxDate && newDate > maxDate ? maxDate : newDate);
9460 /* Notify change of month/year. */
9461 _notifyChange: function(inst) {
9462 var onChange = this._get(inst, "onChangeMonthYear");
9463 if (onChange) {
9464 onChange.apply((inst.input ? inst.input[0] : null),
9465 [inst.selectedYear, inst.selectedMonth + 1, inst]);
9469 /* Determine the number of months to show. */
9470 _getNumberOfMonths: function(inst) {
9471 var numMonths = this._get(inst, "numberOfMonths");
9472 return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
9475 /* Determine the current maximum date - ensure no time components are set. */
9476 _getMinMaxDate: function(inst, minMax) {
9477 return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
9480 /* Find the number of days in a given month. */
9481 _getDaysInMonth: function(year, month) {
9482 return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
9485 /* Find the day of the week of the first of a month. */
9486 _getFirstDayOfMonth: function(year, month) {
9487 return new Date(year, month, 1).getDay();
9490 /* Determines if we should allow a "next/prev" month display change. */
9491 _canAdjustMonth: function(inst, offset, curYear, curMonth) {
9492 var numMonths = this._getNumberOfMonths(inst),
9493 date = this._daylightSavingAdjust(new Date(curYear,
9494 curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
9496 if (offset < 0) {
9497 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
9499 return this._isInRange(inst, date);
9502 /* Is the given date in the accepted range? */
9503 _isInRange: function(inst, date) {
9504 var yearSplit, currentYear,
9505 minDate = this._getMinMaxDate(inst, "min"),
9506 maxDate = this._getMinMaxDate(inst, "max"),
9507 minYear = null,
9508 maxYear = null,
9509 years = this._get(inst, "yearRange");
9510 if (years){
9511 yearSplit = years.split(":");
9512 currentYear = new Date().getFullYear();
9513 minYear = parseInt(yearSplit[0], 10);
9514 maxYear = parseInt(yearSplit[1], 10);
9515 if ( yearSplit[0].match(/[+\-].*/) ) {
9516 minYear += currentYear;
9518 if ( yearSplit[1].match(/[+\-].*/) ) {
9519 maxYear += currentYear;
9523 return ((!minDate || date.getTime() >= minDate.getTime()) &&
9524 (!maxDate || date.getTime() <= maxDate.getTime()) &&
9525 (!minYear || date.getFullYear() >= minYear) &&
9526 (!maxYear || date.getFullYear() <= maxYear));
9529 /* Provide the configuration settings for formatting/parsing. */
9530 _getFormatConfig: function(inst) {
9531 var shortYearCutoff = this._get(inst, "shortYearCutoff");
9532 shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
9533 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
9534 return {shortYearCutoff: shortYearCutoff,
9535 dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
9536 monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
9539 /* Format the given date for display. */
9540 _formatDate: function(inst, day, month, year) {
9541 if (!day) {
9542 inst.currentDay = inst.selectedDay;
9543 inst.currentMonth = inst.selectedMonth;
9544 inst.currentYear = inst.selectedYear;
9546 var date = (day ? (typeof day === "object" ? day :
9547 this._daylightSavingAdjust(new Date(year, month, day))) :
9548 this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
9549 return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
9554 * Bind hover events for datepicker elements.
9555 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
9556 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
9558 function bindHover(dpDiv) {
9559 var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
9560 return dpDiv.delegate(selector, "mouseout", function() {
9561 $(this).removeClass("ui-state-hover");
9562 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9563 $(this).removeClass("ui-datepicker-prev-hover");
9565 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9566 $(this).removeClass("ui-datepicker-next-hover");
9569 .delegate(selector, "mouseover", function(){
9570 if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
9571 $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
9572 $(this).addClass("ui-state-hover");
9573 if (this.className.indexOf("ui-datepicker-prev") !== -1) {
9574 $(this).addClass("ui-datepicker-prev-hover");
9576 if (this.className.indexOf("ui-datepicker-next") !== -1) {
9577 $(this).addClass("ui-datepicker-next-hover");
9583 /* jQuery extend now ignores nulls! */
9584 function extendRemove(target, props) {
9585 $.extend(target, props);
9586 for (var name in props) {
9587 if (props[name] == null) {
9588 target[name] = props[name];
9591 return target;
9594 /* Invoke the datepicker functionality.
9595 @param options string - a command, optionally followed by additional parameters or
9596 Object - settings for attaching new datepicker functionality
9597 @return jQuery object */
9598 $.fn.datepicker = function(options){
9600 /* Verify an empty collection wasn't passed - Fixes #6976 */
9601 if ( !this.length ) {
9602 return this;
9605 /* Initialise the date picker. */
9606 if (!$.datepicker.initialized) {
9607 $(document).mousedown($.datepicker._checkExternalClick);
9608 $.datepicker.initialized = true;
9611 /* Append datepicker main container to body if not exist. */
9612 if ($("#"+$.datepicker._mainDivId).length === 0) {
9613 $("body").append($.datepicker.dpDiv);
9616 var otherArgs = Array.prototype.slice.call(arguments, 1);
9617 if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
9618 return $.datepicker["_" + options + "Datepicker"].
9619 apply($.datepicker, [this[0]].concat(otherArgs));
9621 if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
9622 return $.datepicker["_" + options + "Datepicker"].
9623 apply($.datepicker, [this[0]].concat(otherArgs));
9625 return this.each(function() {
9626 typeof options === "string" ?
9627 $.datepicker["_" + options + "Datepicker"].
9628 apply($.datepicker, [this].concat(otherArgs)) :
9629 $.datepicker._attachDatepicker(this, options);
9633 $.datepicker = new Datepicker(); // singleton instance
9634 $.datepicker.initialized = false;
9635 $.datepicker.uuid = new Date().getTime();
9636 $.datepicker.version = "1.10.3";
9638 })(jQuery);
9640 (function( $, undefined ) {
9642 var sizeRelatedOptions = {
9643 buttons: true,
9644 height: true,
9645 maxHeight: true,
9646 maxWidth: true,
9647 minHeight: true,
9648 minWidth: true,
9649 width: true
9651 resizableRelatedOptions = {
9652 maxHeight: true,
9653 maxWidth: true,
9654 minHeight: true,
9655 minWidth: true
9658 $.widget( "ui.dialog", {
9659 version: "1.10.3",
9660 options: {
9661 appendTo: "body",
9662 autoOpen: true,
9663 buttons: [],
9664 closeOnEscape: true,
9665 closeText: "close",
9666 dialogClass: "",
9667 draggable: true,
9668 hide: null,
9669 height: "auto",
9670 maxHeight: null,
9671 maxWidth: null,
9672 minHeight: 150,
9673 minWidth: 150,
9674 modal: false,
9675 position: {
9676 my: "center",
9677 at: "center",
9678 of: window,
9679 collision: "fit",
9680 // Ensure the titlebar is always visible
9681 using: function( pos ) {
9682 var topOffset = $( this ).css( pos ).offset().top;
9683 if ( topOffset < 0 ) {
9684 $( this ).css( "top", pos.top - topOffset );
9688 resizable: true,
9689 show: null,
9690 title: null,
9691 width: 300,
9693 // callbacks
9694 beforeClose: null,
9695 close: null,
9696 drag: null,
9697 dragStart: null,
9698 dragStop: null,
9699 focus: null,
9700 open: null,
9701 resize: null,
9702 resizeStart: null,
9703 resizeStop: null
9706 _create: function() {
9707 this.originalCss = {
9708 display: this.element[0].style.display,
9709 width: this.element[0].style.width,
9710 minHeight: this.element[0].style.minHeight,
9711 maxHeight: this.element[0].style.maxHeight,
9712 height: this.element[0].style.height
9714 this.originalPosition = {
9715 parent: this.element.parent(),
9716 index: this.element.parent().children().index( this.element )
9718 this.originalTitle = this.element.attr("title");
9719 this.options.title = this.options.title || this.originalTitle;
9721 this._createWrapper();
9723 this.element
9724 .show()
9725 .removeAttr("title")
9726 .addClass("ui-dialog-content ui-widget-content")
9727 .appendTo( this.uiDialog );
9729 this._createTitlebar();
9730 this._createButtonPane();
9732 if ( this.options.draggable && $.fn.draggable ) {
9733 this._makeDraggable();
9735 if ( this.options.resizable && $.fn.resizable ) {
9736 this._makeResizable();
9739 this._isOpen = false;
9742 _init: function() {
9743 if ( this.options.autoOpen ) {
9744 this.open();
9748 _appendTo: function() {
9749 var element = this.options.appendTo;
9750 if ( element && (element.jquery || element.nodeType) ) {
9751 return $( element );
9753 return this.document.find( element || "body" ).eq( 0 );
9756 _destroy: function() {
9757 var next,
9758 originalPosition = this.originalPosition;
9760 this._destroyOverlay();
9762 this.element
9763 .removeUniqueId()
9764 .removeClass("ui-dialog-content ui-widget-content")
9765 .css( this.originalCss )
9766 // Without detaching first, the following becomes really slow
9767 .detach();
9769 this.uiDialog.stop( true, true ).remove();
9771 if ( this.originalTitle ) {
9772 this.element.attr( "title", this.originalTitle );
9775 next = originalPosition.parent.children().eq( originalPosition.index );
9776 // Don't try to place the dialog next to itself (#8613)
9777 if ( next.length && next[0] !== this.element[0] ) {
9778 next.before( this.element );
9779 } else {
9780 originalPosition.parent.append( this.element );
9784 widget: function() {
9785 return this.uiDialog;
9788 disable: $.noop,
9789 enable: $.noop,
9791 close: function( event ) {
9792 var that = this;
9794 if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
9795 return;
9798 this._isOpen = false;
9799 this._destroyOverlay();
9801 if ( !this.opener.filter(":focusable").focus().length ) {
9802 // Hiding a focused element doesn't trigger blur in WebKit
9803 // so in case we have nothing to focus on, explicitly blur the active element
9804 // https://bugs.webkit.org/show_bug.cgi?id=47182
9805 $( this.document[0].activeElement ).blur();
9808 this._hide( this.uiDialog, this.options.hide, function() {
9809 that._trigger( "close", event );
9813 isOpen: function() {
9814 return this._isOpen;
9817 moveToTop: function() {
9818 this._moveToTop();
9821 _moveToTop: function( event, silent ) {
9822 var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
9823 if ( moved && !silent ) {
9824 this._trigger( "focus", event );
9826 return moved;
9829 open: function() {
9830 var that = this;
9831 if ( this._isOpen ) {
9832 if ( this._moveToTop() ) {
9833 this._focusTabbable();
9835 return;
9838 this._isOpen = true;
9839 this.opener = $( this.document[0].activeElement );
9841 this._size();
9842 this._position();
9843 this._createOverlay();
9844 this._moveToTop( null, true );
9845 this._show( this.uiDialog, this.options.show, function() {
9846 that._focusTabbable();
9847 that._trigger("focus");
9850 this._trigger("open");
9853 _focusTabbable: function() {
9854 // Set focus to the first match:
9855 // 1. First element inside the dialog matching [autofocus]
9856 // 2. Tabbable element inside the content element
9857 // 3. Tabbable element inside the buttonpane
9858 // 4. The close button
9859 // 5. The dialog itself
9860 var hasFocus = this.element.find("[autofocus]");
9861 if ( !hasFocus.length ) {
9862 hasFocus = this.element.find(":tabbable");
9864 if ( !hasFocus.length ) {
9865 hasFocus = this.uiDialogButtonPane.find(":tabbable");
9867 if ( !hasFocus.length ) {
9868 hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
9870 if ( !hasFocus.length ) {
9871 hasFocus = this.uiDialog;
9873 hasFocus.eq( 0 ).focus();
9876 _keepFocus: function( event ) {
9877 function checkFocus() {
9878 var activeElement = this.document[0].activeElement,
9879 isActive = this.uiDialog[0] === activeElement ||
9880 $.contains( this.uiDialog[0], activeElement );
9881 if ( !isActive ) {
9882 this._focusTabbable();
9885 event.preventDefault();
9886 checkFocus.call( this );
9887 // support: IE
9888 // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
9889 // so we check again later
9890 this._delay( checkFocus );
9893 _createWrapper: function() {
9894 this.uiDialog = $("<div>")
9895 .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
9896 this.options.dialogClass )
9897 .hide()
9898 .attr({
9899 // Setting tabIndex makes the div focusable
9900 tabIndex: -1,
9901 role: "dialog"
9903 .appendTo( this._appendTo() );
9905 this._on( this.uiDialog, {
9906 keydown: function( event ) {
9907 if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
9908 event.keyCode === $.ui.keyCode.ESCAPE ) {
9909 event.preventDefault();
9910 this.close( event );
9911 return;
9914 // prevent tabbing out of dialogs
9915 if ( event.keyCode !== $.ui.keyCode.TAB ) {
9916 return;
9918 var tabbables = this.uiDialog.find(":tabbable"),
9919 first = tabbables.filter(":first"),
9920 last = tabbables.filter(":last");
9922 if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
9923 first.focus( 1 );
9924 event.preventDefault();
9925 } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
9926 last.focus( 1 );
9927 event.preventDefault();
9930 mousedown: function( event ) {
9931 if ( this._moveToTop( event ) ) {
9932 this._focusTabbable();
9937 // We assume that any existing aria-describedby attribute means
9938 // that the dialog content is marked up properly
9939 // otherwise we brute force the content as the description
9940 if ( !this.element.find("[aria-describedby]").length ) {
9941 this.uiDialog.attr({
9942 "aria-describedby": this.element.uniqueId().attr("id")
9947 _createTitlebar: function() {
9948 var uiDialogTitle;
9950 this.uiDialogTitlebar = $("<div>")
9951 .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
9952 .prependTo( this.uiDialog );
9953 this._on( this.uiDialogTitlebar, {
9954 mousedown: function( event ) {
9955 // Don't prevent click on close button (#8838)
9956 // Focusing a dialog that is partially scrolled out of view
9957 // causes the browser to scroll it into view, preventing the click event
9958 if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
9959 // Dialog isn't getting focus when dragging (#8063)
9960 this.uiDialog.focus();
9965 this.uiDialogTitlebarClose = $("<button></button>")
9966 .button({
9967 label: this.options.closeText,
9968 icons: {
9969 primary: "ui-icon-closethick"
9971 text: false
9973 .addClass("ui-dialog-titlebar-close")
9974 .appendTo( this.uiDialogTitlebar );
9975 this._on( this.uiDialogTitlebarClose, {
9976 click: function( event ) {
9977 event.preventDefault();
9978 this.close( event );
9982 uiDialogTitle = $("<span>")
9983 .uniqueId()
9984 .addClass("ui-dialog-title")
9985 .prependTo( this.uiDialogTitlebar );
9986 this._title( uiDialogTitle );
9988 this.uiDialog.attr({
9989 "aria-labelledby": uiDialogTitle.attr("id")
9993 _title: function( title ) {
9994 if ( !this.options.title ) {
9995 title.html("&#160;");
9997 title.text( this.options.title );
10000 _createButtonPane: function() {
10001 this.uiDialogButtonPane = $("<div>")
10002 .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
10004 this.uiButtonSet = $("<div>")
10005 .addClass("ui-dialog-buttonset")
10006 .appendTo( this.uiDialogButtonPane );
10008 this._createButtons();
10011 _createButtons: function() {
10012 var that = this,
10013 buttons = this.options.buttons;
10015 // if we already have a button pane, remove it
10016 this.uiDialogButtonPane.remove();
10017 this.uiButtonSet.empty();
10019 if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
10020 this.uiDialog.removeClass("ui-dialog-buttons");
10021 return;
10024 $.each( buttons, function( name, props ) {
10025 var click, buttonOptions;
10026 props = $.isFunction( props ) ?
10027 { click: props, text: name } :
10028 props;
10029 // Default to a non-submitting button
10030 props = $.extend( { type: "button" }, props );
10031 // Change the context for the click callback to be the main element
10032 click = props.click;
10033 props.click = function() {
10034 click.apply( that.element[0], arguments );
10036 buttonOptions = {
10037 icons: props.icons,
10038 text: props.showText
10040 delete props.icons;
10041 delete props.showText;
10042 $( "<button></button>", props )
10043 .button( buttonOptions )
10044 .appendTo( that.uiButtonSet );
10046 this.uiDialog.addClass("ui-dialog-buttons");
10047 this.uiDialogButtonPane.appendTo( this.uiDialog );
10050 _makeDraggable: function() {
10051 var that = this,
10052 options = this.options;
10054 function filteredUi( ui ) {
10055 return {
10056 position: ui.position,
10057 offset: ui.offset
10061 this.uiDialog.draggable({
10062 cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
10063 handle: ".ui-dialog-titlebar",
10064 containment: "document",
10065 start: function( event, ui ) {
10066 $( this ).addClass("ui-dialog-dragging");
10067 that._blockFrames();
10068 that._trigger( "dragStart", event, filteredUi( ui ) );
10070 drag: function( event, ui ) {
10071 that._trigger( "drag", event, filteredUi( ui ) );
10073 stop: function( event, ui ) {
10074 options.position = [
10075 ui.position.left - that.document.scrollLeft(),
10076 ui.position.top - that.document.scrollTop()
10078 $( this ).removeClass("ui-dialog-dragging");
10079 that._unblockFrames();
10080 that._trigger( "dragStop", event, filteredUi( ui ) );
10085 _makeResizable: function() {
10086 var that = this,
10087 options = this.options,
10088 handles = options.resizable,
10089 // .ui-resizable has position: relative defined in the stylesheet
10090 // but dialogs have to use absolute or fixed positioning
10091 position = this.uiDialog.css("position"),
10092 resizeHandles = typeof handles === "string" ?
10093 handles :
10094 "n,e,s,w,se,sw,ne,nw";
10096 function filteredUi( ui ) {
10097 return {
10098 originalPosition: ui.originalPosition,
10099 originalSize: ui.originalSize,
10100 position: ui.position,
10101 size: ui.size
10105 this.uiDialog.resizable({
10106 cancel: ".ui-dialog-content",
10107 containment: "document",
10108 alsoResize: this.element,
10109 maxWidth: options.maxWidth,
10110 maxHeight: options.maxHeight,
10111 minWidth: options.minWidth,
10112 minHeight: this._minHeight(),
10113 handles: resizeHandles,
10114 start: function( event, ui ) {
10115 $( this ).addClass("ui-dialog-resizing");
10116 that._blockFrames();
10117 that._trigger( "resizeStart", event, filteredUi( ui ) );
10119 resize: function( event, ui ) {
10120 that._trigger( "resize", event, filteredUi( ui ) );
10122 stop: function( event, ui ) {
10123 options.height = $( this ).height();
10124 options.width = $( this ).width();
10125 $( this ).removeClass("ui-dialog-resizing");
10126 that._unblockFrames();
10127 that._trigger( "resizeStop", event, filteredUi( ui ) );
10130 .css( "position", position );
10133 _minHeight: function() {
10134 var options = this.options;
10136 return options.height === "auto" ?
10137 options.minHeight :
10138 Math.min( options.minHeight, options.height );
10141 _position: function() {
10142 // Need to show the dialog to get the actual offset in the position plugin
10143 var isVisible = this.uiDialog.is(":visible");
10144 if ( !isVisible ) {
10145 this.uiDialog.show();
10147 this.uiDialog.position( this.options.position );
10148 if ( !isVisible ) {
10149 this.uiDialog.hide();
10153 _setOptions: function( options ) {
10154 var that = this,
10155 resize = false,
10156 resizableOptions = {};
10158 $.each( options, function( key, value ) {
10159 that._setOption( key, value );
10161 if ( key in sizeRelatedOptions ) {
10162 resize = true;
10164 if ( key in resizableRelatedOptions ) {
10165 resizableOptions[ key ] = value;
10169 if ( resize ) {
10170 this._size();
10171 this._position();
10173 if ( this.uiDialog.is(":data(ui-resizable)") ) {
10174 this.uiDialog.resizable( "option", resizableOptions );
10178 _setOption: function( key, value ) {
10179 /*jshint maxcomplexity:15*/
10180 var isDraggable, isResizable,
10181 uiDialog = this.uiDialog;
10183 if ( key === "dialogClass" ) {
10184 uiDialog
10185 .removeClass( this.options.dialogClass )
10186 .addClass( value );
10189 if ( key === "disabled" ) {
10190 return;
10193 this._super( key, value );
10195 if ( key === "appendTo" ) {
10196 this.uiDialog.appendTo( this._appendTo() );
10199 if ( key === "buttons" ) {
10200 this._createButtons();
10203 if ( key === "closeText" ) {
10204 this.uiDialogTitlebarClose.button({
10205 // Ensure that we always pass a string
10206 label: "" + value
10210 if ( key === "draggable" ) {
10211 isDraggable = uiDialog.is(":data(ui-draggable)");
10212 if ( isDraggable && !value ) {
10213 uiDialog.draggable("destroy");
10216 if ( !isDraggable && value ) {
10217 this._makeDraggable();
10221 if ( key === "position" ) {
10222 this._position();
10225 if ( key === "resizable" ) {
10226 // currently resizable, becoming non-resizable
10227 isResizable = uiDialog.is(":data(ui-resizable)");
10228 if ( isResizable && !value ) {
10229 uiDialog.resizable("destroy");
10232 // currently resizable, changing handles
10233 if ( isResizable && typeof value === "string" ) {
10234 uiDialog.resizable( "option", "handles", value );
10237 // currently non-resizable, becoming resizable
10238 if ( !isResizable && value !== false ) {
10239 this._makeResizable();
10243 if ( key === "title" ) {
10244 this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
10248 _size: function() {
10249 // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
10250 // divs will both have width and height set, so we need to reset them
10251 var nonContentHeight, minContentHeight, maxContentHeight,
10252 options = this.options;
10254 // Reset content sizing
10255 this.element.show().css({
10256 width: "auto",
10257 minHeight: 0,
10258 maxHeight: "none",
10259 height: 0
10262 if ( options.minWidth > options.width ) {
10263 options.width = options.minWidth;
10266 // reset wrapper sizing
10267 // determine the height of all the non-content elements
10268 nonContentHeight = this.uiDialog.css({
10269 height: "auto",
10270 width: options.width
10272 .outerHeight();
10273 minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
10274 maxContentHeight = typeof options.maxHeight === "number" ?
10275 Math.max( 0, options.maxHeight - nonContentHeight ) :
10276 "none";
10278 if ( options.height === "auto" ) {
10279 this.element.css({
10280 minHeight: minContentHeight,
10281 maxHeight: maxContentHeight,
10282 height: "auto"
10284 } else {
10285 this.element.height( Math.max( 0, options.height - nonContentHeight ) );
10288 if (this.uiDialog.is(":data(ui-resizable)") ) {
10289 this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
10293 _blockFrames: function() {
10294 this.iframeBlocks = this.document.find( "iframe" ).map(function() {
10295 var iframe = $( this );
10297 return $( "<div>" )
10298 .css({
10299 position: "absolute",
10300 width: iframe.outerWidth(),
10301 height: iframe.outerHeight()
10303 .appendTo( iframe.parent() )
10304 .offset( iframe.offset() )[0];
10308 _unblockFrames: function() {
10309 if ( this.iframeBlocks ) {
10310 this.iframeBlocks.remove();
10311 delete this.iframeBlocks;
10315 _allowInteraction: function( event ) {
10316 if ( $( event.target ).closest(".ui-dialog").length ) {
10317 return true;
10320 // TODO: Remove hack when datepicker implements
10321 // the .ui-front logic (#8989)
10322 return !!$( event.target ).closest(".ui-datepicker").length;
10325 _createOverlay: function() {
10326 if ( !this.options.modal ) {
10327 return;
10330 var that = this,
10331 widgetFullName = this.widgetFullName;
10332 if ( !$.ui.dialog.overlayInstances ) {
10333 // Prevent use of anchors and inputs.
10334 // We use a delay in case the overlay is created from an
10335 // event that we're going to be cancelling. (#2804)
10336 this._delay(function() {
10337 // Handle .dialog().dialog("close") (#4065)
10338 if ( $.ui.dialog.overlayInstances ) {
10339 this.document.bind( "focusin.dialog", function( event ) {
10340 if ( !that._allowInteraction( event ) ) {
10341 event.preventDefault();
10342 $(".ui-dialog:visible:last .ui-dialog-content")
10343 .data( widgetFullName )._focusTabbable();
10350 this.overlay = $("<div>")
10351 .addClass("ui-widget-overlay ui-front")
10352 .appendTo( this._appendTo() );
10353 this._on( this.overlay, {
10354 mousedown: "_keepFocus"
10356 $.ui.dialog.overlayInstances++;
10359 _destroyOverlay: function() {
10360 if ( !this.options.modal ) {
10361 return;
10364 if ( this.overlay ) {
10365 $.ui.dialog.overlayInstances--;
10367 if ( !$.ui.dialog.overlayInstances ) {
10368 this.document.unbind( "focusin.dialog" );
10370 this.overlay.remove();
10371 this.overlay = null;
10376 $.ui.dialog.overlayInstances = 0;
10378 // DEPRECATED
10379 if ( $.uiBackCompat !== false ) {
10380 // position option with array notation
10381 // just override with old implementation
10382 $.widget( "ui.dialog", $.ui.dialog, {
10383 _position: function() {
10384 var position = this.options.position,
10385 myAt = [],
10386 offset = [ 0, 0 ],
10387 isVisible;
10389 if ( position ) {
10390 if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
10391 myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
10392 if ( myAt.length === 1 ) {
10393 myAt[1] = myAt[0];
10396 $.each( [ "left", "top" ], function( i, offsetPosition ) {
10397 if ( +myAt[ i ] === myAt[ i ] ) {
10398 offset[ i ] = myAt[ i ];
10399 myAt[ i ] = offsetPosition;
10403 position = {
10404 my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
10405 myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
10406 at: myAt.join(" ")
10410 position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
10411 } else {
10412 position = $.ui.dialog.prototype.options.position;
10415 // need to show the dialog to get the actual offset in the position plugin
10416 isVisible = this.uiDialog.is(":visible");
10417 if ( !isVisible ) {
10418 this.uiDialog.show();
10420 this.uiDialog.position( position );
10421 if ( !isVisible ) {
10422 this.uiDialog.hide();
10428 }( jQuery ) );
10430 (function( $, undefined ) {
10432 var rvertical = /up|down|vertical/,
10433 rpositivemotion = /up|left|vertical|horizontal/;
10435 $.effects.effect.blind = function( o, done ) {
10436 // Create element
10437 var el = $( this ),
10438 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10439 mode = $.effects.setMode( el, o.mode || "hide" ),
10440 direction = o.direction || "up",
10441 vertical = rvertical.test( direction ),
10442 ref = vertical ? "height" : "width",
10443 ref2 = vertical ? "top" : "left",
10444 motion = rpositivemotion.test( direction ),
10445 animation = {},
10446 show = mode === "show",
10447 wrapper, distance, margin;
10449 // if already wrapped, the wrapper's properties are my property. #6245
10450 if ( el.parent().is( ".ui-effects-wrapper" ) ) {
10451 $.effects.save( el.parent(), props );
10452 } else {
10453 $.effects.save( el, props );
10455 el.show();
10456 wrapper = $.effects.createWrapper( el ).css({
10457 overflow: "hidden"
10460 distance = wrapper[ ref ]();
10461 margin = parseFloat( wrapper.css( ref2 ) ) || 0;
10463 animation[ ref ] = show ? distance : 0;
10464 if ( !motion ) {
10466 .css( vertical ? "bottom" : "right", 0 )
10467 .css( vertical ? "top" : "left", "auto" )
10468 .css({ position: "absolute" });
10470 animation[ ref2 ] = show ? margin : distance + margin;
10473 // start at 0 if we are showing
10474 if ( show ) {
10475 wrapper.css( ref, 0 );
10476 if ( ! motion ) {
10477 wrapper.css( ref2, margin + distance );
10481 // Animate
10482 wrapper.animate( animation, {
10483 duration: o.duration,
10484 easing: o.easing,
10485 queue: false,
10486 complete: function() {
10487 if ( mode === "hide" ) {
10488 el.hide();
10490 $.effects.restore( el, props );
10491 $.effects.removeWrapper( el );
10492 done();
10498 })(jQuery);
10500 (function( $, undefined ) {
10502 $.effects.effect.bounce = function( o, done ) {
10503 var el = $( this ),
10504 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10506 // defaults:
10507 mode = $.effects.setMode( el, o.mode || "effect" ),
10508 hide = mode === "hide",
10509 show = mode === "show",
10510 direction = o.direction || "up",
10511 distance = o.distance,
10512 times = o.times || 5,
10514 // number of internal animations
10515 anims = times * 2 + ( show || hide ? 1 : 0 ),
10516 speed = o.duration / anims,
10517 easing = o.easing,
10519 // utility:
10520 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10521 motion = ( direction === "up" || direction === "left" ),
10523 upAnim,
10524 downAnim,
10526 // we will need to re-assemble the queue to stack our animations in place
10527 queue = el.queue(),
10528 queuelen = queue.length;
10530 // Avoid touching opacity to prevent clearType and PNG issues in IE
10531 if ( show || hide ) {
10532 props.push( "opacity" );
10535 $.effects.save( el, props );
10536 el.show();
10537 $.effects.createWrapper( el ); // Create Wrapper
10539 // default distance for the BIGGEST bounce is the outer Distance / 3
10540 if ( !distance ) {
10541 distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
10544 if ( show ) {
10545 downAnim = { opacity: 1 };
10546 downAnim[ ref ] = 0;
10548 // if we are showing, force opacity 0 and set the initial position
10549 // then do the "first" animation
10550 el.css( "opacity", 0 )
10551 .css( ref, motion ? -distance * 2 : distance * 2 )
10552 .animate( downAnim, speed, easing );
10555 // start at the smallest distance if we are hiding
10556 if ( hide ) {
10557 distance = distance / Math.pow( 2, times - 1 );
10560 downAnim = {};
10561 downAnim[ ref ] = 0;
10562 // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
10563 for ( i = 0; i < times; i++ ) {
10564 upAnim = {};
10565 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10567 el.animate( upAnim, speed, easing )
10568 .animate( downAnim, speed, easing );
10570 distance = hide ? distance * 2 : distance / 2;
10573 // Last Bounce when Hiding
10574 if ( hide ) {
10575 upAnim = { opacity: 0 };
10576 upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
10578 el.animate( upAnim, speed, easing );
10581 el.queue(function() {
10582 if ( hide ) {
10583 el.hide();
10585 $.effects.restore( el, props );
10586 $.effects.removeWrapper( el );
10587 done();
10590 // inject all the animations we just queued to be first in line (after "inprogress")
10591 if ( queuelen > 1) {
10592 queue.splice.apply( queue,
10593 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10595 el.dequeue();
10599 })(jQuery);
10601 (function( $, undefined ) {
10603 $.effects.effect.clip = function( o, done ) {
10604 // Create element
10605 var el = $( this ),
10606 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10607 mode = $.effects.setMode( el, o.mode || "hide" ),
10608 show = mode === "show",
10609 direction = o.direction || "vertical",
10610 vert = direction === "vertical",
10611 size = vert ? "height" : "width",
10612 position = vert ? "top" : "left",
10613 animation = {},
10614 wrapper, animate, distance;
10616 // Save & Show
10617 $.effects.save( el, props );
10618 el.show();
10620 // Create Wrapper
10621 wrapper = $.effects.createWrapper( el ).css({
10622 overflow: "hidden"
10624 animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
10625 distance = animate[ size ]();
10627 // Shift
10628 if ( show ) {
10629 animate.css( size, 0 );
10630 animate.css( position, distance / 2 );
10633 // Create Animation Object:
10634 animation[ size ] = show ? distance : 0;
10635 animation[ position ] = show ? 0 : distance / 2;
10637 // Animate
10638 animate.animate( animation, {
10639 queue: false,
10640 duration: o.duration,
10641 easing: o.easing,
10642 complete: function() {
10643 if ( !show ) {
10644 el.hide();
10646 $.effects.restore( el, props );
10647 $.effects.removeWrapper( el );
10648 done();
10654 })(jQuery);
10656 (function( $, undefined ) {
10658 $.effects.effect.drop = function( o, done ) {
10660 var el = $( this ),
10661 props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
10662 mode = $.effects.setMode( el, o.mode || "hide" ),
10663 show = mode === "show",
10664 direction = o.direction || "left",
10665 ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
10666 motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
10667 animation = {
10668 opacity: show ? 1 : 0
10670 distance;
10672 // Adjust
10673 $.effects.save( el, props );
10674 el.show();
10675 $.effects.createWrapper( el );
10677 distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
10679 if ( show ) {
10681 .css( "opacity", 0 )
10682 .css( ref, motion === "pos" ? -distance : distance );
10685 // Animation
10686 animation[ ref ] = ( show ?
10687 ( motion === "pos" ? "+=" : "-=" ) :
10688 ( motion === "pos" ? "-=" : "+=" ) ) +
10689 distance;
10691 // Animate
10692 el.animate( animation, {
10693 queue: false,
10694 duration: o.duration,
10695 easing: o.easing,
10696 complete: function() {
10697 if ( mode === "hide" ) {
10698 el.hide();
10700 $.effects.restore( el, props );
10701 $.effects.removeWrapper( el );
10702 done();
10707 })(jQuery);
10709 (function( $, undefined ) {
10711 $.effects.effect.explode = function( o, done ) {
10713 var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
10714 cells = rows,
10715 el = $( this ),
10716 mode = $.effects.setMode( el, o.mode || "hide" ),
10717 show = mode === "show",
10719 // show and then visibility:hidden the element before calculating offset
10720 offset = el.show().css( "visibility", "hidden" ).offset(),
10722 // width and height of a piece
10723 width = Math.ceil( el.outerWidth() / cells ),
10724 height = Math.ceil( el.outerHeight() / rows ),
10725 pieces = [],
10727 // loop
10728 i, j, left, top, mx, my;
10730 // children animate complete:
10731 function childComplete() {
10732 pieces.push( this );
10733 if ( pieces.length === rows * cells ) {
10734 animComplete();
10738 // clone the element for each row and cell.
10739 for( i = 0; i < rows ; i++ ) { // ===>
10740 top = offset.top + i * height;
10741 my = i - ( rows - 1 ) / 2 ;
10743 for( j = 0; j < cells ; j++ ) { // |||
10744 left = offset.left + j * width;
10745 mx = j - ( cells - 1 ) / 2 ;
10747 // Create a clone of the now hidden main element that will be absolute positioned
10748 // within a wrapper div off the -left and -top equal to size of our pieces
10750 .clone()
10751 .appendTo( "body" )
10752 .wrap( "<div></div>" )
10753 .css({
10754 position: "absolute",
10755 visibility: "visible",
10756 left: -j * width,
10757 top: -i * height
10760 // select the wrapper - make it overflow: hidden and absolute positioned based on
10761 // where the original was located +left and +top equal to the size of pieces
10762 .parent()
10763 .addClass( "ui-effects-explode" )
10764 .css({
10765 position: "absolute",
10766 overflow: "hidden",
10767 width: width,
10768 height: height,
10769 left: left + ( show ? mx * width : 0 ),
10770 top: top + ( show ? my * height : 0 ),
10771 opacity: show ? 0 : 1
10772 }).animate({
10773 left: left + ( show ? 0 : mx * width ),
10774 top: top + ( show ? 0 : my * height ),
10775 opacity: show ? 1 : 0
10776 }, o.duration || 500, o.easing, childComplete );
10780 function animComplete() {
10781 el.css({
10782 visibility: "visible"
10784 $( pieces ).remove();
10785 if ( !show ) {
10786 el.hide();
10788 done();
10792 })(jQuery);
10794 (function( $, undefined ) {
10796 $.effects.effect.fade = function( o, done ) {
10797 var el = $( this ),
10798 mode = $.effects.setMode( el, o.mode || "toggle" );
10800 el.animate({
10801 opacity: mode
10802 }, {
10803 queue: false,
10804 duration: o.duration,
10805 easing: o.easing,
10806 complete: done
10810 })( jQuery );
10812 (function( $, undefined ) {
10814 $.effects.effect.fold = function( o, done ) {
10816 // Create element
10817 var el = $( this ),
10818 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
10819 mode = $.effects.setMode( el, o.mode || "hide" ),
10820 show = mode === "show",
10821 hide = mode === "hide",
10822 size = o.size || 15,
10823 percent = /([0-9]+)%/.exec( size ),
10824 horizFirst = !!o.horizFirst,
10825 widthFirst = show !== horizFirst,
10826 ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
10827 duration = o.duration / 2,
10828 wrapper, distance,
10829 animation1 = {},
10830 animation2 = {};
10832 $.effects.save( el, props );
10833 el.show();
10835 // Create Wrapper
10836 wrapper = $.effects.createWrapper( el ).css({
10837 overflow: "hidden"
10839 distance = widthFirst ?
10840 [ wrapper.width(), wrapper.height() ] :
10841 [ wrapper.height(), wrapper.width() ];
10843 if ( percent ) {
10844 size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
10846 if ( show ) {
10847 wrapper.css( horizFirst ? {
10848 height: 0,
10849 width: size
10850 } : {
10851 height: size,
10852 width: 0
10856 // Animation
10857 animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
10858 animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
10860 // Animate
10861 wrapper
10862 .animate( animation1, duration, o.easing )
10863 .animate( animation2, duration, o.easing, function() {
10864 if ( hide ) {
10865 el.hide();
10867 $.effects.restore( el, props );
10868 $.effects.removeWrapper( el );
10869 done();
10874 })(jQuery);
10876 (function( $, undefined ) {
10878 $.effects.effect.highlight = function( o, done ) {
10879 var elem = $( this ),
10880 props = [ "backgroundImage", "backgroundColor", "opacity" ],
10881 mode = $.effects.setMode( elem, o.mode || "show" ),
10882 animation = {
10883 backgroundColor: elem.css( "backgroundColor" )
10886 if (mode === "hide") {
10887 animation.opacity = 0;
10890 $.effects.save( elem, props );
10892 elem
10893 .show()
10894 .css({
10895 backgroundImage: "none",
10896 backgroundColor: o.color || "#ffff99"
10898 .animate( animation, {
10899 queue: false,
10900 duration: o.duration,
10901 easing: o.easing,
10902 complete: function() {
10903 if ( mode === "hide" ) {
10904 elem.hide();
10906 $.effects.restore( elem, props );
10907 done();
10912 })(jQuery);
10914 (function( $, undefined ) {
10916 $.effects.effect.pulsate = function( o, done ) {
10917 var elem = $( this ),
10918 mode = $.effects.setMode( elem, o.mode || "show" ),
10919 show = mode === "show",
10920 hide = mode === "hide",
10921 showhide = ( show || mode === "hide" ),
10923 // showing or hiding leaves of the "last" animation
10924 anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
10925 duration = o.duration / anims,
10926 animateTo = 0,
10927 queue = elem.queue(),
10928 queuelen = queue.length,
10931 if ( show || !elem.is(":visible")) {
10932 elem.css( "opacity", 0 ).show();
10933 animateTo = 1;
10936 // anims - 1 opacity "toggles"
10937 for ( i = 1; i < anims; i++ ) {
10938 elem.animate({
10939 opacity: animateTo
10940 }, duration, o.easing );
10941 animateTo = 1 - animateTo;
10944 elem.animate({
10945 opacity: animateTo
10946 }, duration, o.easing);
10948 elem.queue(function() {
10949 if ( hide ) {
10950 elem.hide();
10952 done();
10955 // We just queued up "anims" animations, we need to put them next in the queue
10956 if ( queuelen > 1 ) {
10957 queue.splice.apply( queue,
10958 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
10960 elem.dequeue();
10963 })(jQuery);
10965 (function( $, undefined ) {
10967 $.effects.effect.puff = function( o, done ) {
10968 var elem = $( this ),
10969 mode = $.effects.setMode( elem, o.mode || "hide" ),
10970 hide = mode === "hide",
10971 percent = parseInt( o.percent, 10 ) || 150,
10972 factor = percent / 100,
10973 original = {
10974 height: elem.height(),
10975 width: elem.width(),
10976 outerHeight: elem.outerHeight(),
10977 outerWidth: elem.outerWidth()
10980 $.extend( o, {
10981 effect: "scale",
10982 queue: false,
10983 fade: true,
10984 mode: mode,
10985 complete: done,
10986 percent: hide ? percent : 100,
10987 from: hide ?
10988 original :
10990 height: original.height * factor,
10991 width: original.width * factor,
10992 outerHeight: original.outerHeight * factor,
10993 outerWidth: original.outerWidth * factor
10997 elem.effect( o );
11000 $.effects.effect.scale = function( o, done ) {
11002 // Create element
11003 var el = $( this ),
11004 options = $.extend( true, {}, o ),
11005 mode = $.effects.setMode( el, o.mode || "effect" ),
11006 percent = parseInt( o.percent, 10 ) ||
11007 ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
11008 direction = o.direction || "both",
11009 origin = o.origin,
11010 original = {
11011 height: el.height(),
11012 width: el.width(),
11013 outerHeight: el.outerHeight(),
11014 outerWidth: el.outerWidth()
11016 factor = {
11017 y: direction !== "horizontal" ? (percent / 100) : 1,
11018 x: direction !== "vertical" ? (percent / 100) : 1
11021 // We are going to pass this effect to the size effect:
11022 options.effect = "size";
11023 options.queue = false;
11024 options.complete = done;
11026 // Set default origin and restore for show/hide
11027 if ( mode !== "effect" ) {
11028 options.origin = origin || ["middle","center"];
11029 options.restore = true;
11032 options.from = o.from || ( mode === "show" ? {
11033 height: 0,
11034 width: 0,
11035 outerHeight: 0,
11036 outerWidth: 0
11037 } : original );
11038 options.to = {
11039 height: original.height * factor.y,
11040 width: original.width * factor.x,
11041 outerHeight: original.outerHeight * factor.y,
11042 outerWidth: original.outerWidth * factor.x
11045 // Fade option to support puff
11046 if ( options.fade ) {
11047 if ( mode === "show" ) {
11048 options.from.opacity = 0;
11049 options.to.opacity = 1;
11051 if ( mode === "hide" ) {
11052 options.from.opacity = 1;
11053 options.to.opacity = 0;
11057 // Animate
11058 el.effect( options );
11062 $.effects.effect.size = function( o, done ) {
11064 // Create element
11065 var original, baseline, factor,
11066 el = $( this ),
11067 props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
11069 // Always restore
11070 props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
11072 // Copy for children
11073 props2 = [ "width", "height", "overflow" ],
11074 cProps = [ "fontSize" ],
11075 vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
11076 hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
11078 // Set options
11079 mode = $.effects.setMode( el, o.mode || "effect" ),
11080 restore = o.restore || mode !== "effect",
11081 scale = o.scale || "both",
11082 origin = o.origin || [ "middle", "center" ],
11083 position = el.css( "position" ),
11084 props = restore ? props0 : props1,
11085 zero = {
11086 height: 0,
11087 width: 0,
11088 outerHeight: 0,
11089 outerWidth: 0
11092 if ( mode === "show" ) {
11093 el.show();
11095 original = {
11096 height: el.height(),
11097 width: el.width(),
11098 outerHeight: el.outerHeight(),
11099 outerWidth: el.outerWidth()
11102 if ( o.mode === "toggle" && mode === "show" ) {
11103 el.from = o.to || zero;
11104 el.to = o.from || original;
11105 } else {
11106 el.from = o.from || ( mode === "show" ? zero : original );
11107 el.to = o.to || ( mode === "hide" ? zero : original );
11110 // Set scaling factor
11111 factor = {
11112 from: {
11113 y: el.from.height / original.height,
11114 x: el.from.width / original.width
11116 to: {
11117 y: el.to.height / original.height,
11118 x: el.to.width / original.width
11122 // Scale the css box
11123 if ( scale === "box" || scale === "both" ) {
11125 // Vertical props scaling
11126 if ( factor.from.y !== factor.to.y ) {
11127 props = props.concat( vProps );
11128 el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
11129 el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
11132 // Horizontal props scaling
11133 if ( factor.from.x !== factor.to.x ) {
11134 props = props.concat( hProps );
11135 el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
11136 el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
11140 // Scale the content
11141 if ( scale === "content" || scale === "both" ) {
11143 // Vertical props scaling
11144 if ( factor.from.y !== factor.to.y ) {
11145 props = props.concat( cProps ).concat( props2 );
11146 el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
11147 el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
11151 $.effects.save( el, props );
11152 el.show();
11153 $.effects.createWrapper( el );
11154 el.css( "overflow", "hidden" ).css( el.from );
11156 // Adjust
11157 if (origin) { // Calculate baseline shifts
11158 baseline = $.effects.getBaseline( origin, original );
11159 el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
11160 el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
11161 el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
11162 el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
11164 el.css( el.from ); // set top & left
11166 // Animate
11167 if ( scale === "content" || scale === "both" ) { // Scale the children
11169 // Add margins/font-size
11170 vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
11171 hProps = hProps.concat([ "marginLeft", "marginRight" ]);
11172 props2 = props0.concat(vProps).concat(hProps);
11174 el.find( "*[width]" ).each( function(){
11175 var child = $( this ),
11176 c_original = {
11177 height: child.height(),
11178 width: child.width(),
11179 outerHeight: child.outerHeight(),
11180 outerWidth: child.outerWidth()
11182 if (restore) {
11183 $.effects.save(child, props2);
11186 child.from = {
11187 height: c_original.height * factor.from.y,
11188 width: c_original.width * factor.from.x,
11189 outerHeight: c_original.outerHeight * factor.from.y,
11190 outerWidth: c_original.outerWidth * factor.from.x
11192 child.to = {
11193 height: c_original.height * factor.to.y,
11194 width: c_original.width * factor.to.x,
11195 outerHeight: c_original.height * factor.to.y,
11196 outerWidth: c_original.width * factor.to.x
11199 // Vertical props scaling
11200 if ( factor.from.y !== factor.to.y ) {
11201 child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
11202 child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
11205 // Horizontal props scaling
11206 if ( factor.from.x !== factor.to.x ) {
11207 child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
11208 child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
11211 // Animate children
11212 child.css( child.from );
11213 child.animate( child.to, o.duration, o.easing, function() {
11215 // Restore children
11216 if ( restore ) {
11217 $.effects.restore( child, props2 );
11223 // Animate
11224 el.animate( el.to, {
11225 queue: false,
11226 duration: o.duration,
11227 easing: o.easing,
11228 complete: function() {
11229 if ( el.to.opacity === 0 ) {
11230 el.css( "opacity", el.from.opacity );
11232 if( mode === "hide" ) {
11233 el.hide();
11235 $.effects.restore( el, props );
11236 if ( !restore ) {
11238 // we need to calculate our new positioning based on the scaling
11239 if ( position === "static" ) {
11240 el.css({
11241 position: "relative",
11242 top: el.to.top,
11243 left: el.to.left
11245 } else {
11246 $.each([ "top", "left" ], function( idx, pos ) {
11247 el.css( pos, function( _, str ) {
11248 var val = parseInt( str, 10 ),
11249 toRef = idx ? el.to.left : el.to.top;
11251 // if original was "auto", recalculate the new value from wrapper
11252 if ( str === "auto" ) {
11253 return toRef + "px";
11256 return val + toRef + "px";
11262 $.effects.removeWrapper( el );
11263 done();
11269 })(jQuery);
11271 (function( $, undefined ) {
11273 $.effects.effect.shake = function( o, done ) {
11275 var el = $( this ),
11276 props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
11277 mode = $.effects.setMode( el, o.mode || "effect" ),
11278 direction = o.direction || "left",
11279 distance = o.distance || 20,
11280 times = o.times || 3,
11281 anims = times * 2 + 1,
11282 speed = Math.round(o.duration/anims),
11283 ref = (direction === "up" || direction === "down") ? "top" : "left",
11284 positiveMotion = (direction === "up" || direction === "left"),
11285 animation = {},
11286 animation1 = {},
11287 animation2 = {},
11290 // we will need to re-assemble the queue to stack our animations in place
11291 queue = el.queue(),
11292 queuelen = queue.length;
11294 $.effects.save( el, props );
11295 el.show();
11296 $.effects.createWrapper( el );
11298 // Animation
11299 animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
11300 animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
11301 animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
11303 // Animate
11304 el.animate( animation, speed, o.easing );
11306 // Shakes
11307 for ( i = 1; i < times; i++ ) {
11308 el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
11311 .animate( animation1, speed, o.easing )
11312 .animate( animation, speed / 2, o.easing )
11313 .queue(function() {
11314 if ( mode === "hide" ) {
11315 el.hide();
11317 $.effects.restore( el, props );
11318 $.effects.removeWrapper( el );
11319 done();
11322 // inject all the animations we just queued to be first in line (after "inprogress")
11323 if ( queuelen > 1) {
11324 queue.splice.apply( queue,
11325 [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
11327 el.dequeue();
11331 })(jQuery);
11333 (function( $, undefined ) {
11335 $.effects.effect.slide = function( o, done ) {
11337 // Create element
11338 var el = $( this ),
11339 props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
11340 mode = $.effects.setMode( el, o.mode || "show" ),
11341 show = mode === "show",
11342 direction = o.direction || "left",
11343 ref = (direction === "up" || direction === "down") ? "top" : "left",
11344 positiveMotion = (direction === "up" || direction === "left"),
11345 distance,
11346 animation = {};
11348 // Adjust
11349 $.effects.save( el, props );
11350 el.show();
11351 distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
11353 $.effects.createWrapper( el ).css({
11354 overflow: "hidden"
11357 if ( show ) {
11358 el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
11361 // Animation
11362 animation[ ref ] = ( show ?
11363 ( positiveMotion ? "+=" : "-=") :
11364 ( positiveMotion ? "-=" : "+=")) +
11365 distance;
11367 // Animate
11368 el.animate( animation, {
11369 queue: false,
11370 duration: o.duration,
11371 easing: o.easing,
11372 complete: function() {
11373 if ( mode === "hide" ) {
11374 el.hide();
11376 $.effects.restore( el, props );
11377 $.effects.removeWrapper( el );
11378 done();
11383 })(jQuery);
11385 (function( $, undefined ) {
11387 $.effects.effect.transfer = function( o, done ) {
11388 var elem = $( this ),
11389 target = $( o.to ),
11390 targetFixed = target.css( "position" ) === "fixed",
11391 body = $("body"),
11392 fixTop = targetFixed ? body.scrollTop() : 0,
11393 fixLeft = targetFixed ? body.scrollLeft() : 0,
11394 endPosition = target.offset(),
11395 animation = {
11396 top: endPosition.top - fixTop ,
11397 left: endPosition.left - fixLeft ,
11398 height: target.innerHeight(),
11399 width: target.innerWidth()
11401 startPosition = elem.offset(),
11402 transfer = $( "<div class='ui-effects-transfer'></div>" )
11403 .appendTo( document.body )
11404 .addClass( o.className )
11405 .css({
11406 top: startPosition.top - fixTop ,
11407 left: startPosition.left - fixLeft ,
11408 height: elem.innerHeight(),
11409 width: elem.innerWidth(),
11410 position: targetFixed ? "fixed" : "absolute"
11412 .animate( animation, o.duration, o.easing, function() {
11413 transfer.remove();
11414 done();
11418 })(jQuery);
11420 (function( $, undefined ) {
11422 $.widget( "ui.menu", {
11423 version: "1.10.3",
11424 defaultElement: "<ul>",
11425 delay: 300,
11426 options: {
11427 icons: {
11428 submenu: "ui-icon-carat-1-e"
11430 menus: "ul",
11431 position: {
11432 my: "left top",
11433 at: "right top"
11435 role: "menu",
11437 // callbacks
11438 blur: null,
11439 focus: null,
11440 select: null
11443 _create: function() {
11444 this.activeMenu = this.element;
11445 // flag used to prevent firing of the click handler
11446 // as the event bubbles up through nested menus
11447 this.mouseHandled = false;
11448 this.element
11449 .uniqueId()
11450 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11451 .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
11452 .attr({
11453 role: this.options.role,
11454 tabIndex: 0
11456 // need to catch all clicks on disabled menu
11457 // not possible through _on
11458 .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
11459 if ( this.options.disabled ) {
11460 event.preventDefault();
11462 }, this ));
11464 if ( this.options.disabled ) {
11465 this.element
11466 .addClass( "ui-state-disabled" )
11467 .attr( "aria-disabled", "true" );
11470 this._on({
11471 // Prevent focus from sticking to links inside menu after clicking
11472 // them (focus should always stay on UL during navigation).
11473 "mousedown .ui-menu-item > a": function( event ) {
11474 event.preventDefault();
11476 "click .ui-state-disabled > a": function( event ) {
11477 event.preventDefault();
11479 "click .ui-menu-item:has(a)": function( event ) {
11480 var target = $( event.target ).closest( ".ui-menu-item" );
11481 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
11482 this.mouseHandled = true;
11484 this.select( event );
11485 // Open submenu on click
11486 if ( target.has( ".ui-menu" ).length ) {
11487 this.expand( event );
11488 } else if ( !this.element.is( ":focus" ) ) {
11489 // Redirect focus to the menu
11490 this.element.trigger( "focus", [ true ] );
11492 // If the active item is on the top level, let it stay active.
11493 // Otherwise, blur the active item since it is no longer visible.
11494 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
11495 clearTimeout( this.timer );
11500 "mouseenter .ui-menu-item": function( event ) {
11501 var target = $( event.currentTarget );
11502 // Remove ui-state-active class from siblings of the newly focused menu item
11503 // to avoid a jump caused by adjacent elements both having a class with a border
11504 target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
11505 this.focus( event, target );
11507 mouseleave: "collapseAll",
11508 "mouseleave .ui-menu": "collapseAll",
11509 focus: function( event, keepActiveItem ) {
11510 // If there's already an active item, keep it active
11511 // If not, activate the first item
11512 var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
11514 if ( !keepActiveItem ) {
11515 this.focus( event, item );
11518 blur: function( event ) {
11519 this._delay(function() {
11520 if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
11521 this.collapseAll( event );
11525 keydown: "_keydown"
11528 this.refresh();
11530 // Clicks outside of a menu collapse any open menus
11531 this._on( this.document, {
11532 click: function( event ) {
11533 if ( !$( event.target ).closest( ".ui-menu" ).length ) {
11534 this.collapseAll( event );
11537 // Reset the mouseHandled flag
11538 this.mouseHandled = false;
11543 _destroy: function() {
11544 // Destroy (sub)menus
11545 this.element
11546 .removeAttr( "aria-activedescendant" )
11547 .find( ".ui-menu" ).addBack()
11548 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
11549 .removeAttr( "role" )
11550 .removeAttr( "tabIndex" )
11551 .removeAttr( "aria-labelledby" )
11552 .removeAttr( "aria-expanded" )
11553 .removeAttr( "aria-hidden" )
11554 .removeAttr( "aria-disabled" )
11555 .removeUniqueId()
11556 .show();
11558 // Destroy menu items
11559 this.element.find( ".ui-menu-item" )
11560 .removeClass( "ui-menu-item" )
11561 .removeAttr( "role" )
11562 .removeAttr( "aria-disabled" )
11563 .children( "a" )
11564 .removeUniqueId()
11565 .removeClass( "ui-corner-all ui-state-hover" )
11566 .removeAttr( "tabIndex" )
11567 .removeAttr( "role" )
11568 .removeAttr( "aria-haspopup" )
11569 .children().each( function() {
11570 var elem = $( this );
11571 if ( elem.data( "ui-menu-submenu-carat" ) ) {
11572 elem.remove();
11576 // Destroy menu dividers
11577 this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
11580 _keydown: function( event ) {
11581 /*jshint maxcomplexity:20*/
11582 var match, prev, character, skip, regex,
11583 preventDefault = true;
11585 function escape( value ) {
11586 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
11589 switch ( event.keyCode ) {
11590 case $.ui.keyCode.PAGE_UP:
11591 this.previousPage( event );
11592 break;
11593 case $.ui.keyCode.PAGE_DOWN:
11594 this.nextPage( event );
11595 break;
11596 case $.ui.keyCode.HOME:
11597 this._move( "first", "first", event );
11598 break;
11599 case $.ui.keyCode.END:
11600 this._move( "last", "last", event );
11601 break;
11602 case $.ui.keyCode.UP:
11603 this.previous( event );
11604 break;
11605 case $.ui.keyCode.DOWN:
11606 this.next( event );
11607 break;
11608 case $.ui.keyCode.LEFT:
11609 this.collapse( event );
11610 break;
11611 case $.ui.keyCode.RIGHT:
11612 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
11613 this.expand( event );
11615 break;
11616 case $.ui.keyCode.ENTER:
11617 case $.ui.keyCode.SPACE:
11618 this._activate( event );
11619 break;
11620 case $.ui.keyCode.ESCAPE:
11621 this.collapse( event );
11622 break;
11623 default:
11624 preventDefault = false;
11625 prev = this.previousFilter || "";
11626 character = String.fromCharCode( event.keyCode );
11627 skip = false;
11629 clearTimeout( this.filterTimer );
11631 if ( character === prev ) {
11632 skip = true;
11633 } else {
11634 character = prev + character;
11637 regex = new RegExp( "^" + escape( character ), "i" );
11638 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11639 return regex.test( $( this ).children( "a" ).text() );
11641 match = skip && match.index( this.active.next() ) !== -1 ?
11642 this.active.nextAll( ".ui-menu-item" ) :
11643 match;
11645 // If no matches on the current filter, reset to the last character pressed
11646 // to move down the menu to the first item that starts with that character
11647 if ( !match.length ) {
11648 character = String.fromCharCode( event.keyCode );
11649 regex = new RegExp( "^" + escape( character ), "i" );
11650 match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
11651 return regex.test( $( this ).children( "a" ).text() );
11655 if ( match.length ) {
11656 this.focus( event, match );
11657 if ( match.length > 1 ) {
11658 this.previousFilter = character;
11659 this.filterTimer = this._delay(function() {
11660 delete this.previousFilter;
11661 }, 1000 );
11662 } else {
11663 delete this.previousFilter;
11665 } else {
11666 delete this.previousFilter;
11670 if ( preventDefault ) {
11671 event.preventDefault();
11675 _activate: function( event ) {
11676 if ( !this.active.is( ".ui-state-disabled" ) ) {
11677 if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
11678 this.expand( event );
11679 } else {
11680 this.select( event );
11685 refresh: function() {
11686 var menus,
11687 icon = this.options.icons.submenu,
11688 submenus = this.element.find( this.options.menus );
11690 // Initialize nested menus
11691 submenus.filter( ":not(.ui-menu)" )
11692 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
11693 .hide()
11694 .attr({
11695 role: this.options.role,
11696 "aria-hidden": "true",
11697 "aria-expanded": "false"
11699 .each(function() {
11700 var menu = $( this ),
11701 item = menu.prev( "a" ),
11702 submenuCarat = $( "<span>" )
11703 .addClass( "ui-menu-icon ui-icon " + icon )
11704 .data( "ui-menu-submenu-carat", true );
11706 item
11707 .attr( "aria-haspopup", "true" )
11708 .prepend( submenuCarat );
11709 menu.attr( "aria-labelledby", item.attr( "id" ) );
11712 menus = submenus.add( this.element );
11714 // Don't refresh list items that are already adapted
11715 menus.children( ":not(.ui-menu-item):has(a)" )
11716 .addClass( "ui-menu-item" )
11717 .attr( "role", "presentation" )
11718 .children( "a" )
11719 .uniqueId()
11720 .addClass( "ui-corner-all" )
11721 .attr({
11722 tabIndex: -1,
11723 role: this._itemRole()
11726 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
11727 menus.children( ":not(.ui-menu-item)" ).each(function() {
11728 var item = $( this );
11729 // hyphen, em dash, en dash
11730 if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
11731 item.addClass( "ui-widget-content ui-menu-divider" );
11735 // Add aria-disabled attribute to any disabled menu item
11736 menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
11738 // If the active item has been removed, blur the menu
11739 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
11740 this.blur();
11744 _itemRole: function() {
11745 return {
11746 menu: "menuitem",
11747 listbox: "option"
11748 }[ this.options.role ];
11751 _setOption: function( key, value ) {
11752 if ( key === "icons" ) {
11753 this.element.find( ".ui-menu-icon" )
11754 .removeClass( this.options.icons.submenu )
11755 .addClass( value.submenu );
11757 this._super( key, value );
11760 focus: function( event, item ) {
11761 var nested, focused;
11762 this.blur( event, event && event.type === "focus" );
11764 this._scrollIntoView( item );
11766 this.active = item.first();
11767 focused = this.active.children( "a" ).addClass( "ui-state-focus" );
11768 // Only update aria-activedescendant if there's a role
11769 // otherwise we assume focus is managed elsewhere
11770 if ( this.options.role ) {
11771 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
11774 // Highlight active parent menu item, if any
11775 this.active
11776 .parent()
11777 .closest( ".ui-menu-item" )
11778 .children( "a:first" )
11779 .addClass( "ui-state-active" );
11781 if ( event && event.type === "keydown" ) {
11782 this._close();
11783 } else {
11784 this.timer = this._delay(function() {
11785 this._close();
11786 }, this.delay );
11789 nested = item.children( ".ui-menu" );
11790 if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
11791 this._startOpening(nested);
11793 this.activeMenu = item.parent();
11795 this._trigger( "focus", event, { item: item } );
11798 _scrollIntoView: function( item ) {
11799 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
11800 if ( this._hasScroll() ) {
11801 borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
11802 paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
11803 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
11804 scroll = this.activeMenu.scrollTop();
11805 elementHeight = this.activeMenu.height();
11806 itemHeight = item.height();
11808 if ( offset < 0 ) {
11809 this.activeMenu.scrollTop( scroll + offset );
11810 } else if ( offset + itemHeight > elementHeight ) {
11811 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
11816 blur: function( event, fromFocus ) {
11817 if ( !fromFocus ) {
11818 clearTimeout( this.timer );
11821 if ( !this.active ) {
11822 return;
11825 this.active.children( "a" ).removeClass( "ui-state-focus" );
11826 this.active = null;
11828 this._trigger( "blur", event, { item: this.active } );
11831 _startOpening: function( submenu ) {
11832 clearTimeout( this.timer );
11834 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
11835 // shift in the submenu position when mousing over the carat icon
11836 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
11837 return;
11840 this.timer = this._delay(function() {
11841 this._close();
11842 this._open( submenu );
11843 }, this.delay );
11846 _open: function( submenu ) {
11847 var position = $.extend({
11848 of: this.active
11849 }, this.options.position );
11851 clearTimeout( this.timer );
11852 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
11853 .hide()
11854 .attr( "aria-hidden", "true" );
11856 submenu
11857 .show()
11858 .removeAttr( "aria-hidden" )
11859 .attr( "aria-expanded", "true" )
11860 .position( position );
11863 collapseAll: function( event, all ) {
11864 clearTimeout( this.timer );
11865 this.timer = this._delay(function() {
11866 // If we were passed an event, look for the submenu that contains the event
11867 var currentMenu = all ? this.element :
11868 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
11870 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
11871 if ( !currentMenu.length ) {
11872 currentMenu = this.element;
11875 this._close( currentMenu );
11877 this.blur( event );
11878 this.activeMenu = currentMenu;
11879 }, this.delay );
11882 // With no arguments, closes the currently active menu - if nothing is active
11883 // it closes all menus. If passed an argument, it will search for menus BELOW
11884 _close: function( startMenu ) {
11885 if ( !startMenu ) {
11886 startMenu = this.active ? this.active.parent() : this.element;
11889 startMenu
11890 .find( ".ui-menu" )
11891 .hide()
11892 .attr( "aria-hidden", "true" )
11893 .attr( "aria-expanded", "false" )
11894 .end()
11895 .find( "a.ui-state-active" )
11896 .removeClass( "ui-state-active" );
11899 collapse: function( event ) {
11900 var newItem = this.active &&
11901 this.active.parent().closest( ".ui-menu-item", this.element );
11902 if ( newItem && newItem.length ) {
11903 this._close();
11904 this.focus( event, newItem );
11908 expand: function( event ) {
11909 var newItem = this.active &&
11910 this.active
11911 .children( ".ui-menu " )
11912 .children( ".ui-menu-item" )
11913 .first();
11915 if ( newItem && newItem.length ) {
11916 this._open( newItem.parent() );
11918 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
11919 this._delay(function() {
11920 this.focus( event, newItem );
11925 next: function( event ) {
11926 this._move( "next", "first", event );
11929 previous: function( event ) {
11930 this._move( "prev", "last", event );
11933 isFirstItem: function() {
11934 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
11937 isLastItem: function() {
11938 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
11941 _move: function( direction, filter, event ) {
11942 var next;
11943 if ( this.active ) {
11944 if ( direction === "first" || direction === "last" ) {
11945 next = this.active
11946 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
11947 .eq( -1 );
11948 } else {
11949 next = this.active
11950 [ direction + "All" ]( ".ui-menu-item" )
11951 .eq( 0 );
11954 if ( !next || !next.length || !this.active ) {
11955 next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
11958 this.focus( event, next );
11961 nextPage: function( event ) {
11962 var item, base, height;
11964 if ( !this.active ) {
11965 this.next( event );
11966 return;
11968 if ( this.isLastItem() ) {
11969 return;
11971 if ( this._hasScroll() ) {
11972 base = this.active.offset().top;
11973 height = this.element.height();
11974 this.active.nextAll( ".ui-menu-item" ).each(function() {
11975 item = $( this );
11976 return item.offset().top - base - height < 0;
11979 this.focus( event, item );
11980 } else {
11981 this.focus( event, this.activeMenu.children( ".ui-menu-item" )
11982 [ !this.active ? "first" : "last" ]() );
11986 previousPage: function( event ) {
11987 var item, base, height;
11988 if ( !this.active ) {
11989 this.next( event );
11990 return;
11992 if ( this.isFirstItem() ) {
11993 return;
11995 if ( this._hasScroll() ) {
11996 base = this.active.offset().top;
11997 height = this.element.height();
11998 this.active.prevAll( ".ui-menu-item" ).each(function() {
11999 item = $( this );
12000 return item.offset().top - base + height > 0;
12003 this.focus( event, item );
12004 } else {
12005 this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
12009 _hasScroll: function() {
12010 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
12013 select: function( event ) {
12014 // TODO: It should never be possible to not have an active item at this
12015 // point, but the tests don't trigger mouseenter before click.
12016 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
12017 var ui = { item: this.active };
12018 if ( !this.active.has( ".ui-menu" ).length ) {
12019 this.collapseAll( event, true );
12021 this._trigger( "select", event, ui );
12025 }( jQuery ));
12027 (function( $, undefined ) {
12029 $.ui = $.ui || {};
12031 var cachedScrollbarWidth,
12032 max = Math.max,
12033 abs = Math.abs,
12034 round = Math.round,
12035 rhorizontal = /left|center|right/,
12036 rvertical = /top|center|bottom/,
12037 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
12038 rposition = /^\w+/,
12039 rpercent = /%$/,
12040 _position = $.fn.position;
12042 function getOffsets( offsets, width, height ) {
12043 return [
12044 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
12045 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
12049 function parseCss( element, property ) {
12050 return parseInt( $.css( element, property ), 10 ) || 0;
12053 function getDimensions( elem ) {
12054 var raw = elem[0];
12055 if ( raw.nodeType === 9 ) {
12056 return {
12057 width: elem.width(),
12058 height: elem.height(),
12059 offset: { top: 0, left: 0 }
12062 if ( $.isWindow( raw ) ) {
12063 return {
12064 width: elem.width(),
12065 height: elem.height(),
12066 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
12069 if ( raw.preventDefault ) {
12070 return {
12071 width: 0,
12072 height: 0,
12073 offset: { top: raw.pageY, left: raw.pageX }
12076 return {
12077 width: elem.outerWidth(),
12078 height: elem.outerHeight(),
12079 offset: elem.offset()
12083 $.position = {
12084 scrollbarWidth: function() {
12085 if ( cachedScrollbarWidth !== undefined ) {
12086 return cachedScrollbarWidth;
12088 var w1, w2,
12089 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
12090 innerDiv = div.children()[0];
12092 $( "body" ).append( div );
12093 w1 = innerDiv.offsetWidth;
12094 div.css( "overflow", "scroll" );
12096 w2 = innerDiv.offsetWidth;
12098 if ( w1 === w2 ) {
12099 w2 = div[0].clientWidth;
12102 div.remove();
12104 return (cachedScrollbarWidth = w1 - w2);
12106 getScrollInfo: function( within ) {
12107 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
12108 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
12109 hasOverflowX = overflowX === "scroll" ||
12110 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
12111 hasOverflowY = overflowY === "scroll" ||
12112 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
12113 return {
12114 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
12115 height: hasOverflowX ? $.position.scrollbarWidth() : 0
12118 getWithinInfo: function( element ) {
12119 var withinElement = $( element || window ),
12120 isWindow = $.isWindow( withinElement[0] );
12121 return {
12122 element: withinElement,
12123 isWindow: isWindow,
12124 offset: withinElement.offset() || { left: 0, top: 0 },
12125 scrollLeft: withinElement.scrollLeft(),
12126 scrollTop: withinElement.scrollTop(),
12127 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
12128 height: isWindow ? withinElement.height() : withinElement.outerHeight()
12133 $.fn.position = function( options ) {
12134 if ( !options || !options.of ) {
12135 return _position.apply( this, arguments );
12138 // make a copy, we don't want to modify arguments
12139 options = $.extend( {}, options );
12141 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
12142 target = $( options.of ),
12143 within = $.position.getWithinInfo( options.within ),
12144 scrollInfo = $.position.getScrollInfo( within ),
12145 collision = ( options.collision || "flip" ).split( " " ),
12146 offsets = {};
12148 dimensions = getDimensions( target );
12149 if ( target[0].preventDefault ) {
12150 // force left top to allow flipping
12151 options.at = "left top";
12153 targetWidth = dimensions.width;
12154 targetHeight = dimensions.height;
12155 targetOffset = dimensions.offset;
12156 // clone to reuse original targetOffset later
12157 basePosition = $.extend( {}, targetOffset );
12159 // force my and at to have valid horizontal and vertical positions
12160 // if a value is missing or invalid, it will be converted to center
12161 $.each( [ "my", "at" ], function() {
12162 var pos = ( options[ this ] || "" ).split( " " ),
12163 horizontalOffset,
12164 verticalOffset;
12166 if ( pos.length === 1) {
12167 pos = rhorizontal.test( pos[ 0 ] ) ?
12168 pos.concat( [ "center" ] ) :
12169 rvertical.test( pos[ 0 ] ) ?
12170 [ "center" ].concat( pos ) :
12171 [ "center", "center" ];
12173 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
12174 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
12176 // calculate offsets
12177 horizontalOffset = roffset.exec( pos[ 0 ] );
12178 verticalOffset = roffset.exec( pos[ 1 ] );
12179 offsets[ this ] = [
12180 horizontalOffset ? horizontalOffset[ 0 ] : 0,
12181 verticalOffset ? verticalOffset[ 0 ] : 0
12184 // reduce to just the positions without the offsets
12185 options[ this ] = [
12186 rposition.exec( pos[ 0 ] )[ 0 ],
12187 rposition.exec( pos[ 1 ] )[ 0 ]
12191 // normalize collision option
12192 if ( collision.length === 1 ) {
12193 collision[ 1 ] = collision[ 0 ];
12196 if ( options.at[ 0 ] === "right" ) {
12197 basePosition.left += targetWidth;
12198 } else if ( options.at[ 0 ] === "center" ) {
12199 basePosition.left += targetWidth / 2;
12202 if ( options.at[ 1 ] === "bottom" ) {
12203 basePosition.top += targetHeight;
12204 } else if ( options.at[ 1 ] === "center" ) {
12205 basePosition.top += targetHeight / 2;
12208 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
12209 basePosition.left += atOffset[ 0 ];
12210 basePosition.top += atOffset[ 1 ];
12212 return this.each(function() {
12213 var collisionPosition, using,
12214 elem = $( this ),
12215 elemWidth = elem.outerWidth(),
12216 elemHeight = elem.outerHeight(),
12217 marginLeft = parseCss( this, "marginLeft" ),
12218 marginTop = parseCss( this, "marginTop" ),
12219 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
12220 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
12221 position = $.extend( {}, basePosition ),
12222 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
12224 if ( options.my[ 0 ] === "right" ) {
12225 position.left -= elemWidth;
12226 } else if ( options.my[ 0 ] === "center" ) {
12227 position.left -= elemWidth / 2;
12230 if ( options.my[ 1 ] === "bottom" ) {
12231 position.top -= elemHeight;
12232 } else if ( options.my[ 1 ] === "center" ) {
12233 position.top -= elemHeight / 2;
12236 position.left += myOffset[ 0 ];
12237 position.top += myOffset[ 1 ];
12239 // if the browser doesn't support fractions, then round for consistent results
12240 if ( !$.support.offsetFractions ) {
12241 position.left = round( position.left );
12242 position.top = round( position.top );
12245 collisionPosition = {
12246 marginLeft: marginLeft,
12247 marginTop: marginTop
12250 $.each( [ "left", "top" ], function( i, dir ) {
12251 if ( $.ui.position[ collision[ i ] ] ) {
12252 $.ui.position[ collision[ i ] ][ dir ]( position, {
12253 targetWidth: targetWidth,
12254 targetHeight: targetHeight,
12255 elemWidth: elemWidth,
12256 elemHeight: elemHeight,
12257 collisionPosition: collisionPosition,
12258 collisionWidth: collisionWidth,
12259 collisionHeight: collisionHeight,
12260 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
12261 my: options.my,
12262 at: options.at,
12263 within: within,
12264 elem : elem
12269 if ( options.using ) {
12270 // adds feedback as second argument to using callback, if present
12271 using = function( props ) {
12272 var left = targetOffset.left - position.left,
12273 right = left + targetWidth - elemWidth,
12274 top = targetOffset.top - position.top,
12275 bottom = top + targetHeight - elemHeight,
12276 feedback = {
12277 target: {
12278 element: target,
12279 left: targetOffset.left,
12280 top: targetOffset.top,
12281 width: targetWidth,
12282 height: targetHeight
12284 element: {
12285 element: elem,
12286 left: position.left,
12287 top: position.top,
12288 width: elemWidth,
12289 height: elemHeight
12291 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
12292 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
12294 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
12295 feedback.horizontal = "center";
12297 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
12298 feedback.vertical = "middle";
12300 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
12301 feedback.important = "horizontal";
12302 } else {
12303 feedback.important = "vertical";
12305 options.using.call( this, props, feedback );
12309 elem.offset( $.extend( position, { using: using } ) );
12313 $.ui.position = {
12314 fit: {
12315 left: function( position, data ) {
12316 var within = data.within,
12317 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
12318 outerWidth = within.width,
12319 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12320 overLeft = withinOffset - collisionPosLeft,
12321 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
12322 newOverRight;
12324 // element is wider than within
12325 if ( data.collisionWidth > outerWidth ) {
12326 // element is initially over the left side of within
12327 if ( overLeft > 0 && overRight <= 0 ) {
12328 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
12329 position.left += overLeft - newOverRight;
12330 // element is initially over right side of within
12331 } else if ( overRight > 0 && overLeft <= 0 ) {
12332 position.left = withinOffset;
12333 // element is initially over both left and right sides of within
12334 } else {
12335 if ( overLeft > overRight ) {
12336 position.left = withinOffset + outerWidth - data.collisionWidth;
12337 } else {
12338 position.left = withinOffset;
12341 // too far left -> align with left edge
12342 } else if ( overLeft > 0 ) {
12343 position.left += overLeft;
12344 // too far right -> align with right edge
12345 } else if ( overRight > 0 ) {
12346 position.left -= overRight;
12347 // adjust based on position and margin
12348 } else {
12349 position.left = max( position.left - collisionPosLeft, position.left );
12352 top: function( position, data ) {
12353 var within = data.within,
12354 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
12355 outerHeight = data.within.height,
12356 collisionPosTop = position.top - data.collisionPosition.marginTop,
12357 overTop = withinOffset - collisionPosTop,
12358 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
12359 newOverBottom;
12361 // element is taller than within
12362 if ( data.collisionHeight > outerHeight ) {
12363 // element is initially over the top of within
12364 if ( overTop > 0 && overBottom <= 0 ) {
12365 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
12366 position.top += overTop - newOverBottom;
12367 // element is initially over bottom of within
12368 } else if ( overBottom > 0 && overTop <= 0 ) {
12369 position.top = withinOffset;
12370 // element is initially over both top and bottom of within
12371 } else {
12372 if ( overTop > overBottom ) {
12373 position.top = withinOffset + outerHeight - data.collisionHeight;
12374 } else {
12375 position.top = withinOffset;
12378 // too far up -> align with top
12379 } else if ( overTop > 0 ) {
12380 position.top += overTop;
12381 // too far down -> align with bottom edge
12382 } else if ( overBottom > 0 ) {
12383 position.top -= overBottom;
12384 // adjust based on position and margin
12385 } else {
12386 position.top = max( position.top - collisionPosTop, position.top );
12390 flip: {
12391 left: function( position, data ) {
12392 var within = data.within,
12393 withinOffset = within.offset.left + within.scrollLeft,
12394 outerWidth = within.width,
12395 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
12396 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
12397 overLeft = collisionPosLeft - offsetLeft,
12398 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
12399 myOffset = data.my[ 0 ] === "left" ?
12400 -data.elemWidth :
12401 data.my[ 0 ] === "right" ?
12402 data.elemWidth :
12404 atOffset = data.at[ 0 ] === "left" ?
12405 data.targetWidth :
12406 data.at[ 0 ] === "right" ?
12407 -data.targetWidth :
12409 offset = -2 * data.offset[ 0 ],
12410 newOverRight,
12411 newOverLeft;
12413 if ( overLeft < 0 ) {
12414 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
12415 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
12416 position.left += myOffset + atOffset + offset;
12419 else if ( overRight > 0 ) {
12420 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
12421 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
12422 position.left += myOffset + atOffset + offset;
12426 top: function( position, data ) {
12427 var within = data.within,
12428 withinOffset = within.offset.top + within.scrollTop,
12429 outerHeight = within.height,
12430 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
12431 collisionPosTop = position.top - data.collisionPosition.marginTop,
12432 overTop = collisionPosTop - offsetTop,
12433 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
12434 top = data.my[ 1 ] === "top",
12435 myOffset = top ?
12436 -data.elemHeight :
12437 data.my[ 1 ] === "bottom" ?
12438 data.elemHeight :
12440 atOffset = data.at[ 1 ] === "top" ?
12441 data.targetHeight :
12442 data.at[ 1 ] === "bottom" ?
12443 -data.targetHeight :
12445 offset = -2 * data.offset[ 1 ],
12446 newOverTop,
12447 newOverBottom;
12448 if ( overTop < 0 ) {
12449 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
12450 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
12451 position.top += myOffset + atOffset + offset;
12454 else if ( overBottom > 0 ) {
12455 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
12456 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
12457 position.top += myOffset + atOffset + offset;
12462 flipfit: {
12463 left: function() {
12464 $.ui.position.flip.left.apply( this, arguments );
12465 $.ui.position.fit.left.apply( this, arguments );
12467 top: function() {
12468 $.ui.position.flip.top.apply( this, arguments );
12469 $.ui.position.fit.top.apply( this, arguments );
12474 // fraction support test
12475 (function () {
12476 var testElement, testElementParent, testElementStyle, offsetLeft, i,
12477 body = document.getElementsByTagName( "body" )[ 0 ],
12478 div = document.createElement( "div" );
12480 //Create a "fake body" for testing based on method used in jQuery.support
12481 testElement = document.createElement( body ? "div" : "body" );
12482 testElementStyle = {
12483 visibility: "hidden",
12484 width: 0,
12485 height: 0,
12486 border: 0,
12487 margin: 0,
12488 background: "none"
12490 if ( body ) {
12491 $.extend( testElementStyle, {
12492 position: "absolute",
12493 left: "-1000px",
12494 top: "-1000px"
12497 for ( i in testElementStyle ) {
12498 testElement.style[ i ] = testElementStyle[ i ];
12500 testElement.appendChild( div );
12501 testElementParent = body || document.documentElement;
12502 testElementParent.insertBefore( testElement, testElementParent.firstChild );
12504 div.style.cssText = "position: absolute; left: 10.7432222px;";
12506 offsetLeft = $( div ).offset().left;
12507 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
12509 testElement.innerHTML = "";
12510 testElementParent.removeChild( testElement );
12511 })();
12513 }( jQuery ) );
12515 (function( $, undefined ) {
12517 $.widget( "ui.progressbar", {
12518 version: "1.10.3",
12519 options: {
12520 max: 100,
12521 value: 0,
12523 change: null,
12524 complete: null
12527 min: 0,
12529 _create: function() {
12530 // Constrain initial value
12531 this.oldValue = this.options.value = this._constrainedValue();
12533 this.element
12534 .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12535 .attr({
12536 // Only set static values, aria-valuenow and aria-valuemax are
12537 // set inside _refreshValue()
12538 role: "progressbar",
12539 "aria-valuemin": this.min
12542 this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
12543 .appendTo( this.element );
12545 this._refreshValue();
12548 _destroy: function() {
12549 this.element
12550 .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
12551 .removeAttr( "role" )
12552 .removeAttr( "aria-valuemin" )
12553 .removeAttr( "aria-valuemax" )
12554 .removeAttr( "aria-valuenow" );
12556 this.valueDiv.remove();
12559 value: function( newValue ) {
12560 if ( newValue === undefined ) {
12561 return this.options.value;
12564 this.options.value = this._constrainedValue( newValue );
12565 this._refreshValue();
12568 _constrainedValue: function( newValue ) {
12569 if ( newValue === undefined ) {
12570 newValue = this.options.value;
12573 this.indeterminate = newValue === false;
12575 // sanitize value
12576 if ( typeof newValue !== "number" ) {
12577 newValue = 0;
12580 return this.indeterminate ? false :
12581 Math.min( this.options.max, Math.max( this.min, newValue ) );
12584 _setOptions: function( options ) {
12585 // Ensure "value" option is set after other values (like max)
12586 var value = options.value;
12587 delete options.value;
12589 this._super( options );
12591 this.options.value = this._constrainedValue( value );
12592 this._refreshValue();
12595 _setOption: function( key, value ) {
12596 if ( key === "max" ) {
12597 // Don't allow a max less than min
12598 value = Math.max( this.min, value );
12601 this._super( key, value );
12604 _percentage: function() {
12605 return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
12608 _refreshValue: function() {
12609 var value = this.options.value,
12610 percentage = this._percentage();
12612 this.valueDiv
12613 .toggle( this.indeterminate || value > this.min )
12614 .toggleClass( "ui-corner-right", value === this.options.max )
12615 .width( percentage.toFixed(0) + "%" );
12617 this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
12619 if ( this.indeterminate ) {
12620 this.element.removeAttr( "aria-valuenow" );
12621 if ( !this.overlayDiv ) {
12622 this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
12624 } else {
12625 this.element.attr({
12626 "aria-valuemax": this.options.max,
12627 "aria-valuenow": value
12629 if ( this.overlayDiv ) {
12630 this.overlayDiv.remove();
12631 this.overlayDiv = null;
12635 if ( this.oldValue !== value ) {
12636 this.oldValue = value;
12637 this._trigger( "change" );
12639 if ( value === this.options.max ) {
12640 this._trigger( "complete" );
12645 })( jQuery );
12647 (function( $, undefined ) {
12649 // number of pages in a slider
12650 // (how many times can you page up/down to go through the whole range)
12651 var numPages = 5;
12653 $.widget( "ui.slider", $.ui.mouse, {
12654 version: "1.10.3",
12655 widgetEventPrefix: "slide",
12657 options: {
12658 animate: false,
12659 distance: 0,
12660 max: 100,
12661 min: 0,
12662 orientation: "horizontal",
12663 range: false,
12664 step: 1,
12665 value: 0,
12666 values: null,
12668 // callbacks
12669 change: null,
12670 slide: null,
12671 start: null,
12672 stop: null
12675 _create: function() {
12676 this._keySliding = false;
12677 this._mouseSliding = false;
12678 this._animateOff = true;
12679 this._handleIndex = null;
12680 this._detectOrientation();
12681 this._mouseInit();
12683 this.element
12684 .addClass( "ui-slider" +
12685 " ui-slider-" + this.orientation +
12686 " ui-widget" +
12687 " ui-widget-content" +
12688 " ui-corner-all");
12690 this._refresh();
12691 this._setOption( "disabled", this.options.disabled );
12693 this._animateOff = false;
12696 _refresh: function() {
12697 this._createRange();
12698 this._createHandles();
12699 this._setupEvents();
12700 this._refreshValue();
12703 _createHandles: function() {
12704 var i, handleCount,
12705 options = this.options,
12706 existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
12707 handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
12708 handles = [];
12710 handleCount = ( options.values && options.values.length ) || 1;
12712 if ( existingHandles.length > handleCount ) {
12713 existingHandles.slice( handleCount ).remove();
12714 existingHandles = existingHandles.slice( 0, handleCount );
12717 for ( i = existingHandles.length; i < handleCount; i++ ) {
12718 handles.push( handle );
12721 this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
12723 this.handle = this.handles.eq( 0 );
12725 this.handles.each(function( i ) {
12726 $( this ).data( "ui-slider-handle-index", i );
12730 _createRange: function() {
12731 var options = this.options,
12732 classes = "";
12734 if ( options.range ) {
12735 if ( options.range === true ) {
12736 if ( !options.values ) {
12737 options.values = [ this._valueMin(), this._valueMin() ];
12738 } else if ( options.values.length && options.values.length !== 2 ) {
12739 options.values = [ options.values[0], options.values[0] ];
12740 } else if ( $.isArray( options.values ) ) {
12741 options.values = options.values.slice(0);
12745 if ( !this.range || !this.range.length ) {
12746 this.range = $( "<div></div>" )
12747 .appendTo( this.element );
12749 classes = "ui-slider-range" +
12750 // note: this isn't the most fittingly semantic framework class for this element,
12751 // but worked best visually with a variety of themes
12752 " ui-widget-header ui-corner-all";
12753 } else {
12754 this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
12755 // Handle range switching from true to min/max
12756 .css({
12757 "left": "",
12758 "bottom": ""
12762 this.range.addClass( classes +
12763 ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
12764 } else {
12765 this.range = $([]);
12769 _setupEvents: function() {
12770 var elements = this.handles.add( this.range ).filter( "a" );
12771 this._off( elements );
12772 this._on( elements, this._handleEvents );
12773 this._hoverable( elements );
12774 this._focusable( elements );
12777 _destroy: function() {
12778 this.handles.remove();
12779 this.range.remove();
12781 this.element
12782 .removeClass( "ui-slider" +
12783 " ui-slider-horizontal" +
12784 " ui-slider-vertical" +
12785 " ui-widget" +
12786 " ui-widget-content" +
12787 " ui-corner-all" );
12789 this._mouseDestroy();
12792 _mouseCapture: function( event ) {
12793 var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
12794 that = this,
12795 o = this.options;
12797 if ( o.disabled ) {
12798 return false;
12801 this.elementSize = {
12802 width: this.element.outerWidth(),
12803 height: this.element.outerHeight()
12805 this.elementOffset = this.element.offset();
12807 position = { x: event.pageX, y: event.pageY };
12808 normValue = this._normValueFromMouse( position );
12809 distance = this._valueMax() - this._valueMin() + 1;
12810 this.handles.each(function( i ) {
12811 var thisDistance = Math.abs( normValue - that.values(i) );
12812 if (( distance > thisDistance ) ||
12813 ( distance === thisDistance &&
12814 (i === that._lastChangedValue || that.values(i) === o.min ))) {
12815 distance = thisDistance;
12816 closestHandle = $( this );
12817 index = i;
12821 allowed = this._start( event, index );
12822 if ( allowed === false ) {
12823 return false;
12825 this._mouseSliding = true;
12827 this._handleIndex = index;
12829 closestHandle
12830 .addClass( "ui-state-active" )
12831 .focus();
12833 offset = closestHandle.offset();
12834 mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
12835 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
12836 left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
12837 top: event.pageY - offset.top -
12838 ( closestHandle.height() / 2 ) -
12839 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
12840 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
12841 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
12844 if ( !this.handles.hasClass( "ui-state-hover" ) ) {
12845 this._slide( event, index, normValue );
12847 this._animateOff = true;
12848 return true;
12851 _mouseStart: function() {
12852 return true;
12855 _mouseDrag: function( event ) {
12856 var position = { x: event.pageX, y: event.pageY },
12857 normValue = this._normValueFromMouse( position );
12859 this._slide( event, this._handleIndex, normValue );
12861 return false;
12864 _mouseStop: function( event ) {
12865 this.handles.removeClass( "ui-state-active" );
12866 this._mouseSliding = false;
12868 this._stop( event, this._handleIndex );
12869 this._change( event, this._handleIndex );
12871 this._handleIndex = null;
12872 this._clickOffset = null;
12873 this._animateOff = false;
12875 return false;
12878 _detectOrientation: function() {
12879 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
12882 _normValueFromMouse: function( position ) {
12883 var pixelTotal,
12884 pixelMouse,
12885 percentMouse,
12886 valueTotal,
12887 valueMouse;
12889 if ( this.orientation === "horizontal" ) {
12890 pixelTotal = this.elementSize.width;
12891 pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
12892 } else {
12893 pixelTotal = this.elementSize.height;
12894 pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
12897 percentMouse = ( pixelMouse / pixelTotal );
12898 if ( percentMouse > 1 ) {
12899 percentMouse = 1;
12901 if ( percentMouse < 0 ) {
12902 percentMouse = 0;
12904 if ( this.orientation === "vertical" ) {
12905 percentMouse = 1 - percentMouse;
12908 valueTotal = this._valueMax() - this._valueMin();
12909 valueMouse = this._valueMin() + percentMouse * valueTotal;
12911 return this._trimAlignValue( valueMouse );
12914 _start: function( event, index ) {
12915 var uiHash = {
12916 handle: this.handles[ index ],
12917 value: this.value()
12919 if ( this.options.values && this.options.values.length ) {
12920 uiHash.value = this.values( index );
12921 uiHash.values = this.values();
12923 return this._trigger( "start", event, uiHash );
12926 _slide: function( event, index, newVal ) {
12927 var otherVal,
12928 newValues,
12929 allowed;
12931 if ( this.options.values && this.options.values.length ) {
12932 otherVal = this.values( index ? 0 : 1 );
12934 if ( ( this.options.values.length === 2 && this.options.range === true ) &&
12935 ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
12937 newVal = otherVal;
12940 if ( newVal !== this.values( index ) ) {
12941 newValues = this.values();
12942 newValues[ index ] = newVal;
12943 // A slide can be canceled by returning false from the slide callback
12944 allowed = this._trigger( "slide", event, {
12945 handle: this.handles[ index ],
12946 value: newVal,
12947 values: newValues
12948 } );
12949 otherVal = this.values( index ? 0 : 1 );
12950 if ( allowed !== false ) {
12951 this.values( index, newVal, true );
12954 } else {
12955 if ( newVal !== this.value() ) {
12956 // A slide can be canceled by returning false from the slide callback
12957 allowed = this._trigger( "slide", event, {
12958 handle: this.handles[ index ],
12959 value: newVal
12960 } );
12961 if ( allowed !== false ) {
12962 this.value( newVal );
12968 _stop: function( event, index ) {
12969 var uiHash = {
12970 handle: this.handles[ index ],
12971 value: this.value()
12973 if ( this.options.values && this.options.values.length ) {
12974 uiHash.value = this.values( index );
12975 uiHash.values = this.values();
12978 this._trigger( "stop", event, uiHash );
12981 _change: function( event, index ) {
12982 if ( !this._keySliding && !this._mouseSliding ) {
12983 var uiHash = {
12984 handle: this.handles[ index ],
12985 value: this.value()
12987 if ( this.options.values && this.options.values.length ) {
12988 uiHash.value = this.values( index );
12989 uiHash.values = this.values();
12992 //store the last changed value index for reference when handles overlap
12993 this._lastChangedValue = index;
12995 this._trigger( "change", event, uiHash );
12999 value: function( newValue ) {
13000 if ( arguments.length ) {
13001 this.options.value = this._trimAlignValue( newValue );
13002 this._refreshValue();
13003 this._change( null, 0 );
13004 return;
13007 return this._value();
13010 values: function( index, newValue ) {
13011 var vals,
13012 newValues,
13015 if ( arguments.length > 1 ) {
13016 this.options.values[ index ] = this._trimAlignValue( newValue );
13017 this._refreshValue();
13018 this._change( null, index );
13019 return;
13022 if ( arguments.length ) {
13023 if ( $.isArray( arguments[ 0 ] ) ) {
13024 vals = this.options.values;
13025 newValues = arguments[ 0 ];
13026 for ( i = 0; i < vals.length; i += 1 ) {
13027 vals[ i ] = this._trimAlignValue( newValues[ i ] );
13028 this._change( null, i );
13030 this._refreshValue();
13031 } else {
13032 if ( this.options.values && this.options.values.length ) {
13033 return this._values( index );
13034 } else {
13035 return this.value();
13038 } else {
13039 return this._values();
13043 _setOption: function( key, value ) {
13044 var i,
13045 valsLength = 0;
13047 if ( key === "range" && this.options.range === true ) {
13048 if ( value === "min" ) {
13049 this.options.value = this._values( 0 );
13050 this.options.values = null;
13051 } else if ( value === "max" ) {
13052 this.options.value = this._values( this.options.values.length-1 );
13053 this.options.values = null;
13057 if ( $.isArray( this.options.values ) ) {
13058 valsLength = this.options.values.length;
13061 $.Widget.prototype._setOption.apply( this, arguments );
13063 switch ( key ) {
13064 case "orientation":
13065 this._detectOrientation();
13066 this.element
13067 .removeClass( "ui-slider-horizontal ui-slider-vertical" )
13068 .addClass( "ui-slider-" + this.orientation );
13069 this._refreshValue();
13070 break;
13071 case "value":
13072 this._animateOff = true;
13073 this._refreshValue();
13074 this._change( null, 0 );
13075 this._animateOff = false;
13076 break;
13077 case "values":
13078 this._animateOff = true;
13079 this._refreshValue();
13080 for ( i = 0; i < valsLength; i += 1 ) {
13081 this._change( null, i );
13083 this._animateOff = false;
13084 break;
13085 case "min":
13086 case "max":
13087 this._animateOff = true;
13088 this._refreshValue();
13089 this._animateOff = false;
13090 break;
13091 case "range":
13092 this._animateOff = true;
13093 this._refresh();
13094 this._animateOff = false;
13095 break;
13099 //internal value getter
13100 // _value() returns value trimmed by min and max, aligned by step
13101 _value: function() {
13102 var val = this.options.value;
13103 val = this._trimAlignValue( val );
13105 return val;
13108 //internal values getter
13109 // _values() returns array of values trimmed by min and max, aligned by step
13110 // _values( index ) returns single value trimmed by min and max, aligned by step
13111 _values: function( index ) {
13112 var val,
13113 vals,
13116 if ( arguments.length ) {
13117 val = this.options.values[ index ];
13118 val = this._trimAlignValue( val );
13120 return val;
13121 } else if ( this.options.values && this.options.values.length ) {
13122 // .slice() creates a copy of the array
13123 // this copy gets trimmed by min and max and then returned
13124 vals = this.options.values.slice();
13125 for ( i = 0; i < vals.length; i+= 1) {
13126 vals[ i ] = this._trimAlignValue( vals[ i ] );
13129 return vals;
13130 } else {
13131 return [];
13135 // returns the step-aligned value that val is closest to, between (inclusive) min and max
13136 _trimAlignValue: function( val ) {
13137 if ( val <= this._valueMin() ) {
13138 return this._valueMin();
13140 if ( val >= this._valueMax() ) {
13141 return this._valueMax();
13143 var step = ( this.options.step > 0 ) ? this.options.step : 1,
13144 valModStep = (val - this._valueMin()) % step,
13145 alignValue = val - valModStep;
13147 if ( Math.abs(valModStep) * 2 >= step ) {
13148 alignValue += ( valModStep > 0 ) ? step : ( -step );
13151 // Since JavaScript has problems with large floats, round
13152 // the final value to 5 digits after the decimal point (see #4124)
13153 return parseFloat( alignValue.toFixed(5) );
13156 _valueMin: function() {
13157 return this.options.min;
13160 _valueMax: function() {
13161 return this.options.max;
13164 _refreshValue: function() {
13165 var lastValPercent, valPercent, value, valueMin, valueMax,
13166 oRange = this.options.range,
13167 o = this.options,
13168 that = this,
13169 animate = ( !this._animateOff ) ? o.animate : false,
13170 _set = {};
13172 if ( this.options.values && this.options.values.length ) {
13173 this.handles.each(function( i ) {
13174 valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
13175 _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13176 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13177 if ( that.options.range === true ) {
13178 if ( that.orientation === "horizontal" ) {
13179 if ( i === 0 ) {
13180 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
13182 if ( i === 1 ) {
13183 that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13185 } else {
13186 if ( i === 0 ) {
13187 that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
13189 if ( i === 1 ) {
13190 that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
13194 lastValPercent = valPercent;
13196 } else {
13197 value = this.value();
13198 valueMin = this._valueMin();
13199 valueMax = this._valueMax();
13200 valPercent = ( valueMax !== valueMin ) ?
13201 ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
13203 _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
13204 this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
13206 if ( oRange === "min" && this.orientation === "horizontal" ) {
13207 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
13209 if ( oRange === "max" && this.orientation === "horizontal" ) {
13210 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13212 if ( oRange === "min" && this.orientation === "vertical" ) {
13213 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
13215 if ( oRange === "max" && this.orientation === "vertical" ) {
13216 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
13221 _handleEvents: {
13222 keydown: function( event ) {
13223 /*jshint maxcomplexity:25*/
13224 var allowed, curVal, newVal, step,
13225 index = $( event.target ).data( "ui-slider-handle-index" );
13227 switch ( event.keyCode ) {
13228 case $.ui.keyCode.HOME:
13229 case $.ui.keyCode.END:
13230 case $.ui.keyCode.PAGE_UP:
13231 case $.ui.keyCode.PAGE_DOWN:
13232 case $.ui.keyCode.UP:
13233 case $.ui.keyCode.RIGHT:
13234 case $.ui.keyCode.DOWN:
13235 case $.ui.keyCode.LEFT:
13236 event.preventDefault();
13237 if ( !this._keySliding ) {
13238 this._keySliding = true;
13239 $( event.target ).addClass( "ui-state-active" );
13240 allowed = this._start( event, index );
13241 if ( allowed === false ) {
13242 return;
13245 break;
13248 step = this.options.step;
13249 if ( this.options.values && this.options.values.length ) {
13250 curVal = newVal = this.values( index );
13251 } else {
13252 curVal = newVal = this.value();
13255 switch ( event.keyCode ) {
13256 case $.ui.keyCode.HOME:
13257 newVal = this._valueMin();
13258 break;
13259 case $.ui.keyCode.END:
13260 newVal = this._valueMax();
13261 break;
13262 case $.ui.keyCode.PAGE_UP:
13263 newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
13264 break;
13265 case $.ui.keyCode.PAGE_DOWN:
13266 newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
13267 break;
13268 case $.ui.keyCode.UP:
13269 case $.ui.keyCode.RIGHT:
13270 if ( curVal === this._valueMax() ) {
13271 return;
13273 newVal = this._trimAlignValue( curVal + step );
13274 break;
13275 case $.ui.keyCode.DOWN:
13276 case $.ui.keyCode.LEFT:
13277 if ( curVal === this._valueMin() ) {
13278 return;
13280 newVal = this._trimAlignValue( curVal - step );
13281 break;
13284 this._slide( event, index, newVal );
13286 click: function( event ) {
13287 event.preventDefault();
13289 keyup: function( event ) {
13290 var index = $( event.target ).data( "ui-slider-handle-index" );
13292 if ( this._keySliding ) {
13293 this._keySliding = false;
13294 this._stop( event, index );
13295 this._change( event, index );
13296 $( event.target ).removeClass( "ui-state-active" );
13303 }(jQuery));
13305 (function( $ ) {
13307 function modifier( fn ) {
13308 return function() {
13309 var previous = this.element.val();
13310 fn.apply( this, arguments );
13311 this._refresh();
13312 if ( previous !== this.element.val() ) {
13313 this._trigger( "change" );
13318 $.widget( "ui.spinner", {
13319 version: "1.10.3",
13320 defaultElement: "<input>",
13321 widgetEventPrefix: "spin",
13322 options: {
13323 culture: null,
13324 icons: {
13325 down: "ui-icon-triangle-1-s",
13326 up: "ui-icon-triangle-1-n"
13328 incremental: true,
13329 max: null,
13330 min: null,
13331 numberFormat: null,
13332 page: 10,
13333 step: 1,
13335 change: null,
13336 spin: null,
13337 start: null,
13338 stop: null
13341 _create: function() {
13342 // handle string values that need to be parsed
13343 this._setOption( "max", this.options.max );
13344 this._setOption( "min", this.options.min );
13345 this._setOption( "step", this.options.step );
13347 // format the value, but don't constrain
13348 this._value( this.element.val(), true );
13350 this._draw();
13351 this._on( this._events );
13352 this._refresh();
13354 // turning off autocomplete prevents the browser from remembering the
13355 // value when navigating through history, so we re-enable autocomplete
13356 // if the page is unloaded before the widget is destroyed. #7790
13357 this._on( this.window, {
13358 beforeunload: function() {
13359 this.element.removeAttr( "autocomplete" );
13364 _getCreateOptions: function() {
13365 var options = {},
13366 element = this.element;
13368 $.each( [ "min", "max", "step" ], function( i, option ) {
13369 var value = element.attr( option );
13370 if ( value !== undefined && value.length ) {
13371 options[ option ] = value;
13375 return options;
13378 _events: {
13379 keydown: function( event ) {
13380 if ( this._start( event ) && this._keydown( event ) ) {
13381 event.preventDefault();
13384 keyup: "_stop",
13385 focus: function() {
13386 this.previous = this.element.val();
13388 blur: function( event ) {
13389 if ( this.cancelBlur ) {
13390 delete this.cancelBlur;
13391 return;
13394 this._stop();
13395 this._refresh();
13396 if ( this.previous !== this.element.val() ) {
13397 this._trigger( "change", event );
13400 mousewheel: function( event, delta ) {
13401 if ( !delta ) {
13402 return;
13404 if ( !this.spinning && !this._start( event ) ) {
13405 return false;
13408 this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
13409 clearTimeout( this.mousewheelTimer );
13410 this.mousewheelTimer = this._delay(function() {
13411 if ( this.spinning ) {
13412 this._stop( event );
13414 }, 100 );
13415 event.preventDefault();
13417 "mousedown .ui-spinner-button": function( event ) {
13418 var previous;
13420 // We never want the buttons to have focus; whenever the user is
13421 // interacting with the spinner, the focus should be on the input.
13422 // If the input is focused then this.previous is properly set from
13423 // when the input first received focus. If the input is not focused
13424 // then we need to set this.previous based on the value before spinning.
13425 previous = this.element[0] === this.document[0].activeElement ?
13426 this.previous : this.element.val();
13427 function checkFocus() {
13428 var isActive = this.element[0] === this.document[0].activeElement;
13429 if ( !isActive ) {
13430 this.element.focus();
13431 this.previous = previous;
13432 // support: IE
13433 // IE sets focus asynchronously, so we need to check if focus
13434 // moved off of the input because the user clicked on the button.
13435 this._delay(function() {
13436 this.previous = previous;
13441 // ensure focus is on (or stays on) the text field
13442 event.preventDefault();
13443 checkFocus.call( this );
13445 // support: IE
13446 // IE doesn't prevent moving focus even with event.preventDefault()
13447 // so we set a flag to know when we should ignore the blur event
13448 // and check (again) if focus moved off of the input.
13449 this.cancelBlur = true;
13450 this._delay(function() {
13451 delete this.cancelBlur;
13452 checkFocus.call( this );
13455 if ( this._start( event ) === false ) {
13456 return;
13459 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13461 "mouseup .ui-spinner-button": "_stop",
13462 "mouseenter .ui-spinner-button": function( event ) {
13463 // button will add ui-state-active if mouse was down while mouseleave and kept down
13464 if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
13465 return;
13468 if ( this._start( event ) === false ) {
13469 return false;
13471 this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
13473 // TODO: do we really want to consider this a stop?
13474 // shouldn't we just stop the repeater and wait until mouseup before
13475 // we trigger the stop event?
13476 "mouseleave .ui-spinner-button": "_stop"
13479 _draw: function() {
13480 var uiSpinner = this.uiSpinner = this.element
13481 .addClass( "ui-spinner-input" )
13482 .attr( "autocomplete", "off" )
13483 .wrap( this._uiSpinnerHtml() )
13484 .parent()
13485 // add buttons
13486 .append( this._buttonHtml() );
13488 this.element.attr( "role", "spinbutton" );
13490 // button bindings
13491 this.buttons = uiSpinner.find( ".ui-spinner-button" )
13492 .attr( "tabIndex", -1 )
13493 .button()
13494 .removeClass( "ui-corner-all" );
13496 // IE 6 doesn't understand height: 50% for the buttons
13497 // unless the wrapper has an explicit height
13498 if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
13499 uiSpinner.height() > 0 ) {
13500 uiSpinner.height( uiSpinner.height() );
13503 // disable spinner if element was already disabled
13504 if ( this.options.disabled ) {
13505 this.disable();
13509 _keydown: function( event ) {
13510 var options = this.options,
13511 keyCode = $.ui.keyCode;
13513 switch ( event.keyCode ) {
13514 case keyCode.UP:
13515 this._repeat( null, 1, event );
13516 return true;
13517 case keyCode.DOWN:
13518 this._repeat( null, -1, event );
13519 return true;
13520 case keyCode.PAGE_UP:
13521 this._repeat( null, options.page, event );
13522 return true;
13523 case keyCode.PAGE_DOWN:
13524 this._repeat( null, -options.page, event );
13525 return true;
13528 return false;
13531 _uiSpinnerHtml: function() {
13532 return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
13535 _buttonHtml: function() {
13536 return "" +
13537 "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
13538 "<span class='ui-icon " + this.options.icons.up + "'>&#9650;</span>" +
13539 "</a>" +
13540 "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
13541 "<span class='ui-icon " + this.options.icons.down + "'>&#9660;</span>" +
13542 "</a>";
13545 _start: function( event ) {
13546 if ( !this.spinning && this._trigger( "start", event ) === false ) {
13547 return false;
13550 if ( !this.counter ) {
13551 this.counter = 1;
13553 this.spinning = true;
13554 return true;
13557 _repeat: function( i, steps, event ) {
13558 i = i || 500;
13560 clearTimeout( this.timer );
13561 this.timer = this._delay(function() {
13562 this._repeat( 40, steps, event );
13563 }, i );
13565 this._spin( steps * this.options.step, event );
13568 _spin: function( step, event ) {
13569 var value = this.value() || 0;
13571 if ( !this.counter ) {
13572 this.counter = 1;
13575 value = this._adjustValue( value + step * this._increment( this.counter ) );
13577 if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
13578 this._value( value );
13579 this.counter++;
13583 _increment: function( i ) {
13584 var incremental = this.options.incremental;
13586 if ( incremental ) {
13587 return $.isFunction( incremental ) ?
13588 incremental( i ) :
13589 Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
13592 return 1;
13595 _precision: function() {
13596 var precision = this._precisionOf( this.options.step );
13597 if ( this.options.min !== null ) {
13598 precision = Math.max( precision, this._precisionOf( this.options.min ) );
13600 return precision;
13603 _precisionOf: function( num ) {
13604 var str = num.toString(),
13605 decimal = str.indexOf( "." );
13606 return decimal === -1 ? 0 : str.length - decimal - 1;
13609 _adjustValue: function( value ) {
13610 var base, aboveMin,
13611 options = this.options;
13613 // make sure we're at a valid step
13614 // - find out where we are relative to the base (min or 0)
13615 base = options.min !== null ? options.min : 0;
13616 aboveMin = value - base;
13617 // - round to the nearest step
13618 aboveMin = Math.round(aboveMin / options.step) * options.step;
13619 // - rounding is based on 0, so adjust back to our base
13620 value = base + aboveMin;
13622 // fix precision from bad JS floating point math
13623 value = parseFloat( value.toFixed( this._precision() ) );
13625 // clamp the value
13626 if ( options.max !== null && value > options.max) {
13627 return options.max;
13629 if ( options.min !== null && value < options.min ) {
13630 return options.min;
13633 return value;
13636 _stop: function( event ) {
13637 if ( !this.spinning ) {
13638 return;
13641 clearTimeout( this.timer );
13642 clearTimeout( this.mousewheelTimer );
13643 this.counter = 0;
13644 this.spinning = false;
13645 this._trigger( "stop", event );
13648 _setOption: function( key, value ) {
13649 if ( key === "culture" || key === "numberFormat" ) {
13650 var prevValue = this._parse( this.element.val() );
13651 this.options[ key ] = value;
13652 this.element.val( this._format( prevValue ) );
13653 return;
13656 if ( key === "max" || key === "min" || key === "step" ) {
13657 if ( typeof value === "string" ) {
13658 value = this._parse( value );
13661 if ( key === "icons" ) {
13662 this.buttons.first().find( ".ui-icon" )
13663 .removeClass( this.options.icons.up )
13664 .addClass( value.up );
13665 this.buttons.last().find( ".ui-icon" )
13666 .removeClass( this.options.icons.down )
13667 .addClass( value.down );
13670 this._super( key, value );
13672 if ( key === "disabled" ) {
13673 if ( value ) {
13674 this.element.prop( "disabled", true );
13675 this.buttons.button( "disable" );
13676 } else {
13677 this.element.prop( "disabled", false );
13678 this.buttons.button( "enable" );
13683 _setOptions: modifier(function( options ) {
13684 this._super( options );
13685 this._value( this.element.val() );
13688 _parse: function( val ) {
13689 if ( typeof val === "string" && val !== "" ) {
13690 val = window.Globalize && this.options.numberFormat ?
13691 Globalize.parseFloat( val, 10, this.options.culture ) : +val;
13693 return val === "" || isNaN( val ) ? null : val;
13696 _format: function( value ) {
13697 if ( value === "" ) {
13698 return "";
13700 return window.Globalize && this.options.numberFormat ?
13701 Globalize.format( value, this.options.numberFormat, this.options.culture ) :
13702 value;
13705 _refresh: function() {
13706 this.element.attr({
13707 "aria-valuemin": this.options.min,
13708 "aria-valuemax": this.options.max,
13709 // TODO: what should we do with values that can't be parsed?
13710 "aria-valuenow": this._parse( this.element.val() )
13714 // update the value without triggering change
13715 _value: function( value, allowAny ) {
13716 var parsed;
13717 if ( value !== "" ) {
13718 parsed = this._parse( value );
13719 if ( parsed !== null ) {
13720 if ( !allowAny ) {
13721 parsed = this._adjustValue( parsed );
13723 value = this._format( parsed );
13726 this.element.val( value );
13727 this._refresh();
13730 _destroy: function() {
13731 this.element
13732 .removeClass( "ui-spinner-input" )
13733 .prop( "disabled", false )
13734 .removeAttr( "autocomplete" )
13735 .removeAttr( "role" )
13736 .removeAttr( "aria-valuemin" )
13737 .removeAttr( "aria-valuemax" )
13738 .removeAttr( "aria-valuenow" );
13739 this.uiSpinner.replaceWith( this.element );
13742 stepUp: modifier(function( steps ) {
13743 this._stepUp( steps );
13745 _stepUp: function( steps ) {
13746 if ( this._start() ) {
13747 this._spin( (steps || 1) * this.options.step );
13748 this._stop();
13752 stepDown: modifier(function( steps ) {
13753 this._stepDown( steps );
13755 _stepDown: function( steps ) {
13756 if ( this._start() ) {
13757 this._spin( (steps || 1) * -this.options.step );
13758 this._stop();
13762 pageUp: modifier(function( pages ) {
13763 this._stepUp( (pages || 1) * this.options.page );
13766 pageDown: modifier(function( pages ) {
13767 this._stepDown( (pages || 1) * this.options.page );
13770 value: function( newVal ) {
13771 if ( !arguments.length ) {
13772 return this._parse( this.element.val() );
13774 modifier( this._value ).call( this, newVal );
13777 widget: function() {
13778 return this.uiSpinner;
13782 }( jQuery ) );
13784 (function( $, undefined ) {
13786 var tabId = 0,
13787 rhash = /#.*$/;
13789 function getNextTabId() {
13790 return ++tabId;
13793 function isLocal( anchor ) {
13794 return anchor.hash.length > 1 &&
13795 decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
13796 decodeURIComponent( location.href.replace( rhash, "" ) );
13799 $.widget( "ui.tabs", {
13800 version: "1.10.3",
13801 delay: 300,
13802 options: {
13803 active: null,
13804 collapsible: false,
13805 event: "click",
13806 heightStyle: "content",
13807 hide: null,
13808 show: null,
13810 // callbacks
13811 activate: null,
13812 beforeActivate: null,
13813 beforeLoad: null,
13814 load: null
13817 _create: function() {
13818 var that = this,
13819 options = this.options;
13821 this.running = false;
13823 this.element
13824 .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
13825 .toggleClass( "ui-tabs-collapsible", options.collapsible )
13826 // Prevent users from focusing disabled tabs via click
13827 .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
13828 if ( $( this ).is( ".ui-state-disabled" ) ) {
13829 event.preventDefault();
13832 // support: IE <9
13833 // Preventing the default action in mousedown doesn't prevent IE
13834 // from focusing the element, so if the anchor gets focused, blur.
13835 // We don't have to worry about focusing the previously focused
13836 // element since clicking on a non-focusable element should focus
13837 // the body anyway.
13838 .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
13839 if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
13840 this.blur();
13844 this._processTabs();
13845 options.active = this._initialActive();
13847 // Take disabling tabs via class attribute from HTML
13848 // into account and update option properly.
13849 if ( $.isArray( options.disabled ) ) {
13850 options.disabled = $.unique( options.disabled.concat(
13851 $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
13852 return that.tabs.index( li );
13854 ) ).sort();
13857 // check for length avoids error when initializing empty list
13858 if ( this.options.active !== false && this.anchors.length ) {
13859 this.active = this._findActive( options.active );
13860 } else {
13861 this.active = $();
13864 this._refresh();
13866 if ( this.active.length ) {
13867 this.load( options.active );
13871 _initialActive: function() {
13872 var active = this.options.active,
13873 collapsible = this.options.collapsible,
13874 locationHash = location.hash.substring( 1 );
13876 if ( active === null ) {
13877 // check the fragment identifier in the URL
13878 if ( locationHash ) {
13879 this.tabs.each(function( i, tab ) {
13880 if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
13881 active = i;
13882 return false;
13887 // check for a tab marked active via a class
13888 if ( active === null ) {
13889 active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
13892 // no active tab, set to false
13893 if ( active === null || active === -1 ) {
13894 active = this.tabs.length ? 0 : false;
13898 // handle numbers: negative, out of range
13899 if ( active !== false ) {
13900 active = this.tabs.index( this.tabs.eq( active ) );
13901 if ( active === -1 ) {
13902 active = collapsible ? false : 0;
13906 // don't allow collapsible: false and active: false
13907 if ( !collapsible && active === false && this.anchors.length ) {
13908 active = 0;
13911 return active;
13914 _getCreateEventData: function() {
13915 return {
13916 tab: this.active,
13917 panel: !this.active.length ? $() : this._getPanelForTab( this.active )
13921 _tabKeydown: function( event ) {
13922 /*jshint maxcomplexity:15*/
13923 var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
13924 selectedIndex = this.tabs.index( focusedTab ),
13925 goingForward = true;
13927 if ( this._handlePageNav( event ) ) {
13928 return;
13931 switch ( event.keyCode ) {
13932 case $.ui.keyCode.RIGHT:
13933 case $.ui.keyCode.DOWN:
13934 selectedIndex++;
13935 break;
13936 case $.ui.keyCode.UP:
13937 case $.ui.keyCode.LEFT:
13938 goingForward = false;
13939 selectedIndex--;
13940 break;
13941 case $.ui.keyCode.END:
13942 selectedIndex = this.anchors.length - 1;
13943 break;
13944 case $.ui.keyCode.HOME:
13945 selectedIndex = 0;
13946 break;
13947 case $.ui.keyCode.SPACE:
13948 // Activate only, no collapsing
13949 event.preventDefault();
13950 clearTimeout( this.activating );
13951 this._activate( selectedIndex );
13952 return;
13953 case $.ui.keyCode.ENTER:
13954 // Toggle (cancel delayed activation, allow collapsing)
13955 event.preventDefault();
13956 clearTimeout( this.activating );
13957 // Determine if we should collapse or activate
13958 this._activate( selectedIndex === this.options.active ? false : selectedIndex );
13959 return;
13960 default:
13961 return;
13964 // Focus the appropriate tab, based on which key was pressed
13965 event.preventDefault();
13966 clearTimeout( this.activating );
13967 selectedIndex = this._focusNextTab( selectedIndex, goingForward );
13969 // Navigating with control key will prevent automatic activation
13970 if ( !event.ctrlKey ) {
13971 // Update aria-selected immediately so that AT think the tab is already selected.
13972 // Otherwise AT may confuse the user by stating that they need to activate the tab,
13973 // but the tab will already be activated by the time the announcement finishes.
13974 focusedTab.attr( "aria-selected", "false" );
13975 this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
13977 this.activating = this._delay(function() {
13978 this.option( "active", selectedIndex );
13979 }, this.delay );
13983 _panelKeydown: function( event ) {
13984 if ( this._handlePageNav( event ) ) {
13985 return;
13988 // Ctrl+up moves focus to the current tab
13989 if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
13990 event.preventDefault();
13991 this.active.focus();
13995 // Alt+page up/down moves focus to the previous/next tab (and activates)
13996 _handlePageNav: function( event ) {
13997 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
13998 this._activate( this._focusNextTab( this.options.active - 1, false ) );
13999 return true;
14001 if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
14002 this._activate( this._focusNextTab( this.options.active + 1, true ) );
14003 return true;
14007 _findNextTab: function( index, goingForward ) {
14008 var lastTabIndex = this.tabs.length - 1;
14010 function constrain() {
14011 if ( index > lastTabIndex ) {
14012 index = 0;
14014 if ( index < 0 ) {
14015 index = lastTabIndex;
14017 return index;
14020 while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
14021 index = goingForward ? index + 1 : index - 1;
14024 return index;
14027 _focusNextTab: function( index, goingForward ) {
14028 index = this._findNextTab( index, goingForward );
14029 this.tabs.eq( index ).focus();
14030 return index;
14033 _setOption: function( key, value ) {
14034 if ( key === "active" ) {
14035 // _activate() will handle invalid values and update this.options
14036 this._activate( value );
14037 return;
14040 if ( key === "disabled" ) {
14041 // don't use the widget factory's disabled handling
14042 this._setupDisabled( value );
14043 return;
14046 this._super( key, value);
14048 if ( key === "collapsible" ) {
14049 this.element.toggleClass( "ui-tabs-collapsible", value );
14050 // Setting collapsible: false while collapsed; open first panel
14051 if ( !value && this.options.active === false ) {
14052 this._activate( 0 );
14056 if ( key === "event" ) {
14057 this._setupEvents( value );
14060 if ( key === "heightStyle" ) {
14061 this._setupHeightStyle( value );
14065 _tabId: function( tab ) {
14066 return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
14069 _sanitizeSelector: function( hash ) {
14070 return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
14073 refresh: function() {
14074 var options = this.options,
14075 lis = this.tablist.children( ":has(a[href])" );
14077 // get disabled tabs from class attribute from HTML
14078 // this will get converted to a boolean if needed in _refresh()
14079 options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
14080 return lis.index( tab );
14083 this._processTabs();
14085 // was collapsed or no tabs
14086 if ( options.active === false || !this.anchors.length ) {
14087 options.active = false;
14088 this.active = $();
14089 // was active, but active tab is gone
14090 } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
14091 // all remaining tabs are disabled
14092 if ( this.tabs.length === options.disabled.length ) {
14093 options.active = false;
14094 this.active = $();
14095 // activate previous tab
14096 } else {
14097 this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
14099 // was active, active tab still exists
14100 } else {
14101 // make sure active index is correct
14102 options.active = this.tabs.index( this.active );
14105 this._refresh();
14108 _refresh: function() {
14109 this._setupDisabled( this.options.disabled );
14110 this._setupEvents( this.options.event );
14111 this._setupHeightStyle( this.options.heightStyle );
14113 this.tabs.not( this.active ).attr({
14114 "aria-selected": "false",
14115 tabIndex: -1
14117 this.panels.not( this._getPanelForTab( this.active ) )
14118 .hide()
14119 .attr({
14120 "aria-expanded": "false",
14121 "aria-hidden": "true"
14124 // Make sure one tab is in the tab order
14125 if ( !this.active.length ) {
14126 this.tabs.eq( 0 ).attr( "tabIndex", 0 );
14127 } else {
14128 this.active
14129 .addClass( "ui-tabs-active ui-state-active" )
14130 .attr({
14131 "aria-selected": "true",
14132 tabIndex: 0
14134 this._getPanelForTab( this.active )
14135 .show()
14136 .attr({
14137 "aria-expanded": "true",
14138 "aria-hidden": "false"
14143 _processTabs: function() {
14144 var that = this;
14146 this.tablist = this._getList()
14147 .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14148 .attr( "role", "tablist" );
14150 this.tabs = this.tablist.find( "> li:has(a[href])" )
14151 .addClass( "ui-state-default ui-corner-top" )
14152 .attr({
14153 role: "tab",
14154 tabIndex: -1
14157 this.anchors = this.tabs.map(function() {
14158 return $( "a", this )[ 0 ];
14160 .addClass( "ui-tabs-anchor" )
14161 .attr({
14162 role: "presentation",
14163 tabIndex: -1
14166 this.panels = $();
14168 this.anchors.each(function( i, anchor ) {
14169 var selector, panel, panelId,
14170 anchorId = $( anchor ).uniqueId().attr( "id" ),
14171 tab = $( anchor ).closest( "li" ),
14172 originalAriaControls = tab.attr( "aria-controls" );
14174 // inline tab
14175 if ( isLocal( anchor ) ) {
14176 selector = anchor.hash;
14177 panel = that.element.find( that._sanitizeSelector( selector ) );
14178 // remote tab
14179 } else {
14180 panelId = that._tabId( tab );
14181 selector = "#" + panelId;
14182 panel = that.element.find( selector );
14183 if ( !panel.length ) {
14184 panel = that._createPanel( panelId );
14185 panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
14187 panel.attr( "aria-live", "polite" );
14190 if ( panel.length) {
14191 that.panels = that.panels.add( panel );
14193 if ( originalAriaControls ) {
14194 tab.data( "ui-tabs-aria-controls", originalAriaControls );
14196 tab.attr({
14197 "aria-controls": selector.substring( 1 ),
14198 "aria-labelledby": anchorId
14200 panel.attr( "aria-labelledby", anchorId );
14203 this.panels
14204 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14205 .attr( "role", "tabpanel" );
14208 // allow overriding how to find the list for rare usage scenarios (#7715)
14209 _getList: function() {
14210 return this.element.find( "ol,ul" ).eq( 0 );
14213 _createPanel: function( id ) {
14214 return $( "<div>" )
14215 .attr( "id", id )
14216 .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
14217 .data( "ui-tabs-destroy", true );
14220 _setupDisabled: function( disabled ) {
14221 if ( $.isArray( disabled ) ) {
14222 if ( !disabled.length ) {
14223 disabled = false;
14224 } else if ( disabled.length === this.anchors.length ) {
14225 disabled = true;
14229 // disable tabs
14230 for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
14231 if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
14232 $( li )
14233 .addClass( "ui-state-disabled" )
14234 .attr( "aria-disabled", "true" );
14235 } else {
14236 $( li )
14237 .removeClass( "ui-state-disabled" )
14238 .removeAttr( "aria-disabled" );
14242 this.options.disabled = disabled;
14245 _setupEvents: function( event ) {
14246 var events = {
14247 click: function( event ) {
14248 event.preventDefault();
14251 if ( event ) {
14252 $.each( event.split(" "), function( index, eventName ) {
14253 events[ eventName ] = "_eventHandler";
14257 this._off( this.anchors.add( this.tabs ).add( this.panels ) );
14258 this._on( this.anchors, events );
14259 this._on( this.tabs, { keydown: "_tabKeydown" } );
14260 this._on( this.panels, { keydown: "_panelKeydown" } );
14262 this._focusable( this.tabs );
14263 this._hoverable( this.tabs );
14266 _setupHeightStyle: function( heightStyle ) {
14267 var maxHeight,
14268 parent = this.element.parent();
14270 if ( heightStyle === "fill" ) {
14271 maxHeight = parent.height();
14272 maxHeight -= this.element.outerHeight() - this.element.height();
14274 this.element.siblings( ":visible" ).each(function() {
14275 var elem = $( this ),
14276 position = elem.css( "position" );
14278 if ( position === "absolute" || position === "fixed" ) {
14279 return;
14281 maxHeight -= elem.outerHeight( true );
14284 this.element.children().not( this.panels ).each(function() {
14285 maxHeight -= $( this ).outerHeight( true );
14288 this.panels.each(function() {
14289 $( this ).height( Math.max( 0, maxHeight -
14290 $( this ).innerHeight() + $( this ).height() ) );
14292 .css( "overflow", "auto" );
14293 } else if ( heightStyle === "auto" ) {
14294 maxHeight = 0;
14295 this.panels.each(function() {
14296 maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
14297 }).height( maxHeight );
14301 _eventHandler: function( event ) {
14302 var options = this.options,
14303 active = this.active,
14304 anchor = $( event.currentTarget ),
14305 tab = anchor.closest( "li" ),
14306 clickedIsActive = tab[ 0 ] === active[ 0 ],
14307 collapsing = clickedIsActive && options.collapsible,
14308 toShow = collapsing ? $() : this._getPanelForTab( tab ),
14309 toHide = !active.length ? $() : this._getPanelForTab( active ),
14310 eventData = {
14311 oldTab: active,
14312 oldPanel: toHide,
14313 newTab: collapsing ? $() : tab,
14314 newPanel: toShow
14317 event.preventDefault();
14319 if ( tab.hasClass( "ui-state-disabled" ) ||
14320 // tab is already loading
14321 tab.hasClass( "ui-tabs-loading" ) ||
14322 // can't switch durning an animation
14323 this.running ||
14324 // click on active header, but not collapsible
14325 ( clickedIsActive && !options.collapsible ) ||
14326 // allow canceling activation
14327 ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
14328 return;
14331 options.active = collapsing ? false : this.tabs.index( tab );
14333 this.active = clickedIsActive ? $() : tab;
14334 if ( this.xhr ) {
14335 this.xhr.abort();
14338 if ( !toHide.length && !toShow.length ) {
14339 $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
14342 if ( toShow.length ) {
14343 this.load( this.tabs.index( tab ), event );
14345 this._toggle( event, eventData );
14348 // handles show/hide for selecting tabs
14349 _toggle: function( event, eventData ) {
14350 var that = this,
14351 toShow = eventData.newPanel,
14352 toHide = eventData.oldPanel;
14354 this.running = true;
14356 function complete() {
14357 that.running = false;
14358 that._trigger( "activate", event, eventData );
14361 function show() {
14362 eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
14364 if ( toShow.length && that.options.show ) {
14365 that._show( toShow, that.options.show, complete );
14366 } else {
14367 toShow.show();
14368 complete();
14372 // start out by hiding, then showing, then completing
14373 if ( toHide.length && this.options.hide ) {
14374 this._hide( toHide, this.options.hide, function() {
14375 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14376 show();
14378 } else {
14379 eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
14380 toHide.hide();
14381 show();
14384 toHide.attr({
14385 "aria-expanded": "false",
14386 "aria-hidden": "true"
14388 eventData.oldTab.attr( "aria-selected", "false" );
14389 // If we're switching tabs, remove the old tab from the tab order.
14390 // If we're opening from collapsed state, remove the previous tab from the tab order.
14391 // If we're collapsing, then keep the collapsing tab in the tab order.
14392 if ( toShow.length && toHide.length ) {
14393 eventData.oldTab.attr( "tabIndex", -1 );
14394 } else if ( toShow.length ) {
14395 this.tabs.filter(function() {
14396 return $( this ).attr( "tabIndex" ) === 0;
14398 .attr( "tabIndex", -1 );
14401 toShow.attr({
14402 "aria-expanded": "true",
14403 "aria-hidden": "false"
14405 eventData.newTab.attr({
14406 "aria-selected": "true",
14407 tabIndex: 0
14411 _activate: function( index ) {
14412 var anchor,
14413 active = this._findActive( index );
14415 // trying to activate the already active panel
14416 if ( active[ 0 ] === this.active[ 0 ] ) {
14417 return;
14420 // trying to collapse, simulate a click on the current active header
14421 if ( !active.length ) {
14422 active = this.active;
14425 anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
14426 this._eventHandler({
14427 target: anchor,
14428 currentTarget: anchor,
14429 preventDefault: $.noop
14433 _findActive: function( index ) {
14434 return index === false ? $() : this.tabs.eq( index );
14437 _getIndex: function( index ) {
14438 // meta-function to give users option to provide a href string instead of a numerical index.
14439 if ( typeof index === "string" ) {
14440 index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
14443 return index;
14446 _destroy: function() {
14447 if ( this.xhr ) {
14448 this.xhr.abort();
14451 this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
14453 this.tablist
14454 .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
14455 .removeAttr( "role" );
14457 this.anchors
14458 .removeClass( "ui-tabs-anchor" )
14459 .removeAttr( "role" )
14460 .removeAttr( "tabIndex" )
14461 .removeUniqueId();
14463 this.tabs.add( this.panels ).each(function() {
14464 if ( $.data( this, "ui-tabs-destroy" ) ) {
14465 $( this ).remove();
14466 } else {
14467 $( this )
14468 .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
14469 "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
14470 .removeAttr( "tabIndex" )
14471 .removeAttr( "aria-live" )
14472 .removeAttr( "aria-busy" )
14473 .removeAttr( "aria-selected" )
14474 .removeAttr( "aria-labelledby" )
14475 .removeAttr( "aria-hidden" )
14476 .removeAttr( "aria-expanded" )
14477 .removeAttr( "role" );
14481 this.tabs.each(function() {
14482 var li = $( this ),
14483 prev = li.data( "ui-tabs-aria-controls" );
14484 if ( prev ) {
14486 .attr( "aria-controls", prev )
14487 .removeData( "ui-tabs-aria-controls" );
14488 } else {
14489 li.removeAttr( "aria-controls" );
14493 this.panels.show();
14495 if ( this.options.heightStyle !== "content" ) {
14496 this.panels.css( "height", "" );
14500 enable: function( index ) {
14501 var disabled = this.options.disabled;
14502 if ( disabled === false ) {
14503 return;
14506 if ( index === undefined ) {
14507 disabled = false;
14508 } else {
14509 index = this._getIndex( index );
14510 if ( $.isArray( disabled ) ) {
14511 disabled = $.map( disabled, function( num ) {
14512 return num !== index ? num : null;
14514 } else {
14515 disabled = $.map( this.tabs, function( li, num ) {
14516 return num !== index ? num : null;
14520 this._setupDisabled( disabled );
14523 disable: function( index ) {
14524 var disabled = this.options.disabled;
14525 if ( disabled === true ) {
14526 return;
14529 if ( index === undefined ) {
14530 disabled = true;
14531 } else {
14532 index = this._getIndex( index );
14533 if ( $.inArray( index, disabled ) !== -1 ) {
14534 return;
14536 if ( $.isArray( disabled ) ) {
14537 disabled = $.merge( [ index ], disabled ).sort();
14538 } else {
14539 disabled = [ index ];
14542 this._setupDisabled( disabled );
14545 load: function( index, event ) {
14546 index = this._getIndex( index );
14547 var that = this,
14548 tab = this.tabs.eq( index ),
14549 anchor = tab.find( ".ui-tabs-anchor" ),
14550 panel = this._getPanelForTab( tab ),
14551 eventData = {
14552 tab: tab,
14553 panel: panel
14556 // not remote
14557 if ( isLocal( anchor[ 0 ] ) ) {
14558 return;
14561 this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
14563 // support: jQuery <1.8
14564 // jQuery <1.8 returns false if the request is canceled in beforeSend,
14565 // but as of 1.8, $.ajax() always returns a jqXHR object.
14566 if ( this.xhr && this.xhr.statusText !== "canceled" ) {
14567 tab.addClass( "ui-tabs-loading" );
14568 panel.attr( "aria-busy", "true" );
14570 this.xhr
14571 .success(function( response ) {
14572 // support: jQuery <1.8
14573 // http://bugs.jquery.com/ticket/11778
14574 setTimeout(function() {
14575 panel.html( response );
14576 that._trigger( "load", event, eventData );
14577 }, 1 );
14579 .complete(function( jqXHR, status ) {
14580 // support: jQuery <1.8
14581 // http://bugs.jquery.com/ticket/11778
14582 setTimeout(function() {
14583 if ( status === "abort" ) {
14584 that.panels.stop( false, true );
14587 tab.removeClass( "ui-tabs-loading" );
14588 panel.removeAttr( "aria-busy" );
14590 if ( jqXHR === that.xhr ) {
14591 delete that.xhr;
14593 }, 1 );
14598 _ajaxSettings: function( anchor, event, eventData ) {
14599 var that = this;
14600 return {
14601 url: anchor.attr( "href" ),
14602 beforeSend: function( jqXHR, settings ) {
14603 return that._trigger( "beforeLoad", event,
14604 $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
14609 _getPanelForTab: function( tab ) {
14610 var id = $( tab ).attr( "aria-controls" );
14611 return this.element.find( this._sanitizeSelector( "#" + id ) );
14615 })( jQuery );
14617 (function( $ ) {
14619 var increments = 0;
14621 function addDescribedBy( elem, id ) {
14622 var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
14623 describedby.push( id );
14624 elem
14625 .data( "ui-tooltip-id", id )
14626 .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
14629 function removeDescribedBy( elem ) {
14630 var id = elem.data( "ui-tooltip-id" ),
14631 describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
14632 index = $.inArray( id, describedby );
14633 if ( index !== -1 ) {
14634 describedby.splice( index, 1 );
14637 elem.removeData( "ui-tooltip-id" );
14638 describedby = $.trim( describedby.join( " " ) );
14639 if ( describedby ) {
14640 elem.attr( "aria-describedby", describedby );
14641 } else {
14642 elem.removeAttr( "aria-describedby" );
14646 $.widget( "ui.tooltip", {
14647 version: "1.10.3",
14648 options: {
14649 content: function() {
14650 // support: IE<9, Opera in jQuery <1.7
14651 // .text() can't accept undefined, so coerce to a string
14652 var title = $( this ).attr( "title" ) || "";
14653 // Escape title, since we're going from an attribute to raw HTML
14654 return $( "<a>" ).text( title ).html();
14656 hide: true,
14657 // Disabled elements have inconsistent behavior across browsers (#8661)
14658 items: "[title]:not([disabled])",
14659 position: {
14660 my: "left top+15",
14661 at: "left bottom",
14662 collision: "flipfit flip"
14664 show: true,
14665 tooltipClass: null,
14666 track: false,
14668 // callbacks
14669 close: null,
14670 open: null
14673 _create: function() {
14674 this._on({
14675 mouseover: "open",
14676 focusin: "open"
14679 // IDs of generated tooltips, needed for destroy
14680 this.tooltips = {};
14681 // IDs of parent tooltips where we removed the title attribute
14682 this.parents = {};
14684 if ( this.options.disabled ) {
14685 this._disable();
14689 _setOption: function( key, value ) {
14690 var that = this;
14692 if ( key === "disabled" ) {
14693 this[ value ? "_disable" : "_enable" ]();
14694 this.options[ key ] = value;
14695 // disable element style changes
14696 return;
14699 this._super( key, value );
14701 if ( key === "content" ) {
14702 $.each( this.tooltips, function( id, element ) {
14703 that._updateContent( element );
14708 _disable: function() {
14709 var that = this;
14711 // close open tooltips
14712 $.each( this.tooltips, function( id, element ) {
14713 var event = $.Event( "blur" );
14714 event.target = event.currentTarget = element[0];
14715 that.close( event, true );
14718 // remove title attributes to prevent native tooltips
14719 this.element.find( this.options.items ).addBack().each(function() {
14720 var element = $( this );
14721 if ( element.is( "[title]" ) ) {
14722 element
14723 .data( "ui-tooltip-title", element.attr( "title" ) )
14724 .attr( "title", "" );
14729 _enable: function() {
14730 // restore title attributes
14731 this.element.find( this.options.items ).addBack().each(function() {
14732 var element = $( this );
14733 if ( element.data( "ui-tooltip-title" ) ) {
14734 element.attr( "title", element.data( "ui-tooltip-title" ) );
14739 open: function( event ) {
14740 var that = this,
14741 target = $( event ? event.target : this.element )
14742 // we need closest here due to mouseover bubbling,
14743 // but always pointing at the same event target
14744 .closest( this.options.items );
14746 // No element to show a tooltip for or the tooltip is already open
14747 if ( !target.length || target.data( "ui-tooltip-id" ) ) {
14748 return;
14751 if ( target.attr( "title" ) ) {
14752 target.data( "ui-tooltip-title", target.attr( "title" ) );
14755 target.data( "ui-tooltip-open", true );
14757 // kill parent tooltips, custom or native, for hover
14758 if ( event && event.type === "mouseover" ) {
14759 target.parents().each(function() {
14760 var parent = $( this ),
14761 blurEvent;
14762 if ( parent.data( "ui-tooltip-open" ) ) {
14763 blurEvent = $.Event( "blur" );
14764 blurEvent.target = blurEvent.currentTarget = this;
14765 that.close( blurEvent, true );
14767 if ( parent.attr( "title" ) ) {
14768 parent.uniqueId();
14769 that.parents[ this.id ] = {
14770 element: this,
14771 title: parent.attr( "title" )
14773 parent.attr( "title", "" );
14778 this._updateContent( target, event );
14781 _updateContent: function( target, event ) {
14782 var content,
14783 contentOption = this.options.content,
14784 that = this,
14785 eventType = event ? event.type : null;
14787 if ( typeof contentOption === "string" ) {
14788 return this._open( event, target, contentOption );
14791 content = contentOption.call( target[0], function( response ) {
14792 // ignore async response if tooltip was closed already
14793 if ( !target.data( "ui-tooltip-open" ) ) {
14794 return;
14796 // IE may instantly serve a cached response for ajax requests
14797 // delay this call to _open so the other call to _open runs first
14798 that._delay(function() {
14799 // jQuery creates a special event for focusin when it doesn't
14800 // exist natively. To improve performance, the native event
14801 // object is reused and the type is changed. Therefore, we can't
14802 // rely on the type being correct after the event finished
14803 // bubbling, so we set it back to the previous value. (#8740)
14804 if ( event ) {
14805 event.type = eventType;
14807 this._open( event, target, response );
14810 if ( content ) {
14811 this._open( event, target, content );
14815 _open: function( event, target, content ) {
14816 var tooltip, events, delayedShow,
14817 positionOption = $.extend( {}, this.options.position );
14819 if ( !content ) {
14820 return;
14823 // Content can be updated multiple times. If the tooltip already
14824 // exists, then just update the content and bail.
14825 tooltip = this._find( target );
14826 if ( tooltip.length ) {
14827 tooltip.find( ".ui-tooltip-content" ).html( content );
14828 return;
14831 // if we have a title, clear it to prevent the native tooltip
14832 // we have to check first to avoid defining a title if none exists
14833 // (we don't want to cause an element to start matching [title])
14835 // We use removeAttr only for key events, to allow IE to export the correct
14836 // accessible attributes. For mouse events, set to empty string to avoid
14837 // native tooltip showing up (happens only when removing inside mouseover).
14838 if ( target.is( "[title]" ) ) {
14839 if ( event && event.type === "mouseover" ) {
14840 target.attr( "title", "" );
14841 } else {
14842 target.removeAttr( "title" );
14846 tooltip = this._tooltip( target );
14847 addDescribedBy( target, tooltip.attr( "id" ) );
14848 tooltip.find( ".ui-tooltip-content" ).html( content );
14850 function position( event ) {
14851 positionOption.of = event;
14852 if ( tooltip.is( ":hidden" ) ) {
14853 return;
14855 tooltip.position( positionOption );
14857 if ( this.options.track && event && /^mouse/.test( event.type ) ) {
14858 this._on( this.document, {
14859 mousemove: position
14861 // trigger once to override element-relative positioning
14862 position( event );
14863 } else {
14864 tooltip.position( $.extend({
14865 of: target
14866 }, this.options.position ) );
14869 tooltip.hide();
14871 this._show( tooltip, this.options.show );
14872 // Handle tracking tooltips that are shown with a delay (#8644). As soon
14873 // as the tooltip is visible, position the tooltip using the most recent
14874 // event.
14875 if ( this.options.show && this.options.show.delay ) {
14876 delayedShow = this.delayedShow = setInterval(function() {
14877 if ( tooltip.is( ":visible" ) ) {
14878 position( positionOption.of );
14879 clearInterval( delayedShow );
14881 }, $.fx.interval );
14884 this._trigger( "open", event, { tooltip: tooltip } );
14886 events = {
14887 keyup: function( event ) {
14888 if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
14889 var fakeEvent = $.Event(event);
14890 fakeEvent.currentTarget = target[0];
14891 this.close( fakeEvent, true );
14894 remove: function() {
14895 this._removeTooltip( tooltip );
14898 if ( !event || event.type === "mouseover" ) {
14899 events.mouseleave = "close";
14901 if ( !event || event.type === "focusin" ) {
14902 events.focusout = "close";
14904 this._on( true, target, events );
14907 close: function( event ) {
14908 var that = this,
14909 target = $( event ? event.currentTarget : this.element ),
14910 tooltip = this._find( target );
14912 // disabling closes the tooltip, so we need to track when we're closing
14913 // to avoid an infinite loop in case the tooltip becomes disabled on close
14914 if ( this.closing ) {
14915 return;
14918 // Clear the interval for delayed tracking tooltips
14919 clearInterval( this.delayedShow );
14921 // only set title if we had one before (see comment in _open())
14922 if ( target.data( "ui-tooltip-title" ) ) {
14923 target.attr( "title", target.data( "ui-tooltip-title" ) );
14926 removeDescribedBy( target );
14928 tooltip.stop( true );
14929 this._hide( tooltip, this.options.hide, function() {
14930 that._removeTooltip( $( this ) );
14933 target.removeData( "ui-tooltip-open" );
14934 this._off( target, "mouseleave focusout keyup" );
14935 // Remove 'remove' binding only on delegated targets
14936 if ( target[0] !== this.element[0] ) {
14937 this._off( target, "remove" );
14939 this._off( this.document, "mousemove" );
14941 if ( event && event.type === "mouseleave" ) {
14942 $.each( this.parents, function( id, parent ) {
14943 $( parent.element ).attr( "title", parent.title );
14944 delete that.parents[ id ];
14948 this.closing = true;
14949 this._trigger( "close", event, { tooltip: tooltip } );
14950 this.closing = false;
14953 _tooltip: function( element ) {
14954 var id = "ui-tooltip-" + increments++,
14955 tooltip = $( "<div>" )
14956 .attr({
14957 id: id,
14958 role: "tooltip"
14960 .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
14961 ( this.options.tooltipClass || "" ) );
14962 $( "<div>" )
14963 .addClass( "ui-tooltip-content" )
14964 .appendTo( tooltip );
14965 tooltip.appendTo( this.document[0].body );
14966 this.tooltips[ id ] = element;
14967 return tooltip;
14970 _find: function( target ) {
14971 var id = target.data( "ui-tooltip-id" );
14972 return id ? $( "#" + id ) : $();
14975 _removeTooltip: function( tooltip ) {
14976 tooltip.remove();
14977 delete this.tooltips[ tooltip.attr( "id" ) ];
14980 _destroy: function() {
14981 var that = this;
14983 // close open tooltips
14984 $.each( this.tooltips, function( id, element ) {
14985 // Delegate to close method to handle common cleanup
14986 var event = $.Event( "blur" );
14987 event.target = event.currentTarget = element[0];
14988 that.close( event, true );
14990 // Remove immediately; destroying an open tooltip doesn't use the
14991 // hide animation
14992 $( "#" + id ).remove();
14994 // Restore the title
14995 if ( element.data( "ui-tooltip-title" ) ) {
14996 element.attr( "title", element.data( "ui-tooltip-title" ) );
14997 element.removeData( "ui-tooltip-title" );
15003 }( jQuery ) );