From 4007a86fa330477cbac4c20b2ba541ce64b572c3 Mon Sep 17 00:00:00 2001 From: miker Date: Sat, 14 Jan 2006 15:27:12 +0000 Subject: [PATCH] XML DOM, XPath and XMLHttpRequest support for O::U::SpiderMonkey git-svn-id: svn://svn.open-ils.org/ILS/trunk@2713 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/javascript/backend/libs/jsDOM.js | 638 +++ Open-ILS/src/javascript/backend/libs/jsOO.js | 46 + .../javascript/backend/libs/xmlhttprequest.js | 45 + Open-ILS/src/javascript/backend/libs/xpath.js | 4277 +++++++++++++++++ .../backend/test/xmlhttprequest-test.js | 38 + .../perlmods/OpenILS/Utils/SpiderMonkey.pm | 239 +- 6 files changed, 5268 insertions(+), 15 deletions(-) create mode 100644 Open-ILS/src/javascript/backend/libs/jsDOM.js create mode 100644 Open-ILS/src/javascript/backend/libs/jsOO.js create mode 100644 Open-ILS/src/javascript/backend/libs/xmlhttprequest.js create mode 100644 Open-ILS/src/javascript/backend/libs/xpath.js create mode 100644 Open-ILS/src/javascript/backend/test/xmlhttprequest-test.js diff --git a/Open-ILS/src/javascript/backend/libs/jsDOM.js b/Open-ILS/src/javascript/backend/libs/jsDOM.js new file mode 100644 index 0000000000..1738b904a0 --- /dev/null +++ b/Open-ILS/src/javascript/backend/libs/jsDOM.js @@ -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 index 0000000000..d40321c35f --- /dev/null +++ b/Open-ILS/src/javascript/backend/libs/jsOO.js @@ -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 index 0000000000..3a16463d22 --- /dev/null +++ b/Open-ILS/src/javascript/backend/libs/xmlhttprequest.js @@ -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 index 0000000000..a722b382fe --- /dev/null +++ b/Open-ILS/src/javascript/backend/libs/xpath.js @@ -0,0 +1,4277 @@ +/* + * xpath.js + * + * An XPath 1.0 library for JavaScript. + * + * Cameron McCormack + * + * 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 . + * AVL tree code adapted from Raimund Neumann . + * + * 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 . + * + * 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 . + * + * 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 + * . + * + * 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 . + * 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<<<<<<<< <<<<<?@ 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.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 ""; +}; + +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 index 0000000000..b2dfd1bb9e --- /dev/null +++ b/Open-ILS/src/javascript/backend/test/xmlhttprequest-test.js @@ -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¶m=' + 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---------------------------------------'); + diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/SpiderMonkey.pm b/Open-ILS/src/perlmods/OpenILS/Utils/SpiderMonkey.pm index a1da49ea88..88d0a96beb 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/SpiderMonkey.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/SpiderMonkey.pm @@ -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", )) ) { - $logger->error("Script ($file) eval failed in SpiderMonkey run: $@"); - return 0; + { local $/ = undef; + my $content = ; + 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; -- 2.43.2