From 86995509a1d29497fb1c95f810337a567e1d5634 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Mon, 4 Apr 2011 12:24:38 -0400 Subject: Added functional test runner. --- .gitignore | 1 + install/ui/test/bin/functional.sh | 33 + install/ui/test/bin/selenium-results.js | 30 + install/ui/test/bin/selenium.sh | 17 +- install/ui/test/conf/logger.properties | 5 + install/ui/test/lib/env.rhino.1.2.js | 13989 ++++++++++++++++++++++++++++++ 6 files changed, 14068 insertions(+), 7 deletions(-) create mode 100755 install/ui/test/bin/functional.sh create mode 100755 install/ui/test/bin/selenium-results.js create mode 100644 install/ui/test/conf/logger.properties create mode 100644 install/ui/test/lib/env.rhino.1.2.js diff --git a/.gitignore b/.gitignore index b4c315bea..fb0d12edc 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ install/config.status install/install-sh install/missing install/stamp-h1 +install/ui/test/results ipa-client/COPYING ipa-client/ChangeLog ipa-client/INSTALL diff --git a/install/ui/test/bin/functional.sh b/install/ui/test/bin/functional.sh new file mode 100755 index 000000000..c8705dbf0 --- /dev/null +++ b/install/ui/test/bin/functional.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +BIN_DIR=`dirname $0` + +TEST_SUITES=functional +TEST_RESULTS=results +mkdir -p "$TEST_RESULTS" + +LIST=$1 +shift + +while [ -n "$1" ] +do + LIST="$LIST $1" + shift +done + +if [ -z "$LIST" ] +then + LIST="`ls $TEST_SUITES/*-suite.html | sed 's/^.*\/\(.*\)-suite.html$/\1/'`" +fi + +for TEST_NAME in $LIST +do + echo =================================================== + echo Test Suite: $TEST_NAME + echo =================================================== + + "$BIN_DIR/selenium.sh" "$TEST_SUITES/$TEST_NAME-suite.html"\ + "$TEST_RESULTS/$TEST_NAME-results.html" + + rhino -opt -1 "$BIN_DIR/selenium-results.js" "$TEST_RESULTS/$TEST_NAME-results.html" +done diff --git a/install/ui/test/bin/selenium-results.js b/install/ui/test/bin/selenium-results.js new file mode 100755 index 000000000..3fa68a69f --- /dev/null +++ b/install/ui/test/bin/selenium-results.js @@ -0,0 +1,30 @@ +if (!arguments.length) { + print('Usage: selenium-results.js '); + quit(); +} + +load('lib/env.rhino.1.2.js'); +load('../jquery.js'); + +window.location = arguments[0]; + +var labels = { + result: 'Result', + numTestTotal: 'Total', + numTestPasses: 'Passed', + numCommandFailures: 'Failed' +}; + +$('table:first tr:lt(10)').each(function() { + var tr = $(this); + + var td = tr.children().first(); + + var name = td.text().replace(/:$/, ''); + var label = labels[name]; + if (!label) return; + + td = td.next(); + var value = td.text(); + print(label+': '+value); +}); diff --git a/install/ui/test/bin/selenium.sh b/install/ui/test/bin/selenium.sh index 034b1df6f..bd37238e6 100755 --- a/install/ui/test/bin/selenium.sh +++ b/install/ui/test/bin/selenium.sh @@ -1,6 +1,5 @@ #!/bin/sh - TEST_SUITE=$1 TEST_RESULTS=$2 @@ -10,17 +9,21 @@ then exit 1 fi -if [ ! -f $TEST_SUITE ] +if [ ! -f "$TEST_SUITE" ] then echo "Error: Test suite $TEST_SUITE not found." exit 1 fi -PROFILE_DIR=$HOME/.mozilla/firefox -PROFILE=`ls $PROFILE_DIR | grep .default` -PROFILE_TEMPLATE=$PROFILE_DIR/$PROFILE +PROFILE_DIR="$HOME/.mozilla/firefox" +PROFILE=`ls "$PROFILE_DIR" | grep .default` +PROFILE_TEMPLATE="$PROFILE_DIR/$PROFILE" -java -jar /usr/share/java/selenium-server-standalone.jar -firefoxProfileTemplate $PROFILE_TEMPLATE -htmlSuite "*firefox" "http://localhost" $TEST_SUITE $TEST_RESULTS +# Run Selenium Test +java -Djava.util.logging.config.file=conf/logger.properties\ + -jar /usr/share/java/selenium-server-standalone.jar\ + -firefoxProfileTemplate "$PROFILE_TEMPLATE"\ + -htmlSuite "*firefox" "http://localhost" "$TEST_SUITE" "$TEST_RESULTS" # Kill Firefox -ps -ef|grep -i firefox|grep '\-profile'|awk '{print $2;}'|xargs kill -9 +ps -ef|grep -i firefox|grep '\-profile'|awk '{print $2;}'|xargs kill diff --git a/install/ui/test/conf/logger.properties b/install/ui/test/conf/logger.properties new file mode 100644 index 000000000..955429cc3 --- /dev/null +++ b/install/ui/test/conf/logger.properties @@ -0,0 +1,5 @@ +handlers = java.util.logging.ConsoleHandler + +.level = OFF + +java.util.logging.ConsoleHandler.level = OFF diff --git a/install/ui/test/lib/env.rhino.1.2.js b/install/ui/test/lib/env.rhino.1.2.js new file mode 100644 index 000000000..4f7645fa3 --- /dev/null +++ b/install/ui/test/lib/env.rhino.1.2.js @@ -0,0 +1,13989 @@ +/* + * Envjs core-env.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig 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 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 + * - Via an innerHTML parse of a + * - 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 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 + * - Via an innerHTML parse of a + * - A modificiation of the 'href' attribute on a 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 -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 -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 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 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 = "

"+ e+ "

