XML DOM, XPath and XMLHttpRequest support for O::U::SpiderMonkey
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 14 Jan 2006 15:27:12 +0000 (15:27 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 14 Jan 2006 15:27:12 +0000 (15:27 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@2713 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/javascript/backend/libs/jsDOM.js [new file with mode: 0644]
Open-ILS/src/javascript/backend/libs/jsOO.js [new file with mode: 0644]
Open-ILS/src/javascript/backend/libs/xmlhttprequest.js [new file with mode: 0644]
Open-ILS/src/javascript/backend/libs/xpath.js [new file with mode: 0644]
Open-ILS/src/javascript/backend/test/xmlhttprequest-test.js [new file with mode: 0644]
Open-ILS/src/perlmods/OpenILS/Utils/SpiderMonkey.pm

diff --git a/Open-ILS/src/javascript/backend/libs/jsDOM.js b/Open-ILS/src/javascript/backend/libs/jsDOM.js
new file mode 100644 (file)
index 0000000..1738b90
--- /dev/null
@@ -0,0 +1,638 @@
+load_lib('jsOO.js')
+load_lib('xpath.js')
+
+function DOMException  (c) { this.code = c }
+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;
+DOMException.INVALID_STATE_ERR = 11;
+DOMException.SYNTAX_ERR = 12;
+DOMException.INVALID_MODIFICATION_ERR = 13;
+DOMException.NAMESPACE_ERR = 14;
+DOMException.INVALID_ACCESS_ERR = 15;
+
+function DOMImplementation () {
+       this._features = {};
+       this._features['CORE'] = {};
+       this._features['CORE']['any'] = true;
+       this._features['CORE']['1.0'] = true;
+       this._features['CORE']['2.0'] = true;
+       this._features['XML'] = {};
+       this._features['XML']['any'] = true;
+       this._features['XML']['1.0'] = true;
+       this._features['XML']['2.0'] = true;
+}
+
+DOMImplementation.method('hasFeature', function (f, v) {
+       if (!v) v = 'any';
+       if (this._features[f] && this._features[f][v])
+               return this._features[f][v];
+       return false;
+});
+DOMImplementation.method('createDocumentType', function (n, p, s) {
+       return null;
+});
+DOMImplementation.method('createDocument', function (ns,qn,dt) {
+       var d = new Document(dt);
+       d.documentElement = d.createElement(qn);
+       d.appendChild(d.documentElement);
+       if (ns)
+               d.documentElement.namespaceURI = ns;
+
+       installDOM3XPathSupport(d,new XPathParser());
+       return d;
+});
+
+var __XMLDOC = {};
+var __XMLDOCid = 0;
+DOMImplementation.parseString = function (xml) {
+       __XMLDOC['id' + __XMLDOCid] = {};
+       _OILS_FUNC_xml_parse_string(xml, '__XMLDOC.id' + __XMLDOCid);
+       var x = __XMLDOC['id' + __XMLDOCid];
+       __XMLDOCid++;
+       return x;
+}
+
+
+// NodeList interface
+function NodeList () {
+       this.length = 0;
+       //perl_print(' --  NodeList constructor');
+}
+
+NodeList.method('item', function (idx) {
+       return this[idx];
+});
+NodeList.method('push', function (node) {
+       var idx = this.length;
+       this[idx] = node;
+       this.length++;
+       return this[idx];
+});
+
+
+
+
+// NamedNodeMap interface
+function NamedNodeMap () {
+       this.length = 0;
+       this._nodes = {};
+       this._ns_nodes = {};
+       //perl_print(' --  NamedNodeMap constructor');
+}
+
+NamedNodeMap.method('item', function (idx) {
+       return this.getNamedItem(idx);
+});
+
+NamedNodeMap.method('removeNamedItemNS', function (ns, name) {
+       var x = this._ns_nodes[ns][name];
+       for (var i in this._nodes) {
+               if (this._nodes[i] === x) {
+                       this._nodes[i] = null;
+               }
+       }
+       this._ns_nodes[ns][name] = null;
+       return x;
+});
+
+NamedNodeMap.method('removeNamedItem', function (name) {
+       var x = this._nodes[name];
+       for (var i in this._nodes) {
+               if (this._nodes[i] === x) {
+                       this._nodes[i] = null;
+               }
+       }
+       return x;
+});
+
+NamedNodeMap.method('getNamedItem', function (name) {
+       return this._nodes[name];
+});
+
+NamedNodeMap.method('getNamedItemNS', function (ns,name) {
+       return this._ns_nodes[ns][name];
+});
+
+NamedNodeMap.method('setNamedItem', function (node) {
+       if (node.nodeName == 'length') return null;
+       this[node.nodeName] = node.value;
+       this[this.length] = node.value;
+       this._nodes[node.nodeName] = node;
+       this._nodes[this.length] = node;
+       this.length++;
+});
+
+NamedNodeMap.method('setNamedItemNS', function (node) {
+       if (node.nodeName == 'length') return null;
+       this[this.length] = node.value;
+       if (!this._ns_nodes[node.namespaceURI]) this._ns_nodes[node.namespaceURI] = {};
+       this._ns_nodes[node.namespaceURI][node.nodeName] = node;
+       this._nodes[this.length] = node;
+       this.length++;
+});
+
+// Node interface
+function Node (name) {
+       this.nodeName = name;
+       this.nodeValue = null;
+       this.nodeType = null;
+       this.parentNode = null;
+       this.childNodes = new NodeList();
+       this.firstChild = null;
+       this.lastChild = null;
+       this.previousSibling = null;
+       this.nextSibling = null;
+       this.attributes = new NamedNodeMap();
+       this.ownerDocument = null;
+       this.namespaceURI = null;
+
+       if (name) {
+               var p = name.indexOf(':');
+               if (p != -1) {
+                       this.prefix = name.substring(0,p);
+                       this.localName = name.substring(p + 1);
+               } else {
+                       this.localName = name;
+               }
+       }
+
+       //perl_print(' --  Node constructor');
+}
+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_DECL = 18;
+
+Node.method('_childIndex', function (node) {
+       for (var i = 0; i < this.childNodes.length; i++)
+               if (this.childNodes[i] === node) return i;
+});
+Node.method('insertBefore', function (node, target) {
+
+       if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+               for (var i = 0; i < node.childNodes.length; i++)
+                       this.insertBefore(node.childNodes.item(i));
+               return node;
+       }
+
+       node.parentNode = this;
+       node.ownerDocument = this.ownerDocument;
+       var i = this._childIndex(target);
+
+       for (var j = this.childNodes.length; j > i; j--)
+               this.childNodes[j] = this.childNodes[j - 1];
+
+       this.childNodes[i] = node;
+
+       if (i == 0) {
+               this.firstChild = node;
+       } else {
+               this.childNodes[i - 1].nextSibling = node;
+               node.previousSibling = this.childNodes[i + 1];
+       }
+
+       this.childNodes[i + 1].previousSibling = node;
+       node.nextSibling = this.childNodes[i + 1];
+       
+       node._index = this.ownerDocument._nodes.length;
+       this.ownerDocument._nodes[this.ownerDocument._nodes.length] = node;
+
+       this.childNodes.length++;
+       return node;
+});
+
+Node.method('removeChild', function (node) {
+       node.parentNode = this;
+       node.ownerDocument = this.ownerDocument;
+       var i = this._childIndex(node);
+
+       if (node === this.firstChild) {
+               this.firstChild = node.nextSibling;
+       } else {
+               node.previousSibling.nextSibling = node.nextSibling;
+               for (var j = i; j < this.childNodes.length; j++)
+                       this.childNodes[j - 1] = this.childNodes[j];
+       }
+
+       if (node === this.lastChild) {
+               this.lastChild = node.previousSibling;
+       } else {
+               node.nextSibling.previousSibling = node.previousSibling;
+       }
+
+       this.ownerDocument._nodes[node._index] = null;
+       
+       this.childNodes.length--;
+       return node;
+});
+
+Node.method('appendChild', function (node) {
+
+       if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+               for (var i = 0; i < node.childNodes.length; i++)
+                       this.appendChild(node.childNodes.item(i));
+               return node;
+       }
+
+       node.parentNode = this;
+       this.childNodes.push( node );
+       this.lastChild = node;
+       if (this.childNodes.length == 1) {
+               this.firstChild = node;
+       } else {
+               this.lastChild.previousSibling = this.childNodes[this.childNodes.length - 2]
+               this.lastChild.previousSibling.nextSibling = this.lastChild;
+       }
+
+       return node;
+});
+
+Node.method('hasChildNodes', function () {
+       if (this.childNodes.length) return true;
+       return false;
+});
+
+Node.method('hasAttributes', function () {
+       if (this.attributes.length) return true;
+       return false;
+});
+
+Node.method('cloneNode', function (deep) {
+       //perl_print(this.constructor);
+       var newNode = new this.constructor( this.nodeName );
+       newNode.ownerDocument = this.ownerDocument;
+       newNode.namespaceURI = this.namespaceURI;
+       newNode.prefix = this.prefix;
+
+       if (deep) {
+               for (var i = 0; i < this.childNodes.length; i++)
+                       newNode.appendChild( this.childNodes.item(i).cloneNode(deep) );
+       }
+
+       for (var i = 0; i < this.attributes.length; i++)
+               newNode.attributes.setNamedItem( this.attributes.item(i) );
+
+       return newNode;
+});
+
+function DocumentType (n,p,s) {
+       this.uber('constructor');
+       this.constructor = DocumentType;
+       this.nodeType = Node.DOCUMENT_TYPE_NODE;
+       this.name = n;
+       this.entities = new NamedNodeMap();
+       this.notations = new NamedNodeMap();
+       this.publicId = p;
+       this.systemId = s;
+       this.internalSubset = null;
+}
+DocumentType.inherits(Node);
+
+function Notation (p, s) {
+       this.uber('constructor');
+       this.constructor = Notation;
+       this.nodeType = Node.NOTATION_NODE;
+       this.publicId = p;
+       this.systemId = s;
+}
+Notation.inherits(Node);
+
+function ProcessingInstruction (target, data) {
+       this.uber('constructor');
+       this.constructor = ProcessingInstruction;
+       this.nodeType = Node.PROCESSING_INSTRUCTION_NODE;
+       this.target = target;
+       this.data = data;
+}
+ProcessingInstruction.inherits(Node);
+
+function Entity (p, s, n) {
+       this.uber('constructor');
+       this.constructor = Entity;
+       this.nodeType = Node.ENTITY_NODE;
+       this.publicId = p;
+       this.systemId = s;
+       this.notationName = n;
+}
+Entity.inherits(Node);
+
+function EntityReference () {
+       this.uber('constructor');
+       this.constructor = EntityReference;
+       this.nodeType = Node.ENTITY_REFERENCE_NODE;
+}
+EntityReference.inherits(Node);
+
+// Document interface
+function Document (dt) {
+       this.uber('constructor');
+       this.constructor = Document;
+       this.nodeType = Node.DOCUMENT_NODE;
+       this.doctype = dt;
+       this.documentElement = null;
+       this.implementation = new DOMImplementation();
+       this._nodes = [];
+       //perl_print(' --  Document constructor');
+}
+
+Document.inherits(Node);
+Document.method('createAttribute', function (tagName) {
+       var node = new Attr(tagName);
+       node.ownerDocument = this;
+       return node;
+});
+
+Document.method('createAttributeNS', function (ns,tagName) {
+       var node = this.createAttribute(tagName);
+       node.namespaceURI = ns;
+       return node;
+});
+
+Document.method('createElement', function (tagName) {
+       var node = new Element(tagName);
+       node.ownerDocument = this;
+       node._index = this._nodes.length;
+       this._nodes[this._nodes.length] = node;
+       return node;
+});
+
+Document.method('createElementNS', function (ns,tagName) {
+       var node = this.createElement(tagName);
+       node.namespaceURI = ns;
+       return node;
+});
+
+Document.method('importNode', function (node, deep) {
+       var tmp = node.clone(deep);
+       tmp.ownerDocument = this;
+       var newNode = tmp.cloneNode(deep);
+       return newNode;
+});
+
+Document.method('createDocumentFragment', function () {
+       var x = new Node();
+       x.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
+       x.ownerDocument = this;
+});
+
+Document.method('createTextNode', function (content) {
+       var node = new Text(content);
+       node.ownerDocument = this;
+       return node;
+});
+
+Document.method('createComment', function (content) {
+       var node = new Comment(content);
+       node.ownerDocument = this;
+       return node;
+});
+
+Document.method('createCDATASection', function (content) {
+       var node = new CDATASection(content);
+       node.ownerDocument = this;
+       return node;
+});
+
+Document.method('getElementById', function (id) {
+       for (var i in this._nodes) {
+               //perl_print(' id = ' + this._nodes[i].attributes.id);
+               if (this._nodes[i] && this._nodes[i].attributes.id == id)
+                       return this._nodes[i];
+       }
+       return null;
+});
+
+Document.method('getElementsByTagName', function (tname) {
+       var list = new NodeList();
+       this.documentElement.getElementsByTagName(tname, list);
+       return list;
+});
+
+Document.method('getElementsByTagNameNS', function (ns, tname) {
+       var list = new NodeList();
+       this.documentElement.getElementsByTagNameNS(ns, tname, list);
+       return list;
+});
+
+// Attr interface
+function Attr ( name, value ) {
+       this.uber('constructor',name);
+       this.constructor = Attr;
+       this.nodeType = Node.ATTRIBUTE_NODE;
+       this.name = name;
+       this.value = this.nodeValue = value;
+       this.specified = (this.value ? true : false);
+       this.ownerElement = null;
+       //perl_print(' --  Attr constructor');
+}
+Attr.inherits(Node);
+
+
+// Element interface
+function Element ( name ) {
+       this.uber('constructor',name);
+       this.constructor = Element;
+       this.nodeType = Node.ELEMENT_NODE;
+       this.tagName = name;
+       //perl_print(' --  Element constructor')
+}
+Element.inherits(Node);
+
+Element.method('getAttribute', function (aname) {
+       var x = this.attributes.getNamedItem(aname);
+       if (x) return x.value;
+       return null;
+});
+
+Element.method('setAttribute', function (aname,aval) {
+       var attr = new Attr(aname, aval);
+       attr.ownerElement = this;
+       return this.attributes.setNamedItem(attr);
+});
+
+Element.method('removeAttribute', function (aname) {
+       this.attributes.removeNamedItem(aname);
+       return null;
+});
+
+Element.method('getAttributeNode', function (aname) {
+       return this.attributes.getNamedItem(aname);
+});
+
+Element.method('setAttributeNode', function (attr) {
+       attr.ownerElement = this;
+       attr.namespaceURI = (attr.namespaceURI ? attr.namespaceURI : this.namespaceURI);
+       return this.attributes.setNamedItem(attr);
+});
+
+Element.method('removeAttributeNode', function (attr) {
+       if (attr.namespaceURI) {
+               return this.attributes.removeNamedItemNS(attr.namespaceURI, attr.name);
+       } else {
+               return this.attributes.removeNamedItem(attr.name);
+       }
+});
+
+Element.method('getAttributeNS', function (ns,aname) {
+       var x = this.attributes.getNamedItemNS(ns,aname);
+       if (x) return x.value;
+       return null;
+});
+
+Element.method('setAttributeNS', function (ns,aname,aval) {
+       var attr = new Attr(aname, aval);
+       attr.ownerElement = this;
+       attr.namespaceURI = ns;
+       return this.attributes.setNamedItem(ns,attr);
+});
+
+Element.method('removeAttributeNS', function (ns,aname) {
+       this.attributes.removeNamedItemNS(ns,aname);
+       return null;
+});
+
+Element.method('getAttributeNodeNS', function (ns, aname) {
+       return this.attributes.getNamedItemNS(ns,aname);
+});
+
+Element.method('setAttributeNodeNS', function (attr) {
+       attr.ownerElement = this;
+       attr.namespaceURI = (attr.namespaceURI ? attr.namespaceURI : this.namespaceURI);
+       return this.attributes.setNamedItemNS(attr);
+});
+
+Element.method('hasAttribute', function (name) {
+       return ( this.getAttribute(name) ? true : false );
+});
+
+Element.method('hasAttributeNS', function (ns, name) {
+       return ( this.getAttributeNS(ns, name) ? true : false );
+});
+
+Element.method('getElementsByTagName', function (tname, list) {
+       if (!list) list = new NodeList();
+       if (this.tagName == tname) list.push(this);
+       //perl_print(' --  ' + this.tagName + ' :: ' + this.childNodes.length);
+       for (var i = 0;  i < this.childNodes.length; i++) {
+               if (this.childNodes.item(i).nodeType == 1)
+                       this.childNodes.item(i).getElementsByTagName(tname, list);
+       }
+       return list;
+});
+
+Element.method('getElementsByTagNameNS', function (ns, tname, list) {
+       if (!list) list = new NodeList();
+       if (this.localName == tname && this.namespaceURI == ns) list.push(this);
+       //perl_print(' --  {' + this.namespaceURI + '}:' + this.localName + ' :: ' + this.childNodes.length);
+       for (var i = 0;  i < this.childNodes.length; i++) {
+               if (this.childNodes.item(i).nodeType == 1)
+                       this.childNodes.item(i).getElementsByTagNameNS(ns, tname, list);
+       }
+       return list;
+});
+
+
+// CharacterData interface
+function CharacterData ( name, content ) {
+       this.uber('constructor', name);
+       this.constructor = CharacterData;
+       this.setData(content);
+       //perl_print(' --  CharacterData constructor');
+       //perl_print(' --  CharacterData length == ' + this.length + ' :: ' + this.data);
+}
+CharacterData.inherits(Node);
+
+CharacterData.method('setData', function (content) {
+       this.data = this.nodeValue = content;
+       if (this.data)
+               this.length = this.data.length;
+});
+CharacterData.method('substringData', function (offset,count) {
+       this.data.substring(offset,count);
+});
+CharacterData.method('appendData', function (txt) {
+       this.data = this.nodeValue = this.data + txt;
+       this.length = this.data.length;
+});
+CharacterData.method('insertData', function (offset,txt) {
+       var b = this.data.substring(0,offset);
+       var a = this.data.substring(offset);
+       this.data = this.nodeValue = b + txt + a;
+       this.length = this.data.length;
+});
+CharacterData.method('deleteData', function (offset,count) {
+       var b = this.data.substring(0,offset);
+       var a = this.data.substring(offset + count);
+       this.data = this.nodeValue = b + a;
+       this.length = this.data.length;
+});
+CharacterData.method('replaceData', function (offset,count,txt) {
+       var b = this.data.substring(0,offset);
+       var a = this.data.substring(offset + count);
+       this.data = this.nodeValue = b + txt +  a;
+       this.length = this.data.length;
+});
+
+
+
+// Text interface
+function Text ( content ) {
+       this.superclass.constructor.call(this, '#text', content);
+       this.constructor = Text;
+       this.nodeType = Node.TEXT_NODE;
+       //perl_print(' --  Text constructor :: ' + this.data);
+}
+Text.inherits(CharacterData);
+
+
+// CDATASection interface
+function CDATASection ( content ) {
+       this.uber('constructor', '#cdata', content);
+       this.constructor = CDATASection;
+       this.nodeType = Node.COMMENT_NODE;
+       //perl_print(' --  Comment constructor');
+}
+CDATASection.inherits(Text);
+
+// Comment interface
+function Comment ( content ) {
+       this.uber('constructor', '#comment', content);
+       this.constructor = Comment;
+       this.nodeType = Node.COMMENT_NODE;
+       //perl_print(' --  Comment constructor');
+}
+Comment.inherits(Text);
+
+
+
+//////////////////////// XPath stuff /////////////////////////
+
+function XPathNamespaceResolver (data) {
+       this.data = data;
+}
+XPathNamespaceResolver.inherits(NamespaceResolver);
+
+XPathNamespaceResolver.method('lookupNamespaceURI', function (prefix) {
+       return this.data[prefix];
+});
+XPathNamespaceResolver.method('setNamespaceURI', function (prefix, uri) {
+       this.data[prefix] = uri;
+});
+
diff --git a/Open-ILS/src/javascript/backend/libs/jsOO.js b/Open-ILS/src/javascript/backend/libs/jsOO.js
new file mode 100644 (file)
index 0000000..d40321c
--- /dev/null
@@ -0,0 +1,46 @@
+Function.prototype.method = function (name, func) {
+    this.prototype[name] = func;
+    return this;
+}
+
+Function.method('inherits', function (parent) {
+    var d = 0, p = (this.prototype = new parent());
+    this.constructor = this;
+    this.prototype.superclass = parent;
+    this.method('uber', function uber(name) {
+        var f, r, t = d, v = parent.prototype;
+        if (t) {
+            while (t) {
+                v = v.constructor.prototype;
+                t -= 1;
+            }
+            f = v[name];
+        } else {
+            f = p[name];
+            if (f == this[name]) {
+                f = v[name];
+            }
+        }
+        d += 1;
+        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
+        d -= 1;
+        return r;
+    });
+    return this;
+});
+
+
+instance_of = function(o, c) {
+       while (o != null) {
+               if (o.constructor === c) {
+                       return true;
+               }
+               if (o === Object) {
+                       return false;
+               }
+               o = o.superclass;
+       }
+       return false;
+};
+
+
diff --git a/Open-ILS/src/javascript/backend/libs/xmlhttprequest.js b/Open-ILS/src/javascript/backend/libs/xmlhttprequest.js
new file mode 100644 (file)
index 0000000..3a16463
--- /dev/null
@@ -0,0 +1,45 @@
+load_lib('jsOO.js');
+load_lib('jsDOM.js');
+
+var __xmlhttpreqid = 1;
+var __xmlhttpreq_hash = {};
+
+function XMLHttpRequest () {
+        // Cache this for later ...
+        this._hash_id = __xmlhttpreqid;
+        __xmlhttpreqid++;
+}
+
+XMLHttpRequest.method('open',function (method, url, async) {
+        this._method = method;
+        this._url = url;
+        this._async = (async ? 1 : 0);
+});
+
+XMLHttpRequest.method('setRequestHeader', function (header, header_value) {
+        if (!this._headers) this._headers = {}
+        this._headers[header] = header_value;
+});
+
+XMLHttpRequest.method('send', function (data) {
+
+        var headerlist = '';
+        for (var i in this._headers) {
+                headerlist = headerlist + '\n' + i + '|' + this._headers[i];
+        }
+
+        __xmlhttpreq_hash['id' + this._hash_id] = {};
+
+        _OILS_FUNC_xmlhttprequest_send(this._hash_id,this._method,this._url,this._async,headerlist,data);
+
+        this.responseText = __xmlhttpreq_hash['id' + this._hash_id].responseText;
+        this.readyState = __xmlhttpreq_hash['id' + this._hash_id].readyState;
+        this.status = __xmlhttpreq_hash['id' + this._hash_id].status;
+        this.statusText = __xmlhttpreq_hash['id' + this._hash_id].statusText;
+        this.responseXML = DOMImplementation.parseString(this.responseText);
+
+        if (this._async)
+                this.onreadystatechange();
+});
+
+
diff --git a/Open-ILS/src/javascript/backend/libs/xpath.js b/Open-ILS/src/javascript/backend/libs/xpath.js
new file mode 100644 (file)
index 0000000..a722b38
--- /dev/null
@@ -0,0 +1,4277 @@
+/*
+ * xpath.js
+ *
+ * An XPath 1.0 library for JavaScript.
+ *
+ * Cameron McCormack <cam (at) mcc.id.au>
+ *
+ * This work is licensed under the Creative Commons Attribution-ShareAlike
+ * License. To view a copy of this license, visit
+ * 
+ *   http://creativecommons.org/licenses/by-sa/2.0/
+ *
+ * or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford,
+ * California 94305, USA.
+ *
+ * Revision 19: November 29, 2005
+ *   Nodesets now store their nodes in a height balanced tree, increasing
+ *   performance for the common case of selecting nodes in document order,
+ *   thanks to S├ębastien Cramatte <contact (at) zeninteractif.com>.
+ *   AVL tree code adapted from Raimund Neumann <rnova (at) gmx.net>.
+ *
+ * Revision 18: October 27, 2005
+ *   DOM 3 XPath support.  Caveats:
+ *     - namespace prefixes aren't resolved in XPathEvaluator.createExpression,
+ *       but in XPathExpression.evaluate.
+ *     - XPathResult.invalidIteratorState is not implemented.
+ *
+ * Revision 17: October 25, 2005
+ *   Some core XPath function fixes and a patch to avoid crashing certain
+ *   versions of MSXML in PathExpr.prototype.getOwnerElement, thanks to
+ *   S├ębastien Cramatte <contact (at) zeninteractif.com>.
+ *
+ * Revision 16: September 22, 2005
+ *   Workarounds for some IE 5.5 deficiencies.
+ *   Fixed problem with prefix node tests on attribute nodes.
+ *
+ * Revision 15: May 21, 2005
+ *   Fixed problem with QName node tests on elements with an xmlns="...".
+ *
+ * Revision 14: May 19, 2005
+ *   Fixed QName node tests on attribute node regression.
+ *
+ * Revision 13: May 3, 2005
+ *   Node tests are case insensitive now if working in an HTML DOM.
+ *
+ * Revision 12: April 26, 2005
+ *   Updated licence.  Slight code changes to enable use of Dean
+ *   Edwards' script compression, http://dean.edwards.name/packer/ .
+ *
+ * Revision 11: April 23, 2005
+ *   Fixed bug with 'and' and 'or' operators, fix thanks to
+ *   Sandy McArthur <sandy (at) mcarthur.org>.
+ *
+ * Revision 10: April 15, 2005
+ *   Added support for a virtual root node, supposedly helpful for
+ *   implementing XForms.  Fixed problem with QName node tests and
+ *   the parent axis.
+ *
+ * Revision 9: March 17, 2005
+ *   Namespace resolver tweaked so using the document node as the context
+ *   for namespace lookups is equivalent to using the document element.
+ *
+ * Revision 8: February 13, 2005
+ *   Handle implicit declaration of 'xmlns' namespace prefix.
+ *   Fixed bug when comparing nodesets.
+ *   Instance data can now be associated with a FunctionResolver, and
+ *     workaround for MSXML not supporting 'localName' and 'getElementById',
+ *     thanks to Grant Gongaware.
+ *   Fix a few problems when the context node is the root node.
+ *   
+ * Revision 7: February 11, 2005
+ *   Default namespace resolver fix from Grant Gongaware
+ *   <grant (at) gongaware.com>.
+ *
+ * Revision 6: February 10, 2005
+ *   Fixed bug in 'number' function.
+ *
+ * Revision 5: February 9, 2005
+ *   Fixed bug where text nodes not getting converted to string values.
+ *
+ * Revision 4: January 21, 2005
+ *   Bug in 'name' function, fix thanks to Bill Edney.
+ *   Fixed incorrect processing of namespace nodes.
+ *   Fixed NamespaceResolver to resolve 'xml' namespace.
+ *   Implemented union '|' operator.
+ *
+ * Revision 3: January 14, 2005
+ *   Fixed bug with nodeset comparisons, bug lexing < and >.
+ *
+ * Revision 2: October 26, 2004
+ *   QName node test namespace handling fixed.  Few other bug fixes.
+ *   
+ * Revision 1: August 13, 2004
+ *   Bug fixes from William J. Edney <bedney (at) technicalpursuit.com>.
+ *   Added minimal licence.
+ *
+ * Initial version: June 14, 2004
+ */
+
+// XPathParser ///////////////////////////////////////////////////////////////
+
+XPathParser.prototype = new Object();
+XPathParser.prototype.constructor = XPathParser;
+XPathParser.superclass = Object.prototype;
+
+function XPathParser() {
+       this.init();
+}
+
+XPathParser.prototype.init = function() {
+       this.reduceActions = [];
+
+       this.reduceActions[3] = function(rhs) {
+               return new OrOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[5] = function(rhs) {
+               return new AndOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[7] = function(rhs) {
+               return new EqualsOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[8] = function(rhs) {
+               return new NotEqualOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[10] = function(rhs) {
+               return new LessThanOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[11] = function(rhs) {
+               return new GreaterThanOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[12] = function(rhs) {
+               return new LessThanOrEqualOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[13] = function(rhs) {
+               return new GreaterThanOrEqualOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[15] = function(rhs) {
+               return new PlusOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[16] = function(rhs) {
+               return new MinusOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[18] = function(rhs) {
+               return new MultiplyOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[19] = function(rhs) {
+               return new DivOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[20] = function(rhs) {
+               return new ModOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[22] = function(rhs) {
+               return new UnaryMinusOperation(rhs[1]);
+       };
+       this.reduceActions[24] = function(rhs) {
+               return new BarOperation(rhs[0], rhs[2]);
+       };
+       this.reduceActions[25] = function(rhs) {
+               return new PathExpr(undefined, undefined, rhs[0]);
+       };
+       this.reduceActions[27] = function(rhs) {
+               rhs[0].locationPath = rhs[2];
+               return rhs[0];
+       };
+       this.reduceActions[28] = function(rhs) {
+               rhs[0].locationPath = rhs[2];
+               rhs[0].locationPath.steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+               return rhs[0];
+       };
+       this.reduceActions[29] = function(rhs) {
+               return new PathExpr(rhs[0], [], undefined);
+       };
+       this.reduceActions[30] = function(rhs) {
+               if (Utilities.instance_of(rhs[0], PathExpr)) {
+                       if (rhs[0].filterPredicates == undefined) {
+                               rhs[0].filterPredicates = [];
+                       }
+                       rhs[0].filterPredicates.push(rhs[1]);
+                       return rhs[0];
+               } else {
+                       return new PathExpr(rhs[0], [rhs[1]], undefined);
+               }
+       };
+       this.reduceActions[32] = function(rhs) {
+               return rhs[1];
+       };
+       this.reduceActions[33] = function(rhs) {
+               return new XString(rhs[0]);
+       };
+       this.reduceActions[34] = function(rhs) {
+               return new XNumber(rhs[0]);
+       };
+       this.reduceActions[36] = function(rhs) {
+               return new FunctionCall(rhs[0], []);
+       };
+       this.reduceActions[37] = function(rhs) {
+               return new FunctionCall(rhs[0], rhs[2]);
+       };
+       this.reduceActions[38] = function(rhs) {
+               return [ rhs[0] ];
+       };
+       this.reduceActions[39] = function(rhs) {
+               rhs[2].unshift(rhs[0]);
+               return rhs[2];
+       };
+       this.reduceActions[43] = function(rhs) {
+               return new LocationPath(true, []);
+       };
+       this.reduceActions[44] = function(rhs) {
+               rhs[1].absolute = true;
+               return rhs[1];
+       };
+       this.reduceActions[46] = function(rhs) {
+               return new LocationPath(false, [ rhs[0] ]);
+       };
+       this.reduceActions[47] = function(rhs) {
+               rhs[0].steps.push(rhs[2]);
+               return rhs[0];
+       };
+       this.reduceActions[49] = function(rhs) {
+               return new Step(rhs[0], rhs[1], []);
+       };
+       this.reduceActions[50] = function(rhs) {
+               return new Step(Step.CHILD, rhs[0], []);
+       };
+       this.reduceActions[51] = function(rhs) {
+               return new Step(rhs[0], rhs[1], rhs[2]);
+       };
+       this.reduceActions[52] = function(rhs) {
+               return new Step(Step.CHILD, rhs[0], rhs[1]);
+       };
+       this.reduceActions[54] = function(rhs) {
+               return [ rhs[0] ];
+       };
+       this.reduceActions[55] = function(rhs) {
+               rhs[1].unshift(rhs[0]);
+               return rhs[1];
+       };
+       this.reduceActions[56] = function(rhs) {
+               if (rhs[0] == "ancestor") {
+                       return Step.ANCESTOR;
+               } else if (rhs[0] == "ancestor-or-self") {
+                       return Step.ANCESTORORSELF;
+               } else if (rhs[0] == "attribute") {
+                       return Step.ATTRIBUTE;
+               } else if (rhs[0] == "child") {
+                       return Step.CHILD;
+               } else if (rhs[0] == "descendant") {
+                       return Step.DESCENDANT;
+               } else if (rhs[0] == "descendant-or-self") {
+                       return Step.DESCENDANTORSELF;
+               } else if (rhs[0] == "following") {
+                       return Step.FOLLOWING;
+               } else if (rhs[0] == "following-sibling") {
+                       return Step.FOLLOWINGSIBLING;
+               } else if (rhs[0] == "namespace") {
+                       return Step.NAMESPACE;
+               } else if (rhs[0] == "parent") {
+                       return Step.PARENT;
+               } else if (rhs[0] == "preceding") {
+                       return Step.PRECEDING;
+               } else if (rhs[0] == "preceding-sibling") {
+                       return Step.PRECEDINGSIBLING;
+               } else if (rhs[0] == "self") {
+                       return Step.SELF;
+               }
+               return -1;
+       };
+       this.reduceActions[57] = function(rhs) {
+               return Step.ATTRIBUTE;
+       };
+       this.reduceActions[59] = function(rhs) {
+               if (rhs[0] == "comment") {
+                       return new NodeTest(NodeTest.COMMENT, undefined);
+               } else if (rhs[0] == "text") {
+                       return new NodeTest(NodeTest.TEXT, undefined);
+               } else if (rhs[0] == "processing-instruction") {
+                       return new NodeTest(NodeTest.PI, undefined);
+               } else if (rhs[0] == "node") {
+                       return new NodeTest(NodeTest.NODE, undefined);
+               }
+               return new NodeTest(-1, undefined);
+       };
+       this.reduceActions[60] = function(rhs) {
+               return new NodeTest(NodeTest.PI, rhs[2]);
+       };
+       this.reduceActions[61] = function(rhs) {
+               return rhs[1];
+       };
+       this.reduceActions[63] = function(rhs) {
+               rhs[1].absolute = true;
+               rhs[1].steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+               return rhs[1];
+       };
+       this.reduceActions[64] = function(rhs) {
+               rhs[0].steps.push(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+               rhs[0].steps.push(rhs[2]);
+               return rhs[0];
+       };
+       this.reduceActions[65] = function(rhs) {
+               return new Step(Step.SELF, new NodeTest(NodeTest.NODE, undefined), []);
+       };
+       this.reduceActions[66] = function(rhs) {
+               return new Step(Step.PARENT, new NodeTest(NodeTest.NODE, undefined), []);
+       };
+       this.reduceActions[67] = function(rhs) {
+               return new VariableReference(rhs[1]);
+       };
+       this.reduceActions[68] = function(rhs) {
+               return new NodeTest(NodeTest.NAMETESTANY, undefined);
+       };
+       this.reduceActions[69] = function(rhs) {
+               var prefix = rhs[0].substring(0, rhs[0].indexOf(":"));
+               return new NodeTest(NodeTest.NAMETESTPREFIXANY, prefix);
+       };
+       this.reduceActions[70] = function(rhs) {
+               return new NodeTest(NodeTest.NAMETESTQNAME, rhs[0]);
+       };
+};
+
+XPathParser.actionTable = [
+       " s s        sssssssss    s ss  s  ss",
+       "                 s                  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "                rrrrr               ",
+       " s s        sssssssss    s ss  s  ss",
+       "rs  rrrrrrrr s  sssssrrrrrr  rrs rs ",
+       " s s        sssssssss    s ss  s  ss",
+       "                            s       ",
+       "                            s       ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "  s                                 ",
+       "                            s       ",
+       " s           s  sssss          s  s ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "a                                   ",
+       "r       s                    rr  r  ",
+       "r      sr                    rr  r  ",
+       "r   s  rr            s       rr  r  ",
+       "r   rssrr            rss     rr  r  ",
+       "r   rrrrr            rrrss   rr  r  ",
+       "r   rrrrrsss         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrrs  rr  r  ",
+       "r   rrrrrrrr         rrrrrr  rr  r  ",
+       "r   rrrrrrrr         rrrrrr  rr  r  ",
+       "r  srrrrrrrr         rrrrrrs rr sr  ",
+       "r  srrrrrrrr         rrrrrrs rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r   rrrrrrrr         rrrrrr  rr  r  ",
+       "r   rrrrrrrr         rrrrrr  rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "                sssss               ",
+       "r  rrrrrrrrr         rrrrrrr rr sr  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "                             s      ",
+       "r  srrrrrrrr         rrrrrrs rr  r  ",
+       "r   rrrrrrrr         rrrrr   rr  r  ",
+       "              s                     ",
+       "                             s      ",
+       "                rrrrr               ",
+       " s s        sssssssss    s sss s  ss",
+       "r  srrrrrrrr         rrrrrrs rr  r  ",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s s        sssssssss      ss  s  ss",
+       " s s        sssssssss    s ss  s  ss",
+       " s           s  sssss          s  s ",
+       " s           s  sssss          s  s ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       " s           s  sssss          s  s ",
+       " s           s  sssss          s  s ",
+       "r  rrrrrrrrr         rrrrrrr rr sr  ",
+       "r  rrrrrrrrr         rrrrrrr rr sr  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "                             s      ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "                             rr     ",
+       "                             s      ",
+       "                             rs     ",
+       "r      sr                    rr  r  ",
+       "r   s  rr            s       rr  r  ",
+       "r   rssrr            rss     rr  r  ",
+       "r   rssrr            rss     rr  r  ",
+       "r   rrrrr            rrrss   rr  r  ",
+       "r   rrrrr            rrrss   rr  r  ",
+       "r   rrrrr            rrrss   rr  r  ",
+       "r   rrrrr            rrrss   rr  r  ",
+       "r   rrrrrsss         rrrrr   rr  r  ",
+       "r   rrrrrsss         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrr   rr  r  ",
+       "r   rrrrrrrr         rrrrrr  rr  r  ",
+       "                                 r  ",
+       "                                 s  ",
+       "r  srrrrrrrr         rrrrrrs rr  r  ",
+       "r  srrrrrrrr         rrrrrrs rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr  r  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       " s s        sssssssss    s ss  s  ss",
+       "r  rrrrrrrrr         rrrrrrr rr rr  ",
+       "                             r      "
+];
+
+XPathParser.actionTableNumber = [
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       "                 J                  ",
+       "a  aaaaaaaaa         aaaaaaa aa  a  ",
+       "                YYYYY               ",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       "K1  KKKKKKKK .  +*)('KKKKKK  KK# K\" ",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       "                            N       ",
+       "                            O       ",
+       "e  eeeeeeeee         eeeeeee ee ee  ",
+       "f  fffffffff         fffffff ff ff  ",
+       "d  ddddddddd         ddddddd dd dd  ",
+       "B  BBBBBBBBB         BBBBBBB BB BB  ",
+       "A  AAAAAAAAA         AAAAAAA AA AA  ",
+       "  P                                 ",
+       "                            Q       ",
+       " 1           .  +*)('          #  \" ",
+       "b  bbbbbbbbb         bbbbbbb bb  b  ",
+       "                                    ",
+       "!       S                    !!  !  ",
+       "\"      T\"                    \"\"  \"  ",
+       "$   V  $$            U       $$  $  ",
+       "&   &ZY&&            &XW     &&  &  ",
+       ")   )))))            )))\\[   ))  )  ",
+       ".   ....._^]         .....   ..  .  ",
+       "1   11111111         11111   11  1  ",
+       "5   55555555         55555`  55  5  ",
+       "7   77777777         777777  77  7  ",
+       "9   99999999         999999  99  9  ",
+       ":  c::::::::         ::::::b :: a:  ",
+       "I  fIIIIIIII         IIIIIIe II  I  ",
+       "=  =========         ======= == ==  ",
+       "?  ?????????         ??????? ?? ??  ",
+       "C  CCCCCCCCC         CCCCCCC CC CC  ",
+       "J   JJJJJJJJ         JJJJJJ  JJ  J  ",
+       "M   MMMMMMMM         MMMMMM  MM  M  ",
+       "N  NNNNNNNNN         NNNNNNN NN  N  ",
+       "P  PPPPPPPPP         PPPPPPP PP  P  ",
+       "                +*)('               ",
+       "R  RRRRRRRRR         RRRRRRR RR aR  ",
+       "U  UUUUUUUUU         UUUUUUU UU  U  ",
+       "Z  ZZZZZZZZZ         ZZZZZZZ ZZ ZZ  ",
+       "c  ccccccccc         ccccccc cc cc  ",
+       "                             j      ",
+       "L  fLLLLLLLL         LLLLLLe LL  L  ",
+       "6   66666666         66666   66  6  ",
+       "              k                     ",
+       "                             l      ",
+       "                XXXXX               ",
+       " 1 0        /.-,+*)('    & %$m #  \"!",
+       "_  f________         ______e __  _  ",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1 0        /.-,+*)('      %$  #  \"!",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       " 1           .  +*)('          #  \" ",
+       " 1           .  +*)('          #  \" ",
+       ">  >>>>>>>>>         >>>>>>> >> >>  ",
+       " 1           .  +*)('          #  \" ",
+       " 1           .  +*)('          #  \" ",
+       "Q  QQQQQQQQQ         QQQQQQQ QQ aQ  ",
+       "V  VVVVVVVVV         VVVVVVV VV aV  ",
+       "T  TTTTTTTTT         TTTTTTT TT  T  ",
+       "@  @@@@@@@@@         @@@@@@@ @@ @@  ",
+       "                             \x87      ",
+       "[  [[[[[[[[[         [[[[[[[ [[ [[  ",
+       "D  DDDDDDDDD         DDDDDDD DD DD  ",
+       "                             HH     ",
+       "                             \x88      ",
+       "                             F\x89     ",
+       "#      T#                    ##  #  ",
+       "%   V  %%            U       %%  %  ",
+       "'   'ZY''            'XW     ''  '  ",
+       "(   (ZY((            (XW     ((  (  ",
+       "+   +++++            +++\\[   ++  +  ",
+       "*   *****            ***\\[   **  *  ",
+       "-   -----            ---\\[   --  -  ",
+       ",   ,,,,,            ,,,\\[   ,,  ,  ",
+       "0   00000_^]         00000   00  0  ",
+       "/   /////_^]         /////   //  /  ",
+       "2   22222222         22222   22  2  ",
+       "3   33333333         33333   33  3  ",
+       "4   44444444         44444   44  4  ",
+       "8   88888888         888888  88  8  ",
+       "                                 ^  ",
+       "                                 \x8a  ",
+       ";  f;;;;;;;;         ;;;;;;e ;;  ;  ",
+       "<  f<<<<<<<<         <<<<<<e <<  <  ",
+       "O  OOOOOOOOO         OOOOOOO OO  O  ",
+       "`  `````````         ``````` ``  `  ",
+       "S  SSSSSSSSS         SSSSSSS SS  S  ",
+       "W  WWWWWWWWW         WWWWWWW WW  W  ",
+       "\\  \\\\\\\\\\\\\\\\\\         \\\\\\\\\\\\\\ \\\\ \\\\  ",
+       "E  EEEEEEEEE         EEEEEEE EE EE  ",
+       " 1 0        /.-,+*)('    & %$  #  \"!",
+       "]  ]]]]]]]]]         ]]]]]]] ]] ]]  ",
+       "                             G      "
+];
+
+XPathParser.gotoTable = [
+       "3456789:;<=>?@ AB  CDEFGH IJ ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "L456789:;<=>?@ AB  CDEFGH IJ ",
+       "            M        EFGH IJ ",
+       "       N;<=>?@ AB  CDEFGH IJ ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "            S        EFGH IJ ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "              e              ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                        h  J ",
+       "              i          j   ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "o456789:;<=>?@ ABpqCDEFGH IJ ",
+       "                             ",
+       "  r6789:;<=>?@ AB  CDEFGH IJ ",
+       "   s789:;<=>?@ AB  CDEFGH IJ ",
+       "    t89:;<=>?@ AB  CDEFGH IJ ",
+       "    u89:;<=>?@ AB  CDEFGH IJ ",
+       "     v9:;<=>?@ AB  CDEFGH IJ ",
+       "     w9:;<=>?@ AB  CDEFGH IJ ",
+       "     x9:;<=>?@ AB  CDEFGH IJ ",
+       "     y9:;<=>?@ AB  CDEFGH IJ ",
+       "      z:;<=>?@ AB  CDEFGH IJ ",
+       "      {:;<=>?@ AB  CDEFGH IJ ",
+       "       |;<=>?@ AB  CDEFGH IJ ",
+       "       };<=>?@ AB  CDEFGH IJ ",
+       "       ~;<=>?@ AB  CDEFGH IJ ",
+       "         \x7f=>?@ AB  CDEFGH IJ ",
+       "\x80456789:;<=>?@ AB  CDEFGH IJ\x81",
+       "            \x82        EFGH IJ ",
+       "            \x83        EFGH IJ ",
+       "                             ",
+       "                     \x84 GH IJ ",
+       "                     \x85 GH IJ ",
+       "              i          \x86   ",
+       "              i          \x87   ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "                             ",
+       "o456789:;<=>?@ AB\x8cqCDEFGH IJ ",
+       "                             ",
+       "                             "
+];
+
+XPathParser.productions = [
+       [1, 1, 2],
+       [2, 1, 3],
+       [3, 1, 4],
+       [3, 3, 3, -9, 4],
+       [4, 1, 5],
+       [4, 3, 4, -8, 5],
+       [5, 1, 6],
+       [5, 3, 5, -22, 6],
+       [5, 3, 5, -5, 6],
+       [6, 1, 7],
+       [6, 3, 6, -23, 7],
+       [6, 3, 6, -24, 7],
+       [6, 3, 6, -6, 7],
+       [6, 3, 6, -7, 7],
+       [7, 1, 8],
+       [7, 3, 7, -25, 8],
+       [7, 3, 7, -26, 8],
+       [8, 1, 9],
+       [8, 3, 8, -12, 9],
+       [8, 3, 8, -11, 9],
+       [8, 3, 8, -10, 9],
+       [9, 1, 10],
+       [9, 2, -26, 9],
+       [10, 1, 11],
+       [10, 3, 10, -27, 11],
+       [11, 1, 12],
+       [11, 1, 13],
+       [11, 3, 13, -28, 14],
+       [11, 3, 13, -4, 14],
+       [13, 1, 15],
+       [13, 2, 13, 16],
+       [15, 1, 17],
+       [15, 3, -29, 2, -30],
+       [15, 1, -15],
+       [15, 1, -16],
+       [15, 1, 18],
+       [18, 3, -13, -29, -30],
+       [18, 4, -13, -29, 19, -30],
+       [19, 1, 20],
+       [19, 3, 20, -31, 19],
+       [20, 1, 2],
+       [12, 1, 14],
+       [12, 1, 21],
+       [21, 1, -28],
+       [21, 2, -28, 14],
+       [21, 1, 22],
+       [14, 1, 23],
+       [14, 3, 14, -28, 23],
+       [14, 1, 24],
+       [23, 2, 25, 26],
+       [23, 1, 26],
+       [23, 3, 25, 26, 27],
+       [23, 2, 26, 27],
+       [23, 1, 28],
+       [27, 1, 16],
+       [27, 2, 16, 27],
+       [25, 2, -14, -3],
+       [25, 1, -32],
+       [26, 1, 29],
+       [26, 3, -20, -29, -30],
+       [26, 4, -21, -29, -15, -30],
+       [16, 3, -33, 30, -34],
+       [30, 1, 2],
+       [22, 2, -4, 14],
+       [24, 3, 14, -4, 23],
+       [28, 1, -35],
+       [28, 1, -2],
+       [17, 2, -36, -18],
+       [29, 1, -17],
+       [29, 1, -19],
+       [29, 1, -18]
+];
+
+XPathParser.DOUBLEDOT = 2;
+XPathParser.DOUBLECOLON = 3;
+XPathParser.DOUBLESLASH = 4;
+XPathParser.NOTEQUAL = 5;
+XPathParser.LESSTHANOREQUAL = 6;
+XPathParser.GREATERTHANOREQUAL = 7;
+XPathParser.AND = 8;
+XPathParser.OR = 9;
+XPathParser.MOD = 10;
+XPathParser.DIV = 11;
+XPathParser.MULTIPLYOPERATOR = 12;
+XPathParser.FUNCTIONNAME = 13;
+XPathParser.AXISNAME = 14;
+XPathParser.LITERAL = 15;
+XPathParser.NUMBER = 16;
+XPathParser.ASTERISKNAMETEST = 17;
+XPathParser.QNAME = 18;
+XPathParser.NCNAMECOLONASTERISK = 19;
+XPathParser.NODETYPE = 20;
+XPathParser.PROCESSINGINSTRUCTIONWITHLITERAL = 21;
+XPathParser.EQUALS = 22;
+XPathParser.LESSTHAN = 23;
+XPathParser.GREATERTHAN = 24;
+XPathParser.PLUS = 25;
+XPathParser.MINUS = 26;
+XPathParser.BAR = 27;
+XPathParser.SLASH = 28;
+XPathParser.LEFTPARENTHESIS = 29;
+XPathParser.RIGHTPARENTHESIS = 30;
+XPathParser.COMMA = 31;
+XPathParser.AT = 32;
+XPathParser.LEFTBRACKET = 33;
+XPathParser.RIGHTBRACKET = 34;
+XPathParser.DOT = 35;
+XPathParser.DOLLAR = 36;
+
+XPathParser.prototype.tokenize = function(s1) {
+       var types = [];
+       var values = [];
+       var s = s1 + '\0';
+
+       var pos = 0;
+       var c = s.charAt(pos++);
+       while (1) {
+               while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+                       c = s.charAt(pos++);
+               }
+               if (c == '\0' || pos >= s.length) {
+                       break;
+               }
+
+               if (c == '(') {
+                       types.push(XPathParser.LEFTPARENTHESIS);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == ')') {
+                       types.push(XPathParser.RIGHTPARENTHESIS);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '[') {
+                       types.push(XPathParser.LEFTBRACKET);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == ']') {
+                       types.push(XPathParser.RIGHTBRACKET);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '@') {
+                       types.push(XPathParser.AT);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == ',') {
+                       types.push(XPathParser.COMMA);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '|') {
+                       types.push(XPathParser.BAR);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '+') {
+                       types.push(XPathParser.PLUS);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '-') {
+                       types.push(XPathParser.MINUS);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '=') {
+                       types.push(XPathParser.EQUALS);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               if (c == '$') {
+                       types.push(XPathParser.DOLLAR);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+               
+               if (c == '.') {
+                       c = s.charAt(pos++);
+                       if (c == '.') {
+                               types.push(XPathParser.DOUBLEDOT);
+                               values.push("..");
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+                       if (c >= '0' && c <= '9') {
+                               var number = "." + c;
+                               c = s.charAt(pos++);
+                               while (c >= '0' && c <= '9') {
+                                       number += c;
+                                       c = s.charAt(pos++);
+                               }
+                               types.push(XPathParser.NUMBER);
+                               values.push(number);
+                               continue;
+                       }
+                       types.push(XPathParser.DOT);
+                       values.push('.');
+                       continue;
+               }
+
+               if (c == '\'' || c == '"') {
+                       var delimiter = c;
+                       var literal = "";
+                       while ((c = s.charAt(pos++)) != delimiter) {
+                               literal += c;
+                       }
+                       types.push(XPathParser.LITERAL);
+                       values.push(literal);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+
+               if (c >= '0' && c <= '9') {
+                       var number = c;
+                       c = s.charAt(pos++);
+                       while (c >= '0' && c <= '9') {
+                               number += c;
+                               c = s.charAt(pos++);
+                       }
+                       if (c == '.') {
+                               if (s.charAt(pos) >= '0' && s.charAt(pos) <= '9') {
+                                       number += c;
+                                       number += s.charAt(pos++);
+                                       c = s.charAt(pos++);
+                                       while (c >= '0' && c <= '9') {
+                                               number += c;
+                                               c = s.charAt(pos++);
+                                       }
+                               }
+                       }
+                       types.push(XPathParser.NUMBER);
+                       values.push(number);
+                       continue;
+               }
+
+               if (c == '*') {
+                       if (types.length > 0) {
+                               var last = types[types.length - 1];
+                               if (last != XPathParser.AT
+                                               && last != XPathParser.DOUBLECOLON
+                                               && last != XPathParser.LEFTPARENTHESIS
+                                               && last != XPathParser.LEFTBRACKET
+                                               && last != XPathParser.AND
+                                               && last != XPathParser.OR
+                                               && last != XPathParser.MOD
+                                               && last != XPathParser.DIV
+                                               && last != XPathParser.MULTIPLYOPERATOR
+                                               && last != XPathParser.SLASH
+                                               && last != XPathParser.DOUBLESLASH
+                                               && last != XPathParser.BAR
+                                               && last != XPathParser.PLUS
+                                               && last != XPathParser.MINUS
+                                               && last != XPathParser.EQUALS
+                                               && last != XPathParser.NOTEQUAL
+                                               && last != XPathParser.LESSTHAN
+                                               && last != XPathParser.LESSTHANOREQUAL
+                                               && last != XPathParser.GREATERTHAN
+                                               && last != XPathParser.GREATERTHANOREQUAL) {
+                                       types.push(XPathParser.MULTIPLYOPERATOR);
+                                       values.push(c);
+                                       c = s.charAt(pos++);
+                                       continue;
+                               }
+                       }
+                       types.push(XPathParser.ASTERISKNAMETEST);
+                       values.push(c);
+                       c = s.charAt(pos++);
+                       continue;
+               }
+
+               if (c == ':') {
+                       if (s.charAt(pos) == ':') {
+                               types.push(XPathParser.DOUBLECOLON);
+                               values.push("::");
+                               pos++;
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+               }
+
+               if (c == '/') {
+                       c = s.charAt(pos++);
+                       if (c == '/') {
+                               types.push(XPathParser.DOUBLESLASH);
+                               values.push("//");
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+                       types.push(XPathParser.SLASH);
+                       values.push('/');
+                       continue;
+               }
+
+               if (c == '!') {
+                       if (s.charAt(pos) == '=') {
+                               types.push(XPathParser.NOTEQUAL);
+                               values.push("!=");
+                               pos++;
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+               }
+
+               if (c == '<') {
+                       if (s.charAt(pos) == '=') {
+                               types.push(XPathParser.LESSTHANOREQUAL);
+                               values.push("<=");
+                               pos++;
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+                       types.push(XPathParser.LESSTHAN);
+                       values.push('<');
+                       c = s.charAt(pos++);
+                       continue;
+               }
+
+               if (c == '>') {
+                       if (s.charAt(pos) == '=') {
+                               types.push(XPathParser.GREATERTHANOREQUAL);
+                               values.push(">=");
+                               pos++;
+                               c = s.charAt(pos++);
+                               continue;
+                       }
+                       types.push(XPathParser.GREATERTHAN);
+                       values.push('>');
+                       c = s.charAt(pos++);
+                       continue;
+               }
+
+               if (c == '_' || Utilities.isLetter(c.charCodeAt(0))) {
+                       var name = c;
+                       c = s.charAt(pos++);
+                       while (Utilities.isNCNameChar(c.charCodeAt(0))) {
+                               name += c;
+                               c = s.charAt(pos++);
+                       }
+                       if (types.length > 0) {
+                               var last = types[types.length - 1];
+                               if (last != XPathParser.AT
+                                               && last != XPathParser.DOUBLECOLON
+                                               && last != XPathParser.LEFTPARENTHESIS
+                                               && last != XPathParser.LEFTBRACKET
+                                               && last != XPathParser.AND
+                                               && last != XPathParser.OR
+                                               && last != XPathParser.MOD
+                                               && last != XPathParser.DIV
+                                               && last != XPathParser.MULTIPLYOPERATOR
+                                               && last != XPathParser.SLASH
+                                               && last != XPathParser.DOUBLESLASH
+                                               && last != XPathParser.BAR
+                                               && last != XPathParser.PLUS
+                                               && last != XPathParser.MINUS
+                                               && last != XPathParser.EQUALS
+                                               && last != XPathParser.NOTEQUAL
+                                               && last != XPathParser.LESSTHAN
+                                               && last != XPathParser.LESSTHANOREQUAL
+                                               && last != XPathParser.GREATERTHAN
+                                               && last != XPathParser.GREATERTHANOREQUAL) {
+                                       if (name == "and") {
+                                               types.push(XPathParser.AND);
+                                               values.push(name);
+                                               continue;
+                                       }
+                                       if (name == "or") {
+                                               types.push(XPathParser.OR);
+                                               values.push(name);
+                                               continue;
+                                       }
+                                       if (name == "mod") {
+                                               types.push(XPathParser.MOD);
+                                               values.push(name);
+                                               continue;
+                                       }
+                                       if (name == "div") {
+                                               types.push(XPathParser.DIV);
+                                               values.push(name);
+                                               continue;
+                                       }
+                               }
+                       }
+                       if (c == ':') {
+                               if (s.charAt(pos) == '*') {
+                                       types.push(XPathParser.NCNAMECOLONASTERISK);
+                                       values.push(name + ":*");
+                                       pos++;
+                                       c = s.charAt(pos++);
+                                       continue;
+                               }
+                               if (s.charAt(pos) == '_' || Utilities.isLetter(s.charCodeAt(pos))) {
+                                       name += ':';
+                                       c = s.charAt(pos++);
+                                       while (Utilities.isNCNameChar(c.charCodeAt(0))) {
+                                               name += c;
+                                               c = s.charAt(pos++);
+                                       }
+                                       if (c == '(') {
+                                               types.push(XPathParser.FUNCTIONNAME);
+                                               values.push(name);
+                                               continue;
+                                       }
+                                       types.push(XPathParser.QNAME);
+                                       values.push(name);
+                                       continue;
+                               }
+                               if (s.charAt(pos) == ':') {
+                                       types.push(XPathParser.AXISNAME);
+                                       values.push(name);
+                                       continue;
+                               }
+                       }
+                       if (c == '(') {
+                               if (name == "comment" || name == "text" || name == "node") {
+                                       types.push(XPathParser.NODETYPE);
+                                       values.push(name);
+                                       continue;
+                               }
+                               if (name == "processing-instruction") {
+                                       if (s.charAt(pos) == ')') {
+                                               types.push(XPathParser.NODETYPE);
+                                       } else {
+                                               types.push(XPathParser.PROCESSINGINSTRUCTIONWITHLITERAL);
+                                       }
+                                       values.push(name);
+                                       continue;
+                               }
+                               types.push(XPathParser.FUNCTIONNAME);
+                               values.push(name);
+                               continue;
+                       }
+                       types.push(XPathParser.QNAME);
+                       values.push(name);
+                       continue;
+               }
+
+               throw new Error("Unexpected character " + c);
+       }
+       types.push(1);
+       values.push("[EOF]");
+       return [types, values];
+};
+
+XPathParser.SHIFT = 's';
+XPathParser.REDUCE = 'r';
+XPathParser.ACCEPT = 'a';
+
+XPathParser.prototype.parse = function(s) {
+       var types;
+       var values;
+       var res = this.tokenize(s);
+       if (res == undefined) {
+               return undefined;
+       }
+       types = res[0];
+       values = res[1];
+       var tokenPos = 0;
+       var state = [];
+       var tokenType = [];
+       var tokenValue = [];
+       var s;
+       var a;
+       var t;
+
+       state.push(0);
+       tokenType.push(1);
+       tokenValue.push("_S");
+
+       a = types[tokenPos];
+       t = values[tokenPos++];
+       while (1) {
+               s = state[state.length - 1];
+               switch (XPathParser.actionTable[s].charAt(a - 1)) {
+                       case XPathParser.SHIFT:
+                               tokenType.push(-a);
+                               tokenValue.push(t);
+                               state.push(XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32);
+                               a = types[tokenPos];
+                               t = values[tokenPos++];
+                               break;
+                       case XPathParser.REDUCE:
+                               var num = XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][1];
+                               var rhs = [];
+                               for (var i = 0; i < num; i++) {
+                                       tokenType.pop();
+                                       rhs.unshift(tokenValue.pop());
+                                       state.pop();
+                               }
+                               var s_ = state[state.length - 1];
+                               tokenType.push(XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][0]);
+                               if (this.reduceActions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32] == undefined) {
+                                       tokenValue.push(rhs[0]);
+                               } else {
+                                       tokenValue.push(this.reduceActions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32](rhs));
+                               }
+                               state.push(XPathParser.gotoTable[s_].charCodeAt(XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][0] - 2) - 33);
+                               break;
+                       case XPathParser.ACCEPT:
+                               return new XPath(tokenValue.pop());
+                       default:
+                               throw new Error("XPath parse error");
+               }
+       }
+};
+
+// XPath /////////////////////////////////////////////////////////////////////
+
+XPath.prototype = new Object();
+XPath.prototype.constructor = XPath;
+XPath.superclass = Object.prototype;
+
+function XPath(e) {
+       this.expression = e;
+}
+
+XPath.prototype.toString = function() {
+       return this.expression.toString();
+};
+
+XPath.prototype.evaluate = function(c) {
+       c.contextNode = c.expressionContextNode;
+       c.contextSize = 1;
+       c.contextPosition = 1;
+       c.caseInsensitive = false;
+       if (c.contextNode != null) {
+               var doc = c.contextNode;
+               if (doc.nodeType != 9 /*Node.DOCUMENT_NODE*/) {
+                       doc = doc.ownerDocument;
+               }
+               try {
+                       c.caseInsensitive = doc.implementation.hasFeature("HTML", "2.0");
+               } catch (e) {
+                       c.caseInsensitive = true;
+               }
+       }
+       return this.expression.evaluate(c);
+};
+
+XPath.XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
+XPath.XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
+
+// Expression ////////////////////////////////////////////////////////////////
+
+Expression.prototype = new Object();
+Expression.prototype.constructor = Expression;
+Expression.superclass = Object.prototype;
+
+function Expression() {
+}
+
+Expression.prototype.init = function() {
+};
+
+Expression.prototype.toString = function() {
+       return "<Expression>";
+};
+
+Expression.prototype.evaluate = function(c) {
+       throw new Error("Could not evaluate expression.");
+};
+
+// UnaryOperation ////////////////////////////////////////////////////////////
+
+UnaryOperation.prototype = new Expression();
+UnaryOperation.prototype.constructor = UnaryOperation;
+UnaryOperation.superclass = Expression.prototype;
+
+function UnaryOperation(rhs) {
+       if (arguments.length > 0) {
+               this.init(rhs);
+       }
+}
+
+UnaryOperation.prototype.init = function(rhs) {
+       this.rhs = rhs;
+};
+
+// UnaryMinusOperation ///////////////////////////////////////////////////////
+
+UnaryMinusOperation.prototype = new UnaryOperation();
+UnaryMinusOperation.prototype.constructor = UnaryMinusOperation;
+UnaryMinusOperation.superclass = UnaryOperation.prototype;
+
+function UnaryMinusOperation(rhs) {
+       if (arguments.length > 0) {
+               this.init(rhs);
+       }
+}
+
+UnaryMinusOperation.prototype.init = function(rhs) {
+       UnaryMinusOperation.superclass.init.call(this, rhs);
+};
+
+UnaryMinusOperation.prototype.evaluate = function(c) {
+       return this.rhs.evaluate(c).number().negate();
+};
+
+UnaryMinusOperation.prototype.toString = function() {
+       return "-" + this.rhs.toString();
+};
+
+// BinaryOperation ///////////////////////////////////////////////////////////
+
+BinaryOperation.prototype = new Expression();
+BinaryOperation.prototype.constructor = BinaryOperation;
+BinaryOperation.superclass = Expression.prototype;
+
+function BinaryOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+BinaryOperation.prototype.init = function(lhs, rhs) {
+       this.lhs = lhs;
+       this.rhs = rhs;
+};
+
+// OrOperation ///////////////////////////////////////////////////////////////
+
+OrOperation.prototype = new BinaryOperation();
+OrOperation.prototype.constructor = OrOperation;
+OrOperation.superclass = BinaryOperation.prototype;
+
+function OrOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+OrOperation.prototype.init = function(lhs, rhs) {
+       OrOperation.superclass.init.call(this, lhs, rhs);
+};
+
+OrOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " or " + this.rhs.toString() + ")";
+};
+
+OrOperation.prototype.evaluate = function(c) {
+       var b = this.lhs.evaluate(c).bool();
+       if (b.booleanValue()) {
+               return b;
+       }
+       return this.rhs.evaluate(c).bool();
+};
+
+// AndOperation //////////////////////////////////////////////////////////////
+
+AndOperation.prototype = new BinaryOperation();
+AndOperation.prototype.constructor = AndOperation;
+AndOperation.superclass = BinaryOperation.prototype;
+
+function AndOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+AndOperation.prototype.init = function(lhs, rhs) {
+       AndOperation.superclass.init.call(this, lhs, rhs);
+};
+
+AndOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " and " + this.rhs.toString() + ")";
+};
+
+AndOperation.prototype.evaluate = function(c) {
+       var b = this.lhs.evaluate(c).bool();
+       if (!b.booleanValue()) {
+               return b;
+       }
+       return this.rhs.evaluate(c).bool();
+};
+
+// EqualsOperation ///////////////////////////////////////////////////////////
+
+EqualsOperation.prototype = new BinaryOperation();
+EqualsOperation.prototype.constructor = EqualsOperation;
+EqualsOperation.superclass = BinaryOperation.prototype;
+
+function EqualsOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+EqualsOperation.prototype.init = function(lhs, rhs) {
+       EqualsOperation.superclass.init.call(this, lhs, rhs);
+};
+
+EqualsOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " = " + this.rhs.toString() + ")";
+};
+
+EqualsOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).equals(this.rhs.evaluate(c));
+};
+
+// NotEqualOperation /////////////////////////////////////////////////////////
+
+NotEqualOperation.prototype = new BinaryOperation();
+NotEqualOperation.prototype.constructor = NotEqualOperation;
+NotEqualOperation.superclass = BinaryOperation.prototype;
+
+function NotEqualOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+NotEqualOperation.prototype.init = function(lhs, rhs) {
+       NotEqualOperation.superclass.init.call(this, lhs, rhs);
+};
+
+NotEqualOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " != " + this.rhs.toString() + ")";
+};
+
+NotEqualOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).notequal(this.rhs.evaluate(c));
+};
+
+// LessThanOperation /////////////////////////////////////////////////////////
+
+LessThanOperation.prototype = new BinaryOperation();
+LessThanOperation.prototype.constructor = LessThanOperation;
+LessThanOperation.superclass = BinaryOperation.prototype;
+
+function LessThanOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+LessThanOperation.prototype.init = function(lhs, rhs) {
+       LessThanOperation.superclass.init.call(this, lhs, rhs);
+};
+
+LessThanOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).lessthan(this.rhs.evaluate(c));
+};
+
+LessThanOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " < " + this.rhs.toString() + ")";
+};
+
+// GreaterThanOperation //////////////////////////////////////////////////////
+
+GreaterThanOperation.prototype = new BinaryOperation();
+GreaterThanOperation.prototype.constructor = GreaterThanOperation;
+GreaterThanOperation.superclass = BinaryOperation.prototype;
+
+function GreaterThanOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+GreaterThanOperation.prototype.init = function(lhs, rhs) {
+       GreaterThanOperation.superclass.init.call(this, lhs, rhs);
+};
+
+GreaterThanOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).greaterthan(this.rhs.evaluate(c));
+};
+
+GreaterThanOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " > " + this.rhs.toString() + ")";
+};
+
+// LessThanOrEqualOperation //////////////////////////////////////////////////
+
+LessThanOrEqualOperation.prototype = new BinaryOperation();
+LessThanOrEqualOperation.prototype.constructor = LessThanOrEqualOperation;
+LessThanOrEqualOperation.superclass = BinaryOperation.prototype;
+
+function LessThanOrEqualOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+LessThanOrEqualOperation.prototype.init = function(lhs, rhs) {
+       LessThanOrEqualOperation.superclass.init.call(this, lhs, rhs);
+};
+
+LessThanOrEqualOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).lessthanorequal(this.rhs.evaluate(c));
+};
+
+LessThanOrEqualOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " <= " + this.rhs.toString() + ")";
+};
+
+// GreaterThanOrEqualOperation ///////////////////////////////////////////////
+
+GreaterThanOrEqualOperation.prototype = new BinaryOperation();
+GreaterThanOrEqualOperation.prototype.constructor = GreaterThanOrEqualOperation;
+GreaterThanOrEqualOperation.superclass = BinaryOperation.prototype;
+
+function GreaterThanOrEqualOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+GreaterThanOrEqualOperation.prototype.init = function(lhs, rhs) {
+       GreaterThanOrEqualOperation.superclass.init.call(this, lhs, rhs);
+};
+
+GreaterThanOrEqualOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).greaterthanorequal(this.rhs.evaluate(c));
+};
+
+GreaterThanOrEqualOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " >= " + this.rhs.toString() + ")";
+};
+
+// PlusOperation /////////////////////////////////////////////////////////////
+
+PlusOperation.prototype = new BinaryOperation();
+PlusOperation.prototype.constructor = PlusOperation;
+PlusOperation.superclass = BinaryOperation.prototype;
+
+function PlusOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+PlusOperation.prototype.init = function(lhs, rhs) {
+       PlusOperation.superclass.init.call(this, lhs, rhs);
+};
+
+PlusOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).number().plus(this.rhs.evaluate(c).number());
+};
+
+PlusOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " + " + this.rhs.toString() + ")";
+};
+
+// MinusOperation ////////////////////////////////////////////////////////////
+
+MinusOperation.prototype = new BinaryOperation();
+MinusOperation.prototype.constructor = MinusOperation;
+MinusOperation.superclass = BinaryOperation.prototype;
+
+function MinusOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+MinusOperation.prototype.init = function(lhs, rhs) {
+       MinusOperation.superclass.init.call(this, lhs, rhs);
+};
+
+MinusOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).number().minus(this.rhs.evaluate(c).number());
+};
+
+MinusOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " - " + this.rhs.toString() + ")";
+};
+
+// MultiplyOperation /////////////////////////////////////////////////////////
+
+MultiplyOperation.prototype = new BinaryOperation();
+MultiplyOperation.prototype.constructor = MultiplyOperation;
+MultiplyOperation.superclass = BinaryOperation.prototype;
+
+function MultiplyOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+MultiplyOperation.prototype.init = function(lhs, rhs) {
+       MultiplyOperation.superclass.init.call(this, lhs, rhs);
+};
+
+MultiplyOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).number().multiply(this.rhs.evaluate(c).number());
+};
+
+MultiplyOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " * " + this.rhs.toString() + ")";
+};
+
+// DivOperation //////////////////////////////////////////////////////////////
+
+DivOperation.prototype = new BinaryOperation();
+DivOperation.prototype.constructor = DivOperation;
+DivOperation.superclass = BinaryOperation.prototype;
+
+function DivOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+DivOperation.prototype.init = function(lhs, rhs) {
+       DivOperation.superclass.init.call(this, lhs, rhs);
+};
+
+DivOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).number().div(this.rhs.evaluate(c).number());
+};
+
+DivOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " div " + this.rhs.toString() + ")";
+};
+
+// ModOperation //////////////////////////////////////////////////////////////
+
+ModOperation.prototype = new BinaryOperation();
+ModOperation.prototype.constructor = ModOperation;
+ModOperation.superclass = BinaryOperation.prototype;
+
+function ModOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+ModOperation.prototype.init = function(lhs, rhs) {
+       ModOperation.superclass.init.call(this, lhs, rhs);
+};
+
+ModOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).number().mod(this.rhs.evaluate(c).number());
+};
+
+ModOperation.prototype.toString = function() {
+       return "(" + this.lhs.toString() + " mod " + this.rhs.toString() + ")";
+};
+
+// BarOperation //////////////////////////////////////////////////////////////
+
+BarOperation.prototype = new BinaryOperation();
+BarOperation.prototype.constructor = BarOperation;
+BarOperation.superclass = BinaryOperation.prototype;
+
+function BarOperation(lhs, rhs) {
+       if (arguments.length > 0) {
+               this.init(lhs, rhs);
+       }
+}
+
+BarOperation.prototype.init = function(lhs, rhs) {
+       BarOperation.superclass.init.call(this, lhs, rhs);
+};
+
+BarOperation.prototype.evaluate = function(c) {
+       return this.lhs.evaluate(c).nodeset().union(this.rhs.evaluate(c).nodeset());
+};
+
+BarOperation.prototype.toString = function() {
+       return this.lhs.toString() + " | " + this.rhs.toString();
+};
+
+// PathExpr //////////////////////////////////////////////////////////////////
+
+PathExpr.prototype = new Expression();
+PathExpr.prototype.constructor = PathExpr;
+PathExpr.superclass = Expression.prototype;
+
+function PathExpr(filter, filterPreds, locpath) {
+       if (arguments.length > 0) {
+               this.init(filter, filterPreds, locpath);
+       }
+}
+
+PathExpr.prototype.init = function(filter, filterPreds, locpath) {
+       PathExpr.superclass.init.call(this);
+       this.filter = filter;
+       this.filterPredicates = filterPreds;
+       this.locationPath = locpath;
+};
+
+PathExpr.prototype.evaluate = function(c) {
+       var nodes;
+       var xpc = new XPathContext();
+       xpc.variableResolver = c.variableResolver;
+       xpc.functionResolver = c.functionResolver;
+       xpc.namespaceResolver = c.namespaceResolver;
+       xpc.expressionContextNode = c.expressionContextNode;
+       xpc.virtualRoot = c.virtualRoot;
+       xpc.caseInsensitive = c.caseInsensitive;
+       if (this.filter == null) {
+               nodes = [ c.contextNode ];
+       } else {
+               var ns = this.filter.evaluate(c);
+               if (!Utilities.instance_of(ns, XNodeSet)) {
+                       if (this.filterPredicates != null && this.filterPredicates.length > 0 || this.locationPath != null) {
+                               throw new Error("Path expression filter must evaluate to a nodset if predicates or location path are used");
+                       }
+                       return ns;
+               }
+               nodes = ns.toArray();
+               if (this.filterPredicates != null) {
+                       // apply each of the predicates in turn
+                       for (var j = 0; j < this.filterPredicates.length; j++) {
+                               var pred = this.filterPredicates[j];
+                               var newNodes = [];
+                               xpc.contextSize = nodes.length;
+                               for (xpc.contextPosition = 1; xpc.contextPosition <= xpc.contextSize; xpc.contextPosition++) {
+                                       xpc.contextNode = nodes[xpc.contextPosition - 1];
+                                       if (this.predicateMatches(pred, xpc)) {
+                                               newNodes.push(xpc.contextNode);
+                                       }
+                               }
+                               nodes = newNodes;
+                       }
+               }
+       }
+       if (this.locationPath != null) {
+               if (this.locationPath.absolute) {
+                       if (nodes[0].nodeType != 9 /*Node.DOCUMENT_NODE*/) {
+                               if (xpc.virtualRoot != null) {
+                                       nodes = [ xpc.virtualRoot ];
+                               } else {
+                                       if (nodes[0].ownerDocument == null) {
+                                               // IE 5.5 doesn't have ownerDocument?
+                                               var n = nodes[0];
+                                               while (n.parentNode != null) {
+                                                       n = n.parentNode;
+                                               }
+                                               nodes = [ n ];
+                                       } else {
+                                               nodes = [ nodes[0].ownerDocument ];
+                                       }
+                               }
+                       } else {
+                               nodes = [ nodes[0] ];
+                       }
+               }
+               for (var i = 0; i < this.locationPath.steps.length; i++) {
+                       var step = this.locationPath.steps[i];
+                       var newNodes = [];
+                       for (var j = 0; j < nodes.length; j++) {
+                               xpc.contextNode = nodes[j];
+                               switch (step.axis) {
+                                       case Step.ANCESTOR:
+                                               // look at all the ancestor nodes
+                                               if (xpc.contextNode === xpc.virtualRoot) {
+                                                       break;
+                                               }
+                                               var m;
+                                               if (xpc.contextNode.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
+                                                       m = this.getOwnerElement(xpc.contextNode);
+                                               } else {
+                                                       m = xpc.contextNode.parentNode;
+                                               }
+                                               while (m != null) {
+                                                       if (step.nodeTest.matches(m, xpc)) {
+                                                               newNodes.push(m);
+                                                       }
+                                                       if (m === xpc.virtualRoot) {
+                                                               break;
+                                                       }
+                                                       m = m.parentNode;
+                                               }
+                                               break;
+
+                                       case Step.ANCESTORORSELF:
+                                               // look at all the ancestor nodes and the current node
+                                               for (var m = xpc.contextNode; m != null; m = m.nodeType == 2 /*Node.ATTRIBUTE_NODE*/ ? this.getOwnerElement(m) : m.parentNode) {
+                                                       if (step.nodeTest.matches(m, xpc)) {
+                                                               newNodes.push(m);
+                                                       }
+                                                       if (m === xpc.virtualRoot) {
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.ATTRIBUTE:
+                                               // look at the attributes
+                                               var nnm = xpc.contextNode.attributes;
+                                               if (nnm != null) {
+                                                       for (var k = 0; k < nnm.length; k++) {
+                                                               var m = nnm.item(k);
+                                                               if (step.nodeTest.matches(m, xpc)) {
+                                                                       newNodes.push(m);
+                                                               }
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.CHILD:
+                                               // look at all child elements
+                                               for (var m = xpc.contextNode.firstChild; m != null; m = m.nextSibling) {
+                                                       if (step.nodeTest.matches(m, xpc)) {
+                                                               newNodes.push(m);
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.DESCENDANT:
+                                               // look at all descendant nodes
+                                               var st = [ xpc.contextNode.firstChild ];
+                                               while (st.length > 0) {
+                                                       for (var m = st.pop(); m != null; ) {
+                                                               if (step.nodeTest.matches(m, xpc)) {
+                                                                       newNodes.push(m);
+                                                               }
+                                                               if (m.firstChild != null) {
+                                                                       st.push(m.nextSibling);
+                                                                       m = m.firstChild;
+                                                               } else {
+                                                                       m = m.nextSibling;
+                                                               }
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.DESCENDANTORSELF:
+                                               // look at self
+                                               if (step.nodeTest.matches(xpc.contextNode, xpc)) {
+                                                       newNodes.push(xpc.contextNode);
+                                               }
+                                               // look at all descendant nodes
+                                               var st = [ xpc.contextNode.firstChild ];
+                                               while (st.length > 0) {
+                                                       for (var m = st.pop(); m != null; ) {
+                                                               if (step.nodeTest.matches(m, xpc)) {
+                                                                       newNodes.push(m);
+                                                               }
+                                                               if (m.firstChild != null) {
+                                                                       st.push(m.nextSibling);
+                                                                       m = m.firstChild;
+                                                               } else {
+                                                                       m = m.nextSibling;
+                                                               }
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.FOLLOWING:
+                                               if (xpc.contextNode === xpc.virtualRoot) {
+                                                       break;
+                                               }
+                                               var st = [];
+                                               if (xpc.contextNode.firstChild != null) {
+                                                       st.unshift(xpc.contextNode.firstChild);
+                                               } else {
+                                                       st.unshift(xpc.contextNode.nextSibling);
+                                               }
+                                               for (var m = xpc.contextNode.parentNode; m != null && m.nodeType != 9 /*Node.DOCUMENT_NODE*/ && m !== xpc.virtualRoot; m = m.parentNode) {
+                                                       st.unshift(m.nextSibling);
+                                               }
+                                               do {
+                                                       for (var m = st.pop(); m != null; ) {
+                                                               if (step.nodeTest.matches(m, xpc)) {
+                                                                       newNodes.push(m);
+                                                               }
+                                                               if (m.firstChild != null) {
+                                                                       st.push(m.nextSibling);
+                                                                       m = m.firstChild;
+                                                               } else {
+                                                                       m = m.nextSibling;
+                                                               }
+                                                       }
+                                               } while (st.length > 0);
+                                               break;
+                                               
+                                       case Step.FOLLOWINGSIBLING:
+                                               if (xpc.contextNode === xpc.virtualRoot) {
+                                                       break;
+                                               }
+                                               for (var m = xpc.contextNode.nextSibling; m != null; m = m.nextSibling) {
+                                                       if (step.nodeTest.matches(m, xpc)) {
+                                                               newNodes.push(m);
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.NAMESPACE:
+                                               var n = {};
+                                               if (xpc.contextNode.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+                                                       n["xml"] = XPath.XML_NAMESPACE_URI;
+                                                       n["xmlns"] = XPath.XMLNS_NAMESPACE_URI;
+                                                       for (var m = xpc.contextNode; m != null && m.nodeType == 1 /*Node.ELEMENT_NODE*/; m = m.parentNode) {
+                                                               for (var k = 0; k < m.attributes.length; k++) {
+                                                                       var attr = m.attributes.item(k);
+                                                                       var nm = String(attr.name);
+                                                                       if (nm == "xmlns") {
+                                                                               if (n[""] == undefined) {
+                                                                                       n[""] = attr.value;
+                                                                               }
+                                                                       } else if (nm.length > 6 && nm.substring(0, 6) == "xmlns:") {
+                                                                               var pre = nm.substring(6, nm.length);
+                                                                               if (n[pre] == undefined) {
+                                                                                       n[pre] = attr.value;
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                                       for (var pre in n) {
+                                                               var nsn = new NamespaceNode(pre, n[pre], xpc.contextNode);
+                                                               if (step.nodeTest.matches(nsn, xpc)) {
+                                                                       newNodes.push(nsn);
+                                                               }
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.PARENT:
+                                               m = null;
+                                               if (xpc.contextNode !== xpc.virtualRoot) {
+                                                       if (xpc.contextNode.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
+                                                               m = this.getOwnerElement(xpc.contextNode);
+                                                       } else {
+                                                               m = xpc.contextNode.parentNode;
+                                                       }
+                                               }
+                                               if (m != null && step.nodeTest.matches(m, xpc)) {
+                                                       newNodes.push(m);
+                                               }
+                                               break;
+
+                                       case Step.PRECEDING:
+                                               var st;
+                                               if (xpc.virtualRoot != null) {
+                                                       st = [ xpc.virtualRoot ];
+                                               } else {
+                                                       st = xpc.contextNode.nodeType == 9 /*Node.DOCUMENT_NODE*/
+                                                               ? [ xpc.contextNode ]
+                                                               : [ xpc.contextNode.ownerDocument ];
+                                               }
+                                               outer: while (st.length > 0) {
+                                                       for (var m = st.pop(); m != null; ) {
+                                                               if (m == xpc.contextNode) {
+                                                                       break outer;
+                                                               }
+                                                               if (step.nodeTest.matches(m, xpc)) {
+                                                                       newNodes.unshift(m);
+                                                               }
+                                                               if (m.firstChild != null) {
+                                                                       st.push(m.nextSibling);
+                                                                       m = m.firstChild;
+                                                               } else {
+                                                                       m = m.nextSibling;
+                                                               }
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.PRECEDINGSIBLING:
+                                               if (xpc.contextNode === xpc.virtualRoot) {
+                                                       break;
+                                               }
+                                               for (var m = xpc.contextNode.previousSibling; m != null; m = m.previousSibling) {
+                                                       if (step.nodeTest.matches(m, xpc)) {
+                                                               newNodes.push(m);
+                                                       }
+                                               }
+                                               break;
+
+                                       case Step.SELF:
+                                               if (step.nodeTest.matches(xpc.contextNode, xpc)) {
+                                                       newNodes.push(xpc.contextNode);
+                                               }
+                                               break;
+
+                                       default:
+                               }
+                       }
+                       nodes = newNodes;
+                       // apply each of the predicates in turn
+                       for (var j = 0; j < step.predicates.length; j++) {
+                               var pred = step.predicates[j];
+                               var newNodes = [];
+                               xpc.contextSize = nodes.length;
+                               for (xpc.contextPosition = 1; xpc.contextPosition <= xpc.contextSize; xpc.contextPosition++) {
+                                       xpc.contextNode = nodes[xpc.contextPosition - 1];
+                                       if (this.predicateMatches(pred, xpc)) {
+                                               newNodes.push(xpc.contextNode);
+                                       } else {
+                                       }
+                               }
+                               nodes = newNodes;
+                       }
+               }
+       }
+       var ns = new XNodeSet();
+       ns.addArray(nodes);
+       return ns;
+};
+
+PathExpr.prototype.predicateMatches = function(pred, c) {
+       var res = pred.evaluate(c);
+       if (Utilities.instance_of(res, XNumber)) {
+               return c.contextPosition == res.numberValue();
+       }
+       return res.booleanValue();
+};
+
+PathExpr.prototype.toString = function() {
+       if (this.filter != undefined) {
+               var s = this.filter.toString();
+               if (Utilities.instance_of(this.filter, XString)) {
+                       s = "'" + s + "'";
+               }
+               if (this.filterPredicates != undefined) {
+                       for (var i = 0; i < this.filterPredicates.length; i++) {
+                               s = s + "[" + this.filterPredicates[i].toString() + "]";
+                       }
+               }
+               if (this.locationPath != undefined) {
+                       if (!this.locationPath.absolute) {
+                               s += "/";
+                       }
+                       s += this.locationPath.toString();
+               }
+               return s;
+       }
+       return this.locationPath.toString();
+};
+
+PathExpr.prototype.getOwnerElement = function(n) {
+       // DOM 2 has ownerElement
+       if (n.ownerElement) {
+               return n.ownerElement;
+       }
+       // DOM 1 Internet Explorer can use selectSingleNode (ironically)
+       try {
+               if (n.selectSingleNode) {
+                       return n.selectSingleNode("..");
+               }
+       } catch (e) {
+       }
+       // Other DOM 1 implementations must use this egregious search
+       var doc = n.nodeType == 9 /*Node.DOCUMENT_NODE*/
+                       ? n
+                       : n.ownerDocument;
+       var elts = doc.getElementsByTagName("*");
+       for (var i = 0; i < elts.length; i++) {
+               var elt = elts.item(i);
+               var nnm = elt.attributes;
+               for (var j = 0; j < nnm.length; j++) {
+                       var an = nnm.item(j);
+                       if (an === n) {
+                               return elt;
+                       }
+               }
+       }
+       return null;
+};
+
+// LocationPath //////////////////////////////////////////////////////////////
+
+LocationPath.prototype = new Object();
+LocationPath.prototype.constructor = LocationPath;
+LocationPath.superclass = Object.prototype;
+
+function LocationPath(abs, steps) {
+       if (arguments.length > 0) {
+               this.init(abs, steps);
+       }
+}
+
+LocationPath.prototype.init = function(abs, steps) {
+       this.absolute = abs;
+       this.steps = steps;
+};
+
+LocationPath.prototype.toString = function() {
+       var s;
+       if (this.absolute) {
+               s = "/";
+       } else {
+               s = "";
+       }
+       for (var i = 0; i < this.steps.length; i++) {
+               if (i != 0) {
+                       s += "/";
+               }
+               s += this.steps[i].toString();
+       }
+       return s;
+};
+
+// Step //////////////////////////////////////////////////////////////////////
+
+Step.prototype = new Object();
+Step.prototype.constructor = Step;
+Step.superclass = Object.prototype;
+
+function Step(axis, nodetest, preds) {
+       if (arguments.length > 0) {
+               this.init(axis, nodetest, preds);
+       }
+}
+
+Step.prototype.init = function(axis, nodetest, preds) {
+       this.axis = axis;
+       this.nodeTest = nodetest;
+       this.predicates = preds;
+};
+
+Step.prototype.toString = function() {
+       var s;
+       switch (this.axis) {
+               case Step.ANCESTOR:
+                       s = "ancestor";
+                       break;
+               case Step.ANCESTORORSELF:
+                       s = "ancestor-or-self";
+                       break;
+               case Step.ATTRIBUTE:
+                       s = "attribute";
+                       break;
+               case Step.CHILD:
+                       s = "child";
+                       break;
+               case Step.DESCENDANT:
+                       s = "descendant";
+                       break;
+               case Step.DESCENDANTORSELF:
+                       s = "descendant-or-self";
+                       break;
+               case Step.FOLLOWING:
+                       s = "following";
+                       break;
+               case Step.FOLLOWINGSIBLING:
+                       s = "following-sibling";
+                       break;
+               case Step.NAMESPACE:
+                       s = "namespace";
+                       break;
+               case Step.PARENT:
+                       s = "parent";
+                       break;
+               case Step.PRECEDING:
+                       s = "preceding";
+                       break;
+               case Step.PRECEDINGSIBLING:
+                       s = "preceding-sibling";
+                       break;
+               case Step.SELF:
+                       s = "self";
+                       break;
+       }
+       s += "::";
+       s += this.nodeTest.toString();
+       for (var i = 0; i < this.predicates.length; i++) {
+               s += "[" + this.predicates[i].toString() + "]";
+       }
+       return s;
+};
+
+Step.ANCESTOR = 0;
+Step.ANCESTORORSELF = 1;
+Step.ATTRIBUTE = 2;
+Step.CHILD = 3;
+Step.DESCENDANT = 4;
+Step.DESCENDANTORSELF = 5;
+Step.FOLLOWING = 6;
+Step.FOLLOWINGSIBLING = 7;
+Step.NAMESPACE = 8;
+Step.PARENT = 9;
+Step.PRECEDING = 10;
+Step.PRECEDINGSIBLING = 11;
+Step.SELF = 12;
+
+// NodeTest //////////////////////////////////////////////////////////////////
+
+NodeTest.prototype = new Object();
+NodeTest.prototype.constructor = NodeTest;
+NodeTest.superclass = Object.prototype;
+
+function NodeTest(type, value) {
+       if (arguments.length > 0) {
+               this.init(type, value);
+       }
+}
+
+NodeTest.prototype.init = function(type, value) {
+       this.type = type;
+       this.value = value;
+};
+
+NodeTest.prototype.toString = function() {
+       switch (this.type) {
+               case NodeTest.NAMETESTANY:
+                       return "*";
+               case NodeTest.NAMETESTPREFIXANY:
+                       return this.value + ":*";
+               case NodeTest.NAMETESTRESOLVEDANY:
+                       return "{" + this.value + "}*";
+               case NodeTest.NAMETESTQNAME:
+                       return this.value;
+               case NodeTest.NAMETESTRESOLVEDNAME:
+                       return "{" + this.namespaceURI + "}" + this.value;
+               case NodeTest.COMMENT:
+                       return "comment()";
+               case NodeTest.TEXT:
+                       return "text()";
+               case NodeTest.PI:
+                       if (this.value != undefined) {
+                               return "processing-instruction(\"" + this.value + "\")";
+                       }
+                       return "processing-instruction()";
+               case NodeTest.NODE:
+                       return "node()";
+       }
+       return "<unknown nodetest type>";
+};
+
+NodeTest.prototype.matches = function(n, xpc) {
+       switch (this.type) {
+               case NodeTest.NAMETESTANY:
+                       if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
+                                       || n.nodeType == 1 /*Node.ELEMENT_NODE*/
+                                       || n.nodeType == XPathNamespace.XPATH_NAMESPACE_NODE) {
+                               return true;
+                       }
+                       return false;
+               case NodeTest.NAMETESTPREFIXANY:
+                       if ((n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/ || n.nodeType == 1 /*Node.ELEMENT_NODE*/)) {
+                               var ns = xpc.namespaceResolver.getNamespace(this.value, xpc.expressionContextNode);
+                               if (ns == null) {
+                                       throw new Error("Cannot resolve QName " + this.value);
+                               }
+                               return true;    
+                       }
+                       return false;
+               case NodeTest.NAMETESTQNAME:
+                       if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
+                                       || n.nodeType == 1 /*Node.ELEMENT_NODE*/
+                                       || n.nodeType == XPathNamespace.XPATH_NAMESPACE_NODE) {
+                               var test = Utilities.resolveQName(this.value, xpc.namespaceResolver, xpc.expressionContextNode, false);
+                               if (test[0] == null) {
+                                       throw new Error("Cannot resolve QName " + this.value);
+                               }
+                               test[0] = String(test[0]);
+                               test[1] = String(test[1]);
+                               if (test[0] == "") {
+                                       test[0] = null;
+                               }
+                               var node = Utilities.resolveQName(n.nodeName, xpc.namespaceResolver, n, n.nodeType == 1 /*Node.ELEMENT_NODE*/);
+                               node[0] = String(node[0]);
+                               node[1] = String(node[1]);
+                               if (node[0] == "") {
+                                       if (n.namespaceURI) {
+                                               node[0] = n.namespaceURI;
+                                       } else {
+                                               node[0] = null;
+                                       }
+                               }
+                               if (xpc.caseInsensitive) {
+                                       return test[0] == node[0] && String(test[1]).toLowerCase() == String(node[1]).toLowerCase();
+                               }
+                               return test[0] == node[0] && test[1] == node[1];
+                       }
+                       return false;
+               case NodeTest.COMMENT:
+                       return n.nodeType == 8 /*Node.COMMENT_NODE*/;
+               case NodeTest.TEXT:
+                       return n.nodeType == 3 /*Node.TEXT_NODE*/ || n.nodeType == 4 /*Node.CDATA_SECTION_NODE*/;
+               case NodeTest.PI:
+                       return n.nodeType == 7 /*Node.PROCESSING_INSTRUCTION_NODE*/
+                               && (this.value == null || n.nodeName == this.value);
+               case NodeTest.NODE:
+                       return n.nodeType == 9 /*Node.DOCUMENT_NODE*/
+                               || n.nodeType == 1 /*Node.ELEMENT_NODE*/
+                               || n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
+                               || n.nodeType == 3 /*Node.TEXT_NODE*/
+                               || n.nodeType == 4 /*Node.CDATA_SECTION_NODE*/
+                               || n.nodeType == 8 /*Node.COMMENT_NODE*/
+                               || n.nodeType == 7 /*Node.PROCESSING_INSTRUCTION_NODE*/;
+       }
+       return false;
+};
+
+NodeTest.NAMETESTANY = 0;
+NodeTest.NAMETESTPREFIXANY = 1;
+NodeTest.NAMETESTQNAME = 2;
+NodeTest.COMMENT = 3;
+NodeTest.TEXT = 4;
+NodeTest.PI = 5;
+NodeTest.NODE = 6;
+
+// VariableReference /////////////////////////////////////////////////////////
+
+VariableReference.prototype = new Expression();
+VariableReference.prototype.constructor = VariableReference;
+VariableReference.superclass = Expression.prototype;
+
+function VariableReference(v) {
+       if (arguments.length > 0) {
+               this.init(v);
+       }
+}
+
+VariableReference.prototype.init = function(v) {
+       this.variable = v;
+};
+
+VariableReference.prototype.toString = function() {
+       return "$" + this.variable;
+};
+
+VariableReference.prototype.evaluate = function(c) {
+       return c.variableResolver.getVariable(this.variable, c);
+};
+
+// FunctionCall //////////////////////////////////////////////////////////////
+
+FunctionCall.prototype = new Expression();
+FunctionCall.prototype.constructor = FunctionCall;
+FunctionCall.superclass = Expression.prototype;
+
+function FunctionCall(fn, args) {
+       if (arguments.length > 0) {
+               this.init(fn, args);
+       }
+}
+
+FunctionCall.prototype.init = function(fn, args) {
+       this.functionName = fn;
+       this.arguments = args;
+};
+
+FunctionCall.prototype.toString = function() {
+       var s = this.functionName + "(";
+       for (var i = 0; i < this.arguments.length; i++) {
+               if (i > 0) {
+                       s += ", ";
+               }
+               s += this.arguments[i].toString();
+       }
+       return s + ")";
+};
+
+FunctionCall.prototype.evaluate = function(c) {
+       var f = c.functionResolver.getFunction(this.functionName, c);
+       if (f == undefined) {
+               throw new Error("Unknown function " + this.functionName);
+       }
+       var a = [c].concat(this.arguments);
+       return f.apply(c.functionResolver.thisArg, a);
+};
+
+// XString ///////////////////////////////////////////////////////////////////
+
+XString.prototype = new Expression();
+XString.prototype.constructor = XString;
+XString.superclass = Expression.prototype;
+
+function XString(s) {
+       if (arguments.length > 0) {
+               this.init(s);
+       }
+}
+
+XString.prototype.init = function(s) {
+       this.str = s;
+};
+
+XString.prototype.toString = function() {
+       return this.str;
+};
+
+XString.prototype.evaluate = function(c) {
+       return this;
+};
+
+XString.prototype.string = function() {
+       return this;
+};
+
+XString.prototype.number = function() {
+       return new XNumber(this.str);
+};
+
+XString.prototype.bool = function() {
+       return new XBoolean(this.str);
+};
+
+XString.prototype.nodeset = function() {
+       throw new Error("Cannot convert string to nodeset");
+};
+
+XString.prototype.stringValue = function() {
+       return this.str;
+};
+
+XString.prototype.numberValue = function() {
+       return this.number().numberValue();
+};
+
+XString.prototype.booleanValue = function() {
+       return this.bool().booleanValue();
+};
+
+XString.prototype.equals = function(r) {
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.bool().equals(r);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.number().equals(r);
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithString(this, Operators.equals);
+       }
+       return new XBoolean(this.str == r.str);
+};
+
+XString.prototype.notequal = function(r) {
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.bool().notequal(r);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.number().notequal(r);
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithString(this, Operators.notequal);
+       }
+       return new XBoolean(this.str != r.str);
+};
+
+XString.prototype.lessthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.greaterthanorequal);
+       }
+       return this.number().lessthan(r.number());
+};
+
+XString.prototype.greaterthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.lessthanorequal);
+       }
+       return this.number().greaterthan(r.number());
+};
+
+XString.prototype.lessthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.greaterthan);
+       }
+       return this.number().lessthanorequal(r.number());
+};
+
+XString.prototype.greaterthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.lessthan);
+       }
+       return this.number().greaterthanorequal(r.number());
+};
+
+// XNumber ///////////////////////////////////////////////////////////////////
+
+XNumber.prototype = new Expression();
+XNumber.prototype.constructor = XNumber;
+XNumber.superclass = Expression.prototype;
+
+function XNumber(n) {
+       if (arguments.length > 0) {
+               this.init(n);
+       }
+}
+
+XNumber.prototype.init = function(n) {
+       this.num = Number(n);
+};
+
+XNumber.prototype.toString = function() {
+       return this.num;
+};
+
+XNumber.prototype.evaluate = function(c) {
+       return this;
+};
+
+XNumber.prototype.string = function() {
+       return new XString(this.num);
+};
+
+XNumber.prototype.number = function() {
+       return this;
+};
+
+XNumber.prototype.bool = function() {
+       return new XBoolean(this.num);
+};
+
+XNumber.prototype.nodeset = function() {
+       throw new Error("Cannot convert number to nodeset");
+};
+
+XNumber.prototype.stringValue = function() {
+       return this.string().stringValue();
+};
+
+XNumber.prototype.numberValue = function() {
+       return this.num;
+};
+
+XNumber.prototype.booleanValue = function() {
+       return this.bool().booleanValue();
+};
+
+XNumber.prototype.negate = function() {
+       return new XNumber(-this.num);
+};
+
+XNumber.prototype.equals = function(r) {
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.bool().equals(r);
+       }
+       if (Utilities.instance_of(r, XString)) {
+               return this.equals(r.number());
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.equals);
+       }
+       return new XBoolean(this.num == r.num);
+};
+
+XNumber.prototype.notequal = function(r) {
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.bool().notequal(r);
+       }
+       if (Utilities.instance_of(r, XString)) {
+               return this.notequal(r.number());
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.notequal);
+       }
+       return new XBoolean(this.num != r.num);
+};
+
+XNumber.prototype.lessthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.greaterthanorequal);
+       }
+       if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
+               return this.lessthan(r.number());
+       }
+       return new XBoolean(this.num < r.num);
+};
+
+XNumber.prototype.greaterthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.lessthanorequal);
+       }
+       if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
+               return this.greaterthan(r.number());
+       }
+       return new XBoolean(this.num > r.num);
+};
+
+XNumber.prototype.lessthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.greaterthan);
+       }
+       if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
+               return this.lessthanorequal(r.number());
+       }
+       return new XBoolean(this.num <= r.num);
+};
+
+XNumber.prototype.greaterthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this, Operators.lessthan);
+       }
+       if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
+               return this.greaterthanorequal(r.number());
+       }
+       return new XBoolean(this.num >= r.num);
+};
+
+XNumber.prototype.plus = function(r) {
+       return new XNumber(this.num + r.num);
+};
+
+XNumber.prototype.minus = function(r) {
+       return new XNumber(this.num - r.num);
+};
+
+XNumber.prototype.multiply = function(r) {
+       return new XNumber(this.num * r.num);
+};
+
+XNumber.prototype.div = function(r) {
+       return new XNumber(this.num / r.num);
+};
+
+XNumber.prototype.mod = function(r) {
+       return new XNumber(this.num % r.num);
+};
+
+// XBoolean //////////////////////////////////////////////////////////////////
+
+XBoolean.prototype = new Expression();
+XBoolean.prototype.constructor = XBoolean;
+XBoolean.superclass = Expression.prototype;
+
+function XBoolean(b) {
+       if (arguments.length > 0) {
+               this.init(b);
+       }
+}
+
+XBoolean.prototype.init = function(b) {
+       this.b = Boolean(b);
+};
+
+XBoolean.prototype.toString = function() {
+       return this.b.toString();
+};
+
+XBoolean.prototype.evaluate = function(c) {
+       return this;
+};
+
+XBoolean.prototype.string = function() {
+       return new XString(this.b);
+};
+
+XBoolean.prototype.number = function() {
+       return new XNumber(this.b);
+};
+
+XBoolean.prototype.bool = function() {
+       return this;
+};
+
+XBoolean.prototype.nodeset = function() {
+       throw new Error("Cannot convert boolean to nodeset");
+};
+
+XBoolean.prototype.stringValue = function() {
+       return this.string().stringValue();
+};
+
+XBoolean.prototype.numberValue = function() {
+       return this.num().numberValue();
+};
+
+XBoolean.prototype.booleanValue = function() {
+       return this.b;
+};
+
+XBoolean.prototype.not = function() {
+       return new XBoolean(!this.b);
+};
+
+XBoolean.prototype.equals = function(r) {
+       if (Utilities.instance_of(r, XString) || Utilities.instance_of(r, XNumber)) {
+               return this.equals(r.bool());
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithBoolean(this, Operators.equals);
+       }
+       return new XBoolean(this.b == r.b);
+};
+
+XBoolean.prototype.notequal = function(r) {
+       if (Utilities.instance_of(r, XString) || Utilities.instance_of(r, XNumber)) {
+               return this.notequal(r.bool());
+       }
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithBoolean(this, Operators.notequal);
+       }
+       return new XBoolean(this.b != r.b);
+};
+
+XBoolean.prototype.lessthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.greaterthanorequal);
+       }
+       return this.number().lessthan(r.number());
+};
+
+XBoolean.prototype.greaterthan = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.lessthanorequal);
+       }
+       return this.number().greaterthan(r.number());
+};
+
+XBoolean.prototype.lessthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.greaterthan);
+       }
+       return this.number().lessthanorequal(r.number());
+};
+
+XBoolean.prototype.greaterthanorequal = function(r) {
+       if (Utilities.instance_of(r, XNodeSet)) {
+               return r.compareWithNumber(this.number(), Operators.lessthan);
+       }
+       return this.number().greaterthanorequal(r.number());
+};
+
+// AVLTree ///////////////////////////////////////////////////////////////////
+
+AVLTree.prototype = new Object();
+AVLTree.prototype.constructor = AVLTree;
+AVLTree.superclass = Object.prototype;
+
+function AVLTree(n) {
+       this.init(n);
+}
+
+AVLTree.prototype.init = function(n) {
+       this.left = null;
+    this.right = null;
+       this.node = n;
+       this.depth = 1;
+};
+
+AVLTree.prototype.balance = function() {
+    var ldepth = this.left  == null ? 0 : this.left.depth;
+    var rdepth = this.right == null ? 0 : this.right.depth;
+
+       if (ldepth > rdepth + 1) {
+        // LR or LL rotation
+        var lldepth = this.left.left  == null ? 0 : this.left.left.depth;
+        var lrdepth = this.left.right == null ? 0 : this.left.right.depth;
+
+        if (lldepth < lrdepth) {
+            // LR rotation consists of a RR rotation of the left child
+            this.left.rotateRR();
+            // plus a LL rotation of this node, which happens anyway 
+        }
+        this.rotateLL();       
+    } else if (ldepth + 1 < rdepth) {
+        // RR or RL rorarion
+               var rrdepth = this.right.right == null ? 0 : this.right.right.depth;
+               var rldepth = this.right.left  == null ? 0 : this.right.left.depth;
+        
+        if (rldepth > rrdepth) {
+            // RR rotation consists of a LL rotation of the right child
+            this.right.rotateLL();
+            // plus a RR rotation of this node, which happens anyway 
+        }
+        this.rotateRR();
+    }       
+};
+
+AVLTree.prototype.rotateLL = function() {
+    // the left side is too long => rotate from the left (_not_ leftwards)
+    var nodeBefore = this.node;
+    var rightBefore = this.right;
+    this.node = this.left.node;
+    this.right = this.left;
+    this.left = this.left.left;
+    this.right.left = this.right.right;
+    this.right.right = rightBefore;
+    this.right.node = nodeBefore;
+    this.right.updateInNewLocation();
+    this.updateInNewLocation();
+};
+
+AVLTree.prototype.rotateRR = function() {
+    // the right side is too long => rotate from the right (_not_ rightwards)
+    var nodeBefore = this.node;
+    var leftBefore = this.left;
+    this.node = this.right.node;
+    this.left = this.right;
+    this.right = this.right.right;
+    this.left.right = this.left.left;
+    this.left.left = leftBefore;
+    this.left.node = nodeBefore;
+    this.left.updateInNewLocation();
+    this.updateInNewLocation();
+}; 
+       
+AVLTree.prototype.updateInNewLocation = function() {
+    this.getDepthFromChildren();
+};
+
+AVLTree.prototype.getDepthFromChildren = function() {
+    this.depth = this.node == null ? 0 : 1;
+    if (this.left != null) {
+        this.depth = this.left.depth + 1;
+    }
+    if (this.right != null && this.depth <= this.right.depth) {
+        this.depth = this.right.depth + 1;
+    }
+};
+
+AVLTree.prototype.order = function(n1, n2) {
+       if (n1 === n2) {
+               return 0;
+       }
+       var d1 = 0;
+       var d2 = 0;
+       for (var m1 = n1; m1 != null; m1 = m1.parentNode) {
+               d1++;
+       }
+       for (var m2 = n2; m2 != null; m2 = m2.parentNode) {
+               d2++;
+       }
+       if (d1 > d2) {
+               while (d1 > d2) {
+                       n1 = n1.parentNode;
+                       d1--;
+               }
+               if (n1 == n2) {
+                       return 1;
+               }
+       } else if (d2 > d1) {
+               while (d2 > d1) {
+                       n2 = n2.parentNode;
+                       d2--;
+               }
+               if (n1 == n2) {
+                       return -1;
+               }
+       }
+       while (n1.parentNode != n2.parentNode) {
+               n1 = n1.parentNode;
+               n2 = n2.parentNode;
+       }
+       while (n1.previousSibling != null && n2.previousSibling != null) {
+               n1 = n1.previousSibling;
+               n2 = n2.previousSibling;
+       }
+       if (n1.previousSibling == null) {
+               return -1;
+       }
+       return 1;
+};
+
+AVLTree.prototype.add = function(n)  {
+       if (n === this.node) {
+        return false;
+    }
+       
+       var o = this.order(n, this.node);
+       
+    var ret = false;
+    if (o == -1) {
+        if (this.left == null) {
+            this.left = new AVLTree(n);
+            ret = true;
+        } else {
+            ret = this.left.add(n);
+            if (ret) {
+                this.balance();
+            }
+        }
+    } else if (o == 1) {
+        if (this.right == null) {
+            this.right = new AVLTree(n);
+            ret = true;
+        } else {
+            ret = this.right.add(n);
+            if (ret) {
+                this.balance();
+            }
+        }
+    }
+       
+    if (ret) {
+        this.getDepthFromChildren();
+    }
+    return ret;
+};
+
+// XNodeSet //////////////////////////////////////////////////////////////////
+
+XNodeSet.prototype = new Expression();
+XNodeSet.prototype.constructor = XNodeSet;
+XNodeSet.superclass = Expression.prototype;
+
+function XNodeSet() {
+       this.init();
+}
+
+XNodeSet.prototype.init = function() {
+       this.tree = null;
+       this.size = 0;
+};
+
+XNodeSet.prototype.toString = function() {
+       var p = this.first();
+       if (p == null) {
+               return "";
+       }
+       return this.stringForNode(p);
+};
+
+XNodeSet.prototype.evaluate = function(c) {
+       return this;
+};
+
+XNodeSet.prototype.string = function() {
+       return new XString(this.toString());
+};
+
+XNodeSet.prototype.stringValue = function() {
+       return this.toString();
+};
+
+XNodeSet.prototype.number = function() {
+       return new XNumber(this.string());
+};
+
+XNodeSet.prototype.numberValue = function() {
+       return Number(this.string());
+};
+
+XNodeSet.prototype.bool = function() {
+       return new XBoolean(this.tree != null);
+};
+
+XNodeSet.prototype.booleanValue = function() {
+       return this.tree != null;
+};
+
+XNodeSet.prototype.nodeset = function() {
+       return this;
+};
+
+XNodeSet.prototype.stringForNode = function(n) {
+       if (n.nodeType == 9 /*Node.DOCUMENT_NODE*/) {
+               n = n.documentElement;
+       }
+       if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+               return this.stringForNodeRec(n);
+       }
+       if (n.isNamespaceNode) {
+               return n.namespace;
+       }
+       return n.nodeValue;
+};
+
+XNodeSet.prototype.stringForNodeRec = function(n) {
+       var s = "";
+       for (var n2 = n.firstChild; n2 != null; n2 = n2.nextSibling) {
+               if (n2.nodeType == 3 /*Node.TEXT_NODE*/) {
+                       s += n2.nodeValue;
+               } else if (n2.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+                       s += this.stringForNodeRec(n2);
+               }
+       }
+       return s;
+};
+
+XNodeSet.prototype.first = function() {
+       var p = this.tree;
+       if (p == null) {
+               return null;
+       }
+       while (p.left != null) {
+               p = p.left;
+       }
+       return p.node;
+};
+
+XNodeSet.prototype.add = function(n) {
+    var added;
+    if (this.tree == null) {
+        this.tree = new AVLTree(n);
+        added = true;
+    } else {
+        added = this.tree.add(n);
+    }
+    if (added) {
+        this.size++;
+    }
+};
+
+XNodeSet.prototype.addArray = function(ns) {
+       for (var i = 0; i < ns.length; i++) {
+               this.add(ns[i]);
+       }
+};
+
+XNodeSet.prototype.toArray = function() {
+       var a = [];
+       this.toArrayRec(this.tree, a);
+       return a;
+};
+
+XNodeSet.prototype.toArrayRec = function(t, a) {
+       if (t != null) {
+               this.toArrayRec(t.left, a);
+               a.push(t.node);
+               this.toArrayRec(t.right, a);
+       }
+};
+
+XNodeSet.prototype.compareWithString = function(r, o) {
+       var a = this.toArray();
+       for (var i = 0; i < a.length; i++) {
+               var n = a[i];
+               var l = new XString(this.stringForNode(n));
+               var res = o(l, r);
+               if (res.booleanValue()) {
+                       return res;
+               }
+       }
+       return new XBoolean(false);
+};
+
+XNodeSet.prototype.compareWithNumber = function(r, o) {
+       var a = this.toArray();
+       for (var i = 0; i < a.length; i++) {
+               var n = a[i];
+               var l = new XNumber(this.stringForNode(n));
+               var res = o(l, r);
+               if (res.booleanValue()) {
+                       return res;
+               }
+       }
+       return new XBoolean(false);
+};
+
+XNodeSet.prototype.compareWithBoolean = function(r, o) {
+       return o(this.bool(), r);
+};
+
+XNodeSet.prototype.compareWithNodeSet = function(r, o) {
+       var a = this.toArray();
+       for (var i = 0; i < a.length; i++) {
+               var n = a[i];
+               var l = new XString(this.stringForNode(n));
+               var b = r.toArray();
+               for (var j = 0; j < b.length; j++) {
+                       var n2 = b[j];
+                       var r = new XString(this.stringForNode(n2));
+                       var res = o(l, r);
+                       if (res.booleanValue()) {
+                               return res;
+                       }
+               }
+       }
+       return new XBoolean(false);
+};
+
+XNodeSet.prototype.equals = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithString(r, Operators.equals);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.equals);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.equals);
+       }
+       return this.compareWithNodeSet(r, Operators.equals);
+};
+
+XNodeSet.prototype.notequal = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithString(r, Operators.notequal);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.notequal);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.notequal);
+       }
+       return this.compareWithNodeSet(r, Operators.notequal);
+};
+
+XNodeSet.prototype.lessthan = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithNumber(r.number(), Operators.lessthan);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.lessthan);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.lessthan);
+       }
+       return this.compareWithNodeSet(r, Operators.lessthan);
+};
+
+XNodeSet.prototype.greaterthan = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithNumber(r.number(), Operators.greaterthan);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.greaterthan);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.greaterthan);
+       }
+       return this.compareWithNodeSet(r, Operators.greaterthan);
+};
+
+XNodeSet.prototype.lessthanorequal = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithNumber(r.number(), Operators.lessthanorequal);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.lessthanorequal);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.lessthanorequal);
+       }
+       return this.compareWithNodeSet(r, Operators.lessthanorequal);
+};
+
+XNodeSet.prototype.greaterthanorequal = function(r) {
+       if (Utilities.instance_of(r, XString)) {
+               return this.compareWithNumber(r.number(), Operators.greaterthanorequal);
+       }
+       if (Utilities.instance_of(r, XNumber)) {
+               return this.compareWithNumber(r, Operators.greaterthanorequal);
+       }
+       if (Utilities.instance_of(r, XBoolean)) {
+               return this.compareWithBoolean(r, Operators.greaterthanorequal);
+       }
+       return this.compareWithNodeSet(r, Operators.greaterthanorequal);
+};
+
+XNodeSet.prototype.union = function(r) {
+       var ns = new XNodeSet();
+       ns.tree = this.tree;
+       ns.size = this.size;
+       ns.addArray(r.toArray());
+       return ns;
+};
+
+// XPathNamespace ////////////////////////////////////////////////////////////
+
+XPathNamespace.prototype = new Object();
+XPathNamespace.prototype.constructor = XPathNamespace;
+XPathNamespace.superclass = Object.prototype;
+
+function XPathNamespace(pre, ns, p) {
+       this.isXPathNamespace = true;
+       this.ownerDocument = p.ownerDocument;
+       this.nodeName = "#namespace";
+       this.prefix = pre;
+       this.localName = pre;
+       this.namespaceURI = ns;
+       this.nodeValue = ns;
+       this.ownerElement = p;
+       this.nodeType = XPathNamespace.XPATH_NAMESPACE_NODE;
+}
+
+XPathNamespace.prototype.toString = function() {
+       return "{ \"" + this.prefix + "\", \"" + this.namespaceURI + "\" }";
+};
+
+// Operators /////////////////////////////////////////////////////////////////
+
+var Operators = new Object();
+
+Operators.equals = function(l, r) {
+       return l.equals(r);
+};
+
+Operators.notequal = function(l, r) {
+       return l.notequal(r);
+};
+
+Operators.lessthan = function(l, r) {
+       return l.lessthan(r);
+};
+
+Operators.greaterthan = function(l, r) {
+       return l.greaterthan(r);
+};
+
+Operators.lessthanorequal = function(l, r) {
+       return l.lessthanorequal(r);
+};
+
+Operators.greaterthanorequal = function(l, r) {
+       return l.greaterthanorequal(r);
+};
+
+// XPathContext //////////////////////////////////////////////////////////////
+
+XPathContext.prototype = new Object();
+XPathContext.prototype.constructor = XPathContext;
+XPathContext.superclass = Object.prototype;
+
+function XPathContext(vr, nr, fr) {
+       this.variableResolver = vr != null ? vr : new VariableResolver();
+       this.namespaceResolver = nr != null ? nr : new NamespaceResolver();
+       this.functionResolver = fr != null ? fr : new FunctionResolver();
+}
+
+// VariableResolver //////////////////////////////////////////////////////////
+
+VariableResolver.prototype = new Object();
+VariableResolver.prototype.constructor = VariableResolver;
+VariableResolver.superclass = Object.prototype;
+
+function VariableResolver() {
+}
+
+VariableResolver.prototype.getVariable = function(vn, c) {
+       var parts = Utilities.splitQName(vn);
+       if (parts[0] != null) {
+               parts[0] = c.namespaceResolver.getNamespace(parts[0], c.expressionContextNode);
+        if (parts[0] == null) {
+            throw new Error("Cannot resolve QName " + fn);
+        }
+       }
+       return this.getVariableWithName(parts[0], parts[1], c.expressionContextNode);
+};
+
+VariableResolver.prototype.getVariableWithName = function(ns, ln, c) {
+       return null;
+};
+
+// FunctionResolver //////////////////////////////////////////////////////////
+
+FunctionResolver.prototype = new Object();
+FunctionResolver.prototype.constructor = FunctionResolver;
+FunctionResolver.superclass = Object.prototype;
+
+function FunctionResolver(thisArg) {
+       this.thisArg = thisArg != null ? thisArg : Functions;
+       this.functions = new Object();
+       this.addStandardFunctions();
+}
+
+FunctionResolver.prototype.addStandardFunctions = function() {
+       this.functions["{}last"] = Functions.last;
+       this.functions["{}position"] = Functions.position;
+       this.functions["{}count"] = Functions.count;
+       this.functions["{}id"] = Functions.id;
+       this.functions["{}local-name"] = Functions.localName;
+       this.functions["{}namespace-uri"] = Functions.namespaceURI;
+       this.functions["{}name"] = Functions.name;
+       this.functions["{}string"] = Functions.string;
+       this.functions["{}concat"] = Functions.concat;
+       this.functions["{}starts-with"] = Functions.startsWith;
+       this.functions["{}contains"] = Functions.contains;
+       this.functions["{}substring-before"] = Functions.substringBefore;
+       this.functions["{}substring-after"] = Functions.substringAfter;
+       this.functions["{}substring"] = Functions.substring;
+       this.functions["{}string-length"] = Functions.stringLength;
+       this.functions["{}normalize-space"] = Functions.normalizeSpace;
+       this.functions["{}translate"] = Functions.translate;
+       this.functions["{}boolean"] = Functions.boolean_;
+       this.functions["{}not"] = Functions.not;
+       this.functions["{}true"] = Functions.true_;
+       this.functions["{}false"] = Functions.false_;
+       this.functions["{}lang"] = Functions.lang;
+       this.functions["{}number"] = Functions.number;
+       this.functions["{}sum"] = Functions.sum;
+       this.functions["{}floor"] = Functions.floor;
+       this.functions["{}ceiling"] = Functions.ceiling;
+       this.functions["{}round"] = Functions.round;
+};
+
+FunctionResolver.prototype.addFunction = function(ns, ln, f) {
+       this.functions["{" + ns + "}" + ln] = f;
+};
+
+FunctionResolver.prototype.getFunction = function(fn, c) {
+       var parts = Utilities.resolveQName(fn, c.namespaceResolver, c.contextNode, false);
+    if (parts[0] == null) {
+        throw new Error("Cannot resolve QName " + fn);
+    }
+       return this.getFunctionWithName(parts[0], parts[1], c.contextNode);
+};
+
+FunctionResolver.prototype.getFunctionWithName = function(ns, ln, c) {
+       return this.functions["{" + ns + "}" + ln];
+};
+
+// NamespaceResolver /////////////////////////////////////////////////////////
+
+NamespaceResolver.prototype = new Object();
+NamespaceResolver.prototype.constructor = NamespaceResolver;
+NamespaceResolver.superclass = Object.prototype;
+
+function NamespaceResolver() {
+}
+
+NamespaceResolver.prototype.getNamespace = function(prefix, n) {
+       if (prefix == "xml") {
+               return XPath.XML_NAMESPACE_URI;
+       } else if (prefix == "xmlns") {
+               return XPath.XMLNS_NAMESPACE_URI;
+       }
+       if (n.nodeType == 9 /*Node.DOCUMENT_NODE*/) {
+               n = n.documentElement;
+       } else if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
+               n = PathExpr.prototype.getOwnerElement(n);
+       } else if (n.nodeType != 1 /*Node.ELEMENT_NODE*/) {
+               n = n.parentNode;
+       }
+       while (n != null && n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+               var nnm = n.attributes;
+               for (var i = 0; i < nnm.length; i++) {
+                       var a = nnm.item(i);
+                       var aname = a.nodeName;
+                       if (aname == "xmlns" && prefix == ""
+                                       || aname == "xmlns:" + prefix) {
+                               return String(a.nodeValue);
+                       }
+               }
+               n = n.parentNode;
+       }
+       return null;
+};
+
+// Functions /////////////////////////////////////////////////////////////////
+
+Functions = new Object();
+
+Functions.last = function() {
+       var c = arguments[0];
+       if (arguments.length != 1) {
+               throw new Error("Function last expects ()");
+       }
+       return new XNumber(c.contextSize);
+};
+
+Functions.position = function() {
+       var c = arguments[0];
+       if (arguments.length != 1) {
+               throw new Error("Function position expects ()");
+       }
+       return new XNumber(c.contextPosition);
+};
+
+Functions.count = function() {
+       var c = arguments[0];
+       var ns;
+       if (arguments.length != 2 || !Utilities.instance_of(ns = arguments[1].evaluate(c), XNodeSet)) {
+               throw new Error("Function count expects (node-set)");
+       }
+       return new XNumber(ns.size);
+};
+
+Functions.id = function() {
+       var c = arguments[0];
+       var id;
+       if (arguments.length != 2) {
+               throw new Error("Function id expects (object)");
+       }
+       id = arguments[1].evaluate(c);
+       if (Utilities.instance_of(id, XNodeSet)) {
+               id = id.toArray().join(" ");
+       } else {
+               id = id.stringValue();
+       }
+       var ids = id.split(/[\x0d\x0a\x09\x20]+/);
+       var count = 0;
+       var ns = new XNodeSet();
+       var doc = c.contextNode.nodeType == 9 /*Node.DOCUMENT_NODE*/
+                       ? c.contextNode
+                       : c.contextNode.ownerDocument;
+       for (var i = 0; i < ids.length; i++) {
+               var n;
+               if (doc.getElementById) {
+                       n = doc.getElementById(ids[i]);
+               } else {
+                       n = Utilities.getElementById(doc, ids[i]);
+               }
+               if (n != null) {
+                       ns.add(n);
+                       count++;
+               }
+       }
+       return ns;
+};
+
+Functions.localName = function() {
+       var c = arguments[0];
+       var n;
+       if (arguments.length == 1) {
+               n = c.contextNode;
+       } else if (arguments.length == 2) {
+               n = arguments[1].evaluate(c).first();
+       } else {
+               throw new Error("Function local-name expects (node-set?)");
+       }
+       if (n == null) {
+               return new XString("");
+       }
+       return new XString(n.localName ? n.localName : n.baseName);
+};
+
+Functions.namespaceURI = function() {
+       var c = arguments[0];
+       var n;
+       if (arguments.length == 1) {
+               n = c.contextNode;
+       } else if (arguments.length == 2) {
+               n = arguments[1].evaluate(c).first();
+       } else {
+               throw new Error("Function namespace-uri expects (node-set?)");
+       }
+       if (n == null) {
+               return new XString("");
+       }
+       return new XString(n.namespaceURI);
+};
+
+Functions.name = function() {
+       var c = arguments[0];
+       var n;
+       if (arguments.length == 1) {
+               n = c.contextNode;
+       } else if (arguments.length == 2) {
+               n = arguments[1].evaluate(c).first();
+       } else {
+               throw new Error("Function name expects (node-set?)");
+       }
+       if (n == null) {
+               return new XString("");
+       }
+       if (n.nodeType == 1 /*Node.ELEMENT_NODE*/ || n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
+               return new XString(n.nodeName);
+       } else if (n.localName == null) {
+               return new XString("");
+       } else {
+               return new XString(n.localName);
+       }
+};
+
+Functions.string = function() {
+       var c = arguments[0];
+       if (arguments.length == 1) {
+               return XNodeSet.prototype.stringForNode(c.contextNode);
+       } else if (arguments.length == 2) {
+               return arguments[1].evaluate(c).string();
+       }
+       throw new Error("Function string expects (object?)");
+};
+
+Functions.concat = function() {
+       var c = arguments[0];
+       if (arguments.length < 3) {
+               throw new Error("Function concat expects (string, string, string*)");
+       }
+       var s = "";
+       for (var i = 1; i < arguments.length; i++) {
+               s += arguments[i].evaluate(c).stringValue();
+       }
+       return new XString(s);
+};
+
+Functions.startsWith = function() {
+       var c = arguments[0];
+       if (arguments.length != 3) {
+               throw new Error("Function startsWith expects (string, string)");
+       }
+       var s1 = arguments[1].evaluate(c).stringValue();
+       var s2 = arguments[2].evaluate(c).stringValue();
+       return new XBoolean(s1.substring(0, s2.length) == s2);
+};
+
+Functions.contains = function() {
+       var c = arguments[0];
+       if (arguments.length != 3) {
+               throw new Error("Function contains expects (string, string)");
+       }
+       var s1 = arguments[1].evaluate(c).stringValue();
+       var s2 = arguments[2].evaluate(c).stringValue();
+       return new XBoolean(s1.indexOf(s2) != -1);
+};
+
+Functions.substringBefore = function() {
+       var c = arguments[0];
+       if (arguments.length != 3) {
+               throw new Error("Function substring-before expects (string, string)");
+       }
+       var s1 = arguments[1].evaluate(c).stringValue();
+       var s2 = arguments[2].evaluate(c).stringValue();
+       return new XString(s1.substring(0, s1.indexOf(s2)));
+};
+
+Functions.substringAfter = function() {
+       var c = arguments[0];
+       if (arguments.length != 3) {
+               throw new Error("Function substring-after expects (string, string)");
+       }
+       var s1 = arguments[1].evaluate(c).stringValue();
+       var s2 = arguments[2].evaluate(c).stringValue();
+       if (s2.length == 0) {
+               return new XString(s1);
+       }
+       var i = s1.indexOf(s2);
+       if (i == -1) {
+               return new XString("");
+       }
+       return new XString(s1.substring(s1.indexOf(s2) + 1));
+};
+
+Functions.substring = function() {
+       var c = arguments[0];
+       if (!(arguments.length == 3 || arguments.length == 4)) {
+               throw new Error("Function substring expects (string, number, number?)");
+       }
+       var s = arguments[1].evaluate(c).stringValue();
+       var n1 = Math.round(arguments[2].evaluate(c).numberValue()) - 1;
+       var n2 = arguments.length == 4 ? n1 + Math.round(arguments[3].evaluate(c).numberValue()) : undefined;
+       return new XString(s.substring(n1, n2));
+};
+
+Functions.stringLength = function() {
+       var c = arguments[0];
+       var s;
+       if (arguments.length == 1) {
+               s = XNodeSet.prototype.stringForNode(c.contextNode);
+       } else if (arguments.length == 2) {
+               s = arguments[1].evaluate(c).stringValue();
+       } else {
+               throw new Error("Function string-length expects (string?)");
+       }
+       return new XNumber(s.length);
+};
+
+Functions.normalizeSpace = function() {
+       var c = arguments[0];
+       var s;
+       if (arguments.length == 1) {
+               s = XNodeSet.prototype.stringForNode(c.contextNode);
+       } else if (arguments.length == 2) {
+               s = arguments[1].evaluate(c).stringValue();
+       } else {
+               throw new Error("Function normalize-space expects (string?)");
+       }
+       var i = 0;
+       var j = s.length - 1;
+       while (Utilities.isSpace(s.charCodeAt(j))) {
+               j--;
+       }
+       var t = "";
+       while (i <= j && Utilities.isSpace(s.charCodeAt(i))) {
+               i++;
+       }
+       while (i <= j) {
+               if (Utilities.isSpace(s.charCodeAt(i))) {
+                       t += " ";
+                       while (i <= j && Utilities.isSpace(s.charCodeAt(i))) {
+                               i++;
+                       }
+               } else {
+                       t += s.charAt(i);
+                       i++;
+               }
+       }
+       return new XString(t);
+};
+
+Functions.translate = function() {
+       var c = arguments[0];
+       if (arguments.length != 4) {
+               throw new Error("Function translate expects (string, string, string)");
+       }
+       var s1 = arguments[1].evaluate(c).stringValue();
+       var s2 = arguments[2].evaluate(c).stringValue();
+       var s3 = arguments[3].evaluate(c).stringValue();
+       var map = [];
+       for (var i = 0; i < s2.length; i++) {
+               var j = s2.charCodeAt(i);
+               if (map[j] == undefined) {
+                       var k = i > s3.length ? "" : s3.charAt(i);
+                       map[j] = k;
+               }
+       }
+       var t = "";
+       for (var i = 0; i < s1.length; i++) {
+               var c = s1.charCodeAt(i);
+               var r = map[c];
+               if (r == undefined) {
+                       t += s1.charAt(i);
+               } else {
+                       t += r;
+               }
+       }
+       return new XString(t);
+};
+
+Functions.boolean_ = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function boolean expects (object)");
+       }
+       return arguments[1].evaluate(c).bool();
+};
+
+Functions.not = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function not expects (object)");
+       }
+       return arguments[1].evaluate(c).bool().not();
+};
+
+Functions.true_ = function() {
+       if (arguments.length != 1) {
+               throw new Error("Function true expects ()");
+       }
+       return new XBoolean(true);
+};
+
+Functions.false_ = function() {
+       if (arguments.length != 1) {
+               throw new Error("Function false expects ()");
+       }
+       return new XBoolean(false);
+};
+
+Functions.lang = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function lang expects (string)");
+       }
+       var lang;
+       for (var n = c.contextNode; n != null && n.nodeType != 9 /*Node.DOCUMENT_NODE*/; n = n.parentNode) {
+               var a = n.getAttributeNS(XPath.XML_NAMESPACE_URI, "lang");
+               if (a != null) {
+                       lang = String(a);
+                       break;
+               }
+       }
+       if (lang == null) {
+               return new XBoolean(false);
+       }
+       var s = arguments[1].evaluate(c).stringValue();
+       return new XBoolean(lang.substring(0, s.length) == s
+                               && (lang.length == s.length || lang.charAt(s.length) == '-'));
+};
+
+Functions.number = function() {
+       var c = arguments[0];
+       if (!(arguments.length == 1 || arguments.length == 2)) {
+               throw new Error("Function number expects (object?)");
+       }
+       if (arguments.length == 1) {
+               return new XNumber(XNodeSet.prototype.stringForNode(c.contextNode));
+       }
+       return arguments[1].evaluate(c).number();
+};
+
+Functions.sum = function() {
+       var c = arguments[0];
+       var ns;
+       if (arguments.length != 2 || !Utilities.instance_of((ns = arguments[1].evaluate(c)), XNodeSet)) {
+               throw new Error("Function sum expects (node-set)");
+       }
+       ns = ns.toArray();
+       var n = 0;
+       for (var i = 0; i < ns.length; i++) {
+               n += new XNumber(XNodeSet.prototype.stringForNode(ns[i])).numberValue();
+       }
+       return new XNumber(n);
+};
+
+Functions.floor = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function floor expects (number)");
+       }
+       return new XNumber(Math.floor(arguments[1].evaluate(c).numberValue()));
+};
+
+Functions.ceiling = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function ceiling expects (number)");
+       }
+       return new XNumber(Math.ceil(arguments[1].evaluate(c).numberValue()));
+};
+
+Functions.round = function() {
+       var c = arguments[0];
+       if (arguments.length != 2) {
+               throw new Error("Function round expects (number)");
+       }
+       return new XNumber(Math.round(arguments[1].evaluate(c).numberValue()));
+};
+
+// Utilities /////////////////////////////////////////////////////////////////
+
+Utilities = new Object();
+
+Utilities.splitQName = function(qn) {
+       var i = qn.indexOf(":");
+       if (i == -1) {
+               return [ null, qn ];
+       }
+       return [ qn.substring(0, i), qn.substring(i + 1) ];
+};
+
+Utilities.resolveQName = function(qn, nr, n, useDefault) {
+       var parts = Utilities.splitQName(qn);
+       if (parts[0] != null) {
+               parts[0] = nr.getNamespace(parts[0], n);
+       } else {
+               if (useDefault) {
+                       parts[0] = nr.getNamespace("", n);
+                       if (parts[0] == null) {
+                               parts[0] = "";
+                       }
+               } else {
+                       parts[0] = "";
+               }
+       }
+       return parts;
+};
+
+Utilities.isSpace = function(c) {
+       return c == 0x9 || c == 0xd || c == 0xa || c == 0x20;
+};
+
+Utilities.isLetter = function(c) {
+       return c >= 0x0041 && c <= 0x005A ||
+               c >= 0x0061 && c <= 0x007A ||
+               c >= 0x00C0 && c <= 0x00D6 ||
+               c >= 0x00D8 && c <= 0x00F6 ||
+               c >= 0x00F8 && c <= 0x00FF ||
+               c >= 0x0100 && c <= 0x0131 ||
+               c >= 0x0134 && c <= 0x013E ||
+               c >= 0x0141 && c <= 0x0148 ||
+               c >= 0x014A && c <= 0x017E ||
+               c >= 0x0180 && c <= 0x01C3 ||
+               c >= 0x01CD && c <= 0x01F0 ||
+               c >= 0x01F4 && c <= 0x01F5 ||
+               c >= 0x01FA && c <= 0x0217 ||
+               c >= 0x0250 && c <= 0x02A8 ||
+               c >= 0x02BB && c <= 0x02C1 ||
+               c == 0x0386 ||
+               c >= 0x0388 && c <= 0x038A ||
+               c == 0x038C ||
+               c >= 0x038E && c <= 0x03A1 ||
+               c >= 0x03A3 && c <= 0x03CE ||
+               c >= 0x03D0 && c <= 0x03D6 ||
+               c == 0x03DA ||
+               c == 0x03DC ||
+               c == 0x03DE ||
+               c == 0x03E0 ||
+               c >= 0x03E2 && c <= 0x03F3 ||
+               c >= 0x0401 && c <= 0x040C ||
+               c >= 0x040E && c <= 0x044F ||
+               c >= 0x0451 && c <= 0x045C ||
+               c >= 0x045E && c <= 0x0481 ||
+               c >= 0x0490 && c <= 0x04C4 ||
+               c >= 0x04C7 && c <= 0x04C8 ||
+               c >= 0x04CB && c <= 0x04CC ||
+               c >= 0x04D0 && c <= 0x04EB ||
+               c >= 0x04EE && c <= 0x04F5 ||
+               c >= 0x04F8 && c <= 0x04F9 ||
+               c >= 0x0531 && c <= 0x0556 ||
+               c == 0x0559 ||
+               c >= 0x0561 && c <= 0x0586 ||
+               c >= 0x05D0 && c <= 0x05EA ||
+               c >= 0x05F0 && c <= 0x05F2 ||
+               c >= 0x0621 && c <= 0x063A ||
+               c >= 0x0641 && c <= 0x064A ||
+               c >= 0x0671 && c <= 0x06B7 ||
+               c >= 0x06BA && c <= 0x06BE ||
+               c >= 0x06C0 && c <= 0x06CE ||
+               c >= 0x06D0 && c <= 0x06D3 ||
+               c == 0x06D5 ||
+               c >= 0x06E5 && c <= 0x06E6 ||
+               c >= 0x0905 && c <= 0x0939 ||
+               c == 0x093D ||
+               c >= 0x0958 && c <= 0x0961 ||
+               c >= 0x0985 && c <= 0x098C ||
+               c >= 0x098F && c <= 0x0990 ||
+               c >= 0x0993 && c <= 0x09A8 ||
+               c >= 0x09AA && c <= 0x09B0 ||
+               c == 0x09B2 ||
+               c >= 0x09B6 && c <= 0x09B9 ||
+               c >= 0x09DC && c <= 0x09DD ||
+               c >= 0x09DF && c <= 0x09E1 ||
+               c >= 0x09F0 && c <= 0x09F1 ||
+               c >= 0x0A05 && c <= 0x0A0A ||
+               c >= 0x0A0F && c <= 0x0A10 ||
+               c >= 0x0A13 && c <= 0x0A28 ||
+               c >= 0x0A2A && c <= 0x0A30 ||
+               c >= 0x0A32 && c <= 0x0A33 ||
+               c >= 0x0A35 && c <= 0x0A36 ||
+               c >= 0x0A38 && c <= 0x0A39 ||
+               c >= 0x0A59 && c <= 0x0A5C ||
+               c == 0x0A5E ||
+               c >= 0x0A72 && c <= 0x0A74 ||
+               c >= 0x0A85 && c <= 0x0A8B ||
+               c == 0x0A8D ||
+               c >= 0x0A8F && c <= 0x0A91 ||
+               c >= 0x0A93 && c <= 0x0AA8 ||
+               c >= 0x0AAA && c <= 0x0AB0 ||
+               c >= 0x0AB2 && c <= 0x0AB3 ||
+               c >= 0x0AB5 && c <= 0x0AB9 ||
+               c == 0x0ABD ||
+               c == 0x0AE0 ||
+               c >= 0x0B05 && c <= 0x0B0C ||
+               c >= 0x0B0F && c <= 0x0B10 ||
+               c >= 0x0B13 && c <= 0x0B28 ||
+               c >= 0x0B2A && c <= 0x0B30 ||
+               c >= 0x0B32 && c <= 0x0B33 ||
+               c >= 0x0B36 && c <= 0x0B39 ||
+               c == 0x0B3D ||
+               c >= 0x0B5C && c <= 0x0B5D ||
+               c >= 0x0B5F && c <= 0x0B61 ||
+               c >= 0x0B85 && c <= 0x0B8A ||
+               c >= 0x0B8E && c <= 0x0B90 ||
+               c >= 0x0B92 && c <= 0x0B95 ||
+               c >= 0x0B99 && c <= 0x0B9A ||
+               c == 0x0B9C ||
+               c >= 0x0B9E && c <= 0x0B9F ||
+               c >= 0x0BA3 && c <= 0x0BA4 ||
+               c >= 0x0BA8 && c <= 0x0BAA ||
+               c >= 0x0BAE && c <= 0x0BB5 ||
+               c >= 0x0BB7 && c <= 0x0BB9 ||
+               c >= 0x0C05 && c <= 0x0C0C ||
+               c >= 0x0C0E && c <= 0x0C10 ||
+               c >= 0x0C12 && c <= 0x0C28 ||
+               c >= 0x0C2A && c <= 0x0C33 ||
+               c >= 0x0C35 && c <= 0x0C39 ||
+               c >= 0x0C60 && c <= 0x0C61 ||
+               c >= 0x0C85 && c <= 0x0C8C ||
+               c >= 0x0C8E && c <= 0x0C90 ||
+               c >= 0x0C92 && c <= 0x0CA8 ||
+               c >= 0x0CAA && c <= 0x0CB3 ||
+               c >= 0x0CB5 && c <= 0x0CB9 ||
+               c == 0x0CDE ||
+               c >= 0x0CE0 && c <= 0x0CE1 ||
+               c >= 0x0D05 && c <= 0x0D0C ||
+               c >= 0x0D0E && c <= 0x0D10 ||
+               c >= 0x0D12 && c <= 0x0D28 ||
+               c >= 0x0D2A && c <= 0x0D39 ||
+               c >= 0x0D60 && c <= 0x0D61 ||
+               c >= 0x0E01 && c <= 0x0E2E ||
+               c == 0x0E30 ||
+               c >= 0x0E32 && c <= 0x0E33 ||
+               c >= 0x0E40 && c <= 0x0E45 ||
+               c >= 0x0E81 && c <= 0x0E82 ||
+               c == 0x0E84 ||
+               c >= 0x0E87 && c <= 0x0E88 ||
+               c == 0x0E8A ||
+               c == 0x0E8D ||
+               c >= 0x0E94 && c <= 0x0E97 ||
+               c >= 0x0E99 && c <= 0x0E9F ||
+               c >= 0x0EA1 && c <= 0x0EA3 ||
+               c == 0x0EA5 ||
+               c == 0x0EA7 ||
+               c >= 0x0EAA && c <= 0x0EAB ||
+               c >= 0x0EAD && c <= 0x0EAE ||
+               c == 0x0EB0 ||
+               c >= 0x0EB2 && c <= 0x0EB3 ||
+               c == 0x0EBD ||
+               c >= 0x0EC0 && c <= 0x0EC4 ||
+               c >= 0x0F40 && c <= 0x0F47 ||
+               c >= 0x0F49 && c <= 0x0F69 ||
+               c >= 0x10A0 && c <= 0x10C5 ||
+               c >= 0x10D0 && c <= 0x10F6 ||
+               c == 0x1100 ||
+               c >= 0x1102 && c <= 0x1103 ||
+               c >= 0x1105 && c <= 0x1107 ||
+               c == 0x1109 ||
+               c >= 0x110B && c <= 0x110C ||
+               c >= 0x110E && c <= 0x1112 ||
+               c == 0x113C ||
+               c == 0x113E ||
+               c == 0x1140 ||
+               c == 0x114C ||
+               c == 0x114E ||
+               c == 0x1150 ||
+               c >= 0x1154 && c <= 0x1155 ||
+               c == 0x1159 ||
+               c >= 0x115F && c <= 0x1161 ||
+               c == 0x1163 ||
+               c == 0x1165 ||
+               c == 0x1167 ||
+               c == 0x1169 ||
+               c >= 0x116D && c <= 0x116E ||
+               c >= 0x1172 && c <= 0x1173 ||
+               c == 0x1175 ||
+               c == 0x119E ||
+               c == 0x11A8 ||
+               c == 0x11AB ||
+               c >= 0x11AE && c <= 0x11AF ||
+               c >= 0x11B7 && c <= 0x11B8 ||
+               c == 0x11BA ||
+               c >= 0x11BC && c <= 0x11C2 ||
+               c == 0x11EB ||
+               c == 0x11F0 ||
+               c == 0x11F9 ||
+               c >= 0x1E00 && c <= 0x1E9B ||
+               c >= 0x1EA0 && c <= 0x1EF9 ||
+               c >= 0x1F00 && c <= 0x1F15 ||
+               c >= 0x1F18 && c <= 0x1F1D ||
+               c >= 0x1F20 && c <= 0x1F45 ||
+               c >= 0x1F48 && c <= 0x1F4D ||
+               c >= 0x1F50 && c <= 0x1F57 ||
+               c == 0x1F59 ||
+               c == 0x1F5B ||
+               c == 0x1F5D ||
+               c >= 0x1F5F && c <= 0x1F7D ||
+               c >= 0x1F80 && c <= 0x1FB4 ||
+               c >= 0x1FB6 && c <= 0x1FBC ||
+               c == 0x1FBE ||
+               c >= 0x1FC2 && c <= 0x1FC4 ||
+               c >= 0x1FC6 && c <= 0x1FCC ||
+               c >= 0x1FD0 && c <= 0x1FD3 ||
+               c >= 0x1FD6 && c <= 0x1FDB ||
+               c >= 0x1FE0 && c <= 0x1FEC ||
+               c >= 0x1FF2 && c <= 0x1FF4 ||
+               c >= 0x1FF6 && c <= 0x1FFC ||
+               c == 0x2126 ||
+               c >= 0x212A && c <= 0x212B ||
+               c == 0x212E ||
+               c >= 0x2180 && c <= 0x2182 ||
+               c >= 0x3041 && c <= 0x3094 ||
+               c >= 0x30A1 && c <= 0x30FA ||
+               c >= 0x3105 && c <= 0x312C ||
+               c >= 0xAC00 && c <= 0xD7A3 ||
+               c >= 0x4E00 && c <= 0x9FA5 ||
+               c == 0x3007 ||
+               c >= 0x3021 && c <= 0x3029;
+};
+
+Utilities.isNCNameChar = function(c) {
+       return c >= 0x0030 && c <= 0x0039 
+               || c >= 0x0660 && c <= 0x0669 
+               || c >= 0x06F0 && c <= 0x06F9 
+               || c >= 0x0966 && c <= 0x096F 
+               || c >= 0x09E6 && c <= 0x09EF 
+               || c >= 0x0A66 && c <= 0x0A6F 
+               || c >= 0x0AE6 && c <= 0x0AEF 
+               || c >= 0x0B66 && c <= 0x0B6F 
+               || c >= 0x0BE7 && c <= 0x0BEF 
+               || c >= 0x0C66 && c <= 0x0C6F 
+               || c >= 0x0CE6 && c <= 0x0CEF 
+               || c >= 0x0D66 && c <= 0x0D6F 
+               || c >= 0x0E50 && c <= 0x0E59 
+               || c >= 0x0ED0 && c <= 0x0ED9 
+               || c >= 0x0F20 && c <= 0x0F29
+               || c == 0x002E
+               || c == 0x002D
+               || c == 0x005F
+               || Utilities.isLetter(c)
+               || c >= 0x0300 && c <= 0x0345 
+               || c >= 0x0360 && c <= 0x0361 
+               || c >= 0x0483 && c <= 0x0486 
+               || c >= 0x0591 && c <= 0x05A1 
+               || c >= 0x05A3 && c <= 0x05B9 
+               || c >= 0x05BB && c <= 0x05BD 
+               || c == 0x05BF 
+               || c >= 0x05C1 && c <= 0x05C2 
+               || c == 0x05C4 
+               || c >= 0x064B && c <= 0x0652 
+               || c == 0x0670 
+               || c >= 0x06D6 && c <= 0x06DC 
+               || c >= 0x06DD && c <= 0x06DF 
+               || c >= 0x06E0 && c <= 0x06E4 
+               || c >= 0x06E7 && c <= 0x06E8 
+               || c >= 0x06EA && c <= 0x06ED 
+               || c >= 0x0901 && c <= 0x0903 
+               || c == 0x093C 
+               || c >= 0x093E && c <= 0x094C 
+               || c == 0x094D 
+               || c >= 0x0951 && c <= 0x0954 
+               || c >= 0x0962 && c <= 0x0963 
+               || c >= 0x0981 && c <= 0x0983 
+               || c == 0x09BC 
+               || c == 0x09BE 
+               || c == 0x09BF 
+               || c >= 0x09C0 && c <= 0x09C4 
+               || c >= 0x09C7 && c <= 0x09C8 
+               || c >= 0x09CB && c <= 0x09CD 
+               || c == 0x09D7 
+               || c >= 0x09E2 && c <= 0x09E3 
+               || c == 0x0A02 
+               || c == 0x0A3C 
+               || c == 0x0A3E 
+               || c == 0x0A3F 
+               || c >= 0x0A40 && c <= 0x0A42 
+               || c >= 0x0A47 && c <= 0x0A48 
+               || c >= 0x0A4B && c <= 0x0A4D 
+               || c >= 0x0A70 && c <= 0x0A71 
+               || c >= 0x0A81 && c <= 0x0A83 
+               || c == 0x0ABC 
+               || c >= 0x0ABE && c <= 0x0AC5 
+               || c >= 0x0AC7 && c <= 0x0AC9 
+               || c >= 0x0ACB && c <= 0x0ACD 
+               || c >= 0x0B01 && c <= 0x0B03 
+               || c == 0x0B3C 
+               || c >= 0x0B3E && c <= 0x0B43 
+               || c >= 0x0B47 && c <= 0x0B48 
+               || c >= 0x0B4B && c <= 0x0B4D 
+               || c >= 0x0B56 && c <= 0x0B57 
+               || c >= 0x0B82 && c <= 0x0B83 
+               || c >= 0x0BBE && c <= 0x0BC2 
+               || c >= 0x0BC6 && c <= 0x0BC8 
+               || c >= 0x0BCA && c <= 0x0BCD 
+               || c == 0x0BD7 
+               || c >= 0x0C01 && c <= 0x0C03 
+               || c >= 0x0C3E && c <= 0x0C44 
+               || c >= 0x0C46 && c <= 0x0C48 
+               || c >= 0x0C4A && c <= 0x0C4D 
+               || c >= 0x0C55 && c <= 0x0C56 
+               || c >= 0x0C82 && c <= 0x0C83 
+               || c >= 0x0CBE && c <= 0x0CC4 
+               || c >= 0x0CC6 && c <= 0x0CC8 
+               || c >= 0x0CCA && c <= 0x0CCD 
+               || c >= 0x0CD5 && c <= 0x0CD6 
+               || c >= 0x0D02 && c <= 0x0D03 
+               || c >= 0x0D3E && c <= 0x0D43 
+               || c >= 0x0D46 && c <= 0x0D48 
+               || c >= 0x0D4A && c <= 0x0D4D 
+               || c == 0x0D57 
+               || c == 0x0E31 
+               || c >= 0x0E34 && c <= 0x0E3A 
+               || c >= 0x0E47 && c <= 0x0E4E 
+               || c == 0x0EB1 
+               || c >= 0x0EB4 && c <= 0x0EB9 
+               || c >= 0x0EBB && c <= 0x0EBC 
+               || c >= 0x0EC8 && c <= 0x0ECD 
+               || c >= 0x0F18 && c <= 0x0F19 
+               || c == 0x0F35 
+               || c == 0x0F37 
+               || c == 0x0F39 
+               || c == 0x0F3E 
+               || c == 0x0F3F 
+               || c >= 0x0F71 && c <= 0x0F84 
+               || c >= 0x0F86 && c <= 0x0F8B 
+               || c >= 0x0F90 && c <= 0x0F95 
+               || c == 0x0F97 
+               || c >= 0x0F99 && c <= 0x0FAD 
+               || c >= 0x0FB1 && c <= 0x0FB7 
+               || c == 0x0FB9 
+               || c >= 0x20D0 && c <= 0x20DC 
+               || c == 0x20E1 
+               || c >= 0x302A && c <= 0x302F 
+               || c == 0x3099 
+               || c == 0x309A
+               || c == 0x00B7 
+               || c == 0x02D0 
+               || c == 0x02D1 
+               || c == 0x0387 
+               || c == 0x0640 
+               || c == 0x0E46 
+               || c == 0x0EC6 
+               || c == 0x3005 
+               || c >= 0x3031 && c <= 0x3035 
+               || c >= 0x309D && c <= 0x309E 
+               || c >= 0x30FC && c <= 0x30FE;
+};
+
+Utilities.coalesceText = function(n) {
+       for (var m = n.firstChild; m != null; m = m.nextSibling) {
+               if (m.nodeType == 3 /*Node.TEXT_NODE*/ || m.nodeType == 4 /*Node.CDATA_SECTION_NODE*/) {
+                       var s = m.nodeValue;
+                       var first = m;
+                       m = m.nextSibling;
+                       while (m != null && (m.nodeType == 3 /*Node.TEXT_NODE*/ || m.nodeType == 4 /*Node.CDATA_SECTION_NODE*/)) {
+                               s += m.nodeValue;
+                               var del = m;
+                               m = m.nextSibling;
+                               del.parentNode.removeChild(del);
+                       }
+                       if (first.nodeType == 4 /*Node.CDATA_SECTION_NODE*/) {
+                               var p = first.parentNode;
+                               if (first.nextSibling == null) {
+                                       p.removeChild(first);
+                                       p.appendChild(p.ownerDocument.createTextNode(s));
+                               } else {
+                                       var next = first.nextSibling;
+                                       p.removeChild(first);
+                                       p.insertBefore(p.ownerDocument.createTextNode(s), next);
+                               }
+                       } else {
+                               first.nodeValue = s;
+                       }
+                       if (m == null) {
+                               break;
+                       }
+               } else if (m.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+                       Utilities.coalesceText(m);
+               }
+       }
+};
+
+Utilities.instance_of = function(o, c) {
+       while (o != null) {
+               if (o.constructor === c) {
+                       return true;
+               }
+               if (o === Object) {
+                       return false;
+               }
+               o = o.constructor.superclass;
+       }
+       return false;
+};
+
+Utilities.getElementById = function(n, id) {
+       // Note that this does not check the DTD to check for actual
+       // attributes of type ID, so this may be a bit wrong.
+       if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+               if (n.getAttribute("id") == id
+                               || n.getAttributeNS(null, "id") == id) {
+                       return n;
+               }
+       }
+       for (var m = n.firstChild; m != null; m = m.nextSibling) {
+               var res = Utilities.getElementById(m, id);
+               if (res != null) {
+                       return res;
+               }
+       }
+       return null;
+};
+
+// XPathException ////////////////////////////////////////////////////////////
+
+XPathException.prototype = {};
+XPathException.prototype.constructor = XPathException;
+XPathException.superclass = Object.prototype;
+
+function XPathException(c, e) {
+       this.code = c;
+       this.exception = e;
+}
+
+XPathException.prototype.toString = function() {
+       var msg = this.exception ? ": " + this.exception.toString() : "";
+       switch (this.code) {
+               case XPathException.INVALID_EXPRESSION_ERR:
+                       return "Invalid expression" + msg;
+               case XPathException.TYPE_ERR:
+                       return "Type error" + msg;
+       }
+};
+
+XPathException.INVALID_EXPRESSION_ERR = 51;
+XPathException.TYPE_ERR = 52;
+
+// XPathExpression ///////////////////////////////////////////////////////////
+
+XPathExpression.prototype = {};
+XPathExpression.prototype.constructor = XPathExpression;
+XPathExpression.superclass = Object.prototype;
+
+function XPathExpression(e, r, p) {
+       this.xpath = p.parse(e);
+       this.context = new XPathContext();
+       this.context.namespaceResolver = new XPathNSResolverWrapper(r);
+}
+
+XPathExpression.prototype.evaluate = function(n, t, res) {
+       this.context.expressionContextNode = n;
+       var result = this.xpath.evaluate(this.context);
+       return new XPathResult(result, t);
+}
+
+// XPathNSResolverWrapper ////////////////////////////////////////////////////
+
+XPathNSResolverWrapper.prototype = {};
+XPathNSResolverWrapper.prototype.constructor = XPathNSResolverWrapper;
+XPathNSResolverWrapper.superclass = Object.prototype;
+
+function XPathNSResolverWrapper(r) {
+       this.xpathNSResolver = r;
+}
+
+XPathNSResolverWrapper.prototype.getNamespace = function(prefix, n) {
+    if (this.xpathNSResolver == null) {
+        return null;
+    }
+       return this.xpathNSResolver.lookupNamespaceURI(prefix);
+};
+
+// NodeXPathNSResolver ///////////////////////////////////////////////////////
+
+NodeXPathNSResolver.prototype = {};
+NodeXPathNSResolver.prototype.constructor = NodeXPathNSResolver;
+NodeXPathNSResolver.superclass = Object.prototype;
+
+function NodeXPathNSResolver(n) {
+       this.node = n;
+       this.namespaceResolver = new NamespaceResolver();
+}
+
+NodeXPathNSResolver.prototype.lookupNamespaceURI = function(prefix) {
+       return this.namespaceResolver.getNamespace(prefix, this.node);
+};
+
+// XPathResult ///////////////////////////////////////////////////////////////
+
+XPathResult.prototype = {};
+XPathResult.prototype.constructor = XPathResult;
+XPathResult.superclass = Object.prototype;
+
+function XPathResult(v, t) {
+       if (t == XPathResult.ANY_TYPE) {
+               if (v.constructor === XString) {
+                       t = XPathResult.STRING_TYPE;
+               } else if (v.constructor === XNumber) {
+                       t = XPathResult.NUMBER_TYPE;
+               } else if (v.constructor === XBoolean) {
+                       t = XPathResult.BOOLEAN_TYPE;
+               } else if (v.constructor === XNodeSet) {
+                       t = XPathResult.UNORDERED_NODE_ITERATOR_TYPE;
+               }
+       }
+       this.resultType = t;
+       switch (t) {
+               case XPathResult.NUMBER_TYPE:
+                       this.numberValue = v.numberValue();
+                       return;
+               case XPathResult.STRING_TYPE:
+                       this.stringValue = v.stringValue();
+                       return;
+               case XPathResult.BOOLEAN_TYPE:
+                       this.booleanValue = v.booleanValue();
+                       return;
+               case XPathResult.ANY_UNORDERED_NODE_TYPE:
+               case XPathResult.FIRST_UNORDERED_NODE_TYPE:
+                       if (v.constructor === XNodeSet) {
+                               this.singleNodeValue = v.first();
+                               return;
+                       }
+                       break;
+               case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+               case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+                       if (v.constructor === XNodeSet) {
+                               this.invalidIteratorState = false;
+                               this.nodes = v.toArray();
+                               this.iteratorIndex = 0;
+                               return;
+                       }
+                       break;
+               case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+               case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+                       if (v.constructor === XNodeSet) {
+                               this.nodes = v.toArray();
+                               this.snapshotLength = this.nodes.length;
+                               return;
+                       }
+                       break;
+       }
+       throw new XPathException(XPathException.TYPE_ERR);
+};
+
+XPathResult.prototype.iterateNext = function() {
+       if (this.resultType != XPathResult.UNORDERED_NODE_ITERATOR_TYPE
+                       && this.resultType != XPathResult.ORDERED_NODE_ITERATOR_TYPE) {
+               throw new XPathException(XPathException.TYPE_ERR);
+       }
+       return this.nodes[this.iteratorIndex++];
+};
+
+XPathResult.prototype.snapshotItem = function(i) {
+       if (this.resultType != XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE
+                       && this.resultType != XPathResult.ORDERED_NODE_SNAPSHOT_TYPE) {
+               throw new XPathException(XPathException.TYPE_ERR);
+       }
+       return this.nodes[i];
+};
+
+XPathResult.ANY_TYPE = 0;
+XPathResult.NUMBER_TYPE = 1;
+XPathResult.STRING_TYPE = 2;
+XPathResult.BOOLEAN_TYPE = 3;
+XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
+XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
+XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6;
+XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7;
+XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
+XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
+
+// DOM 3 XPath support ///////////////////////////////////////////////////////
+
+function installDOM3XPathSupport(doc, p) {
+       doc.createExpression = function(e, r) {
+               try {
+                       return new XPathExpression(e, r, p);
+               } catch (e) {
+                       throw new XPathException(XPathException.INVALID_EXPRESSION_ERR, e);
+               }
+       };
+       doc.createNSResolver = function(n) {
+               return new NodeXPathNSResolver(n);
+       };
+       doc.evaluate = function(e, cn, r, t, res) {
+               if (!cn) cn = doc;
+               if (t < 0 || t > 9) {
+                       throw { code: 0, toString: function() { return "Request type not supported"; } };
+               }
+               return doc.createExpression(e, r, p).evaluate(cn, t, res);
+       };
+};
+
+// ---------------------------------------------------------------------------
+
+// Install DOM 3 XPath support for the current document.
+try {
+       var shouldInstall = true;
+       try {
+               if (document.implementation
+                               && document.implementation.hasFeature
+                               && document.implementation.hasFeature("XPath", null)) {
+                       shouldInstall = false;
+               }
+       } catch (e) {
+       }
+       if (shouldInstall) {
+               installDOM3XPathSupport(document, new XPathParser());
+       }
+} catch (e) {
+}
diff --git a/Open-ILS/src/javascript/backend/test/xmlhttprequest-test.js b/Open-ILS/src/javascript/backend/test/xmlhttprequest-test.js
new file mode 100644 (file)
index 0000000..b2dfd1b
--- /dev/null
@@ -0,0 +1,38 @@
+// suck in what we need
+perl_print('------------------------------LOAD---------------------------------------');
+load_lib('xmlhttprequest.js');
+
+perl_print('------------------------------START---------------------------------------');
+
+perl_print("recordID is",params.recordID);
+
+// xpath namespace resolver
+var ns_res = new XPathNamespaceResolver(
+       { marc : "http://www.loc.gov/MARC21/slim",
+         mods : "http://www.loc.gov/mods/v3" }
+);
+
+// xmlhttprequest uses the perl xml parser to get the xml doc from 
+var x = new XMLHttpRequest();
+x.open('POST','http://dev.gapines.org/restgateway');
+x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+x.send('service=open-ils.storage&method=open-ils.storage.direct.biblio.record_entry.retrieve&param=' + params.recordID);
+
+
+// use the DOM to parse the marc record
+var marc = DOMImplementation.parseString(x.responseXML.evaluate('//marc/text()').singleNodeValue.data);
+
+
+
+// and then get the title
+var res = marc.evaluate(
+       "/marc:record/marc:datafield[@tag='245']/marc:subfield[@code='a']/text()",
+       marc,
+       ns_res
+);
+
+// print the title we just grabbed
+perl_print(res.singleNodeValue.data);
+
+perl_print('------------------------------END---------------------------------------');
+
index a1da49e..88d0a96 100644 (file)
@@ -1,15 +1,25 @@
 package OpenILS::Utils::SpiderMonkey;
 use strict; use warnings;
 use OpenSRF::Utils::Logger qw(:logger);
