PageRenderTime 26ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/yui/3.3.0/autocomplete/autocomplete-sources.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 394 lines | 197 code | 56 blank | 141 comment | 30 complexity | 1b80ea17b76f79e15e5668aa9152648f MD5 | raw file
  1. YUI.add('autocomplete-sources', function(Y) {
  2. /**
  3. * Mixes support for JSONP and YQL result sources into AutoCompleteBase.
  4. *
  5. * @module autocomplete
  6. * @submodule autocomplete-sources
  7. */
  8. var Lang = Y.Lang,
  9. _SOURCE_SUCCESS = '_sourceSuccess',
  10. MAX_RESULTS = 'maxResults',
  11. REQUEST_TEMPLATE = 'requestTemplate',
  12. RESULT_LIST_LOCATOR = 'resultListLocator';
  13. function ACSources() {}
  14. ACSources.prototype = {
  15. /**
  16. * Regular expression used to determine whether a String source is a YQL
  17. * query.
  18. *
  19. * @property _YQL_SOURCE_REGEX
  20. * @type RegExp
  21. * @protected
  22. * @for AutoCompleteBase
  23. */
  24. _YQL_SOURCE_REGEX: /^(?:select|set|use)\s+/i,
  25. /**
  26. * Creates a DataSource-like object that uses <code>Y.io</code> as a source.
  27. * See the <code>source</code> attribute for more details.
  28. *
  29. * @method _createIOSource
  30. * @param {String} source URL.
  31. * @return {Object} DataSource-like object.
  32. * @protected
  33. * @for AutoCompleteBase
  34. */
  35. _createIOSource: function (source) {
  36. var cache = {},
  37. ioSource = {},
  38. that = this,
  39. ioRequest, lastRequest, loading;
  40. ioSource.sendRequest = function (request) {
  41. var _sendRequest = function (request) {
  42. var query = request.request,
  43. maxResults, requestTemplate, url;
  44. if (cache[query]) {
  45. that[_SOURCE_SUCCESS](cache[query], request);
  46. } else {
  47. maxResults = that.get(MAX_RESULTS);
  48. requestTemplate = that.get(REQUEST_TEMPLATE);
  49. url = source;
  50. if (requestTemplate) {
  51. url += requestTemplate(query);
  52. }
  53. url = Lang.sub(url, {
  54. maxResults: maxResults > 0 ? maxResults : 1000,
  55. query : encodeURIComponent(query)
  56. });
  57. // Cancel any outstanding requests.
  58. if (ioRequest && ioRequest.isInProgress()) {
  59. ioRequest.abort();
  60. }
  61. ioRequest = Y.io(url, {
  62. on: {
  63. success: function (tid, response) {
  64. var data;
  65. try {
  66. data = Y.JSON.parse(response.responseText);
  67. } catch (ex) {
  68. Y.error('JSON parse error', ex);
  69. }
  70. if (data) {
  71. cache[query] = data;
  72. that[_SOURCE_SUCCESS](data, request);
  73. }
  74. }
  75. }
  76. });
  77. }
  78. };
  79. // Keep track of the most recent request in case there are multiple
  80. // requests while we're waiting for the IO module to load. Only the
  81. // most recent request will be sent.
  82. lastRequest = request;
  83. if (!loading) {
  84. loading = true;
  85. // Lazy-load the io and json-parse modules if necessary, then
  86. // overwrite the sendRequest method to bypass this check in the
  87. // future.
  88. Y.use('io-base', 'json-parse', function () {
  89. ioSource.sendRequest = _sendRequest;
  90. _sendRequest(lastRequest);
  91. });
  92. }
  93. };
  94. return ioSource;
  95. },
  96. /**
  97. * Creates a DataSource-like object that uses the specified JSONPRequest
  98. * instance as a source. See the <code>source</code> attribute for more
  99. * details.
  100. *
  101. * @method _createJSONPSource
  102. * @param {JSONPRequest|String} source URL string or JSONPRequest instance.
  103. * @return {Object} DataSource-like object.
  104. * @protected
  105. * @for AutoCompleteBase
  106. */
  107. _createJSONPSource: function (source) {
  108. var cache = {},
  109. jsonpSource = {},
  110. that = this,
  111. lastRequest, loading;
  112. jsonpSource.sendRequest = function (request) {
  113. var _sendRequest = function (request) {
  114. var query = request.request;
  115. if (cache[query]) {
  116. that[_SOURCE_SUCCESS](cache[query], request);
  117. } else {
  118. // Hack alert: JSONPRequest currently doesn't support
  119. // per-request callbacks, so we're reaching into the protected
  120. // _config object to make it happen.
  121. //
  122. // This limitation is mentioned in the following JSONP
  123. // enhancement ticket:
  124. //
  125. // http://yuilibrary.com/projects/yui3/ticket/2529371
  126. source._config.on.success = function (data) {
  127. cache[query] = data;
  128. that[_SOURCE_SUCCESS](data, request);
  129. };
  130. source.send(query);
  131. }
  132. };
  133. // Keep track of the most recent request in case there are multiple
  134. // requests while we're waiting for the JSONP module to load. Only
  135. // the most recent request will be sent.
  136. lastRequest = request;
  137. if (!loading) {
  138. loading = true;
  139. // Lazy-load the JSONP module if necessary, then overwrite the
  140. // sendRequest method to bypass this check in the future.
  141. Y.use('jsonp', function () {
  142. // Turn the source into a JSONPRequest instance if it isn't
  143. // one already.
  144. if (!(source instanceof Y.JSONPRequest)) {
  145. source = new Y.JSONPRequest(source, {
  146. format: Y.bind(that._jsonpFormatter, that)
  147. });
  148. }
  149. jsonpSource.sendRequest = _sendRequest;
  150. _sendRequest(lastRequest);
  151. });
  152. }
  153. };
  154. return jsonpSource;
  155. },
  156. /**
  157. * Creates a DataSource-like object that calls the specified URL or
  158. * executes the specified YQL query for results. If the string starts
  159. * with "select ", "use ", or "set " (case-insensitive), it's assumed to be
  160. * a YQL query; otherwise, it's assumed to be a URL (which may be absolute
  161. * or relative). URLs containing a "{callback}" placeholder are assumed to
  162. * be JSONP URLs; all others will use XHR. See the <code>source</code>
  163. * attribute for more details.
  164. *
  165. * @method _createStringSource
  166. * @param {String} source URL or YQL query.
  167. * @return {Object} DataSource-like object.
  168. * @protected
  169. * @for AutoCompleteBase
  170. */
  171. _createStringSource: function (source) {
  172. if (this._YQL_SOURCE_REGEX.test(source)) {
  173. // Looks like a YQL query.
  174. return this._createYQLSource(source);
  175. } else if (source.indexOf('{callback}') !== -1) {
  176. // Contains a {callback} param and isn't a YQL query, so it must be
  177. // JSONP.
  178. return this._createJSONPSource(source);
  179. } else {
  180. // Not a YQL query or JSONP, so we'll assume it's an XHR URL.
  181. return this._createIOSource(source);
  182. }
  183. },
  184. /**
  185. * Creates a DataSource-like object that uses the specified YQL query string
  186. * to create a YQL-based source. See the <code>source</code> attribute for
  187. * details. If no <code>resultListLocator</code> is defined, this method
  188. * will set a best-guess locator that might work for many typical YQL
  189. * queries.
  190. *
  191. * @method _createYQLSource
  192. * @param {String} source YQL query.
  193. * @return {Object} DataSource-like object.
  194. * @protected
  195. * @for AutoCompleteBase
  196. */
  197. _createYQLSource: function (source) {
  198. var cache = {},
  199. yqlSource = {},
  200. that = this,
  201. lastRequest, loading;
  202. if (!this.get(RESULT_LIST_LOCATOR)) {
  203. this.set(RESULT_LIST_LOCATOR, this._defaultYQLLocator);
  204. }
  205. yqlSource.sendRequest = function (request) {
  206. var yqlRequest,
  207. _sendRequest = function (request) {
  208. var query = request.request,
  209. callback, env, maxResults, opts, yqlQuery;
  210. if (cache[query]) {
  211. that[_SOURCE_SUCCESS](cache[query], request);
  212. } else {
  213. callback = function (data) {
  214. cache[query] = data;
  215. that[_SOURCE_SUCCESS](data, request);
  216. };
  217. env = that.get('yqlEnv');
  218. maxResults = that.get(MAX_RESULTS);
  219. opts = {proto: that.get('yqlProtocol')};
  220. yqlQuery = Lang.sub(source, {
  221. maxResults: maxResults > 0 ? maxResults : 1000,
  222. query : query
  223. });
  224. // Only create a new YQLRequest instance if this is the
  225. // first request. For subsequent requests, we'll reuse the
  226. // original instance.
  227. if (yqlRequest) {
  228. yqlRequest._callback = callback;
  229. yqlRequest._opts = opts;
  230. yqlRequest._params.q = yqlQuery;
  231. if (env) {
  232. yqlRequest._params.env = env;
  233. }
  234. } else {
  235. yqlRequest = new Y.YQLRequest(yqlQuery, {
  236. on: {success: callback},
  237. allowCache: false // temp workaround until JSONP has per-URL callback proxies
  238. }, env ? {env: env} : null, opts);
  239. }
  240. yqlRequest.send();
  241. }
  242. };
  243. // Keep track of the most recent request in case there are multiple
  244. // requests while we're waiting for the YQL module to load. Only the
  245. // most recent request will be sent.
  246. lastRequest = request;
  247. if (!loading) {
  248. // Lazy-load the YQL module if necessary, then overwrite the
  249. // sendRequest method to bypass this check in the future.
  250. loading = true;
  251. Y.use('yql', function () {
  252. yqlSource.sendRequest = _sendRequest;
  253. _sendRequest(lastRequest);
  254. });
  255. }
  256. };
  257. return yqlSource;
  258. },
  259. /**
  260. * Default resultListLocator used when a string-based YQL source is set and
  261. * the implementer hasn't already specified one.
  262. *
  263. * @method _defaultYQLLocator
  264. * @param {Object} response YQL response object.
  265. * @return {Array}
  266. * @protected
  267. * @for AutoCompleteBase
  268. */
  269. _defaultYQLLocator: function (response) {
  270. var results = response && response.query && response.query.results,
  271. values;
  272. if (results && Lang.isObject(results)) {
  273. // If there's only a single value on YQL's results object, that
  274. // value almost certainly contains the array of results we want. If
  275. // there are 0 or 2+ values, then the values themselves are most
  276. // likely the results we want.
  277. values = Y.Object.values(results) || [];
  278. results = values.length === 1 ? values[0] : values;
  279. if (!Lang.isArray(results)) {
  280. results = [results];
  281. }
  282. } else {
  283. results = [];
  284. }
  285. return results;
  286. },
  287. /**
  288. * URL formatter passed to <code>JSONPRequest</code> instances.
  289. *
  290. * @method _jsonpFormatter
  291. * @param {String} url
  292. * @param {String} proxy
  293. * @param {String} query
  294. * @return {String} Formatted URL
  295. * @protected
  296. * @for AutoCompleteBase
  297. */
  298. _jsonpFormatter: function (url, proxy, query) {
  299. var maxResults = this.get(MAX_RESULTS),
  300. requestTemplate = this.get(REQUEST_TEMPLATE);
  301. if (requestTemplate) {
  302. url += requestTemplate(query);
  303. }
  304. return Lang.sub(url, {
  305. callback : proxy,
  306. maxResults: maxResults > 0 ? maxResults : 1000,
  307. query : encodeURIComponent(query)
  308. });
  309. }
  310. };
  311. ACSources.ATTRS = {
  312. /**
  313. * YQL environment file URL to load when the <code>source</code> is set to
  314. * a YQL query. Set this to <code>null</code> to use the default Open Data
  315. * Tables environment file (http://datatables.org/alltables.env).
  316. *
  317. * @attribute yqlEnv
  318. * @type String
  319. * @default null
  320. * @for AutoCompleteBase
  321. */
  322. yqlEnv: {
  323. value: null
  324. },
  325. /**
  326. * URL protocol to use when the <code>source</code> is set to a YQL query.
  327. *
  328. * @attribute yqlProtocol
  329. * @type String
  330. * @default 'http'
  331. * @for AutoCompleteBase
  332. */
  333. yqlProtocol: {
  334. value: 'http'
  335. }
  336. };
  337. Y.Base.mix(Y.AutoCompleteBase, [ACSources]);
  338. }, '@VERSION@' ,{optional:['io-base', 'json-parse', 'jsonp', 'yql'], requires:['autocomplete-base']});