1 (function($, undefined) { |
|
2 $.extend({ |
|
3 jsonRPC: { |
|
4 // RPC Version Number |
|
5 version: '2.0', |
|
6 |
|
7 // End point URL, sets default in requests if not |
|
8 // specified with the request call |
|
9 endPoint: null, |
|
10 |
|
11 // Default namespace for methods |
|
12 namespace: null, |
|
13 |
|
14 /* |
|
15 * Provides the RPC client with an optional default endpoint and namespace |
|
16 * |
|
17 * @param {object} The params object which can contain |
|
18 * endPoint {string} The default endpoint for RPC requests |
|
19 * namespace {string} The default namespace for RPC requests |
|
20 * cache {boolean} If set to false, it will force requested |
|
21 * pages not to be cached by the browser. Setting cache |
|
22 * to false also appends a query string parameter, |
|
23 * "_=[TIMESTAMP]", to the URL. (Default: true) |
|
24 */ |
|
25 setup: function(params) { |
|
26 this._validateConfigParams(params); |
|
27 this.endPoint = params.endPoint; |
|
28 this.namespace = params.namespace; |
|
29 this.cache = params.cache !== undefined ? params.cache : true; |
|
30 return this; |
|
31 }, |
|
32 |
|
33 /* |
|
34 * Convenience wrapper method to allow you to temporarily set a config parameter |
|
35 * (endPoint or namespace) and ensure it gets set back to what it was before |
|
36 * |
|
37 * @param {object} The params object which can contains |
|
38 * endPoint {string} The default endpoint for RPC requests |
|
39 * namespace {string} The default namespace for RPC requests |
|
40 * @param {function} callback The function to call with the new params in place |
|
41 */ |
|
42 withOptions: function(params, callback) { |
|
43 this._validateConfigParams(params); |
|
44 // No point in running if there isn't a callback received to run |
|
45 if(callback === undefined) throw("No callback specified"); |
|
46 |
|
47 origParams = {endPoint: this.endPoint, namespace: this.namespace}; |
|
48 this.setup(params); |
|
49 callback.call(this); |
|
50 this.setup(origParams); |
|
51 }, |
|
52 |
|
53 /* |
|
54 * Performas a single RPC request |
|
55 * |
|
56 * @param {string} method The name of the rpc method to be called |
|
57 * @param {object} options A collection of object which can contains |
|
58 * params {array} the params array to send along with the request |
|
59 * success {function} a function that will be executed if the request succeeds |
|
60 * error {function} a function that will be executed if the request fails |
|
61 * url {string} the url to send the request to |
|
62 * id {string} the provenance id for this request (defaults to 1) |
|
63 * cache {boolean} If set to false, it will force requested |
|
64 * pages not to be cached by the browser. Setting cache |
|
65 * to false also appends a query string parameter, |
|
66 * "_=[TIMESTAMP]", to the URL. (Default: cache value |
|
67 * set with the setup method) |
|
68 * @return {undefined} |
|
69 */ |
|
70 request: function(method, options) { |
|
71 if(options === undefined) { |
|
72 options = { id: 1 }; |
|
73 } |
|
74 if (options.id === undefined) { |
|
75 options.id = 1; |
|
76 } |
|
77 if (options.cache === undefined) { |
|
78 options.cache = this.cache; |
|
79 } |
|
80 |
|
81 // Validate method arguments |
|
82 this._validateRequestMethod(method); |
|
83 this._validateRequestParams(options.params); |
|
84 this._validateRequestCallbacks(options.success, options.error); |
|
85 |
|
86 // Perform the actual request |
|
87 this._doRequest(JSON.stringify(this._requestDataObj(method, options.params, options.id)), options); |
|
88 |
|
89 return true; |
|
90 }, |
|
91 |
|
92 /* |
|
93 * Submits multiple requests |
|
94 * Takes an array of objects that contain a method and params |
|
95 * |
|
96 * @params {array} requests an array of request object which can contain |
|
97 * method {string} the name of the method |
|
98 * param {object} the params object to be sent with the request |
|
99 * id {string} the provenance id for the request (defaults to an incrementer starting at 1) |
|
100 * @param {object} options A collection of object which can contains |
|
101 * success {function} a function that will be executed if the request succeeds |
|
102 * error {function} a function that will be executed if the request fails |
|
103 * url {string} the url to send the request to |
|
104 * @return {undefined} |
|
105 */ |
|
106 batchRequest: function(requests, options) { |
|
107 if(options === undefined) { |
|
108 options = {}; |
|
109 } |
|
110 |
|
111 // Ensure our requests come in as an array |
|
112 if(!$.isArray(requests) || requests.length === 0) throw("Invalid requests supplied for jsonRPC batchRequest. Must be an array object that contain at least a method attribute"); |
|
113 |
|
114 // Make sure each of our request objects are valid |
|
115 var _that = this; |
|
116 $.each(requests, function(i, req) { |
|
117 _that._validateRequestMethod(req.method); |
|
118 _that._validateRequestParams(req.params); |
|
119 if (req.id === undefined) { |
|
120 req.id = i + 1; |
|
121 } |
|
122 }); |
|
123 this._validateRequestCallbacks(options.success, options.error); |
|
124 |
|
125 var data = [], |
|
126 request; |
|
127 |
|
128 // Prepare our request object |
|
129 for(var i = 0; i<requests.length; i++) { |
|
130 request = requests[i]; |
|
131 data.push(this._requestDataObj(request.method, request.params, request.id)); |
|
132 } |
|
133 |
|
134 this._doRequest(JSON.stringify(data), options); |
|
135 }, |
|
136 |
|
137 // Validate a params hash |
|
138 _validateConfigParams: function(params) { |
|
139 if(params === undefined) { |
|
140 throw("No params specified"); |
|
141 } |
|
142 else { |
|
143 if(params.endPoint && typeof(params.endPoint) !== 'string'){ |
|
144 throw("endPoint must be a string"); |
|
145 } |
|
146 if(params.namespace && typeof(params.namespace) !== 'string'){ |
|
147 throw("namespace must be a string"); |
|
148 } |
|
149 } |
|
150 }, |
|
151 |
|
152 // Request method must be a string |
|
153 _validateRequestMethod: function(method) { |
|
154 if(typeof(method) !== 'string') throw("Invalid method supplied for jsonRPC request") |
|
155 return true; |
|
156 }, |
|
157 |
|
158 // Validate request params. Must be a) empty, b) an object (e.g. {}), or c) an array |
|
159 _validateRequestParams: function(params) { |
|
160 if(!(params === null || |
|
161 params === undefined || |
|
162 typeof(params) === 'object' || |
|
163 $.isArray(params))) { |
|
164 throw("Invalid params supplied for jsonRPC request. It must be empty, an object or an array."); |
|
165 } |
|
166 return true; |
|
167 }, |
|
168 |
|
169 _validateRequestCallbacks: function(success, error) { |
|
170 // Make sure callbacks are either empty or a function |
|
171 if(success !== undefined && |
|
172 typeof(success) !== 'function') throw("Invalid success callback supplied for jsonRPC request"); |
|
173 if(error !== undefined && |
|
174 typeof(error) !== 'function') throw("Invalid error callback supplied for jsonRPC request"); |
|
175 return true; |
|
176 }, |
|
177 |
|
178 // Internal method used for generic ajax requests |
|
179 _doRequest: function(data, options) { |
|
180 var _that = this; |
|
181 $.ajax({ |
|
182 type: 'POST', |
|
183 async: false !== options.async, |
|
184 dataType: 'json', |
|
185 contentType: 'application/json', |
|
186 url: this._requestUrl((options.endPoint || options.url), options.cache), |
|
187 data: data, |
|
188 cache: options.cache, |
|
189 processData: false, |
|
190 error: function(json) { |
|
191 _that._requestError.call(_that, json, options.error); |
|
192 }, |
|
193 success: function(json) { |
|
194 _that._requestSuccess.call(_that, json, options.success, options.error); |
|
195 } |
|
196 }) |
|
197 }, |
|
198 |
|
199 // Determines the appropriate request URL to call for a request |
|
200 _requestUrl: function(url, cache) { |
|
201 url = url || this.endPoint; |
|
202 if (!cache) { |
|
203 if (url.indexOf("?") < 0) { |
|
204 url += '?tm=' + new Date().getTime(); |
|
205 } |
|
206 else { |
|
207 url += "&tm=" + new Date().getTime(); |
|
208 } |
|
209 } |
|
210 return url; |
|
211 }, |
|
212 |
|
213 // Creates an RPC suitable request object |
|
214 _requestDataObj: function(method, params, id) { |
|
215 var dataObj = { |
|
216 jsonrpc: this.version, |
|
217 method: this.namespace ? this.namespace +'.'+ method : method, |
|
218 id: id |
|
219 } |
|
220 if(params !== undefined) { |
|
221 dataObj.params = params; |
|
222 } |
|
223 return dataObj; |
|
224 }, |
|
225 |
|
226 // Handles calling of error callback function |
|
227 _requestError: function(json, error) { |
|
228 if (error !== undefined && typeof(error) === 'function') { |
|
229 if(typeof(json.responseText) === 'string') { |
|
230 try { |
|
231 error(eval ( '(' + json.responseText + ')' )); |
|
232 } |
|
233 catch(e) { |
|
234 error(this._response()); |
|
235 } |
|
236 } |
|
237 else { |
|
238 error(this._response()); |
|
239 } |
|
240 } |
|
241 }, |
|
242 |
|
243 // Handles calling of RPC success, calls error callback |
|
244 // if the response contains an error |
|
245 // TODO: Handle error checking for batch requests |
|
246 _requestSuccess: function(json, success, error) { |
|
247 var response = this._response(json); |
|
248 |
|
249 // If we've encountered an error in the response, trigger the error callback if it exists |
|
250 if(response.error && typeof(error) === 'function') { |
|
251 error(response); |
|
252 return; |
|
253 } |
|
254 |
|
255 // Otherwise, successful request, run the success request if it exists |
|
256 if(typeof(success) === 'function') { |
|
257 success(response); |
|
258 } |
|
259 }, |
|
260 |
|
261 // Returns a generic RPC 2.0 compatible response object |
|
262 _response: function(json) { |
|
263 if (json === undefined) { |
|
264 return { |
|
265 error: 'Internal server error', |
|
266 version: '2.0' |
|
267 }; |
|
268 } |
|
269 else { |
|
270 try { |
|
271 if(typeof(json) === 'string') { |
|
272 json = eval ( '(' + json + ')' ); |
|
273 } |
|
274 |
|
275 if (($.isArray(json) && json.length > 0 && json[0].jsonrpc !== '2.0') || |
|
276 (!$.isArray(json) && json.jsonrpc !== '2.0')) { |
|
277 throw 'Version error'; |
|
278 } |
|
279 |
|
280 return json; |
|
281 } |
|
282 catch (e) { |
|
283 return { |
|
284 error: 'Internal server error: ' + e, |
|
285 version: '2.0' |
|
286 } |
|
287 } |
|
288 } |
|
289 } |
|
290 |
|
291 } |
|
292 }); |
|
293 })(jQuery); |
|