]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to\r | |
3 | * server side methods on the client (a remote procedure call (RPC) type of\r | |
4 | * connection where the client can initiate a procedure on the server).\r | |
5 | * \r | |
6 | * This allows for code to be organized in a fashion that is maintainable,\r | |
7 | * while providing a clear path between client and server, something that is\r | |
8 | * not always apparent when using URLs.\r | |
9 | * \r | |
10 | * To accomplish this the server-side needs to describe what classes and methods\r | |
11 | * are available on the client-side. This configuration will typically be\r | |
12 | * outputted by the server-side Ext Direct stack when the API description is built.\r | |
13 | */\r | |
14 | Ext.define('Ext.direct.RemotingProvider', {\r | |
15 | extend: 'Ext.direct.JsonProvider', \r | |
16 | alias: 'direct.remotingprovider',\r | |
17 | \r | |
18 | requires: [\r | |
19 | 'Ext.util.MixedCollection', \r | |
20 | 'Ext.util.DelayedTask', \r | |
21 | 'Ext.direct.Transaction',\r | |
22 | 'Ext.direct.RemotingMethod',\r | |
23 | 'Ext.direct.Manager'\r | |
24 | ],\r | |
25 | \r | |
26 | type: 'remoting',\r | |
27 | \r | |
28 | /**\r | |
29 | * @cfg {Object} actions\r | |
30 | *\r | |
31 | * Object literal defining the server side actions and methods. For example, if\r | |
32 | * the Provider is configured with:\r | |
33 | *\r | |
34 | * // each property within the 'actions' object represents a server side Class\r | |
35 | * actions: {\r | |
36 | * // array of methods in each server side Class to be stubbed out on client\r | |
37 | * TestAction: [{\r | |
38 | * name: 'doEcho', // stub method will be TestAction.doEcho\r | |
39 | * len: 1,\r | |
40 | * batched: false // always send requests immediately for this method\r | |
41 | * }, {\r | |
42 | * name: 'multiply', // name of method\r | |
43 | * len: 2 // The number of parameters that will be used to create an\r | |
44 | * // array of data to send to the server side function.\r | |
45 | * }, {\r | |
46 | * name: 'doForm',\r | |
47 | * formHandler: true // tells the client that this method handles form calls\r | |
48 | * }],\r | |
49 | * \r | |
50 | * // These methods will be created in nested namespace TestAction.Foo\r | |
51 | * 'TestAction.Foo': [{\r | |
52 | * name: 'ordered', // stub method will be TestAction.Foo.ordered\r | |
53 | * len: 1\r | |
54 | * }, {\r | |
55 | * name: 'noParams', // this method does not accept any parameters\r | |
56 | * len: 0\r | |
57 | * }, {\r | |
58 | * name: 'named', // stub method will be TestAction.Foo.named\r | |
59 | * params: ['foo', 'bar'] // parameters are passed by name\r | |
60 | * }, {\r | |
61 | * name: 'namedNoStrict',\r | |
62 | * params: [], // this method accepts parameters by name\r | |
63 | * strict: false // but does not check if they are required\r | |
64 | * // and will pass any to the server side\r | |
65 | * }]\r | |
66 | * }\r | |
67 | *\r | |
68 | * Note that starting with 4.2, dotted Action names will generate nested objects.\r | |
69 | * If you wish to reverse to previous behavior, set {@link #cfg-disableNestedActions}\r | |
70 | * to `true`.\r | |
71 | *\r | |
72 | * In the following example a *client side* handler is used to call the\r | |
73 | * server side method "multiply" in the server-side "TestAction" Class:\r | |
74 | *\r | |
75 | * TestAction.multiply(\r | |
76 | * // pass two arguments to server, so specify len=2\r | |
77 | * 2, 4,\r | |
78 | * \r | |
79 | * // callback function after the server is called\r | |
80 | * // result: the result returned by the server\r | |
81 | * // e: Ext.direct.RemotingEvent object\r | |
82 | * // success: true or false\r | |
83 | * // options: options to be applied to method call and passed to callback\r | |
84 | * function (result, e, success, options) {\r | |
85 | * var t, action, method;\r | |
86 | * \r | |
87 | * t = e.getTransaction();\r | |
88 | * action = t.action; // server side Class called\r | |
89 | * method = t.method; // server side method called\r | |
90 | * \r | |
91 | * if (e.status) {\r | |
92 | * var answer = Ext.encode(result); // 8\r | |
93 | * }\r | |
94 | * else {\r | |
95 | * var msg = e.message; // failure message\r | |
96 | * }\r | |
97 | * },\r | |
98 | * \r | |
99 | * // Scope to call the callback in (optional)\r | |
100 | * window,\r | |
101 | * \r | |
102 | * // Options to apply to this method call. This can include\r | |
103 | * // Ajax.request() options; only `timeout` is supported at this time.\r | |
104 | * // When timeout is set for a method call, it will be executed immediately\r | |
105 | * // without buffering.\r | |
106 | * // The same options object is passed to the callback so it's possible\r | |
107 | * // to "forward" some data when needed.\r | |
108 | * {\r | |
109 | * timeout: 60000, // milliseconds\r | |
110 | * foo: 'bar'\r | |
111 | * }\r | |
112 | * );\r | |
113 | *\r | |
114 | * In the example above, the server side "multiply" function will be passed two\r | |
115 | * arguments (2 and 4). The "multiply" method should return the value 8 which will be\r | |
116 | * available as the `result` in the callback example above. \r | |
117 | */\r | |
118 | \r | |
119 | /**\r | |
120 | * @cfg {Boolean} [disableNestedActions=false]\r | |
121 | * In versions prior to 4.2, using dotted Action names was not really meaningful,\r | |
122 | * because it generated flat {@link #cfg-namespace} object with dotted property names.\r | |
123 | * For example, take this API declaration:\r | |
124 | *\r | |
125 | * {\r | |
126 | * actions: {\r | |
127 | * TestAction: [{\r | |
128 | * name: 'foo',\r | |
129 | * len: 1\r | |
130 | * }],\r | |
131 | * 'TestAction.Foo' [{\r | |
132 | * name: 'bar',\r | |
133 | * len: 1\r | |
134 | * }]\r | |
135 | * },\r | |
136 | * namespace: 'MyApp'\r | |
137 | * }\r | |
138 | *\r | |
139 | * Before 4.2, that would generate the following API object:\r | |
140 | *\r | |
141 | * window.MyApp = {\r | |
142 | * TestAction: {\r | |
143 | * foo: function() { ... }\r | |
144 | * },\r | |
145 | * 'TestAction.Foo': {\r | |
146 | * bar: function() { ... }\r | |
147 | * }\r | |
148 | * }\r | |
149 | *\r | |
150 | * In Ext JS 4.2, we introduced new namespace handling behavior. Now the same API object\r | |
151 | * will be like this:\r | |
152 | *\r | |
153 | * window.MyApp = {\r | |
154 | * TestAction: {\r | |
155 | * foo: function() { ... },\r | |
156 | *\r | |
157 | * Foo: {\r | |
158 | * bar: function() { ... }\r | |
159 | * }\r | |
160 | * }\r | |
161 | * }\r | |
162 | *\r | |
163 | * Instead of addressing Action methods array-style `MyApp['TestAction.Foo'].bar()`,\r | |
164 | * now it is possible to use object addressing: `MyApp.TestAction.Foo.bar()`.\r | |
165 | *\r | |
166 | * If you find this behavior undesirable, set this config option to `true`.\r | |
167 | */\r | |
168 | \r | |
169 | /**\r | |
170 | * @cfg {String/Object} namespace\r | |
171 | *\r | |
172 | * Namespace for the Remoting Provider (defaults to `Ext.global`).\r | |
173 | * Explicitly specify the namespace Object, or specify a String to have a\r | |
174 | * {@link Ext#namespace namespace} created implicitly.\r | |
175 | */\r | |
176 | \r | |
177 | /**\r | |
178 | * @cfg {String} url\r | |
179 | *\r | |
180 | * **Required**. The url to connect to the {@link Ext.direct.Manager} server-side router. \r | |
181 | */\r | |
182 | \r | |
183 | /**\r | |
184 | * @cfg {String} [enableUrlEncode=data]\r | |
185 | *\r | |
186 | * Specify which param will hold the arguments for the method.\r | |
187 | */\r | |
188 | \r | |
189 | /**\r | |
190 | * @cfg {Number/Boolean} [enableBuffer=10]\r | |
191 | *\r | |
192 | * `true` or `false` to enable or disable combining of method\r | |
193 | * calls. If a number is specified this is the amount of time in milliseconds\r | |
194 | * to wait before sending a batched request.\r | |
195 | *\r | |
196 | * Calls which are received within the specified timeframe will be\r | |
197 | * concatenated together and sent in a single request, optimizing the\r | |
198 | * application by reducing the amount of round trips that have to be made\r | |
199 | * to the server. To cancel buffering for some particular invocations, pass\r | |
200 | * `timeout` parameter in `options` object for that method call.\r | |
201 | */\r | |
202 | enableBuffer: 10,\r | |
203 | \r | |
204 | /**\r | |
205 | * @cfg {Number} bufferLimit The maximum number of requests to batch together.\r | |
206 | * By default, an unlimited number of requests will be batched. This option will\r | |
207 | * allow to wait only for a certain number of Direct method calls before\r | |
208 | * dispatching a request to the server, even if {@link #enableBuffer} timeout\r | |
209 | * has not yet expired.\r | |
210 | * \r | |
211 | * Note that this option does nothing if {@link #enableBuffer} is set to `false`.\r | |
212 | */\r | |
213 | bufferLimit: Number.MAX_VALUE,\r | |
214 | \r | |
215 | /**\r | |
216 | * @cfg {Number} [maxRetries=1]\r | |
217 | *\r | |
218 | * Number of times to re-attempt delivery on failure of a call.\r | |
219 | */\r | |
220 | maxRetries: 1,\r | |
221 | \r | |
222 | /**\r | |
223 | * @cfg {Number} [timeout]\r | |
224 | *\r | |
225 | * The timeout to use for each request.\r | |
226 | */\r | |
227 | \r | |
228 | /**\r | |
229 | * @event beforecall\r | |
230 | * @preventable\r | |
231 | *\r | |
232 | * Fires immediately before the client-side sends off the RPC call. By returning\r | |
233 | * `false` from an event handler you can prevent the call from being made.\r | |
234 | *\r | |
235 | * @param {Ext.direct.RemotingProvider} provider\r | |
236 | * @param {Ext.direct.Transaction} transaction\r | |
237 | * @param {Object} meta The meta data\r | |
238 | */ \r | |
239 | \r | |
240 | /**\r | |
241 | * @event call\r | |
242 | *\r | |
243 | * Fires immediately after the request to the server-side is sent. This does\r | |
244 | * NOT fire after the response has come back from the call.\r | |
245 | *\r | |
246 | * @param {Ext.direct.RemotingProvider} provider\r | |
247 | * @param {Ext.direct.Transaction} transaction\r | |
248 | * @param {Object} meta The meta data\r | |
249 | */ \r | |
250 | \r | |
251 | /**\r | |
252 | * @event beforecallback\r | |
253 | * @preventable\r | |
254 | *\r | |
255 | * Fires before callback function is executed. By returning `false` from an event handler\r | |
256 | * you can prevent the callback from executing.\r | |
257 | *\r | |
258 | * @param {Ext.direct.RemotingProvider} provider The provider instance\r | |
259 | * @param {Ext.direct.Event} event Event associated with the callback invocation\r | |
260 | * @param {Ext.direct.Transaction} transaction Transaction for which the callback\r | |
261 | * is about to be fired\r | |
262 | */\r | |
263 | \r | |
264 | constructor: function(config) {\r | |
265 | var me = this;\r | |
266 | \r | |
267 | me.callParent(arguments);\r | |
268 | \r | |
269 | me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global;\r | |
270 | me.transactions = new Ext.util.MixedCollection();\r | |
271 | me.callBuffer = [];\r | |
272 | },\r | |
273 | \r | |
274 | doConnect: function() {\r | |
275 | if (!this.apiCreated) {\r | |
276 | this.initAPI();\r | |
277 | this.apiCreated = true;\r | |
278 | }\r | |
279 | },\r | |
280 | \r | |
281 | /**\r | |
282 | * Get nested namespace by property.\r | |
283 | *\r | |
284 | * @private\r | |
285 | */\r | |
286 | getNamespace: function(root, action) {\r | |
287 | var parts, ns, i, len;\r | |
288 | \r | |
289 | root = root || Ext.global;\r | |
290 | parts = action.toString().split('.');\r | |
291 | \r | |
292 | for (i = 0, len = parts.length; i < len; i++) {\r | |
293 | ns = parts[i];\r | |
294 | root = root[ns];\r | |
295 | \r | |
296 | if (typeof root === 'undefined') {\r | |
297 | return root;\r | |
298 | }\r | |
299 | }\r | |
300 | \r | |
301 | return root;\r | |
302 | },\r | |
303 | \r | |
304 | /**\r | |
305 | * Create nested namespaces. Unlike {@link Ext#ns} this method supports\r | |
306 | * nested objects as root of the namespace, not only Ext.global (window).\r | |
307 | *\r | |
308 | * @private\r | |
309 | */\r | |
310 | createNamespaces: function(root, action) {\r | |
311 | var parts, ns, i, len;\r | |
312 | \r | |
313 | root = root || Ext.global;\r | |
314 | parts = action.toString().split('.');\r | |
315 | \r | |
316 | for (i = 0, len = parts.length; i < len; i++) {\r | |
317 | ns = parts[i];\r | |
318 | \r | |
319 | root[ns] = root[ns] || {};\r | |
320 | root = root[ns];\r | |
321 | }\r | |
322 | \r | |
323 | return root;\r | |
324 | },\r | |
325 | \r | |
326 | /**\r | |
327 | * Initialize the API\r | |
328 | *\r | |
329 | * @private\r | |
330 | */\r | |
331 | initAPI: function() {\r | |
332 | var me = this,\r | |
333 | actions = me.actions,\r | |
334 | namespace = me.namespace,\r | |
335 | Manager = Ext.direct.Manager,\r | |
336 | action, cls, methods, i, len, method, handler;\r | |
337 | \r | |
338 | for (action in actions) {\r | |
339 | if (actions.hasOwnProperty(action)) {\r | |
340 | if (me.disableNestedActions) {\r | |
341 | cls = namespace[action];\r | |
342 | \r | |
343 | if (!cls) {\r | |
344 | cls = namespace[action] = {};\r | |
345 | }\r | |
346 | }\r | |
347 | else {\r | |
348 | cls = me.getNamespace(namespace, action);\r | |
349 | \r | |
350 | if (!cls) {\r | |
351 | cls = me.createNamespaces(namespace, action);\r | |
352 | }\r | |
353 | }\r | |
354 | \r | |
355 | methods = actions[action];\r | |
356 | \r | |
357 | for (i = 0, len = methods.length; i < len; ++i) {\r | |
358 | method = new Ext.direct.RemotingMethod(methods[i]);\r | |
359 | cls[method.name] = handler = me.createHandler(action, method);\r | |
360 | \r | |
361 | Manager.registerMethod(handler.$name, handler);\r | |
362 | }\r | |
363 | }\r | |
364 | }\r | |
365 | },\r | |
366 | \r | |
367 | /**\r | |
368 | * Create a handler function for a direct call.\r | |
369 | *\r | |
370 | * @param {String} action The action the call is for\r | |
371 | * @param {Object} method The details of the method\r | |
372 | *\r | |
373 | * @return {Function} A JS function that will kick off the call\r | |
374 | *\r | |
375 | * @private\r | |
376 | */\r | |
377 | createHandler: function(action, method) {\r | |
378 | var me = this,\r | |
379 | slice = Array.prototype.slice,\r | |
380 | handler;\r | |
381 | \r | |
382 | if (!method.formHandler) {\r | |
383 | handler = function() {\r | |
384 | me.configureRequest(action, method, slice.call(arguments, 0));\r | |
385 | };\r | |
386 | }\r | |
387 | else {\r | |
388 | handler = function() {\r | |
389 | me.configureFormRequest(action, method, slice.call(arguments, 0));\r | |
390 | };\r | |
391 | }\r | |
392 | \r | |
393 | handler.name = handler.$name = action + '.' + method.name;\r | |
394 | handler.$directFn = true;\r | |
395 | \r | |
396 | handler.directCfg = handler.$directCfg = {\r | |
397 | action: action,\r | |
398 | method: method\r | |
399 | };\r | |
400 | \r | |
401 | return handler;\r | |
402 | },\r | |
403 | \r | |
404 | /**\r | |
405 | * @inheritdoc\r | |
406 | */\r | |
407 | connect: function() {\r | |
408 | var me = this;\r | |
409 | \r | |
410 | //<debug>\r | |
411 | if (!me.url) {\r | |
412 | Ext.raise('Error initializing RemotingProvider "' + me.id +\r | |
413 | '", no url configured.');\r | |
414 | }\r | |
415 | //</debug>\r | |
416 | \r | |
417 | me.callParent();\r | |
418 | },\r | |
419 | \r | |
420 | /**\r | |
421 | * Run any callbacks related to the transaction.\r | |
422 | *\r | |
423 | * @param {Ext.direct.Transaction} transaction The transaction\r | |
424 | * @param {Ext.direct.Event} event The event\r | |
425 | *\r | |
426 | * @private\r | |
427 | */\r | |
428 | runCallback: function(transaction, event) {\r | |
429 | var success = !!event.status,\r | |
430 | funcName = success ? 'success' : 'failure',\r | |
431 | callback, options, result;\r | |
432 | \r | |
433 | if (transaction && transaction.callback) {\r | |
434 | callback = transaction.callback;\r | |
435 | options = transaction.callbackOptions;\r | |
436 | result = typeof event.result !== 'undefined' ? event.result : event.data;\r | |
437 | \r | |
438 | if (Ext.isFunction(callback)) {\r | |
439 | callback(result, event, success, options);\r | |
440 | }\r | |
441 | else {\r | |
442 | Ext.callback(callback[funcName], callback.scope, [result, event, success, options]);\r | |
443 | Ext.callback(callback.callback, callback.scope, [result, event, success, options]);\r | |
444 | }\r | |
445 | }\r | |
446 | },\r | |
447 | \r | |
448 | /**\r | |
449 | * React to the ajax request being completed\r | |
450 | *\r | |
451 | * @private\r | |
452 | */\r | |
453 | onData: function(options, success, response) {\r | |
454 | var me = this,\r | |
455 | i, len, events, event, transaction, transactions;\r | |
456 | \r | |
457 | if (success) {\r | |
458 | events = me.createEvents(response);\r | |
459 | \r | |
460 | for (i = 0, len = events.length; i < len; ++i) {\r | |
461 | event = events[i];\r | |
462 | transaction = me.getTransaction(event);\r | |
463 | me.fireEvent('data', me, event);\r | |
464 | \r | |
465 | if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {\r | |
466 | me.runCallback(transaction, event, true);\r | |
467 | }\r | |
468 | \r | |
469 | Ext.direct.Manager.removeTransaction(transaction);\r | |
470 | }\r | |
471 | }\r | |
472 | else {\r | |
473 | transactions = [].concat(options.transaction);\r | |
474 | \r | |
475 | for (i = 0, len = transactions.length; i < len; ++i) {\r | |
476 | transaction = me.getTransaction(transactions[i]);\r | |
477 | \r | |
478 | if (transaction && transaction.retryCount < me.maxRetries) {\r | |
479 | transaction.retry();\r | |
480 | }\r | |
481 | else {\r | |
482 | event = new Ext.direct.ExceptionEvent({\r | |
483 | data: null,\r | |
484 | transaction: transaction,\r | |
485 | code: Ext.direct.Manager.exceptions.TRANSPORT,\r | |
486 | message: 'Unable to connect to the server.',\r | |
487 | xhr: response\r | |
488 | });\r | |
489 | \r | |
490 | me.fireEvent('data', me, event);\r | |
491 | \r | |
492 | if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {\r | |
493 | me.runCallback(transaction, event, false);\r | |
494 | }\r | |
495 | \r | |
496 | Ext.direct.Manager.removeTransaction(transaction);\r | |
497 | }\r | |
498 | }\r | |
499 | }\r | |
500 | },\r | |
501 | \r | |
502 | /**\r | |
503 | * Get transaction from XHR options\r | |
504 | *\r | |
505 | * @param {Object} options The options sent to the Ajax request\r | |
506 | *\r | |
507 | * @return {Ext.direct.Transaction} The transaction, null if not found\r | |
508 | *\r | |
509 | * @private\r | |
510 | */\r | |
511 | getTransaction: function(options) {\r | |
512 | return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;\r | |
513 | },\r | |
514 | \r | |
515 | /**\r | |
516 | * Gets the Ajax call info for a transaction\r | |
517 | *\r | |
518 | * @param {Ext.direct.Transaction} transaction The transaction\r | |
519 | *\r | |
520 | * @return {Object} The call params\r | |
521 | *\r | |
522 | * @private\r | |
523 | */\r | |
524 | getPayload: function(transaction) {\r | |
525 | var result = {\r | |
526 | action: transaction.action,\r | |
527 | method: transaction.method,\r | |
528 | data: transaction.data,\r | |
529 | type: 'rpc',\r | |
530 | tid: transaction.id\r | |
531 | };\r | |
532 | \r | |
533 | if (transaction.metadata) {\r | |
534 | result.metadata = transaction.metadata;\r | |
535 | }\r | |
536 | \r | |
537 | return result;\r | |
538 | },\r | |
539 | \r | |
540 | /**\r | |
541 | * Sends a request to the server\r | |
542 | *\r | |
543 | * @param {Object/Array} transaction The transaction(s) to send\r | |
544 | *\r | |
545 | * @private\r | |
546 | */\r | |
547 | sendRequest: function(transaction) {\r | |
548 | var me = this,\r | |
549 | request, callData, params,\r | |
550 | enableUrlEncode = me.enableUrlEncode,\r | |
551 | payload, i, len;\r | |
552 | \r | |
553 | request = {\r | |
554 | url: me.url,\r | |
555 | callback: me.onData,\r | |
556 | scope: me,\r | |
557 | transaction: transaction,\r | |
558 | timeout: me.timeout\r | |
559 | };\r | |
560 | \r | |
561 | // Explicitly specified timeout for Ext Direct call overrides defaults\r | |
562 | if (transaction.timeout) {\r | |
563 | request.timeout = transaction.timeout;\r | |
564 | }\r | |
565 | \r | |
566 | if (Ext.isArray(transaction)) {\r | |
567 | callData = [];\r | |
568 | \r | |
569 | for (i = 0, len = transaction.length; i < len; ++i) {\r | |
570 | payload = me.getPayload(transaction[i]);\r | |
571 | callData.push(payload);\r | |
572 | }\r | |
573 | }\r | |
574 | else {\r | |
575 | callData = me.getPayload(transaction);\r | |
576 | }\r | |
577 | \r | |
578 | if (enableUrlEncode) {\r | |
579 | params = {};\r | |
580 | params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);\r | |
581 | request.params = params;\r | |
582 | }\r | |
583 | else {\r | |
584 | request.jsonData = callData;\r | |
585 | }\r | |
586 | \r | |
587 | Ext.Ajax.request(request);\r | |
588 | },\r | |
589 | \r | |
590 | /**\r | |
591 | * Add a new transaction to the queue\r | |
592 | *\r | |
593 | * @param {Ext.direct.Transaction} transaction The transaction\r | |
594 | *\r | |
595 | * @private\r | |
596 | */\r | |
597 | queueTransaction: function(transaction) {\r | |
598 | var me = this,\r | |
599 | callBuffer = me.callBuffer,\r | |
600 | enableBuffer = me.enableBuffer;\r | |
601 | \r | |
602 | if (transaction.form) {\r | |
603 | me.sendFormRequest(transaction);\r | |
604 | return;\r | |
605 | }\r | |
606 | \r | |
607 | if (enableBuffer === false || transaction.disableBatching ||\r | |
608 | typeof transaction.timeout !== 'undefined') {\r | |
609 | me.sendRequest(transaction);\r | |
610 | return;\r | |
611 | }\r | |
612 | \r | |
613 | callBuffer.push(transaction);\r | |
614 | \r | |
615 | if (enableBuffer && callBuffer.length < me.bufferLimit) {\r | |
616 | if (!me.callTask) {\r | |
617 | me.callTask = new Ext.util.DelayedTask(me.combineAndSend, me);\r | |
618 | }\r | |
619 | \r | |
620 | me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);\r | |
621 | }\r | |
622 | else {\r | |
623 | me.combineAndSend();\r | |
624 | }\r | |
625 | },\r | |
626 | \r | |
627 | /**\r | |
628 | * Combine any buffered requests and send them off\r | |
629 | *\r | |
630 | * @private\r | |
631 | */\r | |
632 | combineAndSend : function() {\r | |
633 | var me = this,\r | |
634 | buffer = me.callBuffer,\r | |
635 | len = buffer.length;\r | |
636 | \r | |
637 | if (len > 0) {\r | |
638 | me.sendRequest(len === 1 ? buffer[0] : buffer);\r | |
639 | me.callBuffer = [];\r | |
640 | }\r | |
641 | },\r | |
642 | \r | |
643 | /**\r | |
644 | * Configure a transaction for a Direct request\r | |
645 | *\r | |
646 | * @param {String} action The action being executed\r | |
647 | * @param {Object} method The method being executed\r | |
648 | * @param {Array} args Method invocation arguments\r | |
649 | * @param {Boolean} isForm True for a form submit\r | |
650 | *\r | |
651 | * @return {Object} Transaction object\r | |
652 | *\r | |
653 | * @private\r | |
654 | */\r | |
655 | configureTransaction: function(action, method, args, isForm) {\r | |
656 | var data, cb, scope, options, params;\r | |
657 | \r | |
658 | data = method.getCallData(args);\r | |
659 | \r | |
660 | cb = data.callback;\r | |
661 | scope = data.scope;\r | |
662 | options = data.options;\r | |
663 | \r | |
664 | //<debug>\r | |
665 | if (cb && !Ext.isFunction(cb)) {\r | |
666 | Ext.raise("Callback argument is not a function " +\r | |
667 | "for Ext Direct method " +\r | |
668 | action + "." + method.name);\r | |
669 | }\r | |
670 | //</debug>\r | |
671 | \r | |
672 | // Callback might be unspecified for a notification\r | |
673 | // that does not expect any return value\r | |
674 | cb = cb && scope ? Ext.Function.bind(cb, scope) : cb;\r | |
675 | \r | |
676 | params = Ext.apply({}, {\r | |
677 | provider: this,\r | |
678 | args: args,\r | |
679 | action: action,\r | |
680 | method: method.name,\r | |
681 | form: data.form,\r | |
682 | data: data.data,\r | |
683 | metadata: data.metadata,\r | |
684 | callbackOptions: options,\r | |
685 | callback: cb,\r | |
686 | isForm: isForm,\r | |
687 | disableBatching: method.disableBatching\r | |
688 | });\r | |
689 | \r | |
690 | if (options && options.timeout != null) {\r | |
691 | params.timeout = options.timeout;\r | |
692 | }\r | |
693 | \r | |
694 | return new Ext.direct.Transaction(params);\r | |
695 | },\r | |
696 | \r | |
697 | /**\r | |
698 | * Configure a direct request\r | |
699 | *\r | |
700 | * @param {String} action The action being executed\r | |
701 | * @param {Object} method The method being executed\r | |
702 | *\r | |
703 | * @private\r | |
704 | */\r | |
705 | configureRequest: function(action, method, args) {\r | |
706 | var me = this,\r | |
707 | transaction;\r | |
708 | \r | |
709 | transaction = me.configureTransaction(action, method, args);\r | |
710 | \r | |
711 | if (me.fireEvent('beforecall', me, transaction, method) !== false) {\r | |
712 | Ext.direct.Manager.addTransaction(transaction);\r | |
713 | me.queueTransaction(transaction);\r | |
714 | me.fireEvent('call', me, transaction, method);\r | |
715 | }\r | |
716 | },\r | |
717 | \r | |
718 | /**\r | |
719 | * Configure a form submission request\r | |
720 | *\r | |
721 | * @param {String} action The action being executed\r | |
722 | * @param {Object} method The method being executed\r | |
723 | * @param {Array} args Method invocation arguments\r | |
724 | *\r | |
725 | * @private\r | |
726 | */\r | |
727 | configureFormRequest: function(action, method, args) {\r | |
728 | var me = this,\r | |
729 | transaction, form, isUpload, postParams;\r | |
730 | \r | |
731 | transaction = me.configureTransaction(action, method, args, true);\r | |
732 | \r | |
733 | if (me.fireEvent('beforecall', me, transaction, method) !== false) {\r | |
734 | Ext.direct.Manager.addTransaction(transaction);\r | |
735 | \r | |
736 | form = transaction.form;\r | |
737 | \r | |
738 | isUpload = String(form.getAttribute("enctype")).toLowerCase() === 'multipart/form-data';\r | |
739 | \r | |
740 | postParams = {\r | |
741 | extTID: transaction.id,\r | |
742 | extAction: action,\r | |
743 | extMethod: method.name,\r | |
744 | extType: 'rpc',\r | |
745 | extUpload: String(isUpload)\r | |
746 | };\r | |
747 | \r | |
748 | if (transaction.metadata) {\r | |
749 | postParams.extMetadata = Ext.JSON.encode(transaction.metadata);\r | |
750 | }\r | |
751 | \r | |
752 | Ext.apply(transaction, {\r | |
753 | form: form,\r | |
754 | isUpload: isUpload,\r | |
755 | params: postParams\r | |
756 | });\r | |
757 | \r | |
758 | me.sendFormRequest(transaction);\r | |
759 | me.fireEvent('call', me, transaction, method);\r | |
760 | }\r | |
761 | },\r | |
762 | \r | |
763 | /**\r | |
764 | * Sends a form request\r | |
765 | *\r | |
766 | * @param {Ext.direct.Transaction} transaction The transaction to send\r | |
767 | *\r | |
768 | * @private\r | |
769 | */\r | |
770 | sendFormRequest: function(transaction) {\r | |
771 | var me = this;\r | |
772 | \r | |
773 | Ext.Ajax.request({\r | |
774 | url: me.url,\r | |
775 | params: transaction.params,\r | |
776 | callback: me.onData,\r | |
777 | scope: me,\r | |
778 | form: transaction.form,\r | |
779 | isUpload: transaction.isUpload,\r | |
780 | transaction: transaction\r | |
781 | });\r | |
782 | },\r | |
783 | \r | |
784 | inheritableStatics: {\r | |
785 | /**\r | |
786 | * @private\r | |
787 | * @static\r | |
788 | * @inheritable\r | |
789 | */\r | |
790 | checkConfig: function(config) {\r | |
791 | // RemotingProvider needs service URI,\r | |
792 | // type and array of Actions\r | |
793 | return config && config.type === 'remoting' &&\r | |
794 | config.url && Ext.isArray(config.actions);\r | |
795 | }\r | |
796 | }\r | |
797 | });\r |