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