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