back to devpro   download
/** Elsewhere, cross-browser sandboxes for any purpose
 * @author      Andrea Giammarchi
 * @blog        http://webreflection.blogspot.com/
 * @version     1.1 - 20090713T02:01:00Z
 * @license     Mit Style License
 */

/** constructor
 * @param   [String/Array]  optional string or list of string to use as external sandbox scripts
 */
function Elsewhere(script){
    var body = document.body || document.documentElement, style;
    if(!(script instanceof Array))
        script = script ? [script] : [];
    body.insertBefore(this._iframe = document.createElement("iframe"), body.lastChild);
    style = this._iframe.style;
    style.position = "absolute";
    style.width = style.height = "1px";
    style.top = style.left = "-10000px";
    for(var i = script.length; 0 < i; --i)
        script[i] = '<script type="text/javascript" src="' + script[i - 1] + '"></script>';
    script[i] = '<script type="text/javascript">(function(parent){while(!parent.Elsewhere)parent=parent.parent;window["@_Elsewhere"]=parent.Elsewhere.runtime;})(window.parent);' + (
        script.length === 0 ? 'window["@_Elsewhere"]._execute();' : '(function(fn){window.addEventListener?window.addEventListener("load",fn,false):window.attachEvent("onload",fn);})(function(){window["@_Elsewhere"]._execute();});'
    ) + '</script>';
    Elsewhere.runtime = this;
    this._queue = [];
    this._document = frames[frames.length-1].document;
    this._document.open();
    this._document.write(script.join(""));
    this._document.close();
    this._head = this._document.getElementsByTagName("head")[0] || this._document.documentElement;
    delete Elsewhere.runtime;
};

/** public properties **/

/**
 * @type    Boolean
 * @name    ready       this flag is true when the Sandbox is ready.
 *                      If new Elsewhere has no arguments, this flag
 *                      and the created sandbox scope will be
 *                      sincronously ready
 */
Elsewhere.prototype.ready = false;

/** public methods **/

/** destroy, delete every property/method and destroy the iframe
 */
Elsewhere.prototype.destroy = function(){
    var iframe = this._iframe, k;
    for(k in this)
        delete this[k];
    iframe.parentNode.removeChild(iframe);
    iframe = null;
};

/** execute, execute a function via decompilation into Elsewhere scope.
 * @param   Function    a generic NOT NATIVE callback to execute Elsewhere.
 * @param   [Array]     optional list of arguments to send when function is invoked.
 */
(function(){
    function execute(self, script, arguments){
        self.arguments = arguments || [];
        self._head.appendChild(script).parentNode.removeChild(script);
        var runtime = self.runtime;
        delete self.runtime;
        delete self.arguments;
        return runtime;
    };
    function text(fn){
        return 'window["@_Elsewhere"].runtime=(' + toString.call(fn) + ').apply(window,window["@_Elsewhere"].arguments);';
    };
    var script = document.createElement("script"),
        toString = Function.prototype.toString
    ;
    try{
        script.appendChild(document.createTextNode(";"));
        Elsewhere.prototype.execute = function(fn, arguments){
            if(this.ready){
                var script = this._document.createElement("script");
                script.appendChild(this._document.createTextNode(text(fn)));
                return execute(this, script, arguments);
            } else
                this._queue.push([fn, arguments]);
        };
    }catch(e){
        Elsewhere.prototype.execute = function(fn, arguments){
            if(this.ready){
                var script = this._document.createElement("script");
                script.text = text(fn);
                return execute(this, script, arguments);
            } else
                this._queue.push([fn, arguments]);
        };
    };
    script = undefined;
})();

/** extend, inject methods or properties from Elsewhere scope.
 * @param   Object      an object with one or more key/value pairs.
 *                      If a value is a function, it is assigned from Elsewhere scope.
 *                      If a value is not a function it is directly assigned.
 *                      If a key is not undefined in the global Elsewhere scope,
 *                      this function/object/primitive will be assigned regardless the value.
 */
Elsewhere.prototype.extend = function(o){
    this.execute(function(o){
        for(var k in o)
            window["@_Elsewhere"][k] = typeof window[k] !== "undefined" ? window[k] : (typeof o[k] === "function" ? Function("return " + o[k])() : o[k]);
    }, [o]);
    return this;
};

/** public properties **/

/**
 * @type    HTMLDocument
 * @name    _document   the sandbox document
 */
Elsewhere.prototype._document;

/**
 * @type    HTMLHeadElement/HTMLDocument
 * @name    _head       where new executed scripts are appended and removed
 */
Elsewhere.prototype._head;

/**
 * @type    HTMLIframeElement
 * @name    _iframe     the created iframe for current Elsewhere instance
 */
Elsewhere.prototype._iframe;

/**
 * @type    Array
 * @name    _queue      if current instance is not ready,
 *                      every execute function will be queued
 *                      and the execution order preserved.
 */
Elsewhere.prototype._queue;

/** private iframe window properties **/

/** this property will be injected once and runtime into super global iframe object.
 * @type    Elsewhere
 * @name    "@_Elsewhere"   a reference in Elsewhere scope to Elsewhere instance itself.
 */

/** protected methods **/

/** _execute, set the instance as ready and execute queued callbacks, if any.
 * @protected
 */
Elsewhere.prototype._execute = function(){
    this.ready = true;
    while(this._queue.length)
        this.execute.apply(this, this._queue.shift());
};
back to devpro   download
Creative Commons License