1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2013, 2014, 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) 2006, 2007, 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Default constructor.
 26  * @constructor
 27  * @class
 28  * This class contains utility functions for using templates.
 29  * 
 30  * @author Andy Clark
 31  */
 32 AjxTemplate = function() {};
 33 
 34 //
 35 // Data
 36 //
 37 
 38 AjxTemplate._templates = {};
 39 AjxTemplate._stack = [];
 40 
 41 //
 42 // Public functions
 43 //
 44 
 45 /**
 46  * Sets the base path.
 47  * 
 48  * @param	{string}	basePath		the base path
 49  */
 50 AjxTemplate.setBasePath = function(basePath) {
 51     AjxTemplate._basePath = basePath;
 52 };
 53 /**
 54  * Sets the extension.
 55  * 
 56  * @param	{string}	extension		the extension
 57  */
 58 AjxTemplate.setExtension = function(extension) {
 59     AjxTemplate._extension = extension;
 60 };
 61 
 62 AjxTemplate.register = function(name, func, params, authoritative) {
 63     if (!authoritative && AjxTemplate._templates[name] &&
 64         AjxTemplate._templates[name].authoritative) {
 65         return;
 66     }
 67     AjxTemplate._templates[name] = {
 68         name: name, func: func, params: params || {}, authoritative: authoritative 
 69     };
 70 };
 71 
 72 AjxTemplate.getTemplate = function(name) {
 73     var template = AjxTemplate._templates[name];
 74     return template && template.func;
 75 };
 76 
 77 AjxTemplate.getParams = function(name) {
 78     var template = AjxTemplate._templates[name];
 79     return template && template.params;
 80 };
 81 
 82 /**
 83  * Expands the template.
 84  * 
 85  * @param	{string}		name		the template name
 86  * @param	{array}			[data]		the template date
 87  * @param	{array}			[buffer]	the buffer to use for template content
 88  * @return	{string}	the template content		
 89  */
 90 AjxTemplate.expand = function(name, data, buffer) {
 91 	// allow template text to come from document
 92 	if (!AjxTemplate._templates[name] && AjxTemplate.compile) {
 93 		var el = document.getElementById(name);
 94 		if (el) {
 95 			// NOTE: In all major browsers (IE, FF, Saf) the value property
 96 			//       of the textarea will be the literal text of the content.
 97 			//       Using the innerHTML will escape the HTML content which
 98 			//       is not desirable.
 99 			var isTextArea = el.nodeName.toUpperCase() == "TEXTAREA";
100 			AjxTemplate.compile(name, true, true, isTextArea ? el.value : el.innerHTML);
101 		}
102 	}
103 
104     var pkg = AjxTemplate.__name2Package(name);
105     var id = name.replace(/^[^#]*#?/, "");
106     if (id) {
107         name = [pkg, id].join("#");
108     }
109 
110     AjxTemplate.require(pkg);
111 
112     var hasBuffer = Boolean(buffer);
113     buffer = buffer || [];
114     var func = AjxTemplate.getTemplate(name);
115     if (func) {
116         try {
117             AjxTemplate._stack.push(pkg);
118             var params = AjxTemplate.getParams(name);
119             func(name, params, data, buffer);
120 	    }
121         catch (e) {
122 	    	buffer.push(this.__formatError(name, e));
123 	    }
124         finally {
125             AjxTemplate._stack.pop();
126         }
127     } else {
128     	buffer.push(this.__formatError(name, "template not found"));
129     }
130 
131     return hasBuffer ? buffer.length : buffer.join("");
132 };
133 
134 /**
135  * Force load of template.
136  * 
137  * @return <code>true</code> if the template is defined
138  * 
139  * @private
140  */
141 AjxTemplate.require = function(name) {
142 	AjxPackage.require({
143 		name: AjxTemplate.__name2Package(name),
144 		basePath: AjxTemplate._basePath,
145 		extension: AjxTemplate._extension
146 	});
147 	return AjxTemplate.getTemplate(name) != null;
148 };
149 
150 // set innerHTML of a DOM element with the results of a template expansion
151 // TODO: have some sort of actual error reporting
152 AjxTemplate.setContent = function(element, name, data) {
153 	if (typeof element == "string") {
154 		element = document.getElementById(element);
155 	}
156 	if (element == null) return;
157 	var html = AjxTemplate.expand(name, data);
158 	element.innerHTML = html;
159 };
160 
161 AjxTemplate.__name2Package = function(name) {
162 	var pkg = name.replace(/#.*$/, "");
163 	if (name.match(/^#/) && AjxTemplate._stack.length > 0) {
164 	    pkg = AjxTemplate._stack[AjxTemplate._stack.length - 1];
165 	}
166 	return pkg;
167 };
168 
169 // temporary API for handling logic errors in templates
170 //	may change to more robust solution later
171 AjxTemplate.__formatError = function(templateName, error) {
172 	return "Error in template '" + templateName + "': " + error;	
173 };