<@script name="Xml"> Contains macros for handling XML objects (c) SAP AG 2003-2006. All rights reserved. #INCLUDE[dev:defs.inc] var Msxml2XMLHTTP = null; ////////////////////////////////////////////////////////////////////////////// // XML FUNCTIONS <@func name="PARSEXML" also="CHECKXML GETXML" group="Functions" > Parses a string containing an XML document and returns the results in a new XML object The XML string; if omitted, an empty XML object is created The parsed XML object. Use @CHECKXML to check for errors. function PARSEXML(str) { try { if (!Msxml2XMLHTTP) getMsxml2Version(); var xml = new ActiveXObject('Msxml2.DOMDocument.'+Msxml2XMLHTTP+'.0'); } catch (e) { if ( e.number == -2146827859 ) { this.error = "Microsoft XML parser Version 4.0 is not installed."; } return null; } if (str) xml.loadXML(str); return xml; } <@func name="CHECKXML" also="PARSEXML GETXML"> Checks an XML object for parsing errors The XML object Title for error prompts; if omitted, no prompts will be displayed (silent mode) The error description, or an empty string if %0 is valid function CHECKXML(xml, prompt) { var err=''; try { if (!xml) { if (this.error) err = this.error+''; else throw -1; } else if (xml.parseError.errorCode != 0) { err = xml.parseError.reason.replace(/[\.\n\r]/g,'') + ', line '+xml.parseError.line; } else if (xml.documentElement.nodeName == 'ERROR') { err = xml.documentElement.text; } } catch(e) { err = 'Bad or missing XML object'; } if (err && prompt) { if (typeof prompt == 'string') err = prompt+' ('+err+')'; if (typeof PROMPT != 'undefined') PROMPT(err, {icon:'ERROR'}); else alert(err); } return err; } //only accept MSXML.XMLHTTP.4.0 and MSXML.XMLHTTP.6.0 to ensure support of XML/HTTP support, both synchronous and asynchronous + full XPATH and XSLT support. function getMsxml2Version() { if (Msxml2XMLHTTP) return new ActiveXObject("Msxml2.XMLHTTP."+Msxml2XMLHTTP+".0"); try{ Msxml2XMLHTTP=6; return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }catch (e){} try{ Msxml2XMLHTTP=4; return new ActiveXObject("Msxml2.XMLHTTP.4.0"); }catch (e){} return e; } <@func name="GETXML" also="PARSEXML"> Gets an XML object from a specified url (using HTTP/GET) The url containing the XML document Callback function to invoke upon successfull completion of the operation A parameters object to pass to the callback function Callback function to invoke when an error is encountered The loaded XML object if the operation is synchronous; otherwise, ~null If the ~onsuccess callback is omitted, the operation is synchronous. In this case, the function will wait unitl the load operation is completed. The loaded XML object will be returned as the function return value. If the ~onsuccess callback is provided, the operation is asynchronous. In this case, the function will return immediately, and the load operation will be spawned in a separate thread. Once the load operation is completed, either the ~onsuccess or ~onerror callback will be invoked depending on the operation success. In either case, the loaded XML object and the ~params object will be passed to the callback function. Use @CHECKXML to check the returned XML object for errors. function GETXML(url, onsuccess, params, onerror) { try { var xml = getMsxml2Version(); if (xml.name=="Error"){ if (this.error) this.error = '#TEXT[XMSG_UNABLE_TO_INVOKE_MSXML]'; if (typeof onerror == 'function') onerror(xml, '#TEXT[XMSG_UNABLE_TO_INVOKE_MSXML]', params); return null; } var wait=(typeof onsuccess != 'function'); xml.open("GET",url,!wait); if (wait) { xml.send(); return xml.responseXML; } xml.onreadystatechange = waitForResults; xml.send(); return xml; } catch (e) { if (this.error) this.error = 'GETXML. Exception in MSXML'; if (typeof onerror == 'function') onerror(xml, 'GETXML. Exception in MSXML', params); return null; } function waitForResults() { if (xml.readyState == 4) processResults(xml, null, wait, onsuccess, onerror, params); } } <@func name="POSTXML" also="PARSEXML"> Posts an XML object to a specified url (using XML/HTTP) The XML object to save The target url for the save operation Callback function to invoke upon successfull completion of the operation A parameters object to pass to the callback function Callback function to invoke when an error is encountered An optional table of headers to set in the request An optional object for receiving the raw XML/HTTP response (synchronous mode only) The save results if the operation is synchronous; otherwise, ~null If the ~onsuccess callback is omitted, the operation is synchronous. In this case, the function will wait until the save operation is completed. The save results will be returned as the function return value. If the ~onsuccess callback is provided, the operation is asynchronous. In this case, the function will return immediately, and the save operation will be spawned in a separate thread. Once the save operation is completed, either the ~onsuccess or ~onerror callback will be invoked depending on the operation success. In either case, the save results and the ~params object will be passed to the callback function. function POSTXML(xml, url, onsuccess, params, onerror, headers, response) { var wait=(typeof onsuccess != 'function'); var xmlhttp=getMsxml2Version(); if (xmlhttp.name=="Error"){ if (typeof onerror == 'function'){ onerror(xml, '#TEXT[XMSG_UNABLE_TO_INVOKE_MSXML]', params); return null; } else throw new Error(#[ERR_CREATE_XML_OBJ], '#TEXT[XMSG_UNABLE_TO_INVOKE_MSXML]: ' + xmlhttp.description); } try { xmlhttp.Open("POST", url, !wait); if (!wait) xmlhttp.onreadystatechange = waitForResults; if (headers && typeof headers == 'object') { for (var k in headers) { xmlhttp.setRequestHeader(k, headers[k]); } } xmlhttp.Send(xml); return wait ? processResults(xmlhttp, response, wait, onsuccess, onerror, params) : xmlhttp; } catch (e) { if (typeof onerror == 'function') { onerror(xml, '#TEXT[XMSG_BAD_OR_MISSING_URL] ('+url+')', params); return null; } else throw new Error(#[ERR_CREATE_XML_OBJ], '#TEXT[XMSG_BAD_OR_MISSING_URL] ('+url+'). ' + e.description); } function waitForResults() { if (xmlhttp.readyState == 4) processResults(xmlhttp, response, wait, onsuccess, onerror, params); } } <@func name="POSTXML" also="PARSEXML"> Posts an XML object to a specified url (using XML/HTTP) HTTP object xml returned from server synchronic call for server (wait with execution until the response from server) call this method with the response xml call this method in case of error params for the onsucess/onerror methods function processResults(xmlhttp, response, wait, onsuccess, onerror, params) { try { if (wait && response != null && response != undefined) { response.status = xmlhttp.status; response.text = xmlhttp.responseText; } if (xmlhttp.status != 200) throw new Error(xmlhttp.status, xmlhttp.statusText); var res = xmlhttp.responseXML; if (!res || !res.xml) res = PARSEXML(xmlhttp.responseText); if (res.parseError.errorCode != 0) throw new Error(res.parseError.errorCode, res.parseError.reason.replace(/[\n\r]/g,'')); var failure = res.selectSingleNode('/ERROR'); if (failure) throw new Error(failure.getAttribute('code'), failure.text); if (wait){ return res; } else { onsuccess(res, params); if(xmlhttp && ('onreadystatechange' in xmlhttp)){ xmlhttp.onreadystatechange = {}; xmlhttp = null; } } } catch (e) { if (typeof onerror == 'function') onerror(res, e.description, params, xmlhttp); return res; } } <@func name="JS2XML" also="XML2JS"> Converts a JavaScript object of arbitrary complexity into a corresponding XML string fragment The JavaScript object to convert The XML string fragment representing the given JavaScript object Sample A sample JavaScript object ~obj:
var obj = {a:'aaa', b:'bbb', c:123, d:true, e:[{f:'fff', g:100}, true, [10, 20, 30]]}
The corresponding XML fragement JS2XML(~obj):
<struct>
   <string name="a">aaa</string>
   <string name="b">bbb</string>
   <number name="c">123</number>
   <boolean name="d">true</boolean>
   <array name="e">
      <struct>
         <string name="f">fff</string>
         <number name="g">100</number>
      </struct>
      <boolean>true</boolean>
      <array>
         <number>10</number>
         <number>20</number>
         <number>30</number>
      </array>
   </array>
