Statistiques
| Révision:

fr_b02 / petri / doc / jquery / jquery-ui.js @ 5

Historique | Voir | Annoter | Télécharger (69,886 ko)

1
/*! jQuery UI - v1.12.1 - 2018-12-06
2
* http://jqueryui.com
3
* Includes: widget.js, position.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

    
20
var 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

    
40
var widgetUuid = 0;
41
var 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

    
743
var 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() {
765
var 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

    
775
function 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

    
782
function parseCss( element, property ) {
783
        return parseInt( $.css( element, property ), 10 ) || 0;
784
}
785

    
786
function 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

    
1231
var position = $.ui.position;
1232

    
1233

    
1234
/*!
1235
 * jQuery UI Keycode 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: Keycode
1244
//>>group: Core
1245
//>>description: Provide keycodes as keynames
1246
//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
1247

    
1248

    
1249
var keycode = $.ui.keyCode = {
1250
        BACKSPACE: 8,
1251
        COMMA: 188,
1252
        DELETE: 46,
1253
        DOWN: 40,
1254
        END: 35,
1255
        ENTER: 13,
1256
        ESCAPE: 27,
1257
        HOME: 36,
1258
        LEFT: 37,
1259
        PAGE_DOWN: 34,
1260
        PAGE_UP: 33,
1261
        PERIOD: 190,
1262
        RIGHT: 39,
1263
        SPACE: 32,
1264
        TAB: 9,
1265
        UP: 38
1266
};
1267

    
1268

    
1269
/*!
1270
 * jQuery UI Unique ID 1.12.1
1271
 * http://jqueryui.com
1272
 *
1273
 * Copyright jQuery Foundation and other contributors
1274
 * Released under the MIT license.
1275
 * http://jquery.org/license
1276
 */
1277

    
1278
//>>label: uniqueId
1279
//>>group: Core
1280
//>>description: Functions to generate and remove uniqueId's
1281
//>>docs: http://api.jqueryui.com/uniqueId/
1282

    
1283

    
1284

    
1285
var uniqueId = $.fn.extend( {
1286
        uniqueId: ( function() {
1287
                var uuid = 0;
1288

    
1289
                return function() {
1290
                        return this.each( function() {
1291
                                if ( !this.id ) {
1292
                                        this.id = "ui-id-" + ( ++uuid );
1293
                                }
1294
                        } );
1295
                };
1296
        } )(),
1297

    
1298
        removeUniqueId: function() {
1299
                return this.each( function() {
1300
                        if ( /^ui-id-\d+$/.test( this.id ) ) {
1301
                                $( this ).removeAttr( "id" );
1302
                        }
1303
                } );
1304
        }
1305
} );
1306

    
1307

    
1308

    
1309
var safeActiveElement = $.ui.safeActiveElement = function( document ) {
1310
        var activeElement;
1311

    
1312
        // Support: IE 9 only
1313
        // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
1314
        try {
1315
                activeElement = document.activeElement;
1316
        } catch ( error ) {
1317
                activeElement = document.body;
1318
        }
1319

    
1320
        // Support: IE 9 - 11 only
1321
        // IE may return null instead of an element
1322
        // Interestingly, this only seems to occur when NOT in an iframe
1323
        if ( !activeElement ) {
1324
                activeElement = document.body;
1325
        }
1326

    
1327
        // Support: IE 11 only
1328
        // IE11 returns a seemingly empty object in some cases when accessing
1329
        // document.activeElement from an <iframe>
1330
        if ( !activeElement.nodeName ) {
1331
                activeElement = document.body;
1332
        }
1333

    
1334
        return activeElement;
1335
};
1336

    
1337

    
1338
/*!
1339
 * jQuery UI Menu 1.12.1
1340
 * http://jqueryui.com
1341
 *
1342
 * Copyright jQuery Foundation and other contributors
1343
 * Released under the MIT license.
1344
 * http://jquery.org/license
1345
 */
