1 /** 2 * ==================================================================== 3 * About 4 * ==================================================================== 5 * Sarissa cross browser XML library - IE XPath Emulation 6 * @version ${project.version} 7 * @author: Copyright 2004-2007 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net 8 * 9 * This script emulates Internet Explorer's selectNodes and selectSingleNode 10 * for Mozilla. Associating namespace prefixes with URIs for your XPath queries 11 * is easy with IE's setProperty. 12 * USers may also map a namespace prefix to a default (unprefixed) namespace in the 13 * source document with Sarissa.setXpathNamespaces 14 * 15 * ==================================================================== 16 * Licence 17 * ==================================================================== 18 * Sarissa is free software distributed under the GNU GPL version 2 (see <a href="gpl.txt">gpl.txt</a>) or higher, 19 * GNU LGPL version 2.1 (see <a href="lgpl.txt">lgpl.txt</a>) or higher and Apache Software License 2.0 or higher 20 * (see <a href="asl.txt">asl.txt</a>). This means you can choose one of the three and use that if you like. If 21 * you make modifications under the ASL, i would appreciate it if you submitted those. 22 * In case your copy of Sarissa does not include the license texts, you may find 23 * them online in various formats at <a href="http://www.gnu.org">http://www.gnu.org</a> and 24 * <a href="http://www.apache.org">http://www.apache.org</a>. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 27 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 28 * WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE 29 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 30 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 32 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 33 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 */ 35 if(Sarissa._SARISSA_HAS_DOM_FEATURE && document.implementation.hasFeature("XPath", "3.0")){ 36 /** 37 * <p>SarissaNodeList behaves as a NodeList but is only used as a result to <code>selectNodes</code>, 38 * so it also has some properties IEs proprietery object features.</p> 39 * @private 40 * @constructor 41 * @argument i the (initial) list size 42 */ 43 SarissaNodeList = function (i){ 44 this.length = i; 45 }; 46 /** 47 * <p>Set an Array as the prototype object</p> 48 * @private 49 */ 50 SarissaNodeList.prototype = []; 51 /** 52 * <p>Inherit the Array constructor </p> 53 * @private 54 */ 55 SarissaNodeList.prototype.constructor = Array; 56 /** 57 * <p>Returns the node at the specified index or null if the given index 58 * is greater than the list size or less than zero </p> 59 * <p><b>Note</b> that in ECMAScript you can also use the square-bracket 60 * array notation instead of calling <code>item</code> 61 * @argument i the index of the member to return 62 * @returns the member corresponding to the given index 63 * @private 64 */ 65 SarissaNodeList.prototype.item = function(i) { 66 return (i < 0 || i >= this.length)?null:this[i]; 67 }; 68 /** 69 * <p>Emulate IE's expr property 70 * (Here the SarissaNodeList object is given as the result of selectNodes).</p> 71 * @returns the XPath expression passed to selectNodes that resulted in 72 * this SarissaNodeList 73 * @private 74 */ 75 SarissaNodeList.prototype.expr = ""; 76 /** dummy, used to accept IE's stuff without throwing errors */ 77 if(window.XMLDocument && (!XMLDocument.prototype.setProperty)){ 78 XMLDocument.prototype.setProperty = function(x,y){}; 79 } 80 /** 81 * <p>Programmatically control namespace URI/prefix mappings for XPath 82 * queries.</p> 83 * <p>This method comes especially handy when used to apply XPath queries 84 * on XML documents with a default namespace, as there is no other way 85 * of mapping that to a prefix.</p> 86 * <p>Using no namespace prefix in DOM Level 3 XPath queries, implies you 87 * are looking for elements in the null namespace. If you need to look 88 * for nodes in the default namespace, you need to map a prefix to it 89 * first like:</p> 90 * <pre>Sarissa.setXpathNamespaces(oDoc, "xmlns:myprefix'http://mynsURI'");</pre> 91 * <p><b>Note 1 </b>: Use this method only if the source document features 92 * a default namespace (without a prefix) or contains namespace declarations with 93 * a scope that does not cover the entire document (i.e. declared but not within the 94 * root element node). Otherwise just use IE's setProperty. You will need to map that 95 * namespace to a prefix for queries to work. 96 * Moz/FF will resolve non-default namespaces automatically if those are declared 97 * in the root element. </p> 98 * <p><b>Note 2 </b>: This method calls IE's setProperty method to set the 99 * appropriate namespace-prefix mappings, so you dont have to do that.</p> 100 * @param oDoc The target XMLDocument to set the namespace mappings for. 101 * @param sNsSet A whilespace-seperated list of namespace declarations as 102 * those would appear in an XML document. E.g.: 103 * <code>"xmlns:xhtml='http://www.w3.org/1999/xhtml' 104 * xmlns:'http://www.w3.org/1999/XSL/Transform'"</code> 105 * @throws An error if the format of the given namespace declarations is bad. 106 */ 107 Sarissa.setXpathNamespaces = function(oDoc, sNsSet) { 108 //oDoc._sarissa_setXpathNamespaces(sNsSet); 109 oDoc._sarissa_useCustomResolver = true; 110 var namespaces = sNsSet.indexOf(" ")>-1?sNsSet.split(" "):[sNsSet]; 111 oDoc._sarissa_xpathNamespaces = []; 112 for(var i=0;i < namespaces.length;i++){ 113 var ns = namespaces[i]; 114 var colonPos = ns.indexOf(":"); 115 var assignPos = ns.indexOf("="); 116 if(colonPos > 0 && assignPos > colonPos+1){ 117 var prefix = ns.substring(colonPos+1, assignPos); 118 var uri = ns.substring(assignPos+2, ns.length-1); 119 oDoc._sarissa_xpathNamespaces[prefix] = uri; 120 }else{ 121 throw "Bad format on namespace declaration(s) given"; 122 } 123 } 124 }; 125 /** 126 * @private Flag to control whether a custom namespace resolver should 127 * be used, set to true by Sarissa.setXpathNamespaces 128 */ 129 XMLDocument.prototype._sarissa_useCustomResolver = false; 130 /** @private */ 131 XMLDocument.prototype._sarissa_xpathNamespaces = []; 132 /** 133 * <p>Extends the XMLDocument to emulate IE's selectNodes.</p> 134 * @argument sExpr the XPath expression to use 135 * @argument contextNode this is for internal use only by the same 136 * method when called on Elements 137 * @returns the result of the XPath search as a SarissaNodeList 138 * @throws An error if no namespace URI is found for the given prefix. 139 */ 140 XMLDocument.prototype.selectNodes = function(sExpr, contextNode, returnSingle){ 141 var nsDoc = this; 142 var nsresolver; 143 if(this._sarissa_useCustomResolver){ 144 nsresolver = function(prefix){ 145 var s = nsDoc._sarissa_xpathNamespaces[prefix]; 146 if(s){ 147 return s; 148 } 149 else { 150 throw "No namespace URI found for prefix: '" + prefix+"'"; 151 } 152 }; 153 } 154 else{ 155 nsresolver = this.createNSResolver(this.documentElement); 156 } 157 var result = null; 158 if(!returnSingle){ 159 var oResult = this.evaluate(sExpr, 160 (contextNode?contextNode:this), 161 nsresolver, 162 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 163 var nodeList = new SarissaNodeList(oResult.snapshotLength); 164 nodeList.expr = sExpr; 165 for(var i=0;i<nodeList.length;i++){ 166 nodeList[i] = oResult.snapshotItem(i); 167 } 168 result = nodeList; 169 } 170 else { 171 result = this.evaluate(sExpr, 172 (contextNode?contextNode:this), 173 nsresolver, 174 XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 175 } 176 return result; 177 }; 178 /** 179 * <p>Extends the Element to emulate IE's selectNodes</p> 180 * @argument sExpr the XPath expression to use 181 * @returns the result of the XPath search as an (Sarissa)NodeList 182 * @throws An 183 * error if invoked on an HTML Element as this is only be 184 * available to XML Elements. 185 */ 186 Element.prototype.selectNodes = function(sExpr){ 187 var doc = this.ownerDocument; 188 if(doc.selectNodes){ 189 return doc.selectNodes(sExpr, this); 190 } 191 else{ 192 throw "Method selectNodes is only supported by XML Elements"; 193 } 194 }; 195 /** 196 * <p>Extends the XMLDocument to emulate IE's selectSingleNode.</p> 197 * @argument sExpr the XPath expression to use 198 * @argument contextNode this is for internal use only by the same 199 * method when called on Elements 200 * @returns the result of the XPath search as an (Sarissa)NodeList 201 */ 202 XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode){ 203 var ctx = contextNode?contextNode:null; 204 return this.selectNodes(sExpr, ctx, true); 205 }; 206 /** 207 * <p>Extends the Element to emulate IE's selectSingleNode.</p> 208 * @argument sExpr the XPath expression to use 209 * @returns the result of the XPath search as an (Sarissa)NodeList 210 * @throws An error if invoked on an HTML Element as this is only be 211 * available to XML Elements. 212 */ 213 Element.prototype.selectSingleNode = function(sExpr){ 214 var doc = this.ownerDocument; 215 if(doc.selectSingleNode){ 216 return doc.selectSingleNode(sExpr, this); 217 } 218 else{ 219 throw "Method selectNodes is only supported by XML Elements"; 220 } 221 }; 222 Sarissa.IS_ENABLED_SELECT_NODES = true; 223 }