Ext.apply(Ext, {
    /**
     * Returns the current document body as an {@link Ext.core.Element}.
     * @ignore
     * @memberOf Ext
     * @return Ext.core.Element The document body
     */
    getHead : function() {
        var head;
        
        return function() {
            if (head === undefined) {
                head = Ext.get(document.getElementsByTagName("head")[0]);
            }
            
            return head;
        };
    }()
});

/** * @author Ed Spencer * @class Ext.data.ScriptTagProxy * @extends Ext.data.ServerProxy * *

ScriptTagProxy 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.AjaxProxy 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 ScriptTagProxy. ScriptTagProxy 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: "[email protected]"
        }
    ]
});
* *

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.

* *

ScriptTagProxy 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.AjaxProxy AjaxProxy}. * Here's how we might set that up:

*

Ext.regModel('User', {
    fields: ['id', 'name', 'email']
});

var store = new Ext.data.Store({
    model: 'User',
    proxy: {
        type: 'scripttag',
        url : 'http://domainB.com/users'
    }
});

store.load();
* *

That's all we need to do - ScriptTagProxy 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=stcCallback001" id="stcScript001"></script>
* *

Customization

* *

Most parts of this script tag can be customized using the {@link #callbackParam}, {@link #callbackPrefix} and * {@link #scriptIdPrefix} configurations. For example: *


var store = new Ext.data.Store({
    model: 'User',
    proxy: {
        type: 'scripttag',
        url : 'http://domainB.com/users',
        callbackParam: 'theCallbackFunction',
        callbackPrefix: 'ABC',
        scriptIdPrefix: 'injectedScript'
    }
});

store.load();
* *

Would inject a script tag like this:

*

<script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"></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 scriptTag = false;
String cb = request.getParameter("callback");
if (cb != null) {
    scriptTag = true;
    response.setContentType("text/javascript");
} else {
    response.setContentType("application/x-json");
}
Writer out = response.getWriter();
if (scriptTag) {
    out.write(cb + "(");
}
out.print(dataBlock.toJsonString());
if (scriptTag) {
    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.ScriptTagProxy', { extend: 'Ext.data.ServerProxy', alias: 'proxy.scripttag', statics: { TRANS_ID: 1000 }, defaultWriterType: 'base',
/** * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells * the server the name of the callback function set up by the load call to process the returned data object. * Defaults to "callback".

The server-side processing must read this parameter value, and generate * javascript output which calls this named function passing the data object as its only parameter. */ callbackParam : "callback",

/** * @cfg {String} scriptIdPrefix * The prefix string that is used to create a unique ID for the injected script tag element (defaults to 'stcScript') */ scriptIdPrefix: 'stcScript',
/** * @cfg {String} callbackPrefix * The prefix string that is used to create a unique callback function name in the global scope. This can optionally * be modified to give control over how the callback string passed to the remote server is generated. Defaults to 'stcCallback' */ callbackPrefix: 'stcCallback',
/** * @cfg {String} recordParam * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString'). * Defaults to 'records' */ recordParam: 'records',
/** * Reference to the most recent request made through this Proxy. Used internally to clean up when the Proxy is destroyed * @property lastRequest * @type Ext.data.Request */ lastRequest: undefined,
/** * @cfg {Boolean} autoAppendParams True to automatically append the request's params to the generated url. Defaults to true */ autoAppendParams: true, constructor: function(){ this.addEvents(
/** * @event exception * Fires when the server returns an exception * @param {Ext.data.Proxy} this * @param {Ext.data.Request} request The request that was sent * @param {Ext.data.Operation} operation The operation that triggered the request */ 'exception' ); Ext.data.ScriptTagProxy.superclass.constructor.apply(this, arguments); }, /** * @private * Performs the read request to the remote domain. ScriptTagProxy does not actually create an Ajax request, * instead we write out a