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