1346

    
1347
//>>label: Menu
1348
//>>group: Widgets
1349
//>>description: Creates nestable menus.
1350
//>>docs: http://api.jqueryui.com/menu/
1351
//>>demos: http://jqueryui.com/menu/
1352
//>>css.structure: ../../themes/base/core.css
1353
//>>css.structure: ../../themes/base/menu.css
1354
//>>css.theme: ../../themes/base/theme.css
1355

    
1356

    
1357

    
1358
var widgetsMenu = $.widget( "ui.menu", {
1359
        version: "1.12.1",
1360
        defaultElement: "<ul>",
1361
        delay: 300,
1362
        options: {
1363
                icons: {
1364
                        submenu: "ui-icon-caret-1-e"
1365
                },
1366
                items: "> *",
1367
                menus: "ul",
1368
                position: {
1369
                        my: "left top",
1370
                        at: "right top"
1371
                },
1372
                role: "menu",
1373

    
1374
                // Callbacks
1375
                blur: null,
1376
                focus: null,
1377
                select: null
1378
        },
1379

    
1380
        _create: function() {
1381
                this.activeMenu = this.element;
1382

    
1383
                // Flag used to prevent firing of the click handler
1384
                // as the event bubbles up through nested menus
1385
                this.mouseHandled = false;
1386
                this.element
1387
                        .uniqueId()
1388
                        .attr( {
1389
                                role: this.options.role,
1390
                                tabIndex: 0
1391
                        } );
1392

    
1393
                this._addClass( "ui-menu", "ui-widget ui-widget-content" );
1394
                this._on( {
1395

    
1396
                        // Prevent focus from sticking to links inside menu after clicking
1397
                        // them (focus should always stay on UL during navigation).
1398
                        "mousedown .ui-menu-item": function( event ) {
1399
                                event.preventDefault();
1400
                        },
1401
                        "click .ui-menu-item": function( event ) {
1402
                                var target = $( event.target );
1403
                                var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
1404
                                if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
1405
                                        this.select( event );
1406

    
1407
                                        // Only set the mouseHandled flag if the event will bubble, see #9469.
1408
                                        if ( !event.isPropagationStopped() ) {
1409
                                                this.mouseHandled = true;
1410
                                        }
1411

    
1412
                                        // Open submenu on click
1413
                                        if ( target.has( ".ui-menu" ).length ) {
1414
                                                this.expand( event );
1415
                                        } else if ( !this.element.is( ":focus" ) &&
1416
                                                        active.closest( ".ui-menu" ).length ) {
1417

    
1418
                                                // Redirect focus to the menu
1419
                                                this.element.trigger( "focus", [ true ] );
1420

    
1421
                                                // If the active item is on the top level, let it stay active.
1422
                                                // Otherwise, blur the active item since it is no longer visible.
1423
                                                if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
1424
                                                        clearTimeout( this.timer );
1425
                                                }
1426
                                        }
1427
                                }
1428
                        },
1429
                        "mouseenter .ui-menu-item": function( event ) {
1430

    
1431
                                // Ignore mouse events while typeahead is active, see #10458.
1432
                                // Prevents focusing the wrong item when typeahead causes a scroll while the mouse
1433
                                // is over an item in the menu
1434
                                if ( this.previousFilter ) {
1435
                                        return;
1436
                                }
1437

    
1438
                                var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
1439
                                        target = $( event.currentTarget );
1440

    
1441
                                // Ignore bubbled events on parent items, see #11641
1442
                                if ( actualTarget[ 0 ] !== target[ 0 ] ) {
1443
                                        return;
1444
                                }
1445

    
1446
                                // Remove ui-state-active class from siblings of the newly focused menu item
1447
                                // to avoid a jump caused by adjacent elements both having a class with a border
1448
                                this._removeClass( target.siblings().children( ".ui-state-active" ),
1449
                                        null, "ui-state-active" );
1450
                                this.focus( event, target );
1451
                        },
1452
                        mouseleave: "collapseAll",
1453
                        "mouseleave .ui-menu": "collapseAll",
1454
                        focus: function( event, keepActiveItem ) {
1455

    
1456
                                // If there's already an active item, keep it active
1457
                                // If not, activate the first item
1458
                                var item = this.active || this.element.find( this.options.items ).eq( 0 );
1459

    
1460
                                if ( !keepActiveItem ) {
1461
                                        this.focus( event, item );
1462
                                }
1463
                        },
1464
                        blur: function( event ) {
1465
                                this._delay( function() {
1466
                                        var notContained = !$.contains(
1467
                                                this.element[ 0 ],
1468
                                                $.ui.safeActiveElement( this.document[ 0 ] )
1469
                                        );
1470
                                        if ( notContained ) {
1471
                                                this.collapseAll( event );
1472
                                        }
1473
                                } );
1474
                        },
1475
                        keydown: "_keydown"
1476
                } );
1477

    
1478
                this.refresh();
1479

    
1480
                // Clicks outside of a menu collapse any open menus
1481
                this._on( this.document, {
1482
                        click: function( event ) {
1483
                                if ( this._closeOnDocumentClick( event ) ) {
1484
                                        this.collapseAll( event );
1485
                                }
1486

    
1487
                                // Reset the mouseHandled flag
1488
                                this.mouseHandled = false;
1489
                        }
1490
                } );
1491
        },
1492

    
1493
        _destroy: function() {
1494
                var items = this.element.find( ".ui-menu-item" )
1495
                                .removeAttr( "role aria-disabled" ),
1496
                        submenus = items.children( ".ui-menu-item-wrapper" )
1497
                                .removeUniqueId()
1498
                                .removeAttr( "tabIndex role aria-haspopup" );
1499

    
1500
                // Destroy (sub)menus
1501
                this.element
1502
                        .removeAttr( "aria-activedescendant" )
1503
                        .find( ".ui-menu" ).addBack()
1504
                                .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
1505
                                        "tabIndex" )
1506
                                .removeUniqueId()
1507
                                .show();
1508

    
1509
                submenus.children().each( function() {
1510
                        var elem = $( this );
1511
                        if ( elem.data( "ui-menu-submenu-caret" ) ) {
1512
                                elem.remove();
1513
                        }
1514
                } );
1515
        },
1516

    
1517
        _keydown: function( event ) {
1518
                var match, prev, character, skip,
1519
                        preventDefault = true;
1520

    
1521
                switch ( event.keyCode ) {
1522
                case $.ui.keyCode.PAGE_UP:
1523
                        this.previousPage( event );
1524
                        break;
1525
                case $.ui.keyCode.PAGE_DOWN:
1526
                        this.nextPage( event );
1527
                        break;
1528
                case $.ui.keyCode.HOME:
1529
                        this._move( "first", "first", event );
1530
                        break;
1531
                case $.ui.keyCode.END:
1532
                        this._move( "last", "last", event );
1533
                        break;
1534
                case $.ui.keyCode.UP:
1535
                        this.previous( event );
1536
                        break;
1537
                case $.ui.keyCode.DOWN:
1538
                        this.next( event );
1539
                        break;
1540
                case $.ui.keyCode.LEFT:
1541
                        this.collapse( event );
1542
                        break;
1543
                case $.ui.keyCode.RIGHT:
1544
                        if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
1545
                                this.expand( event );
1546
                        }
1547
                        break;
1548
                case $.ui.keyCode.ENTER:
1549
                case $.ui.keyCode.SPACE:
1550
                        this._activate( event );
1551
                        break;
1552
                case $.ui.keyCode.ESCAPE:
1553
                        this.collapse( event );
1554
                        break;
1555
                default:
1556
                        preventDefault = false;
1557
                        prev = this.previousFilter || "";
1558
                        skip = false;
1559

    
1560
                        // Support number pad values
1561
                        character = event.keyCode >= 96 && event.keyCode <= 105 ?
1562
                                ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );
1563

    
1564
                        clearTimeout( this.filterTimer );
1565

    
1566
                        if ( character === prev ) {
1567
                                skip = true;
1568
                        } else {
1569
                                character = prev + character;
1570
                        }
1571

    
1572
                        match = this._filterMenuItems( character );
1573
                        match = skip && match.index( this.active.next() ) !== -1 ?
1574
                                this.active.nextAll( ".ui-menu-item" ) :