+use OpenSRF::EX qw(:try);
 use OpenILS::Utils::ScriptRunner;
 use base 'OpenILS::Utils::ScriptRunner';
 use JavaScript::SpiderMonkey;
+use LWP::UserAgent;
+use XML::LibXML;
+use Time::HiRes qw/time/;
+use vars qw/%_paths/;
 
 sub new {
        my ( $class, %params ) = @_;
        $class = ref($class) || $class;
-       my $self = { file => $params{file}, libs => $params{libs} };
-       return bless( $self, $class );
+       $params{paths} ||= [];
+
+       my $self = { file => $params{file}, libs => $params{libs}, _path => {%_paths} };
+       bless( $self, $class );
+
+       $self->add_path($_) for @{$params{paths}};
+       return $self;
 }
 
 sub context {
@@ -22,17 +32,30 @@ sub init {
        my $self = shift;
        my $js = JavaScript::SpiderMonkey->new();
        $js->init();
-       $js->function_set("perl_print",         sub { print "@_\n"; } );
-       $js->function_set("log_activity",       sub { $logger->activity(@_); return 1;} );
-       $js->function_set("log_error",          sub { $logger->error(@_); return 1;} );
-       $js->function_set("log_warn",                   sub { $logger->warn(@_); return 1;} );
-       $js->function_set("log_info",                   sub { $logger->info(@_); return 1;} );
-       $js->function_set("log_debug",          sub { $logger->debug(@_); return 1;} );
-       $js->function_set("log_internal",       sub { $logger->internal(@_); return 1;} );
-       $js->function_set("debug",                              sub { $logger->debug(@_); return 1;} );
-       $js->function_set("alert",                              sub { $logger->warn(@_); return 1;} );
+
+       $js->function_set(perl_print    => sub { print "@_\n"; } );
+       $js->function_set(perl_warn     => sub { warn @_; } );
+       $js->function_set(log_activity  => sub { $logger->activity(@_); return 1;} );
+       $js->function_set(log_error     => sub { $logger->error(@_); return 1;} );
+       $js->function_set(log_warn      => sub { $logger->warn(@_); return 1;} );
+       $js->function_set(log_info      => sub { $logger->info(@_); return 1;} );
+       $js->function_set(log_debug     => sub { $logger->debug(@_); return 1;} );
+       $js->function_set(log_internal  => sub { $logger->internal(@_); return 1;} );
+       $js->function_set(debug         => sub { $logger->debug(@_); return 1;} );
+       $js->function_set(alert         => sub { $logger->warn(@_); return 1;} );
+       
+       $js->function_set(load_lib      => sub { $self->load_lib(@_); });
+
+       # XML support functions
+       $js->function_set(
+               _OILS_FUNC_xmlhttprequest_send  => sub { $self->_xmlhttprequest_send(@_); });
+       $js->function_set(
+               _OILS_FUNC_xml_parse_string     => sub { $self->_parse_xml_string(@_); });
+       
        $self->context($js);
        $self->load_lib($_) for @{$self->{libs}};
+
+       return $self;
 }
 
 
@@ -46,23 +69,72 @@ sub run {
        my $file = shift() || $self->{file};
        my $js = $self->context;
 
+       $file = $self->_find_file($file);
+
        if( ! open(F, $file) ) {
                $logger->error("Error opening script file: $file");
                return 0;
        }
 
-       if( ! $js->eval(join("\n", <F>)) ) {
-               $logger->error("Script ($file) eval failed in SpiderMonkey run: $@");  
-               return 0;
+       {       local $/ = undef;
+               my $content = <F>;
+               my $s = time();
+               if( !$js || !$content || !$js->eval($content) ) {
+                       $logger->error("$file Eval failed: $@");  
+                       return 0;
+               }
+               $logger->debug("eval of $file took ". sprintf('%0.3f', time - $s) . " seconds");
        }
 
        close(F);
        return 1;
 }
 
+sub remove_path { 
+       my( $self, $path ) = @_;
+       if (ref($self)) {
+               if ($self->{_path}{$path}) {
+                       $self->{_path}{$path} = 0;
+               }
+               return $self->{_path}{$path};
+       } else {
+               if ($_paths{$path}) {
+                       $_paths{$path} = 0;
+               }
+               return $_paths{$path};
+       }
+}
+
+sub add_path { 
+       my( $self, $path ) = @_;
+       if (ref($self)) {
+               if (!$self->{_path}{$path}) {
+                       $self->{_path}{$path} = 1;
+               }
+       } else {
+               if (!$_paths{$path}) {
+                       $_paths{$path} = 1;
+               }
+       }
+       return $self;
+}
+
+sub _find_file {
+       my $self = shift;
+       my $file = shift;
+       for my $p ( keys %{ $self->{_path} } ) {
+               next unless ($self->{_path}{$p});
+               my $full = join('/',$p,$file);
+               return $full if (-e $full);
+       }
+}
+
 sub load_lib { 
        my( $self, $file ) = @_;
-       $self->run( $file );
+       if (!$self->{_loaded}{$file} && $self->run( $file )) {
+               $self->{_loaded}{$file} = 1;
+       }
+       return $self->{_loaded}{$file};
 }
 
 sub _js_prop_name {
@@ -189,4 +261,141 @@ sub insert_array {
        }
 }
 
+sub _xmlhttprequest_send {
+       my $self = shift;
+       my $id = shift;
+       my $method = shift;
+       my $url = shift;
+       my $blocking = shift;
+       my $headerlist = shift;
+       my $data = shift;
+
+       my $ctx = $self->context;
+
+       # just so perl has access to it...
+       $ctx->object_by_path('__xmlhttpreq_hash.id'.$id);
+
+       my $headers = new HTTP::Headers;
+       my @lines = split(/\n/so, $headerlist);
+       for my $line (@lines) {
+               if ($line =~ /^(.+?)|(.+)$/o) {
+                       $headers->header($1 => $2);
+               }
+       }
+
+       my $ua = LWP::UserAgent->new;
+       $ua->agent("OpenILS/0.1");
+
+       my $req = HTTP::Request->new($method => $url => $headers => $data);
+       my $res = $ua->request($req);
+
+       if ($res->is_success) {
+               
+               $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.responseText', $res->content);
+               $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.readyState', 4);
+               $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.statusText', $res->status_line);
+               $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.status', $res->code);
+
+       }
+               
+}
+
+sub _parse_xml_string {
+       my $self = shift;
+       my $string = shift;
+       my $key = shift;
+
+
+       my $doc;
+       my $s = 0;
+       try {
+               $doc = XML::LibXML->new->parse_string( $string );
+               $s = 1;
+       } catch Error with {
+               my $e = shift;
+               warn "Could not parse document: $e\n";
+       };
+       return unless ($s);
+
+       _JS_DOM($self->context, $key, $doc);
+}
+
+sub _JS_DOM {
+       my $ctx = shift;
+       my $key = shift;
+       my $node = shift;
+
+       if ($node->nodeType == 9) {
+               $node = $node->documentElement;
+
+               my $n = $node->nodeName;
+               my $ns = $node->namespaceURI;
+               $ns =~ s/'/\'/gso if ($ns);
+               $ns = "'$ns'" if ($ns);
+               $ns = 'null' unless ($ns);
+               $n =~ s/'/\'/gso;
+
+               #warn("$key = DOMImplementation().createDocument($ns,'$n');");
+               $ctx->eval("$key = new DOMImplementation().createDocument($ns,'$n');");
+
+               $key = $key.'.documentElement';
+       }
+
+       for my $a ($node->attributes) {
+               my $n = $a->nodeName;
+               my $v = $a->value;
+               $n =~ s/'/\'/gso;
+               $v =~ s/'/\'/gso;
+               #warn("$key.setAttribute('$n','$v');");
+               $ctx->eval("$key.setAttribute('$n','$v');");
+
+       }
+
+       my $k = 0;
+       for my $c ($node->childNodes) {
+               if ($c->nodeType == 1) {
+                       my $n = $c->nodeName;
+                       my $ns = $node->namespaceURI;
+
+                       $n =~ s/'/\'/gso;
+                       $ns =~ s/'/\'/gso if ($ns);
+                       $ns = "'$ns'" if ($ns);
+                       $ns = 'null' unless ($ns);
+
+                       #warn("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
+                       $ctx->eval("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
+                       _JS_DOM($ctx, "$key.childNodes.item($k)",$c);
+
+               } elsif ($c->nodeType == 3) {
+                       my $n = $c->data;
+                       $n =~ s/'/\'/gso;
+                       #warn("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
+                       #warn("path is $key.item($k);");
+                       $ctx->eval("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
+
+               } elsif ($c->nodeType == 4) {
+                       my $n = $c->data;
+                       $n =~ s/'/\'/gso;
+                       #warn("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
+                       $ctx->eval("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
+
+               } elsif ($c->nodeType == 8) {
+                       my $n = $c->data;
+                       $n =~ s/'/\'/gso;
+                       #warn("$key.appendChild($key.ownerDocument.createComment('$n'));");
+                       $ctx->eval("$key.appendChild($key.ownerDocument.createComment('$n'));");
+
+               } else {
+                       warn "ACK! I don't know how to handle node type ".$c->nodeType;
+               }
+               
+
+               $k++;
+       }
+
+       return 1;
+}
+
+
+
 1;