src/cm/media/js/lib/yui/yui_3.10.3/build/autocomplete-base/autocomplete-base-debug.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('autocomplete-base', function (Y, NAME) {
       
     9 
       
    10 /**
       
    11 Provides automatic input completion or suggestions for text input fields and
       
    12 textareas.
       
    13 
       
    14 @module autocomplete
       
    15 @main autocomplete
       
    16 @since 3.3.0
       
    17 **/
       
    18 
       
    19 /**
       
    20 `Y.Base` extension that provides core autocomplete logic (but no UI
       
    21 implementation) for a text input field or textarea. Must be mixed into a
       
    22 `Y.Base`-derived class to be useful.
       
    23 
       
    24 @module autocomplete
       
    25 @submodule autocomplete-base
       
    26 **/
       
    27 
       
    28 /**
       
    29 Extension that provides core autocomplete logic (but no UI implementation) for a
       
    30 text input field or textarea.
       
    31 
       
    32 The `AutoCompleteBase` class provides events and attributes that abstract away
       
    33 core autocomplete logic and configuration, but does not provide a widget
       
    34 implementation or suggestion UI. For a prepackaged autocomplete widget, see
       
    35 `AutoCompleteList`.
       
    36 
       
    37 This extension cannot be instantiated directly, since it doesn't provide an
       
    38 actual implementation. It's intended to be mixed into a `Y.Base`-based class or
       
    39 widget.
       
    40 
       
    41 `Y.Widget`-based example:
       
    42 
       
    43     YUI().use('autocomplete-base', 'widget', function (Y) {
       
    44         var MyAC = Y.Base.create('myAC', Y.Widget, [Y.AutoCompleteBase], {
       
    45             // Custom prototype methods and properties.
       
    46         }, {
       
    47             // Custom static methods and properties.
       
    48         });
       
    49 
       
    50         // Custom implementation code.
       
    51     });
       
    52 
       
    53 `Y.Base`-based example:
       
    54 
       
    55     YUI().use('autocomplete-base', function (Y) {
       
    56         var MyAC = Y.Base.create('myAC', Y.Base, [Y.AutoCompleteBase], {
       
    57             initializer: function () {
       
    58                 this._bindUIACBase();
       
    59                 this._syncUIACBase();
       
    60             },
       
    61 
       
    62             // Custom prototype methods and properties.
       
    63         }, {
       
    64             // Custom static methods and properties.
       
    65         });
       
    66 
       
    67         // Custom implementation code.
       
    68     });
       
    69 
       
    70 @class AutoCompleteBase
       
    71 **/
       
    72 
       
    73 var Escape  = Y.Escape,
       
    74     Lang    = Y.Lang,
       
    75     YArray  = Y.Array,
       
    76     YObject = Y.Object,
       
    77 
       
    78     isFunction = Lang.isFunction,
       
    79     isString   = Lang.isString,
       
    80     trim       = Lang.trim,
       
    81 
       
    82     INVALID_VALUE = Y.Attribute.INVALID_VALUE,
       
    83 
       
    84     _FUNCTION_VALIDATOR = '_functionValidator',
       
    85     _SOURCE_SUCCESS     = '_sourceSuccess',
       
    86 
       
    87     ALLOW_BROWSER_AC    = 'allowBrowserAutocomplete',
       
    88     INPUT_NODE          = 'inputNode',
       
    89     QUERY               = 'query',
       
    90     QUERY_DELIMITER     = 'queryDelimiter',
       
    91     REQUEST_TEMPLATE    = 'requestTemplate',
       
    92     RESULTS             = 'results',
       
    93     RESULT_LIST_LOCATOR = 'resultListLocator',
       
    94     VALUE               = 'value',
       
    95     VALUE_CHANGE        = 'valueChange',
       
    96 
       
    97     EVT_CLEAR   = 'clear',
       
    98     EVT_QUERY   = QUERY,
       
    99     EVT_RESULTS = RESULTS;
       
   100 
       
   101 function AutoCompleteBase() {}
       
   102 
       
   103 AutoCompleteBase.prototype = {
       
   104     // -- Lifecycle Methods ----------------------------------------------------
       
   105     initializer: function () {
       
   106         // AOP bindings.
       
   107         Y.before(this._bindUIACBase, this, 'bindUI');
       
   108         Y.before(this._syncUIACBase, this, 'syncUI');
       
   109 
       
   110         // -- Public Events ----------------------------------------------------
       
   111 
       
   112         /**
       
   113         Fires after the query has been completely cleared or no longer meets the
       
   114         minimum query length requirement.
       
   115 
       
   116         @event clear
       
   117         @param {String} prevVal Value of the query before it was cleared.
       
   118         @param {String} src Source of the event.
       
   119         @preventable _defClearFn
       
   120         **/
       
   121         this.publish(EVT_CLEAR, {
       
   122             defaultFn: this._defClearFn
       
   123         });
       
   124 
       
   125         /**
       
   126         Fires when the contents of the input field have changed and the input
       
   127         value meets the criteria necessary to generate an autocomplete query.
       
   128 
       
   129         @event query
       
   130         @param {String} inputValue Full contents of the text input field or
       
   131             textarea that generated the query.
       
   132         @param {String} query AutoComplete query. This is the string that will
       
   133             be used to request completion results. It may or may not be the same
       
   134             as `inputValue`.
       
   135         @param {String} src Source of the event.
       
   136         @preventable _defQueryFn
       
   137         **/
       
   138         this.publish(EVT_QUERY, {
       
   139             defaultFn: this._defQueryFn
       
   140         });
       
   141 
       
   142         /**
       
   143         Fires after query results are received from the source. If no source has
       
   144         been set, this event will not fire.
       
   145 
       
   146         @event results
       
   147         @param {Array|Object} data Raw, unfiltered result data (if available).
       
   148         @param {String} query Query that generated these results.
       
   149         @param {Object[]} results Array of filtered, formatted, and highlighted
       
   150             results. Each item in the array is an object with the following
       
   151             properties:
       
   152 
       
   153             @param {Node|HTMLElement|String} results.display Formatted result
       
   154                 HTML suitable for display to the user. If no custom formatter is
       
   155                 set, this will be an HTML-escaped version of the string in the
       
   156                 `text` property.
       
   157             @param {String} [results.highlighted] Highlighted (but not
       
   158                 formatted) result text. This property will only be set if a
       
   159                 highlighter is in use.
       
   160             @param {Any} results.raw Raw, unformatted result in whatever form it
       
   161                 was provided by the source.
       
   162             @param {String} results.text Plain text version of the result,
       
   163                 suitable for being inserted into the value of a text input field
       
   164                 or textarea when the result is selected by a user. This value is
       
   165                 not HTML-escaped and should not be inserted into the page using
       
   166                 `innerHTML` or `Node#setContent()`.
       
   167 
       
   168         @preventable _defResultsFn
       
   169         **/
       
   170         this.publish(EVT_RESULTS, {
       
   171             defaultFn: this._defResultsFn
       
   172         });
       
   173     },
       
   174 
       
   175     destructor: function () {
       
   176         this._acBaseEvents && this._acBaseEvents.detach();
       
   177 
       
   178         delete this._acBaseEvents;
       
   179         delete this._cache;
       
   180         delete this._inputNode;
       
   181         delete this._rawSource;
       
   182     },
       
   183 
       
   184     // -- Public Prototype Methods ---------------------------------------------
       
   185 
       
   186     /**
       
   187     Clears the result cache.
       
   188 
       
   189     @method clearCache
       
   190     @chainable
       
   191     @since 3.5.0
       
   192     **/
       
   193     clearCache: function () {
       
   194         this._cache && (this._cache = {});
       
   195         return this;
       
   196     },
       
   197 
       
   198     /**
       
   199     Sends a request to the configured source. If no source is configured, this
       
   200     method won't do anything.
       
   201 
       
   202     Usually there's no reason to call this method manually; it will be called
       
   203     automatically when user input causes a `query` event to be fired. The only
       
   204     time you'll need to call this method manually is if you want to force a
       
   205     request to be sent when no user input has occurred.
       
   206 
       
   207     @method sendRequest
       
   208     @param {String} [query] Query to send. If specified, the `query` attribute
       
   209         will be set to this query. If not specified, the current value of the
       
   210         `query` attribute will be used.
       
   211     @param {Function} [requestTemplate] Request template function. If not
       
   212         specified, the current value of the `requestTemplate` attribute will be
       
   213         used.
       
   214     @chainable
       
   215     **/
       
   216     sendRequest: function (query, requestTemplate) {
       
   217         var request,
       
   218             source = this.get('source');
       
   219 
       
   220         if (query || query === '') {
       
   221             this._set(QUERY, query);
       
   222         } else {
       
   223             query = this.get(QUERY) || '';
       
   224         }
       
   225 
       
   226         if (source) {
       
   227             if (!requestTemplate) {
       
   228                 requestTemplate = this.get(REQUEST_TEMPLATE);
       
   229             }
       
   230 
       
   231             request = requestTemplate ?
       
   232                 requestTemplate.call(this, query) : query;
       
   233 
       
   234             Y.log('sendRequest: ' + request, 'info', 'autocomplete-base');
       
   235 
       
   236             source.sendRequest({
       
   237                 query  : query,
       
   238                 request: request,
       
   239 
       
   240                 callback: {
       
   241                     success: Y.bind(this._onResponse, this, query)
       
   242                 }
       
   243             });
       
   244         }
       
   245 
       
   246         return this;
       
   247     },
       
   248 
       
   249     // -- Protected Lifecycle Methods ------------------------------------------
       
   250 
       
   251     /**
       
   252     Attaches event listeners and behaviors.
       
   253 
       
   254     @method _bindUIACBase
       
   255     @protected
       
   256     **/
       
   257     _bindUIACBase: function () {
       
   258         var inputNode  = this.get(INPUT_NODE),
       
   259             tokenInput = inputNode && inputNode.tokenInput;
       
   260 
       
   261         // If the inputNode has a node-tokeninput plugin attached, bind to the
       
   262         // plugin's inputNode instead.
       
   263         if (tokenInput) {
       
   264             inputNode = tokenInput.get(INPUT_NODE);
       
   265             this._set('tokenInput', tokenInput);
       
   266         }
       
   267 
       
   268         if (!inputNode) {
       
   269             Y.error('No inputNode specified.');
       
   270             return;
       
   271         }
       
   272 
       
   273         this._inputNode = inputNode;
       
   274 
       
   275         this._acBaseEvents = new Y.EventHandle([
       
   276             // This is the valueChange event on the inputNode, provided by the
       
   277             // event-valuechange module, not our own valueChange.
       
   278             inputNode.on(VALUE_CHANGE, this._onInputValueChange, this),
       
   279             inputNode.on('blur', this._onInputBlur, this),
       
   280 
       
   281             this.after(ALLOW_BROWSER_AC + 'Change', this._syncBrowserAutocomplete),
       
   282             this.after('sourceTypeChange', this._afterSourceTypeChange),
       
   283             this.after(VALUE_CHANGE, this._afterValueChange)
       
   284         ]);
       
   285     },
       
   286 
       
   287     /**
       
   288     Synchronizes the UI state of the `inputNode`.
       
   289 
       
   290     @method _syncUIACBase
       
   291     @protected
       
   292     **/
       
   293     _syncUIACBase: function () {
       
   294         this._syncBrowserAutocomplete();
       
   295         this.set(VALUE, this.get(INPUT_NODE).get(VALUE));
       
   296     },
       
   297 
       
   298     // -- Protected Prototype Methods ------------------------------------------
       
   299 
       
   300     /**
       
   301     Creates a DataSource-like object that simply returns the specified array as
       
   302     a response. See the `source` attribute for more details.
       
   303 
       
   304     @method _createArraySource
       
   305     @param {Array} source
       
   306     @return {Object} DataSource-like object.
       
   307     @protected
       
   308     **/
       
   309     _createArraySource: function (source) {
       
   310         var that = this;
       
   311 
       
   312         return {
       
   313             type: 'array',
       
   314             sendRequest: function (request) {
       
   315                 that[_SOURCE_SUCCESS](source.concat(), request);
       
   316             }
       
   317         };
       
   318     },
       
   319 
       
   320     /**
       
   321     Creates a DataSource-like object that passes the query to a custom-defined
       
   322     function, which is expected to call the provided callback with an array of
       
   323     results. See the `source` attribute for more details.
       
   324 
       
   325     @method _createFunctionSource
       
   326     @param {Function} source Function that accepts a query and a callback as
       
   327       parameters, and calls the callback with an array of results.
       
   328     @return {Object} DataSource-like object.
       
   329     @protected
       
   330     **/
       
   331     _createFunctionSource: function (source) {
       
   332         var that = this;
       
   333 
       
   334         return {
       
   335             type: 'function',
       
   336             sendRequest: function (request) {
       
   337                 var value;
       
   338 
       
   339                 function afterResults(results) {
       
   340                     that[_SOURCE_SUCCESS](results || [], request);
       
   341                 }
       
   342 
       
   343                 // Allow both synchronous and asynchronous functions. If we get
       
   344                 // a truthy return value, assume the function is synchronous.
       
   345                 if ((value = source(request.query, afterResults))) {
       
   346                     afterResults(value);
       
   347                 }
       
   348             }
       
   349         };
       
   350     },
       
   351 
       
   352     /**
       
   353     Creates a DataSource-like object that looks up queries as properties on the
       
   354     specified object, and returns the found value (if any) as a response. See
       
   355     the `source` attribute for more details.
       
   356 
       
   357     @method _createObjectSource
       
   358     @param {Object} source
       
   359     @return {Object} DataSource-like object.
       
   360     @protected
       
   361     **/
       
   362     _createObjectSource: function (source) {
       
   363         var that = this;
       
   364 
       
   365         return {
       
   366             type: 'object',
       
   367             sendRequest: function (request) {
       
   368                 var query = request.query;
       
   369 
       
   370                 that[_SOURCE_SUCCESS](
       
   371                     YObject.owns(source, query) ? source[query] : [],
       
   372                     request
       
   373                 );
       
   374             }
       
   375         };
       
   376     },
       
   377 
       
   378     /**
       
   379     Returns `true` if _value_ is either a function or `null`.
       
   380 
       
   381     @method _functionValidator
       
   382     @param {Function|null} value Value to validate.
       
   383     @protected
       
   384     **/
       
   385     _functionValidator: function (value) {
       
   386         return value === null || isFunction(value);
       
   387     },
       
   388 
       
   389     /**
       
   390     Faster and safer alternative to `Y.Object.getValue()`. Doesn't bother
       
   391     casting the path to an array (since we already know it's an array) and
       
   392     doesn't throw an error if a value in the middle of the object hierarchy is
       
   393     neither `undefined` nor an object.
       
   394 
       
   395     @method _getObjectValue
       
   396     @param {Object} obj
       
   397     @param {Array} path
       
   398     @return {Any} Located value, or `undefined` if the value was
       
   399         not found at the specified path.
       
   400     @protected
       
   401     **/
       
   402     _getObjectValue: function (obj, path) {
       
   403         if (!obj) {
       
   404             return;
       
   405         }
       
   406 
       
   407         for (var i = 0, len = path.length; obj && i < len; i++) {
       
   408             obj = obj[path[i]];
       
   409         }
       
   410 
       
   411         return obj;
       
   412     },
       
   413 
       
   414     /**
       
   415     Parses result responses, performs filtering and highlighting, and fires the
       
   416     `results` event.
       
   417 
       
   418     @method _parseResponse
       
   419     @param {String} query Query that generated these results.
       
   420     @param {Object} response Response containing results.
       
   421     @param {Object} data Raw response data.
       
   422     @protected
       
   423     **/
       
   424     _parseResponse: function (query, response, data) {
       
   425         var facade = {
       
   426                 data   : data,
       
   427                 query  : query,
       
   428                 results: []
       
   429             },
       
   430 
       
   431             listLocator = this.get(RESULT_LIST_LOCATOR),
       
   432             results     = [],
       
   433             unfiltered  = response && response.results,
       
   434 
       
   435             filters,
       
   436             formatted,
       
   437             formatter,
       
   438             highlighted,
       
   439             highlighter,
       
   440             i,
       
   441             len,
       
   442             maxResults,
       
   443             result,
       
   444             text,
       
   445             textLocator;
       
   446 
       
   447         if (unfiltered && listLocator) {
       
   448             unfiltered = listLocator.call(this, unfiltered);
       
   449         }
       
   450 
       
   451         if (unfiltered && unfiltered.length) {
       
   452             filters     = this.get('resultFilters');
       
   453             textLocator = this.get('resultTextLocator');
       
   454 
       
   455             // Create a lightweight result object for each result to make them
       
   456             // easier to work with. The various properties on the object
       
   457             // represent different formats of the result, and will be populated
       
   458             // as we go.
       
   459             for (i = 0, len = unfiltered.length; i < len; ++i) {
       
   460                 result = unfiltered[i];
       
   461 
       
   462                 text = textLocator ?
       
   463                         textLocator.call(this, result) :
       
   464                         result.toString();
       
   465 
       
   466                 results.push({
       
   467                     display: Escape.html(text),
       
   468                     raw    : result,
       
   469                     text   : text
       
   470                 });
       
   471             }
       
   472 
       
   473             // Run the results through all configured result filters. Each
       
   474             // filter returns an array of (potentially fewer) result objects,
       
   475             // which is then passed to the next filter, and so on.
       
   476             for (i = 0, len = filters.length; i < len; ++i) {
       
   477                 results = filters[i].call(this, query, results.concat());
       
   478 
       
   479                 if (!results) {
       
   480                     Y.log("Filter didn't return anything.", 'warn', 'autocomplete-base');
       
   481                     return;
       
   482                 }
       
   483 
       
   484                 if (!results.length) {
       
   485                     break;
       
   486                 }
       
   487             }
       
   488 
       
   489             if (results.length) {
       
   490                 formatter   = this.get('resultFormatter');
       
   491                 highlighter = this.get('resultHighlighter');
       
   492                 maxResults  = this.get('maxResults');
       
   493 
       
   494                 // If maxResults is set and greater than 0, limit the number of
       
   495                 // results.
       
   496                 if (maxResults && maxResults > 0 &&
       
   497                         results.length > maxResults) {
       
   498                     results.length = maxResults;
       
   499                 }
       
   500 
       
   501                 // Run the results through the configured highlighter (if any).
       
   502                 // The highlighter returns an array of highlighted strings (not
       
   503                 // an array of result objects), and these strings are then added
       
   504                 // to each result object.
       
   505                 if (highlighter) {
       
   506                     highlighted = highlighter.call(this, query,
       
   507                             results.concat());
       
   508 
       
   509                     if (!highlighted) {
       
   510                         Y.log("Highlighter didn't return anything.", 'warn', 'autocomplete-base');
       
   511                         return;
       
   512                     }
       
   513 
       
   514                     for (i = 0, len = highlighted.length; i < len; ++i) {
       
   515                         result = results[i];
       
   516                         result.highlighted = highlighted[i];
       
   517                         result.display     = result.highlighted;
       
   518                     }
       
   519                 }
       
   520 
       
   521                 // Run the results through the configured formatter (if any) to
       
   522                 // produce the final formatted results. The formatter returns an
       
   523                 // array of strings or Node instances (not an array of result
       
   524                 // objects), and these strings/Nodes are then added to each
       
   525                 // result object.
       
   526                 if (formatter) {
       
   527                     formatted = formatter.call(this, query, results.concat());
       
   528 
       
   529                     if (!formatted) {
       
   530                         Y.log("Formatter didn't return anything.", 'warn', 'autocomplete-base');
       
   531                         return;
       
   532                     }
       
   533 
       
   534                     for (i = 0, len = formatted.length; i < len; ++i) {
       
   535                         results[i].display = formatted[i];
       
   536                     }
       
   537                 }
       
   538             }
       
   539         }
       
   540 
       
   541         facade.results = results;
       
   542         this.fire(EVT_RESULTS, facade);
       
   543     },
       
   544 
       
   545     /**
       
   546     Returns the query portion of the specified input value, or `null` if there
       
   547     is no suitable query within the input value.
       
   548 
       
   549     If a query delimiter is defined, the query will be the last delimited part
       
   550     of of the string.
       
   551 
       
   552     @method _parseValue
       
   553     @param {String} value Input value from which to extract the query.
       
   554     @return {String|null} query
       
   555     @protected
       
   556     **/
       
   557     _parseValue: function (value) {
       
   558         var delim = this.get(QUERY_DELIMITER);
       
   559 
       
   560         if (delim) {
       
   561             value = value.split(delim);
       
   562             value = value[value.length - 1];
       
   563         }
       
   564 
       
   565         return Lang.trimLeft(value);
       
   566     },
       
   567 
       
   568     /**
       
   569     Setter for the `enableCache` attribute.
       
   570 
       
   571     @method _setEnableCache
       
   572     @param {Boolean} value
       
   573     @protected
       
   574     @since 3.5.0
       
   575     **/
       
   576     _setEnableCache: function (value) {
       
   577         // When `this._cache` is an object, result sources will store cached
       
   578         // results in it. When it's falsy, they won't. This way result sources
       
   579         // don't need to get the value of the `enableCache` attribute on every
       
   580         // request, which would be sloooow.
       
   581         this._cache = value ? {} : null;
       
   582         Y.log('Cache ' + (value ? 'enabled' : 'disabled'), 'debug', 'autocomplete-base');
       
   583     },
       
   584 
       
   585     /**
       
   586     Setter for locator attributes.
       
   587 
       
   588     @method _setLocator
       
   589     @param {Function|String|null} locator
       
   590     @return {Function|null}
       
   591     @protected
       
   592     **/
       
   593     _setLocator: function (locator) {
       
   594         if (this[_FUNCTION_VALIDATOR](locator)) {
       
   595             return locator;
       
   596         }
       
   597 
       
   598         var that = this;
       
   599 
       
   600         locator = locator.toString().split('.');
       
   601 
       
   602         return function (result) {
       
   603             return result && that._getObjectValue(result, locator);
       
   604         };
       
   605     },
       
   606 
       
   607     /**
       
   608     Setter for the `requestTemplate` attribute.
       
   609 
       
   610     @method _setRequestTemplate
       
   611     @param {Function|String|null} template
       
   612     @return {Function|null}
       
   613     @protected
       
   614     **/
       
   615     _setRequestTemplate: function (template) {
       
   616         if (this[_FUNCTION_VALIDATOR](template)) {
       
   617             return template;
       
   618         }
       
   619 
       
   620         template = template.toString();
       
   621 
       
   622         return function (query) {
       
   623             return Lang.sub(template, {query: encodeURIComponent(query)});
       
   624         };
       
   625     },
       
   626 
       
   627     /**
       
   628     Setter for the `resultFilters` attribute.
       
   629 
       
   630     @method _setResultFilters
       
   631     @param {Array|Function|String|null} filters `null`, a filter
       
   632         function, an array of filter functions, or a string or array of strings
       
   633         representing the names of methods on `Y.AutoCompleteFilters`.
       
   634     @return {Function[]} Array of filter functions (empty if <i>filters</i> is
       
   635         `null`).
       
   636     @protected
       
   637     **/
       
   638     _setResultFilters: function (filters) {
       
   639         var acFilters, getFilterFunction;
       
   640 
       
   641         if (filters === null) {
       
   642             return [];
       
   643         }
       
   644 
       
   645         acFilters = Y.AutoCompleteFilters;
       
   646 
       
   647         getFilterFunction = function (filter) {
       
   648             if (isFunction(filter)) {
       
   649                 return filter;
       
   650             }
       
   651 
       
   652             if (isString(filter) && acFilters &&
       
   653                     isFunction(acFilters[filter])) {
       
   654                 return acFilters[filter];
       
   655             }
       
   656 
       
   657             return false;
       
   658         };
       
   659 
       
   660         if (Lang.isArray(filters)) {
       
   661             filters = YArray.map(filters, getFilterFunction);
       
   662             return YArray.every(filters, function (f) { return !!f; }) ?
       
   663                     filters : INVALID_VALUE;
       
   664         } else {
       
   665             filters = getFilterFunction(filters);
       
   666             return filters ? [filters] : INVALID_VALUE;
       
   667         }
       
   668     },
       
   669 
       
   670     /**
       
   671     Setter for the `resultHighlighter` attribute.
       
   672 
       
   673     @method _setResultHighlighter
       
   674     @param {Function|String|null} highlighter `null`, a highlighter function, or
       
   675         a string representing the name of a method on
       
   676         `Y.AutoCompleteHighlighters`.
       
   677     @return {Function|null}
       
   678     @protected
       
   679     **/
       
   680     _setResultHighlighter: function (highlighter) {
       
   681         var acHighlighters;
       
   682 
       
   683         if (this[_FUNCTION_VALIDATOR](highlighter)) {
       
   684             return highlighter;
       
   685         }
       
   686 
       
   687         acHighlighters = Y.AutoCompleteHighlighters;
       
   688 
       
   689         if (isString(highlighter) && acHighlighters &&
       
   690                 isFunction(acHighlighters[highlighter])) {
       
   691             return acHighlighters[highlighter];
       
   692         }
       
   693 
       
   694         return INVALID_VALUE;
       
   695     },
       
   696 
       
   697     /**
       
   698     Setter for the `source` attribute. Returns a DataSource or a DataSource-like
       
   699     object depending on the type of _source_ and/or the value of the
       
   700     `sourceType` attribute.
       
   701 
       
   702     @method _setSource
       
   703     @param {Any} source AutoComplete source. See the `source` attribute for
       
   704         details.
       
   705     @return {DataSource|Object}
       
   706     @protected
       
   707     **/
       
   708     _setSource: function (source) {
       
   709         var sourceType = this.get('sourceType') || Lang.type(source),
       
   710             sourceSetter;
       
   711 
       
   712         if ((source && isFunction(source.sendRequest))
       
   713                 || source === null
       
   714                 || sourceType === 'datasource') {
       
   715 
       
   716             // Quacks like a DataSource instance (or null). Make it so!
       
   717             this._rawSource = source;
       
   718             return source;
       
   719         }
       
   720 
       
   721         // See if there's a registered setter for this source type.
       
   722         if ((sourceSetter = AutoCompleteBase.SOURCE_TYPES[sourceType])) {
       
   723             this._rawSource = source;
       
   724             return Lang.isString(sourceSetter) ?
       
   725                     this[sourceSetter](source) : sourceSetter(source);
       
   726         }
       
   727 
       
   728         Y.error("Unsupported source type '" + sourceType + "'. Maybe autocomplete-sources isn't loaded?");
       
   729         return INVALID_VALUE;
       
   730     },
       
   731 
       
   732     /**
       
   733     Shared success callback for non-DataSource sources.
       
   734 
       
   735     @method _sourceSuccess
       
   736     @param {Any} data Response data.
       
   737     @param {Object} request Request object.
       
   738     @protected
       
   739     **/
       
   740     _sourceSuccess: function (data, request) {
       
   741         request.callback.success({
       
   742             data: data,
       
   743             response: {results: data}
       
   744         });
       
   745     },
       
   746 
       
   747     /**
       
   748     Synchronizes the UI state of the `allowBrowserAutocomplete` attribute.
       
   749 
       
   750     @method _syncBrowserAutocomplete
       
   751     @protected
       
   752     **/
       
   753     _syncBrowserAutocomplete: function () {
       
   754         var inputNode = this.get(INPUT_NODE);
       
   755 
       
   756         if (inputNode.get('nodeName').toLowerCase() === 'input') {
       
   757             inputNode.setAttribute('autocomplete',
       
   758                     this.get(ALLOW_BROWSER_AC) ? 'on' : 'off');
       
   759         }
       
   760     },
       
   761 
       
   762     /**
       
   763     Updates the query portion of the `value` attribute.
       
   764 
       
   765     If a query delimiter is defined, the last delimited portion of the input
       
   766     value will be replaced with the specified _value_.
       
   767 
       
   768     @method _updateValue
       
   769     @param {String} newVal New value.
       
   770     @protected
       
   771     **/
       
   772     _updateValue: function (newVal) {
       
   773         var delim = this.get(QUERY_DELIMITER),
       
   774             insertDelim,
       
   775             len,
       
   776             prevVal;
       
   777 
       
   778         newVal = Lang.trimLeft(newVal);
       
   779 
       
   780         if (delim) {
       
   781             insertDelim = trim(delim); // so we don't double up on spaces
       
   782             prevVal     = YArray.map(trim(this.get(VALUE)).split(delim), trim);
       
   783             len         = prevVal.length;
       
   784 
       
   785             if (len > 1) {
       
   786                 prevVal[len - 1] = newVal;
       
   787                 newVal = prevVal.join(insertDelim + ' ');
       
   788             }
       
   789 
       
   790             newVal = newVal + insertDelim + ' ';
       
   791         }
       
   792 
       
   793         this.set(VALUE, newVal);
       
   794     },
       
   795 
       
   796     // -- Protected Event Handlers ---------------------------------------------
       
   797 
       
   798     /**
       
   799     Updates the current `source` based on the new `sourceType` to ensure that
       
   800     the two attributes don't get out of sync when they're changed separately.
       
   801 
       
   802     @method _afterSourceTypeChange
       
   803     @param {EventFacade} e
       
   804     @protected
       
   805     **/
       
   806     _afterSourceTypeChange: function (e) {
       
   807         if (this._rawSource) {
       
   808             this.set('source', this._rawSource);
       
   809         }
       
   810     },
       
   811 
       
   812     /**
       
   813     Handles change events for the `value` attribute.
       
   814 
       
   815     @method _afterValueChange
       
   816     @param {EventFacade} e
       
   817     @protected
       
   818     **/
       
   819     _afterValueChange: function (e) {
       
   820         var newVal   = e.newVal,
       
   821             self     = this,
       
   822             uiChange = e.src === AutoCompleteBase.UI_SRC,
       
   823             delay, fire, minQueryLength, query;
       
   824 
       
   825         // Update the UI if the value was changed programmatically.
       
   826         if (!uiChange) {
       
   827             self._inputNode.set(VALUE, newVal);
       
   828         }
       
   829 
       
   830         Y.log('valueChange: new: "' + newVal + '"; old: "' + e.prevVal + '"', 'info', 'autocomplete-base');
       
   831 
       
   832         minQueryLength = self.get('minQueryLength');
       
   833         query          = self._parseValue(newVal) || '';
       
   834 
       
   835         if (minQueryLength >= 0 && query.length >= minQueryLength) {
       
   836             // Only query on changes that originate from the UI.
       
   837             if (uiChange) {
       
   838                 delay = self.get('queryDelay');
       
   839 
       
   840                 fire = function () {
       
   841                     self.fire(EVT_QUERY, {
       
   842                         inputValue: newVal,
       
   843                         query     : query,
       
   844                         src       : e.src
       
   845                     });
       
   846                 };
       
   847 
       
   848                 if (delay) {
       
   849                     clearTimeout(self._delay);
       
   850                     self._delay = setTimeout(fire, delay);
       
   851                 } else {
       
   852                     fire();
       
   853                 }
       
   854             } else {
       
   855                 // For programmatic value changes, just update the query
       
   856                 // attribute without sending a query.
       
   857                 self._set(QUERY, query);
       
   858             }
       
   859         } else {
       
   860             clearTimeout(self._delay);
       
   861 
       
   862             self.fire(EVT_CLEAR, {
       
   863                 prevVal: e.prevVal ? self._parseValue(e.prevVal) : null,
       
   864                 src    : e.src
       
   865             });
       
   866         }
       
   867     },
       
   868 
       
   869     /**
       
   870     Handles `blur` events on the input node.
       
   871 
       
   872     @method _onInputBlur
       
   873     @param {EventFacade} e
       
   874     @protected
       
   875     **/
       
   876     _onInputBlur: function (e) {
       
   877         var delim = this.get(QUERY_DELIMITER),
       
   878             delimPos,
       
   879             newVal,
       
   880             value;
       
   881 
       
   882         // If a query delimiter is set and the input's value contains one or
       
   883         // more trailing delimiters, strip them.
       
   884         if (delim && !this.get('allowTrailingDelimiter')) {
       
   885             delim = Lang.trimRight(delim);
       
   886             value = newVal = this._inputNode.get(VALUE);
       
   887 
       
   888             if (delim) {
       
   889                 while ((newVal = Lang.trimRight(newVal)) &&
       
   890                         (delimPos = newVal.length - delim.length) &&
       
   891                         newVal.lastIndexOf(delim) === delimPos) {
       
   892 
       
   893                     newVal = newVal.substring(0, delimPos);
       
   894                 }
       
   895             } else {
       
   896                 // Delimiter is one or more space characters, so just trim the
       
   897                 // value.
       
   898                 newVal = Lang.trimRight(newVal);
       
   899             }
       
   900 
       
   901             if (newVal !== value) {
       
   902                 this.set(VALUE, newVal);
       
   903             }
       
   904         }
       
   905     },
       
   906 
       
   907     /**
       
   908     Handles `valueChange` events on the input node and fires a `query` event
       
   909     when the input value meets the configured criteria.
       
   910 
       
   911     @method _onInputValueChange
       
   912     @param {EventFacade} e
       
   913     @protected
       
   914     **/
       
   915     _onInputValueChange: function (e) {
       
   916         var newVal = e.newVal;
       
   917 
       
   918         // Don't query if the internal value is the same as the new value
       
   919         // reported by valueChange.
       
   920         if (newVal !== this.get(VALUE)) {
       
   921             this.set(VALUE, newVal, {src: AutoCompleteBase.UI_SRC});
       
   922         }
       
   923     },
       
   924 
       
   925     /**
       
   926     Handles source responses and fires the `results` event.
       
   927 
       
   928     @method _onResponse
       
   929     @param {EventFacade} e
       
   930     @protected
       
   931     **/
       
   932     _onResponse: function (query, e) {
       
   933         // Ignore stale responses that aren't for the current query.
       
   934         if (query === (this.get(QUERY) || '')) {
       
   935             this._parseResponse(query || '', e.response, e.data);
       
   936         }
       
   937     },
       
   938 
       
   939     // -- Protected Default Event Handlers -------------------------------------
       
   940 
       
   941     /**
       
   942     Default `clear` event handler. Sets the `results` attribute to an empty
       
   943     array and `query` to null.
       
   944 
       
   945     @method _defClearFn
       
   946     @protected
       
   947     **/
       
   948     _defClearFn: function () {
       
   949         this._set(QUERY, null);
       
   950         this._set(RESULTS, []);
       
   951     },
       
   952 
       
   953     /**
       
   954     Default `query` event handler. Sets the `query` attribute and sends a
       
   955     request to the source if one is configured.
       
   956 
       
   957     @method _defQueryFn
       
   958     @param {EventFacade} e
       
   959     @protected
       
   960     **/
       
   961     _defQueryFn: function (e) {
       
   962         Y.log('query: "' + e.query + '"; inputValue: "' + e.inputValue + '"', 'info', 'autocomplete-base');
       
   963         this.sendRequest(e.query); // sendRequest will set the 'query' attribute
       
   964     },
       
   965 
       
   966     /**
       
   967     Default `results` event handler. Sets the `results` attribute to the latest
       
   968     results.
       
   969 
       
   970     @method _defResultsFn
       
   971     @param {EventFacade} e
       
   972     @protected
       
   973     **/
       
   974     _defResultsFn: function (e) {
       
   975         Y.log('results: ' + Y.dump(e.results), 'info', 'autocomplete-base');
       
   976         this._set(RESULTS, e[RESULTS]);
       
   977     }
       
   978 };
       
   979 
       
   980 AutoCompleteBase.ATTRS = {
       
   981     /**
       
   982     Whether or not to enable the browser's built-in autocomplete functionality
       
   983     for input fields.
       
   984 
       
   985     @attribute allowBrowserAutocomplete
       
   986     @type Boolean
       
   987     @default false
       
   988     **/
       
   989     allowBrowserAutocomplete: {
       
   990         value: false
       
   991     },
       
   992 
       
   993     /**
       
   994     When a `queryDelimiter` is set, trailing delimiters will automatically be
       
   995     stripped from the input value by default when the input node loses focus.
       
   996     Set this to `true` to allow trailing delimiters.
       
   997 
       
   998     @attribute allowTrailingDelimiter
       
   999     @type Boolean
       
  1000     @default false
       
  1001     **/
       
  1002     allowTrailingDelimiter: {
       
  1003         value: false
       
  1004     },
       
  1005 
       
  1006     /**
       
  1007     Whether or not to enable in-memory caching in result sources that support
       
  1008     it.
       
  1009 
       
  1010     @attribute enableCache
       
  1011     @type Boolean
       
  1012     @default true
       
  1013     @since 3.5.0
       
  1014     **/
       
  1015     enableCache: {
       
  1016         lazyAdd: false, // we need the setter to run on init
       
  1017         setter: '_setEnableCache',
       
  1018         value: true
       
  1019     },
       
  1020 
       
  1021     /**
       
  1022     Node to monitor for changes, which will generate `query` events when
       
  1023     appropriate. May be either an `<input>` or a `<textarea>`.
       
  1024 
       
  1025     @attribute inputNode
       
  1026     @type Node|HTMLElement|String
       
  1027     @initOnly
       
  1028     **/
       
  1029     inputNode: {
       
  1030         setter: Y.one,
       
  1031         writeOnce: 'initOnly'
       
  1032     },
       
  1033 
       
  1034     /**
       
  1035     Maximum number of results to return. A value of `0` or less will allow an
       
  1036     unlimited number of results.
       
  1037 
       
  1038     @attribute maxResults
       
  1039     @type Number
       
  1040     @default 0
       
  1041     **/
       
  1042     maxResults: {
       
  1043         value: 0
       
  1044     },
       
  1045 
       
  1046     /**
       
  1047     Minimum number of characters that must be entered before a `query` event
       
  1048     will be fired. A value of `0` allows empty queries; a negative value will
       
  1049     effectively disable all `query` events.
       
  1050 
       
  1051     @attribute minQueryLength
       
  1052     @type Number
       
  1053     @default 1
       
  1054     **/
       
  1055     minQueryLength: {
       
  1056         value: 1
       
  1057     },
       
  1058 
       
  1059     /**
       
  1060     Current query, or `null` if there is no current query.
       
  1061 
       
  1062     The query might not be the same as the current value of the input node, both
       
  1063     for timing reasons (due to `queryDelay`) and because when one or more
       
  1064     `queryDelimiter` separators are in use, only the last portion of the
       
  1065     delimited input string will be used as the query value.
       
  1066 
       
  1067     @attribute query
       
  1068     @type String|null
       
  1069     @default null
       
  1070     @readonly
       
  1071     **/
       
  1072     query: {
       
  1073         readOnly: true,
       
  1074         value: null
       
  1075     },
       
  1076 
       
  1077     /**
       
  1078     Number of milliseconds to delay after input before triggering a `query`
       
  1079     event. If new input occurs before this delay is over, the previous input
       
  1080     event will be ignored and a new delay will begin.
       
  1081 
       
  1082     This can be useful both to throttle queries to a remote data source and to
       
  1083     avoid distracting the user by showing them less relevant results before
       
  1084     they've paused their typing.
       
  1085 
       
  1086     @attribute queryDelay
       
  1087     @type Number
       
  1088     @default 100
       
  1089     **/
       
  1090     queryDelay: {
       
  1091         value: 100
       
  1092     },
       
  1093 
       
  1094     /**
       
  1095     Query delimiter string. When a delimiter is configured, the input value
       
  1096     will be split on the delimiter, and only the last portion will be used in
       
  1097     autocomplete queries and updated when the `query` attribute is
       
  1098     modified.
       
  1099 
       
  1100     @attribute queryDelimiter
       
  1101     @type String|null
       
  1102     @default null
       
  1103     **/
       
  1104     queryDelimiter: {
       
  1105         value: null
       
  1106     },
       
  1107 
       
  1108     /**
       
  1109     Source request template. This can be a function that accepts a query as a
       
  1110     parameter and returns a request string, or it can be a string containing the
       
  1111     placeholder "{query}", which will be replaced with the actual URI-encoded
       
  1112     query. In either case, the resulting string will be appended to the request
       
  1113     URL when the `source` attribute is set to a remote DataSource, JSONP URL, or
       
  1114     XHR URL (it will not be appended to YQL URLs).
       
  1115 
       
  1116     While `requestTemplate` may be set to either a function or a string, it will
       
  1117     always be returned as a function that accepts a query argument and returns a
       
  1118     string.
       
  1119 
       
  1120     @attribute requestTemplate
       
  1121     @type Function|String|null
       
  1122     @default null
       
  1123     **/
       
  1124     requestTemplate: {
       
  1125         setter: '_setRequestTemplate',
       
  1126         value: null
       
  1127     },
       
  1128 
       
  1129     /**
       
  1130     Array of local result filter functions. If provided, each filter will be
       
  1131     called with two arguments when results are received: the query and an array
       
  1132     of result objects. See the documentation for the `results` event for a list
       
  1133     of the properties available on each result object.
       
  1134 
       
  1135     Each filter is expected to return a filtered or modified version of the
       
  1136     results array, which will then be passed on to subsequent filters, then the
       
  1137     `resultHighlighter` function (if set), then the `resultFormatter` function
       
  1138     (if set), and finally to subscribers to the `results` event.
       
  1139 
       
  1140     If no `source` is set, result filters will not be called.
       
  1141 
       
  1142     Prepackaged result filters provided by the autocomplete-filters and
       
  1143     autocomplete-filters-accentfold modules can be used by specifying the filter
       
  1144     name as a string, such as `'phraseMatch'` (assuming the necessary filters
       
  1145     module is loaded).
       
  1146 
       
  1147     @attribute resultFilters
       
  1148     @type Array
       
  1149     @default []
       
  1150     **/
       
  1151     resultFilters: {
       
  1152         setter: '_setResultFilters',
       
  1153         value: []
       
  1154     },
       
  1155 
       
  1156     /**
       
  1157     Function which will be used to format results. If provided, this function
       
  1158     will be called with two arguments after results have been received and
       
  1159     filtered: the query and an array of result objects. The formatter is
       
  1160     expected to return an array of HTML strings or Node instances containing the
       
  1161     desired HTML for each result.
       
  1162 
       
  1163     See the documentation for the `results` event for a list of the properties
       
  1164     available on each result object.
       
  1165 
       
  1166     If no `source` is set, the formatter will not be called.
       
  1167 
       
  1168     @attribute resultFormatter
       
  1169     @type Function|null
       
  1170     **/
       
  1171     resultFormatter: {
       
  1172         validator: _FUNCTION_VALIDATOR,
       
  1173         value: null
       
  1174     },
       
  1175 
       
  1176     /**
       
  1177     Function which will be used to highlight results. If provided, this function
       
  1178     will be called with two arguments after results have been received and
       
  1179     filtered: the query and an array of filtered result objects. The highlighter
       
  1180     is expected to return an array of highlighted result text in the form of
       
  1181     HTML strings.
       
  1182 
       
  1183     See the documentation for the `results` event for a list of the properties
       
  1184     available on each result object.
       
  1185 
       
  1186     If no `source` is set, the highlighter will not be called.
       
  1187 
       
  1188     @attribute resultHighlighter
       
  1189     @type Function|null
       
  1190     **/
       
  1191     resultHighlighter: {
       
  1192         setter: '_setResultHighlighter',
       
  1193         value: null
       
  1194     },
       
  1195 
       
  1196     /**
       
  1197     Locator that should be used to extract an array of results from a non-array
       
  1198     response.
       
  1199 
       
  1200     By default, no locator is applied, and all responses are assumed to be
       
  1201     arrays by default. If all responses are already arrays, you don't need to
       
  1202     define a locator.
       
  1203 
       
  1204     The locator may be either a function (which will receive the raw response as
       
  1205     an argument and must return an array) or a string representing an object
       
  1206     path, such as "foo.bar.baz" (which would return the value of
       
  1207     `result.foo.bar.baz` if the response is an object).
       
  1208 
       
  1209     While `resultListLocator` may be set to either a function or a string, it
       
  1210     will always be returned as a function that accepts a response argument and
       
  1211     returns an array.
       
  1212 
       
  1213     @attribute resultListLocator
       
  1214     @type Function|String|null
       
  1215     **/
       
  1216     resultListLocator: {
       
  1217         setter: '_setLocator',
       
  1218         value: null
       
  1219     },
       
  1220 
       
  1221     /**
       
  1222     Current results, or an empty array if there are no results.
       
  1223 
       
  1224     @attribute results
       
  1225     @type Array
       
  1226     @default []
       
  1227     @readonly
       
  1228     **/
       
  1229     results: {
       
  1230         readOnly: true,
       
  1231         value: []
       
  1232     },
       
  1233 
       
  1234     /**
       
  1235     Locator that should be used to extract a plain text string from a non-string
       
  1236     result item. The resulting text value will typically be the value that ends
       
  1237     up being inserted into an input field or textarea when the user of an
       
  1238     autocomplete implementation selects a result.
       
  1239 
       
  1240     By default, no locator is applied, and all results are assumed to be plain
       
  1241     text strings. If all results are already plain text strings, you don't need
       
  1242     to define a locator.
       
  1243 
       
  1244     The locator may be either a function (which will receive the raw result as
       
  1245     an argument and must return a string) or a string representing an object
       
  1246     path, such as "foo.bar.baz" (which would return the value of
       
  1247     `result.foo.bar.baz` if the result is an object).
       
  1248 
       
  1249     While `resultTextLocator` may be set to either a function or a string, it
       
  1250     will always be returned as a function that accepts a result argument and
       
  1251     returns a string.
       
  1252 
       
  1253     @attribute resultTextLocator
       
  1254     @type Function|String|null
       
  1255     **/
       
  1256     resultTextLocator: {
       
  1257         setter: '_setLocator',
       
  1258         value: null
       
  1259     },
       
  1260 
       
  1261     /**
       
  1262     Source for autocomplete results. The following source types are supported:
       
  1263 
       
  1264     <dl>
       
  1265       <dt>Array</dt>
       
  1266       <dd>
       
  1267         <p>
       
  1268         The full array will be provided to any configured filters for each
       
  1269         query. This is an easy way to create a fully client-side autocomplete
       
  1270         implementation.
       
  1271         </p>
       
  1272 
       
  1273         <p>
       
  1274         Example: `['first result', 'second result', 'etc']`
       
  1275         </p>
       
  1276       </dd>
       
  1277 
       
  1278       <dt>DataSource</dt>
       
  1279       <dd>
       
  1280         A `DataSource` instance or other object that provides a DataSource-like
       
  1281         `sendRequest` method. See the `DataSource` documentation for details.
       
  1282       </dd>
       
  1283 
       
  1284       <dt>Function</dt>
       
  1285       <dd>
       
  1286         <p>
       
  1287         A function source will be called with the current query and a
       
  1288         callback function as parameters, and should either return an array of
       
  1289         results (for synchronous operation) or return nothing and pass an
       
  1290         array of results to the provided callback (for asynchronous
       
  1291         operation).
       
  1292         </p>
       
  1293 
       
  1294         <p>
       
  1295         Example (synchronous):
       
  1296         </p>
       
  1297 
       
  1298         <pre>
       
  1299         function (query) {
       
  1300             return ['foo', 'bar'];
       
  1301         }
       
  1302         </pre>
       
  1303 
       
  1304         <p>
       
  1305         Example (async):
       
  1306         </p>
       
  1307 
       
  1308         <pre>
       
  1309         function (query, callback) {
       
  1310             callback(['foo', 'bar']);
       
  1311         }
       
  1312         </pre>
       
  1313       </dd>
       
  1314 
       
  1315       <dt>Object</dt>
       
  1316       <dd>
       
  1317         <p>
       
  1318         An object will be treated as a query hashmap. If a property on the
       
  1319         object matches the current query, the value of that property will be
       
  1320         used as the response.
       
  1321         </p>
       
  1322 
       
  1323         <p>
       
  1324         The response is assumed to be an array of results by default. If the
       
  1325         response is not an array, provide a `resultListLocator` to
       
  1326         process the response and return an array.
       
  1327         </p>
       
  1328 
       
  1329         <p>
       
  1330         Example: `{foo: ['foo result 1', 'foo result 2'], bar: ['bar result']}`
       
  1331         </p>
       
  1332       </dd>
       
  1333     </dl>
       
  1334 
       
  1335     If the optional `autocomplete-sources` module is loaded, then
       
  1336     the following additional source types will be supported as well:
       
  1337 
       
  1338     <dl>
       
  1339       <dt>&lt;select&gt; Node</dt>
       
  1340       <dd>
       
  1341         You may provide a YUI Node instance wrapping a &lt;select&gt;
       
  1342         element, and the options in the list will be used as results. You
       
  1343         will also need to specify a `resultTextLocator` of 'text'
       
  1344         or 'value', depending on what you want to use as the text of the
       
  1345         result.
       
  1346 
       
  1347         Each result will be an object with the following properties:
       
  1348 
       
  1349         <dl>
       
  1350           <dt>html (String)</dt>
       
  1351           <dd>
       
  1352             <p>HTML content of the &lt;option&gt; element.</p>
       
  1353           </dd>
       
  1354 
       
  1355           <dt>index (Number)</dt>
       
  1356           <dd>
       
  1357             <p>Index of the &lt;option&gt; element in the list.</p>
       
  1358           </dd>
       
  1359 
       
  1360           <dt>node (Y.Node)</dt>
       
  1361           <dd>
       
  1362             <p>Node instance referring to the original &lt;option&gt; element.</p>
       
  1363           </dd>
       
  1364 
       
  1365           <dt>selected (Boolean)</dt>
       
  1366           <dd>
       
  1367             <p>Whether or not this item is currently selected in the
       
  1368             &lt;select&gt; list.</p>
       
  1369           </dd>
       
  1370 
       
  1371           <dt>text (String)</dt>
       
  1372           <dd>
       
  1373             <p>Text content of the &lt;option&gt; element.</p>
       
  1374           </dd>
       
  1375 
       
  1376           <dt>value (String)</dt>
       
  1377           <dd>
       
  1378             <p>Value of the &lt;option&gt; element.</p>
       
  1379           </dd>
       
  1380         </dl>
       
  1381       </dd>
       
  1382 
       
  1383       <dt>String (JSONP URL)</dt>
       
  1384       <dd>
       
  1385         <p>
       
  1386         If a URL with a `{callback}` placeholder is provided, it will be used to
       
  1387         make a JSONP request. The `{query}` placeholder will be replaced with
       
  1388         the current query, and the `{callback}` placeholder will be replaced
       
  1389         with an internally-generated JSONP callback name. Both placeholders must
       
  1390         appear in the URL, or the request will fail. An optional `{maxResults}`
       
  1391         placeholder may also be provided, and will be replaced with the value of
       
  1392         the maxResults attribute (or 1000 if the maxResults attribute is 0 or
       
  1393         less).
       
  1394         </p>
       
  1395 
       
  1396         <p>
       
  1397         The response is assumed to be an array of results by default. If the
       
  1398         response is not an array, provide a `resultListLocator` to process the
       
  1399         response and return an array.
       
  1400         </p>
       
  1401 
       
  1402         <p>
       
  1403         <strong>The `jsonp` module must be loaded in order for
       
  1404         JSONP URL sources to work.</strong> If the `jsonp` module
       
  1405         is not already loaded, it will be loaded on demand if possible.
       
  1406         </p>
       
  1407 
       
  1408         <p>
       
  1409         Example: `'http://example.com/search?q={query}&callback={callback}'`
       
  1410         </p>
       
  1411       </dd>
       
  1412 
       
  1413       <dt>String (XHR URL)</dt>
       
  1414       <dd>
       
  1415         <p>
       
  1416         If a URL without a `{callback}` placeholder is provided, it will be used
       
  1417         to make a same-origin XHR request. The `{query}` placeholder will be
       
  1418         replaced with the current query. An optional `{maxResults}` placeholder
       
  1419         may also be provided, and will be replaced with the value of the
       
  1420         maxResults attribute (or 1000 if the maxResults attribute is 0 or less).
       
  1421         </p>
       
  1422 
       
  1423         <p>
       
  1424         The response is assumed to be a JSON array of results by default. If the
       
  1425         response is a JSON object and not an array, provide a
       
  1426         `resultListLocator` to process the response and return an array. If the
       
  1427         response is in some form other than JSON, you will need to use a custom
       
  1428         DataSource instance as the source.
       
  1429         </p>
       
  1430 
       
  1431         <p>
       
  1432         <strong>The `io-base` and `json-parse` modules
       
  1433         must be loaded in order for XHR URL sources to work.</strong> If
       
  1434         these modules are not already loaded, they will be loaded on demand
       
  1435         if possible.
       
  1436         </p>
       
  1437 
       
  1438         <p>
       
  1439         Example: `'http://example.com/search?q={query}'`
       
  1440         </p>
       
  1441       </dd>
       
  1442 
       
  1443       <dt>String (YQL query)</dt>
       
  1444       <dd>
       
  1445         <p>
       
  1446         If a YQL query is provided, it will be used to make a YQL request. The
       
  1447         `{query}` placeholder will be replaced with the current autocomplete
       
  1448         query. This placeholder must appear in the YQL query, or the request
       
  1449         will fail. An optional `{maxResults}` placeholder may also be provided,
       
  1450         and will be replaced with the value of the maxResults attribute (or 1000
       
  1451         if the maxResults attribute is 0 or less).
       
  1452         </p>
       
  1453 
       
  1454         <p>
       
  1455         <strong>The `yql` module must be loaded in order for YQL
       
  1456         sources to work.</strong> If the `yql` module is not
       
  1457         already loaded, it will be loaded on demand if possible.
       
  1458         </p>
       
  1459 
       
  1460         <p>
       
  1461         Example: `'select * from search.suggest where query="{query}"'`
       
  1462         </p>
       
  1463       </dd>
       
  1464     </dl>
       
  1465 
       
  1466     As an alternative to providing a source, you could simply listen for `query`
       
  1467     events and handle them any way you see fit. Providing a source is optional,
       
  1468     but will usually be simpler.
       
  1469 
       
  1470     @attribute source
       
  1471     @type Array|DataSource|Function|Node|Object|String|null
       
  1472     **/
       
  1473     source: {
       
  1474         setter: '_setSource',
       
  1475         value: null
       
  1476     },
       
  1477 
       
  1478     /**
       
  1479     May be used to force a specific source type, overriding the automatic source
       
  1480     type detection. It should almost never be necessary to do this, but as they
       
  1481     taught us in the Boy Scouts, one should always be prepared, so it's here if
       
  1482     you need it. Be warned that if you set this attribute and something breaks,
       
  1483     it's your own fault.
       
  1484 
       
  1485     Supported `sourceType` values are: 'array', 'datasource', 'function', and
       
  1486     'object'.
       
  1487 
       
  1488     If the `autocomplete-sources` module is loaded, the following additional
       
  1489     source types are supported: 'io', 'jsonp', 'select', 'string', 'yql'
       
  1490 
       
  1491     @attribute sourceType
       
  1492     @type String
       
  1493     **/
       
  1494     sourceType: {
       
  1495         value: null
       
  1496     },
       
  1497 
       
  1498     /**
       
  1499     If the `inputNode` specified at instantiation time has a `node-tokeninput`
       
  1500     plugin attached to it, this attribute will be a reference to the
       
  1501     `Y.Plugin.TokenInput` instance.
       
  1502 
       
  1503     @attribute tokenInput
       
  1504     @type Plugin.TokenInput
       
  1505     @readonly
       
  1506     **/
       
  1507     tokenInput: {
       
  1508         readOnly: true
       
  1509     },
       
  1510 
       
  1511     /**
       
  1512     Current value of the input node.
       
  1513 
       
  1514     @attribute value
       
  1515     @type String
       
  1516     @default ''
       
  1517     **/
       
  1518     value: {
       
  1519         // Why duplicate this._inputNode.get('value')? Because we need a
       
  1520         // reliable way to track the source of value changes. We want to perform
       
  1521         // completion when the user changes the value, but not when we change
       
  1522         // the value.
       
  1523         value: ''
       
  1524     }
       
  1525 };
       
  1526 
       
  1527 // This tells Y.Base.create() to copy these static properties to any class
       
  1528 // AutoCompleteBase is mixed into.
       
  1529 AutoCompleteBase._buildCfg = {
       
  1530     aggregates: ['SOURCE_TYPES'],
       
  1531     statics   : ['UI_SRC']
       
  1532 };
       
  1533 
       
  1534 /**
       
  1535 Mapping of built-in source types to their setter functions. DataSource instances
       
  1536 and DataSource-like objects are handled natively, so are not mapped here.
       
  1537 
       
  1538 @property SOURCE_TYPES
       
  1539 @type {Object}
       
  1540 @static
       
  1541 **/
       
  1542 AutoCompleteBase.SOURCE_TYPES = {
       
  1543     array     : '_createArraySource',
       
  1544     'function': '_createFunctionSource',
       
  1545     object    : '_createObjectSource'
       
  1546 };
       
  1547 
       
  1548 AutoCompleteBase.UI_SRC = (Y.Widget && Y.Widget.UI_SRC) || 'ui';
       
  1549 
       
  1550 Y.AutoCompleteBase = AutoCompleteBase;
       
  1551 
       
  1552 
       
  1553 }, '3.10.3', {
       
  1554     "optional": [
       
  1555         "autocomplete-sources"
       
  1556     ],
       
  1557     "requires": [
       
  1558         "array-extras",
       
  1559         "base-build",
       
  1560         "escape",
       
  1561         "event-valuechange",
       
  1562         "node-base"
       
  1563     ]
       
  1564 });