1575
                                match;
1576

    
1577
                        // If no matches on the current filter, reset to the last character pressed
1578
                        // to move down the menu to the first item that starts with that character
1579
                        if ( !match.length ) {
1580
                                character = String.fromCharCode( event.keyCode );
1581
                                match = this._filterMenuItems( character );
1582
                        }
1583

    
1584
                        if ( match.length ) {
1585
                                this.focus( event, match );
1586
                                this.previousFilter = character;
1587
                                this.filterTimer = this._delay( function() {
1588
                                        delete this.previousFilter;
1589
                                }, 1000 );
1590
                        } else {
1591
                                delete this.previousFilter;
1592
                        }
1593
                }
1594

    
1595
                if ( preventDefault ) {
1596
                        event.preventDefault();
1597
                }
1598
        },
1599

    
1600
        _activate: function( event ) {
1601
                if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
1602
                        if ( this.active.children( "[aria-haspopup='true']" ).length ) {
1603
                                this.expand( event );
1604
                        } else {
1605
                                this.select( event );
1606
                        }
1607
                }
1608
        },
1609

    
1610
        refresh: function() {
1611
                var menus, items, newSubmenus, newItems, newWrappers,
1612
                        that = this,
1613
                        icon = this.options.icons.submenu,
1614
                        submenus = this.element.find( this.options.menus );
1615

    
1616
                this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
1617

    
1618
                // Initialize nested menus
1619
                newSubmenus = submenus.filter( ":not(.ui-menu)" )
1620
                        .hide()
1621
                        .attr( {
1622
                                role: this.options.role,
1623
                                "aria-hidden": "true",
1624
                                "aria-expanded": "false"
1625
                        } )
1626
                        .each( function() {
1627
                                var menu = $( this ),
1628
                                        item = menu.prev(),
1629
                                        submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
1630

    
1631
                                that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
1632
                                item
1633
                                        .attr( "aria-haspopup", "true" )
1634
                                        .prepend( submenuCaret );
1635
                                menu.attr( "aria-labelledby", item.attr( "id" ) );
1636
                        } );
1637

    
1638
                this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
1639

    
1640
                menus = submenus.add( this.element );
1641
                items = menus.find( this.options.items );
1642

    
1643
                // Initialize menu-items containing spaces and/or dashes only as dividers
1644
                items.not( ".ui-menu-item" ).each( function() {
1645
                        var item = $( this );
1646
                        if ( that._isDivider( item ) ) {
1647
                                that._addClass( item, "ui-menu-divider", "ui-widget-content" );
1648
                        }
1649
                } );
1650

    
1651
                // Don't refresh list items that are already adapted
1652
                newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
1653
                newWrappers = newItems.children()
1654
                        .not( ".ui-menu" )
1655
                                .uniqueId()
1656
                                .attr( {
1657
                                        tabIndex: -1,
1658
                                        role: this._itemRole()
1659
                                } );
1660
                this._addClass( newItems, "ui-menu-item" )
1661
                        ._addClass( newWrappers, "ui-menu-item-wrapper" );
1662

    
1663
                // Add aria-disabled attribute to any disabled menu item
1664
                items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
1665

    
1666
                // If the active item has been removed, blur the menu
1667
                if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
1668
                        this.blur();
1669
                }
1670
        },
1671

    
1672
        _itemRole: function() {
1673
                return {
1674
                        menu: "menuitem",
1675
                        listbox: "option"
1676
                }[ this.options.role ];
1677
        },
1678

    
1679
        _setOption: function( key, value ) {
1680
                if ( key === "icons" ) {
1681
                        var icons = this.element.find( ".ui-menu-icon" );
1682
                        this._removeClass( icons, null, this.options.icons.submenu )
1683
                                ._addClass( icons, null, value.submenu );
1684
                }
1685
                this._super( key, value );
1686
        },
1687

    
1688
        _setOptionDisabled: function( value ) {
1689
                this._super( value );
1690

    
1691
                this.element.attr( "aria-disabled", String( value ) );
1692
                this._toggleClass( null, "ui-state-disabled", !!value );
1693
        },
1694

    
1695
        focus: function( event, item ) {
1696
                var nested, focused, activeParent;
1697
                this.blur( event, event && event.type === "focus" );
1698

    
1699
                this._scrollIntoView( item );
1700

    
1701
                this.active = item.first();
1702

    
1703
                focused = this.active.children( ".ui-menu-item-wrapper" );
1704
                this._addClass( focused, null, "ui-state-active" );
1705

    
1706
                // Only update aria-activedescendant if there's a role
1707
                // otherwise we assume focus is managed elsewhere
1708
                if ( this.options.role ) {
1709
                        this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
1710
                }
1711

    
1712
                // Highlight active parent menu item, if any
1713
                activeParent = this.active
1714
                        .parent()
1715
                                .closest( ".ui-menu-item" )
1716
                                        .children( ".ui-menu-item-wrapper" );
1717
                this._addClass( activeParent, null, "ui-state-active" );
1718

    
1719
                if ( event && event.type === "keydown" ) {
1720
                        this._close();
1721
                } else {
1722
                        this.timer = this._delay( function() {
1723
                                this._close();
1724
                        }, this.delay );
1725
                }
1726

    
1727
                nested = item.children( ".ui-menu" );
1728
                if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
1729
                        this._startOpening( nested );
1730
                }
1731
                this.activeMenu = item.parent();
1732

    
1733
                this._trigger( "focus", event, { item: item } );
1734
        },
1735

    
1736
        _scrollIntoView: function( item ) {
1737
                var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
1738
                if ( this._hasScroll() ) {
1739
                        borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
1740
                        paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
1741
                        offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
1742
                        scroll = this.activeMenu.scrollTop();
1743
                        elementHeight = this.activeMenu.height();
1744
                        itemHeight = item.outerHeight();
1745

    
1746
                        if ( offset < 0 ) {
1747
                                this.activeMenu.scrollTop( scroll + offset );
1748
                        } else if ( offset + itemHeight > elementHeight ) {
1749
                                this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
1750
                        }
1751
                }
1752
        },