</struct>
function JS2XML(obj,xmlns) { var name = (arguments.length == 3) ? ' name="'+ESCAPEXML(arguments[2])+'"' : ''; var pref = (xmlns ? '<'+xmlns+':' : '<'); var suff = (xmlns ? '' + ESCAPEXML(typeObj[1]) + suff+'date>'; else if (type == '#Time' ) return pref+'time'+name+'>' + ESCAPEXML(typeObj[1]) + suff+'time>'; else if (type == '#DateTime' ) return pref+'datetime'+name+'>' + ESCAPEXML(typeObj[1]) + suff+'datetime>'; return pref+'string'+name+'>'+ESCAPEXML(obj)+suff+'string>'; } else { // Removes spaces from the string var trimmed = TRIM(obj); // Checks if the string contains spaces if (trimmed.length != obj.length) { return pref+'string'+name+'>'+ '' +suff+'string>'; } else { return pref+'string'+name+'>'+ESCAPEXML(obj)+suff+'string>'; } } } case 'number': return pref+'number'+name+'>'+obj+suff+'number>'; case 'boolean': return pref+'boolean'+name+'>'+obj+suff+'boolean>'; case 'object': if (obj === null || obj === undefined) return ''; var buf=[]; if (ISARRAY(obj)) { buf.push(pref+'array'+name+'>'); for (var i=0, len=obj.length; i'); } else { buf.push(pref+'struct'+name+'>'); for (var k in obj) { buf.push(JS2XML(obj[k], xmlns, k)); } buf.push(suff+'struct>'); } return buf.join(''); default: return ''; } } <@func name="XML2JS" also="JS2XML"> Converts an XML fragment into a native JavaScript object An XML fragment The JavaScript object represented by the given XML fragment Sample A sample XML fragement ~node:
<struct>
   <string name="a">aaa</string>
   <string name="b">bbb</string>
   <number name="c">123</number>
   <boolean name="d">true</boolean>
   <array name="e">
      <struct>
         <string name="f">fff</string>
         <number name="g">100</number>
      </struct>
      <boolean>true</boolean>
      <array>
         <number>10</number>
         <number>20</number>
         <number>30</number>
      </array>
   </array>
</struct>
The corresponding JavaScript object XML2JS(~node):
XML2JS(node) => {a:'aaa', b:'bbb', c:123, d:true, e:[{f:'fff', g:100}, true, [10, 20, 30]]}
function XML2JS(node) { switch (node.baseName) { case 'string': return node.text; case 'number': return FLOAT(node.text); case 'boolean': return BOOL(node.text); case 'date': return ('#Date@' + node.text); case 'time': return ('#Time@' + node.text); case 'datetime': return ('#DateTime@' + node.text); case 'array': var A=[], C=node.childNodes; for (var i=0, len=C.length; i Returns an encoded form of the given string that can be used in XML documents The string to encode The endcoded XML string function ESCAPEXML(str) { if (str === undefined || str === null) return ''; return (str+'').replace(/([\"\&\<\>])/g, ESCAPEXML.rep); } ESCAPEXML.map = {'"':'"', '&':'&', '<':'<', '>':'>'} ESCAPEXML.rep = function(a,b) { return ESCAPEXML.map[b]||b; } <@func name="UNESCAPEXML" also="ESCAPEXML"> Returns the decoded form of a string encoded with the @ESCAPEXML. The XML string to decode The decoded string function UNESCAPEXML(str) { if (str === undefined || str === null) return ''; return (str+'').replace(/(\"\;|\&\;|\<\;|\>\;)/g, UNESCAPEXML.rep); } UNESCAPEXML.map = {'"':'"', '&':'&', '<':'<', '>':'>'} UNESCAPEXML.rep = function(a,b) { return UNESCAPEXML.map[b]||b; } <@func name="XMLNODES" also="XMLNODE"> Returns the list of nodes that match a specified XPATH query The query context (can be either an XML node or XML document object) An XPATH query (relative to %0) that defines the requested node The requested nodes list function XMLNODES(node, path) { try { return node.selectNodes(path); } catch (e) { return null; } }