2
YUI 3.10.3 (build 2fb5187)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('autocomplete-sources', function (Y, NAME) {
11
Mixes support for JSONP and YQL result sources into AutoCompleteBase.
14
@submodule autocomplete-sources
17
var ACBase = Y.AutoCompleteBase,
20
_SOURCE_SUCCESS = '_sourceSuccess',
22
MAX_RESULTS = 'maxResults',
23
REQUEST_TEMPLATE = 'requestTemplate',
24
RESULT_LIST_LOCATOR = 'resultListLocator';
26
// Add prototype properties and methods to AutoCompleteBase.
27
Y.mix(ACBase.prototype, {
29
Regular expression used to determine whether a String source is a YQL query.
31
@property _YQL_SOURCE_REGEX
36
_YQL_SOURCE_REGEX: /^(?:select|set|use)\s+/i,
39
Runs before AutoCompleteBase's `_createObjectSource()` method and augments
40
it to support additional object-based source types.
42
@method _beforeCreateObjectSource
43
@param {String} source
47
_beforeCreateObjectSource: function (source) {
48
// If the object is a <select> node, use the options as the result
50
if (source instanceof Y.Node &&
51
source.get('nodeName').toLowerCase() === 'select') {
53
return this._createSelectSource(source);
56
// If the object is a JSONPRequest instance, try to use it as a JSONP
58
if (Y.JSONPRequest && source instanceof Y.JSONPRequest) {
59
return this._createJSONPSource(source);
62
// Fall back to a basic object source.
63
return this._createObjectSource(source);
67
Creates a DataSource-like object that uses `Y.io` as a source. See the
68
`source` attribute for more details.
70
@method _createIOSource
71
@param {String} source URL.
72
@return {Object} DataSource-like object.
76
_createIOSource: function (source) {
77
var ioSource = {type: 'io'},
79
ioRequest, lastRequest, loading;
81
// Private internal _sendRequest method that will be assigned to
82
// ioSource.sendRequest once io-base and json-parse are available.
83
function _sendRequest(request) {
84
var cacheKey = request.request;
86
// Return immediately on a cached response.
87
if (that._cache && cacheKey in that._cache) {
88
that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
92
// Cancel any outstanding requests.
93
if (ioRequest && ioRequest.isInProgress()) {
97
ioRequest = Y.io(that._getXHRUrl(source, request), {
99
success: function (tid, response) {
103
data = Y.JSON.parse(response.responseText);
105
Y.error('JSON parse error', ex);
109
that._cache && (that._cache[cacheKey] = data);
110
that[_SOURCE_SUCCESS](data, request);
117
ioSource.sendRequest = function (request) {
118
// Keep track of the most recent request in case there are multiple
119
// requests while we're waiting for the IO module to load. Only the
120
// most recent request will be sent.
121
lastRequest = request;
123
if (loading) { return; }
127
// Lazy-load the io-base and json-parse modules if necessary,
128
// then overwrite the sendRequest method to bypass this check in
130
Y.use('io-base', 'json-parse', function () {
131
ioSource.sendRequest = _sendRequest;
132
_sendRequest(lastRequest);
140
Creates a DataSource-like object that uses the specified JSONPRequest
141
instance as a source. See the `source` attribute for more details.
143
@method _createJSONPSource
144
@param {JSONPRequest|String} source URL string or JSONPRequest instance.
145
@return {Object} DataSource-like object.
147
@for AutoCompleteBase
149
_createJSONPSource: function (source) {
150
var jsonpSource = {type: 'jsonp'},
152
lastRequest, loading;
154
function _sendRequest(request) {
155
var cacheKey = request.request,
156
query = request.query;
158
if (that._cache && cacheKey in that._cache) {
159
that[_SOURCE_SUCCESS](that._cache[cacheKey], request);
163
// Hack alert: JSONPRequest currently doesn't support
164
// per-request callbacks, so we're reaching into the protected
165
// _config object to make it happen.
167
// This limitation is mentioned in the following JSONP
168
// enhancement ticket:
170
// http://yuilibrary.com/projects/yui3/ticket/2529371
171
source._config.on.success = function (data) {
172
that._cache && (that._cache[cacheKey] = data);
173
that[_SOURCE_SUCCESS](data, request);
179
jsonpSource.sendRequest = function (request) {
180
// Keep track of the most recent request in case there are multiple
181
// requests while we're waiting for the JSONP module to load. Only
182
// the most recent request will be sent.
183
lastRequest = request;
185
if (loading) { return; }
189
// Lazy-load the JSONP module if necessary, then overwrite the
190
// sendRequest method to bypass this check in the future.
191
Y.use('jsonp', function () {
192
// Turn the source into a JSONPRequest instance if it isn't
194
if (!(source instanceof Y.JSONPRequest)) {
195
source = new Y.JSONPRequest(source, {
196
format: Y.bind(that._jsonpFormatter, that)
200
jsonpSource.sendRequest = _sendRequest;
201
_sendRequest(lastRequest);
209
Creates a DataSource-like object that uses the specified `<select>` node as
212
@method _createSelectSource
213
@param {Node} source YUI Node instance wrapping a `<select>` node.
214
@return {Object} DataSource-like object.
216
@for AutoCompleteBase
218
_createSelectSource: function (source) {
223
sendRequest: function (request) {
226
source.get('options').each(function (option) {
228
html : option.get('innerHTML'),
229
index : option.get('index'),
231
selected: option.get('selected'),
232
text : option.get('text'),
233
value : option.get('value')
237
that[_SOURCE_SUCCESS](options, request);
243
Creates a DataSource-like object that calls the specified URL or executes
244
the specified YQL query for results. If the string starts with "select ",
245
"use ", or "set " (case-insensitive), it's assumed to be a YQL query;
246
otherwise, it's assumed to be a URL (which may be absolute or relative).
247
URLs containing a "{callback}" placeholder are assumed to be JSONP URLs; all
248
others will use XHR. See the `source` attribute for more details.
250
@method _createStringSource
251
@param {String} source URL or YQL query.
252
@return {Object} DataSource-like object.
254
@for AutoCompleteBase
256
_createStringSource: function (source) {
257
if (this._YQL_SOURCE_REGEX.test(source)) {
258
// Looks like a YQL query.
259
return this._createYQLSource(source);
260
} else if (source.indexOf('{callback}') !== -1) {
261
// Contains a {callback} param and isn't a YQL query, so it must be
263
return this._createJSONPSource(source);
265
// Not a YQL query or JSONP, so we'll assume it's an XHR URL.
266
return this._createIOSource(source);
271
Creates a DataSource-like object that uses the specified YQL query string to
272
create a YQL-based source. See the `source` attribute for details. If no
273
`resultListLocator` is defined, this method will set a best-guess locator
274
that might work for many typical YQL queries.
276
@method _createYQLSource
277
@param {String} source YQL query.
278
@return {Object} DataSource-like object.
280
@for AutoCompleteBase
282
_createYQLSource: function (source) {
284
yqlSource = {type: 'yql'},
285
lastRequest, loading, yqlRequest;
287
if (!that.get(RESULT_LIST_LOCATOR)) {
288
that.set(RESULT_LIST_LOCATOR, that._defaultYQLLocator);
291
function _sendRequest(request) {
292
var query = request.query,
293
env = that.get('yqlEnv'),
294
maxResults = that.get(MAX_RESULTS),
295
callback, opts, yqlQuery;
297
yqlQuery = Lang.sub(source, {
298
maxResults: maxResults > 0 ? maxResults : 1000,
299
request : request.request,
303
if (that._cache && yqlQuery in that._cache) {
304
that[_SOURCE_SUCCESS](that._cache[yqlQuery], request);
308
callback = function (data) {
309
that._cache && (that._cache[yqlQuery] = data);
310
that[_SOURCE_SUCCESS](data, request);
313
opts = {proto: that.get('yqlProtocol')};
315
// Only create a new YQLRequest instance if this is the
316
// first request. For subsequent requests, we'll reuse the
317
// original instance.
319
yqlRequest._callback = callback;
320
yqlRequest._opts = opts;
321
yqlRequest._params.q = yqlQuery;
324
yqlRequest._params.env = env;
327
yqlRequest = new Y.YQLRequest(yqlQuery, {
328
on: {success: callback},
329
allowCache: false // temp workaround until JSONP has per-URL callback proxies
330
}, env ? {env: env} : null, opts);
336
yqlSource.sendRequest = function (request) {
337
// Keep track of the most recent request in case there are multiple
338
// requests while we're waiting for the YQL module to load. Only the
339
// most recent request will be sent.
340
lastRequest = request;
343
// Lazy-load the YQL module if necessary, then overwrite the
344
// sendRequest method to bypass this check in the future.
347
Y.use('yql', function () {
348
yqlSource.sendRequest = _sendRequest;
349
_sendRequest(lastRequest);
358
Default resultListLocator used when a string-based YQL source is set and the
359
implementer hasn't already specified one.
361
@method _defaultYQLLocator
362
@param {Object} response YQL response object.
365
@for AutoCompleteBase
367
_defaultYQLLocator: function (response) {
368
var results = response && response.query && response.query.results,
371
if (results && Lang.isObject(results)) {
372
// If there's only a single value on YQL's results object, that
373
// value almost certainly contains the array of results we want. If
374
// there are 0 or 2+ values, then the values themselves are most
375
// likely the results we want.
376
values = Y.Object.values(results) || [];
377
results = values.length === 1 ? values[0] : values;
379
if (!Lang.isArray(results)) {
390
Returns a formatted XHR URL based on the specified base _url_, _query_, and
391
the current _requestTemplate_ if any.
394
@param {String} url Base URL.
395
@param {Object} request Request object containing `query` and `request`
397
@return {String} Formatted URL.
399
@for AutoCompleteBase
401
_getXHRUrl: function (url, request) {
402
var maxResults = this.get(MAX_RESULTS);
404
if (request.query !== request.request) {
405
// Append the request template to the URL.
406
url += request.request;
409
return Lang.sub(url, {
410
maxResults: maxResults > 0 ? maxResults : 1000,
411
query : encodeURIComponent(request.query)
416
URL formatter passed to `JSONPRequest` instances.
418
@method _jsonpFormatter
420
@param {String} proxy
421
@param {String} query
422
@return {String} Formatted URL
424
@for AutoCompleteBase
426
_jsonpFormatter: function (url, proxy, query) {
427
var maxResults = this.get(MAX_RESULTS),
428
requestTemplate = this.get(REQUEST_TEMPLATE);
430
if (requestTemplate) {
431
url += requestTemplate(query);
434
return Lang.sub(url, {
436
maxResults: maxResults > 0 ? maxResults : 1000,
437
query : encodeURIComponent(query)
442
// Add attributes to AutoCompleteBase.
443
Y.mix(ACBase.ATTRS, {
445
YQL environment file URL to load when the `source` is set to a YQL query.
446
Set this to `null` to use the default Open Data Tables environment file
447
(http://datatables.org/alltables.env).
452
@for AutoCompleteBase
459
URL protocol to use when the `source` is set to a YQL query.
461
@attribute yqlProtocol
464
@for AutoCompleteBase
471
// Tell AutoCompleteBase about the new source types it can now support.
472
Y.mix(ACBase.SOURCE_TYPES, {
473
io : '_createIOSource',
474
jsonp : '_createJSONPSource',
475
object: '_beforeCreateObjectSource', // Run our version before the base version.
476
select: '_createSelectSource',
477
string: '_createStringSource',
478
yql : '_createYQLSource'
482
}, '3.10.3', {"optional": ["io-base", "json-parse", "jsonp", "yql"], "requires": ["autocomplete-base"]});