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.
18 * The JsonP proxy is useful when you need to load data from a domain other than the one your application is running on. If
19 * your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its data
20 * from http://domainB.com because cross-domain ajax requests are prohibited by the browser.
22 * We can get around this using a JsonP proxy. JsonP proxy injects a `<script>` tag into the DOM whenever an AJAX request
23 * would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag that would be
24 * injected might look like this:
26 * <script src="http://domainB.com/users?callback=someCallback"></script>
28 * When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
29 * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we want
30 * to be notified when the result comes in and that it should call our callback function with the data it sends back. So
31 * long as the server formats the response to look like this, everything will work:
38 * email: "ed@sencha.com"
43 * As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the JSON
44 * object that the server returned.
46 * JsonP proxy takes care of all of this automatically. It formats the url you pass, adding the callback parameter
47 * automatically. It even creates a temporary callback function, waits for it to be called and then puts the data into
48 * the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}. Here's how
49 * we might set that up:
51 * Ext.define('User', {
52 * extend: 'Ext.data.Model',
53 * fields: ['id', 'name', 'email']
56 * var store = Ext.create('Ext.data.Store', {
60 * url : 'http://domainB.com/users'
66 * That's all we need to do - JsonP proxy takes care of the rest. In this case the Proxy will have injected a script tag
69 * <script src="http://domainB.com/users?callback=callback1"></script>
73 * This script tag can be customized using the {@link #callbackKey} configuration. For example:
75 * var store = Ext.create('Ext.data.Store', {
79 * url : 'http://domainB.com/users',
80 * callbackKey: 'theCallbackFunction'
86 * Would inject a script tag like this:
88 * <script src="http://domainB.com/users?theCallbackFunction=callback1"></script>
90 * # Implementing on the server side
92 * The remote server side needs to be configured to return data in this format. Here are suggestions for how you might
93 * achieve this using Java, PHP and ASP.net:
97 * boolean jsonP = false;
98 * String cb = request.getParameter("callback");
101 * response.setContentType("text/javascript");
103 * response.setContentType("application/x-json");
105 * Writer out = response.getWriter();
107 * out.write(cb + "(");
109 * out.print(dataBlock.toJsonString());
116 * $callback = $_REQUEST['callback'];
118 * // Create the output object.
119 * $output = array('a' => 'Apple', 'b' => 'Banana');
123 * header('Content-Type: text/javascript');
124 * echo $callback . '(' . json_encode($output) . ');';
126 * header('Content-Type: application/x-json');
127 * echo json_encode($output);
132 * String jsonString = "{success: true}";
133 * String cb = Request.Params.Get("callback");
134 * String responseString = "";
135 * if (!String.IsNullOrEmpty(cb)) {
136 * responseString = cb + "(" + jsonString + ")";
138 * responseString = jsonString;
140 * Response.Write(responseString);
142 Ext.define('Ext.data.proxy.JsonP', {
143 extend: 'Ext.data.proxy.Server',
144 alternateClassName: 'Ext.data.ScriptTagProxy',
145 alias: ['proxy.jsonp', 'proxy.scripttag'],
146 requires: ['Ext.data.JsonP'],
148 defaultWriterType: 'base',
151 * @cfg {String} callbackKey
152 * See {@link Ext.data.JsonP#callbackKey}.
154 callbackKey : 'callback',
157 * @cfg {String} recordParam
158 * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString'). Defaults to
161 recordParam: 'records',
164 * @cfg {Boolean} autoAppendParams
165 * True to automatically append the request's params to the generated url. Defaults to true
167 autoAppendParams: true,
169 constructor: function(){
173 * Fires when the server returns an exception
174 * @param {Ext.data.proxy.Proxy} this
175 * @param {Ext.data.Request} request The request that was sent
176 * @param {Ext.data.Operation} operation The operation that triggered the request
180 this.callParent(arguments);
185 * Performs the read request to the remote domain. JsonP proxy does not actually create an Ajax request,
186 * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
187 * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
188 * @param {Function} callback A callback function to execute when the Operation has been completed
189 * @param {Object} scope The scope to execute the callback in
191 doRequest: function(operation, callback, scope) {
192 //generate the unique IDs for this request
194 writer = me.getWriter(),
195 request = me.buildRequest(operation),
196 params = request.params;
198 if (operation.allowWrite()) {
199 request = writer.write(request);
202 // apply JsonP proxy-specific attributes to the Request
204 callbackKey: me.callbackKey,
207 disableCaching: false, // handled by the proxy
208 callback: me.createRequestCallback(request, operation, callback, scope)
211 // prevent doubling up
212 if (me.autoAppendParams) {
216 request.jsonp = Ext.data.JsonP.request(request);
217 // restore on the request
218 request.params = params;
219 operation.setStarted();
220 me.lastRequest = request;
227 * Creates and returns the function that is called when the request has completed. The returned function
228 * should accept a Response object, which contains the response to be read by the configured Reader.
229 * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
230 * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
231 * theCallback refers to the callback argument received by this function.
232 * See {@link #doRequest} for details.
233 * @param {Ext.data.Request} request The Request object
234 * @param {Ext.data.Operation} operation The Operation being executed
235 * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
236 * passed to doRequest
237 * @param {Object} scope The scope in which to execute the callback function
238 * @return {Function} The callback function
240 createRequestCallback: function(request, operation, callback, scope) {
243 return function(success, response, errorType) {
244 delete me.lastRequest;
245 me.processResponse(success, operation, request, response, callback, scope);
250 setException: function(operation, response) {
251 operation.setException(operation.request.jsonp.errorType);
256 * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
257 * @param {Ext.data.Request} request The request object
258 * @return {String} The url
260 buildUrl: function(request) {
262 url = me.callParent(arguments),
263 params = Ext.apply({}, request.params),
264 filters = params.filters,
268 delete params.filters;
270 if (me.autoAppendParams) {
271 url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
274 if (filters && filters.length) {
275 for (i = 0; i < filters.length; i++) {
279 url = Ext.urlAppend(url, filter.property + "=" + filter.value);
284 //if there are any records present, append them to the url also
285 records = request.records;
287 if (Ext.isArray(records) && records.length > 0) {
288 url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
295 destroy: function() {
301 * Aborts the current server request if one is currently running
304 var lastRequest = this.lastRequest;
306 Ext.data.JsonP.abort(lastRequest.jsonp);
311 * Encodes an array of records into a string suitable to be appended to the script src url. This is broken out into
312 * its own function so that it can be easily overridden.
313 * @param {Ext.data.Model[]} records The records array
314 * @return {String} The encoded records string
316 encodeRecords: function(records) {
319 len = records.length;
321 for (; i < len; i++) {
322 encoded += Ext.Object.toQueryString(records[i].data);