1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2008, 2009, 2010, 2011, 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) 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @author Andy Clark
 26  * 
 27  * @private
 28  */
 29 AjxTemplate.compile = function(pkg, authoritative, define, templateText) {
 30 	var name = AjxPackage.__package2path(pkg);
 31 	var lines = templateText != null ? templateText : AjxLoader.load(name).reponseText;
 32 	var buffer = [], offset = 0, first = true;
 33 
 34 	AjxTemplate.__RE_TEMPLATE.lastIndex = 0;
 35 	var m = AjxTemplate.__RE_TEMPLATE.exec(lines);
 36 	if (m) {
 37 		do {
 38 			var attrs = AjxTemplate.__parseAttrs(m[1]);
 39 			var body = m[2];
 40 			if (attrs["xml:space"] != "preserve") {
 41                 // bug 47973: IE doesn't support String.prototype.trim
 42                 // NOTE: This was caused when the Java TemplateCompiler class was ported.
 43                 body = body.replace(AjxTemplate.__RE_GT_LINESEP_LT, "><").replace(/^\s+|\s+$/,"");
 44 			}
 45 
 46 			var packageId = pkg;
 47 			var templateId = attrs.id;
 48 			// NOTE: Template ids can be specified absolutely (i.e.
 49 			//       overriding the default package) if the id starts
 50 			//       with a forward slash (/), or if the id contains
 51 			//       a hash mark (#). This allows a template file to
 52 			//       override both types of template files (i.e. a
 53 			//       single template per file or multiple templates
 54 			//       per file).
 55 			if (templateId && (templateId.indexOf('#') != -1 || templateId.match(/^\//))) {
 56 				if (templateId.indexOf('#') == -1) {
 57 					templateId += "#";
 58 				}
 59 				packageId = templateId.replace(/#.*$/, "").replace(/^\//,"").replace(/\//g,'.');
 60 				templateId = templateId.replace(/^.*#/, "");
 61 			}
 62 			var id = templateId ? packageId+"#"+templateId : packageId;
 63 
 64 			var func = AjxTemplate.__convertLines(body);
 65 			AjxTemplate.register(id, func, attrs, authoritative);
 66 
 67 			if (first && define) {
 68 				AjxPackage.define(packageId);
 69 			}
 70 			if (first) {
 71 				first = false;
 72 				AjxTemplate.register(packageId, func, attrs, authoritative);
 73 			}
 74 		} while (m = AjxTemplate.__RE_TEMPLATE.exec(lines));
 75 	}
 76 	else {
 77 		if (define) {
 78 			AjxPackage.define(pkg);
 79 		}
 80 		var func = AjxTemplate.__convertLines(lines);
 81 		AjxTemplate.register(pkg, func, {}, authoritative);
 82 	}
 83 };
 84 
 85 // template compilation utility
 86 
 87 AjxTemplate.__RE_REPLACE = new RegExp([ "\\$\\{(.+?)\\}", "<\\$=(.+?)\\$>", "<\\$(.+?)\\$>" ].join("|"), "mg");
 88 AjxTemplate.__RE_TEMPLATE = new RegExp("<template(.*?)>(.*?)</template>", "mg");
 89 AjxTemplate.__RE_ATTR = new RegExp("\\s*(\\S+)\\s*=\\s*('[^']*'|\"[^\"]*\")", "mg");
 90 AjxTemplate.__RE_PARAM_PART = new RegExp("([^\\(\\.]+)(\\(.*?\\))?\\.?", "g");
 91 AjxTemplate.__RE_GT_LINESEP_LT = new RegExp([">", "\\s*\\n+\\s*", "<"].join(""), "mg");
 92 
 93 AjxTemplate.__convertLines = function(lines) {
 94 	var buffer = [], offset = 0;
 95 
 96 	buffer[offset++] = "\tvar _hasBuffer = Boolean(buffer);";
 97 	buffer[offset++] = "\tdata = (typeof data == \"string\" ? { id: data } : data) || {};";
 98 	buffer[offset++] = "\tbuffer = buffer || [];";
 99 	buffer[offset++] = "\tvar _i = buffer.length;";
100 	buffer[offset++] = "\n";
101 
102 	AjxTemplate.__RE_REPLACE.lastIndex = 0;
103 	var m = AjxTemplate.__RE_REPLACE.exec(lines);
104 	if (m) {
105 		var head = 0;
106 		do {
107 			var tail = AjxTemplate.__RE_REPLACE.lastIndex - m[0].length;
108 			if (head < tail) {
109 				AjxTemplate.__printStringLines(buffer, lines.substring(head, tail));
110 			}
111 			var param = m[1];
112 			var inline = m[2];
113 			if (param) {
114 				offset = AjxTemplate.__printDataLine(buffer, param);
115 			}
116 			else if (inline) {
117 				offset = AjxTemplate.__printBufferLine(buffer, inline);
118 			}
119 			else {
120 				offset = AjxTemplate.__printLine(buffer, "\t", m[3].replace(/\n/g, "\n\t"), "\n");
121 			}
122 			head = AjxTemplate.__RE_REPLACE.lastIndex;
123 		} while (m = AjxTemplate.__RE_REPLACE.exec(lines));
124 		if (head < lines.length) {
125 			offset = AjxTemplate.__printStringLines(buffer, lines.substring(head));
126 		}
127 	}
128 	else {
129 		offset = AjxTemplate.__printStringLines(buffer, lines);
130 	}
131 	buffer[offset++] = "\n";
132 
133 	buffer[offset++] = "\treturn _hasBuffer ? buffer.length : buffer.join(\"\");";
134 
135 	return new Function("name,params,data,buffer",buffer.join(""));
136 };
137 
138 AjxTemplate.__parseAttrs = function(s) {
139 	var attrs = {}, m;
140 	AjxTemplate.__RE_ATTR.lastIndex = 0;
141 	while (m = AjxTemplate.__RE_ATTR.exec(s)) {
142 		var value = m[2];
143 		attrs[m[1]] = value.substring(1, value.length - 1);
144 	}
145 	return attrs;
146 };
147 
148 AjxTemplate.__printLine = function(buffer, s1 /* ..., sN */) {
149 	var offset = buffer.length;
150 	for (var i = 1; i < arguments.length; i++) {
151 		buffer[offset++] = arguments[i];
152 	}
153 	return offset;
154 };
155 
156 AjxTemplate.__printStringLines = function(buffer, s1 /* ..., sN */) {
157 	var offset = buffer.length;
158 	for (var j = 1; j < arguments.length; j++) {
159 		var s = arguments[j];
160 		var lines = s.split("\n");
161 		for (var i = 0; i < lines.length; i++) {
162 			var line = lines[i];
163 			offset = AjxTemplate.__printStringLine(buffer, line, i < lines.length - 1 ? "\n" : "");
164 		}
165 	}
166 	return offset;
167 };
168 
169 AjxTemplate.__printStringLine = function(buffer, s1 /* ..., sN */) {
170 	var offset = buffer.length;
171 	buffer[offset++] = "\tbuffer[_i++] = \"";
172 	for (var i = 1; i < arguments.length; i++) {
173 		offset = AjxTemplate.__printEscaped(buffer, arguments[i]);
174 	}
175 	buffer[offset++] = "\";";
176 	return offset;
177 };
178 
179 AjxTemplate.__printDataLine = function(buffer, s) {
180 	var offset = buffer.length, m;
181 	buffer[offset++] = "\tbuffer[_i++] = data";
182 	AjxTemplate.__RE_PARAM_PART.lastIndex = 0;
183 	while (m = AjxTemplate.__RE_PARAM_PART.exec(s)) {
184 		buffer[offset++] = "[\"";
185 		buffer[offset++] = m[1];
186 		buffer[offset++] = "\"]";
187 		if (m[2] != null) {
188 			buffer[offset++] = m[2];
189 		}
190 	}
191 	buffer[offset++] = ";";
192 	return offset;
193 };
194 
195 AjxTemplate.__printBufferLine = function(buffer, s1 /* ..., sN */) {
196 	var offset = buffer.length;
197 	buffer[offset++] = "\tbuffer[_i++] = ";
198 	for (var i = 1; i < arguments.length; i++) {
199 		buffer[offset++] = arguments[i];
200 	}
201 	buffer[offset++] = ";";
202 	return offset;
203 };
204 
205 AjxTemplate.__printEscaped = function(buffer, s) {
206 	var offset = buffer.length;
207 	buffer[offset++] = s.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace('\n',"\\n").replace('\r',"\\r").replace('\t',"\\t");
208 	return offset;
209 };
210