|
1 /* |
|
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/ |
|
6 */ |
|
7 |
|
8 YUI.add('jsonp', function (Y, NAME) { |
|
9 |
|
10 var isFunction = Y.Lang.isFunction; |
|
11 |
|
12 /** |
|
13 * <p>Provides a JSONPRequest class for repeated JSONP calls, and a convenience |
|
14 * method Y.jsonp(url, callback) to instantiate and send a JSONP request.</p> |
|
15 * |
|
16 * <p>Both the constructor as well as the convenience function take two |
|
17 * parameters: a url string and a callback.</p> |
|
18 * |
|
19 * <p>The url provided must include the placeholder string |
|
20 * "{callback}" which will be replaced by a dynamically |
|
21 * generated routing function to pass the data to your callback function. |
|
22 * An example url might look like |
|
23 * "http://example.com/service?callback={callback}".</p> |
|
24 * |
|
25 * <p>The second parameter can be a callback function that accepts the JSON |
|
26 * payload as its argument, or a configuration object supporting the keys:</p> |
|
27 * <ul> |
|
28 * <li>on - map of callback subscribers |
|
29 * <ul> |
|
30 * <li>success - function handler for successful transmission</li> |
|
31 * <li>failure - function handler for failed transmission</li> |
|
32 * <li>timeout - function handler for transactions that timeout</li> |
|
33 * </ul> |
|
34 * </li> |
|
35 * <li>format - override function for inserting the proxy name in the url</li> |
|
36 * <li>timeout - the number of milliseconds to wait before giving up</li> |
|
37 * <li>context - becomes <code>this</code> in the callbacks</li> |
|
38 * <li>args - array of subsequent parameters to pass to the callbacks</li> |
|
39 * <li>allowCache - use the same proxy name for all requests? (boolean)</li> |
|
40 * </ul> |
|
41 * |
|
42 * @module jsonp |
|
43 * @class JSONPRequest |
|
44 * @constructor |
|
45 * @param url {String} the url of the JSONP service |
|
46 * @param callback {Object|Function} the default callback configuration or |
|
47 * success handler |
|
48 */ |
|
49 function JSONPRequest() { |
|
50 this._init.apply(this, arguments); |
|
51 } |
|
52 |
|
53 JSONPRequest.prototype = { |
|
54 /** |
|
55 * Set up the success and failure handlers and the regex pattern used |
|
56 * to insert the temporary callback name in the url. |
|
57 * |
|
58 * @method _init |
|
59 * @param url {String} the url of the JSONP service |
|
60 * @param callback {Object|Function} Optional success callback or config |
|
61 * object containing success and failure functions and |
|
62 * the url regex. |
|
63 * @protected |
|
64 */ |
|
65 _init : function (url, callback) { |
|
66 this.url = url; |
|
67 |
|
68 /** |
|
69 * Map of the number of requests currently pending responses per |
|
70 * generated proxy. Used to ensure the proxy is not flushed if the |
|
71 * request times out and there is a timeout handler and success |
|
72 * handler, and used by connections configured to allowCache to make |
|
73 * sure the proxy isn't deleted until the last response has returned. |
|
74 * |
|
75 * @property _requests |
|
76 * @private |
|
77 * @type {Object} |
|
78 */ |
|
79 this._requests = {}; |
|
80 |
|
81 /** |
|
82 * Map of the number of timeouts received from the destination url |
|
83 * by generated proxy. Used to ensure the proxy is not flushed if the |
|
84 * request times out and there is a timeout handler and success |
|
85 * handler, and used by connections configured to allowCache to make |
|
86 * sure the proxy isn't deleted until the last response has returned. |
|
87 * |
|
88 * @property _timeouts |
|
89 * @private |
|
90 * @type {Object} |
|
91 */ |
|
92 this._timeouts = {}; |
|
93 |
|
94 // Accept a function, an object, or nothing |
|
95 callback = (isFunction(callback)) ? |
|
96 { on: { success: callback } } : |
|
97 callback || {}; |
|
98 |
|
99 var subs = callback.on || {}; |
|
100 |
|
101 if (!subs.success) { |
|
102 subs.success = this._defaultCallback(url, callback); |
|
103 } |
|
104 |
|
105 // Apply defaults and store |
|
106 this._config = Y.merge({ |
|
107 context: this, |
|
108 args : [], |
|
109 format : this._format, |
|
110 allowCache: false |
|
111 }, callback, { on: subs }); |
|
112 }, |
|
113 |
|
114 /** |
|
115 * Override this method to provide logic to default the success callback if |
|
116 * it is not provided at construction. This is overridden by jsonp-url to |
|
117 * parse the callback from the url string. |
|
118 * |
|
119 * @method _defaultCallback |
|
120 * @param url {String} the url passed at construction |
|
121 * @param config {Object} (optional) the config object passed at |
|
122 * construction |
|
123 * @return {Function} |
|
124 */ |
|
125 _defaultCallback: function () {}, |
|
126 |
|
127 /** |
|
128 * Issues the JSONP request. |
|
129 * |
|
130 * @method send |
|
131 * @param args* {any} any additional arguments to pass to the url formatter |
|
132 * beyond the base url and the proxy function name |
|
133 * @chainable |
|
134 */ |
|
135 send : function () { |
|
136 var self = this, |
|
137 args = Y.Array(arguments, 0, true), |
|
138 config = self._config, |
|
139 proxy = self._proxy || Y.guid(), |
|
140 url; |
|
141 |
|
142 // TODO: support allowCache as time value |
|
143 if (config.allowCache) { |
|
144 self._proxy = proxy; |
|
145 } |
|
146 |
|
147 if (self._requests[proxy] === undefined) { |
|
148 self._requests[proxy] = 0; |
|
149 } |
|
150 if (self._timeouts[proxy] === undefined) { |
|
151 self._timeouts[proxy] = 0; |
|
152 } |
|
153 self._requests[proxy]++; |
|
154 |
|
155 |
|
156 args.unshift(self.url, 'YUI.Env.JSONP.' + proxy); |
|
157 url = config.format.apply(self, args); |
|
158 |
|
159 if (!config.on.success) { |
|
160 return self; |
|
161 } |
|
162 |
|
163 function wrap(fn, isTimeout) { |
|
164 return (isFunction(fn)) ? |
|
165 function (data) { |
|
166 var execute = true, |
|
167 counter = '_requests'; |
|
168 |
|
169 //if (config.allowCache) { |
|
170 // A lot of wrangling to make sure timeouts result in |
|
171 // fewer success callbacks, but the proxy is properly |
|
172 // cleaned up. |
|
173 if (isTimeout) { |
|
174 ++self._timeouts[proxy]; |
|
175 --self._requests[proxy]; |
|
176 } else { |
|
177 if (!self._requests[proxy]) { |
|
178 execute = false; |
|
179 counter = '_timeouts'; |
|
180 } |
|
181 --self[counter][proxy]; |
|
182 } |
|
183 //} |
|
184 |
|
185 if (!self._requests[proxy] && !self._timeouts[proxy]) { |
|
186 delete YUI.Env.JSONP[proxy]; |
|
187 } |
|
188 |
|
189 if (execute) { |
|
190 fn.apply(config.context, [data].concat(config.args)); |
|
191 } |
|
192 } : |
|
193 null; |
|
194 } |
|
195 |
|
196 // Temporary un-sandboxed function alias |
|
197 // TODO: queuing |
|
198 YUI.Env.JSONP[proxy] = wrap(config.on.success); |
|
199 |
|
200 // Y.Get transactions block each other by design, but can easily |
|
201 // be made non-blocking by just calling execute() on the transaction. |
|
202 // https://github.com/yui/yui3/pull/393#issuecomment-11961608 |
|
203 Y.Get.js(url, { |
|
204 onFailure : wrap(config.on.failure), |
|
205 onTimeout : wrap(config.on.timeout, true), |
|
206 timeout : config.timeout, |
|
207 charset : config.charset, |
|
208 attributes: config.attributes, |
|
209 async : config.async |
|
210 }).execute(); |
|
211 |
|
212 return self; |
|
213 }, |
|
214 |
|
215 /** |
|
216 * Default url formatter. Looks for callback= in the url and appends it |
|
217 * if not present. The supplied proxy name will be assigned to the query |
|
218 * param. Override this method by passing a function as the |
|
219 * "format" property in the config object to the constructor. |
|
220 * |
|
221 * @method _format |
|
222 * @param url { String } the original url |
|
223 * @param proxy {String} the function name that will be used as a proxy to |
|
224 * the configured callback methods. |
|
225 * @param args* {any} additional args passed to send() |
|
226 * @return {String} fully qualified JSONP url |
|
227 * @protected |
|
228 */ |
|
229 _format: function (url, proxy) { |
|
230 return url.replace(/\{callback\}/, proxy); |
|
231 } |
|
232 }; |
|
233 |
|
234 Y.JSONPRequest = JSONPRequest; |
|
235 |
|
236 /** |
|
237 * |
|
238 * @method jsonp |
|
239 * @param url {String} the url of the JSONP service with the {callback} |
|
240 * placeholder where the callback function name typically goes. |
|
241 * @param c {Function|Object} Callback function accepting the JSON payload |
|
242 * as its argument, or a configuration object (see above). |
|
243 * @param args* {any} additional arguments to pass to send() |
|
244 * @return {JSONPRequest} |
|
245 * @static |
|
246 * @for YUI |
|
247 */ |
|
248 Y.jsonp = function (url,c) { |
|
249 var req = new Y.JSONPRequest(url,c); |
|
250 return req.send.apply(req, Y.Array(arguments, 2, true)); |
|
251 }; |
|
252 |
|
253 if (!YUI.Env.JSONP) { |
|
254 YUI.Env.JSONP = {}; |
|
255 } |
|
256 |
|
257 |
|
258 }, '3.10.3', {"requires": ["get", "oop"]}); |