4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-data-proxy-JsonP'>/**
19 </span> * @author Ed Spencer
20 * @class Ext.data.proxy.JsonP
21 * @extends Ext.data.proxy.Server
23 * <p>JsonPProxy is useful when you need to load data from a domain other than the one your application is running
24 * on. If your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its
25 * data from http://domainB.com because cross-domain ajax requests are prohibited by the browser.</p>
27 * <p>We can get around this using a JsonPProxy. JsonPProxy injects a &lt;script&gt; tag into the DOM whenever
28 * an AJAX request would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag
29 * that would be injected might look like this:</p>
31 <pre><code>
32 &lt;script src="http://domainB.com/users?callback=someCallback"&gt;&lt;/script&gt;
33 </code></pre>
35 * <p>When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
36 * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we
37 * want to be notified when the result comes in and that it should call our callback function with the data it sends
38 * back. So long as the server formats the response to look like this, everything will work:</p>
40 <pre><code>
45 name: "Ed Spencer",
46 email: "ed@sencha.com"
50 </code></pre>
52 * <p>As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the
53 * JSON object that the server returned.</p>
55 * <p>JsonPProxy takes care of all of this automatically. It formats the url you pass, adding the callback
56 * parameter automatically. It even creates a temporary callback function, waits for it to be called and then puts
57 * the data into the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}.
58 * Here's how we might set that up:</p>
60 <pre><code>
62 extend: 'Ext.data.Model',
63 fields: ['id', 'name', 'email']
66 var store = new Ext.data.Store({
70 url : 'http://domainB.com/users'
75 </code></pre>
77 * <p>That's all we need to do - JsonPProxy takes care of the rest. In this case the Proxy will have injected a
78 * script tag like this:
80 <pre><code>
81 &lt;script src="http://domainB.com/users?callback=stcCallback001" id="stcScript001"&gt;&lt;/script&gt;
82 </code></pre>
84 * <p><u>Customization</u></p>
86 * <p>Most parts of this script tag can be customized using the {@link #callbackParam}, {@link #callbackPrefix} and
87 * {@link #scriptIdPrefix} configurations. For example:
89 <pre><code>
90 var store = new Ext.data.Store({
94 url : 'http://domainB.com/users',
95 callbackParam: 'theCallbackFunction',
96 callbackPrefix: 'ABC',
97 scriptIdPrefix: 'injectedScript'
102 </code></pre>
104 * <p>Would inject a script tag like this:</p>
106 <pre><code>
107 &lt;script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"&gt;&lt;/script&gt;
108 </code></pre>
110 * <p><u>Implementing on the server side</u></p>
112 * <p>The remote server side needs to be configured to return data in this format. Here are suggestions for how you
113 * might achieve this using Java, PHP and ASP.net:</p>
115 * <p>Java:</p>
117 <pre><code>
118 boolean jsonP = false;
119 String cb = request.getParameter("callback");
122 response.setContentType("text/javascript");
124 response.setContentType("application/x-json");
126 Writer out = response.getWriter();
128 out.write(cb + "(");
130 out.print(dataBlock.toJsonString());
132 out.write(");");
134 </code></pre>
136 * <p>PHP:</p>
138 <pre><code>
139 $callback = $_REQUEST['callback'];
141 // Create the output object.
142 $output = array('a' => 'Apple', 'b' => 'Banana');
146 header('Content-Type: text/javascript');
147 echo $callback . '(' . json_encode($output) . ');';
149 header('Content-Type: application/x-json');
150 echo json_encode($output);
152 </code></pre>
154 * <p>ASP.net:</p>
156 <pre><code>
157 String jsonString = "{success: true}";
158 String cb = Request.Params.Get("callback");
159 String responseString = "";
160 if (!String.IsNullOrEmpty(cb)) {
161 responseString = cb + "(" + jsonString + ")";
163 responseString = jsonString;
165 Response.Write(responseString);
166 </code></pre>
169 Ext.define('Ext.data.proxy.JsonP', {
170 extend: 'Ext.data.proxy.Server',
171 alternateClassName: 'Ext.data.ScriptTagProxy',
172 alias: ['proxy.jsonp', 'proxy.scripttag'],
173 requires: ['Ext.data.JsonP'],
175 defaultWriterType: 'base',
177 <span id='Ext-data-proxy-JsonP-cfg-callbackKey'> /**
178 </span> * @cfg {String} callbackKey (Optional) See {@link Ext.data.JsonP#callbackKey}.
180 callbackKey : 'callback',
182 <span id='Ext-data-proxy-JsonP-cfg-recordParam'> /**
183 </span> * @cfg {String} recordParam
184 * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString').
185 * Defaults to 'records'
187 recordParam: 'records',
189 <span id='Ext-data-proxy-JsonP-cfg-autoAppendParams'> /**
190 </span> * @cfg {Boolean} autoAppendParams True to automatically append the request's params to the generated url. Defaults to true
192 autoAppendParams: true,
194 constructor: function(){
196 <span id='Ext-data-proxy-JsonP-event-exception'> /**
197 </span> * @event exception
198 * Fires when the server returns an exception
199 * @param {Ext.data.proxy.Proxy} this
200 * @param {Ext.data.Request} request The request that was sent
201 * @param {Ext.data.Operation} operation The operation that triggered the request
205 this.callParent(arguments);
208 <span id='Ext-data-proxy-JsonP-method-doRequest'> /**
210 * Performs the read request to the remote domain. JsonPProxy does not actually create an Ajax request,
211 * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
212 * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
213 * @param {Function} callback A callback function to execute when the Operation has been completed
214 * @param {Object} scope The scope to execute the callback in
216 doRequest: function(operation, callback, scope) {
217 //generate the unique IDs for this request
219 writer = me.getWriter(),
220 request = me.buildRequest(operation),
221 params = request.params;
223 if (operation.allowWrite()) {
224 request = writer.write(request);
227 //apply JsonPProxy-specific attributes to the Request
229 callbackKey: me.callbackKey,
232 disableCaching: false, // handled by the proxy
233 callback: me.createRequestCallback(request, operation, callback, scope)
236 // prevent doubling up
237 if (me.autoAppendParams) {
241 request.jsonp = Ext.data.JsonP.request(request);
242 // restore on the request
243 request.params = params;
244 operation.setStarted();
245 me.lastRequest = request;
250 <span id='Ext-data-proxy-JsonP-method-createRequestCallback'> /**
252 * Creates and returns the function that is called when the request has completed. The returned function
253 * should accept a Response object, which contains the response to be read by the configured Reader.
254 * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
255 * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
256 * theCallback refers to the callback argument received by this function.
257 * See {@link #doRequest} for details.
258 * @param {Ext.data.Request} request The Request object
259 * @param {Ext.data.Operation} operation The Operation being executed
260 * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
261 * passed to doRequest
262 * @param {Object} scope The scope in which to execute the callback function
263 * @return {Function} The callback function
265 createRequestCallback: function(request, operation, callback, scope) {
268 return function(success, response, errorType) {
269 delete me.lastRequest;
270 me.processResponse(success, operation, request, response, callback, scope);
275 setException: function(operation, response) {
276 operation.setException(operation.request.jsonp.errorType);
280 <span id='Ext-data-proxy-JsonP-method-buildUrl'> /**
281 </span> * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
282 * @param {Ext.data.Request} request The request object
283 * @return {String} The url
285 buildUrl: function(request) {
287 url = me.callParent(arguments),
288 params = Ext.apply({}, request.params),
289 filters = params.filters,
293 delete params.filters;
295 if (me.autoAppendParams) {
296 url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
299 if (filters && filters.length) {
300 for (i = 0; i < filters.length; i++) {
304 url = Ext.urlAppend(url, filter.property + "=" + filter.value);
309 //if there are any records present, append them to the url also
310 records = request.records;
312 if (Ext.isArray(records) && records.length > 0) {
313 url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
320 destroy: function() {
325 <span id='Ext-data-proxy-JsonP-method-abort'> /**
326 </span> * Aborts the current server request if one is currently running
329 var lastRequest = this.lastRequest;
331 Ext.data.JsonP.abort(lastRequest.jsonp);
335 <span id='Ext-data-proxy-JsonP-method-encodeRecords'> /**
336 </span> * Encodes an array of records into a string suitable to be appended to the script src url. This is broken
337 * out into its own function so that it can be easily overridden.
338 * @param {Array} records The records array
339 * @return {String} The encoded records string
341 encodeRecords: function(records) {
342 var encoded = "",
344 len = records.length;
346 for (; i < len; i++) {
347 encoded += Ext.Object.toQueryString(records[i].data);