/* javascript-xpath, an implementation of DOM Level 3 XPath for Internet Explorer 5+ Copyright (C) 2004 Dimitri Glazkov Modified 2006 Mehdi Hassan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software */ // Mozilla, Firefox, Safari etc have support by default, we don't have an implementation for the rest if (!(XPathResult.NUMBER_TYPE + XPathResult.NUMBER_TYPE)) { window._content = window; var _tb_windowEvents = new Array(); window.addEventListener = function(event, handler, flag) { _tb_windowEvents[event] = handler; }; function _tb_postProcess() { if (_tb_windowEvents["load"]) { _tb_windowEvents["load"](); } } var extern; if (window.dialogArguments && window.dialogArguments.external) { extern = window.dialogArguments.external; } else if (window.external) { extern = window.external; } // XPathException // An Error object will be thrown, this is just a handler to instantiate that object var XPathException = new _XPathExceptionHandler(); function _XPathExceptionHandler() { this.INVALID_EXPRESSION_ERR = 51; this.TYPE_ERR = 52; this.NOT_IMPLEMENTED_ERR = -1; this.RUNTIME_ERR = -2; this.ThrowNotImplemented = function(message) { ThrowError(this.NOT_IMPLEMENTED_ERR, "This functionality is not implemented.", message); }; this.ThrowInvalidExpression = function(message) { ThrowError(this.INVALID_EXPRESSION_ERR, "Invalid expression", message); }; this.ThrowType = function(message) { ThrowError(this.TYPE_ERR, "Type error", message); }; this.Throw = function(message) { ThrowError(this.RUNTIME_ERR, "Run-time error", message); }; function ThrowError(code, description, message) { var error = new Error(code, "DOM-L3-XPath : " + description + (message ? ", \"" + message + "\"": "")); error.code = code; error.name = "XPathException"; throw error; } } // DOMException // An Error object will be thrown, this is just a handler to instantiate that object var DOMException = new _DOMExceptionHandler(); function _DOMExceptionHandler() { this.ThrowInvalidState = function(message) { ThrowError(13, "The state of the object is no longer valid", message); }; function ThrowError(code, description, message) { var error = new Error(code, "DOM : " + description + (message ? ", \"" + message + "\"": "")); error.code = code; error.name = "DOMException"; throw error; } } // XPathEvaluator // implemented as document object methods // XPathExpression createExpression(String expression, XPathNSResolver resolver) document.createExpression = function ( expression, // String resolver // XPathNSResolver ) { // returns XPathExpression object return new XPathExpression(expression, resolver); }; // XPathNSResolver createNSResolver(nodeResolver) document.createNSResolver = function ( nodeResolver // Node ) { // returns XPathNSResolver return new XPathNSResolver(nodeResolver); }; // XPathResult evaluate(String expresison, Node contextNode, XPathNSResolver resolver, Number type, XPathResult result) document.evaluate = function ( expression, // String contextNode, // Node resolver, // XPathNSResolver type, // Number result // XPathResult ) // can raise XPathException, DOMException { // return XPathResult return document.createExpression(expression, resolver).evaluate(contextNode, type, result); }; // XPathExpression function XPathExpression ( expression, // String resolver // XPathNSResolver ) { this.expressionString = expression; this.resolver = resolver; // XPathResult evaluate(Node contextNode, Number type, XPathResult result) this.evaluate = function ( contextNode, // Node type, // Number result // XPathResult ) // raises XPathException, DOMException { // return XPathResult return (result && result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type)); }; this.toString = function() { return "[XPathExpression]"; }; } // XPathNSResolver function XPathNSResolver(node) { this.node = node; // String lookupNamespaceURI(String prefix) this.lookupNamespaceURI = function ( prefix // String ) { XPathException.ThrowNotImplemented(); // return String return null; }; this.toString = function() { return "[XPathNSResolver]"; }; } // XPathResult 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_SNAPSHOT_TYPE = 6; XPathResult.ORDERED_SNAPSHOT_TYPE = 7; XPathResult.ANY_UNORDERED_NODE_TYPE = 8; XPathResult.FIRST_ORDERED_NODE_TYPE = 9; function XPathResult ( expression, // XPathExpression contextNode, // Node resolver, // XPathNSResolver type // Number ) { this.initialize = function(expression, contextNode, resolver, type) { this._domResult = null; this._expression = expression; this._contextNode = contextNode; this._resolver = resolver; if (type) { this.resultType = type; this._isIterator = (type == XPathResult.UNORDERED_NODE_ITERATOR_TYPE || type == XPathResult.ORDERED_NODE_ITERATOR_TYPE || type == XPathResult.ANY_TYPE); this._isSnapshot = (type == XPathResult.UNORDERED_SNAPSHOT_TYPE || type == XPathResult.ORDERED_SNAPSHOT_TYPE); this._isNodeSet = type > XPathResult.BOOLEAN_TYPE; } else { this.resultType = XPathResult.ANY_TYPE; this._isIterator = true; this._isSnapshot = false; this._isNodeSet = true; } return this; }; this.initialize(expression, contextNode, resolver, type); this.getInvalidIteratorState = function() { return documentChangeDetected() || !this._isIterator; }; this.getSnapshotLength = function() // raises XPathException { if (!this._isSnapshot) { XPathException.ThrowType("Snapshot is not an expected result type"); } activateResult(this); // return Number return this._domResult.length; }; // Node iterateNext() this.iterateNext = function() // raises XPathException, DOMException { if (!this._isIterator) { XPathException.ThrowType("Iterator is not an expected result type"); } activateResult(this); if (documentChangeDetected()) { DOMException.ThrowInvalidState("iterateNext"); } // return Node return getNextNode(this); }; // Node snapshotItem(Number index) this.snapshotItem = function(index) // raises XPathException { if (!this._isSnapshot) { XPathException.ThrowType("Snapshot is not an expected result type"); } // return Node return getItemNode(this, index); }; this.toString = function() { return "[XPathResult]"; }; // returns string value of the result, if result type is STRING_TYPE // otherwise throws an XPathException this.getStringValue = function() { if (this.resultType != XPathResult.STRING_TYPE) { XPathException.ThrowType("The expression can not be converted to return String"); } return getNodeText(this); }; // returns number value of the result, if the result is NUMBER_TYPE // otherwise throws an XPathException this.getNumberValue = function() { if (this.resultType != XPathResult.NUMBER_TYPE) { XPathException.ThrowType("The expression can not be converted to return Number"); } var number = parseInt(getNodeText(this)); if (isNaN(number)) { XPathException.ThrowType("The result can not be converted to Number"); } return number; }; // returns boolean value of the result, if the result is BOOLEAN_TYPE // otherwise throws an XPathException this.getBooleanValue = function() { if (this.resultType != XPathResult.BOOLEAN_TYPE) { XPathException.ThrowType("The expression can not be converted to return Boolean"); } var text = getNodeText(this); bool = (text ? text.toLowerCase() : null); if (bool == "false" || bool == "true") { return bool; } XPathException.ThrowType("The result can not be converted to Boolean"); }; // returns single node, if the result is ANY_UNORDERED_NODE_TYPE or FIRST_ORDERED_NODE_TYPE // otherwise throws an XPathException this.getSingleNodeValue = function() { if (this.resultType != XPathResult.ANY_UNORDERED_NODE_TYPE && this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE) { XPathException.ThrowType("The expression can not be converted to return single Node value"); } return getSingleNode(this); }; function documentChangeDetected() { return document._XPathMsxmlDocumentHelper.documentChangeDetected(); } function getNodeText(result) { activateResult(result); return result._textResult; // return ((node = getSingleNode(result)) ? (node.nodeType == 1 ? node.innerText : node.nodeValue) : null); } function findNode(result, current) { switch(current.nodeType) { case 1: // NODE_ELEMENT var id = current.attributes.getNamedItem("id"); if (id) { return document.getElementById(id.value); } XPathException.Throw("unable to locate element in XML tree"); case 2: // NODE_ATTRIBUTE var id = current.selectSingleNode("..").attributes.getNamedItem("id"); if (id) { var node = document.getElementById(id.text); if (node) { return node.attributes.getNamedItem(current.nodeName); } } XPathException.Throw("unable to locate attribute in XML tree"); case 3: // NODE_TEXT var id = current.selectSingleNode("..").attributes.getNamedItem("id"); if (id) { var node = document.getElementById(id.value); if (node) { for(child in node.childNodes) { if (child.nodeType == 3 && child.nodeValue == current.nodeValue) { return child; } } } } XPathException.Throw("unable to locate text in XML tree"); } XPathException.Throw("unknown node type"); } function activateResult(result) { if (!result._domResult) { try { var expression = result._expression.expressionString; // adjust expression if contextNode is not a document if (result._contextNode != document && expression.indexOf("//") != 0) { expression = "//*[@id = '" + result._contextNode.id + "']" + (expression.indexOf("/") == 0 ? "" : "/") + expression; } if (result._isNodeSet) { result._domResult = document._XPathMsxmlDocumentHelper.getDom().selectNodes(expression); } else { result._domResult = true; result._textResult = document._XPathMsxmlDocumentHelper.getTextResult(expression); } } catch(error) { alert(error.description); XPathException.ThrowInvalidExpression(error.description); } } } function getSingleNode(result) { var node = getItemNode(result, 0); result._domResult = null; return node; } function getItemNode(result, index) { activateResult(result); var current = result._domResult.item(index); return (current ? findNode(result, current) : null); } function getNextNode(result) { var current = result._domResult.nextNode; if (current) { return findNode(result, current); } result._domResult = null; return null; } } document.reloadDom = function() { document._XPathMsxmlDocumentHelper.reset(); }; document._XPathMsxmlDocumentHelper = new _XPathMsxmlDocumentHelper(); function _XPathMsxmlDocumentHelper() { this.getDom = function() { activateDom(this); return this.dom; }; this.getXml = function() { activateDom(this); return this.dom.xml; }; this.getTextResult = function(expression) { expression = expression.replace(//g, ">").replace(/"/g, "\""); var xslText = "" + "" + ""; var xsl = new ActiveXObject("Msxml2.DOMDocument"); xsl.loadXML(xslText); try { var result = this.getDom().transformNode(xsl); } catch(error) { alert("Error: " + error.description); } return result; }; this.reset = function() { this.dom = null; }; function onPropertyChangeEventHandler() { document._propertyChangeDetected = true; }; this.documentChangeDetected = function() { return (document.ignoreDocumentChanges ? false : this._currentElementCount != document.all.length || document._propertyChangeDetected); }; function activateDom(helper) { if (!helper.dom) { var dom = new ActiveXObject("Msxml2.DOMDocument"); dom.async = false; dom.resolveExternals = false; loadDocument(dom, helper); helper.dom = dom; helper._currentElementCount = document.all.length; document._propertyChangeDetected = false; } else { if (helper.documentChangeDetected()) { var dom = helper.dom; dom.load(""); loadDocument(dom, helper); helper._currentElementCount = document.all.length; document._propertyChangeDetected = false; } } } function loadDocument(dom, helper) { return loadNode(dom, dom, document.body, helper); } function loadNode(dom, domParentNode, node, helper) { if (node.nodeType == 3) { domParentNode.appendChild(dom.createTextNode(node.nodeValue)); } else { var domNode = dom.createElement(node.nodeName.toLowerCase()); if (!node.id) { node.id = node.uniqueID; } domParentNode.appendChild(domNode); loadAttributes(dom, domNode, node); var length = node.childNodes.length; for(var i = 0; i < length; i ++ ) { loadNode(dom, domNode, node.childNodes[i], helper); } node.attachEvent("onpropertychange", onPropertyChangeEventHandler); } } function loadAttributes(dom, domParentNode, node) { for (var i = 0; i < node.attributes.length; i ++ ) { var attribute = node.attributes[i]; var attributeValue = attribute.nodeValue; if (attributeValue && attribute.specified) { var domAttribute = dom.createAttribute(attribute.nodeName); domAttribute.value = attributeValue; domParentNode.setAttributeNode(domAttribute); } } } } } else { document.reloadDom = function() {}; XPathResult.prototype.getStringValue = function() { return this.stringValue; }; XPathResult.prototype.getNumberValue = function() { return this.numberValue; }; XPathResult.prototype.getBooleanValue = function() { return this.booleanValue; }; XPathResult.prototype.getSingleNodeValue = function() { return this.singleNodeValue; }; XPathResult.prototype.getInvalidIteratorState = function() { return this.invalidIteratorState; }; XPathResult.prototype.getSnapshotLength = function() { return this.snapshotLength; }; XPathResult.prototype.getResultType = function() { return this.resultType; }; }