<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-data-proxy-JsonP'>/**
|
</span> * @author Ed Spencer
|
*
|
* The JsonP proxy is useful when you need to load data from a domain other than the one your application is running on. If
|
* your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its data
|
* from http://domainB.com because cross-domain ajax requests are prohibited by the browser.
|
*
|
* We can get around this using a JsonP proxy. JsonP proxy injects a `<script>` tag into the DOM whenever an AJAX request
|
* would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag that would be
|
* injected might look like this:
|
*
|
* <script src="http://domainB.com/users?callback=someCallback"></script>
|
*
|
* When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
|
* other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we want
|
* to be notified when the result comes in and that it should call our callback function with the data it sends back. So
|
* long as the server formats the response to look like this, everything will work:
|
*
|
* someCallback({
|
* users: [
|
* {
|
* id: 1,
|
* name: "Ed Spencer",
|
* email: "ed@sencha.com"
|
* }
|
* ]
|
* });
|
*
|
* As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the JSON
|
* object that the server returned.
|
*
|
* JsonP proxy takes care of all of this automatically. It formats the url you pass, adding the callback parameter
|
* automatically. It even creates a temporary callback function, waits for it to be called and then puts the data into
|
* the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}. Here's how
|
* we might set that up:
|
*
|
* Ext.define('User', {
|
* extend: 'Ext.data.Model',
|
* fields: ['id', 'name', 'email']
|
* });
|
*
|
* var store = Ext.create('Ext.data.Store', {
|
* model: 'User',
|
* proxy: {
|
* type: 'jsonp',
|
* url : 'http://domainB.com/users'
|
* }
|
* });
|
*
|
* store.load();
|
*
|
* 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
|
* like this:
|
*
|
* <script src="http://domainB.com/users?callback=callback1"></script>
|
*
|
* # Customization
|
*
|
* This script tag can be customized using the {@link #callbackKey} configuration. For example:
|
*
|
* var store = Ext.create('Ext.data.Store', {
|
* model: 'User',
|
* proxy: {
|
* type: 'jsonp',
|
* url : 'http://domainB.com/users',
|
* callbackKey: 'theCallbackFunction'
|
* }
|
* });
|
*
|
* store.load();
|
*
|
* Would inject a script tag like this:
|
*
|
* <script src="http://domainB.com/users?theCallbackFunction=callback1"></script>
|
*
|
* # Implementing on the server side
|
*
|
* The remote server side needs to be configured to return data in this format. Here are suggestions for how you might
|
* achieve this using Java, PHP and ASP.net:
|
*
|
* Java:
|
*
|
* boolean jsonP = false;
|
* String cb = request.getParameter("callback");
|
* if (cb != null) {
|
* jsonP = true;
|
* response.setContentType("text/javascript");
|
* } else {
|
* response.setContentType("application/x-json");
|
* }
|
* Writer out = response.getWriter();
|
* if (jsonP) {
|
* out.write(cb + "(");
|
* }
|
* out.print(dataBlock.toJsonString());
|
* if (jsonP) {
|
* out.write(");");
|
* }
|
*
|
* PHP:
|
*
|
* $callback = $_REQUEST['callback'];
|
*
|
* // Create the output object.
|
* $output = array('a' => 'Apple', 'b' => 'Banana');
|
*
|
* //start output
|
* if ($callback) {
|
* header('Content-Type: text/javascript');
|
* echo $callback . '(' . json_encode($output) . ');';
|
* } else {
|
* header('Content-Type: application/x-json');
|
* echo json_encode($output);
|
* }
|
*
|
* ASP.net:
|
*
|
* String jsonString = "{success: true}";
|
* String cb = Request.Params.Get("callback");
|
* String responseString = "";
|
* if (!String.IsNullOrEmpty(cb)) {
|
* responseString = cb + "(" + jsonString + ")";
|
* } else {
|
* responseString = jsonString;
|
* }
|
* Response.Write(responseString);
|
*/
|
Ext.define('Ext.data.proxy.JsonP', {
|
extend: 'Ext.data.proxy.Server',
|
alternateClassName: 'Ext.data.ScriptTagProxy',
|
alias: ['proxy.jsonp', 'proxy.scripttag'],
|
requires: ['Ext.data.JsonP'],
|
|
<span id='Ext-data-proxy-JsonP-cfg-defaultWriterType'> defaultWriterType: 'base',
|
</span>
|
<span id='Ext-data-proxy-JsonP-cfg-callbackKey'> /**
|
</span> * @cfg {String} callbackKey
|
* See {@link Ext.data.JsonP#callbackKey}.
|
*/
|
callbackKey : 'callback',
|
|
<span id='Ext-data-proxy-JsonP-cfg-recordParam'> /**
|
</span> * @cfg {String} [recordParam=records]
|
* The HTTP parameter name to use when passing records to the server and the {@link #writer Json writer} is not configured
|
* to {@link Ext.data.writer.Json#encode encode} records into a parameter.
|
*
|
* The {@link #encodeRecords} method is used to encode the records to create this parameter's value.
|
*/
|
recordParam: 'records',
|
|
<span id='Ext-data-proxy-JsonP-cfg-autoAppendParams'> /**
|
</span> * @cfg {Boolean} autoAppendParams
|
* True to automatically append the request's params to the generated url. Defaults to true
|
*/
|
autoAppendParams: true,
|
|
<span id='Ext-data-proxy-JsonP-method-constructor'> constructor: function() {
|
</span> this.addEvents(
|
<span id='Ext-data-proxy-JsonP-event-exception'> /**
|
</span> * @event
|
* Fires when the server returns an exception. This event may also be listened
|
* to in the event that a request has timed out or has been aborted.
|
* @param {Ext.data.proxy.Proxy} this
|
* @param {Ext.data.Request} request The request that was sent
|
* @param {Ext.data.Operation} operation The operation that triggered the request
|
*/
|
'exception'
|
);
|
this.callParent(arguments);
|
},
|
|
<span id='Ext-data-proxy-JsonP-method-doRequest'> /**
|
</span> * @private
|
* Performs the read request to the remote domain. JsonP proxy does not actually create an Ajax request,
|
* instead we write out a `<script>` tag based on the configuration of the internal Ext.data.Request object
|
* @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
|
* @param {Function} callback A callback function to execute when the Operation has been completed
|
* @param {Object} scope The scope to execute the callback in
|
*/
|
doRequest: function(operation, callback, scope) {
|
//generate the unique IDs for this request
|
var me = this,
|
request = me.buildRequest(operation),
|
params = request.params;
|
|
// apply JsonP proxy-specific attributes to the Request
|
Ext.apply(request, {
|
callbackKey: me.callbackKey,
|
timeout: me.timeout,
|
scope: me,
|
disableCaching: false, // handled by the proxy
|
callback: me.createRequestCallback(request, operation, callback, scope)
|
});
|
|
// If we are responsible for appending the params to the URL, clear them now so that
|
// The Ext.data.JsonP singleton does not append them.
|
if (me.autoAppendParams) {
|
request.params = {};
|
}
|
|
request.jsonp = Ext.data.JsonP.request(request);
|
// restore on the request
|
request.params = params;
|
operation.setStarted();
|
me.lastRequest = request;
|
|
return request;
|
},
|
|
<span id='Ext-data-proxy-JsonP-method-createRequestCallback'> /**
|
</span> * @private
|
* Creates and returns the function that is called when the request has completed. The returned function
|
* should accept a Response object, which contains the response to be read by the configured Reader.
|
* The third argument is the callback that should be called after the request has been completed and the Reader has decoded
|
* the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
|
* theCallback refers to the callback argument received by this function.
|
* See {@link #doRequest} for details.
|
* @param {Ext.data.Request} request The Request object
|
* @param {Ext.data.Operation} operation The Operation being executed
|
* @param {Function} callback The callback function to be called when the request completes. This is usually the callback
|
* passed to doRequest
|
* @param {Object} scope The scope in which to execute the callback function
|
* @return {Function} The callback function
|
*/
|
createRequestCallback: function(request, operation, callback, scope) {
|
var me = this;
|
|
return function(success, response, errorType) {
|
delete me.lastRequest;
|
me.processResponse(success, operation, request, response, callback, scope);
|
};
|
},
|
|
<span id='Ext-data-proxy-JsonP-method-setException'> // inherit docs
|
</span> setException: function(operation, response) {
|
operation.setException(operation.request.jsonp.errorType);
|
},
|
|
|
<span id='Ext-data-proxy-JsonP-method-buildUrl'> /**
|
</span> * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
|
* @param {Ext.data.Request} request The request object
|
* @return {String} The url
|
*/
|
buildUrl: function(request) {
|
var me = this,
|
url = me.callParent(arguments),
|
records = request.records,
|
writer = me.getWriter(),
|
params,
|
filters,
|
filter, i;
|
|
// In the JsonP proxy, params may only go into the URL.
|
// So params created by the Writer get applied to the request's params here
|
if (writer && request.operation.allowWrite()) {
|
request = writer.write(request);
|
}
|
|
// Encode filters into the URL via params
|
params = request.params;
|
filters = params.filters,
|
delete params.filters;
|
if (filters && filters.length) {
|
for (i = 0; i < filters.length; i++) {
|
filter = filters[i];
|
|
if (filter.value) {
|
params[filter.property] = filter.value;
|
}
|
}
|
}
|
|
// If there's no writer, or the writer is not configured to encode the records into a parameter, then we have to do it here.
|
if ((!writer || !writer.encode) && Ext.isArray(records) && records.length > 0) {
|
params[me.recordParam] = me.encodeRecords(records);
|
}
|
|
// If we are responsible for appending the params to the URL, do it now.
|
// The params are cleared in doRequest so that the Ext.data.JsonP singleton does not add them.
|
if (me.autoAppendParams) {
|
url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
|
}
|
|
return url;
|
},
|
|
<span id='Ext-data-proxy-JsonP-method-abort'> /**
|
</span> * Aborts the current server request if one is currently running
|
*/
|
abort: function() {
|
var lastRequest = this.lastRequest;
|
if (lastRequest) {
|
Ext.data.JsonP.abort(lastRequest.jsonp);
|
}
|
},
|
|
<span id='Ext-data-proxy-JsonP-method-encodeRecords'> /**
|
</span> * Encodes an array of records into a value suitable to be added to the request `params` as the {@link #recordParam} parameter.
|
* This is broken out into its own function so that it can be easily overridden.
|
*
|
* The default implementation
|
* @param {Ext.data.Model[]} records The records array
|
* @return {Array} An array of record data objects
|
*/
|
encodeRecords: function(records) {
|
var encoded = [],
|
i = 0,
|
len = records.length;
|
|
for (; i < len; i++) {
|
encoded.push(Ext.encode(records[i].getData()));
|
}
|
|
return encoded;
|
}
|
});
|
</pre>
|
</body>
|
</html>
|