1753

    
1754
        blur: function( event, fromFocus ) {
1755
                if ( !fromFocus ) {
1756
                        clearTimeout( this.timer );
1757
                }
1758

    
1759
                if ( !this.active ) {
1760
                        return;
1761
                }
1762

    
1763
                this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
1764
                        null, "ui-state-active" );
1765

    
1766
                this._trigger( "blur", event, { item: this.active } );
1767
                this.active = null;
1768
        },
1769

    
1770
        _startOpening: function( submenu ) {
1771
                clearTimeout( this.timer );
1772

    
1773
                // Don't open if already open fixes a Firefox bug that caused a .5 pixel
1774
                // shift in the submenu position when mousing over the caret icon
1775
                if ( submenu.attr( "aria-hidden" ) !== "true" ) {
1776
                        return;
1777
                }
1778

    
1779
                this.timer = this._delay( function() {
1780
                        this._close();
1781
                        this._open( submenu );
1782
                }, this.delay );
1783
        },
1784

    
1785
        _open: function( submenu ) {
1786
                var position = $.extend( {
1787
                        of: this.active
1788
                }, this.options.position );
1789

    
1790
                clearTimeout( this.timer );
1791
                this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
1792
                        .hide()
1793
                        .attr( "aria-hidden", "true" );
1794

    
1795
                submenu
1796
                        .show()
1797
                        .removeAttr( "aria-hidden" )
1798
                        .attr( "aria-expanded", "true" )
1799
                        .position( position );
1800
        },
1801

    
1802
        collapseAll: function( event, all ) {
1803
                clearTimeout( this.timer );
1804
                this.timer = this._delay( function() {
1805

    
1806
                        // If we were passed an event, look for the submenu that contains the event
1807
                        var currentMenu = all ? this.element :
1808
                                $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
1809

    
1810
                        // If we found no valid submenu ancestor, use the main menu to close all
1811
                        // sub menus anyway
1812
                        if ( !currentMenu.length ) {
1813
                                currentMenu = this.element;
1814
                        }
1815

    
1816
                        this._close( currentMenu );
1817

    
1818
                        this.blur( event );
1819

    
1820
                        // Work around active item staying active after menu is blurred
1821
                        this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );
1822

    
1823
                        this.activeMenu = currentMenu;
1824
                }, this.delay );
1825
        },
1826

    
1827
        // With no arguments, closes the currently active menu - if nothing is active
1828
        // it closes all menus.  If passed an argument, it will search for menus BELOW
1829
        _close: function( startMenu ) {
1830
                if ( !startMenu ) {
1831
                        startMenu = this.active ? this.active.parent() : this.element;
1832
                }
1833

    
1834
                startMenu.find( ".ui-menu" )
1835
                        .hide()
1836
                        .attr( "aria-hidden", "true" )
1837
                        .attr( "aria-expanded", "false" );
1838
        },
1839

    
1840
        _closeOnDocumentClick: function( event ) {
1841
                return !$( event.target ).closest( ".ui-menu" ).length;
1842
        },
1843

    
1844
        _isDivider: function( item ) {
1845

    
1846
                // Match hyphen, em dash, en dash
1847
                return !/[^\-\u2014\u2013\s]/.test( item.text() );
1848
        },
1849

    
1850
        collapse: function( event ) {
1851
                var newItem = this.active &&
1852
                        this.active.parent().closest( ".ui-menu-item", this.element );
1853
                if ( newItem && newItem.length ) {
1854
                        this._close();
1855
                        this.focus( event, newItem );
1856
                }
1857
        },
1858

    
1859
        expand: function( event ) {
1860
                var newItem = this.active &&
1861
                        this.active
1862
                                .children( ".ui-menu " )
1863
                                        .find( this.options.items )
1864
                                                .first();
1865

    
1866
                if ( newItem && newItem.length ) {
1867
                        this._open( newItem.parent() );
1868

    
1869
                        // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
1870
                        this._delay( function() {
1871
                                this.focus( event, newItem );
1872
                        } );
1873
                }
1874
        },
1875

    
1876
        next: function( event ) {
1877
                this._move( "next", "first", event );
1878
        },
1879

    
1880
        previous: function( event ) {
1881
                this._move( "prev", "last", event );
1882
        },
1883

    
1884
        isFirstItem: function() {
1885
                return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
1886
        },
1887

    
1888
        isLastItem: function() {
1889
                return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
1890
        },
