UNPKG

generator-egress

Version:
2,035 lines (1,728 loc) 452 kB
(function(e){if("function"==typeof bootstrap)bootstrap("jade",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeJade=e}else"undefined"!=typeof window?window.jade=e():global.jade=e()})(function(){var define,ses,bootstrap,module,exports; return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /*! * Jade - Compiler * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var nodes = require('./nodes') , filters = require('./filters') , doctypes = require('./doctypes') , selfClosing = require('./self-closing') , runtime = require('./runtime') , utils = require('./utils') , parseJSExpression = require('character-parser').parseMax , isConstant = require('constantinople') , toConstant = require('constantinople').toConstant /** * Initialize `Compiler` with the given `node`. * * @param {Node} node * @param {Object} options * @api public */ var Compiler = module.exports = function Compiler(node, options) { this.options = options = options || {}; this.node = node; this.hasCompiledDoctype = false; this.hasCompiledTag = false; this.pp = options.pretty || false; this.debug = false !== options.compileDebug; this.indents = 0; this.parentIndents = 0; if (options.doctype) this.setDoctype(options.doctype); }; /** * Compiler prototype. */ Compiler.prototype = { /** * Compile parse tree to JavaScript. * * @api public */ compile: function(){ this.buf = []; if (this.pp) this.buf.push("jade.indent = [];"); this.lastBufferedIdx = -1; this.visit(this.node); return this.buf.join('\n'); }, /** * Sets the default doctype `name`. Sets terse mode to `true` when * html 5 is used, causing self-closing tags to end with ">" vs "/>", * and boolean attributes are not mirrored. * * @param {string} name * @api public */ setDoctype: function(name){ name = name || 'default'; this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>'; this.terse = this.doctype.toLowerCase() == '<!doctype html>'; this.xml = 0 == this.doctype.indexOf('<?xml'); }, /** * Buffer the given `str` exactly as is or with interpolation * * @param {String} str * @param {Boolean} interpolate * @api public */ buffer: function (str, interpolate) { var self = this; if (interpolate) { var match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str); if (match) { this.buffer(str.substr(0, match.index), false); if (match[1]) { // escape this.buffer(match[2] + '{', false); this.buffer(match[3], true); return; } else { try { var rest = match[3]; var range = parseJSExpression(rest); var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade.interp = " + range.src + ") == null ? '' : jade.interp)"; } catch (ex) { throw ex; //didn't match, just as if escaped this.buffer(match[2] + '{', false); this.buffer(match[3], true); return; } this.bufferExpression(code); this.buffer(rest.substr(range.end + 1), true); return; } } } str = JSON.stringify(str); str = str.substr(1, str.length - 2); if (this.lastBufferedIdx == this.buf.length) { if (this.lastBufferedType === 'code') this.lastBuffered += ' + "'; this.lastBufferedType = 'text'; this.lastBuffered += str; this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + '");' } else { this.buf.push('buf.push("' + str + '");'); this.lastBufferedType = 'text'; this.bufferStartChar = '"'; this.lastBuffered = str; this.lastBufferedIdx = this.buf.length; } }, /** * Buffer the given `src` so it is evaluated at run time * * @param {String} src * @api public */ bufferExpression: function (src) { var fn = Function('', 'return (' + src + ');'); if (isConstant(src)) { return this.buffer(fn(), false) } if (this.lastBufferedIdx == this.buf.length) { if (this.lastBufferedType === 'text') this.lastBuffered += '"'; this.lastBufferedType = 'code'; this.lastBuffered += ' + (' + src + ')'; this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + ');' } else { this.buf.push('buf.push(' + src + ');'); this.lastBufferedType = 'code'; this.bufferStartChar = ''; this.lastBuffered = '(' + src + ')'; this.lastBufferedIdx = this.buf.length; } }, /** * Buffer an indent based on the current `indent` * property and an additional `offset`. * * @param {Number} offset * @param {Boolean} newline * @api public */ prettyIndent: function(offset, newline){ offset = offset || 0; newline = newline ? '\n' : ''; this.buffer(newline + Array(this.indents + offset).join(' ')); if (this.parentIndents) this.buf.push("buf.push.apply(buf, jade.indent);"); }, /** * Visit `node`. * * @param {Node} node * @api public */ visit: function(node){ var debug = this.debug; if (debug) { this.buf.push('jade.debug.unshift({ lineno: ' + node.line + ', filename: ' + (node.filename ? JSON.stringify(node.filename) : 'jade.debug[0].filename') + ' });'); } // Massive hack to fix our context // stack for - else[ if] etc if (false === node.debug && this.debug) { this.buf.pop(); this.buf.pop(); } this.visitNode(node); if (debug) this.buf.push('jade.debug.shift();'); }, /** * Visit `node`. * * @param {Node} node * @api public */ visitNode: function(node){ var name = node.constructor.name || node.constructor.toString().match(/function ([^(\s]+)()/)[1]; return this['visit' + name](node); }, /** * Visit case `node`. * * @param {Literal} node * @api public */ visitCase: function(node){ var _ = this.withinCase; this.withinCase = true; this.buf.push('switch (' + node.expr + '){'); this.visit(node.block); this.buf.push('}'); this.withinCase = _; }, /** * Visit when `node`. * * @param {Literal} node * @api public */ visitWhen: function(node){ if ('default' == node.expr) { this.buf.push('default:'); } else { this.buf.push('case ' + node.expr + ':'); } this.visit(node.block); this.buf.push(' break;'); }, /** * Visit literal `node`. * * @param {Literal} node * @api public */ visitLiteral: function(node){ this.buffer(node.str); }, /** * Visit all nodes in `block`. * * @param {Block} block * @api public */ visitBlock: function(block){ var len = block.nodes.length , escape = this.escape , pp = this.pp // Block keyword has a special meaning in mixins if (this.parentIndents && block.mode) { if (pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join(' ') + "');") this.buf.push('block && block();'); if (pp) this.buf.push("jade.indent.pop();") return; } // Pretty print multi-line text if (pp && len > 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) this.prettyIndent(1, true); for (var i = 0; i < len; ++i) { // Pretty print text if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) this.prettyIndent(1, false); this.visit(block.nodes[i]); // Multiple text nodes are separated by newlines if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) this.buffer('\n'); } }, /** * Visit `doctype`. Sets terse mode to `true` when html 5 * is used, causing self-closing tags to end with ">" vs "/>", * and boolean attributes are not mirrored. * * @param {Doctype} doctype * @api public */ visitDoctype: function(doctype){ if (doctype && (doctype.val || !this.doctype)) { this.setDoctype(doctype.val || 'default'); } if (this.doctype) this.buffer(this.doctype); this.hasCompiledDoctype = true; }, /** * Visit `mixin`, generating a function that * may be called within the template. * * @param {Mixin} mixin * @api public */ visitMixin: function(mixin){ var name = mixin.name.replace(/-/g, '_') + '_mixin' , args = mixin.args || '' , block = mixin.block , attrs = mixin.attrs , pp = this.pp; if (mixin.call) { if (pp) this.buf.push("jade.indent.push('" + Array(this.indents + 1).join(' ') + "');") if (block || attrs.length) { this.buf.push(name + '.call({'); if (block) { this.buf.push('block: function(){'); // Render block with no indents, dynamically added when rendered this.parentIndents++; var _indents = this.indents; this.indents = 0; this.visit(mixin.block); this.indents = _indents; this.parentIndents--; if (attrs.length) { this.buf.push('},'); } else { this.buf.push('}'); } } if (attrs.length) { var val = this.attrs(attrs); if (val.inherits) { this.buf.push('attributes: jade.merge({' + val.buf + '}, attributes), escaped: jade.merge(' + val.escaped + ', escaped, true)'); } else { this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); } } if (args) { this.buf.push('}, ' + args + ');'); } else { this.buf.push('});'); } } else { this.buf.push(name + '(' + args + ');'); } if (pp) this.buf.push("jade.indent.pop();") } else { this.buf.push('var ' + name + ' = function(' + args + '){'); this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); this.parentIndents++; this.visit(block); this.parentIndents--; this.buf.push('};'); } }, /** * Visit `tag` buffering tag markup, generating * attributes, visiting the `tag`'s code and block. * * @param {Tag} tag * @api public */ visitTag: function(tag){ this.indents++; var name = tag.name , pp = this.pp , self = this; function bufferName() { if (tag.buffer) self.bufferExpression(name); else self.buffer(name); } if (!this.hasCompiledTag) { if (!this.hasCompiledDoctype && 'html' == name) { this.visitDoctype(); } this.hasCompiledTag = true; } // pretty print if (pp && !tag.isInline()) this.prettyIndent(0, true); if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { this.buffer('<'); bufferName(); this.visitAttributes(tag.attrs); this.terse ? this.buffer('>') : this.buffer('/>'); } else { // Optimize attributes buffering if (tag.attrs.length) { this.buffer('<'); bufferName(); if (tag.attrs.length) this.visitAttributes(tag.attrs); this.buffer('>'); } else { this.buffer('<'); bufferName(); this.buffer('>'); } if (tag.code) this.visitCode(tag.code); this.escape = 'pre' == tag.name; this.visit(tag.block); // pretty print if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) this.prettyIndent(0, true); this.buffer('</'); bufferName(); this.buffer('>'); } this.indents--; }, /** * Visit `filter`, throwing when the filter does not exist. * * @param {Filter} filter * @api public */ visitFilter: function(filter){ var text = filter.block.nodes.map( function(node){ return node.val; } ).join('\n'); filter.attrs = filter.attrs || {}; filter.attrs.filename = this.options.filename; this.buffer(filters(filter.name, text, filter.attrs), true); }, /** * Visit `text` node. * * @param {Text} text * @api public */ visitText: function(text){ this.buffer(text.val, true); }, /** * Visit a `comment`, only buffering when the buffer flag is set. * * @param {Comment} comment * @api public */ visitComment: function(comment){ if (!comment.buffer) return; if (this.pp) this.prettyIndent(1, true); this.buffer('<!--' + comment.val + '-->'); }, /** * Visit a `BlockComment`. * * @param {Comment} comment * @api public */ visitBlockComment: function(comment){ if (!comment.buffer) return; if (this.pp) this.prettyIndent(1, true); if (0 == comment.val.trim().indexOf('if')) { this.buffer('<!--[' + comment.val.trim() + ']>'); this.visit(comment.block); if (this.pp) this.prettyIndent(1, true); this.buffer('<![endif]-->'); } else { this.buffer('<!--' + comment.val); this.visit(comment.block); if (this.pp) this.prettyIndent(1, true); this.buffer('-->'); } }, /** * Visit `code`, respecting buffer / escape flags. * If the code is followed by a block, wrap it in * a self-calling function. * * @param {Code} code * @api public */ visitCode: function(code){ // Wrap code blocks with {}. // we only wrap unbuffered code blocks ATM // since they are usually flow control // Buffer code if (code.buffer) { var val = code.val.trimLeft(); val = 'null == (jade.interp = '+val+') ? "" : jade.interp'; if (code.escape) val = 'jade.escape(' + val + ')'; this.bufferExpression(val); } else { this.buf.push(code.val); } // Block support if (code.block) { if (!code.buffer) this.buf.push('{'); this.visit(code.block); if (!code.buffer) this.buf.push('}'); } }, /** * Visit `each` block. * * @param {Each} each * @api public */ visitEach: function(each){ this.buf.push('' + '// iterate ' + each.obj + '\n' + ';(function(){\n' + ' var $$obj = ' + each.obj + ';\n' + ' if (\'number\' == typeof $$obj.length) {\n'); if (each.alternative) { this.buf.push(' if ($$obj.length) {'); } this.buf.push('' + ' for (var ' + each.key + ' = 0, $$l = $$obj.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + ' var ' + each.val + ' = $$obj[' + each.key + '];\n'); this.visit(each.block); this.buf.push(' }\n'); if (each.alternative) { this.buf.push(' } else {'); this.visit(each.alternative); this.buf.push(' }'); } this.buf.push('' + ' } else {\n' + ' var $$l = 0;\n' + ' for (var ' + each.key + ' in $$obj) {\n' + ' $$l++;' + ' var ' + each.val + ' = $$obj[' + each.key + '];\n'); this.visit(each.block); this.buf.push(' }\n'); if (each.alternative) { this.buf.push(' if ($$l === 0) {'); this.visit(each.alternative); this.buf.push(' }'); } this.buf.push(' }\n}).call(this);\n'); }, /** * Visit `attrs`. * * @param {Array} attrs * @api public */ visitAttributes: function(attrs){ var val = this.attrs(attrs); if (val.inherits) { this.bufferExpression("jade.attrs(jade.merge({ " + val.buf + " }, attributes), jade.merge(" + val.escaped + ", escaped, true))"); } else if (val.constant) { this.buffer(runtime.attrs(toConstant('{' + val.buf + '}'), JSON.parse(val.escaped))); } else { this.bufferExpression("jade.attrs({ " + val.buf + " }, " + val.escaped + ")"); } }, /** * Compile attributes. */ attrs: function(attrs){ var buf = [] , classes = [] , escaped = {} , constant = attrs.every(function(attr){ return isConstant(attr.val) }) , inherits = false; if (this.terse) buf.push('terse: true'); attrs.forEach(function(attr){ if (attr.name == 'attributes') return inherits = true; escaped[attr.name] = attr.escaped; if (attr.name == 'class') { classes.push('(' + attr.val + ')'); } else { var pair = "'" + attr.name + "':(" + attr.val + ')'; buf.push(pair); } }); if (classes.length) { buf.push('"class": [' + classes.join(',') + ']'); } return { buf: buf.join(', '), escaped: JSON.stringify(escaped), inherits: inherits, constant: constant }; } }; },{"./doctypes":2,"./filters":3,"./nodes":16,"./runtime":23,"./self-closing":24,"./utils":25,"character-parser":31,"constantinople":32}],2:[function(require,module,exports){ /*! * Jade - doctypes * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ module.exports = { '5': '<!DOCTYPE html>' , 'default': '<!DOCTYPE html>' , 'xml': '<?xml version="1.0" encoding="utf-8" ?>' , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' , 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' , 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">' , '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' , 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">' , 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">' }; },{}],3:[function(require,module,exports){ /*! * Jade - filters * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ module.exports = filter; function filter(name, str, options) { if (typeof filter[name] === 'function') { var res = filter[name](str, options); } else { throw new Error('unknown filter ":' + name + '"'); } return res; } filter.exists = function (name, str, options) { return typeof filter[name] === 'function'; }; },{}],4:[function(require,module,exports){ /*! * Jade - inline tags * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ module.exports = [ 'a' , 'abbr' , 'acronym' , 'b' , 'br' , 'code' , 'em' , 'font' , 'i' , 'img' , 'ins' , 'kbd' , 'map' , 'samp' , 'small' , 'span' , 'strong' , 'sub' , 'sup' ]; },{}],5:[function(require,module,exports){ /*! * Jade * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Parser = require('./parser') , Lexer = require('./lexer') , Compiler = require('./compiler') , runtime = require('./runtime') , addWith = require('with') , fs = require('fs'); /** * Expose self closing tags. */ exports.selfClosing = require('./self-closing'); /** * Default supported doctypes. */ exports.doctypes = require('./doctypes'); /** * Text filters. */ exports.filters = require('./filters'); /** * Utilities. */ exports.utils = require('./utils'); /** * Expose `Compiler`. */ exports.Compiler = Compiler; /** * Expose `Parser`. */ exports.Parser = Parser; /** * Expose `Lexer`. */ exports.Lexer = Lexer; /** * Nodes. */ exports.nodes = require('./nodes'); /** * Jade runtime helpers. */ exports.runtime = runtime; /** * Template function cache. */ exports.cache = {}; /** * Parse the given `str` of jade and return a function body. * * @param {String} str * @param {Object} options * @return {String} * @api private */ function parse(str, options){ try { // Parse var parser = new (options.parser || Parser)(str, options.filename, options); // Compile var compiler = new (options.compiler || Compiler)(parser.parse(), options) , js = compiler.compile(); // Debug compiler if (options.debug) { console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); } return '' + 'var buf = [];\n' + (options.self ? 'var self = locals || {};\n' + js : addWith('locals || {}', js, ['jade', 'buf'])) + ';' + 'return buf.join("");'; } catch (err) { parser = parser.context(); runtime.rethrow(err, parser.filename, parser.lexer.lineno, str); } } /** * Compile a `Function` representation of the given jade `str`. * * Options: * * - `compileDebug` when `false` debugging code is stripped from the compiled template, when it is explicitly `true`, the source code is included in the compiled template for better accuracy. * - `filename` used to improve errors when `compileDebug` is not `false` * * @param {String} str * @param {Options} options * @return {Function} * @api public */ exports.compile = function(str, options){ var options = options || {} , filename = options.filename ? JSON.stringify(options.filename) : 'undefined' , fn; str = String(str); if (options.compileDebug !== false) { fn = [ 'jade.debug = [{ lineno: 1, filename: ' + filename + ' }];' , 'try {' , parse(str, options) , '} catch (err) {' , ' jade.rethrow(err, jade.debug[0].filename, jade.debug[0].lineno' + (options.compileDebug === true ? ',' + JSON.stringify(str) : '') + ');' , '}' ].join('\n'); } else { fn = parse(str, options); } if (options.client) return new Function('locals', fn) fn = new Function('locals, jade', fn) return function(locals){ return fn(locals, Object.create(runtime)) } }; /** * Render the given `str` of jade. * * Options: * * - `cache` enable template caching * - `filename` filename required for `include` / `extends` and caching * * @param {String} str * @param {Object|Function} options or fn * @param {Function|undefined} fn * @returns {String} * @api public */ exports.render = function(str, options, fn){ // support callback API if ('function' == typeof options) { fn = options, options = undefined; } if (typeof fn === 'function') { var res try { res = exports.render(str, options); } catch (ex) { return fn(ex); } return fn(null, res); } options = options || {}; // cache requires .filename if (options.cache && !options.filename) { throw new Error('the "filename" option is required for caching'); } var path = options.filename; var tmpl = options.cache ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) : exports.compile(str, options); return tmpl(options); }; /** * Render a Jade file at the given `path`. * * @param {String} path * @param {Object|Function} options or callback * @param {Function|undefined} fn * @returns {String} * @api public */ exports.renderFile = function(path, options, fn){ // support callback API if ('function' == typeof options) { fn = options, options = undefined; } if (typeof fn === 'function') { var res try { res = exports.renderFile(path, options); } catch (ex) { return fn(ex); } return fn(null, res); } options = options || {}; var key = path + ':string'; options.filename = path; var str = options.cache ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) : fs.readFileSync(path, 'utf8'); return exports.render(str, options); }; /** * Express support. */ exports.__express = exports.renderFile; },{"./compiler":1,"./doctypes":2,"./filters":3,"./lexer":6,"./nodes":16,"./parser":22,"./runtime":23,"./self-closing":24,"./utils":25,"fs":27,"with":44}],6:[function(require,module,exports){ /*! * Jade - Lexer * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ var utils = require('./utils'); var characterParser = require('character-parser'); /** * Initialize `Lexer` with the given `str`. * * Options: * * - `colons` allow colons for attr delimiters * * @param {String} str * @param {Object} options * @api private */ var Lexer = module.exports = function Lexer(str, options) { options = options || {}; this.input = str.replace(/\r\n|\r/g, '\n'); this.colons = options.colons; this.deferredTokens = []; this.lastIndents = 0; this.lineno = 1; this.stash = []; this.indentStack = []; this.indentRe = null; this.pipeless = false; }; function assertExpression(exp) { //this verifies that a JavaScript expression is valid Function('', 'return (' + exp + ')'); } function assertNestingCorrect(exp) { //this verifies that code is properly nested, but allows //invalid JavaScript such as the contents of `attributes` var res = characterParser(exp) if (res.isNesting()) { throw new Error('Nesting must match on expression `' + exp + '`') } } /** * Lexer prototype. */ Lexer.prototype = { /** * Construct a token with the given `type` and `val`. * * @param {String} type * @param {String} val * @return {Object} * @api private */ tok: function(type, val){ return { type: type , line: this.lineno , val: val } }, /** * Consume the given `len` of input. * * @param {Number} len * @api private */ consume: function(len){ this.input = this.input.substr(len); }, /** * Scan for `type` with the given `regexp`. * * @param {String} type * @param {RegExp} regexp * @return {Object} * @api private */ scan: function(regexp, type){ var captures; if (captures = regexp.exec(this.input)) { this.consume(captures[0].length); return this.tok(type, captures[1]); } }, /** * Defer the given `tok`. * * @param {Object} tok * @api private */ defer: function(tok){ this.deferredTokens.push(tok); }, /** * Lookahead `n` tokens. * * @param {Number} n * @return {Object} * @api private */ lookahead: function(n){ var fetch = n - this.stash.length; while (fetch-- > 0) this.stash.push(this.next()); return this.stash[--n]; }, /** * Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters. * * @return {Number} * @api private */ bracketExpression: function(skip){ skip = skip || 0; var start = this.input[skip]; if (start != '(' && start != '{' && start != '[') throw new Error('unrecognized start character'); var end = ({'(': ')', '{': '}', '[': ']'})[start]; var range = characterParser.parseMax(this.input, {start: skip + 1}); if (this.input[range.end] !== end) throw new Error('start character ' + start + ' does not match end character ' + this.input[range.end]); return range; }, /** * Stashed token. */ stashed: function() { return this.stash.length && this.stash.shift(); }, /** * Deferred token. */ deferred: function() { return this.deferredTokens.length && this.deferredTokens.shift(); }, /** * end-of-source. */ eos: function() { if (this.input.length) return; if (this.indentStack.length) { this.indentStack.shift(); return this.tok('outdent'); } else { return this.tok('eos'); } }, /** * Blank line. */ blank: function() { var captures; if (captures = /^\n *\n/.exec(this.input)) { this.consume(captures[0].length - 1); ++this.lineno; if (this.pipeless) return this.tok('text', ''); return this.next(); } }, /** * Comment. */ comment: function() { var captures; if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { this.consume(captures[0].length); var tok = this.tok('comment', captures[2]); tok.buffer = '-' != captures[1]; return tok; } }, /** * Interpolated tag. */ interpolation: function() { if (/^#\{/.test(this.input)) { var match; try { match = this.bracketExpression(1); } catch (ex) { return;//not an interpolation expression, just an unmatched open interpolation } this.consume(match.end + 1); return this.tok('interpolation', match.src); } }, /** * Tag. */ tag: function() { var captures; if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { this.consume(captures[0].length); var tok, name = captures[1]; if (':' == name[name.length - 1]) { name = name.slice(0, -1); tok = this.tok('tag', name); this.defer(this.tok(':')); while (' ' == this.input[0]) this.input = this.input.substr(1); } else { tok = this.tok('tag', name); } tok.selfClosing = !! captures[2]; return tok; } }, /** * Filter. */ filter: function() { return this.scan(/^:(\w+)/, 'filter'); }, /** * Doctype. */ doctype: function() { return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); }, /** * Id. */ id: function() { return this.scan(/^#([\w-]+)/, 'id'); }, /** * Class. */ className: function() { return this.scan(/^\.([\w-]+)/, 'class'); }, /** * Text. */ text: function() { return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); }, /** * Extends. */ "extends": function() { return this.scan(/^extends? +([^\n]+)/, 'extends'); }, /** * Block prepend. */ prepend: function() { var captures; if (captures = /^prepend +([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var mode = 'prepend' , name = captures[1] , tok = this.tok('block', name); tok.mode = mode; return tok; } }, /** * Block append. */ append: function() { var captures; if (captures = /^append +([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var mode = 'append' , name = captures[1] , tok = this.tok('block', name); tok.mode = mode; return tok; } }, /** * Block. */ block: function() { var captures; if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { this.consume(captures[0].length); var mode = captures[1] || 'replace' , name = captures[2] , tok = this.tok('block', name); tok.mode = mode; return tok; } }, /** * Yield. */ yield: function() { return this.scan(/^yield */, 'yield'); }, /** * Include. */ include: function() { return this.scan(/^include +([^\n]+)/, 'include'); }, /** * Case. */ "case": function() { return this.scan(/^case +([^\n]+)/, 'case'); }, /** * When. */ when: function() { return this.scan(/^when +([^:\n]+)/, 'when'); }, /** * Default. */ "default": function() { return this.scan(/^default */, 'default'); }, /** * Assignment. */ assignment: function() { var captures; if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { this.consume(captures[0].length); var name = captures[1] , val = captures[2]; return this.tok('code', 'var ' + name + ' = (' + val + ');'); } }, /** * Call mixin. */ call: function(){ var captures; if (captures = /^\+([-\w]+)/.exec(this.input)) { this.consume(captures[0].length); var tok = this.tok('call', captures[1]); // Check for args (not attributes) if (captures = /^ *\(/.exec(this.input)) { try { var range = this.bracketExpression(captures[0].length - 1); if (!/^ *[-\w]+ *=/.test(range.src)) { // not attributes this.consume(range.end + 1); tok.args = range.src; } } catch (ex) { //not a bracket expcetion, just unmatched open parens } } return tok; } }, /** * Mixin. */ mixin: function(){ var captures; if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { this.consume(captures[0].length); var tok = this.tok('mixin', captures[1]); tok.args = captures[2]; return tok; } }, /** * Conditional. */ conditional: function() { var captures; if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { this.consume(captures[0].length); var type = captures[1] , js = captures[2]; switch (type) { case 'if': assertExpression(js) js = 'if (' + js + ')'; break; case 'unless': assertExpression(js) js = 'if (!(' + js + '))'; break; case 'else if': assertExpression(js) js = 'else if (' + js + ')'; break; case 'else': if (js && js.trim()) { throw new Error('`else` cannot have a condition, perhaps you meant `else if`'); } js = 'else'; break; } return this.tok('code', js); } }, /** * While. */ "while": function() { var captures; if (captures = /^while +([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); assertExpression(captures[1]) return this.tok('code', 'while (' + captures[1] + ')'); } }, /** * Each. */ each: function() { var captures; if (captures = /^(?:- *)?(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var tok = this.tok('each', captures[1]); tok.key = captures[2] || '$index'; assertExpression(captures[3]) tok.code = captures[3]; return tok; } }, /** * Code. */ code: function() { var captures; if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var flags = captures[1]; captures[1] = captures[2]; var tok = this.tok('code', captures[1]); tok.escape = flags.charAt(0) === '='; tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '='; if (tok.buffer) assertExpression(captures[1]) return tok; } }, /** * Attributes. */ attrs: function() { if ('(' == this.input.charAt(0)) { var index = this.bracketExpression().end , str = this.input.substr(1, index-1) , tok = this.tok('attrs') , equals = this.colons ? ':' : '='; if (equals === ':') { console.warn('`:` in jade is deprecated, please use `=`'); } assertNestingCorrect(str); var quote = ''; function interpolate(attr) { return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){ if (escape) return _; try { var range = characterParser.parseMax(expr); if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2)); assertExpression(range.src) return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1)); } catch (ex) { return _.substr(0, 2) + interpolate(_.substr(2)); } }); } this.consume(index + 1); tok.attrs = {}; tok.escaped = {}; var escapedAttr = true var key = ''; var val = ''; var interpolatable = ''; var state = characterParser.defaultState(); var loc = 'key'; function isEndOfAttribute(i) { if (key.trim() === '') return false; if (i === str.length) return true; if (loc === 'key') { if (str[i] === ' ' || str[i] === '\n') { for (var x = i; x < str.length; x++) { if (str[x] != ' ' && str[x] != '\n') { if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false; else return true; } } } return str[i] === ',' } else if (loc === 'value' && !state.isNesting()) { try { Function('', 'return (' + val + ');'); if (str[i] === ' ' || str[i] === '\n') { for (var x = i; x < str.length; x++) { if (str[x] != ' ' && str[x] != '\n') { if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false; else return true; } } } return str[i] === ','; } catch (ex) { return false; } } } for (var i = 0; i <= str.length; i++) { if (isEndOfAttribute(i)) { val = val.trim(); if (val) assertExpression(val) key = key.trim(); key = key.replace(/^['"]|['"]$/g, ''); tok.escaped[key] = escapedAttr; tok.attrs[key] = '' == val ? true : val; key = val = ''; loc = 'key'; escapedAttr = false; } else { switch (loc) { case 'key-char': if (str[i] === quote) { loc = 'key'; if (i + 1 < str.length && [' ', ',', '!', equals, '\n'].indexOf(str[i + 1]) === -1) throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`'); } else if (loc === 'key-char') { key += str[i]; } break; case 'key': if (key === '' && (str[i] === '"' || str[i] === "'")) { loc = 'key-char'; quote = str[i]; } else if (str[i] === '!' || str[i] === equals) { escapedAttr = str[i] !== '!'; if (str[i] === '!') i++; if (str[i] !== equals) throw new Error('Unexpected character ' + str[i] + ' expected `=`'); loc = 'value'; state = characterParser.defaultState(); } else { key += str[i] } break; case 'value': state = characterParser.parseChar(str[i], state); if (state.isString()) { loc = 'string'; quote = str[i]; interpolatable = str[i]; } else { val += str[i]; } break; case 'string': state = characterParser.parseChar(str[i], state); interpolatable += str[i]; if (!state.isString()) { loc = 'value'; val += interpolate(interpolatable); } break; } } } if ('/' == this.input.charAt(0)) { this.consume(1); tok.selfClosing = true; } return tok; } }, /** * Indent | Outdent | Newline. */ indent: function() { var captures, re; // established regexp if (this.indentRe) { captures = this.indentRe.exec(this.input); // determine regexp } else { // tabs re = /^\n(\t*) */; captures = re.exec(this.input); // spaces if (captures && !captures[1].length) { re = /^\n( *)/; captures = re.exec(this.input); } // established if (captures && captures[1].length) this.indentRe = re; } if (captures) { var tok , indents = captures[1].length; ++this.lineno; this.consume(indents + 1); if (' ' == this.input[0] || '\t' == this.input[0]) { throw new Error('Invalid indentation, you can use tabs or spaces but not both'); } // blank line if ('\n' == this.input[0]) return this.tok('newline'); // outdent if (this.indentStack.length && indents < this.indentStack[0]) { while (this.indentStack.length && this.indentStack[0] > indents) { this.stash.push(this.tok('outdent')); this.indentStack.shift(); } tok = this.stash.pop(); // indent } else if (indents && indents != this.indentStack[0]) { this.indentStack.unshift(indents); tok = this.tok('indent', indents); // newline } else { tok = this.tok('newline'); } return tok; } }, /** * Pipe-less text consumed only when * pipeless is true; */ pipelessText: function() { if (this.pipeless) { if ('\n' == this.input[0]) return; var i = this.input.indexOf('\n'); if (-1 == i) i = this.input.length; var str = this.input.substr(0, i); this.consume(str.length); return this.tok('text', str); } }, /** * ':' */ colon: function() { return this.scan(/^: */, ':'); }, /** * Return the next token object, or those * previously stashed by lookahead. * * @return {Object} * @api private */ advance: function(){ return this.stashed() || this.next(); }, /** * Return the next token object. * * @return {Object} * @api private */ next: function() { return this.deferred() || this.blank() || this.eos() || this.pipelessText() || this.yield() || this.doctype() || this.interpolation() || this["case"]() || this.when() || this["default"]() || this["extends"]() || this.append() || this.prepend() || this.block() || this.include() || this.mixin() || this.call() || this.conditional() || this.each() || this["while"]() || this.assignment() || this.tag() || this.filter() || this.code() || this.id() || this.className() || this.attrs() || this.indent() || this.comment() || this.colon() || this.text(); } }; },{"./utils":25,"character-parser":31}],7:[function(require,module,exports){ /*! * Jade - nodes - Attrs * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'), Block = require('./block'); /** * Initialize a `Attrs` node. * * @api public */ var Attrs = module.exports = function Attrs() { this.attrs = []; }; /** * Inherit from `Node`. */ Attrs.prototype = Object.create(Node.prototype); Attrs.prototype.constructor = Attrs; Attrs.prototype.constructor.name = 'Attrs'; // prevent the minifiers removing this /** * Set attribute `name` to `val`, keep in mind these become * part of a raw js object literal, so to quote a value you must * '"quote me"', otherwise or example 'user.name' is literal JavaScript. * * @param {String} name * @param {String} val * @param {Boolean} escaped * @return {Tag} for chaining * @api public */ Attrs.prototype.setAttribute = function(name, val, escaped){ this.attrs.push({ name: name, val: val, escaped: escaped }); return this; }; /** * Remove attribute `name` when present. * * @param {String} name * @api public */ Attrs.prototype.removeAttribute = function(name){ for (var i = 0, len = this.attrs.length; i < len; ++i) { if (this.attrs[i] && this.attrs[i].name == name) { delete this.attrs[i]; } } }; /** * Get attribute value by `name`. * * @param {String} name * @return {String} * @api public */ Attrs.prototype.getAttribute = function(name){ for (var i = 0, len = this.attrs.length; i < len; ++i) { if (this.attrs[i] && this.attrs[i].name == name) { return this.attrs[i].val; } } }; },{"./block":9,"./node":19}],8:[function(require,module,exports){ /*! * Jade - nodes - BlockComment * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a `BlockComment` with the given `block`. * * @param {String} val * @param {Block} block * @param {Boolean} buffer * @api public */ var BlockComment = module.exports = function BlockComment(val, block, buffer) { this.block = block; this.val = val; this.buffer = buffer; }; /** * Inherit from `Node`. */ BlockComment.prototype = Object.create(Node.prototype); BlockComment.prototype.constructor = BlockComment; BlockComment.prototype.constructor.name = 'BlockComment'; // prevent the minifiers removing this },{"./node":19}],9:[function(require,module,exports){ /*! * Jade - nodes - Block * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a new `Block` with an optional `node`. * * @param {Node} node * @api public */ var Block = module.exports = function Block(node){ this.nodes = []; if (node) this.push(node); }; /** * Inherit from `Node`. */ Block.prototype = Object.create(Node.prototype); Block.prototype.constructor = Block; Block.prototype.constructor.name = 'Block'; // prevent the minifiers removing this /** * Block flag. */ Block.prototype.isBlock = true; /** * Replace the nodes in `other` with the nodes * in `this` block. * * @param {Block} other * @api private */ Block.prototype.replace = function(other){ other.nodes = this.nodes; }; /** * Pust the given `node`. * * @param {Node} node * @return {Number} * @api public */ Block.prototype.push = function(node){ return this.nodes.push(node); }; /** * Check if this block is empty. * * @return {Boolean} * @api public */ Block.prototype.isEmpty = function(){ return 0 == this.nodes.length; }; /** * Unshift the given `node`. * * @param {Node} node * @return {Number} * @api public */ Block.prototype.unshift = function(node){ return this.nodes.unshift(node); }; /** * Return the "last" block, or the first `yield` node. * * @return {Block} * @api private */ Block.prototype.includeBlock = function(){ var ret = this , node; for (var i = 0, len = this.nodes.length; i < len; ++i) { node = this.nodes[i]; if (node.yield) return node; else if (node.textOnly) continue; else if (node.includeBlock) ret = node.includeBlock(); else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); if (ret.yield) return ret; } return ret; }; /** * Return a clone of this block. * * @return {Block} * @api private */ Block.prototype.clone = function(){ var clone = new Block; for (var i = 0, len = this.nodes.length; i < len; ++i) { clone.push(this.nodes[i].clone()); } return clone; }; },{"./node":19}],10:[function(require,module,exports){ /*! * Jade - nodes - Case * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a new `Case` with `expr`. * * @param {String} expr * @api public */ var Case = exports = module.exports = function Case(expr, block){ this.expr = expr; this.block = block; }; /** * Inherit from `Node`. */ Case.prototype = Object.create(Node.prototype); Case.prototype.constructor = Case; Case.prototype.constructor.name = 'Case'; // prevent the minifiers removing this var When = exports.When = function Wh