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 };