]>
git.immae.eu Git - github/wallabag/wallabag.git/blob - themes/default/js/jquery-ui-1.10.4.custom.js
1 /*! jQuery UI - v1.10.4 - 2014-03-08
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.position.js, jquery.ui.autocomplete.js, jquery.ui.menu.js
4 * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId
= /^ui-id-\d+$/;
11 // $.ui might exist from components with no dependencies, e.g., $.ui.position
45 focus: (function( orig
) {
46 return function( delay
, fn
) {
47 return typeof delay
=== "number" ?
48 this.each(function() {
50 setTimeout(function() {
57 orig
.apply( this, arguments
);
61 scrollParent: function() {
63 if (($.ui
.ie
&& (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
64 scrollParent
= this.parents().filter(function() {
65 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
68 scrollParent
= this.parents().filter(function() {
69 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
73 return (/fixed/).test(this.css("position")) || !scrollParent
.length
? $(document
) : scrollParent
;
76 zIndex: function( zIndex
) {
77 if ( zIndex
!== undefined ) {
78 return this.css( "zIndex", zIndex
);
82 var elem
= $( this[ 0 ] ), position
, value
;
83 while ( elem
.length
&& elem
[ 0 ] !== document
) {
84 // Ignore z-index if position is set to a value where z-index is ignored by the browser
85 // This makes behavior of this function consistent across browsers
86 // WebKit always returns auto if the element is positioned
87 position
= elem
.css( "position" );
88 if ( position
=== "absolute" || position
=== "relative" || position
=== "fixed" ) {
89 // IE returns 0 when zIndex is not specified
90 // other browsers return a string
91 // we ignore the case of nested elements with an explicit value of 0
92 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
93 value
= parseInt( elem
.css( "zIndex" ), 10 );
94 if ( !isNaN( value
) && value
!== 0 ) {
105 uniqueId: function() {
106 return this.each(function() {
108 this.id
= "ui-id-" + (++uuid
);
113 removeUniqueId: function() {
114 return this.each(function() {
115 if ( runiqueId
.test( this.id
) ) {
116 $( this ).removeAttr( "id" );
123 function focusable( element
, isTabIndexNotNaN
) {
124 var map
, mapName
, img
,
125 nodeName
= element
.nodeName
.toLowerCase();
126 if ( "area" === nodeName
) {
127 map
= element
.parentNode
;
129 if ( !element
.href
|| !mapName
|| map
.nodeName
.toLowerCase() !== "map" ) {
132 img
= $( "img[usemap=#" + mapName
+ "]" )[0];
133 return !!img
&& visible( img
);
135 return ( /input|select|textarea|button|object/.test( nodeName
) ?
138 element
.href
|| isTabIndexNotNaN :
140 // the element and all of its ancestors must be visible
144 function visible( element
) {
145 return $.expr
.filters
.visible( element
) &&
146 !$( element
).parents().addBack().filter(function() {
147 return $.css( this, "visibility" ) === "hidden";
151 $.extend( $.expr
[ ":" ], {
152 data: $.expr
.createPseudo
?
153 $.expr
.createPseudo(function( dataName
) {
154 return function( elem
) {
155 return !!$.data( elem
, dataName
);
158 // support: jQuery <1.8
159 function( elem
, i
, match
) {
160 return !!$.data( elem
, match
[ 3 ] );
163 focusable: function( element
) {
164 return focusable( element
, !isNaN( $.attr( element
, "tabindex" ) ) );
167 tabbable: function( element
) {
168 var tabIndex
= $.attr( element
, "tabindex" ),
169 isTabIndexNaN
= isNaN( tabIndex
);
170 return ( isTabIndexNaN
|| tabIndex
>= 0 ) && focusable( element
, !isTabIndexNaN
);
174 // support: jQuery <1.8
175 if ( !$( "<a>" ).outerWidth( 1 ).jquery
) {
176 $.each( [ "Width", "Height" ], function( i
, name
) {
177 var side
= name
=== "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
178 type
= name
.toLowerCase(),
180 innerWidth: $.fn
.innerWidth
,
181 innerHeight: $.fn
.innerHeight
,
182 outerWidth: $.fn
.outerWidth
,
183 outerHeight: $.fn
.outerHeight
186 function reduce( elem
, size
, border
, margin
) {
187 $.each( side
, function() {
188 size
-= parseFloat( $.css( elem
, "padding" + this ) ) || 0;
190 size
-= parseFloat( $.css( elem
, "border" + this + "Width" ) ) || 0;
193 size
-= parseFloat( $.css( elem
, "margin" + this ) ) || 0;
199 $.fn
[ "inner" + name
] = function( size
) {
200 if ( size
=== undefined ) {
201 return orig
[ "inner" + name
].call( this );
204 return this.each(function() {
205 $( this ).css( type
, reduce( this, size
) + "px" );
209 $.fn
[ "outer" + name
] = function( size
, margin
) {
210 if ( typeof size
!== "number" ) {
211 return orig
[ "outer" + name
].call( this, size
);
214 return this.each(function() {
215 $( this).css( type
, reduce( this, size
, true, margin
) + "px" );
221 // support: jQuery <1.8
222 if ( !$.fn
.addBack
) {
223 $.fn
.addBack = function( selector
) {
224 return this.add( selector
== null ?
225 this.prevObject : this.prevObject
.filter( selector
)
230 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
231 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
232 $.fn
.removeData
= (function( removeData
) {
233 return function( key
) {
234 if ( arguments
.length
) {
235 return removeData
.call( this, $.camelCase( key
) );
237 return removeData
.call( this );
240 })( $.fn
.removeData
);
248 $.ui
.ie
= !!/msie
[\w
.]+/.exec( navigator
.userAgent
.toLowerCase() );
250 $.support
.selectstart
= "onselectstart" in document
.createElement( "div" );
252 disableSelection: function() {
253 return this.bind( ( $.support
.selectstart
? "selectstart" : "mousedown" ) +
254 ".ui-disableSelection", function( event
) {
255 event
.preventDefault();
259 enableSelection: function() {
260 return this.unbind( ".ui-disableSelection" );
265 // $.ui.plugin is deprecated. Use $.widget() extensions instead.
267 add: function( module
, option
, set ) {
269 proto
= $.ui
[ module
].prototype;
271 proto
.plugins
[ i
] = proto
.plugins
[ i
] || [];
272 proto
.plugins
[ i
].push( [ option
, set[ i
] ] );
275 call: function( instance
, name
, args
) {
277 set = instance
.plugins
[ name
];
278 if ( !set || !instance
.element
[ 0 ].parentNode
|| instance
.element
[ 0 ].parentNode
.nodeType
=== 11 ) {
282 for ( i
= 0; i
< set.length
; i
++ ) {
283 if ( instance
.options
[ set[ i
][ 0 ] ] ) {
284 set[ i
][ 1 ].apply( instance
.element
, args
);
290 // only used by resizable
291 hasScroll: function( el
, a
) {
293 //If overflow is hidden, the element might have extra content, but the user wants to hide it
294 if ( $( el
).css( "overflow" ) === "hidden") {
298 var scroll
= ( a
&& a
=== "left" ) ? "scrollLeft" : "scrollTop",
301 if ( el
[ scroll
] > 0 ) {
305 // TODO: determine which cases actually cause this to happen
306 // if the element doesn't have the scroll set, see if it's possible to
309 has
= ( el
[ scroll
] > 0 );
316 (function( $, undefined ) {
319 slice
= Array
.prototype.slice
,
320 _cleanData
= $.cleanData
;
321 $.cleanData = function( elems
) {
322 for ( var i
= 0, elem
; (elem
= elems
[i
]) != null; i
++ ) {
324 $( elem
).triggerHandler( "remove" );
325 // http://bugs.jquery.com/ticket/8235
331 $.widget = function( name
, base
, prototype ) {
332 var fullName
, existingConstructor
, constructor, basePrototype
,
333 // proxiedPrototype allows the provided prototype to remain unmodified
334 // so that it can be used as a mixin for multiple widgets (#8876)
335 proxiedPrototype
= {},
336 namespace = name
.split( "." )[ 0 ];
338 name
= name
.split( "." )[ 1 ];
339 fullName
= namespace + "-" + name
;
346 // create selector for plugin
347 $.expr
[ ":" ][ fullName
.toLowerCase() ] = function( elem
) {
348 return !!$.data( elem
, fullName
);
351 $[ namespace ] = $[ namespace ] || {};
352 existingConstructor
= $[ namespace ][ name
];
353 constructor = $[ namespace ][ name
] = function( options
, element
) {
354 // allow instantiation without "new" keyword
355 if ( !this._createWidget
) {
356 return new constructor( options
, element
);
359 // allow instantiation without initializing for simple inheritance
360 // must use "new" keyword (the code above always passes args)
361 if ( arguments
.length
) {
362 this._createWidget( options
, element
);
365 // extend with the existing constructor to carry over any static properties
366 $.extend( constructor, existingConstructor
, {
367 version: prototype.version
,
368 // copy the object used to create the prototype in case we need to
369 // redefine the widget later
370 _proto: $.extend( {}, prototype ),
371 // track widgets that inherit from this widget in case this widget is
372 // redefined after a widget inherits from it
373 _childConstructors: []
376 basePrototype
= new base();
377 // we need to make the options hash a property directly on the new instance
378 // otherwise we'll modify the options hash on the prototype that we're
380 basePrototype
.options
= $.widget
.extend( {}, basePrototype
.options
);
381 $.each( prototype, function( prop
, value
) {
382 if ( !$.isFunction( value
) ) {
383 proxiedPrototype
[ prop
] = value
;
386 proxiedPrototype
[ prop
] = (function() {
387 var _super = function() {
388 return base
.prototype[ prop
].apply( this, arguments
);
390 _superApply = function( args
) {
391 return base
.prototype[ prop
].apply( this, args
);
394 var __super
= this._super
,
395 __superApply
= this._superApply
,
398 this._super
= _super
;
399 this._superApply
= _superApply
;
401 returnValue
= value
.apply( this, arguments
);
403 this._super
= __super
;
404 this._superApply
= __superApply
;
410 constructor.prototype = $.widget
.extend( basePrototype
, {
411 // TODO: remove support for widgetEventPrefix
412 // always use the name + a colon as the prefix, e.g., draggable:start
413 // don't prefix for widgets that aren't DOM-based
414 widgetEventPrefix: existingConstructor
? (basePrototype
.widgetEventPrefix
|| name
) : name
415 }, proxiedPrototype
, {
416 constructor: constructor,
417 namespace: namespace,
419 widgetFullName: fullName
422 // If this widget is being redefined then we need to find all widgets that
423 // are inheriting from it and redefine all of them so that they inherit from
424 // the new version of this widget. We're essentially trying to replace one
425 // level in the prototype chain.
426 if ( existingConstructor
) {
427 $.each( existingConstructor
._childConstructors
, function( i
, child
) {
428 var childPrototype
= child
.prototype;
430 // redefine the child widget using the same prototype that was
431 // originally used, but inherit from the new version of the base
432 $.widget( childPrototype
.namespace + "." + childPrototype
.widgetName
, constructor, child
._proto
);
434 // remove the list of existing child constructors from the old constructor
435 // so the old child constructors can be garbage collected
436 delete existingConstructor
._childConstructors
;
438 base
._childConstructors
.push( constructor );
441 $.widget
.bridge( name
, constructor );
444 $.widget
.extend = function( target
) {
445 var input
= slice
.call( arguments
, 1 ),
447 inputLength
= input
.length
,
450 for ( ; inputIndex
< inputLength
; inputIndex
++ ) {
451 for ( key
in input
[ inputIndex
] ) {
452 value
= input
[ inputIndex
][ key
];
453 if ( input
[ inputIndex
].hasOwnProperty( key
) && value
!== undefined ) {
455 if ( $.isPlainObject( value
) ) {
456 target
[ key
] = $.isPlainObject( target
[ key
] ) ?
457 $.widget
.extend( {}, target
[ key
], value
) :
458 // Don't extend strings, arrays, etc. with objects
459 $.widget
.extend( {}, value
);
460 // Copy everything else by reference
462 target
[ key
] = value
;
470 $.widget
.bridge = function( name
, object
) {
471 var fullName
= object
.prototype.widgetFullName
|| name
;
472 $.fn
[ name
] = function( options
) {
473 var isMethodCall
= typeof options
=== "string",
474 args
= slice
.call( arguments
, 1 ),
477 // allow multiple hashes to be passed on init
478 options
= !isMethodCall
&& args
.length
?
479 $.widget
.extend
.apply( null, [ options
].concat(args
) ) :
482 if ( isMethodCall
) {
483 this.each(function() {
485 instance
= $.data( this, fullName
);
487 return $.error( "cannot call methods on " + name
+ " prior to initialization; " +
488 "attempted to call method '" + options
+ "'" );
490 if ( !$.isFunction( instance
[options
] ) || options
.charAt( 0 ) === "_" ) {
491 return $.error( "no such method '" + options
+ "' for " + name
+ " widget instance" );
493 methodValue
= instance
[ options
].apply( instance
, args
);
494 if ( methodValue
!== instance
&& methodValue
!== undefined ) {
495 returnValue
= methodValue
&& methodValue
.jquery
?
496 returnValue
.pushStack( methodValue
.get() ) :
502 this.each(function() {
503 var instance
= $.data( this, fullName
);
505 instance
.option( options
|| {} )._init();
507 $.data( this, fullName
, new object( options
, this ) );
516 $.Widget = function( /* options, element */ ) {};
517 $.Widget
._childConstructors
= [];
519 $.Widget
.prototype = {
520 widgetName: "widget",
521 widgetEventPrefix: "",
522 defaultElement: "<div>",
529 _createWidget: function( options
, element
) {
530 element
= $( element
|| this.defaultElement
|| this )[ 0 ];
531 this.element
= $( element
);
533 this.eventNamespace
= "." + this.widgetName
+ this.uuid
;
534 this.options
= $.widget
.extend( {},
536 this._getCreateOptions(),
540 this.hoverable
= $();
541 this.focusable
= $();
543 if ( element
!== this ) {
544 $.data( element
, this.widgetFullName
, this );
545 this._on( true, this.element
, {
546 remove: function( event
) {
547 if ( event
.target
=== element
) {
552 this.document
= $( element
.style
?
553 // element within the document
554 element
.ownerDocument :
555 // element is window or document
556 element
.document
|| element
);
557 this.window
= $( this.document
[0].defaultView
|| this.document
[0].parentWindow
);
561 this._trigger( "create", null, this._getCreateEventData() );
564 _getCreateOptions: $.noop
,
565 _getCreateEventData: $.noop
,
569 destroy: function() {
571 // we can probably remove the unbind calls in 2.0
572 // all event bindings should go through this._on()
574 .unbind( this.eventNamespace
)
576 // TODO remove dual storage
577 .removeData( this.widgetName
)
578 .removeData( this.widgetFullName
)
579 // support: jquery <1.6.3
580 // http://bugs.jquery.com/ticket/9413
581 .removeData( $.camelCase( this.widgetFullName
) );
583 .unbind( this.eventNamespace
)
584 .removeAttr( "aria-disabled" )
586 this.widgetFullName
+ "-disabled " +
587 "ui-state-disabled" );
589 // clean up events and states
590 this.bindings
.unbind( this.eventNamespace
);
591 this.hoverable
.removeClass( "ui-state-hover" );
592 this.focusable
.removeClass( "ui-state-focus" );
600 option: function( key
, value
) {
606 if ( arguments
.length
=== 0 ) {
607 // don't return a reference to the internal hash
608 return $.widget
.extend( {}, this.options
);
611 if ( typeof key
=== "string" ) {
612 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
614 parts
= key
.split( "." );
616 if ( parts
.length
) {
617 curOption
= options
[ key
] = $.widget
.extend( {}, this.options
[ key
] );
618 for ( i
= 0; i
< parts
.length
- 1; i
++ ) {
619 curOption
[ parts
[ i
] ] = curOption
[ parts
[ i
] ] || {};
620 curOption
= curOption
[ parts
[ i
] ];
623 if ( arguments
.length
=== 1 ) {
624 return curOption
[ key
] === undefined ? null : curOption
[ key
];
626 curOption
[ key
] = value
;
628 if ( arguments
.length
=== 1 ) {
629 return this.options
[ key
] === undefined ? null : this.options
[ key
];
631 options
[ key
] = value
;
635 this._setOptions( options
);
639 _setOptions: function( options
) {
642 for ( key
in options
) {
643 this._setOption( key
, options
[ key
] );
648 _setOption: function( key
, value
) {
649 this.options
[ key
] = value
;
651 if ( key
=== "disabled" ) {
653 .toggleClass( this.widgetFullName
+ "-disabled ui-state-disabled", !!value
)
654 .attr( "aria-disabled", value
);
655 this.hoverable
.removeClass( "ui-state-hover" );
656 this.focusable
.removeClass( "ui-state-focus" );
663 return this._setOption( "disabled", false );
665 disable: function() {
666 return this._setOption( "disabled", true );
669 _on: function( suppressDisabledCheck
, element
, handlers
) {
673 // no suppressDisabledCheck flag, shuffle arguments
674 if ( typeof suppressDisabledCheck
!== "boolean" ) {
676 element
= suppressDisabledCheck
;
677 suppressDisabledCheck
= false;
680 // no element argument, shuffle and use this.element
683 element
= this.element
;
684 delegateElement
= this.widget();
686 // accept selectors, DOM elements
687 element
= delegateElement
= $( element
);
688 this.bindings
= this.bindings
.add( element
);
691 $.each( handlers
, function( event
, handler
) {
692 function handlerProxy() {
693 // allow widgets to customize the disabled handling
694 // - disabled as an array instead of boolean
695 // - disabled class as method for disabling individual parts
696 if ( !suppressDisabledCheck
&&
697 ( instance
.options
.disabled
=== true ||
698 $( this ).hasClass( "ui-state-disabled" ) ) ) {
701 return ( typeof handler
=== "string" ? instance
[ handler
] : handler
)
702 .apply( instance
, arguments
);
705 // copy the guid so direct unbinding works
706 if ( typeof handler
!== "string" ) {
707 handlerProxy
.guid
= handler
.guid
=
708 handler
.guid
|| handlerProxy
.guid
|| $.guid
++;
711 var match
= event
.match( /^(\w+)\s*(.*)$/ ),
712 eventName
= match
[1] + instance
.eventNamespace
,
715 delegateElement
.delegate( selector
, eventName
, handlerProxy
);
717 element
.bind( eventName
, handlerProxy
);
722 _off: function( element
, eventName
) {
723 eventName
= (eventName
|| "").split( " " ).join( this.eventNamespace
+ " " ) + this.eventNamespace
;
724 element
.unbind( eventName
).undelegate( eventName
);
727 _delay: function( handler
, delay
) {
728 function handlerProxy() {
729 return ( typeof handler
=== "string" ? instance
[ handler
] : handler
)
730 .apply( instance
, arguments
);
733 return setTimeout( handlerProxy
, delay
|| 0 );
736 _hoverable: function( element
) {
737 this.hoverable
= this.hoverable
.add( element
);
739 mouseenter: function( event
) {
740 $( event
.currentTarget
).addClass( "ui-state-hover" );
742 mouseleave: function( event
) {
743 $( event
.currentTarget
).removeClass( "ui-state-hover" );
748 _focusable: function( element
) {
749 this.focusable
= this.focusable
.add( element
);
751 focusin: function( event
) {
752 $( event
.currentTarget
).addClass( "ui-state-focus" );
754 focusout: function( event
) {
755 $( event
.currentTarget
).removeClass( "ui-state-focus" );
760 _trigger: function( type
, event
, data
) {
762 callback
= this.options
[ type
];
765 event
= $.Event( event
);
766 event
.type
= ( type
=== this.widgetEventPrefix
?
768 this.widgetEventPrefix
+ type
).toLowerCase();
769 // the original event may come from any element
770 // so we need to reset the target on the new event
771 event
.target
= this.element
[ 0 ];
773 // copy original event properties over to the new event
774 orig
= event
.originalEvent
;
776 for ( prop
in orig
) {
777 if ( !( prop
in event
) ) {
778 event
[ prop
] = orig
[ prop
];
783 this.element
.trigger( event
, data
);
784 return !( $.isFunction( callback
) &&
785 callback
.apply( this.element
[0], [ event
].concat( data
) ) === false ||
786 event
.isDefaultPrevented() );
790 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method
, defaultEffect
) {
791 $.Widget
.prototype[ "_" + method
] = function( element
, options
, callback
) {
792 if ( typeof options
=== "string" ) {
793 options
= { effect: options
};
796 effectName
= !options
?
798 options
=== true || typeof options
=== "number" ?
800 options
.effect
|| defaultEffect
;
801 options
= options
|| {};
802 if ( typeof options
=== "number" ) {
803 options
= { duration: options
};
805 hasOptions
= !$.isEmptyObject( options
);
806 options
.complete
= callback
;
807 if ( options
.delay
) {
808 element
.delay( options
.delay
);
810 if ( hasOptions
&& $.effects
&& $.effects
.effect
[ effectName
] ) {
811 element
[ method
]( options
);
812 } else if ( effectName
!== method
&& element
[ effectName
] ) {
813 element
[ effectName
]( options
.duration
, options
.easing
, callback
);
815 element
.queue(function( next
) {
816 $( this )[ method
]();
818 callback
.call( element
[ 0 ] );
827 (function( $, undefined ) {
831 var cachedScrollbarWidth
,
835 rhorizontal
= /left|center|right/,
836 rvertical
= /top|center|bottom/,
837 roffset
= /[\+\-]\d+(\.[\d]+)?%?/,
840 _position
= $.fn
.position
;
842 function getOffsets( offsets
, width
, height
) {
844 parseFloat( offsets
[ 0 ] ) * ( rpercent
.test( offsets
[ 0 ] ) ? width
/ 100 : 1 ),
845 parseFloat( offsets
[ 1 ] ) * ( rpercent
.test( offsets
[ 1 ] ) ? height
/ 100 : 1 )
849 function parseCss( element
, property
) {
850 return parseInt( $.css( element
, property
), 10 ) || 0;
853 function getDimensions( elem
) {
855 if ( raw
.nodeType
=== 9 ) {
858 height: elem
.height(),
859 offset: { top: 0, left: 0 }
862 if ( $.isWindow( raw
) ) {
865 height: elem
.height(),
866 offset: { top: elem
.scrollTop(), left: elem
.scrollLeft() }
869 if ( raw
.preventDefault
) {
873 offset: { top: raw
.pageY
, left: raw
.pageX
}
877 width: elem
.outerWidth(),
878 height: elem
.outerHeight(),
879 offset: elem
.offset()
884 scrollbarWidth: function() {
885 if ( cachedScrollbarWidth
!== undefined ) {
886 return cachedScrollbarWidth
;
889 div
= $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
890 innerDiv
= div
.children()[0];
892 $( "body" ).append( div
);
893 w1
= innerDiv
.offsetWidth
;
894 div
.css( "overflow", "scroll" );
896 w2
= innerDiv
.offsetWidth
;
899 w2
= div
[0].clientWidth
;
904 return (cachedScrollbarWidth
= w1
- w2
);
906 getScrollInfo: function( within
) {
907 var overflowX
= within
.isWindow
|| within
.isDocument
? "" :
908 within
.element
.css( "overflow-x" ),
909 overflowY
= within
.isWindow
|| within
.isDocument
? "" :
910 within
.element
.css( "overflow-y" ),
911 hasOverflowX
= overflowX
=== "scroll" ||
912 ( overflowX
=== "auto" && within
.width
< within
.element
[0].scrollWidth
),
913 hasOverflowY
= overflowY
=== "scroll" ||
914 ( overflowY
=== "auto" && within
.height
< within
.element
[0].scrollHeight
);
916 width: hasOverflowY
? $.position
.scrollbarWidth() : 0,
917 height: hasOverflowX
? $.position
.scrollbarWidth() : 0
920 getWithinInfo: function( element
) {
921 var withinElement
= $( element
|| window
),
922 isWindow
= $.isWindow( withinElement
[0] ),
923 isDocument
= !!withinElement
[ 0 ] && withinElement
[ 0 ].nodeType
=== 9;
925 element: withinElement
,
927 isDocument: isDocument
,
928 offset: withinElement
.offset() || { left: 0, top: 0 },
929 scrollLeft: withinElement
.scrollLeft(),
930 scrollTop: withinElement
.scrollTop(),
931 width: isWindow
? withinElement
.width() : withinElement
.outerWidth(),
932 height: isWindow
? withinElement
.height() : withinElement
.outerHeight()
937 $.fn
.position = function( options
) {
938 if ( !options
|| !options
.of ) {
939 return _position
.apply( this, arguments
);
942 // make a copy, we don't want to modify arguments
943 options
= $.extend( {}, options
);
945 var atOffset
, targetWidth
, targetHeight
, targetOffset
, basePosition
, dimensions
,
946 target
= $( options
.of ),
947 within
= $.position
.getWithinInfo( options
.within
),
948 scrollInfo
= $.position
.getScrollInfo( within
),
949 collision
= ( options
.collision
|| "flip" ).split( " " ),
952 dimensions
= getDimensions( target
);
953 if ( target
[0].preventDefault
) {
954 // force left top to allow flipping
955 options
.at
= "left top";
957 targetWidth
= dimensions
.width
;
958 targetHeight
= dimensions
.height
;
959 targetOffset
= dimensions
.offset
;
960 // clone to reuse original targetOffset later
961 basePosition
= $.extend( {}, targetOffset
);
963 // force my and at to have valid horizontal and vertical positions
964 // if a value is missing or invalid, it will be converted to center
965 $.each( [ "my", "at" ], function() {
966 var pos
= ( options
[ this ] || "" ).split( " " ),
970 if ( pos
.length
=== 1) {
971 pos
= rhorizontal
.test( pos
[ 0 ] ) ?
972 pos
.concat( [ "center" ] ) :
973 rvertical
.test( pos
[ 0 ] ) ?
974 [ "center" ].concat( pos
) :
975 [ "center", "center" ];
977 pos
[ 0 ] = rhorizontal
.test( pos
[ 0 ] ) ? pos
[ 0 ] : "center";
978 pos
[ 1 ] = rvertical
.test( pos
[ 1 ] ) ? pos
[ 1 ] : "center";
981 horizontalOffset
= roffset
.exec( pos
[ 0 ] );
982 verticalOffset
= roffset
.exec( pos
[ 1 ] );
984 horizontalOffset
? horizontalOffset
[ 0 ] : 0,
985 verticalOffset
? verticalOffset
[ 0 ] : 0
988 // reduce to just the positions without the offsets
990 rposition
.exec( pos
[ 0 ] )[ 0 ],
991 rposition
.exec( pos
[ 1 ] )[ 0 ]
995 // normalize collision option
996 if ( collision
.length
=== 1 ) {
997 collision
[ 1 ] = collision
[ 0 ];
1000 if ( options
.at
[ 0 ] === "right" ) {
1001 basePosition
.left
+= targetWidth
;
1002 } else if ( options
.at
[ 0 ] === "center" ) {
1003 basePosition
.left
+= targetWidth
/ 2;
1006 if ( options
.at
[ 1 ] === "bottom" ) {
1007 basePosition
.top
+= targetHeight
;
1008 } else if ( options
.at
[ 1 ] === "center" ) {
1009 basePosition
.top
+= targetHeight
/ 2;
1012 atOffset
= getOffsets( offsets
.at
, targetWidth
, targetHeight
);
1013 basePosition
.left
+= atOffset
[ 0 ];
1014 basePosition
.top
+= atOffset
[ 1 ];
1016 return this.each(function() {
1017 var collisionPosition
, using
,
1019 elemWidth
= elem
.outerWidth(),
1020 elemHeight
= elem
.outerHeight(),
1021 marginLeft
= parseCss( this, "marginLeft" ),
1022 marginTop
= parseCss( this, "marginTop" ),
1023 collisionWidth
= elemWidth
+ marginLeft
+ parseCss( this, "marginRight" ) + scrollInfo
.width
,
1024 collisionHeight
= elemHeight
+ marginTop
+ parseCss( this, "marginBottom" ) + scrollInfo
.height
,
1025 position
= $.extend( {}, basePosition
),
1026 myOffset
= getOffsets( offsets
.my
, elem
.outerWidth(), elem
.outerHeight() );
1028 if ( options
.my
[ 0 ] === "right" ) {
1029 position
.left
-= elemWidth
;
1030 } else if ( options
.my
[ 0 ] === "center" ) {
1031 position
.left
-= elemWidth
/ 2;
1034 if ( options
.my
[ 1 ] === "bottom" ) {
1035 position
.top
-= elemHeight
;
1036 } else if ( options
.my
[ 1 ] === "center" ) {
1037 position
.top
-= elemHeight
/ 2;
1040 position
.left
+= myOffset
[ 0 ];
1041 position
.top
+= myOffset
[ 1 ];
1043 // if the browser doesn't support fractions, then round for consistent results
1044 if ( !$.support
.offsetFractions
) {
1045 position
.left
= round( position
.left
);
1046 position
.top
= round( position
.top
);
1049 collisionPosition
= {
1050 marginLeft: marginLeft
,
1051 marginTop: marginTop
1054 $.each( [ "left", "top" ], function( i
, dir
) {
1055 if ( $.ui
.position
[ collision
[ i
] ] ) {
1056 $.ui
.position
[ collision
[ i
] ][ dir
]( position
, {
1057 targetWidth: targetWidth
,
1058 targetHeight: targetHeight
,
1059 elemWidth: elemWidth
,
1060 elemHeight: elemHeight
,
1061 collisionPosition: collisionPosition
,
1062 collisionWidth: collisionWidth
,
1063 collisionHeight: collisionHeight
,
1064 offset: [ atOffset
[ 0 ] + myOffset
[ 0 ], atOffset
[ 1 ] + myOffset
[ 1 ] ],
1073 if ( options
.using
) {
1074 // adds feedback as second argument to using callback, if present
1075 using = function( props
) {
1076 var left
= targetOffset
.left
- position
.left
,
1077 right
= left
+ targetWidth
- elemWidth
,
1078 top
= targetOffset
.top
- position
.top
,
1079 bottom
= top
+ targetHeight
- elemHeight
,
1083 left: targetOffset
.left
,
1084 top: targetOffset
.top
,
1086 height: targetHeight
1090 left: position
.left
,
1095 horizontal: right
< 0 ? "left" : left
> 0 ? "right" : "center",
1096 vertical: bottom
< 0 ? "top" : top
> 0 ? "bottom" : "middle"
1098 if ( targetWidth
< elemWidth
&& abs( left
+ right
) < targetWidth
) {
1099 feedback
.horizontal
= "center";
1101 if ( targetHeight
< elemHeight
&& abs( top
+ bottom
) < targetHeight
) {
1102 feedback
.vertical
= "middle";
1104 if ( max( abs( left
), abs( right
) ) > max( abs( top
), abs( bottom
) ) ) {
1105 feedback
.important
= "horizontal";
1107 feedback
.important
= "vertical";
1109 options
.using
.call( this, props
, feedback
);
1113 elem
.offset( $.extend( position
, { using: using
} ) );
1119 left: function( position
, data
) {
1120 var within
= data
.within
,
1121 withinOffset
= within
.isWindow
? within
.scrollLeft : within
.offset
.left
,
1122 outerWidth
= within
.width
,
1123 collisionPosLeft
= position
.left
- data
.collisionPosition
.marginLeft
,
1124 overLeft
= withinOffset
- collisionPosLeft
,
1125 overRight
= collisionPosLeft
+ data
.collisionWidth
- outerWidth
- withinOffset
,
1128 // element is wider than within
1129 if ( data
.collisionWidth
> outerWidth
) {
1130 // element is initially over the left side of within
1131 if ( overLeft
> 0 && overRight
<= 0 ) {
1132 newOverRight
= position
.left
+ overLeft
+ data
.collisionWidth
- outerWidth
- withinOffset
;
1133 position
.left
+= overLeft
- newOverRight
;
1134 // element is initially over right side of within
1135 } else if ( overRight
> 0 && overLeft
<= 0 ) {
1136 position
.left
= withinOffset
;
1137 // element is initially over both left and right sides of within
1139 if ( overLeft
> overRight
) {
1140 position
.left
= withinOffset
+ outerWidth
- data
.collisionWidth
;
1142 position
.left
= withinOffset
;
1145 // too far left -> align with left edge
1146 } else if ( overLeft
> 0 ) {
1147 position
.left
+= overLeft
;
1148 // too far right -> align with right edge
1149 } else if ( overRight
> 0 ) {
1150 position
.left
-= overRight
;
1151 // adjust based on position and margin
1153 position
.left
= max( position
.left
- collisionPosLeft
, position
.left
);
1156 top: function( position
, data
) {
1157 var within
= data
.within
,
1158 withinOffset
= within
.isWindow
? within
.scrollTop : within
.offset
.top
,
1159 outerHeight
= data
.within
.height
,
1160 collisionPosTop
= position
.top
- data
.collisionPosition
.marginTop
,
1161 overTop
= withinOffset
- collisionPosTop
,
1162 overBottom
= collisionPosTop
+ data
.collisionHeight
- outerHeight
- withinOffset
,
1165 // element is taller than within
1166 if ( data
.collisionHeight
> outerHeight
) {
1167 // element is initially over the top of within
1168 if ( overTop
> 0 && overBottom
<= 0 ) {
1169 newOverBottom
= position
.top
+ overTop
+ data
.collisionHeight
- outerHeight
- withinOffset
;
1170 position
.top
+= overTop
- newOverBottom
;
1171 // element is initially over bottom of within
1172 } else if ( overBottom
> 0 && overTop
<= 0 ) {
1173 position
.top
= withinOffset
;
1174 // element is initially over both top and bottom of within
1176 if ( overTop
> overBottom
) {
1177 position
.top
= withinOffset
+ outerHeight
- data
.collisionHeight
;
1179 position
.top
= withinOffset
;
1182 // too far up -> align with top
1183 } else if ( overTop
> 0 ) {
1184 position
.top
+= overTop
;
1185 // too far down -> align with bottom edge
1186 } else if ( overBottom
> 0 ) {
1187 position
.top
-= overBottom
;
1188 // adjust based on position and margin
1190 position
.top
= max( position
.top
- collisionPosTop
, position
.top
);
1195 left: function( position
, data
) {
1196 var within
= data
.within
,
1197 withinOffset
= within
.offset
.left
+ within
.scrollLeft
,
1198 outerWidth
= within
.width
,
1199 offsetLeft
= within
.isWindow
? within
.scrollLeft : within
.offset
.left
,
1200 collisionPosLeft
= position
.left
- data
.collisionPosition
.marginLeft
,
1201 overLeft
= collisionPosLeft
- offsetLeft
,
1202 overRight
= collisionPosLeft
+ data
.collisionWidth
- outerWidth
- offsetLeft
,
1203 myOffset
= data
.my
[ 0 ] === "left" ?
1205 data
.my
[ 0 ] === "right" ?
1208 atOffset
= data
.at
[ 0 ] === "left" ?
1210 data
.at
[ 0 ] === "right" ?
1213 offset
= -2 * data
.offset
[ 0 ],
1217 if ( overLeft
< 0 ) {
1218 newOverRight
= position
.left
+ myOffset
+ atOffset
+ offset
+ data
.collisionWidth
- outerWidth
- withinOffset
;
1219 if ( newOverRight
< 0 || newOverRight
< abs( overLeft
) ) {
1220 position
.left
+= myOffset
+ atOffset
+ offset
;
1223 else if ( overRight
> 0 ) {
1224 newOverLeft
= position
.left
- data
.collisionPosition
.marginLeft
+ myOffset
+ atOffset
+ offset
- offsetLeft
;
1225 if ( newOverLeft
> 0 || abs( newOverLeft
) < overRight
) {
1226 position
.left
+= myOffset
+ atOffset
+ offset
;
1230 top: function( position
, data
) {
1231 var within
= data
.within
,
1232 withinOffset
= within
.offset
.top
+ within
.scrollTop
,
1233 outerHeight
= within
.height
,
1234 offsetTop
= within
.isWindow
? within
.scrollTop : within
.offset
.top
,
1235 collisionPosTop
= position
.top
- data
.collisionPosition
.marginTop
,
1236 overTop
= collisionPosTop
- offsetTop
,
1237 overBottom
= collisionPosTop
+ data
.collisionHeight
- outerHeight
- offsetTop
,
1238 top
= data
.my
[ 1 ] === "top",
1241 data
.my
[ 1 ] === "bottom" ?
1244 atOffset
= data
.at
[ 1 ] === "top" ?
1246 data
.at
[ 1 ] === "bottom" ?
1247 -data
.targetHeight :
1249 offset
= -2 * data
.offset
[ 1 ],
1252 if ( overTop
< 0 ) {
1253 newOverBottom
= position
.top
+ myOffset
+ atOffset
+ offset
+ data
.collisionHeight
- outerHeight
- withinOffset
;
1254 if ( ( position
.top
+ myOffset
+ atOffset
+ offset
) > overTop
&& ( newOverBottom
< 0 || newOverBottom
< abs( overTop
) ) ) {
1255 position
.top
+= myOffset
+ atOffset
+ offset
;
1258 else if ( overBottom
> 0 ) {
1259 newOverTop
= position
.top
- data
.collisionPosition
.marginTop
+ myOffset
+ atOffset
+ offset
- offsetTop
;
1260 if ( ( position
.top
+ myOffset
+ atOffset
+ offset
) > overBottom
&& ( newOverTop
> 0 || abs( newOverTop
) < overBottom
) ) {
1261 position
.top
+= myOffset
+ atOffset
+ offset
;
1268 $.ui
.position
.flip
.left
.apply( this, arguments
);
1269 $.ui
.position
.fit
.left
.apply( this, arguments
);
1272 $.ui
.position
.flip
.top
.apply( this, arguments
);
1273 $.ui
.position
.fit
.top
.apply( this, arguments
);
1278 // fraction support test
1280 var testElement
, testElementParent
, testElementStyle
, offsetLeft
, i
,
1281 body
= document
.getElementsByTagName( "body" )[ 0 ],
1282 div
= document
.createElement( "div" );
1284 //Create a "fake body" for testing based on method used in jQuery.support
1285 testElement
= document
.createElement( body
? "div" : "body" );
1286 testElementStyle
= {
1287 visibility: "hidden",
1295 $.extend( testElementStyle
, {
1296 position: "absolute",
1301 for ( i
in testElementStyle
) {
1302 testElement
.style
[ i
] = testElementStyle
[ i
];
1304 testElement
.appendChild( div
);
1305 testElementParent
= body
|| document
.documentElement
;
1306 testElementParent
.insertBefore( testElement
, testElementParent
.firstChild
);
1308 div
.style
.cssText
= "position: absolute; left: 10.7432222px;";
1310 offsetLeft
= $( div
).offset().left
;
1311 $.support
.offsetFractions
= offsetLeft
> 10 && offsetLeft
< 11;
1313 testElement
.innerHTML
= "";
1314 testElementParent
.removeChild( testElement
);
1318 (function( $, undefined ) {
1320 $.widget( "ui.autocomplete", {
1322 defaultElement: "<input>",
1348 _create: function() {
1349 // Some browsers only repeat keydown events, not keypress events,
1350 // so we use the suppressKeyPress flag to determine if we've already
1351 // handled the keydown event. #7269
1352 // Unfortunately the code for & in keypress is the same as the up arrow,
1353 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
1354 // events when we know the keydown event was used to modify the
1355 // search term. #7799
1356 var suppressKeyPress
, suppressKeyPressRepeat
, suppressInput
,
1357 nodeName
= this.element
[0].nodeName
.toLowerCase(),
1358 isTextarea
= nodeName
=== "textarea",
1359 isInput
= nodeName
=== "input";
1362 // Textareas are always multi-line
1364 // Inputs are always single-line, even if inside a contentEditable element
1365 // IE also treats inputs as contentEditable
1367 // All other element types are determined by whether or not they're contentEditable
1368 this.element
.prop( "isContentEditable" );
1370 this.valueMethod
= this.element
[ isTextarea
|| isInput
? "val" : "text" ];
1371 this.isNewMenu
= true;
1374 .addClass( "ui-autocomplete-input" )
1375 .attr( "autocomplete", "off" );
1377 this._on( this.element
, {
1378 keydown: function( event
) {
1379 if ( this.element
.prop( "readOnly" ) ) {
1380 suppressKeyPress
= true;
1381 suppressInput
= true;
1382 suppressKeyPressRepeat
= true;
1386 suppressKeyPress
= false;
1387 suppressInput
= false;
1388 suppressKeyPressRepeat
= false;
1389 var keyCode
= $.ui
.keyCode
;
1390 switch( event
.keyCode
) {
1391 case keyCode
.PAGE_UP:
1392 suppressKeyPress
= true;
1393 this._move( "previousPage", event
);
1395 case keyCode
.PAGE_DOWN:
1396 suppressKeyPress
= true;
1397 this._move( "nextPage", event
);
1400 suppressKeyPress
= true;
1401 this._keyEvent( "previous", event
);
1404 suppressKeyPress
= true;
1405 this._keyEvent( "next", event
);
1408 case keyCode
.NUMPAD_ENTER:
1409 // when menu is open and has focus
1410 if ( this.menu
.active
) {
1411 // #6055 - Opera still allows the keypress to occur
1412 // which causes forms to submit
1413 suppressKeyPress
= true;
1414 event
.preventDefault();
1415 this.menu
.select( event
);
1419 if ( this.menu
.active
) {
1420 this.menu
.select( event
);
1423 case keyCode
.ESCAPE:
1424 if ( this.menu
.element
.is( ":visible" ) ) {
1425 this._value( this.term
);
1426 this.close( event
);
1427 // Different browsers have different default behavior for escape
1428 // Single press can mean undo or clear
1429 // Double press in IE means clear the whole form
1430 event
.preventDefault();
1434 suppressKeyPressRepeat
= true;
1435 // search timeout should be triggered before the input value is changed
1436 this._searchTimeout( event
);
1440 keypress: function( event
) {
1441 if ( suppressKeyPress
) {
1442 suppressKeyPress
= false;
1443 if ( !this.isMultiLine
|| this.menu
.element
.is( ":visible" ) ) {
1444 event
.preventDefault();
1448 if ( suppressKeyPressRepeat
) {
1452 // replicate some key handlers to allow them to repeat in Firefox and Opera
1453 var keyCode
= $.ui
.keyCode
;
1454 switch( event
.keyCode
) {
1455 case keyCode
.PAGE_UP:
1456 this._move( "previousPage", event
);
1458 case keyCode
.PAGE_DOWN:
1459 this._move( "nextPage", event
);
1462 this._keyEvent( "previous", event
);
1465 this._keyEvent( "next", event
);
1469 input: function( event
) {
1470 if ( suppressInput
) {
1471 suppressInput
= false;
1472 event
.preventDefault();
1475 this._searchTimeout( event
);
1478 this.selectedItem
= null;
1479 this.previous
= this._value();
1481 blur: function( event
) {
1482 if ( this.cancelBlur
) {
1483 delete this.cancelBlur
;
1487 clearTimeout( this.searching
);
1488 this.close( event
);
1489 this._change( event
);
1494 this.menu
= $( "<ul>" )
1495 .addClass( "ui-autocomplete ui-front" )
1496 .appendTo( this._appendTo() )
1498 // disable ARIA support, the live region takes care of that
1504 this._on( this.menu
.element
, {
1505 mousedown: function( event
) {
1506 // prevent moving focus out of the text field
1507 event
.preventDefault();
1509 // IE doesn't prevent moving focus even with event.preventDefault()
1510 // so we set a flag to know when we should ignore the blur event
1511 this.cancelBlur
= true;
1512 this._delay(function() {
1513 delete this.cancelBlur
;
1516 // clicking on the scrollbar causes focus to shift to the body
1517 // but we can't detect a mouseup or a click immediately afterward
1518 // so we have to track the next mousedown and close the menu if
1519 // the user clicks somewhere outside of the autocomplete
1520 var menuElement
= this.menu
.element
[ 0 ];
1521 if ( !$( event
.target
).closest( ".ui-menu-item" ).length
) {
1522 this._delay(function() {
1524 this.document
.one( "mousedown", function( event
) {
1525 if ( event
.target
!== that
.element
[ 0 ] &&
1526 event
.target
!== menuElement
&&
1527 !$.contains( menuElement
, event
.target
) ) {
1534 menufocus: function( event
, ui
) {
1536 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
1537 if ( this.isNewMenu
) {
1538 this.isNewMenu
= false;
1539 if ( event
.originalEvent
&& /^mouse/.test( event
.originalEvent
.type
) ) {
1542 this.document
.one( "mousemove", function() {
1543 $( event
.target
).trigger( event
.originalEvent
);
1550 var item
= ui
.item
.data( "ui-autocomplete-item" );
1551 if ( false !== this._trigger( "focus", event
, { item: item
} ) ) {
1552 // use value to match what will end up in the input, if it was a key event
1553 if ( event
.originalEvent
&& /^key/.test( event
.originalEvent
.type
) ) {
1554 this._value( item
.value
);
1557 // Normally the input is populated with the item's value as the
1558 // menu is navigated, causing screen readers to notice a change and
1559 // announce the item. Since the focus event was canceled, this doesn't
1560 // happen, so we update the live region so that screen readers can
1561 // still notice the change and announce it.
1562 this.liveRegion
.text( item
.value
);
1565 menuselect: function( event
, ui
) {
1566 var item
= ui
.item
.data( "ui-autocomplete-item" ),
1567 previous
= this.previous
;
1569 // only trigger when focus was lost (click on menu)
1570 if ( this.element
[0] !== this.document
[0].activeElement
) {
1571 this.element
.focus();
1572 this.previous
= previous
;
1573 // #6109 - IE triggers two focus events and the second
1574 // is asynchronous, so we need to reset the previous
1575 // term synchronously and asynchronously :-(
1576 this._delay(function() {
1577 this.previous
= previous
;
1578 this.selectedItem
= item
;
1582 if ( false !== this._trigger( "select", event
, { item: item
} ) ) {
1583 this._value( item
.value
);
1585 // reset the term after the select event
1586 // this allows custom select handling to work properly
1587 this.term
= this._value();
1589 this.close( event
);
1590 this.selectedItem
= item
;
1594 this.liveRegion
= $( "<span>", {
1596 "aria-live": "polite"
1598 .addClass( "ui-helper-hidden-accessible" )
1599 .insertBefore( this.element
);
1601 // turning off autocomplete prevents the browser from remembering the
1602 // value when navigating through history, so we re-enable autocomplete
1603 // if the page is unloaded before the widget is destroyed. #7790
1604 this._on( this.window
, {
1605 beforeunload: function() {
1606 this.element
.removeAttr( "autocomplete" );
1611 _destroy: function() {
1612 clearTimeout( this.searching
);
1614 .removeClass( "ui-autocomplete-input" )
1615 .removeAttr( "autocomplete" );
1616 this.menu
.element
.remove();
1617 this.liveRegion
.remove();
1620 _setOption: function( key
, value
) {
1621 this._super( key
, value
);
1622 if ( key
=== "source" ) {
1625 if ( key
=== "appendTo" ) {
1626 this.menu
.element
.appendTo( this._appendTo() );
1628 if ( key
=== "disabled" && value
&& this.xhr
) {
1633 _appendTo: function() {
1634 var element
= this.options
.appendTo
;
1637 element
= element
.jquery
|| element
.nodeType
?
1639 this.document
.find( element
).eq( 0 );
1643 element
= this.element
.closest( ".ui-front" );
1646 if ( !element
.length
) {
1647 element
= this.document
[0].body
;
1653 _initSource: function() {
1656 if ( $.isArray(this.options
.source
) ) {
1657 array
= this.options
.source
;
1658 this.source = function( request
, response
) {
1659 response( $.ui
.autocomplete
.filter( array
, request
.term
) );
1661 } else if ( typeof this.options
.source
=== "string" ) {
1662 url
= this.options
.source
;
1663 this.source = function( request
, response
) {
1671 success: function( data
) {
1680 this.source
= this.options
.source
;
1684 _searchTimeout: function( event
) {
1685 clearTimeout( this.searching
);
1686 this.searching
= this._delay(function() {
1687 // only search if the value has changed
1688 if ( this.term
!== this._value() ) {
1689 this.selectedItem
= null;
1690 this.search( null, event
);
1692 }, this.options
.delay
);
1695 search: function( value
, event
) {
1696 value
= value
!= null ? value : this._value();
1698 // always save the actual value, not the one passed as an argument
1699 this.term
= this._value();
1701 if ( value
.length
< this.options
.minLength
) {
1702 return this.close( event
);
1705 if ( this._trigger( "search", event
) === false ) {
1709 return this._search( value
);
1712 _search: function( value
) {
1714 this.element
.addClass( "ui-autocomplete-loading" );
1715 this.cancelSearch
= false;
1717 this.source( { term: value
}, this._response() );
1720 _response: function() {
1721 var index
= ++this.requestIndex
;
1723 return $.proxy(function( content
) {
1724 if ( index
=== this.requestIndex
) {
1725 this.__response( content
);
1729 if ( !this.pending
) {
1730 this.element
.removeClass( "ui-autocomplete-loading" );
1735 __response: function( content
) {
1737 content
= this._normalize( content
);
1739 this._trigger( "response", null, { content: content
} );
1740 if ( !this.options
.disabled
&& content
&& content
.length
&& !this.cancelSearch
) {
1741 this._suggest( content
);
1742 this._trigger( "open" );
1744 // use ._close() instead of .close() so we don't cancel future searches
1749 close: function( event
) {
1750 this.cancelSearch
= true;
1751 this._close( event
);
1754 _close: function( event
) {
1755 if ( this.menu
.element
.is( ":visible" ) ) {
1756 this.menu
.element
.hide();
1758 this.isNewMenu
= true;
1759 this._trigger( "close", event
);
1763 _change: function( event
) {
1764 if ( this.previous
!== this._value() ) {
1765 this._trigger( "change", event
, { item: this.selectedItem
} );
1769 _normalize: function( items
) {
1770 // assume all items have the right format when the first item is complete
1771 if ( items
.length
&& items
[0].label
&& items
[0].value
) {
1774 return $.map( items
, function( item
) {
1775 if ( typeof item
=== "string" ) {
1782 label: item
.label
|| item
.value
,
1783 value: item
.value
|| item
.label
1788 _suggest: function( items
) {
1789 var ul
= this.menu
.element
.empty();
1790 this._renderMenu( ul
, items
);
1791 this.isNewMenu
= true;
1792 this.menu
.refresh();
1794 // size and position menu
1797 ul
.position( $.extend({
1799 }, this.options
.position
));
1801 if ( this.options
.autoFocus
) {
1806 _resizeMenu: function() {
1807 var ul
= this.menu
.element
;
1808 ul
.outerWidth( Math
.max(
1809 // Firefox wraps long text (possibly a rounding bug)
1810 // so we add 1px to avoid the wrapping (#7513)
1811 ul
.width( "" ).outerWidth() + 1,
1812 this.element
.outerWidth()
1816 _renderMenu: function( ul
, items
) {
1818 $.each( items
, function( index
, item
) {
1819 that
._renderItemData( ul
, item
);
1823 _renderItemData: function( ul
, item
) {
1824 return this._renderItem( ul
, item
).data( "ui-autocomplete-item", item
);
1827 _renderItem: function( ul
, item
) {
1829 .append( $( "<a>" ).text( item
.label
) )
1833 _move: function( direction
, event
) {
1834 if ( !this.menu
.element
.is( ":visible" ) ) {
1835 this.search( null, event
);
1838 if ( this.menu
.isFirstItem() && /^previous/.test( direction
) ||
1839 this.menu
.isLastItem() && /^next/.test( direction
) ) {
1840 this._value( this.term
);
1844 this.menu
[ direction
]( event
);
1847 widget: function() {
1848 return this.menu
.element
;
1851 _value: function() {
1852 return this.valueMethod
.apply( this.element
, arguments
);
1855 _keyEvent: function( keyEvent
, event
) {
1856 if ( !this.isMultiLine
|| this.menu
.element
.is( ":visible" ) ) {
1857 this._move( keyEvent
, event
);
1859 // prevents moving cursor to beginning/end of the text field in some browsers
1860 event
.preventDefault();
1865 $.extend( $.ui
.autocomplete
, {
1866 escapeRegex: function( value
) {
1867 return value
.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
1869 filter: function(array
, term
) {
1870 var matcher
= new RegExp( $.ui
.autocomplete
.escapeRegex(term
), "i" );
1871 return $.grep( array
, function(value
) {
1872 return matcher
.test( value
.label
|| value
.value
|| value
);
1878 // live region extension, adding a `messages` option
1879 // NOTE: This is an experimental API. We are still investigating
1880 // a full solution for string manipulation and internationalization.
1881 $.widget( "ui.autocomplete", $.ui
.autocomplete
, {
1884 noResults: "No search results.",
1885 results: function( amount
) {
1886 return amount
+ ( amount
> 1 ? " results are" : " result is" ) +
1887 " available, use up and down arrow keys to navigate.";
1892 __response: function( content
) {
1894 this._superApply( arguments
);
1895 if ( this.options
.disabled
|| this.cancelSearch
) {
1898 if ( content
&& content
.length
) {
1899 message
= this.options
.messages
.results( content
.length
);
1901 message
= this.options
.messages
.noResults
;
1903 this.liveRegion
.text( message
);
1908 (function( $, undefined ) {
1910 $.widget( "ui.menu", {
1912 defaultElement: "<ul>",
1916 submenu: "ui-icon-carat-1-e"
1931 _create: function() {
1932 this.activeMenu
= this.element
;
1933 // flag used to prevent firing of the click handler
1934 // as the event bubbles up through nested menus
1935 this.mouseHandled
= false;
1938 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
1939 .toggleClass( "ui-menu-icons", !!this.element
.find( ".ui-icon" ).length
)
1941 role: this.options
.role
,
1944 // need to catch all clicks on disabled menu
1945 // not possible through _on
1946 .bind( "click" + this.eventNamespace
, $.proxy(function( event
) {
1947 if ( this.options
.disabled
) {
1948 event
.preventDefault();
1952 if ( this.options
.disabled
) {
1954 .addClass( "ui-state-disabled" )
1955 .attr( "aria-disabled", "true" );
1959 // Prevent focus from sticking to links inside menu after clicking
1960 // them (focus should always stay on UL during navigation).
1961 "mousedown .ui-menu-item > a": function( event
) {
1962 event
.preventDefault();
1964 "click .ui-state-disabled > a": function( event
) {
1965 event
.preventDefault();
1967 "click .ui-menu-item:has(a)": function( event
) {
1968 var target
= $( event
.target
).closest( ".ui-menu-item" );
1969 if ( !this.mouseHandled
&& target
.not( ".ui-state-disabled" ).length
) {
1970 this.select( event
);
1972 // Only set the mouseHandled flag if the event will bubble, see #9469.
1973 if ( !event
.isPropagationStopped() ) {
1974 this.mouseHandled
= true;
1977 // Open submenu on click
1978 if ( target
.has( ".ui-menu" ).length
) {
1979 this.expand( event
);
1980 } else if ( !this.element
.is( ":focus" ) && $( this.document
[ 0 ].activeElement
).closest( ".ui-menu" ).length
) {
1982 // Redirect focus to the menu
1983 this.element
.trigger( "focus", [ true ] );
1985 // If the active item is on the top level, let it stay active.
1986 // Otherwise, blur the active item since it is no longer visible.
1987 if ( this.active
&& this.active
.parents( ".ui-menu" ).length
=== 1 ) {
1988 clearTimeout( this.timer
);
1993 "mouseenter .ui-menu-item": function( event
) {
1994 var target
= $( event
.currentTarget
);
1995 // Remove ui-state-active class from siblings of the newly focused menu item
1996 // to avoid a jump caused by adjacent elements both having a class with a border
1997 target
.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
1998 this.focus( event
, target
);
2000 mouseleave: "collapseAll",
2001 "mouseleave .ui-menu": "collapseAll",
2002 focus: function( event
, keepActiveItem
) {
2003 // If there's already an active item, keep it active
2004 // If not, activate the first item
2005 var item
= this.active
|| this.element
.children( ".ui-menu-item" ).eq( 0 );
2007 if ( !keepActiveItem
) {
2008 this.focus( event
, item
);
2011 blur: function( event
) {
2012 this._delay(function() {
2013 if ( !$.contains( this.element
[0], this.document
[0].activeElement
) ) {
2014 this.collapseAll( event
);
2023 // Clicks outside of a menu collapse any open menus
2024 this._on( this.document
, {
2025 click: function( event
) {
2026 if ( !$( event
.target
).closest( ".ui-menu" ).length
) {
2027 this.collapseAll( event
);
2030 // Reset the mouseHandled flag
2031 this.mouseHandled
= false;
2036 _destroy: function() {
2037 // Destroy (sub)menus
2039 .removeAttr( "aria-activedescendant" )
2040 .find( ".ui-menu" ).addBack()
2041 .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
2042 .removeAttr( "role" )
2043 .removeAttr( "tabIndex" )
2044 .removeAttr( "aria-labelledby" )
2045 .removeAttr( "aria-expanded" )
2046 .removeAttr( "aria-hidden" )
2047 .removeAttr( "aria-disabled" )
2051 // Destroy menu items
2052 this.element
.find( ".ui-menu-item" )
2053 .removeClass( "ui-menu-item" )
2054 .removeAttr( "role" )
2055 .removeAttr( "aria-disabled" )
2058 .removeClass( "ui-corner-all ui-state-hover" )
2059 .removeAttr( "tabIndex" )
2060 .removeAttr( "role" )
2061 .removeAttr( "aria-haspopup" )
2062 .children().each( function() {
2063 var elem
= $( this );
2064 if ( elem
.data( "ui-menu-submenu-carat" ) ) {
2069 // Destroy menu dividers
2070 this.element
.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
2073 _keydown: function( event
) {
2074 var match
, prev
, character
, skip
, regex
,
2075 preventDefault
= true;
2077 function escape( value
) {
2078 return value
.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
2081 switch ( event
.keyCode
) {
2082 case $.ui
.keyCode
.PAGE_UP:
2083 this.previousPage( event
);
2085 case $.ui
.keyCode
.PAGE_DOWN:
2086 this.nextPage( event
);
2088 case $.ui
.keyCode
.HOME:
2089 this._move( "first", "first", event
);
2091 case $.ui
.keyCode
.END:
2092 this._move( "last", "last", event
);
2094 case $.ui
.keyCode
.UP:
2095 this.previous( event
);
2097 case $.ui
.keyCode
.DOWN:
2100 case $.ui
.keyCode
.LEFT:
2101 this.collapse( event
);
2103 case $.ui
.keyCode
.RIGHT:
2104 if ( this.active
&& !this.active
.is( ".ui-state-disabled" ) ) {
2105 this.expand( event
);
2108 case $.ui
.keyCode
.ENTER:
2109 case $.ui
.keyCode
.SPACE:
2110 this._activate( event
);
2112 case $.ui
.keyCode
.ESCAPE:
2113 this.collapse( event
);
2116 preventDefault
= false;
2117 prev
= this.previousFilter
|| "";
2118 character
= String
.fromCharCode( event
.keyCode
);
2121 clearTimeout( this.filterTimer
);
2123 if ( character
=== prev
) {
2126 character
= prev
+ character
;
2129 regex
= new RegExp( "^" + escape( character
), "i" );
2130 match
= this.activeMenu
.children( ".ui-menu-item" ).filter(function() {
2131 return regex
.test( $( this ).children( "a" ).text() );
2133 match
= skip
&& match
.index( this.active
.next() ) !== -1 ?
2134 this.active
.nextAll( ".ui-menu-item" ) :
2137 // If no matches on the current filter, reset to the last character pressed
2138 // to move down the menu to the first item that starts with that character
2139 if ( !match
.length
) {
2140 character
= String
.fromCharCode( event
.keyCode
);
2141 regex
= new RegExp( "^" + escape( character
), "i" );
2142 match
= this.activeMenu
.children( ".ui-menu-item" ).filter(function() {
2143 return regex
.test( $( this ).children( "a" ).text() );
2147 if ( match
.length
) {
2148 this.focus( event
, match
);
2149 if ( match
.length
> 1 ) {
2150 this.previousFilter
= character
;
2151 this.filterTimer
= this._delay(function() {
2152 delete this.previousFilter
;
2155 delete this.previousFilter
;
2158 delete this.previousFilter
;
2162 if ( preventDefault
) {
2163 event
.preventDefault();
2167 _activate: function( event
) {
2168 if ( !this.active
.is( ".ui-state-disabled" ) ) {
2169 if ( this.active
.children( "a[aria-haspopup='true']" ).length
) {
2170 this.expand( event
);
2172 this.select( event
);
2177 refresh: function() {
2179 icon
= this.options
.icons
.submenu
,
2180 submenus
= this.element
.find( this.options
.menus
);
2182 this.element
.toggleClass( "ui-menu-icons", !!this.element
.find( ".ui-icon" ).length
);
2184 // Initialize nested menus
2185 submenus
.filter( ":not(.ui-menu)" )
2186 .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
2189 role: this.options
.role
,
2190 "aria-hidden": "true",
2191 "aria-expanded": "false"
2194 var menu
= $( this ),
2195 item
= menu
.prev( "a" ),
2196 submenuCarat
= $( "<span>" )
2197 .addClass( "ui-menu-icon ui-icon " + icon
)
2198 .data( "ui-menu-submenu-carat", true );
2201 .attr( "aria-haspopup", "true" )
2202 .prepend( submenuCarat
);
2203 menu
.attr( "aria-labelledby", item
.attr( "id" ) );
2206 menus
= submenus
.add( this.element
);
2208 // Don't refresh list items that are already adapted
2209 menus
.children( ":not(.ui-menu-item):has(a)" )
2210 .addClass( "ui-menu-item" )
2211 .attr( "role", "presentation" )
2214 .addClass( "ui-corner-all" )
2217 role: this._itemRole()
2220 // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
2221 menus
.children( ":not(.ui-menu-item)" ).each(function() {
2222 var item
= $( this );
2223 // hyphen, em dash, en dash
2224 if ( !/[^\-\u2014\u2013\s]/.test( item
.text() ) ) {
2225 item
.addClass( "ui-widget-content ui-menu-divider" );
2229 // Add aria-disabled attribute to any disabled menu item
2230 menus
.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
2232 // If the active item has been removed, blur the menu
2233 if ( this.active
&& !$.contains( this.element
[ 0 ], this.active
[ 0 ] ) ) {
2238 _itemRole: function() {
2242 }[ this.options
.role
];
2245 _setOption: function( key
, value
) {
2246 if ( key
=== "icons" ) {
2247 this.element
.find( ".ui-menu-icon" )
2248 .removeClass( this.options
.icons
.submenu
)
2249 .addClass( value
.submenu
);
2251 this._super( key
, value
);
2254 focus: function( event
, item
) {
2255 var nested
, focused
;
2256 this.blur( event
, event
&& event
.type
=== "focus" );
2258 this._scrollIntoView( item
);
2260 this.active
= item
.first();
2261 focused
= this.active
.children( "a" ).addClass( "ui-state-focus" );
2262 // Only update aria-activedescendant if there's a role
2263 // otherwise we assume focus is managed elsewhere
2264 if ( this.options
.role
) {
2265 this.element
.attr( "aria-activedescendant", focused
.attr( "id" ) );
2268 // Highlight active parent menu item, if any
2271 .closest( ".ui-menu-item" )
2272 .children( "a:first" )
2273 .addClass( "ui-state-active" );
2275 if ( event
&& event
.type
=== "keydown" ) {
2278 this.timer
= this._delay(function() {
2283 nested
= item
.children( ".ui-menu" );
2284 if ( nested
.length
&& event
&& ( /^mouse/.test( event
.type
) ) ) {
2285 this._startOpening(nested
);
2287 this.activeMenu
= item
.parent();
2289 this._trigger( "focus", event
, { item: item
} );
2292 _scrollIntoView: function( item
) {
2293 var borderTop
, paddingTop
, offset
, scroll
, elementHeight
, itemHeight
;
2294 if ( this._hasScroll() ) {
2295 borderTop
= parseFloat( $.css( this.activeMenu
[0], "borderTopWidth" ) ) || 0;
2296 paddingTop
= parseFloat( $.css( this.activeMenu
[0], "paddingTop" ) ) || 0;
2297 offset
= item
.offset().top
- this.activeMenu
.offset().top
- borderTop
- paddingTop
;
2298 scroll
= this.activeMenu
.scrollTop();
2299 elementHeight
= this.activeMenu
.height();
2300 itemHeight
= item
.height();
2303 this.activeMenu
.scrollTop( scroll
+ offset
);
2304 } else if ( offset
+ itemHeight
> elementHeight
) {
2305 this.activeMenu
.scrollTop( scroll
+ offset
- elementHeight
+ itemHeight
);
2310 blur: function( event
, fromFocus
) {
2312 clearTimeout( this.timer
);
2315 if ( !this.active
) {
2319 this.active
.children( "a" ).removeClass( "ui-state-focus" );
2322 this._trigger( "blur", event
, { item: this.active
} );
2325 _startOpening: function( submenu
) {
2326 clearTimeout( this.timer
);
2328 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
2329 // shift in the submenu position when mousing over the carat icon
2330 if ( submenu
.attr( "aria-hidden" ) !== "true" ) {
2334 this.timer
= this._delay(function() {
2336 this._open( submenu
);
2340 _open: function( submenu
) {
2341 var position
= $.extend({
2343 }, this.options
.position
);
2345 clearTimeout( this.timer
);
2346 this.element
.find( ".ui-menu" ).not( submenu
.parents( ".ui-menu" ) )
2348 .attr( "aria-hidden", "true" );
2352 .removeAttr( "aria-hidden" )
2353 .attr( "aria-expanded", "true" )
2354 .position( position
);
2357 collapseAll: function( event
, all
) {
2358 clearTimeout( this.timer
);
2359 this.timer
= this._delay(function() {
2360 // If we were passed an event, look for the submenu that contains the event
2361 var currentMenu
= all
? this.element :
2362 $( event
&& event
.target
).closest( this.element
.find( ".ui-menu" ) );
2364 // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
2365 if ( !currentMenu
.length
) {
2366 currentMenu
= this.element
;
2369 this._close( currentMenu
);
2372 this.activeMenu
= currentMenu
;
2376 // With no arguments, closes the currently active menu - if nothing is active
2377 // it closes all menus. If passed an argument, it will search for menus BELOW
2378 _close: function( startMenu
) {
2380 startMenu
= this.active
? this.active
.parent() : this.element
;
2386 .attr( "aria-hidden", "true" )
2387 .attr( "aria-expanded", "false" )
2389 .find( "a.ui-state-active" )
2390 .removeClass( "ui-state-active" );
2393 collapse: function( event
) {
2394 var newItem
= this.active
&&
2395 this.active
.parent().closest( ".ui-menu-item", this.element
);
2396 if ( newItem
&& newItem
.length
) {
2398 this.focus( event
, newItem
);
2402 expand: function( event
) {
2403 var newItem
= this.active
&&
2405 .children( ".ui-menu " )
2406 .children( ".ui-menu-item" )
2409 if ( newItem
&& newItem
.length
) {
2410 this._open( newItem
.parent() );
2412 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
2413 this._delay(function() {
2414 this.focus( event
, newItem
);
2419 next: function( event
) {
2420 this._move( "next", "first", event
);
2423 previous: function( event
) {
2424 this._move( "prev", "last", event
);
2427 isFirstItem: function() {
2428 return this.active
&& !this.active
.prevAll( ".ui-menu-item" ).length
;
2431 isLastItem: function() {
2432 return this.active
&& !this.active
.nextAll( ".ui-menu-item" ).length
;
2435 _move: function( direction
, filter
, event
) {
2437 if ( this.active
) {
2438 if ( direction
=== "first" || direction
=== "last" ) {
2440 [ direction
=== "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
2444 [ direction
+ "All" ]( ".ui-menu-item" )
2448 if ( !next
|| !next
.length
|| !this.active
) {
2449 next
= this.activeMenu
.children( ".ui-menu-item" )[ filter
]();
2452 this.focus( event
, next
);
2455 nextPage: function( event
) {
2456 var item
, base
, height
;
2458 if ( !this.active
) {
2462 if ( this.isLastItem() ) {
2465 if ( this._hasScroll() ) {
2466 base
= this.active
.offset().top
;
2467 height
= this.element
.height();
2468 this.active
.nextAll( ".ui-menu-item" ).each(function() {
2470 return item
.offset().top
- base
- height
< 0;
2473 this.focus( event
, item
);
2475 this.focus( event
, this.activeMenu
.children( ".ui-menu-item" )
2476 [ !this.active
? "first" : "last" ]() );
2480 previousPage: function( event
) {
2481 var item
, base
, height
;
2482 if ( !this.active
) {
2486 if ( this.isFirstItem() ) {
2489 if ( this._hasScroll() ) {
2490 base
= this.active
.offset().top
;
2491 height
= this.element
.height();
2492 this.active
.prevAll( ".ui-menu-item" ).each(function() {
2494 return item
.offset().top
- base
+ height
> 0;
2497 this.focus( event
, item
);
2499 this.focus( event
, this.activeMenu
.children( ".ui-menu-item" ).first() );
2503 _hasScroll: function() {
2504 return this.element
.outerHeight() < this.element
.prop( "scrollHeight" );
2507 select: function( event
) {
2508 // TODO: It should never be possible to not have an active item at this
2509 // point, but the tests don't trigger mouseenter before click.
2510 this.active
= this.active
|| $( event
.target
).closest( ".ui-menu-item" );
2511 var ui
= { item: this.active
};
2512 if ( !this.active
.has( ".ui-menu" ).length
) {
2513 this.collapseAll( event
, true );
2515 this._trigger( "select", event
, ui
);