1 /* 2 * ==================================================================== 3 * About Sarissa: http://dev.abiss.gr/sarissa 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 Opera 8 * @version ${project.version} 9 * @author: Copyright 2004-2008 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net 10 * ==================================================================== 11 * Licence 12 * ==================================================================== 13 * Sarissa is free software distributed under the GNU GPL version 2 (see <a href="gpl.txt">gpl.txt</a>) or higher, 14 * GNU LGPL version 2.1 (see <a href="lgpl.txt">lgpl.txt</a>) or higher and Apache Software License 2.0 or higher 15 * (see <a href="asl.txt">asl.txt</a>). This means you can choose one of the three and use that if you like. If 16 * you make modifications under the ASL, i would appreciate it if you submitted those. 17 * In case your copy of Sarissa does not include the license texts, you may find 18 * them online in various formats at <a href="http://www.gnu.org">http://www.gnu.org</a> and 19 * <a href="http://www.apache.org">http://www.apache.org</a>. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 22 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 23 * WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE 24 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 25 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 27 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 28 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 */ 30 /** 31 * <p>Sarissa is a utility class. Provides "static" methods for DOMDocument, 32 * DOM Node serialization to XML strings and other utility goodies.</p> 33 * @constructor 34 * @static 35 */ 36 function Sarissa(){} 37 Sarissa.VERSION = "0.9.9.7"; 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 Sarissa.REMOTE_CALL_FLAG = "gr.abiss.sarissa.REMOTE_CALL_FLAG"; 43 /** @private */ 44 Sarissa._lastUniqueSuffix = 0; 45 /** @private */ 46 Sarissa._getUniqueSuffix = function(){ 47 return Sarissa._lastUniqueSuffix++; 48 }; 49 /** @private */ 50 Sarissa._SARISSA_IEPREFIX4XSLPARAM = ""; 51 /** @private */ 52 Sarissa._SARISSA_HAS_DOM_IMPLEMENTATION = document.implementation && true; 53 /** @private */ 54 Sarissa._SARISSA_HAS_DOM_CREATE_DOCUMENT = Sarissa._SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.createDocument; 55 /** @private */ 56 Sarissa._SARISSA_HAS_DOM_FEATURE = Sarissa._SARISSA_HAS_DOM_IMPLEMENTATION && document.implementation.hasFeature; 57 /** @private */ 58 Sarissa._SARISSA_IS_MOZ = Sarissa._SARISSA_HAS_DOM_CREATE_DOCUMENT && Sarissa._SARISSA_HAS_DOM_FEATURE; 59 /** @private */ 60 Sarissa._SARISSA_IS_SAFARI = navigator.userAgent.toLowerCase().indexOf("safari") != -1 || navigator.userAgent.toLowerCase().indexOf("konqueror") != -1; 61 /** @private */ 62 Sarissa._SARISSA_IS_SAFARI_OLD = Sarissa._SARISSA_IS_SAFARI && (parseInt((navigator.userAgent.match(/AppleWebKit\/(\d+)/)||{})[1], 10) < 420); 63 /** @private */ 64 Sarissa._SARISSA_IS_IE = document.documentMode!=null; 65 /** @private */ 66 Sarissa._SARISSA_IS_IE7 = Sarissa._SARISSA_IS_IE && (navigator.appVersion.indexOf("MSIE 7.") != -1); 67 /** @private */ 68 Sarissa._SARISSA_IS_IE9 = Sarissa._SARISSA_IS_IE && document.documentMode >= 9; 69 /** @private */ 70 Sarissa._SARISSA_IS_OPERA = navigator.userAgent.toLowerCase().indexOf("opera") != -1; 71 if(!window.Node || !Node.ELEMENT_NODE){ 72 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}; 73 } 74 75 //This breaks for(x in o) loops in the old Safari 76 if(Sarissa._SARISSA_IS_SAFARI_OLD){ 77 HTMLHtmlElement = document.createElement("html").constructor; 78 Node = HTMLElement = {}; 79 HTMLElement.prototype = HTMLHtmlElement.__proto__.__proto__; 80 HTMLDocument = Document = document.constructor; 81 var x = new DOMParser(); 82 XMLDocument = x.constructor; 83 Element = x.parseFromString("<Single />", "text/xml").documentElement.constructor; 84 x = null; 85 } 86 if(typeof XMLDocument == "undefined" && typeof Document !="undefined"){ XMLDocument = Document; } 87 88 // IE initialization 89 if(Sarissa._SARISSA_IS_IE){ 90 // for XSLT parameter names, prefix needed by IE 91 Sarissa._SARISSA_IEPREFIX4XSLPARAM = "xsl:"; 92 // used to store the most recent ProgID available out of the above 93 var _SARISSA_DOM_PROGID = ""; 94 var _SARISSA_XMLHTTP_PROGID = ""; 95 var _SARISSA_DOM_XMLWRITER = ""; 96 /** 97 * Called when the sarissa.js file is parsed, to pick most recent 98 * ProgIDs for IE, then gets destroyed. 99 * @memberOf Sarissa 100 * @private 101 * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object 102 * @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled 103 */ 104 Sarissa.pickRecentProgID = function (idList){ 105 // found progID flag 106 var bFound = false, e; 107 var o2Store; 108 for(var i=0; i < idList.length && !bFound; i++){ 109 try{ 110 var oDoc = new ActiveXObject(idList[i]); 111 o2Store = idList[i]; 112 bFound = true; 113 }catch (objException){ 114 // trap; try next progID 115 e = objException; 116 } 117 } 118 if (!bFound) { 119 throw "Could not retrieve a valid progID of Class: " + idList[idList.length-1]+". (original exception: "+e+")"; 120 } 121 idList = null; 122 return o2Store; 123 }; 124 // pick best available MSXML progIDs 125 _SARISSA_DOM_PROGID = null; 126 _SARISSA_THREADEDDOM_PROGID = null; 127 _SARISSA_XSLTEMPLATE_PROGID = null; 128 _SARISSA_XMLHTTP_PROGID = null; 129 // define XMLHttpRequest if missing or in case of IE7 130 // as it hardcodes to MSXML3.0, causing version problems 131 // between different activex controls 132 if(!window.XMLHttpRequest || Sarissa._SARISSA_IS_IE9){ 133 /** 134 * Emulate XMLHttpRequest 135 * @constructor 136 */ 137 XMLHttpRequest = function() { 138 if(!_SARISSA_XMLHTTP_PROGID){ 139 _SARISSA_XMLHTTP_PROGID = Sarissa.pickRecentProgID(["Msxml2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]); 140 } 141 return new ActiveXObject(_SARISSA_XMLHTTP_PROGID); 142 }; 143 } 144 // we dont need this anymore 145 //============================================ 146 // Factory methods (IE) 147 //============================================ 148 // see non-IE version 149 Sarissa.getDomDocument = function(sUri, sName){ 150 if(!_SARISSA_DOM_PROGID){ 151 try{ 152 _SARISSA_DOM_PROGID = Sarissa.pickRecentProgID(["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]); 153 }catch(e){ 154 _SARISSA_DOM_PROGID = "noActiveX"; 155 } 156 } 157 158 // Not sure how far IE can carry this but try to do something useful when ActiveX is disabled 159 var oDoc = _SARISSA_DOM_PROGID == "noActiveX" ? document.createElement("xml") : new ActiveXObject(_SARISSA_DOM_PROGID); 160 // set validation off, make sure older IEs dont choke (no time or IEs to test ;-) 161 try{ 162 oDoc.validateOnParse = false; 163 oDoc.resolveExternals = "false"; 164 oDoc.setProperty("ProhibitDTD", false); 165 }catch(e){} 166 167 // if a root tag name was provided, we need to load it in the DOM object 168 if (sName){ 169 // create an artifical namespace prefix 170 // or reuse existing prefix if applicable 171 var prefix = ""; 172 if(sUri){ 173 if(sName.indexOf(":") > 1){ 174 prefix = sName.substring(0, sName.indexOf(":")); 175 sName = sName.substring(sName.indexOf(":")+1); 176 }else{ 177 prefix = "a" + Sarissa._getUniqueSuffix(); 178 } 179 } 180 // use namespaces if a namespace URI exists 181 if(sUri){ 182 oDoc.loadXML('<' + prefix+':'+sName + " xmlns:" + prefix + "=\"" + sUri + "\"" + " />"); 183 } else { 184 oDoc.loadXML('<' + sName + " />"); 185 } 186 } 187 return oDoc; 188 }; 189 // see non-IE version 190 Sarissa.getParseErrorText = function (oDoc) { 191 var parseErrorText = Sarissa.PARSED_OK; 192 if(oDoc && oDoc.parseError && oDoc.parseError.errorCode && oDoc.parseError.errorCode != 0){ 193 parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + 194 "\nLocation: " + oDoc.parseError.url + 195 "\nLine Number " + oDoc.parseError.line + ", Column " + 196 oDoc.parseError.linepos + 197 ":\n" + oDoc.parseError.srcText + 198 "\n"; 199 for(var i = 0; i < oDoc.parseError.linepos;i++){ 200 parseErrorText += "-"; 201 } 202 parseErrorText += "^\n"; 203 } 204 else if(oDoc.documentElement === null){ 205 parseErrorText = Sarissa.PARSED_EMPTY; 206 } 207 return parseErrorText; 208 }; 209 // see non-IE version 210 Sarissa.setXpathNamespaces = function(oDoc, sNsSet) { 211 oDoc.setProperty("SelectionLanguage", "XPath"); 212 oDoc.setProperty("SelectionNamespaces", sNsSet); 213 }; 214 /** 215 * A class that reuses the same XSLT stylesheet for multiple transforms. 216 * @constructor 217 */ 218 XSLTProcessor = function(){ 219 if(!_SARISSA_XSLTEMPLATE_PROGID){ 220 _SARISSA_XSLTEMPLATE_PROGID = Sarissa.pickRecentProgID(["Msxml2.XSLTemplate.6.0", "MSXML2.XSLTemplate.3.0"]); 221 } 222 this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID); 223 this.processor = null; 224 }; 225 /** 226 * Imports the given XSLT DOM and compiles it to a reusable transform 227 * <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 228 * @param {DOMDocument} xslDoc The XSLT DOMDocument to import 229 */ 230 XSLTProcessor.prototype.importStylesheet = function(xslDoc){ 231 if(!_SARISSA_THREADEDDOM_PROGID){ 232 _SARISSA_THREADEDDOM_PROGID = Sarissa.pickRecentProgID(["MSXML2.FreeThreadedDOMDocument.6.0", "MSXML2.FreeThreadedDOMDocument.3.0"]); 233 } 234 xslDoc.setProperty("SelectionLanguage", "XPath"); 235 xslDoc.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"); 236 // convert stylesheet to free threaded 237 var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID); 238 // make included/imported stylesheets work if exist and xsl was originally loaded from url 239 try{ 240 converted.resolveExternals = true; 241 converted.setProperty("AllowDocumentFunction", true); 242 converted.setProperty("AllowXsltScript", true); 243 } 244 catch(e){ 245 // Ignore. "AllowDocumentFunction" and "AllowXsltScript" is only supported in MSXML 3.0 SP4+ and 3.0 SP8+ respectively. 246 } 247 if(xslDoc.url && xslDoc.selectSingleNode("//xsl:*[local-name() = 'import' or local-name() = 'include']") != null){ 248 converted.async = false; 249 converted.load(xslDoc.url); 250 } 251 else { 252 converted.loadXML(xslDoc.xml); 253 } 254 converted.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"); 255 var output = converted.selectSingleNode("//xsl:output"); 256 //this.outputMethod = output ? output.getAttribute("method") : "html"; 257 if(output) { 258 this.outputMethod = output.getAttribute("method"); 259 } 260 else { 261 delete this.outputMethod; 262 } 263 this.template.stylesheet = converted; 264 this.processor = this.template.createProcessor(); 265 // for getParameter and clearParameters 266 this.paramsSet = []; 267 }; 268 269 /** 270 * Transform the given XML DOM and return the transformation result as a new DOM document 271 * @param {DOMDocument} sourceDoc The XML DOMDocument to transform 272 * @return {DOMDocument} The transformation result as a DOM Document 273 */ 274 XSLTProcessor.prototype.transformToDocument = function(sourceDoc){ 275 // fix for bug 1549749 276 var outDoc; 277 if(_SARISSA_THREADEDDOM_PROGID){ 278 this.processor.input=sourceDoc; 279 outDoc=new ActiveXObject(_SARISSA_DOM_PROGID); 280 this.processor.output=outDoc; 281 this.processor.transform(); 282 return outDoc; 283 } 284 else{ 285 if(!_SARISSA_DOM_XMLWRITER){ 286 _SARISSA_DOM_XMLWRITER = Sarissa.pickRecentProgID(["Msxml2.MXXMLWriter.6.0", "Msxml2.MXXMLWriter.3.0", "MSXML2.MXXMLWriter", "MSXML.MXXMLWriter", "Microsoft.XMLDOM"]); 287 } 288 this.processor.input = sourceDoc; 289 outDoc = new ActiveXObject(_SARISSA_DOM_XMLWRITER); 290 this.processor.output = outDoc; 291 this.processor.transform(); 292 var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); 293 oDoc.loadXML(outDoc.output+""); 294 return oDoc; 295 } 296 }; 297 298 /** 299 * Transform the given XML DOM and return the transformation result as a new DOM fragment. 300 * <b>Note</b>: The xsl:output method must match the nature of the owner document (XML/HTML). 301 * @param {DOMDocument} sourceDoc The XML DOMDocument to transform 302 * @param {DOMDocument} ownerDoc The owner of the result fragment 303 * @return {DOMDocument} The transformation result as a DOM Document 304 */ 305 XSLTProcessor.prototype.transformToFragment = function (sourceDoc, ownerDoc) { 306 this.processor.input = sourceDoc; 307 this.processor.transform(); 308 var s = this.processor.output; 309 var f = ownerDoc.createDocumentFragment(); 310 var container; 311 if (this.outputMethod == 'text') { 312 f.appendChild(ownerDoc.createTextNode(s)); 313 } else if (ownerDoc.body && ownerDoc.body.innerHTML) { 314 container = ownerDoc.createElement('div'); 315 if (s.substring(0, 5) == '<?xml') { 316 s = s.substring(s.indexOf('?>') + 2); 317 } 318 319 container.innerHTML = s.replace(/\s+$/g, ''); 320 while (container.hasChildNodes()) { 321 f.appendChild(container.firstChild); 322 } 323 } 324 else { 325 var oDoc = new ActiveXObject(_SARISSA_DOM_PROGID); 326 if (s.substring(0, 5) == '<?xml') { 327 s = s.substring(s.indexOf('?>') + 2); 328 } 329 var xml = ''.concat('<my>', s, '</my>'); 330 oDoc.loadXML(xml); 331 container = oDoc.documentElement; 332 while (container.hasChildNodes()) { 333 f.appendChild(container.firstChild); 334 } 335 } 336 return f; 337 }; 338 339 /** 340 * Set global XSLT parameter of the imported stylesheet. This method should 341 * only be used <strong>after</strong> the importStylesheet method for the 342 * context XSLTProcessor instance. 343 * @param {String} nsURI The parameter namespace URI 344 * @param {String} name The parameter base name 345 * @param {String} value The new parameter value 346 */ 347 XSLTProcessor.prototype.setParameter = function(nsURI, name, value){ 348 // make value a zero length string if null to allow clearing 349 value = value ? value : ""; 350 // nsURI is optional but cannot be null 351 if(nsURI){ 352 this.processor.addParameter(name, value, nsURI); 353 }else{ 354 this.processor.addParameter(name, value); 355 } 356 // update updated params for getParameter 357 nsURI = "" + (nsURI || ""); 358 if(!this.paramsSet[nsURI]){ 359 this.paramsSet[nsURI] = []; 360 } 361 this.paramsSet[nsURI][name] = value; 362 }; 363 /** 364 * Gets a parameter if previously set by setParameter. Returns null 365 * otherwise 366 * @param {String} name The parameter base name 367 * @param {String} value The new parameter value 368 * @return {String} The parameter value if reviously set by setParameter, null otherwise 369 */ 370 XSLTProcessor.prototype.getParameter = function(nsURI, name){ 371 nsURI = "" + (nsURI || ""); 372 if(this.paramsSet[nsURI] && this.paramsSet[nsURI][name]){ 373 return this.paramsSet[nsURI][name]; 374 }else{ 375 return null; 376 } 377 }; 378 379 /** 380 * Clear parameters (set them to default values as defined in the stylesheet itself) 381 */ 382 XSLTProcessor.prototype.clearParameters = function(){ 383 for(var nsURI in this.paramsSet){ 384 for(var name in this.paramsSet[nsURI]){ 385 if(nsURI!=""){ 386 this.processor.addParameter(name, "", nsURI); 387 }else{ 388 this.processor.addParameter(name, ""); 389 } 390 } 391 } 392 this.paramsSet = []; 393 }; 394 }else{ /* end IE initialization, try to deal with real browsers now ;-) */ 395 if(Sarissa._SARISSA_HAS_DOM_CREATE_DOCUMENT){ 396 /** 397 * <p>Ensures the document was loaded correctly, otherwise sets the 398 * parseError to -1 to indicate something went wrong. Internal use</p> 399 * @private 400 */ 401 Sarissa.__handleLoad__ = function(oDoc){ 402 Sarissa.__setReadyState__(oDoc, 4); 403 }; 404 /** 405 * <p>Attached by an event handler to the load event. Internal use.</p> 406 * @private 407 */ 408 _sarissa_XMLDocument_onload = function(){ 409 Sarissa.__handleLoad__(this); 410 }; 411 /** 412 * <p>Sets the readyState property of the given DOM Document object. 413 * Internal use.</p> 414 * @memberOf Sarissa 415 * @private 416 * @param oDoc the DOM Document object to fire the 417 * readystatechange event 418 * @param iReadyState the number to change the readystate property to 419 */ 420 Sarissa.__setReadyState__ = function(oDoc, iReadyState){ 421 oDoc.readyState = iReadyState; 422 oDoc.readystate = iReadyState; 423 if (oDoc.onreadystatechange != null && typeof oDoc.onreadystatechange == "function") { 424 oDoc.onreadystatechange(); 425 } 426 }; 427 428 Sarissa.getDomDocument = function(sUri, sName){ 429 var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); 430 if(!oDoc.onreadystatechange){ 431 432 /** 433 * <p>Emulate IE's onreadystatechange attribute</p> 434 */ 435 oDoc.onreadystatechange = null; 436 } 437 if(!oDoc.readyState){ 438 /** 439 * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p> 440 * <ul><li>1 == LOADING,</li> 441 * <li>2 == LOADED,</li> 442 * <li>3 == INTERACTIVE,</li> 443 * <li>4 == COMPLETED</li></ul> 444 */ 445 oDoc.readyState = 0; 446 } 447 if(!oDoc.load){ 448 oDoc.load = function(url) { 449 var xmlhttp=new XMLHttpRequest(); 450 xmlhttp.onreadystatechange=oDoc.onreadystatechange; 451 xmlhttp.readyState=oDoc.readyState; 452 xmlhttp.addEventListener("load", function(e) { 453 oDoc.innerHtml=e.target.responseXML; 454 var evt = document.createEvent('Event'); 455 evt.initEvent('load', false, false); 456 oDoc.dispatchEvent(evt); 457 } , false); 458 xmlhttp.open("GET", url, oDoc.async); 459 xmlhttp.send(); 460 return oDoc.async?true:xmlhttp.response; 461 } 462 } 463 oDoc.addEventListener("load", _sarissa_XMLDocument_onload, false); 464 return oDoc; 465 }; 466 if(window.XMLDocument){ 467 // do nothing 468 }// TODO: check if the new document has content before trying to copynodes, check for error handling in DOM 3 LS 469 else if(Sarissa._SARISSA_HAS_DOM_FEATURE && window.Document && !Document.prototype.load && document.implementation.hasFeature('LS', '3.0')){ 470 //Opera 9 may get the XPath branch which gives creates XMLDocument, therefore it doesn't reach here which is good 471 /** 472 * <p>Factory method to obtain a new DOM Document object</p> 473 * @memberOf Sarissa 474 * @param {String} sUri the namespace of the root node (if any) 475 * @param {String} sUri the local name of the root node (if any) 476 * @returns {DOMDOcument} a new DOM Document 477 */ 478 Sarissa.getDomDocument = function(sUri, sName){ 479 var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); 480 return oDoc; 481 }; 482 } 483 else { 484 Sarissa.getDomDocument = function(sUri, sName){ 485 var oDoc = document.implementation.createDocument(sUri?sUri:null, sName?sName:null, null); 486 // looks like safari does not create the root element for some unknown reason 487 if(oDoc && (sUri || sName) && !oDoc.documentElement){ 488 oDoc.appendChild(oDoc.createElementNS(sUri, sName)); 489 } 490 return oDoc; 491 }; 492 } 493 }//if(Sarissa._SARISSA_HAS_DOM_CREATE_DOCUMENT) 494 } 495 //========================================== 496 // Common stuff 497 //========================================== 498 if(!window.DOMParser || Sarissa._SARISSA_IS_IE9){ 499 if(Sarissa._SARISSA_IS_SAFARI){ 500 /** 501 * DOMParser is a utility class, used to construct DOMDocuments from XML strings 502 * @constructor 503 */ 504 DOMParser = function() { }; 505 /** 506 * Construct a new DOM Document from the given XMLstring 507 * @param {String} sXml the given XML string 508 * @param {String} contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml). 509 * @return {DOMDocument} a new DOM Document from the given XML string 510 */ 511 DOMParser.prototype.parseFromString = function(sXml, contentType){ 512 var xmlhttp = new XMLHttpRequest(); 513 xmlhttp.open("GET", "data:text/xml;charset=utf-8," + encodeURIComponent(sXml), false); 514 xmlhttp.send(null); 515 return xmlhttp.responseXML; 516 }; 517 }else if(Sarissa.getDomDocument && Sarissa.getDomDocument() && Sarissa.getDomDocument(null, "bar").xml){ 518 DOMParser = function() { }; 519 DOMParser.prototype.parseFromString = function(sXml, contentType){ 520 var doc = Sarissa.getDomDocument(); 521 try{ 522 doc.validateOnParse = false; 523 doc.setProperty("ProhibitDTD", false); 524 }catch(e){} 525 doc.loadXML(sXml); 526 return doc; 527 }; 528 } 529 } 530 531 if((typeof(document.importNode) == "undefined") && Sarissa._SARISSA_IS_IE){ 532 try{ 533 /** 534 * Implementation of importNode for the context window document in IE. 535 * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored. 536 * @param {DOMNode} oNode the Node to import 537 * @param {boolean} bChildren whether to include the children of oNode 538 * @returns the imported node for further use 539 */ 540 document.importNode = function(oNode, bChildren){ 541 var tmp; 542 if (oNode.nodeName=='#text') { 543 return document.createTextNode(oNode.data); 544 } 545 else { 546 if(oNode.nodeName == "tbody" || oNode.nodeName == "tr"){ 547 tmp = document.createElement("table"); 548 } 549 else if(oNode.nodeName == "td"){ 550 tmp = document.createElement("tr"); 551 } 552 else if(oNode.nodeName == "option"){ 553 tmp = document.createElement("select"); 554 } 555 else{ 556 tmp = document.createElement("div"); 557 } 558 if(bChildren){ 559 tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML; 560 }else{ 561 tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML; 562 } 563 return tmp.getElementsByTagName("*")[0]; 564 } 565 }; 566 }catch(e){ } 567 } 568 if(!Sarissa.getParseErrorText){ 569 /** 570 * <p>Returns a human readable description of the parsing error. Usefull 571 * for debugging. Tip: append the returned error string in a <pre> 572 * element if you want to render it.</p> 573 * <p>Many thanks to Christian Stocker for the initial patch.</p> 574 * @memberOf Sarissa 575 * @param {DOMDocument} oDoc The target DOM document 576 * @returns {String} The parsing error description of the target Document in 577 * human readable form (preformated text) 578 */ 579 Sarissa.getParseErrorText = function (oDoc){ 580 var parseErrorText = Sarissa.PARSED_OK; 581 if((!oDoc) || (!oDoc.documentElement)){ 582 parseErrorText = Sarissa.PARSED_EMPTY; 583 } else if(oDoc.documentElement.tagName == "parsererror"){ 584 parseErrorText = oDoc.documentElement.firstChild.data; 585 parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data; 586 } else if(oDoc.getElementsByTagName("parsererror").length > 0){ 587 var parsererror = oDoc.getElementsByTagName("parsererror")[0]; 588 parseErrorText = Sarissa.getText(parsererror, true)+"\n"; 589 } else if(oDoc.parseError && oDoc.parseError.errorCode != 0){ 590 parseErrorText = Sarissa.PARSED_UNKNOWN_ERROR; 591 } 592 return parseErrorText; 593 }; 594 } 595 /** 596 * Get a string with the concatenated values of all string nodes under the given node 597 * @param {DOMNode} oNode the given DOM node 598 * @param {boolean} deep whether to recursively scan the children nodes of the given node for text as well. Default is <code>false</code> 599 * @memberOf Sarissa 600 */ 601 Sarissa.getText = function(oNode, deep){ 602 var s = ""; 603 var nodes = oNode.childNodes; 604 // opera fix, finds no child text node for attributes so we use .value 605 if (oNode.nodeType == Node.ATTRIBUTE_NODE && nodes.length == 0) { 606 return oNode.value; 607 } 608 // END opera fix 609 for(var i=0; i < nodes.length; i++){ 610 var node = nodes[i]; 611 var nodeType = node.nodeType; 612 if(nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE){ 613 s += node.data; 614 } else if(deep === true && (nodeType == Node.ELEMENT_NODE || nodeType == Node.DOCUMENT_NODE || nodeType == Node.DOCUMENT_FRAGMENT_NODE)){ 615 s += Sarissa.getText(node, true); 616 } 617 } 618 return s; 619 }; 620 if(!window.XMLSerializer && Sarissa.getDomDocument && Sarissa.getDomDocument("","foo", null).xml){ 621 /** 622 * Utility class to serialize DOM Node objects to XML strings 623 * @constructor 624 */ 625 XMLSerializer = function(){}; 626 /** 627 * Serialize the given DOM Node to an XML string 628 * @param {DOMNode} oNode the DOM Node to serialize 629 */ 630 XMLSerializer.prototype.serializeToString = function(oNode) { 631 return oNode.xml; 632 }; 633 } else if (Sarissa._SARISSA_IS_IE9 && window.XMLSerializer) { 634 // We save old object for any futur use with IE Document (not xml) 635 IE9XMLSerializer= XMLSerializer; 636 XMLSerializer = function(){ this._oldSerializer=new IE9XMLSerializer() }; 637 XMLSerializer.prototype.serializeToString = function(oNode) { 638 if (typeof(oNode)=='object' && 'xml' in oNode) { 639 return oNode.xml; 640 } else { 641 return this._oldSerializer.serializeToString(oNode); 642 } 643 }; 644 } 645 646 /** 647 * Strips tags from the given markup string. If the given string is 648 * <code>undefined</code>, <code>null</code> or empty, it is returned as is. 649 * @memberOf Sarissa 650 * @param {String} s the string to strip the tags from 651 */ 652 Sarissa.stripTags = function (s) { 653 return s?s.replace(/<[^>]+>/g,""):s; 654 }; 655 /** 656 * <p>Deletes all child nodes of the given node</p> 657 * @memberOf Sarissa 658 * @param {DOMNode} oNode the Node to empty 659 */ 660 Sarissa.clearChildNodes = function(oNode) { 661 // need to check for firstChild due to opera 8 bug with hasChildNodes 662 while(oNode.firstChild) { 663 oNode.removeChild(oNode.firstChild); 664 } 665 }; 666 /** 667 * <p> Copies the childNodes of nodeFrom to nodeTo</p> 668 * <p> <b>Note:</b> The second object's original content is deleted before 669 * the copy operation, unless you supply a true third parameter</p> 670 * @memberOf Sarissa 671 * @param {DOMNode} nodeFrom the Node to copy the childNodes from 672 * @param {DOMNode} nodeTo the Node to copy the childNodes to 673 * @param {boolean} bPreserveExisting whether to preserve the original content of nodeTo, default is false 674 */ 675 Sarissa.copyChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { 676 if(Sarissa._SARISSA_IS_SAFARI_OLD && nodeTo.nodeType == Node.DOCUMENT_NODE){ 677 nodeTo = nodeTo.documentElement; //Apparently there's a bug in safari where you can't appendChild to a document node 678 } 679 680 if((!nodeFrom) || (!nodeTo)){ 681 throw "Both source and destination nodes must be provided"; 682 } 683 if(!bPreserveExisting){ 684 Sarissa.clearChildNodes(nodeTo); 685 } 686 var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; 687 var nodes = nodeFrom.childNodes; 688 var i; 689 if(typeof(ownerDoc.importNode) != "undefined") { 690 for(i=0;i < nodes.length;i++) { 691 nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); 692 } 693 } else { 694 for(i=0;i < nodes.length;i++) { 695 nodeTo.appendChild(nodes[i].cloneNode(true)); 696 } 697 } 698 }; 699 700 /** 701 * <p> Moves the childNodes of nodeFrom to nodeTo</p> 702 * <p> <b>Note:</b> The second object's original content is deleted before 703 * the move operation, unless you supply a true third parameter</p> 704 * @memberOf Sarissa 705 * @param {DOMNode} nodeFrom the Node to copy the childNodes from 706 * @param {DOMNode} nodeTo the Node to copy the childNodes to 707 * @param {boolean} bPreserveExisting whether to preserve the original content of nodeTo, default is 708 */ 709 Sarissa.moveChildNodes = function(nodeFrom, nodeTo, bPreserveExisting) { 710 if((!nodeFrom) || (!nodeTo)){ 711 throw "Both source and destination nodes must be provided"; 712 } 713 if(!bPreserveExisting){ 714 Sarissa.clearChildNodes(nodeTo); 715 } 716 var nodes = nodeFrom.childNodes; 717 // if within the same doc, just move, else copy and delete 718 if(nodeFrom.ownerDocument == nodeTo.ownerDocument){ 719 while(nodeFrom.firstChild){ 720 nodeTo.appendChild(nodeFrom.firstChild); 721 } 722 } else { 723 var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; 724 var i; 725 if(typeof(ownerDoc.importNode) != "undefined") { 726 for(i=0;i < nodes.length;i++) { 727 nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); 728 } 729 }else{ 730 for(i=0;i < nodes.length;i++) { 731 nodeTo.appendChild(nodes[i].cloneNode(true)); 732 } 733 } 734 Sarissa.clearChildNodes(nodeFrom); 735 } 736 }; 737 738 /** 739 * <p>Serialize any <strong>non</strong> DOM object to an XML string. All properties are serialized using the property name 740 * as the XML element name. Array elements are rendered as <code>array-item</code> elements, 741 * using their index/key as the value of the <code>key</code> attribute.</p> 742 * @memberOf Sarissa 743 * @param {Object} anyObject the object to serialize 744 * @param {String} objectName a name for that object, to be used as the root element name 745 * @param {String} indentSpace Optional, the indentation space to use, default is an empty 746 * string. A single space character is added in any recursive call. 747 * @param {noolean} skipEscape Optional, whether to skip escaping characters that map to the 748 * five predefined XML entities. Default is <code>false</code>. 749 * @return {String} the XML serialization of the given object as a string 750 */ 751 Sarissa.xmlize = function(anyObject, objectName, indentSpace, skipEscape){ 752 indentSpace = indentSpace?indentSpace:''; 753 var s = indentSpace + '<' + objectName + '>'; 754 var isLeaf = false; 755 if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String || anyObject instanceof Boolean || anyObject instanceof Date){ 756 s += (skipEscape ? Sarissa.escape(anyObject) : anyObject); 757 isLeaf = true; 758 }else{ 759 s += "\n"; 760 var isArrayItem = anyObject instanceof Array; 761 for(var name in anyObject){ 762 // do not xmlize functions 763 if (anyObject[name] instanceof Function){ 764 continue; 765 } 766 s += Sarissa.xmlize(anyObject[name], (isArrayItem?"array-item key=\""+name+"\"":name), indentSpace + " "); 767 } 768 s += indentSpace; 769 } 770 return (s += (objectName.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName + ">\n")); 771 }; 772 773 /** 774 * Escape the given string chacters that correspond to the five predefined XML entities 775 * @memberOf Sarissa 776 * @param {String} sXml the string to escape 777 */ 778 Sarissa.escape = function(sXml){ 779 return sXml.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); 780 }; 781 782 /** 783 * Unescape the given string. This turns the occurences of the predefined XML 784 * entities to become the characters they represent correspond to the five predefined XML entities 785 * @memberOf Sarissa 786 * @param {String}sXml the string to unescape 787 */ 788 Sarissa.unescape = function(sXml){ 789 return sXml.replace(/'/g,"'").replace(/"/g,"\"").replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&"); 790 }; 791 792 /** @private */ 793 Sarissa.updateCursor = function(oTargetElement, sValue) { 794 if(oTargetElement && oTargetElement.style && oTargetElement.style.cursor != undefined ){ 795 oTargetElement.style.cursor = sValue; 796 } 797 }; 798 799 /** 800 * Asynchronously update an element with response of a GET request on the given URL. Passing a configured XSLT 801 * processor will result in transforming and updating oNode before using it to update oTargetElement. 802 * You can also pass a callback function to be executed when the update is finished. The function will be called as 803 * <code>functionName(oNode, oTargetElement);</code> 804 * @memberOf Sarissa 805 * @param {String} sFromUrl the URL to make the request to 806 * @param {DOMElement} oTargetElement the element to update 807 * @param {XSLTProcessor} xsltproc (optional) the transformer to use on the returned 808 * content before updating the target element with it 809 * @param {Function} callback (optional) a Function object to execute once the update is finished successfuly, called as <code>callback(sFromUrl, oTargetElement)</code>. 810 * In case an exception is thrown during execution, the callback is called as called as <code>callback(sFromUrl, oTargetElement, oException)</code> 811 * @param {boolean} skipCache (optional) whether to skip any cache 812 */ 813 Sarissa.updateContentFromURI = function(sFromUrl, oTargetElement, xsltproc, callback, skipCache) { 814 try{ 815 Sarissa.updateCursor(oTargetElement, "wait"); 816 var xmlhttp = new XMLHttpRequest(); 817 xmlhttp.open("GET", sFromUrl, true); 818 xmlhttp.onreadystatechange = function() { 819 if (xmlhttp.readyState == 4) { 820 try{ 821 var oDomDoc = xmlhttp.responseXML; 822 if(oDomDoc && Sarissa.getParseErrorText(oDomDoc) == Sarissa.PARSED_OK){ 823 Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc); 824 if(callback){ 825 callback(sFromUrl, oTargetElement); 826 } 827 } 828 else{ 829 throw Sarissa.getParseErrorText(oDomDoc); 830 } 831 } 832 catch(e){ 833 if(callback){ 834 callback(sFromUrl, oTargetElement, e); 835 } 836 else{ 837 throw e; 838 } 839 } 840 } 841 }; 842 if (skipCache) { 843 var oldage = "Sat, 1 Jan 2000 00:00:00 GMT"; 844 xmlhttp.setRequestHeader("If-Modified-Since", oldage); 845 } 846 xmlhttp.send(""); 847 } 848 catch(e){ 849 Sarissa.updateCursor(oTargetElement, "auto"); 850 if(callback){ 851 callback(sFromUrl, oTargetElement, e); 852 } 853 else{ 854 throw e; 855 } 856 } 857 }; 858 859 /** 860 * Update an element's content with the given DOM node. Passing a configured XSLT 861 * processor will result in transforming and updating oNode before using it to update oTargetElement. 862 * You can also pass a callback function to be executed when the update is finished. The function will be called as 863 * <code>functionName(oNode, oTargetElement);</code> 864 * @memberOf Sarissa 865 * @param {DOMNode} oNode the URL to make the request to 866 * @param {DOMElement} oTargetElement the element to update 867 * @param {XSLTProcessor} xsltproc (optional) the transformer to use on the given 868 * DOM node before updating the target element with it 869 */ 870 Sarissa.updateContentFromNode = function(oNode, oTargetElement, xsltproc) { 871 try { 872 Sarissa.updateCursor(oTargetElement, "wait"); 873 Sarissa.clearChildNodes(oTargetElement); 874 // check for parsing errors 875 var ownerDoc = oNode.nodeType == Node.DOCUMENT_NODE?oNode:oNode.ownerDocument; 876 if(ownerDoc.parseError && ownerDoc.parseError.errorCode != 0) { 877 var pre = document.createElement("pre"); 878 pre.appendChild(document.createTextNode(Sarissa.getParseErrorText(ownerDoc))); 879 oTargetElement.appendChild(pre); 880 } 881 else { 882 // transform if appropriate 883 if(xsltproc) { 884 oNode = xsltproc.transformToDocument(oNode); 885 } 886 // be smart, maybe the user wants to display the source instead 887 if(oTargetElement.tagName.toLowerCase() == "textarea" || oTargetElement.tagName.toLowerCase() == "input") { 888 oTargetElement.value = new XMLSerializer().serializeToString(oNode); 889 } 890 else { 891 // ok that was not smart; it was paranoid. Keep up the good work by trying to use DOM instead of innerHTML 892 try{ 893 oTargetElement.appendChild(oTargetElement.ownerDocument.importNode(oNode, true)); 894 } 895 catch(e){ 896 oTargetElement.innerHTML = new XMLSerializer().serializeToString(oNode); 897 } 898 } 899 } 900 } 901 catch(e) { 902 throw e; 903 } 904 finally{ 905 Sarissa.updateCursor(oTargetElement, "auto"); 906 } 907 }; 908 909 910 /** 911 * Creates an HTTP URL query string from the given HTML form data 912 * @memberOf Sarissa 913 * @param {HTMLFormElement} oForm the form to construct the query string from 914 */ 915 Sarissa.formToQueryString = function(oForm){ 916 var qs = ""; 917 for(var i = 0;i < oForm.elements.length;i++) { 918 var oField = oForm.elements[i]; 919 var sFieldName = oField.getAttribute("name") ? oField.getAttribute("name") : oField.getAttribute("id"); 920 // ensure we got a proper name/id and that the field is not disabled 921 if(sFieldName && 922 ((!oField.disabled) || oField.type == "hidden")) { 923 switch(oField.type) { 924 case "hidden": 925 case "text": 926 case "textarea": 927 case "password": 928 qs += sFieldName + "=" + encodeURIComponent(oField.value) + "&"; 929 break; 930 case "select-one": 931 qs += sFieldName + "=" + encodeURIComponent(oField.options[oField.selectedIndex].value) + "&"; 932 break; 933 case "select-multiple": 934 for (var j = 0; j < oField.length; j++) { 935 var optElem = oField.options[j]; 936 if (optElem.selected === true) { 937 qs += sFieldName + "[]" + "=" + encodeURIComponent(optElem.value) + "&"; 938 } 939 } 940 break; 941 case "checkbox": 942 case "radio": 943 if(oField.checked) { 944 qs += sFieldName + "=" + encodeURIComponent(oField.value) + "&"; 945 } 946 break; 947 } 948 } 949 } 950 // return after removing last '&' 951 return qs.substr(0, qs.length - 1); 952 }; 953 954 955 /** 956 * Asynchronously update an element with response of an XMLHttpRequest-based emulation of a form submission. <p>The form <code>action</code> and 957 * <code>method</code> attributess will be followed. Passing a configured XSLT processor will result in 958 * transforming and updating the server response before using it to update the target element. 959 * You can also pass a callback function to be executed when the update is finished. The function will be called as 960 * <code>functionName(oNode, oTargetElement);</code></p> 961 * <p>Here is an example of using this in a form element:</p> 962 * <pre name="code" class="xml"> 963 * <div id="targetId"> this content will be updated</div> 964 * <form action="/my/form/handler" method="post" 965 * onbeforesubmit="return Sarissa.updateContentFromForm(this, document.getElementById('targetId'));"><pre> 966 * <p>If JavaScript is supported, the form will not be submitted. Instead, Sarissa will 967 * scan the form and make an appropriate AJAX request, also adding a parameter 968 * to signal to the server that this is an AJAX call. The parameter is 969 * constructed as <code>Sarissa.REMOTE_CALL_FLAG = "=true"</code> so you can change the name in your webpage 970 * simply by assigning another value to Sarissa.REMOTE_CALL_FLAG. If JavaScript is not supported 971 * the form will be submitted normally. 972 * @memberOf Sarissa 973 * @param {HTMLFormElement} oForm the form submition to emulate 974 * @param {DOMElement} oTargetElement the element to update 975 * @param {XSLTProcessor} xsltproc (optional) the transformer to use on the returned 976 * content before updating the target element with it 977 * @param {Function} callback (optional) a Function object to execute once the update is finished successfuly, called as <code>callback(oNode, oTargetElement)</code>. 978 * In case an exception occurs during excecution and a callback function was provided, the exception is cought and the callback is called as 979 * <code>callback(oForm, oTargetElement, exception)</code> 980 */ 981 Sarissa.updateContentFromForm = function(oForm, oTargetElement, xsltproc, callback) { 982 try{ 983 Sarissa.updateCursor(oTargetElement, "wait"); 984 // build parameters from form fields 985 var params = Sarissa.formToQueryString(oForm) + "&" + Sarissa.REMOTE_CALL_FLAG + "=true"; 986 var xmlhttp = new XMLHttpRequest(); 987 var bUseGet = oForm.getAttribute("method") && oForm.getAttribute("method").toLowerCase() == "get"; 988 if(bUseGet) { 989 xmlhttp.open("GET", oForm.getAttribute("action")+"?"+params, true); 990 } 991 else{ 992 xmlhttp.open('POST', oForm.getAttribute("action"), true); 993 xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 994 xmlhttp.setRequestHeader("Content-length", params.length); 995 xmlhttp.setRequestHeader("Connection", "close"); 996 } 997 xmlhttp.onreadystatechange = function() { 998 try{ 999 if (xmlhttp.readyState == 4) { 1000 var oDomDoc = xmlhttp.responseXML; 1001 if(oDomDoc && Sarissa.getParseErrorText(oDomDoc) == Sarissa.PARSED_OK){ 1002 Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc); 1003 if(callback){ 1004 callback(oForm, oTargetElement); 1005 } 1006 } 1007 else{ 1008 throw Sarissa.getParseErrorText(oDomDoc); 1009 } 1010 } 1011 } 1012 catch(e){ 1013 if(callback){ 1014 callback(oForm, oTargetElement, e); 1015 } 1016 else{ 1017 throw e; 1018 } 1019 } 1020 }; 1021 xmlhttp.send(bUseGet?"":params); 1022 } 1023 catch(e){ 1024 Sarissa.updateCursor(oTargetElement, "auto"); 1025 if(callback){ 1026 callback(oForm, oTargetElement, e); 1027 } 1028 else{ 1029 throw e; 1030 } 1031 } 1032 return false; 1033 }; 1034 1035 /** 1036 * Get the name of a function created like: 1037 * <pre>function functionName(){}</pre> 1038 * If a name is not found, attach the function to 1039 * the window object with a new name and return that 1040 * @param {Function} oFunc the function object 1041 */ 1042 Sarissa.getFunctionName = function(oFunc){ 1043 if(!oFunc || (typeof oFunc != 'function' )){ 1044 throw "The value of parameter 'oFunc' must be a function"; 1045 } 1046 if(oFunc.name) { 1047 return oFunc.name; 1048 } 1049 // try to parse the function name from the defintion 1050 var sFunc = oFunc.toString(); 1051 alert("sFunc: "+sFunc); 1052 var name = sFunc.substring(sFunc.indexOf('function') + 8 , sFunc.indexOf('(')); 1053 if(!name || name.length == 0 || name == " "){ 1054 // attach to window object under a new name 1055 name = "SarissaAnonymous" + Sarissa._getUniqueSuffix(); 1056 window[name] = oFunc; 1057 } 1058 return name; 1059 }; 1060 1061 /** 1062 * 1063 */ 1064 Sarissa.setRemoteJsonCallback = function(url, callback, callbackParam) { 1065 if(!callbackParam){ 1066 callbackParam = "callback"; 1067 } 1068 var callbackFunctionName = Sarissa.getFunctionName(callback); 1069 //alert("callbackFunctionName: '" + callbackFunctionName+"', length: "+callbackFunctionName.length); 1070 var id = "sarissa_json_script_id_" + Sarissa._getUniqueSuffix(); 1071 var oHead = document.getElementsByTagName("head")[0]; 1072 var scriptTag = document.createElement('script'); 1073 scriptTag.type = 'text/javascript'; 1074 scriptTag.id = id; 1075 scriptTag.onload = function(){ 1076 // cleanUp 1077 // document.removeChild(scriptTag); 1078 }; 1079 if(url.indexOf("?") != -1){ 1080 url += ("&" + callbackParam + "=" + callbackFunctionName); 1081 } 1082 else{ 1083 url += ("?" + callbackParam + "=" + callbackFunctionName); 1084 } 1085 scriptTag.src = url; 1086 oHead.appendChild(scriptTag); 1087 return id; 1088 }; 1089 1090 // EOF 1091