1891

    
1892
        _move: function( direction, filter, event ) {
1893
                var next;
1894
                if ( this.active ) {
1895
                        if ( direction === "first" || direction === "last" ) {
1896
                                next = this.active
1897
                                        [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
1898
                                        .eq( -1 );
1899
                        } else {
1900
                                next = this.active
1901
                                        [ direction + "All" ]( ".ui-menu-item" )
1902
                                        .eq( 0 );
1903
                        }
1904
                }
1905
                if ( !next || !next.length || !this.active ) {
1906
                        next = this.activeMenu.find( this.options.items )[ filter ]();
1907
                }
1908

    
1909
                this.focus( event, next );
1910
        },
1911

    
1912
        nextPage: function( event ) {
1913
                var item, base, height;
1914

    
1915
                if ( !this.active ) {
1916
                        this.next( event );
1917
                        return;
1918
                }
1919
                if ( this.isLastItem() ) {
1920
                        return;
1921
                }
1922
                if ( this._hasScroll() ) {
1923
                        base = this.active.offset().top;
1924
                        height = this.element.height();
1925
                        this.active.nextAll( ".ui-menu-item" ).each( function() {
1926
                                item = $( this );
1927
                                return item.offset().top - base - height < 0;
1928
                        } );
1929

    
1930
                        this.focus( event, item );
1931
                } else {
1932
                        this.focus( event, this.activeMenu.find( this.options.items )
1933
                                [ !this.active ? "first" : "last" ]() );
1934
                }
1935
        },
1936

    
1937
        previousPage: function( event ) {
1938
                var item, base, height;
1939
                if ( !this.active ) {
1940
                        this.next( event );
1941
                        return;
1942
                }
1943
                if ( this.isFirstItem() ) {
1944
                        return;
1945
                }
1946
                if ( this._hasScroll() ) {
1947
                        base = this.active.offset().top;
1948
                        height = this.element.height();
1949
                        this.active.prevAll( ".ui-menu-item" ).each( function() {
1950
                                item = $( this );
1951
                                return item.offset().top - base + height > 0;
1952
                        } );
1953

    
1954
                        this.focus( event, item );
1955
                } else {
1956
                        this.focus( event, this.activeMenu.find( this.options.items ).first() );
1957
                }
1958
        },
1959

    
1960
        _hasScroll: function() {
1961
                return this.element.outerHeight() < this.element.prop( "scrollHeight" );
1962
        },
1963

    
1964
        select: function( event ) {
1965

    
1966
                // TODO: It should never be possible to not have an active item at this
1967
                // point, but the tests don't trigger mouseenter before click.
1968
                this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
1969
                var ui = { item: this.active };
1970
                if ( !this.active.has( ".ui-menu" ).length ) {
1971
                        this.collapseAll( event, true );
1972
                }
1973
                this._trigger( "select", event, ui );
1974
        },
1975

    
1976
        _filterMenuItems: function( character ) {
1977
                var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
1978
                        regex = new RegExp( "^" + escapedCharacter, "i" );
1979

    
1980
                return this.activeMenu
1981
                        .find( this.options.items )
1982

    
1983
                                // Only match on items, not dividers or other content (#10571)
1984
                                .filter( ".ui-menu-item" )
1985
                                        .filter( function() {
1986
                                                return regex.test(
1987
                                                        $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
1988
                                        } );
1989
        }
1990
} );
1991

    
1992

    
1993
/*!
1994
 * jQuery UI Autocomplete 1.12.1
1995
 * http://jqueryui.com
1996
 *
1997
 * Copyright jQuery Foundation and other contributors
1998
 * Released under the MIT license.
1999
 * http://jquery.org/license
2000
 */
