[76] | 1 | /*! |
---|
| 2 | * Ext JS Library 3.4.0 |
---|
| 3 | * Copyright(c) 2006-2011 Sencha Inc. |
---|
| 4 | * licensing@sencha.com |
---|
| 5 | * http://www.sencha.com/license |
---|
| 6 | */ |
---|
| 7 | /** |
---|
| 8 | * @class Ext.direct.RemotingProvider |
---|
| 9 | * @extends Ext.direct.JsonProvider |
---|
| 10 | * |
---|
| 11 | * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to |
---|
| 12 | * server side methods on the client (a remote procedure call (RPC) type of |
---|
| 13 | * connection where the client can initiate a procedure on the server).</p> |
---|
| 14 | * |
---|
| 15 | * <p>This allows for code to be organized in a fashion that is maintainable, |
---|
| 16 | * while providing a clear path between client and server, something that is |
---|
| 17 | * not always apparent when using URLs.</p> |
---|
| 18 | * |
---|
| 19 | * <p>To accomplish this the server-side needs to describe what classes and methods |
---|
| 20 | * are available on the client-side. This configuration will typically be |
---|
| 21 | * outputted by the server-side Ext.Direct stack when the API description is built.</p> |
---|
| 22 | */ |
---|
| 23 | Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, { |
---|
| 24 | /** |
---|
| 25 | * @cfg {Object} actions |
---|
| 26 | * Object literal defining the server side actions and methods. For example, if |
---|
| 27 | * the Provider is configured with: |
---|
| 28 | * <pre><code> |
---|
| 29 | "actions":{ // each property within the 'actions' object represents a server side Class |
---|
| 30 | "TestAction":[ // array of methods within each server side Class to be |
---|
| 31 | { // stubbed out on client |
---|
| 32 | "name":"doEcho", |
---|
| 33 | "len":1 |
---|
| 34 | },{ |
---|
| 35 | "name":"multiply",// name of method |
---|
| 36 | "len":2 // The number of parameters that will be used to create an |
---|
| 37 | // array of data to send to the server side function. |
---|
| 38 | // Ensure the server sends back a Number, not a String. |
---|
| 39 | },{ |
---|
| 40 | "name":"doForm", |
---|
| 41 | "formHandler":true, // direct the client to use specialized form handling method |
---|
| 42 | "len":1 |
---|
| 43 | }] |
---|
| 44 | } |
---|
| 45 | * </code></pre> |
---|
| 46 | * <p>Note that a Store is not required, a server method can be called at any time. |
---|
| 47 | * In the following example a <b>client side</b> handler is used to call the |
---|
| 48 | * server side method "multiply" in the server-side "TestAction" Class:</p> |
---|
| 49 | * <pre><code> |
---|
| 50 | TestAction.multiply( |
---|
| 51 | 2, 4, // pass two arguments to server, so specify len=2 |
---|
| 52 | // callback function after the server is called |
---|
| 53 | // result: the result returned by the server |
---|
| 54 | // e: Ext.Direct.RemotingEvent object |
---|
| 55 | function(result, e){ |
---|
| 56 | var t = e.getTransaction(); |
---|
| 57 | var action = t.action; // server side Class called |
---|
| 58 | var method = t.method; // server side method called |
---|
| 59 | if(e.status){ |
---|
| 60 | var answer = Ext.encode(result); // 8 |
---|
| 61 | |
---|
| 62 | }else{ |
---|
| 63 | var msg = e.message; // failure message |
---|
| 64 | } |
---|
| 65 | } |
---|
| 66 | ); |
---|
| 67 | * </code></pre> |
---|
| 68 | * In the example above, the server side "multiply" function will be passed two |
---|
| 69 | * arguments (2 and 4). The "multiply" method should return the value 8 which will be |
---|
| 70 | * available as the <tt>result</tt> in the example above. |
---|
| 71 | */ |
---|
| 72 | |
---|
| 73 | /** |
---|
| 74 | * @cfg {String/Object} namespace |
---|
| 75 | * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>). |
---|
| 76 | * Explicitly specify the namespace Object, or specify a String to have a |
---|
| 77 | * {@link Ext#namespace namespace created} implicitly. |
---|
| 78 | */ |
---|
| 79 | |
---|
| 80 | /** |
---|
| 81 | * @cfg {String} url |
---|
| 82 | * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. |
---|
| 83 | */ |
---|
| 84 | |
---|
| 85 | /** |
---|
| 86 | * @cfg {String} enableUrlEncode |
---|
| 87 | * Specify which param will hold the arguments for the method. |
---|
| 88 | * Defaults to <tt>'data'</tt>. |
---|
| 89 | */ |
---|
| 90 | |
---|
| 91 | /** |
---|
| 92 | * @cfg {Number/Boolean} enableBuffer |
---|
| 93 | * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method |
---|
| 94 | * calls. If a number is specified this is the amount of time in milliseconds |
---|
| 95 | * to wait before sending a batched request (defaults to <tt>10</tt>).</p> |
---|
| 96 | * <br><p>Calls which are received within the specified timeframe will be |
---|
| 97 | * concatenated together and sent in a single request, optimizing the |
---|
| 98 | * application by reducing the amount of round trips that have to be made |
---|
| 99 | * to the server.</p> |
---|
| 100 | */ |
---|
| 101 | enableBuffer: 10, |
---|
| 102 | |
---|
| 103 | /** |
---|
| 104 | * @cfg {Number} maxRetries |
---|
| 105 | * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>. |
---|
| 106 | */ |
---|
| 107 | maxRetries: 1, |
---|
| 108 | |
---|
| 109 | /** |
---|
| 110 | * @cfg {Number} timeout |
---|
| 111 | * The timeout to use for each request. Defaults to <tt>undefined</tt>. |
---|
| 112 | */ |
---|
| 113 | timeout: undefined, |
---|
| 114 | |
---|
| 115 | constructor : function(config){ |
---|
| 116 | Ext.direct.RemotingProvider.superclass.constructor.call(this, config); |
---|
| 117 | this.addEvents( |
---|
| 118 | /** |
---|
| 119 | * @event beforecall |
---|
| 120 | * Fires immediately before the client-side sends off the RPC call. |
---|
| 121 | * By returning false from an event handler you can prevent the call from |
---|
| 122 | * executing. |
---|
| 123 | * @param {Ext.direct.RemotingProvider} provider |
---|
| 124 | * @param {Ext.Direct.Transaction} transaction |
---|
| 125 | * @param {Object} meta The meta data |
---|
| 126 | */ |
---|
| 127 | 'beforecall', |
---|
| 128 | /** |
---|
| 129 | * @event call |
---|
| 130 | * Fires immediately after the request to the server-side is sent. This does |
---|
| 131 | * NOT fire after the response has come back from the call. |
---|
| 132 | * @param {Ext.direct.RemotingProvider} provider |
---|
| 133 | * @param {Ext.Direct.Transaction} transaction |
---|
| 134 | * @param {Object} meta The meta data |
---|
| 135 | */ |
---|
| 136 | 'call' |
---|
| 137 | ); |
---|
| 138 | this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window; |
---|
| 139 | this.transactions = {}; |
---|
| 140 | this.callBuffer = []; |
---|
| 141 | }, |
---|
| 142 | |
---|
| 143 | // private |
---|
| 144 | initAPI : function(){ |
---|
| 145 | var o = this.actions; |
---|
| 146 | for(var c in o){ |
---|
| 147 | var cls = this.namespace[c] || (this.namespace[c] = {}), |
---|
| 148 | ms = o[c]; |
---|
| 149 | for(var i = 0, len = ms.length; i < len; i++){ |
---|
| 150 | var m = ms[i]; |
---|
| 151 | cls[m.name] = this.createMethod(c, m); |
---|
| 152 | } |
---|
| 153 | } |
---|
| 154 | }, |
---|
| 155 | |
---|
| 156 | // inherited |
---|
| 157 | isConnected: function(){ |
---|
| 158 | return !!this.connected; |
---|
| 159 | }, |
---|
| 160 | |
---|
| 161 | connect: function(){ |
---|
| 162 | if(this.url){ |
---|
| 163 | this.initAPI(); |
---|
| 164 | this.connected = true; |
---|
| 165 | this.fireEvent('connect', this); |
---|
| 166 | }else if(!this.url){ |
---|
| 167 | throw 'Error initializing RemotingProvider, no url configured.'; |
---|
| 168 | } |
---|
| 169 | }, |
---|
| 170 | |
---|
| 171 | disconnect: function(){ |
---|
| 172 | if(this.connected){ |
---|
| 173 | this.connected = false; |
---|
| 174 | this.fireEvent('disconnect', this); |
---|
| 175 | } |
---|
| 176 | }, |
---|
| 177 | |
---|
| 178 | onData: function(opt, success, xhr){ |
---|
| 179 | if(success){ |
---|
| 180 | var events = this.getEvents(xhr); |
---|
| 181 | for(var i = 0, len = events.length; i < len; i++){ |
---|
| 182 | var e = events[i], |
---|
| 183 | t = this.getTransaction(e); |
---|
| 184 | this.fireEvent('data', this, e); |
---|
| 185 | if(t){ |
---|
| 186 | this.doCallback(t, e, true); |
---|
| 187 | Ext.Direct.removeTransaction(t); |
---|
| 188 | } |
---|
| 189 | } |
---|
| 190 | }else{ |
---|
| 191 | var ts = [].concat(opt.ts); |
---|
| 192 | for(var i = 0, len = ts.length; i < len; i++){ |
---|
| 193 | var t = this.getTransaction(ts[i]); |
---|
| 194 | if(t && t.retryCount < this.maxRetries){ |
---|
| 195 | t.retry(); |
---|
| 196 | }else{ |
---|
| 197 | var e = new Ext.Direct.ExceptionEvent({ |
---|
| 198 | data: e, |
---|
| 199 | transaction: t, |
---|
| 200 | code: Ext.Direct.exceptions.TRANSPORT, |
---|
| 201 | message: 'Unable to connect to the server.', |
---|
| 202 | xhr: xhr |
---|
| 203 | }); |
---|
| 204 | this.fireEvent('data', this, e); |
---|
| 205 | if(t){ |
---|
| 206 | this.doCallback(t, e, false); |
---|
| 207 | Ext.Direct.removeTransaction(t); |
---|
| 208 | } |
---|
| 209 | } |
---|
| 210 | } |
---|
| 211 | } |
---|
| 212 | }, |
---|
| 213 | |
---|
| 214 | getCallData: function(t){ |
---|
| 215 | return { |
---|
| 216 | action: t.action, |
---|
| 217 | method: t.method, |
---|
| 218 | data: t.data, |
---|
| 219 | type: 'rpc', |
---|
| 220 | tid: t.tid |
---|
| 221 | }; |
---|
| 222 | }, |
---|
| 223 | |
---|
| 224 | doSend : function(data){ |
---|
| 225 | var o = { |
---|
| 226 | url: this.url, |
---|
| 227 | callback: this.onData, |
---|
| 228 | scope: this, |
---|
| 229 | ts: data, |
---|
| 230 | timeout: this.timeout |
---|
| 231 | }, callData; |
---|
| 232 | |
---|
| 233 | if(Ext.isArray(data)){ |
---|
| 234 | callData = []; |
---|
| 235 | for(var i = 0, len = data.length; i < len; i++){ |
---|
| 236 | callData.push(this.getCallData(data[i])); |
---|
| 237 | } |
---|
| 238 | }else{ |
---|
| 239 | callData = this.getCallData(data); |
---|
| 240 | } |
---|
| 241 | |
---|
| 242 | if(this.enableUrlEncode){ |
---|
| 243 | var params = {}; |
---|
| 244 | params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData); |
---|
| 245 | o.params = params; |
---|
| 246 | }else{ |
---|
| 247 | o.jsonData = callData; |
---|
| 248 | } |
---|
| 249 | Ext.Ajax.request(o); |
---|
| 250 | }, |
---|
| 251 | |
---|
| 252 | combineAndSend : function(){ |
---|
| 253 | var len = this.callBuffer.length; |
---|
| 254 | if(len > 0){ |
---|
| 255 | this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer); |
---|
| 256 | this.callBuffer = []; |
---|
| 257 | } |
---|
| 258 | }, |
---|
| 259 | |
---|
| 260 | queueTransaction: function(t){ |
---|
| 261 | if(t.form){ |
---|
| 262 | this.processForm(t); |
---|
| 263 | return; |
---|
| 264 | } |
---|
| 265 | this.callBuffer.push(t); |
---|
| 266 | if(this.enableBuffer){ |
---|
| 267 | if(!this.callTask){ |
---|
| 268 | this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this); |
---|
| 269 | } |
---|
| 270 | this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10); |
---|
| 271 | }else{ |
---|
| 272 | this.combineAndSend(); |
---|
| 273 | } |
---|
| 274 | }, |
---|
| 275 | |
---|
| 276 | doCall : function(c, m, args){ |
---|
| 277 | var data = null, hs = args[m.len], scope = args[m.len+1]; |
---|
| 278 | |
---|
| 279 | if(m.len !== 0){ |
---|
| 280 | data = args.slice(0, m.len); |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | var t = new Ext.Direct.Transaction({ |
---|
| 284 | provider: this, |
---|
| 285 | args: args, |
---|
| 286 | action: c, |
---|
| 287 | method: m.name, |
---|
| 288 | data: data, |
---|
| 289 | cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs |
---|
| 290 | }); |
---|
| 291 | |
---|
| 292 | if(this.fireEvent('beforecall', this, t, m) !== false){ |
---|
| 293 | Ext.Direct.addTransaction(t); |
---|
| 294 | this.queueTransaction(t); |
---|
| 295 | this.fireEvent('call', this, t, m); |
---|
| 296 | } |
---|
| 297 | }, |
---|
| 298 | |
---|
| 299 | doForm : function(c, m, form, callback, scope){ |
---|
| 300 | var t = new Ext.Direct.Transaction({ |
---|
| 301 | provider: this, |
---|
| 302 | action: c, |
---|
| 303 | method: m.name, |
---|
| 304 | args:[form, callback, scope], |
---|
| 305 | cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback, |
---|
| 306 | isForm: true |
---|
| 307 | }); |
---|
| 308 | |
---|
| 309 | if(this.fireEvent('beforecall', this, t, m) !== false){ |
---|
| 310 | Ext.Direct.addTransaction(t); |
---|
| 311 | var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data', |
---|
| 312 | params = { |
---|
| 313 | extTID: t.tid, |
---|
| 314 | extAction: c, |
---|
| 315 | extMethod: m.name, |
---|
| 316 | extType: 'rpc', |
---|
| 317 | extUpload: String(isUpload) |
---|
| 318 | }; |
---|
| 319 | |
---|
| 320 | // change made from typeof callback check to callback.params |
---|
| 321 | // to support addl param passing in DirectSubmit EAC 6/2 |
---|
| 322 | Ext.apply(t, { |
---|
| 323 | form: Ext.getDom(form), |
---|
| 324 | isUpload: isUpload, |
---|
| 325 | params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params |
---|
| 326 | }); |
---|
| 327 | this.fireEvent('call', this, t, m); |
---|
| 328 | this.processForm(t); |
---|
| 329 | } |
---|
| 330 | }, |
---|
| 331 | |
---|
| 332 | processForm: function(t){ |
---|
| 333 | Ext.Ajax.request({ |
---|
| 334 | url: this.url, |
---|
| 335 | params: t.params, |
---|
| 336 | callback: this.onData, |
---|
| 337 | scope: this, |
---|
| 338 | form: t.form, |
---|
| 339 | isUpload: t.isUpload, |
---|
| 340 | ts: t |
---|
| 341 | }); |
---|
| 342 | }, |
---|
| 343 | |
---|
| 344 | createMethod : function(c, m){ |
---|
| 345 | var f; |
---|
| 346 | if(!m.formHandler){ |
---|
| 347 | f = function(){ |
---|
| 348 | this.doCall(c, m, Array.prototype.slice.call(arguments, 0)); |
---|
| 349 | }.createDelegate(this); |
---|
| 350 | }else{ |
---|
| 351 | f = function(form, callback, scope){ |
---|
| 352 | this.doForm(c, m, form, callback, scope); |
---|
| 353 | }.createDelegate(this); |
---|
| 354 | } |
---|
| 355 | f.directCfg = { |
---|
| 356 | action: c, |
---|
| 357 | method: m |
---|
| 358 | }; |
---|
| 359 | return f; |
---|
| 360 | }, |
---|
| 361 | |
---|
| 362 | getTransaction: function(opt){ |
---|
| 363 | return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null; |
---|
| 364 | }, |
---|
| 365 | |
---|
| 366 | doCallback: function(t, e){ |
---|
| 367 | var fn = e.status ? 'success' : 'failure'; |
---|
| 368 | if(t && t.cb){ |
---|
| 369 | var hs = t.cb, |
---|
| 370 | result = Ext.isDefined(e.result) ? e.result : e.data; |
---|
| 371 | if(Ext.isFunction(hs)){ |
---|
| 372 | hs(result, e); |
---|
| 373 | } else{ |
---|
| 374 | Ext.callback(hs[fn], hs.scope, [result, e]); |
---|
| 375 | Ext.callback(hs.callback, hs.scope, [result, e]); |
---|
| 376 | } |
---|
| 377 | } |
---|
| 378 | } |
---|
| 379 | }); |
---|
| 380 | Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider; |
---|