src/cm/media/js/lib/yui/yui3-3.15.0/build/autocomplete-sources/autocomplete-sources-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('autocomplete-sources', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4 Mixes support for JSONP and YQL result sources into AutoCompleteBase.
       
     5 
       
     6 @module autocomplete
       
     7 @submodule autocomplete-sources
       
     8 **/
       
     9 
       
    10 var ACBase = Y.AutoCompleteBase,
       
    11     Lang   = Y.Lang,
       
    12 
       
    13     _SOURCE_SUCCESS = '_sourceSuccess',
       
    14 
       
    15     MAX_RESULTS         = 'maxResults',
       
    16     REQUEST_TEMPLATE    = 'requestTemplate',
       
    17     RESULT_LIST_LOCATOR = 'resultListLocator';
       
    18 
       
    19 // Add prototype properties and methods to AutoCompleteBase.
       
    20 Y.mix(ACBase.prototype, {
       
    21     /**
       
    22     Regular expression used to determine whether a String source is a YQL query.
       
    23 
       
    24     @property _YQL_SOURCE_REGEX
       
    25     @type RegExp
       
    26     @protected
       
    27     @for AutoCompleteBase
       
    28     **/
       
    29     _YQL_SOURCE_REGEX: /^(?:select|set|use)\s+/i,
       
    30 
       
    31     /**
       
    32     Runs before AutoCompleteBase's `_createObjectSource()` method and augments
       
    33     it to support additional object-based source types.
       
    34 
       
    35     @method _beforeCreateObjectSource
       
    36     @param {String} source
       
    37     @protected
       
    38     @for AutoCompleteBase
       
    39     **/
       
    40     _beforeCreateObjectSource: function (source) {
       
    41         // If the object is a <select> node, use the options as the result
       
    42         // source.
       
    43         if (source instanceof Y.Node &&
       
    44                 source.get('nodeName').toLowerCase() === 'select') {
       
    45 
       
    46             return this._createSelectSource(source);
       
    47         }
       
    48 
       
    49         // If the object is a JSONPRequest instance, try to use it as a JSONP
       
    50         // source.
       
    51         if (Y.JSONPRequest && source instanceof Y.JSONPRequest) {
       
    52             return this._createJSONPSource(source);
       
    53         }
       
    54 
       
    55         // Fall back to a basic object source.
       
    56         return this._createObjectSource(source);
       
    57     },
       
    58 
       
    59     /**
       
    60     Creates a DataSource-like object that uses `Y.io` as a source. See the
       
    61     `source` attribute for more details.
       
    62 
       
    63     @method _createIOSource
       
    64     @param {String} source URL.
       
    65     @return {Object} DataSource-like object.
       
    66     @protected
       
    67     @for AutoCompleteBase
       
    68     **/
       
    69     _createIOSource: function (source) {
       
    70         var ioSource = {type: 'io'},
       
    71             that     = this,
       
    72             ioRequest, lastRequest, loading;
       
    73 
       
    74         // Private internal _sendRequest method that will be assigned to
       
    75         // ioSource.sendRequest once io-base and json-parse are available.
       
    76         function _sendRequest(request) {
       
    77             var cacheKey = request.request;
       
    78 
       
    79             // Return immediately on a cached response.
       
    80             if (that._cache && cacheKey in that._cache) {
       
    81                 that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
       
    82                 return;
       
    83             }
       
    84 
       
    85             // Cancel any outstanding requests.
       
    86             if (ioRequest && ioRequest.isInProgress()) {
       
    87                 ioRequest.abort();
       
    88             }
       
    89 
       
    90             ioRequest = Y.io(that._getXHRUrl(source, request), {
       
    91                 on: {
       
    92                     success: function (tid, response) {
       
    93                         var data;
       
    94 
       
    95                         try {
       
    96                             data = Y.JSON.parse(response.responseText);
       
    97                         } catch (ex) {
       
    98                             Y.error('JSON parse error', ex);
       
    99                         }
       
   100 
       
   101                         if (data) {
       
   102                             that._cache && (that._cache[cacheKey] = data);
       
   103                             that[_SOURCE_SUCCESS](data, request);
       
   104                         }
       
   105                     }
       
   106                 }
       
   107             });
       
   108         }
       
   109 
       
   110         ioSource.sendRequest = function (request) {
       
   111             // Keep track of the most recent request in case there are multiple
       
   112             // requests while we're waiting for the IO module to load. Only the
       
   113             // most recent request will be sent.
       
   114             lastRequest = request;
       
   115 
       
   116             if (loading) { return; }
       
   117 
       
   118             loading = true;
       
   119 
       
   120             // Lazy-load the io-base and json-parse modules if necessary,
       
   121             // then overwrite the sendRequest method to bypass this check in
       
   122             // the future.
       
   123             Y.use('io-base', 'json-parse', function () {
       
   124                 ioSource.sendRequest = _sendRequest;
       
   125                 _sendRequest(lastRequest);
       
   126             });
       
   127         };
       
   128 
       
   129         return ioSource;
       
   130     },
       
   131 
       
   132     /**
       
   133     Creates a DataSource-like object that uses the specified JSONPRequest
       
   134     instance as a source. See the `source` attribute for more details.
       
   135 
       
   136     @method _createJSONPSource
       
   137     @param {JSONPRequest|String} source URL string or JSONPRequest instance.
       
   138     @return {Object} DataSource-like object.
       
   139     @protected
       
   140     @for AutoCompleteBase
       
   141     **/
       
   142     _createJSONPSource: function (source) {
       
   143         var jsonpSource = {type: 'jsonp'},
       
   144             that        = this,
       
   145             lastRequest, loading;
       
   146 
       
   147         function _sendRequest(request) {
       
   148             var cacheKey = request.request,
       
   149                 query    = request.query;
       
   150 
       
   151             if (that._cache && cacheKey in that._cache) {
       
   152                 that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
       
   153                 return;
       
   154             }
       
   155 
       
   156             // Hack alert: JSONPRequest currently doesn't support
       
   157             // per-request callbacks, so we're reaching into the protected
       
   158             // _config object to make it happen.
       
   159             //
       
   160             // This limitation is mentioned in the following JSONP
       
   161             // enhancement ticket:
       
   162             //
       
   163             // http://yuilibrary.com/projects/yui3/ticket/2529371
       
   164             source._config.on.success = function (data) {
       
   165                 that._cache && (that._cache[cacheKey] = data);
       
   166                 that[_SOURCE_SUCCESS](data, request);
       
   167             };
       
   168 
       
   169             source.send(query);
       
   170         }
       
   171 
       
   172         jsonpSource.sendRequest = function (request) {
       
   173             // Keep track of the most recent request in case there are multiple
       
   174             // requests while we're waiting for the JSONP module to load. Only
       
   175             // the most recent request will be sent.
       
   176             lastRequest = request;
       
   177 
       
   178             if (loading) { return; }
       
   179 
       
   180             loading = true;
       
   181 
       
   182             // Lazy-load the JSONP module if necessary, then overwrite the
       
   183             // sendRequest method to bypass this check in the future.
       
   184             Y.use('jsonp', function () {
       
   185                 // Turn the source into a JSONPRequest instance if it isn't
       
   186                 // one already.
       
   187                 if (!(source instanceof Y.JSONPRequest)) {
       
   188                     source = new Y.JSONPRequest(source, {
       
   189                         format: Y.bind(that._jsonpFormatter, that)
       
   190                     });
       
   191                 }
       
   192 
       
   193                 jsonpSource.sendRequest = _sendRequest;
       
   194                 _sendRequest(lastRequest);
       
   195             });
       
   196         };
       
   197 
       
   198         return jsonpSource;
       
   199     },
       
   200 
       
   201     /**
       
   202     Creates a DataSource-like object that uses the specified `<select>` node as
       
   203     a source.
       
   204 
       
   205     @method _createSelectSource
       
   206     @param {Node} source YUI Node instance wrapping a `<select>` node.
       
   207     @return {Object} DataSource-like object.
       
   208     @protected
       
   209     @for AutoCompleteBase
       
   210     **/
       
   211     _createSelectSource: function (source) {
       
   212         var that = this;
       
   213 
       
   214         return {
       
   215             type: 'select',
       
   216             sendRequest: function (request) {
       
   217                 var options = [];
       
   218 
       
   219                 source.get('options').each(function (option) {
       
   220                     options.push({
       
   221                         html    : option.get('innerHTML'),
       
   222                         index   : option.get('index'),
       
   223                         node    : option,
       
   224                         selected: option.get('selected'),
       
   225                         text    : option.get('text'),
       
   226                         value   : option.get('value')
       
   227                     });
       
   228                 });
       
   229 
       
   230                 that[_SOURCE_SUCCESS](options, request);
       
   231             }
       
   232         };
       
   233     },
       
   234 
       
   235     /**
       
   236     Creates a DataSource-like object that calls the specified  URL or executes
       
   237     the specified YQL query for results. If the string starts with "select ",
       
   238     "use ", or "set " (case-insensitive), it's assumed to be a YQL query;
       
   239     otherwise, it's assumed to be a URL (which may be absolute or relative).
       
   240     URLs containing a "{callback}" placeholder are assumed to be JSONP URLs; all
       
   241     others will use XHR. See the `source` attribute for more details.
       
   242 
       
   243     @method _createStringSource
       
   244     @param {String} source URL or YQL query.
       
   245     @return {Object} DataSource-like object.
       
   246     @protected
       
   247     @for AutoCompleteBase
       
   248     **/
       
   249     _createStringSource: function (source) {
       
   250         if (this._YQL_SOURCE_REGEX.test(source)) {
       
   251             // Looks like a YQL query.
       
   252             return this._createYQLSource(source);
       
   253         } else if (source.indexOf('{callback}') !== -1) {
       
   254             // Contains a {callback} param and isn't a YQL query, so it must be
       
   255             // JSONP.
       
   256             return this._createJSONPSource(source);
       
   257         } else {
       
   258             // Not a YQL query or JSONP, so we'll assume it's an XHR URL.
       
   259             return this._createIOSource(source);
       
   260         }
       
   261     },
       
   262 
       
   263     /**
       
   264     Creates a DataSource-like object that uses the specified YQL query string to
       
   265     create a YQL-based source. See the `source` attribute for details. If no
       
   266     `resultListLocator` is defined, this method will set a best-guess locator
       
   267     that might work for many typical YQL queries.
       
   268 
       
   269     @method _createYQLSource
       
   270     @param {String} source YQL query.
       
   271     @return {Object} DataSource-like object.
       
   272     @protected
       
   273     @for AutoCompleteBase
       
   274     **/
       
   275     _createYQLSource: function (source) {
       
   276         var that      = this,
       
   277             yqlSource = {type: 'yql'},
       
   278             lastRequest, loading, yqlRequest;
       
   279 
       
   280         if (!that.get(RESULT_LIST_LOCATOR)) {
       
   281             that.set(RESULT_LIST_LOCATOR, that._defaultYQLLocator);
       
   282         }
       
   283 
       
   284         function _sendRequest(request) {
       
   285             var query      = request.query,
       
   286                 env        = that.get('yqlEnv'),
       
   287                 maxResults = that.get(MAX_RESULTS),
       
   288                 callback, opts, yqlQuery;
       
   289 
       
   290             yqlQuery = Lang.sub(source, {
       
   291                 maxResults: maxResults > 0 ? maxResults : 1000,
       
   292                 request   : request.request,
       
   293                 query     : query
       
   294             });
       
   295 
       
   296             if (that._cache && yqlQuery in that._cache) {
       
   297                 that[_SOURCE_SUCCESS](that._cache[yqlQuery], request);
       
   298                 return;
       
   299             }
       
   300 
       
   301             callback = function (data) {
       
   302                 that._cache && (that._cache[yqlQuery] = data);
       
   303                 that[_SOURCE_SUCCESS](data, request);
       
   304             };
       
   305 
       
   306             opts = {proto: that.get('yqlProtocol')};
       
   307 
       
   308             // Only create a new YQLRequest instance if this is the
       
   309             // first request. For subsequent requests, we'll reuse the
       
   310             // original instance.
       
   311             if (yqlRequest) {
       
   312                 yqlRequest._callback   = callback;
       
   313                 yqlRequest._opts       = opts;
       
   314                 yqlRequest._params.q   = yqlQuery;
       
   315 
       
   316                 if (env) {
       
   317                     yqlRequest._params.env = env;
       
   318                 }
       
   319             } else {
       
   320                 yqlRequest = new Y.YQLRequest(yqlQuery, {
       
   321                     on: {success: callback},
       
   322                     allowCache: false // temp workaround until JSONP has per-URL callback proxies
       
   323                 }, env ? {env: env} : null, opts);
       
   324             }
       
   325 
       
   326             yqlRequest.send();
       
   327         }
       
   328 
       
   329         yqlSource.sendRequest = function (request) {
       
   330             // Keep track of the most recent request in case there are multiple
       
   331             // requests while we're waiting for the YQL module to load. Only the
       
   332             // most recent request will be sent.
       
   333             lastRequest = request;
       
   334 
       
   335             if (!loading) {
       
   336                 // Lazy-load the YQL module if necessary, then overwrite the
       
   337                 // sendRequest method to bypass this check in the future.
       
   338                 loading = true;
       
   339 
       
   340                 Y.use('yql', function () {
       
   341                     yqlSource.sendRequest = _sendRequest;
       
   342                     _sendRequest(lastRequest);
       
   343                 });
       
   344             }
       
   345         };
       
   346 
       
   347         return yqlSource;
       
   348     },
       
   349 
       
   350     /**
       
   351     Default resultListLocator used when a string-based YQL source is set and the
       
   352     implementer hasn't already specified one.
       
   353 
       
   354     @method _defaultYQLLocator
       
   355     @param {Object} response YQL response object.
       
   356     @return {Array}
       
   357     @protected
       
   358     @for AutoCompleteBase
       
   359     **/
       
   360     _defaultYQLLocator: function (response) {
       
   361         var results = response && response.query && response.query.results,
       
   362             values;
       
   363 
       
   364         if (results && Lang.isObject(results)) {
       
   365             // If there's only a single value on YQL's results object, that
       
   366             // value almost certainly contains the array of results we want. If
       
   367             // there are 0 or 2+ values, then the values themselves are most
       
   368             // likely the results we want.
       
   369             values  = Y.Object.values(results) || [];
       
   370             results = values.length === 1 ? values[0] : values;
       
   371 
       
   372             if (!Lang.isArray(results)) {
       
   373                 results = [results];
       
   374             }
       
   375         } else {
       
   376             results = [];
       
   377         }
       
   378 
       
   379         return results;
       
   380     },
       
   381 
       
   382     /**
       
   383     Returns a formatted XHR URL based on the specified base _url_, _query_, and
       
   384     the current _requestTemplate_ if any.
       
   385 
       
   386     @method _getXHRUrl
       
   387     @param {String} url Base URL.
       
   388     @param {Object} request Request object containing `query` and `request`
       
   389       properties.
       
   390     @return {String} Formatted URL.
       
   391     @protected
       
   392     @for AutoCompleteBase
       
   393     **/
       
   394     _getXHRUrl: function (url, request) {
       
   395         var maxResults = this.get(MAX_RESULTS);
       
   396 
       
   397         if (request.query !== request.request) {
       
   398             // Append the request template to the URL.
       
   399             url += request.request;
       
   400         }
       
   401 
       
   402         return Lang.sub(url, {
       
   403             maxResults: maxResults > 0 ? maxResults : 1000,
       
   404             query     : encodeURIComponent(request.query)
       
   405         });
       
   406     },
       
   407 
       
   408     /**
       
   409     URL formatter passed to `JSONPRequest` instances.
       
   410 
       
   411     @method _jsonpFormatter
       
   412     @param {String} url
       
   413     @param {String} proxy
       
   414     @param {String} query
       
   415     @return {String} Formatted URL
       
   416     @protected
       
   417     @for AutoCompleteBase
       
   418     **/
       
   419     _jsonpFormatter: function (url, proxy, query) {
       
   420         var maxResults      = this.get(MAX_RESULTS),
       
   421             requestTemplate = this.get(REQUEST_TEMPLATE);
       
   422 
       
   423         if (requestTemplate) {
       
   424             url += requestTemplate(query);
       
   425         }
       
   426 
       
   427         return Lang.sub(url, {
       
   428             callback  : proxy,
       
   429             maxResults: maxResults > 0 ? maxResults : 1000,
       
   430             query     : encodeURIComponent(query)
       
   431         });
       
   432     }
       
   433 });
       
   434 
       
   435 // Add attributes to AutoCompleteBase.
       
   436 Y.mix(ACBase.ATTRS, {
       
   437     /**
       
   438     YQL environment file URL to load when the `source` is set to a YQL query.
       
   439     Set this to `null` to use the default Open Data Tables environment file
       
   440     (http://datatables.org/alltables.env).
       
   441 
       
   442     @attribute yqlEnv
       
   443     @type String
       
   444     @default null
       
   445     @for AutoCompleteBase
       
   446     **/
       
   447     yqlEnv: {
       
   448         value: null
       
   449     },
       
   450 
       
   451     /**
       
   452     URL protocol to use when the `source` is set to a YQL query.
       
   453 
       
   454     @attribute yqlProtocol
       
   455     @type String
       
   456     @default 'http'
       
   457     @for AutoCompleteBase
       
   458     **/
       
   459     yqlProtocol: {
       
   460         value: 'http'
       
   461     }
       
   462 });
       
   463 
       
   464 // Tell AutoCompleteBase about the new source types it can now support.
       
   465 Y.mix(ACBase.SOURCE_TYPES, {
       
   466     io    : '_createIOSource',
       
   467     jsonp : '_createJSONPSource',
       
   468     object: '_beforeCreateObjectSource', // Run our version before the base version.
       
   469     select: '_createSelectSource',
       
   470     string: '_createStringSource',
       
   471     yql   : '_createYQLSource'
       
   472 }, true);
       
   473 
       
   474 
       
   475 }, '@VERSION@', {"optional": ["io-base", "json-parse", "jsonp", "yql"], "requires": ["autocomplete-base"]});