paulb@389 | 1 | /** |
paulb@389 | 2 | * ==================================================================== |
paulb@389 | 3 | * About |
paulb@389 | 4 | * ==================================================================== |
paulb@389 | 5 | * Sarissa cross browser XML library |
paulb@389 | 6 | * @version 0.9.6 |
paulb@389 | 7 | * @author: Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net |
paulb@389 | 8 | * |
paulb@389 | 9 | * Sarissa is an ECMAScript library acting as a cross-browser wrapper for native XML APIs. |
paulb@389 | 10 | * The library supports Gecko based browsers like Mozilla and Firefox, |
paulb@389 | 11 | * Internet Explorer (5.5+ with MSXML3.0+) and, last but not least, KHTML based browsers like |
paulb@389 | 12 | * Konqueror and Safari. |
paulb@389 | 13 | * |
paulb@389 | 14 | * ==================================================================== |
paulb@389 | 15 | * Licence |
paulb@389 | 16 | * ==================================================================== |
paulb@389 | 17 | * This program is free software; you can redistribute it and/or modify |
paulb@389 | 18 | * it under the terms of the GNU General Public License version 2 or |
paulb@389 | 19 | * the GNU Lesser General Public License version 2.1 as published by |
paulb@389 | 20 | * the Free Software Foundation (your choice of the two). |
paulb@389 | 21 | * |
paulb@389 | 22 | * This program is distributed in the hope that it will be useful, |
paulb@389 | 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paulb@389 | 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paulb@389 | 25 | * GNU General Public License or GNU Lesser General Public License for more details. |
paulb@389 | 26 | * |
paulb@389 | 27 | * You should have received a copy of the GNU General Public License |
paulb@389 | 28 | * or GNU Lesser General Public License along with this program; if not, |
paulb@389 | 29 | * write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
paulb@389 | 30 | * or visit http://www.gnu.org |
paulb@389 | 31 | * |
paulb@389 | 32 | */ |
paulb@389 | 33 | /** |
paulb@389 | 34 | * <p>Sarissa is a utility class. Provides static methods for DOMDocument and |
paulb@389 | 35 | * XMLHTTP objects, DOM Node serializatrion to XML strings and other goodies.</p> |
paulb@389 | 36 | * @constructor |
paulb@389 | 37 | */ |
paulb@389 | 38 | function Sarissa(){}; |
paulb@389 | 39 | /** @private */ |
paulb@389 | 40 | Sarissa.PARSED_OK = "Document contains no parsing errors"; |
paulb@389 | 41 | /** |
paulb@389 | 42 | * Tells you whether transformNode and transformNodeToObject are available. This functionality |
paulb@389 | 43 | * is contained in sarissa_ieemu_xslt.js and is deprecated. If you want to control XSLT transformations |
paulb@389 | 44 | * use the XSLTProcessor |
paulb@389 | 45 | * @deprecated |
paulb@389 | 46 | * @type boolean |
paulb@389 | 47 | */ |
paulb@389 | 48 | Sarissa.IS_ENABLED_TRANSFORM_NODE = false; |
paulb@389 | 49 | /** |
paulb@389 | 50 | * tells you whether XMLHttpRequest (or equivalent) is available |
paulb@389 | 51 | * @type boolean |
paulb@389 | 52 | */ |
paulb@389 | 53 | Sarissa.IS_ENABLED_XMLHTTP = false; |
paulb@389 | 54 | /** |
paulb@389 | 55 | * tells you whether selectNodes/selectSingleNode is available |
paulb@389 | 56 | * @type boolean |
paulb@389 | 57 | */ |
paulb@389 | 58 | Sarissa.IS_ENABLED_SELECT_NODES = false; |
paulb@389 | 59 | var _sarissa_iNsCounter = 0; |
paulb@389 | 60 | var _SARISSA_IEPREFIX4XSLPARAM = ""; |
paulb@389 | 61 | var _SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true; |
paulb@389 | 62 | var _SARISSA_HAS_DOM_CREATE_DOCUMENT = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument; |
paulb@389 | 63 | var _SARISSA_HAS_DOM_FEATURE = _SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature; |
paulb@389 | 64 | var _SARISSA_IS_MOZ = _SARISSA_HAS_DOM_CREATE_DOCUMENT && _SARISSA_HAS_DOM_FEATURE; |
paulb@389 | 65 | var _SARISSA_IS_SAFARI = navigator.userAgent.toLowerCase().indexOf("applewebkit") != -1; |
paulb@389 | 66 | var _SARISSA_IS_IE = document.all && window.ActiveXObject && navigator.userAgent.toLowerCase().indexOf("msie") > -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1; |
paulb@389 | 67 | |
paulb@389 | 68 | if(!window.Node || !window.Node.ELEMENT_NODE){ |
paulb@389 | 69 | var 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@389 | 70 | }; |
paulb@389 | 71 | |
paulb@389 | 72 | // IE initialization |
paulb@389 | 73 | if(_SARISSA_IS_IE){ |
paulb@389 | 74 | // for XSLT parameter names, prefix needed by IE |
paulb@389 | 75 | _SARISSA_IEPREFIX4XSLPARAM = "xsl:"; |
paulb@389 | 76 | // used to store the most recent ProgID available out of the above |
paulb@389 | 77 | var _SARISSA_DOM_PROGID = ""; |
paulb@389 | 78 | var _SARISSA_XMLHTTP_PROGID = ""; |
paulb@389 | 79 | /** |
paulb@389 | 80 | * Called when the Sarissa_xx.js file is parsed, to pick most recent |
paulb@389 | 81 | * ProgIDs for IE, then gets destroyed. |
paulb@389 | 82 | * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object |
paulb@389 | 83 | * @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@389 | 84 | */ |
paulb@389 | 85 | pickRecentProgID = function (idList, enabledList){ |
paulb@389 | 86 | // found progID flag |
paulb@389 | 87 | var bFound = false; |
paulb@389 | 88 | for(var i=0; i < idList.length && !bFound; i++){ |
paulb@389 | 89 | try{ |
paulb@389 | 90 | var oDoc = new ActiveXObject(idList[i]); |
paulb@389 | 91 | o2Store = idList[i]; |
paulb@389 | 92 | bFound = true; |
paulb@389 | 93 | for(var j=0;j<enabledList.length;j++) |
paulb@389 | 94 | if(i <= enabledList[j][1]) |
paulb@389 | 95 | Sarissa["IS_ENABLED_"+enabledList[j][0]] = true; |
paulb@389 | 96 | }catch (objException){ |
paulb@389 | 97 | // trap; try next progID |
paulb@389 | 98 | }; |
paulb@389 | 99 | }; |
paulb@389 | 100 | if (!bFound) |
paulb@389 | 101 | throw "Could not retreive a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")"; |
paulb@389 | 102 | idList = null; |
paulb@389 | 103 | return o2Store; |
paulb@389 | 104 | }; |
paulb@389 | 105 | // pick best available MSXML progIDs |
paulb@389 | 106 | _SARISSA_DOM_PROGID = pickRecentProgID(["Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"], [["SELECT_NODES", 2],["TRANSFORM_NODE", 2]]); |
paulb@389 | 107 | _SARISSA_XMLHTTP_PROGID = pickRecentProgID(["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"], [["XMLHTTP", 4]]); |
paulb@389 | 108 | _SARISSA_THREADEDDOM_PROGID = pickRecentProgID(["Msxml2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]); |
paulb@389 | 109 | _SARISSA_XSLTEMPLATE_PROGID = pickRecentProgID(["Msxml2.XSLTemplate.5.0", "Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"], [["XSLTPROC", 2]]); |
paulb@389 | 110 | // we dont need this anymore |
paulb@389 | 111 | pickRecentProgID = null; |
paulb@389 | 112 | //============================================ |
paulb@389 | 113 | // Factory methods (IE) |
paulb@389 | 114 | //============================================ |
paulb@389 | 115 | // see non-IE version |
paulb@389 | 116 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@389 | 117 | var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@389 | 118 | // if a root tag name was provided, we need to load it in the DOM |
paulb@389 | 119 | // object |
paulb@389 | 120 | if (sName){ |
paulb@389 | 121 | // if needed, create an artifical namespace prefix the way Moz |
paulb@389 | 122 | // does |
paulb@389 | 123 | if (sUri){ |
paulb@389 | 124 | oDoc.loadXML("<a" + _sarissa_iNsCounter + ":" + sName + " xmlns:a" + _sarissa_iNsCounter + "=\"" + sUri + "\" />"); |
paulb@389 | 125 | // don't use the same prefix again |
paulb@389 | 126 | ++_sarissa_iNsCounter; |
paulb@389 | 127 | } |
paulb@389 | 128 | else |
paulb@389 | 129 | oDoc.loadXML("<" + sName + "/>"); |
paulb@389 | 130 | }; |
paulb@389 | 131 | return oDoc; |
paulb@389 | 132 | }; |
paulb@389 | 133 | // see non-IE version |
paulb@389 | 134 | Sarissa.getParseErrorText = function (oDoc) { |
paulb@389 | 135 | var parseErrorText = Sarissa.PARSED_OK; |
paulb@389 | 136 | if(oDoc.parseError != 0){ |
paulb@389 | 137 | parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + |
paulb@389 | 138 | "\nLocation: " + oDoc.parseError.url + |
paulb@389 | 139 | "\nLine Number " + oDoc.parseError.line + ", Column " + |
paulb@389 | 140 | oDoc.parseError.linepos + |
paulb@389 | 141 | ":\n" + oDoc.parseError.srcText + |
paulb@389 | 142 | "\n"; |
paulb@389 | 143 | for(var i = 0; i < oDoc.parseError.linepos;i++){ |
paulb@389 | 144 | parseErrorText += "-"; |
paulb@389 | 145 | }; |
paulb@389 | 146 | parseErrorText += "^\n"; |
paulb@389 | 147 | }; |
paulb@389 | 148 | return parseErrorText; |
paulb@389 | 149 | }; |
paulb@389 | 150 | // see non-IE version |
paulb@389 | 151 | Sarissa.setXpathNamespaces = function(oDoc, sNsSet) { |
paulb@389 | 152 | oDoc.setProperty("SelectionLanguage", "XPath"); |
paulb@389 | 153 | oDoc.setProperty("SelectionNamespaces", sNsSet); |
paulb@389 | 154 | }; |
paulb@389 | 155 | /** |
paulb@389 | 156 | * Basic implementation of Mozilla's XSLTProcessor for IE. |
paulb@389 | 157 | * Reuses the same XSLT stylesheet for multiple transforms |
paulb@389 | 158 | * @constructor |
paulb@389 | 159 | */ |
paulb@389 | 160 | XSLTProcessor = function(){ |
paulb@389 | 161 | this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID); |
paulb@389 | 162 | this.processor = null; |
paulb@389 | 163 | }; |
paulb@389 | 164 | /** |
paulb@389 | 165 | * Impoprts the given XSLT DOM and compiles it to a reusable transform |
paulb@389 | 166 | * @argument xslDoc The XSLT DOMDocument to import |
paulb@389 | 167 | */ |
paulb@389 | 168 | XSLTProcessor.prototype.importStylesheet = function(xslDoc){ |
paulb@389 | 169 | // convert stylesheet to free threaded |
paulb@389 | 170 | var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID); |
paulb@389 | 171 | converted.loadXML(xslDoc.xml); |
paulb@389 | 172 | this.template.stylesheet = converted; |
paulb@389 | 173 | this.processor = this.template.createProcessor(); |
paulb@389 | 174 | // (re)set default param values |
paulb@389 | 175 | this.paramsSet = new Array(); |
paulb@389 | 176 | }; |
paulb@389 | 177 | /** |
paulb@389 | 178 | * Transform the given XML DOM |
paulb@389 | 179 | * @argument sourceDoc The XML DOMDocument to transform |
paulb@389 | 180 | * @return The transformation result as a DOM Document |
paulb@389 | 181 | */ |
paulb@389 | 182 | XSLTProcessor.prototype.transformToDocument = function(sourceDoc){ |
paulb@389 | 183 | this.processor.input = sourceDoc; |
paulb@389 | 184 | var outDoc = new ActiveXObject(_SARISSA_DOM_PROGID); |
paulb@389 | 185 | this.processor.output = outDoc; |
paulb@389 | 186 | this.processor.transform(); |
paulb@389 | 187 | return outDoc; |
paulb@389 | 188 | }; |
paulb@389 | 189 | /** |
paulb@389 | 190 | * Not sure if this works in IE. Maybe this will allow non-well-formed |
paulb@389 | 191 | * transformation results (i.e. with no single root element) |
paulb@389 | 192 | * @argument sourceDoc The XML DOMDocument to transform |
paulb@389 | 193 | * @return The transformation result as a DOM Fragment |
paulb@389 | 194 | */ |
paulb@389 | 195 | XSLTProcessor.prototype.transformToFragment = function(sourceDoc, ownerDocument){ |
paulb@389 | 196 | return this.transformToDocument(sourceDoc); |
paulb@389 | 197 | }; |
paulb@389 | 198 | /** |
paulb@389 | 199 | * Set global XSLT parameter of the imported stylesheet |
paulb@389 | 200 | * @argument nsURI The parameter namespace URI |
paulb@389 | 201 | * @argument name The parameter base name |
paulb@389 | 202 | * @argument value The new parameter value |
paulb@389 | 203 | */ |
paulb@389 | 204 | XSLTProcessor.prototype.setParameter = function(nsURI, name, value){ |
paulb@389 | 205 | /* nsURI is optional but cannot be null */ |
paulb@389 | 206 | if(nsURI){ |
paulb@389 | 207 | this.processor.addParameter(name, value, nsURI); |
paulb@389 | 208 | }else{ |
paulb@389 | 209 | this.processor.addParameter(name, value); |
paulb@389 | 210 | }; |
paulb@389 | 211 | /* update updated params for getParameter */ |
paulb@389 | 212 | if(!this.paramsSet[""+nsURI]){ |
paulb@389 | 213 | this.paramsSet[""+nsURI] = new Array(); |
paulb@389 | 214 | }; |
paulb@389 | 215 | this.paramsSet[""+nsURI][name] = value; |
paulb@389 | 216 | }; |
paulb@389 | 217 | /** |
paulb@389 | 218 | * Gets a parameter if previously set by setParameter. Returns null |
paulb@389 | 219 | * otherwise |
paulb@389 | 220 | * @argument name The parameter base name |
paulb@389 | 221 | * @argument value The new parameter value |
paulb@389 | 222 | * @return The parameter value if reviously set by setParameter, null otherwise |
paulb@389 | 223 | */ |
paulb@389 | 224 | XSLTProcessor.prototype.getParameter = function(nsURI, name){ |
paulb@389 | 225 | if(this.paramsSet[""+nsURI] && this.paramsSet[""+nsURI][name]) |
paulb@389 | 226 | return this.paramsSet[""+nsURI][name]; |
paulb@389 | 227 | else |
paulb@389 | 228 | return null; |
paulb@389 | 229 | }; |
paulb@389 | 230 | } |
paulb@389 | 231 | else{ /* end IE initialization, try to deal with real browsers now ;-) */ |
paulb@389 | 232 | if(_SARISSA_HAS_DOM_CREATE_DOCUMENT){ |
paulb@389 | 233 | if(window.XMLDocument){ |
paulb@389 | 234 | /** |
paulb@389 | 235 | * <p>Emulate IE's onreadystatechange attribute</p> |
paulb@389 | 236 | */ |
paulb@389 | 237 | XMLDocument.prototype.onreadystatechange = null; |
paulb@389 | 238 | /** |
paulb@389 | 239 | * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p> |
paulb@389 | 240 | * <ul><li>1 == LOADING,</li> |
paulb@389 | 241 | * <li>2 == LOADED,</li> |
paulb@389 | 242 | * <li>3 == INTERACTIVE,</li> |
paulb@389 | 243 | * <li>4 == COMPLETED</li></ul> |
paulb@389 | 244 | */ |
paulb@389 | 245 | XMLDocument.prototype.readyState = 0; |
paulb@389 | 246 | /** |
paulb@389 | 247 | * <p>Emulate IE's parseError attribute</p> |
paulb@389 | 248 | */ |
paulb@389 | 249 | XMLDocument.prototype.parseError = 0; |
paulb@389 | 250 | |
paulb@389 | 251 | // NOTE: setting async to false will only work with documents |
paulb@389 | 252 | // called over HTTP (meaning a server), not the local file system, |
paulb@389 | 253 | // unless you are using Moz 1.4+. |
paulb@389 | 254 | // BTW the try>catch block is for 1.4; I haven't found a way to check if |
paulb@389 | 255 | // the property is implemented without |
paulb@389 | 256 | // causing an error and I dont want to use user agent stuff for that... |
paulb@389 | 257 | var _SARISSA_SYNC_NON_IMPLEMENTED = false; |
paulb@389 | 258 | try{ |
paulb@389 | 259 | /** |
paulb@389 | 260 | * <p>Emulates IE's async property for Moz versions prior to 1.4. |
paulb@389 | 261 | * It controls whether loading of remote XML files works |
paulb@389 | 262 | * synchronously or asynchronously.</p> |
paulb@389 | 263 | */ |
paulb@389 | 264 | XMLDocument.prototype.async = true; |
paulb@389 | 265 | _SARISSA_SYNC_NON_IMPLEMENTED = true; |
paulb@389 | 266 | }catch(e){/* trap */}; |
paulb@389 | 267 | /** |
paulb@389 | 268 | * <p>Keeps a handle to the original load() method. Internal use and only |
paulb@389 | 269 | * if Mozilla version is lower than 1.4</p> |
paulb@389 | 270 | * @private |
paulb@389 | 271 | */ |
paulb@389 | 272 | XMLDocument.prototype._sarissa_load = XMLDocument.prototype.load; |
paulb@389 | 273 | |
paulb@389 | 274 | /** |
paulb@389 | 275 | * <p>Overrides the original load method to provide synchronous loading for |
paulb@389 | 276 | * Mozilla versions prior to 1.4, using an XMLHttpRequest object (if |
paulb@389 | 277 | * async is set to false)</p> |
paulb@389 | 278 | * @returns the DOM Object as it was before the load() call (may be empty) |
paulb@389 | 279 | */ |
paulb@389 | 280 | XMLDocument.prototype.load = function(sURI) { |
paulb@389 | 281 | var oDoc = document.implementation.createDocument("", "", null); |
paulb@389 | 282 | Sarissa.copyChildNodes(this, oDoc); |
paulb@389 | 283 | this.parseError = 0; |
paulb@389 | 284 | Sarissa.__setReadyState__(this, 1); |
paulb@389 | 285 | try { |
paulb@389 | 286 | if(this.async == false && _SARISSA_SYNC_NON_IMPLEMENTED) { |
paulb@389 | 287 | var tmp = new XMLHttpRequest(); |
paulb@389 | 288 | tmp.open("GET", sURI, false); |
paulb@389 | 289 | tmp.send(null); |
paulb@389 | 290 | Sarissa.__setReadyState__(this, 2); |
paulb@389 | 291 | Sarissa.copyChildNodes(tmp.responseXML, this); |
paulb@389 | 292 | Sarissa.__setReadyState__(this, 3); |
paulb@389 | 293 | } |
paulb@389 | 294 | else { |
paulb@389 | 295 | this._sarissa_load(sURI); |
paulb@389 | 296 | }; |
paulb@389 | 297 | } |
paulb@389 | 298 | catch (objException) { |
paulb@389 | 299 | this.parseError = -1; |
paulb@389 | 300 | } |
paulb@389 | 301 | finally { |
paulb@389 | 302 | if(this.async == false){ |
paulb@389 | 303 | Sarissa.__handleLoad__(this); |
paulb@389 | 304 | }; |
paulb@389 | 305 | }; |
paulb@389 | 306 | return oDoc; |
paulb@389 | 307 | }; |
paulb@389 | 308 | };//if(window.XMLDocument) |
paulb@389 | 309 | |
paulb@389 | 310 | /** |
paulb@389 | 311 | * <p>Ensures the document was loaded correctly, otherwise sets the |
paulb@389 | 312 | * parseError to -1 to indicate something went wrong. Internal use</p> |
paulb@389 | 313 | * @private |
paulb@389 | 314 | */ |
paulb@389 | 315 | Sarissa.__handleLoad__ = function(oDoc){ |
paulb@389 | 316 | if (!oDoc.documentElement || oDoc.documentElement.tagName == "parsererror") |
paulb@389 | 317 | oDoc.parseError = -1; |
paulb@389 | 318 | Sarissa.__setReadyState__(oDoc, 4); |
paulb@389 | 319 | }; |
paulb@389 | 320 | |
paulb@389 | 321 | /** |
paulb@389 | 322 | * <p>Attached by an event handler to the load event. Internal use.</p> |
paulb@389 | 323 | * @private |
paulb@389 | 324 | */ |
paulb@389 | 325 | _sarissa_XMLDocument_onload = function(){ |
paulb@389 | 326 | Sarissa.__handleLoad__(this); |
paulb@389 | 327 | }; |
paulb@389 | 328 | |
paulb@389 | 329 | /** |
paulb@389 | 330 | * <p>Sets the readyState property of the given DOM Document object. |
paulb@389 | 331 | * Internal use.</p> |
paulb@389 | 332 | * @private |
paulb@389 | 333 | * @argument oDoc the DOM Document object to fire the |
paulb@389 | 334 | * readystatechange event |
paulb@389 | 335 | * @argument iReadyState the number to change the readystate property to |
paulb@389 | 336 | */ |
paulb@389 | 337 | Sarissa.__setReadyState__ = function(oDoc, iReadyState){ |
paulb@389 | 338 | oDoc.readyState = iReadyState; |
paulb@389 | 339 | if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function") |
paulb@389 | 340 | oDoc.onreadystatechange(); |
paulb@389 | 341 | }; |
paulb@389 | 342 | /** |
paulb@389 | 343 | * <p>Factory method to obtain a new DOM Document object</p> |
paulb@389 | 344 | * @argument sUri the namespace of the root node (if any) |
paulb@389 | 345 | * @argument sUri the local name of the root node (if any) |
paulb@389 | 346 | * @returns a new DOM Document |
paulb@389 | 347 | */ |
paulb@389 | 348 | Sarissa.getDomDocument = function(sUri, sName){ |
paulb@389 | 349 | var oDoc = document.implementation.createDocument(sUri?sUri:"", sName?sName:"", null); |
paulb@389 | 350 | oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false); |
paulb@389 | 351 | return oDoc; |
paulb@389 | 352 | }; |
paulb@389 | 353 | };//if(_SARISSA_HAS_DOM_CREATE_DOCUMENT) |
paulb@389 | 354 | }; |
paulb@389 | 355 | //========================================== |
paulb@389 | 356 | // Common stuff |
paulb@389 | 357 | //========================================== |
paulb@389 | 358 | if(!window.DOMParser){ |
paulb@389 | 359 | /** |
paulb@389 | 360 | * DOMParser is a utility class, used to construct DOMDocuments from XML strings |
paulb@389 | 361 | * @constructor |
paulb@389 | 362 | */ |
paulb@389 | 363 | DOMParser = function() { |
paulb@389 | 364 | }; |
paulb@389 | 365 | /** |
paulb@389 | 366 | * Construct a new DOM Document from the given XMLstring |
paulb@389 | 367 | * @param sXml the given XML string |
paulb@389 | 368 | * @param contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml). |
paulb@389 | 369 | * @return a new DOM Document from the given XML string |
paulb@389 | 370 | */ |
paulb@389 | 371 | DOMParser.prototype.parseFromString = function(sXml, contentType){ |
paulb@389 | 372 | var doc = Sarissa.getDomDocument(); |
paulb@389 | 373 | doc.loadXML(sXml); |
paulb@389 | 374 | return doc; |
paulb@389 | 375 | }; |
paulb@389 | 376 | |
paulb@389 | 377 | }; |
paulb@389 | 378 | |
paulb@389 | 379 | if(window.XMLHttpRequest){ |
paulb@389 | 380 | Sarissa.IS_ENABLED_XMLHTTP = true; |
paulb@389 | 381 | } |
paulb@389 | 382 | else if(_SARISSA_IS_IE){ |
paulb@389 | 383 | /** |
paulb@389 | 384 | * Emulate XMLHttpRequest |
paulb@389 | 385 | * @constructor |
paulb@389 | 386 | */ |
paulb@389 | 387 | XMLHttpRequest = function() { |
paulb@389 | 388 | return new ActiveXObject(_SARISSA_XMLHTTP_PROGID); |
paulb@389 | 389 | }; |
paulb@389 | 390 | Sarissa.IS_ENABLED_XMLHTTP = true; |
paulb@389 | 391 | }; |
paulb@389 | 392 | |
paulb@389 | 393 | if(!window.document.importNode && _SARISSA_IS_IE){ |
paulb@389 | 394 | try{ |
paulb@389 | 395 | /** |
paulb@389 | 396 | * Implements importNode for the current window document in IE using innerHTML. |
paulb@389 | 397 | * Testing showed that DOM was multiple times slower than innerHTML for this, |
paulb@389 | 398 | * sorry folks. If you encounter trouble (who knows what IE does behind innerHTML) |
paulb@389 | 399 | * please gimme a call. |
paulb@389 | 400 | * @param oNode the Node to import |
paulb@389 | 401 | * @param bChildren whether to include the children of oNode |
paulb@389 | 402 | * @returns the imported node for further use |
paulb@389 | 403 | */ |
paulb@389 | 404 | window.document.importNode = function(oNode, bChildren){ |
paulb@389 | 405 | var importNode = document.createElement("div"); |
paulb@389 | 406 | if(bChildren) |
paulb@389 | 407 | importNode.innerHTML = Sarissa.serialize(oNode); |
paulb@389 | 408 | else |
paulb@389 | 409 | importNode.innerHTML = Sarissa.serialize(oNode.cloneNode(false)); |
paulb@389 | 410 | return importNode.firstChild; |
paulb@389 | 411 | }; |
paulb@389 | 412 | }catch(e){}; |
paulb@389 | 413 | }; |
paulb@389 | 414 | if(!Sarissa.getParseErrorText){ |
paulb@389 | 415 | /** |
paulb@389 | 416 | * <p>Returns a human readable description of the parsing error. Usefull |
paulb@389 | 417 | * for debugging. Tip: append the returned error string in a <pre> |
paulb@389 | 418 | * element if you want to render it.</p> |
paulb@389 | 419 | * <p>Many thanks to Christian Stocker for the initial patch.</p> |
paulb@389 | 420 | * @argument oDoc The target DOM document |
paulb@389 | 421 | * @returns The parsing error description of the target Document in |
paulb@389 | 422 | * human readable form (preformated text) |
paulb@389 | 423 | */ |
paulb@389 | 424 | Sarissa.getParseErrorText = function (oDoc){ |
paulb@389 | 425 | var parseErrorText = Sarissa.PARSED_OK; |
paulb@389 | 426 | if(oDoc.parseError != 0){ |
paulb@389 | 427 | /*moz*/ |
paulb@389 | 428 | if(oDoc.documentElement.tagName == "parsererror"){ |
paulb@389 | 429 | parseErrorText = oDoc.documentElement.firstChild.data; |
paulb@389 | 430 | parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data; |
paulb@389 | 431 | }/*konq*/ |
paulb@389 | 432 | else if(oDoc.documentElement.tagName == "html"){ |
paulb@389 | 433 | parseErrorText = Sarissa.getText(oDoc.documentElement.getElementsByTagName("h1")[0], false) + "\n"; |
paulb@389 | 434 | parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("body")[0], false) + "\n"; |
paulb@389 | 435 | parseErrorText += Sarissa.getText(oDoc.documentElement.getElementsByTagName("pre")[0], false); |
paulb@389 | 436 | }; |
paulb@389 | 437 | }; |
paulb@389 | 438 | return parseErrorText; |
paulb@389 | 439 | }; |
paulb@389 | 440 | }; |
paulb@389 | 441 | Sarissa.getText = function(oNode, deep){ |
paulb@389 | 442 | var s = ""; |
paulb@389 | 443 | var nodes = oNode.childNodes; |
paulb@389 | 444 | for(var i=0; i < nodes.length; i++){ |
paulb@389 | 445 | var node = nodes[i]; |
paulb@389 | 446 | var nodeType = node.nodeType; |
paulb@389 | 447 | if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){ |
paulb@389 | 448 | s += node.data; |
paulb@389 | 449 | } |
paulb@389 | 450 | else if(deep == true |
paulb@389 | 451 | && (nodeType == Node.ELEMENT_NODE |
paulb@389 | 452 | || nodeType == Node.DOCUMENT_NODE |
paulb@389 | 453 | || nodeType == Node.DOCUMENT_FRAGMENT_NODE)){ |
paulb@389 | 454 | s += Sarissa.getText(node, true); |
paulb@389 | 455 | }; |
paulb@389 | 456 | }; |
paulb@389 | 457 | return s; |
paulb@389 | 458 | }; |
paulb@389 | 459 | if(window.XMLSerializer){ |
paulb@389 | 460 | /** |
paulb@389 | 461 | * <p>Factory method to obtain the serialization of a DOM Node</p> |
paulb@389 | 462 | * @returns the serialized Node as an XML string |
paulb@389 | 463 | */ |
paulb@389 | 464 | Sarissa.serialize = function(oDoc){ |
paulb@389 | 465 | return (new XMLSerializer()).serializeToString(oDoc); |
paulb@389 | 466 | }; |
paulb@389 | 467 | }else{ |
paulb@389 | 468 | if((Sarissa.getDomDocument("","foo", null)).xml){ |
paulb@389 | 469 | // see non-IE version |
paulb@389 | 470 | Sarissa.serialize = function(oDoc) { |
paulb@389 | 471 | // TODO: check for HTML document and return innerHTML instead |
paulb@389 | 472 | return oDoc.xml; |
paulb@389 | 473 | }; |
paulb@389 | 474 | /** |
paulb@389 | 475 | * Utility class to serialize DOM Node objects to XML strings |
paulb@389 | 476 | * @constructor |
paulb@389 | 477 | */ |
paulb@389 | 478 | XMLSerializer = function(){}; |
paulb@389 | 479 | /** |
paulb@389 | 480 | * Serialize the given DOM Node to an XML string |
paulb@389 | 481 | * @param oNode the DOM Node to serialize |
paulb@389 | 482 | */ |
paulb@389 | 483 | XMLSerializer.prototype.serializeToString = function(oNode) { |
paulb@389 | 484 | return oNode.xml; |
paulb@389 | 485 | }; |
paulb@389 | 486 | }; |
paulb@389 | 487 | }; |
paulb@389 | 488 | |
paulb@389 | 489 | /** |
paulb@389 | 490 | * strips tags from a markup string |
paulb@389 | 491 | */ |
paulb@389 | 492 | Sarissa.stripTags = function (s) { |
paulb@389 | 493 | return s.replace(/<[^>]+>/g,""); |
paulb@389 | 494 | }; |
paulb@389 | 495 | /** |
paulb@389 | 496 | * <p>Deletes all child nodes of the given node</p> |
paulb@389 | 497 | * @argument oNode the Node to empty |
paulb@389 | 498 | */ |
paulb@389 | 499 | Sarissa.clearChildNodes = function(oNode) { |
paulb@389 | 500 | while(oNode.hasChildNodes()){ |
paulb@389 | 501 | oNode.removeChild(oNode.firstChild); |
paulb@389 | 502 | }; |
paulb@389 | 503 | }; |
paulb@389 | 504 | /** |
paulb@389 | 505 | * <p> Copies the childNodes of nodeFrom to nodeTo</p> |
paulb@389 | 506 | * <p> <b>Note:</b> The second object's original content is deleted before |
paulb@389 | 507 | * the copy operation, unless you supply a true third parameter</p> |
paulb@389 | 508 | * @argument nodeFrom the Node to copy the childNodes from |
paulb@389 | 509 | * @argument nodeTo the Node to copy the childNodes to |
paulb@389 | 510 | * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false |
paulb@389 | 511 | */ |
paulb@389 | 512 | Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { |
paulb@389 | 513 | if(!bPreserveExisting){ |
paulb@389 | 514 | Sarissa.clearChildNodes(nodeTo); |
paulb@389 | 515 | }; |
paulb@389 | 516 | var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; |
paulb@389 | 517 | var nodes = nodeFrom.childNodes; |
paulb@389 | 518 | if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { |
paulb@389 | 519 | for(var i=0;i < nodes.length;i++) { |
paulb@389 | 520 | nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); |
paulb@389 | 521 | }; |
paulb@389 | 522 | } |
paulb@389 | 523 | else{ |
paulb@389 | 524 | for(var i=0;i < nodes.length;i++) { |
paulb@389 | 525 | nodeTo.appendChild(nodes[i].cloneNode(true)); |
paulb@389 | 526 | }; |
paulb@389 | 527 | }; |
paulb@389 | 528 | }; |
paulb@389 | 529 | |
paulb@389 | 530 | /** |
paulb@389 | 531 | * <p> Moves the childNodes of nodeFrom to nodeTo</p> |
paulb@389 | 532 | * <p> <b>Note:</b> The second object's original content is deleted before |
paulb@389 | 533 | * the move operation, unless you supply a true third parameter</p> |
paulb@389 | 534 | * @argument nodeFrom the Node to copy the childNodes from |
paulb@389 | 535 | * @argument nodeTo the Node to copy the childNodes to |
paulb@389 | 536 | * @argument bPreserveExisting whether to preserve the original content of nodeTo, default is false |
paulb@389 | 537 | */ |
paulb@389 | 538 | Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { |
paulb@389 | 539 | if(!bPreserveExisting){ |
paulb@389 | 540 | Sarissa.clearChildNodes(nodeTo); |
paulb@389 | 541 | }; |
paulb@389 | 542 | |
paulb@389 | 543 | var nodes = nodeFrom.childNodes; |
paulb@389 | 544 | // if within the same doc, just move, else copy and delete |
paulb@389 | 545 | if(nodeFrom.ownerDocument == nodeTo.ownerDocument){ |
paulb@389 | 546 | nodeTo.appendChild(nodes[i]); |
paulb@389 | 547 | }else{ |
paulb@389 | 548 | var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; |
paulb@389 | 549 | if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { |
paulb@389 | 550 | for(var i=0;i < nodes.length;i++) { |
paulb@389 | 551 | nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); |
paulb@389 | 552 | }; |
paulb@389 | 553 | } |
paulb@389 | 554 | else{ |
paulb@389 | 555 | for(var i=0;i < nodes.length;i++) { |
paulb@389 | 556 | nodeTo.appendChild(nodes[i].cloneNode(true)); |
paulb@389 | 557 | }; |
paulb@389 | 558 | }; |
paulb@389 | 559 | Sarissa.clearChildNodes(nodeFrom); |
paulb@389 | 560 | }; |
paulb@389 | 561 | |
paulb@389 | 562 | }; |
paulb@389 | 563 | |
paulb@389 | 564 | /** |
paulb@389 | 565 | * <p>Serialize any object to an XML string. All properties are serialized using the property name |
paulb@389 | 566 | * as the XML element name. Array elements are rendered as <code>array-item</code> elements, |
paulb@389 | 567 | * using their index/key as the value of the <code>key</code> attribute.</p> |
paulb@389 | 568 | * @argument anyObject the object to serialize |
paulb@389 | 569 | * @argument objectName a name for that object |
paulb@389 | 570 | * @return the XML serializationj of the given object as a string |
paulb@389 | 571 | */ |
paulb@389 | 572 | Sarissa.xmlize = function(anyObject, objectName, indentSpace){ |
paulb@389 | 573 | indentSpace = indentSpace?indentSpace:''; |
paulb@389 | 574 | var s = indentSpace + '<' + objectName + '>'; |
paulb@389 | 575 | var isLeaf = false; |
paulb@389 | 576 | if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String |
paulb@389 | 577 | || anyObject instanceof Boolean || anyObject instanceof Date){ |
paulb@389 | 578 | s += Sarissa.escape(""+anyObject); |
paulb@389 | 579 | isLeaf = true; |
paulb@389 | 580 | }else{ |
paulb@389 | 581 | s += "\n"; |
paulb@389 | 582 | var itemKey = ''; |
paulb@389 | 583 | var isArrayItem = anyObject instanceof Array; |
paulb@389 | 584 | for(var name in anyObject){ |
paulb@389 | 585 | s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=\""+name+"\"":name), indentSpace + " "); |
paulb@389 | 586 | }; |
paulb@389 | 587 | s += indentSpace; |
paulb@389 | 588 | }; |
paulb@389 | 589 | return s += (objectName.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName + ">\n"); |
paulb@389 | 590 | }; |
paulb@389 | 591 | |
paulb@389 | 592 | /** |
paulb@389 | 593 | * Escape the given string chacters that correspond to the five predefined XML entities |
paulb@389 | 594 | * @param sXml the string to escape |
paulb@389 | 595 | */ |
paulb@389 | 596 | Sarissa.escape = function(sXml){ |
paulb@389 | 597 | return sXml.replace(/&/g, "&") |
paulb@389 | 598 | .replace(/</g, "<") |
paulb@389 | 599 | .replace(/>/g, ">") |
paulb@389 | 600 | .replace(/"/g, """) |
paulb@389 | 601 | .replace(/'/g, "'"); |
paulb@389 | 602 | }; |
paulb@389 | 603 | |
paulb@389 | 604 | /** |
paulb@389 | 605 | * Unescape the given string. This turns the occurences of the predefined XML |
paulb@389 | 606 | * entities to become the characters they represent correspond to the five predefined XML entities |
paulb@389 | 607 | * @param sXml the string to unescape |
paulb@389 | 608 | */ |
paulb@389 | 609 | Sarissa.unescape = function(sXml){ |
paulb@389 | 610 | return sXml.replace(/'/g,"'") |
paulb@389 | 611 | .replace(/"/g,"\"") |
paulb@389 | 612 | .replace(/>/g,">") |
paulb@389 | 613 | .replace(/</g,"<") |
paulb@389 | 614 | .replace(/&/g,"&"); |
paulb@389 | 615 | }; |
paulb@389 | 616 | // EOF |