"; + } + } 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 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 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 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= 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 same data as the importedNode) + importNode = __ownerDocument__(this).createTextNode(importedNode.data); + } else if (importedNode.nodeType == Node.CDATA_SECTION_NODE) { + // create a local CDATANode (with the same data as the importedNode) + importNode = __ownerDocument__(this).createCDATASection(importedNode.data); + } else if (importedNode.nodeType == Node.PROCESSING_INSTRUCTION_NODE) { + // create a local ProcessingInstruction (with the same target & data as the importedNode) + importNode = __ownerDocument__(this).createProcessingInstruction(importedNode.target, importedNode.data); + } else if (importedNode.nodeType == Node.COMMENT_NODE) { + // create a local Comment (with the same data as the importedNode) + importNode = __ownerDocument__(this).createComment(importedNode.data); + } else { // throw Exception if nodeType is not supported + throw(new DOMException(DOMException.NOT_SUPPORTED_ERR)); + } + + if (deep) { + // recurse childNodes + for(i = 0; i < importedNode.childNodes.length; i++) { + importNode.appendChild(__ownerDocument__(this).importNode(importedNode.childNodes.item(i), true)); + } + } + + //reset importing + __ownerDocument__(this).importing = false; + return importNode; + + }, + contains : function(node){ + while(node && node != this ){ + node = node.parentNode; + } + return !!node; + }, + compareDocumentPosition : function(b){ + //console.log("comparing document position %s %s", this, b); + var i, + length, + a = this, + parent, + aparents, + bparents; + //handle a couple simpler case first + if(a === b) { + return Node.DOCUMENT_POSITION_EQUAL; + } + if(a.ownerDocument !== b.ownerDocument) { + return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC| + Node.DOCUMENT_POSITION_FOLLOWING| + Node.DOCUMENT_POSITION_DISCONNECTED; + } + if(a.parentNode === b.parentNode){ + length = a.parentNode.childNodes.length; + for(i=0;i aparents.length){ + return Node.DOCUMENT_POSITION_FOLLOWING; + }else if(bparents.length < aparents.length){ + return Node.DOCUMENT_POSITION_PRECEDING; + }else{ + //common ancestor diverge point + if (i === 0) { + return Node.DOCUMENT_POSITION_FOLLOWING; + } else { + parent = aparents[i-1]; + } + return parent.compareDocumentPosition(bparents.pop()); + } + } + } + + return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC| + Node.DOCUMENT_POSITION_DISCONNECTED; + + }, + toString : function() { + return '[object Node]'; + } + +}); + + + +/** + * @method __getElementsByTagNameRecursive__ - implements getElementsByTagName() + * @param elem : Element - The element which are checking and then recursing into + * @param tagname : string - The name of the tag to match on. The special value "*" matches all tags + * @param nodeList : NodeList - The accumulating list of matching nodes + * + * @return : NodeList + */ +var __getElementsByTagNameRecursive__ = function (elem, tagname, nodeList) { + + if (elem.nodeType == Node.ELEMENT_NODE || elem.nodeType == Node.DOCUMENT_NODE) { + + if(elem.nodeType !== Node.DOCUMENT_NODE && + ((elem.nodeName.toUpperCase() == tagname.toUpperCase()) || + (tagname == "*")) ){ + // add matching node to nodeList + __appendChild__(nodeList, elem); + } + + // recurse childNodes + for(var i = 0; i < elem.childNodes.length; i++) { + nodeList = __getElementsByTagNameRecursive__(elem.childNodes.item(i), tagname, nodeList); + } + } + + return nodeList; +}; + +/** + * @method __getElementsByTagNameNSRecursive__ + * implements getElementsByTagName() + * + * @param elem : Element - The element which are checking and then recursing into + * @param namespaceURI : string - the namespace URI of the required node + * @param localName : string - the local name of the required node + * @param nodeList : NodeList - The accumulating list of matching nodes + * + * @return : NodeList + */ +var __getElementsByTagNameNSRecursive__ = function(elem, namespaceURI, localName, nodeList) { + if (elem.nodeType == Node.ELEMENT_NODE || elem.nodeType == Node.DOCUMENT_NODE) { + + if (((elem.namespaceURI == namespaceURI) || (namespaceURI == "*")) && + ((elem.localName == localName) || (localName == "*"))) { + // add matching node to nodeList + __appendChild__(nodeList, elem); + } + + // recurse childNodes + for(var i = 0; i < elem.childNodes.length; i++) { + nodeList = __getElementsByTagNameNSRecursive__( + elem.childNodes.item(i), namespaceURI, localName, nodeList); + } + } + + return nodeList; +}; + +/** + * @method __isAncestor__ - returns true if node is ancestor of target + * @param target : Node - The node we are using as context + * @param node : Node - The candidate ancestor node + * @return : boolean + */ +var __isAncestor__ = function(target, node) { + // if this node matches, return true, + // otherwise recurse up (if there is a parentNode) + return ((target == node) || ((target.parentNode) && (__isAncestor__(target.parentNode, node)))); +}; + + + +var __recursivelyGatherText__ = function(aNode) { + var accumulateText = "", + idx, + node; + for (idx=0;idx < aNode.childNodes.length;idx++){ + node = aNode.childNodes.item(idx); + if(node.nodeType == Node.TEXT_NODE) + accumulateText += node.data; + else + accumulateText += __recursivelyGatherText__(node); + } + return accumulateText; +}; + +/** + * function __escapeXML__ + * @param str : string - The string to be escaped + * @return : string - The escaped string + */ +var escAmpRegEx = /&(?!(amp;|lt;|gt;|quot|apos;))/g; +var escLtRegEx = //g; +var quotRegEx = /"/g; +var aposRegEx = /'/g; + +function __escapeXML__(str) { + str = str.replace(escAmpRegEx, "&"). + replace(escLtRegEx, "<"). + replace(escGtRegEx, ">"). + replace(quotRegEx, """). + replace(aposRegEx, "'"); + + return str; +}; + +/* +function __escapeHTML5__(str) { + str = str.replace(escAmpRegEx, "&"). + replace(escLtRegEx, "<"). + replace(escGtRegEx, ">"); + + return str; +}; +function __escapeHTML5Atribute__(str) { + str = str.replace(escAmpRegEx, "&"). + replace(escLtRegEx, "<"). + replace(escGtRegEx, ">"). + replace(quotRegEx, """). + replace(aposRegEx, "'"); + + return str; +}; +*/ + +/** + * function __unescapeXML__ + * @param str : string - The string to be unescaped + * @return : string - The unescaped string + */ +var unescAmpRegEx = /&/g; +var unescLtRegEx = /</g; +var unescGtRegEx = />/g; +var unquotRegEx = /"/g; +var unaposRegEx = /'/g; +function __unescapeXML__(str) { + str = str.replace(unescAmpRegEx, "&"). + replace(unescLtRegEx, "<"). + replace(unescGtRegEx, ">"). + replace(unquotRegEx, "\""). + replace(unaposRegEx, "'"); + + return str; +}; + +/** + * @class NamedNodeMap - + * used to represent collections of nodes that can be accessed by name + * typically a set of Element attributes + * + * @extends NodeList - + * note W3C spec says that this is not the case, but we need an item() + * method identical to NodeList's, so why not? + * @param ownerDocument : Document - the ownerDocument + * @param parentNode : Node - the node that the NamedNodeMap is attached to (or null) + */ +NamedNodeMap = function(ownerDocument, parentNode) { + NodeList.apply(this, arguments); + __setArray__(this, []); +}; +NamedNodeMap.prototype = new NodeList(); +__extend__(NamedNodeMap.prototype, { + add: function(name){ + this[this.length] = name; + }, + getNamedItem : function(name) { + var ret = null; + //console.log('NamedNodeMap getNamedItem %s', name); + // test that Named Node exists + var itemIndex = __findNamedItemIndex__(this, name); + + if (itemIndex > -1) { + // found it! + ret = this[itemIndex]; + } + // if node is not found, default value null is returned + return ret; + }, + setNamedItem : function(arg) { + //console.log('setNamedItem %s', arg); + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if arg was not created by this Document + if (this.ownerDocument != arg.ownerDocument) { + throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if DOMNamedNodeMap is readonly + if (this._readonly || (this.parentNode && this.parentNode._readonly)) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if arg is already an attribute of another Element object + if (arg.ownerElement && (arg.ownerElement != this.parentNode)) { + throw(new DOMException(DOMException.INUSE_ATTRIBUTE_ERR)); + } + } + + //console.log('setNamedItem __findNamedItemIndex__ '); + // get item index + var itemIndex = __findNamedItemIndex__(this, arg.name); + var ret = null; + + //console.log('setNamedItem __findNamedItemIndex__ %s', itemIndex); + if (itemIndex > -1) { // found it! + ret = this[itemIndex]; // use existing Attribute + + // throw Exception if DOMAttr is readonly + if (__ownerDocument__(this).implementation.errorChecking && ret._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } else { + this[itemIndex] = arg; // over-write existing NamedNode + this[arg.name.toLowerCase()] = arg; + } + } else { + // add new NamedNode + //console.log('setNamedItem add new named node map (by index)'); + Array.prototype.push.apply(this, [arg]); + //console.log('setNamedItem add new named node map (by name) %s %s', arg, arg.name); + this[arg.name] = arg; + //console.log('finsished setNamedItem add new named node map (by name) %s', arg.name); + + } + + //console.log('setNamedItem parentNode'); + arg.ownerElement = this.parentNode; // update ownerElement + // return old node or new node + //console.log('setNamedItem exit'); + return ret; + }, + removeNamedItem : function(name) { + var ret = null; + // test for exceptions + // throw Exception if NamedNodeMap is readonly + if (__ownerDocument__(this).implementation.errorChecking && + (this._readonly || (this.parentNode && this.parentNode._readonly))) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get item index + var itemIndex = __findNamedItemIndex__(this, name); + + // throw Exception if there is no node named name in this map + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // get Node + var oldNode = this[itemIndex]; + //this[oldNode.name] = undefined; + + // throw Exception if Node is readonly + if (__ownerDocument__(this).implementation.errorChecking && oldNode._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // return removed node + return __removeChild__(this, itemIndex); + }, + getNamedItemNS : function(namespaceURI, localName) { + var ret = null; + + // test that Named Node exists + var itemIndex = __findNamedItemNSIndex__(this, namespaceURI, localName); + + if (itemIndex > -1) { + // found it! return NamedNode + ret = this[itemIndex]; + } + // if node is not found, default value null is returned + return ret; + }, + setNamedItemNS : function(arg) { + //console.log('setNamedItemNS %s', arg); + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if NamedNodeMap is readonly + if (this._readonly || (this.parentNode && this.parentNode._readonly)) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if arg was not created by this Document + if (__ownerDocument__(this) != __ownerDocument__(arg)) { + throw(new DOMException(DOMException.WRONG_DOCUMENT_ERR)); + } + + // throw Exception if arg is already an attribute of another Element object + if (arg.ownerElement && (arg.ownerElement != this.parentNode)) { + throw(new DOMException(DOMException.INUSE_ATTRIBUTE_ERR)); + } + } + + // get item index + var itemIndex = __findNamedItemNSIndex__(this, arg.namespaceURI, arg.localName); + var ret = null; + + if (itemIndex > -1) { + // found it! + // use existing Attribute + ret = this[itemIndex]; + // throw Exception if Attr is readonly + if (__ownerDocument__(this).implementation.errorChecking && ret._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } else { + // over-write existing NamedNode + this[itemIndex] = arg; + } + }else { + // add new NamedNode + Array.prototype.push.apply(this, [arg]); + } + arg.ownerElement = this.parentNode; + + // return old node or null + return ret; + //console.log('finished setNamedItemNS %s', arg); + }, + removeNamedItemNS : function(namespaceURI, localName) { + var ret = null; + + // test for exceptions + // throw Exception if NamedNodeMap is readonly + if (__ownerDocument__(this).implementation.errorChecking && (this._readonly || (this.parentNode && this.parentNode._readonly))) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get item index + var itemIndex = __findNamedItemNSIndex__(this, namespaceURI, localName); + + // throw Exception if there is no matching node in this map + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + // get Node + var oldNode = this[itemIndex]; + + // throw Exception if Node is readonly + if (__ownerDocument__(this).implementation.errorChecking && oldNode._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + return __removeChild__(this, itemIndex); // return removed node + }, + get xml() { + var ret = ""; + + // create string containing concatenation of all (but last) Attribute string values (separated by spaces) + for (var i=0; i < this.length -1; i++) { + ret += this[i].xml +" "; + } + + // add last Attribute to string (without trailing space) + if (this.length > 0) { + ret += this[this.length -1].xml; + } + + return ret; + }, + toString : function(){ + return "[object NamedNodeMap]"; + } + +}); + +/** + * @method __findNamedItemIndex__ + * find the item index of the node with the specified name + * + * @param name : string - the name of the required node + * @param isnsmap : if its a NamespaceNodeMap + * @return : int + */ +var __findNamedItemIndex__ = function(namednodemap, name, isnsmap) { + var ret = -1; + // loop through all nodes + for (var i=0; i -1) { + // found it! + ret = true; + } + // if node is not found, default value false is returned + return ret; +} + +/** + * @method __hasAttributeNS__ + * Returns true if specified node exists + * + * @param namespaceURI : string - the namespace URI of the required node + * @param localName : string - the local name of the required node + * @return : boolean + */ +var __hasAttributeNS__ = function(namednodemap, namespaceURI, localName) { + var ret = false; + // test that Named Node exists + var itemIndex = __findNamedItemNSIndex__(namednodemap, namespaceURI, localName); + if (itemIndex > -1) { + // found it! + ret = true; + } + // if node is not found, default value false is returned + return ret; +} + +/** + * @method __cloneNamedNodes__ + * Returns a NamedNodeMap containing clones of the Nodes in this NamedNodeMap + * + * @param parentNode : Node - the new parent of the cloned NodeList + * @param isnsmap : bool - is this a NamespaceNodeMap + * @return NamedNodeMap containing clones of the Nodes in this NamedNodeMap + */ +var __cloneNamedNodes__ = function(namednodemap, parentNode, isnsmap) { + var cloneNamedNodeMap = isnsmap? + new NamespaceNodeMap(namednodemap.ownerDocument, parentNode): + new NamedNodeMap(namednodemap.ownerDocument, parentNode); + + // create list containing clones of all children + for (var i=0; i < namednodemap.length; i++) { + __appendChild__(cloneNamedNodeMap, namednodemap[i].cloneNode(false)); + } + + return cloneNamedNodeMap; +}; + + +/** + * @class NamespaceNodeMap - + * used to represent collections of namespace nodes that can be + * accessed by name typically a set of Element attributes + * + * @extends NamedNodeMap + * + * @param ownerDocument : Document - the ownerDocument + * @param parentNode : Node - the node that the NamespaceNodeMap is attached to (or null) + */ +var NamespaceNodeMap = function(ownerDocument, parentNode) { + this.NamedNodeMap = NamedNodeMap; + this.NamedNodeMap(ownerDocument, parentNode); + __setArray__(this, []); +}; +NamespaceNodeMap.prototype = new NamedNodeMap(); +__extend__(NamespaceNodeMap.prototype, { + get xml() { + var ret = "", + ns, + ind; + // identify namespaces declared local to this Element (ie, not inherited) + for (ind = 0; ind < this.length; ind++) { + // if namespace declaration does not exist in the containing node's, parentNode's namespaces + ns = null; + try { + var ns = this.parentNode.parentNode._namespaces. + getNamedItem(this[ind].localName); + }catch (e) { + //breaking to prevent default namespace being inserted into return value + break; + } + if (!(ns && (""+ ns.nodeValue == ""+ this[ind].nodeValue))) { + // display the namespace declaration + ret += this[ind].xml +" "; + } + } + return ret; + } +}); + +/** + * @class Namespace - + * The Namespace interface represents an namespace in an Element object + * + * @param ownerDocument : The Document object associated with this node. + */ +Namespace = function(ownerDocument) { + Node.apply(this, arguments); + // the name of this attribute + this.name = ""; + + // If this attribute was explicitly given a value in the original document, + // this is true; otherwise, it is false. + // Note that the implementation is in charge of this attribute, not the user. + // If the user changes the value of the attribute (even if it ends up having + // the same value as the default value) then the specified flag is + // automatically flipped to true + this.specified = false; +}; +Namespace.prototype = new Node(); +__extend__(Namespace.prototype, { + get value(){ + // the value of the attribute is returned as a string + return this.nodeValue; + }, + set value(value){ + this.nodeValue = value+''; + }, + get nodeType(){ + return Node.NAMESPACE_NODE; + }, + get xml(){ + var ret = ""; + + // serialize Namespace Declaration + if (this.nodeName != "") { + ret += this.nodeName +"=\""+ __escapeXML__(this.nodeValue) +"\""; + } + else { // handle default namespace + ret += "xmlns=\""+ __escapeXML__(this.nodeValue) +"\""; + } + + return ret; + }, + toString: function(){ + return '[object Namespace]'; + } +}); + + +/** + * @class CharacterData - parent abstract class for Text and Comment + * @extends Node + * @param ownerDocument : The Document object associated with this node. + */ +CharacterData = function(ownerDocument) { + Node.apply(this, arguments); +}; +CharacterData.prototype = new Node(); +__extend__(CharacterData.prototype,{ + get data(){ + return this.nodeValue; + }, + set data(data){ + this.nodeValue = data; + }, + get textContent(){ + return this.nodeValue; + }, + set textContent(newText){ + this.nodeValue = newText; + }, + get length(){return this.nodeValue.length;}, + appendData: function(arg){ + // throw Exception if CharacterData is readonly + if (__ownerDocument__(this).implementation.errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + // append data + this.data = "" + this.data + arg; + }, + deleteData: function(offset, count){ + // throw Exception if CharacterData is readonly + if (__ownerDocument__(this).implementation.errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + if (this.data) { + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // delete data + if(!count || (offset + count) > this.data.length) { + this.data = this.data.substring(0, offset); + }else { + this.data = this.data.substring(0, offset). + concat(this.data.substring(offset + count)); + } + } + }, + insertData: function(offset, arg){ + // throw Exception if CharacterData is readonly + if(__ownerDocument__(this).implementation.errorChecking && this._readonly){ + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + if(this.data){ + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // insert data + this.data = this.data.substring(0, offset).concat(arg, this.data.substring(offset)); + }else { + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && (offset !== 0)) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // set data + this.data = arg; + } + }, + replaceData: function(offset, count, arg){ + // throw Exception if CharacterData is readonly + if (__ownerDocument__(this).implementation.errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + if (this.data) { + // throw Exception if offset is negative or greater than the data length, + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + + // replace data + this.data = this.data.substring(0, offset). + concat(arg, this.data.substring(offset + count)); + }else { + // set data + this.data = arg; + } + }, + substringData: function(offset, count){ + var ret = null; + if (this.data) { + // throw Exception if offset is negative or greater than the data length, + // or the count is negative + if (__ownerDocument__(this).implementation.errorChecking && + ((offset < 0) || (offset > this.data.length) || (count < 0))) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + // if count is not specified + if (!count) { + ret = this.data.substring(offset); // default to 'end of string' + }else{ + ret = this.data.substring(offset, offset + count); + } + } + return ret; + }, + toString : function(){ + return "[object CharacterData]"; + } +}); + +/** + * @class Text + * The Text interface represents the textual content (termed + * character data in XML) of an Element or Attr. + * If there is no markup inside an element's content, the text is + * contained in a single object implementing the Text interface that + * is the only child of the element. If there is markup, it is + * parsed into a list of elements and Text nodes that form the + * list of children of the element. + * @extends CharacterData + * @param ownerDocument The Document object associated with this node. + */ +Text = function(ownerDocument) { + CharacterData.apply(this, arguments); + this.nodeName = "#text"; +}; +Text.prototype = new CharacterData(); +__extend__(Text.prototype,{ + get localName(){ + return null; + }, + // Breaks this Text node into two Text nodes at the specified offset, + // keeping both in the tree as siblings. This node then only contains + // all the content up to the offset point. And a new Text node, which + // is inserted as the next sibling of this node, contains all the + // content at and after the offset point. + splitText : function(offset) { + var data, + inode; + // 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 offset is negative or greater than the data length, + if ((offset < 0) || (offset > this.data.length)) { + throw(new DOMException(DOMException.INDEX_SIZE_ERR)); + } + } + if (this.parentNode) { + // get remaining string (after offset) + data = this.substringData(offset); + // create new TextNode with remaining string + inode = __ownerDocument__(this).createTextNode(data); + // attach new TextNode + if (this.nextSibling) { + this.parentNode.insertBefore(inode, this.nextSibling); + } else { + this.parentNode.appendChild(inode); + } + // remove remaining string from original TextNode + this.deleteData(offset); + } + return inode; + }, + get nodeType(){ + return Node.TEXT_NODE; + }, + get xml(){ + return __escapeXML__(""+ this.nodeValue); + }, + toString: function(){ + return "[object Text]"; + } +}); + +/** + * @class CDATASection + * CDATA sections are used to escape blocks of text containing + * characters that would otherwise be regarded as markup. + * The only delimiter that is recognized in a CDATA section is + * the "\]\]\>" string that ends the CDATA section + * @extends Text + * @param ownerDocument : The Document object associated with this node. + */ +CDATASection = function(ownerDocument) { + Text.apply(this, arguments); + this.nodeName = '#cdata-section'; +}; +CDATASection.prototype = new Text(); +__extend__(CDATASection.prototype,{ + get nodeType(){ + return Node.CDATA_SECTION_NODE; + }, + get xml(){ + return ""; + }, + toString : function(){ + return "[object CDATASection]"; + } +}); +/** + * @class Comment + * This represents the content of a comment, i.e., all the + * characters between the starting '' + * @extends CharacterData + * @param ownerDocument : The Document object associated with this node. + */ +Comment = function(ownerDocument) { + CharacterData.apply(this, arguments); + this.nodeName = "#comment"; +}; +Comment.prototype = new CharacterData(); +__extend__(Comment.prototype, { + get localName(){ + return null; + }, + get nodeType(){ + return Node.COMMENT_NODE; + }, + get xml(){ + return ""; + }, + toString : function(){ + return "[object Comment]"; + } +}); + + +/** + * @author envjs team + * @param {Document} onwnerDocument + */ +DocumentType = function(ownerDocument) { + Node.apply(this, arguments); + this.systemId = null; + this.publicId = null; +}; +DocumentType.prototype = new Node(); +__extend__({ + get name(){ + return this.nodeName; + }, + get entities(){ + return null; + }, + get internalSubsets(){ + return null; + }, + get notations(){ + return null; + }, + toString : function(){ + return "[object DocumentType]"; + } +}); + +/** + * @class Attr + * The Attr interface represents an attribute in an Element object + * @extends Node + * @param ownerDocument : The Document object associated with this node. + */ +Attr = function(ownerDocument) { + Node.apply(this, arguments); + // set when Attr is added to NamedNodeMap + this.ownerElement = null; + //TODO: our implementation of Attr is incorrect because we don't + // treat the value of the attribute as a child text node. +}; +Attr.prototype = new Node(); +__extend__(Attr.prototype, { + // the name of this attribute + get name(){ + return this.nodeName; + }, + // the value of the attribute is returned as a string + get value(){ + return this.nodeValue||''; + }, + set value(value){ + // throw Exception if Attribute is readonly + if (__ownerDocument__(this).implementation.errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + // delegate to node + this.nodeValue = value; + }, + get textContent(){ + return this.nodeValue; + }, + set textContent(newText){ + this.nodeValue = newText; + }, + get specified(){ + return (this !== null && this !== undefined); + }, + get nodeType(){ + return Node.ATTRIBUTE_NODE; + }, + get xml() { + if (this.nodeValue) { + return __escapeXML__(this.nodeValue+""); + } else { + return ''; + } + }, + toString : function() { + return '[object Attr]'; + } +}); + + +/** + * @class Element - + * By far the vast majority of objects (apart from text) + * that authors encounter when traversing a document are + * Element nodes. + * @extends Node + * @param ownerDocument : The Document object associated with this node. + */ +Element = function(ownerDocument) { + Node.apply(this, arguments); + this.attributes = new NamedNodeMap(this.ownerDocument, this); +}; +Element.prototype = new Node(); +__extend__(Element.prototype, { + // The name of the element. + get tagName(){ + return this.nodeName; + }, + + getAttribute: function(name) { + var ret = null; + // if attribute exists, use it + var attr = this.attributes.getNamedItem(name); + if (attr) { + ret = attr.value; + } + // if Attribute exists, return its value, otherwise, return null + return ret; + }, + setAttribute : function (name, value) { + // if attribute exists, use it + var attr = this.attributes.getNamedItem(name); + //console.log('attr %s', attr); + //I had to add this check because as the script initializes + //the id may be set in the constructor, and the html element + //overrides the id property with a getter/setter. + if(__ownerDocument__(this)){ + if (attr===null||attr===undefined) { + // otherwise create it + attr = __ownerDocument__(this).createAttribute(name); + //console.log('attr %s', attr); + } + + + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Attribute is readonly + if (attr._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if the value string contains an illegal character + if (!__isValidString__(value+'')) { + throw(new DOMException(DOMException.INVALID_CHARACTER_ERR)); + } + } + + // assign values to properties (and aliases) + attr.value = value + ''; + + // add/replace Attribute in NamedNodeMap + this.attributes.setNamedItem(attr); + //console.log('element setNamedItem %s', attr); + }else{ + console.warn('Element has no owner document '+this.tagName+ + '\n\t cant set attribute ' + name + ' = '+value ); + } + }, + removeAttribute : function removeAttribute(name) { + // delegate to NamedNodeMap.removeNamedItem + return this.attributes.removeNamedItem(name); + }, + getAttributeNode : function getAttributeNode(name) { + // delegate to NamedNodeMap.getNamedItem + return this.attributes.getNamedItem(name); + }, + setAttributeNode: function(newAttr) { + // if this Attribute is an ID + if (__isIdDeclaration__(newAttr.name)) { + this.id = newAttr.value; // cache ID for getElementById() + } + // delegate to NamedNodeMap.setNamedItem + return this.attributes.setNamedItem(newAttr); + }, + removeAttributeNode: function(oldAttr) { + // throw Exception if Attribute is readonly + if (__ownerDocument__(this).implementation.errorChecking && oldAttr._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // get item index + var itemIndex = this.attributes._findItemIndex(oldAttr._id); + + // throw Exception if node does not exist in this map + if (__ownerDocument__(this).implementation.errorChecking && (itemIndex < 0)) { + throw(new DOMException(DOMException.NOT_FOUND_ERR)); + } + + return this.attributes._removeChild(itemIndex); + }, + getAttributeNS : function(namespaceURI, localName) { + var ret = ""; + // delegate to NAmedNodeMap.getNamedItemNS + var attr = this.attributes.getNamedItemNS(namespaceURI, localName); + if (attr) { + ret = attr.value; + } + return ret; // if Attribute exists, return its value, otherwise return "" + }, + setAttributeNS : function(namespaceURI, qualifiedName, value) { + // call NamedNodeMap.getNamedItem + //console.log('setAttributeNS %s %s %s', namespaceURI, qualifiedName, value); + var attr = this.attributes.getNamedItem(namespaceURI, qualifiedName); + + if (!attr) { // if Attribute exists, use it + // otherwise create it + attr = __ownerDocument__(this).createAttributeNS(namespaceURI, qualifiedName); + } + + value = '' + value; + + // test for exceptions + if (__ownerDocument__(this).implementation.errorChecking) { + // throw Exception if Attribute is readonly + if (attr._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + + // throw Exception if the Namespace is invalid + if (!__isValidNamespace__(this.ownerDocument, namespaceURI, qualifiedName, true)) { + throw(new DOMException(DOMException.NAMESPACE_ERR)); + } + + // throw Exception if the value string contains an illegal character + if (!__isValidString__(value)) { + throw(new DOMException(DOMException.INVALID_CHARACTER_ERR)); + } + } + + // if this Attribute is an ID + //if (__isIdDeclaration__(name)) { + // this.id = value; + //} + + // assign values to properties (and aliases) + attr.value = value; + attr.nodeValue = value; + + // delegate to NamedNodeMap.setNamedItem + this.attributes.setNamedItemNS(attr); + }, + removeAttributeNS : function(namespaceURI, localName) { + // delegate to NamedNodeMap.removeNamedItemNS + return this.attributes.removeNamedItemNS(namespaceURI, localName); + }, + getAttributeNodeNS : function(namespaceURI, localName) { + // delegate to NamedNodeMap.getNamedItemNS + return this.attributes.getNamedItemNS(namespaceURI, localName); + }, + setAttributeNodeNS : function(newAttr) { + // if this Attribute is an ID + if ((newAttr.prefix == "") && __isIdDeclaration__(newAttr.name)) { + this.id = newAttr.value+''; // cache ID for getElementById() + } + + // delegate to NamedNodeMap.setNamedItemNS + return this.attributes.setNamedItemNS(newAttr); + }, + hasAttribute : function(name) { + // delegate to NamedNodeMap._hasAttribute + return __hasAttribute__(this.attributes,name); + }, + hasAttributeNS : function(namespaceURI, localName) { + // delegate to NamedNodeMap._hasAttributeNS + return __hasAttributeNS__(this.attributes, namespaceURI, localName); + }, + get nodeType(){ + return Node.ELEMENT_NODE; + }, + get xml() { + var ret = "", + ns = "", + attrs, + attrstring, + i; + + // serialize namespace declarations + if (this.namespaceURI ){ + if((this === this.ownerDocument.documentElement) || + (!this.parentNode)|| + (this.parentNode && (this.parentNode.namespaceURI !== this.namespaceURI))) { + ns = ' xmlns' + (this.prefix?(':'+this.prefix):'') + + '="' + this.namespaceURI + '"'; + } + } + + // serialize Attribute declarations + attrs = this.attributes; + attrstring = ""; + for(i=0;i< attrs.length;i++){ + if(attrs[i].name.match('xmlns:')) { + attrstring += " "+attrs[i].name+'="'+attrs[i].xml+'"'; + } + } + for(i=0;i< attrs.length;i++){ + if(!attrs[i].name.match('xmlns:')) { + attrstring += " "+attrs[i].name+'="'+attrs[i].xml+'"'; + } + } + + if(this.hasChildNodes()){ + // serialize this Element + ret += "<" + this.tagName + ns + attrstring +">"; + ret += this.childNodes.xml; + ret += ""; + }else{ + ret += "<" + this.tagName + ns + attrstring +"/>"; + } + + return ret; + }, + toString : function(){ + return '[object Element]'; + } +}); +/** + * @class DOMException - raised when an operation is impossible to perform + * @author Jon van Noort (jon@webarcana.com.au) + * @param code : int - the exception code (one of the DOMException constants) + */ +DOMException = function(code) { + this.code = code; +}; + +// DOMException constants +// Introduced in DOM Level 1: +DOMException.INDEX_SIZE_ERR = 1; +DOMException.DOMSTRING_SIZE_ERR = 2; +DOMException.HIERARCHY_REQUEST_ERR = 3; +DOMException.WRONG_DOCUMENT_ERR = 4; +DOMException.INVALID_CHARACTER_ERR = 5; +DOMException.NO_DATA_ALLOWED_ERR = 6; +DOMException.NO_MODIFICATION_ALLOWED_ERR = 7; +DOMException.NOT_FOUND_ERR = 8; +DOMException.NOT_SUPPORTED_ERR = 9; +DOMException.INUSE_ATTRIBUTE_ERR = 10; + +// Introduced in DOM Level 2: +DOMException.INVALID_STATE_ERR = 11; +DOMException.SYNTAX_ERR = 12; +DOMException.INVALID_MODIFICATION_ERR = 13; +DOMException.NAMESPACE_ERR = 14; +DOMException.INVALID_ACCESS_ERR = 15; + +/** + * @class DocumentFragment - + * DocumentFragment is a "lightweight" or "minimal" Document object. + * @extends Node + * @param ownerDocument : The Document object associated with this node. + */ +DocumentFragment = function(ownerDocument) { + Node.apply(this, arguments); + this.nodeName = "#document-fragment"; +}; +DocumentFragment.prototype = new Node(); +__extend__(DocumentFragment.prototype,{ + get nodeType(){ + return Node.DOCUMENT_FRAGMENT_NODE; + }, + get xml(){ + var xml = "", + count = this.childNodes.length; + + // create string concatenating the serialized ChildNodes + for (var i = 0; i < count; i++) { + xml += this.childNodes.item(i).xml; + } + + return xml; + }, + toString : function(){ + return "[object DocumentFragment]"; + }, + get localName(){ + return null; + } +}); + + +/** + * @class ProcessingInstruction - + * The ProcessingInstruction interface represents a + * "processing instruction", used in XML as a way to + * keep processor-specific information in the text of + * the document + * @extends Node + * @author Jon van Noort (jon@webarcana.com.au) + * @param ownerDocument : The Document object associated with this node. + */ +ProcessingInstruction = function(ownerDocument) { + Node.apply(this, arguments); +}; +ProcessingInstruction.prototype = new Node(); +__extend__(ProcessingInstruction.prototype, { + get data(){ + return this.nodeValue; + }, + set data(data){ + // throw Exception if Node is readonly + if (__ownerDocument__(this).errorChecking && this._readonly) { + throw(new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); + } + this.nodeValue = data; + }, + get textContent(){ + return this.data; + }, + get localName(){ + return null; + }, + get target(){ + // The target of this processing instruction. + // XML defines this as being the first token following the markup that begins the processing instruction. + // The content of this processing instruction. + return this.nodeName; + }, + set target(value){ + // The target of this processing instruction. + // XML defines this as being the first token following the markup that begins the processing instruction. + // The content of this processing instruction. + this.nodeName = value; + }, + get nodeType(){ + return Node.PROCESSING_INSTRUCTION_NODE; + }, + get xml(){ + return ""; + }, + toString : function(){ + return "[object ProcessingInstruction]"; + } +}); + + +/** + * @author envjs team + */ + +Entity = function() { + throw new Error("Entity Not Implemented" ); +}; + +Entity.constants = { + // content taken from W3C "HTML 4.01 Specification" + // "W3C Recommendation 24 December 1999" + + nbsp: "\u00A0", + iexcl: "\u00A1", + cent: "\u00A2", + pound: "\u00A3", + curren: "\u00A4", + yen: "\u00A5", + brvbar: "\u00A6", + sect: "\u00A7", + uml: "\u00A8", + copy: "\u00A9", + ordf: "\u00AA", + laquo: "\u00AB", + not: "\u00AC", + shy: "\u00AD", + reg: "\u00AE", + macr: "\u00AF", + deg: "\u00B0", + plusmn: "\u00B1", + sup2: "\u00B2", + sup3: "\u00B3", + acute: "\u00B4", + micro: "\u00B5", + para: "\u00B6", + middot: "\u00B7", + cedil: "\u00B8", + sup1: "\u00B9", + ordm: "\u00BA", + raquo: "\u00BB", + frac14: "\u00BC", + frac12: "\u00BD", + frac34: "\u00BE", + iquest: "\u00BF", + Agrave: "\u00C0", + Aacute: "\u00C1", + Acirc: "\u00C2", + Atilde: "\u00C3", + Auml: "\u00C4", + Aring: "\u00C5", + AElig: "\u00C6", + Ccedil: "\u00C7", + Egrave: "\u00C8", + Eacute: "\u00C9", + Ecirc: "\u00CA", + Euml: "\u00CB", + Igrave: "\u00CC", + Iacute: "\u00CD", + Icirc: "\u00CE", + Iuml: "\u00CF", + ETH: "\u00D0", + Ntilde: "\u00D1", + Ograve: "\u00D2", + Oacute: "\u00D3", + Ocirc: "\u00D4", + Otilde: "\u00D5", + Ouml: "\u00D6", + times: "\u00D7", + Oslash: "\u00D8", + Ugrave: "\u00D9", + Uacute: "\u00DA", + Ucirc: "\u00DB", + Uuml: "\u00DC", + Yacute: "\u00DD", + THORN: "\u00DE", + szlig: "\u00DF", + agrave: "\u00E0", + aacute: "\u00E1", + acirc: "\u00E2", + atilde: "\u00E3", + auml: "\u00E4", + aring: "\u00E5", + aelig: "\u00E6", + ccedil: "\u00E7", + egrave: "\u00E8", + eacute: "\u00E9", + ecirc: "\u00EA", + euml: "\u00EB", + igrave: "\u00EC", + iacute: "\u00ED", + icirc: "\u00EE", + iuml: "\u00EF", + eth: "\u00F0", + ntilde: "\u00F1", + ograve: "\u00F2", + oacute: "\u00F3", + ocirc: "\u00F4", + otilde: "\u00F5", + ouml: "\u00F6", + divide: "\u00F7", + oslash: "\u00F8", + ugrave: "\u00F9", + uacute: "\u00FA", + ucirc: "\u00FB", + uuml: "\u00FC", + yacute: "\u00FD", + thorn: "\u00FE", + yuml: "\u00FF", + fnof: "\u0192", + Alpha: "\u0391", + Beta: "\u0392", + Gamma: "\u0393", + Delta: "\u0394", + Epsilon: "\u0395", + Zeta: "\u0396", + Eta: "\u0397", + Theta: "\u0398", + Iota: "\u0399", + Kappa: "\u039A", + Lambda: "\u039B", + Mu: "\u039C", + Nu: "\u039D", + Xi: "\u039E", + Omicron: "\u039F", + Pi: "\u03A0", + Rho: "\u03A1", + Sigma: "\u03A3", + Tau: "\u03A4", + Upsilon: "\u03A5", + Phi: "\u03A6", + Chi: "\u03A7", + Psi: "\u03A8", + Omega: "\u03A9", + alpha: "\u03B1", + beta: "\u03B2", + gamma: "\u03B3", + delta: "\u03B4", + epsilon: "\u03B5", + zeta: "\u03B6", + eta: "\u03B7", + theta: "\u03B8", + iota: "\u03B9", + kappa: "\u03BA", + lambda: "\u03BB", + mu: "\u03BC", + nu: "\u03BD", + xi: "\u03BE", + omicron: "\u03BF", + pi: "\u03C0", + rho: "\u03C1", + sigmaf: "\u03C2", + sigma: "\u03C3", + tau: "\u03C4", + upsilon: "\u03C5", + phi: "\u03C6", + chi: "\u03C7", + psi: "\u03C8", + omega: "\u03C9", + thetasym: "\u03D1", + upsih: "\u03D2", + piv: "\u03D6", + bull: "\u2022", + hellip: "\u2026", + prime: "\u2032", + Prime: "\u2033", + oline: "\u203E", + frasl: "\u2044", + weierp: "\u2118", + image: "\u2111", + real: "\u211C", + trade: "\u2122", + alefsym: "\u2135", + larr: "\u2190", + uarr: "\u2191", + rarr: "\u2192", + darr: "\u2193", + harr: "\u2194", + crarr: "\u21B5", + lArr: "\u21D0", + uArr: "\u21D1", + rArr: "\u21D2", + dArr: "\u21D3", + hArr: "\u21D4", + forall: "\u2200", + part: "\u2202", + exist: "\u2203", + empty: "\u2205", + nabla: "\u2207", + isin: "\u2208", + notin: "\u2209", + ni: "\u220B", + prod: "\u220F", + sum: "\u2211", + minus: "\u2212", + lowast: "\u2217", + radic: "\u221A", + prop: "\u221D", + infin: "\u221E", + ang: "\u2220", + and: "\u2227", + or: "\u2228", + cap: "\u2229", + cup: "\u222A", + intXX: "\u222B", + there4: "\u2234", + sim: "\u223C", + cong: "\u2245", + asymp: "\u2248", + ne: "\u2260", + equiv: "\u2261", + le: "\u2264", + ge: "\u2265", + sub: "\u2282", + sup: "\u2283", + nsub: "\u2284", + sube: "\u2286", + supe: "\u2287", + oplus: "\u2295", + otimes: "\u2297", + perp: "\u22A5", + sdot: "\u22C5", + lceil: "\u2308", + rceil: "\u2309", + lfloor: "\u230A", + rfloor: "\u230B", + lang: "\u2329", + rang: "\u232A", + loz: "\u25CA", + spades: "\u2660", + clubs: "\u2663", + hearts: "\u2665", + diams: "\u2666", + quot: "\u0022", + amp: "\u0026", + lt: "\u003C", + gt: "\u003E", + OElig: "\u0152", + oelig: "\u0153", + Scaron: "\u0160", + scaron: "\u0161", + Yuml: "\u0178", + circ: "\u02C6", + tilde: "\u02DC", + ensp: "\u2002", + emsp: "\u2003", + thinsp: "\u2009", + zwnj: "\u200C", + zwj: "\u200D", + lrm: "\u200E", + rlm: "\u200F", + ndash: "\u2013", + mdash: "\u2014", + lsquo: "\u2018", + rsquo: "\u2019", + sbquo: "\u201A", + ldquo: "\u201C", + rdquo: "\u201D", + bdquo: "\u201E", + dagger: "\u2020", + Dagger: "\u2021", + permil: "\u2030", + lsaquo: "\u2039", + rsaquo: "\u203A", + euro: "\u20AC", + + // non-standard entities + apos: "'" +}; + +/** + * @author envjs team + */ + +EntityReference = function() { + throw new Error("EntityReference Not Implemented" ); +}; + +/** + * @class DOMImplementation - + * provides a number of methods for performing operations + * that are independent of any particular instance of the + * document object model. + * + * @author Jon van Noort (jon@webarcana.com.au) + */ +DOMImplementation = function() { + this.preserveWhiteSpace = false; // by default, ignore whitespace + this.namespaceAware = true; // by default, handle namespaces + this.errorChecking = true; // by default, test for exceptions +}; + +__extend__(DOMImplementation.prototype,{ + // @param feature : string - The package name of the feature to test. + // the legal only values are "XML" and "CORE" (case-insensitive). + // @param version : string - This is the version number of the package + // name to test. In Level 1, this is the string "1.0".* + // @return : boolean + hasFeature : function(feature, version) { + var ret = false; + if (feature.toLowerCase() == "xml") { + ret = (!version || (version == "1.0") || (version == "2.0")); + } + else if (feature.toLowerCase() == "core") { + ret = (!version || (version == "2.0")); + } + else if (feature == "http://www.w3.org/TR/SVG11/feature#BasicStructure") { + ret = (version == "1.1"); + } + return ret; + }, + createDocumentType : function(qname, publicId, systemId){ + var doctype = new DocumentType(); + doctype.nodeName = qname?qname.toUpperCase():null; + doctype.publicId = publicId?publicId:null; + doctype.systemId = systemId?systemId:null; + return doctype; + }, + createDocument : function(nsuri, qname, doctype){ + + var doc = null, documentElement; + + doc = new Document(this, null); + if(doctype){ + doc.doctype = doctype; + } + + if(nsuri && qname){ + documentElement = doc.createElementNS(nsuri, qname); + }else if(qname){ + documentElement = doc.createElement(qname); + } + if(documentElement){ + doc.appendChild(documentElement); + } + return doc; + }, + createHTMLDocument : function(title){ + var doc = new HTMLDocument($implementation, null, ""); + var html = doc.createElement("html"); doc.appendChild(html); + var head = doc.createElement("head"); html.appendChild(head); + var body = doc.createElement("body"); html.appendChild(body); + var t = doc.createElement("title"); head.appendChild(t); + if( title) { + t.appendChild(doc.createTextNode(title)); + } + return doc; + }, + translateErrCode : function(code) { + //convert DOMException Code to human readable error message; + var msg = ""; + + switch (code) { + case DOMException.INDEX_SIZE_ERR : // 1 + msg = "INDEX_SIZE_ERR: Index out of bounds"; + break; + + case DOMException.DOMSTRING_SIZE_ERR : // 2 + msg = "DOMSTRING_SIZE_ERR: The resulting string is too long to fit in a DOMString"; + break; + + case DOMException.HIERARCHY_REQUEST_ERR : // 3 + msg = "HIERARCHY_REQUEST_ERR: The Node can not be inserted at this location"; + break; + + case DOMException.WRONG_DOCUMENT_ERR : // 4 + msg = "WRONG_DOCUMENT_ERR: The source and the destination Documents are not the same"; + break; + + case DOMException.INVALID_CHARACTER_ERR : // 5 + msg = "INVALID_CHARACTER_ERR: The string contains an invalid character"; + break; + + case DOMException.NO_DATA_ALLOWED_ERR : // 6 + msg = "NO_DATA_ALLOWED_ERR: This Node / NodeList does not support data"; + break; + + case DOMException.NO_MODIFICATION_ALLOWED_ERR : // 7 + msg = "NO_MODIFICATION_ALLOWED_ERR: This object cannot be modified"; + break; + + case DOMException.NOT_FOUND_ERR : // 8 + msg = "NOT_FOUND_ERR: The item cannot be found"; + break; + + case DOMException.NOT_SUPPORTED_ERR : // 9 + msg = "NOT_SUPPORTED_ERR: This implementation does not support function"; + break; + + case DOMException.INUSE_ATTRIBUTE_ERR : // 10 + msg = "INUSE_ATTRIBUTE_ERR: The Attribute has already been assigned to another Element"; + break; + + // Introduced in DOM Level 2: + case DOMException.INVALID_STATE_ERR : // 11 + msg = "INVALID_STATE_ERR: The object is no longer usable"; + break; + + case DOMException.SYNTAX_ERR : // 12 + msg = "SYNTAX_ERR: Syntax error"; + break; + + case DOMException.INVALID_MODIFICATION_ERR : // 13 + msg = "INVALID_MODIFICATION_ERR: Cannot change the type of the object"; + break; + + case DOMException.NAMESPACE_ERR : // 14 + msg = "NAMESPACE_ERR: The namespace declaration is incorrect"; + break; + + case DOMException.INVALID_ACCESS_ERR : // 15 + msg = "INVALID_ACCESS_ERR: The object does not support this function"; + break; + + default : + msg = "UNKNOWN: Unknown Exception Code ("+ code +")"; + } + + return msg; + }, + toString : function(){ + return "[object DOMImplementation]"; + } +}); + + + +/** + * @method DOMImplementation._isNamespaceDeclaration - Return true, if attributeName is a namespace declaration + * @author Jon van Noort (jon@webarcana.com.au) + * @param attributeName : string - the attribute name + * @return : boolean + */ +function __isNamespaceDeclaration__(attributeName) { + // test if attributeName is 'xmlns' + return (attributeName.indexOf('xmlns') > -1); +} + +/** + * @method DOMImplementation._isIdDeclaration - Return true, if attributeName is an id declaration + * @author Jon van Noort (jon@webarcana.com.au) + * @param attributeName : string - the attribute name + * @return : boolean + */ +function __isIdDeclaration__(attributeName) { + // test if attributeName is 'id' (case insensitive) + return attributeName?(attributeName.toLowerCase() == 'id'):false; +} + +/** + * @method DOMImplementation._isValidName - Return true, + * if name contains no invalid characters + * @author Jon van Noort (jon@webarcana.com.au) + * @param name : string - the candidate name + * @return : boolean + */ +function __isValidName__(name) { + // test if name contains only valid characters + return name.match(re_validName); +} +var re_validName = /^[a-zA-Z_:][a-zA-Z0-9\.\-_:]*$/; + +/** + * @method DOMImplementation._isValidString - Return true, if string does not contain any illegal chars + * All of the characters 0 through 31 and character 127 are nonprinting control characters. + * With the exception of characters 09, 10, and 13, (Ox09, Ox0A, and Ox0D) + * Note: different from _isValidName in that ValidStrings may contain spaces + * @author Jon van Noort (jon@webarcana.com.au) + * @param name : string - the candidate string + * @return : boolean + */ +function __isValidString__(name) { + // test that string does not contains invalid characters + return (name.search(re_invalidStringChars) < 0); +} +var re_invalidStringChars = /\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E|\x0F|\x10|\x11|\x12|\x13|\x14|\x15|\x16|\x17|\x18|\x19|\x1A|\x1B|\x1C|\x1D|\x1E|\x1F|\x7F/; + +/** + * @method DOMImplementation._parseNSName - parse the namespace name. + * if there is no colon, the + * @author Jon van Noort (jon@webarcana.com.au) + * @param qualifiedName : string - The qualified name + * @return : NSName - [ + .prefix : string - The prefix part of the qname + .namespaceName : string - The namespaceURI part of the qname + ] + */ +function __parseNSName__(qualifiedName) { + var resultNSName = {}; + // unless the qname has a namespaceName, the prefix is the entire String + resultNSName.prefix = qualifiedName; + resultNSName.namespaceName = ""; + // split on ':' + var delimPos = qualifiedName.indexOf(':'); + if (delimPos > -1) { + // get prefix + resultNSName.prefix = qualifiedName.substring(0, delimPos); + // get namespaceName + resultNSName.namespaceName = qualifiedName.substring(delimPos +1, qualifiedName.length); + } + return resultNSName; +} + +/** + * @method DOMImplementation._parseQName - parse the qualified name + * @author Jon van Noort (jon@webarcana.com.au) + * @param qualifiedName : string - The qualified name + * @return : QName + */ +function __parseQName__(qualifiedName) { + var resultQName = {}; + // unless the qname has a prefix, the local name is the entire String + resultQName.localName = qualifiedName; + resultQName.prefix = ""; + // split on ':' + var delimPos = qualifiedName.indexOf(':'); + if (delimPos > -1) { + // get prefix + resultQName.prefix = qualifiedName.substring(0, delimPos); + // get localName + resultQName.localName = qualifiedName.substring(delimPos +1, qualifiedName.length); + } + return resultQName; +} +/** + * @author envjs team + */ +Notation = function() { + throw new Error("Notation Not Implemented" ); +};/** + * @author thatcher + */ +Range = function(){ + +}; + +__extend__(Range.prototype, { + get startContainer(){ + + }, + get endContainer(){ + + }, + get startOffset(){ + + }, + get endOffset(){ + + }, + get collapsed(){ + + }, + get commonAncestorContainer(){ + + }, + setStart: function(refNode, offset){//throws RangeException + + }, + setEnd: function(refNode, offset){//throws RangeException + + }, + setStartBefore: function(refNode){//throws RangeException + + }, + setStartAfter: function(refNode){//throws RangeException + + }, + setEndBefore: function(refNode){//throws RangeException + + }, + setEndAfter: function(refNode){//throws RangeException + + }, + collapse: function(toStart){//throws RangeException + + }, + selectNode: function(refNode){//throws RangeException + + }, + selectNodeContents: function(refNode){//throws RangeException + + }, + compareBoundaryPoints: function(how, sourceRange){ + + }, + deleteContents: function(){ + + }, + extractContents: function(){ + + }, + cloneContents: function(){ + + }, + insertNode: function(newNode){ + + }, + surroundContents: function(newParent){ + + }, + cloneRange: function(){ + + }, + toString: function(){ + return '[object Range]'; + }, + detach: function(){ + + } +}); + + + // CompareHow +Range.START_TO_START = 0; +Range.START_TO_END = 1; +Range.END_TO_END = 2; +Range.END_TO_START = 3; + +/* + * Forward declarations + */ +var __isValidNamespace__; + +/** + * @class Document - The Document interface represents the entire HTML + * or XML document. Conceptually, it is the root of the document tree, + * and provides the primary access to the document's data. + * + * @extends Node + * @param implementation : DOMImplementation - the creator Implementation + */ +Document = function(implementation, docParentWindow) { + Node.apply(this, arguments); + + //TODO: Temporary!!! Cnage back to true!!! + this.async = true; + // The Document Type Declaration (see DocumentType) associated with this document + this.doctype = null; + // The DOMImplementation object that handles this document. + this.implementation = implementation; + + this.nodeName = "#document"; + // initially false, set to true by parser + this.parsing = false; + this.baseURI = 'about:blank'; + + this.ownerDocument = null; + + this.importing = false; +}; + +Document.prototype = new Node(); +__extend__(Document.prototype,{ + get localName(){ + return null; + }, + get textContent(){ + return null; + }, + get all(){ + return this.getElementsByTagName("*"); + }, + get documentElement(){ + var i, length = this.childNodes?this.childNodes.length:0; + for(i=0;i -1 ){ + valid = false; + } + + if ((valid) && (!isAttribute)) { + // if the namespaceURI is not null + if (!namespaceURI) { + valid = false; + } + } + + // if the qualifiedName has a prefix + if ((valid) && (qName.prefix === "")) { + valid = false; + } + } + + // if the qualifiedName has a prefix that is "xml" and the namespaceURI is + // different from "http://www.w3.org/XML/1998/namespace" [Namespaces]. + if ((valid) && (qName.prefix === "xml") && (namespaceURI !== "http://www.w3.org/XML/1998/namespace")) { + valid = false; + } + + return valid; +}; +/** + * + * This file only handles XML parser. + * It is extended by parser/domparser.js (and parser/htmlparser.js) + * + * This depends on e4x, which some engines may not have. + * + * @author thatcher + */ +DOMParser = function(principle, documentURI, baseURI) { + // TODO: why/what should these 3 args do? +}; +__extend__(DOMParser.prototype,{ + parseFromString: function(xmlstring, mimetype){ + var doc = new Document(new DOMImplementation()), + e4; + + // The following are e4x directives. + // Full spec is here: + // http://www.ecma-international.org/publications/standards/Ecma-357.htm + // + // that is pretty gross, so checkout this summary + // http://rephrase.net/days/07/06/e4x + // + // also see the Mozilla Developer Center: + // https://developer.mozilla.org/en/E4X + // + XML.ignoreComments = false; + XML.ignoreProcessingInstructions = false; + XML.ignoreWhitespace = false; + + // for some reason e4x can't handle initial xml declarations + // https://bugzilla.mozilla.org/show_bug.cgi?id=336551 + // The official workaround is the big regexp below + // but simpler one seems to be ok + // xmlstring = xmlstring.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); + // + xmlstring = xmlstring.replace(/<\?xml.*\?>/); + + e4 = new XMLList(xmlstring); + + __toDomNode__(e4, doc, doc); + + //console.log('xml \n %s', doc.documentElement.xml); + return doc; + } +}); + +var __toDomNode__ = function(e4, parent, doc){ + var xnode, + domnode, + children, + target, + value, + length, + element, + kind, + item; + //console.log('converting e4x node list \n %s', e4) + + // not using the for each(item in e4) since some engines can't + // handle the syntax (i.e. says syntax error) + // + // for each(xnode in e4) { + for (item in e4) { + // NO do not do this if (e4.hasOwnProperty(item)) { + // breaks spidermonkey + xnode = e4[item]; + + kind = xnode.nodeKind(); + //console.log('treating node kind %s', kind); + switch(kind){ + case 'element': + // add node + //console.log('creating element %s %s', xnode.localName(), xnode.namespace()); + if(xnode.namespace() && (xnode.namespace()+'') !== ''){ + //console.log('createElementNS %s %s',xnode.namespace()+'', xnode.localName() ); + domnode = doc.createElementNS(xnode.namespace()+'', xnode.localName()); + }else{ + domnode = doc.createElement(xnode.name()+''); + } + parent.appendChild(domnode); + + // add attributes + __toDomNode__(xnode.attributes(), domnode, doc); + + // add children + children = xnode.children(); + length = children.length(); + //console.log('recursing? %s', length ? 'yes' : 'no'); + if (length > 0) { + __toDomNode__(children, domnode, doc); + } + break; + case 'attribute': + // console.log('setting attribute %s %s %s', + // xnode.localName(), xnode.namespace(), xnode.valueOf()); + + // + // cross-platform alert. The original code used + // xnode.text() to get the attribute value + // This worked in Rhino, but did not in Spidermonkey + // valueOf seemed to work in both + // + if(xnode.namespace() && xnode.namespace().prefix){ + //console.log("%s", xnode.namespace().prefix); + parent.setAttributeNS(xnode.namespace()+'', + xnode.namespace().prefix+':'+xnode.localName(), + xnode.valueOf()); + }else if((xnode.name()+'').match('http://www.w3.org/2000/xmlns/::')){ + if(xnode.localName()!=='xmlns'){ + parent.setAttributeNS('http://www.w3.org/2000/xmlns/', + 'xmlns:'+xnode.localName(), + xnode.valueOf()); + } + }else{ + parent.setAttribute(xnode.localName()+'', xnode.valueOf()); + } + break; + case 'text': + //console.log('creating text node : %s', xnode); + domnode = doc.createTextNode(xnode+''); + parent.appendChild(domnode); + break; + case 'comment': + //console.log('creating comment node : %s', xnode); + value = xnode+''; + domnode = doc.createComment(value.substring(4,value.length-3)); + parent.appendChild(domnode); + break; + case 'processing-instruction': + //console.log('creating processing-instruction node : %s', xnode); + value = xnode+''; + target = value.split(' ')[0].substring(2); + value = value.split(' ').splice(1).join(' ').replace('?>',''); + //console.log('creating processing-instruction data : %s', value); + domnode = doc.createProcessingInstruction(target, value); + parent.appendChild(domnode); + break; + default: + console.log('e4x DOM ERROR'); + throw new Error("Assertion failed in xml parser"); + } + } +}; +/** + * @author envjs team + * @class XMLSerializer + */ + +XMLSerializer = function() {}; + +__extend__(XMLSerializer.prototype, { + serializeToString: function(node){ + return node.xml; + }, + toString : function(){ + return "[object XMLSerializer]"; + } +}); + +/** + * @author john resig & the envjs team + * @uri http://www.envjs.com/ + * @copyright 2008-2010 + * @license MIT + */ +//CLOSURE_END +}()); +/* + * Envjs event.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + * + * This file simply provides the global definitions we need to + * be able to correctly implement to core browser DOM Event interfaces. + */ +var Event, + MouseEvent, + UIEvent, + KeyboardEvent, + MutationEvent, + DocumentEvent, + EventTarget, + EventException, + //nonstandard but very useful for implementing mutation events + //among other things like general profiling + Aspect; +/* + * Envjs event.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig 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 ); +} +/** + * Borrowed with love from: + * + * jQuery AOP - jQuery plugin to add features of aspect-oriented programming (AOP) to jQuery. + * http://jquery-aop.googlecode.com/ + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Version: 1.1 + */ +(function() { + + var _after = 1; + var _before = 2; + var _around = 3; + var _intro = 4; + var _regexEnabled = true; + + /** + * Private weaving function. + */ + var weaveOne = function(source, method, advice) { + + var old = source[method]; + + var aspect; + if (advice.type == _after) + aspect = function() { + var returnValue = old.apply(this, arguments); + return advice.value.apply(this, [returnValue, method]); + }; + else if (advice.type == _before) + aspect = function() { + advice.value.apply(this, [arguments, method]); + return old.apply(this, arguments); + }; + else if (advice.type == _intro) + aspect = function() { + return advice.value.apply(this, arguments); + }; + else if (advice.type == _around) { + aspect = function() { + var invocation = { object: this, args: arguments }; + return advice.value.apply(invocation.object, [{ arguments: invocation.args, method: method, proceed : + function() { + return old.apply(invocation.object, invocation.args); + } + }] ); + }; + } + + aspect.unweave = function() { + source[method] = old; + pointcut = source = aspect = old = null; + }; + + source[method] = aspect; + + return aspect; + + }; + + + /** + * Private weaver and pointcut parser. + */ + var weave = function(pointcut, advice) + { + + var source = (typeof(pointcut.target.prototype) != 'undefined') ? pointcut.target.prototype : pointcut.target; + var advices = []; + + // If it's not an introduction and no method was found, try with regex... + if (advice.type != _intro && typeof(source[pointcut.method]) == 'undefined') + { + + for (var method in source) + { + if (source[method] != null && source[method] instanceof Function && method.match(pointcut.method)) + { + advices[advices.length] = weaveOne(source, method, advice); + } + } + + if (advices.length == 0) + throw 'No method: ' + pointcut.method; + + } + else + { + // Return as an array of one element + advices[0] = weaveOne(source, pointcut.method, advice); + } + + return _regexEnabled ? advices : advices[0]; + + }; + + Aspect = + { + /** + * Creates an advice after the defined point-cut. The advice will be executed after the point-cut method + * has completed execution successfully, and will receive one parameter with the result of the execution. + * This function returns an array of weaved aspects (Function). + * + * @example jQuery.aop.after( {target: window, method: 'MyGlobalMethod'}, function(result) { alert('Returned: ' + result); } ); + * @result Array + * + * @example jQuery.aop.after( {target: String, method: 'indexOf'}, function(index) { alert('Result found at: ' + index + ' on:' + this); } ); + * @result Array + * + * @name after + * @param Map pointcut Definition of the point-cut to apply the advice. A point-cut is the definition of the object/s and method/s to be weaved. + * @option Object target Target object to be weaved. + * @option String method Name of the function to be weaved. Regex are supported, but not on built-in objects. + * @param Function advice Function containing the code that will get called after the execution of the point-cut. It receives one parameter + * with the result of the point-cut's execution. + * + * @type Array + * @cat Plugins/General + */ + after : function(pointcut, advice) + { + return weave( pointcut, { type: _after, value: advice } ); + }, + + /** + * Creates an advice before the defined point-cut. The advice will be executed before the point-cut method + * but cannot modify the behavior of the method, or prevent its execution. + * This function returns an array of weaved aspects (Function). + * + * @example jQuery.aop.before( {target: window, method: 'MyGlobalMethod'}, function() { alert('About to execute MyGlobalMethod'); } ); + * @result Array + * + * @example jQuery.aop.before( {target: String, method: 'indexOf'}, function(index) { alert('About to execute String.indexOf on: ' + this); } ); + * @result Array + * + * @name before + * @param Map pointcut Definition of the point-cut to apply the advice. A point-cut is the definition of the object/s and method/s to be weaved. + * @option Object target Target object to be weaved. + * @option String method Name of the function to be weaved. Regex are supported, but not on built-in objects. + * @param Function advice Function containing the code that will get called before the execution of the point-cut. + * + * @type Array + * @cat Plugins/General + */ + before : function(pointcut, advice) + { + return weave( pointcut, { type: _before, value: advice } ); + }, + + + /** + * Creates an advice 'around' the defined point-cut. This type of advice can control the point-cut method execution by calling + * the functions '.proceed()' on the 'invocation' object, and also, can modify the arguments collection before sending them to the function call. + * This function returns an array of weaved aspects (Function). + * + * @example jQuery.aop.around( {target: window, method: 'MyGlobalMethod'}, function(invocation) { + * alert('# of Arguments: ' + invocation.arguments.length); + * return invocation.proceed(); + * } ); + * @result Array + * + * @example jQuery.aop.around( {target: String, method: 'indexOf'}, function(invocation) { + * alert('Searching: ' + invocation.arguments[0] + ' on: ' + this); + * return invocation.proceed(); + * } ); + * @result Array + * + * @example jQuery.aop.around( {target: window, method: /Get(\d+)/}, function(invocation) { + * alert('Executing ' + invocation.method); + * return invocation.proceed(); + * } ); + * @desc Matches all global methods starting with 'Get' and followed by a number. + * @result Array + * + * + * @name around + * @param Map pointcut Definition of the point-cut to apply the advice. A point-cut is the definition of the object/s and method/s to be weaved. + * @option Object target Target object to be weaved. + * @option String method Name of the function to be weaved. Regex are supported, but not on built-in objects. + * @param Function advice Function containing the code that will get called around the execution of the point-cut. This advice will be called with one + * argument containing one function '.proceed()', the collection of arguments '.arguments', and the matched method name '.method'. + * + * @type Array + * @cat Plugins/General + */ + around : function(pointcut, advice) + { + return weave( pointcut, { type: _around, value: advice } ); + }, + + /** + * Creates an introduction on the defined point-cut. This type of advice replaces any existing methods with the same + * name. To restore them, just unweave it. + * This function returns an array with only one weaved aspect (Function). + * + * @example jQuery.aop.introduction( {target: window, method: 'MyGlobalMethod'}, function(result) { alert('Returned: ' + result); } ); + * @result Array + * + * @example jQuery.aop.introduction( {target: String, method: 'log'}, function() { alert('Console: ' + this); } ); + * @result Array + * + * @name introduction + * @param Map pointcut Definition of the point-cut to apply the advice. A point-cut is the definition of the object/s and method/s to be weaved. + * @option Object target Target object to be weaved. + * @option String method Name of the function to be weaved. + * @param Function advice Function containing the code that will be executed on the point-cut. + * + * @type Array + * @cat Plugins/General + */ + introduction : function(pointcut, advice) + { + return weave( pointcut, { type: _intro, value: advice } ); + }, + + /** + * Configures global options. + * + * @name setup + * @param Map settings Configuration options. + * @option Boolean regexMatch Enables/disables regex matching of method names. + * + * @example jQuery.aop.setup( { regexMatch: false } ); + * @desc Disable regex matching. + * + * @type Void + * @cat Plugins/General + */ + setup: function(settings) + { + _regexEnabled = settings.regexMatch; + } + }; + +})(); + + + + +/** + * @name EventTarget + * @w3c:domlevel 2 + * @uri -//TODO: paste dom event level 2 w3c spc uri here + */ +EventTarget = function(){}; +EventTarget.prototype.addEventListener = function(type, fn, phase){ + __addEventListener__(this, type, fn, phase); +}; +EventTarget.prototype.removeEventListener = function(type, fn){ + __removeEventListener__(this, type, fn); +}; +EventTarget.prototype.dispatchEvent = function(event, bubbles){ + __dispatchEvent__(this, event, bubbles); +}; + +__extend__(Node.prototype, EventTarget.prototype); + + +var $events = [{}]; + +function __addEventListener__(target, type, fn, phase){ + phase = !!phase?"CAPTURING":"BUBBLING"; + if ( !target.uuid ) { + //console.log('event uuid %s %s', target, target.uuid); + target.uuid = $events.length+''; + } + if ( !$events[target.uuid] ) { + //console.log('creating listener for target: %s %s', target, target.uuid); + $events[target.uuid] = {}; + } + if ( !$events[target.uuid][type] ){ + //console.log('creating listener for type: %s %s %s', target, target.uuid, type); + $events[target.uuid][type] = { + CAPTURING:[], + BUBBLING:[] + }; + } + if ( $events[target.uuid][type][phase].indexOf( fn ) < 0 ){ + //console.log('adding event listener %s %s %s %s %s %s', target, target.uuid, type, phase, + // $events[target.uuid][type][phase].length, $events[target.uuid][type][phase].indexOf( fn )); + //console.log('creating listener for function: %s %s %s', target, target.uuid, phase); + $events[target.uuid][type][phase].push( fn ); + //console.log('adding event listener %s %s %s %s %s %s', target, target.uuid, type, phase, + // $events[target.uuid][type][phase].length, $events[target.uuid][type][phase].indexOf( fn )); + } + //console.log('registered event listeners %s', $events.length); +} + +function __removeEventListener__(target, type, fn, phase){ + + phase = !!phase?"CAPTURING":"BUBBLING"; + if ( !target.uuid ) { + return; + } + if ( !$events[target.uuid] ) { + return; + } + if(type == '*'){ + //used to clean all event listeners for a given node + //console.log('cleaning all event listeners for node %s %s',target, target.uuid); + delete $events[target.uuid]; + return; + }else if ( !$events[target.uuid][type] ){ + return; + } + $events[target.uuid][type][phase] = + $events[target.uuid][type][phase].filter(function(f){ + //console.log('removing event listener %s %s %s %s', target, type, phase, fn); + return f != fn; + }); +} + +var __eventuuid__ = 0; +function __dispatchEvent__(target, event, bubbles){ + + if (!event.uuid) { + event.uuid = __eventuuid__++; + } + //the window scope defines the $event object, for IE(^^^) compatibility; + //$event = event; + //console.log('dispatching event %s', event.uuid); + if (bubbles === undefined || bubbles === null) { + bubbles = true; + } + + if (!event.target) { + event.target = target; + } + + //console.log('dispatching? %s %s %s', target, event.type, bubbles); + if ( event.type && (target.nodeType || target === window )) { + + //console.log('dispatching event %s %s %s', target, event.type, bubbles); + __captureEvent__(target, event); + + event.eventPhase = Event.AT_TARGET; + if ( target.uuid && $events[target.uuid] && $events[target.uuid][event.type] ) { + event.currentTarget = target; + //console.log('dispatching %s %s %s %s', target, event.type, + // $events[target.uuid][event.type]['CAPTURING'].length); + $events[target.uuid][event.type].CAPTURING.forEach(function(fn){ + //console.log('AT_TARGET (CAPTURING) event %s', fn); + var returnValue = fn( event ); + //console.log('AT_TARGET (CAPTURING) return value %s', returnValue); + if(returnValue === false){ + event.stopPropagation(); + } + }); + //console.log('dispatching %s %s %s %s', target, event.type, + // $events[target.uuid][event.type]['BUBBLING'].length); + $events[target.uuid][event.type].BUBBLING.forEach(function(fn){ + //console.log('AT_TARGET (BUBBLING) event %s', fn); + var returnValue = fn( event ); + //console.log('AT_TARGET (BUBBLING) return value %s', returnValue); + if(returnValue === false){ + event.stopPropagation(); + } + }); + } + if (target["on" + event.type]) { + target["on" + event.type](event); + } + if (bubbles && !event.cancelled){ + __bubbleEvent__(target, event); + } + if(!event._preventDefault){ + //At this point I'm guessing that just HTMLEvents are concerned + //with default behavior being executed in a browser but I could be + //wrong as usual. The goal is much more to filter at this point + //what events have no need to be handled + //console.log('triggering default behavior for %s', event.type); + if(event.type in Envjs.defaultEventBehaviors){ + Envjs.defaultEventBehaviors[event.type](event); + } + } + //console.log('deleting event %s', event.uuid); + event.target = null; + event = null; + }else{ + throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR); + } +} + +function __captureEvent__(target, event){ + var ancestorStack = [], + parent = target.parentNode; + + event.eventPhase = Event.CAPTURING_PHASE; + while(parent){ + if(parent.uuid && $events[parent.uuid] && $events[parent.uuid][event.type]){ + ancestorStack.push(parent); + } + parent = parent.parentNode; + } + while(ancestorStack.length && !event.cancelled){ + event.currentTarget = ancestorStack.pop(); + if($events[event.currentTarget.uuid] && $events[event.currentTarget.uuid][event.type]){ + $events[event.currentTarget.uuid][event.type].CAPTURING.forEach(function(fn){ + var returnValue = fn( event ); + if(returnValue === false){ + event.stopPropagation(); + } + }); + } + } +} + +function __bubbleEvent__(target, event){ + var parent = target.parentNode; + event.eventPhase = Event.BUBBLING_PHASE; + while(parent){ + if(parent.uuid && $events[parent.uuid] && $events[parent.uuid][event.type] ){ + event.currentTarget = parent; + $events[event.currentTarget.uuid][event.type].BUBBLING.forEach(function(fn){ + var returnValue = fn( event ); + if(returnValue === false){ + event.stopPropagation(); + } + }); + } + parent = parent.parentNode; + } +} + +/** + * @class Event + */ +Event = function(options){ + // event state is kept read-only by forcing + // a new object for each event. This may not + // be appropriate in the long run and we'll + // have to decide if we simply dont adhere to + // the read-only restriction of the specification + this._bubbles = true; + this._cancelable = true; + this._cancelled = false; + this._currentTarget = null; + this._target = null; + this._eventPhase = Event.AT_TARGET; + this._timeStamp = new Date().getTime(); + this._preventDefault = false; + this._stopPropogation = false; +}; + +__extend__(Event.prototype,{ + get bubbles(){return this._bubbles;}, + get cancelable(){return this._cancelable;}, + get currentTarget(){return this._currentTarget;}, + set currentTarget(currentTarget){ this._currentTarget = currentTarget; }, + get eventPhase(){return this._eventPhase;}, + set eventPhase(eventPhase){this._eventPhase = eventPhase;}, + get target(){return this._target;}, + set target(target){ this._target = target;}, + get timeStamp(){return this._timeStamp;}, + get type(){return this._type;}, + initEvent: function(type, bubbles, cancelable){ + this._type=type?type:''; + this._bubbles=!!bubbles; + this._cancelable=!!cancelable; + }, + preventDefault: function(){ + this._preventDefault = true; + }, + stopPropagation: function(){ + if(this._cancelable){ + this._cancelled = true; + this._bubbles = false; + } + }, + get cancelled(){ + return this._cancelled; + }, + toString: function(){ + return '[object Event]'; + } +}); + +__extend__(Event,{ + CAPTURING_PHASE : 1, + AT_TARGET : 2, + BUBBLING_PHASE : 3 +}); + + + +/** + * @name UIEvent + * @param {Object} options + */ +UIEvent = function(options) { + this._view = null; + this._detail = 0; +}; + +UIEvent.prototype = new Event(); +__extend__(UIEvent.prototype,{ + get view(){ + return this._view; + }, + get detail(){ + return this._detail; + }, + initUIEvent: function(type, bubbles, cancelable, windowObject, detail){ + this.initEvent(type, bubbles, cancelable); + this._detail = 0; + this._view = windowObject; + } +}); + +var $onblur, + $onfocus, + $onresize; + + +/** + * @name MouseEvent + * @w3c:domlevel 2 + * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html + */ +MouseEvent = function(options) { + this._screenX= 0; + this._screenY= 0; + this._clientX= 0; + this._clientY= 0; + this._ctrlKey= false; + this._metaKey= false; + this._altKey= false; + this._button= null; + this._relatedTarget= null; +}; +MouseEvent.prototype = new UIEvent(); +__extend__(MouseEvent.prototype,{ + get screenX(){ + return this._screenX; + }, + get screenY(){ + return this._screenY; + }, + get clientX(){ + return this._clientX; + }, + get clientY(){ + return this._clientY; + }, + get ctrlKey(){ + return this._ctrlKey; + }, + get altKey(){ + return this._altKey; + }, + get shiftKey(){ + return this._shiftKey; + }, + get metaKey(){ + return this._metaKey; + }, + get button(){ + return this._button; + }, + get relatedTarget(){ + return this._relatedTarget; + }, + initMouseEvent: function(type, bubbles, cancelable, windowObject, detail, + screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, + metaKey, button, relatedTarget){ + this.initUIEvent(type, bubbles, cancelable, windowObject, detail); + this._screenX = screenX; + this._screenY = screenY; + this._clientX = clientX; + this._clientY = clientY; + this._ctrlKey = ctrlKey; + this._altKey = altKey; + this._shiftKey = shiftKey; + this._metaKey = metaKey; + this._button = button; + this._relatedTarget = relatedTarget; + } +}); + +/** + * Interface KeyboardEvent (introduced in DOM Level 3) + */ +KeyboardEvent = function(options) { + this._keyIdentifier = 0; + this._keyLocation = 0; + this._ctrlKey = false; + this._metaKey = false; + this._altKey = false; + this._metaKey = false; +}; +KeyboardEvent.prototype = new UIEvent(); + +__extend__(KeyboardEvent.prototype,{ + + get ctrlKey(){ + return this._ctrlKey; + }, + get altKey(){ + return this._altKey; + }, + get shiftKey(){ + return this._shiftKey; + }, + get metaKey(){ + return this._metaKey; + }, + get button(){ + return this._button; + }, + get relatedTarget(){ + return this._relatedTarget; + }, + getModifiersState: function(keyIdentifier){ + + }, + initMouseEvent: function(type, bubbles, cancelable, windowObject, + keyIdentifier, keyLocation, modifiersList, repeat){ + this.initUIEvent(type, bubbles, cancelable, windowObject, 0); + this._keyIdentifier = keyIdentifier; + this._keyLocation = keyLocation; + this._modifiersList = modifiersList; + this._repeat = repeat; + } +}); + +KeyboardEvent.DOM_KEY_LOCATION_STANDARD = 0; +KeyboardEvent.DOM_KEY_LOCATION_LEFT = 1; +KeyboardEvent.DOM_KEY_LOCATION_RIGHT = 2; +KeyboardEvent.DOM_KEY_LOCATION_NUMPAD = 3; +KeyboardEvent.DOM_KEY_LOCATION_MOBILE = 4; +KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK = 5; + + + +//We dont fire mutation events until someone has registered for them +var __supportedMutations__ = /DOMSubtreeModified|DOMNodeInserted|DOMNodeRemoved|DOMAttrModified|DOMCharacterDataModified/; + +var __fireMutationEvents__ = Aspect.before({ + target: EventTarget, + method: 'addEventListener' +}, function(target, type){ + if(type && type.match(__supportedMutations__)){ + //unweaving removes the __addEventListener__ aspect + __fireMutationEvents__.unweave(); + // These two methods are enough to cover all dom 2 manipulations + Aspect.around({ + target: Node, + method:"removeChild" + }, function(invocation){ + var event, + node = invocation.arguments[0]; + event = node.ownerDocument.createEvent('MutationEvents'); + event.initEvent('DOMNodeRemoved', true, false, node.parentNode, null, null, null, null); + node.dispatchEvent(event, false); + return invocation.proceed(); + + }); + Aspect.around({ + target: Node, + method:"appendChild" + }, function(invocation) { + var event, + node = invocation.proceed(); + event = node.ownerDocument.createEvent('MutationEvents'); + event.initEvent('DOMNodeInserted', true, false, node.parentNode, null, null, null, null); + node.dispatchEvent(event, false); + return node; + }); + } +}); + +/** + * @name MutationEvent + * @param {Object} options + */ +MutationEvent = function(options) { + this._cancelable = false; + this._timeStamp = 0; +}; + +MutationEvent.prototype = new Event(); +__extend__(MutationEvent.prototype,{ + get relatedNode(){ + return this._relatedNode; + }, + get prevValue(){ + return this._prevValue; + }, + get newValue(){ + return this._newValue; + }, + get attrName(){ + return this._attrName; + }, + get attrChange(){ + return this._attrChange; + }, + initMutationEvent: function( type, bubbles, cancelable, + relatedNode, prevValue, newValue, attrName, attrChange ){ + this._relatedNode = relatedNode; + this._prevValue = prevValue; + this._newValue = newValue; + this._attrName = attrName; + this._attrChange = attrChange; + switch(type){ + case "DOMSubtreeModified": + this.initEvent(type, true, false); + break; + case "DOMNodeInserted": + this.initEvent(type, true, false); + break; + case "DOMNodeRemoved": + this.initEvent(type, true, false); + break; + case "DOMNodeRemovedFromDocument": + this.initEvent(type, false, false); + break; + case "DOMNodeInsertedIntoDocument": + this.initEvent(type, false, false); + break; + case "DOMAttrModified": + this.initEvent(type, true, false); + break; + case "DOMCharacterDataModified": + this.initEvent(type, true, false); + break; + default: + this.initEvent(type, bubbles, cancelable); + } + } +}); + +// constants +MutationEvent.ADDITION = 0; +MutationEvent.MODIFICATION = 1; +MutationEvent.REMOVAL = 2; + + +/** + * @name EventException + */ +EventException = function(code) { + this.code = code; +}; +EventException.UNSPECIFIED_EVENT_TYPE_ERR = 0; +/** + * + * DOM Level 2: http://www.w3.org/TR/DOM-Level-2-Events/events.html + * DOM Level 3: http://www.w3.org/TR/DOM-Level-3-Events/ + * + * interface DocumentEvent { + * Event createEvent (in DOMString eventType) + * raises (DOMException); + * }; + * + * Firefox (3.6) exposes DocumentEvent + * Safari (4) does NOT. + */ + +/** + * TODO: Not sure we need a full prototype. We not just an regular object? + */ +DocumentEvent = function(){}; +DocumentEvent.prototype.__EventMap__ = { + // Safari4: singular and plural forms accepted + // Firefox3.6: singular and plural forms accepted + 'Event' : Event, + 'Events' : Event, + 'UIEvent' : UIEvent, + 'UIEvents' : UIEvent, + 'MouseEvent' : MouseEvent, + 'MouseEvents' : MouseEvent, + 'MutationEvent' : MutationEvent, + 'MutationEvents' : MutationEvent, + + // Safari4: accepts HTMLEvents, but not HTMLEvent + // Firefox3.6: accepts HTMLEvents, but not HTMLEvent + 'HTMLEvent' : Event, + 'HTMLEvents' : Event, + + // Safari4: both not accepted + // Firefox3.6, only KeyEvents is accepted + 'KeyEvent' : KeyboardEvent, + 'KeyEvents' : KeyboardEvent, + + // Safari4: both accepted + // Firefox3.6: none accepted + 'KeyboardEvent' : KeyboardEvent, + 'KeyboardEvents' : KeyboardEvent +}; + +DocumentEvent.prototype.createEvent = function(eventType) { + var Clazz = this.__EventMap__[eventType]; + if (Clazz) { + return new Clazz(); + } + throw(new DOMException(DOMException.NOT_SUPPORTED_ERR)); +}; + +__extend__(Document.prototype, DocumentEvent.prototype); + +/** + * @author john resig & the envjs team + * @uri http://www.envjs.com/ + * @copyright 2008-2010 + * @license MIT + */ +//CLOSURE_END +}()); + +/* + * Envjs timer.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + * + * Parts of the implementation were originally written by:\ + * Steven Parkes + * + * requires Envjs.wait, Envjs.sleep, Envjs.WAIT_INTERVAL + */ +var setTimeout, + clearTimeout, + setInterval, + clearInterval; + +/* + * Envjs timer.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + */ + +//CLOSURE_START +(function(){ + + + + +/* +* timer.js +* implementation provided by Steven Parkes +*/ + +//private +var $timers = [], + EVENT_LOOP_RUNNING = false; + +$timers.lock = function(fn){ + Envjs.sync(fn)(); +}; + +//private internal class +var Timer = function(fn, interval){ + this.fn = fn; + this.interval = interval; + this.at = Date.now() + interval; + // allows for calling wait() from callbacks + this.running = false; +}; + +Timer.prototype.start = function(){}; +Timer.prototype.stop = function(){}; + +//static +Timer.normalize = function(time) { + time = time*1; + if ( isNaN(time) || time < 0 ) { + time = 0; + } + + if ( EVENT_LOOP_RUNNING && time < Timer.MIN_TIME ) { + time = Timer.MIN_TIME; + } + return time; +}; +// html5 says this should be at least 4, but the parser is using +// a setTimeout for the SAX stuff which messes up the world +Timer.MIN_TIME = /* 4 */ 0; + +/** + * @function setTimeout + * @param {Object} fn + * @param {Object} time + */ +setTimeout = function(fn, time){ + var num; + time = Timer.normalize(time); + $timers.lock(function(){ + num = $timers.length+1; + var tfn; + if (typeof fn == 'string') { + tfn = function() { + try { + // eval in global scope + eval(fn, null); + } catch (e) { + console.log('timer error %s %s', fn, e); + } finally { + clearInterval(num); + } + }; + } else { + tfn = function() { + try { + fn(); + } catch (e) { + console.log('timer error %s %s', fn, e); + } finally { + clearInterval(num); + } + }; + } + //console.log("Creating timer number %s", num); + $timers[num] = new Timer(tfn, time); + $timers[num].start(); + }); + return num; +}; + +/** + * @function setInterval + * @param {Object} fn + * @param {Object} time + */ +setInterval = function(fn, time){ + //console.log('setting interval %s %s', time, fn.toString().substring(0,64)); + time = Timer.normalize(time); + if ( time < 10 ) { + time = 10; + } + if (typeof fn == 'string') { + var fnstr = fn; + fn = function() { + eval(fnstr); + }; + } + var num; + $timers.lock(function(){ + num = $timers.length+1; + //Envjs.debug("Creating timer number "+num); + $timers[num] = new Timer(fn, time); + $timers[num].start(); + }); + return num; +}; + +/** + * clearInterval + * @param {Object} num + */ +clearInterval = clearTimeout = function(num){ + //console.log("clearing interval "+num); + $timers.lock(function(){ + if ( $timers[num] ) { + $timers[num].stop(); + delete $timers[num]; + } + }); +}; + +// wait === null/undefined: execute any timers as they fire, +// waiting until there are none left +// wait(n) (n > 0): execute any timers as they fire until there +// are none left waiting at least n ms but no more, even if there +// are future events/current threads +// wait(0): execute any immediately runnable timers and return +// wait(-n): keep sleeping until the next event is more than n ms +// in the future +// +// TODO: make a priority queue ... + +Envjs.wait = function(wait) { + //console.log('wait %s', wait); + var delta_wait, + start = Date.now(), + was_running = EVENT_LOOP_RUNNING; + + if (wait < 0) { + delta_wait = -wait; + wait = 0; + } + EVENT_LOOP_RUNNING = true; + if (wait !== 0 && wait !== null && wait !== undefined){ + wait += Date.now(); + } + + var earliest, + timer, + sleep, + index, + goal, + now, + nextfn; + + for (;;) { + //console.log('timer loop'); + earliest = sleep = goal = now = nextfn = null; + $timers.lock(function(){ + for(index in $timers){ + if( isNaN(index*0) ) { + continue; + } + timer = $timers[index]; + // determine timer with smallest run-at time that is + // not already running + if( !timer.running && ( !earliest || timer.at < earliest.at) ) { + earliest = timer; + } + } + }); + //next sleep time + sleep = earliest && earliest.at - Date.now(); + if ( earliest && sleep <= 0 ) { + nextfn = earliest.fn; + try { + //console.log('running stack %s', nextfn.toString().substring(0,64)); + earliest.running = true; + nextfn(); + } catch (e) { + console.log('timer error %s %s', nextfn, e); + } finally { + earliest.running = false; + } + goal = earliest.at + earliest.interval; + now = Date.now(); + if ( goal < now ) { + earliest.at = now; + } else { + earliest.at = goal; + } + continue; + } + + // bunch of subtle cases here ... + if ( !earliest ) { + // no events in the queue (but maybe XHR will bring in events, so ... + if ( !wait || wait < Date.now() ) { + // Loop ends if there are no events and a wait hasn't been + // requested or has expired + break; + } + // no events, but a wait requested: fall through to sleep + } else { + // there are events in the queue, but they aren't firable now + /*if ( delta_wait && sleep <= delta_wait ) { + //TODO: why waste a check on a tight + // loop if it just falls through? + // if they will happen within the next delta, fall through to sleep + } else */if ( wait === 0 || ( wait > 0 && wait < Date.now () ) ) { + // loop ends even if there are events but the user + // specifcally asked not to wait too long + break; + } + // there are events and the user wants to wait: fall through to sleep + } + + // Related to ajax threads ... hopefully can go away .. + var interval = Envjs.WAIT_INTERVAL || 100; + if ( !sleep || sleep > interval ) { + sleep = interval; + } + //console.log('sleeping %s', sleep); + Envjs.sleep(sleep); + + } + EVENT_LOOP_RUNNING = was_running; +}; + + +/** + * @author john resig & the envjs team + * @uri http://www.envjs.com/ + * @copyright 2008-2010 + * @license MIT + */ +//CLOSURE_END +}()); +/* + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + * + * This file simply provides the global definitions we need to + * be able to correctly implement to core browser DOM HTML interfaces. + */ +var HTMLDocument, + HTMLElement, + HTMLCollection, + HTMLAnchorElement, + HTMLAreaElement, + HTMLBaseElement, + HTMLQuoteElement, + HTMLBodyElement, + HTMLBRElement, + HTMLButtonElement, + HTMLCanvasElement, + HTMLTableColElement, + HTMLModElement, + HTMLDivElement, + HTMLDListElement, + HTMLFieldSetElement, + HTMLFormElement, + HTMLFrameElement, + HTMLFrameSetElement, + HTMLHeadElement, + HTMLHeadingElement, + HTMLHRElement, + HTMLHtmlElement, + HTMLIFrameElement, + HTMLImageElement, + HTMLInputElement, + HTMLLabelElement, + HTMLLegendElement, + HTMLLIElement, + HTMLLinkElement, + HTMLMapElement, + HTMLMetaElement, + HTMLObjectElement, + HTMLOListElement, + HTMLOptGroupElement, + HTMLOptionElement, + HTMLParagraphElement, + HTMLParamElement, + HTMLPreElement, + HTMLScriptElement, + HTMLSelectElement, + HTMLSpanElement, + HTMLStyleElement, + HTMLTableElement, + HTMLTableSectionElement, + HTMLTableCellElement, + HTMLTableDataCellElement, + HTMLTableHeaderCellElement, + HTMLTableRowElement, + HTMLTextAreaElement, + HTMLTitleElement, + HTMLUListElement, + HTMLUnknownElement, + Image, + Option, + __loadImage__, + __loadLink__; + +/* + * Envjs html.1.2.13 + * Pure JavaScript Browser Environment + * By John Resig and the Envjs Team + * Copyright 2008-2010 John Resig, under the MIT License + */ + +//CLOSURE_START +(function(){ + + + + + +/** + * @author ariel flesler + * http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html + * @param {Object} str + */ +function __trim__( str ){ + return (str || "").replace( /^\s+|\s+$/g, "" ); +} + + +/** + * @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 HTMLDocument + * The Document interface represents the entire HTML or XML document. + * Conceptually, it is the root of the document tree, and provides + * the primary access to the document's data. + * + * @extends Document + */ +HTMLDocument = function(implementation, ownerWindow, referrer) { + Document.apply(this, arguments); + this.referrer = referrer || ''; + this.baseURI = "about:blank"; + this.ownerWindow = ownerWindow; +}; + +HTMLDocument.prototype = new Document(); + +__extend__(HTMLDocument.prototype, { + createElement: function(tagName){ + var node; + tagName = tagName.toUpperCase(); + // create Element specifying 'this' as ownerDocument + // This is an html document so we need to use explicit interfaces per the + //TODO: would be much faster as a big switch + switch(tagName){ + case "A": + node = new HTMLAnchorElement(this);break; + case "AREA": + node = new HTMLAreaElement(this);break; + case "BASE": + node = new HTMLBaseElement(this);break; + case "BLOCKQUOTE": + node = new HTMLQuoteElement(this);break; + case "CANVAS": + node = new HTMLCanvasElement(this);break; + case "Q": + node = new HTMLQuoteElement(this);break; + case "BODY": + node = new HTMLBodyElement(this);break; + case "BR": + node = new HTMLBRElement(this);break; + case "BUTTON": + node = new HTMLButtonElement(this);break; + case "CAPTION": + node = new HTMLElement(this);break; + case "COL": + node = new HTMLTableColElement(this);break; + case "COLGROUP": + node = new HTMLTableColElement(this);break; + case "DEL": + node = new HTMLModElement(this);break; + case "INS": + node = new HTMLModElement(this);break; + case "DIV": + node = new HTMLDivElement(this);break; + case "DL": + node = new HTMLDListElement(this);break; + case "DT": + node = new HTMLElement(this); break; + case "FIELDSET": + node = new HTMLFieldSetElement(this);break; + case "FORM": + node = new HTMLFormElement(this);break; + case "FRAME": + node = new HTMLFrameElement(this);break; + case "H1": + node = new HTMLHeadingElement(this);break; + case "H2": + node = new HTMLHeadingElement(this);break; + case "H3": + node = new HTMLHeadingElement(this);break; + case "H4": + node = new HTMLHeadingElement(this);break; + case "H5": + node = new HTMLHeadingElement(this);break; + case "H6": + node = new HTMLHeadingElement(this);break; + case "HEAD": + node = new HTMLHeadElement(this);break; + case "HR": + node = new HTMLHRElement(this);break; + case "HTML": + node = new HTMLHtmlElement(this);break; + case "IFRAME": + node = new HTMLIFrameElement(this);break; + case "IMG": + node = new HTMLImageElement(this);break; + case "INPUT": + node = new HTMLInputElement(this);break; + case "LABEL": + node = new HTMLLabelElement(this);break; + case "LEGEND": + node = new HTMLLegendElement(this);break; + case "LI": + node = new HTMLLIElement(this);break; + case "LINK": + node = new HTMLLinkElement(this);break; + case "MAP": + node = new HTMLMapElement(this);break; + case "META": + node = new HTMLMetaElement(this);break; + case "NOSCRIPT": + node = new HTMLElement(this);break; + case "OBJECT": + node = new HTMLObjectElement(this);break; + case "OPTGROUP": + node = new HTMLOptGroupElement(this);break; + case "OL": + node = new HTMLOListElement(this); break; + case "OPTION": + node = new HTMLOptionElement(this);break; + case "P": + node = new HTMLParagraphElement(this);break; + case "PARAM": + node = new HTMLParamElement(this);break; + case "PRE": + node = new HTMLPreElement(this);break; + case "SCRIPT": + node = new HTMLScriptElement(this);break; + case "SELECT": + node = new HTMLSelectElement(this);break; + case "SMALL": + node = new HTMLElement(this);break; + case "SPAN": + node = new HTMLSpanElement(this);break; + case "STRONG": + node = new HTMLElement(this);break; + case "STYLE": + node = new HTMLStyleElement(this);break; + case "TABLE": + node = new HTMLTableElement(this);break; + case "TBODY": + node = new HTMLTableSectionElement(this);break; + case "TFOOT": + node = new HTMLTableSectionElement(this);break; + case "THEAD": + node = new HTMLTableSectionElement(this);break; + case "TD": + node = new HTMLTableDataCellElement(this);break; + case "TH": + node = new HTMLTableHeaderCellElement(this);break; + case "TEXTAREA": + node = new HTMLTextAreaElement(this);break; + case "TITLE": + node = new HTMLTitleElement(this);break; + case "TR": + node = new HTMLTableRowElement(this);break; + case "UL": + node = new HTMLUListElement(this);break; + default: + node = new HTMLUnknownElement(this); + } + // assign values to properties (and aliases) + node.nodeName = tagName; + return node; + }, + createElementNS : function (uri, local) { + //print('createElementNS :'+uri+" "+local); + if(!uri){ + return this.createElement(local); + }else if ("http://www.w3.org/1999/xhtml" == uri) { + return this.createElement(local); + } else if ("http://www.w3.org/1998/Math/MathML" == uri) { + return this.createElement(local); + } else { + return Document.prototype.createElementNS.apply(this,[uri, local]); + } + }, + get anchors(){ + return new HTMLCollection(this.getElementsByTagName('a')); + }, + get applets(){ + return new HTMLCollection(this.getElementsByTagName('applet')); + }, + get documentElement(){ + var html = Document.prototype.__lookupGetter__('documentElement').apply(this,[]); + if( html === null){ + html = this.createElement('html'); + this.appendChild(html); + html.appendChild(this.createElement('head')); + html.appendChild(this.createElement('body')); + } + return html; + }, + //document.head is non-standard + get head(){ + //console.log('get head'); + if (!this.documentElement) { + this.appendChild(this.createElement('html')); + } + var element = this.documentElement, + length = element.childNodes.length, + i; + //check for the presence of the head element in this html doc + for(i=0;i1?matches[1]:""; + }, + set domain(value){ + var i, + domainParts = this.domain.split('.').reverse(), + newDomainParts = value.split('.').reverse(); + if(newDomainParts.length > 1){ + for(i=0;i 0){ + event = doc.createEvent('HTMLEvents'); + event.initEvent( okay ? "load" : "error", false, false ); + node.dispatchEvent( event, false ); + } + }catch(e){ + console.log('error loading html element %s %e', node, e.toString()); + } + } + break; + case 'frame': + case 'iframe': + node.contentWindow = { }; + node.contentDocument = new HTMLDocument(new DOMImplementation(), node.contentWindow); + node.contentWindow.document = node.contentDocument; + try{ + Window; + }catch(e){ + node.contentDocument.addEventListener('DOMContentLoaded', function(){ + event = node.contentDocument.createEvent('HTMLEvents'); + event.initEvent("load", false, false); + node.dispatchEvent( event, false ); + }); + } + try{ + if (node.src && node.src.length > 0){ + //console.log("getting content document for (i)frame from %s", node.src); + Envjs.loadFrame(node, Envjs.uri(node.src)); + event = node.contentDocument.createEvent('HTMLEvents'); + event.initEvent("load", false, false); + node.dispatchEvent( event, false ); + }else{ + //I dont like this being here: + //TODO: better mix-in strategy so the try/catch isnt required + try{ + if(Window){ + Envjs.loadFrame(node); + //console.log('src/html/document.js: triggering frame load'); + event = node.contentDocument.createEvent('HTMLEvents'); + event.initEvent("load", false, false); + node.dispatchEvent( event, false ); + } + }catch(e){} + } + }catch(e){ + console.log('error loading html element %s %e', node, e.toString()); + } + break; + + case 'link': + if (node.href && node.href.length > 0) { + __loadLink__(node, node.href); + } + break; + /* + case 'img': + if (node.src && node.src.length > 0){ + // don't actually load anything, so we're "done" immediately: + event = doc.createEvent('HTMLEvents'); + event.initEvent("load", false, false); + node.dispatchEvent( event, false ); + } + break; + */ + case 'option': + node._updateoptions(); + break; + default: + if(node.getAttribute('onload')){ + console.log('calling attribute onload %s | %s', node.onload, node.tagName); + node.onload(); + } + break; + }//switch on name + default: + break; + }//switch on ns + break; + default: + // console.log('element appended: %s %s', node+'', node.namespaceURI); + }//switch on doc.parsing + return node; + +}); + +Aspect.around({ + target: Node, + method:"removeChild" +}, function(invocation) { + var event, + okay, + node = invocation.proceed(), + doc = node.ownerDocument; + if((node.nodeType !== Node.ELEMENT_NODE)){ + //for now we are only handling element insertions. probably we will need + //to handle text node changes to script tags and changes to src + //attributes + if(node.nodeType !== Node.DOCUMENT_NODE && node.uuid){ + //console.log('removing event listeners, %s', node, node.uuid); + node.removeEventListener('*', null, null); + } + return node; + } + //console.log('appended html element %s %s %s', node.namespaceURI, node.nodeName, node); + + switch(doc.parsing){ + case true: + //handled by parser if included + break; + case false: + switch(node.namespaceURI){ + case null: + //fall through + case "": + //fall through + case "http://www.w3.org/1999/xhtml": + //this is interesting dillema since our event engine is + //storing the registered events in an array accessed + //by the uuid property of the node. unforunately this + //means listeners hang out way after(forever ;)) the node + //has been removed and gone out of scope. + //console.log('removing event listeners, %s', node, node.uuid); + node.removeEventListener('*', null, null); + switch(node.tagName.toLowerCase()){ + case 'frame': + case 'iframe': + try{ + //console.log('removing iframe document'); + try{ + Envjs.unloadFrame(node); + }catch(e){ + console.log('error freeing resources from frame %s', e); + } + node.contentWindow = null; + node.contentDocument = null; + }catch(e){ + console.log('error unloading html element %s %e', node, e.toString()); + } + break; + default: + break; + }//switch on name + default: + break; + }//switch on ns + break; + default: + console.log('element appended: %s %s', node+'', node.namespaceURI); + }//switch on doc.parsing + return node; + +}); + + + +/** + * Named Element Support + * + * + */ + +/* + * + * @returns 'name' if the node has a appropriate name + * null if node does not have a name + */ + +var __isNamedElement__ = function(node) { + if (node.nodeType !== Node.ELEMENT_NODE) { + return null; + } + var tagName = node.tagName.toLowerCase(); + var nodename = null; + + switch (tagName) { + case 'embed': + case 'form': + case 'iframe': + nodename = node.getAttribute('name'); + break; + case 'applet': + nodename = node.id; + break; + case 'object': + // TODO: object needs to be 'fallback free' + nodename = node.id; + break; + case 'img': + nodename = node.id; + if (!nodename || ! node.getAttribute('name')) { + nodename = null; + } + break; + } + return (nodename) ? nodename : null; +}; + + +var __addNamedMap__ = function(target, node) { + var nodename = __isNamedElement__(node); + if (nodename) { + target.__defineGetter__(nodename, function() { + return node; + }); + } +}; + +var __removeNamedMap__ = function(target, node) { + if (!node) { + return; + } + var nodename = __isNamedElement__(node); + if (nodename) { + delete target[nodename]; + } +}; + +/** + * @name HTMLEvents + * @w3c:domlevel 2 + * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html + */ + +var __eval__ = function(script, node){ + if (!script == ""){ + // don't assemble environment if no script... + try{ + eval(script); + }catch(e){ + console.log('error evaluating %s', e); + } + } +}; + +var HTMLEvents= function(){}; +HTMLEvents.prototype = { + onload: function(event){ + __eval__(this.getAttribute('onload')||'', this); + }, + onunload: function(event){ + __eval__(this.getAttribute('onunload')||'', this); + }, + onabort: function(event){ + __eval__(this.getAttribute('onabort')||'', this); + }, + onerror: function(event){ + __eval__(this.getAttribute('onerror')||'', this); + }, + onselect: function(event){ + __eval__(this.getAttribute('onselect')||'', this); + }, + onchange: function(event){ + __eval__(this.getAttribute('onchange')||'', this); + }, + onsubmit: function(event){ + if (__eval__(this.getAttribute('onsubmit')||'', this)) { + this.submit(); + } + }, + onreset: function(event){ + __eval__(this.getAttribute('onreset')||'', this); + }, + onfocus: function(event){ + __eval__(this.getAttribute('onfocus')||'', this); + }, + onblur: function(event){ + __eval__(this.getAttribute('onblur')||'', this); + }, + onresize: function(event){ + __eval__(this.getAttribute('onresize')||'', this); + }, + onscroll: function(event){ + __eval__(this.getAttribute('onscroll')||'', this); + } +}; + +//HTMLDocument, HTMLFramesetElement, HTMLObjectElement +var __load__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("load", false, false); + element.dispatchEvent(event); + return event; +}; + +//HTMLFramesetElement, HTMLBodyElement +var __unload__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("unload", false, false); + element.dispatchEvent(event); + return event; +}; + +//HTMLObjectElement +var __abort__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("abort", true, false); + element.dispatchEvent(event); + return event; +}; + +//HTMLFramesetElement, HTMLObjectElement, HTMLBodyElement +var __error__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("error", true, false); + element.dispatchEvent(event); + return event; +}; + +//HTMLInputElement, HTMLTextAreaElement +var __select__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("select", true, false); + element.dispatchEvent(event); + return event; +}; + +//HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement +var __change__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("change", true, false); + element.dispatchEvent(event); + return event; +}; + +//HtmlFormElement +var __submit__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("submit", true, true); + element.dispatchEvent(event); + return event; +}; + +//HtmlFormElement +var __reset__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("reset", false, false); + element.dispatchEvent(event); + return event; +}; + +//LABEL, INPUT, SELECT, TEXTAREA, and BUTTON +var __focus__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("focus", false, false); + element.dispatchEvent(event); + return event; +}; + +//LABEL, INPUT, SELECT, TEXTAREA, and BUTTON +var __blur__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("blur", false, false); + element.dispatchEvent(event); + return event; +}; + +//Window +var __resize__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("resize", true, false); + element.dispatchEvent(event); + return event; +}; + +//Window +var __scroll__ = function(element){ + var event = new Event('HTMLEvents'); + event.initEvent("scroll", true, false); + element.dispatchEvent(event); + return event; +}; + +/** + * @name KeyboardEvents + * @w3c:domlevel 2 + * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html + */ +var KeyboardEvents= function(){}; +KeyboardEvents.prototype = { + onkeydown: function(event){ + __eval__(this.getAttribute('onkeydown')||'', this); + }, + onkeypress: function(event){ + __eval__(this.getAttribute('onkeypress')||'', this); + }, + onkeyup: function(event){ + __eval__(this.getAttribute('onkeyup')||'', this); + } +}; + + +var __registerKeyboardEventAttrs__ = function(elm){ + if(elm.hasAttribute('onkeydown')){ + elm.addEventListener('keydown', elm.onkeydown, false); + } + if(elm.hasAttribute('onkeypress')){ + elm.addEventListener('keypress', elm.onkeypress, false); + } + if(elm.hasAttribute('onkeyup')){ + elm.addEventListener('keyup', elm.onkeyup, false); + } + return elm; +}; + +//HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement +var __keydown__ = function(element){ + var event = new Event('KeyboardEvents'); + event.initEvent("keydown", false, false); + element.dispatchEvent(event); +}; + +//HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement +var __keypress__ = function(element){ + var event = new Event('KeyboardEvents'); + event.initEvent("keypress", false, false); + element.dispatchEvent(event); +}; + +//HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement +var __keyup__ = function(element){ + var event = new Event('KeyboardEvents'); + event.initEvent("keyup", false, false); + element.dispatchEvent(event); +}; + +/** + * @name MaouseEvents + * @w3c:domlevel 2 + * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html + */ +var MouseEvents= function(){}; +MouseEvents.prototype = { + onclick: function(event){ + __eval__(this.getAttribute('onclick')||'', this); + }, + ondblclick: function(event){ + __eval__(this.getAttribute('ondblclick')||'', this); + }, + onmousedown: function(event){ + __eval__(this.getAttribute('onmousedown')||'', this); + }, + onmousemove: function(event){ + __eval__(this.getAttribute('onmousemove')||'', this); + }, + onmouseout: function(event){ + __eval__(this.getAttribute('onmouseout')||'', this); + }, + onmouseover: function(event){ + __eval__(this.getAttribute('onmouseover')||'', this); + }, + onmouseup: function(event){ + __eval__(this.getAttribute('onmouseup')||'', this); + } +}; + +var __registerMouseEventAttrs__ = function(elm){ + if(elm.hasAttribute('onclick')){ + elm.addEventListener('click', elm.onclick, false); + } + if(elm.hasAttribute('ondblclick')){ + elm.addEventListener('dblclick', elm.ondblclick, false); + } + if(elm.hasAttribute('onmousedown')){ + elm.addEventListener('mousedown', elm.onmousedown, false); + } + if(elm.hasAttribute('onmousemove')){ + elm.addEventListener('mousemove', elm.onmousemove, false); + } + if(elm.hasAttribute('onmouseout')){ + elm.addEventListener('mouseout', elm.onmouseout, false); + } + if(elm.hasAttribute('onmouseover')){ + elm.addEventListener('mouseover', elm.onmouseover, false); + } + if(elm.hasAttribute('onmouseup')){ + elm.addEventListener('mouseup', elm.onmouseup, false); + } + return elm; +}; + + +var __click__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("click", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; +var __mousedown__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("mousedown", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; +var __mouseup__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("mouseup", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; +var __mouseover__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("mouseover", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; +var __mousemove__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("mousemove", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; +var __mouseout__ = function(element){ + var event = new Event('MouseEvents'); + event.initEvent("mouseout", true, true, null, 0, + 0, 0, 0, 0, false, false, false, + false, null, null); + element.dispatchEvent(event); +}; + +/** + * HTMLElement - DOM Level 2 + */ + + +/* Hack for http://www.prototypejs.org/ + * + * Prototype 1.6 (the library) creates a new global Element, which causes + * envjs to use the wrong Element. + * + * http://envjs.lighthouseapp.com/projects/21590/tickets/108-prototypejs-wont-load-due-it-clobbering-element + * + * Options: + * (1) Rename the dom/element to something else + * rejected: been done before. people want Element. + * (2) merge dom+html and not export Element to global namespace + * (meaning we would use a local var Element in a closure, so prototype + * can do what ever it wants) + * rejected: want dom and html separate + * (3) use global namespace (put everything under Envjs = {}) + * rejected: massive change + * (4) use commonjs modules (similar to (3) in spirit) + * rejected: massive change + * + * or + * + * (5) take a reference to Element during initial loading ("compile + * time"), and use the reference instead of "Element". That's + * what the next line does. We use __DOMElement__ if we need to + * reference the parent class. Only this file explcity uses + * Element so this should work, and is the most minimal change I + * could think of with no external API changes. + * + */ +var __DOMElement__ = Element; + +HTMLElement = function(ownerDocument) { + __DOMElement__.apply(this, arguments); +}; + +HTMLElement.prototype = new Element(); +__extend__(HTMLElement.prototype, HTMLEvents.prototype); +__extend__(HTMLElement.prototype, { + get className() { + return this.getAttribute("class")||''; + }, + set className(value) { + return this.setAttribute("class",__trim__(value)); + }, + get dir() { + return this.getAttribute("dir")||"ltr"; + }, + set dir(val) { + return this.setAttribute("dir",val); + }, + get id(){ + return this.getAttribute('id'); + }, + set id(id){ + this.setAttribute('id', id); + }, + get innerHTML(){ + var ret = "", + i; + + // create string containing the concatenation of the string + // values of each child + for (i=0; i < this.childNodes.length; i++) { + if(this.childNodes[i]){ + if(this.childNodes[i].nodeType === Node.ELEMENT_NODE){ + ret += this.childNodes[i].xhtml; + } else if (this.childNodes[i].nodeType === Node.TEXT_NODE && i>0 && + this.childNodes[i-1].nodeType === Node.TEXT_NODE){ + //add a single space between adjacent text nodes + ret += " "+this.childNodes[i].xml; + }else{ + ret += this.childNodes[i].xml; + } + } + } + return ret; + }, + get lang() { + return this.getAttribute("lang"); + }, + set lang(val) { + return this.setAttribute("lang",val); + }, + get offsetHeight(){ + return Number((this.style.height || '').replace("px","")); + }, + get offsetWidth(){ + return Number((this.style.width || '').replace("px","")); + }, + offsetLeft: 0, + offsetRight: 0, + get offsetParent(){ + /* TODO */ + return; + }, + set offsetParent(element){ + /* TODO */ + return; + }, + scrollHeight: 0, + scrollWidth: 0, + scrollLeft: 0, + scrollRight: 0, + get style(){ + return this.getAttribute('style')||''; + }, + get title() { + return this.getAttribute("title"); + }, + set title(value) { + return this.setAttribute("title", value); + }, + get tabIndex(){ + var tabindex = this.getAttribute('tabindex'); + if(tabindex!==null){ + return Number(tabindex); + } else { + return 0; + } + }, + set tabIndex(value){ + if (value === undefined || value === null) { + value = 0; + } + this.setAttribute('tabindex',Number(value)); + }, + get outerHTML(){ + //Not in the specs but I'll leave it here for now. + return this.xhtml; + }, + scrollIntoView: function(){ + /*TODO*/ + return; + }, + toString: function(){ + return '[object HTMLElement]'; + }, + get xhtml() { + // HTMLDocument.xhtml is non-standard + // This is exactly like Document.xml except the tagName has to be + // lower cased. I dont like to duplicate this but its really not + // a simple work around between xml and html serialization via + // XMLSerializer (which uppercases html tags) and innerHTML (which + // lowercases tags) + + var ret = "", + ns = "", + name = (this.tagName+"").toLowerCase(), + attrs, + attrstring = "", + i; + + // serialize namespace declarations + if (this.namespaceURI){ + if((this === this.ownerDocument.documentElement) || + (!this.parentNode) || + (this.parentNode && + (this.parentNode.namespaceURI !== this.namespaceURI))) { + ns = ' xmlns' + (this.prefix ? (':' + this.prefix) : '') + + '="' + this.namespaceURI + '"'; + } + } + + // serialize Attribute declarations + attrs = this.attributes; + for(i=0;i< attrs.length;i++){ + attrstring += " "+attrs[i].name+'="'+attrs[i].xml+'"'; + } + + if(this.hasChildNodes()){ + // serialize this Element + ret += "<" + name + ns + attrstring +">"; + for(i=0;i< this.childNodes.length;i++){ + ret += this.childNodes[i].xhtml ? + this.childNodes[i].xhtml : + this.childNodes[i].xml; + } + ret += ""; + }else{ + switch(name){ + case 'script': + ret += "<" + name + ns + attrstring +">"; + break; + default: + ret += "<" + name + ns + attrstring +"/>"; + } + } + + return ret; + }, + + /** + * setAttribute use a dispatch table that other tags can set to + * "listen" to various values being set. The dispatch table + * and registration functions are at the end of the file. + * + */ + + setAttribute: function(name, value) { + var result = __DOMElement__.prototype.setAttribute.apply(this, arguments); + __addNamedMap__(this.ownerDocument, this); + var tagname = this.tagName; + var callback = HTMLElement.getAttributeCallback('set', tagname, name); + if (callback) { + callback(this, value); + } + }, + setAttributeNS: function(namespaceURI, name, value) { + var result = __DOMElement__.prototype.setAttributeNS.apply(this, arguments); + __addNamedMap__(this.ownerDocument, this); + var tagname = this.tagName; + var callback = HTMLElement.getAttributeCallback('set', tagname, name); + if (callback) { + callback(this, value); + } + + return result; + }, + setAttributeNode: function(newnode) { + var result = __DOMElement__.prototype.setAttributeNode.apply(this, arguments); + __addNamedMap__(this.ownerDocument, this); + var tagname = this.tagName; + var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); + if (callback) { + callback(this, node.value); + } + return result; + }, + setAttributeNodeNS: function(newnode) { + var result = __DOMElement__.prototype.setAttributeNodeNS.apply(this, arguments); + __addNamedMap__(this.ownerDocument, this); + var tagname = this.tagName; + var callback = HTMLElement.getAttributeCallback('set', tagname, newnode.name); + if (callback) { + callback(this, node.value); + } + return result; + }, + removeAttribute: function(name) { + __removeNamedMap__(this.ownerDocument, this); + return __DOMElement__.prototype.removeAttribute.apply(this, arguments); + }, + removeAttributeNS: function(namespace, localname) { + __removeNamedMap__(this.ownerDocument, this); + return __DOMElement__.prototype.removeAttributeNS.apply(this, arguments); + }, + removeAttributeNode: function(name) { + __removeNamedMap__(this.ownerDocument, this); + return __DOMElement__.prototype.removeAttribute.apply(this, arguments); + }, + removeChild: function(oldChild) { + __removeNamedMap__(this.ownerDocument, oldChild); + return __DOMElement__.prototype.removeChild.apply(this, arguments); + }, + importNode: function(othernode, deep) { + var newnode = __DOMElement__.prototype.importNode.apply(this, arguments); + __addNamedMap__(this.ownerDocument, newnode); + return newnode; + }, + + // not actually sure if this is needed or not + replaceNode: function(newchild, oldchild) { + var newnode = __DOMElement__.prototype.replaceNode.apply(this, arguments); + __removeNamedMap__(this.ownerDocument, oldchild); + __addNamedMap__(this.ownerDocument, newnode); + return newnode; + } +}); + + +HTMLElement.attributeCallbacks = {}; +HTMLElement.registerSetAttribute = function(tag, attrib, callbackfn) { + HTMLElement.attributeCallbacks[tag + ':set:' + attrib] = callbackfn; +}; +HTMLElement.registerRemoveAttribute = function(tag, attrib, callbackfn) { + HTMLElement.attributeCallbacks[tag + ':remove:' + attrib] = callbackfn; +}; + +/** + * This is really only useful internally + * + */ +HTMLElement.getAttributeCallback = function(type, tag, attrib) { + return HTMLElement.attributeCallbacks[tag + ':' + type + ':' + attrib] || null; +}; +/* + * HTMLCollection + * + * HTML5 -- 2.7.2.1 HTMLCollection + * http://dev.w3.org/html5/spec/Overview.html#htmlcollection + * http://dev.w3.org/html5/spec/Overview.html#collections + */ +HTMLCollection = function(nodelist, type) { + + __setArray__(this, []); + var n; + for (var i=0; i= 0) && (idx < this.length)) ? this[idx] : null; + }, + + namedItem: function (name) { + return this[name] || null; + }, + + toString: function() { + return '[object HTMLCollection]'; + } +}; +/* + * a set of convenience classes to centralize implementation of + * properties and methods across multiple in-form elements + * + * the hierarchy of related HTML elements and their members is as follows: + * + * Condensed Version + * + * HTMLInputCommon + * * legent (no value attr) + * * fieldset (no value attr) + * * label (no value attr) + * * option (custom value) + * HTMLTypeValueInputs (extends InputCommon) + * * select (custom value) + * * button (just sets value) + * HTMLInputAreaCommon (extends TypeValueIput) + * * input (custom) + * * textarea (just sets value) + * + * ----------------------- + * HTMLInputCommon: common to all elements + * .form + * + * + * [common plus:] + * .align + * + *
+ * [identical to "legend" plus:] + * .margin + * + * + * **** + * + *