// Global NS variables var AIURI='http://www.asoft.ru/xsl/ai'; var AINS={'ai': AIURI}; var STURI='http://www.asoft.ru/xml/st'; var STNS={'st': STURI}; /** * Cross-browser XML DOM parser * Requires: ajax-request.js **/ var AXML = Class.create({ /** * Creates XML document (empty by default) * @param sRoot if present, root element with such name will be created automatically **/ initialize: function(sRoot, sNS) { if (!sRoot) sRoot = ""; if (!sNS) sNS = ""; var sText = ""; if (sRoot) { // Look for a namespace prefix var sPrefix = ""; var sTagName = sRoot; var p = sRoot.indexOf(':'); if (p != -1) { sPrefix = sRoot.substring(0, p); sTagName = sRoot.substring(p+1); } // If we have a namespace, we must have a namespace prefix // If we don't have a namespace, we discard any prefix if (sNS) { if (!sPrefix) sPrefix = "a0"; // What Firefox uses } else sPrefix = ""; // Create the root element (with optional namespace) as a // string of text sText = '<' + (sPrefix?(sPrefix+':'):'') + sTagName + (sNS ? (' xmlns:'+sPrefix+'="'+sNS +'"'):'')+'/>'; // And parse that text into the empty document } if (AXML.bUseActiveX_) { // IE way this.oXML_ = new ActiveXObject(AXML.DOMVersion); if (sText) this.Parse(sText); } else if (AXML.bUseDOM_) { // Mozilla way to do things if (sText) this.Parse(sText); else this.oXML_ = document.implementation.createDocument(sNS, sRoot, null); } }, /** * Loads XML document located by passed URL * @param sURL URL containing XML content * @param fCallback callback function called when loading is done * @param oThis object that will be bound as this to fCallback **/ Load: function(sURL, fCallback, oThis) { // TODO: if possible, use Mozilla implementation of .prototype.load if (!oThis) oThis = null; var bAsync = true; if (!fCallback) bAsync = false; else this.fLoadingCallback_ = fCallback.bind(oThis); new AjaxRequest("get", sURL, "", { onSuccess: this.loadingCallback.bind(this) }, bAsync); }, /** * Internal callback that is called when XML is loaded * @param oAjaxRequest Ajax request handler carrying data **/ loadingCallback: function(oAjaxRequest) { // Callback is called with AXML object in single argument this.oXML_ = oAjaxRequest.XML(); // get response XML if (oAjaxRequest.IsAsync()) this.fLoadingCallback_(this); }, /** * Parses text content * @param sContent string containing XML text * @return false if parsing failed, true otherwise **/ Parse: function(sContent) { if (!sContent) sContent = ""; if (this.oXML_ && typeof(this.oXML_.loadXML) != "undefined") { this.oXML_.loadXML(sContent); } else if (window.DOMParser) { // This browser appears to support DOMParser var oParser = new DOMParser(); this.oXML_ = oParser.parseFromString(sContent, "text/xml"); } else { throw new Error("XML document not initialized or parsing not supported! "); } }, /** * Sets XML document to given object * @param oXMLDom DOM document (please note that no copying is performed!) **/ SetXML: function(oXMLDom) { this.oXML_ = oXMLDom; }, /** * Serializes XML document to string * @return serialized XML content (string) **/ Serialize: function() { return this.oXML_.xml; }, /** * Returns reference to internal XML document * @return XML document (formally, browser-dependent, but differences are not necessary) **/ XML: function() { return this.oXML_; }, /** * Returns XML transformed by XSLT template (as string) * @return transformed content (text) **/ Transform: function(oXSLTDoc) { return AXML.TransformDOM(this.oXML_, oXSLTDoc.XML(), false); } }); AXML.bUseDOM_ = (document.implementation && document.implementation.createDocument ? true : false); AXML.bUseActiveX_ = (typeof ActiveXObject != "undefined") || ("ActiveXObject" in window); if (AXML.bUseActiveX_) { AXML.DOMVersion = (function () { var aDOMVersions = [ "Microsoft.XMLDOM", "MSXML2.DOMDocument.6.0", "MSXML2.DOMDocument.3.0", "Microsoft.XMLDOM"]; for (var i = 0; i < aDOMVersions.length; i++) { try { if (new ActiveXObject(aDOMVersions[i])) { return aDOMVersions[i]; } } catch(e) { } } throw new Error("AXML::detectDOMVersion(): Cannot acquire DOM object! "); })(); } AXML.typeDocs = {}; AXML.typeSets = {}; AXML.getTypeXsl = function() { if (AXML.typeXsl) return AXML.typeXsl; var t = new AXSLTemplate(document.location.pathname.replace(/[^\/]+$/, "") + "/xslt/types.xsl"); t.Compile(); return AXML.typeXsl = t; }; AXML.getTypeDoc = function(nm) { if (AXML.typeDocs[nm]) return AXML.typeDocs[nm]; var doc=new AXML(); doc.Load(document.location.pathname.replace(/[^\/]+$/, "")+"/types/"+nm+".xml"); return AXML.typeDocs[nm]=doc; }; AXML.getTypeSet = function(typeList) { typeList = typeList.sort(); var hashKey = typeList.join(":"); if (AXML.typeSets[hashKey]) return AXML.typeSets[hashKey]; var tset = new AXML("st:typeset", STURI); var r = tset.oXML_.documentElement; for (var i = 0; i < typeList.length; ++i) { var tdoc = AXML.getTypeDoc(typeList[i]); if (!tdoc || !tdoc.oXML_ || !tdoc.oXML_.documentElement) { LogE("Cannot load types from " + typeList[i]); continue; } var types = AXML.SelectNodes(tdoc.oXML_.documentElement, "//*[local-name(.) = 'types']"); for (var t = 0; t < types.length; ++t) { var c = ImportNode(tset.oXML_, types[t], true); c.setAttribute("from", typeList[i]); r.appendChild(c); } } var xsl = AXML.getTypeXsl(); var xml = AXML.TransformDOM(tset.oXML_, xsl, false); var ret = new AXML(); ret.Parse(xml) AXML.typeSets[hashKey] = ret return AXML.typeSets[hashKey]; }; AXML.EmbedPDLTypes = function(oXMLDom) { var rootElement = (oXMLDom.documentElement || oXMLDom); oXMLDom = (rootElement.ownerDocument || oXMLDom); // embed types only in kosher AI-namespaced documents // this allows performing XSLT transformations w/o types embedding if (rootElement.namespaceURI != AIURI) return; var types = []; for (var i = 0; i < rootElement.childNodes.length; ++i) { var n = rootElement.childNodes[i]; if ( (n.baseName || n.localName) != "types" || n.namespaceURI != AIURI) continue; var nm = n.getAttribute('from'); types.push(nm); rootElement.removeChild(n); i--; } if (!types.length) return; var tset = AXML.getTypeSet(types); if (!tset || !tset.oXML_.documentElement) return; var tnodes = AXML.SelectNodes(tset.oXML_.documentElement, "/*/*[local-name(.) = 'types']"); for (var i = 0; i < tnodes.length; ++i) { rootElement.appendChild( ImportNode(oXMLDom, tnodes[i], true) ); } } AXML.TransformDOM = function(oXMLDom, xslTemplate, bReturnTree) { AXML.EmbedPDLTypes(oXMLDom); //LogE("Transform started"); try { //LogE(oXMLDom.xml); var result = xslTemplate.Transform(oXMLDom, bReturnTree); //LogE(result.xml); return result; } catch (exc) { LogL(oXMLDom.xml); throw exc; } finally { //LogL("Transform done"); } } AXML.helpDefineGetter = function (obj, prop, getter) { if (typeof(Object.defineProperty) != "undefined") { Object.defineProperty(obj, prop, {get: getter}); } else { obj.__defineGetter__(prop, getter); } } // Raw code if (document.implementation && document.implementation.createDocument) { AXML.helpDefineGetter(Node.prototype, "xml", function () { var oSerializer = new XMLSerializer(); return oSerializer.serializeToString(this, "text/xml"); }); AXML.helpDefineGetter(Node.prototype, "text", function () { var sText = ""; for (var i = 0; i < this.childNodes.length; i++) { if (this.childNodes[i].hasChildNodes()) { sText += this.childNodes[i].text; } else { sText += this.childNodes[i].nodeValue; } } return sText; }); } AXML.createNSResolver = function(node, ns){ var xmldoc = null; if (node.nodeType === Node.DOCUMENT_NODE){ xmldoc = node; node = xmldoc.documentElement; }else xmldoc = node.ownerDocument; var defres = xmldoc.createNSResolver(node); return typeof ns === "object" && null !== ns ?// function(prefix){ return ns[prefix] || defres(prefix); } : defres; } /** * Selects the first node matching a given XPath expression. * @param oRefNode The node from which to evaluate the expression. * @param sXPath The XPath expression. * @param oNS An object containing the namespaces used in the expression. * @return An XML node matching the expression or null if no matches found. **/ AXML.SelectNodes = function(oRefNode, sXPath, oNS) { if (typeof XPathEvaluator != "undefined") { var oEvaluator = new XPathEvaluator(); var nsResolver = AXML.createNSResolver(oRefNode.ownerDocument, oNS); var oResult = oEvaluator.evaluate(sXPath, oRefNode, nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var aNodes = new Array; if (oResult != null) { var oElement = oResult.iterateNext(); while (oElement) { aNodes.push(oElement); oElement = oResult.iterateNext(); } } return aNodes; } else if (window.ActiveXObject || "ActiveXObject" in window) { // IE or IE11 AXML.registerNS(oRefNode, oNS); if (typeof oRefNode.ownerDocument.setProperty != "undefined") { oRefNode.ownerDocument.setProperty("SelectionLanguage", "XPath") } return oRefNode.selectNodes(sXPath); } else { throw new Error("No XPath engine found. "); } }; /** * Selects the first node matching a given XPath expression. * @param oRefNode The node from which to evaluate the expression. * @param sXPath The XPath expression. * @param oNS An object containing the namespaces used in the expression. * @return An XML node matching the expression or null if no matches found. **/ AXML.SelectNode = function (oRefNode, sXPath, oNS) { if (typeof XPathEvaluator != "undefined") { var oEvaluator = new XPathEvaluator(); var nsResolver = AXML.createNSResolver(oRefNode.ownerDocument, oNS); var oResult = oEvaluator.evaluate(sXPath, oRefNode, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null); if (oResult != null) { return oResult.singleNodeValue; } else { return null; } } else if (window.ActiveXObject || "ActiveXObject" in window) { // IE or IE11 AXML.registerNS(oRefNode, oNS); try{oRefNode.ownerDocument.setProperty("SelectionLanguage", "XPath")}catch(err){} return oRefNode.selectSingleNode(sXPath); } else { throw new Error("No XPath engine found.") } }; AXML.registerNS = function(oRefNode, oNS) { if (oNS) { var sNS = ""; for (var sProp in oNS) { sNS += "xmlns:" + sProp + "=\'" + oNS[sProp] + "\' "; } oRefNode.ownerDocument.setProperty("SelectionNamespaces", sNS); } } /** * Escapes XML entities in string * @param s input string * @return escaped string **/ AXML.EscapeEntities = function(s) { // TODO: rewrite to str_replace (or so on...) var r = ""; for (var i = 0; i < s.length; i++) { var sym = s.charAt(i); if (sym == '&') r += "&"; else if (sym == '<') r += "<"; else if (sym == '>') r += ">"; else if (sym == "'") r += "'"; // ' does not work in IE! else if (sym == "\"") r += """; else if (sym == "\n") r += " " else r = r + sym; } return r; } AXML.Parse = function (sContent) { if (!sContent) sContent = ""; if (AXML.bUseActiveX_) { var doc = new window.ActiveXObject(AXML.DOMVersion); doc.async = true; doc.loadXML(sContent); return doc; } else if (window.DOMParser) { // This browser appears to support DOMParser var oParser = new DOMParser(); return oParser.parseFromString(sContent, "text/xml"); } else { throw new Error("XML document not initialized or parsing not supported! "); } } var AXSLTemplate = Class.create({ MsVersions: [".6.0", ".3.0"], initialize: function(url) { if (window.XSLTProcessor) { var d = new AXML(); d.Load(url); this.doc = d.oXML_; this.processor = new XSLTProcessor(); } else { for (var i = 0; i < this.MsVersions.length; i++) try { this.msVersion = this.MsVersions[i]; if (this.processor = new ActiveXObject("Msxml2.XSLTemplate" + this.msVersion)) break; } catch(e) { } if (!this.processor) { throw new Error("AXML::detectDOMVersion(): Cannot acquire XSL template object! "); } this.doc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument' + this.msVersion); this.doc.async = false; this.doc.resolveExternals = true; this.doc.setProperty("AllowXsltScript", true); this.doc.load(url); //var txt = this.doc.documentElement.xml; //LogL(txt); //txt = txt.replace(/xmlns:exsl="[^"]*"/, 'xmlns:exsl="urn:schemas-microsoft-com:xslt"'); //this.doc.loadXML(txt); } }, FixXSLInclude: function(baseUrl, additionalXsl) { var includes = (additionalXsl.slice() || []); for (var i = 0; i < includes.length; ++i) { includes[i] = baseUrl + "../../" + includes[i]; } var nodes = this.doc.documentElement.childNodes; for (var i = 0; i < nodes.length; ++i) { var n = nodes[i]; if (n.nodeName != "xsl:include") continue; var abs_href = baseUrl + n.getAttribute('href'); includes.push(abs_href); this.doc.documentElement.removeChild(n); i--; } for (var i = 0; i < includes.length; ++i) { var nm = includes[i]; var re = /[^\/]+\/\.\.\//; while (nm.search(re) != -1) { nm = nm.replace(re, ''); } var d; if (window.XSLTProcessor) { var doc = new AXML(); doc.Load(nm); d = doc.oXML_.documentElement; } else { var doc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument' + this.msVersion); doc.async = false; doc.load(nm); d = doc.documentElement; } if(d) for (var j = 0; j < d.childNodes.length; ++j) { c = d.childNodes[j]; var cnode=ImportNode(this.doc, c, true); this.doc.documentElement.appendChild(cnode); } } }, Compile: function() { if (window.XSLTProcessor) { this.processor.importStylesheet(this.doc); } else { this.processor.stylesheet = this.doc; } this.compiled = true; }, Transform: function(oXMLDom, bReturnTree) { if (typeof XSLTProcessor != "undefined") { var oResultDOM = this.processor.transformToDocument(oXMLDom); if (bReturnTree) return oResultDOM; Element.wrap(oResultDOM.documentElement, 'div'); return (new XMLSerializer()).serializeToString(oResultDOM.documentElement.firstChild, "text/xml"); // in Opera innerHTML works incorrectly with namespaces } else if (AXML.bUseActiveX_) { var p = this.processor.createProcessor(); p.input = oXMLDom; p.transform(); return p.output; // works only with (fails on some tables) //var oResultDOM = new ActiveXObject(AXML.prototype.DOMVersion); //oResultDOM.validateOnParse = false; //oXMLDom.transformNodeToObject(this, oResultDOM); //if (oResultDOM.parseError && oResultDOM.parseError.errorCode) alert(oResultDOM.parseError.reason); //return oResultDOM; } else { throw new Error("AXSLTemplate::Transform: No XSLT engine found. "); } } }); /** * Implementation of importNode for the context document in IE. * If oNode is a TextNode, bRecursive is ignored. * @param {doc} context document * @param {DOMNode} oNode the Node to import * @param {boolean} bRecursive whether to include the children of oNode * @returns the imported node for further use */ ie_importNode = function(doc, oNode, bRecursive){ var tmp; if (oNode.nodeName=='#text') { return doc.createTextNode(oNode.data); } else { if (oNode.nodeName == "tbody" || oNode.nodeName == "tr"){ tmp = doc.createElement("table"); } else if (oNode.nodeName == "td"){ tmp = doc.createElement("tr"); } else if (oNode.nodeName == "option"){ tmp = doc.createElement("select"); } else { tmp = doc.createElement("div"); } if (bRecursive) { tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML; } else { tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML; } return tmp.getElementsByTagName("*")[0]; } }; function ImportNode(doc, node, bRecursive) { if ((typeof(doc.importNode) == "undefined") && (typeof(node.cloneNode) == "undefined") && Prototype.Browser.IE) { return ie_importNode(doc, node, bRecursive); } if(typeof(doc.importNode) == "undefined") return node.cloneNode(bRecursive); return doc.importNode(node, bRecursive); } function AdoptNode(doc, node) { return doc.adoptNode ? doc.adoptNode(node) : node; }