2001

    
2002
//>>label: Autocomplete
2003
//>>group: Widgets
2004
//>>description: Lists suggested words as the user is typing.
2005
//>>docs: http://api.jqueryui.com/autocomplete/
2006
//>>demos: http://jqueryui.com/autocomplete/
2007
//>>css.structure: ../../themes/base/core.css
2008
//>>css.structure: ../../themes/base/autocomplete.css
2009
//>>css.theme: ../../themes/base/theme.css
2010

    
2011

    
2012

    
2013
$.widget( "ui.autocomplete", {
2014
        version: "1.12.1",
2015
        defaultElement: "<input>",
2016
        options: {
2017
                appendTo: null,
2018
                autoFocus: false,
2019
                delay: 300,
2020
                minLength: 1,
2021
                position: {
2022
                        my: "left top",
2023
                        at: "left bottom",
2024
                        collision: "none"
2025
                },
2026
                source: null,
2027

    
2028
                // Callbacks
2029
                change: null,
2030
                close: null,
2031
                focus: null,
2032
                open: null,
2033
                response: null,
2034
                search: null,
2035
                select: null
2036
        },
2037

    
2038
        requestIndex: 0,
2039
        pending: 0,
2040

    
2041
        _create: function() {
2042

    
2043
                // Some browsers only repeat keydown events, not keypress events,
2044
                // so we use the suppressKeyPress flag to determine if we've already
2045
                // handled the keydown event. #7269
2046
                // Unfortunately the code for & in keypress is the same as the up arrow,
2047
                // so we use the suppressKeyPressRepeat flag to avoid handling keypress
2048
                // events when we know the keydown event was used to modify the
2049
                // search term. #7799
2050
                var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
2051
                        nodeName = this.element[ 0 ].nodeName.toLowerCase(),
2052
                        isTextarea = nodeName === "textarea",
2053
                        isInput = nodeName === "input";
2054

    
2055
                // Textareas are always multi-line
2056
                // Inputs are always single-line, even if inside a contentEditable element
2057
                // IE also treats inputs as contentEditable
2058
                // All other element types are determined by whether or not they're contentEditable
2059
                this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
2060

    
2061
                this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
2062
                this.isNewMenu = true;
2063

    
2064
                this._addClass( "ui-autocomplete-input" );
2065
                this.element.attr( "autocomplete", "off" );
2066

    
2067
                this._on( this.element, {
2068
                        keydown: function( event ) {
2069
                                if ( this.element.prop( "readOnly" ) ) {
2070
                                        suppressKeyPress = true;
2071
                                        suppressInput = true;
2072
                                        suppressKeyPressRepeat = true;
2073
                                        return;
2074
                                }
2075

    
2076
                                suppressKeyPress = false;
2077
                                suppressInput = false;
2078
                                suppressKeyPressRepeat = false;
2079
                                var keyCode = $.ui.keyCode;
2080
                                switch ( event.keyCode ) {
2081
                                case keyCode.PAGE_UP:
2082
                                        suppressKeyPress = true;
2083
                                        this._move( "previousPage", event );
2084
                                        break;
2085
                                case keyCode.PAGE_DOWN:
2086
                                        suppressKeyPress = true;
2087
                                        this._move( "nextPage", event );
2088
                                        break;
2089
                                case keyCode.UP:
2090
                                        suppressKeyPress = true;
2091
                                        this._keyEvent( "previous", event );
2092
                                        break;
2093
                                case keyCode.DOWN:
2094
                                        suppressKeyPress = true;
2095
                                        this._keyEvent( "next", event );
2096
                                        break;
2097
                                case keyCode.ENTER:
2098

    
2099
                                        // when menu is open and has focus
2100
                                        if ( this.menu.active ) {
2101

    
2102
                                                // #6055 - Opera still allows the keypress to occur
2103
                                                // which causes forms to submit
2104
                                                suppressKeyPress = true;
2105
                                                event.preventDefault();
2106
                                                this.menu.select( event );
2107
                                        }
2108
                                        break;
2109
                                case keyCode.TAB:
2110
                                        if ( this.menu.active ) {
2111
                                                this.menu.select( event );
2112
                                        }
2113
                                        break;
2114
                                case keyCode.ESCAPE:
2115
                                        if ( this.menu.element.is( ":visible" ) ) {
2116
                                                if ( !this.isMultiLine ) {
2117
                                                        this._value( this.term );
2118
                                                }
2119
                                                this.close( event );
2120

    
2121
                                                // Different browsers have different default behavior for escape
2122
                                                // Single press can mean undo or clear
2123
                                                // Double press in IE means clear the whole form
2124
                                                event.preventDefault();
2125
                                        }
2126
                                        break;
2127
                                default:
2128
                                        suppressKeyPressRepeat = true;
2129

    
2130
                                        // search timeout should be triggered before the input value is changed
2131
                                        this._searchTimeout( event );
2132
                                        break;
2133
                                }
2134
                        },
2135
                        keypress: function( event ) {
2136
                                if ( suppressKeyPress ) {
2137
                                        suppressKeyPress = false;
2138
                                        if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2139
                                                event.preventDefault();
2140
                                        }
2141
                                        return;
2142
                                }
2143
                                if ( suppressKeyPressRepeat ) {
2144
                                        return;
2145
                                }
2146

    
2147
                                // Replicate some key handlers to allow them to repeat in Firefox and Opera
2148
                                var keyCode = $.ui.keyCode;
2149
                                switch ( event.keyCode ) {
2150
                                case keyCode.PAGE_UP:
2151
                                        this._move( "previousPage", event );
2152
                                        break;
2153
                                case keyCode.PAGE_DOWN:
2154
                                        this._move( "nextPage", event );
2155
                                        break;
2156
                                case keyCode.UP:
2157
                                        this._keyEvent( "previous", event );
2158
                                        break;
2159
                                case keyCode.DOWN:
2160
                                        this._keyEvent( "next", event );
2161
                                        break;
2162
                                }
2163
                        },
2164
                        input: function( event ) {
2165
                                if ( suppressInput ) {
2166
                                        suppressInput = false;
2167
                                        event.preventDefault();
2168
                                        return;
2169
                                }
2170
                                this._searchTimeout( event );
2171
                        },
2172
                        focus: function() {
2173
                                this.selectedItem = null;
2174
                                this.previous = this._value();
2175
                        },
2176
                        blur: function( event ) {
2177
                                if ( this.cancelBlur ) {
2178
                                        delete this.cancelBlur;
2179
                                        return;
2180
                                }
2181

    
2182
                                clearTimeout( this.searching );
2183
                                this.close( event );
2184
                                this._change( event );
2185
                        }
2186
                } );
2187

    
2188
                this._initSource();
2189
                this.menu = $( "<ul>" )
2190
                        .appendTo( this._appendTo() )
2191
                        .menu( {
2192

    
2193
                                // disable ARIA support, the live region takes care of that
2194
                                role: null
2195
                        } )
2196
                        .hide()
2197
                        .menu( "instance" );
2198

    
2199
                this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
2200
                this._on( this.menu.element, {
2201
                        mousedown: function( event ) {
2202

    
2203
                                // prevent moving focus out of the text field
2204
                                event.preventDefault();
2205

    
2206
                                // IE doesn't prevent moving focus even with event.preventDefault()
2207
                                // so we set a flag to know when we should ignore the blur event
2208
                                this.cancelBlur = true;
2209
                                this._delay( function() {
2210
                                        delete this.cancelBlur;
2211

    
2212
                                        // Support: IE 8 only
2213
                                        // Right clicking a menu item or selecting text from the menu items will
2214
                                        // result in focus moving out of the input. However, we've already received
2215
                                        // and ignored the blur event because of the cancelBlur flag set above. So
2216
                                        // we restore focus to ensure that the menu closes properly based on the user's
2217
                                        // next actions.
2218
                                        if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
2219
                                                this.element.trigger( "focus" );
2220
                                        }
2221
                                } );
2222
                        },
2223
                        menufocus: function( event, ui ) {
2224
                                var label, item;
2225

    
2226
                                // support: Firefox
2227
                                // Prevent accidental activation of menu items in Firefox (#7024 #9118)
2228
                                if ( this.isNewMenu ) {
2229
                                        this.isNewMenu = false;
2230
                                        if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
2231
                                                this.menu.blur();
2232

    
2233
                                                this.document.one( "mousemove", function() {
2234
                                                        $( event.target ).trigger( event.originalEvent );
2235
                                                } );
2236

    
2237
                                                return;
2238
                                        }
2239
                                }
2240

    
2241
                                item = ui.item.data( "ui-autocomplete-item" );
2242
                                if ( false !== this._trigger( "focus", event, { item: item } ) ) {
2243

    
2244
                                        // use value to match what will end up in the input, if it was a key event
2245
                                        if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
2246
                                                this._value( item.value );
2247
                                        }
2248
                                }
2249

    
2250
                                // Announce the value in the liveRegion
2251
                                label = ui.item.attr( "aria-label" ) || item.value;
2252
                                if ( label && $.trim( label ).length ) {
2253
                                        this.liveRegion.children().hide();
2254
                                        $( "<div>" ).text( label ).appendTo( this.liveRegion );
2255
                                }
2256
                        },
2257
                        menuselect: function( event, ui ) {
2258
                                var item = ui.item.data( "ui-autocomplete-item" ),
2259
                                        previous = this.previous;
2260

    
2261
                                // Only trigger when focus was lost (click on menu)
2262
                                if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
2263
                                        this.element.trigger( "focus" );
2264
                                        this.previous = previous;
2265

    
2266
                                        // #6109 - IE triggers two focus events and the second
2267
                                        // is asynchronous, so we need to reset the previous
2268
                                        // term synchronously and asynchronously :-(
2269
                                        this._delay( function() {
2270
                                                this.previous = previous;
2271
                                                this.selectedItem = item;
2272
                                        } );
2273
                                }
2274

    
2275
                                if ( false !== this._trigger( "select", event, { item: item } ) ) {
2276
                                        this._value( item.value );
2277
                                }
2278

    
2279
                                // reset the term after the select event
2280
                                // this allows custom select handling to work properly
2281
                                this.term = this._value();
2282

    
2283
                                this.close( event );
2284
                                this.selectedItem = item;
2285
                        }
