· 6 years ago · Sep 10, 2019, 07:06 PM
1/*! jQuery UI - v1.12.1 - 2017-06-05
2* http://jqueryui.com
3* Includes: widget.js, position.js, disable-selection.js, keycode.js, unique-id.js, widgets/autocomplete.js, widgets/menu.js
4* Copyright jQuery Foundation and other contributors; Licensed MIT */
5
6(function( factory ) {
7 if ( typeof define === "function" && define.amd ) {
8
9 // AMD. Register as an anonymous module.
10 define([ "jquery" ], factory );
11 } else {
12
13 // Browser globals
14 factory( jQuery );
15 }
16}(function( $ ) {
17
18$.ui = $.ui || {};
19
20var version = $.ui.version = "1.12.1";
21
22
23/*!
24 * jQuery UI Widget 1.12.1
25 * http://jqueryui.com
26 *
27 * Copyright jQuery Foundation and other contributors
28 * Released under the MIT license.
29 * http://jquery.org/license
30 */
31
32//>>label: Widget
33//>>group: Core
34//>>description: Provides a factory for creating stateful widgets with a common API.
35//>>docs: http://api.jqueryui.com/jQuery.widget/
36//>>demos: http://jqueryui.com/widget/
37
38
39
40var widgetUuid = 0;
41var widgetSlice = Array.prototype.slice;
42
43$.cleanData = ( function( orig ) {
44 return function( elems ) {
45 var events, elem, i;
46 for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
47 try {
48
49 // Only trigger remove when necessary to save time
50 events = $._data( elem, "events" );
51 if ( events && events.remove ) {
52 $( elem ).triggerHandler( "remove" );
53 }
54
55 // Http://bugs.jquery.com/ticket/8235
56 } catch ( e ) {}
57 }
58 orig( elems );
59 };
60} )( $.cleanData );
61
62$.widget = function( name, base, prototype ) {
63 var existingConstructor, constructor, basePrototype;
64
65 // ProxiedPrototype allows the provided prototype to remain unmodified
66 // so that it can be used as a mixin for multiple widgets (#8876)
67 var proxiedPrototype = {};
68
69 var namespace = name.split( "." )[ 0 ];
70 name = name.split( "." )[ 1 ];
71 var fullName = namespace + "-" + name;
72
73 if ( !prototype ) {
74 prototype = base;
75 base = $.Widget;
76 }
77
78 if ( $.isArray( prototype ) ) {
79 prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
80 }
81
82 // Create selector for plugin
83 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
84 return !!$.data( elem, fullName );
85 };
86
87 $[ namespace ] = $[ namespace ] || {};
88 existingConstructor = $[ namespace ][ name ];
89 constructor = $[ namespace ][ name ] = function( options, element ) {
90
91 // Allow instantiation without "new" keyword
92 if ( !this._createWidget ) {
93 return new constructor( options, element );
94 }
95
96 // Allow instantiation without initializing for simple inheritance
97 // must use "new" keyword (the code above always passes args)
98 if ( arguments.length ) {
99 this._createWidget( options, element );
100 }
101 };
102
103 // Extend with the existing constructor to carry over any static properties
104 $.extend( constructor, existingConstructor, {
105 version: prototype.version,
106
107 // Copy the object used to create the prototype in case we need to
108 // redefine the widget later
109 _proto: $.extend( {}, prototype ),
110
111 // Track widgets that inherit from this widget in case this widget is
112 // redefined after a widget inherits from it
113 _childConstructors: []
114 } );
115
116 basePrototype = new base();
117
118 // We need to make the options hash a property directly on the new instance
119 // otherwise we'll modify the options hash on the prototype that we're
120 // inheriting from
121 basePrototype.options = $.widget.extend( {}, basePrototype.options );
122 $.each( prototype, function( prop, value ) {
123 if ( !$.isFunction( value ) ) {
124 proxiedPrototype[ prop ] = value;
125 return;
126 }
127 proxiedPrototype[ prop ] = ( function() {
128 function _super() {
129 return base.prototype[ prop ].apply( this, arguments );
130 }
131
132 function _superApply( args ) {
133 return base.prototype[ prop ].apply( this, args );
134 }
135
136 return function() {
137 var __super = this._super;
138 var __superApply = this._superApply;
139 var returnValue;
140
141 this._super = _super;
142 this._superApply = _superApply;
143
144 returnValue = value.apply( this, arguments );
145
146 this._super = __super;
147 this._superApply = __superApply;
148
149 return returnValue;
150 };
151 } )();
152 } );
153 constructor.prototype = $.widget.extend( basePrototype, {
154
155 // TODO: remove support for widgetEventPrefix
156 // always use the name + a colon as the prefix, e.g., draggable:start
157 // don't prefix for widgets that aren't DOM-based
158 widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
159 }, proxiedPrototype, {
160 constructor: constructor,
161 namespace: namespace,
162 widgetName: name,
163 widgetFullName: fullName
164 } );
165
166 // If this widget is being redefined then we need to find all widgets that
167 // are inheriting from it and redefine all of them so that they inherit from
168 // the new version of this widget. We're essentially trying to replace one
169 // level in the prototype chain.
170 if ( existingConstructor ) {
171 $.each( existingConstructor._childConstructors, function( i, child ) {
172 var childPrototype = child.prototype;
173
174 // Redefine the child widget using the same prototype that was
175 // originally used, but inherit from the new version of the base
176 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
177 child._proto );
178 } );
179
180 // Remove the list of existing child constructors from the old constructor
181 // so the old child constructors can be garbage collected
182 delete existingConstructor._childConstructors;
183 } else {
184 base._childConstructors.push( constructor );
185 }
186
187 $.widget.bridge( name, constructor );
188
189 return constructor;
190};
191
192$.widget.extend = function( target ) {
193 var input = widgetSlice.call( arguments, 1 );
194 var inputIndex = 0;
195 var inputLength = input.length;
196 var key;
197 var value;
198
199 for ( ; inputIndex < inputLength; inputIndex++ ) {
200 for ( key in input[ inputIndex ] ) {
201 value = input[ inputIndex ][ key ];
202 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
203
204 // Clone objects
205 if ( $.isPlainObject( value ) ) {
206 target[ key ] = $.isPlainObject( target[ key ] ) ?
207 $.widget.extend( {}, target[ key ], value ) :
208
209 // Don't extend strings, arrays, etc. with objects
210 $.widget.extend( {}, value );
211
212 // Copy everything else by reference
213 } else {
214 target[ key ] = value;
215 }
216 }
217 }
218 }
219 return target;
220};
221
222$.widget.bridge = function( name, object ) {
223 var fullName = object.prototype.widgetFullName || name;
224 $.fn[ name ] = function( options ) {
225 var isMethodCall = typeof options === "string";
226 var args = widgetSlice.call( arguments, 1 );
227 var returnValue = this;
228
229 if ( isMethodCall ) {
230
231 // If this is an empty collection, we need to have the instance method
232 // return undefined instead of the jQuery instance
233 if ( !this.length && options === "instance" ) {
234 returnValue = undefined;
235 } else {
236 this.each( function() {
237 var methodValue;
238 var instance = $.data( this, fullName );
239
240 if ( options === "instance" ) {
241 returnValue = instance;
242 return false;
243 }
244
245 if ( !instance ) {
246 return $.error( "cannot call methods on " + name +
247 " prior to initialization; " +
248 "attempted to call method '" + options + "'" );
249 }
250
251 if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
252 return $.error( "no such method '" + options + "' for " + name +
253 " widget instance" );
254 }
255
256 methodValue = instance[ options ].apply( instance, args );
257
258 if ( methodValue !== instance && methodValue !== undefined ) {
259 returnValue = methodValue && methodValue.jquery ?
260 returnValue.pushStack( methodValue.get() ) :
261 methodValue;
262 return false;
263 }
264 } );
265 }
266 } else {
267
268 // Allow multiple hashes to be passed on init
269 if ( args.length ) {
270 options = $.widget.extend.apply( null, [ options ].concat( args ) );
271 }
272
273 this.each( function() {
274 var instance = $.data( this, fullName );
275 if ( instance ) {
276 instance.option( options || {} );
277 if ( instance._init ) {
278 instance._init();
279 }
280 } else {
281 $.data( this, fullName, new object( options, this ) );
282 }
283 } );
284 }
285
286 return returnValue;
287 };
288};
289
290$.Widget = function( /* options, element */ ) {};
291$.Widget._childConstructors = [];
292
293$.Widget.prototype = {
294 widgetName: "widget",
295 widgetEventPrefix: "",
296 defaultElement: "<div>",
297
298 options: {
299 classes: {},
300 disabled: false,
301
302 // Callbacks
303 create: null
304 },
305
306 _createWidget: function( options, element ) {
307 element = $( element || this.defaultElement || this )[ 0 ];
308 this.element = $( element );
309 this.uuid = widgetUuid++;
310 this.eventNamespace = "." + this.widgetName + this.uuid;
311
312 this.bindings = $();
313 this.hoverable = $();
314 this.focusable = $();
315 this.classesElementLookup = {};
316
317 if ( element !== this ) {
318 $.data( element, this.widgetFullName, this );
319 this._on( true, this.element, {
320 remove: function( event ) {
321 if ( event.target === element ) {
322 this.destroy();
323 }
324 }
325 } );
326 this.document = $( element.style ?
327
328 // Element within the document
329 element.ownerDocument :
330
331 // Element is window or document
332 element.document || element );
333 this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
334 }
335
336 this.options = $.widget.extend( {},
337 this.options,
338 this._getCreateOptions(),
339 options );
340
341 this._create();
342
343 if ( this.options.disabled ) {
344 this._setOptionDisabled( this.options.disabled );
345 }
346
347 this._trigger( "create", null, this._getCreateEventData() );
348 this._init();
349 },
350
351 _getCreateOptions: function() {
352 return {};
353 },
354
355 _getCreateEventData: $.noop,
356
357 _create: $.noop,
358
359 _init: $.noop,
360
361 destroy: function() {
362 var that = this;
363
364 this._destroy();
365 $.each( this.classesElementLookup, function( key, value ) {
366 that._removeClass( value, key );
367 } );
368
369 // We can probably remove the unbind calls in 2.0
370 // all event bindings should go through this._on()
371 this.element
372 .off( this.eventNamespace )
373 .removeData( this.widgetFullName );
374 this.widget()
375 .off( this.eventNamespace )
376 .removeAttr( "aria-disabled" );
377
378 // Clean up events and states
379 this.bindings.off( this.eventNamespace );
380 },
381
382 _destroy: $.noop,
383
384 widget: function() {
385 return this.element;
386 },
387
388 option: function( key, value ) {
389 var options = key;
390 var parts;
391 var curOption;
392 var i;
393
394 if ( arguments.length === 0 ) {
395
396 // Don't return a reference to the internal hash
397 return $.widget.extend( {}, this.options );
398 }
399
400 if ( typeof key === "string" ) {
401
402 // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
403 options = {};
404 parts = key.split( "." );
405 key = parts.shift();
406 if ( parts.length ) {
407 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
408 for ( i = 0; i < parts.length - 1; i++ ) {
409 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
410 curOption = curOption[ parts[ i ] ];
411 }
412 key = parts.pop();
413 if ( arguments.length === 1 ) {
414 return curOption[ key ] === undefined ? null : curOption[ key ];
415 }
416 curOption[ key ] = value;
417 } else {
418 if ( arguments.length === 1 ) {
419 return this.options[ key ] === undefined ? null : this.options[ key ];
420 }
421 options[ key ] = value;
422 }
423 }
424
425 this._setOptions( options );
426
427 return this;
428 },
429
430 _setOptions: function( options ) {
431 var key;
432
433 for ( key in options ) {
434 this._setOption( key, options[ key ] );
435 }
436
437 return this;
438 },
439
440 _setOption: function( key, value ) {
441 if ( key === "classes" ) {
442 this._setOptionClasses( value );
443 }
444
445 this.options[ key ] = value;
446
447 if ( key === "disabled" ) {
448 this._setOptionDisabled( value );
449 }
450
451 return this;
452 },
453
454 _setOptionClasses: function( value ) {
455 var classKey, elements, currentElements;
456
457 for ( classKey in value ) {
458 currentElements = this.classesElementLookup[ classKey ];
459 if ( value[ classKey ] === this.options.classes[ classKey ] ||
460 !currentElements ||
461 !currentElements.length ) {
462 continue;
463 }
464
465 // We are doing this to create a new jQuery object because the _removeClass() call
466 // on the next line is going to destroy the reference to the current elements being
467 // tracked. We need to save a copy of this collection so that we can add the new classes
468 // below.
469 elements = $( currentElements.get() );
470 this._removeClass( currentElements, classKey );
471
472 // We don't use _addClass() here, because that uses this.options.classes
473 // for generating the string of classes. We want to use the value passed in from
474 // _setOption(), this is the new value of the classes option which was passed to
475 // _setOption(). We pass this value directly to _classes().
476 elements.addClass( this._classes( {
477 element: elements,
478 keys: classKey,
479 classes: value,
480 add: true
481 } ) );
482 }
483 },
484
485 _setOptionDisabled: function( value ) {
486 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
487
488 // If the widget is becoming disabled, then nothing is interactive
489 if ( value ) {
490 this._removeClass( this.hoverable, null, "ui-state-hover" );
491 this._removeClass( this.focusable, null, "ui-state-focus" );
492 }
493 },
494
495 enable: function() {
496 return this._setOptions( { disabled: false } );
497 },
498
499 disable: function() {
500 return this._setOptions( { disabled: true } );
501 },
502
503 _classes: function( options ) {
504 var full = [];
505 var that = this;
506
507 options = $.extend( {
508 element: this.element,
509 classes: this.options.classes || {}
510 }, options );
511
512 function processClassString( classes, checkOption ) {
513 var current, i;
514 for ( i = 0; i < classes.length; i++ ) {
515 current = that.classesElementLookup[ classes[ i ] ] || $();
516 if ( options.add ) {
517 current = $( $.unique( current.get().concat( options.element.get() ) ) );
518 } else {
519 current = $( current.not( options.element ).get() );
520 }
521 that.classesElementLookup[ classes[ i ] ] = current;
522 full.push( classes[ i ] );
523 if ( checkOption && options.classes[ classes[ i ] ] ) {
524 full.push( options.classes[ classes[ i ] ] );
525 }
526 }
527 }
528
529 this._on( options.element, {
530 "remove": "_untrackClassesElement"
531 } );
532
533 if ( options.keys ) {
534 processClassString( options.keys.match( /\S+/g ) || [], true );
535 }
536 if ( options.extra ) {
537 processClassString( options.extra.match( /\S+/g ) || [] );
538 }
539
540 return full.join( " " );
541 },
542
543 _untrackClassesElement: function( event ) {
544 var that = this;
545 $.each( that.classesElementLookup, function( key, value ) {
546 if ( $.inArray( event.target, value ) !== -1 ) {
547 that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
548 }
549 } );
550 },
551
552 _removeClass: function( element, keys, extra ) {
553 return this._toggleClass( element, keys, extra, false );
554 },
555
556 _addClass: function( element, keys, extra ) {
557 return this._toggleClass( element, keys, extra, true );
558 },
559
560 _toggleClass: function( element, keys, extra, add ) {
561 add = ( typeof add === "boolean" ) ? add : extra;
562 var shift = ( typeof element === "string" || element === null ),
563 options = {
564 extra: shift ? keys : extra,
565 keys: shift ? element : keys,
566 element: shift ? this.element : element,
567 add: add
568 };
569 options.element.toggleClass( this._classes( options ), add );
570 return this;
571 },
572
573 _on: function( suppressDisabledCheck, element, handlers ) {
574 var delegateElement;
575 var instance = this;
576
577 // No suppressDisabledCheck flag, shuffle arguments
578 if ( typeof suppressDisabledCheck !== "boolean" ) {
579 handlers = element;
580 element = suppressDisabledCheck;
581 suppressDisabledCheck = false;
582 }
583
584 // No element argument, shuffle and use this.element
585 if ( !handlers ) {
586 handlers = element;
587 element = this.element;
588 delegateElement = this.widget();
589 } else {
590 element = delegateElement = $( element );
591 this.bindings = this.bindings.add( element );
592 }
593
594 $.each( handlers, function( event, handler ) {
595 function handlerProxy() {
596
597 // Allow widgets to customize the disabled handling
598 // - disabled as an array instead of boolean
599 // - disabled class as method for disabling individual parts
600 if ( !suppressDisabledCheck &&
601 ( instance.options.disabled === true ||
602 $( this ).hasClass( "ui-state-disabled" ) ) ) {
603 return;
604 }
605 return ( typeof handler === "string" ? instance[ handler ] : handler )
606 .apply( instance, arguments );
607 }
608
609 // Copy the guid so direct unbinding works
610 if ( typeof handler !== "string" ) {
611 handlerProxy.guid = handler.guid =
612 handler.guid || handlerProxy.guid || $.guid++;
613 }
614
615 var match = event.match( /^([\w:-]*)\s*(.*)$/ );
616 var eventName = match[ 1 ] + instance.eventNamespace;
617 var selector = match[ 2 ];
618
619 if ( selector ) {
620 delegateElement.on( eventName, selector, handlerProxy );
621 } else {
622 element.on( eventName, handlerProxy );
623 }
624 } );
625 },
626
627 _off: function( element, eventName ) {
628 eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
629 this.eventNamespace;
630 element.off( eventName ).off( eventName );
631
632 // Clear the stack to avoid memory leaks (#10056)
633 this.bindings = $( this.bindings.not( element ).get() );
634 this.focusable = $( this.focusable.not( element ).get() );
635 this.hoverable = $( this.hoverable.not( element ).get() );
636 },
637
638 _delay: function( handler, delay ) {
639 function handlerProxy() {
640 return ( typeof handler === "string" ? instance[ handler ] : handler )
641 .apply( instance, arguments );
642 }
643 var instance = this;
644 return setTimeout( handlerProxy, delay || 0 );
645 },
646
647 _hoverable: function( element ) {
648 this.hoverable = this.hoverable.add( element );
649 this._on( element, {
650 mouseenter: function( event ) {
651 this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
652 },
653 mouseleave: function( event ) {
654 this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
655 }
656 } );
657 },
658
659 _focusable: function( element ) {
660 this.focusable = this.focusable.add( element );
661 this._on( element, {
662 focusin: function( event ) {
663 this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
664 },
665 focusout: function( event ) {
666 this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
667 }
668 } );
669 },
670
671 _trigger: function( type, event, data ) {
672 var prop, orig;
673 var callback = this.options[ type ];
674
675 data = data || {};
676 event = $.Event( event );
677 event.type = ( type === this.widgetEventPrefix ?
678 type :
679 this.widgetEventPrefix + type ).toLowerCase();
680
681 // The original event may come from any element
682 // so we need to reset the target on the new event
683 event.target = this.element[ 0 ];
684
685 // Copy original event properties over to the new event
686 orig = event.originalEvent;
687 if ( orig ) {
688 for ( prop in orig ) {
689 if ( !( prop in event ) ) {
690 event[ prop ] = orig[ prop ];
691 }
692 }
693 }
694
695 this.element.trigger( event, data );
696 return !( $.isFunction( callback ) &&
697 callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
698 event.isDefaultPrevented() );
699 }
700};
701
702$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
703 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
704 if ( typeof options === "string" ) {
705 options = { effect: options };
706 }
707
708 var hasOptions;
709 var effectName = !options ?
710 method :
711 options === true || typeof options === "number" ?
712 defaultEffect :
713 options.effect || defaultEffect;
714
715 options = options || {};
716 if ( typeof options === "number" ) {
717 options = { duration: options };
718 }
719
720 hasOptions = !$.isEmptyObject( options );
721 options.complete = callback;
722
723 if ( options.delay ) {
724 element.delay( options.delay );
725 }
726
727 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
728 element[ method ]( options );
729 } else if ( effectName !== method && element[ effectName ] ) {
730 element[ effectName ]( options.duration, options.easing, callback );
731 } else {
732 element.queue( function( next ) {
733 $( this )[ method ]();
734 if ( callback ) {
735 callback.call( element[ 0 ] );
736 }
737 next();
738 } );
739 }
740 };
741} );
742
743var widget = $.widget;
744
745
746/*!
747 * jQuery UI Position 1.12.1
748 * http://jqueryui.com
749 *
750 * Copyright jQuery Foundation and other contributors
751 * Released under the MIT license.
752 * http://jquery.org/license
753 *
754 * http://api.jqueryui.com/position/
755 */
756
757//>>label: Position
758//>>group: Core
759//>>description: Positions elements relative to other elements.
760//>>docs: http://api.jqueryui.com/position/
761//>>demos: http://jqueryui.com/position/
762
763
764( function() {
765var cachedScrollbarWidth,
766 max = Math.max,
767 abs = Math.abs,
768 rhorizontal = /left|center|right/,
769 rvertical = /top|center|bottom/,
770 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
771 rposition = /^\w+/,
772 rpercent = /%$/,
773 _position = $.fn.position;
774
775function getOffsets( offsets, width, height ) {
776 return [
777 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
778 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
779 ];
780}
781
782function parseCss( element, property ) {
783 return parseInt( $.css( element, property ), 10 ) || 0;
784}
785
786function getDimensions( elem ) {
787 var raw = elem[ 0 ];
788 if ( raw.nodeType === 9 ) {
789 return {
790 width: elem.width(),
791 height: elem.height(),
792 offset: { top: 0, left: 0 }
793 };
794 }
795 if ( $.isWindow( raw ) ) {
796 return {
797 width: elem.width(),
798 height: elem.height(),
799 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
800 };
801 }
802 if ( raw.preventDefault ) {
803 return {
804 width: 0,
805 height: 0,
806 offset: { top: raw.pageY, left: raw.pageX }
807 };
808 }
809 return {
810 width: elem.outerWidth(),
811 height: elem.outerHeight(),
812 offset: elem.offset()
813 };
814}
815
816$.position = {
817 scrollbarWidth: function() {
818 if ( cachedScrollbarWidth !== undefined ) {
819 return cachedScrollbarWidth;
820 }
821 var w1, w2,
822 div = $( "<div " +
823 "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
824 "<div style='height:100px;width:auto;'></div></div>" ),
825 innerDiv = div.children()[ 0 ];
826
827 $( "body" ).append( div );
828 w1 = innerDiv.offsetWidth;
829 div.css( "overflow", "scroll" );
830
831 w2 = innerDiv.offsetWidth;
832
833 if ( w1 === w2 ) {
834 w2 = div[ 0 ].clientWidth;
835 }
836
837 div.remove();
838
839 return ( cachedScrollbarWidth = w1 - w2 );
840 },
841 getScrollInfo: function( within ) {
842 var overflowX = within.isWindow || within.isDocument ? "" :
843 within.element.css( "overflow-x" ),
844 overflowY = within.isWindow || within.isDocument ? "" :
845 within.element.css( "overflow-y" ),
846 hasOverflowX = overflowX === "scroll" ||
847 ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
848 hasOverflowY = overflowY === "scroll" ||
849 ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
850 return {
851 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
852 height: hasOverflowX ? $.position.scrollbarWidth() : 0
853 };
854 },
855 getWithinInfo: function( element ) {
856 var withinElement = $( element || window ),
857 isWindow = $.isWindow( withinElement[ 0 ] ),
858 isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
859 hasOffset = !isWindow && !isDocument;
860 return {
861 element: withinElement,
862 isWindow: isWindow,
863 isDocument: isDocument,
864 offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
865 scrollLeft: withinElement.scrollLeft(),
866 scrollTop: withinElement.scrollTop(),
867 width: withinElement.outerWidth(),
868 height: withinElement.outerHeight()
869 };
870 }
871};
872
873$.fn.position = function( options ) {
874 if ( !options || !options.of ) {
875 return _position.apply( this, arguments );
876 }
877
878 // Make a copy, we don't want to modify arguments
879 options = $.extend( {}, options );
880
881 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
882 target = $( options.of ),
883 within = $.position.getWithinInfo( options.within ),
884 scrollInfo = $.position.getScrollInfo( within ),
885 collision = ( options.collision || "flip" ).split( " " ),
886 offsets = {};
887
888 dimensions = getDimensions( target );
889 if ( target[ 0 ].preventDefault ) {
890
891 // Force left top to allow flipping
892 options.at = "left top";
893 }
894 targetWidth = dimensions.width;
895 targetHeight = dimensions.height;
896 targetOffset = dimensions.offset;
897
898 // Clone to reuse original targetOffset later
899 basePosition = $.extend( {}, targetOffset );
900
901 // Force my and at to have valid horizontal and vertical positions
902 // if a value is missing or invalid, it will be converted to center
903 $.each( [ "my", "at" ], function() {
904 var pos = ( options[ this ] || "" ).split( " " ),
905 horizontalOffset,
906 verticalOffset;
907
908 if ( pos.length === 1 ) {
909 pos = rhorizontal.test( pos[ 0 ] ) ?
910 pos.concat( [ "center" ] ) :
911 rvertical.test( pos[ 0 ] ) ?
912 [ "center" ].concat( pos ) :
913 [ "center", "center" ];
914 }
915 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
916 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
917
918 // Calculate offsets
919 horizontalOffset = roffset.exec( pos[ 0 ] );
920 verticalOffset = roffset.exec( pos[ 1 ] );
921 offsets[ this ] = [
922 horizontalOffset ? horizontalOffset[ 0 ] : 0,
923 verticalOffset ? verticalOffset[ 0 ] : 0
924 ];
925
926 // Reduce to just the positions without the offsets
927 options[ this ] = [
928 rposition.exec( pos[ 0 ] )[ 0 ],
929 rposition.exec( pos[ 1 ] )[ 0 ]
930 ];
931 } );
932
933 // Normalize collision option
934 if ( collision.length === 1 ) {
935 collision[ 1 ] = collision[ 0 ];
936 }
937
938 if ( options.at[ 0 ] === "right" ) {
939 basePosition.left += targetWidth;
940 } else if ( options.at[ 0 ] === "center" ) {
941 basePosition.left += targetWidth / 2;
942 }
943
944 if ( options.at[ 1 ] === "bottom" ) {
945 basePosition.top += targetHeight;
946 } else if ( options.at[ 1 ] === "center" ) {
947 basePosition.top += targetHeight / 2;
948 }
949
950 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
951 basePosition.left += atOffset[ 0 ];
952 basePosition.top += atOffset[ 1 ];
953
954 return this.each( function() {
955 var collisionPosition, using,
956 elem = $( this ),
957 elemWidth = elem.outerWidth(),
958 elemHeight = elem.outerHeight(),
959 marginLeft = parseCss( this, "marginLeft" ),
960 marginTop = parseCss( this, "marginTop" ),
961 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
962 scrollInfo.width,
963 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
964 scrollInfo.height,
965 position = $.extend( {}, basePosition ),
966 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
967
968 if ( options.my[ 0 ] === "right" ) {
969 position.left -= elemWidth;
970 } else if ( options.my[ 0 ] === "center" ) {
971 position.left -= elemWidth / 2;
972 }
973
974 if ( options.my[ 1 ] === "bottom" ) {
975 position.top -= elemHeight;
976 } else if ( options.my[ 1 ] === "center" ) {
977 position.top -= elemHeight / 2;
978 }
979
980 position.left += myOffset[ 0 ];
981 position.top += myOffset[ 1 ];
982
983 collisionPosition = {
984 marginLeft: marginLeft,
985 marginTop: marginTop
986 };
987
988 $.each( [ "left", "top" ], function( i, dir ) {
989 if ( $.ui.position[ collision[ i ] ] ) {
990 $.ui.position[ collision[ i ] ][ dir ]( position, {
991 targetWidth: targetWidth,
992 targetHeight: targetHeight,
993 elemWidth: elemWidth,
994 elemHeight: elemHeight,
995 collisionPosition: collisionPosition,
996 collisionWidth: collisionWidth,
997 collisionHeight: collisionHeight,
998 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
999 my: options.my,
1000 at: options.at,
1001 within: within,
1002 elem: elem
1003 } );
1004 }
1005 } );
1006
1007 if ( options.using ) {
1008
1009 // Adds feedback as second argument to using callback, if present
1010 using = function( props ) {
1011 var left = targetOffset.left - position.left,
1012 right = left + targetWidth - elemWidth,
1013 top = targetOffset.top - position.top,
1014 bottom = top + targetHeight - elemHeight,
1015 feedback = {
1016 target: {
1017 element: target,
1018 left: targetOffset.left,
1019 top: targetOffset.top,
1020 width: targetWidth,
1021 height: targetHeight
1022 },
1023 element: {
1024 element: elem,
1025 left: position.left,
1026 top: position.top,
1027 width: elemWidth,
1028 height: elemHeight
1029 },
1030 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1031 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1032 };
1033 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1034 feedback.horizontal = "center";
1035 }
1036 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1037 feedback.vertical = "middle";
1038 }
1039 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1040 feedback.important = "horizontal";
1041 } else {
1042 feedback.important = "vertical";
1043 }
1044 options.using.call( this, props, feedback );
1045 };
1046 }
1047
1048 elem.offset( $.extend( position, { using: using } ) );
1049 } );
1050};
1051
1052$.ui.position = {
1053 fit: {
1054 left: function( position, data ) {
1055 var within = data.within,
1056 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1057 outerWidth = within.width,
1058 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1059 overLeft = withinOffset - collisionPosLeft,
1060 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1061 newOverRight;
1062
1063 // Element is wider than within
1064 if ( data.collisionWidth > outerWidth ) {
1065
1066 // Element is initially over the left side of within
1067 if ( overLeft > 0 && overRight <= 0 ) {
1068 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
1069 withinOffset;
1070 position.left += overLeft - newOverRight;
1071
1072 // Element is initially over right side of within
1073 } else if ( overRight > 0 && overLeft <= 0 ) {
1074 position.left = withinOffset;
1075
1076 // Element is initially over both left and right sides of within
1077 } else {
1078 if ( overLeft > overRight ) {
1079 position.left = withinOffset + outerWidth - data.collisionWidth;
1080 } else {
1081 position.left = withinOffset;
1082 }
1083 }
1084
1085 // Too far left -> align with left edge
1086 } else if ( overLeft > 0 ) {
1087 position.left += overLeft;
1088
1089 // Too far right -> align with right edge
1090 } else if ( overRight > 0 ) {
1091 position.left -= overRight;
1092
1093 // Adjust based on position and margin
1094 } else {
1095 position.left = max( position.left - collisionPosLeft, position.left );
1096 }
1097 },
1098 top: function( position, data ) {
1099 var within = data.within,
1100 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1101 outerHeight = data.within.height,
1102 collisionPosTop = position.top - data.collisionPosition.marginTop,
1103 overTop = withinOffset - collisionPosTop,
1104 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1105 newOverBottom;
1106
1107 // Element is taller than within
1108 if ( data.collisionHeight > outerHeight ) {
1109
1110 // Element is initially over the top of within
1111 if ( overTop > 0 && overBottom <= 0 ) {
1112 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
1113 withinOffset;
1114 position.top += overTop - newOverBottom;
1115
1116 // Element is initially over bottom of within
1117 } else if ( overBottom > 0 && overTop <= 0 ) {
1118 position.top = withinOffset;
1119
1120 // Element is initially over both top and bottom of within
1121 } else {
1122 if ( overTop > overBottom ) {
1123 position.top = withinOffset + outerHeight - data.collisionHeight;
1124 } else {
1125 position.top = withinOffset;
1126 }
1127 }
1128
1129 // Too far up -> align with top
1130 } else if ( overTop > 0 ) {
1131 position.top += overTop;
1132
1133 // Too far down -> align with bottom edge
1134 } else if ( overBottom > 0 ) {
1135 position.top -= overBottom;
1136
1137 // Adjust based on position and margin
1138 } else {
1139 position.top = max( position.top - collisionPosTop, position.top );
1140 }
1141 }
1142 },
1143 flip: {
1144 left: function( position, data ) {
1145 var within = data.within,
1146 withinOffset = within.offset.left + within.scrollLeft,
1147 outerWidth = within.width,
1148 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1149 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1150 overLeft = collisionPosLeft - offsetLeft,
1151 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1152 myOffset = data.my[ 0 ] === "left" ?
1153 -data.elemWidth :
1154 data.my[ 0 ] === "right" ?
1155 data.elemWidth :
1156 0,
1157 atOffset = data.at[ 0 ] === "left" ?
1158 data.targetWidth :
1159 data.at[ 0 ] === "right" ?
1160 -data.targetWidth :
1161 0,
1162 offset = -2 * data.offset[ 0 ],
1163 newOverRight,
1164 newOverLeft;
1165
1166 if ( overLeft < 0 ) {
1167 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
1168 outerWidth - withinOffset;
1169 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1170 position.left += myOffset + atOffset + offset;
1171 }
1172 } else if ( overRight > 0 ) {
1173 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
1174 atOffset + offset - offsetLeft;
1175 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1176 position.left += myOffset + atOffset + offset;
1177 }
1178 }
1179 },
1180 top: function( position, data ) {
1181 var within = data.within,
1182 withinOffset = within.offset.top + within.scrollTop,
1183 outerHeight = within.height,
1184 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1185 collisionPosTop = position.top - data.collisionPosition.marginTop,
1186 overTop = collisionPosTop - offsetTop,
1187 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1188 top = data.my[ 1 ] === "top",
1189 myOffset = top ?
1190 -data.elemHeight :
1191 data.my[ 1 ] === "bottom" ?
1192 data.elemHeight :
1193 0,
1194 atOffset = data.at[ 1 ] === "top" ?
1195 data.targetHeight :
1196 data.at[ 1 ] === "bottom" ?
1197 -data.targetHeight :
1198 0,
1199 offset = -2 * data.offset[ 1 ],
1200 newOverTop,
1201 newOverBottom;
1202 if ( overTop < 0 ) {
1203 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
1204 outerHeight - withinOffset;
1205 if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
1206 position.top += myOffset + atOffset + offset;
1207 }
1208 } else if ( overBottom > 0 ) {
1209 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
1210 offset - offsetTop;
1211 if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
1212 position.top += myOffset + atOffset + offset;
1213 }
1214 }
1215 }
1216 },
1217 flipfit: {
1218 left: function() {
1219 $.ui.position.flip.left.apply( this, arguments );
1220 $.ui.position.fit.left.apply( this, arguments );
1221 },
1222 top: function() {
1223 $.ui.position.flip.top.apply( this, arguments );
1224 $.ui.position.fit.top.apply( this, arguments );
1225 }
1226 }
1227};
1228
1229} )();
1230
1231var position = $.ui.position;
1232
1233
1234/*!
1235 * jQuery UI Disable Selection 1.12.1
1236 * http://jqueryui.com
1237 *
1238 * Copyright jQuery Foundation and other contributors
1239 * Released under the MIT license.
1240 * http://jquery.org/license
1241 */
1242
1243//>>label: disableSelection
1244//>>group: Core
1245//>>description: Disable selection of text content within the set of matched elements.
1246//>>docs: http://api.jqueryui.com/disableSelection/
1247
1248// This file is deprecated
1249
1250
1251var disableSelection = $.fn.extend( {
1252 disableSelection: ( function() {
1253 var eventType = "onselectstart" in document.createElement( "div" ) ?
1254 "selectstart" :
1255 "mousedown";
1256
1257 return function() {
1258 return this.on( eventType + ".ui-disableSelection", function( event ) {
1259 event.preventDefault();
1260 } );
1261 };
1262 } )(),
1263
1264 enableSelection: function() {
1265 return this.off( ".ui-disableSelection" );
1266 }
1267} );
1268
1269
1270/*!
1271 * jQuery UI Keycode 1.12.1
1272 * http://jqueryui.com
1273 *
1274 * Copyright jQuery Foundation and other contributors
1275 * Released under the MIT license.
1276 * http://jquery.org/license
1277 */
1278
1279//>>label: Keycode
1280//>>group: Core
1281//>>description: Provide keycodes as keynames
1282//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
1283
1284
1285var keycode = $.ui.keyCode = {
1286 BACKSPACE: 8,
1287 COMMA: 188,
1288 DELETE: 46,
1289 DOWN: 40,
1290 END: 35,
1291 ENTER: 13,
1292 ESCAPE: 27,
1293 HOME: 36,
1294 LEFT: 37,
1295 PAGE_DOWN: 34,
1296 PAGE_UP: 33,
1297 PERIOD: 190,
1298 RIGHT: 39,
1299 SPACE: 32,
1300 TAB: 9,
1301 UP: 38
1302};
1303
1304
1305/*!
1306 * jQuery UI Unique ID 1.12.1
1307 * http://jqueryui.com
1308 *
1309 * Copyright jQuery Foundation and other contributors
1310 * Released under the MIT license.
1311 * http://jquery.org/license
1312 */
1313
1314//>>label: uniqueId
1315//>>group: Core
1316//>>description: Functions to generate and remove uniqueId's
1317//>>docs: http://api.jqueryui.com/uniqueId/
1318
1319
1320
1321var uniqueId = $.fn.extend( {
1322 uniqueId: ( function() {
1323 var uuid = 0;
1324
1325 return function() {
1326 return this.each( function() {
1327 if ( !this.id ) {
1328 this.id = "ui-id-" + ( ++uuid );
1329 }
1330 } );
1331 };
1332 } )(),
1333
1334 removeUniqueId: function() {
1335 return this.each( function() {
1336 if ( /^ui-id-\d+$/.test( this.id ) ) {
1337 $( this ).removeAttr( "id" );
1338 }
1339 } );
1340 }
1341} );
1342
1343
1344
1345var safeActiveElement = $.ui.safeActiveElement = function( document ) {
1346 var activeElement;
1347
1348 // Support: IE 9 only
1349 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
1350 try {
1351 activeElement = document.activeElement;
1352 } catch ( error ) {
1353 activeElement = document.body;
1354 }
1355
1356 // Support: IE 9 - 11 only
1357 // IE may return null instead of an element
1358 // Interestingly, this only seems to occur when NOT in an iframe
1359 if ( !activeElement ) {
1360 activeElement = document.body;
1361 }
1362
1363 // Support: IE 11 only
1364 // IE11 returns a seemingly empty object in some cases when accessing
1365 // document.activeElement from an <iframe>
1366 if ( !activeElement.nodeName ) {
1367 activeElement = document.body;
1368 }
1369
1370 return activeElement;
1371};
1372
1373
1374/*!
1375 * jQuery UI Menu 1.12.1
1376 * http://jqueryui.com
1377 *
1378 * Copyright jQuery Foundation and other contributors
1379 * Released under the MIT license.
1380 * http://jquery.org/license
1381 */
1382
1383//>>label: Menu
1384//>>group: Widgets
1385//>>description: Creates nestable menus.
1386//>>docs: http://api.jqueryui.com/menu/
1387//>>demos: http://jqueryui.com/menu/
1388//>>css.structure: ../../themes/base/core.css
1389//>>css.structure: ../../themes/base/menu.css
1390//>>css.theme: ../../themes/base/theme.css
1391
1392
1393
1394var widgetsMenu = $.widget( "ui.menu", {
1395 version: "1.12.1",
1396 defaultElement: "<ul>",
1397 delay: 300,
1398 options: {
1399 icons: {
1400 submenu: "ui-icon-caret-1-e"
1401 },
1402 items: "> *",
1403 menus: "ul",
1404 position: {
1405 my: "left top",
1406 at: "right top"
1407 },
1408 role: "menu",
1409
1410 // Callbacks
1411 blur: null,
1412 focus: null,
1413 select: null
1414 },
1415
1416 _create: function() {
1417 this.activeMenu = this.element;
1418
1419 // Flag used to prevent firing of the click handler
1420 // as the event bubbles up through nested menus
1421 this.mouseHandled = false;
1422 this.element
1423 .uniqueId()
1424 .attr( {
1425 role: this.options.role,
1426 tabIndex: 0
1427 } );
1428
1429 this._addClass( "ui-menu", "ui-widget ui-widget-content" );
1430 this._on( {
1431
1432 // Prevent focus from sticking to links inside menu after clicking
1433 // them (focus should always stay on UL during navigation).
1434 "mousedown .ui-menu-item": function( event ) {
1435 event.preventDefault();
1436 },
1437 "click .ui-menu-item": function( event ) {
1438 var target = $( event.target );
1439 var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
1440 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
1441 this.select( event );
1442
1443 // Only set the mouseHandled flag if the event will bubble, see #9469.
1444 if ( !event.isPropagationStopped() ) {
1445 this.mouseHandled = true;
1446 }
1447
1448 // Open submenu on click
1449 if ( target.has( ".ui-menu" ).length ) {
1450 this.expand( event );
1451 } else if ( !this.element.is( ":focus" ) &&
1452 active.closest( ".ui-menu" ).length ) {
1453
1454 // Redirect focus to the menu
1455 this.element.trigger( "focus", [ true ] );
1456
1457 // If the active item is on the top level, let it stay active.
1458 // Otherwise, blur the active item since it is no longer visible.
1459 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
1460 clearTimeout( this.timer );
1461 }
1462 }
1463 }
1464 },
1465 "mouseenter .ui-menu-item": function( event ) {
1466
1467 // Ignore mouse events while typeahead is active, see #10458.
1468 // Prevents focusing the wrong item when typeahead causes a scroll while the mouse
1469 // is over an item in the menu
1470 if ( this.previousFilter ) {
1471 return;
1472 }
1473
1474 var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
1475 target = $( event.currentTarget );
1476
1477 // Ignore bubbled events on parent items, see #11641
1478 if ( actualTarget[ 0 ] !== target[ 0 ] ) {
1479 return;
1480 }
1481
1482 // Remove ui-state-active class from siblings of the newly focused menu item
1483 // to avoid a jump caused by adjacent elements both having a class with a border
1484 this._removeClass( target.siblings().children( ".ui-state-active" ),
1485 null, "ui-state-active" );
1486 this.focus( event, target );
1487 },
1488 mouseleave: "collapseAll",
1489 "mouseleave .ui-menu": "collapseAll",
1490 focus: function( event, keepActiveItem ) {
1491
1492 // If there's already an active item, keep it active
1493 // If not, activate the first item
1494 var item = this.active || this.element.find( this.options.items ).eq( 0 );
1495
1496 if ( !keepActiveItem ) {
1497 this.focus( event, item );
1498 }
1499 },
1500 blur: function( event ) {
1501 this._delay( function() {
1502 var notContained = !$.contains(
1503 this.element[ 0 ],
1504 $.ui.safeActiveElement( this.document[ 0 ] )
1505 );
1506 if ( notContained ) {
1507 this.collapseAll( event );
1508 }
1509 } );
1510 },
1511 keydown: "_keydown"
1512 } );
1513
1514 this.refresh();
1515
1516 // Clicks outside of a menu collapse any open menus
1517 this._on( this.document, {
1518 click: function( event ) {
1519 if ( this._closeOnDocumentClick( event ) ) {
1520 this.collapseAll( event );
1521 }
1522
1523 // Reset the mouseHandled flag
1524 this.mouseHandled = false;
1525 }
1526 } );
1527 },
1528
1529 _destroy: function() {
1530 var items = this.element.find( ".ui-menu-item" )
1531 .removeAttr( "role aria-disabled" ),
1532 submenus = items.children( ".ui-menu-item-wrapper" )
1533 .removeUniqueId()
1534 .removeAttr( "tabIndex role aria-haspopup" );
1535
1536 // Destroy (sub)menus
1537 this.element
1538 .removeAttr( "aria-activedescendant" )
1539 .find( ".ui-menu" ).addBack()
1540 .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
1541 "tabIndex" )
1542 .removeUniqueId()
1543 .show();
1544
1545 submenus.children().each( function() {
1546 var elem = $( this );
1547 if ( elem.data( "ui-menu-submenu-caret" ) ) {
1548 elem.remove();
1549 }
1550 } );
1551 },
1552
1553 _keydown: function( event ) {
1554 var match, prev, character, skip,
1555 preventDefault = true;
1556
1557 switch ( event.keyCode ) {
1558 case $.ui.keyCode.PAGE_UP:
1559 this.previousPage( event );
1560 break;
1561 case $.ui.keyCode.PAGE_DOWN:
1562 this.nextPage( event );
1563 break;
1564 case $.ui.keyCode.HOME:
1565 this._move( "first", "first", event );
1566 break;
1567 case $.ui.keyCode.END:
1568 this._move( "last", "last", event );
1569 break;
1570 case $.ui.keyCode.UP:
1571 this.previous( event );
1572 break;
1573 case $.ui.keyCode.DOWN:
1574 this.next( event );
1575 break;
1576 case $.ui.keyCode.LEFT:
1577 this.collapse( event );
1578 break;
1579 case $.ui.keyCode.RIGHT:
1580 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
1581 this.expand( event );
1582 }
1583 break;
1584 case $.ui.keyCode.ENTER:
1585 case $.ui.keyCode.SPACE:
1586 this._activate( event );
1587 break;
1588 case $.ui.keyCode.ESCAPE:
1589 this.collapse( event );
1590 break;
1591 default:
1592 preventDefault = false;
1593 prev = this.previousFilter || "";
1594 skip = false;
1595
1596 // Support number pad values
1597 character = event.keyCode >= 96 && event.keyCode <= 105 ?
1598 ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );
1599
1600 clearTimeout( this.filterTimer );
1601
1602 if ( character === prev ) {
1603 skip = true;
1604 } else {
1605 character = prev + character;
1606 }
1607
1608 match = this._filterMenuItems( character );
1609 match = skip && match.index( this.active.next() ) !== -1 ?
1610 this.active.nextAll( ".ui-menu-item" ) :
1611 match;
1612
1613 // If no matches on the current filter, reset to the last character pressed
1614 // to move down the menu to the first item that starts with that character
1615 if ( !match.length ) {
1616 character = String.fromCharCode( event.keyCode );
1617 match = this._filterMenuItems( character );
1618 }
1619
1620 if ( match.length ) {
1621 this.focus( event, match );
1622 this.previousFilter = character;
1623 this.filterTimer = this._delay( function() {
1624 delete this.previousFilter;
1625 }, 1000 );
1626 } else {
1627 delete this.previousFilter;
1628 }
1629 }
1630
1631 if ( preventDefault ) {
1632 event.preventDefault();
1633 }
1634 },
1635
1636 _activate: function( event ) {
1637 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
1638 if ( this.active.children( "[aria-haspopup='true']" ).length ) {
1639 this.expand( event );
1640 } else {
1641 this.select( event );
1642 }
1643 }
1644 },
1645
1646 refresh: function() {
1647 var menus, items, newSubmenus, newItems, newWrappers,
1648 that = this,
1649 icon = this.options.icons.submenu,
1650 submenus = this.element.find( this.options.menus );
1651
1652 this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
1653
1654 // Initialize nested menus
1655 newSubmenus = submenus.filter( ":not(.ui-menu)" )
1656 .hide()
1657 .attr( {
1658 role: this.options.role,
1659 "aria-hidden": "true",
1660 "aria-expanded": "false"
1661 } )
1662 .each( function() {
1663 var menu = $( this ),
1664 item = menu.prev(),
1665 submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
1666
1667 that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
1668 item
1669 .attr( "aria-haspopup", "true" )
1670 .prepend( submenuCaret );
1671 menu.attr( "aria-labelledby", item.attr( "id" ) );
1672 } );
1673
1674 this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
1675
1676 menus = submenus.add( this.element );
1677 items = menus.find( this.options.items );
1678
1679 // Initialize menu-items containing spaces and/or dashes only as dividers
1680 items.not( ".ui-menu-item" ).each( function() {
1681 var item = $( this );
1682 if ( that._isDivider( item ) ) {
1683 that._addClass( item, "ui-menu-divider", "ui-widget-content" );
1684 }
1685 } );
1686
1687 // Don't refresh list items that are already adapted
1688 newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
1689 newWrappers = newItems.children()
1690 .not( ".ui-menu" )
1691 .uniqueId()
1692 .attr( {
1693 tabIndex: -1,
1694 role: this._itemRole()
1695 } );
1696 this._addClass( newItems, "ui-menu-item" )
1697 ._addClass( newWrappers, "ui-menu-item-wrapper" );
1698
1699 // Add aria-disabled attribute to any disabled menu item
1700 items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
1701
1702 // If the active item has been removed, blur the menu
1703 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
1704 this.blur();
1705 }
1706 },
1707
1708 _itemRole: function() {
1709 return {
1710 menu: "menuitem",
1711 listbox: "option"
1712 }[ this.options.role ];
1713 },
1714
1715 _setOption: function( key, value ) {
1716 if ( key === "icons" ) {
1717 var icons = this.element.find( ".ui-menu-icon" );
1718 this._removeClass( icons, null, this.options.icons.submenu )
1719 ._addClass( icons, null, value.submenu );
1720 }
1721 this._super( key, value );
1722 },
1723
1724 _setOptionDisabled: function( value ) {
1725 this._super( value );
1726
1727 this.element.attr( "aria-disabled", String( value ) );
1728 this._toggleClass( null, "ui-state-disabled", !!value );
1729 },
1730
1731 focus: function( event, item ) {
1732 var nested, focused, activeParent;
1733 this.blur( event, event && event.type === "focus" );
1734
1735 this._scrollIntoView( item );
1736
1737 this.active = item.first();
1738
1739 focused = this.active.children( ".ui-menu-item-wrapper" );
1740 this._addClass( focused, null, "ui-state-active" );
1741
1742 // Only update aria-activedescendant if there's a role
1743 // otherwise we assume focus is managed elsewhere
1744 if ( this.options.role ) {
1745 this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
1746 }
1747
1748 // Highlight active parent menu item, if any
1749 activeParent = this.active
1750 .parent()
1751 .closest( ".ui-menu-item" )
1752 .children( ".ui-menu-item-wrapper" );
1753 this._addClass( activeParent, null, "ui-state-active" );
1754
1755 if ( event && event.type === "keydown" ) {
1756 this._close();
1757 } else {
1758 this.timer = this._delay( function() {
1759 this._close();
1760 }, this.delay );
1761 }
1762
1763 nested = item.children( ".ui-menu" );
1764 if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
1765 this._startOpening( nested );
1766 }
1767 this.activeMenu = item.parent();
1768
1769 this._trigger( "focus", event, { item: item } );
1770 },
1771
1772 _scrollIntoView: function( item ) {
1773 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
1774 if ( this._hasScroll() ) {
1775 borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
1776 paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
1777 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
1778 scroll = this.activeMenu.scrollTop();
1779 elementHeight = this.activeMenu.height();
1780 itemHeight = item.outerHeight();
1781
1782 if ( offset < 0 ) {
1783 this.activeMenu.scrollTop( scroll + offset );
1784 } else if ( offset + itemHeight > elementHeight ) {
1785 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
1786 }
1787 }
1788 },
1789
1790 blur: function( event, fromFocus ) {
1791 if ( !fromFocus ) {
1792 clearTimeout( this.timer );
1793 }
1794
1795 if ( !this.active ) {
1796 return;
1797 }
1798
1799 this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
1800 null, "ui-state-active" );
1801
1802 this._trigger( "blur", event, { item: this.active } );
1803 this.active = null;
1804 },
1805
1806 _startOpening: function( submenu ) {
1807 clearTimeout( this.timer );
1808
1809 // Don't open if already open fixes a Firefox bug that caused a .5 pixel
1810 // shift in the submenu position when mousing over the caret icon
1811 if ( submenu.attr( "aria-hidden" ) !== "true" ) {
1812 return;
1813 }
1814
1815 this.timer = this._delay( function() {
1816 this._close();
1817 this._open( submenu );
1818 }, this.delay );
1819 },
1820
1821 _open: function( submenu ) {
1822 var position = $.extend( {
1823 of: this.active
1824 }, this.options.position );
1825
1826 clearTimeout( this.timer );
1827 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
1828 .hide()
1829 .attr( "aria-hidden", "true" );
1830
1831 submenu
1832 .show()
1833 .removeAttr( "aria-hidden" )
1834 .attr( "aria-expanded", "true" )
1835 .position( position );
1836 },
1837
1838 collapseAll: function( event, all ) {
1839 clearTimeout( this.timer );
1840 this.timer = this._delay( function() {
1841
1842 // If we were passed an event, look for the submenu that contains the event
1843 var currentMenu = all ? this.element :
1844 $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
1845
1846 // If we found no valid submenu ancestor, use the main menu to close all
1847 // sub menus anyway
1848 if ( !currentMenu.length ) {
1849 currentMenu = this.element;
1850 }
1851
1852 this._close( currentMenu );
1853
1854 this.blur( event );
1855
1856 // Work around active item staying active after menu is blurred
1857 this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );
1858
1859 this.activeMenu = currentMenu;
1860 }, this.delay );
1861 },
1862
1863 // With no arguments, closes the currently active menu - if nothing is active
1864 // it closes all menus. If passed an argument, it will search for menus BELOW
1865 _close: function( startMenu ) {
1866 if ( !startMenu ) {
1867 startMenu = this.active ? this.active.parent() : this.element;
1868 }
1869
1870 startMenu.find( ".ui-menu" )
1871 .hide()
1872 .attr( "aria-hidden", "true" )
1873 .attr( "aria-expanded", "false" );
1874 },
1875
1876 _closeOnDocumentClick: function( event ) {
1877 return !$( event.target ).closest( ".ui-menu" ).length;
1878 },
1879
1880 _isDivider: function( item ) {
1881
1882 // Match hyphen, em dash, en dash
1883 return !/[^\-\u2014\u2013\s]/.test( item.text() );
1884 },
1885
1886 collapse: function( event ) {
1887 var newItem = this.active &&
1888 this.active.parent().closest( ".ui-menu-item", this.element );
1889 if ( newItem && newItem.length ) {
1890 this._close();
1891 this.focus( event, newItem );
1892 }
1893 },
1894
1895 expand: function( event ) {
1896 var newItem = this.active &&
1897 this.active
1898 .children( ".ui-menu " )
1899 .find( this.options.items )
1900 .first();
1901
1902 if ( newItem && newItem.length ) {
1903 this._open( newItem.parent() );
1904
1905 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
1906 this._delay( function() {
1907 this.focus( event, newItem );
1908 } );
1909 }
1910 },
1911
1912 next: function( event ) {
1913 this._move( "next", "first", event );
1914 },
1915
1916 previous: function( event ) {
1917 this._move( "prev", "last", event );
1918 },
1919
1920 isFirstItem: function() {
1921 return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
1922 },
1923
1924 isLastItem: function() {
1925 return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
1926 },
1927
1928 _move: function( direction, filter, event ) {
1929 var next;
1930 if ( this.active ) {
1931 if ( direction === "first" || direction === "last" ) {
1932 next = this.active
1933 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
1934 .eq( -1 );
1935 } else {
1936 next = this.active
1937 [ direction + "All" ]( ".ui-menu-item" )
1938 .eq( 0 );
1939 }
1940 }
1941 if ( !next || !next.length || !this.active ) {
1942 next = this.activeMenu.find( this.options.items )[ filter ]();
1943 }
1944
1945 this.focus( event, next );
1946 },
1947
1948 nextPage: function( event ) {
1949 var item, base, height;
1950
1951 if ( !this.active ) {
1952 this.next( event );
1953 return;
1954 }
1955 if ( this.isLastItem() ) {
1956 return;
1957 }
1958 if ( this._hasScroll() ) {
1959 base = this.active.offset().top;
1960 height = this.element.height();
1961 this.active.nextAll( ".ui-menu-item" ).each( function() {
1962 item = $( this );
1963 return item.offset().top - base - height < 0;
1964 } );
1965
1966 this.focus( event, item );
1967 } else {
1968 this.focus( event, this.activeMenu.find( this.options.items )
1969 [ !this.active ? "first" : "last" ]() );
1970 }
1971 },
1972
1973 previousPage: function( event ) {
1974 var item, base, height;
1975 if ( !this.active ) {
1976 this.next( event );
1977 return;
1978 }
1979 if ( this.isFirstItem() ) {
1980 return;
1981 }
1982 if ( this._hasScroll() ) {
1983 base = this.active.offset().top;
1984 height = this.element.height();
1985 this.active.prevAll( ".ui-menu-item" ).each( function() {
1986 item = $( this );
1987 return item.offset().top - base + height > 0;
1988 } );
1989
1990 this.focus( event, item );
1991 } else {
1992 this.focus( event, this.activeMenu.find( this.options.items ).first() );
1993 }
1994 },
1995
1996 _hasScroll: function() {
1997 return this.element.outerHeight() < this.element.prop( "scrollHeight" );
1998 },
1999
2000 select: function( event ) {
2001
2002 // TODO: It should never be possible to not have an active item at this
2003 // point, but the tests don't trigger mouseenter before click.
2004 this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
2005 var ui = { item: this.active };
2006 if ( !this.active.has( ".ui-menu" ).length ) {
2007 this.collapseAll( event, true );
2008 }
2009 this._trigger( "select", event, ui );
2010 },
2011
2012 _filterMenuItems: function( character ) {
2013 var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
2014 regex = new RegExp( "^" + escapedCharacter, "i" );
2015
2016 return this.activeMenu
2017 .find( this.options.items )
2018
2019 // Only match on items, not dividers or other content (#10571)
2020 .filter( ".ui-menu-item" )
2021 .filter( function() {
2022 return regex.test(
2023 $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
2024 } );
2025 }
2026} );
2027
2028
2029/*!
2030 * jQuery UI Autocomplete 1.12.1
2031 * http://jqueryui.com
2032 *
2033 * Copyright jQuery Foundation and other contributors
2034 * Released under the MIT license.
2035 * http://jquery.org/license
2036 */
2037
2038//>>label: Autocomplete
2039//>>group: Widgets
2040//>>description: Lists suggested words as the user is typing.
2041//>>docs: http://api.jqueryui.com/autocomplete/
2042//>>demos: http://jqueryui.com/autocomplete/
2043//>>css.structure: ../../themes/base/core.css
2044//>>css.structure: ../../themes/base/autocomplete.css
2045//>>css.theme: ../../themes/base/theme.css
2046
2047
2048
2049$.widget( "ui.autocomplete", {
2050 version: "1.12.1",
2051 defaultElement: "<input>",
2052 options: {
2053 appendTo: null,
2054 autoFocus: false,
2055 delay: 300,
2056 minLength: 1,
2057 position: {
2058 my: "left top",
2059 at: "left bottom",
2060 collision: "none"
2061 },
2062 source: null,
2063
2064 // Callbacks
2065 change: null,
2066 close: null,
2067 focus: null,
2068 open: null,
2069 response: null,
2070 search: null,
2071 select: null
2072 },
2073
2074 requestIndex: 0,
2075 pending: 0,
2076
2077 _create: function() {
2078
2079 // Some browsers only repeat keydown events, not keypress events,
2080 // so we use the suppressKeyPress flag to determine if we've already
2081 // handled the keydown event. #7269
2082 // Unfortunately the code for & in keypress is the same as the up arrow,
2083 // so we use the suppressKeyPressRepeat flag to avoid handling keypress
2084 // events when we know the keydown event was used to modify the
2085 // search term. #7799
2086 var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
2087 nodeName = this.element[ 0 ].nodeName.toLowerCase(),
2088 isTextarea = nodeName === "textarea",
2089 isInput = nodeName === "input";
2090
2091 // Textareas are always multi-line
2092 // Inputs are always single-line, even if inside a contentEditable element
2093 // IE also treats inputs as contentEditable
2094 // All other element types are determined by whether or not they're contentEditable
2095 this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
2096
2097 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
2098 this.isNewMenu = true;
2099
2100 this._addClass( "ui-autocomplete-input" );
2101 this.element.attr( "autocomplete", "off" );
2102
2103 this._on( this.element, {
2104 keydown: function( event ) {
2105 if ( this.element.prop( "readOnly" ) ) {
2106 suppressKeyPress = true;
2107 suppressInput = true;
2108 suppressKeyPressRepeat = true;
2109 return;
2110 }
2111
2112 suppressKeyPress = false;
2113 suppressInput = false;
2114 suppressKeyPressRepeat = false;
2115 var keyCode = $.ui.keyCode;
2116 switch ( event.keyCode ) {
2117 case keyCode.PAGE_UP:
2118 suppressKeyPress = true;
2119 this._move( "previousPage", event );
2120 break;
2121 case keyCode.PAGE_DOWN:
2122 suppressKeyPress = true;
2123 this._move( "nextPage", event );
2124 break;
2125 case keyCode.UP:
2126 suppressKeyPress = true;
2127 this._keyEvent( "previous", event );
2128 break;
2129 case keyCode.DOWN:
2130 suppressKeyPress = true;
2131 this._keyEvent( "next", event );
2132 break;
2133 case keyCode.ENTER:
2134
2135 // when menu is open and has focus
2136 if ( this.menu.active ) {
2137
2138 // #6055 - Opera still allows the keypress to occur
2139 // which causes forms to submit
2140 suppressKeyPress = true;
2141 event.preventDefault();
2142 this.menu.select( event );
2143 }
2144 break;
2145 case keyCode.TAB:
2146 if ( this.menu.active ) {
2147 this.menu.select( event );
2148 }
2149 break;
2150 case keyCode.ESCAPE:
2151 if ( this.menu.element.is( ":visible" ) ) {
2152 if ( !this.isMultiLine ) {
2153 this._value( this.term );
2154 }
2155 this.close( event );
2156
2157 // Different browsers have different default behavior for escape
2158 // Single press can mean undo or clear
2159 // Double press in IE means clear the whole form
2160 event.preventDefault();
2161 }
2162 break;
2163 default:
2164 suppressKeyPressRepeat = true;
2165
2166 // search timeout should be triggered before the input value is changed
2167 this._searchTimeout( event );
2168 break;
2169 }
2170 },
2171 keypress: function( event ) {
2172 if ( suppressKeyPress ) {
2173 suppressKeyPress = false;
2174 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2175 event.preventDefault();
2176 }
2177 return;
2178 }
2179 if ( suppressKeyPressRepeat ) {
2180 return;
2181 }
2182
2183 // Replicate some key handlers to allow them to repeat in Firefox and Opera
2184 var keyCode = $.ui.keyCode;
2185 switch ( event.keyCode ) {
2186 case keyCode.PAGE_UP:
2187 this._move( "previousPage", event );
2188 break;
2189 case keyCode.PAGE_DOWN:
2190 this._move( "nextPage", event );
2191 break;
2192 case keyCode.UP:
2193 this._keyEvent( "previous", event );
2194 break;
2195 case keyCode.DOWN:
2196 this._keyEvent( "next", event );
2197 break;
2198 }
2199 },
2200 input: function( event ) {
2201 if ( suppressInput ) {
2202 suppressInput = false;
2203 event.preventDefault();
2204 return;
2205 }
2206 this._searchTimeout( event );
2207 },
2208 focus: function() {
2209 this.selectedItem = null;
2210 this.previous = this._value();
2211 },
2212 blur: function( event ) {
2213 if ( this.cancelBlur ) {
2214 delete this.cancelBlur;
2215 return;
2216 }
2217
2218 clearTimeout( this.searching );
2219 this.close( event );
2220 this._change( event );
2221 }
2222 } );
2223
2224 this._initSource();
2225 this.menu = $( "<ul>" )
2226 .appendTo( this._appendTo() )
2227 .menu( {
2228
2229 // disable ARIA support, the live region takes care of that
2230 role: null
2231 } )
2232 .hide()
2233 .menu( "instance" );
2234
2235 this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
2236 this._on( this.menu.element, {
2237 mousedown: function( event ) {
2238
2239 // prevent moving focus out of the text field
2240 event.preventDefault();
2241
2242 // IE doesn't prevent moving focus even with event.preventDefault()
2243 // so we set a flag to know when we should ignore the blur event
2244 this.cancelBlur = true;
2245 this._delay( function() {
2246 delete this.cancelBlur;
2247
2248 // Support: IE 8 only
2249 // Right clicking a menu item or selecting text from the menu items will
2250 // result in focus moving out of the input. However, we've already received
2251 // and ignored the blur event because of the cancelBlur flag set above. So
2252 // we restore focus to ensure that the menu closes properly based on the user's
2253 // next actions.
2254 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
2255 this.element.trigger( "focus" );
2256 }
2257 } );
2258 },
2259 menufocus: function( event, ui ) {
2260 var label, item;
2261
2262 // support: Firefox
2263 // Prevent accidental activation of menu items in Firefox (#7024 #9118)
2264 if ( this.isNewMenu ) {
2265 this.isNewMenu = false;
2266 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
2267 this.menu.blur();
2268
2269 this.document.one( "mousemove", function() {
2270 $( event.target ).trigger( event.originalEvent );
2271 } );
2272
2273 return;
2274 }
2275 }
2276
2277 item = ui.item.data( "ui-autocomplete-item" );
2278 if ( false !== this._trigger( "focus", event, { item: item } ) ) {
2279
2280 // use value to match what will end up in the input, if it was a key event
2281 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
2282 this._value( item.value );
2283 }
2284 }
2285
2286 // Announce the value in the liveRegion
2287 label = ui.item.attr( "aria-label" ) || item.value;
2288 if ( label && $.trim( label ).length ) {
2289 this.liveRegion.children().hide();
2290 $( "<div>" ).text( label ).appendTo( this.liveRegion );
2291 }
2292 },
2293 menuselect: function( event, ui ) {
2294 var item = ui.item.data( "ui-autocomplete-item" ),
2295 previous = this.previous;
2296
2297 // Only trigger when focus was lost (click on menu)
2298 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
2299 this.element.trigger( "focus" );
2300 this.previous = previous;
2301
2302 // #6109 - IE triggers two focus events and the second
2303 // is asynchronous, so we need to reset the previous
2304 // term synchronously and asynchronously :-(
2305 this._delay( function() {
2306 this.previous = previous;
2307 this.selectedItem = item;
2308 } );
2309 }
2310
2311 if ( false !== this._trigger( "select", event, { item: item } ) ) {
2312 this._value( item.value );
2313 }
2314
2315 // reset the term after the select event
2316 // this allows custom select handling to work properly
2317 this.term = this._value();
2318
2319 this.close( event );
2320 this.selectedItem = item;
2321 }
2322 } );
2323
2324 this.liveRegion = $( "<div>", {
2325 role: "status",
2326 "aria-live": "assertive",
2327 "aria-relevant": "additions"
2328 } )
2329 .appendTo( this.document[ 0 ].body );
2330
2331 this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
2332
2333 // Turning off autocomplete prevents the browser from remembering the
2334 // value when navigating through history, so we re-enable autocomplete
2335 // if the page is unloaded before the widget is destroyed. #7790
2336 this._on( this.window, {
2337 beforeunload: function() {
2338 this.element.removeAttr( "autocomplete" );
2339 }
2340 } );
2341 },
2342
2343 _destroy: function() {
2344 clearTimeout( this.searching );
2345 this.element.removeAttr( "autocomplete" );
2346 this.menu.element.remove();
2347 this.liveRegion.remove();
2348 },
2349
2350 _setOption: function( key, value ) {
2351 this._super( key, value );
2352 if ( key === "source" ) {
2353 this._initSource();
2354 }
2355 if ( key === "appendTo" ) {
2356 this.menu.element.appendTo( this._appendTo() );
2357 }
2358 if ( key === "disabled" && value && this.xhr ) {
2359 this.xhr.abort();
2360 }
2361 },
2362
2363 _isEventTargetInWidget: function( event ) {
2364 var menuElement = this.menu.element[ 0 ];
2365
2366 return event.target === this.element[ 0 ] ||
2367 event.target === menuElement ||
2368 $.contains( menuElement, event.target );
2369 },
2370
2371 _closeOnClickOutside: function( event ) {
2372 if ( !this._isEventTargetInWidget( event ) ) {
2373 this.close();
2374 }
2375 },
2376
2377 _appendTo: function() {
2378 var element = this.options.appendTo;
2379
2380 if ( element ) {
2381 element = element.jquery || element.nodeType ?
2382 $( element ) :
2383 this.document.find( element ).eq( 0 );
2384 }
2385
2386 if ( !element || !element[ 0 ] ) {
2387 element = this.element.closest( ".ui-front, dialog" );
2388 }
2389
2390 if ( !element.length ) {
2391 element = this.document[ 0 ].body;
2392 }
2393
2394 return element;
2395 },
2396
2397 _initSource: function() {
2398 var array, url,
2399 that = this;
2400 if ( $.isArray( this.options.source ) ) {
2401 array = this.options.source;
2402 this.source = function( request, response ) {
2403 response( $.ui.autocomplete.filter( array, request.term ) );
2404 };
2405 } else if ( typeof this.options.source === "string" ) {
2406 url = this.options.source;
2407 this.source = function( request, response ) {
2408 if ( that.xhr ) {
2409 that.xhr.abort();
2410 }
2411 that.xhr = $.ajax( {
2412 url: url,
2413 data: request,
2414 dataType: "json",
2415 success: function( data ) {
2416 response( data );
2417 },
2418 error: function() {
2419 response( [] );
2420 }
2421 } );
2422 };
2423 } else {
2424 this.source = this.options.source;
2425 }
2426 },
2427
2428 _searchTimeout: function( event ) {
2429 clearTimeout( this.searching );
2430 this.searching = this._delay( function() {
2431
2432 // Search if the value has changed, or if the user retypes the same value (see #7434)
2433 var equalValues = this.term === this._value(),
2434 menuVisible = this.menu.element.is( ":visible" ),
2435 modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
2436
2437 if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
2438 this.selectedItem = null;
2439 this.search( null, event );
2440 }
2441 }, this.options.delay );
2442 },
2443
2444 search: function( value, event ) {
2445 value = value != null ? value : this._value();
2446
2447 // Always save the actual value, not the one passed as an argument
2448 this.term = this._value();
2449
2450 if ( value.length < this.options.minLength ) {
2451 return this.close( event );
2452 }
2453
2454 if ( this._trigger( "search", event ) === false ) {
2455 return;
2456 }
2457
2458 return this._search( value );
2459 },
2460
2461 _search: function( value ) {
2462 this.pending++;
2463 this._addClass( "ui-autocomplete-loading" );
2464 this.cancelSearch = false;
2465
2466 this.source( { term: value }, this._response() );
2467 },
2468
2469 _response: function() {
2470 var index = ++this.requestIndex;
2471
2472 return $.proxy( function( content ) {
2473 if ( index === this.requestIndex ) {
2474 this.__response( content );
2475 }
2476
2477 this.pending--;
2478 if ( !this.pending ) {
2479 this._removeClass( "ui-autocomplete-loading" );
2480 }
2481 }, this );
2482 },
2483
2484 __response: function( content ) {
2485 if ( content ) {
2486 content = this._normalize( content );
2487 }
2488 this._trigger( "response", null, { content: content } );
2489 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
2490 this._suggest( content );
2491 this._trigger( "open" );
2492 } else {
2493
2494 // use ._close() instead of .close() so we don't cancel future searches
2495 this._close();
2496 }
2497 },
2498
2499 close: function( event ) {
2500 this.cancelSearch = true;
2501 this._close( event );
2502 },
2503
2504 _close: function( event ) {
2505
2506 // Remove the handler that closes the menu on outside clicks
2507 this._off( this.document, "mousedown" );
2508
2509 if ( this.menu.element.is( ":visible" ) ) {
2510 this.menu.element.hide();
2511 this.menu.blur();
2512 this.isNewMenu = true;
2513 this._trigger( "close", event );
2514 }
2515 },
2516
2517 _change: function( event ) {
2518 if ( this.previous !== this._value() ) {
2519 this._trigger( "change", event, { item: this.selectedItem } );
2520 }
2521 },
2522
2523 _normalize: function( items ) {
2524
2525 // assume all items have the right format when the first item is complete
2526 if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
2527 return items;
2528 }
2529 return $.map( items, function( item ) {
2530 if ( typeof item === "string" ) {
2531 return {
2532 label: item,
2533 value: item
2534 };
2535 }
2536 return $.extend( {}, item, {
2537 label: item.label || item.value,
2538 value: item.value || item.label
2539 } );
2540 } );
2541 },
2542
2543 _suggest: function( items ) {
2544 var ul = this.menu.element.empty();
2545 this._renderMenu( ul, items );
2546 this.isNewMenu = true;
2547 this.menu.refresh();
2548
2549 // Size and position menu
2550 ul.show();
2551 this._resizeMenu();
2552 ul.position( $.extend( {
2553 of: this.element
2554 }, this.options.position ) );
2555
2556 if ( this.options.autoFocus ) {
2557 this.menu.next();
2558 }
2559
2560 // Listen for interactions outside of the widget (#6642)
2561 this._on( this.document, {
2562 mousedown: "_closeOnClickOutside"
2563 } );
2564 },
2565
2566 _resizeMenu: function() {
2567 var ul = this.menu.element;
2568 ul.outerWidth( Math.max(
2569
2570 // Firefox wraps long text (possibly a rounding bug)
2571 // so we add 1px to avoid the wrapping (#7513)
2572 ul.width( "" ).outerWidth() + 1,
2573 this.element.outerWidth()
2574 ) );
2575 },
2576
2577 _renderMenu: function( ul, items ) {
2578 var that = this;
2579 $.each( items, function( index, item ) {
2580 that._renderItemData( ul, item );
2581 } );
2582 },
2583
2584 _renderItemData: function( ul, item ) {
2585 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
2586 },
2587
2588 _renderItem: function( ul, item ) {
2589 return $( "<li>" )
2590 .append( $( "<div>" ).text( item.label ) )
2591 .appendTo( ul );
2592 },
2593
2594 _move: function( direction, event ) {
2595 if ( !this.menu.element.is( ":visible" ) ) {
2596 this.search( null, event );
2597 return;
2598 }
2599 if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
2600 this.menu.isLastItem() && /^next/.test( direction ) ) {
2601
2602 if ( !this.isMultiLine ) {
2603 this._value( this.term );
2604 }
2605
2606 this.menu.blur();
2607 return;
2608 }
2609 this.menu[ direction ]( event );
2610 },
2611
2612 widget: function() {
2613 return this.menu.element;
2614 },
2615
2616 _value: function() {
2617 return this.valueMethod.apply( this.element, arguments );
2618 },
2619
2620 _keyEvent: function( keyEvent, event ) {
2621 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2622 this._move( keyEvent, event );
2623
2624 // Prevents moving cursor to beginning/end of the text field in some browsers
2625 event.preventDefault();
2626 }
2627 },
2628
2629 // Support: Chrome <=50
2630 // We should be able to just use this.element.prop( "isContentEditable" )
2631 // but hidden elements always report false in Chrome.
2632 // https://code.google.com/p/chromium/issues/detail?id=313082
2633 _isContentEditable: function( element ) {
2634 if ( !element.length ) {
2635 return false;
2636 }
2637
2638 var editable = element.prop( "contentEditable" );
2639
2640 if ( editable === "inherit" ) {
2641 return this._isContentEditable( element.parent() );
2642 }
2643
2644 return editable === "true";
2645 }
2646} );
2647
2648$.extend( $.ui.autocomplete, {
2649 escapeRegex: function( value ) {
2650 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
2651 },
2652 filter: function( array, term ) {
2653 var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
2654 return $.grep( array, function( value ) {
2655 return matcher.test( value.label || value.value || value );
2656 } );
2657 }
2658} );
2659
2660// Live region extension, adding a `messages` option
2661// NOTE: This is an experimental API. We are still investigating
2662// a full solution for string manipulation and internationalization.
2663$.widget( "ui.autocomplete", $.ui.autocomplete, {
2664 options: {
2665 messages: {
2666 noResults: "No search results.",
2667 results: function( amount ) {
2668 return amount + ( amount > 1 ? " results are" : " result is" ) +
2669 " available, use up and down arrow keys to navigate.";
2670 }
2671 }
2672 },
2673
2674 __response: function( content ) {
2675 var message;
2676 this._superApply( arguments );
2677 if ( this.options.disabled || this.cancelSearch ) {
2678 return;
2679 }
2680 if ( content && content.length ) {
2681 message = this.options.messages.results( content.length );
2682 } else {
2683 message = this.options.messages.noResults;
2684 }
2685 this.liveRegion.children().hide();
2686 $( "<div>" ).text( message ).appendTo( this.liveRegion );
2687 }
2688} );
2689
2690var widgetsAutocomplete = $.ui.autocomplete;
2691
2692
2693
2694
2695}));