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