Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('autocomplete-list', function (Y, NAME) {
2
 
3
/**
4
Traditional autocomplete dropdown list widget, just like Mom used to make.
5
 
6
@module autocomplete
7
@submodule autocomplete-list
8
**/
9
 
10
/**
11
Traditional autocomplete dropdown list widget, just like Mom used to make.
12
 
13
@class AutoCompleteList
14
@extends Widget
15
@uses AutoCompleteBase
16
@uses WidgetPosition
17
@uses WidgetPositionAlign
18
@constructor
19
@param {Object} config Configuration object.
20
**/
21
 
22
var Lang   = Y.Lang,
23
    Node   = Y.Node,
24
    YArray = Y.Array,
25
 
26
    // Whether or not we need an iframe shim.
27
    useShim = Y.UA.ie && Y.UA.ie < 7,
28
 
29
    // keyCode constants.
30
    KEY_TAB = 9,
31
 
32
    // String shorthand.
33
    _CLASS_ITEM        = '_CLASS_ITEM',
34
    _CLASS_ITEM_ACTIVE = '_CLASS_ITEM_ACTIVE',
35
    _CLASS_ITEM_HOVER  = '_CLASS_ITEM_HOVER',
36
    _SELECTOR_ITEM     = '_SELECTOR_ITEM',
37
 
38
    ACTIVE_ITEM      = 'activeItem',
39
    ALWAYS_SHOW_LIST = 'alwaysShowList',
40
    CIRCULAR         = 'circular',
41
    HOVERED_ITEM     = 'hoveredItem',
42
    ID               = 'id',
43
    ITEM             = 'item',
44
    LIST             = 'list',
45
    RESULT           = 'result',
46
    RESULTS          = 'results',
47
    VISIBLE          = 'visible',
48
    WIDTH            = 'width',
49
 
50
    // Event names.
51
    EVT_SELECT = 'select',
52
 
53
List = Y.Base.create('autocompleteList', Y.Widget, [
54
    Y.AutoCompleteBase,
55
    Y.WidgetPosition,
56
    Y.WidgetPositionAlign
57
], {
58
    // -- Prototype Properties -------------------------------------------------
59
    ARIA_TEMPLATE: '<div/>',
60
    ITEM_TEMPLATE: '<li/>',
61
    LIST_TEMPLATE: '<ul/>',
62
 
63
    // Widget automatically attaches delegated event handlers to everything in
64
    // Y.Node.DOM_EVENTS, including synthetic events. Since Widget's event
65
    // delegation won't work for the synthetic valuechange event, and since
66
    // it creates a name collision between the backcompat "valueChange" synth
67
    // event alias and AutoCompleteList's "valueChange" event for the "value"
68
    // attr, this hack is necessary in order to prevent Widget from attaching
69
    // valuechange handlers.
70
    UI_EVENTS: (function () {
71
        var uiEvents = Y.merge(Y.Node.DOM_EVENTS);
72
 
73
        delete uiEvents.valuechange;
74
        delete uiEvents.valueChange;
75
 
76
        return uiEvents;
77
    }()),
78
 
79
    // -- Lifecycle Prototype Methods ------------------------------------------
80
    initializer: function () {
81
        var inputNode = this.get('inputNode');
82
 
83
        if (!inputNode) {
84
            Y.error('No inputNode specified.');
85
            return;
86
        }
87
 
88
        this._inputNode  = inputNode;
89
        this._listEvents = [];
90
 
91
        // This ensures that the list is rendered inside the same parent as the
92
        // input node by default, which is necessary for proper ARIA support.
93
        this.DEF_PARENT_NODE = inputNode.get('parentNode');
94
 
95
        // Cache commonly used classnames and selectors for performance.
96
        this[_CLASS_ITEM]        = this.getClassName(ITEM);
97
        this[_CLASS_ITEM_ACTIVE] = this.getClassName(ITEM, 'active');
98
        this[_CLASS_ITEM_HOVER]  = this.getClassName(ITEM, 'hover');
99
        this[_SELECTOR_ITEM]     = '.' + this[_CLASS_ITEM];
100
 
101
        /**
102
        Fires when an autocomplete suggestion is selected from the list,
103
        typically via a keyboard action or mouse click.
104
 
105
        @event select
106
        @param {Node} itemNode List item node that was selected.
107
        @param {Object} result AutoComplete result object.
108
        @preventable _defSelectFn
109
        **/
110
        this.publish(EVT_SELECT, {
111
            defaultFn: this._defSelectFn
112
        });
113
    },
114
 
115
    destructor: function () {
116
        while (this._listEvents.length) {
117
            this._listEvents.pop().detach();
118
        }
119
 
120
        if (this._ariaNode) {
121
            this._ariaNode.remove().destroy(true);
122
        }
123
    },
124
 
125
    bindUI: function () {
126
        this._bindInput();
127
        this._bindList();
128
    },
129
 
130
    renderUI: function () {
131
        var ariaNode    = this._createAriaNode(),
132
            boundingBox = this.get('boundingBox'),
133
            contentBox  = this.get('contentBox'),
134
            inputNode   = this._inputNode,
135
            listNode    = this._createListNode(),
136
            parentNode  = inputNode.get('parentNode');
137
 
138
        inputNode.addClass(this.getClassName('input')).setAttrs({
139
            'aria-autocomplete': LIST,
140
            'aria-expanded'    : false,
141
            'aria-owns'        : listNode.get('id')
142
        });
143
 
144
        // ARIA node must be outside the widget or announcements won't be made
145
        // when the widget is hidden.
146
        parentNode.append(ariaNode);
147
 
148
        // Add an iframe shim for IE6.
149
        if (useShim) {
150
            boundingBox.plug(Y.Plugin.Shim);
151
        }
152
 
153
        this._ariaNode    = ariaNode;
154
        this._boundingBox = boundingBox;
155
        this._contentBox  = contentBox;
156
        this._listNode    = listNode;
157
        this._parentNode  = parentNode;
158
    },
159
 
160
    syncUI: function () {
161
        // No need to call _syncPosition() here; the other _sync methods will
162
        // call it when necessary.
163
        this._syncResults();
164
        this._syncVisibility();
165
    },
166
 
167
    // -- Public Prototype Methods ---------------------------------------------
168
 
169
    /**
170
    Hides the list, unless the `alwaysShowList` attribute is `true`.
171
 
172
    @method hide
173
    @see show
174
    @chainable
175
    **/
176
    hide: function () {
177
        return this.get(ALWAYS_SHOW_LIST) ? this : this.set(VISIBLE, false);
178
    },
179
 
180
    /**
181
    Selects the specified _itemNode_, or the current `activeItem` if _itemNode_
182
    is not specified.
183
 
184
    @method selectItem
185
    @param {Node} [itemNode] Item node to select.
186
    @param {EventFacade} [originEvent] Event that triggered the selection, if
187
        any.
188
    @chainable
189
    **/
190
    selectItem: function (itemNode, originEvent) {
191
        if (itemNode) {
192
            if (!itemNode.hasClass(this[_CLASS_ITEM])) {
193
                return this;
194
            }
195
        } else {
196
            itemNode = this.get(ACTIVE_ITEM);
197
 
198
            if (!itemNode) {
199
                return this;
200
            }
201
        }
202
 
203
        this.fire(EVT_SELECT, {
204
            itemNode   : itemNode,
205
            originEvent: originEvent || null,
206
            result     : itemNode.getData(RESULT)
207
        });
208
 
209
        return this;
210
    },
211
 
212
    // -- Protected Prototype Methods ------------------------------------------
213
 
214
    /**
215
    Activates the next item after the currently active item. If there is no next
216
    item and the `circular` attribute is `true`, focus will wrap back to the
217
    input node.
218
 
219
    @method _activateNextItem
220
    @chainable
221
    @protected
222
    **/
223
    _activateNextItem: function () {
224
        var item = this.get(ACTIVE_ITEM),
225
            nextItem;
226
 
227
        if (item) {
228
            nextItem = item.next(this[_SELECTOR_ITEM]) ||
229
                    (this.get(CIRCULAR) ? null : item);
230
        } else {
231
            nextItem = this._getFirstItemNode();
232
        }
233
 
234
        this.set(ACTIVE_ITEM, nextItem);
235
 
236
        return this;
237
    },
238
 
239
    /**
240
    Activates the item previous to the currently active item. If there is no
241
    previous item and the `circular` attribute is `true`, focus will wrap back
242
    to the input node.
243
 
244
    @method _activatePrevItem
245
    @chainable
246
    @protected
247
    **/
248
    _activatePrevItem: function () {
249
        var item     = this.get(ACTIVE_ITEM),
250
            prevItem = item ? item.previous(this[_SELECTOR_ITEM]) :
251
                    this.get(CIRCULAR) && this._getLastItemNode();
252
 
253
        this.set(ACTIVE_ITEM, prevItem || null);
254
 
255
        return this;
256
    },
257
 
258
    /**
259
    Appends the specified result _items_ to the list inside a new item node.
260
 
261
    @method _add
262
    @param {Array|Node|HTMLElement|String} items Result item or array of
263
        result items.
264
    @return {NodeList} Added nodes.
265
    @protected
266
    **/
267
    _add: function (items) {
268
        var itemNodes = [];
269
 
270
        YArray.each(Lang.isArray(items) ? items : [items], function (item) {
271
            itemNodes.push(this._createItemNode(item).setData(RESULT, item));
272
        }, this);
273
 
274
        itemNodes = Y.all(itemNodes);
275
        this._listNode.append(itemNodes.toFrag());
276
 
277
        return itemNodes;
278
    },
279
 
280
    /**
281
    Updates the ARIA live region with the specified message.
282
 
283
    @method _ariaSay
284
    @param {String} stringId String id (from the `strings` attribute) of the
285
        message to speak.
286
    @param {Object} [subs] Substitutions for placeholders in the string.
287
    @protected
288
    **/
289
    _ariaSay: function (stringId, subs) {
290
        var message = this.get('strings.' + stringId);
291
        this._ariaNode.set('text', subs ? Lang.sub(message, subs) : message);
292
    },
293
 
294
    /**
295
    Binds `inputNode` events and behavior.
296
 
297
    @method _bindInput
298
    @protected
299
    **/
300
    _bindInput: function () {
301
        var inputNode = this._inputNode,
302
            alignNode, alignWidth, tokenInput;
303
 
304
        // Null align means we can auto-align. Set align to false to prevent
305
        // auto-alignment, or a valid alignment config to customize the
306
        // alignment.
307
        if (this.get('align') === null) {
308
            // If this is a tokenInput, align with its bounding box.
309
            // Otherwise, align with the inputNode. Bit of a cheat.
310
            tokenInput = this.get('tokenInput');
311
            alignNode  = (tokenInput && tokenInput.get('boundingBox')) || inputNode;
312
 
313
            this.set('align', {
314
                node  : alignNode,
315
                points: ['tl', 'bl']
316
            });
317
 
318
            // If no width config is set, attempt to set the list's width to the
319
            // width of the alignment node. If the alignment node's width is
320
            // falsy, do nothing.
321
            if (!this.get(WIDTH) && (alignWidth = alignNode.get('offsetWidth'))) {
322
                this.set(WIDTH, alignWidth);
323
            }
324
        }
325
 
326
        // Attach inputNode events.
327
        this._listEvents = this._listEvents.concat([
328
            inputNode.after('blur',  this._afterListInputBlur, this),
329
            inputNode.after('focus', this._afterListInputFocus, this)
330
        ]);
331
    },
332
 
333
    /**
334
    Binds list events.
335
 
336
    @method _bindList
337
    @protected
338
    **/
339
    _bindList: function () {
340
        this._listEvents = this._listEvents.concat([
341
            Y.one('doc').after('click', this._afterDocClick, this),
342
            Y.one('win').after('windowresize', this._syncPosition, this),
343
 
344
            this.after({
345
                mouseover: this._afterMouseOver,
346
                mouseout : this._afterMouseOut,
347
 
348
                activeItemChange    : this._afterActiveItemChange,
349
                alwaysShowListChange: this._afterAlwaysShowListChange,
350
                hoveredItemChange   : this._afterHoveredItemChange,
351
                resultsChange       : this._afterResultsChange,
352
                visibleChange       : this._afterVisibleChange
353
            }),
354
 
355
            this._listNode.delegate('click', this._onItemClick,
356
                    this[_SELECTOR_ITEM], this)
357
        ]);
358
    },
359
 
360
    /**
361
    Clears the contents of the tray.
362
 
363
    @method _clear
364
    @protected
365
    **/
366
    _clear: function () {
367
        this.set(ACTIVE_ITEM, null);
368
        this._set(HOVERED_ITEM, null);
369
 
370
        this._listNode.get('children').remove(true);
371
    },
372
 
373
    /**
374
    Creates and returns an ARIA live region node.
375
 
376
    @method _createAriaNode
377
    @return {Node} ARIA node.
378
    @protected
379
    **/
380
    _createAriaNode: function () {
381
        var ariaNode = Node.create(this.ARIA_TEMPLATE);
382
 
383
        return ariaNode.addClass(this.getClassName('aria')).setAttrs({
384
            'aria-live': 'polite',
385
            role       : 'status'
386
        });
387
    },
388
 
389
    /**
390
    Creates and returns an item node with the specified _content_.
391
 
392
    @method _createItemNode
393
    @param {Object} result Result object.
394
    @return {Node} Item node.
395
    @protected
396
    **/
397
    _createItemNode: function (result) {
398
        var itemNode = Node.create(this.ITEM_TEMPLATE);
399
 
400
        return itemNode.addClass(this[_CLASS_ITEM]).setAttrs({
401
            id  : Y.stamp(itemNode),
402
            role: 'option'
403
        }).setAttribute('data-text', result.text).append(result.display);
404
    },
405
 
406
    /**
407
    Creates and returns a list node. If the `listNode` attribute is already set
408
    to an existing node, that node will be used.
409
 
410
    @method _createListNode
411
    @return {Node} List node.
412
    @protected
413
    **/
414
    _createListNode: function () {
415
        var listNode = this.get('listNode') || Node.create(this.LIST_TEMPLATE);
416
 
417
        listNode.addClass(this.getClassName(LIST)).setAttrs({
418
            id  : Y.stamp(listNode),
419
            role: 'listbox'
420
        });
421
 
422
        this._set('listNode', listNode);
423
        this.get('contentBox').append(listNode);
424
 
425
        return listNode;
426
    },
427
 
428
    /**
429
    Gets the first item node in the list, or `null` if the list is empty.
430
 
431
    @method _getFirstItemNode
432
    @return {Node|null}
433
    @protected
434
    **/
435
    _getFirstItemNode: function () {
436
        return this._listNode.one(this[_SELECTOR_ITEM]);
437
    },
438
 
439
    /**
440
    Gets the last item node in the list, or `null` if the list is empty.
441
 
442
    @method _getLastItemNode
443
    @return {Node|null}
444
    @protected
445
    **/
446
    _getLastItemNode: function () {
447
        return this._listNode.one(this[_SELECTOR_ITEM] + ':last-child');
448
    },
449
 
450
    /**
451
    Synchronizes the result list's position and alignment.
452
 
453
    @method _syncPosition
454
    @protected
455
    **/
456
    _syncPosition: function () {
457
        // Force WidgetPositionAlign to refresh its alignment.
458
        this._syncUIPosAlign();
459
 
460
        // Resize the IE6 iframe shim to match the list's dimensions.
461
        this._syncShim();
462
    },
463
 
464
    /**
465
    Synchronizes the results displayed in the list with those in the _results_
466
    argument, or with the `results` attribute if an argument is not provided.
467
 
468
    @method _syncResults
469
    @param {Array} [results] Results.
470
    @protected
471
    **/
472
    _syncResults: function (results) {
473
        if (!results) {
474
            results = this.get(RESULTS);
475
        }
476
 
477
        this._clear();
478
 
479
        if (results.length) {
480
            this._add(results);
481
            this._ariaSay('items_available');
482
        }
483
 
484
        this._syncPosition();
485
 
486
        if (this.get('activateFirstItem') && !this.get(ACTIVE_ITEM)) {
487
            this.set(ACTIVE_ITEM, this._getFirstItemNode());
488
        }
489
    },
490
 
491
    /**
492
    Synchronizes the size of the iframe shim used for IE6 and lower. In other
493
    browsers, this method is a noop.
494
 
495
    @method _syncShim
496
    @protected
497
    **/
498
    _syncShim: useShim ? function () {
499
        var shim = this._boundingBox.shim;
500
 
501
        if (shim) {
502
            shim.sync();
503
        }
504
    } : function () {},
505
 
506
    /**
507
    Synchronizes the visibility of the tray with the _visible_ argument, or with
508
    the `visible` attribute if an argument is not provided.
509
 
510
    @method _syncVisibility
511
    @param {Boolean} [visible] Visibility.
512
    @protected
513
    **/
514
    _syncVisibility: function (visible) {
515
        if (this.get(ALWAYS_SHOW_LIST)) {
516
            visible = true;
517
            this.set(VISIBLE, visible);
518
        }
519
 
520
        if (typeof visible === 'undefined') {
521
            visible = this.get(VISIBLE);
522
        }
523
 
524
        this._inputNode.set('aria-expanded', visible);
525
        this._boundingBox.set('aria-hidden', !visible);
526
 
527
        if (visible) {
528
            this._syncPosition();
529
        } else {
530
            this.set(ACTIVE_ITEM, null);
531
            this._set(HOVERED_ITEM, null);
532
 
533
            // Force a reflow to work around a glitch in IE6 and 7 where some of
534
            // the contents of the list will sometimes remain visible after the
535
            // container is hidden.
536
            this._boundingBox.get('offsetWidth');
537
        }
538
 
539
        // In some pages, IE7 fails to repaint the contents of the list after it
540
        // becomes visible. Toggling a bogus class on the body forces a repaint
541
        // that fixes the issue.
542
        if (Y.UA.ie === 7) {
543
            // Note: We don't actually need to use ClassNameManager here. This
544
            // class isn't applying any actual styles; it's just frobbing the
545
            // body element to force a repaint. The actual class name doesn't
546
            // really matter.
547
            Y.one('body')
548
                .addClass('yui3-ie7-sucks')
549
                .removeClass('yui3-ie7-sucks');
550
        }
551
    },
552
 
553
    // -- Protected Event Handlers ---------------------------------------------
554
 
555
    /**
556
    Handles `activeItemChange` events.
557
 
558
    @method _afterActiveItemChange
559
    @param {EventFacade} e
560
    @protected
561
    **/
562
    _afterActiveItemChange: function (e) {
563
        var inputNode = this._inputNode,
564
            newVal    = e.newVal,
565
            prevVal   = e.prevVal,
566
            node;
567
 
568
        // The previous item may have disappeared by the time this handler runs,
569
        // so we need to be careful.
570
        if (prevVal && prevVal._node) {
571
            prevVal.removeClass(this[_CLASS_ITEM_ACTIVE]);
572
        }
573
 
574
        if (newVal) {
575
            newVal.addClass(this[_CLASS_ITEM_ACTIVE]);
576
            inputNode.set('aria-activedescendant', newVal.get(ID));
577
        } else {
578
            inputNode.removeAttribute('aria-activedescendant');
579
        }
580
 
581
        if (this.get('scrollIntoView')) {
582
            node = newVal || inputNode;
583
 
584
            if (!node.inRegion(Y.DOM.viewportRegion(), true)
585
                    || !node.inRegion(this._contentBox, true)) {
586
 
587
                node.scrollIntoView();
588
            }
589
        }
590
    },
591
 
592
    /**
593
    Handles `alwaysShowListChange` events.
594
 
595
    @method _afterAlwaysShowListChange
596
    @param {EventFacade} e
597
    @protected
598
    **/
599
    _afterAlwaysShowListChange: function (e) {
600
        this.set(VISIBLE, e.newVal || this.get(RESULTS).length > 0);
601
    },
602
 
603
    /**
604
    Handles click events on the document. If the click is outside both the
605
    input node and the bounding box, the list will be hidden.
606
 
607
    @method _afterDocClick
608
    @param {EventFacade} e
609
    @protected
610
    @since 3.5.0
611
    **/
612
    _afterDocClick: function (e) {
613
        var boundingBox = this._boundingBox,
614
            target      = e.target;
615
 
616
        if (target !== this._inputNode && target !== boundingBox &&
617
                !target.ancestor('#' + boundingBox.get('id'), true)){
618
            this.hide();
619
        }
620
    },
621
 
622
    /**
623
    Handles `hoveredItemChange` events.
624
 
625
    @method _afterHoveredItemChange
626
    @param {EventFacade} e
627
    @protected
628
    **/
629
    _afterHoveredItemChange: function (e) {
630
        var newVal  = e.newVal,
631
            prevVal = e.prevVal;
632
 
633
        if (prevVal) {
634
            prevVal.removeClass(this[_CLASS_ITEM_HOVER]);
635
        }
636
 
637
        if (newVal) {
638
            newVal.addClass(this[_CLASS_ITEM_HOVER]);
639
        }
640
    },
641
 
642
    /**
643
    Handles `inputNode` blur events.
644
 
645
    @method _afterListInputBlur
646
    @protected
647
    **/
648
    _afterListInputBlur: function () {
649
        this._listInputFocused = false;
650
 
651
        if (this.get(VISIBLE) &&
652
                !this._mouseOverList &&
653
                (this._lastInputKey !== KEY_TAB ||
654
                    !this.get('tabSelect') ||
655
                    !this.get(ACTIVE_ITEM))) {
656
            this.hide();
657
        }
658
    },
659
 
660
    /**
661
    Handles `inputNode` focus events.
662
 
663
    @method _afterListInputFocus
664
    @protected
665
    **/
666
    _afterListInputFocus: function () {
667
        this._listInputFocused = true;
668
    },
669
 
670
    /**
671
    Handles `mouseover` events.
672
 
673
    @method _afterMouseOver
674
    @param {EventFacade} e
675
    @protected
676
    **/
677
    _afterMouseOver: function (e) {
678
        var itemNode = e.domEvent.target.ancestor(this[_SELECTOR_ITEM], true);
679
 
680
        this._mouseOverList = true;
681
 
682
        if (itemNode) {
683
            this._set(HOVERED_ITEM, itemNode);
684
        }
685
    },
686
 
687
    /**
688
    Handles `mouseout` events.
689
 
690
    @method _afterMouseOut
691
    @param {EventFacade} e
692
    @protected
693
    **/
694
    _afterMouseOut: function () {
695
        this._mouseOverList = false;
696
        this._set(HOVERED_ITEM, null);
697
    },
698
 
699
    /**
700
    Handles `resultsChange` events.
701
 
702
    @method _afterResultsChange
703
    @param {EventFacade} e
704
    @protected
705
    **/
706
    _afterResultsChange: function (e) {
707
        this._syncResults(e.newVal);
708
 
709
        if (!this.get(ALWAYS_SHOW_LIST)) {
710
            this.set(VISIBLE, !!e.newVal.length);
711
        }
712
    },
713
 
714
    /**
715
    Handles `visibleChange` events.
716
 
717
    @method _afterVisibleChange
718
    @param {EventFacade} e
719
    @protected
720
    **/
721
    _afterVisibleChange: function (e) {
722
        this._syncVisibility(!!e.newVal);
723
    },
724
 
725
    /**
726
    Delegated event handler for item `click` events.
727
 
728
    @method _onItemClick
729
    @param {EventFacade} e
730
    @protected
731
    **/
732
    _onItemClick: function (e) {
733
        var itemNode = e.currentTarget;
734
 
735
        this.set(ACTIVE_ITEM, itemNode);
736
        this.selectItem(itemNode, e);
737
    },
738
 
739
    // -- Protected Default Event Handlers -------------------------------------
740
 
741
    /**
742
    Default `select` event handler.
743
 
744
    @method _defSelectFn
745
    @param {EventFacade} e
746
    @protected
747
    **/
748
    _defSelectFn: function (e) {
749
        var text = e.result.text;
750
 
751
        // TODO: support typeahead completion, etc.
752
        this._inputNode.focus();
753
        this._updateValue(text);
754
        this._ariaSay('item_selected', {item: text});
755
        this.hide();
756
    }
757
}, {
758
    ATTRS: {
759
        /**
760
        If `true`, the first item in the list will be activated by default when
761
        the list is initially displayed and when results change.
762
 
763
        @attribute activateFirstItem
764
        @type Boolean
765
        @default false
766
        **/
767
        activateFirstItem: {
768
            value: false
769
        },
770
 
771
        /**
772
        Item that's currently active, if any. When the user presses enter, this
773
        is the item that will be selected.
774
 
775
        @attribute activeItem
776
        @type Node
777
        **/
778
        activeItem: {
779
            setter: Y.one,
780
            value: null
781
        },
782
 
783
        /**
784
        If `true`, the list will remain visible even when there are no results
785
        to display.
786
 
787
        @attribute alwaysShowList
788
        @type Boolean
789
        @default false
790
        **/
791
        alwaysShowList: {
792
            value: false
793
        },
794
 
795
        /**
796
        If `true`, keyboard navigation will wrap around to the opposite end of
797
        the list when navigating past the first or last item.
798
 
799
        @attribute circular
800
        @type Boolean
801
        @default true
802
        **/
803
        circular: {
804
            value: true
805
        },
806
 
807
        /**
808
        Item currently being hovered over by the mouse, if any.
809
 
810
        @attribute hoveredItem
811
        @type Node|null
812
        @readOnly
813
        **/
814
        hoveredItem: {
815
            readOnly: true,
816
            value: null
817
        },
818
 
819
        /**
820
        Node that will contain result items.
821
 
822
        @attribute listNode
823
        @type Node|null
824
        @initOnly
825
        **/
826
        listNode: {
827
            writeOnce: 'initOnly',
828
            value: null
829
        },
830
 
831
        /**
832
        If `true`, the viewport will be scrolled to ensure that the active list
833
        item is visible when necessary.
834
 
835
        @attribute scrollIntoView
836
        @type Boolean
837
        @default false
838
        **/
839
        scrollIntoView: {
840
            value: false
841
        },
842
 
843
        /**
844
        Translatable strings used by the AutoCompleteList widget.
845
 
846
        @attribute strings
847
        @type Object
848
        **/
849
        strings: {
850
            valueFn: function () {
851
                return Y.Intl.get('autocomplete-list');
852
            }
853
        },
854
 
855
        /**
856
        If `true`, pressing the tab key while the list is visible will select
857
        the active item, if any.
858
 
859
        @attribute tabSelect
860
        @type Boolean
861
        @default true
862
        **/
863
        tabSelect: {
864
            value: true
865
        },
866
 
867
        // The "visible" attribute is documented in Widget.
868
        visible: {
869
            value: false
870
        }
871
    },
872
 
873
    CSS_PREFIX: Y.ClassNameManager.getClassName('aclist')
874
});
875
 
876
Y.AutoCompleteList = List;
877
 
878
/**
879
Alias for <a href="AutoCompleteList.html">`AutoCompleteList`</a>. See that class
880
for API docs.
881
 
882
@class AutoComplete
883
**/
884
 
885
Y.AutoComplete = List;
886
 
887
 
888
}, '3.18.1', {
889
    "lang": [
890
        "en",
891
        "es",
892
        "hu",
893
        "it"
894
    ],
895
    "requires": [
896
        "autocomplete-base",
897
        "event-resize",
898
        "node-screen",
899
        "selector-css3",
900
        "shim-plugin",
901
        "widget",
902
        "widget-position",
903
        "widget-position-align"
904
    ],
905
    "skinnable": true
906
});