3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
17 * @class Ext.data.proxy.JsonP
18 * @extends Ext.data.proxy.Server
20 * <p>JsonPProxy is useful when you need to load data from a domain other than the one your application is running
21 * on. If your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its
22 * data from http://domainB.com because cross-domain ajax requests are prohibited by the browser.</p>
24 * <p>We can get around this using a JsonPProxy. JsonPProxy injects a <script> tag into the DOM whenever
25 * an AJAX request would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag
26 * that would be injected might look like this:</p>
29 <script src="http://domainB.com/users?callback=someCallback"></script>
32 * <p>When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
33 * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we
34 * want to be notified when the result comes in and that it should call our callback function with the data it sends
35 * back. So long as the server formats the response to look like this, everything will work:</p>
43 email: "ed@sencha.com"
49 * <p>As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the
50 * JSON object that the server returned.</p>
52 * <p>JsonPProxy takes care of all of this automatically. It formats the url you pass, adding the callback
53 * parameter automatically. It even creates a temporary callback function, waits for it to be called and then puts
54 * the data into the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}.
55 * Here's how we might set that up:</p>
59 extend: 'Ext.data.Model',
60 fields: ['id', 'name', 'email']
63 var store = new Ext.data.Store({
67 url : 'http://domainB.com/users'
74 * <p>That's all we need to do - JsonPProxy takes care of the rest. In this case the Proxy will have injected a
75 * script tag like this:
78 <script src="http://domainB.com/users?callback=stcCallback001" id="stcScript001"></script>
81 * <p><u>Customization</u></p>
83 * <p>Most parts of this script tag can be customized using the {@link #callbackParam}, {@link #callbackPrefix} and
84 * {@link #scriptIdPrefix} configurations. For example:
87 var store = new Ext.data.Store({
91 url : 'http://domainB.com/users',
92 callbackParam: 'theCallbackFunction',
93 callbackPrefix: 'ABC',
94 scriptIdPrefix: 'injectedScript'
101 * <p>Would inject a script tag like this:</p>
104 <script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"></script>
107 * <p><u>Implementing on the server side</u></p>
109 * <p>The remote server side needs to be configured to return data in this format. Here are suggestions for how you
110 * might achieve this using Java, PHP and ASP.net:</p>
115 boolean jsonP = false;
116 String cb = request.getParameter("callback");
119 response.setContentType("text/javascript");
121 response.setContentType("application/x-json");
123 Writer out = response.getWriter();
127 out.print(dataBlock.toJsonString());
136 $callback = $_REQUEST['callback'];
138 // Create the output object.
139 $output = array('a' => 'Apple', 'b' => 'Banana');
143 header('Content-Type: text/javascript');
144 echo $callback . '(' . json_encode($output) . ');';
146 header('Content-Type: application/x-json');
147 echo json_encode($output);
154 String jsonString = "{success: true}";
155 String cb = Request.Params.Get("callback");
156 String responseString = "";
157 if (!String.IsNullOrEmpty(cb)) {
158 responseString = cb + "(" + jsonString + ")";
160 responseString = jsonString;
162 Response.Write(responseString);
166 Ext.define('Ext.data.proxy.JsonP', {
167 extend: 'Ext.data.proxy.Server',
168 alternateClassName: 'Ext.data.ScriptTagProxy',
169 alias: ['proxy.jsonp', 'proxy.scripttag'],
170 requires: ['Ext.data.JsonP'],
172 defaultWriterType: 'base',
175 * @cfg {String} callbackKey (Optional) See {@link Ext.data.JsonP#callbackKey}.
177 callbackKey : 'callback',
180 * @cfg {String} recordParam
181 * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString').
182 * Defaults to 'records'
184 recordParam: 'records',
187 * @cfg {Boolean} autoAppendParams True to automatically append the request's params to the generated url. Defaults to true
189 autoAppendParams: true,
191 constructor: function(){
195 * Fires when the server returns an exception
196 * @param {Ext.data.proxy.Proxy} this
197 * @param {Ext.data.Request} request The request that was sent
198 * @param {Ext.data.Operation} operation The operation that triggered the request
202 this.callParent(arguments);
207 * Performs the read request to the remote domain. JsonPProxy does not actually create an Ajax request,
208 * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
209 * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
210 * @param {Function} callback A callback function to execute when the Operation has been completed
211 * @param {Object} scope The scope to execute the callback in
213 doRequest: function(operation, callback, scope) {
214 //generate the unique IDs for this request
216 writer = me.getWriter(),
217 request = me.buildRequest(operation),
218 params = request.params;
220 if (operation.allowWrite()) {
221 request = writer.write(request);
224 //apply JsonPProxy-specific attributes to the Request
226 callbackKey: me.callbackKey,
229 disableCaching: false, // handled by the proxy
230 callback: me.createRequestCallback(request, operation, callback, scope)
233 // prevent doubling up
234 if (me.autoAppendParams) {
238 request.jsonp = Ext.data.JsonP.request(request);
239 // restore on the request
240 request.params = params;
241 operation.setStarted();
242 me.lastRequest = request;
249 * Creates and returns the function that is called when the request has completed. The returned function
250 * should accept a Response object, which contains the response to be read by the configured Reader.
251 * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
252 * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
253 * theCallback refers to the callback argument received by this function.
254 * See {@link #doRequest} for details.
255 * @param {Ext.data.Request} request The Request object
256 * @param {Ext.data.Operation} operation The Operation being executed
257 * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
258 * passed to doRequest
259 * @param {Object} scope The scope in which to execute the callback function
260 * @return {Function} The callback function
262 createRequestCallback: function(request, operation, callback, scope) {
265 return function(success, response, errorType) {
266 delete me.lastRequest;
267 me.processResponse(success, operation, request, response, callback, scope);
272 setException: function(operation, response) {
273 operation.setException(operation.request.jsonp.errorType);
278 * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
279 * @param {Ext.data.Request} request The request object
280 * @return {String} The url
282 buildUrl: function(request) {
284 url = me.callParent(arguments),
285 params = Ext.apply({}, request.params),
286 filters = params.filters,
290 delete params.filters;
292 if (me.autoAppendParams) {
293 url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
296 if (filters && filters.length) {
297 for (i = 0; i < filters.length; i++) {
301 url = Ext.urlAppend(url, filter.property + "=" + filter.value);
306 //if there are any records present, append them to the url also
307 records = request.records;
309 if (Ext.isArray(records) && records.length > 0) {
310 url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
317 destroy: function() {
323 * Aborts the current server request if one is currently running
326 var lastRequest = this.lastRequest;
328 Ext.data.JsonP.abort(lastRequest.jsonp);
333 * Encodes an array of records into a string suitable to be appended to the script src url. This is broken
334 * out into its own function so that it can be easily overridden.
335 * @param {Array} records The records array
336 * @return {String} The encoded records string
338 encodeRecords: function(records) {
341 len = records.length;
343 for (; i < len; i++) {
344 encoded += Ext.Object.toQueryString(records[i].data);