text: /*
  Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  Code licensed under the BSD License:
  http://developer.yahoo.com/yui/license.html
  version: 3.7.2
  build: 3.7.2
  */
  YUI.add('autocomplete-sources', function (Y, NAME) {
  
  /**
  Mixes support for JSONP and YQL result sources into AutoCompleteBase.
  
  @module autocomplete
  @submodule autocomplete-sources
  **/
  
  var ACBase = Y.AutoCompleteBase,
      Lang   = Y.Lang,
  
      _SOURCE_SUCCESS = '_sourceSuccess',
  
      MAX_RESULTS         = 'maxResults',
      REQUEST_TEMPLATE    = 'requestTemplate',
      RESULT_LIST_LOCATOR = 'resultListLocator';
  
  // Add prototype properties and methods to AutoCompleteBase.
  Y.mix(ACBase.prototype, {
      /**
      Regular expression used to determine whether a String source is a YQL query.
  
      @property _YQL_SOURCE_REGEX
      @type RegExp
      @protected
      @for AutoCompleteBase
      **/
      _YQL_SOURCE_REGEX: /^(?:select|set|use)\s+/i,
  
      /**
      Runs before AutoCompleteBase's `_createObjectSource()` method and augments
      it to support additional object-based source types.
  
      @method _beforeCreateObjectSource
      @param {String} source
      @protected
      @for AutoCompleteBase
      **/
      _beforeCreateObjectSource: function (source) {
          // If the object is a <select> node, use the options as the result
          // source.
          if (source instanceof Y.Node &&
                  source.get('nodeName').toLowerCase() === 'select') {
  
              return this._createSelectSource(source);
          }
  
          // If the object is a JSONPRequest instance, try to use it as a JSONP
          // source.
          if (Y.JSONPRequest && source instanceof Y.JSONPRequest) {
              return this._createJSONPSource(source);
          }
  
          // Fall back to a basic object source.
          return this._createObjectSource(source);
      },
  
      /**
      Creates a DataSource-like object that uses `Y.io` as a source. See the
      `source` attribute for more details.
  
      @method _createIOSource
      @param {String} source URL.
      @return {Object} DataSource-like object.
      @protected
      @for AutoCompleteBase
      **/
      _createIOSource: function (source) {
          var ioSource = {type: 'io'},
              that     = this,
              ioRequest, lastRequest, loading;
  
          // Private internal _sendRequest method that will be assigned to
          // ioSource.sendRequest once io-base and json-parse are available.
          function _sendRequest(request) {
              var cacheKey = request.request;
  
              // Return immediately on a cached response.
              if (that._cache && cacheKey in that._cache) {
                  that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
                  return;
              }
  
              // Cancel any outstanding requests.
              if (ioRequest && ioRequest.isInProgress()) {
                  ioRequest.abort();
              }
  
              ioRequest = Y.io(that._getXHRUrl(source, request), {
                  on: {
                      success: function (tid, response) {
                          var data;
  
                          try {
                              data = Y.JSON.parse(response.responseText);
                          } catch (ex) {
                              Y.error('JSON parse error', ex);
                          }
  
                          if (data) {
                              that._cache && (that._cache[cacheKey] = data);
                              that[_SOURCE_SUCCESS](data, request);
                          }
                      }
                  }
              });
          }
  
          ioSource.sendRequest = function (request) {
              // Keep track of the most recent request in case there are multiple
              // requests while we're waiting for the IO module to load. Only the
              // most recent request will be sent.
              lastRequest = request;
  
              if (loading) { return; }
  
              loading = true;
  
              // Lazy-load the io-base and json-parse modules if necessary,
              // then overwrite the sendRequest method to bypass this check in
              // the future.
              Y.use('io-base', 'json-parse', function () {
                  ioSource.sendRequest = _sendRequest;
                  _sendRequest(lastRequest);
              });
          };
  
          return ioSource;
      },
  
      /**
      Creates a DataSource-like object that uses the specified JSONPRequest
      instance as a source. See the `source` attribute for more details.
  
      @method _createJSONPSource
      @param {JSONPRequest|String} source URL string or JSONPRequest instance.
      @return {Object} DataSource-like object.
      @protected
      @for AutoCompleteBase
      **/
      _createJSONPSource: function (source) {
          var jsonpSource = {type: 'jsonp'},
              that        = this,
              lastRequest, loading;
  
          function _sendRequest(request) {
              var cacheKey = request.request,
                  query    = request.query;
  
              if (that._cache && cacheKey in that._cache) {
                  that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
                  return;
              }
  
              // Hack alert: JSONPRequest currently doesn't support
              // per-request callbacks, so we're reaching into the protected
              // _config object to make it happen.
              //
              // This limitation is mentioned in the following JSONP
              // enhancement ticket:
              //
              // http://yuilibrary.com/projects/yui3/ticket/2529371
              source._config.on.success = function (data) {
                  that._cache && (that._cache[cacheKey] = data);
                  that[_SOURCE_SUCCESS](data, request);
              };
  
              source.send(query);
          }
  
          jsonpSource.sendRequest = function (request) {
              // Keep track of the most recent request in case there are multiple
              // requests while we're waiting for the JSONP module to load. Only
              // the most recent request will be sent.
              lastRequest = request;
  
              if (loading) { return; }
  
              loading = true;
  
              // Lazy-load the JSONP module if necessary, then overwrite the
              // sendRequest method to bypass this check in the future.
              Y.use('jsonp', function () {
                  // Turn the source into a JSONPRequest instance if it isn't
                  // one already.
                  if (!(source instanceof Y.JSONPRequest)) {
                      source = new Y.JSONPRequest(source, {
                          format: Y.bind(that._jsonpFormatter, that)
                      });
                  }
  
                  jsonpSource.sendRequest = _sendRequest;
                  _sendRequest(lastRequest);
              });
          };
  
          return jsonpSource;
      },
  
      /**
      Creates a DataSource-like object that uses the specified `<select>` node as
      a source.
  
      @method _createSelectSource
      @param {Node} source YUI Node instance wrapping a `<select>` node.
      @return {Object} DataSource-like object.
      @protected
      @for AutoCompleteBase
      **/
      _createSelectSource: function (source) {
          var that = this;
  
          return {
              type: 'select',
              sendRequest: function (request) {
                  var options = [];
  
                  source.get('options').each(function (option) {
                      options.push({
                          html    : option.get('innerHTML'),
                          index   : option.get('index'),
                          node    : option,
                          selected: option.get('selected'),
                          text    : option.get('text'),
                          value   : option.get('value')
                      });
                  });
  
                  that[_SOURCE_SUCCESS](options, request);
              }
          };
      },
  
      /**
      Creates a DataSource-like object that calls the specified  URL or executes
      the specified YQL query for results. If the string starts with "select ",
      "use ", or "set " (case-insensitive), it's assumed to be a YQL query;
      otherwise, it's assumed to be a URL (which may be absolute or relative).
      URLs containing a "{callback}" placeholder are assumed to be JSONP URLs; all
      others will use XHR. See the `source` attribute for more details.
  
      @method _createStringSource
      @param {String} source URL or YQL query.
      @return {Object} DataSource-like object.
      @protected
      @for AutoCompleteBase
      **/
      _createStringSource: function (source) {
          if (this._YQL_SOURCE_REGEX.test(source)) {
              // Looks like a YQL query.
              return this._createYQLSource(source);
          } else if (source.indexOf('{callback}') !== -1) {
              // Contains a {callback} param and isn't a YQL query, so it must be
              // JSONP.
              return this._createJSONPSource(source);
          } else {
              // Not a YQL query or JSONP, so we'll assume it's an XHR URL.
              return this._createIOSource(source);
          }
      },
  
      /**
      Creates a DataSource-like object that uses the specified YQL query string to
      create a YQL-based source. See the `source` attribute for details. If no
      `resultListLocator` is defined, this method will set a best-guess locator
      that might work for many typical YQL queries.
  
      @method _createYQLSource
      @param {String} source YQL query.
      @return {Object} DataSource-like object.
      @protected
      @for AutoCompleteBase
      **/
      _createYQLSource: function (source) {
          var that      = this,
              yqlSource = {type: 'yql'},
              lastRequest, loading, yqlRequest;
  
          if (!that.get(RESULT_LIST_LOCATOR)) {
              that.set(RESULT_LIST_LOCATOR, that._defaultYQLLocator);
          }
  
          function _sendRequest(request) {
              var query      = request.query,
                  env        = that.get('yqlEnv'),
                  maxResults = that.get(MAX_RESULTS),
                  callback, opts, yqlQuery;
  
              yqlQuery = Lang.sub(source, {
                  maxResults: maxResults > 0 ? maxResults : 1000,
                  request   : request.request,
                  query     : query
              });
  
              if (that._cache && yqlQuery in that._cache) {
                  that[_SOURCE_SUCCESS](that._cache[yqlQuery], request);
                  return;
              }
  
              callback = function (data) {
                  that._cache && (that._cache[yqlQuery] = data);
                  that[_SOURCE_SUCCESS](data, request);
              };
  
              opts = {proto: that.get('yqlProtocol')};
  
              // Only create a new YQLRequest instance if this is the
              // first request. For subsequent requests, we'll reuse the
              // original instance.
              if (yqlRequest) {
                  yqlRequest._callback   = callback;
                  yqlRequest._opts       = opts;
                  yqlRequest._params.q   = yqlQuery;
  
                  if (env) {
                      yqlRequest._params.env = env;
                  }
              } else {
                  yqlRequest = new Y.YQLRequest(yqlQuery, {
                      on: {success: callback},
                      allowCache: false // temp workaround until JSONP has per-URL callback proxies
                  }, env ? {env: env} : null, opts);
              }
  
              yqlRequest.send();
          }
  
          yqlSource.sendRequest = function (request) {
              // Keep track of the most recent request in case there are multiple
              // requests while we're waiting for the YQL module to load. Only the
              // most recent request will be sent.
              lastRequest = request;
  
              if (!loading) {
                  // Lazy-load the YQL module if necessary, then overwrite the
                  // sendRequest method to bypass this check in the future.
                  loading = true;
  
                  Y.use('yql', function () {
                      yqlSource.sendRequest = _sendRequest;
                      _sendRequest(lastRequest);
                  });
              }
          };
  
          return yqlSource;
      },
  
      /**
      Default resultListLocator used when a string-based YQL source is set and the
      implementer hasn't already specified one.
  
      @method _defaultYQLLocator
      @param {Object} response YQL response object.
      @return {Array}
      @protected
      @for AutoCompleteBase
      **/
      _defaultYQLLocator: function (response) {
          var results = response && response.query && response.query.results,
              values;
  
          if (results && Lang.isObject(results)) {
              // If there's only a single value on YQL's results object, that
              // value almost certainly contains the array of results we want. If
              // there are 0 or 2+ values, then the values themselves are most
              // likely the results we want.
              values  = Y.Object.values(results) || [];
              results = values.length === 1 ? values[0] : values;
  
              if (!Lang.isArray(results)) {
                  results = [results];
              }
          } else {
              results = [];
          }
  
          return results;
      },
  
      /**
      Returns a formatted XHR URL based on the specified base _url_, _query_, and
      the current _requestTemplate_ if any.
  
      @method _getXHRUrl
      @param {String} url Base URL.
      @param {Object} request Request object containing `query` and `request`
        properties.
      @return {String} Formatted URL.
      @protected
      @for AutoCompleteBase
      **/
      _getXHRUrl: function (url, request) {
          var maxResults = this.get(MAX_RESULTS);
  
          if (request.query !== request.request) {
              // Append the request template to the URL.
              url += request.request;
          }
  
          return Lang.sub(url, {
              maxResults: maxResults > 0 ? maxResults : 1000,
              query     : encodeURIComponent(request.query)
          });
      },
  
      /**
      URL formatter passed to `JSONPRequest` instances.
  
      @method _jsonpFormatter
      @param {String} url
      @param {String} proxy
      @param {String} query
      @return {String} Formatted URL
      @protected
      @for AutoCompleteBase
      **/
      _jsonpFormatter: function (url, proxy, query) {
          var maxResults      = this.get(MAX_RESULTS),
              requestTemplate = this.get(REQUEST_TEMPLATE);
  
          if (requestTemplate) {
              url += requestTemplate(query);
          }
  
          return Lang.sub(url, {
              callback  : proxy,
              maxResults: maxResults > 0 ? maxResults : 1000,
              query     : encodeURIComponent(query)
          });
      }
  });
  
  // Add attributes to AutoCompleteBase.
  Y.mix(ACBase.ATTRS, {
      /**
      YQL environment file URL to load when the `source` is set to a YQL query.
      Set this to `null` to use the default Open Data Tables environment file
      (http://datatables.org/alltables.env).
  
      @attribute yqlEnv
      @type String
      @default null
      @for AutoCompleteBase
      **/
      yqlEnv: {
          value: null
      },
  
      /**
      URL protocol to use when the `source` is set to a YQL query.
  
      @attribute yqlProtocol
      @type String
      @default 'http'
      @for AutoCompleteBase
      **/
      yqlProtocol: {
          value: 'http'
      }
  });
  
  // Tell AutoCompleteBase about the new source types it can now support.
  Y.mix(ACBase.SOURCE_TYPES, {
      io    : '_createIOSource',
      jsonp : '_createJSONPSource',
      object: '_beforeCreateObjectSource', // Run our version before the base version.
      select: '_createSelectSource',
      string: '_createStringSource',
      yql   : '_createYQLSource'
  }, true);
  
  
  }, '3.7.2', {"optional": ["io-base", "json-parse", "jsonp", "yql"], "requires": ["autocomplete-base"]});
  