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