paulb@44 | 1 | /** |
paulb@44 | 2 | * ==================================================================== |
paulb@44 | 3 | * About |
paulb@44 | 4 | * ==================================================================== |
paulb@44 | 5 | * Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs. |
paulb@44 | 6 | * The library supports Gecko based browsers like Mozilla and Firefox, |
paulb@600 | 7 | * Internet Explorer (5.5+ with MSXML3.0+), Konqueror, Safari and a little of Opera |
paulb@600 | 8 | * @version ${project.version} |
paulb@619 | 9 | * @author: @author: Copyright 2004-2007 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net |
paulb@619 | 10 | * |
paulb@44 | 11 | * ==================================================================== |
paulb@44 | 12 | * Licence |
paulb@44 | 13 | * ==================================================================== |
paulb@600 | 14 | * Sarissa is free software distributed under the GNU GPL version 2 (see <a href="gpl.txt">gpl.txt</a>) or higher, |
paulb@600 | 15 | * GNU LGPL version 2.1 (see <a href="lgpl.txt">lgpl.txt</a>) or higher and Apache Software License 2.0 or higher |
paulb@600 | 16 | * (see <a href="asl.txt">asl.txt</a>). This means you can choose one of the three and use that if you like. If |
paulb@600 | 17 | * you make modifications under the ASL, i would appreciate it if you submitted those. |
paulb@600 | 18 | * In case your copy of Sarissa does not include the license texts, you may find |
paulb@600 | 19 | * them online in various formats at <a href="http://www.gnu.org">http://www.gnu.org</a> and |
paulb@600 | 20 | * <a href="http://www.apache.org">http://www.apache.org</a>. |
paulb@619 | 21 | * |
paulb@600 | 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
paulb@600 | 23 | * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
paulb@600 | 24 | * WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE |
paulb@600 | 25 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
paulb@600 | 26 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
paulb@600 | 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
paulb@600 | 28 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
paulb@600 | 29 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
paulb@44 | 30 | */ |
paulb@44 | 31 | /** |
paulb@600 | 32 | * <p>Sarissa is a utility class. Provides "static" methods for DOMDocument, |
paulb@600 | 33 | * DOM Node serialization to XML strings and other utility goodies.</p> |
paulb@44 | 34 | * @constructor |
paulb@44 | 35 | */ |
paulb@44 | 36 | function Sarissa(){}; |
paulb@600 | 37 | Sarissa.VERSION = "${project.version}"; |
paulb@44 | 38 | Sarissa.PARSED_OK = "Document contains no parsing errors"; |
paulb@600 | 39 | Sarissa.PARSED_EMPTY = "Document is empty"; |
paulb@600 | 40 | Sarissa.PARSED_UNKNOWN_ERROR = "Not well-formed or other error"; |
paulb@44 | 41 | Sarissa.IS_ENABLED_TRANSFORM_NODE = false; |
paulb@44 | 42 | var _sarissa_iNsCounter = 0; |
paulb@44 | 43 | var _SARISSA_IEPREFIX4XSLPARAM = ""; |
paulb@44 | 44 | var _SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true; |
paulb@44 | 45 | var _SARISSA_HAS_DOM_CREATE_DOCUMENT = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument; |
paulb@44 | 46 | var _SARISSA_HAS_DOM_FEATURE = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature; |
paulb@44 | 47 | var _SARISSA_IS_MOZ = _SARISSA_HAS_DOM_CREATE_DOCUMENT && _SARISSA_HAS_DOM_FEATURE; |
paulb@619 | 48 | var _SARISSA_IS_SAFARI = navigator.userAgent.toLowerCase().indexOf("safari") != -1 || navigator.userAgent.toLowerCase().indexOf("konqueror") != -1; |
paulb@619 | 49 | var _SARISSA_IS_SAFARI_OLD = _SARISSA_IS_SAFARI && parseInt((navigator.userAgent.match(/AppleWebKit\/(\d+)/)||{})[1]) < 420; |
paulb@73 | 50 | var _SARISSA_IS_IE = document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1; |
paulb@619 | 51 | var _SARISSA_IS_OPERA = navigator.userAgent.toLowerCase().indexOf("opera") != -1; |
paulb@600 | 52 | if(!window.Node || !Node.ELEMENT_NODE){ |
paulb@600 | 53 | Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12}; |
paulb@600 | 54 | }; |
paulb@73 | 55 | |
paulb@619 | 56 | //This breaks for(x in o) loops in the old Safari |
paulb@619 | 57 | if(_SARISSA_IS_SAFARI_OLD){ |
paulb@619 | 58 | HTMLHtmlElement = document.createElement("html").constructor; |
paulb@619 | 59 | Node = HTMLElement = {}; |
paulb@619 | 60 | HTMLElement.prototype = HTMLHtmlElement.__proto__.__proto__; |
paulb@619 | 61 | HTMLDocument = Document = document.constructor; |
paulb@619 | 62 | var x = new DOMParser(); |
paulb@619 | 63 | XMLDocument = x.constructor; |
paulb@619 | 64 | Element = x.parseFromString("<Single />", "text/xml").documentElement.constructor; |
paulb@619 | 65 | x = null; |
paulb@619 | 66 | } |
paulb@600 | 67 | if(typeof XMLDocument == "undefined" && typeof Document !="undefined"){ XMLDocument = Document; } |
paulb@73 | 68 | |
paulb@44 | 69 | // IE initialization |
paulb@44 | 70 | if(_SARISSA_IS_IE){ |
paulb@44 | 71 | // for XSLT parameter names, prefix needed by IE |
paulb@44 | 72 | _SARISSA_IEPREFIX4XSLPARAM = "xsl:"; |
paulb@44 | 73 | // used to store the most recent ProgID available out of the above |
paulb@44 | 74 | var _SARISSA_DOM_PROGID = ""; |
paulb@44 | 75 | var _SARISSA_XMLHTTP_PROGID = ""; |
paulb@600 | 76 | var _SARISSA_DOM_XMLWRITER = ""; |
paulb@44 | 77 | /** |
paulb@44 | 78 | * Called when the Sarissa_xx.js file is parsed, to pick most recent |
paulb@44 | 79 | * ProgIDs for IE, then gets destroyed. |
paulb@600 | 80 | * @private |
paulb@44 | 81 | * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object |
paulb@44 | 82 | * @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled |
paulb@44 | 83 | */ |
paulb@600 | 84 | Sarissa.pickRecentProgID = function (idList){ |
paulb@44 | 85 | // found progID flag |
paulb@619 | 86 | var bFound = false, e; |
paulb@44 | 87 | for(var i=0; i < idList.length && !bFound; i++){ |
paulb@44 | 88 | try{ |
paulb@44 | 89 | var oDoc = new ActiveXObject(idList[i]); |
paulb@619 | 90 | var o2Store = idList[i]; |
paulb@44 | 91 | bFound = true; |
paulb@44 | 92 | }catch (objException){ |
paulb@44 | 93 | // trap; try next progID |
paulb@619 | 94 | e = objException; |
paulb@44 | 95 | }; |
paulb@44 | 96 | }; |
paulb@600 | 97 | if (!bFound) { |
paulb@619 | 98 | throw "Could not retrieve a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")"; |
paulb@600 | 99 | }; |
paulb@44 | 100 | idList = null; |
paulb@44 | 101 | return o2Store; |
paulb@44 | 102 | }; |
paulb@44 | 103 | // pick best available MSXML progIDs |
paulb@600 | 104 | _SARISSA_DOM_PROGID = null; |
paulb@600 | 105 | _SARISSA_THREADEDDOM_PROGID = null; |
paulb@600 | 106 | _SARISSA_XSLTEMPLATE_PROGID = null; |
paulb@600 | 107 | _SARISSA_XMLHTTP_PROGID = null; |
paulb@600 | 108 | if(!window.XMLHttpRequest){ |
paulb@600 | 109 | /** |
paulb@600 | 110 | * Emulate XMLHttpRequest |
paulb@600 | 111 | * @constructor |
paulb@600 | 112 | */ |
paulb@600 | 113 | XMLHttpRequest = function() { |
paulb@600 | 114 | if(!_SARISSA_XMLHTTP_PROGID){ |
paulb@600 | 115 | _SARISSA_XMLHTTP_PROGID = Sarissa.pickRecentProgID(["Msxml2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]); |
paulb@600 | 116 | }; |
paulb@600 | 117 | return new ActiveXObject(_SARISSA_XMLHTTP_PROGID); |
paulb@600 | 118 | }; |
paulb@600 | 119 | }; |
paulb@44 | 120 | // we dont need this anymore |
paulb@44 | 121 | //============================================ |
paulb@44 | 122 | // Factory methods (IE) |
paulb@44 | 123 | //============================================ |
paulb@44 | 124 | // see non-IE version |
paulb@44 | 125 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@600 | 126 | if(!_SARISSA_DOM_PROGID){ |
paulb@600 | 127 | _SARISSA_DOM_PROGID = Sarissa.pickRecentProgID(["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]); |
paulb@600 | 128 | }; |
paulb@44 | 129 | var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@600 | 130 | // if a root tag name was provided, we need to load it in the DOM object |
paulb@44 | 131 | if (sName){ |
paulb@600 | 132 | // create an artifical namespace prefix |
paulb@600 | 133 | // or reuse existing prefix if applicable |
paulb@600 | 134 | var prefix = ""; |
paulb@600 | 135 | if(sUri){ |
paulb@600 | 136 | if(sName.indexOf(":") > 1){ |
paulb@600 | 137 | prefix = sName.substring(0, sName.indexOf(":")); |
paulb@600 | 138 | sName = sName.substring(sName.indexOf(":")+1); |
paulb@600 | 139 | }else{ |
paulb@600 | 140 | prefix = "a" + (_sarissa_iNsCounter++); |
paulb@600 | 141 | }; |
paulb@600 | 142 | }; |
paulb@600 | 143 | // use namespaces if a namespace URI exists |
paulb@600 | 144 | if(sUri){ |
paulb@600 | 145 | oDoc.loadXML('<' + prefix+':'+sName + " xmlns:" + prefix + "=\"" + sUri + "\"" + " />"); |
paulb@600 | 146 | } else { |
paulb@600 | 147 | oDoc.loadXML('<' + sName + " />"); |
paulb@600 | 148 | }; |
paulb@44 | 149 | }; |
paulb@44 | 150 | return oDoc; |
paulb@44 | 151 | }; |
paulb@44 | 152 | // see non-IE version |
paulb@44 | 153 | Sarissa.getParseErrorText = function (oDoc) { |
paulb@44 | 154 | var parseErrorText = Sarissa.PARSED_OK; |
paulb@600 | 155 | if(oDoc && oDoc.parseError && oDoc.parseError.errorCode && oDoc.parseError.errorCode != 0){ |
paulb@44 | 156 | parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + |
paulb@44 | 157 | "\nLocation: " + oDoc.parseError.url + |
paulb@44 | 158 | "\nLine Number " + oDoc.parseError.line + ", Column " + |
paulb@44 | 159 | oDoc.parseError.linepos + |
paulb@44 | 160 | ":\n" + oDoc.parseError.srcText + |
paulb@44 | 161 | "\n"; |
paulb@44 | 162 | for(var i = 0; i < oDoc.parseError.linepos;i++){ |
paulb@44 | 163 | parseErrorText += "-"; |
paulb@44 | 164 | }; |
paulb@44 | 165 | parseErrorText += "^\n"; |
paulb@600 | 166 | } |
paulb@600 | 167 | else if(oDoc.documentElement == null){ |
paulb@600 | 168 | parseErrorText = Sarissa.PARSED_EMPTY; |
paulb@44 | 169 | }; |
paulb@44 | 170 | return parseErrorText; |
paulb@44 | 171 | }; |
paulb@44 | 172 | // see non-IE version |
paulb@44 | 173 | Sarissa.setXpathNamespaces = function(oDoc, sNsSet) { |
paulb@44 | 174 | oDoc.setProperty("SelectionLanguage", "XPath"); |
paulb@44 | 175 | oDoc.setProperty("SelectionNamespaces", sNsSet); |
paulb@44 | 176 | }; |
paulb@44 | 177 | /** |
paulb@44 | 178 | * Basic implementation of Mozilla's XSLTProcessor for IE. |
paulb@44 | 179 | * Reuses the same XSLT stylesheet for multiple transforms |
paulb@44 | 180 | * @constructor |
paulb@44 | 181 | */ |
paulb@73 | 182 | XSLTProcessor = function(){ |
paulb@600 | 183 | if(!_SARISSA_XSLTEMPLATE_PROGID){ |
paulb@600 | 184 | _SARISSA_XSLTEMPLATE_PROGID = Sarissa.pickRecentProgID(["Msxml2.XSLTemplate.6.0", "MSXML2.XSLTemplate.3.0"]); |
paulb@600 | 185 | }; |
paulb@44 | 186 | this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID); |
paulb@44 | 187 | this.processor = null; |
paulb@44 | 188 | }; |
paulb@44 | 189 | /** |
paulb@600 | 190 | * Imports the given XSLT DOM and compiles it to a reusable transform |
paulb@600 | 191 | * <b>Note:</b> If the stylesheet was loaded from a URL and contains xsl:import or xsl:include elements,it will be reloaded to resolve those |
paulb@44 | 192 | * @argument xslDoc The XSLT DOMDocument to import |
paulb@44 | 193 | */ |
paulb@44 | 194 | XSLTProcessor.prototype.importStylesheet = function(xslDoc){ |
paulb@600 | 195 | if(!_SARISSA_THREADEDDOM_PROGID){ |
paulb@600 | 196 | _SARISSA_THREADEDDOM_PROGID = Sarissa.pickRecentProgID(["MSXML2.FreeThreadedDOMDocument.6.0", "MSXML2.FreeThreadedDOMDocument.3.0"]); |
paulb@600 | 197 | }; |
paulb@600 | 198 | xslDoc.setProperty("SelectionLanguage", "XPath"); |
paulb@600 | 199 | xslDoc.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"); |
paulb@44 | 200 | // convert stylesheet to free threaded |
paulb@600 | 201 | var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID); |
paulb@600 | 202 | // make included/imported stylesheets work if exist and xsl was originally loaded from url |
paulb@619 | 203 | try{ |
paulb@619 | 204 | converted.resolveExternals = true; |
paulb@619 | 205 | converted.setProperty("AllowDocumentFunction", true); |
paulb@619 | 206 | } |
paulb@619 | 207 | catch(e){ |
paulb@619 | 208 | // Ignore. "AllowDocumentFunction" is only supported in MSXML 3.0 SP4 and later. |
paulb@619 | 209 | }; |
paulb@600 | 210 | if(xslDoc.url && xslDoc.selectSingleNode("//xsl:*[local-name() = 'import' or local-name() = 'include']") != null){ |
paulb@600 | 211 | converted.async = false; |
paulb@600 | 212 | converted.load(xslDoc.url); |
paulb@600 | 213 | } else { |
paulb@600 | 214 | converted.loadXML(xslDoc.xml); |
paulb@600 | 215 | }; |
paulb@600 | 216 | converted.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"); |
paulb@600 | 217 | var output = converted.selectSingleNode("//xsl:output"); |
paulb@600 | 218 | this.outputMethod = output ? output.getAttribute("method") : "html"; |
paulb@44 | 219 | this.template.stylesheet = converted; |
paulb@44 | 220 | this.processor = this.template.createProcessor(); |
paulb@600 | 221 | // for getParameter and clearParameters |
paulb@619 | 222 | this.paramsSet = []; |
paulb@44 | 223 | }; |
paulb@600 | 224 | |
paulb@44 | 225 | /** |
paulb@600 | 226 | * Transform the given XML DOM and return the transformation result as a new DOM document |
paulb@44 | 227 | * @argument sourceDoc The XML DOMDocument to transform |
paulb@44 | 228 | * @return The transformation result as a DOM Document |
paulb@44 | 229 | */ |
paulb@44 | 230 | XSLTProcessor.prototype.transformToDocument = function(sourceDoc){ |
paulb@600 | 231 | // fix for bug 1549749 |
paulb@600 | 232 | if(_SARISSA_THREADEDDOM_PROGID){ |
paulb@600 | 233 | this.processor.input=sourceDoc; |
paulb@600 | 234 | var outDoc=new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@600 | 235 | this.processor.output=outDoc; |
paulb@600 | 236 | this.processor.transform(); |
paulb@600 | 237 | return outDoc; |
paulb@600 | 238 | } |
paulb@600 | 239 | else{ |
paulb@600 | 240 | if(!_SARISSA_DOM_XMLWRITER){ |
paulb@600 | 241 | _SARISSA_DOM_XMLWRITER = Sarissa.pickRecentProgID(["Msxml2.MXXMLWriter.6.0", "Msxml2.MXXMLWriter.3.0", "MSXML2.MXXMLWriter", "MSXML.MXXMLWriter", "Microsoft.XMLDOM"]); |
paulb@600 | 242 | }; |
paulb@600 | 243 | this.processor.input = sourceDoc; |
paulb@600 | 244 | var outDoc = new ActiveXObject(_SARISSA_DOM_XMLWRITER); |
paulb@600 | 245 | this.processor.output = outDoc; |
paulb@600 | 246 | this.processor.transform(); |
paulb@600 | 247 | var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@600 | 248 | oDoc.loadXML(outDoc.output+""); |
paulb@600 | 249 | return oDoc; |
paulb@600 | 250 | }; |
paulb@44 | 251 | }; |
paulb@600 | 252 | |
paulb@44 | 253 | /** |
paulb@600 | 254 | * Transform the given XML DOM and return the transformation result as a new DOM fragment. |
paulb@600 | 255 | * <b>Note</b>: The xsl:output method must match the nature of the owner document (XML/HTML). |
paulb@44 | 256 | * @argument sourceDoc The XML DOMDocument to transform |
paulb@600 | 257 | * @argument ownerDoc The owner of the result fragment |
paulb@600 | 258 | * @return The transformation result as a DOM Document |
paulb@44 | 259 | */ |
paulb@600 | 260 | XSLTProcessor.prototype.transformToFragment = function (sourceDoc, ownerDoc) { |
paulb@600 | 261 | this.processor.input = sourceDoc; |
paulb@600 | 262 | this.processor.transform(); |
paulb@600 | 263 | var s = this.processor.output; |
paulb@600 | 264 | var f = ownerDoc.createDocumentFragment(); |
paulb@600 | 265 | if (this.outputMethod == 'text') { |
paulb@600 | 266 | f.appendChild(ownerDoc.createTextNode(s)); |
paulb@600 | 267 | } else if (ownerDoc.body && ownerDoc.body.innerHTML) { |
paulb@600 | 268 | var container = ownerDoc.createElement('div'); |
paulb@600 | 269 | container.innerHTML = s; |
paulb@600 | 270 | while (container.hasChildNodes()) { |
paulb@600 | 271 | f.appendChild(container.firstChild); |
paulb@600 | 272 | } |
paulb@600 | 273 | } |
paulb@600 | 274 | else { |
paulb@600 | 275 | var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@600 | 276 | if (s.substring(0, 5) == '<?xml') { |
paulb@600 | 277 | s = s.substring(s.indexOf('?>') + 2); |
paulb@600 | 278 | } |
paulb@600 | 279 | var xml = ''.concat('<my>', s, '</my>'); |
paulb@600 | 280 | oDoc.loadXML(xml); |
paulb@600 | 281 | var container = oDoc.documentElement; |
paulb@600 | 282 | while (container.hasChildNodes()) { |
paulb@600 | 283 | f.appendChild(container.firstChild); |
paulb@600 | 284 | } |
paulb@600 | 285 | } |
paulb@600 | 286 | return f; |
paulb@44 | 287 | }; |
paulb@600 | 288 | |
paulb@44 | 289 | /** |
paulb@44 | 290 | * Set global XSLT parameter of the imported stylesheet |
paulb@44 | 291 | * @argument nsURI The parameter namespace URI |
paulb@44 | 292 | * @argument name The parameter base name |
paulb@44 | 293 | * @argument value The new parameter value |
paulb@44 | 294 | */ |
paulb@619 | 295 | XSLTProcessor.prototype.setParameter = function(nsURI, name, value){ |
paulb@619 | 296 | // make value a zero length string if null to allow clearing |
paulb@619 | 297 | value = value ? value : ""; |
paulb@619 | 298 | // nsURI is optional but cannot be null |
paulb@619 | 299 | if(nsURI){ |
paulb@619 | 300 | this.processor.addParameter(name, value, nsURI); |
paulb@619 | 301 | }else{ |
paulb@619 | 302 | this.processor.addParameter(name, value); |
paulb@619 | 303 | }; |
paulb@619 | 304 | // update updated params for getParameter |
paulb@619 | 305 | nsURI = "" + (nsURI || ""); |
paulb@619 | 306 | if(!this.paramsSet[nsURI]){ |
paulb@619 | 307 | this.paramsSet[nsURI] = new Array(); |
paulb@619 | 308 | }; |
paulb@619 | 309 | this.paramsSet[nsURI][name] = value; |
paulb@619 | 310 | }; |
paulb@44 | 311 | /** |
paulb@44 | 312 | * Gets a parameter if previously set by setParameter. Returns null |
paulb@44 | 313 | * otherwise |
paulb@44 | 314 | * @argument name The parameter base name |
paulb@44 | 315 | * @argument value The new parameter value |
paulb@44 | 316 | * @return The parameter value if reviously set by setParameter, null otherwise |
paulb@44 | 317 | */ |
paulb@44 | 318 | XSLTProcessor.prototype.getParameter = function(nsURI, name){ |
paulb@619 | 319 | nsURI = "" + (nsURI || ""); |
paulb@600 | 320 | if(this.paramsSet[nsURI] && this.paramsSet[nsURI][name]){ |
paulb@600 | 321 | return this.paramsSet[nsURI][name]; |
paulb@600 | 322 | }else{ |
paulb@44 | 323 | return null; |
paulb@600 | 324 | }; |
paulb@44 | 325 | }; |
paulb@600 | 326 | /** |
paulb@600 | 327 | * Clear parameters (set them to default values as defined in the stylesheet itself) |
paulb@600 | 328 | */ |
paulb@600 | 329 | XSLTProcessor.prototype.clearParameters = function(){ |
paulb@600 | 330 | for(var nsURI in this.paramsSet){ |
paulb@600 | 331 | for(var name in this.paramsSet[nsURI]){ |
paulb@619 | 332 | if(nsURI!=""){ |
paulb@600 | 333 | this.processor.addParameter(name, "", nsURI); |
paulb@600 | 334 | }else{ |
paulb@600 | 335 | this.processor.addParameter(name, ""); |
paulb@44 | 336 | }; |
paulb@44 | 337 | }; |
paulb@600 | 338 | }; |
paulb@600 | 339 | this.paramsSet = new Array(); |
paulb@600 | 340 | }; |
paulb@600 | 341 | }else{ /* end IE initialization, try to deal with real browsers now ;-) */ |
paulb@600 | 342 | if(_SARISSA_HAS_DOM_CREATE_DOCUMENT){ |
paulb@44 | 343 | /** |
paulb@44 | 344 | * <p>Ensures the document was loaded correctly, otherwise sets the |
paulb@44 | 345 | * parseError to -1 to indicate something went wrong. Internal use</p> |
paulb@44 | 346 | * @private |
paulb@44 | 347 | */ |
paulb@44 | 348 | Sarissa.__handleLoad__ = function(oDoc){ |
paulb@44 | 349 | Sarissa.__setReadyState__(oDoc, 4); |
paulb@44 | 350 | }; |
paulb@44 | 351 | /** |
paulb@44 | 352 | * <p>Attached by an event handler to the load event. Internal use.</p> |
paulb@44 | 353 | * @private |
paulb@44 | 354 | */ |
paulb@73 | 355 | _sarissa_XMLDocument_onload = function(){ |
paulb@44 | 356 | Sarissa.__handleLoad__(this); |
paulb@44 | 357 | }; |
paulb@44 | 358 | /** |
paulb@44 | 359 | * <p>Sets the readyState property of the given DOM Document object. |
paulb@44 | 360 | * Internal use.</p> |
paulb@44 | 361 | * @private |
paulb@44 | 362 | * @argument oDoc the DOM Document object to fire the |
paulb@44 | 363 | * readystatechange event |
paulb@44 | 364 | * @argument iReadyState the number to change the readystate property to |
paulb@44 | 365 | */ |
paulb@44 | 366 | Sarissa.__setReadyState__ = function(oDoc, iReadyState){ |
paulb@44 | 367 | oDoc.readyState = iReadyState; |
paulb@600 | 368 | oDoc.readystate = iReadyState; |
paulb@619 | 369 | if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function") { |
paulb@44 | 370 | oDoc.onreadystatechange(); |
paulb@619 | 371 | } |
paulb@44 | 372 | }; |
paulb@44 | 373 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@600 | 374 | var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); |
paulb@600 | 375 | if(!oDoc.onreadystatechange){ |
paulb@600 | 376 | |
paulb@600 | 377 | /** |
paulb@600 | 378 | * <p>Emulate IE's onreadystatechange attribute</p> |
paulb@600 | 379 | */ |
paulb@600 | 380 | oDoc.onreadystatechange = null; |
paulb@600 | 381 | }; |
paulb@600 | 382 | if(!oDoc.readyState){ |
paulb@600 | 383 | /** |
paulb@600 | 384 | * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p> |
paulb@600 | 385 | * <ul><li>1 == LOADING,</li> |
paulb@600 | 386 | * <li>2 == LOADED,</li> |
paulb@600 | 387 | * <li>3 == INTERACTIVE,</li> |
paulb@600 | 388 | * <li>4 == COMPLETED</li></ul> |
paulb@600 | 389 | */ |
paulb@600 | 390 | oDoc.readyState = 0; |
paulb@600 | 391 | }; |
paulb@44 | 392 | oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false); |
paulb@44 | 393 | return oDoc; |
paulb@600 | 394 | }; |
paulb@600 | 395 | if(window.XMLDocument){ |
paulb@600 | 396 | // do nothing |
paulb@600 | 397 | }// TODO: check if the new document has content before trying to copynodes, check for error handling in DOM 3 LS |
paulb@600 | 398 | else if(_SARISSA_HAS_DOM_FEATURE && window.Document && !Document.prototype.load && document.implementation.hasFeature('LS', '3.0')){ |
paulb@600 | 399 | //Opera 9 may get the XPath branch which gives creates XMLDocument, therefore it doesn't reach here which is good |
paulb@600 | 400 | /** |
paulb@600 | 401 | * <p>Factory method to obtain a new DOM Document object</p> |
paulb@600 | 402 | * @argument sUri the namespace of the root node (if any) |
paulb@600 | 403 | * @argument sUri the local name of the root node (if any) |
paulb@600 | 404 | * @returns a new DOM Document |
paulb@600 | 405 | */ |
paulb@600 | 406 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@600 | 407 | var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); |
paulb@600 | 408 | return oDoc; |
paulb@600 | 409 | }; |
paulb@600 | 410 | } |
paulb@600 | 411 | else { |
paulb@600 | 412 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@600 | 413 | var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); |
paulb@600 | 414 | // looks like safari does not create the root element for some unknown reason |
paulb@600 | 415 | if(oDoc && (sUri || sName) && !oDoc.documentElement){ |
paulb@600 | 416 | oDoc.appendChild(oDoc.createElementNS(sUri, sName)); |
paulb@600 | 417 | }; |
paulb@600 | 418 | return oDoc; |
paulb@600 | 419 | }; |
paulb@600 | 420 | }; |
paulb@44 | 421 | };//if(_SARISSA_HAS_DOM_CREATE_DOCUMENT) |
paulb@44 | 422 | }; |
paulb@44 | 423 | //========================================== |
paulb@44 | 424 | // Common stuff |
paulb@44 | 425 | //========================================== |
paulb@73 | 426 | if(!window.DOMParser){ |
paulb@600 | 427 | if(_SARISSA_IS_SAFARI){ |
paulb@600 | 428 | /* |
paulb@600 | 429 | * DOMParser is a utility class, used to construct DOMDocuments from XML strings |
paulb@600 | 430 | * @constructor |
paulb@600 | 431 | */ |
paulb@600 | 432 | DOMParser = function() { }; |
paulb@600 | 433 | /** |
paulb@600 | 434 | * Construct a new DOM Document from the given XMLstring |
paulb@600 | 435 | * @param sXml the given XML string |
paulb@600 | 436 | * @param contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml). |
paulb@600 | 437 | * @return a new DOM Document from the given XML string |
paulb@600 | 438 | */ |
paulb@600 | 439 | DOMParser.prototype.parseFromString = function(sXml, contentType){ |
paulb@600 | 440 | var xmlhttp = new XMLHttpRequest(); |
paulb@600 | 441 | xmlhttp.open("GET", "data:text/xml;charset=utf-8," + encodeURIComponent(sXml), false); |
paulb@600 | 442 | xmlhttp.send(null); |
paulb@600 | 443 | return xmlhttp.responseXML; |
paulb@600 | 444 | }; |
paulb@619 | 445 | }else if(Sarissa.getDomDocument && Sarissa.getDomDocument() && Sarissa.getDomDocument(null, "bar").xml){ |
paulb@600 | 446 | DOMParser = function() { }; |
paulb@600 | 447 | DOMParser.prototype.parseFromString = function(sXml, contentType){ |
paulb@600 | 448 | var doc = Sarissa.getDomDocument(); |
paulb@600 | 449 | doc.loadXML(sXml); |
paulb@600 | 450 | return doc; |
paulb@600 | 451 | }; |
paulb@73 | 452 | }; |
paulb@73 | 453 | }; |
paulb@73 | 454 | |
paulb@600 | 455 | if((typeof(document.importNode) == "undefined") && _SARISSA_IS_IE){ |
paulb@73 | 456 | try{ |
paulb@73 | 457 | /** |
paulb@600 | 458 | * Implementation of importNode for the context window document in IE. |
paulb@600 | 459 | * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored. |
paulb@73 | 460 | * @param oNode the Node to import |
paulb@73 | 461 | * @param bChildren whether to include the children of oNode |
paulb@73 | 462 | * @returns the imported node for further use |
paulb@73 | 463 | */ |
paulb@600 | 464 | document.importNode = function(oNode, bChildren){ |
paulb@600 | 465 | var tmp; |
paulb@600 | 466 | if (oNode.nodeName=='#text') { |
paulb@600 | 467 | return document.createTextElement(oNode.data); |
paulb@600 | 468 | } |
paulb@600 | 469 | else { |
paulb@600 | 470 | if(oNode.nodeName == "tbody" || oNode.nodeName == "tr"){ |
paulb@600 | 471 | tmp = document.createElement("table"); |
paulb@600 | 472 | } |
paulb@600 | 473 | else if(oNode.nodeName == "td"){ |
paulb@600 | 474 | tmp = document.createElement("tr"); |
paulb@600 | 475 | } |
paulb@600 | 476 | else if(oNode.nodeName == "option"){ |
paulb@600 | 477 | tmp = document.createElement("select"); |
paulb@600 | 478 | } |
paulb@600 | 479 | else{ |
paulb@600 | 480 | tmp = document.createElement("div"); |
paulb@600 | 481 | }; |
paulb@600 | 482 | if(bChildren){ |
paulb@600 | 483 | tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML; |
paulb@600 | 484 | }else{ |
paulb@600 | 485 | tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML; |
paulb@600 | 486 | }; |
paulb@600 | 487 | return tmp.getElementsByTagName("*")[0]; |
paulb@600 | 488 | }; |
paulb@600 | 489 | |
paulb@73 | 490 | }; |
paulb@600 | 491 | }catch(e){ }; |
paulb@44 | 492 | }; |
paulb@44 | 493 | if(!Sarissa.getParseErrorText){ |
paulb@44 | 494 | /** |
paulb@44 | 495 | * <p>Returns a human readable description of the parsing error. Usefull |
paulb@44 | 496 | * for debugging. Tip: append the returned error string in a <pre> |
paulb@44 | 497 | * element if you want to render it.</p> |
paulb@44 | 498 | * <p>Many thanks to Christian Stocker for the initial patch.</p> |
paulb@44 | 499 | * @argument oDoc The target DOM document |
paulb@44 | 500 | * @returns The parsing error description of the target Document in |
paulb@44 | 501 | * human readable form (preformated text) |
paulb@44 | 502 | */ |
paulb@44 | 503 | Sarissa.getParseErrorText = function (oDoc){ |
paulb@44 | 504 | var parseErrorText = Sarissa.PARSED_OK; |
paulb@600 | 505 | if(!oDoc.documentElement){ |
paulb@600 | 506 | parseErrorText = Sarissa.PARSED_EMPTY; |
paulb@600 | 507 | } else if(oDoc.documentElement.tagName == "parsererror"){ |
paulb@600 | 508 | parseErrorText = oDoc.documentElement.firstChild.data; |
paulb@600 | 509 | parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data; |
paulb@600 | 510 | } else if(oDoc.getElementsByTagName("parsererror").length > 0){ |
paulb@600 | 511 | var parsererror = oDoc.getElementsByTagName("parsererror")[0]; |
paulb@600 | 512 | parseErrorText = Sarissa.getText(parsererror, true)+"\n"; |
paulb@600 | 513 | } else if(oDoc.parseError && oDoc.parseError.errorCode != 0){ |
paulb@600 | 514 | parseErrorText = Sarissa.PARSED_UNKNOWN_ERROR; |
paulb@44 | 515 | }; |
paulb@44 | 516 | return parseErrorText; |
paulb@44 | 517 | }; |
paulb@44 | 518 | }; |
paulb@44 | 519 | Sarissa.getText = function(oNode, deep){ |
paulb@44 | 520 | var s = ""; |
paulb@44 | 521 | var nodes = oNode.childNodes; |
paulb@44 | 522 | for(var i=0; i < nodes.length; i++){ |
paulb@44 | 523 | var node = nodes[i]; |
paulb@73 | 524 | var nodeType = node.nodeType; |
paulb@73 | 525 | if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){ |
paulb@44 | 526 | s += node.data; |
paulb@600 | 527 | } else if(deep == true |
paulb@73 | 528 | && (nodeType == Node.ELEMENT_NODE |
paulb@73 | 529 | || nodeType == Node.DOCUMENT_NODE |
paulb@73 | 530 | || nodeType == Node.DOCUMENT_FRAGMENT_NODE)){ |
paulb@44 | 531 | s += Sarissa.getText(node, true); |
paulb@44 | 532 | }; |
paulb@44 | 533 | }; |
paulb@44 | 534 | return s; |
paulb@44 | 535 | }; |
paulb@600 | 536 | if(!window.XMLSerializer |
paulb@600 | 537 | && Sarissa.getDomDocument |
paulb@600 | 538 | && Sarissa.getDomDocument("","foo", null).xml){ |
paulb@44 | 539 | /** |
paulb@600 | 540 | * Utility class to serialize DOM Node objects to XML strings |
paulb@600 | 541 | * @constructor |
paulb@44 | 542 | */ |
paulb@600 | 543 | XMLSerializer = function(){}; |
paulb@600 | 544 | /** |
paulb@600 | 545 | * Serialize the given DOM Node to an XML string |
paulb@600 | 546 | * @param oNode the DOM Node to serialize |
paulb@600 | 547 | */ |
paulb@600 | 548 | XMLSerializer.prototype.serializeToString = function(oNode) { |
paulb@600 | 549 | return oNode.xml; |
paulb@44 | 550 | }; |
paulb@44 | 551 | }; |
paulb@73 | 552 | |
paulb@44 | 553 | /** |
paulb@44 | 554 | * strips tags from a markup string |
paulb@44 | 555 | */ |
paulb@44 | 556 | Sarissa.stripTags = function (s) { |
paulb@44 | 557 | return s.replace(/<[^>]+>/g,""); |
paulb@44 | 558 | }; |
paulb@44 | 559 | /** |
paulb@44 | 560 | * <p>Deletes all child nodes of the given node</p> |
paulb@44 | 561 | * @argument oNode the Node to empty |
paulb@44 | 562 | */ |
paulb@44 | 563 | Sarissa.clearChildNodes = function(oNode) { |
paulb@600 | 564 | // need to check for firstChild due to opera 8 bug with hasChildNodes |
paulb@600 | 565 | while(oNode.firstChild) { |
paulb@44 | 566 | oNode.removeChild(oNode.firstChild); |
paulb@44 | 567 | }; |
paulb@44 | 568 | }; |
paulb@44 | 569 | /** |
paulb@73 | 570 | * <p> Copies the childNodes of nodeFrom to nodeTo</p> |
paulb@73 | 571 | * <p> <b>Note:</b> The second object's original content is deleted before |
paulb@73 | 572 | * the copy operation, unless you supply a true third parameter</p> |
paulb@44 | 573 | * @argument nodeFrom the Node to copy the childNodes from |
paulb@44 | 574 | * @argument nodeTo the Node to copy the childNodes to |
paulb@73 | 575 | * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false |
paulb@44 | 576 | */ |
paulb@73 | 577 | Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { |
paulb@619 | 578 | if(_SARISSA_IS_SAFARI && nodeTo.nodeType == Node.DOCUMENT_NODE){ // SAFARI_OLD ?? |
paulb@619 | 579 | nodeTo = nodeTo.documentElement; //Appearantly there's a bug in safari where you can't appendChild to a document node |
paulb@619 | 580 | } |
paulb@619 | 581 | |
paulb@600 | 582 | if((!nodeFrom) || (!nodeTo)){ |
paulb@600 | 583 | throw "Both source and destination nodes must be provided"; |
paulb@600 | 584 | }; |
paulb@73 | 585 | if(!bPreserveExisting){ |
paulb@73 | 586 | Sarissa.clearChildNodes(nodeTo); |
paulb@73 | 587 | }; |
paulb@44 | 588 | var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; |
paulb@44 | 589 | var nodes = nodeFrom.childNodes; |
paulb@600 | 590 | if(typeof(ownerDoc.importNode) != "undefined") { |
paulb@44 | 591 | for(var i=0;i < nodes.length;i++) { |
paulb@44 | 592 | nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); |
paulb@44 | 593 | }; |
paulb@600 | 594 | } else { |
paulb@44 | 595 | for(var i=0;i < nodes.length;i++) { |
paulb@44 | 596 | nodeTo.appendChild(nodes[i].cloneNode(true)); |
paulb@44 | 597 | }; |
paulb@44 | 598 | }; |
paulb@44 | 599 | }; |
paulb@44 | 600 | |
paulb@73 | 601 | /** |
paulb@73 | 602 | * <p> Moves the childNodes of nodeFrom to nodeTo</p> |
paulb@73 | 603 | * <p> <b>Note:</b> The second object's original content is deleted before |
paulb@73 | 604 | * the move operation, unless you supply a true third parameter</p> |
paulb@73 | 605 | * @argument nodeFrom the Node to copy the childNodes from |
paulb@73 | 606 | * @argument nodeTo the Node to copy the childNodes to |
paulb@600 | 607 | * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is |
paulb@600 | 608 | */ |
paulb@73 | 609 | Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { |
paulb@600 | 610 | if((!nodeFrom) || (!nodeTo)){ |
paulb@600 | 611 | throw "Both source and destination nodes must be provided"; |
paulb@600 | 612 | }; |
paulb@73 | 613 | if(!bPreserveExisting){ |
paulb@73 | 614 | Sarissa.clearChildNodes(nodeTo); |
paulb@73 | 615 | }; |
paulb@73 | 616 | var nodes = nodeFrom.childNodes; |
paulb@73 | 617 | // if within the same doc, just move, else copy and delete |
paulb@73 | 618 | if(nodeFrom.ownerDocument == nodeTo.ownerDocument){ |
paulb@600 | 619 | while(nodeFrom.firstChild){ |
paulb@600 | 620 | nodeTo.appendChild(nodeFrom.firstChild); |
paulb@600 | 621 | }; |
paulb@600 | 622 | } else { |
paulb@73 | 623 | var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; |
paulb@600 | 624 | if(typeof(ownerDoc.importNode) != "undefined") { |
paulb@600 | 625 | for(var i=0;i < nodes.length;i++) { |
paulb@600 | 626 | nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); |
paulb@600 | 627 | }; |
paulb@600 | 628 | }else{ |
paulb@600 | 629 | for(var i=0;i < nodes.length;i++) { |
paulb@600 | 630 | nodeTo.appendChild(nodes[i].cloneNode(true)); |
paulb@600 | 631 | }; |
paulb@73 | 632 | }; |
paulb@73 | 633 | Sarissa.clearChildNodes(nodeFrom); |
paulb@73 | 634 | }; |
paulb@73 | 635 | }; |
paulb@73 | 636 | |
paulb@44 | 637 | /** |
paulb@619 | 638 | * <p>Serialize any <strong>non</strong> DOM object to an XML string. All properties are serialized using the property name |
paulb@44 | 639 | * as the XML element name. Array elements are rendered as <code>array-item</code> elements, |
paulb@44 | 640 | * using their index/key as the value of the <code>key</code> attribute.</p> |
paulb@44 | 641 | * @argument anyObject the object to serialize |
paulb@44 | 642 | * @argument objectName a name for that object |
paulb@619 | 643 | * @return the XML serialization of the given object as a string |
paulb@44 | 644 | */ |
paulb@44 | 645 | Sarissa.xmlize = function(anyObject, objectName, indentSpace){ |
paulb@44 | 646 | indentSpace = indentSpace?indentSpace:''; |
paulb@44 | 647 | var s = indentSpace + '<' + objectName + '>'; |
paulb@44 | 648 | var isLeaf = false; |
paulb@44 | 649 | if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String |
paulb@44 | 650 | || anyObject instanceof Boolean || anyObject instanceof Date){ |
paulb@44 | 651 | s += Sarissa.escape(""+anyObject); |
paulb@44 | 652 | isLeaf = true; |
paulb@44 | 653 | }else{ |
paulb@44 | 654 | s += "\n"; |
paulb@44 | 655 | var isArrayItem = anyObject instanceof Array; |
paulb@44 | 656 | for(var name in anyObject){ |
paulb@44 | 657 | s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=\""+name+"\"":name), indentSpace + " "); |
paulb@44 | 658 | }; |
paulb@44 | 659 | s += indentSpace; |
paulb@44 | 660 | }; |
paulb@619 | 661 | return (s += (objectName.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName + ">\n")); |
paulb@44 | 662 | }; |
paulb@44 | 663 | |
paulb@44 | 664 | /** |
paulb@44 | 665 | * Escape the given string chacters that correspond to the five predefined XML entities |
paulb@44 | 666 | * @param sXml the string to escape |
paulb@44 | 667 | */ |
paulb@44 | 668 | Sarissa.escape = function(sXml){ |
paulb@44 | 669 | return sXml.replace(/&/g, "&") |
paulb@44 | 670 | .replace(/</g, "<") |
paulb@44 | 671 | .replace(/>/g, ">") |
paulb@44 | 672 | .replace(/"/g, """) |
paulb@44 | 673 | .replace(/'/g, "'"); |
paulb@44 | 674 | }; |
paulb@44 | 675 | |
paulb@44 | 676 | /** |
paulb@44 | 677 | * Unescape the given string. This turns the occurences of the predefined XML |
paulb@44 | 678 | * entities to become the characters they represent correspond to the five predefined XML entities |
paulb@44 | 679 | * @param sXml the string to unescape |
paulb@44 | 680 | */ |
paulb@44 | 681 | Sarissa.unescape = function(sXml){ |
paulb@44 | 682 | return sXml.replace(/'/g,"'") |
paulb@44 | 683 | .replace(/"/g,"\"") |
paulb@44 | 684 | .replace(/>/g,">") |
paulb@44 | 685 | .replace(/</g,"<") |
paulb@44 | 686 | .replace(/&/g,"&"); |
paulb@44 | 687 | }; |
paulb@44 | 688 | // EOF |