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