initial import
[roojs1] / Roo / data / ScriptTagProxy.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12 /**
13  * @class Roo.data.ScriptTagProxy
14  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15  * other than the originating domain of the running page.<br><br>
16  * <p>
17  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
18  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
19  * <p>
20  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
21  * source code that is used as the source inside a &lt;script> tag.<br><br>
22  * <p>
23  * In order for the browser to process the returned data, the server must wrap the data object
24  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26  * depending on whether the callback name was passed:
27  * <p>
28  * <pre><code>
29 boolean scriptTag = false;
30 String cb = request.getParameter("callback");
31 if (cb != null) {
32     scriptTag = true;
33     response.setContentType("text/javascript");
34 } else {
35     response.setContentType("application/x-json");
36 }
37 Writer out = response.getWriter();
38 if (scriptTag) {
39     out.write(cb + "(");
40 }
41 out.print(dataBlock.toJsonString());
42 if (scriptTag) {
43     out.write(");");
44 }
45 </pre></code>
46  *
47  * @constructor
48  * @param {Object} config A configuration object.
49  */
50 Roo.data.ScriptTagProxy = function(config){
51     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
52     Roo.apply(this, config);
53     this.head = document.getElementsByTagName("head")[0];
54 };
55
56 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
57
58 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
59     /**
60      * @cfg {String} url The URL from which to request the data object.
61      */
62     /**
63      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
64      */
65     timeout : 30000,
66     /**
67      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
68      * the server the name of the callback function set up by the load call to process the returned data object.
69      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
70      * javascript output which calls this named function passing the data object as its only parameter.
71      */
72     callbackParam : "callback",
73     /**
74      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
75      * name to the request.
76      */
77     nocache : true,
78
79     /**
80      * Load data from the configured URL, read the data object into
81      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
82      * process that block using the passed callback.
83      * @param {Object} params An object containing properties which are to be used as HTTP parameters
84      * for the request to the remote server.
85      * @param {Roo.data.DataReader} reader The Reader object which converts the data
86      * object into a block of Roo.data.Records.
87      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
88      * The function must be passed <ul>
89      * <li>The Record block object</li>
90      * <li>The "arg" argument from the load function</li>
91      * <li>A boolean success indicator</li>
92      * </ul>
93      * @param {Object} scope The scope in which to call the callback
94      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
95      */
96     load : function(params, reader, callback, scope, arg){
97         if(this.fireEvent("beforeload", this, params) !== false){
98
99             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
100
101             var url = this.url;
102             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
103             if(this.nocache){
104                 url += "&_dc=" + (new Date().getTime());
105             }
106             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
107             var trans = {
108                 id : transId,
109                 cb : "stcCallback"+transId,
110                 scriptId : "stcScript"+transId,
111                 params : params,
112                 arg : arg,
113                 url : url,
114                 callback : callback,
115                 scope : scope,
116                 reader : reader
117             };
118             var conn = this;
119
120             window[trans.cb] = function(o){
121                 conn.handleResponse(o, trans);
122             };
123
124             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
125
126             if(this.autoAbort !== false){
127                 this.abort();
128             }
129
130             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
131
132             var script = document.createElement("script");
133             script.setAttribute("src", url);
134             script.setAttribute("type", "text/javascript");
135             script.setAttribute("id", trans.scriptId);
136             this.head.appendChild(script);
137
138             this.trans = trans;
139         }else{
140             callback.call(scope||this, null, arg, false);
141         }
142     },
143
144     // private
145     isLoading : function(){
146         return this.trans ? true : false;
147     },
148
149     /**
150      * Abort the current server request.
151      */
152     abort : function(){
153         if(this.isLoading()){
154             this.destroyTrans(this.trans);
155         }
156     },
157
158     // private
159     destroyTrans : function(trans, isLoaded){
160         this.head.removeChild(document.getElementById(trans.scriptId));
161         clearTimeout(trans.timeoutId);
162         if(isLoaded){
163             window[trans.cb] = undefined;
164             try{
165                 delete window[trans.cb];
166             }catch(e){}
167         }else{
168             // if hasn't been loaded, wait for load to remove it to prevent script error
169             window[trans.cb] = function(){
170                 window[trans.cb] = undefined;
171                 try{
172                     delete window[trans.cb];
173                 }catch(e){}
174             };
175         }
176     },
177
178     // private
179     handleResponse : function(o, trans){
180         this.trans = false;
181         this.destroyTrans(trans, true);
182         var result;
183         try {
184             result = trans.reader.readRecords(o);
185         }catch(e){
186             this.fireEvent("loadexception", this, o, trans.arg, e);
187             trans.callback.call(trans.scope||window, null, trans.arg, false);
188             return;
189         }
190         this.fireEvent("load", this, o, trans.arg);
191         trans.callback.call(trans.scope||window, result, trans.arg, true);
192     },
193
194     // private
195     handleFailure : function(trans){
196         this.trans = false;
197         this.destroyTrans(trans, false);
198         this.fireEvent("loadexception", this, null, trans.arg);
199         trans.callback.call(trans.scope||window, null, trans.arg, false);
200     }
201 });