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
9 * @author: Copyright 2004-2008 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net
10 * ====================================================================
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>.
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.
31 * <p>Sarissa is a utility class. Provides "static" methods for DOMDocument,
32 * DOM Node serialization to XML strings and other utility goodies.</p>
37 Sarissa
.VERSION
= "0.9.9.6";
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";
44 Sarissa
._lastUniqueSuffix
= 0;
46 Sarissa
._getUniqueSuffix = function(){
47 return Sarissa
._lastUniqueSuffix
++;
50 Sarissa
._SARISSA_IEPREFIX4XSLPARAM
= "";
52 Sarissa
._SARISSA_HAS_DOM_IMPLEMENTATION
= document
.implementation
&& true;
54 Sarissa
._SARISSA_HAS_DOM_CREATE_DOCUMENT
= Sarissa
._SARISSA_HAS_DOM_IMPLEMENTATION
&& document
.implementation
.createDocument
;
56 Sarissa
._SARISSA_HAS_DOM_FEATURE
= Sarissa
._SARISSA_HAS_DOM_IMPLEMENTATION
&& document
.implementation
.hasFeature
;
58 Sarissa
._SARISSA_IS_MOZ
= Sarissa
._SARISSA_HAS_DOM_CREATE_DOCUMENT
&& Sarissa
._SARISSA_HAS_DOM_FEATURE
;
60 Sarissa
._SARISSA_IS_SAFARI
= navigator
.userAgent
.toLowerCase().indexOf("safari") != -1 || navigator
.userAgent
.toLowerCase().indexOf("konqueror") != -1;
62 Sarissa
._SARISSA_IS_SAFARI_OLD
= Sarissa
._SARISSA_IS_SAFARI
&& (parseInt((navigator
.userAgent
.match(/AppleWebKit\/(\d+)/)||{})[1], 10) < 420);
64 Sarissa
._SARISSA_IS_IE
= document
.all
&& window
.ActiveXObject
&& navigator
.userAgent
.toLowerCase().indexOf("msie") > -1 && navigator
.userAgent
.toLowerCase().indexOf("opera") == -1;
66 Sarissa
._SARISSA_IS_IE9
= Sarissa
._SARISSA_IS_IE
&& navigator
.userAgent
.toLowerCase().indexOf("msie 9") > -1;
68 Sarissa
._SARISSA_IS_OPERA
= navigator
.userAgent
.toLowerCase().indexOf("opera") != -1;
69 if(!window
.Node
|| !Node
.ELEMENT_NODE
){
70 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 //This breaks for(x in o) loops in the old Safari
74 if(Sarissa
._SARISSA_IS_SAFARI_OLD
){
75 HTMLHtmlElement
= document
.createElement("html").constructor;
76 Node
= HTMLElement
= {};
77 HTMLElement
.prototype = HTMLHtmlElement
.__proto__
.__proto__
;
78 HTMLDocument
= Document
= document
.constructor;
79 var x
= new DOMParser();
80 XMLDocument
= x
.constructor;
81 Element
= x
.parseFromString("<Single />", "text/xml").documentElement
.constructor;
84 if(typeof XMLDocument
== "undefined" && typeof Document
!="undefined"){ XMLDocument
= Document
; }
87 if(Sarissa
._SARISSA_IS_IE
){
88 // for XSLT parameter names, prefix needed by IE
89 Sarissa
._SARISSA_IEPREFIX4XSLPARAM
= "xsl:";
90 // used to store the most recent ProgID available out of the above
91 var _SARISSA_DOM_PROGID
= "";
92 var _SARISSA_XMLHTTP_PROGID
= "";
93 var _SARISSA_DOM_XMLWRITER
= "";
95 * Called when the sarissa.js file is parsed, to pick most recent
96 * ProgIDs for IE, then gets destroyed.
99 * @param idList an array of MSXML PROGIDs from which the most recent will be picked for a given object
100 * @param enabledList an array of arrays where each array has two items; the index of the PROGID for which a certain feature is enabled
102 Sarissa
.pickRecentProgID = function (idList
){
104 var bFound
= false, e
;
106 for(var i
=0; i
< idList
.length
&& !bFound
; i
++){
108 var oDoc
= new ActiveXObject(idList
[i
]);
111 }catch (objException
){
112 // trap; try next progID
117 throw "Could not retrieve a valid progID of Class: " + idList
[idList
.length
-1]+". (original exception: "+e
+")";
122 // pick best available MSXML progIDs
123 _SARISSA_DOM_PROGID
= null;
124 _SARISSA_THREADEDDOM_PROGID
= null;
125 _SARISSA_XSLTEMPLATE_PROGID
= null;
126 _SARISSA_XMLHTTP_PROGID
= null;
127 // commenting the condition out; we need to redefine XMLHttpRequest
128 // anyway as IE7 hardcodes it to MSXML3.0 causing version problems
129 // between different activex controls
130 //if(!window.XMLHttpRequest){
132 * Emulate XMLHttpRequest
135 XMLHttpRequest = function() {
136 if(!_SARISSA_XMLHTTP_PROGID
){
137 _SARISSA_XMLHTTP_PROGID
= Sarissa
.pickRecentProgID(["Msxml2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]);
139 return new ActiveXObject(_SARISSA_XMLHTTP_PROGID
);
142 // we dont need this anymore
143 //============================================
144 // Factory methods (IE)
145 //============================================
146 // see non-IE version
147 Sarissa
.getDomDocument = function(sUri
, sName
){
148 if(!_SARISSA_DOM_PROGID
){
150 _SARISSA_DOM_PROGID
= Sarissa
.pickRecentProgID(["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]);
152 _SARISSA_DOM_PROGID
= "noActiveX";
156 // Not sure how far IE can carry this but try to do something useful when ActiveX is disabled
157 var oDoc
= _SARISSA_DOM_PROGID
== "noActiveX" ? document
.createElement("xml") : new ActiveXObject(_SARISSA_DOM_PROGID
);
158 // set validation off, make sure older IEs dont choke (no time or IEs to test ;-)
160 oDoc
.validateOnParse
= false;
161 oDoc
.resolveExternals
= "false";
162 oDoc
.setProperty("ProhibitDTD", false);
165 // if a root tag name was provided, we need to load it in the DOM object
167 // create an artifical namespace prefix
168 // or reuse existing prefix if applicable
171 if(sName
.indexOf(":") > 1){
172 prefix
= sName
.substring(0, sName
.indexOf(":"));
173 sName
= sName
.substring(sName
.indexOf(":")+1);
175 prefix
= "a" + Sarissa
._getUniqueSuffix();
178 // use namespaces if a namespace URI exists
180 oDoc
.loadXML('<' + prefix
+':'+sName
+ " xmlns:" + prefix
+ "=\"" + sUri
+ "\"" + " />");
182 oDoc
.loadXML('<' + sName
+ " />");
187 // see non-IE version
188 Sarissa
.getParseErrorText = function (oDoc
) {
189 var parseErrorText
= Sarissa
.PARSED_OK
;
190 if(oDoc
&& oDoc
.parseError
&& oDoc
.parseError
.errorCode
&& oDoc
.parseError
.errorCode
!= 0){
191 parseErrorText
= "XML Parsing Error: " + oDoc
.parseError
.reason
+
192 "\nLocation: " + oDoc
.parseError
.url
+
193 "\nLine Number " + oDoc
.parseError
.line
+ ", Column " +
194 oDoc
.parseError
.linepos
+
195 ":\n" + oDoc
.parseError
.srcText
+
197 for(var i
= 0; i
< oDoc
.parseError
.linepos
;i
++){
198 parseErrorText
+= "-";
200 parseErrorText
+= "^\n";
202 else if(oDoc
.documentElement
=== null){
203 parseErrorText
= Sarissa
.PARSED_EMPTY
;
205 return parseErrorText
;
207 // see non-IE version
208 Sarissa
.setXpathNamespaces = function(oDoc
, sNsSet
) {
209 oDoc
.setProperty("SelectionLanguage", "XPath");
210 oDoc
.setProperty("SelectionNamespaces", sNsSet
);
213 * A class that reuses the same XSLT stylesheet for multiple transforms.
216 XSLTProcessor = function(){
217 if(!_SARISSA_XSLTEMPLATE_PROGID
){
218 _SARISSA_XSLTEMPLATE_PROGID
= Sarissa
.pickRecentProgID(["Msxml2.XSLTemplate.6.0", "MSXML2.XSLTemplate.3.0"]);
220 this.template
= new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID
);
221 this.processor
= null;
224 * Imports the given XSLT DOM and compiles it to a reusable transform
225 * <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
226 * @param {DOMDocument} xslDoc The XSLT DOMDocument to import
228 XSLTProcessor
.prototype.importStylesheet = function(xslDoc
){
229 if(!_SARISSA_THREADEDDOM_PROGID
){
230 _SARISSA_THREADEDDOM_PROGID
= Sarissa
.pickRecentProgID(["MSXML2.FreeThreadedDOMDocument.6.0", "MSXML2.FreeThreadedDOMDocument.3.0"]);
232 xslDoc
.setProperty("SelectionLanguage", "XPath");
233 xslDoc
.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
234 // convert stylesheet to free threaded
235 var converted
= new ActiveXObject(_SARISSA_THREADEDDOM_PROGID
);
236 // make included/imported stylesheets work if exist and xsl was originally loaded from url
238 converted
.resolveExternals
= true;
239 converted
.setProperty("AllowDocumentFunction", true);
240 converted
.setProperty("AllowXsltScript", true);
243 // Ignore. "AllowDocumentFunction" and "AllowXsltScript" is only supported in MSXML 3.0 SP4+ and 3.0 SP8+ respectively.
245 if(xslDoc
.url
&& xslDoc
.selectSingleNode("//xsl:*[local-name() = 'import' or local-name() = 'include']") != null){
246 converted
.async
= false;
247 converted
.load(xslDoc
.url
);
250 converted
.loadXML(xslDoc
.xml
);
252 converted
.setProperty("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
253 var output
= converted
.selectSingleNode("//xsl:output");
254 //this.outputMethod = output ? output.getAttribute("method") : "html";
256 this.outputMethod
= output
.getAttribute("method");
259 delete this.outputMethod
;
261 this.template
.stylesheet
= converted
;
262 this.processor
= this.template
.createProcessor();
263 // for getParameter and clearParameters
268 * Transform the given XML DOM and return the transformation result as a new DOM document
269 * @param {DOMDocument} sourceDoc The XML DOMDocument to transform
270 * @return {DOMDocument} The transformation result as a DOM Document
272 XSLTProcessor
.prototype.transformToDocument = function(sourceDoc
){
273 // fix for bug 1549749
275 if(_SARISSA_THREADEDDOM_PROGID
){
276 this.processor
.input
=sourceDoc
;
277 outDoc
=new ActiveXObject(_SARISSA_DOM_PROGID
);
278 this.processor
.output
=outDoc
;
279 this.processor
.transform();
283 if(!_SARISSA_DOM_XMLWRITER
){
284 _SARISSA_DOM_XMLWRITER
= Sarissa
.pickRecentProgID(["Msxml2.MXXMLWriter.6.0", "Msxml2.MXXMLWriter.3.0", "MSXML2.MXXMLWriter", "MSXML.MXXMLWriter", "Microsoft.XMLDOM"]);
286 this.processor
.input
= sourceDoc
;
287 outDoc
= new ActiveXObject(_SARISSA_DOM_XMLWRITER
);
288 this.processor
.output
= outDoc
;
289 this.processor
.transform();
290 var oDoc
= new ActiveXObject(_SARISSA_DOM_PROGID
);
291 oDoc
.loadXML(outDoc
.output
+"");
297 * Transform the given XML DOM and return the transformation result as a new DOM fragment.
298 * <b>Note</b>: The xsl:output method must match the nature of the owner document (XML/HTML).
299 * @param {DOMDocument} sourceDoc The XML DOMDocument to transform
300 * @param {DOMDocument} ownerDoc The owner of the result fragment
301 * @return {DOMDocument} The transformation result as a DOM Document
303 XSLTProcessor
.prototype.transformToFragment = function (sourceDoc
, ownerDoc
) {
304 this.processor
.input
= sourceDoc
;
305 this.processor
.transform();
306 var s
= this.processor
.output
;
307 var f
= ownerDoc
.createDocumentFragment();
309 if (this.outputMethod
== 'text') {
310 f
.appendChild(ownerDoc
.createTextNode(s
));
311 } else if (ownerDoc
.body
&& ownerDoc
.body
.innerHTML
) {
312 container
= ownerDoc
.createElement('div');
313 container
.innerHTML
= s
;
314 while (container
.hasChildNodes()) {
315 f
.appendChild(container
.firstChild
);
319 var oDoc
= new ActiveXObject(_SARISSA_DOM_PROGID
);
320 if (s
.substring(0, 5) == '<?xml') {
321 s
= s
.substring(s
.indexOf('?>') + 2);
323 var xml
= ''.concat('<my>', s
, '</my>');
325 container
= oDoc
.documentElement
;
326 while (container
.hasChildNodes()) {
327 f
.appendChild(container
.firstChild
);
334 * Set global XSLT parameter of the imported stylesheet. This method should
335 * only be used <strong>after</strong> the importStylesheet method for the
336 * context XSLTProcessor instance.
337 * @param {String} nsURI The parameter namespace URI
338 * @param {String} name The parameter base name
339 * @param {String} value The new parameter value
341 XSLTProcessor
.prototype.setParameter = function(nsURI
, name
, value
){
342 // make value a zero length string if null to allow clearing
343 value
= value
? value
: "";
344 // nsURI is optional but cannot be null
346 this.processor
.addParameter(name
, value
, nsURI
);
348 this.processor
.addParameter(name
, value
);
350 // update updated params for getParameter
351 nsURI
= "" + (nsURI
|| "");
352 if(!this.paramsSet
[nsURI
]){
353 this.paramsSet
[nsURI
] = [];
355 this.paramsSet
[nsURI
][name
] = value
;
358 * Gets a parameter if previously set by setParameter. Returns null
360 * @param {String} name The parameter base name
361 * @param {String} value The new parameter value
362 * @return {String} The parameter value if reviously set by setParameter, null otherwise
364 XSLTProcessor
.prototype.getParameter = function(nsURI
, name
){
365 nsURI
= "" + (nsURI
|| "");
366 if(this.paramsSet
[nsURI
] && this.paramsSet
[nsURI
][name
]){
367 return this.paramsSet
[nsURI
][name
];
374 * Clear parameters (set them to default values as defined in the stylesheet itself)
376 XSLTProcessor
.prototype.clearParameters = function(){
377 for(var nsURI
in this.paramsSet
){
378 for(var name
in this.paramsSet
[nsURI
]){
380 this.processor
.addParameter(name
, "", nsURI
);
382 this.processor
.addParameter(name
, "");
388 }else{ /* end IE initialization, try to deal with real browsers now ;-) */
389 if(Sarissa
._SARISSA_HAS_DOM_CREATE_DOCUMENT
){
391 * <p>Ensures the document was loaded correctly, otherwise sets the
392 * parseError to -1 to indicate something went wrong. Internal use</p>
395 Sarissa
.__handleLoad__ = function(oDoc
){
396 Sarissa
.__setReadyState__(oDoc
, 4);
399 * <p>Attached by an event handler to the load event. Internal use.</p>
402 _sarissa_XMLDocument_onload = function(){
403 Sarissa
.__handleLoad__(this);
406 * <p>Sets the readyState property of the given DOM Document object.
410 * @param oDoc the DOM Document object to fire the
411 * readystatechange event
412 * @param iReadyState the number to change the readystate property to
414 Sarissa
.__setReadyState__ = function(oDoc
, iReadyState
){
415 oDoc
.readyState
= iReadyState
;
416 oDoc
.readystate
= iReadyState
;
417 if (oDoc
.onreadystatechange
!= null && typeof oDoc
.onreadystatechange
== "function") {
418 oDoc
.onreadystatechange();
422 Sarissa
.getDomDocument = function(sUri
, sName
){
423 var oDoc
= document
.implementation
.createDocument(sUri
?sUri
:null, sName
?sName
:null, null);
424 if(!oDoc
.onreadystatechange
){
427 * <p>Emulate IE's onreadystatechange attribute</p>
429 oDoc
.onreadystatechange
= null;
431 if(!oDoc
.readyState
){
433 * <p>Emulates IE's readyState property, which always gives an integer from 0 to 4:</p>
434 * <ul><li>1 == LOADING,</li>
435 * <li>2 == LOADED,</li>
436 * <li>3 == INTERACTIVE,</li>
437 * <li>4 == COMPLETED</li></ul>
441 oDoc
.addEventListener("load", _sarissa_XMLDocument_onload
, false);
444 if(window
.XMLDocument
){
446 }// TODO: check if the new document has content before trying to copynodes, check for error handling in DOM 3 LS
447 else if(Sarissa
._SARISSA_HAS_DOM_FEATURE
&& window
.Document
&& !Document
.prototype.load
&& document
.implementation
.hasFeature('LS', '3.0')){
448 //Opera 9 may get the XPath branch which gives creates XMLDocument, therefore it doesn't reach here which is good
450 * <p>Factory method to obtain a new DOM Document object</p>
452 * @param {String} sUri the namespace of the root node (if any)
453 * @param {String} sUri the local name of the root node (if any)
454 * @returns {DOMDOcument} a new DOM Document
456 Sarissa
.getDomDocument = function(sUri
, sName
){
457 var oDoc
= document
.implementation
.createDocument(sUri
?sUri
:null, sName
?sName
:null, null);
462 Sarissa
.getDomDocument = function(sUri
, sName
){
463 var oDoc
= document
.implementation
.createDocument(sUri
?sUri
:null, sName
?sName
:null, null);
464 // looks like safari does not create the root element for some unknown reason
465 if(oDoc
&& (sUri
|| sName
) && !oDoc
.documentElement
){
466 oDoc
.appendChild(oDoc
.createElementNS(sUri
, sName
));
471 }//if(Sarissa._SARISSA_HAS_DOM_CREATE_DOCUMENT)
473 //==========================================
475 //==========================================
476 if(!window
.DOMParser
|| Sarissa
._SARISSA_IS_IE9
){
477 if(Sarissa
._SARISSA_IS_SAFARI
){
479 * DOMParser is a utility class, used to construct DOMDocuments from XML strings
482 DOMParser = function() { };
484 * Construct a new DOM Document from the given XMLstring
485 * @param {String} sXml the given XML string
486 * @param {String} contentType the content type of the document the given string represents (one of text/xml, application/xml, application/xhtml+xml).
487 * @return {DOMDocument} a new DOM Document from the given XML string
489 DOMParser
.prototype.parseFromString = function(sXml
, contentType
){
490 var xmlhttp
= new XMLHttpRequest();
491 xmlhttp
.open("GET", "data:text/xml;charset=utf-8," + encodeURIComponent(sXml
), false);
493 return xmlhttp
.responseXML
;
495 }else if(Sarissa
.getDomDocument
&& Sarissa
.getDomDocument() && Sarissa
.getDomDocument(null, "bar").xml
){
496 DOMParser = function() { };
497 DOMParser
.prototype.parseFromString = function(sXml
, contentType
){
498 var doc
= Sarissa
.getDomDocument();
500 doc
.validateOnParse
= false;
501 doc
.setProperty("ProhibitDTD", false);
509 if((typeof(document
.importNode
) == "undefined") && Sarissa
._SARISSA_IS_IE
){
512 * Implementation of importNode for the context window document in IE.
513 * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored.
514 * @param {DOMNode} oNode the Node to import
515 * @param {boolean} bChildren whether to include the children of oNode
516 * @returns the imported node for further use
518 document
.importNode = function(oNode
, bChildren
){
520 if (oNode
.nodeName
=='#text') {
521 return document
.createTextNode(oNode
.data
);
524 if(oNode
.nodeName
== "tbody" || oNode
.nodeName
== "tr"){
525 tmp
= document
.createElement("table");
527 else if(oNode
.nodeName
== "td"){
528 tmp
= document
.createElement("tr");
530 else if(oNode
.nodeName
== "option"){
531 tmp
= document
.createElement("select");
534 tmp
= document
.createElement("div");
537 tmp
.innerHTML
= oNode
.xml
? oNode
.xml
: oNode
.outerHTML
;
539 tmp
.innerHTML
= oNode
.xml
? oNode
.cloneNode(false).xml
: oNode
.cloneNode(false).outerHTML
;
541 return tmp
.getElementsByTagName("*")[0];
546 if(!Sarissa
.getParseErrorText
){
548 * <p>Returns a human readable description of the parsing error. Usefull
549 * for debugging. Tip: append the returned error string in a <pre>
550 * element if you want to render it.</p>
551 * <p>Many thanks to Christian Stocker for the initial patch.</p>
553 * @param {DOMDocument} oDoc The target DOM document
554 * @returns {String} The parsing error description of the target Document in
555 * human readable form (preformated text)
557 Sarissa
.getParseErrorText = function (oDoc
){
558 var parseErrorText
= Sarissa
.PARSED_OK
;
559 if((!oDoc
) || (!oDoc
.documentElement
)){
560 parseErrorText
= Sarissa
.PARSED_EMPTY
;
561 } else if(oDoc
.documentElement
.tagName
== "parsererror"){
562 parseErrorText
= oDoc
.documentElement
.firstChild
.data
;
563 parseErrorText
+= "\n" + oDoc
.documentElement
.firstChild
.nextSibling
.firstChild
.data
;
564 } else if(oDoc
.getElementsByTagName("parsererror").length
> 0){
565 var parsererror
= oDoc
.getElementsByTagName("parsererror")[0];
566 parseErrorText
= Sarissa
.getText(parsererror
, true)+"\n";
567 } else if(oDoc
.parseError
&& oDoc
.parseError
.errorCode
!= 0){
568 parseErrorText
= Sarissa
.PARSED_UNKNOWN_ERROR
;
570 return parseErrorText
;
574 * Get a string with the concatenated values of all string nodes under the given node
575 * @param {DOMNode} oNode the given DOM node
576 * @param {boolean} deep whether to recursively scan the children nodes of the given node for text as well. Default is <code>false</code>
579 Sarissa
.getText = function(oNode
, deep
){
581 var nodes
= oNode
.childNodes
;
582 // opera fix, finds no child text node for attributes so we use .value
583 if (oNode
.nodeType
== Node
.ATTRIBUTE_NODE
&& nodes
.length
== 0) {
587 for(var i
=0; i
< nodes
.length
; i
++){
589 var nodeType
= node
.nodeType
;
590 if(nodeType
== Node
.TEXT_NODE
|| nodeType
== Node
.CDATA_SECTION_NODE
){
592 } else if(deep
=== true && (nodeType
== Node
.ELEMENT_NODE
|| nodeType
== Node
.DOCUMENT_NODE
|| nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
)){
593 s
+= Sarissa
.getText(node
, true);
598 if(!window
.XMLSerializer
&& Sarissa
.getDomDocument
&& Sarissa
.getDomDocument("","foo", null).xml
){
600 * Utility class to serialize DOM Node objects to XML strings
603 XMLSerializer = function(){};
605 * Serialize the given DOM Node to an XML string
606 * @param {DOMNode} oNode the DOM Node to serialize
608 XMLSerializer
.prototype.serializeToString = function(oNode
) {
611 } else if (Sarissa
._SARISSA_IS_IE9
&& window
.XMLSerializer
) {
612 // We save old object for any futur use with IE Document (not xml)
613 IE9XMLSerializer
= XMLSerializer
;
614 XMLSerializer = function(){ this._oldSerializer
=new IE9XMLSerializer() };
615 XMLSerializer
.prototype.serializeToString = function(oNode
) {
616 if (typeof(oNode
)=='object' && 'xml' in oNode
) {
619 return this._oldSerializer
.serializeToString(oNode
);
625 * Strips tags from the given markup string. If the given string is
626 * <code>undefined</code>, <code>null</code> or empty, it is returned as is.
628 * @param {String} s the string to strip the tags from
630 Sarissa
.stripTags = function (s
) {
631 return s
?s
.replace(/<[^>]+>/g,""):s
;
634 * <p>Deletes all child nodes of the given node</p>
636 * @param {DOMNode} oNode the Node to empty
638 Sarissa
.clearChildNodes = function(oNode
) {
639 // need to check for firstChild due to opera 8 bug with hasChildNodes
640 while(oNode
.firstChild
) {
641 oNode
.removeChild(oNode
.firstChild
);
645 * <p> Copies the childNodes of nodeFrom to nodeTo</p>
646 * <p> <b>Note:</b> The second object's original content is deleted before
647 * the copy operation, unless you supply a true third parameter</p>
649 * @param {DOMNode} nodeFrom the Node to copy the childNodes from
650 * @param {DOMNode} nodeTo the Node to copy the childNodes to
651 * @param {boolean} bPreserveExisting whether to preserve the original content of nodeTo, default is false
653 Sarissa
.copyChildNodes = function(nodeFrom
, nodeTo
, bPreserveExisting
) {
654 if(Sarissa
._SARISSA_IS_SAFARI
&& nodeTo
.nodeType
== Node
.DOCUMENT_NODE
){ // SAFARI_OLD ??
655 nodeTo
= nodeTo
.documentElement
; //Apparently there's a bug in safari where you can't appendChild to a document node
658 if((!nodeFrom
) || (!nodeTo
)){
659 throw "Both source and destination nodes must be provided";
661 if(!bPreserveExisting
){
662 Sarissa
.clearChildNodes(nodeTo
);
664 var ownerDoc
= nodeTo
.nodeType
== Node
.DOCUMENT_NODE
? nodeTo
: nodeTo
.ownerDocument
;
665 var nodes
= nodeFrom
.childNodes
;
667 if(typeof(ownerDoc
.importNode
) != "undefined") {
668 for(i
=0;i
< nodes
.length
;i
++) {
669 nodeTo
.appendChild(ownerDoc
.importNode(nodes
[i
], true));
672 for(i
=0;i
< nodes
.length
;i
++) {
673 nodeTo
.appendChild(nodes
[i
].cloneNode(true));
679 * <p> Moves the childNodes of nodeFrom to nodeTo</p>
680 * <p> <b>Note:</b> The second object's original content is deleted before
681 * the move operation, unless you supply a true third parameter</p>
683 * @param {DOMNode} nodeFrom the Node to copy the childNodes from
684 * @param {DOMNode} nodeTo the Node to copy the childNodes to
685 * @param {boolean} bPreserveExisting whether to preserve the original content of nodeTo, default is
687 Sarissa
.moveChildNodes = function(nodeFrom
, nodeTo
, bPreserveExisting
) {
688 if((!nodeFrom
) || (!nodeTo
)){
689 throw "Both source and destination nodes must be provided";
691 if(!bPreserveExisting
){
692 Sarissa
.clearChildNodes(nodeTo
);
694 var nodes
= nodeFrom
.childNodes
;
695 // if within the same doc, just move, else copy and delete
696 if(nodeFrom
.ownerDocument
== nodeTo
.ownerDocument
){
697 while(nodeFrom
.firstChild
){
698 nodeTo
.appendChild(nodeFrom
.firstChild
);
701 var ownerDoc
= nodeTo
.nodeType
== Node
.DOCUMENT_NODE
? nodeTo
: nodeTo
.ownerDocument
;
703 if(typeof(ownerDoc
.importNode
) != "undefined") {
704 for(i
=0;i
< nodes
.length
;i
++) {
705 nodeTo
.appendChild(ownerDoc
.importNode(nodes
[i
], true));
708 for(i
=0;i
< nodes
.length
;i
++) {
709 nodeTo
.appendChild(nodes
[i
].cloneNode(true));
712 Sarissa
.clearChildNodes(nodeFrom
);
717 * <p>Serialize any <strong>non</strong> DOM object to an XML string. All properties are serialized using the property name
718 * as the XML element name. Array elements are rendered as <code>array-item</code> elements,
719 * using their index/key as the value of the <code>key</code> attribute.</p>
721 * @param {Object} anyObject the object to serialize
722 * @param {String} objectName a name for that object, to be used as the root element name
723 * @param {String} indentSpace Optional, the indentation space to use, default is an empty
724 * string. A single space character is added in any recursive call.
725 * @param {noolean} skipEscape Optional, whether to skip escaping characters that map to the
726 * five predefined XML entities. Default is <code>false</code>.
727 * @return {String} the XML serialization of the given object as a string
729 Sarissa
.xmlize = function(anyObject
, objectName
, indentSpace
, skipEscape
){
730 indentSpace
= indentSpace
?indentSpace
:'';
731 var s
= indentSpace
+ '<' + objectName
+ '>';
733 if(!(anyObject
instanceof Object
) || anyObject
instanceof Number
|| anyObject
instanceof String
|| anyObject
instanceof Boolean
|| anyObject
instanceof Date
){
734 s
+= (skipEscape
? Sarissa
.escape(anyObject
) : anyObject
);
738 var isArrayItem
= anyObject
instanceof Array
;
739 for(var name
in anyObject
){
740 // do not xmlize functions
741 if (anyObject
[name
] instanceof Function
){
744 s
+= Sarissa
.xmlize(anyObject
[name
], (isArrayItem
?"array-item key=\""+name
+"\"":name
), indentSpace
+ " ");
748 return (s
+= (objectName
.indexOf(' ')!=-1?"</array-item>\n":"</" + objectName
+ ">\n"));
752 * Escape the given string chacters that correspond to the five predefined XML entities
754 * @param {String} sXml the string to escape
756 Sarissa
.escape = function(sXml
){
757 return sXml
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, ""
;").replace(/'/g, "&apos
;");
761 * Unescape the given string. This turns the occurences of the predefined XML
762 * entities to become the characters they represent correspond to the five predefined XML entities
764 * @param {String}sXml the string to unescape
766 Sarissa.unescape = function(sXml){
767 return sXml.replace(/'/g,"'").replace(/"/g,"\"").replace(/>/g,">").replace(/</g,"<").replace(/&/g,"&");
771 Sarissa.updateCursor = function(oTargetElement, sValue) {
772 if(oTargetElement && oTargetElement.style && oTargetElement.style.cursor != undefined ){
773 oTargetElement.style.cursor = sValue;
778 * Asynchronously update an element with response of a GET request on the given URL. Passing a configured XSLT
779 * processor will result in transforming and updating oNode before using it to update oTargetElement.
780 * You can also pass a callback function to be executed when the update is finished. The function will be called as
781 * <code>functionName(oNode, oTargetElement);</code>
783 * @param {String} sFromUrl the URL to make the request to
784 * @param {DOMElement} oTargetElement the element to update
785 * @param {XSLTProcessor} xsltproc (optional) the transformer to use on the returned
786 * content before updating the target element with it
787 * @param {Function} callback (optional) a Function object to execute once the update is finished successfuly, called as <code>callback(sFromUrl, oTargetElement)</code>.
788 * In case an exception is thrown during execution, the callback is called as called as <code>callback(sFromUrl, oTargetElement, oException)</code>
789 * @param {boolean} skipCache (optional) whether to skip any cache
791 Sarissa.updateContentFromURI = function(sFromUrl, oTargetElement, xsltproc, callback, skipCache) {
793 Sarissa.updateCursor(oTargetElement, "wait");
794 var xmlhttp = new XMLHttpRequest();
795 xmlhttp.open("GET", sFromUrl, true);
796 xmlhttp.onreadystatechange = function() {
797 if (xmlhttp.readyState == 4) {
799 var oDomDoc = xmlhttp.responseXML;
800 if(oDomDoc && Sarissa.getParseErrorText(oDomDoc) == Sarissa.PARSED_OK){
801 Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc);
803 callback(sFromUrl, oTargetElement);
807 throw Sarissa.getParseErrorText(oDomDoc);
812 callback(sFromUrl, oTargetElement, e);
821 var oldage = "Sat, 1 Jan 2000 00:00:00 GMT";
822 xmlhttp.setRequestHeader("If-Modified-Since", oldage);
827 Sarissa.updateCursor(oTargetElement, "auto");
829 callback(sFromUrl, oTargetElement, e);
838 * Update an element's content
with the given DOM node
. Passing a configured XSLT
839 * processor will result
in transforming and updating oNode before using it to update oTargetElement
.
840 * You can also pass a callback
function to be executed when the update is finished
. The
function will be called as
841 * <code
>functionName(oNode
, oTargetElement
);</code
>
843 * @param
{DOMNode
} oNode the URL to make the request to
844 * @param
{DOMElement
} oTargetElement the element to update
845 * @param
{XSLTProcessor
} xsltproc (optional
) the transformer to
use on the given
846 * DOM node before updating the target element
with it
848 Sarissa
.updateContentFromNode = function(oNode
, oTargetElement
, xsltproc
) {
850 Sarissa
.updateCursor(oTargetElement
, "wait");
851 Sarissa
.clearChildNodes(oTargetElement
);
852 // check for parsing errors
853 var ownerDoc
= oNode
.nodeType
== Node
.DOCUMENT_NODE
?oNode
:oNode
.ownerDocument
;
854 if(ownerDoc
.parseError
&& ownerDoc
.parseError
.errorCode
!= 0) {
855 var pre
= document
.createElement("pre");
856 pre
.appendChild(document
.createTextNode(Sarissa
.getParseErrorText(ownerDoc
)));
857 oTargetElement
.appendChild(pre
);
860 // transform if appropriate
862 oNode
= xsltproc
.transformToDocument(oNode
);
864 // be smart, maybe the user wants to display the source instead
865 if(oTargetElement
.tagName
.toLowerCase() == "textarea" || oTargetElement
.tagName
.toLowerCase() == "input") {
866 oTargetElement
.value
= new XMLSerializer().serializeToString(oNode
);
869 // ok that was not smart; it was paranoid. Keep up the good work by trying to use DOM instead of innerHTML
871 oTargetElement
.appendChild(oTargetElement
.ownerDocument
.importNode(oNode
, true));
874 oTargetElement
.innerHTML
= new XMLSerializer().serializeToString(oNode
);
883 Sarissa
.updateCursor(oTargetElement
, "auto");
889 * Creates an HTTP URL query string from the given HTML form data
891 * @param {HTMLFormElement} oForm the form to construct the query string from
893 Sarissa
.formToQueryString = function(oForm
){
895 for(var i
= 0;i
< oForm
.elements
.length
;i
++) {
896 var oField
= oForm
.elements
[i
];
897 var sFieldName
= oField
.getAttribute("name") ? oField
.getAttribute("name") : oField
.getAttribute("id");
898 // ensure we got a proper name/id and that the field is not disabled
900 ((!oField
.disabled
) || oField
.type
== "hidden")) {
901 switch(oField
.type
) {
906 qs
+= sFieldName
+ "=" + encodeURIComponent(oField
.value
) + "&";
909 qs
+= sFieldName
+ "=" + encodeURIComponent(oField
.options
[oField
.selectedIndex
].value
) + "&";
911 case "select-multiple":
912 for (var j
= 0; j
< oField
.length
; j
++) {
913 var optElem
= oField
.options
[j
];
914 if (optElem
.selected
=== true) {
915 qs
+= sFieldName
+ "[]" + "=" + encodeURIComponent(optElem
.value
) + "&";
922 qs
+= sFieldName
+ "=" + encodeURIComponent(oField
.value
) + "&";
928 // return after removing last '&'
929 return qs
.substr(0, qs
.length
- 1);
934 * Asynchronously update an element with response of an XMLHttpRequest-based emulation of a form submission. <p>The form <code>action</code> and
935 * <code>method</code> attributess will be followed. Passing a configured XSLT processor will result in
936 * transforming and updating the server response before using it to update the target element.
937 * You can also pass a callback function to be executed when the update is finished. The function will be called as
938 * <code>functionName(oNode, oTargetElement);</code></p>
939 * <p>Here is an example of using this in a form element:</p>
940 * <pre name="code" class="xml">
941 * <div id="targetId"> this content will be updated</div>
942 * <form action="/my/form/handler" method="post"
943 * onbeforesubmit="return Sarissa.updateContentFromForm(this, document.getElementById('targetId'));"><pre>
944 * <p>If JavaScript is supported, the form will not be submitted. Instead, Sarissa will
945 * scan the form and make an appropriate AJAX request, also adding a parameter
946 * to signal to the server that this is an AJAX call. The parameter is
947 * constructed as <code>Sarissa.REMOTE_CALL_FLAG = "=true"</code> so you can change the name in your webpage
948 * simply by assigning another value to Sarissa.REMOTE_CALL_FLAG. If JavaScript is not supported
949 * the form will be submitted normally.
951 * @param {HTMLFormElement} oForm the form submition to emulate
952 * @param {DOMElement} oTargetElement the element to update
953 * @param {XSLTProcessor} xsltproc (optional) the transformer to use on the returned
954 * content before updating the target element with it
955 * @param {Function} callback (optional) a Function object to execute once the update is finished successfuly, called as <code>callback(oNode, oTargetElement)</code>.
956 * In case an exception occurs during excecution and a callback function was provided, the exception is cought and the callback is called as
957 * <code>callback(oForm, oTargetElement, exception)</code>
959 Sarissa
.updateContentFromForm = function(oForm
, oTargetElement
, xsltproc
, callback
) {
961 Sarissa
.updateCursor(oTargetElement
, "wait");
962 // build parameters from form fields
963 var params
= Sarissa
.formToQueryString(oForm
) + "&" + Sarissa
.REMOTE_CALL_FLAG
+ "=true";
964 var xmlhttp
= new XMLHttpRequest();
965 var bUseGet
= oForm
.getAttribute("method") && oForm
.getAttribute("method").toLowerCase() == "get";
967 xmlhttp
.open("GET", oForm
.getAttribute("action")+"?"+params
, true);
970 xmlhttp
.open('POST', oForm
.getAttribute("action"), true);
971 xmlhttp
.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
972 xmlhttp
.setRequestHeader("Content-length", params
.length
);
973 xmlhttp
.setRequestHeader("Connection", "close");
975 xmlhttp
.onreadystatechange = function() {
977 if (xmlhttp
.readyState
== 4) {
978 var oDomDoc
= xmlhttp
.responseXML
;
979 if(oDomDoc
&& Sarissa
.getParseErrorText(oDomDoc
) == Sarissa
.PARSED_OK
){
980 Sarissa
.updateContentFromNode(xmlhttp
.responseXML
, oTargetElement
, xsltproc
);
982 callback(oForm
, oTargetElement
);
986 throw Sarissa
.getParseErrorText(oDomDoc
);
992 callback(oForm
, oTargetElement
, e
);
999 xmlhttp
.send(bUseGet
?"":params
);
1002 Sarissa
.updateCursor(oTargetElement
, "auto");
1004 callback(oForm
, oTargetElement
, e
);
1014 * Get the name of a function created like:
1015 * <pre>function functionName(){}</pre>
1016 * If a name is not found, attach the function to
1017 * the window object with a new name and return that
1018 * @param {Function} oFunc the function object
1020 Sarissa
.getFunctionName = function(oFunc
){
1021 if(!oFunc
|| (typeof oFunc
!= 'function' )){
1022 throw "The value of parameter 'oFunc' must be a function";
1027 // try to parse the function name from the defintion
1028 var sFunc
= oFunc
.toString();
1029 alert("sFunc: "+sFunc
);
1030 var name
= sFunc
.substring(sFunc
.indexOf('function') + 8 , sFunc
.indexOf('('));
1031 if(!name
|| name
.length
== 0 || name
== " "){
1032 // attach to window object under a new name
1033 name
= "SarissaAnonymous" + Sarissa
._getUniqueSuffix();
1034 window
[name
] = oFunc
;
1042 Sarissa
.setRemoteJsonCallback = function(url
, callback
, callbackParam
) {
1044 callbackParam
= "callback";
1046 var callbackFunctionName
= Sarissa
.getFunctionName(callback
);
1047 //alert("callbackFunctionName: '" + callbackFunctionName+"', length: "+callbackFunctionName.length);
1048 var id
= "sarissa_json_script_id_" + Sarissa
._getUniqueSuffix();
1049 var oHead
= document
.getElementsByTagName("head")[0];
1050 var scriptTag
= document
.createElement('script');
1051 scriptTag
.type
= 'text/javascript';
1053 scriptTag
.onload = function(){
1055 // document.removeChild(scriptTag);
1057 if(url
.indexOf("?") != -1){
1058 url
+= ("&" + callbackParam
+ "=" + callbackFunctionName
);
1061 url
+= ("?" + callbackParam
+ "=" + callbackFunctionName
);
1063 scriptTag
.src
= url
;
1064 oHead
.appendChild(scriptTag
);