# ScriptTagProxy.js

    /**
     * @class Ext.data.ScriptTagProxy
     * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain
     * other than the originating domain of the running page.<br><br>
     * <p>
     * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
     * of the running page, you must use this class, rather than DataProxy.</em><br><br>
     * <p>
     * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
     * source code that is used as the source inside a &lt;script> tag.<br><br>
     * <p>
     * In order for the browser to process the returned data, the server must wrap the data object
     * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
     * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
     * depending on whether the callback name was passed:
     * <p>
     * <pre><code>
    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(");");
    }
    </pre></code>
     *
     * @constructor
     * @param {Object} config A configuration object.
     */
    Ext.data.ScriptTagProxy = function(config){
        Ext.data.ScriptTagProxy.superclass.constructor.call(this);
        Ext.apply(this, config);
        this.head = document.getElementsByTagName("head")[0];
    };

    Ext.data.ScriptTagProxy.TRANS_ID = 1000;

    Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {
        /**
         * @cfg {String} url The URL from which to request the data object.
         */
    // holder
    /***
         * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
         */
        timeout : 30000,
        /**
         * @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".<p>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 {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
         * name to the request.
         */
        nocache : true,

        /**
         * Load data from the configured URL, read the data object into
         * a block of Ext.data.Records using the passed Ext.data.DataReader implementation, and
         * process that block using the passed callback.
         * @param {Object} params An object containing properties which are to be used as HTTP parameters
         * for the request to the remote server.
         * @param {Ext.data.DataReader} reader The Reader object which converts the data
         * object into a block of Ext.data.Records.
         * @param {Function} callback The function into which to pass the block of Ext.data.Records.
         * The function must be passed <ul>
         * <li>The Record block object</li>
         * <li>The "arg" argument from the load function</li>
         * <li>A boolean success indicator</li>
         * </ul>
         * @param {Object} scope The scope in which to call the callback
         * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
         */
        load : function(params, reader, callback, scope, arg){
            if(this.fireEvent("beforeload", this, params) !== false){

                var p = Ext.urlEncode(Ext.apply(params, this.extraParams));

                var url = this.url;
                url += (url.indexOf("?") != -1 ? "&" : "?") + p;
                if(this.nocache){
                    url += "&_dc=" + (new Date().getTime());
                }
                var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;
                var trans = {
                    id : transId,
                    cb : "stcCallback"+transId,
                    scriptId : "stcScript"+transId,
                    params : params,
                    arg : arg,
                    url : url,
                    callback : callback,
                    scope : scope,
                    reader : reader
                };
                var conn = this;

                window[trans.cb] = function(o){
                    conn.handleResponse(o, trans);
                };

                url += String.format("&{0}={1}", this.callbackParam, trans.cb);

                if(this.autoAbort !== false){
                    this.abort();
                }

                trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);

                var script = document.createElement("script");
                script.setAttribute("src", url);
                script.setAttribute("type", "text/javascript");
                script.setAttribute("id", trans.scriptId);
                this.head.appendChild(script);

                this.trans = trans;
            }else{
                callback.call(scope||this, null, arg, false);
            }
        },

        // private
        isLoading : function(){
            return this.trans ? true : false;
        },

        /**
         * Abort the current server request.
         */
        abort : function(){
            if(this.isLoading()){
                this.destroyTrans(this.trans);
            }
        },

        // private
        destroyTrans : function(trans, isLoaded){
            this.head.removeChild(document.getElementById(trans.scriptId));
            clearTimeout(trans.timeoutId);
            if(isLoaded){
                window[trans.cb] = undefined;
                try{
                    delete window[trans.cb];
                }catch(e){}
            }else{
                // if hasn't been loaded, wait for load to remove it to prevent script error
                window[trans.cb] = function(){
                    window[trans.cb] = undefined;
                    try{
                        delete window[trans.cb];
                    }catch(e){}
                };
            }
        },

        // private
        handleResponse : function(o, trans){
            this.trans = false;
            this.destroyTrans(trans, true);
            var result;
            try {
                result = trans.reader.readRecords(o);
            }catch(e){
                this.fireEvent("loadexception", this, o, trans.arg, e);
                trans.callback.call(trans.scope||window, null, trans.arg, false);
                return;
            }
            this.fireEvent("load", this, o, trans.arg);
            trans.callback.call(trans.scope||window, result, trans.arg, true);
        },

        // private
        handleFailure : function(trans){
            this.trans = false;
            this.destroyTrans(trans, false);
            this.fireEvent("loadexception", this, null, trans.arg);
            trans.callback.call(trans.scope||window, null, trans.arg, false);
        }
    });

Ext - Copyright © 2006-2007 Ext JS, LLC
All rights reserved.