autocomplete-list.js revision f5738feb0b3d461c1398fc1cd8fc01b0de21d592
246N/A/**
246N/A * Traditional autocomplete dropdown list widget, just like Mom used to make.
246N/A *
246N/A * @module autocomplete
246N/A * @submodule autocomplete-list
246N/A * @class AutoCompleteList
246N/A * @extends Widget
246N/A * @uses AutoCompleteBase
246N/A * @uses WidgetPosition
246N/A * @uses WidgetPositionAlign
246N/A * @uses WidgetStack
246N/A * @constructor
246N/A * @param {Object} config Configuration object.
246N/A */
246N/A
246N/Avar Node = Y.Node,
246N/A YArray = Y.Array,
246N/A
246N/A // keyCode constants.
246N/A // KEY_DOWN = 40,
246N/A // KEY_ENTER = 13,
246N/A // KEY_ESC = 27,
246N/A KEY_TAB = 9,
246N/A // KEY_UP = 38,
246N/A
246N/A // String shorthand.
246N/A INPUT_NODE = 'inputNode',
246N/A VISIBLE = 'visible',
246N/A WIDTH = 'width',
246N/A
246N/AList = Y.Base.create('autocompleteList', Y.Widget, [
246N/A Y.AutoCompleteBase,
246N/A Y.WidgetPosition,
246N/A Y.WidgetPositionAlign,
246N/A Y.WidgetStack
246N/A], {
246N/A // -- Prototype Properties -------------------------------------------------
246N/A CONTENT_TEMPLATE: '<ul/>',
246N/A ITEM_TEMPLATE: '<li/>',
246N/A
246N/A // -- Lifecycle Prototype Methods ------------------------------------------
246N/A initializer: function () {
246N/A this._inputNode = this.get(INPUT_NODE);
246N/A this._events = [];
246N/A
246N/A if (!this._inputNode) {
246N/A Y.error('No inputNode specified.');
246N/A }
246N/A
246N/A if (!this.get('align.node')) {
246N/A this.set('align.node', this._inputNode);
246N/A }
246N/A
246N/A if (!this.get(WIDTH)) {
246N/A this.set(WIDTH, this._inputNode.get('clientWidth'));
246N/A }
246N/A },
246N/A
246N/A destructor: function () {
246N/A this.unbindInput();
246N/A
246N/A while (this._events.length) {
246N/A this._events.pop().detach();
246N/A }
246N/A },
246N/A
246N/A bindUI: function () {
246N/A this._bindInput();
246N/A this._bindList();
246N/A },
246N/A
246N/A renderUI: function () {
246N/A // See http://www.w3.org/WAI/PF/aria/roles#combobox for ARIA details.
246N/A this._contentBox = this.get('contentBox').set('role', 'listbox');
246N/A
246N/A this._inputNode.addClass(this.getClassName('input')).setAttrs({
246N/A 'aria-autocomplete': 'list',
246N/A 'aria-owns': this._contentBox.get('id'),
246N/A role: 'combobox'
246N/A });
246N/A },
246N/A
246N/A syncUI: function () {
246N/A this._syncResults();
246N/A this._syncVisibility();
246N/A },
246N/A
246N/A // -- Public Prototype Methods ---------------------------------------------
246N/A
246N/A /**
246N/A * Hides the list.
246N/A *
246N/A * @method hide
246N/A * @see show
246N/A * @chainable
246N/A */
246N/A hide: function () {
246N/A return this.set(VISIBLE, false);
246N/A },
246N/A
246N/A /**
246N/A * Shows the list.
246N/A *
246N/A * @method show
246N/A * @see hide
246N/A * @chainable
246N/A */
246N/A show: function () {
246N/A return this.set(VISIBLE, true);
246N/A },
246N/A
246N/A // -- Protected Prototype Methods ------------------------------------------
246N/A
246N/A /**
246N/A * Appends the specified result <i>items</i> to the list inside a new item
246N/A * node.
246N/A *
246N/A * @method _add
246N/A * @param {Array|Node|HTMLElement|String} items Result item or array of
246N/A * result items.
246N/A * @protected
246N/A */
246N/A _add: function (items) {
246N/A var itemNodes = [];
246N/A
246N/A YArray.each(Y.Lang.isArray(items) ? items : [items], function (item) {
246N/A itemNodes.push(this._createItemNode(item));
246N/A }, this);
246N/A
246N/A this._contentBox.append(itemNodes);
246N/A },
246N/A
246N/A /**
246N/A * Binds <code>inputNode</code> events, in addition to those already bound
246N/A * by <code>AutoCompleteBase</code>'s public <code>bindInput()</code>
246N/A * method.
246N/A *
246N/A * @method _bindInput
246N/A * @protected
246N/A */
246N/A _bindInput: function () {
246N/A var inputNode = this._inputNode;
246N/A
246N/A // Call AutoCompleteBase's bind method first.
246N/A this.bindInput();
246N/A
246N/A this._events.concat([
246N/A inputNode.on('blur', this._onInputBlur, this),
246N/A inputNode.on('keydown', this._onInputKeyDown, this)
246N/A ]);
246N/A },
246N/A
246N/A /**
246N/A * Binds list events.
246N/A *
246N/A * @method _bindList
246N/A * @protected
246N/A */
246N/A _bindList: function () {
246N/A this._events.concat([
246N/A this.after('mouseenter', this._afterMouseEnter, this),
246N/A this.after('mouseleave', this._afterMouseLeave, this),
246N/A
246N/A this.after('resultsChange', this._afterResultsChange, this),
246N/A this.after('visibleChange', this._afterVisibleChange, this)
246N/A ]);
246N/A },
246N/A
246N/A /**
246N/A * Clears the contents of the tray.
246N/A *
246N/A * @method _clear
246N/A * @protected
246N/A */
246N/A _clear: function () {
246N/A this._contentBox.get('children').remove(true);
246N/A },
246N/A
246N/A /**
246N/A * Creates an item node with the specified <i>content</i>.
246N/A *
246N/A * @method _createItemNode
246N/A * @param {Node|HTMLElement|String} content
246N/A * @protected
246N/A * @returns {Node} Item node.
246N/A */
246N/A _createItemNode: function (content) {
246N/A var itemNode = Node.create(this.ITEM_TEMPLATE);
246N/A
246N/A return itemNode.append(content).setAttrs({
246N/A id : Y.stamp(itemNode),
246N/A role: 'option'
246N/A }).addClass(this.getClassName('item'));
246N/A },
246N/A
246N/A /**
246N/A * Synchronizes the results displayed in the list with those in the
246N/A * <i>results</i> argument, or with the <code>results</code> attribute if an
246N/A * argument is not provided.
246N/A *
246N/A * @method _syncResults
246N/A * @param {Array} results (optional) Results.
246N/A * @protected
246N/A */
246N/A _syncResults: function (results) {
246N/A if (!results) {
246N/A results = this.get('results');
246N/A }
246N/A
246N/A this._clear();
246N/A
246N/A if (results.length) {
246N/A this._add(results);
246N/A }
246N/A },
246N/A
246N/A /**
246N/A * Synchronizes the visibility of the tray with the <i>visible</i> argument,
246N/A * or with the <code>visible</code> attribute if an argument is not
246N/A * provided.
246N/A *
246N/A * @method _syncVisibility
246N/A * @param {Boolean} visible (optional) Visibility.
246N/A * @protected
246N/A */
246N/A _syncVisibility: function (visible) {
246N/A if (visible === undefined) {
246N/A visible = this.get(VISIBLE);
246N/A }
246N/A
246N/A this._contentBox.set('aria-hidden', !visible);
246N/A },
246N/A
246N/A // -- Protected Event Handlers ---------------------------------------------
246N/A
246N/A /**
246N/A * Handles <code>inputNode</code> <code>blur</code> events.
246N/A *
246N/A * @method _onInputBlur
246N/A * @param {EventTarget} e
246N/A * @protected
246N/A */
246N/A _onInputBlur: function (e) {
246N/A // Hide the list on inputNode blur events, unless the mouse is currently
246N/A // over the list (which indicates that the user is probably interacting
246N/A // with it) or the tab key was pressed.
246N/A if (this._mouseOverList && this._lastInputKey !== KEY_TAB) {
246N/A this._inputNode.focus();
246N/A } else {
246N/A this.hide();
246N/A }
246N/A },
246N/A
246N/A /**
246N/A * Handles <code>inputNode</code> key events.
246N/A *
246N/A * @method _onInputKeyDown
246N/A * @param {EventTarget} e
246N/A * @protected
246N/A */
246N/A _onInputKeyDown: function (e) {
246N/A this._lastInputKey = e.keyCode;
246N/A },
246N/A
246N/A /**
246N/A * Handles <code>mouseenter</code> events.
246N/A *
246N/A * @method _afterMouseEnter
246N/A * @param {EventTarget} e
246N/A * @protected
246N/A */
246N/A _afterMouseEnter: function () {
246N/A this._mouseOverList = true;
246N/A },
246N/A
246N/A /**
246N/A * Handles <code>mouseleave</code> events.
246N/A *
246N/A * @method _afterMouseLeave
246N/A * @param {EventTarget} e
246N/A * @protected
246N/A */
246N/A _afterMouseLeave: function () {
246N/A this._mouseOverList = false;
246N/A },
246N/A
246N/A /**
246N/A * Handles <code>resultsChange</code> events.
246N/A *
246N/A * @method _afterResultsChange
246N/A * @param {EventFacade} e
246N/A * @protected
246N/A */
246N/A _afterResultsChange: function (e) {
246N/A this._syncResults(e.newVal);
246N/A this.set(VISIBLE, !!e.newVal.length);
246N/A },
246N/A
246N/A /**
246N/A * Handles <code>visibleChange</code> events.
246N/A *
246N/A * @method _afterVisibleChange
246N/A * @param {EventFacade} e
246N/A * @protected
246N/A */
246N/A _afterVisibleChange: function (e) {
246N/A this._syncVisibility(!!e.newVal);
246N/A }
246N/A}, {
246N/A ATTRS: {
246N/A align: {
246N/A value: {
246N/A points: ['tl', 'bl']
246N/A }
246N/A },
246N/A
246N/A visible: {
246N/A value: false
246N/A }
246N/A },
246N/A
246N/A CSS_PREFIX: Y.ClassNameManager.getClassName('aclist')
246N/A});
246N/A
246N/AY.AutoCompleteList = List;
246N/A
246N/A/**
246N/A * Alias for <a href="AutoCompleteList.html"><code>AutoCompleteList</code></a>.
246N/A * See that class for API docs.
246N/A *
246N/A * @class AutoComplete
246N/A */
246N/A
246N/AY.AutoComplete = List;
246N/A