2286
                } );
2287

    
2288
                this.liveRegion = $( "<div>", {
2289
                        role: "status",
2290
                        "aria-live": "assertive",
2291
                        "aria-relevant": "additions"
2292
                } )
2293
                        .appendTo( this.document[ 0 ].body );
2294

    
2295
                this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
2296

    
2297
                // Turning off autocomplete prevents the browser from remembering the
2298
                // value when navigating through history, so we re-enable autocomplete
2299
                // if the page is unloaded before the widget is destroyed. #7790
2300
                this._on( this.window, {
2301
                        beforeunload: function() {
2302
                                this.element.removeAttr( "autocomplete" );
2303
                        }
2304
                } );
2305
        },
2306

    
2307
        _destroy: function() {
2308
                clearTimeout( this.searching );
2309
                this.element.removeAttr( "autocomplete" );
2310
                this.menu.element.remove();
2311
                this.liveRegion.remove();
2312
        },
2313

    
2314
        _setOption: function( key, value ) {
2315
                this._super( key, value );
2316
                if ( key === "source" ) {
2317
                        this._initSource();
2318
                }
2319
                if ( key === "appendTo" ) {
2320
                        this.menu.element.appendTo( this._appendTo() );
2321
                }
2322
                if ( key === "disabled" && value && this.xhr ) {
2323
                        this.xhr.abort();
2324
                }
2325
        },
2326

    
2327
        _isEventTargetInWidget: function( event ) {
2328
                var menuElement = this.menu.element[ 0 ];
2329

    
2330
                return event.target === this.element[ 0 ] ||
2331
                        event.target === menuElement ||
2332
                        $.contains( menuElement, event.target );
2333
        },
2334

    
2335
        _closeOnClickOutside: function( event ) {
2336
                if ( !this._isEventTargetInWidget( event ) ) {
2337
                        this.close();
2338
                }
2339
        },
2340

    
2341
        _appendTo: function() {
2342
                var element = this.options.appendTo;
2343

    
2344
                if ( element ) {
2345
                        element = element.jquery || element.nodeType ?
2346
                                $( element ) :
2347
                                this.document.find( element ).eq( 0 );
2348
                }
2349

    
2350
                if ( !element || !element[ 0 ] ) {
2351
                        element = this.element.closest( ".ui-front, dialog" );
2352
                }
2353

    
2354
                if ( !element.length ) {
2355
                        element = this.document[ 0 ].body;
2356
                }
2357

    
2358
                return element;
2359
        },
2360

    
2361
        _initSource: function() {
2362
                var array, url,
2363
                        that = this;
2364
                if ( $.isArray( this.options.source ) ) {
2365
                        array = this.options.source;
2366
                        this.source = function( request, response ) {
2367
                                response( $.ui.autocomplete.filter( array, request.term ) );
2368
                        };
2369
                } else if ( typeof this.options.source === "string" ) {
2370
                        url = this.options.source;
2371
                        this.source = function( request, response ) {
2372
                                if ( that.xhr ) {
2373
                                        that.xhr.abort();
2374
                                }
2375
                                that.xhr = $.ajax( {
2376
                                        url: url,
2377
                                        data: request,
2378
                                        dataType: "json",
2379
                                        success: function( data ) {
2380
                                                response( data );
2381
                                        },
2382
                                        error: function() {
2383
                                                response( [] );
2384
                                        }
2385
                                } );
2386
                        };
2387
                } else {
2388
                        this.source = this.options.source;
2389
                }
2390
        },
2391

    
2392
        _searchTimeout: function( event ) {
2393
                clearTimeout( this.searching );
2394
                this.searching = this._delay( function() {
2395

    
2396
                        // Search if the value has changed, or if the user retypes the same value (see #7434)
2397
                        var equalValues = this.term === this._value(),
2398
                                menuVisible = this.menu.element.is( ":visible" ),
2399
                                modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
2400

    
2401
                        if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
2402
                                this.selectedItem = null;
2403
                                this.search( null, event );
2404
                        }
2405
                }, this.options.delay );
2406
        },
2407

    
2408
        search: function( value, event ) {
2409
                value = value != null ? value : this._value();
2410

    
2411
                // Always save the actual value, not the one passed as an argument
2412
                this.term = this._value();
2413

    
2414
                if ( value.length < this.options.minLength ) {
2415
                        return this.close( event );
2416
                }
2417

    
2418
                if ( this._trigger( "search", event ) === false ) {
2419
                        return;
2420
                }
2421

    
2422
                return this._search( value );
2423
        },
2424

    
2425
        _search: function( value ) {
2426
                this.pending++;
2427
                this._addClass( "ui-autocomplete-loading" );
2428
                this.cancelSearch = false;
2429

    
2430
                this.source( { term: value }, this._response() );
2431
        },
2432

    
2433
        _response: function() {
2434
                var index = ++this.requestIndex;
2435

    
2436
                return $.proxy( function( content ) {
2437
                        if ( index === this.requestIndex ) {
2438
                                this.__response( content );
2439
                        }
2440

    
2441
                        this.pending--;
2442
                        if ( !this.pending ) {
2443
                                this._removeClass( "ui-autocomplete-loading" );
2444
                        }
2445
                }, this );
2446
        },
2447

    
2448
        __response: function( content ) {
2449
                if ( content ) {
2450
                        content = this._normalize( content );
2451
                }
2452
                this._trigger( "response", null, { content: content } );
2453
                if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
2454
                        this._suggest( content );
2455
                        this._trigger( "open" );
2456
                } else {
2457

    
2458
                        // use ._close() instead of .close() so we don't cancel future searches
2459
                        this._close();
2460
                }
2461
        },
2462

    
2463
        close: function( event ) {
2464
                this.cancelSearch = true;
2465
                this._close( event );
2466
        },
