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