4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/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
21 * The JsonP proxy is useful when you need to load data from a domain other than the one your application is running on. If
22 * your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its data
23 * from http://domainB.com because cross-domain ajax requests are prohibited by the browser.
25 * We can get around this using a JsonP proxy. JsonP proxy injects a `<script>` tag into the DOM whenever an AJAX request
26 * would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag that would be
27 * injected might look like this:
29 * <script src="http://domainB.com/users?callback=someCallback"></script>
31 * When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
32 * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we want
33 * to be notified when the result comes in and that it should call our callback function with the data it sends back. So
34 * long as the server formats the response to look like this, everything will work:
40 * name: "Ed Spencer",
41 * email: "ed@sencha.com"
46 * As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the JSON
47 * object that the server returned.
49 * JsonP proxy takes care of all of this automatically. It formats the url you pass, adding the callback parameter
50 * automatically. It even creates a temporary callback function, waits for it to be called and then puts the data into
51 * the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}. Here's how
52 * we might set that up:
54 * Ext.define('User', {
55 * extend: 'Ext.data.Model',
56 * fields: ['id', 'name', 'email']
59 * var store = Ext.create('Ext.data.Store', {
63 * url : 'http://domainB.com/users'
69 * 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
72 * <script src="http://domainB.com/users?callback=callback1"></script>
76 * This script tag can be customized using the {@link #callbackKey} configuration. For example:
78 * var store = Ext.create('Ext.data.Store', {
82 * url : 'http://domainB.com/users',
83 * callbackKey: 'theCallbackFunction'
89 * Would inject a script tag like this:
91 * <script src="http://domainB.com/users?theCallbackFunction=callback1"></script>
93 * # Implementing on the server side
95 * The remote server side needs to be configured to return data in this format. Here are suggestions for how you might
96 * achieve this using Java, PHP and ASP.net:
100 * boolean jsonP = false;
101 * String cb = request.getParameter("callback");
104 * response.setContentType("text/javascript");
106 * response.setContentType("application/x-json");
108 * Writer out = response.getWriter();
110 * out.write(cb + "(");
112 * out.print(dataBlock.toJsonString());
114 * out.write(");");
119 * $callback = $_REQUEST['callback'];
121 * // Create the output object.
122 * $output = array('a' => 'Apple', 'b' => 'Banana');
126 * header('Content-Type: text/javascript');
127 * echo $callback . '(' . json_encode($output) . ');';
129 * header('Content-Type: application/x-json');
130 * echo json_encode($output);
135 * String jsonString = "{success: true}";
136 * String cb = Request.Params.Get("callback");
137 * String responseString = "";
138 * if (!String.IsNullOrEmpty(cb)) {
139 * responseString = cb + "(" + jsonString + ")";
141 * responseString = jsonString;
143 * Response.Write(responseString);
145 Ext.define('Ext.data.proxy.JsonP', {
146 extend: 'Ext.data.proxy.Server',
147 alternateClassName: 'Ext.data.ScriptTagProxy',
148 alias: ['proxy.jsonp', 'proxy.scripttag'],
149 requires: ['Ext.data.JsonP'],
151 defaultWriterType: 'base',
153 <span id='Ext-data-proxy-JsonP-cfg-callbackKey'> /**
154 </span> * @cfg {String} callbackKey
155 * See {@link Ext.data.JsonP#callbackKey}.
157 callbackKey : 'callback',
159 <span id='Ext-data-proxy-JsonP-cfg-recordParam'> /**
160 </span> * @cfg {String} recordParam
161 * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString'). Defaults to
164 recordParam: 'records',
166 <span id='Ext-data-proxy-JsonP-cfg-autoAppendParams'> /**
167 </span> * @cfg {Boolean} autoAppendParams
168 * True to automatically append the request's params to the generated url. Defaults to true
170 autoAppendParams: true,
172 constructor: function(){
174 <span id='Ext-data-proxy-JsonP-event-exception'> /**
176 * Fires when the server returns an exception
177 * @param {Ext.data.proxy.Proxy} this
178 * @param {Ext.data.Request} request The request that was sent
179 * @param {Ext.data.Operation} operation The operation that triggered the request
183 this.callParent(arguments);
186 <span id='Ext-data-proxy-JsonP-method-doRequest'> /**
188 * Performs the read request to the remote domain. JsonP proxy does not actually create an Ajax request,
189 * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
190 * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
191 * @param {Function} callback A callback function to execute when the Operation has been completed
192 * @param {Object} scope The scope to execute the callback in
194 doRequest: function(operation, callback, scope) {
195 //generate the unique IDs for this request
197 writer = me.getWriter(),
198 request = me.buildRequest(operation),
199 params = request.params;
201 if (operation.allowWrite()) {
202 request = writer.write(request);
205 // apply JsonP proxy-specific attributes to the Request
207 callbackKey: me.callbackKey,
210 disableCaching: false, // handled by the proxy
211 callback: me.createRequestCallback(request, operation, callback, scope)
214 // prevent doubling up
215 if (me.autoAppendParams) {
219 request.jsonp = Ext.data.JsonP.request(request);
220 // restore on the request
221 request.params = params;
222 operation.setStarted();
223 me.lastRequest = request;
228 <span id='Ext-data-proxy-JsonP-method-createRequestCallback'> /**
230 * Creates and returns the function that is called when the request has completed. The returned function
231 * should accept a Response object, which contains the response to be read by the configured Reader.
232 * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
233 * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
234 * theCallback refers to the callback argument received by this function.
235 * See {@link #doRequest} for details.
236 * @param {Ext.data.Request} request The Request object
237 * @param {Ext.data.Operation} operation The Operation being executed
238 * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
239 * passed to doRequest
240 * @param {Object} scope The scope in which to execute the callback function
241 * @return {Function} The callback function
243 createRequestCallback: function(request, operation, callback, scope) {
246 return function(success, response, errorType) {
247 delete me.lastRequest;
248 me.processResponse(success, operation, request, response, callback, scope);
253 setException: function(operation, response) {
254 operation.setException(operation.request.jsonp.errorType);
258 <span id='Ext-data-proxy-JsonP-method-buildUrl'> /**
259 </span> * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
260 * @param {Ext.data.Request} request The request object
261 * @return {String} The url
263 buildUrl: function(request) {
265 url = me.callParent(arguments),
266 params = Ext.apply({}, request.params),
267 filters = params.filters,
271 delete params.filters;
273 if (me.autoAppendParams) {
274 url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
277 if (filters && filters.length) {
278 for (i = 0; i < filters.length; i++) {
282 url = Ext.urlAppend(url, filter.property + "=" + filter.value);
287 //if there are any records present, append them to the url also
288 records = request.records;
290 if (Ext.isArray(records) && records.length > 0) {
291 url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
298 destroy: function() {
303 <span id='Ext-data-proxy-JsonP-method-abort'> /**
304 </span> * Aborts the current server request if one is currently running
307 var lastRequest = this.lastRequest;
309 Ext.data.JsonP.abort(lastRequest.jsonp);
313 <span id='Ext-data-proxy-JsonP-method-encodeRecords'> /**
314 </span> * Encodes an array of records into a string suitable to be appended to the script src url. This is broken out into
315 * its own function so that it can be easily overridden.
316 * @param {Ext.data.Model[]} records The records array
317 * @return {String} The encoded records string
319 encodeRecords: function(records) {
320 var encoded = "",
322 len = records.length;
324 for (; i < len; i++) {
325 encoded += Ext.Object.toQueryString(records[i].data);