1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2009, 2010, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 25 /** 26 * XSLT engine <a href="http://www.w3.org/TR/xslt">http://www.w3.org/TR/xslt</a> 27 * @class 28 * Supports IE and Firefox. Use the following static methods to create instance. 29 * 30 * <pre> 31 * xslt = AjxXslt.createFromUrl(url of the stylesheet) 32 * xslt = AjxXslt.createFromString(stylesheet in string) 33 * </pre> 34 * 35 * Then apply the transformation on a document. Two methods are available depending on the needs. 36 * 37 * <pre> 38 * dom = xslt.transformToDom(doc); 39 * xml = xslt.transformToString(doc); 40 * </pre> 41 * 42 * @private 43 */ 44 AjxXslt = function() { 45 var doc = AjxXmlDoc.create(); 46 if (AjxEnv.isIE) { 47 var msdoc = null; 48 var vers = ["MSXML2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.3.0"]; 49 for (var i = 0; i < vers.length; i++) { 50 try { 51 msdoc = new ActiveXObject(vers[i]); 52 break; 53 } catch (ex) { 54 } 55 } 56 if (!msdoc) { 57 throw new AjxException("FreeThreadedDOMDocument", AjxException.UNSUPPORTED, "AjxXslt"); 58 } 59 msdoc.async = false; 60 doc._doc = msdoc; 61 } 62 this._doc = doc; 63 }; 64 65 AjxXslt.prototype.toString = 66 function() { 67 return "AjxXslt"; 68 }; 69 70 AjxXslt.createFromUrl = 71 function(url) { 72 var xslt = new AjxXslt(); 73 74 xslt.loadUrl(url); 75 76 return xslt; 77 }; 78 79 AjxXslt.createFromString = 80 function(str) { 81 var xslt = new AjxXslt(); 82 83 xslt._doc.loadFromString(str); 84 xslt.createProcessor(); 85 86 return xslt; 87 }; 88 89 AjxXslt.prototype.createProcessor = 90 function() { 91 var doc = this._doc.getDoc(); 92 if (AjxEnv.isIE) { 93 var err = doc.parseError; 94 if (err.errorCode != 0) { 95 DBG.println(AjxDebug.DBG1, "Parse error (" + err.reason + ") at line " + err.line + ", character " + err.linepos + "\n" + err.srcText); 96 throw new AjxException(err.reason, AjxException.INVALID_PARAM, "AjxXslt.createProcessor"); 97 } 98 99 var proc = null; 100 var vers = ["MSXML2.XSLTemplate.5.0", "MSXML2.XSLTemplate.3.0"]; 101 for (var i = 0; i < vers.length; i++) { 102 try { 103 proc = new ActiveXObject(vers[i]); 104 break; 105 } catch (ex) { 106 } 107 } 108 if (!proc) { 109 throw new AjxException("XSLTemplate", AjxException.UNSUPPORTED, "AjxXslt.createProcessor"); 110 } 111 this._processor = proc; 112 if(this._processor) { 113 this._processor.stylesheet = doc; 114 } 115 } else { 116 this._processor = new XSLTProcessor(); 117 if(this._processor) { 118 this._processor.importStylesheet(doc); 119 } 120 } 121 }; 122 123 AjxXslt._finishedLoading = 124 function() { 125 var xslt = this._xslt; // "this" is the document which xsl is being loaded to. 126 xslt.createProcessor(); 127 }; 128 129 AjxXslt.prototype.loadUrl = 130 function(url) { 131 var doc = this._doc; 132 133 if (AjxEnv.isNav) { 134 var docImpl = doc.getDoc(); 135 docImpl._xslt = this; // for callback 136 docImpl.addEventListener("load", AjxXslt._finishedLoading, false); 137 } 138 139 doc.loadFromUrl(url); 140 141 if (AjxEnv.isIE || AjxEnv.isChrome || AjxEnv.isSafari) { 142 this.createProcessor(); 143 } 144 }; 145 146 AjxXslt.prototype.transformToDom = 147 function(dom) { 148 var ret; 149 if (AjxEnv.isIE) { 150 ret = this.transformIE(dom); 151 } else { 152 return this.transformNav(dom); // already in dom 153 } 154 var doc = AjxXmlDoc.createFromXml(ret); 155 return doc.getDoc(); 156 }; 157 158 AjxXslt.prototype.transformToString = 159 function(dom) { 160 var ret; 161 if (AjxEnv.isIE) { 162 return this.transformIE(dom); // already in str 163 } else if (AjxEnv.isNav || AjxEnv.isChrome || AjxEnv.isSafari) { 164 ret = this.transformNav(dom); 165 } else { 166 DBG.println(AjxDebug.DBG1, "No XSL transformation due to browser incompatibility."); 167 return dom.documentElement.innerHTML; 168 } 169 170 if (!ret || !ret.documentElement) { 171 throw new AjxException("XSL transformation failed.", AjxException.INVALID_PARAM, "AjxXslt.transformToString"); 172 } 173 174 var elem = ret.documentElement; 175 if ((elem instanceof HTMLElement) || 176 (elem instanceof HTMLHtmlElement)) { 177 // good. 178 return elem.innerHTML; 179 } else if (elem instanceof Element) { 180 // ok. 181 return AjxXmlDoc.replaceInvalidChars(elem.xml); 182 } 183 DBG.println(AjxDebug.DBG1, "Transformation resulted in non-element."); 184 return dom.documentElement.innerHTML; 185 }; 186 187 /** 188 * IE returns html text. 189 */ 190 AjxXslt.prototype.transformIE = 191 function(dom) { 192 try { 193 var xsltProc = this._processor.createProcessor(); 194 xsltProc.input = dom; 195 xsltProc.transform(); 196 return xsltProc.output; 197 } catch (exception) { 198 DBG.println(AjxDebug.DBG1, "Exception in XSL transformation: "+exception.description); 199 throw new AjxException(exception.description, AjxException.INVALID_PARAM, "AjxXslt.transformIE"); 200 } 201 }; 202 203 /** 204 * Returns either HTMLDocument or XMLDocument, depending on the transformation. 205 */ 206 AjxXslt.prototype.transformNav = 207 function(dom) { 208 if(!this._processor) { 209 return ""; 210 } 211 return this._processor.transformToDocument(dom); 212 }; 213 214 /** 215 * Returns DocumentFragment 216 */ 217 AjxXslt.prototype.transformNav2 = 218 function(dom) { 219 this._fragment = document.implementation.createDocument("", "", null); 220 if(!this._processor) { 221 return ""; 222 } 223 return this._processor.transformToFragment(dom, this._fragment); 224 }; 225