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