UNPKG

requirejs-dustjs

Version:

A requirejs plugin for loading and compiling dustjs templates.

1,807 lines (1,634 loc) 190 kB
// // Dust - Asynchronous Templating v2.2.2 // http://akdubya.github.com/dustjs // // Copyright (c) 2010, Aleksander Williams // Released under the MIT License. // /*global console */ var dust = {}; function getGlobal(){ return (function(){ return this.dust; }).call(null); } (function(dust) { if(!dust) { return; } var ERROR = 'ERROR', WARN = 'WARN', INFO = 'INFO', DEBUG = 'DEBUG', levels = [DEBUG, INFO, WARN, ERROR], logger = function() {}; dust.isDebug = false; dust.debugLevel = INFO; // Try to find the console logger in window scope (browsers) or top level scope (node.js) if (typeof window !== 'undefined' && window && window.console && window.console.log) { logger = window.console.log; } else if (typeof console !== 'undefined' && console && console.log) { logger = console.log; } /** * If dust.isDebug is true, Log dust debug statements, info statements, warning statements, and errors. * This default implementation will print to the console if it exists. * @param {String} message the message to print * @param {String} type the severity of the message(ERROR, WARN, INFO, or DEBUG) * @public */ dust.log = function(message, type) { var type = type || INFO; if(dust.isDebug && levels.indexOf(type) >= levels.indexOf(dust.debugLevel)) { if(!dust.logQueue) { dust.logQueue = []; } dust.logQueue.push({message: message, type: type}); logger.call(console || window.console, '[DUST ' + type + ']: ' + message); } }; /** * If debugging is turned on(dust.isDebug=true) log the error message and throw it. * Otherwise try to keep rendering. This is useful to fail hard in dev mode, but keep rendering in production. * @param {Error} error the error message to throw * @param {Object} chunk the chunk the error was thrown from * @public */ dust.onError = function(error, chunk) { dust.log(error.message || error, ERROR); if(dust.isDebug) { throw error; } else { return chunk; } }; dust.helpers = {}; dust.cache = {}; dust.register = function(name, tmpl) { if (!name) { return; } dust.cache[name] = tmpl; }; dust.render = function(name, context, callback) { var chunk = new Stub(callback).head; try { dust.load(name, chunk, Context.wrap(context, name)).end(); } catch (err) { dust.onError(err, chunk); } }; dust.stream = function(name, context) { var stream = new Stream(); dust.nextTick(function() { try { dust.load(name, stream.head, Context.wrap(context, name)).end(); } catch (err) { dust.onError(err, stream.head); } }); return stream; }; dust.renderSource = function(source, context, callback) { return dust.compileFn(source)(context, callback); }; dust.compileFn = function(source, name) { var tmpl = dust.loadSource(dust.compile(source, name)); return function(context, callback) { var master = callback ? new Stub(callback) : new Stream(); dust.nextTick(function() { if(typeof tmpl === 'function') { tmpl(master.head, Context.wrap(context, name)).end(); } else { dust.onError(new Error('Template [' + name + '] cannot be resolved to a Dust function')); } }); return master; }; }; dust.load = function(name, chunk, context) { var tmpl = dust.cache[name]; if (tmpl) { return tmpl(chunk, context); } else { if (dust.onLoad) { return chunk.map(function(chunk) { dust.onLoad(name, function(err, src) { if (err) { return chunk.setError(err); } if (!dust.cache[name]) { dust.loadSource(dust.compile(src, name)); } dust.cache[name](chunk, context).end(); }); }); } return chunk.setError(new Error('Template Not Found: ' + name)); } }; dust.loadSource = function(source, path) { return eval(source); }; if (Array.isArray) { dust.isArray = Array.isArray; } else { dust.isArray = function(arr) { return Object.prototype.toString.call(arr) === '[object Array]'; }; } dust.nextTick = (function() { if (typeof process !== 'undefined') { return process.nextTick; } else { return function(callback) { setTimeout(callback,0); }; } } )(); dust.isEmpty = function(value) { if (dust.isArray(value) && !value.length) { return true; } if (value === 0) { return false; } return (!value); }; // apply the filter chain and return the output string dust.filter = function(string, auto, filters) { if (filters) { for (var i=0, len=filters.length; i<len; i++) { var name = filters[i]; if (name === 's') { auto = null; dust.log('Using unescape filter on [' + string + ']', DEBUG); } else if (typeof dust.filters[name] === 'function') { string = dust.filters[name](string); } else { dust.onError(new Error('Invalid filter [' + name + ']')); } } } // by default always apply the h filter, unless asked to unescape with |s if (auto) { string = dust.filters[auto](string); } return string; }; dust.filters = { h: function(value) { return dust.escapeHtml(value); }, j: function(value) { return dust.escapeJs(value); }, u: encodeURI, uc: encodeURIComponent, js: function(value) { if (!JSON) { dust.log('JSON is undefined. JSON stringify has not been used on [' + value + ']', WARN); return value; } else { return JSON.stringify(value); } }, jp: function(value) { if (!JSON) {dust.log('JSON is undefined. JSON parse has not been used on [' + value + ']', WARN); return value; } else { return JSON.parse(value); } } }; function Context(stack, global, blocks, templateName) { this.stack = stack; this.global = global; this.blocks = blocks; this.templateName = templateName; } dust.makeBase = function(global) { return new Context(new Stack(), global); }; Context.wrap = function(context, name) { if (context instanceof Context) { return context; } return new Context(new Stack(context), {}, null, name); }; /** * Public API for getting a value from the context. * @method get * @param {string|array} path The path to the value. Supported formats are: * 'key' * 'path.to.key' * '.path.to.key' * ['path', 'to', 'key'] * ['key'] * @param {boolean} [cur=false] Boolean which determines if the search should be limited to the * current context (true), or if get should search in parent contexts as well (false). * @public * @returns {string|object} */ Context.prototype.get = function(path, cur) { if (typeof path === 'string') { if (path[0] === '.') { cur = true; path = path.substr(1); } path = path.split('.'); } return this._get(cur, path); }; /** * Get a value from the context * @method _get * @param {boolean} cur Get only from the current context * @param {array} down An array of each step in the path * @private * @return {string | object} */ Context.prototype._get = function(cur, down) { var ctx = this.stack, i = 1, value, first, len, ctxThis; dust.log('Searching for reference [{' + down.join('.') + '}] in template [' + this.getTemplateName() + ']', DEBUG); first = down[0]; len = down.length; if (cur && len === 0) { ctxThis = ctx; ctx = ctx.head; } else { if (!cur) { // Search up the stack for the first value while (ctx) { if (ctx.isObject) { ctxThis = ctx.head; value = ctx.head[first]; if (value !== undefined) { break; } } ctx = ctx.tail; } if (value !== undefined) { ctx = value; } else { ctx = this.global ? this.global[first] : undefined; } } else { // if scope is limited by a leading dot, don't search up the tree ctx = ctx.head[first]; } while (ctx && i < len) { ctxThis = ctx; ctx = ctx[down[i]]; i++; } } // Return the ctx or a function wrapping the application of the context. if (typeof ctx === 'function') { var fn = function() { return ctx.apply(ctxThis, arguments); }; fn.isFunction = true; return fn; } else { if (ctx === undefined) { dust.log('Cannot find the value for reference [{' + down.join('.') + '}] in template [' + this.getTemplateName() + ']'); } return ctx; } }; Context.prototype.getPath = function(cur, down) { return this._get(cur, down); }; Context.prototype.push = function(head, idx, len) { return new Context(new Stack(head, this.stack, idx, len), this.global, this.blocks, this.getTemplateName()); }; Context.prototype.rebase = function(head) { return new Context(new Stack(head), this.global, this.blocks, this.getTemplateName()); }; Context.prototype.current = function() { return this.stack.head; }; Context.prototype.getBlock = function(key, chk, ctx) { if (typeof key === 'function') { var tempChk = new Chunk(); key = key(tempChk, this).data.join(''); } var blocks = this.blocks; if (!blocks) { dust.log('No blocks for context[{' + key + '}] in template [' + this.getTemplateName() + ']', DEBUG); return; } var len = blocks.length, fn; while (len--) { fn = blocks[len][key]; if (fn) { return fn; } } }; Context.prototype.shiftBlocks = function(locals) { var blocks = this.blocks, newBlocks; if (locals) { if (!blocks) { newBlocks = [locals]; } else { newBlocks = blocks.concat([locals]); } return new Context(this.stack, this.global, newBlocks, this.getTemplateName()); } return this; }; Context.prototype.getTemplateName = function() { return this.templateName; } function Stack(head, tail, idx, len) { this.tail = tail; this.isObject = !dust.isArray(head) && head && typeof head === 'object'; this.head = head; this.index = idx; this.of = len; } function Stub(callback) { this.head = new Chunk(this); this.callback = callback; this.out = ''; } Stub.prototype.flush = function() { var chunk = this.head; while (chunk) { if (chunk.flushable) { this.out += chunk.data.join(''); //ie7 perf } else if (chunk.error) { this.callback(chunk.error); dust.onError(new Error('Chunk error [' + chunk.error + '] thrown. Ceasing to render this template.')); this.flush = function() {}; return; } else { return; } chunk = chunk.next; this.head = chunk; } this.callback(null, this.out); }; function Stream() { this.head = new Chunk(this); } Stream.prototype.flush = function() { var chunk = this.head; while(chunk) { if (chunk.flushable) { this.emit('data', chunk.data.join('')); //ie7 perf } else if (chunk.error) { this.emit('error', chunk.error); dust.onError(new Error('Chunk error [' + chunk.error + '] thrown. Ceasing to render this template.')); this.flush = function() {}; return; } else { return; } chunk = chunk.next; this.head = chunk; } this.emit('end'); }; Stream.prototype.emit = function(type, data) { if (!this.events) { dust.log('No events to emit', INFO); return false; } var handler = this.events[type]; if (!handler) { dust.log('Event type [' + type + '] does not exist', WARN); return false; } if (typeof handler === 'function') { handler(data); } else if (dust.isArray(handler)) { var listeners = handler.slice(0); for (var i = 0, l = listeners.length; i < l; i++) { listeners[i](data); } } else { dust.onError(new Error('Event Handler [' + handler + '] is not of a type that is handled by emit')); } }; Stream.prototype.on = function(type, callback) { if (!this.events) { this.events = {}; } if (!this.events[type]) { dust.log('Event type [' + type + '] does not exist. Using just the specified callback.', WARN); if(callback) { this.events[type] = callback; } else { dust.log('Callback for type [' + type + '] does not exist. Listener not registered.', WARN); } } else if(typeof this.events[type] === 'function') { this.events[type] = [this.events[type], callback]; } else { this.events[type].push(callback); } return this; }; Stream.prototype.pipe = function(stream) { this.on('data', function(data) { try { stream.write(data, 'utf8'); } catch (err) { dust.onError(err, stream.head); } }).on('end', function() { try { return stream.end(); } catch (err) { dust.onError(err, stream.head); } }).on('error', function(err) { stream.error(err); }); return this; }; function Chunk(root, next, taps) { this.root = root; this.next = next; this.data = []; //ie7 perf this.flushable = false; this.taps = taps; } Chunk.prototype.write = function(data) { var taps = this.taps; if (taps) { data = taps.go(data); } this.data.push(data); return this; }; Chunk.prototype.end = function(data) { if (data) { this.write(data); } this.flushable = true; this.root.flush(); return this; }; Chunk.prototype.map = function(callback) { var cursor = new Chunk(this.root, this.next, this.taps), branch = new Chunk(this.root, cursor, this.taps); this.next = branch; this.flushable = true; callback(branch); return cursor; }; Chunk.prototype.tap = function(tap) { var taps = this.taps; if (taps) { this.taps = taps.push(tap); } else { this.taps = new Tap(tap); } return this; }; Chunk.prototype.untap = function() { this.taps = this.taps.tail; return this; }; Chunk.prototype.render = function(body, context) { return body(this, context); }; Chunk.prototype.reference = function(elem, context, auto, filters) { if (typeof elem === 'function') { elem.isFunction = true; // Changed the function calling to use apply with the current context to make sure // that "this" is wat we expect it to be inside the function elem = elem.apply(context.current(), [this, context, null, {auto: auto, filters: filters}]); if (elem instanceof Chunk) { return elem; } } if (!dust.isEmpty(elem)) { return this.write(dust.filter(elem, auto, filters)); } else { return this; } }; Chunk.prototype.section = function(elem, context, bodies, params) { // anonymous functions if (typeof elem === 'function') { elem = elem.apply(context.current(), [this, context, bodies, params]); // functions that return chunks are assumed to have handled the body and/or have modified the chunk // use that return value as the current chunk and go to the next method in the chain if (elem instanceof Chunk) { return elem; } } var body = bodies.block, skip = bodies['else']; // a.k.a Inline parameters in the Dust documentations if (params) { context = context.push(params); } /* Dust's default behavior is to enumerate over the array elem, passing each object in the array to the block. When elem resolves to a value or object instead of an array, Dust sets the current context to the value and renders the block one time. */ //non empty array is truthy, empty array is falsy if (dust.isArray(elem)) { if (body) { var len = elem.length, chunk = this; if (len > 0) { // any custom helper can blow up the stack // and store a flattened context, guard defensively if(context.stack.head) { context.stack.head['$len'] = len; } for (var i=0; i<len; i++) { if(context.stack.head) { context.stack.head['$idx'] = i; } chunk = body(chunk, context.push(elem[i], i, len)); } if(context.stack.head) { context.stack.head['$idx'] = undefined; context.stack.head['$len'] = undefined; } return chunk; } else if (skip) { return skip(this, context); } } } else if (elem === true) { // true is truthy but does not change context if (body) { return body(this, context); } } else if (elem || elem === 0) { // everything that evaluates to true are truthy ( e.g. Non-empty strings and Empty objects are truthy. ) // zero is truthy // for anonymous functions that did not returns a chunk, truthiness is evaluated based on the return value if (body) { return body(this, context.push(elem)); } // nonexistent, scalar false value, scalar empty string, null, // undefined are all falsy } else if (skip) { return skip(this, context); } dust.log('Not rendering section (#) block in template [' + context.getTemplateName() + '], because above key was not found', DEBUG); return this; }; Chunk.prototype.exists = function(elem, context, bodies) { var body = bodies.block, skip = bodies['else']; if (!dust.isEmpty(elem)) { if (body) { return body(this, context); } } else if (skip) { return skip(this, context); } dust.log('Not rendering exists (?) block in template [' + context.getTemplateName() + '], because above key was not found', DEBUG); return this; }; Chunk.prototype.notexists = function(elem, context, bodies) { var body = bodies.block, skip = bodies['else']; if (dust.isEmpty(elem)) { if (body) { return body(this, context); } } else if (skip) { return skip(this, context); } dust.log('Not rendering not exists (^) block check in template [' + context.getTemplateName() + '], because above key was found', DEBUG); return this; }; Chunk.prototype.block = function(elem, context, bodies) { var body = bodies.block; if (elem) { body = elem; } if (body) { return body(this, context); } return this; }; Chunk.prototype.partial = function(elem, context, params) { var partialContext; //put the params context second to match what section does. {.} matches the current context without parameters // start with an empty context partialContext = dust.makeBase(context.global); partialContext.blocks = context.blocks; if (context.stack && context.stack.tail){ // grab the stack(tail) off of the previous context if we have it partialContext.stack = context.stack.tail; } if (params){ //put params on partialContext = partialContext.push(params); } if(typeof elem === 'string') { partialContext.templateName = elem; } //reattach the head partialContext = partialContext.push(context.stack.head); var partialChunk; if (typeof elem === 'function') { partialChunk = this.capture(elem, partialContext, function(name, chunk) { partialContext.templateName = partialContext.templateName || name; dust.load(name, chunk, partialContext).end(); }); } else { partialChunk = dust.load(elem, this, partialContext); } return partialChunk; }; Chunk.prototype.helper = function(name, context, bodies, params) { var chunk = this; // handle invalid helpers, similar to invalid filters try { if(dust.helpers[name]) { return dust.helpers[name](chunk, context, bodies, params); } else { return dust.onError(new Error('Invalid helper [' + name + ']'), chunk); } } catch (err) { return dust.onError(err, chunk); } }; Chunk.prototype.capture = function(body, context, callback) { return this.map(function(chunk) { var stub = new Stub(function(err, out) { if (err) { chunk.setError(err); } else { callback(out, chunk); } }); body(stub.head, context).end(); }); }; Chunk.prototype.setError = function(err) { this.error = err; this.root.flush(); return this; }; function Tap(head, tail) { this.head = head; this.tail = tail; } Tap.prototype.push = function(tap) { return new Tap(tap, this); }; Tap.prototype.go = function(value) { var tap = this; while(tap) { value = tap.head(value); tap = tap.tail; } return value; }; var HCHARS = new RegExp(/[&<>\"\']/), AMP = /&/g, LT = /</g, GT = />/g, QUOT = /\"/g, SQUOT = /\'/g; dust.escapeHtml = function(s) { if (typeof s === 'string') { if (!HCHARS.test(s)) { return s; } return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;').replace(SQUOT, '&#39;'); } return s; }; var BS = /\\/g, FS = /\//g, CR = /\r/g, LS = /\u2028/g, PS = /\u2029/g, NL = /\n/g, LF = /\f/g, SQ = /'/g, DQ = /"/g, TB = /\t/g; dust.escapeJs = function(s) { if (typeof s === 'string') { return s .replace(BS, '\\\\') .replace(FS, '\\/') .replace(DQ, '\\"') .replace(SQ, '\\\'') .replace(CR, '\\r') .replace(LS, '\\u2028') .replace(PS, '\\u2029') .replace(NL, '\\n') .replace(LF, '\\f') .replace(TB, '\\t'); } return s; }; })(dust); if (typeof exports !== 'undefined') { if (typeof process !== 'undefined') { require('./server')(dust); } module.exports = dust; } var dustCompiler = (function(dust) { dust.compile = function(source, name) { try { var ast = filterAST(dust.parse(source)); return compile(ast, name); } catch (err) { if (!err.line || !err.column) { throw err; } throw new SyntaxError(err.message + " At line : " + err.line + ", column : " + err.column); } }; function filterAST(ast) { var context = {}; return dust.filterNode(context, ast); }; dust.filterNode = function(context, node) { return dust.optimizers[node[0]](context, node); }; dust.optimizers = { body: compactBuffers, buffer: noop, special: convertSpecial, format: nullify, // TODO: convert format reference: visit, "#": visit, "?": visit, "^": visit, "<": visit, "+": visit, "@": visit, "%": visit, partial: visit, context: visit, params: visit, bodies: visit, param: visit, filters: noop, key: noop, path: noop, literal: noop, comment: nullify, line: nullify, col: nullify }; dust.pragmas = { esc: function(compiler, context, bodies, params) { var old = compiler.auto, out; if (!context) { context = 'h'; } compiler.auto = (context === 's') ? '' : context; out = compileParts(compiler, bodies.block); compiler.auto = old; return out; } }; function visit(context, node) { var out = [node[0]], i, len, res; for (i=1, len=node.length; i<len; i++) { res = dust.filterNode(context, node[i]); if (res) { out.push(res); } } return out; }; // Compacts consecutive buffer nodes into a single node function compactBuffers(context, node) { var out = [node[0]], memo, i, len, res; for (i=1, len=node.length; i<len; i++) { res = dust.filterNode(context, node[i]); if (res) { if (res[0] === 'buffer') { if (memo) { memo[1] += res[1]; } else { memo = res; out.push(res); } } else { memo = null; out.push(res); } } } return out; }; var specialChars = { "s": " ", "n": "\n", "r": "\r", "lb": "{", "rb": "}" }; function convertSpecial(context, node) { return ['buffer', specialChars[node[1]]] }; function noop(context, node) { return node }; function nullify(){}; function compile(ast, name) { var context = { name: name, bodies: [], blocks: {}, index: 0, auto: "h" } return "(function(){dust.register(" + (name ? "\"" + name + "\"" : "null") + "," + dust.compileNode(context, ast) + ");" + compileBlocks(context) + compileBodies(context) + "return body_0;" + "})();"; }; function compileBlocks(context) { var out = [], blocks = context.blocks, name; for (name in blocks) { out.push("'" + name + "':" + blocks[name]); } if (out.length) { context.blocks = "ctx=ctx.shiftBlocks(blocks);"; return "var blocks={" + out.join(',') + "};"; } return context.blocks = ""; }; function compileBodies(context) { var out = [], bodies = context.bodies, blx = context.blocks, i, len; for (i=0, len=bodies.length; i<len; i++) { out[i] = "function body_" + i + "(chk,ctx){" + blx + "return chk" + bodies[i] + ";}"; } return out.join(''); }; function compileParts(context, body) { var parts = '', i, len; for (i=1, len=body.length; i<len; i++) { parts += dust.compileNode(context, body[i]); } return parts; }; dust.compileNode = function(context, node) { return dust.nodes[node[0]](context, node); }; dust.nodes = { body: function(context, node) { var id = context.index++, name = "body_" + id; context.bodies[id] = compileParts(context, node); return name; }, buffer: function(context, node) { return ".write(" + escape(node[1]) + ")"; }, format: function(context, node) { return ".write(" + escape(node[1] + node[2]) + ")"; }, reference: function(context, node) { return ".reference(" + dust.compileNode(context, node[1]) + ",ctx," + dust.compileNode(context, node[2]) + ")"; }, "#": function(context, node) { return compileSection(context, node, "section"); }, "?": function(context, node) { return compileSection(context, node, "exists"); }, "^": function(context, node) { return compileSection(context, node, "notexists"); }, "<": function(context, node) { var bodies = node[4]; for (var i=1, len=bodies.length; i<len; i++) { var param = bodies[i], type = param[1][1]; if (type === "block") { context.blocks[node[1].text] = dust.compileNode(context, param[2]); return ''; } } return ''; }, "+": function(context, node) { if(typeof(node[1].text) === "undefined" && typeof(node[4]) === "undefined"){ return ".block(ctx.getBlock(" + dust.compileNode(context, node[1]) + ",chk, ctx)," + dust.compileNode(context, node[2]) + ", {}," + dust.compileNode(context, node[3]) + ")"; }else { return ".block(ctx.getBlock(" + escape(node[1].text) + ")," + dust.compileNode(context, node[2]) + "," + dust.compileNode(context, node[4]) + "," + dust.compileNode(context, node[3]) + ")"; } }, "@": function(context, node) { return ".helper(" + escape(node[1].text) + "," + dust.compileNode(context, node[2]) + "," + dust.compileNode(context, node[4]) + "," + dust.compileNode(context, node[3]) + ")"; }, "%": function(context, node) { // TODO: Move these hacks into pragma precompiler var name = node[1][1]; if (!dust.pragmas[name]) return ''; var rawBodies = node[4]; var bodies = {}; for (var i=1, len=rawBodies.length; i<len; i++) { var b = rawBodies[i]; bodies[b[1][1]] = b[2]; } var rawParams = node[3]; var params = {}; for (var i=1, len=rawParams.length; i<len; i++) { var p = rawParams[i]; params[p[1][1]] = p[2][1]; } var ctx = node[2][1] ? node[2][1].text : null; return dust.pragmas[name](context, ctx, bodies, params); }, partial: function(context, node) { return ".partial(" + dust.compileNode(context, node[1]) + "," + dust.compileNode(context, node[2]) + "," + dust.compileNode(context, node[3]) + ")"; }, context: function(context, node) { if (node[1]) { return "ctx.rebase(" + dust.compileNode(context, node[1]) + ")"; } return "ctx"; }, params: function(context, node) { var out = []; for (var i=1, len=node.length; i<len; i++) { out.push(dust.compileNode(context, node[i])); } if (out.length) { return "{" + out.join(',') + "}"; } return "null"; }, bodies: function(context, node) { var out = []; for (var i=1, len=node.length; i<len; i++) { out.push(dust.compileNode(context, node[i])); } return "{" + out.join(',') + "}"; }, param: function(context, node) { return dust.compileNode(context, node[1]) + ":" + dust.compileNode(context, node[2]); }, filters: function(context, node) { var list = []; for (var i=1, len=node.length; i<len; i++) { var filter = node[i]; list.push("\"" + filter + "\""); } return "\"" + context.auto + "\"" + (list.length ? ",[" + list.join(',') + "]" : ''); }, key: function(context, node) { return "ctx._get(false, [\"" + node[1] + "\"])"; }, path: function(context, node) { var current = node[1], keys = node[2], list = []; for (var i=0,len=keys.length; i<len; i++) { if (dust.isArray(keys[i])) list.push(dust.compileNode(context, keys[i])); else list.push("\"" + keys[i] + "\""); } return "ctx._get(" + current + ",[" + list.join(',') + "])"; }, literal: function(context, node) { return escape(node[1]); } }; function compileSection(context, node, cmd) { return "." + cmd + "(" + dust.compileNode(context, node[1]) + "," + dust.compileNode(context, node[2]) + "," + dust.compileNode(context, node[4]) + "," + dust.compileNode(context, node[3]) + ")"; }; var escape = (typeof JSON === "undefined") ? function(str) { return "\"" + dust.escapeJs(str) + "\"" } : JSON.stringify; return dust; }); if (typeof exports !== 'undefined') { module.exports = dustCompiler; } else { dustCompiler(getGlobal()); } (function(dust){ var parser = (function(){ /* * Generated by PEG.js 0.7.0. * * http://pegjs.majda.cz/ */ function quote(s) { /* * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a * string literal except for the closing quote character, backslash, * carriage return, line separator, paragraph separator, and line feed. * Any character may appear in the form of an escape sequence. * * For portability, we also escape escape all control and non-ASCII * characters. Note that "\0" and "\v" escape sequences are not used * because JSHint does not like the first and IE the second. */ return '"' + s .replace(/\\/g, '\\\\') // backslash .replace(/"/g, '\\"') // closing quote character .replace(/\x08/g, '\\b') // backspace .replace(/\t/g, '\\t') // horizontal tab .replace(/\n/g, '\\n') // line feed .replace(/\f/g, '\\f') // form feed .replace(/\r/g, '\\r') // carriage return .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) + '"'; } var result = { /* * Parses the input with a generated parser. If the parsing is successfull, * returns a value explicitly or implicitly specified by the grammar from * which the parser was generated (see |PEG.buildParser|). If the parsing is * unsuccessful, throws |PEG.parser.SyntaxError| describing the error. */ parse: function(input, startRule) { var parseFunctions = { "body": parse_body, "part": parse_part, "section": parse_section, "sec_tag_start": parse_sec_tag_start, "end_tag": parse_end_tag, "context": parse_context, "params": parse_params, "bodies": parse_bodies, "reference": parse_reference, "partial": parse_partial, "filters": parse_filters, "special": parse_special, "identifier": parse_identifier, "number": parse_number, "float": parse_float, "integer": parse_integer, "path": parse_path, "key": parse_key, "array": parse_array, "array_part": parse_array_part, "inline": parse_inline, "inline_part": parse_inline_part, "buffer": parse_buffer, "literal": parse_literal, "esc": parse_esc, "comment": parse_comment, "tag": parse_tag, "ld": parse_ld, "rd": parse_rd, "lb": parse_lb, "rb": parse_rb, "eol": parse_eol, "ws": parse_ws }; if (startRule !== undefined) { if (parseFunctions[startRule] === undefined) { throw new Error("Invalid rule name: " + quote(startRule) + "."); } } else { startRule = "body"; } var pos = { offset: 0, line: 1, column: 1, seenCR: false }; var reportFailures = 0; var rightmostFailuresPos = { offset: 0, line: 1, column: 1, seenCR: false }; var rightmostFailuresExpected = []; function padLeft(input, padding, length) { var result = input; var padLength = length - input.length; for (var i = 0; i < padLength; i++) { result = padding + result; } return result; } function escape(ch) { var charCode = ch.charCodeAt(0); var escapeChar; var length; if (charCode <= 0xFF) { escapeChar = 'x'; length = 2; } else { escapeChar = 'u'; length = 4; } return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); } function clone(object) { var result = {}; for (var key in object) { result[key] = object[key]; } return result; } function advance(pos, n) { var endOffset = pos.offset + n; for (var offset = pos.offset; offset < endOffset; offset++) { var ch = input.charAt(offset); if (ch === "\n") { if (!pos.seenCR) { pos.line++; } pos.column = 1; pos.seenCR = false; } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { pos.line++; pos.column = 1; pos.seenCR = true; } else { pos.column++; pos.seenCR = false; } } pos.offset += n; } function matchFailed(failure) { if (pos.offset < rightmostFailuresPos.offset) { return; } if (pos.offset > rightmostFailuresPos.offset) { rightmostFailuresPos = clone(pos); rightmostFailuresExpected = []; } rightmostFailuresExpected.push(failure); } function parse_body() { var result0, result1; var pos0; pos0 = clone(pos); result0 = []; result1 = parse_part(); while (result1 !== null) { result0.push(result1); result1 = parse_part(); } if (result0 !== null) { result0 = (function(offset, line, column, p) { return ["body"].concat(p).concat([['line', line], ['col', column]]) })(pos0.offset, pos0.line, pos0.column, result0); } if (result0 === null) { pos = clone(pos0); } return result0; } function parse_part() { var result0; result0 = parse_comment(); if (result0 === null) { result0 = parse_section(); if (result0 === null) { result0 = parse_partial(); if (result0 === null) { result0 = parse_special(); if (result0 === null) { result0 = parse_reference(); if (result0 === null) { result0 = parse_buffer(); } } } } } return result0; } function parse_section() { var result0, result1, result2, result3, result4, result5, result6; var pos0, pos1; reportFailures++; pos0 = clone(pos); pos1 = clone(pos); result0 = parse_sec_tag_start(); if (result0 !== null) { result1 = []; result2 = parse_ws(); while (result2 !== null) { result1.push(result2); result2 = parse_ws(); } if (result1 !== null) { result2 = parse_rd(); if (result2 !== null) { result3 = parse_body(); if (result3 !== null) { result4 = parse_bodies(); if (result4 !== null) { result5 = parse_end_tag(); result5 = result5 !== null ? result5 : ""; if (result5 !== null) { result6 = (function(offset, line, column, t, b, e, n) {if( (!n) || (t[1].text !== n.text) ) { throw new Error("Expected end tag for "+t[1].text+" but it was not found. At line : "+line+", column : " + column)} return true;})(pos.offset, pos.line, pos.column, result0, result3, result4, result5) ? "" : null; if (result6 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6]; } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } if (result0 !== null) { result0 = (function(offset, line, column, t, b, e, n) { e.push(["param", ["literal", "block"], b]); t.push(e); return t.concat([['line', line], ['col', column]]) })(pos0.offset, pos0.line, pos0.column, result0[0], result0[3], result0[4], result0[5]); } if (result0 === null) { pos = clone(pos0); } if (result0 === null) { pos0 = clone(pos); pos1 = clone(pos); result0 = parse_sec_tag_start(); if (result0 !== null) { result1 = []; result2 = parse_ws(); while (result2 !== null) { result1.push(result2); result2 = parse_ws(); } if (result1 !== null) { if (input.charCodeAt(pos.offset) === 47) { result2 = "/"; advance(pos, 1); } else { result2 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result2 !== null) { result3 = parse_rd(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } if (result0 !== null) { result0 = (function(offset, line, column, t) { t.push(["bodies"]); return t.concat([['line', line], ['col', column]]) })(pos0.offset, pos0.line, pos0.column, result0[0]); } if (result0 === null) { pos = clone(pos0); } } reportFailures--; if (reportFailures === 0 && result0 === null) { matchFailed("section"); } return result0; } function parse_sec_tag_start() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = clone(pos); pos1 = clone(pos); result0 = parse_ld(); if (result0 !== null) { if (/^[#?^<+@%]/.test(input.charAt(pos.offset))) { result1 = input.charAt(pos.offset); advance(pos, 1); } else { result1 = null; if (reportFailures === 0) { matchFailed("[#?^<+@%]"); } } if (result1 !== null) { result2 = []; result3 = parse_ws(); while (result3 !== null) { result2.push(result3); result3 = parse_ws(); } if (result2 !== null) { result3 = parse_identifier(); if (result3 !== null) { result4 = parse_context(); if (result4 !== null) { result5 = parse_params(); if (result5 !== null) { result0 = [result0, result1, result2, result3, result4, result5]; } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } if (result0 !== null) { result0 = (function(offset, line, column, t, n, c, p) { return [t, n, c, p] })(pos0.offset, pos0.line, pos0.column, result0[1], result0[3], result0[4], result0[5]); } if (result0 === null) { pos = clone(pos0); } return result0; } function parse_end_tag() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; reportFailures++; pos0 = clone(pos); pos1 = clone(pos); result0 = parse_ld(); if (result0 !== null) { if (input.charCodeAt(pos.offset) === 47) { result1 = "/"; advance(pos, 1); } else { result1 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result1 !== null) { result2 = []; result3 = parse_ws(); while (result3 !== null) { result2.push(result3); result3 = parse_ws(); } if (result2 !== null) { result3 = parse_identifier(); if (result3 !== null) { result4 = []; result5 = parse_ws(); while (result5 !== null) { result4.push(result5); result5 = parse_ws(); } if (result4 !== null) { result5 = parse_rd(); if (result5 !== null) { result0 = [result0, result1, result2, result3, result4, result5]; } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } } else { result0 = null; pos = clone(pos1); } if (result0 !== null) { result0 = (function(offset, line, column, n) { return n })(pos0.offset, pos0.line, pos0.column, result0[3]); } if (result0 === null) { pos = clone(pos0); } reportFailures--; if (reportFailures === 0 && result0 === null) { matchFailed("end tag"); } return result0; } function parse_context() { var result0, result1; var pos0, pos1, pos2; pos0 = clone(pos); pos1 = clone(pos); pos2 = clone(pos); if (input.charCodeAt(pos.offset) === 58) { result0 = ":"; advance(pos, 1); } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 !== null) { result1 = parse_identifier(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = clone(pos2); } } else { result0 = null; pos = clone(pos2); } if (result0 !== null) { result0 = (function(offset, line, column, n) {return n})(pos1.offset, pos1.line, pos1.column, result0[1]); } if (result0 === null) { pos = clone(pos1); } result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result0 = (function(offset, line, column, n) { return n ? ["context", n] : ["context"] })(pos0.offset, pos0.line, pos0.column, result0); } if (result0 === null) { pos = clone(pos0); } return result0; } function parse_params() { var result0, result1, result2, result3, result4; var pos0, pos1, pos2; reportFailures++; pos0 = clone(pos); result0 = []; pos1 = clone(pos); pos2 = clone(pos); result2 = parse_ws(); if (result2 !== null) { result1 = []; while (result2 !== null) { result1.push(result2); result2 = parse_ws(); } } else { result1 = null; } if (result1 !== null) { result2 = parse_key(); if (result2 !== null) { if (input.charCodeAt(pos.offset) === 61) { result3 = "="; advance(pos, 1); } else { result3 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result3 !== null) { result4 = parse_number(); if (result4 === null) { result4 = parse_identifier(); if (result4 === null) { result4 = parse_inline(); } } if (result4 !== null) { result1 = [result1, result2, result3, result4]; } else { result1 = null; pos = clone(pos2); } } else { result1 = null; pos = clone(pos2); } } else { result1 = null; pos = clone(pos2); } } else { result1 = null; pos = clone(pos2); } if (result1 !== null) { result1 = (function(offset, line, column, k, v) {return ["param", ["literal", k], v]})(pos1.offset, pos1.line, pos1.column, result1[1], result1[3]); } if (result1 === null) { pos = clone(pos1); } while (result1 !== null) { result0.push(result1); pos1 = clone(pos); pos2 = clone(pos); result2 = parse_ws(); if (result2 !== null) { result1 = []; while (result2 !== null) { result1.push(result2); result2 = parse_ws(); } } else { result1 = null; } if (result1 !== null) { result2 = parse_key(); if (result2 !== null) { if (input.charCodeAt(pos.offset) === 61) { result3 = "="; advance(pos, 1); } else { result3 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result3 !== null) { result4 = parse_number(); if (result4 === null) { result4 = parse_identifier(); if (result4 === null) { result4 = parse_inline(); } } if (result4 !== null) { result1 = [result1, result2, result3, result4]; } else { result1 = null; pos