/** 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());
};