1 |
|
2 | var Code, call_bound_func, coffee, coffeecup, parser, skeleton, uglify, _ref,
|
3 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
4 |
|
5 | coffee = require('coffee-script');
|
6 |
|
7 | _ref = require('uglify-js'), uglify = _ref.uglify, parser = _ref.parser;
|
8 |
|
9 | coffeecup = null;
|
10 |
|
11 | exports.setup = function(cc) {
|
12 | return coffeecup = cc;
|
13 | };
|
14 |
|
15 | skeleton = 'var __cc = {\n buffer: \'\'\n};\nvar text = function(txt) {\n if (typeof txt === \'string\' || txt instanceof String) {\n __cc.buffer += txt;\n } else if (typeof txt === \'number\' || txt instanceof Number) {\n __cc.buffer += txt.toString();\n }\n};\nvar h = function(txt) {\n var escaped;\n if (typeof txt === \'string\' || txt instanceof String) {\n escaped = txt.replace(/&/g, \'&\')\n .replace(/</g, \'<\')\n .replace(/>/g, \'>\')\n .replace(/"/g, \'"\');\n } else {\n escaped = txt;\n }\n return escaped;\n};\nvar cede = function(f) {\n var temp_buffer = \'\';\n var old_buffer = __cc.buffer;\n __cc.buffer = temp_buffer;\n f();\n temp_buffer = __cc.buffer;\n __cc.buffer = old_buffer;\n return temp_buffer;\n};\n';
|
16 |
|
17 | call_bound_func = function(func) {
|
18 | return ['call', ['dot', func, 'call'], [['name', 'data']]];
|
19 | };
|
20 |
|
21 | Code = (function() {
|
22 |
|
23 | function Code(parent) {
|
24 | this.parent = parent;
|
25 | this.nodes = [];
|
26 | this.line = '';
|
27 | }
|
28 |
|
29 | Code.prototype.call = function(arg) {
|
30 | return ['stat', ['call', ['name', 'text'], [arg]]];
|
31 | };
|
32 |
|
33 | Code.prototype.append = function(str) {
|
34 | if (this.block != null) {
|
35 | return this.block.append(str);
|
36 | } else {
|
37 | return this.line += str;
|
38 | }
|
39 | };
|
40 |
|
41 | Code.prototype.flush = function() {
|
42 | if (this.block != null) {
|
43 | return this.block.flush();
|
44 | } else {
|
45 | this.merge_text(['string', this.line]);
|
46 | return this.line = '';
|
47 | }
|
48 | };
|
49 |
|
50 | Code.prototype.open_if = function(condition) {
|
51 | this.flush();
|
52 | if (this.block != null) {
|
53 | return this.block.open_if(condition);
|
54 | } else {
|
55 | this.block = new Code();
|
56 | return this.block.condition = condition;
|
57 | }
|
58 | };
|
59 |
|
60 | Code.prototype.close_if = function() {
|
61 | this.flush();
|
62 | if (this.block.block != null) {
|
63 | return this.block.close_if();
|
64 | } else {
|
65 | this.nodes.push(['if', this.block.condition, ['block', this.block.nodes]]);
|
66 | return delete this.block;
|
67 | }
|
68 | };
|
69 |
|
70 | Code.prototype.push = function(node) {
|
71 | this.flush();
|
72 | if (this.block != null) {
|
73 | return this.block.push(node);
|
74 | } else {
|
75 | return this.merge_text(node);
|
76 | }
|
77 | };
|
78 |
|
79 | Code.prototype.merge_text = function(arg) {
|
80 | var l, ok, oldArg, prev, _ref1, _ref2;
|
81 | if (arg[0] === 'binary' && arg[1] === '+') {
|
82 | this.merge_text(arg[2]);
|
83 | arg = arg[3];
|
84 | }
|
85 | if (l = this.nodes.length) {
|
86 | prev = this.nodes[l - 1];
|
87 | if (prev[0] === 'stat' && prev[1][0] === 'call' && prev[1][1][0] === 'name' && prev[1][1][1] === 'text') {
|
88 | oldArg = prev[1][2][0];
|
89 | ok = ['string', 'num'];
|
90 | if ((_ref1 = oldArg[0], __indexOf.call(ok, _ref1) >= 0) && (_ref2 = arg[0], __indexOf.call(ok, _ref2) >= 0)) {
|
91 | prev[1][2][0] = ['string', oldArg[1] + arg[1]];
|
92 | return;
|
93 | }
|
94 | }
|
95 | }
|
96 | return this.nodes.push(this.call(arg));
|
97 | };
|
98 |
|
99 | Code.prototype.get_nodes = function() {
|
100 | this.flush();
|
101 | if (this.parent[0] === 'stat') {
|
102 | return ['splice', this.nodes];
|
103 | }
|
104 | return call_bound_func(['function', null, [], this.nodes]);
|
105 | };
|
106 |
|
107 | return Code;
|
108 |
|
109 | })();
|
110 |
|
111 | exports.compile = function(source, hardcoded_locals, options) {
|
112 | var ast, code, compiled, escape, w;
|
113 | escape = function(node) {
|
114 | if (options.autoescape) {
|
115 | return ['call', ['name', 'h'], [node]];
|
116 | }
|
117 | return node;
|
118 | };
|
119 | ast = parser.parse(hardcoded_locals + ("(" + source + ").call(data);"));
|
120 | w = uglify.ast_walker();
|
121 | ast = w.with_walkers({
|
122 | call: function(expr, args) {
|
123 | var arg, classes, code, comment, condition, contents, doctype, escape_all, func, i, id, idx, name, node, render_attrs, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref1, _ref2, _ref3;
|
124 | name = expr[1];
|
125 | if (name === 'doctype') {
|
126 | code = new Code(w.parent());
|
127 | if (args.length > 0) {
|
128 | doctype = args[0][1].toString();
|
129 | if (doctype in coffeecup.doctypes) {
|
130 | code.append(coffeecup.doctypes[doctype]);
|
131 | } else {
|
132 | throw new Error('Invalid doctype');
|
133 | }
|
134 | } else {
|
135 | code.append(coffeecup.doctypes["default"]);
|
136 | }
|
137 | return code.get_nodes();
|
138 | } else if (name === 'comment') {
|
139 | comment = args[0];
|
140 | code = new Code(w.parent());
|
141 | if (comment[0] === 'string') {
|
142 | code.append("<!--" + comment[1] + "-->");
|
143 | } else {
|
144 | code.append('<!--');
|
145 | code.push(escape(comment));
|
146 | code.append('-->');
|
147 | }
|
148 | return code.get_nodes();
|
149 | } else if (name === 'ie') {
|
150 | condition = args[0], contents = args[1];
|
151 | code = new Code(w.parent());
|
152 | if (condition[0] === 'string') {
|
153 | code.append("<!--[if " + condition[1] + "]>");
|
154 | } else {
|
155 | code.append('<!--[if ');
|
156 | code.push(escape(condition));
|
157 | code.append(']>');
|
158 | }
|
159 | code.push(call_bound_func(w.walk(contents)));
|
160 | code.append('<![endif]-->');
|
161 | return code.get_nodes();
|
162 | } else if (__indexOf.call(coffeecup.tags, name) >= 0 || (name === 'tag' || name === 'coffeescript')) {
|
163 | if (name === 'tag') {
|
164 | name = args.shift()[1];
|
165 | }
|
166 | if (name === 'coffeescript') {
|
167 | name = 'script';
|
168 | for (_i = 0, _len = args.length; _i < _len; _i++) {
|
169 | arg = args[_i];
|
170 | if ((_ref1 = arg[0]) !== 'string' && _ref1 !== 'object' && _ref1 !== 'function') {
|
171 | throw new Error('Invalid argument to coffeescript function');
|
172 | }
|
173 | if (arg[0] === 'string' && (args.length === 1 || arg !== args[0])) {
|
174 | arg[1] = coffee.compile(arg[1], {
|
175 | bare: true
|
176 | });
|
177 | }
|
178 | }
|
179 | }
|
180 | code = new Code(w.parent());
|
181 | code.append("<" + name);
|
182 | for (_j = 0, _len1 = args.length; _j < _len1; _j++) {
|
183 | arg = args[_j];
|
184 | switch (arg[0]) {
|
185 | case 'function':
|
186 | if (name === 'script') {
|
187 | func = uglify.gen_code(arg, {
|
188 | beautify: true,
|
189 | indent_level: 2
|
190 | });
|
191 | contents = ['string', "" + func + ".call(this);"];
|
192 | } else {
|
193 | func = w.walk(arg);
|
194 | _ref2 = func[3];
|
195 | for (idx = _k = 0, _len2 = _ref2.length; _k < _len2; idx = ++_k) {
|
196 | node = _ref2[idx];
|
197 | if (node[0] === 'return' && (node[1] != null) && node[1][0] !== 'string') {
|
198 | func[3][idx][1] = escape(node[1]);
|
199 | }
|
200 | }
|
201 | contents = call_bound_func(func);
|
202 | }
|
203 | break;
|
204 | case 'object':
|
205 | render_attrs = function(obj, prefix) {
|
206 | var attr, key, value, varname, _l, _len3, _ref3, _ref4, _results;
|
207 | if (prefix == null) {
|
208 | prefix = '';
|
209 | }
|
210 | _results = [];
|
211 | for (_l = 0, _len3 = obj.length; _l < _len3; _l++) {
|
212 | attr = obj[_l];
|
213 | key = attr[0];
|
214 | value = attr[1];
|
215 | if (value[0] === 'name' && value[1] === 'true') {
|
216 | _results.push(code.append(" " + key + "=\"" + key + "\""));
|
217 | } else if (value[0] === 'name' && ((_ref3 = value[1]) === 'undefined' || _ref3 === 'null' || _ref3 === 'false')) {
|
218 | continue;
|
219 | } else if ((_ref4 = value[0]) === 'name' || _ref4 === 'dot') {
|
220 | varname = uglify.gen_code(value);
|
221 | condition = "typeof " + varname + " !== 'undefined' && " + varname + " !== null && " + varname + " !== false";
|
222 | code.open_if(parser.parse(condition)[1][0][1]);
|
223 | code.append(" " + (prefix + key) + "=\"");
|
224 | code.push(escape(value));
|
225 | code.append('"');
|
226 | _results.push(code.close_if());
|
227 | } else if (value[0] === 'string') {
|
228 | _results.push(code.append(" " + (prefix + key) + "=\"" + value[1] + "\""));
|
229 | } else if (value[0] === 'function') {
|
230 | func = uglify.gen_code(value).replace(/"/g, '"');
|
231 | _results.push(code.append(" " + (prefix + key) + "=\"" + func + ".call(this);\""));
|
232 | } else if (value[0] === 'object') {
|
233 | _results.push(render_attrs(value[1], prefix + key + '-'));
|
234 | } else {
|
235 | code.append(" " + (prefix + key) + "=\"");
|
236 | code.push(escape(value));
|
237 | _results.push(code.append('"'));
|
238 | }
|
239 | }
|
240 | return _results;
|
241 | };
|
242 | render_attrs(arg[1]);
|
243 | break;
|
244 | case 'string':
|
245 | if (args.length > 1 && arg === args[0]) {
|
246 | classes = [];
|
247 | _ref3 = arg[1].split('.');
|
248 | for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
|
249 | i = _ref3[_l];
|
250 | if (__indexOf.call(i, '#') >= 0) {
|
251 | id = i.replace('#', '');
|
252 | } else {
|
253 | if (i !== '') {
|
254 | classes.push(i);
|
255 | }
|
256 | }
|
257 | }
|
258 | if (id) {
|
259 | code.append(" id=\"" + id + "\"");
|
260 | }
|
261 | if (classes.length > 0) {
|
262 | code.append(" class=\"" + (classes.join(' ')) + "\"");
|
263 | }
|
264 | } else {
|
265 | arg[1] = arg[1].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
266 | contents = arg;
|
267 | }
|
268 | break;
|
269 | case 'binary':
|
270 | escape_all = function(node) {
|
271 | switch (node[0]) {
|
272 | case 'binary':
|
273 | node[2] = escape_all(node[2]);
|
274 | node[3] = escape_all(node[3]);
|
275 | return node;
|
276 | case 'string':
|
277 | return node;
|
278 | case 'call':
|
279 | if (node[1][0] === 'name' && node[1][1] === 'cede') {
|
280 | return node;
|
281 | }
|
282 | return escape(node);
|
283 | default:
|
284 | return escape(node);
|
285 | }
|
286 | };
|
287 | contents = escape_all(w.walk(arg));
|
288 | break;
|
289 | default:
|
290 | contents = escape(w.walk(arg));
|
291 | }
|
292 | }
|
293 | if (__indexOf.call(coffeecup.self_closing, name) >= 0) {
|
294 | code.append(' />');
|
295 | } else {
|
296 | code.append('>');
|
297 | }
|
298 | if (contents != null) {
|
299 | code.push(contents);
|
300 | }
|
301 | if (!(__indexOf.call(coffeecup.self_closing, name) >= 0)) {
|
302 | code.append("</" + name + ">");
|
303 | }
|
304 | return code.get_nodes();
|
305 | }
|
306 | return null;
|
307 | }
|
308 | }, function() {
|
309 | return w.walk(ast);
|
310 | });
|
311 | compiled = uglify.gen_code(ast, {
|
312 | beautify: true,
|
313 | indent_level: 2
|
314 | });
|
315 | if (options.locals) {
|
316 | compiled = "with(data.locals){" + compiled + "}";
|
317 | }
|
318 | code = skeleton + compiled + "return __cc.buffer;";
|
319 | return new Function('data', code);
|
320 | };
|