UNPKG

d2-ui

Version:
2,343 lines (1,941 loc) 75 kB
(function() { // CommonJS require() function require(p){ var path = require.resolve(p) , mod = require.modules[path]; if (!mod) throw new Error('failed to require "' + p + '"'); if (!mod.exports) { mod.exports = {}; mod.call(mod.exports, mod, mod.exports, require.relative(path)); } return mod.exports; } require.modules = {}; require.resolve = function (path){ var orig = path , reg = path + '.js' , index = path + '/index.js'; return require.modules[reg] && reg || require.modules[index] && index || orig; }; require.register = function (path, fn){ require.modules[path] = fn; }; require.relative = function (parent) { return function(p){ if ('.' != p.charAt(0)) return require(p); var path = parent.split('/') , segs = p.split('/'); path.pop(); for (var i = 0; i < segs.length; i++) { var seg = segs[i]; if ('..' == seg) path.pop(); else if ('.' != seg) path.push(seg); } return require(path.join('/')); }; }; require.register("compiler.js", function(module, exports, require){ /*! * 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'); if (!Object.keys) { Object.keys = function(obj){ var arr = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { arr.push(key); } } return arr; } } if (!String.prototype.trimLeft) { String.prototype.trimLeft = function(){ return this.replace(/^\s+/, ''); } } /** * 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 = ['var interp;']; if (this.pp) this.buf.push("var __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){ var doctype = doctypes[(name || 'default').toLowerCase()]; doctype = doctype || '<!DOCTYPE ' + name + '>'; this.doctype = doctype; this.terse = '5' == name || 'html' == name; this.xml = 0 == this.doctype.indexOf('<?xml'); }, /** * Buffer the given `str` optionally escaped. * * @param {String} str * @param {Boolean} esc * @api public */ buffer: function(str, esc){ if (esc) str = utils.escape(str); if (this.lastBufferedIdx == this.buf.length) { this.lastBuffered += str; this.buf[this.lastBufferedIdx - 1] = "buf.push('" + this.lastBuffered + "');" } else { this.buf.push("buf.push('" + str + "');"); this.lastBuffered = str; 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, __indent);"); }, /** * Visit `node`. * * @param {Node} node * @api public */ visit: function(node){ var debug = this.debug; if (debug) { this.buf.push('__jade.unshift({ lineno: ' + node.line + ', filename: ' + (node.filename ? JSON.stringify(node.filename) : '__jade[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.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){ var str = node.str.replace(/\n/g, '\\\\n'); this.buffer(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("__indent.push('" + Array(this.indents + 1).join(' ') + "');") this.buf.push('block && block();'); if (pp) this.buf.push("__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("__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: merge({' + val.buf + '}, attributes), escaped: 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("__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; if (tag.buffer) name = "' + (" + 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('<' + name); this.visitAttributes(tag.attrs); this.terse ? this.buffer('>') : this.buffer('/>'); } else { // Optimize attributes buffering if (tag.attrs.length) { this.buffer('<' + name); if (tag.attrs.length) this.visitAttributes(tag.attrs); this.buffer('>'); } else { this.buffer('<' + name + '>'); } 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('</' + name + '>'); } this.indents--; }, /** * Visit `filter`, throwing when the filter does not exist. * * @param {Filter} filter * @api public */ visitFilter: function(filter){ var fn = filters[filter.name]; // unknown filter if (!fn) { if (filter.isASTFilter) { throw new Error('unknown ast filter "' + filter.name + ':"'); } else { throw new Error('unknown filter ":' + filter.name + '"'); } } if (filter.isASTFilter) { this.buf.push(fn(filter.block, this, filter.attrs)); } else { 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(utils.text(fn(text, filter.attrs))); } }, /** * Visit `text` node. * * @param {Text} text * @api public */ visitText: function(text){ text = utils.text(text.val.replace(/\\/g, '\\\\')); if (this.escape) text = escape(text); this.buffer(text); }, /** * 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('<!--' + utils.escape(comment.val) + '-->'); }, /** * Visit a `BlockComment`. * * @param {Comment} comment * @api public */ visitBlockComment: function(comment){ if (!comment.buffer) return; if (0 == comment.val.trim().indexOf('if')) { this.buffer('<!--[' + comment.val.trim() + ']>'); this.visit(comment.block); this.buffer('<![endif]-->'); } else { this.buffer('<!--' + comment.val); this.visit(comment.block); 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(); this.buf.push('var __val__ = ' + val); val = 'null == __val__ ? "" : __val__'; if (code.escape) val = 'escape(' + val + ')'; this.buf.push("buf.push(" + 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' + ' if (\'number\' == typeof ' + each.obj + '.length) {\n' + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); this.visit(each.block); this.buf.push('' + ' }\n' + ' } else {\n' + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); this.visit(each.block); this.buf.push(' }\n'); this.buf.push(' }\n }\n}).call(this);\n'); }, /** * Visit `attrs`. * * @param {Array} attrs * @api public */ visitAttributes: function(attrs){ var val = this.attrs(attrs); if (val.inherits) { this.buf.push("buf.push(attrs(merge({ " + val.buf + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); } else if (val.constant) { eval('var buf={' + val.buf + '};'); this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); } else { this.buf.push("buf.push(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) { classes = classes.join(" + ' ' + "); buf.push("class: " + classes); } return { buf: buf.join(', ').replace('class:', '"class":'), escaped: JSON.stringify(escaped), inherits: inherits, constant: constant }; } }; /** * Check if expression can be evaluated to a constant * * @param {String} expression * @return {Boolean} * @api private */ function isConstant(val){ // Check strings/literals if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) return true; // Check numbers if (!isNaN(Number(val))) return true; // Check arrays var matches; if (matches = /^ *\[(.*)\] *$/.exec(val)) return matches[1].split(',').every(isConstant); return false; } /** * Escape the given string of `html`. * * @param {String} html * @return {String} * @api private */ function escape(html){ return String(html) .replace(/&(?!\w+;)/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;'); } }); // module: compiler.js require.register("doctypes.js", function(module, exports, require){ /*! * 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">' }; }); // module: doctypes.js require.register("filters.js", function(module, exports, require){ /*! * Jade - filters * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ module.exports = { /** * Wrap text with CDATA block. */ cdata: function(str){ return '<![CDATA[\\n' + str + '\\n]]>'; }, /** * Transform sass to css, wrapped in style tags. */ sass: function(str){ str = str.replace(/\\n/g, '\n'); var sass = require('sass').render(str).replace(/\n/g, '\\n'); return '<style type="text/css">' + sass + '</style>'; }, /** * Transform stylus to css, wrapped in style tags. */ stylus: function(str, options){ var ret; str = str.replace(/\\n/g, '\n'); var stylus = require('stylus'); stylus(str, options).render(function(err, css){ if (err) throw err; ret = css.replace(/\n/g, '\\n'); }); return '<style type="text/css">' + ret + '</style>'; }, /** * Transform less to css, wrapped in style tags. */ less: function(str){ var ret; str = str.replace(/\\n/g, '\n'); require('less').render(str, function(err, css){ if (err) throw err; ret = '<style type="text/css">' + css.replace(/\n/g, '\\n') + '</style>'; }); return ret; }, /** * Transform markdown to html. */ markdown: function(str){ var md; // support markdown / discount try { md = require('markdown'); } catch (err){ try { md = require('discount'); } catch (err) { try { md = require('markdown-js'); } catch (err) { try { md = require('marked'); } catch (err) { throw new Error('Cannot find markdown library, install markdown, discount, or marked.'); } } } } str = str.replace(/\\n/g, '\n'); return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'&#39;'); }, /** * Transform coffeescript to javascript. */ coffeescript: function(str){ str = str.replace(/\\n/g, '\n'); var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); return '<script type="text/javascript">\\n' + js + '</script>'; } }; }); // module: filters.js require.register("inline-tags.js", function(module, exports, require){ /*! * 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' ]; }); // module: inline-tags.js require.register("jade.js", function(module, exports, require){ /*! * 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') /** * Library version. */ exports.version = '0.26.1'; /** * 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 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 : 'with (locals || {}) {\n' + js + '\n}\n') + 'return buf.join("");'; } catch (err) { parser = parser.context(); runtime.rethrow(err, parser.filename, parser.lexer.lineno); } } /** * Compile a `Function` representation of the given jade `str`. * * Options: * * - `compileDebug` when `false` debugging code is stripped from the compiled template * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` * for use with the Jade client-side runtime.js * * @param {String} str * @param {Options} options * @return {Function} * @api public */ exports.compile = function(str, options){ var options = options || {} , client = options.client , filename = options.filename ? JSON.stringify(options.filename) : 'undefined' , fn; if (options.compileDebug !== false) { fn = [ 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' , 'try {' , parse(String(str), options) , '} catch (err) {' , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' , '}' ].join('\n'); } else { fn = parse(String(str), options); } if (client) { fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; } fn = new Function('locals, attrs, escape, rethrow, merge', fn); if (client) return fn; return function(locals){ return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); }; }; /** * Render the given `str` of jade and invoke * the callback `fn(err, str)`. * * Options: * * - `cache` enable template caching * - `filename` filename required for `include` / `extends` and caching * * @param {String} str * @param {Object|Function} options or fn * @param {Function} fn * @api public */ exports.render = function(str, options, fn){ // swap args if ('function' == typeof options) { fn = options, options = {}; } // cache requires .filename if (options.cache && !options.filename) { return fn(new Error('the "filename" option is required for caching')); } try { var path = options.filename; var tmpl = options.cache ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) : exports.compile(str, options); fn(null, tmpl(options)); } catch (err) { fn(err); } }; /** * Render a Jade file at the given `path` and callback `fn(err, str)`. * * @param {String} path * @param {Object|Function} options or callback * @param {Function} fn * @api public */ exports.renderFile = function(path, options, fn){ var key = path + ':string'; if ('function' == typeof options) { fn = options, options = {}; } try { options.filename = path; var str = options.cache ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) : fs.readFileSync(path, 'utf8'); exports.render(str, options, fn); } catch (err) { fn(err); } }; /** * Express support. */ exports.__express = exports.renderFile; }); // module: jade.js require.register("lexer.js", function(module, exports, require){ /*! * Jade - Lexer * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * 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; }; /** * 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 `start` / `end` delimiters. * * @param {String} start * @param {String} end * @return {Number} * @api private */ indexOfDelimiters: function(start, end){ var str = this.input , nstart = 0 , nend = 0 , pos = 0; for (var i = 0, len = str.length; i < len; ++i) { if (start == str.charAt(i)) { ++nstart; } else if (end == str.charAt(i)) { if (++nend == nstart) { pos = i; break; } } } return pos; }, /** * 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); 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() { var captures; if (captures = /^#\{(.*?)\}/.exec(this.input)) { this.consume(captures[0].length); return this.tok('interpolation', captures[1]); } }, /** * 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)) { if (!/^ *[-\w]+ *=/.test(captures[1])) { this.consume(captures[0].length); tok.args = captures[1]; } } 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': js = 'if (' + js + ')'; break; case 'unless': js = 'if (!(' + js + '))'; break; case 'else if': js = 'else if (' + js + ')'; break; case 'else': 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); return this.tok('code', 'while (' + captures[1] + ')'); } }, /** * Each. */ each: function() { var captures; if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var tok = this.tok('each', captures[1]); tok.key = captures[2] || '$index'; tok.code = captures[3]; return tok; } }, /** * Code. */ code: function() { var captures; if (captures = /^(!?=|-)([^\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[0] === '='; tok.buffer = flags[0] === '=' || flags[1] === '='; return tok; } }, /** * Attributes. */ attrs: function() { if ('(' == this.input.charAt(0)) { var index = this.indexOfDelimiters('(', ')') , str = this.input.substr(1, index-1) , tok = this.tok('attrs') , len = str.length , colons = this.colons , states = ['key'] , escapedAttr , key = '' , val = '' , quote , c , p; function state(){ return states[states.length - 1]; } function interpolate(attr) { return attr.replace(/#\{([^}]+)\}/g, function(_, expr){ return quote + " + (" + expr + ") + " + quote; }); } this.consume(index + 1); tok.attrs = {}; tok.escaped = {}; function parse(c) { var real = c; // TODO: remove when people fix ":" if (colons && ':' == c) c = '='; switch (c) { case ',': case '\n': switch (state()) { case 'expr': case 'array': case 'string': case 'object': val += c; break; default: states.push('key'); val = val.trim(); key = key.trim(); if ('' == key) return; key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); tok.escaped[key] = escapedAttr; tok.attrs[key] = '' == val ? true : interpolate(val); key = val = ''; } break; case '=': switch (state()) { case 'key char': key += real; break; case 'val': case 'expr': case 'array': case 'string': case 'object': val += real; break; default: escapedAttr = '!' != p; states.push('val'); } break; case '(': if ('val' == state() || 'expr' == state()) states.push('expr'); val += c; break; case ')': if ('expr' == state() || 'val' == state()) states.pop(); val += c; break; case '{': if ('val' == state()) states.push('object'); val += c; break; case '}': if ('object' == state()) states.pop(); val += c; break; case '[': if ('val' == state()) states.push('array'); val += c; break; case ']': if ('array' == state()) states.pop(); val += c; break; case '"': case "'": switch (state()) { case 'key': states.push('key char'); break; case 'key char': states.pop(); break; case 'string': if (c == quote) states.pop(); val += c; break; default: states.push('string'); val += c; quote = c; } break; case '': break; default: switch (state()) { case 'key': case 'key char': key += c; break; default: val += c; } } p = c; } for (var i = 0; i < len; ++i) { parse(str.charAt(i)); } parse(','); 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(); } }; }); // module: lexer.js require.register("nodes/attrs.js", function(module, exports, require){ /*! * 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 = new Node; Attrs.prototype.constructor = Attrs; /** * 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; } } }; }); // module: nodes/attrs.js require.register("nodes/block-comment.js", function(module, exports, require){ /*! * 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 = new Node; BlockComment.prototype.constructor = BlockComment; }); // module: nodes/block-comment.js require.register("nodes/block.js", function(module, exports, require){ /*! * 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 = new Node; Block.prototype.constructor = Block; /** * 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(); } 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; }; }); // module: nodes/block.js require.register("nodes/case.js", function(module, exports, require){ /*! * 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 = new Node; Case.prototype.constructor = Case; var When = exports.When = function When(expr, block){ this.expr = expr; this.block = block; this.debug = false; }; /** * Inherit from `Node`. */ When.prototype = new Node; When.prototype.constructor = When; }); // module: nodes/case.js require.register("nodes/code.js", function(module, exports, require){ /*! * Jade - nodes - Code * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a `Code` node with the given code `val`. * Code may also be optionally buffered and escaped. * * @param {String} val * @param {Boolean} buffer * @param {Boolean} escape * @api public */ var Code = module.exports = function Code(val, buffer, escape) { this.val = val; this.buffer = buffer; this.escape = escape; if (val.match(/^ *else/)) this.debug = false; }; /** * Inherit from `Node`. */ Code.prototype = new Node; Code.prototype.constructor = Code; }); // module: nodes/code.js require.register("nodes/comment.js", function(module, exports, require){ /*! * Jade - nodes - Comment * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a `Comment` with the given `val`, optionally `buffer`, * otherwise the comment may render in the output. * * @param {String} val * @param {Boolean} buffer * @api public */ var Comment = module.exports = function Comment(val, buffer) { this.val = val; this.buffer = buffer; }; /** * Inherit from `Node`. */ Comment.prototype = new Node; Comment.prototype.constructor = Comment; }); // module: nodes/comment.js require.register("nodes/doctype.js", function(module, exports, require){ /*! * Jade - nodes - Doctype * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize a `Doctype` with the given `val`. * * @param {String} val * @api public */ var Doctype = module.exports = function Doctype(val) { this.val = val; }; /** * Inherit from `Node`. */ Doctype.prototype = new Node; Doctype.prototype.constructor = Doctype; }); // module: nodes/doctype.js require.register("nodes/each.js", function(module, exports, require){ /*! * Jade - nodes - Each * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node'); /** * Initialize an `Each` node, representing iteration * * @param {String} obj * @param {String} val * @param {String} key * @param {Block} block * @api public */ var Each = module.exports = function Each(obj, val, key, block) { this.obj = obj; this.val = val; this.key = key; this.block = block; }; /** * Inherit from `Node`. */ Each.prototype = new Node; Each.prototype.constructor = Each; }); // module: nodes/each.js require.register("nodes/filter.js", function(module, exports, require){ /*! * Jade - nodes - Filter * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var Node = require('./node') , Block = require('./block'); /** * Initialize a `Filter` node with the given * filter `name` and `block`. * * @param {String} name * @param {Block|Node} block * @api public */ var Filter = module.exports = function Filter(name, block, attrs) { this.name = name; this.block = block; this.attrs = attrs; this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); }; /** * Inherit from `Node`. */ Filter.prototype = new Node; Filter.prototype.constructor = Filter; }); // module: nodes/filter.j