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