2467

    
2468
        _close: function( event ) {
2469

    
2470
                // Remove the handler that closes the menu on outside clicks
2471
                this._off( this.document, "mousedown" );
2472

    
2473
                if ( this.menu.element.is( ":visible" ) ) {
2474
                        this.menu.element.hide();
2475
                        this.menu.blur();
2476
                        this.isNewMenu = true;
2477
                        this._trigger( "close", event );
2478
                }
2479
        },
2480

    
2481
        _change: function( event ) {
2482
                if ( this.previous !== this._value() ) {
2483
                        this._trigger( "change", event, { item: this.selectedItem } );
2484
                }
2485
        },
2486

    
2487
        _normalize: function( items ) {
2488

    
2489
                // assume all items have the right format when the first item is complete
2490
                if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
2491
                        return items;
2492
                }
2493
                return $.map( items, function( item ) {
2494
                        if ( typeof item === "string" ) {
2495
                                return {
2496
                                        label: item,
2497
                                        value: item
2498
                                };
2499
                        }
2500
                        return $.extend( {}, item, {
2501
                                label: item.label || item.value,
2502
                                value: item.value || item.label
2503
                        } );
2504
                } );
2505
        },
2506

    
2507
        _suggest: function( items ) {
2508
                var ul = this.menu.element.empty();
2509
                this._renderMenu( ul, items );
2510
                this.isNewMenu = true;
2511
                this.menu.refresh();
2512

    
2513
                // Size and position menu
2514
                ul.show();
2515
                this._resizeMenu();
2516
                ul.position( $.extend( {
2517
                        of: this.element
2518
                }, this.options.position ) );
2519

    
2520
                if ( this.options.autoFocus ) {
2521
                        this.menu.next();
2522
                }
2523

    
2524
                // Listen for interactions outside of the widget (#6642)
2525
                this._on( this.document, {
2526
                        mousedown: "_closeOnClickOutside"
2527
                } );
2528
        },
2529

    
2530
        _resizeMenu: function() {
2531
                var ul = this.menu.element;
2532
                ul.outerWidth( Math.max(
2533

    
2534
                        // Firefox wraps long text (possibly a rounding bug)
2535
                        // so we add 1px to avoid the wrapping (#7513)
2536
                        ul.width( "" ).outerWidth() + 1,
2537
                        this.element.outerWidth()
2538
                ) );
2539
        },
2540

    
2541
        _renderMenu: function( ul, items ) {
2542
                var that = this;
2543
                $.each( items, function( index, item ) {
2544
                        that._renderItemData( ul, item );
2545
                } );
2546
        },
2547

    
2548
        _renderItemData: function( ul, item ) {
2549
                return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
2550
        },
2551

    
2552
        _renderItem: function( ul, item ) {
2553
                return $( "<li>" )
2554
                        .append( $( "<div>" ).text( item.label ) )
2555
                        .appendTo( ul );
2556
        },
2557

    
2558
        _move: function( direction, event ) {
2559
                if ( !this.menu.element.is( ":visible" ) ) {
2560
                        this.search( null, event );
2561
                        return;
2562
                }
2563
                if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
2564
                                this.menu.isLastItem() && /^next/.test( direction ) ) {
2565

    
2566
                        if ( !this.isMultiLine ) {
2567
                                this._value( this.term );
2568
                        }
2569

    
2570
                        this.menu.blur();
2571
                        return;
2572
                }
2573
                this.menu[ direction ]( event );
2574
        },
2575

    
2576
        widget: function() {
2577
                return this.menu.element;
2578
        },
2579

    
2580
        _value: function() {
2581
                return this.valueMethod.apply( this.element, arguments );
2582
        },
2583

    
2584
        _keyEvent: function( keyEvent, event ) {
2585
                if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
2586
                        this._move( keyEvent, event );
2587

    
2588
                        // Prevents moving cursor to beginning/end of the text field in some browsers
2589
                        event.preventDefault();
2590
                }
2591
        },
2592

    
2593
        // Support: Chrome <=50
2594
        // We should be able to just use this.element.prop( "isContentEditable" )
2595
        // but hidden elements always report false in Chrome.
2596
        // https://code.google.com/p/chromium/issues/detail?id=313082
2597
        _isContentEditable: function( element ) {
2598
                if ( !element.length ) {
2599
                        return false;
2600
                }
2601

    
2602
                var editable = element.prop( "contentEditable" );
2603

    
2604
                if ( editable === "inherit" ) {
2605
                  return this._isContentEditable( element.parent() );
2606
                }
2607

    
2608
                return editable === "true";
2609
        }
2610
} );
2611

    
2612
$.extend( $.ui.autocomplete, {
2613
        escapeRegex: function( value ) {
2614
                return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
2615
        },
2616
        filter: function( array, term ) {
2617
                var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
2618
                return $.grep( array, function( value ) {
2619
                        return matcher.test( value.label || value.value || value );
2620
                } );
2621
        }
2622
} );
2623

    
2624
// Live region extension, adding a `messages` option
2625
// NOTE: This is an experimental API. We are still investigating
2626
// a full solution for string manipulation and internationalization.
2627
$.widget( "ui.autocomplete", $.ui.autocomplete, {
2628
        options: {
2629
                messages: {
2630
                        noResults: "No search results.",
2631
                        results: function( amount ) {
2632
                                return amount + ( amount > 1 ? " results are" : " result is" ) +
2633
                                        " available, use up and down arrow keys to navigate.";
2634
                        }
2635
                }
2636
        },
2637

    
2638
        __response: function( content ) {
2639
                var message;
2640
                this._superApply( arguments );
2641
                if ( this.options.disabled || this.cancelSearch ) {
2642
                        return;
2643
                }
2644
                if ( content && content.length ) {
2645
                        message = this.options.messages.results( content.length );
2646
                } else {
2647
                        message = this.options.messages.noResults;
2648
                }
2649
                this.liveRegion.children().hide();
2650
                $( "<div>" ).text( message ).appendTo( this.liveRegion );
2651
        }
2652
} );
2653

    
2654
var widgetsAutocomplete = $.ui.autocomplete;
2655

    
2656

    
2657

    
2658

    
2659
}));