Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / pkgs / direct-debug.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.data.DirectProxy\r
9  * @extends Ext.data.DataProxy\r
10  */\r
11 Ext.data.DirectProxy = function(config){\r
12     Ext.apply(this, config);\r
13     if(typeof this.paramOrder == 'string'){\r
14         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
15     }\r
16     Ext.data.DirectProxy.superclass.constructor.call(this, config);\r
17 };\r
18 \r
19 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {\r
20     /**\r
21      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed\r
22      * server side.  Specify the params in the order in which they must be executed on the server-side\r
23      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
24      * comma, or pipe. For example,\r
25      * any of the following would be acceptable:<pre><code>\r
26 paramOrder: ['param1','param2','param3']\r
27 paramOrder: 'param1 param2 param3'\r
28 paramOrder: 'param1,param2,param3'\r
29 paramOrder: 'param1|param2|param'\r
30      </code></pre>\r
31      */\r
32     paramOrder: undefined,\r
33 \r
34     /**\r
35      * @cfg {Boolean} paramsAsHash\r
36      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a\r
37      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
38      */\r
39     paramsAsHash: true,\r
40 \r
41     /**\r
42      * @cfg {Function} directFn\r
43      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter\r
44      * for Store's which will not implement a full CRUD api.\r
45      */\r
46     directFn : undefined,\r
47 \r
48     /**\r
49      * DirectProxy implementation of {@link Ext.data.DataProxy#doRequest}\r
50      * @param {String} action The crud action type (create, read, update, destroy)\r
51      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
52      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
53      * for the request to the remote server.\r
54      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
55      * object into a block of Ext.data.Records.\r
56      * @param {Function} callback\r
57      * <div class="sub-desc"><p>A function to be called after the request.\r
58      * The <tt>callback</tt> is passed the following arguments:<ul>\r
59      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>\r
60      * <li><tt>options</tt>: Options object from the action request</li>\r
61      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>\r
62      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
63      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
64      * @protected\r
65      */\r
66     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
67         var args = [],\r
68             directFn = this.api[action] || this.directFn;\r
69 \r
70         switch (action) {\r
71             case Ext.data.Api.actions.create:\r
72                 args.push(params.jsonData);             // <-- create(Hash)\r
73                 break;\r
74             case Ext.data.Api.actions.read:\r
75                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.\r
76                 if(directFn.directCfg.method.len > 0){\r
77                     if(this.paramOrder){\r
78                         for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
79                             args.push(params[this.paramOrder[i]]);\r
80                         }\r
81                     }else if(this.paramsAsHash){\r
82                         args.push(params);\r
83                     }\r
84                 }\r
85                 break;\r
86             case Ext.data.Api.actions.update:\r
87                 args.push(params.jsonData);        // <-- update(Hash/Hash[])\r
88                 break;\r
89             case Ext.data.Api.actions.destroy:\r
90                 args.push(params.jsonData);        // <-- destroy(Int/Int[])\r
91                 break;\r
92         }\r
93 \r
94         var trans = {\r
95             params : params || {},\r
96             request: {\r
97                 callback : callback,\r
98                 scope : scope,\r
99                 arg : options\r
100             },\r
101             reader: reader\r
102         };\r
103 \r
104         args.push(this.createCallback(action, rs, trans), this);\r
105         directFn.apply(window, args);\r
106     },\r
107 \r
108     // private\r
109     createCallback : function(action, rs, trans) {\r
110         return function(result, res) {\r
111             if (!res.status) {\r
112                 // @deprecated fire loadexception\r
113                 if (action === Ext.data.Api.actions.read) {\r
114                     this.fireEvent("loadexception", this, trans, res, null);\r
115                 }\r
116                 this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
117                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
118                 return;\r
119             }\r
120             if (action === Ext.data.Api.actions.read) {\r
121                 this.onRead(action, trans, result, res);\r
122             } else {\r
123                 this.onWrite(action, trans, result, res, rs);\r
124             }\r
125         };\r
126     },\r
127     /**\r
128      * Callback for read actions\r
129      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
130      * @param {Object} trans The request transaction object\r
131      * @param {Object} result Data object picked out of the server-response.\r
132      * @param {Object} res The server response\r
133      * @protected\r
134      */\r
135     onRead : function(action, trans, result, res) {\r
136         var records;\r
137         try {\r
138             records = trans.reader.readRecords(result);\r
139         }\r
140         catch (ex) {\r
141             // @deprecated: Fire old loadexception for backwards-compat.\r
142             this.fireEvent("loadexception", this, trans, res, ex);\r
143 \r
144             this.fireEvent('exception', this, 'response', action, trans, res, ex);\r
145             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
146             return;\r
147         }\r
148         this.fireEvent("load", this, res, trans.request.arg);\r
149         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);\r
150     },\r
151     /**\r
152      * Callback for write actions\r
153      * @param {String} action [{@link Ext.data.Api#actions create|read|update|destroy}]\r
154      * @param {Object} trans The request transaction object\r
155      * @param {Object} result Data object picked out of the server-response.\r
156      * @param {Object} res The server response\r
157      * @param {Ext.data.Record/[Ext.data.Record]} rs The Store resultset associated with the action.\r
158      * @protected\r
159      */\r
160     onWrite : function(action, trans, result, res, rs) {\r
161         var data = trans.reader.extractData(result, false);\r
162         this.fireEvent("write", this, action, data, res, rs, trans.request.arg);\r
163         trans.request.callback.call(trans.request.scope, data, res, true);\r
164     }\r
165 });\r
166 \r
167 /**\r
168  * @class Ext.data.DirectStore\r
169  * @extends Ext.data.Store\r
170  * <p>Small helper class to create an {@link Ext.data.Store} configured with an\r
171  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting\r
172  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.\r
173  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}\r
174  * configured as needed.</p>\r
175  *\r
176  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>\r
177  * <div><ul class="mdetail-params">\r
178  * <li><b>{@link Ext.data.Store Store}</b></li>\r
179  * <div class="sub-desc"><ul class="mdetail-params">\r
180  *\r
181  * </ul></div>\r
182  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>\r
183  * <div class="sub-desc"><ul class="mdetail-params">\r
184  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>\r
185  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>\r
186  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>\r
187  * </ul></div>\r
188  *\r
189  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>\r
190  * <div class="sub-desc"><ul class="mdetail-params">\r
191  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>\r
192  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>\r
193  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>\r
194  * </ul></div>\r
195  * </ul></div>\r
196  *\r
197  * @xtype directstore\r
198  *\r
199  * @constructor\r
200  * @param {Object} config\r
201  */\r
202 Ext.data.DirectStore = function(c){\r
203     // each transaction upon a singe record will generatie a distinct Direct transaction since Direct queues them into one Ajax request.\r
204     c.batchTransactions = false;\r
205 \r
206     Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {\r
207         proxy: (typeof(c.proxy) == 'undefined') ? new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')) : c.proxy,\r
208         reader: (typeof(c.reader) == 'undefined' && typeof(c.fields) == 'object') ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader\r
209     }));\r
210 };\r
211 Ext.extend(Ext.data.DirectStore, Ext.data.Store, {});\r
212 Ext.reg('directstore', Ext.data.DirectStore);
213 /**\r
214  * @class Ext.Direct\r
215  * @extends Ext.util.Observable\r
216  * <p><b><u>Overview</u></b></p>\r
217  * \r
218  * <p>Ext.Direct aims to streamline communication between the client and server\r
219  * by providing a single interface that reduces the amount of common code\r
220  * typically required to validate data and handle returned data packets\r
221  * (reading data, error conditions, etc).</p>\r
222  *  \r
223  * <p>The Ext.direct namespace includes several classes for a closer integration\r
224  * with the server-side. The Ext.data namespace also includes classes for working\r
225  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>\r
226  * \r
227  * <p><b><u>Specification</u></b></p>\r
228  * \r
229  * <p>For additional information consult the \r
230  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>\r
231  *   \r
232  * <p><b><u>Providers</u></b></p>\r
233  * \r
234  * <p>Ext.Direct uses a provider architecture, where one or more providers are\r
235  * used to transport data to and from the server. There are several providers\r
236  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>\r
237  * \r
238  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>\r
239  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>\r
240  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side\r
241  * on the client.</li>\r
242  * </ul></div>\r
243  * \r
244  * <p>A provider does not need to be invoked directly, providers are added via\r
245  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>\r
246  * \r
247  * <p><b><u>Router</u></b></p>\r
248  * \r
249  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client\r
250  * to the appropriate server-side method. Because the Ext.Direct API is completely\r
251  * platform-agnostic, you could completely swap out a Java based server solution\r
252  * and replace it with one that uses C# without changing the client side JavaScript\r
253  * at all.</p>\r
254  * \r
255  * <p><b><u>Server side events</u></b></p>\r
256  * \r
257  * <p>Custom events from the server may be handled by the client by adding\r
258  * listeners, for example:</p>\r
259  * <pre><code>\r
260 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}\r
261 \r
262 // add a handler for a 'message' event sent by the server \r
263 Ext.Direct.on('message', function(e){\r
264     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));\r
265             out.el.scrollTo('t', 100000, true);\r
266 });\r
267  * </code></pre>\r
268  * @singleton\r
269  */\r
270 Ext.Direct = Ext.extend(Ext.util.Observable, {\r
271     /**\r
272      * Each event type implements a getData() method. The default event types are:\r
273      * <div class="mdetail-params"><ul>\r
274      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>\r
275      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>\r
276      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>\r
277      * </ul></div>\r
278      * @property eventTypes\r
279      * @type Object\r
280      */\r
281 \r
282     /**\r
283      * Four types of possible exceptions which can occur:\r
284      * <div class="mdetail-params"><ul>\r
285      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>\r
286      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>\r
287      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>\r
288      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>\r
289      * </ul></div>\r
290      * @property exceptions\r
291      * @type Object\r
292      */\r
293     exceptions: {\r
294         TRANSPORT: 'xhr',\r
295         PARSE: 'parse',\r
296         LOGIN: 'login',\r
297         SERVER: 'exception'\r
298     },\r
299     \r
300     // private\r
301     constructor: function(){\r
302         this.addEvents(\r
303             /**\r
304              * @event event\r
305              * Fires after an event.\r
306              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
307              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
308              */\r
309             'event',\r
310             /**\r
311              * @event exception\r
312              * Fires after an event exception.\r
313              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
314              */\r
315             'exception'\r
316         );\r
317         this.transactions = {};\r
318         this.providers = {};\r
319     },\r
320 \r
321     /**\r
322      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.\r
323      * If the provider is not already connected, it will auto-connect.\r
324      * <pre><code>\r
325 var pollProv = new Ext.direct.PollingProvider({\r
326     url: 'php/poll2.php'\r
327 }); \r
328 \r
329 Ext.Direct.addProvider(\r
330     {\r
331         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider} \r
332         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.\r
333         "actions":{              // each property within the actions object represents a Class \r
334             "TestAction":[       // array of methods within each server side Class   \r
335             {\r
336                 "name":"doEcho", // name of method\r
337                 "len":1\r
338             },{\r
339                 "name":"multiply",\r
340                 "len":1\r
341             },{\r
342                 "name":"doForm",\r
343                 "formHandler":true, // handle form on server with Ext.Direct.Transaction \r
344                 "len":1\r
345             }]\r
346         },\r
347         "namespace":"myApplication",// namespace to create the Remoting Provider in\r
348     },{\r
349         type: 'polling', // create a {@link Ext.direct.PollingProvider} \r
350         url:  'php/poll.php'\r
351     },\r
352     pollProv // reference to previously created instance\r
353 );\r
354      * </code></pre>\r
355      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance\r
356      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each\r
357      * Provider description instructs Ext.Direct how to create client-side stub methods.\r
358      */\r
359     addProvider : function(provider){        \r
360         var a = arguments;\r
361         if(a.length > 1){\r
362             for(var i = 0, len = a.length; i < len; i++){\r
363                 this.addProvider(a[i]);\r
364             }\r
365             return;\r
366         }\r
367         \r
368         // if provider has not already been instantiated\r
369         if(!provider.events){\r
370             provider = new Ext.Direct.PROVIDERS[provider.type](provider);\r
371         }\r
372         provider.id = provider.id || Ext.id();\r
373         this.providers[provider.id] = provider;\r
374 \r
375         provider.on('data', this.onProviderData, this);\r
376         provider.on('exception', this.onProviderException, this);\r
377 \r
378 \r
379         if(!provider.isConnected()){\r
380             provider.connect();\r
381         }\r
382 \r
383         return provider;\r
384     },\r
385 \r
386     /**\r
387      * Retrieve a {@link Ext.direct.Provider provider} by the\r
388      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is\r
389      * {@link #addProvider added}.\r
390      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider} \r
391      */\r
392     getProvider : function(id){\r
393         return this.providers[id];\r
394     },\r
395 \r
396     removeProvider : function(id){\r
397         var provider = id.id ? id : this.providers[id.id];\r
398         provider.un('data', this.onProviderData, this);\r
399         provider.un('exception', this.onProviderException, this);\r
400         delete this.providers[provider.id];\r
401         return provider;\r
402     },\r
403 \r
404     addTransaction: function(t){\r
405         this.transactions[t.tid] = t;\r
406         return t;\r
407     },\r
408 \r
409     removeTransaction: function(t){\r
410         delete this.transactions[t.tid || t];\r
411         return t;\r
412     },\r
413 \r
414     getTransaction: function(tid){\r
415         return this.transactions[tid.tid || tid];\r
416     },\r
417 \r
418     onProviderData : function(provider, e){\r
419         if(Ext.isArray(e)){\r
420             for(var i = 0, len = e.length; i < len; i++){\r
421                 this.onProviderData(provider, e[i]);\r
422             }\r
423             return;\r
424         }\r
425         if(e.name && e.name != 'event' && e.name != 'exception'){\r
426             this.fireEvent(e.name, e);\r
427         }else if(e.type == 'exception'){\r
428             this.fireEvent('exception', e);\r
429         }\r
430         this.fireEvent('event', e, provider);\r
431     },\r
432 \r
433     createEvent : function(response, extraProps){\r
434         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));\r
435     }\r
436 });\r
437 // overwrite impl. with static instance\r
438 Ext.Direct = new Ext.Direct();\r
439 \r
440 Ext.Direct.TID = 1;\r
441 Ext.Direct.PROVIDERS = {};/**\r
442  * @class Ext.Direct.Transaction\r
443  * @extends Object\r
444  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>\r
445  * @constructor\r
446  * @param {Object} config\r
447  */\r
448 Ext.Direct.Transaction = function(config){\r
449     Ext.apply(this, config);\r
450     this.tid = ++Ext.Direct.TID;\r
451     this.retryCount = 0;\r
452 };\r
453 Ext.Direct.Transaction.prototype = {\r
454     send: function(){\r
455         this.provider.queueTransaction(this);\r
456     },\r
457 \r
458     retry: function(){\r
459         this.retryCount++;\r
460         this.send();\r
461     },\r
462 \r
463     getProvider: function(){\r
464         return this.provider;\r
465     }\r
466 };Ext.Direct.Event = function(config){\r
467     Ext.apply(this, config);\r
468 }\r
469 Ext.Direct.Event.prototype = {\r
470     status: true,\r
471     getData: function(){\r
472         return this.data;\r
473     }\r
474 };\r
475 \r
476 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {\r
477     type: 'rpc',\r
478     getTransaction: function(){\r
479         return this.transaction || Ext.Direct.getTransaction(this.tid);\r
480     }\r
481 });\r
482 \r
483 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {\r
484     status: false,\r
485     type: 'exception'\r
486 });\r
487 \r
488 Ext.Direct.eventTypes = {\r
489     'rpc':  Ext.Direct.RemotingEvent,\r
490     'event':  Ext.Direct.Event,\r
491     'exception':  Ext.Direct.ExceptionEvent\r
492 };\r
493 \r
494 /**\r
495  * @class Ext.direct.Provider\r
496  * @extends Ext.util.Observable\r
497  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>\r
498  * \r
499  * <p>For example ExtJs implements the following subclasses:</p>\r
500  * <pre><code>\r
501 Provider\r
502 |\r
503 +---{@link Ext.direct.JsonProvider JsonProvider} \r
504     |\r
505     +---{@link Ext.direct.PollingProvider PollingProvider}   \r
506     |\r
507     +---{@link Ext.direct.RemotingProvider RemotingProvider}   \r
508  * </code></pre>\r
509  * @abstract\r
510  */\r
511 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    \r
512     /**\r
513      * @cfg {String} id\r
514      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).\r
515      * You should assign an id if you need to be able to access the provider later and you do\r
516      * not have an object reference available, for example:\r
517      * <pre><code>\r
518 Ext.Direct.addProvider(\r
519     {\r
520         type: 'polling',\r
521         url:  'php/poll.php',\r
522         id:   'poll-provider'\r
523     }\r
524 );\r
525      \r
526 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');\r
527 p.disconnect();\r
528      * </code></pre>\r
529      */\r
530         \r
531     /**\r
532      * @cfg {Number} priority\r
533      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).\r
534      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.\r
535      */    \r
536     priority: 1,\r
537 \r
538     /**\r
539      * @cfg {String} type\r
540      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified\r
541      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a\r
542      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>\r
543      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>\r
544      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>\r
545      * </ul></div>\r
546      */    \r
547  \r
548     // private\r
549     constructor : function(config){\r
550         Ext.apply(this, config);\r
551         this.addEvents(\r
552             /**\r
553              * @event connect\r
554              * Fires when the Provider connects to the server-side\r
555              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
556              */            \r
557             'connect',\r
558             /**\r
559              * @event disconnect\r
560              * Fires when the Provider disconnects from the server-side\r
561              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
562              */            \r
563             'disconnect',\r
564             /**\r
565              * @event data\r
566              * Fires when the Provider receives data from the server-side\r
567              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
568              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
569              */            \r
570             'data',\r
571             /**\r
572              * @event exception\r
573              * Fires when the Provider receives an exception from the server-side\r
574              */                        \r
575             'exception'\r
576         );\r
577         Ext.direct.Provider.superclass.constructor.call(this, config);\r
578     },\r
579 \r
580     /**\r
581      * Returns whether or not the server-side is currently connected.\r
582      * Abstract method for subclasses to implement.\r
583      */\r
584     isConnected: function(){\r
585         return false;\r
586     },\r
587 \r
588     /**\r
589      * Abstract methods for subclasses to implement.\r
590      */\r
591     connect: Ext.emptyFn,\r
592     \r
593     /**\r
594      * Abstract methods for subclasses to implement.\r
595      */\r
596     disconnect: Ext.emptyFn\r
597 });\r
598 /**\r
599  * @class Ext.direct.JsonProvider\r
600  * @extends Ext.direct.Provider\r
601  */\r
602 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {\r
603     parseResponse: function(xhr){\r
604         if(!Ext.isEmpty(xhr.responseText)){\r
605             if(typeof xhr.responseText == 'object'){\r
606                 return xhr.responseText;\r
607             }\r
608             return Ext.decode(xhr.responseText);\r
609         }\r
610         return null;\r
611     },\r
612 \r
613     getEvents: function(xhr){\r
614         var data = null;\r
615         try{\r
616             data = this.parseResponse(xhr);\r
617         }catch(e){\r
618             var event = new Ext.Direct.ExceptionEvent({\r
619                 data: e,\r
620                 xhr: xhr,\r
621                 code: Ext.Direct.exceptions.PARSE,\r
622                 message: 'Error parsing json response: \n\n ' + data\r
623             })\r
624             return [event];\r
625         }\r
626         var events = [];\r
627         if(Ext.isArray(data)){\r
628             for(var i = 0, len = data.length; i < len; i++){\r
629                 events.push(Ext.Direct.createEvent(data[i]));\r
630             }\r
631         }else{\r
632             events.push(Ext.Direct.createEvent(data));\r
633         }\r
634         return events;\r
635     }\r
636 });/**\r
637  * @class Ext.direct.PollingProvider\r
638  * @extends Ext.direct.JsonProvider\r
639  *\r
640  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.\r
641  * The initial request for data originates from the client, and then is responded to by the\r
642  * server.</p>\r
643  * \r
644  * <p>All configurations for the PollingProvider should be generated by the server-side\r
645  * API portion of the Ext.Direct stack.</p>\r
646  *\r
647  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply\r
648  * specifying <tt>type = 'polling'</tt>.  For example:</p>\r
649  * <pre><code>\r
650 var pollA = new Ext.direct.PollingProvider({\r
651     type:'polling',\r
652     url: 'php/pollA.php',\r
653 });\r
654 Ext.Direct.addProvider(pollA);\r
655 pollA.disconnect();\r
656 \r
657 Ext.Direct.addProvider(\r
658     {\r
659         type:'polling',\r
660         url: 'php/pollB.php',\r
661         id: 'pollB-provider'\r
662     }\r
663 );\r
664 var pollB = Ext.Direct.getProvider('pollB-provider');\r
665  * </code></pre>\r
666  */\r
667 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {\r
668     /**\r
669      * @cfg {Number} priority\r
670      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.\r
671      */\r
672     // override default priority\r
673     priority: 3,\r
674     \r
675     /**\r
676      * @cfg {Number} interval\r
677      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every\r
678      * 3 seconds).\r
679      */\r
680     interval: 3000,\r
681 \r
682     /**\r
683      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters\r
684      * on every polling request\r
685      */\r
686     \r
687     /**\r
688      * @cfg {String/Function} url\r
689      * The url which the PollingProvider should contact with each request. This can also be\r
690      * an imported Ext.Direct method which will accept the baseParams as its only argument.\r
691      */\r
692 \r
693     // private\r
694     constructor : function(config){\r
695         Ext.direct.PollingProvider.superclass.constructor.call(this, config);\r
696         this.addEvents(\r
697             /**\r
698              * @event beforepoll\r
699              * Fired immediately before a poll takes place, an event handler can return false\r
700              * in order to cancel the poll.\r
701              * @param {Ext.direct.PollingProvider}\r
702              */\r
703             'beforepoll',            \r
704             /**\r
705              * @event poll\r
706              * This event has not yet been implemented.\r
707              * @param {Ext.direct.PollingProvider}\r
708              */\r
709             'poll'\r
710         );\r
711     },\r
712 \r
713     // inherited\r
714     isConnected: function(){\r
715         return !!this.pollTask;\r
716     },\r
717 \r
718     /**\r
719      * Connect to the server-side and begin the polling process. To handle each\r
720      * response subscribe to the data event.\r
721      */\r
722     connect: function(){\r
723         if(this.url && !this.pollTask){\r
724             this.pollTask = Ext.TaskMgr.start({\r
725                 run: function(){\r
726                     if(this.fireEvent('beforepoll', this) !== false){\r
727                         if(typeof this.url == 'function'){\r
728                             this.url(this.baseParams);\r
729                         }else{\r
730                             Ext.Ajax.request({\r
731                                 url: this.url,\r
732                                 callback: this.onData,\r
733                                 scope: this,\r
734                                 params: this.baseParams\r
735                             });\r
736                         }\r
737                     }\r
738                 },\r
739                 interval: this.interval,\r
740                 scope: this\r
741             });\r
742             this.fireEvent('connect', this);\r
743         }else if(!this.url){\r
744             throw 'Error initializing PollingProvider, no url configured.';\r
745         }\r
746     },\r
747 \r
748     /**\r
749      * Disconnect from the server-side and stop the polling process. The disconnect\r
750      * event will be fired on a successful disconnect.\r
751      */\r
752     disconnect: function(){\r
753         if(this.pollTask){\r
754             Ext.TaskMgr.stop(this.pollTask);\r
755             delete this.pollTask;\r
756             this.fireEvent('disconnect', this);\r
757         }\r
758     },\r
759 \r
760     // private\r
761     onData: function(opt, success, xhr){\r
762         if(success){\r
763             var events = this.getEvents(xhr);\r
764             for(var i = 0, len = events.length; i < len; i++){\r
765                 var e = events[i];\r
766                 this.fireEvent('data', this, e);\r
767             }\r
768         }else{\r
769             var e = new Ext.Direct.ExceptionEvent({\r
770                 data: e,\r
771                 code: Ext.Direct.exceptions.TRANSPORT,\r
772                 message: 'Unable to connect to the server.',\r
773                 xhr: xhr\r
774             });\r
775             this.fireEvent('data', this, e);\r
776         }\r
777     }\r
778 });\r
779 \r
780 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**\r
781  * @class Ext.direct.RemotingProvider\r
782  * @extends Ext.direct.JsonProvider\r
783  * \r
784  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to\r
785  * server side methods on the client (a remote procedure call (RPC) type of\r
786  * connection where the client can initiate a procedure on the server).</p>\r
787  * \r
788  * <p>This allows for code to be organized in a fashion that is maintainable,\r
789  * while providing a clear path between client and server, something that is\r
790  * not always apparent when using URLs.</p>\r
791  * \r
792  * <p>To accomplish this the server-side needs to describe what classes and methods\r
793  * are available on the client-side. This configuration will typically be\r
794  * outputted by the server-side Ext.Direct stack when the API description is built.</p>\r
795  */\r
796 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       \r
797     /**\r
798      * @cfg {Object} actions\r
799      * Object literal defining the server side actions and methods. For example, if\r
800      * the Provider is configured with:\r
801      * <pre><code>\r
802 "actions":{ // each property within the 'actions' object represents a server side Class \r
803     "TestAction":[ // array of methods within each server side Class to be   \r
804     {              // stubbed out on client\r
805         "name":"doEcho", \r
806         "len":1            \r
807     },{\r
808         "name":"multiply",// name of method\r
809         "len":2           // The number of parameters that will be used to create an\r
810                           // array of data to send to the server side function.\r
811                           // Ensure the server sends back a Number, not a String. \r
812     },{\r
813         "name":"doForm",\r
814         "formHandler":true, // direct the client to use specialized form handling method \r
815         "len":1\r
816     }]\r
817 }\r
818      * </code></pre>\r
819      * <p>Note that a Store is not required, a server method can be called at any time.\r
820      * In the following example a <b>client side</b> handler is used to call the\r
821      * server side method "multiply" in the server-side "TestAction" Class:</p>\r
822      * <pre><code>\r
823 TestAction.multiply(\r
824     2, 4, // pass two arguments to server, so specify len=2\r
825     // callback function after the server is called\r
826     // result: the result returned by the server\r
827     //      e: Ext.Direct.RemotingEvent object\r
828     function(result, e){\r
829         var t = e.getTransaction();\r
830         var action = t.action; // server side Class called\r
831         var method = t.method; // server side method called\r
832         if(e.status){\r
833             var answer = Ext.encode(result); // 8\r
834     \r
835         }else{\r
836             var msg = e.message; // failure message\r
837         }\r
838     }\r
839 );\r
840      * </code></pre>\r
841      * In the example above, the server side "multiply" function will be passed two\r
842      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be\r
843      * available as the <tt>result</tt> in the example above. \r
844      */\r
845     \r
846     /**\r
847      * @cfg {String/Object} namespace\r
848      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).\r
849      * Explicitly specify the namespace Object, or specify a String to have a\r
850      * {@link Ext#namespace namespace created} implicitly.\r
851      */\r
852     \r
853     /**\r
854      * @cfg {String} url\r
855      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. \r
856      */\r
857     \r
858     /**\r
859      * @cfg {String} enableUrlEncode\r
860      * Specify which param will hold the arguments for the method.\r
861      * Defaults to <tt>'data'</tt>.\r
862      */\r
863     \r
864     /**\r
865      * @cfg {Number/Boolean} enableBuffer\r
866      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method\r
867      * calls. If a number is specified this is the amount of time in milliseconds\r
868      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>\r
869      * <br><p>Calls which are received within the specified timeframe will be\r
870      * concatenated together and sent in a single request, optimizing the\r
871      * application by reducing the amount of round trips that have to be made\r
872      * to the server.</p>\r
873      */\r
874     enableBuffer: 10,\r
875     \r
876     /**\r
877      * @cfg {Number} maxRetries\r
878      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.\r
879      */\r
880     maxRetries: 1,\r
881     \r
882     /**\r
883      * @cfg {Number} timeout\r
884      * The timeout to use for each request. Defaults to <tt>undefined</tt>.\r
885      */\r
886     timeout: undefined,\r
887 \r
888     constructor : function(config){\r
889         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);\r
890         this.addEvents(\r
891             /**\r
892              * @event beforecall\r
893              * Fires immediately before the client-side sends off the RPC call.\r
894              * By returning false from an event handler you can prevent the call from\r
895              * executing.\r
896              * @param {Ext.direct.RemotingProvider} provider\r
897              * @param {Ext.Direct.Transaction} transaction\r
898              */            \r
899             'beforecall',            \r
900             /**\r
901              * @event call\r
902              * Fires immediately after the request to the server-side is sent. This does\r
903              * NOT fire after the response has come back from the call.\r
904              * @param {Ext.direct.RemotingProvider} provider\r
905              * @param {Ext.Direct.Transaction} transaction\r
906              */            \r
907             'call'\r
908         );\r
909         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;\r
910         this.transactions = {};\r
911         this.callBuffer = [];\r
912     },\r
913 \r
914     // private\r
915     initAPI : function(){\r
916         var o = this.actions;\r
917         for(var c in o){\r
918             var cls = this.namespace[c] || (this.namespace[c] = {}),\r
919                 ms = o[c];\r
920             for(var i = 0, len = ms.length; i < len; i++){\r
921                 var m = ms[i];\r
922                 cls[m.name] = this.createMethod(c, m);\r
923             }\r
924         }\r
925     },\r
926 \r
927     // inherited\r
928     isConnected: function(){\r
929         return !!this.connected;\r
930     },\r
931 \r
932     connect: function(){\r
933         if(this.url){\r
934             this.initAPI();\r
935             this.connected = true;\r
936             this.fireEvent('connect', this);\r
937         }else if(!this.url){\r
938             throw 'Error initializing RemotingProvider, no url configured.';\r
939         }\r
940     },\r
941 \r
942     disconnect: function(){\r
943         if(this.connected){\r
944             this.connected = false;\r
945             this.fireEvent('disconnect', this);\r
946         }\r
947     },\r
948 \r
949     onData: function(opt, success, xhr){\r
950         if(success){\r
951             var events = this.getEvents(xhr);\r
952             for(var i = 0, len = events.length; i < len; i++){\r
953                 var e = events[i],\r
954                     t = this.getTransaction(e);\r
955                 this.fireEvent('data', this, e);\r
956                 if(t){\r
957                     this.doCallback(t, e, true);\r
958                     Ext.Direct.removeTransaction(t);\r
959                 }\r
960             }\r
961         }else{\r
962             var ts = [].concat(opt.ts);\r
963             for(var i = 0, len = ts.length; i < len; i++){\r
964                 var t = this.getTransaction(ts[i]);\r
965                 if(t && t.retryCount < this.maxRetries){\r
966                     t.retry();\r
967                 }else{\r
968                     var e = new Ext.Direct.ExceptionEvent({\r
969                         data: e,\r
970                         transaction: t,\r
971                         code: Ext.Direct.exceptions.TRANSPORT,\r
972                         message: 'Unable to connect to the server.',\r
973                         xhr: xhr\r
974                     });\r
975                     this.fireEvent('data', this, e);\r
976                     if(t){\r
977                         this.doCallback(t, e, false);\r
978                         Ext.Direct.removeTransaction(t);\r
979                     }\r
980                 }\r
981             }\r
982         }\r
983     },\r
984 \r
985     getCallData: function(t){\r
986         return {\r
987             action: t.action,\r
988             method: t.method,\r
989             data: t.data,\r
990             type: 'rpc',\r
991             tid: t.tid\r
992         };\r
993     },\r
994 \r
995     doSend : function(data){\r
996         var o = {\r
997             url: this.url,\r
998             callback: this.onData,\r
999             scope: this,\r
1000             ts: data,\r
1001             timeout: this.timeout\r
1002         }, callData;\r
1003 \r
1004         if(Ext.isArray(data)){\r
1005             callData = [];\r
1006             for(var i = 0, len = data.length; i < len; i++){\r
1007                 callData.push(this.getCallData(data[i]));\r
1008             }\r
1009         }else{\r
1010             callData = this.getCallData(data);\r
1011         }\r
1012 \r
1013         if(this.enableUrlEncode){\r
1014             var params = {};\r
1015             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);\r
1016             o.params = params;\r
1017         }else{\r
1018             o.jsonData = callData;\r
1019         }\r
1020         Ext.Ajax.request(o);\r
1021     },\r
1022 \r
1023     combineAndSend : function(){\r
1024         var len = this.callBuffer.length;\r
1025         if(len > 0){\r
1026             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);\r
1027             this.callBuffer = [];\r
1028         }\r
1029     },\r
1030 \r
1031     queueTransaction: function(t){\r
1032         if(t.form){\r
1033             this.processForm(t);\r
1034             return;\r
1035         }\r
1036         this.callBuffer.push(t);\r
1037         if(this.enableBuffer){\r
1038             if(!this.callTask){\r
1039                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);\r
1040             }\r
1041             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);\r
1042         }else{\r
1043             this.combineAndSend();\r
1044         }\r
1045     },\r
1046 \r
1047     doCall : function(c, m, args){\r
1048         var data = null, hs = args[m.len], scope = args[m.len+1];\r
1049 \r
1050         if(m.len !== 0){\r
1051             data = args.slice(0, m.len);\r
1052         }\r
1053 \r
1054         var t = new Ext.Direct.Transaction({\r
1055             provider: this,\r
1056             args: args,\r
1057             action: c,\r
1058             method: m.name,\r
1059             data: data,\r
1060             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs\r
1061         });\r
1062 \r
1063         if(this.fireEvent('beforecall', this, t) !== false){\r
1064             Ext.Direct.addTransaction(t);\r
1065             this.queueTransaction(t);\r
1066             this.fireEvent('call', this, t);\r
1067         }\r
1068     },\r
1069 \r
1070     doForm : function(c, m, form, callback, scope){\r
1071         var t = new Ext.Direct.Transaction({\r
1072             provider: this,\r
1073             action: c,\r
1074             method: m.name,\r
1075             args:[form, callback, scope],\r
1076             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,\r
1077             isForm: true\r
1078         });\r
1079 \r
1080         if(this.fireEvent('beforecall', this, t) !== false){\r
1081             Ext.Direct.addTransaction(t);\r
1082             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',\r
1083                 params = {\r
1084                     extTID: t.tid,\r
1085                     extAction: c,\r
1086                     extMethod: m.name,\r
1087                     extType: 'rpc',\r
1088                     extUpload: String(isUpload)\r
1089                 };\r
1090             \r
1091             // change made from typeof callback check to callback.params\r
1092             // to support addl param passing in DirectSubmit EAC 6/2\r
1093             Ext.apply(t, {\r
1094                 form: Ext.getDom(form),\r
1095                 isUpload: isUpload,\r
1096                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params\r
1097             });\r
1098             this.fireEvent('call', this, t);\r
1099             this.processForm(t);\r
1100         }\r
1101     },\r
1102     \r
1103     processForm: function(t){\r
1104         Ext.Ajax.request({\r
1105             url: this.url,\r
1106             params: t.params,\r
1107             callback: this.onData,\r
1108             scope: this,\r
1109             form: t.form,\r
1110             isUpload: t.isUpload,\r
1111             ts: t\r
1112         });\r
1113     },\r
1114 \r
1115     createMethod : function(c, m){\r
1116         var f;\r
1117         if(!m.formHandler){\r
1118             f = function(){\r
1119                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));\r
1120             }.createDelegate(this);\r
1121         }else{\r
1122             f = function(form, callback, scope){\r
1123                 this.doForm(c, m, form, callback, scope);\r
1124             }.createDelegate(this);\r
1125         }\r
1126         f.directCfg = {\r
1127             action: c,\r
1128             method: m\r
1129         };\r
1130         return f;\r
1131     },\r
1132 \r
1133     getTransaction: function(opt){\r
1134         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;\r
1135     },\r
1136 \r
1137     doCallback: function(t, e){\r
1138         var fn = e.status ? 'success' : 'failure';\r
1139         if(t && t.cb){\r
1140             var hs = t.cb,\r
1141                 result = Ext.isDefined(e.result) ? e.result : e.data;\r
1142             if(Ext.isFunction(hs)){\r
1143                 hs(result, e);\r
1144             } else{\r
1145                 Ext.callback(hs[fn], hs.scope, [result, e]);\r
1146                 Ext.callback(hs.callback, hs.scope, [result, e]);\r
1147             }\r
1148         }\r
1149     }\r
1150 });\r
1151 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;