You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by bh...@apache.org on 2014/03/27 16:08:35 UTC

[21/51] [partial] CB-6346 - Add node_modules to source control

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/1139813c/blackberry10/node_modules/jasmine-node/node_modules/jasmine-reporters/ext/env.rhino.1.2.js
----------------------------------------------------------------------
diff --git a/blackberry10/node_modules/jasmine-node/node_modules/jasmine-reporters/ext/env.rhino.1.2.js b/blackberry10/node_modules/jasmine-node/node_modules/jasmine-reporters/ext/env.rhino.1.2.js
new file mode 100644
index 0000000..8da1c07
--- /dev/null
+++ b/blackberry10/node_modules/jasmine-node/node_modules/jasmine-reporters/ext/env.rhino.1.2.js
@@ -0,0 +1,13989 @@
+/*
+ * Envjs core-env.1.2.13
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+var Envjs = function(){
+    var i,
+        name,
+        override = function(){
+            for(i=0;i<arguments.length;i++){
+                for ( name in arguments[i] ) {
+                    var g = arguments[i].__lookupGetter__(name),
+                        s = arguments[i].__lookupSetter__(name);
+                    if ( g || s ) {
+                        if ( g ) { Envjs.__defineGetter__(name, g); }
+                        if ( s ) { Envjs.__defineSetter__(name, s); }
+                    } else {
+                        Envjs[name] = arguments[i][name];
+                    }
+                }
+            }
+        };
+    if(arguments.length === 1 && typeof(arguments[0]) == 'string'){
+        window.location = arguments[0];
+    }else if (arguments.length === 1 && typeof(arguments[0]) == "object"){
+        override(arguments[0]);
+    }else if(arguments.length === 2 && typeof(arguments[0]) == 'string'){
+        override(arguments[1]);
+        window.location = arguments[0];
+    }
+    return;
+},
+__this__ = this;
+
+//eg "Mozilla"
+Envjs.appCodeName  = "Envjs";
+
+//eg "Gecko/20070309 Firefox/2.0.0.3"
+Envjs.appName      = "Resig/20070309 PilotFish/1.2.13";
+
+Envjs.version = "1.6";//?
+Envjs.revision = '';
+/*
+ * Envjs core-env.1.2.13 
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+//CLOSURE_START
+(function(){
+
+
+
+
+
+/**
+ * @author john resig
+ */
+// Helper method for extending one object with another.
+function __extend__(a,b) {
+    for ( var i in b ) {
+        var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
+        if ( g || s ) {
+            if ( g ) { a.__defineGetter__(i, g); }
+            if ( s ) { a.__defineSetter__(i, s); }
+        } else {
+            a[i] = b[i];
+        }
+    } return a;
+}
+
+/**
+ * Writes message to system out
+ * @param {String} message
+ */
+Envjs.log = function(message){};
+
+/**
+ * Constants providing enumerated levels for logging in modules
+ */
+Envjs.DEBUG = 1;
+Envjs.INFO = 2;
+Envjs.WARN = 3;
+Envjs.ERROR = 3;
+Envjs.NONE = 3;
+
+/**
+ * Writes error info out to console
+ * @param {Error} e
+ */
+Envjs.lineSource = function(e){};
+
+    
+/**
+ * TODO: used in ./event/eventtarget.js
+ * @param {Object} event
+ */
+Envjs.defaultEventBehaviors = {};
+
+
+/**
+ * describes which script src values will trigger Envjs to load
+ * the script like a browser would
+ */
+Envjs.scriptTypes = {
+    "text/javascript"   :false,
+    "text/envjs"        :true
+};
+
+/**
+ * will be called when loading a script throws an error
+ * @param {Object} script
+ * @param {Object} e
+ */
+Envjs.onScriptLoadError = function(script, e){
+    console.log('error loading script %s %s', script, e);
+};
+
+
+/**
+ * load and execute script tag text content
+ * @param {Object} script
+ */
+Envjs.loadInlineScript = function(script){
+    var tmpFile;
+    tmpFile = Envjs.writeToTempFile(script.text, 'js') ;
+    load(tmpFile);
+};
+
+/**
+ * Should evaluate script in some context
+ * @param {Object} context
+ * @param {Object} source
+ * @param {Object} name
+ */
+Envjs.eval = function(context, source, name){};
+
+
+/**
+ * Executes a script tag
+ * @param {Object} script
+ * @param {Object} parser
+ */
+Envjs.loadLocalScript = function(script){
+    //console.log("loading script %s", script);
+    var types,
+    src,
+    i,
+    base,
+    filename,
+    xhr;
+
+    if(script.type){
+        types = script.type.split(";");
+        for(i=0;i<types.length;i++){
+            if(Envjs.scriptTypes[types[i]]){
+                //ok this script type is allowed
+                break;
+            }
+            if(i+1 == types.length){
+                //console.log('wont load script type %s', script.type);
+                return false;
+            }
+        }
+    }
+
+    try{
+        //console.log('handling inline scripts');
+        if(!script.src.length){
+            Envjs.loadInlineScript(script);
+            return true;
+        }
+    }catch(e){
+        //Envjs.error("Error loading script.", e);
+        Envjs.onScriptLoadError(script, e);
+        return false;
+    }
+
+
+    //console.log("loading allowed external script %s", script.src);
+
+    //lets you register a function to execute
+    //before the script is loaded
+    if(Envjs.beforeScriptLoad){
+        for(src in Envjs.beforeScriptLoad){
+            if(script.src.match(src)){
+                Envjs.beforeScriptLoad[src](script);
+            }
+        }
+    }
+    base = "" + script.ownerDocument.location;
+    //filename = Envjs.uri(script.src.match(/([^\?#]*)/)[1], base );
+    //console.log('loading script from base %s', base);
+    filename = Envjs.uri(script.src, base);
+    try {
+        xhr = new XMLHttpRequest();
+        xhr.open("GET", filename, false/*syncronous*/);
+        //console.log("loading external script %s", filename);
+        xhr.onreadystatechange = function(){
+            //console.log("readyState %s", xhr.readyState);
+            if(xhr.readyState === 4){
+                Envjs.eval(
+                    script.ownerDocument.ownerWindow,
+                    xhr.responseText,
+                    filename
+                );
+            }
+        };
+        xhr.send(null, false);
+    } catch(e) {
+        console.log("could not load script %s \n %s", filename, e );
+        Envjs.onScriptLoadError(script, e);
+        return false;
+    }
+    //lets you register a function to execute
+    //after the script is loaded
+    if(Envjs.afterScriptLoad){
+        for(src in Envjs.afterScriptLoad){
+            if(script.src.match(src)){
+                Envjs.afterScriptLoad[src](script);
+            }
+        }
+    }
+    return true;
+};
+
+
+/**
+ * An 'image' was requested by the document.
+ *
+ * - During inital parse of a <link>
+ * - Via an innerHTML parse of a <link>
+ * - A modificiation of the 'src' attribute of an Image/HTMLImageElement
+ *
+ * NOTE: this is optional API.  If this doesn't exist then the default
+ * 'loaded' event occurs.
+ *
+ * @param node {Object} the <img> node
+ * @param node the src value
+ * @return 'true' to indicate the 'load' succeed, false otherwise
+ */
+Envjs.loadImage = function(node, src) {
+    return true;
+};
+
+
+/**
+ * A 'link'  was requested by the document.  Typically this occurs when:
+ * - During inital parse of a <link>
+ * - Via an innerHTML parse of a <link>
+ * - A modificiation of the 'href' attribute on a <link> node in the tree
+ *
+ * @param node {Object} is the link node in question
+ * @param href {String} is the href.
+ *
+ * Return 'true' to indicate that the 'load' was successful, or false
+ * otherwise.  The appropriate event is then triggered.
+ *
+ * NOTE: this is optional API.  If this doesn't exist then the default
+ *   'loaded' event occurs
+ */
+Envjs.loadLink = function(node, href) {
+    return true;
+};
+
+(function(){
+
+
+/*
+ *  cookie handling
+ *  Private internal helper class used to save/retreive cookies
+ */
+
+/**
+ * Specifies the location of the cookie file
+ */
+Envjs.cookieFile = function(){
+    return 'file://'+Envjs.homedir+'/.cookies';
+};
+
+/**
+ * saves cookies to a local file
+ * @param {Object} htmldoc
+ */
+Envjs.saveCookies = function(){
+    var cookiejson = JSON.stringify(Envjs.cookies.peristent,null,'\t');
+    //console.log('persisting cookies %s', cookiejson);
+    Envjs.writeToFile(cookiejson, Envjs.cookieFile());
+};
+
+/**
+ * loads cookies from a local file
+ * @param {Object} htmldoc
+ */
+Envjs.loadCookies = function(){
+    var cookiejson,
+        js;
+    try{
+        cookiejson = Envjs.readFromFile(Envjs.cookieFile())
+        js = JSON.parse(cookiejson, null, '\t');
+    }catch(e){
+        //console.log('failed to load cookies %s', e);
+        js = {};
+    }
+    return js;
+};
+
+Envjs.cookies = {
+    persistent:{
+        //domain - key on domain name {
+            //path - key on path {
+                //name - key on name {
+                     //value : cookie value
+                     //other cookie properties
+                //}
+            //}
+        //}
+        //expire - provides a timestamp for expiring the cookie
+        //cookie - the cookie!
+    },
+    temporary:{//transient is a reserved word :(
+        //like above
+    }
+};
+
+var __cookies__;
+
+//HTMLDocument cookie
+Envjs.setCookie = function(url, cookie){
+    var i,
+        index,
+        name,
+        value,
+        properties = {},
+        attr,
+        attrs;
+    url = Envjs.urlsplit(url);
+    if(cookie)
+        attrs = cookie.split(";");
+    else
+        return;
+    
+    //for now the strategy is to simply create a json object
+    //and post it to a file in the .cookies.js file.  I hate parsing
+    //dates so I decided not to implement support for 'expires' 
+    //(which is deprecated) and instead focus on the easier 'max-age'
+    //(which succeeds 'expires') 
+    cookie = {};//keyword properties of the cookie
+    cookie['domain'] = url.hostname;
+    cookie['path'] = url.path||'/';
+    for(i=0;i<attrs.length;i++){
+        index = attrs[i].indexOf("=");
+        if(index > -1){
+            name = __trim__(attrs[i].slice(0,index));
+            value = __trim__(attrs[i].slice(index+1));
+            if(name=='max-age'){
+                //we'll have to when to check these
+                //and garbage collect expired cookies
+                cookie[name] = parseInt(value, 10);
+            } else if( name == 'domain' ){
+                if(__domainValid__(url, value)){
+                    cookie['domain'] = value;
+                }
+            } else if( name == 'path' ){
+                //not sure of any special logic for path
+                cookie['path'] = value;
+            } else {
+                //its not a cookie keyword so store it in our array of properties
+                //and we'll serialize individually in a moment
+                properties[name] = value;
+            }
+        }else{
+            if( attrs[i] == 'secure' ){
+                cookie[attrs[i]] = true;
+            }
+        }
+    }
+    if(!('max-age' in cookie)){
+        //it's a transient cookie so it only lasts as long as 
+        //the window.location remains the same (ie in-memory cookie)
+        __mergeCookie__(Envjs.cookies.temporary, cookie, properties);
+    }else{
+        //the cookie is persistent
+        __mergeCookie__(Envjs.cookies.persistent, cookie, properties);
+        Envjs.saveCookies();
+    }
+};
+
+function __domainValid__(url, value){
+    var i,
+        domainParts = url.hostname.split('.').reverse(),
+        newDomainParts = value.split('.').reverse();
+    if(newDomainParts.length > 1){
+        for(i=0;i<newDomainParts.length;i++){
+            if(!(newDomainParts[i] == domainParts[i])){
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+};
+
+Envjs.getCookies = function(url){
+    //The cookies that are returned must belong to the same domain
+    //and be at or below the current window.location.path.  Also
+    //we must check to see if the cookie was set to 'secure' in which
+    //case we must check our current location.protocol to make sure it's
+    //https:
+    var persisted;
+    url = Envjs.urlsplit(url);
+    if(!__cookies__){
+        try{
+            __cookies__ = true;
+            try{
+                persisted = Envjs.loadCookies();
+            }catch(e){
+                //fail gracefully
+                //console.log('%s', e);
+            }   
+            if(persisted){
+                __extend__(Envjs.cookies.persistent, persisted);
+            }
+            //console.log('set cookies for doc %s', doc.baseURI);
+        }catch(e){
+            console.log('cookies not loaded %s', e)
+        };
+    }
+    var temporary = __cookieString__(Envjs.cookies.temporary, url),
+        persistent =  __cookieString__(Envjs.cookies.persistent, url);
+    //console.log('temporary cookies: %s', temporary);  
+    //console.log('persistent cookies: %s', persistent);  
+    return  temporary + persistent;
+};
+
+function __cookieString__(cookies, url) {
+    var cookieString = "",
+        domain, 
+        path,
+        name,
+        i=0;
+    for (domain in cookies) {
+        // check if the cookie is in the current domain (if domain is set)
+        // console.log('cookie domain %s', domain);
+        if (domain == "" || domain == url.hostname) {
+            for (path in cookies[domain]) {
+                // console.log('cookie domain path %s', path);
+                // make sure path is at or below the window location path
+                if (path == "/" || url.path.indexOf(path) > -1) {
+                    for (name in cookies[domain][path]) {
+                        // console.log('cookie domain path name %s', name);
+                        cookieString += 
+                            ((i++ > 0)?'; ':'') +
+                            name + "=" + 
+                            cookies[domain][path][name].value;
+                    }
+                }
+            }
+        }
+    }
+    return cookieString;
+};
+
+function __mergeCookie__(target, cookie, properties){
+    var name, now;
+    if(!target[cookie.domain]){
+        target[cookie.domain] = {};
+    }
+    if(!target[cookie.domain][cookie.path]){
+        target[cookie.domain][cookie.path] = {};
+    }
+    for(name in properties){
+        now = new Date().getTime();
+        target[cookie.domain][cookie.path][name] = {
+            "value":properties[name],
+            "secure":cookie.secure,
+            "max-age":cookie['max-age'],
+            "date-created":now,
+            "expiration":(cookie['max-age']===0) ? 
+                0 :
+                now + cookie['max-age']
+        };
+        //console.log('cookie is %o',target[cookie.domain][cookie.path][name]);
+    }
+};
+
+})();//end cookies
+/*
+    http://www.JSON.org/json2.js
+    2008-07-15
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+   
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+*/
+try{ JSON; }catch(e){ 
+JSON = function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    Date.prototype.toJSON = function (key) {
+
+        return this.getUTCFullYear()   + '-' +
+             f(this.getUTCMonth() + 1) + '-' +
+             f(this.getUTCDate())      + 'T' +
+             f(this.getUTCHours())     + ':' +
+             f(this.getUTCMinutes())   + ':' +
+             f(this.getUTCSeconds())   + 'Z';
+    };
+
+    String.prototype.toJSON = function (key) {
+        return String(this);
+    };
+    Number.prototype.toJSON =
+    Boolean.prototype.toJSON = function (key) {
+        return this.valueOf();
+    };
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+        
+        escapeable.lastIndex = 0;
+        return escapeable.test(string) ?
+            '"' + string.replace(escapeable, function (a) {
+                var c = meta[a];
+                if (typeof c === 'string') {
+                    return c;
+                }
+                return '\\u' + ('0000' +
+                        (+(a.charCodeAt(0))).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+            return String(value);
+            
+        case 'object':
+
+            if (!value) {
+                return 'null';
+            }
+            gap += indent;
+            partial = [];
+
+            if (typeof value.length === 'number' &&
+                    !(value.propertyIsEnumerable('length'))) {
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+                
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+    return {
+        stringify: function (value, replacer, space) {
+
+            var i;
+            gap = '';
+            indent = '';
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+            return str('', {'': value});
+        },
+
+
+        parse: function (text, reviver) {
+            var j;
+            function walk(holder, key) {
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' + ('0000' +
+                            (+(a.charCodeAt(0))).toString(16)).slice(-4);
+                });
+            }
+
+
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+        
+                j = eval('(' + text + ')');
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+            throw new SyntaxError('JSON.parse');
+        }
+    };
+}();
+
+}
+
+/**
+ * synchronizes thread modifications
+ * @param {Function} fn
+ */
+Envjs.sync = function(fn){};
+
+/**
+ * sleep thread for specified duration
+ * @param {Object} millseconds
+ */
+Envjs.sleep = function(millseconds){};
+
+/**
+ * Interval to wait on event loop when nothing is happening
+ */
+Envjs.WAIT_INTERVAL = 20;//milliseconds
+
+/*
+ * Copyright (c) 2010 Nick Galbreath
+ * http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * url processing in the spirit of python's urlparse module
+ * see `pydoc urlparse` or
+ * http://docs.python.org/library/urlparse.html
+ *
+ *  urlsplit: break apart a URL into components
+ *  urlunsplit:  reconsistute a URL from componets
+ *  urljoin: join an absolute and another URL
+ *  urldefrag: remove the fragment from a URL
+ *
+ * Take a look at the tests in urlparse-test.html
+ *
+ * On URL Normalization:
+ *
+ * urlsplit only does minor normalization the components Only scheme
+ * and hostname are lowercased urljoin does a bit more, normalizing
+ * paths with "."  and "..".
+
+ * urlnormalize adds additional normalization
+ *
+ *   * removes default port numbers
+ *     http://abc.com:80/ -> http://abc.com/, etc
+ *   * normalizes path
+ *     http://abc.com -> http://abc.com/
+ *     and other "." and ".." cleanups
+ *   * if file, remove query and fragment
+ *
+ * It does not do:
+ *   * normalizes escaped hex values
+ *     http://abc.com/%7efoo -> http://abc.com/%7Efoo
+ *   * normalize '+' <--> '%20'
+ *
+ * Differences with Python
+ *
+ * The javascript urlsplit returns a normal object with the following
+ * properties: scheme, netloc, hostname, port, path, query, fragment.
+ * All properties are read-write.
+ *
+ * In python, the resulting object is not a dict, but a specialized,
+ * read-only, and has alternative tuple interface (e.g. obj[0] ==
+ * obj.scheme).  It's not clear why such a simple function requires
+ * a unique datastructure.
+ *
+ * urlunsplit in javascript takes an duck-typed object,
+ *  { scheme: 'http', netloc: 'abc.com', ...}
+ *  while in  * python it takes a list-like object.
+ *  ['http', 'abc.com'... ]
+ *
+ * For all functions, the javascript version use
+ * hostname+port if netloc is missing.  In python
+ * hostname+port were always ignored.
+ *
+ * Similar functionality in different languages:
+ *
+ *   http://php.net/manual/en/function.parse-url.php
+ *   returns assocative array but cannot handle relative URL
+ *
+ * TODO: test allowfragments more
+ * TODO: test netloc missing, but hostname present
+ */
+
+var urlparse = {};
+
+// Unlike to be useful standalone
+//
+// NORMALIZE PATH with "../" and "./"
+//   http://en.wikipedia.org/wiki/URL_normalization
+//   http://tools.ietf.org/html/rfc3986#section-5.2.3
+//
+urlparse.normalizepath = function(path)
+{
+    if (!path || path === '/') {
+        return '/';
+    }
+
+    var parts = path.split('/');
+
+    var newparts = [];
+    // make sure path always starts with '/'
+    if (parts[0]) {
+        newparts.push('');
+    }
+
+    for (var i = 0; i < parts.length; ++i) {
+        if (parts[i] === '..') {
+            if (newparts.length > 1) {
+                newparts.pop();
+            } else {
+                newparts.push(parts[i]);
+            }
+        } else if (parts[i] != '.') {
+            newparts.push(parts[i]);
+        }
+    }
+
+    path = newparts.join('/');
+    if (!path) {
+        path = '/';
+    }
+    return path;
+};
+
+//
+// Does many of the normalizations that the stock
+//  python urlsplit/urlunsplit/urljoin neglects
+//
+// Doesn't do hex-escape normalization on path or query
+//   %7e -> %7E
+// Nor, '+' <--> %20 translation
+//
+urlparse.urlnormalize = function(url)
+{
+    var parts = urlparse.urlsplit(url);
+    switch (parts.scheme) {
+    case 'file':
+        // files can't have query strings
+        //  and we don't bother with fragments
+        parts.query = '';
+        parts.fragment = '';
+        break;
+    case 'http':
+    case 'https':
+        // remove default port
+        if ((parts.scheme === 'http' && parts.port == 80) ||
+            (parts.scheme === 'https' && parts.port == 443)) {
+            parts.port = null;
+            // hostname is already lower case
+            parts.netloc = parts.hostname;
+        }
+        break;
+    default:
+        // if we don't have specific normalizations for this
+        // scheme, return the original url unmolested
+        return url;
+    }
+
+    // for [file|http|https].  Not sure about other schemes
+    parts.path = urlparse.normalizepath(parts.path);
+
+    return urlparse.urlunsplit(parts);
+};
+
+urlparse.urldefrag = function(url)
+{
+    var idx = url.indexOf('#');
+    if (idx == -1) {
+        return [ url, '' ];
+    } else {
+        return [ url.substr(0,idx), url.substr(idx+1) ];
+    }
+};
+
+urlparse.urlsplit = function(url, default_scheme, allow_fragments)
+{
+    var leftover;
+
+    if (typeof allow_fragments === 'undefined') {
+        allow_fragments = true;
+    }
+
+    // scheme (optional), host, port
+    var fullurl = /^([A-Za-z]+)?(:?\/\/)([0-9.\-A-Za-z]*)(?::(\d+))?(.*)$/;
+    // path, query, fragment
+    var parse_leftovers = /([^?#]*)?(?:\?([^#]*))?(?:#(.*))?$/;
+
+    var o = {};
+
+    var parts = url.match(fullurl);
+    if (parts) {
+        o.scheme = parts[1] || default_scheme || '';
+        o.hostname = parts[3].toLowerCase() || '';
+        o.port = parseInt(parts[4],10) || '';
+        // Probably should grab the netloc from regexp
+        //  and then parse again for hostname/port
+
+        o.netloc = parts[3];
+        if (parts[4]) {
+            o.netloc += ':' + parts[4];
+        }
+
+        leftover = parts[5];
+    } else {
+        o.scheme = default_scheme || '';
+        o.netloc = '';
+        o.hostname = '';
+        leftover = url;
+    }
+    o.scheme = o.scheme.toLowerCase();
+
+    parts = leftover.match(parse_leftovers);
+
+    o.path =  parts[1] || '';
+    o.query = parts[2] || '';
+
+    if (allow_fragments) {
+        o.fragment = parts[3] || '';
+    } else {
+        o.fragment = '';
+    }
+
+    return o;
+};
+
+urlparse.urlunsplit = function(o) {
+    var s = '';
+    if (o.scheme) {
+        s += o.scheme + '://';
+    }
+
+    if (o.netloc) {
+        if (s == '') {
+            s += '//';
+        }
+        s +=  o.netloc;
+    } else if (o.hostname) {
+        // extension.  Python only uses netloc
+        if (s == '') {
+            s += '//';
+        }
+        s += o.hostname;
+        if (o.port) {
+            s += ':' + o.port;
+        }
+    }
+
+    if (o.path) {
+        s += o.path;
+    }
+
+    if (o.query) {
+        s += '?' + o.query;
+    }
+    if (o.fragment) {
+        s += '#' + o.fragment;
+    }
+    return s;
+};
+
+urlparse.urljoin = function(base, url, allow_fragments)
+{
+    if (typeof allow_fragments === 'undefined') {
+        allow_fragments = true;
+    }
+
+    var url_parts = urlparse.urlsplit(url);
+
+    // if url parts has a scheme (i.e. absolute)
+    // then nothing to do
+    if (url_parts.scheme) {
+        if (! allow_fragments) {
+            return url;
+        } else {
+            return urlparse.urldefrag(url)[0];
+        }
+    }
+    var base_parts = urlparse.urlsplit(base);
+
+    // copy base, only if not present
+    if (!base_parts.scheme) {
+        base_parts.scheme = url_parts.scheme;
+    }
+
+    // copy netloc, only if not present
+    if (!base_parts.netloc || !base_parts.hostname) {
+        base_parts.netloc = url_parts.netloc;
+        base_parts.hostname = url_parts.hostname;
+        base_parts.port = url_parts.port;
+    }
+
+    // paths
+    if (url_parts.path.length > 0) {
+        if (url_parts.path.charAt(0) == '/') {
+            base_parts.path = url_parts.path;
+        } else {
+            // relative path.. get rid of "current filename" and
+            //   replace.  Same as var parts =
+            //   base_parts.path.split('/'); parts[parts.length-1] =
+            //   url_parts.path; base_parts.path = parts.join('/');
+            var idx = base_parts.path.lastIndexOf('/');
+            if (idx == -1) {
+                base_parts.path = url_parts.path;
+            } else {
+                base_parts.path = base_parts.path.substr(0,idx) + '/' +
+                    url_parts.path;
+            }
+        }
+    }
+
+    // clean up path
+    base_parts.path = urlparse.normalizepath(base_parts.path);
+
+    // copy query string
+    base_parts.query = url_parts.query;
+
+    // copy fragments
+    if (allow_fragments) {
+        base_parts.fragment = url_parts.fragment;
+    } else {
+        base_parts.fragment = '';
+    }
+
+    return urlparse.urlunsplit(base_parts);
+};
+
+/**
+ * getcwd - named after posix call of same name (see 'man 2 getcwd')
+ *
+ */
+Envjs.getcwd = function() {
+    return '.';
+};
+
+/**
+ * resolves location relative to doc location
+ *
+ * @param {Object} path  Relative or absolute URL
+ * @param {Object} base  (semi-optional)  The base url used in resolving "path" above
+ */
+Envjs.uri = function(path, base) {
+    //console.log('constructing uri from path %s and base %s', path, base);
+
+    // Semi-common trick is to make an iframe with src='javascript:false'
+    //  (or some equivalent).  By returning '', the load is skipped
+    if (path.indexOf('javascript') === 0) {
+        return '';
+    }
+
+    // if path is absolute, then just normalize and return
+    if (path.match('^[a-zA-Z]+://')) {
+        return urlparse.urlnormalize(path);
+    }
+
+    // interesting special case, a few very large websites use
+    // '//foo/bar/' to mean 'http://foo/bar'
+    if (path.match('^//')) {
+        path = 'http:' + path;
+    }
+
+    // if base not passed in, try to get it from document
+    // Ideally I would like the caller to pass in document.baseURI to
+    //  make this more self-sufficient and testable
+    if (!base && document) {
+        base = document.baseURI;
+    }
+
+    // about:blank doesn't count
+    if (base === 'about:blank'){
+        base = '';
+    }
+
+    // if base is still empty, then we are in QA mode loading local
+    // files.  Get current working directory
+    if (!base) {
+        base = 'file://' +  Envjs.getcwd() + '/';
+    }
+    // handles all cases if path is abosulte or relative to base
+    // 3rd arg is "false" --> remove fragments
+    var newurl = urlparse.urlnormalize(urlparse.urljoin(base, path, false));
+
+    return newurl;
+};
+
+
+
+/**
+ * Used in the XMLHttpRquest implementation to run a
+ * request in a seperate thread
+ * @param {Object} fn
+ */
+Envjs.runAsync = function(fn){};
+
+
+/**
+ * Used to write to a local file
+ * @param {Object} text
+ * @param {Object} url
+ */
+Envjs.writeToFile = function(text, url){};
+
+
+/**
+ * Used to write to a local file
+ * @param {Object} text
+ * @param {Object} suffix
+ */
+Envjs.writeToTempFile = function(text, suffix){};
+
+/**
+ * Used to read the contents of a local file
+ * @param {Object} url
+ */
+Envjs.readFromFile = function(url){};
+
+/**
+ * Used to delete a local file
+ * @param {Object} url
+ */
+Envjs.deleteFile = function(url){};
+
+/**
+ * establishes connection and calls responsehandler
+ * @param {Object} xhr
+ * @param {Object} responseHandler
+ * @param {Object} data
+ */
+Envjs.connection = function(xhr, responseHandler, data){};
+
+
+__extend__(Envjs, urlparse);
+
+/**
+ * Makes an object window-like by proxying object accessors
+ * @param {Object} scope
+ * @param {Object} parent
+ */
+Envjs.proxy = function(scope, parent, aliasList){};
+
+Envjs.javaEnabled = false;
+
+Envjs.homedir        = '';
+Envjs.tmpdir         = '';
+Envjs.os_name        = '';
+Envjs.os_arch        = '';
+Envjs.os_version     = '';
+Envjs.lang           = '';
+Envjs.platform       = '';
+
+/**
+ *
+ * @param {Object} frameElement
+ * @param {Object} url
+ */
+Envjs.loadFrame = function(frame, url){
+    try {
+        if(frame.contentWindow){
+            //mark for garbage collection
+            frame.contentWindow = null;
+        }
+
+        //create a new scope for the window proxy
+        //platforms will need to override this function
+        //to make sure the scope is global-like
+        frame.contentWindow = (function(){return this;})();
+        new Window(frame.contentWindow, window);
+
+        //I dont think frames load asynchronously in firefox
+        //and I think the tests have verified this but for
+        //some reason I'm less than confident... Are there cases?
+        frame.contentDocument = frame.contentWindow.document;
+        frame.contentDocument.async = false;
+        if(url){
+            //console.log('envjs.loadFrame async %s', frame.contentDocument.async);
+            frame.contentWindow.location = url;
+        }
+    } catch(e) {
+        console.log("failed to load frame content: from %s %s", url, e);
+    }
+};
+
+
+// The following are in rhino/window.js
+// TODO: Envjs.unloadFrame
+// TODO: Envjs.proxy
+
+/**
+ * @author john resig & the envjs team
+ * @uri http://www.envjs.com/
+ * @copyright 2008-2010
+ * @license MIT
+ */
+//CLOSURE_END
+}());
+/*
+ * Envjs rhino-env.1.2.13
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+var __context__ = Packages.org.mozilla.javascript.Context.getCurrentContext();
+
+Envjs.platform       = "Rhino";
+Envjs.revision       = "1.7.0.rc2";
+
+/*
+ * Envjs rhino-env.1.2.13 
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+//CLOSURE_START
+(function(){
+
+
+
+
+
+/**
+ * @author john resig
+ */
+// Helper method for extending one object with another.
+function __extend__(a,b) {
+    for ( var i in b ) {
+        var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
+        if ( g || s ) {
+            if ( g ) { a.__defineGetter__(i, g); }
+            if ( s ) { a.__defineSetter__(i, s); }
+        } else {
+            a[i] = b[i];
+        }
+    } return a;
+}
+
+/**
+ * Writes message to system out.
+ *
+ * Some sites redefine 'print' as in 'window.print', so instead of
+ * printing to stdout, you are popping open a new window, which might
+ * call print, etc, etc,etc This can cause infinite loops and can
+ * exhausing all memory.
+ *
+ * By defining this upfront now, Envjs.log will always call the native 'print'
+ * function
+ *
+ * @param {Object} message
+ */
+Envjs.log = print;
+
+Envjs.lineSource = function(e){
+    return e&&e.rhinoException?e.rhinoException.lineSource():"(line ?)";
+};
+/**
+ * load and execute script tag text content
+ * @param {Object} script
+ */
+Envjs.loadInlineScript = function(script){
+    if(script.ownerDocument.ownerWindow){
+        Envjs.eval(
+            script.ownerDocument.ownerWindow,
+            script.text,
+            'eval('+script.text.substring(0,16)+'...):'+new Date().getTime()
+        );
+    }else{
+        Envjs.eval(
+            __this__,
+            script.text,
+            'eval('+script.text.substring(0,16)+'...):'+new Date().getTime()
+        );
+    }
+    //console.log('evaluated at scope %s \n%s',
+    //    script.ownerDocument.ownerWindow.guid, script.text);
+};
+
+
+Envjs.eval = function(context, source, name){
+    __context__.evaluateString(
+        context,
+        source,
+        name,
+        0,
+        null
+    );
+};
+
+//Temporary patch for parser module
+Packages.org.mozilla.javascript.Context.
+    getCurrentContext().setOptimizationLevel(-1);
+
+/**
+ * Rhino provides a very succinct 'sync'
+ * @param {Function} fn
+ */
+try{
+    Envjs.sync = sync;
+    Envjs.spawn = spawn;
+} catch(e){
+    //sync unavailable on AppEngine
+    Envjs.sync = function(fn){
+        //console.log('Threadless platform, sync is safe');
+        return fn;
+    };
+
+    Envjs.spawn = function(fn){
+        //console.log('Threadless platform, spawn shares main thread.');
+        return fn();
+    };
+}
+
+/**
+ * sleep thread for specified duration
+ * @param {Object} millseconds
+ */
+Envjs.sleep = function(millseconds){
+    try{
+        java.lang.Thread.currentThread().sleep(millseconds);
+    }catch(e){
+        console.log('Threadless platform, cannot sleep.');
+    }
+};
+
+/**
+ * provides callback hook for when the system exits
+ */
+Envjs.onExit = function(callback){
+    var rhino = Packages.org.mozilla.javascript,
+        contextFactory =  __context__.getFactory(),
+        listener = new rhino.ContextFactory.Listener({
+            contextReleased: function(context){
+                if(context === __context__)
+                    console.log('context released', context);
+                contextFactory.removeListener(this);
+                if(callback)
+                    callback();
+            }
+        });
+    contextFactory.addListener(listener);
+};
+
+/**
+ * Get 'Current Working Directory'
+ */
+Envjs.getcwd = function() {
+    return java.lang.System.getProperty('user.dir');
+}
+
+/**
+ *
+ * @param {Object} fn
+ * @param {Object} onInterupt
+ */
+Envjs.runAsync = function(fn, onInterupt){
+    ////Envjs.debug("running async");
+    var running = true,
+        run;
+
+    try{
+        run = Envjs.sync(function(){
+            fn();
+            Envjs.wait();
+        });
+        Envjs.spawn(run);
+    }catch(e){
+        console.log("error while running async operation", e);
+        try{if(onInterrupt)onInterrupt(e)}catch(ee){};
+    }
+};
+
+/**
+ * Used to write to a local file
+ * @param {Object} text
+ * @param {Object} url
+ */
+Envjs.writeToFile = function(text, url){
+    //Envjs.debug("writing text to url : " + url);
+    var out = new java.io.FileWriter(
+        new java.io.File(
+            new java.net.URI(url.toString())));
+    out.write( text, 0, text.length );
+    out.flush();
+    out.close();
+};
+
+/**
+ * Used to write to a local file
+ * @param {Object} text
+ * @param {Object} suffix
+ */
+Envjs.writeToTempFile = function(text, suffix){
+    //Envjs.debug("writing text to temp url : " + suffix);
+    // Create temp file.
+    var temp = java.io.File.createTempFile("envjs-tmp", suffix);
+
+    // Delete temp file when program exits.
+    temp.deleteOnExit();
+
+    // Write to temp file
+    var out = new java.io.FileWriter(temp);
+    out.write(text, 0, text.length);
+    out.close();
+    return temp.getAbsolutePath().toString()+'';
+};
+
+
+/**
+ * Used to read the contents of a local file
+ * @param {Object} url
+ */
+Envjs.readFromFile = function( url ){
+    var fileReader = new java.io.FileReader(
+        new java.io.File( 
+            new java.net.URI( url )));
+            
+    var stringwriter = new java.io.StringWriter(),
+        buffer = java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 1024),
+        length;
+
+    while ((length = fileReader.read(buffer, 0, 1024)) != -1) {
+        stringwriter.write(buffer, 0, length);
+    }
+
+    stringwriter.close();
+    return stringwriter.toString()+"";
+};
+    
+
+/**
+ * Used to delete a local file
+ * @param {Object} url
+ */
+Envjs.deleteFile = function(url){
+    var file = new java.io.File( new java.net.URI( url ) );
+    file["delete"]();
+};
+
+/**
+ * establishes connection and calls responsehandler
+ * @param {Object} xhr
+ * @param {Object} responseHandler
+ * @param {Object} data
+ */
+Envjs.connection = function(xhr, responseHandler, data){
+    var url = java.net.URL(xhr.url),
+        connection,
+        header,
+        outstream,
+        buffer,
+        length,
+        binary = false,
+        name, value,
+        contentEncoding,
+        instream,
+        responseXML,
+        i;
+    if ( /^file\:/.test(url) ) {
+        try{
+            if ( "PUT" == xhr.method || "POST" == xhr.method ) {
+                data =  data || "" ;
+                Envjs.writeToFile(data, url);
+                xhr.readyState = 4;
+                //could be improved, I just cant recall the correct http codes
+                xhr.status = 200;
+                xhr.statusText = "";
+            } else if ( xhr.method == "DELETE" ) {
+                Envjs.deleteFile(url);
+                xhr.readyState = 4;
+                //could be improved, I just cant recall the correct http codes
+                xhr.status = 200;
+                xhr.statusText = "";
+            } else {
+                connection = url.openConnection();
+                connection.connect();
+                //try to add some canned headers that make sense
+
+                try{
+                    if(xhr.url.match(/html$/)){
+                        xhr.responseHeaders["Content-Type"] = 'text/html';
+                    }else if(xhr.url.match(/.xml$/)){
+                        xhr.responseHeaders["Content-Type"] = 'text/xml';
+                    }else if(xhr.url.match(/.js$/)){
+                        xhr.responseHeaders["Content-Type"] = 'text/javascript';
+                    }else if(xhr.url.match(/.json$/)){
+                        xhr.responseHeaders["Content-Type"] = 'application/json';
+                    }else{
+                        xhr.responseHeaders["Content-Type"] = 'text/plain';
+                    }
+                    //xhr.responseHeaders['Last-Modified'] = connection.getLastModified();
+                    //xhr.responseHeaders['Content-Length'] = headerValue+'';
+                    //xhr.responseHeaders['Date'] = new Date()+'';*/
+                }catch(e){
+                    console.log('failed to load response headers',e);
+                }
+            }
+        }catch(e){
+            console.log('failed to open file %s %s', url, e);
+            connection = null;
+            xhr.readyState = 4;
+            xhr.statusText = "Local File Protocol Error";
+            xhr.responseText = "<html><head/><body><p>"+ e+ "</p></body></html>";
+        }
+    } else {
+        connection = url.openConnection();
+        connection.setRequestMethod( xhr.method );
+
+        // Add headers to Java connection
+        for (header in xhr.headers){
+            connection.addRequestProperty(header+'', xhr.headers[header]+'');
+        }
+
+        //write data to output stream if required
+        if(data){
+            if(data instanceof Document){
+                if ( xhr.method == "PUT" || xhr.method == "POST" ) {
+                    connection.setDoOutput(true);
+                    outstream = connection.getOutputStream(),
+                    xml = (new XMLSerializer()).serializeToString(data);
+                    buffer = new java.lang.String(xml).getBytes('UTF-8');
+                    outstream.write(buffer, 0, buffer.length);
+                    outstream.close();
+                }
+            }else if(data.length&&data.length>0){
+                if ( xhr.method == "PUT" || xhr.method == "POST" ) {
+                    connection.setDoOutput(true);
+                    outstream = connection.getOutputStream();
+                    buffer = new java.lang.String(data).getBytes('UTF-8');
+                    outstream.write(buffer, 0, buffer.length);
+                    outstream.close();
+                }
+            }
+            connection.connect();
+        }else{
+            connection.connect();
+        }
+    }
+
+    if(connection){
+        try{
+            length = connection.getHeaderFields().size();
+            // Stick the response headers into responseHeaders
+            for (i = 0; i < length; i++) {
+                name = connection.getHeaderFieldKey(i);
+                value = connection.getHeaderField(i);
+                if (name)
+                    xhr.responseHeaders[name+''] = value+'';
+            }
+        }catch(e){
+            console.log('failed to load response headers \n%s',e);
+        }
+
+        xhr.readyState = 4;
+        xhr.status = parseInt(connection.responseCode,10) || undefined;
+        xhr.statusText = connection.responseMessage || "";
+
+        contentEncoding = connection.getContentEncoding() || "utf-8";
+        instream = null;
+        responseXML = null;
+        
+        try{
+            //console.log('contentEncoding %s', contentEncoding);
+            if( contentEncoding.equalsIgnoreCase("gzip") ||
+                contentEncoding.equalsIgnoreCase("decompress")){
+                //zipped content
+                binary = true;
+                outstream = new java.io.ByteArrayOutputStream();
+                buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 1024);
+                instream = new java.util.zip.GZIPInputStream(connection.getInputStream())
+            }else{
+                //this is a text file
+                outstream = new java.io.StringWriter();
+                buffer = java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 1024);
+                instream = new java.io.InputStreamReader(connection.getInputStream());
+            }
+        }catch(e){
+            if (connection.getResponseCode() == 404){
+                console.log('failed to open connection stream \n %s %s',
+                            e.toString(), e);
+            }else{
+                console.log('failed to open connection stream \n %s %s',
+                            e.toString(), e);
+            }
+            instream = connection.getErrorStream();
+        }
+
+        while ((length = instream.read(buffer, 0, 1024)) != -1) {
+            outstream.write(buffer, 0, length);
+        }
+
+        outstream.close();
+        instream.close();
+        
+        if(binary){
+            xhr.responseText = new String(outstream.toByteArray(), 'UTF-8') + '';
+        }else{
+            xhr.responseText = outstream.toString() + '';
+        }
+
+    }
+    if(responseHandler){
+        //Envjs.debug('calling ajax response handler');
+        responseHandler();
+    }
+};
+
+//Since we're running in rhino I guess we can safely assume
+//java is 'enabled'.  I'm sure this requires more thought
+//than I've given it here
+Envjs.javaEnabled = true;
+
+Envjs.homedir        = java.lang.System.getProperty("user.home");
+Envjs.tmpdir         = java.lang.System.getProperty("java.io.tmpdir");
+Envjs.os_name        = java.lang.System.getProperty("os.name");
+Envjs.os_arch        = java.lang.System.getProperty("os.arch");
+Envjs.os_version     = java.lang.System.getProperty("os.version");
+Envjs.lang           = java.lang.System.getProperty("user.lang");
+
+
+/**
+ *
+ * @param {Object} frameElement
+ * @param {Object} url
+ */
+Envjs.loadFrame = function(frame, url){
+    try {
+        if(frame.contentWindow){
+            //mark for garbage collection
+            frame.contentWindow = null;
+        }
+
+        //create a new scope for the window proxy
+        frame.contentWindow = Envjs.proxy();
+        new Window(frame.contentWindow, window);
+
+        //I dont think frames load asynchronously in firefox
+        //and I think the tests have verified this but for
+        //some reason I'm less than confident... Are there cases?
+        frame.contentDocument = frame.contentWindow.document;
+        frame.contentDocument.async = false;
+        if(url){
+            //console.log('envjs.loadFrame async %s', frame.contentDocument.async);
+            frame.contentWindow.location = url;
+        }
+    } catch(e) {
+        console.log("failed to load frame content: from %s %s", url, e);
+    }
+};
+
+/**
+ * unloadFrame
+ * @param {Object} frame
+ */
+Envjs.unloadFrame = function(frame){
+    var all, length, i;
+    try{
+        //TODO: probably self-referencing structures within a document tree
+        //preventing it from being entirely garbage collected once orphaned.
+        //Should have code to walk tree and break all links between contained
+        //objects.
+        frame.contentDocument = null;
+        if(frame.contentWindow){
+            frame.contentWindow.close();
+        }
+        gc();
+    }catch(e){
+        console.log(e);
+    }
+};
+
+/**
+ * Makes an object window-like by proxying object accessors
+ * @param {Object} scope
+ * @param {Object} parent
+ */
+Envjs.proxy = function(scope, parent) {
+    try{
+        if(scope+'' == '[object global]'){
+            return scope
+        }else{
+            return  __context__.initStandardObjects();
+        }
+    }catch(e){
+        console.log('failed to init standard objects %s %s \n%s', scope, parent, e);
+    }
+
+};
+
+/**
+ * @author john resig & the envjs team
+ * @uri http://www.envjs.com/
+ * @copyright 2008-2010
+ * @license MIT
+ */
+//CLOSURE_END
+}());
+
+/**
+ * @author envjs team
+ */
+var Console,
+    console;
+
+/*
+ * Envjs console.1.2.13 
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+//CLOSURE_START
+(function(){
+
+
+
+
+
+/**
+ * @author envjs team
+ * borrowed 99%-ish with love from firebug-lite
+ *
+ * http://wiki.commonjs.org/wiki/Console
+ */
+Console = function(module){
+    var $level,
+    $logger,
+    $null = function(){};
+
+
+    if(Envjs[module] && Envjs[module].loglevel){
+        $level = Envjs.module.loglevel;
+        $logger = {
+            log: function(level){
+                logFormatted(arguments, (module)+" ");
+            },
+            debug: $level>1 ? $null: function() {
+                logFormatted(arguments, (module)+" debug");
+            },
+            info: $level>2 ? $null:function(){
+                logFormatted(arguments, (module)+" info");
+            },
+            warn: $level>3 ? $null:function(){
+                logFormatted(arguments, (module)+" warning");
+            },
+            error: $level>4 ? $null:function(){
+                logFormatted(arguments, (module)+" error");
+            }
+        };
+    } else {
+        $logger = {
+            log: function(level){
+                logFormatted(arguments, "");
+            },
+            debug: $null,
+            info: $null,
+            warn: $null,
+            error: $null
+        };
+    }
+
+    return $logger;
+};
+
+console = new Console("console",1);
+
+function logFormatted(objects, className)
+{
+    var html = [];
+
+    var format = objects[0];
+    var objIndex = 0;
+
+    if (typeof(format) != "string")
+    {
+        format = "";
+        objIndex = -1;
+    }
+
+    var parts = parseFormat(format);
+    for (var i = 0; i < parts.length; ++i)
+    {
+        var part = parts[i];
+        if (part && typeof(part) == "object")
+        {
+            var object = objects[++objIndex];
+            part.appender(object, html);
+        }
+        else {
+            appendText(part, html);
+	}
+    }
+
+    for (var i = objIndex+1; i < objects.length; ++i)
+    {
+        appendText(" ", html);
+
+        var object = objects[i];
+        if (typeof(object) == "string") {
+            appendText(object, html);
+        } else {
+            appendObject(object, html);
+	}
+    }
+
+    Envjs.log(html.join(' '));
+}
+
+function parseFormat(format)
+{
+    var parts = [];
+
+    var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;
+    var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat};
+
+    for (var m = reg.exec(format); m; m = reg.exec(format))
+    {
+        var type = m[8] ? m[8] : m[5];
+        var appender = type in appenderMap ? appenderMap[type] : appendObject;
+        var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
+
+        parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
+        parts.push({appender: appender, precision: precision});
+
+        format = format.substr(m.index+m[0].length);
+    }
+
+    parts.push(format);
+
+    return parts;
+}
+
+function escapeHTML(value)
+{
+    return value;
+}
+
+function objectToString(object)
+{
+    try
+    {
+        return object+"";
+    }
+    catch (exc)
+    {
+        return null;
+    }
+}
+
+// ********************************************************************************************
+
+function appendText(object, html)
+{
+    html.push(escapeHTML(objectToString(object)));
+}
+
+function appendNull(object, html)
+{
+    html.push(escapeHTML(objectToString(object)));
+}
+
+function appendString(object, html)
+{
+    html.push(escapeHTML(objectToString(object)));
+}
+
+function appendInteger(object, html)
+{
+    html.push(escapeHTML(objectToString(object)));
+}
+
+function appendFloat(object, html)
+{
+    html.push(escapeHTML(objectToString(object)));
+}
+
+function appendFunction(object, html)
+{
+    var reName = /function ?(.*?)\(/;
+    var m = reName.exec(objectToString(object));
+    var name = m ? m[1] : "function";
+    html.push(escapeHTML(name));
+}
+
+function appendObject(object, html)
+{
+    try
+    {
+        if (object == undefined) {
+            appendNull("undefined", html);
+        } else if (object == null) {
+            appendNull("null", html);
+        } else if (typeof object == "string") {
+            appendString(object, html);
+	} else if (typeof object == "number") {
+            appendInteger(object, html);
+	} else if (typeof object == "function") {
+            appendFunction(object, html);
+        } else if (object.nodeType == 1) {
+            appendSelector(object, html);
+        } else if (typeof object == "object") {
+            appendObjectFormatted(object, html);
+        } else {
+            appendText(object, html);
+	}
+    }
+    catch (exc)
+    {
+    }
+}
+
+function appendObjectFormatted(object, html)
+{
+    var text = objectToString(object);
+    var reObject = /\[object (.*?)\]/;
+
+    var m = reObject.exec(text);
+    html.push( m ? m[1] : text);
+}
+
+function appendSelector(object, html)
+{
+
+    html.push(escapeHTML(object.nodeName.toLowerCase()));
+    if (object.id) {
+        html.push(escapeHTML(object.id));
+    }
+    if (object.className) {
+        html.push(escapeHTML(object.className));
+    }
+}
+
+function appendNode(node, html)
+{
+    if (node.nodeType == 1)
+    {
+        html.push( node.nodeName.toLowerCase());
+
+        for (var i = 0; i < node.attributes.length; ++i)
+        {
+            var attr = node.attributes[i];
+            if (!attr.specified) {
+                continue;
+	    }
+
+            html.push( attr.nodeName.toLowerCase(),escapeHTML(attr.nodeValue));
+        }
+
+        if (node.firstChild)
+        {
+            for (var child = node.firstChild; child; child = child.nextSibling) {
+                appendNode(child, html);
+	    }
+
+            html.push( node.nodeName.toLowerCase());
+        }
+    }
+    else if (node.nodeType === 3)
+    {
+        html.push(escapeHTML(node.nodeValue));
+    }
+};
+
+/**
+ * @author john resig & the envjs team
+ * @uri http://www.envjs.com/
+ * @copyright 2008-2010
+ * @license MIT
+ */
+//CLOSURE_END
+}());
+/*
+ * Envjs dom.1.2.13 
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ * 
+ * Parts of the implementation were originally written by:\
+ * and Jon van Noort   (jon@webarcana.com.au) \
+ * and David Joham     (djoham@yahoo.com)",\ 
+ * and Scott Severtson
+ * 
+ * This file simply provides the global definitions we need to \
+ * be able to correctly implement to core browser DOM interfaces."
+ */
+
+var Attr,
+    CDATASection,
+    CharacterData,
+    Comment,
+    Document,
+    DocumentFragment,
+    DocumentType,
+    DOMException,
+    DOMImplementation,
+    Element,
+    Entity,
+    EntityReference,
+    NamedNodeMap,
+    Namespace,
+    Node,
+    NodeList,
+    Notation,
+    ProcessingInstruction,
+    Text,
+    Range,
+    XMLSerializer,
+    DOMParser;
+
+
+
+/*
+ * Envjs dom.1.2.13 
+ * Pure JavaScript Browser Environment
+ * By John Resig <http://ejohn.org/> and the Envjs Team
+ * Copyright 2008-2010 John Resig, under the MIT License
+ */
+
+//CLOSURE_START
+(function(){
+
+
+
+
+
+/**
+ * @author john resig
+ */
+// Helper method for extending one object with another.
+function __extend__(a,b) {
+    for ( var i in b ) {
+        var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
+        if ( g || s ) {
+            if ( g ) { a.__defineGetter__(i, g); }
+            if ( s ) { a.__defineSetter__(i, s); }
+        } else {
+            a[i] = b[i];
+        }
+    } return a;
+}
+
+/**
+ * @author john resig
+ */
+//from jQuery
+function __setArray__( target, array ) {
+    // Resetting the length to 0, then using the native Array push
+    // is a super-fast way to populate an object with array-like properties
+    target.length = 0;
+    Array.prototype.push.apply( target, array );
+}
+
+/**
+ * @class  NodeList -
+ *      provides the abstraction of an ordered collection of nodes
+ *
+ * @param  ownerDocument : Document - the ownerDocument
+ * @param  parentNode    : Node - the node that the NodeList is attached to (or null)
+ */
+NodeList = function(ownerDocument, parentNode) {
+    this.length = 0;
+    this.parentNode = parentNode;
+    this.ownerDocument = ownerDocument;
+    this._readonly = false;
+    __setArray__(this, []);
+};
+
+__extend__(NodeList.prototype, {
+    item : function(index) {
+        var ret = null;
+        if ((index >= 0) && (index < this.length)) {
+            // bounds check
+            ret = this[index];
+        }
+        // if the index is out of bounds, default value null is returned
+        return ret;
+    },
+    get xml() {
+        var ret = "",
+            i;
+
+        // create string containing the concatenation of the string values of each child
+        for (i=0; i < this.length; i++) {
+            if(this[i]){
+                if(this[i].nodeType == Node.TEXT_NODE && i>0 &&
+                   this[i-1].nodeType == Node.TEXT_NODE){
+                    //add a single space between adjacent text nodes
+                    ret += " "+this[i].xml;
+                }else{
+                    ret += this[i].xml;
+                }
+            }
+        }
+        return ret;
+    },
+    toArray: function () {
+        var children = [],
+            i;
+        for ( i=0; i < this.length; i++) {
+            children.push (this[i]);
+        }
+        return children;
+    },
+    toString: function(){
+        return "[object NodeList]";
+    }
+});
+
+
+/**
+ * @method __findItemIndex__
+ *      find the item index of the node
+ * @author Jon van Noort (jon@webarcana.com.au)
+ * @param  node : Node
+ * @return : int
+ */
+var __findItemIndex__ = function (nodelist, node) {
+    var ret = -1, i;
+    for (i=0; i<nodelist.length; i++) {
+        // compare id to each node's _id
+        if (nodelist[i] === node) {
+            // found it!
+            ret = i;
+            break;
+        }
+    }
+    // if node is not found, default value -1 is returned
+    return ret;
+};
+
+/**
+ * @method __insertBefore__
+ *      insert the specified Node into the NodeList before the specified index
+ *      Used by Node.insertBefore(). Note: Node.insertBefore() is responsible
+ *      for Node Pointer surgery __insertBefore__ simply modifies the internal
+ *      data structure (Array).
+ * @param  newChild      : Node - the Node to be inserted
+ * @param  refChildIndex : int     - the array index to insert the Node before
+ */
+var __insertBefore__ = function(nodelist, newChild, refChildIndex) {
+    if ((refChildIndex >= 0) && (refChildIndex <= nodelist.length)) {
+        // bounds check
+        if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // node is a DocumentFragment
+            // append the children of DocumentFragment
+            Array.prototype.splice.apply(nodelist,
+                [refChildIndex, 0].concat(newChild.childNodes.toArray()));
+        }
+        else {
+            // append the newChild
+            Array.prototype.splice.apply(nodelist,[refChildIndex, 0, newChild]);
+        }
+    }
+};
+
+/**
+ * @method __replaceChild__
+ *      replace the specified Node in the NodeList at the specified index
+ *      Used by Node.replaceChild(). Note: Node.replaceChild() is responsible
+ *      for Node Pointer surgery __replaceChild__ simply modifies the internal
+ *      data structure (Array).
+ *
+ * @param  newChild      : Node - the Node to be inserted
+ * @param  refChildIndex : int     - the array index to hold the Node
+ */
+var __replaceChild__ = function(nodelist, newChild, refChildIndex) {
+    var ret = null;
+
+    // bounds check
+    if ((refChildIndex >= 0) && (refChildIndex < nodelist.length)) {
+        // preserve old child for return
+        ret = nodelist[refChildIndex];
+
+        if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // node is a DocumentFragment
+            // get array containing children prior to refChild
+            Array.prototype.splice.apply(nodelist,
+                [refChildIndex, 1].concat(newChild.childNodes.toArray()));
+        }
+        else {
+            // simply replace node in array (links between Nodes are
+            // made at higher level)
+            nodelist[refChildIndex] = newChild;
+        }
+    }
+    // return replaced node
+    return ret;
+};
+
+/**
+ * @method __removeChild__
+ *      remove the specified Node in the NodeList at the specified index
+ *      Used by Node.removeChild(). Note: Node.removeChild() is responsible
+ *      for Node Pointer surgery __removeChild__ simply modifies the internal
+ *      data structure (Array).
+ * @param  refChildIndex : int - the array index holding the Node to be removed
+ */
+var __removeChild__ = function(nodelist, refChildIndex) {
+    var ret = null;
+
+    if (refChildIndex > -1) {
+        // found it!
+        // return removed node
+        ret = nodelist[refChildIndex];
+
+        // rebuild array without removed child
+        Array.prototype.splice.apply(nodelist,[refChildIndex, 1]);
+    }
+    // return removed node
+    return ret;
+};
+
+/**
+ * @method __appendChild__
+ *      append the specified Node to the NodeList. Used by Node.appendChild().
+ *      Note: Node.appendChild() is responsible for Node Pointer surgery
+ *      __appendChild__ simply modifies the internal data structure (Array).
+ * @param  newChild      : Node - the Node to be inserted
+ */
+var __appendChild__ = function(nodelist, newChild) {
+    if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+        // node is a DocumentFragment
+        // append the children of DocumentFragment
+        Array.prototype.push.apply(nodelist, newChild.childNodes.toArray() );
+    } else {
+        // simply add node to array (links between Nodes are made at higher level)
+        Array.prototype.push.apply(nodelist, [newChild]);
+    }
+
+};
+
+/**
+ * @method __cloneNodes__ -
+ *      Returns a NodeList containing clones of the Nodes in this NodeList
+ * @param  deep : boolean -
+ *      If true, recursively clone the subtree under each of the nodes;
+ *      if false, clone only the nodes themselves (and their attributes,
+ *      if it is an Element).
+ * @param  parentNode : Node - the new parent of the cloned NodeList
+ * @return : NodeList - NodeList containing clones of the Nodes in this NodeList
+ */
+var __cloneNodes__ = function(nodelist, deep, parentNode) {
+    var cloneNodeList = new NodeList(nodelist.ownerDocument, parentNode);
+
+    // create list containing clones of each child
+    for (var i=0; i < nodelist.length; i++) {
+        __appendChild__(cloneNodeList, nodelist[i].cloneNode(deep));
+    }
+
+    return cloneNodeList;
+};
+
+
+var __ownerDocument__ = function(node){
+    return (node.nodeType == Node.DOCUMENT_NODE)?node:node.ownerDocument;
+};
+
+/**
+ * @class  Node -
+ *      The Node interface is the primary datatype for the entire
+ *      Document Object Model. It represents a single node in the
+ *      document tree.
+ * @param  ownerDocument : Document - The Document object associated with this node.
+ */
+
+Node = function(ownerDocument) {
+    this.baseURI = 'about:blank';
+    this.namespaceURI = null;
+    this.nodeName = "";
+    this.nodeValue = null;
+
+    // A NodeList that contains all children of this node. If there are no
+    // children, this is a NodeList containing no nodes.  The content of the
+    // returned NodeList is "live" in the sense that, for instance, changes to
+    // the children of the node object that it was created from are immediately
+    // reflected in the nodes returned by the NodeList accessors; it is not a
+    // static snapshot of the content of the node. This is true for every
+    // NodeList, including the ones returned by the getElementsByTagName method.
+    this.childNodes      = new NodeList(ownerDocument, this);
+
+    // The first child of this node. If there is no such node, this is null
+    this.firstChild      = null;
+    // The last child of this node. If there is no such node, this is null.
+    this.lastChild       = null;
+    // The node immediately preceding this node. If there is no such node,
+    // this is null.
+    this.previousSibling = null;
+    // The node immediately following this node. If there is no such node,
+    // this is null.
+    this.nextSibling     = null;
+
+    this.attributes = null;
+    // The namespaces in scope for this node
+    this._namespaces = new NamespaceNodeMap(ownerDocument, this);
+    this._readonly = false;
+
+    //IMPORTANT: These must come last so rhino will not iterate parent
+    //           properties before child properties.  (qunit.equiv issue)
+
+    // The parent of this node. All nodes, except Document, DocumentFragment,
+    // and Attr may have a parent.  However, if a node has just been created
+    // and not yet added to the tree, or if it has been removed from the tree,
+    // this is null
+    this.parentNode      = null;
+    // The Document object associated with this node
+    this.ownerDocument = ownerDocument;
+
+};
+
+// nodeType constants
+Node.ELEMENT_NODE                = 1;
+Node.ATTRIBUTE_NODE              = 2;
+Node.TEXT_NODE                   = 3;
+Node.CDATA_SECTION_NODE          = 4;
+Node.ENTITY_REFERENCE_NODE       = 5;
+Node.ENTITY_NODE                 = 6;
+Node.PROCESSING_INSTRUCTION_NODE = 7;
+Node.COMMENT_NODE                = 8;
+Node.DOCUMENT_NODE               = 9;
+Node.DOCUMENT_TYPE_NODE          = 10;
+Node.DOCUMENT_FRAGMENT_NODE      = 11;
+Node.NOTATION_NODE               = 12;
+Node.NAMESPACE_NODE              = 13;
+
+Node.DOCUMENT_POSITION_EQUAL        = 0x00;
+Node.DOCUMENT_POSITION_DISCONNECTED = 0x01;
+Node.DOCUMENT_POSITION_PRECEDING    = 0x02;
+Node.DOCUMENT_POSITION_FOLLOWING    = 0x04;
+Node.DOCUMENT_POSITION_CONTAINS     = 0x08;
+Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10;
+Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC      = 0x20;
+
+
+__extend__(Node.prototype, {
+    get localName(){
+        return this.prefix?
+            this.nodeName.substring(this.prefix.length+1, this.nodeName.length):
+            this.nodeName;
+    },
+    get prefix(){
+        return this.nodeName.split(':').length>1?
+            this.nodeName.split(':')[0]:
+            null;
+    },
+    set prefix(value){
+        if(value === null){
+            this.nodeName = this.localName;
+        }else{
+            this.nodeName = value+':'+this.localName;
+        }
+    },
+    hasAttributes : function() {
+        if (this.attributes.length == 0) {
+            return false;
+        }else{
+            return true;
+        }
+    },
+    get textContent(){
+        return __recursivelyGatherText__(this);
+    },
+    set textContent(newText){
+        while(this.firstChild != null){
+            this.removeChild( this.firstChild );
+        }
+        var text = this.ownerDocument.createTextNode(newText);
+        this.appendChild(text);
+    },
+    insertBefore : function(newChild, refChild) {
+        var prevNode;
+
+        if(newChild==null){
+            return newChild;
+        }
+        if(refChild==null){
+            this.appendChild(newChild);
+            return this.newChild;
+        }
+
+        // test for exceptions
+        if (__ownerDocument__(this).implementation.errorChecking) {
+            // throw Exception if Node is readonly
+            if (this._readonly) {
+                throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
+            }
+
+            // throw Exception if newChild was not created by this Document
+            if (__ownerDocument__(this) != __ownerDocument__(newChild)) {
+                throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR));
+            }
+
+            // throw Exception if the node is an ancestor
+            if (__isAncestor__(this, newChild)) {
+                throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
+            }
+        }
+
+        // if refChild is specified, insert before it
+        if (refChild) {
+            // find index of refChild
+            var itemIndex = __findItemIndex__(this.childNodes, refChild);
+            // throw Exception if there is no child node with this id
+            if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) {
+                throw(new DOMException(DOMException.NOT_FOUND_ERR));
+            }
+
+            // if the newChild is already in the tree,
+            var newChildParent = newChild.parentNode;
+            if (newChildParent) {
+                // remove it
+                newChildParent.removeChild(newChild);
+            }
+
+            // insert newChild into childNodes
+            __insertBefore__(this.childNodes, newChild, itemIndex);
+
+            // do node pointer surgery
+            prevNode = refChild.previousSibling;
+
+            // handle DocumentFragment
+            if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+                if (newChild.childNodes.length > 0) {
+                    // set the parentNode of DocumentFragment's children
+                    for (var ind = 0; ind < newChild.childNodes.length; ind++) {
+                        newChild.childNodes[ind].parentNode = this;
+                    }
+
+                    // link refChild to last child of DocumentFragment
+                    refChild.previousSibling = newChild.childNodes[newChild.childNodes.length-1];
+                }
+            }else {
+                // set the parentNode of the newChild
+                newChild.parentNode = this;
+                // link refChild to newChild
+                refChild.previousSibling = newChild;
+            }
+
+        }else {
+            // otherwise, append to end
+            prevNode = this.lastChild;
+            this.appendChild(newChild);
+        }
+
+        if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // do node pointer surgery for DocumentFragment
+            if (newChild.childNodes.length > 0) {
+                if (prevNode) {
+                    prevNode.nextSibling = newChild.childNodes[0];
+                }else {
+                    // this is the first child in the list
+                    this.firstChild = newChild.childNodes[0];
+                }
+                newChild.childNodes[0].previousSibling = prevNode;
+                newChild.childNodes[newChild.childNodes.length-1].nextSibling = refChild;
+            }
+        }else {
+            // do node pointer surgery for newChild
+            if (prevNode) {
+                prevNode.nextSibling = newChild;
+            }else {
+                // this is the first child in the list
+                this.firstChild = newChild;
+            }
+            newChild.previousSibling = prevNode;
+            newChild.nextSibling     = refChild;
+        }
+
+        return newChild;
+    },
+    replaceChild : function(newChild, oldChild) {
+        var ret = null;
+
+        if(newChild==null || oldChild==null){
+            return oldChild;
+        }
+
+        // test for exceptions
+        if (__ownerDocument__(this).implementation.errorChecking) {
+            // throw Exception if Node is readonly
+            if (this._readonly) {
+                throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
+            }
+
+            // throw Exception if newChild was not created by this Document
+            if (__ownerDocument__(this) != __ownerDocument__(newChild)) {
+                throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR));
+            }
+
+            // throw Exception if the node is an ancestor
+            if (__isAncestor__(this, newChild)) {
+                throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
+            }
+        }
+
+        // get index of oldChild
+        var index = __findItemIndex__(this.childNodes, oldChild);
+
+        // throw Exception if there is no child node with this id
+        if (__ownerDocument__(this).implementation.errorChecking && (index < 0)) {
+            throw(new DOMException(DOMException.NOT_FOUND_ERR));
+        }
+
+        // if the newChild is already in the tree,
+        var newChildParent = newChild.parentNode;
+        if (newChildParent) {
+            // remove it
+            newChildParent.removeChild(newChild);
+        }
+
+        // add newChild to childNodes
+        ret = __replaceChild__(this.childNodes,newChild, index);
+
+
+        if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // do node pointer surgery for Document Fragment
+            if (newChild.childNodes.length > 0) {
+                for (var ind = 0; ind < newChild.childNodes.length; ind++) {
+                    newChild.childNodes[ind].parentNode = this;
+                }
+
+                if (oldChild.previousSibling) {
+                    oldChild.previousSibling.nextSibling = newChild.childNodes[0];
+                } else {
+                    this.firstChild = newChild.childNodes[0];
+                }
+
+                if (oldChild.nextSibling) {
+                    oldChild.nextSibling.previousSibling = newChild;
+                } else {
+                    this.lastChild = newChild.childNodes[newChild.childNodes.length-1];
+                }
+
+                newChild.childNodes[0].previousSibling = oldChild.previousSibling;
+                newChild.childNodes[newChild.childNodes.length-1].nextSibling = oldChild.nextSibling;
+            }
+        } else {
+            // do node pointer surgery for newChild
+            newChild.parentNode = this;
+
+            if (oldChild.previousSibling) {
+                oldChild.previousSibling.nextSibling = newChild;
+            }else{
+                this.firstChild = newChild;
+            }
+            if (oldChild.nextSibling) {
+                oldChild.nextSibling.previousSibling = newChild;
+            }else{
+                this.lastChild = newChild;
+            }
+            newChild.previousSibling = oldChild.previousSibling;
+            newChild.nextSibling = oldChild.nextSibling;
+        }
+
+        return ret;
+    },
+    removeChild : function(oldChild) {
+        if(!oldChild){
+            return null;
+        }
+        // throw Exception if NamedNodeMap is readonly
+        if (__ownerDocument__(this).implementation.errorChecking &&
+            (this._readonly || oldChild._readonly)) {
+            throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
+        }
+
+        // get index of oldChild
+        var itemIndex = __findItemIndex__(this.childNodes, oldChild);
+
+        // throw Exception if there is no child node with this id
+        if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) {
+            throw(new DOMException(DOMException.NOT_FOUND_ERR));
+        }
+
+        // remove oldChild from childNodes
+        __removeChild__(this.childNodes, itemIndex);
+
+        // do node pointer surgery
+        oldChild.parentNode = null;
+
+        if (oldChild.previousSibling) {
+            oldChild.previousSibling.nextSibling = oldChild.nextSibling;
+        }else {
+            this.firstChild = oldChild.nextSibling;
+        }
+        if (oldChild.nextSibling) {
+            oldChild.nextSibling.previousSibling = oldChild.previousSibling;
+        }else {
+            this.lastChild = oldChild.previousSibling;
+        }
+
+        oldChild.previousSibling = null;
+        oldChild.nextSibling = null;
+
+        return oldChild;
+    },
+    appendChild : function(newChild) {
+        if(!newChild){
+            return null;
+        }
+        // test for exceptions
+        if (__ownerDocument__(this).implementation.errorChecking) {
+            // throw Exception if Node is readonly
+            if (this._readonly) {
+                throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
+            }
+
+            // throw Exception if arg was not created by this Document
+            if (__ownerDocument__(this) != __ownerDocument__(this)) {
+                throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR));
+            }
+
+            // throw Exception if the node is an ancestor
+            if (__isAncestor__(this, newChild)) {
+              throw(new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
+            }
+        }
+
+        // if the newChild is already in the tree,
+        var newChildParent = newChild.parentNode;
+        if (newChildParent) {
+            // remove it
+           //console.debug('removing node %s', newChild);
+            newChildParent.removeChild(newChild);
+        }
+
+        // add newChild to childNodes
+        __appendChild__(this.childNodes, newChild);
+
+        if (newChild.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // do node pointer surgery for DocumentFragment
+            if (newChild.childNodes.length > 0) {
+                for (var ind = 0; ind < newChild.childNodes.length; ind++) {
+                    newChild.childNodes[ind].parentNode = this;
+                }
+
+                if (this.lastChild) {
+                    this.lastChild.nextSibling = newChild.childNodes[0];
+                    newChild.childNodes[0].previousSibling = this.lastChild;
+                    this.lastChild = newChild.childNodes[newChild.childNodes.length-1];
+                } else {
+                    this.lastChild = newChild.childNodes[newChild.childNodes.length-1];
+                    this.firstChild = newChild.childNodes[0];
+                }
+            }
+        } else {
+            // do node pointer surgery for newChild
+            newChild.parentNode = this;
+            if (this.lastChild) {
+                this.lastChild.nextSibling = newChild;
+                newChild.previousSibling = this.lastChild;
+                this.lastChild = newChild;
+            } else {
+                this.lastChild = newChild;
+                this.firstChild = newChild;
+            }
+       }
+       return newChild;
+    },
+    hasChildNodes : function() {
+        return (this.childNodes.length > 0);
+    },
+    cloneNode: function(deep) {
+        // use importNode to clone this Node
+        //do not throw any exceptions
+        try {
+            return __ownerDocument__(this).importNode(this, deep);
+        } catch (e) {
+            //there shouldn't be any exceptions, but if there are, return null
+            // may want to warn: $debug("could not clone node: "+e.code);
+            return null;
+        }
+    },
+    normalize : function() {
+        var i;
+        var inode;
+        var nodesToRemove = new NodeList();
+
+        if (this.nodeType == Node.ELEMENT_NODE || this.nodeType == Node.DOCUMENT_NODE) {
+            var adjacentTextNode = null;
+
+            // loop through all childNodes
+            for(i = 0; i < this.childNodes.length; i++) {
+                inode = this.childNodes.item(i);
+
+                if (inode.nodeType == Node.TEXT_NODE) {
+                    // this node is a text node
+                    if (inode.length < 1) {
+                        // this text node is empty
+                        // add this node to the list of nodes to be remove
+                        __appendChild__(nodesToRemove, inode);
+                    }else {
+                        if (adjacentTextNode) {
+                            // previous node was also text
+                            adjacentTextNode.appendData(inode.data);
+                            // merge the data in adjacent text nodes
+                            // add this node to the list of nodes to be removed
+                            __appendChild__(nodesToRemove, inode);
+                        } else {
+                            // remember this node for next cycle
+                            adjacentTextNode = inode;
+                        }
+                    }
+                } else {
+                    // (soon to be) previous node is not a text node
+                    adjacentTextNode = null;
+                    // normalize non Text childNodes
+                    inode.normalize();
+                }
+            }
+
+            // remove redundant Text Nodes
+            for(i = 0; i < nodesToRemove.length; i++) {
+                inode = nodesToRemove.item(i);
+                inode.parentNode.removeChild(inode);
+            }
+        }
+    },
+    isSupported : function(feature, version) {
+        // use Implementation.hasFeature to determine if this feature is supported
+        return __ownerDocument__(this).implementation.hasFeature(feature, version);
+    },
+    getElementsByTagName : function(tagname) {
+        // delegate to _getElementsByTagNameRecursive
+        // recurse childNodes
+        var nodelist = new NodeList(__ownerDocument__(this));
+        for (var i = 0; i < this.childNodes.length; i++) {
+            __getElementsByTagNameRecursive__(this.childNodes.item(i),
+                                              tagname,
+                                              nodelist);
+        }
+        return nodelist;
+    },
+    getElementsByTagNameNS : function(namespaceURI, localName) {
+        // delegate to _getElementsByTagNameNSRecursive
+        return __getElementsByTagNameNSRecursive__(this, namespaceURI, localName,
+            new NodeList(__ownerDocument__(this)));
+    },
+    importNode : function(importedNode, deep) {
+        var i;
+        var importNode;
+
+        //there is no need to perform namespace checks since everything has already gone through them
+        //in order to have gotten into the DOM in the first place. The following line
+        //turns namespace checking off in ._isValidNamespace
+        __ownerDocument__(this).importing = true;
+
+        if (importedNode.nodeType == Node.ELEMENT_NODE) {
+            if (!__ownerDocument__(this).implementation.namespaceAware) {
+                // create a local Element (with the name of the importedNode)
+                importNode = __ownerDocument__(this).createElement(importedNode.tagName);
+
+                // create attributes matching those of the importedNode
+                for(i = 0; i < importedNode.attributes.length; i++) {
+                    importNode.setAttribute(importedNode.attributes.item(i).name, importedNode.attributes.item(i).value);
+                }
+            } else {
+                // create a local Element (with the name & namespaceURI of the importedNode)
+                importNode = __ownerDocument__(this).createElementNS(importedNode.namespaceURI, importedNode.nodeName);
+
+                // create attributes matching those of the importedNode
+                for(i = 0; i < importedNode.attributes.length; i++) {
+                    importNode.setAttributeNS(importedNode.attributes.item(i).namespaceURI,
+                        importedNode.attributes.item(i).name, importedNode.attributes.item(i).value);
+                }
+
+                // create namespace definitions matching those of the importedNode
+                for(i = 0; i < importedNode._namespaces.length; i++) {
+                    importNode._namespaces[i] = __ownerDocument__(this).createNamespace(importedNode._namespaces.item(i).localName);
+                    importNode._namespaces[i].value = importedNode._namespaces.item(i).value;
+                }
+            }
+        } else if (importedNode.nodeType == Node.ATTRIBUTE_NODE) {
+            if (!__ownerDocument__(this).implementation.namespaceAware) {
+                // create a local Attribute (with the name of the importedAttribute)
+                importNode = __ownerDocument__(this).createAttribute(importedNode.name);
+            } else {
+                // create a local Attribute (with the name & namespaceURI of the importedAttribute)
+                importNode = __ownerDocument__(this).createAttributeNS(importedNode.namespaceURI, importedNode.nodeName);
+
+                // create namespace definitions matching those of the importedAttribute
+                for(i = 0; i < importedNode._namespaces.length; i++) {
+                    importNode._namespaces[i] = __ownerDocument__(this).createNamespace(importedNode._namespaces.item(i).localName);
+                    importNode._namespaces[i].value = importedNode._namespaces.item(i).value;
+                }
+            }
+
+            // set the value of the local Attribute to match that of the importedAttribute
+            importNode.value = importedNode.value;
+
+        } else if (importedNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+            // create a local DocumentFragment
+            importNode = __ownerDocument__(this).createDocumentFragment();
+        } else if (importedNode.nodeType == Node.NAMESPACE_NODE) {
+            // create a local NamespaceNode (with the same name & value as the importedNode)
+            importNode = __ownerDocument__(this).createNamespace(importedNode.nodeName);
+            importNode.value = importedNode.value;
+        } else if (importedNode.nodeType == Node.TEXT_NODE) {
+            // create a local TextNode (with the

<TRUNCATED>