UNPKG

55 kBJavaScriptView Raw
1
2// CommonJS require()
3
4function require(p){
5 var path = require.resolve(p)
6 , mod = require.modules[path];
7 if (!mod) throw new Error('failed to require "' + p + '"');
8 if (!mod.exports) {
9 mod.exports = {};
10 mod.call(mod.exports, mod, mod.exports, require.relative(path));
11 }
12 return mod.exports;
13 }
14
15require.modules = {};
16
17require.resolve = function (path){
18 var orig = path
19 , reg = path + '.js'
20 , index = path + '/index.js';
21 return require.modules[reg] && reg
22 || require.modules[index] && index
23 || orig;
24 };
25
26require.register = function (path, fn){
27 require.modules[path] = fn;
28 };
29
30require.relative = function (parent) {
31 return function(p){
32 if ('.' != p[0]) return require(p);
33
34 var path = parent.split('/')
35 , segs = p.split('/');
36 path.pop();
37
38 for (var i = 0; i < segs.length; i++) {
39 var seg = segs[i];
40 if ('..' == seg) path.pop();
41 else if ('.' != seg) path.push(seg);
42 }
43
44 return require(path.join('/'));
45 };
46 };
47
48
49require.register("compiler.js", function(module, exports, require){
50
51/*!
52 * Jade - Compiler
53 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
54 * MIT Licensed
55 */
56
57/**
58 * Module dependencies.
59 */
60
61var nodes = require('./nodes')
62 , filters = require('./filters')
63 , doctypes = require('./doctypes')
64 , selfClosing = require('./self-closing')
65 , inlineTags = require('./inline-tags')
66 , utils = require('./utils');
67
68
69 if (!Object.keys) {
70 Object.keys = function(obj){
71 var arr = [];
72 for (var key in obj) {
73 if (obj.hasOwnProperty(key)) {
74 arr.push(obj);
75 }
76 }
77 return arr;
78 }
79 }
80
81 if (!String.prototype.trimLeft) {
82 String.prototype.trimLeft = function(){
83 return this.replace(/^\s+/, '');
84 }
85 }
86
87
88
89/**
90 * Initialize `Compiler` with the given `node`.
91 *
92 * @param {Node} node
93 * @param {Object} options
94 * @api public
95 */
96
97var Compiler = module.exports = function Compiler(node, options) {
98 this.options = options = options || {};
99 this.node = node;
100 this.hasCompiledDoctype = false;
101 this.hasCompiledTag = false;
102 this.pp = options.pretty || false;
103 this.debug = false !== options.compileDebug;
104 this.indents = 0;
105 if (options.doctype) this.setDoctype(options.doctype);
106};
107
108/**
109 * Compiler prototype.
110 */
111
112Compiler.prototype = {
113
114 /**
115 * Compile parse tree to JavaScript.
116 *
117 * @api public
118 */
119
120 compile: function(){
121 this.buf = ['var interp;'];
122 this.lastBufferedIdx = -1
123 this.visit(this.node);
124 return this.buf.join('\n');
125 },
126
127 /**
128 * Sets the default doctype `name`. Sets terse mode to `true` when
129 * html 5 is used, causing self-closing tags to end with ">" vs "/>",
130 * and boolean attributes are not mirrored.
131 *
132 * @param {string} name
133 * @api public
134 */
135
136 setDoctype: function(name){
137 var doctype = doctypes[(name || 'default').toLowerCase()];
138 if (!doctype) throw new Error('unknown doctype "' + name + '"');
139 this.doctype = doctype;
140 this.terse = '5' == name || 'html' == name;
141 this.xml = 0 == this.doctype.indexOf('<?xml');
142 },
143
144 /**
145 * Buffer the given `str` optionally escaped.
146 *
147 * @param {String} str
148 * @param {Boolean} esc
149 * @api public
150 */
151
152 buffer: function(str, esc){
153 if (esc) str = utils.escape(str);
154
155 if (this.lastBufferedIdx == this.buf.length) {
156 this.lastBuffered += str;
157 this.buf[this.lastBufferedIdx - 1] = "buf.push('" + this.lastBuffered + "');"
158 } else {
159 this.buf.push("buf.push('" + str + "');");
160 this.lastBuffered = str;
161 this.lastBufferedIdx = this.buf.length;
162 }
163 },
164
165 /**
166 * Buffer the given `node`'s lineno.
167 *
168 * @param {Node} node
169 * @api public
170 */
171
172 line: function(node){
173 if (false === node.instrumentLineNumber) return;
174 this.buf.push('__.lineno = ' + node.line + ';');
175 },
176
177 /**
178 * Visit `node`.
179 *
180 * @param {Node} node
181 * @api public
182 */
183
184 visit: function(node){
185 if (this.debug) this.line(node);
186 return this.visitNode(node);
187 },
188
189 /**
190 * Visit `node`.
191 *
192 * @param {Node} node
193 * @api public
194 */
195
196 visitNode: function(node){
197 var name = node.constructor.name
198 || node.constructor.toString().match(/function ([^(\s]+)()/)[1];
199 return this['visit' + name](node);
200 },
201
202 /**
203 * Visit all nodes in `block`.
204 *
205 * @param {Block} block
206 * @api public
207 */
208
209 visitBlock: function(block){
210 var len = len = block.nodes.length;
211 for (var i = 0; i < len; ++i) {
212 this.visit(block.nodes[i]);
213 }
214 },
215
216 /**
217 * Visit `doctype`. Sets terse mode to `true` when html 5
218 * is used, causing self-closing tags to end with ">" vs "/>",
219 * and boolean attributes are not mirrored.
220 *
221 * @param {Doctype} doctype
222 * @api public
223 */
224
225 visitDoctype: function(doctype){
226 if (doctype && (doctype.val || !this.doctype)) {
227 this.setDoctype(doctype.val || 'default');
228 }
229
230 if (this.doctype) this.buffer(this.doctype);
231 this.hasCompiledDoctype = true;
232 },
233
234 /**
235 * Visit `mixin`, generating a function that
236 * may be called within the template.
237 *
238 * @param {Mixin} mixin
239 * @api public
240 */
241
242 visitMixin: function(mixin){
243 var name = mixin.name.replace(/-/g, '_') + '_mixin'
244 , args = mixin.args || '';
245
246 if (mixin.block) {
247 this.buf.push('var ' + name + ' = function(' + args + '){');
248 this.visit(mixin.block);
249 this.buf.push('}');
250 } else {
251 this.buf.push(name + '(' + args + ');');
252 }
253 },
254
255 /**
256 * Visit `tag` buffering tag markup, generating
257 * attributes, visiting the `tag`'s code and block.
258 *
259 * @param {Tag} tag
260 * @api public
261 */
262
263 visitTag: function(tag){
264 this.indents++;
265 var name = tag.name;
266
267 if (!this.hasCompiledTag) {
268 if (!this.hasCompiledDoctype && 'html' == name) {
269 this.visitDoctype();
270 }
271 this.hasCompiledTag = true;
272 }
273
274 // pretty print
275 if (this.pp && inlineTags.indexOf(name) == -1) {
276 this.buffer('\\n' + Array(this.indents).join(' '));
277 }
278
279 if (~selfClosing.indexOf(name) && !this.xml) {
280 this.buffer('<' + name);
281 this.visitAttributes(tag.attrs);
282 this.terse
283 ? this.buffer('>')
284 : this.buffer('/>');
285 } else {
286 // Optimize attributes buffering
287 if (tag.attrs.length) {
288 this.buffer('<' + name);
289 if (tag.attrs.length) this.visitAttributes(tag.attrs);
290 this.buffer('>');
291 } else {
292 this.buffer('<' + name + '>');
293 }
294 if (tag.code) this.visitCode(tag.code);
295 if (tag.text) this.buffer(utils.text(tag.text.nodes[0].trimLeft()));
296 this.escape = 'pre' == tag.name;
297 this.visit(tag.block);
298
299 // pretty print
300 if (this.pp && !~inlineTags.indexOf(name) && !tag.textOnly) {
301 this.buffer('\\n' + Array(this.indents).join(' '));
302 }
303
304 this.buffer('</' + name + '>');
305 }
306 this.indents--;
307 },
308
309 /**
310 * Visit `filter`, throwing when the filter does not exist.
311 *
312 * @param {Filter} filter
313 * @api public
314 */
315
316 visitFilter: function(filter){
317 var fn = filters[filter.name];
318
319 // unknown filter
320 if (!fn) {
321 if (filter.isASTFilter) {
322 throw new Error('unknown ast filter "' + filter.name + ':"');
323 } else {
324 throw new Error('unknown filter ":' + filter.name + '"');
325 }
326 }
327 if (filter.isASTFilter) {
328 this.buf.push(fn(filter.block, this, filter.attrs));
329 } else {
330 var text = filter.block.nodes.join('');
331 this.buffer(utils.text(fn(text, filter.attrs)));
332 }
333 },
334
335 /**
336 * Visit `text` node.
337 *
338 * @param {Text} text
339 * @api public
340 */
341
342 visitText: function(text){
343 text = utils.text(text.nodes.join(''));
344 if (this.escape) text = escape(text);
345 this.buffer(text);
346 this.buffer('\\n');
347 },
348
349 /**
350 * Visit a `comment`, only buffering when the buffer flag is set.
351 *
352 * @param {Comment} comment
353 * @api public
354 */
355
356 visitComment: function(comment){
357 if (!comment.buffer) return;
358 if (this.pp) this.buffer('\\n' + Array(this.indents + 1).join(' '));
359 this.buffer('<!--' + utils.escape(comment.val) + '-->');
360 },
361
362 /**
363 * Visit a `BlockComment`.
364 *
365 * @param {Comment} comment
366 * @api public
367 */
368
369 visitBlockComment: function(comment){
370 if (!comment.buffer) return;
371 if (0 == comment.val.indexOf('if')) {
372 this.buffer('<!--[' + comment.val + ']>');
373 this.visit(comment.block);
374 this.buffer('<![endif]-->');
375 } else {
376 this.buffer('<!--' + comment.val);
377 this.visit(comment.block);
378 this.buffer('-->');
379 }
380 },
381
382 /**
383 * Visit `code`, respecting buffer / escape flags.
384 * If the code is followed by a block, wrap it in
385 * a self-calling function.
386 *
387 * @param {Code} code
388 * @api public
389 */
390
391 visitCode: function(code){
392 // Wrap code blocks with {}.
393 // we only wrap unbuffered code blocks ATM
394 // since they are usually flow control
395
396 // Buffer code
397 if (code.buffer) {
398 var val = code.val.trimLeft();
399 this.buf.push('var __val__ = ' + val);
400 val = 'null == __val__ ? "" : __val__';
401 if (code.escape) val = 'escape(' + val + ')';
402 this.buf.push("buf.push(" + val + ");");
403 } else {
404 this.buf.push(code.val);
405 }
406
407 // Block support
408 if (code.block) {
409 if (!code.buffer) this.buf.push('{');
410 this.visit(code.block);
411 if (!code.buffer) this.buf.push('}');
412 }
413 },
414
415 /**
416 * Visit `each` block.
417 *
418 * @param {Each} each
419 * @api public
420 */
421
422 visitEach: function(each){
423 this.buf.push(''
424 + '// iterate ' + each.obj + '\n'
425 + '(function(){\n'
426 + ' if (\'number\' == typeof ' + each.obj + '.length) {\n'
427 + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
428 + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');
429
430 this.visit(each.block);
431
432 this.buf.push(''
433 + ' }\n'
434 + ' } else {\n'
435 + ' for (var ' + each.key + ' in ' + each.obj + ') {\n'
436 + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){'
437 + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');
438
439 this.visit(each.block);
440
441 this.buf.push(' }\n');
442
443 this.buf.push(' }\n }\n}).call(this);\n');
444 },
445
446 /**
447 * Visit `attrs`.
448 *
449 * @param {Array} attrs
450 * @api public
451 */
452
453 visitAttributes: function(attrs){
454 var buf = []
455 , classes = [];
456
457 if (this.terse) buf.push('terse: true');
458
459 attrs.forEach(function(attr){
460 if (attr.name == 'class') {
461 classes.push('(' + attr.val + ')');
462 } else {
463 var pair = "'" + attr.name + "':(" + attr.val + ')';
464 buf.push(pair);
465 }
466 });
467
468 if (classes.length) {
469 classes = classes.join(" + ' ' + ");
470 buf.push("class: " + classes);
471 }
472
473 buf = buf.join(', ').replace('class:', '"class":');
474
475 this.buf.push("buf.push(attrs({ " + buf + " }));");
476 }
477};
478
479/**
480 * Escape the given string of `html`.
481 *
482 * @param {String} html
483 * @return {String}
484 * @api private
485 */
486
487function escape(html){
488 return String(html)
489 .replace(/&(?!\w+;)/g, '&amp;')
490 .replace(/</g, '&lt;')
491 .replace(/>/g, '&gt;')
492 .replace(/"/g, '&quot;');
493}
494
495}); // module: compiler.js
496
497require.register("doctypes.js", function(module, exports, require){
498
499/*!
500 * Jade - doctypes
501 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
502 * MIT Licensed
503 */
504
505module.exports = {
506 '5': '<!DOCTYPE html>'
507 , 'html': '<!DOCTYPE html>'
508 , 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
509 , 'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
510 , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
511 , 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
512 , 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
513 , '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
514 , 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
515 , 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
516};
517}); // module: doctypes.js
518
519require.register("filters.js", function(module, exports, require){
520
521/*!
522 * Jade - filters
523 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
524 * MIT Licensed
525 */
526
527module.exports = {
528
529 /**
530 * Wrap text with CDATA block.
531 */
532
533 cdata: function(str){
534 return '<![CDATA[\\n' + str + '\\n]]>';
535 },
536
537 /**
538 * Transform sass to css, wrapped in style tags.
539 */
540
541 sass: function(str){
542 str = str.replace(/\\n/g, '\n');
543 var sass = require('sass').render(str).replace(/\n/g, '\\n');
544 return '<style>' + sass + '</style>';
545 },
546
547 /**
548 * Transform stylus to css, wrapped in style tags.
549 */
550
551 stylus: function(str, options){
552 var ret;
553 str = str.replace(/\\n/g, '\n');
554 var stylus = require('stylus');
555 stylus(str, options).render(function(err, css){
556 if (err) throw err;
557 ret = css.replace(/\n/g, '\\n');
558 });
559 return '<style>' + ret + '</style>';
560 },
561
562 /**
563 * Transform sass to css, wrapped in style tags.
564 */
565
566 less: function(str){
567 var ret;
568 str = str.replace(/\\n/g, '\n');
569 require('less').render(str, function(err, css){
570 if (err) throw err;
571 ret = '<style>' + css.replace(/\n/g, '\\n') + '</style>';
572 });
573 return ret;
574 },
575
576 /**
577 * Transform markdown to html.
578 */
579
580 markdown: function(str){
581 var md;
582
583 // support markdown / discount
584 try {
585 md = require('markdown');
586 } catch (err){
587 try {
588 md = require('discount');
589 } catch (err) {
590 try {
591 md = require('markdown-js');
592 } catch (err) {
593 throw new Error('Cannot find markdown library, install markdown or discount');
594 }
595 }
596 }
597
598 str = str.replace(/\\n/g, '\n');
599 return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'&#39;');
600 },
601
602 /**
603 * Transform coffeescript to javascript.
604 */
605
606 coffeescript: function(str){
607 str = str.replace(/\\n/g, '\n');
608 var js = require('coffee-script').compile(str).replace(/\n/g, '\\n');
609 return '<script type="text/javascript">\\n' + js + '</script>';
610 }
611};
612}); // module: filters.js
613
614require.register("inline-tags.js", function(module, exports, require){
615
616/*!
617 * Jade - inline tags
618 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
619 * MIT Licensed
620 */
621
622module.exports = [
623 'a'
624 , 'abbr'
625 , 'acronym'
626 , 'b'
627 , 'br'
628 , 'code'
629 , 'em'
630 , 'font'
631 , 'i'
632 , 'img'
633 , 'ins'
634 , 'kbd'
635 , 'map'
636 , 'samp'
637 , 'small'
638 , 'span'
639 , 'strong'
640 , 'sub'
641 , 'sup'
642];
643}); // module: inline-tags.js
644
645require.register("jade.js", function(module, exports, require){
646
647/*!
648 * Jade
649 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
650 * MIT Licensed
651 */
652
653/**
654 * Module dependencies.
655 */
656
657var Parser = require('./parser')
658 , Compiler = require('./compiler')
659 , runtime = require('./runtime')
660
661/**
662 * Library version.
663 */
664
665exports.version = '0.14.0';
666
667/**
668 * Intermediate JavaScript cache.
669 */
670
671var cache = exports.cache = {};
672
673/**
674 * Expose self closing tags.
675 */
676
677exports.selfClosing = require('./self-closing');
678
679/**
680 * Default supported doctypes.
681 */
682
683exports.doctypes = require('./doctypes');
684
685/**
686 * Text filters.
687 */
688
689exports.filters = require('./filters');
690
691/**
692 * Utilities.
693 */
694
695exports.utils = require('./utils');
696
697/**
698 * Expose `Compiler`.
699 */
700
701exports.Compiler = Compiler;
702
703/**
704 * Expose `Parser`.
705 */
706
707exports.Parser = Parser;
708
709/**
710 * Nodes.
711 */
712
713exports.nodes = require('./nodes');
714
715/**
716 * Jade runtime helpers.
717 */
718
719exports.runtime = runtime;
720
721/**
722 * Parse the given `str` of jade and return a function body.
723 *
724 * @param {String} str
725 * @param {Object} options
726 * @return {String}
727 * @api private
728 */
729
730function parse(str, options){
731 var filename = options.filename
732 , inline = false !== options.inline
733 , inlined = '';
734
735 if (inline) {
736 inlined += runtime.attrs.toString() + '\n';
737 inlined += runtime.escape.toString() + '\n';
738 } else {
739 inlined = 'var attrs = jade.attrs, escape = jade.escape;\n';
740 }
741
742 try {
743 // Parse
744 var parser = new Parser(str, filename, options);
745 if (options.debug) parser.debug();
746
747 // Compile
748 var compiler = new (options.compiler || Compiler)(parser.parse(), options)
749 , js = compiler.compile();
750
751 // Debug compiler
752 if (options.debug) {
753 console.log('\n\x1b[1mCompiled Function\x1b[0m:\n\n%s', js.replace(/^/gm, ' '));
754 }
755
756 try {
757 return ''
758 + inlined
759 + 'var buf = [];\n'
760 + (options.self
761 ? 'var self = locals || {}, __ = __ || locals.__;\n' + js
762 : 'with (locals || {}) {\n' + js + '\n}\n')
763 + 'return buf.join("");';
764
765 } catch (err) {
766 process.compile(js, filename || 'Jade');
767 return;
768 }
769 } catch (err) {
770 runtime.rethrow(err, str, filename, parser.lexer.lineno);
771 }
772}
773
774/**
775 * Compile a `Function` representation of the given jade `str`.
776 *
777 * Options:
778 *
779 * - `compileDebug` when `false` debugging code is stripped from the compiled template
780 * - `inline` when `false` helpers are not inlined, and `jade.<helper>` is used
781 *
782 * @param {String} str
783 * @param {Options} options
784 * @return {Function}
785 * @api public
786 */
787
788exports.compile = function(str, options){
789 var options = options || {}
790 , input = JSON.stringify(str)
791 , inline = false !== options.inline
792 , filename = options.filename
793 ? JSON.stringify(options.filename)
794 : 'undefined'
795 , inlined = ''
796 , fn;
797
798 if (inline) {
799 inlined = runtime.rethrow.toString();
800 } else {
801 inlined = 'var rethrow = jade.rethrow;';
802 }
803
804 if (options.compileDebug !== false) {
805 // Reduce closure madness by injecting some locals
806 fn = [
807 'var __ = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };'
808 , inlined
809 , 'try {'
810 , parse(String(str), options || {})
811 , '} catch (err) {'
812 , ' rethrow(err, __.input, __.filename, __.lineno);'
813 , '}'
814 ].join('\n');
815 } else {
816 fn = parse(String(str), options || {});
817 }
818
819 return new Function('locals', fn);
820};
821
822/**
823 * Render the given `str` of jade.
824 *
825 * Options:
826 *
827 * - `scope` Evaluation scope (`this`)
828 * - `locals` Local variable object
829 * - `filename` Used in exceptions, and required by `cache`
830 * - `cache` Cache intermediate JavaScript in memory keyed by `filename`
831 * - `compiler` Compiler to replade jade's default
832 * - `doctype` Specify the default doctype
833 *
834 * @param {String|Buffer} str
835 * @param {Object} options
836 * @return {String}
837 * @api public
838 */
839
840exports.render = function(str, options){
841 var fn
842 , options = options || {}
843 , filename = options.filename;
844
845 // Accept Buffers
846 str = String(str);
847
848 // Cache support
849 if (options.cache) {
850 if (filename) {
851 if (cache[filename]) {
852 fn = cache[filename];
853 } else {
854 fn = cache[filename] = new Function('locals', parse(str, options));
855 }
856 } else {
857 throw new Error('filename is required when using the cache option');
858 }
859 } else {
860 fn = new Function('locals', parse(str, options));
861 }
862
863 // Render the template
864 try {
865 var locals = options.locals || {}
866 , meta = { lineno: 1 };
867 locals.__ = meta;
868 return fn.call(options.scope, locals);
869 } catch (err) {
870 runtime.rethrow(err, str, filename, meta.lineno);
871 }
872};
873
874/**
875 * Render jade template at the given `path`.
876 *
877 * @param {String} path
878 * @param {Object} options
879 * @param {Function} fn
880 * @api public
881 */
882
883exports.renderFile = function(path, options, fn){
884 var ret;
885
886 if (typeof options === 'function') {
887 fn = options;
888 options = {};
889 }
890 options.filename = path;
891
892 // Primed cache
893 if (options.cache && cache[path]) {
894 try {
895 ret = exports.render('', options);
896 } catch (err) {
897 return fn(err);
898 }
899 fn(null, ret);
900 } else {
901 fs.readFile(path, 'utf8', function(err, str){
902 if (err) return fn(err);
903 try {
904 ret = exports.render(str, options);
905 } catch (err) {
906 return fn(err);
907 }
908 fn(null, ret);
909 });
910 }
911};
912}); // module: jade.js
913
914require.register("lexer.js", function(module, exports, require){
915
916/*!
917 * Jade - Lexer
918 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
919 * MIT Licensed
920 */
921
922/**
923 * Initialize `Lexer` with the given `str`.
924 *
925 * Options:
926 *
927 * - `colons` allow colons for attr delimiters
928 *
929 * @param {String} str
930 * @param {Object} options
931 * @api private
932 */
933
934var Lexer = module.exports = function Lexer(str, options) {
935 options = options || {};
936 this.input = str.replace(/\r\n|\r/g, '\n');
937 this.colons = options.colons;
938 this.deferredTokens = [];
939 this.lastIndents = 0;
940 this.lineno = 1;
941 this.stash = [];
942 this.indentStack = [];
943 this.indentRe = null;
944 this.pipeless = false;
945};
946
947/**
948 * Lexer prototype.
949 */
950
951Lexer.prototype = {
952
953 /**
954 * Construct a token with the given `type` and `val`.
955 *
956 * @param {String} type
957 * @param {String} val
958 * @return {Object}
959 * @api private
960 */
961
962 tok: function(type, val){
963 return {
964 type: type
965 , line: this.lineno
966 , val: val
967 }
968 },
969
970 /**
971 * Consume the given `len` of input.
972 *
973 * @param {Number} len
974 * @api private
975 */
976
977 consume: function(len){
978 this.input = this.input.substr(len);
979 },
980
981 /**
982 * Scan for `type` with the given `regexp`.
983 *
984 * @param {String} type
985 * @param {RegExp} regexp
986 * @return {Object}
987 * @api private
988 */
989
990 scan: function(regexp, type){
991 var captures;
992 if (captures = regexp.exec(this.input)) {
993 this.consume(captures[0].length);
994 return this.tok(type, captures[1]);
995 }
996 },
997
998 /**
999 * Defer the given `tok`.
1000 *
1001 * @param {Object} tok
1002 * @api private
1003 */
1004
1005 defer: function(tok){
1006 this.deferredTokens.push(tok);
1007 },
1008
1009 /**
1010 * Lookahead `n` tokens.
1011 *
1012 * @param {Number} n
1013 * @return {Object}
1014 * @api private
1015 */
1016
1017 lookahead: function(n){
1018 var fetch = n - this.stash.length;
1019 while (fetch-- > 0) this.stash.push(this.next());
1020 return this.stash[--n];
1021 },
1022
1023 /**
1024 * Return the indexOf `start` / `end` delimiters.
1025 *
1026 * @param {String} start
1027 * @param {String} end
1028 * @return {Number}
1029 * @api private
1030 */
1031
1032 indexOfDelimiters: function(start, end){
1033 var str = this.input
1034 , nstart = 0
1035 , nend = 0
1036 , pos = 0;
1037 for (var i = 0, len = str.length; i < len; ++i) {
1038 if (start == str[i]) {
1039 ++nstart;
1040 } else if (end == str[i]) {
1041 if (++nend == nstart) {
1042 pos = i;
1043 break;
1044 }
1045 }
1046 }
1047 return pos;
1048 },
1049
1050 /**
1051 * Stashed token.
1052 */
1053
1054 stashed: function() {
1055 return this.stash.length
1056 && this.stash.shift();
1057 },
1058
1059 /**
1060 * Deferred token.
1061 */
1062
1063 deferred: function() {
1064 return this.deferredTokens.length
1065 && this.deferredTokens.shift();
1066 },
1067
1068 /**
1069 * end-of-source.
1070 */
1071
1072 eos: function() {
1073 if (this.input.length) return;
1074 if (this.indentStack.length) {
1075 this.indentStack.shift();
1076 return this.tok('outdent');
1077 } else {
1078 return this.tok('eos');
1079 }
1080 },
1081
1082 /**
1083 * Comment.
1084 */
1085
1086 comment: function() {
1087 var captures;
1088 if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) {
1089 this.consume(captures[0].length);
1090 var tok = this.tok('comment', captures[2]);
1091 tok.buffer = '-' != captures[1];
1092 return tok;
1093 }
1094 },
1095
1096 /**
1097 * Tag.
1098 */
1099
1100 tag: function() {
1101 var captures;
1102 if (captures = /^(\w[-:\w]*)/.exec(this.input)) {
1103 this.consume(captures[0].length);
1104 var tok, name = captures[1];
1105 if (':' == name[name.length - 1]) {
1106 name = name.slice(0, -1);
1107 tok = this.tok('tag', name);
1108 this.deferredTokens.push(this.tok(':'));
1109 while (' ' == this.input[0]) this.input = this.input.substr(1);
1110 } else {
1111 tok = this.tok('tag', name);
1112 }
1113 return tok;
1114 }
1115 },
1116
1117 /**
1118 * Filter.
1119 */
1120
1121 filter: function() {
1122 return this.scan(/^:(\w+)/, 'filter');
1123 },
1124
1125 /**
1126 * Doctype.
1127 */
1128
1129 doctype: function() {
1130 return this.scan(/^(?:!!!|doctype) *(\w+)?/, 'doctype');
1131 },
1132
1133 /**
1134 * Id.
1135 */
1136
1137 id: function() {
1138 return this.scan(/^#([\w-]+)/, 'id');
1139 },
1140
1141 /**
1142 * Class.
1143 */
1144
1145 className: function() {
1146 return this.scan(/^\.([\w-]+)/, 'class');
1147 },
1148
1149 /**
1150 * Text.
1151 */
1152
1153 text: function() {
1154 return this.scan(/^(?:\| ?)?([^\n]+)/, 'text');
1155 },
1156
1157 /**
1158 * Include.
1159 */
1160
1161 include: function() {
1162 return this.scan(/^include +([^\n]+)/, 'include');
1163 },
1164
1165 /**
1166 * Mixin.
1167 */
1168
1169 mixin: function(){
1170 var captures;
1171 if (captures = /^mixin +([-\w]+)(?:\(([^\)]+)\))?/.exec(this.input)) {
1172 this.consume(captures[0].length);
1173 var tok = this.tok('mixin', captures[1]);
1174 tok.args = captures[2];
1175 return tok;
1176 }
1177 },
1178
1179 /**
1180 * Each.
1181 */
1182
1183 each: function() {
1184 var captures;
1185 if (captures = /^- *each *(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
1186 this.consume(captures[0].length);
1187 var tok = this.tok('each', captures[1]);
1188 tok.key = captures[2] || 'index';
1189 tok.code = captures[3];
1190 return tok;
1191 }
1192 },
1193
1194 /**
1195 * Code.
1196 */
1197
1198 code: function() {
1199 var captures;
1200 if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
1201 this.consume(captures[0].length);
1202 var flags = captures[1];
1203 captures[1] = captures[2];
1204 var tok = this.tok('code', captures[1]);
1205 tok.escape = flags[0] === '=';
1206 tok.buffer = flags[0] === '=' || flags[1] === '=';
1207 return tok;
1208 }
1209 },
1210
1211 /**
1212 * Attributes.
1213 */
1214
1215 attrs: function() {
1216 if ('(' == this.input[0]) {
1217 var index = this.indexOfDelimiters('(', ')')
1218 , str = this.input.substr(1, index-1)
1219 , tok = this.tok('attrs')
1220 , len = str.length
1221 , colons = this.colons
1222 , states = ['key']
1223 , key = ''
1224 , val = ''
1225 , quote
1226 , c;
1227
1228 function state(){
1229 return states[states.length - 1];
1230 }
1231
1232 function interpolate(attr) {
1233 return attr.replace(/#\{([^}]+)\}/g, function(_, expr){
1234 return quote + " + (" + expr + ") + " + quote;
1235 });
1236 }
1237
1238 this.consume(index + 1);
1239 tok.attrs = {};
1240
1241 function parse(c) {
1242 var real = c;
1243 // TODO: remove when people fix ":"
1244 if (colons && ':' == c) c = '=';
1245 switch (c) {
1246 case ',':
1247 case '\n':
1248 switch (state()) {
1249 case 'expr':
1250 case 'array':
1251 case 'string':
1252 case 'object':
1253 val += c;
1254 break;
1255 default:
1256 states.push('key');
1257 val = val.trim();
1258 key = key.trim();
1259 if ('' == key) return;
1260 tok.attrs[key.replace(/^['"]|['"]$/g, '')] = '' == val
1261 ? true
1262 : interpolate(val);
1263 key = val = '';
1264 }
1265 break;
1266 case '=':
1267 switch (state()) {
1268 case 'key char':
1269 key += real;
1270 break;
1271 case 'val':
1272 case 'expr':
1273 case 'array':
1274 case 'string':
1275 case 'object':
1276 val += real;
1277 break;
1278 default:
1279 states.push('val');
1280 }
1281 break;
1282 case '(':
1283 if ('val' == state()) states.push('expr');
1284 val += c;
1285 break;
1286 case ')':
1287 if ('expr' == state()) states.pop();
1288 val += c;
1289 break;
1290 case '{':
1291 if ('val' == state()) states.push('object');
1292 val += c;
1293 break;
1294 case '}':
1295 if ('object' == state()) states.pop();
1296 val += c;
1297 break;
1298 case '[':
1299 if ('val' == state()) states.push('array');
1300 val += c;
1301 break;
1302 case ']':
1303 if ('array' == state()) states.pop();
1304 val += c;
1305 break;
1306 case '"':
1307 case "'":
1308 switch (state()) {
1309 case 'key':
1310 states.push('key char');
1311 break;
1312 case 'key char':
1313 states.pop();
1314 break;
1315 case 'string':
1316 if (c == quote) states.pop();
1317 val += c;
1318 break;
1319 default:
1320 states.push('string');
1321 val += c;
1322 quote = c;
1323 }
1324 break;
1325 case '':
1326 break;
1327 default:
1328 switch (state()) {
1329 case 'key':
1330 case 'key char':
1331 key += c;
1332 break;
1333 default:
1334 val += c;
1335 }
1336 }
1337 }
1338
1339 for (var i = 0; i < len; ++i) {
1340 parse(str[i]);
1341 }
1342
1343 parse(',');
1344
1345 return tok;
1346 }
1347 },
1348
1349 /**
1350 * Indent | Outdent | Newline.
1351 */
1352
1353 indent: function() {
1354 var captures, re;
1355
1356 // established regexp
1357 if (this.indentRe) {
1358 captures = this.indentRe.exec(this.input);
1359 // determine regexp
1360 } else {
1361 // tabs
1362 re = /^\n(\t*) */;
1363 captures = re.exec(this.input);
1364
1365 // spaces
1366 if (captures && !captures[1].length) {
1367 re = /^\n( *)/;
1368 captures = re.exec(this.input);
1369 }
1370
1371 // established
1372 if (captures && captures[1].length) this.indentRe = re;
1373 }
1374
1375 if (captures) {
1376 var tok
1377 , indents = captures[1].length;
1378
1379 ++this.lineno;
1380 this.consume(indents + 1);
1381
1382 if (' ' == this.input[0] || '\t' == this.input[0]) {
1383 throw new Error('Invalid indentation, you can use tabs or spaces but not both');
1384 }
1385
1386 // blank line
1387 if ('\n' == this.input[0]) return this.tok('newline');
1388
1389 // outdent
1390 if (this.indentStack.length && indents < this.indentStack[0]) {
1391 while (this.indentStack.length && this.indentStack[0] > indents) {
1392 this.stash.push(this.tok('outdent'));
1393 this.indentStack.shift();
1394 }
1395 tok = this.stash.pop();
1396 // indent
1397 } else if (indents && indents != this.indentStack[0]) {
1398 this.indentStack.unshift(indents);
1399 tok = this.tok('indent', indents);
1400 // newline
1401 } else {
1402 tok = this.tok('newline');
1403 }
1404
1405 return tok;
1406 }
1407 },
1408
1409 /**
1410 * Pipe-less text consumed only when
1411 * pipeless is true;
1412 */
1413
1414 pipelessText: function() {
1415 if (this.pipeless) {
1416 if ('\n' == this.input[0]) return;
1417 var i = this.input.indexOf('\n');
1418 if (-1 == i) i = this.input.length;
1419 var str = this.input.substr(0, i);
1420 this.consume(str.length);
1421 return this.tok('text', str);
1422 }
1423 },
1424
1425 /**
1426 * ':'
1427 */
1428
1429 colon: function() {
1430 return this.scan(/^: */, ':');
1431 },
1432
1433 /**
1434 * Return the next token object, or those
1435 * previously stashed by lookahead.
1436 *
1437 * @return {Object}
1438 * @api private
1439 */
1440
1441 advance: function(){
1442 return this.stashed()
1443 || this.next();
1444 },
1445
1446 /**
1447 * Return the next token object.
1448 *
1449 * @return {Object}
1450 * @api private
1451 */
1452
1453 next: function() {
1454 return this.deferred()
1455 || this.eos()
1456 || this.pipelessText()
1457 || this.doctype()
1458 || this.include()
1459 || this.mixin()
1460 || this.tag()
1461 || this.filter()
1462 || this.each()
1463 || this.code()
1464 || this.id()
1465 || this.className()
1466 || this.attrs()
1467 || this.indent()
1468 || this.comment()
1469 || this.colon()
1470 || this.text();
1471 }
1472};
1473
1474}); // module: lexer.js
1475
1476require.register("nodes/block-comment.js", function(module, exports, require){
1477
1478/*!
1479 * Jade - nodes - BlockComment
1480 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1481 * MIT Licensed
1482 */
1483
1484/**
1485 * Module dependencies.
1486 */
1487
1488var Node = require('./node');
1489
1490/**
1491 * Initialize a `BlockComment` with the given `block`.
1492 *
1493 * @param {String} val
1494 * @param {Block} block
1495 * @param {Boolean} buffer
1496 * @api public
1497 */
1498
1499var BlockComment = module.exports = function BlockComment(val, block, buffer) {
1500 this.block = block;
1501 this.val = val;
1502 this.buffer = buffer;
1503};
1504
1505/**
1506 * Inherit from `Node`.
1507 */
1508
1509BlockComment.prototype = new Node;
1510BlockComment.prototype.constructor = BlockComment;
1511
1512}); // module: nodes/block-comment.js
1513
1514require.register("nodes/block.js", function(module, exports, require){
1515
1516/*!
1517 * Jade - nodes - Block
1518 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1519 * MIT Licensed
1520 */
1521
1522/**
1523 * Module dependencies.
1524 */
1525
1526var Node = require('./node');
1527
1528/**
1529 * Initialize a new `Block` with an optional `node`.
1530 *
1531 * @param {Node} node
1532 * @api public
1533 */
1534
1535var Block = module.exports = function Block(node){
1536 this.nodes = [];
1537 if (node) this.push(node);
1538};
1539
1540/**
1541 * Inherit from `Node`.
1542 */
1543
1544Block.prototype = new Node;
1545Block.prototype.constructor = Block;
1546
1547
1548/**
1549 * Pust the given `node`.
1550 *
1551 * @param {Node} node
1552 * @return {Number}
1553 * @api public
1554 */
1555
1556Block.prototype.push = function(node){
1557 return this.nodes.push(node);
1558};
1559
1560/**
1561 * Unshift the given `node`.
1562 *
1563 * @param {Node} node
1564 * @return {Number}
1565 * @api public
1566 */
1567
1568Block.prototype.unshift = function(node){
1569 return this.nodes.unshift(node);
1570};
1571
1572}); // module: nodes/block.js
1573
1574require.register("nodes/code.js", function(module, exports, require){
1575
1576/*!
1577 * Jade - nodes - Code
1578 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1579 * MIT Licensed
1580 */
1581
1582/**
1583 * Module dependencies.
1584 */
1585
1586var Node = require('./node');
1587
1588/**
1589 * Initialize a `Code` node with the given code `val`.
1590 * Code may also be optionally buffered and escaped.
1591 *
1592 * @param {String} val
1593 * @param {Boolean} buffer
1594 * @param {Boolean} escape
1595 * @api public
1596 */
1597
1598var Code = module.exports = function Code(val, buffer, escape) {
1599 this.val = val;
1600 this.buffer = buffer;
1601 this.escape = escape;
1602 if (/^ *else/.test(val)) this.instrumentLineNumber = false;
1603};
1604
1605/**
1606 * Inherit from `Node`.
1607 */
1608
1609Code.prototype = new Node;
1610Code.prototype.constructor = Code;
1611
1612}); // module: nodes/code.js
1613
1614require.register("nodes/comment.js", function(module, exports, require){
1615
1616/*!
1617 * Jade - nodes - Comment
1618 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1619 * MIT Licensed
1620 */
1621
1622/**
1623 * Module dependencies.
1624 */
1625
1626var Node = require('./node');
1627
1628/**
1629 * Initialize a `Comment` with the given `val`, optionally `buffer`,
1630 * otherwise the comment may render in the output.
1631 *
1632 * @param {String} val
1633 * @param {Boolean} buffer
1634 * @api public
1635 */
1636
1637var Comment = module.exports = function Comment(val, buffer) {
1638 this.val = val;
1639 this.buffer = buffer;
1640};
1641
1642/**
1643 * Inherit from `Node`.
1644 */
1645
1646Comment.prototype = new Node;
1647Comment.prototype.constructor = Comment;
1648
1649}); // module: nodes/comment.js
1650
1651require.register("nodes/doctype.js", function(module, exports, require){
1652
1653/*!
1654 * Jade - nodes - Doctype
1655 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1656 * MIT Licensed
1657 */
1658
1659/**
1660 * Module dependencies.
1661 */
1662
1663var Node = require('./node');
1664
1665/**
1666 * Initialize a `Doctype` with the given `val`.
1667 *
1668 * @param {String} val
1669 * @api public
1670 */
1671
1672var Doctype = module.exports = function Doctype(val) {
1673 this.val = val;
1674};
1675
1676/**
1677 * Inherit from `Node`.
1678 */
1679
1680Doctype.prototype = new Node;
1681Doctype.prototype.constructor = Doctype;
1682
1683}); // module: nodes/doctype.js
1684
1685require.register("nodes/each.js", function(module, exports, require){
1686
1687/*!
1688 * Jade - nodes - Each
1689 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1690 * MIT Licensed
1691 */
1692
1693/**
1694 * Module dependencies.
1695 */
1696
1697var Node = require('./node');
1698
1699/**
1700 * Initialize an `Each` node, representing iteration
1701 *
1702 * @param {String} obj
1703 * @param {String} val
1704 * @param {String} key
1705 * @param {Block} block
1706 * @api public
1707 */
1708
1709var Each = module.exports = function Each(obj, val, key, block) {
1710 this.obj = obj;
1711 this.val = val;
1712 this.key = key;
1713 this.block = block;
1714};
1715
1716/**
1717 * Inherit from `Node`.
1718 */
1719
1720Each.prototype = new Node;
1721Each.prototype.constructor = Each;
1722
1723}); // module: nodes/each.js
1724
1725require.register("nodes/filter.js", function(module, exports, require){
1726
1727/*!
1728 * Jade - nodes - Filter
1729 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1730 * MIT Licensed
1731 */
1732
1733/**
1734 * Module dependencies.
1735 */
1736
1737var Node = require('./node')
1738 , Block = require('./block');
1739
1740/**
1741 * Initialize a `Filter` node with the given
1742 * filter `name` and `block`.
1743 *
1744 * @param {String} name
1745 * @param {Block|Node} block
1746 * @api public
1747 */
1748
1749var Filter = module.exports = function Filter(name, block, attrs) {
1750 this.name = name;
1751 this.block = block;
1752 this.attrs = attrs;
1753 this.isASTFilter = block instanceof Block;
1754};
1755
1756/**
1757 * Inherit from `Node`.
1758 */
1759
1760Filter.prototype = new Node;
1761Filter.prototype.constructor = Filter;
1762
1763}); // module: nodes/filter.js
1764
1765require.register("nodes/index.js", function(module, exports, require){
1766
1767/*!
1768 * Jade - nodes
1769 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1770 * MIT Licensed
1771 */
1772
1773exports.Node = require('./node');
1774exports.Tag = require('./tag');
1775exports.Code = require('./code');
1776exports.Each = require('./each');
1777exports.Text = require('./text');
1778exports.Block = require('./block');
1779exports.Mixin = require('./mixin');
1780exports.Filter = require('./filter');
1781exports.Comment = require('./comment');
1782exports.BlockComment = require('./block-comment');
1783exports.Doctype = require('./doctype');
1784
1785}); // module: nodes/index.js
1786
1787require.register("nodes/mixin.js", function(module, exports, require){
1788
1789/*!
1790 * Jade - nodes - Mixin
1791 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1792 * MIT Licensed
1793 */
1794
1795/**
1796 * Module dependencies.
1797 */
1798
1799var Node = require('./node');
1800
1801/**
1802 * Initialize a new `Mixin` with `name` and `block`.
1803 *
1804 * @param {String} name
1805 * @param {String} args
1806 * @param {Block} block
1807 * @api public
1808 */
1809
1810var Mixin = module.exports = function Mixin(name, args, block){
1811 this.name = name;
1812 this.args = args;
1813 this.block = block;
1814};
1815
1816/**
1817 * Inherit from `Node`.
1818 */
1819
1820Mixin.prototype = new Node;
1821Mixin.prototype.constructor = Mixin;
1822
1823
1824
1825}); // module: nodes/mixin.js
1826
1827require.register("nodes/node.js", function(module, exports, require){
1828
1829/*!
1830 * Jade - nodes - Node
1831 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1832 * MIT Licensed
1833 */
1834
1835/**
1836 * Initialize a `Node`.
1837 *
1838 * @api public
1839 */
1840
1841var Node = module.exports = function Node(){};
1842}); // module: nodes/node.js
1843
1844require.register("nodes/tag.js", function(module, exports, require){
1845
1846/*!
1847 * Jade - nodes - Tag
1848 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1849 * MIT Licensed
1850 */
1851
1852/**
1853 * Module dependencies.
1854 */
1855
1856var Node = require('./node'),
1857 Block = require('./block');
1858
1859/**
1860 * Initialize a `Tag` node with the given tag `name` and optional `block`.
1861 *
1862 * @param {String} name
1863 * @param {Block} block
1864 * @api public
1865 */
1866
1867var Tag = module.exports = function Tag(name, block) {
1868 this.name = name;
1869 this.attrs = [];
1870 this.block = block || new Block;
1871};
1872
1873/**
1874 * Inherit from `Node`.
1875 */
1876
1877Tag.prototype = new Node;
1878Tag.prototype.constructor = Tag;
1879
1880
1881/**
1882 * Set attribute `name` to `val`, keep in mind these become
1883 * part of a raw js object literal, so to quote a value you must
1884 * '"quote me"', otherwise or example 'user.name' is literal JavaScript.
1885 *
1886 * @param {String} name
1887 * @param {String} val
1888 * @return {Tag} for chaining
1889 * @api public
1890 */
1891
1892Tag.prototype.setAttribute = function(name, val){
1893 this.attrs.push({ name: name, val: val });
1894 return this;
1895};
1896
1897/**
1898 * Remove attribute `name` when present.
1899 *
1900 * @param {String} name
1901 * @api public
1902 */
1903
1904Tag.prototype.removeAttribute = function(name){
1905 for (var i = 0, len = this.attrs.length; i < len; ++i) {
1906 if (this.attrs[i] && this.attrs[i].name == name) {
1907 delete this.attrs[i];
1908 }
1909 }
1910};
1911
1912/**
1913 * Get attribute value by `name`.
1914 *
1915 * @param {String} name
1916 * @return {String}
1917 * @api public
1918 */
1919
1920Tag.prototype.getAttribute = function(name){
1921 for (var i = 0, len = this.attrs.length; i < len; ++i) {
1922 if (this.attrs[i] && this.attrs[i].name == name) {
1923 return this.attrs[i].val;
1924 }
1925 }
1926};
1927
1928}); // module: nodes/tag.js
1929
1930require.register("nodes/text.js", function(module, exports, require){
1931
1932/*!
1933 * Jade - nodes - Text
1934 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1935 * MIT Licensed
1936 */
1937
1938/**
1939 * Module dependencies.
1940 */
1941
1942var Node = require('./node');
1943
1944/**
1945 * Initialize a `Text` node with optional `line`.
1946 *
1947 * @param {String} line
1948 * @api public
1949 */
1950
1951var Text = module.exports = function Text(line) {
1952 this.nodes = [];
1953 if ('string' == typeof line) this.push(line);
1954};
1955
1956/**
1957 * Inherit from `Node`.
1958 */
1959
1960Text.prototype = new Node;
1961Text.prototype.constructor = Text;
1962
1963
1964/**
1965 * Push the given `node.`
1966 *
1967 * @param {Node} node
1968 * @return {Number}
1969 * @api public
1970 */
1971
1972Text.prototype.push = function(node){
1973 return this.nodes.push(node);
1974};
1975
1976}); // module: nodes/text.js
1977
1978require.register("parser.js", function(module, exports, require){
1979
1980/*!
1981 * Jade - Parser
1982 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1983 * MIT Licensed
1984 */
1985
1986/**
1987 * Module dependencies.
1988 */
1989
1990var Lexer = require('./lexer')
1991 , nodes = require('./nodes');
1992
1993/**
1994 * Initialize `Parser` with the given input `str` and `filename`.
1995 *
1996 * @param {String} str
1997 * @param {String} filename
1998 * @param {Object} options
1999 * @api public
2000 */
2001
2002var Parser = exports = module.exports = function Parser(str, filename, options){
2003 this.input = str;
2004 this.lexer = new Lexer(str, options);
2005 this.filename = filename;
2006};
2007
2008/**
2009 * Tags that may not contain tags.
2010 */
2011
2012var textOnly = exports.textOnly = ['code', 'script', 'textarea', 'style', 'title'];
2013
2014/**
2015 * Parser prototype.
2016 */
2017
2018Parser.prototype = {
2019
2020 /**
2021 * Output parse tree to stdout.
2022 *
2023 * @api public
2024 */
2025
2026 debug: function(){
2027 var lexer = new Lexer(this.input)
2028 , tree = require('sys').inspect(this.parse(), false, 12, true);
2029 console.log('\n\x1b[1mParse Tree\x1b[0m:\n');
2030 console.log(tree);
2031 this.lexer = lexer;
2032 },
2033
2034 /**
2035 * Return the next token object.
2036 *
2037 * @return {Object}
2038 * @api private
2039 */
2040
2041 advance: function(){
2042 return this.lexer.advance();
2043 },
2044
2045 /**
2046 * Single token lookahead.
2047 *
2048 * @return {Object}
2049 * @api private
2050 */
2051
2052 peek: function() {
2053 return this.lookahead(1);
2054 },
2055
2056 /**
2057 * Return lexer lineno.
2058 *
2059 * @return {Number}
2060 * @api private
2061 */
2062
2063 line: function() {
2064 return this.lexer.lineno;
2065 },
2066
2067 /**
2068 * `n` token lookahead.
2069 *
2070 * @param {Number} n
2071 * @return {Object}
2072 * @api private
2073 */
2074
2075 lookahead: function(n){
2076 return this.lexer.lookahead(n);
2077 },
2078
2079 /**
2080 * Parse input returning a string of js for evaluation.
2081 *
2082 * @return {String}
2083 * @api public
2084 */
2085
2086 parse: function(){
2087 var block = new nodes.Block;
2088 block.line = this.line();
2089 while ('eos' != this.peek().type) {
2090 if ('newline' == this.peek().type) {
2091 this.advance();
2092 } else {
2093 block.push(this.parseExpr());
2094 }
2095 }
2096 return block;
2097 },
2098
2099 /**
2100 * Expect the given type, or throw an exception.
2101 *
2102 * @param {String} type
2103 * @api private
2104 */
2105
2106 expect: function(type){
2107 if (this.peek().type === type) {
2108 return this.advance();
2109 } else {
2110 throw new Error('expected "' + type + '", but got "' + this.peek().type + '"');
2111 }
2112 },
2113
2114 /**
2115 * Accept the given `type`.
2116 *
2117 * @param {String} type
2118 * @api private
2119 */
2120
2121 accept: function(type){
2122 if (this.peek().type === type) {
2123 return this.advance();
2124 }
2125 },
2126
2127 /**
2128 * tag
2129 * | doctype
2130 * | mixin
2131 * | include
2132 * | filter
2133 * | comment
2134 * | text
2135 * | each
2136 * | code
2137 * | id
2138 * | class
2139 */
2140
2141 parseExpr: function(){
2142 switch (this.peek().type) {
2143 case 'tag':
2144 return this.parseTag();
2145 case 'mixin':
2146 return this.parseMixin();
2147 case 'include':
2148 return this.parseInclude();
2149 case 'doctype':
2150 return this.parseDoctype();
2151 case 'filter':
2152 return this.parseFilter();
2153 case 'comment':
2154 return this.parseComment();
2155 case 'text':
2156 return this.parseText();
2157 case 'each':
2158 return this.parseEach();
2159 case 'code':
2160 return this.parseCode();
2161 case 'id':
2162 case 'class':
2163 var tok = this.advance();
2164 this.lexer.defer(this.lexer.tok('tag', 'div'));
2165 this.lexer.defer(tok);
2166 return this.parseExpr();
2167 default:
2168 throw new Error('unexpected token "' + this.peek().type + '"');
2169 }
2170 },
2171
2172 /**
2173 * Text
2174 */
2175
2176 parseText: function(){
2177 var tok = this.expect('text')
2178 , node = new nodes.Text(tok.val);
2179 node.line = this.line();
2180 return node;
2181 },
2182
2183 /**
2184 * code
2185 */
2186
2187 parseCode: function(){
2188 var tok = this.expect('code')
2189 , node = new nodes.Code(tok.val, tok.buffer, tok.escape);
2190 node.line = this.line();
2191 if ('indent' == this.peek().type) {
2192 node.block = this.parseBlock();
2193 }
2194 return node;
2195 },
2196
2197 /**
2198 * comment
2199 */
2200
2201 parseComment: function(){
2202 var tok = this.expect('comment')
2203 , node;
2204
2205 if ('indent' == this.peek().type) {
2206 node = new nodes.BlockComment(tok.val, this.parseBlock(), tok.buffer);
2207 } else {
2208 node = new nodes.Comment(tok.val, tok.buffer);
2209 }
2210
2211 node.line = this.line();
2212 return node;
2213 },
2214
2215 /**
2216 * doctype
2217 */
2218
2219 parseDoctype: function(){
2220 var tok = this.expect('doctype')
2221 , node = new nodes.Doctype(tok.val);
2222 node.line = this.line();
2223 return node;
2224 },
2225
2226 /**
2227 * filter attrs? text-block
2228 */
2229
2230 parseFilter: function(){
2231 var block
2232 , tok = this.expect('filter')
2233 , attrs = this.accept('attrs');
2234
2235 this.lexer.pipeless = true;
2236 block = this.parseTextBlock();
2237 this.lexer.pipeless = false;
2238
2239 var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs);
2240 node.line = this.line();
2241 return node;
2242 },
2243
2244 /**
2245 * tag ':' attrs? block
2246 */
2247
2248 parseASTFilter: function(){
2249 var block
2250 , tok = this.expect('tag')
2251 , attrs = this.accept('attrs');
2252
2253 this.expect(':');
2254 block = this.parseBlock();
2255
2256 var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs);
2257 node.line = this.line();
2258 return node;
2259 },
2260
2261 /**
2262 * each block
2263 */
2264
2265 parseEach: function(){
2266 var tok = this.expect('each')
2267 , node = new nodes.Each(tok.code, tok.val, tok.key, this.parseBlock());
2268 node.line = this.line();
2269 return node;
2270 },
2271
2272 /**
2273 * include
2274 */
2275
2276 parseInclude: function(){
2277 var path = require('path')
2278 , fs = require('fs')
2279 , dirname = path.dirname
2280 , join = path.join;
2281
2282 if (!this.filename)
2283 throw new Error('the "filename" option is required to use includes');
2284
2285 var path = name = this.expect('include').val.trim()
2286 , dir = dirname(this.filename)
2287 , path = join(dir, path + '.jade');
2288
2289 var str = fs.readFileSync(path, 'utf8')
2290 , parser = new Parser(str, path)
2291 , ast = parser.parse();
2292
2293 return ast;
2294 },
2295
2296 /**
2297 * mixin block
2298 */
2299
2300 parseMixin: function(){
2301 var tok = this.expect('mixin')
2302 , name = tok.val
2303 , args = tok.args;
2304 var block = 'indent' == this.peek().type
2305 ? this.parseBlock()
2306 : null;
2307 return new nodes.Mixin(name, args, block);
2308 },
2309
2310 /**
2311 * indent (text | newline)* outdent
2312 */
2313
2314 parseTextBlock: function(){
2315 var text = new nodes.Text;
2316 text.line = this.line();
2317 var spaces = this.expect('indent').val;
2318 if (null == this._spaces) this._spaces = spaces;
2319 var indent = Array(spaces - this._spaces + 1).join(' ');
2320 while ('outdent' != this.peek().type) {
2321 switch (this.peek().type) {
2322 case 'newline':
2323 text.push('\\n');
2324 this.advance();
2325 break;
2326 case 'indent':
2327 text.push('\\n');
2328 this.parseTextBlock().nodes.forEach(function(node){
2329 text.push(node);
2330 });
2331 text.push('\\n');
2332 break;
2333 default:
2334 text.push(indent + this.advance().val);
2335 }
2336 }
2337
2338 if (spaces == this._spaces) this._spaces = null;
2339 this.expect('outdent');
2340 return text;
2341 },
2342
2343 /**
2344 * indent expr* outdent
2345 */
2346
2347 parseBlock: function(){
2348 var block = new nodes.Block;
2349 block.line = this.line();
2350 this.expect('indent');
2351 while ('outdent' != this.peek().type) {
2352 if ('newline' == this.peek().type) {
2353 this.advance();
2354 } else {
2355 block.push(this.parseExpr());
2356 }
2357 }
2358 this.expect('outdent');
2359 return block;
2360 },
2361
2362 /**
2363 * tag (attrs | class | id)* (text | code | ':')? newline* block?
2364 */
2365
2366 parseTag: function(){
2367 // ast-filter look-ahead
2368 var i = 2;
2369 if ('attrs' == this.lookahead(i).type) ++i;
2370 if (':' == this.lookahead(i).type) {
2371 if ('indent' == this.lookahead(++i).type) {
2372 return this.parseASTFilter();
2373 }
2374 }
2375
2376 var name = this.advance().val
2377 , tag = new nodes.Tag(name);
2378
2379 tag.line = this.line();
2380
2381 // (attrs | class | id)*
2382 out:
2383 while (true) {
2384 switch (this.peek().type) {
2385 case 'id':
2386 case 'class':
2387 var tok = this.advance();
2388 tag.setAttribute(tok.type, "'" + tok.val + "'");
2389 continue;
2390 case 'attrs':
2391 var obj = this.advance().attrs
2392 , names = Object.keys(obj);
2393 for (var i = 0, len = names.length; i < len; ++i) {
2394 var name = names[i]
2395 , val = obj[name];
2396 tag.setAttribute(name, val);
2397 }
2398 continue;
2399 default:
2400 break out;
2401 }
2402 }
2403
2404 // check immediate '.'
2405 if ('.' == this.peek().val) {
2406 tag.textOnly = true;
2407 this.advance();
2408 }
2409
2410 // (text | code | ':')?
2411 switch (this.peek().type) {
2412 case 'text':
2413 tag.text = this.parseText();
2414 break;
2415 case 'code':
2416 tag.code = this.parseCode();
2417 break;
2418 case ':':
2419 this.advance();
2420 tag.block = new nodes.Block;
2421 tag.block.push(this.parseTag());
2422 break;
2423 }
2424
2425 // newline*
2426 while ('newline' == this.peek().type) this.advance();
2427
2428 tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name);
2429
2430 // script special-case
2431 if ('script' == tag.name) {
2432 var type = tag.getAttribute('type');
2433 if (type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) {
2434 tag.textOnly = false;
2435 }
2436 }
2437
2438 // block?
2439 if ('indent' == this.peek().type) {
2440 if (tag.textOnly) {
2441 this.lexer.pipeless = true;
2442 tag.block = this.parseTextBlock();
2443 this.lexer.pipeless = false;
2444 } else {
2445 var block = this.parseBlock();
2446 if (tag.block) {
2447 for (var i = 0, len = block.nodes.length; i < len; ++i) {
2448 tag.block.push(block.nodes[i]);
2449 }
2450 } else {
2451 tag.block = block;
2452 }
2453 }
2454 }
2455
2456 return tag;
2457 }
2458};
2459
2460}); // module: parser.js
2461
2462require.register("runtime.js", function(module, exports, require){
2463
2464/*!
2465 * Jade - runtime
2466 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2467 * MIT Licensed
2468 */
2469
2470/**
2471 * Render the given attributes object.
2472 *
2473 * @param {Object} obj
2474 * @return {String}
2475 * @api private
2476 */
2477
2478exports.attrs = function attrs(obj){
2479 var buf = []
2480 , terse = obj.terse;
2481 delete obj.terse;
2482 var keys = Object.keys(obj)
2483 , len = keys.length;
2484 if (len) {
2485 buf.push('');
2486 for (var i = 0; i < len; ++i) {
2487 var key = keys[i]
2488 , val = obj[key];
2489 if ('boolean' == typeof val || null == val) {
2490 if (val) {
2491 terse
2492 ? buf.push(key)
2493 : buf.push(key + '="' + key + '"');
2494 }
2495 } else if ('class' == key && Array.isArray(val)) {
2496 buf.push(key + '="' + escape(val.join(' ')) + '"');
2497 } else {
2498 buf.push(key + '="' + escape(val) + '"');
2499 }
2500 }
2501 }
2502 return buf.join(' ');
2503};
2504
2505/**
2506 * Escape the given string of `html`.
2507 *
2508 * @param {String} html
2509 * @return {String}
2510 * @api private
2511 */
2512
2513exports.escape = function escape(html){
2514 return String(html)
2515 .replace(/&(?!\w+;)/g, '&amp;')
2516 .replace(/</g, '&lt;')
2517 .replace(/>/g, '&gt;')
2518 .replace(/"/g, '&quot;');
2519};
2520
2521/**
2522 * Re-throw the given `err` in context to the
2523 * `str` of jade, `filename`, and `lineno`.
2524 *
2525 * @param {Error} err
2526 * @param {String} str
2527 * @param {String} filename
2528 * @param {String} lineno
2529 * @api private
2530 */
2531
2532exports.rethrow = function rethrow(err, str, filename, lineno){
2533 var context = 3
2534 , lines = str.split('\n')
2535 , start = Math.max(lineno - context, 0)
2536 , end = Math.min(lines.length, lineno + context);
2537
2538 // Error context
2539 var context = lines.slice(start, end).map(function(line, i){
2540 var curr = i + start + 1;
2541 return (curr == lineno ? ' > ' : ' ')
2542 + curr
2543 + '| '
2544 + line;
2545 }).join('\n');
2546
2547 // Alter exception message
2548 err.path = filename;
2549 err.message = (filename || 'Jade') + ':' + lineno
2550 + '\n' + context + '\n\n' + err.message;
2551 throw err;
2552};
2553
2554}); // module: runtime.js
2555
2556require.register("self-closing.js", function(module, exports, require){
2557
2558/*!
2559 * Jade - self closing tags
2560 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2561 * MIT Licensed
2562 */
2563
2564module.exports = [
2565 'meta'
2566 , 'img'
2567 , 'link'
2568 , 'input'
2569 , 'area'
2570 , 'base'
2571 , 'col'
2572 , 'br'
2573 , 'hr'
2574];
2575}); // module: self-closing.js
2576
2577require.register("utils.js", function(module, exports, require){
2578
2579/*!
2580 * Jade - utils
2581 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2582 * MIT Licensed
2583 */
2584
2585/**
2586 * Convert interpolation in the given string to JavaScript.
2587 *
2588 * @param {String} str
2589 * @return {String}
2590 * @api private
2591 */
2592
2593var interpolate = exports.interpolate = function(str){
2594 return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){
2595 return escape
2596 ? str
2597 : "' + "
2598 + ('!' == flag ? '' : 'escape')
2599 + "((interp = " + code.replace(/\\'/g, "'")
2600 + ") == null ? '' : interp) + '";
2601 });
2602};
2603
2604/**
2605 * Escape single quotes in `str`.
2606 *
2607 * @param {String} str
2608 * @return {String}
2609 * @api private
2610 */
2611
2612var escape = exports.escape = function(str) {
2613 return str.replace(/'/g, "\\'");
2614};
2615
2616/**
2617 * Interpolate, and escape the given `str`.
2618 *
2619 * @param {String} str
2620 * @return {String}
2621 * @api private
2622 */
2623
2624exports.text = function(str){
2625 return interpolate(escape(str));
2626};
2627}); // module: utils.js