certidude/certidude/static/js/nunjucks.js

6234 lines
183 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*! Browser bundle of nunjucks 2.3.0 */
var nunjucks =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lib = __webpack_require__(1);
var env = __webpack_require__(2);
var Loader = __webpack_require__(15);
var loaders = __webpack_require__(14);
var precompile = __webpack_require__(3);
module.exports = {};
module.exports.Environment = env.Environment;
module.exports.Template = env.Template;
module.exports.Loader = Loader;
module.exports.FileSystemLoader = loaders.FileSystemLoader;
module.exports.PrecompiledLoader = loaders.PrecompiledLoader;
module.exports.WebLoader = loaders.WebLoader;
module.exports.compiler = __webpack_require__(7);
module.exports.parser = __webpack_require__(8);
module.exports.lexer = __webpack_require__(9);
module.exports.runtime = __webpack_require__(12);
module.exports.lib = lib;
module.exports.nodes = __webpack_require__(10);
module.exports.installJinjaCompat = __webpack_require__(18);
// A single instance of an environment, since this is so commonly used
var e;
module.exports.configure = function(templatesPath, opts) {
opts = opts || {};
if(lib.isObject(templatesPath)) {
opts = templatesPath;
templatesPath = null;
}
var TemplateLoader;
if(loaders.FileSystemLoader) {
TemplateLoader = new loaders.FileSystemLoader(templatesPath, {
watch: opts.watch,
noCache: opts.noCache
});
}
else if(loaders.WebLoader) {
TemplateLoader = new loaders.WebLoader(templatesPath, {
useCache: opts.web && opts.web.useCache,
async: opts.web && opts.web.async
});
}
e = new env.Environment(TemplateLoader, opts);
if(opts && opts.express) {
e.express(opts.express);
}
return e;
};
module.exports.compile = function(src, env, path, eagerCompile) {
if(!e) {
module.exports.configure();
}
return new module.exports.Template(src, env, path, eagerCompile);
};
module.exports.render = function(name, ctx, cb) {
if(!e) {
module.exports.configure();
}
return e.render(name, ctx, cb);
};
module.exports.renderString = function(src, ctx, cb) {
if(!e) {
module.exports.configure();
}
return e.renderString(src, ctx, cb);
};
if(precompile) {
module.exports.precompile = precompile.precompile;
module.exports.precompileString = precompile.precompileString;
}
/***/ },
/* 1 */
/***/ function(module, exports) {
'use strict';
var ArrayProto = Array.prototype;
var ObjProto = Object.prototype;
var escapeMap = {
'&': '&',
'"': '"',
'\'': ''',
'<': '&lt;',
'>': '&gt;'
};
var escapeRegex = /[&"'<>]/g;
var lookupEscape = function(ch) {
return escapeMap[ch];
};
var exports = module.exports = {};
exports.prettifyError = function(path, withInternals, err) {
// jshint -W022
// http://jslinterrors.com/do-not-assign-to-the-exception-parameter
if (!err.Update) {
// not one of ours, cast it
err = new exports.TemplateError(err);
}
err.Update(path);
// Unless they marked the dev flag, show them a trace from here
if (!withInternals) {
var old = err;
err = new Error(old.message);
err.name = old.name;
}
return err;
};
exports.TemplateError = function(message, lineno, colno) {
var err = this;
if (message instanceof Error) { // for casting regular js errors
err = message;
message = message.name + ': ' + message.message;
try {
if(err.name = '') {}
}
catch(e) {
// If we can't set the name of the error object in this
// environment, don't use it
err = this;
}
} else {
if(Error.captureStackTrace) {
Error.captureStackTrace(err);
}
}
err.name = 'Template render error';
err.message = message;
err.lineno = lineno;
err.colno = colno;
err.firstUpdate = true;
err.Update = function(path) {
var message = '(' + (path || 'unknown path') + ')';
// only show lineno + colno next to path of template
// where error occurred
if (this.firstUpdate) {
if(this.lineno && this.colno) {
message += ' [Line ' + this.lineno + ', Column ' + this.colno + ']';
}
else if(this.lineno) {
message += ' [Line ' + this.lineno + ']';
}
}
message += '\n ';
if (this.firstUpdate) {
message += ' ';
}
this.message = message + (this.message || '');
this.firstUpdate = false;
return this;
};
return err;
};
exports.TemplateError.prototype = Error.prototype;
exports.escape = function(val) {
return val.replace(escapeRegex, lookupEscape);
};
exports.isFunction = function(obj) {
return ObjProto.toString.call(obj) === '[object Function]';
};
exports.isArray = Array.isArray || function(obj) {
return ObjProto.toString.call(obj) === '[object Array]';
};
exports.isString = function(obj) {
return ObjProto.toString.call(obj) === '[object String]';
};
exports.isObject = function(obj) {
return ObjProto.toString.call(obj) === '[object Object]';
};
exports.groupBy = function(obj, val) {
var result = {};
var iterator = exports.isFunction(val) ? val : function(obj) { return obj[val]; };
for(var i=0; i<obj.length; i++) {
var value = obj[i];
var key = iterator(value, i);
(result[key] || (result[key] = [])).push(value);
}
return result;
};
exports.toArray = function(obj) {
return Array.prototype.slice.call(obj);
};
exports.without = function(array) {
var result = [];
if (!array) {
return result;
}
var index = -1,
length = array.length,
contains = exports.toArray(arguments).slice(1);
while(++index < length) {
if(exports.indexOf(contains, array[index]) === -1) {
result.push(array[index]);
}
}
return result;
};
exports.extend = function(obj, obj2) {
for(var k in obj2) {
obj[k] = obj2[k];
}
return obj;
};
exports.repeat = function(char_, n) {
var str = '';
for(var i=0; i<n; i++) {
str += char_;
}
return str;
};
exports.each = function(obj, func, context) {
if(obj == null) {
return;
}
if(ArrayProto.each && obj.each === ArrayProto.each) {
obj.forEach(func, context);
}
else if(obj.length === +obj.length) {
for(var i=0, l=obj.length; i<l; i++) {
func.call(context, obj[i], i, obj);
}
}
};
exports.map = function(obj, func) {
var results = [];
if(obj == null) {
return results;
}
if(ArrayProto.map && obj.map === ArrayProto.map) {
return obj.map(func);
}
for(var i=0; i<obj.length; i++) {
results[results.length] = func(obj[i], i);
}
if(obj.length === +obj.length) {
results.length = obj.length;
}
return results;
};
exports.asyncIter = function(arr, iter, cb) {
var i = -1;
function next() {
i++;
if(i < arr.length) {
iter(arr[i], i, next, cb);
}
else {
cb();
}
}
next();
};
exports.asyncFor = function(obj, iter, cb) {
var keys = exports.keys(obj);
var len = keys.length;
var i = -1;
function next() {
i++;
var k = keys[i];
if(i < len) {
iter(k, obj[k], i, len, next);
}
else {
cb();
}
}
next();
};
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
exports.indexOf = Array.prototype.indexOf ?
function (arr, searchElement, fromIndex) {
return Array.prototype.indexOf.call(arr, searchElement, fromIndex);
} :
function (arr, searchElement, fromIndex) {
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
fromIndex = +fromIndex || 0;
if(Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if(fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for(;fromIndex < length; fromIndex++) {
if (arr[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
if(!Array.prototype.map) {
Array.prototype.map = function() {
throw new Error('map is unimplemented for this js engine');
};
}
exports.keys = function(obj) {
if(Object.prototype.keys) {
return obj.keys();
}
else {
var keys = [];
for(var k in obj) {
if(obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
}
};
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var path = __webpack_require__(3);
var asap = __webpack_require__(4);
var lib = __webpack_require__(1);
var Obj = __webpack_require__(6);
var compiler = __webpack_require__(7);
var builtin_filters = __webpack_require__(13);
var builtin_loaders = __webpack_require__(14);
var runtime = __webpack_require__(12);
var globals = __webpack_require__(17);
var Frame = runtime.Frame;
var Template;
// Unconditionally load in this loader, even if no other ones are
// included (possible in the slim browser build)
builtin_loaders.PrecompiledLoader = __webpack_require__(16);
// If the user is using the async API, *always* call it
// asynchronously even if the template was synchronous.
function callbackAsap(cb, err, res) {
asap(function() { cb(err, res); });
}
var Environment = Obj.extend({
init: function(loaders, opts) {
// The dev flag determines the trace that'll be shown on errors.
// If set to true, returns the full trace from the error point,
// otherwise will return trace starting from Template.render
// (the full trace from within nunjucks may confuse developers using
// the library)
// defaults to false
opts = this.opts = opts || {};
this.opts.dev = !!opts.dev;
// The autoescape flag sets global autoescaping. If true,
// every string variable will be escaped by default.
// If false, strings can be manually escaped using the `escape` filter.
// defaults to true
this.opts.autoescape = opts.autoescape != null ? opts.autoescape : true;
// If true, this will make the system throw errors if trying
// to output a null or undefined value
this.opts.throwOnUndefined = !!opts.throwOnUndefined;
this.opts.trimBlocks = !!opts.trimBlocks;
this.opts.lstripBlocks = !!opts.lstripBlocks;
this.loaders = [];
if(!loaders) {
// The filesystem loader is only available server-side
if(builtin_loaders.FileSystemLoader) {
this.loaders = [new builtin_loaders.FileSystemLoader('views')];
}
else if(builtin_loaders.WebLoader) {
this.loaders = [new builtin_loaders.WebLoader('/views')];
}
}
else {
this.loaders = lib.isArray(loaders) ? loaders : [loaders];
}
// It's easy to use precompiled templates: just include them
// before you configure nunjucks and this will automatically
// pick it up and use it
if((true) && window.nunjucksPrecompiled) {
this.loaders.unshift(
new builtin_loaders.PrecompiledLoader(window.nunjucksPrecompiled)
);
}
this.initCache();
this.globals = globals();
this.filters = {};
this.asyncFilters = [];
this.extensions = {};
this.extensionsList = [];
for(var name in builtin_filters) {
this.addFilter(name, builtin_filters[name]);
}
},
initCache: function() {
// Caching and cache busting
lib.each(this.loaders, function(loader) {
loader.cache = {};
if(typeof loader.on === 'function') {
loader.on('update', function(template) {
loader.cache[template] = null;
});
}
});
},
addExtension: function(name, extension) {
extension._name = name;
this.extensions[name] = extension;
this.extensionsList.push(extension);
return this;
},
removeExtension: function(name) {
var extension = this.getExtension(name);
if (!extension) return;
this.extensionsList = lib.without(this.extensionsList, extension);
delete this.extensions[name];
},
getExtension: function(name) {
return this.extensions[name];
},
hasExtension: function(name) {
return !!this.extensions[name];
},
addGlobal: function(name, value) {
this.globals[name] = value;
return this;
},
getGlobal: function(name) {
if(!this.globals[name]) {
throw new Error('global not found: ' + name);
}
return this.globals[name];
},
addFilter: function(name, func, async) {
var wrapped = func;
if(async) {
this.asyncFilters.push(name);
}
this.filters[name] = wrapped;
return this;
},
getFilter: function(name) {
if(!this.filters[name]) {
throw new Error('filter not found: ' + name);
}
return this.filters[name];
},
resolveTemplate: function(loader, parentName, filename) {
var isRelative = (loader.isRelative && parentName)? loader.isRelative(filename) : false;
return (isRelative && loader.resolve)? loader.resolve(parentName, filename) : filename;
},
getTemplate: function(name, eagerCompile, parentName, ignoreMissing, cb) {
var that = this;
var tmpl = null;
if(name && name.raw) {
// this fixes autoescape for templates referenced in symbols
name = name.raw;
}
if(lib.isFunction(parentName)) {
cb = parentName;
parentName = null;
eagerCompile = eagerCompile || false;
}
if(lib.isFunction(eagerCompile)) {
cb = eagerCompile;
eagerCompile = false;
}
if (name instanceof Template) {
tmpl = name;
}
else if(typeof name !== 'string') {
throw new Error('template names must be a string: ' + name);
}
else {
for (var i = 0; i < this.loaders.length; i++) {
var _name = this.resolveTemplate(this.loaders[i], parentName, name);
tmpl = this.loaders[i].cache[_name];
if (tmpl) break;
}
}
if(tmpl) {
if(eagerCompile) {
tmpl.compile();
}
if(cb) {
cb(null, tmpl);
}
else {
return tmpl;
}
} else {
var syncResult;
var _this = this;
var createTemplate = function(err, info) {
if(!info && !err) {
if(!ignoreMissing) {
err = new Error('template not found: ' + name);
}
}
if (err) {
if(cb) {
cb(err);
}
else {
throw err;
}
}
else {
var tmpl;
if(info) {
tmpl = new Template(info.src, _this,
info.path, eagerCompile);
if(!info.noCache) {
info.loader.cache[name] = tmpl;
}
}
else {
tmpl = new Template('', _this,
'', eagerCompile);
}
if(cb) {
cb(null, tmpl);
}
else {
syncResult = tmpl;
}
}
};
lib.asyncIter(this.loaders, function(loader, i, next, done) {
function handle(err, src) {
if(err) {
done(err);
}
else if(src) {
src.loader = loader;
done(null, src);
}
else {
next();
}
}
// Resolve name relative to parentName
name = that.resolveTemplate(loader, parentName, name);
if(loader.async) {
loader.getSource(name, handle);
}
else {
handle(null, loader.getSource(name));
}
}, createTemplate);
return syncResult;
}
},
express: function(app) {
var env = this;
function NunjucksView(name, opts) {
this.name = name;
this.path = name;
this.defaultEngine = opts.defaultEngine;
this.ext = path.extname(name);
if (!this.ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
if (!this.ext) this.name += (this.ext = ('.' !== this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
}
NunjucksView.prototype.render = function(opts, cb) {
env.render(this.name, opts, cb);
};
app.set('view', NunjucksView);
return this;
},
render: function(name, ctx, cb) {
if(lib.isFunction(ctx)) {
cb = ctx;
ctx = null;
}
// We support a synchronous API to make it easier to migrate
// existing code to async. This works because if you don't do
// anything async work, the whole thing is actually run
// synchronously.
var syncResult = null;
this.getTemplate(name, function(err, tmpl) {
if(err && cb) {
callbackAsap(cb, err);
}
else if(err) {
throw err;
}
else {
syncResult = tmpl.render(ctx, cb);
}
});
return syncResult;
},
renderString: function(src, ctx, opts, cb) {
if(lib.isFunction(opts)) {
cb = opts;
opts = {};
}
opts = opts || {};
var tmpl = new Template(src, this, opts.path);
return tmpl.render(ctx, cb);
}
});
var Context = Obj.extend({
init: function(ctx, blocks, env) {
// Has to be tied to an environment so we can tap into its globals.
this.env = env || new Environment();
// Make a duplicate of ctx
this.ctx = {};
for(var k in ctx) {
if(ctx.hasOwnProperty(k)) {
this.ctx[k] = ctx[k];
}
}
this.blocks = {};
this.exported = [];
for(var name in blocks) {
this.addBlock(name, blocks[name]);
}
},
lookup: function(name) {
// This is one of the most called functions, so optimize for
// the typical case where the name isn't in the globals
if(name in this.env.globals && !(name in this.ctx)) {
return this.env.globals[name];
}
else {
return this.ctx[name];
}
},
setVariable: function(name, val) {
this.ctx[name] = val;
},
getVariables: function() {
return this.ctx;
},
addBlock: function(name, block) {
this.blocks[name] = this.blocks[name] || [];
this.blocks[name].push(block);
return this;
},
getBlock: function(name) {
if(!this.blocks[name]) {
throw new Error('unknown block "' + name + '"');
}
return this.blocks[name][0];
},
getSuper: function(env, name, block, frame, runtime, cb) {
var idx = lib.indexOf(this.blocks[name] || [], block);
var blk = this.blocks[name][idx + 1];
var context = this;
if(idx === -1 || !blk) {
throw new Error('no super block available for "' + name + '"');
}
blk(env, context, frame, runtime, cb);
},
addExport: function(name) {
this.exported.push(name);
},
getExported: function() {
var exported = {};
for(var i=0; i<this.exported.length; i++) {
var name = this.exported[i];
exported[name] = this.ctx[name];
}
return exported;
}
});
Template = Obj.extend({
init: function (src, env, path, eagerCompile) {
this.env = env || new Environment();
if(lib.isObject(src)) {
switch(src.type) {
case 'code': this.tmplProps = src.obj; break;
case 'string': this.tmplStr = src.obj; break;
}
}
else if(lib.isString(src)) {
this.tmplStr = src;
}
else {
throw new Error('src must be a string or an object describing ' +
'the source');
}
this.path = path;
if(eagerCompile) {
var _this = this;
try {
_this._compile();
}
catch(err) {
throw lib.prettifyError(this.path, this.env.dev, err);
}
}
else {
this.compiled = false;
}
},
render: function(ctx, parentFrame, cb) {
if (typeof ctx === 'function') {
cb = ctx;
ctx = {};
}
else if (typeof parentFrame === 'function') {
cb = parentFrame;
parentFrame = null;
}
var forceAsync = true;
if(parentFrame) {
// If there is a frame, we are being called from internal
// code of another template, and the internal system
// depends on the sync/async nature of the parent template
// to be inherited, so force an async callback
forceAsync = false;
}
var _this = this;
// Catch compile errors for async rendering
try {
_this.compile();
} catch (_err) {
var err = lib.prettifyError(this.path, this.env.dev, _err);
if (cb) return callbackAsap(cb, err);
else throw err;
}
var context = new Context(ctx || {}, _this.blocks, _this.env);
var frame = parentFrame ? parentFrame.push() : new Frame();
frame.topLevel = true;
var syncResult = null;
_this.rootRenderFunc(
_this.env,
context,
frame || new Frame(),
runtime,
function(err, res) {
if(err) {
err = lib.prettifyError(_this.path, _this.env.dev, err);
}
if(cb) {
if(forceAsync) {
callbackAsap(cb, err, res);
}
else {
cb(err, res);
}
}
else {
if(err) { throw err; }
syncResult = res;
}
}
);
return syncResult;
},
getExported: function(ctx, parentFrame, cb) {
if (typeof ctx === 'function') {
cb = ctx;
ctx = {};
}
if (typeof parentFrame === 'function') {
cb = parentFrame;
parentFrame = null;
}
// Catch compile errors for async rendering
try {
this.compile();
} catch (e) {
if (cb) return cb(e);
else throw e;
}
var frame = parentFrame ? parentFrame.push() : new Frame();
frame.topLevel = true;
// Run the rootRenderFunc to populate the context with exported vars
var context = new Context(ctx || {}, this.blocks, this.env);
this.rootRenderFunc(this.env,
context,
frame,
runtime,
function(err) {
if ( err ) {
cb(err, null);
} else {
cb(null, context.getExported());
}
});
},
compile: function() {
if(!this.compiled) {
this._compile();
}
},
_compile: function() {
var props;
if(this.tmplProps) {
props = this.tmplProps;
}
else {
var source = compiler.compile(this.tmplStr,
this.env.asyncFilters,
this.env.extensionsList,
this.path,
this.env.opts);
/* jslint evil: true */
var func = new Function(source);
props = func();
}
this.blocks = this._getBlocks(props);
this.rootRenderFunc = props.root;
this.compiled = true;
},
_getBlocks: function(props) {
var blocks = {};
for(var k in props) {
if(k.slice(0, 2) === 'b_') {
blocks[k.slice(2)] = props[k];
}
}
return blocks;
}
});
module.exports = {
Environment: Environment,
Template: Template
};
/***/ },
/* 3 */
/***/ function(module, exports) {
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
// rawAsap provides everything we need except exception management.
var rawAsap = __webpack_require__(5);
// RawTasks are recycled to reduce GC churn.
var freeTasks = [];
// We queue errors to ensure they are thrown in right order (FIFO).
// Array-as-queue is good enough here, since we are just dealing with exceptions.
var pendingErrors = [];
var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);
function throwFirstError() {
if (pendingErrors.length) {
throw pendingErrors.shift();
}
}
/**
* Calls a task as soon as possible after returning, in its own event, with priority
* over other events like animation, reflow, and repaint. An error thrown from an
* event will not interrupt, nor even substantially slow down the processing of
* other events, but will be rather postponed to a lower priority event.
* @param {{call}} task A callable object, typically a function that takes no
* arguments.
*/
module.exports = asap;
function asap(task) {
var rawTask;
if (freeTasks.length) {
rawTask = freeTasks.pop();
} else {
rawTask = new RawTask();
}
rawTask.task = task;
rawAsap(rawTask);
}
// We wrap tasks with recyclable task objects. A task object implements
// `call`, just like a function.
function RawTask() {
this.task = null;
}
// The sole purpose of wrapping the task is to catch the exception and recycle
// the task object after its single use.
RawTask.prototype.call = function () {
try {
this.task.call();
} catch (error) {
if (asap.onerror) {
// This hook exists purely for testing purposes.
// Its name will be periodically randomized to break any code that
// depends on its existence.
asap.onerror(error);
} else {
// In a web browser, exceptions are not fatal. However, to avoid
// slowing down the queue of pending tasks, we rethrow the error in a
// lower priority turn.
pendingErrors.push(error);
requestErrorThrow();
}
} finally {
this.task = null;
freeTasks[freeTasks.length] = this;
}
};
/***/ },
/* 5 */
/***/ function(module, exports) {
/* WEBPACK VAR INJECTION */(function(global) {"use strict";
// Use the fastest means possible to execute a task in its own turn, with
// priority over other events including IO, animation, reflow, and redraw
// events in browsers.
//
// An exception thrown by a task will permanently interrupt the processing of
// subsequent tasks. The higher level `asap` function ensures that if an
// exception is thrown by a task, that the task queue will continue flushing as
// soon as possible, but if you use `rawAsap` directly, you are responsible to
// either ensure that no exceptions are thrown from your task, or to manually
// call `rawAsap.requestFlush` if an exception is thrown.
module.exports = rawAsap;
function rawAsap(task) {
if (!queue.length) {
requestFlush();
flushing = true;
}
// Equivalent to push, but avoids a function call.
queue[queue.length] = task;
}
var queue = [];
// Once a flush has been requested, no further calls to `requestFlush` are
// necessary until the next `flush` completes.
var flushing = false;
// `requestFlush` is an implementation-specific method that attempts to kick
// off a `flush` event as quickly as possible. `flush` will attempt to exhaust
// the event queue before yielding to the browser's own event loop.
var requestFlush;
// The position of the next task to execute in the task queue. This is
// preserved between calls to `flush` so that it can be resumed if
// a task throws an exception.
var index = 0;
// If a task schedules additional tasks recursively, the task queue can grow
// unbounded. To prevent memory exhaustion, the task queue will periodically
// truncate already-completed tasks.
var capacity = 1024;
// The flush function processes all tasks that have been scheduled with
// `rawAsap` unless and until one of those tasks throws an exception.
// If a task throws an exception, `flush` ensures that its state will remain
// consistent and will resume where it left off when called again.
// However, `flush` does not make any arrangements to be called again if an
// exception is thrown.
function flush() {
while (index < queue.length) {
var currentIndex = index;
// Advance the index before calling the task. This ensures that we will
// begin flushing on the next task the task throws an error.
index = index + 1;
queue[currentIndex].call();
// Prevent leaking memory for long chains of recursive calls to `asap`.
// If we call `asap` within tasks scheduled by `asap`, the queue will
// grow, but to avoid an O(n) walk for every task we execute, we don't
// shift tasks off the queue after they have been executed.
// Instead, we periodically shift 1024 tasks off the queue.
if (index > capacity) {
// Manually shift all values starting at the index back to the
// beginning of the queue.
for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {
queue[scan] = queue[scan + index];
}
queue.length -= index;
index = 0;
}
}
queue.length = 0;
index = 0;
flushing = false;
}
// `requestFlush` is implemented using a strategy based on data collected from
// every available SauceLabs Selenium web driver worker at time of writing.
// https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593
// Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that
// have WebKitMutationObserver but not un-prefixed MutationObserver.
// Must use `global` instead of `window` to work in both frames and web
// workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.
var BrowserMutationObserver = global.MutationObserver || global.WebKitMutationObserver;
// MutationObservers are desirable because they have high priority and work
// reliably everywhere they are implemented.
// They are implemented in all modern browsers.
//
// - Android 4-4.3
// - Chrome 26-34
// - Firefox 14-29
// - Internet Explorer 11
// - iPad Safari 6-7.1
// - iPhone Safari 7-7.1
// - Safari 6-7
if (typeof BrowserMutationObserver === "function") {
requestFlush = makeRequestCallFromMutationObserver(flush);
// MessageChannels are desirable because they give direct access to the HTML
// task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera
// 11-12, and in web workers in many engines.
// Although message channels yield to any queued rendering and IO tasks, they
// would be better than imposing the 4ms delay of timers.
// However, they do not work reliably in Internet Explorer or Safari.
// Internet Explorer 10 is the only browser that has setImmediate but does
// not have MutationObservers.
// Although setImmediate yields to the browser's renderer, it would be
// preferrable to falling back to setTimeout since it does not have
// the minimum 4ms penalty.
// Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and
// Desktop to a lesser extent) that renders both setImmediate and
// MessageChannel useless for the purposes of ASAP.
// https://github.com/kriskowal/q/issues/396
// Timers are implemented universally.
// We fall back to timers in workers in most engines, and in foreground
// contexts in the following browsers.
// However, note that even this simple case requires nuances to operate in a
// broad spectrum of browsers.
//
// - Firefox 3-13
// - Internet Explorer 6-9
// - iPad Safari 4.3
// - Lynx 2.8.7
} else {
requestFlush = makeRequestCallFromTimer(flush);
}
// `requestFlush` requests that the high priority event queue be flushed as
// soon as possible.
// This is useful to prevent an error thrown in a task from stalling the event
// queue if the exception handled by Node.jss
// `process.on("uncaughtException")` or by a domain.
rawAsap.requestFlush = requestFlush;
// To request a high priority event, we induce a mutation observer by toggling
// the text of a text node between "1" and "-1".
function makeRequestCallFromMutationObserver(callback) {
var toggle = 1;
var observer = new BrowserMutationObserver(callback);
var node = document.createTextNode("");
observer.observe(node, {characterData: true});
return function requestCall() {
toggle = -toggle;
node.data = toggle;
};
}
// The message channel technique was discovered by Malte Ubl and was the
// original foundation for this library.
// http://www.nonblocking.io/2011/06/windownexttick.html
// Safari 6.0.5 (at least) intermittently fails to create message ports on a
// page's first load. Thankfully, this version of Safari supports
// MutationObservers, so we don't need to fall back in that case.
// function makeRequestCallFromMessageChannel(callback) {
// var channel = new MessageChannel();
// channel.port1.onmessage = callback;
// return function requestCall() {
// channel.port2.postMessage(0);
// };
// }
// For reasons explained above, we are also unable to use `setImmediate`
// under any circumstances.
// Even if we were, there is another bug in Internet Explorer 10.
// It is not sufficient to assign `setImmediate` to `requestFlush` because
// `setImmediate` must be called *by name* and therefore must be wrapped in a
// closure.
// Never forget.
// function makeRequestCallFromSetImmediate(callback) {
// return function requestCall() {
// setImmediate(callback);
// };
// }
// Safari 6.0 has a problem where timers will get lost while the user is
// scrolling. This problem does not impact ASAP because Safari 6.0 supports
// mutation observers, so that implementation is used instead.
// However, if we ever elect to use timers in Safari, the prevalent work-around
// is to add a scroll event listener that calls for a flush.
// `setTimeout` does not call the passed callback if the delay is less than
// approximately 7 in web workers in Firefox 8 through 18, and sometimes not
// even then.
function makeRequestCallFromTimer(callback) {
return function requestCall() {
// We dispatch a timeout with a specified delay of 0 for engines that
// can reliably accommodate that request. This will usually be snapped
// to a 4 milisecond delay, but once we're flushing, there's no delay
// between events.
var timeoutHandle = setTimeout(handleTimer, 0);
// However, since this timer gets frequently dropped in Firefox
// workers, we enlist an interval handle that will try to fire
// an event 20 times per second until it succeeds.
var intervalHandle = setInterval(handleTimer, 50);
function handleTimer() {
// Whichever timer succeeds will cancel both timers and
// execute the callback.
clearTimeout(timeoutHandle);
clearInterval(intervalHandle);
callback();
}
};
}
// This is for `asap.js` only.
// Its name will be periodically randomized to break any code that depends on
// its existence.
rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;
// ASAP was originally a nextTick shim included in Q. This was factored out
// into this ASAP package. It was later adapted to RSVP which made further
// amendments. These decisions, particularly to marginalize MessageChannel and
// to capture the MutationObserver implementation in a closure, were integrated
// back into ASAP proper.
// https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 6 */
/***/ function(module, exports) {
'use strict';
// A simple class system, more documentation to come
function extend(cls, name, props) {
// This does that same thing as Object.create, but with support for IE8
var F = function() {};
F.prototype = cls.prototype;
var prototype = new F();
// jshint undef: false
var fnTest = /xyz/.test(function(){ xyz; }) ? /\bparent\b/ : /.*/;
props = props || {};
for(var k in props) {
var src = props[k];
var parent = prototype[k];
if(typeof parent === 'function' &&
typeof src === 'function' &&
fnTest.test(src)) {
/*jshint -W083 */
prototype[k] = (function (src, parent) {
return function() {
// Save the current parent method
var tmp = this.parent;
// Set parent to the previous method, call, and restore
this.parent = parent;
var res = src.apply(this, arguments);
this.parent = tmp;
return res;
};
})(src, parent);
}
else {
prototype[k] = src;
}
}
prototype.typename = name;
var new_cls = function() {
if(prototype.init) {
prototype.init.apply(this, arguments);
}
};
new_cls.prototype = prototype;
new_cls.prototype.constructor = new_cls;
new_cls.extend = function(name, props) {
if(typeof name === 'object') {
props = name;
name = 'anonymous';
}
return extend(new_cls, name, props);
};
return new_cls;
}
module.exports = extend(Object, 'Object', {});
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lib = __webpack_require__(1);
var parser = __webpack_require__(8);
var transformer = __webpack_require__(11);
var nodes = __webpack_require__(10);
// jshint -W079
var Object = __webpack_require__(6);
var Frame = __webpack_require__(12).Frame;
// These are all the same for now, but shouldn't be passed straight
// through
var compareOps = {
'==': '==',
'!=': '!=',
'<': '<',
'>': '>',
'<=': '<=',
'>=': '>='
};
// A common pattern is to emit binary operators
function binOpEmitter(str) {
return function(node, frame) {
this.compile(node.left, frame);
this.emit(str);
this.compile(node.right, frame);
};
}
var Compiler = Object.extend({
init: function(templateName, throwOnUndefined) {
this.templateName = templateName;
this.codebuf = [];
this.lastId = 0;
this.buffer = null;
this.bufferStack = [];
this.scopeClosers = '';
this.inBlock = false;
this.throwOnUndefined = throwOnUndefined;
},
fail: function (msg, lineno, colno) {
if (lineno !== undefined) lineno += 1;
if (colno !== undefined) colno += 1;
throw new lib.TemplateError(msg, lineno, colno);
},
pushBufferId: function(id) {
this.bufferStack.push(this.buffer);
this.buffer = id;
this.emit('var ' + this.buffer + ' = "";');
},
popBufferId: function() {
this.buffer = this.bufferStack.pop();
},
emit: function(code) {
this.codebuf.push(code);
},
emitLine: function(code) {
this.emit(code + '\n');
},
emitLines: function() {
lib.each(lib.toArray(arguments), function(line) {
this.emitLine(line);
}, this);
},
emitFuncBegin: function(name) {
this.buffer = 'output';
this.scopeClosers = '';
this.emitLine('function ' + name + '(env, context, frame, runtime, cb) {');
this.emitLine('var lineno = null;');
this.emitLine('var colno = null;');
this.emitLine('var ' + this.buffer + ' = "";');
this.emitLine('try {');
},
emitFuncEnd: function(noReturn) {
if(!noReturn) {
this.emitLine('cb(null, ' + this.buffer +');');
}
this.closeScopeLevels();
this.emitLine('} catch (e) {');
this.emitLine(' cb(runtime.handleError(e, lineno, colno));');
this.emitLine('}');
this.emitLine('}');
this.buffer = null;
},
addScopeLevel: function() {
this.scopeClosers += '})';
},
closeScopeLevels: function() {
this.emitLine(this.scopeClosers + ';');
this.scopeClosers = '';
},
withScopedSyntax: function(func) {
var scopeClosers = this.scopeClosers;
this.scopeClosers = '';
func.call(this);
this.closeScopeLevels();
this.scopeClosers = scopeClosers;
},
makeCallback: function(res) {
var err = this.tmpid();
return 'function(' + err + (res ? ',' + res : '') + ') {\n' +
'if(' + err + ') { cb(' + err + '); return; }';
},
tmpid: function() {
this.lastId++;
return 't_' + this.lastId;
},
_templateName: function() {
return this.templateName == null? 'undefined' : JSON.stringify(this.templateName);
},
_compileChildren: function(node, frame) {
var children = node.children;
for(var i=0, l=children.length; i<l; i++) {
this.compile(children[i], frame);
}
},
_compileAggregate: function(node, frame, startChar, endChar) {
if(startChar) {
this.emit(startChar);
}
for(var i=0; i<node.children.length; i++) {
if(i > 0) {
this.emit(',');
}
this.compile(node.children[i], frame);
}
if(endChar) {
this.emit(endChar);
}
},
_compileExpression: function(node, frame) {
// TODO: I'm not really sure if this type check is worth it or
// not.
this.assertType(
node,
nodes.Literal,
nodes.Symbol,
nodes.Group,
nodes.Array,
nodes.Dict,
nodes.FunCall,
nodes.Caller,
nodes.Filter,
nodes.LookupVal,
nodes.Compare,
nodes.InlineIf,
nodes.In,
nodes.And,
nodes.Or,
nodes.Not,
nodes.Add,
nodes.Concat,
nodes.Sub,
nodes.Mul,
nodes.Div,
nodes.FloorDiv,
nodes.Mod,
nodes.Pow,
nodes.Neg,
nodes.Pos,
nodes.Compare,
nodes.NodeList
);
this.compile(node, frame);
},
assertType: function(node /*, types */) {
var types = lib.toArray(arguments).slice(1);
var success = false;
for(var i=0; i<types.length; i++) {
if(node instanceof types[i]) {
success = true;
}
}
if(!success) {
this.fail('assertType: invalid type: ' + node.typename,
node.lineno,
node.colno);
}
},
compileCallExtension: function(node, frame, async) {
var args = node.args;
var contentArgs = node.contentArgs;
var autoescape = typeof node.autoescape === 'boolean' ? node.autoescape : true;
if(!async) {
this.emit(this.buffer + ' += runtime.suppressValue(');
}
this.emit('env.getExtension("' + node.extName + '")["' + node.prop + '"](');
this.emit('context');
if(args || contentArgs) {
this.emit(',');
}
if(args) {
if(!(args instanceof nodes.NodeList)) {
this.fail('compileCallExtension: arguments must be a NodeList, ' +
'use `parser.parseSignature`');
}
lib.each(args.children, function(arg, i) {
// Tag arguments are passed normally to the call. Note
// that keyword arguments are turned into a single js
// object as the last argument, if they exist.
this._compileExpression(arg, frame);
if(i !== args.children.length - 1 || contentArgs.length) {
this.emit(',');
}
}, this);
}
if(contentArgs.length) {
lib.each(contentArgs, function(arg, i) {
if(i > 0) {
this.emit(',');
}
if(arg) {
var id = this.tmpid();
this.emitLine('function(cb) {');
this.emitLine('if(!cb) { cb = function(err) { if(err) { throw err; }}}');
this.pushBufferId(id);
this.withScopedSyntax(function() {
this.compile(arg, frame);
this.emitLine('cb(null, ' + id + ');');
});
this.popBufferId();
this.emitLine('return ' + id + ';');
this.emitLine('}');
}
else {
this.emit('null');
}
}, this);
}
if(async) {
var res = this.tmpid();
this.emitLine(', ' + this.makeCallback(res));
this.emitLine(this.buffer + ' += runtime.suppressValue(' + res + ', ' + autoescape + ' && env.opts.autoescape);');
this.addScopeLevel();
}
else {
this.emit(')');
this.emit(', ' + autoescape + ' && env.opts.autoescape);\n');
}
},
compileCallExtensionAsync: function(node, frame) {
this.compileCallExtension(node, frame, true);
},
compileNodeList: function(node, frame) {
this._compileChildren(node, frame);
},
compileLiteral: function(node) {
if(typeof node.value === 'string') {
var val = node.value.replace(/\\/g, '\\\\');
val = val.replace(/"/g, '\\"');
val = val.replace(/\n/g, '\\n');
val = val.replace(/\r/g, '\\r');
val = val.replace(/\t/g, '\\t');
this.emit('"' + val + '"');
}
else if (node.value === null) {
this.emit('null');
}
else {
this.emit(node.value.toString());
}
},
compileSymbol: function(node, frame) {
var name = node.value;
var v;
if((v = frame.lookup(name))) {
this.emit(v);
}
else {
this.emit('runtime.contextOrFrameLookup(' +
'context, frame, "' + name + '")');
}
},
compileGroup: function(node, frame) {
this._compileAggregate(node, frame, '(', ')');
},
compileArray: function(node, frame) {
this._compileAggregate(node, frame, '[', ']');
},
compileDict: function(node, frame) {
this._compileAggregate(node, frame, '{', '}');
},
compilePair: function(node, frame) {
var key = node.key;
var val = node.value;
if(key instanceof nodes.Symbol) {
key = new nodes.Literal(key.lineno, key.colno, key.value);
}
else if(!(key instanceof nodes.Literal &&
typeof key.value === 'string')) {
this.fail('compilePair: Dict keys must be strings or names',
key.lineno,
key.colno);
}
this.compile(key, frame);
this.emit(': ');
this._compileExpression(val, frame);
},
compileInlineIf: function(node, frame) {
this.emit('(');
this.compile(node.cond, frame);
this.emit('?');
this.compile(node.body, frame);
this.emit(':');
if(node.else_ !== null)
this.compile(node.else_, frame);
else
this.emit('""');
this.emit(')');
},
compileIn: function(node, frame) {
this.emit('(');
this.compile(node.right, frame);
this.emit('.indexOf(');
this.compile(node.left, frame);
this.emit(') !== -1)');
},
compileOr: binOpEmitter(' || '),
compileAnd: binOpEmitter(' && '),
compileAdd: binOpEmitter(' + '),
// ensure concatenation instead of addition
// by adding empty string in between
compileConcat: binOpEmitter(' + "" + '),
compileSub: binOpEmitter(' - '),
compileMul: binOpEmitter(' * '),
compileDiv: binOpEmitter(' / '),
compileMod: binOpEmitter(' % '),
compileNot: function(node, frame) {
this.emit('!');
this.compile(node.target, frame);
},
compileFloorDiv: function(node, frame) {
this.emit('Math.floor(');
this.compile(node.left, frame);
this.emit(' / ');
this.compile(node.right, frame);
this.emit(')');
},
compilePow: function(node, frame) {
this.emit('Math.pow(');
this.compile(node.left, frame);
this.emit(', ');
this.compile(node.right, frame);
this.emit(')');
},
compileNeg: function(node, frame) {
this.emit('-');
this.compile(node.target, frame);
},
compilePos: function(node, frame) {
this.emit('+');
this.compile(node.target, frame);
},
compileCompare: function(node, frame) {
this.compile(node.expr, frame);
for(var i=0; i<node.ops.length; i++) {
var n = node.ops[i];
this.emit(' ' + compareOps[n.type] + ' ');
this.compile(n.expr, frame);
}
},
compileLookupVal: function(node, frame) {
this.emit('runtime.memberLookup((');
this._compileExpression(node.target, frame);
this.emit('),');
this._compileExpression(node.val, frame);
this.emit(')');
},
_getNodeName: function(node) {
switch (node.typename) {
case 'Symbol':
return node.value;
case 'FunCall':
return 'the return value of (' + this._getNodeName(node.name) + ')';
case 'LookupVal':
return this._getNodeName(node.target) + '["' +
this._getNodeName(node.val) + '"]';
case 'Literal':
return node.value.toString().substr(0, 10);
default:
return '--expression--';
}
},
compileFunCall: function(node, frame) {
// Keep track of line/col info at runtime by settings
// variables within an expression. An expression in javascript
// like (x, y, z) returns the last value, and x and y can be
// anything
this.emit('(lineno = ' + node.lineno +
', colno = ' + node.colno + ', ');
this.emit('runtime.callWrap(');
// Compile it as normal.
this._compileExpression(node.name, frame);
// Output the name of what we're calling so we can get friendly errors
// if the lookup fails.
this.emit(', "' + this._getNodeName(node.name).replace(/"/g, '\\"') + '", context, ');
this._compileAggregate(node.args, frame, '[', '])');
this.emit(')');
},
compileFilter: function(node, frame) {
var name = node.name;
this.assertType(name, nodes.Symbol);
this.emit('env.getFilter("' + name.value + '").call(context, ');
this._compileAggregate(node.args, frame);
this.emit(')');
},
compileFilterAsync: function(node, frame) {
var name = node.name;
this.assertType(name, nodes.Symbol);
var symbol = node.symbol.value;
frame.set(symbol, symbol);
this.emit('env.getFilter("' + name.value + '").call(context, ');
this._compileAggregate(node.args, frame);
this.emitLine(', ' + this.makeCallback(symbol));
this.addScopeLevel();
},
compileKeywordArgs: function(node, frame) {
var names = [];
lib.each(node.children, function(pair) {
names.push(pair.key.value);
});
this.emit('runtime.makeKeywordArgs(');
this.compileDict(node, frame);
this.emit(')');
},
compileSet: function(node, frame) {
var ids = [];
// Lookup the variable names for each identifier and create
// new ones if necessary
lib.each(node.targets, function(target) {
var name = target.value;
var id = frame.lookup(name);
if (id === null || id === undefined) {
id = this.tmpid();
// Note: This relies on js allowing scope across
// blocks, in case this is created inside an `if`
this.emitLine('var ' + id + ';');
}
ids.push(id);
}, this);
this.emit(ids.join(' = ') + ' = ');
this._compileExpression(node.value, frame);
this.emitLine(';');
lib.each(node.targets, function(target, i) {
var id = ids[i];
var name = target.value;
// We are running this for every var, but it's very
// uncommon to assign to multiple vars anyway
this.emitLine('frame.set("' + name + '", ' + id + ', true);');
this.emitLine('if(frame.topLevel) {');
this.emitLine('context.setVariable("' + name + '", ' + id + ');');
this.emitLine('}');
if(name.charAt(0) !== '_') {
this.emitLine('if(frame.topLevel) {');
this.emitLine('context.addExport("' + name + '", ' + id + ');');
this.emitLine('}');
}
}, this);
},
compileIf: function(node, frame, async) {
this.emit('if(');
this._compileExpression(node.cond, frame);
this.emitLine(') {');
this.withScopedSyntax(function() {
this.compile(node.body, frame);
if(async) {
this.emit('cb()');
}
});
if(node.else_) {
this.emitLine('}\nelse {');
this.withScopedSyntax(function() {
this.compile(node.else_, frame);
if(async) {
this.emit('cb()');
}
});
} else if(async) {
this.emitLine('}\nelse {');
this.emit('cb()');
}
this.emitLine('}');
},
compileIfAsync: function(node, frame) {
this.emit('(function(cb) {');
this.compileIf(node, frame, true);
this.emit('})(function() {');
this.addScopeLevel();
},
emitLoopBindings: function(node, arr, i, len) {
var bindings = {
index: i + ' + 1',
index0: i,
revindex: len + ' - ' + i,
revindex0: len + ' - ' + i + ' - 1',
first: i + ' === 0',
last: i + ' === ' + len + ' - 1',
length: len
};
for (var name in bindings) {
this.emitLine('frame.set("loop.' + name + '", ' + bindings[name] + ');');
}
},
compileFor: function(node, frame) {
// Some of this code is ugly, but it keeps the generated code
// as fast as possible. ForAsync also shares some of this, but
// not much.
var v;
var i = this.tmpid();
var len = this.tmpid();
var arr = this.tmpid();
frame = frame.push();
this.emitLine('frame = frame.push();');
this.emit('var ' + arr + ' = ');
this._compileExpression(node.arr, frame);
this.emitLine(';');
this.emit('if(' + arr + ') {');
// If multiple names are passed, we need to bind them
// appropriately
if(node.name instanceof nodes.Array) {
this.emitLine('var ' + i + ';');
// The object could be an arroy or object. Note that the
// body of the loop is duplicated for each condition, but
// we are optimizing for speed over size.
this.emitLine('if(runtime.isArray(' + arr + ')) {'); {
this.emitLine('var ' + len + ' = ' + arr + '.length;');
this.emitLine('for(' + i + '=0; ' + i + ' < ' + arr + '.length; '
+ i + '++) {');
// Bind each declared var
for (var u=0; u < node.name.children.length; u++) {
var tid = this.tmpid();
this.emitLine('var ' + tid + ' = ' + arr + '[' + i + '][' + u + ']');
this.emitLine('frame.set("' + node.name.children[u].value
+ '", ' + arr + '[' + i + '][' + u + ']' + ');');
frame.set(node.name.children[u].value, tid);
}
this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
});
this.emitLine('}');
}
this.emitLine('} else {'); {
// Iterate over the key/values of an object
var key = node.name.children[0];
var val = node.name.children[1];
var k = this.tmpid();
v = this.tmpid();
frame.set(key.value, k);
frame.set(val.value, v);
this.emitLine(i + ' = -1;');
this.emitLine('var ' + len + ' = runtime.keys(' + arr + ').length;');
this.emitLine('for(var ' + k + ' in ' + arr + ') {');
this.emitLine(i + '++;');
this.emitLine('var ' + v + ' = ' + arr + '[' + k + '];');
this.emitLine('frame.set("' + key.value + '", ' + k + ');');
this.emitLine('frame.set("' + val.value + '", ' + v + ');');
this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
});
this.emitLine('}');
}
this.emitLine('}');
}
else {
// Generate a typical array iteration
v = this.tmpid();
frame.set(node.name.value, v);
this.emitLine('var ' + len + ' = ' + arr + '.length;');
this.emitLine('for(var ' + i + '=0; ' + i + ' < ' + arr + '.length; ' +
i + '++) {');
this.emitLine('var ' + v + ' = ' + arr + '[' + i + '];');
this.emitLine('frame.set("' + node.name.value + '", ' + v + ');');
this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
this.compile(node.body, frame);
});
this.emitLine('}');
}
this.emitLine('}');
if (node.else_) {
this.emitLine('if (!' + len + ') {');
this.compile(node.else_, frame);
this.emitLine('}');
}
this.emitLine('frame = frame.pop();');
},
_compileAsyncLoop: function(node, frame, parallel) {
// This shares some code with the For tag, but not enough to
// worry about. This iterates across an object asynchronously,
// but not in parallel.
var i = this.tmpid();
var len = this.tmpid();
var arr = this.tmpid();
var asyncMethod = parallel ? 'asyncAll' : 'asyncEach';
frame = frame.push();
this.emitLine('frame = frame.push();');
this.emit('var ' + arr + ' = ');
this._compileExpression(node.arr, frame);
this.emitLine(';');
if(node.name instanceof nodes.Array) {
this.emit('runtime.' + asyncMethod + '(' + arr + ', ' +
node.name.children.length + ', function(');
lib.each(node.name.children, function(name) {
this.emit(name.value + ',');
}, this);
this.emit(i + ',' + len + ',next) {');
lib.each(node.name.children, function(name) {
var id = name.value;
frame.set(id, id);
this.emitLine('frame.set("' + id + '", ' + id + ');');
}, this);
}
else {
var id = node.name.value;
this.emitLine('runtime.' + asyncMethod + '(' + arr + ', 1, function(' + id + ', ' + i + ', ' + len + ',next) {');
this.emitLine('frame.set("' + id + '", ' + id + ');');
frame.set(id, id);
}
this.emitLoopBindings(node, arr, i, len);
this.withScopedSyntax(function() {
var buf;
if(parallel) {
buf = this.tmpid();
this.pushBufferId(buf);
}
this.compile(node.body, frame);
this.emitLine('next(' + i + (buf ? ',' + buf : '') + ');');
if(parallel) {
this.popBufferId();
}
});
var output = this.tmpid();
this.emitLine('}, ' + this.makeCallback(output));
this.addScopeLevel();
if(parallel) {
this.emitLine(this.buffer + ' += ' + output + ';');
}
if (node.else_) {
this.emitLine('if (!' + arr + '.length) {');
this.compile(node.else_, frame);
this.emitLine('}');
}
this.emitLine('frame = frame.pop();');
},
compileAsyncEach: function(node, frame) {
this._compileAsyncLoop(node, frame);
},
compileAsyncAll: function(node, frame) {
this._compileAsyncLoop(node, frame, true);
},
_compileMacro: function(node, frame) {
var args = [];
var kwargs = null;
var funcId = 'macro_' + this.tmpid();
// Type check the definition of the args
lib.each(node.args.children, function(arg, i) {
if(i === node.args.children.length - 1 &&
arg instanceof nodes.Dict) {
kwargs = arg;
}
else {
this.assertType(arg, nodes.Symbol);
args.push(arg);
}
}, this);
var realNames = lib.map(args, function(n) { return 'l_' + n.value; });
realNames.push('kwargs');
// Quoted argument names
var argNames = lib.map(args, function(n) { return '"' + n.value + '"'; });
var kwargNames = lib.map((kwargs && kwargs.children) || [],
function(n) { return '"' + n.key.value + '"'; });
// We pass a function to makeMacro which destructures the
// arguments so support setting positional args with keywords
// args and passing keyword args as positional args
// (essentially default values). See runtime.js.
frame = frame.push();
this.emitLines(
'var ' + funcId + ' = runtime.makeMacro(',
'[' + argNames.join(', ') + '], ',
'[' + kwargNames.join(', ') + '], ',
'function (' + realNames.join(', ') + ') {',
'frame = frame.push();',
'kwargs = kwargs || {};',
'if (kwargs.hasOwnProperty("caller")) {',
'frame.set("caller", kwargs.caller); }'
);
// Expose the arguments to the template. Don't need to use
// random names because the function
// will create a new run-time scope for us
lib.each(args, function(arg) {
this.emitLine('frame.set("' + arg.value + '", ' +
'l_' + arg.value + ');');
frame.set(arg.value, 'l_' + arg.value);
}, this);
// Expose the keyword arguments
if(kwargs) {
lib.each(kwargs.children, function(pair) {
var name = pair.key.value;
this.emit('frame.set("' + name + '", ' +
'kwargs.hasOwnProperty("' + name + '") ? ' +
'kwargs["' + name + '"] : ');
this._compileExpression(pair.value, frame);
this.emitLine(');');
}, this);
}
var bufferId = this.tmpid();
this.pushBufferId(bufferId);
this.withScopedSyntax(function () {
this.compile(node.body, frame);
});
frame = frame.pop();
this.emitLine('frame = frame.pop();');
this.emitLine('return new runtime.SafeString(' + bufferId + ');');
this.emitLine('});');
this.popBufferId();
return funcId;
},
compileMacro: function(node, frame) {
var funcId = this._compileMacro(node, frame);
// Expose the macro to the templates
var name = node.name.value;
frame.set(name, funcId);
if(frame.parent) {
this.emitLine('frame.set("' + name + '", ' + funcId + ');');
}
else {
if(node.name.value.charAt(0) !== '_') {
this.emitLine('context.addExport("' + name + '");');
}
this.emitLine('context.setVariable("' + name + '", ' + funcId + ');');
}
},
compileCaller: function(node, frame) {
// basically an anonymous "macro expression"
this.emit('(function (){');
var funcId = this._compileMacro(node, frame);
this.emit('return ' + funcId + ';})()');
},
compileImport: function(node, frame) {
var id = this.tmpid();
var target = node.target.value;
this.emit('env.getTemplate(');
this._compileExpression(node.template, frame);
this.emitLine(', false, '+this._templateName()+', false, ' + this.makeCallback(id));
this.addScopeLevel();
this.emitLine(id + '.getExported(' +
(node.withContext ? 'context.getVariables(), frame, ' : '') +
this.makeCallback(id));
this.addScopeLevel();
frame.set(target, id);
if(frame.parent) {
this.emitLine('frame.set("' + target + '", ' + id + ');');
}
else {
this.emitLine('context.setVariable("' + target + '", ' + id + ');');
}
},
compileFromImport: function(node, frame) {
var importedId = this.tmpid();
this.emit('env.getTemplate(');
this._compileExpression(node.template, frame);
this.emitLine(', false, '+this._templateName()+', false, ' + this.makeCallback(importedId));
this.addScopeLevel();
this.emitLine(importedId + '.getExported(' +
(node.withContext ? 'context.getVariables(), frame, ' : '') +
this.makeCallback(importedId));
this.addScopeLevel();
lib.each(node.names.children, function(nameNode) {
var name;
var alias;
var id = this.tmpid();
if(nameNode instanceof nodes.Pair) {
name = nameNode.key.value;
alias = nameNode.value.value;
}
else {
name = nameNode.value;
alias = name;
}
this.emitLine('if(' + importedId + '.hasOwnProperty("' + name + '")) {');
this.emitLine('var ' + id + ' = ' + importedId + '.' + name + ';');
this.emitLine('} else {');
this.emitLine('cb(new Error("cannot import \'' + name + '\'")); return;');
this.emitLine('}');
frame.set(alias, id);
if(frame.parent) {
this.emitLine('frame.set("' + alias + '", ' + id + ');');
}
else {
this.emitLine('context.setVariable("' + alias + '", ' + id + ');');
}
}, this);
},
compileBlock: function(node) {
var id = this.tmpid();
// If we are executing outside a block (creating a top-level
// block), we really don't want to execute its code because it
// will execute twice: once when the child template runs and
// again when the parent template runs. Note that blocks
// within blocks will *always* execute immediately *and*
// wherever else they are invoked (like used in a parent
// template). This may have behavioral differences from jinja
// because blocks can have side effects, but it seems like a
// waste of performance to always execute huge top-level
// blocks twice
if(!this.inBlock) {
this.emit('(parentTemplate ? function(e, c, f, r, cb) { cb(""); } : ');
}
this.emit('context.getBlock("' + node.name.value + '")');
if(!this.inBlock) {
this.emit(')');
}
this.emitLine('(env, context, frame, runtime, ' + this.makeCallback(id));
this.emitLine(this.buffer + ' += ' + id + ';');
this.addScopeLevel();
},
compileSuper: function(node, frame) {
var name = node.blockName.value;
var id = node.symbol.value;
this.emitLine('context.getSuper(env, ' +
'"' + name + '", ' +
'b_' + name + ', ' +
'frame, runtime, '+
this.makeCallback(id));
this.emitLine(id + ' = runtime.markSafe(' + id + ');');
this.addScopeLevel();
frame.set(id, id);
},
compileExtends: function(node, frame) {
var k = this.tmpid();
this.emit('env.getTemplate(');
this._compileExpression(node.template, frame);
this.emitLine(', true, '+this._templateName()+', false, ' + this.makeCallback('_parentTemplate'));
// extends is a dynamic tag and can occur within a block like
// `if`, so if this happens we need to capture the parent
// template in the top-level scope
this.emitLine('parentTemplate = _parentTemplate');
this.emitLine('for(var ' + k + ' in parentTemplate.blocks) {');
this.emitLine('context.addBlock(' + k +
', parentTemplate.blocks[' + k + ']);');
this.emitLine('}');
this.addScopeLevel();
},
compileInclude: function(node, frame) {
var id = this.tmpid();
var id2 = this.tmpid();
this.emit('env.getTemplate(');
this._compileExpression(node.template, frame);
this.emitLine(', false, '+this._templateName()+', ' + node.ignoreMissing + ', ' + this.makeCallback(id));
this.addScopeLevel();
this.emitLine(id + '.render(' +
'context.getVariables(), frame, ' + this.makeCallback(id2));
this.emitLine(this.buffer + ' += ' + id2);
this.addScopeLevel();
},
compileTemplateData: function(node, frame) {
this.compileLiteral(node, frame);
},
compileOutput: function(node, frame) {
var children = node.children;
for(var i=0, l=children.length; i<l; i++) {
// TemplateData is a special case because it is never
// autoescaped, so simply output it for optimization
if(children[i] instanceof nodes.TemplateData) {
if(children[i].value) {
this.emit(this.buffer + ' += ');
this.compileLiteral(children[i], frame);
this.emitLine(';');
}
}
else {
this.emit(this.buffer + ' += runtime.suppressValue(');
if(this.throwOnUndefined) {
this.emit('runtime.ensureDefined(');
}
this.compile(children[i], frame);
if(this.throwOnUndefined) {
this.emit(',' + node.lineno + ',' + node.colno + ')');
}
this.emit(', env.opts.autoescape);\n');
}
}
},
compileRoot: function(node, frame) {
if(frame) {
this.fail('compileRoot: root node can\'t have frame');
}
frame = new Frame();
this.emitFuncBegin('root');
this.emitLine('var parentTemplate = null;');
this._compileChildren(node, frame);
this.emitLine('if(parentTemplate) {');
this.emitLine('parentTemplate.rootRenderFunc(env, context, frame, runtime, cb);');
this.emitLine('} else {');
this.emitLine('cb(null, ' + this.buffer +');');
this.emitLine('}');
this.emitFuncEnd(true);
this.inBlock = true;
var i, name, block, blocks = node.findAll(nodes.Block);
for (i = 0; i < blocks.length; i++) {
block = blocks[i];
name = block.name.value;
this.emitFuncBegin('b_' + name);
var tmpFrame = new Frame();
this.compile(block.body, tmpFrame);
this.emitFuncEnd();
}
this.emitLine('return {');
for (i = 0; i < blocks.length; i++) {
block = blocks[i];
name = 'b_' + block.name.value;
this.emitLine(name + ': ' + name + ',');
}
this.emitLine('root: root\n};');
},
compile: function (node, frame) {
var _compile = this['compile' + node.typename];
if(_compile) {
_compile.call(this, node, frame);
}
else {
this.fail('compile: Cannot compile node: ' + node.typename,
node.lineno,
node.colno);
}
},
getCode: function() {
return this.codebuf.join('');
}
});
// var c = new Compiler();
// var src = 'hello {% filter title %}' +
// 'Hello madam how are you' +
// '{% endfilter %}'
// var ast = transformer.transform(parser.parse(src));
// nodes.printNodes(ast);
// c.compile(ast);
// var tmpl = c.getCode();
// console.log(tmpl);
module.exports = {
compile: function(src, asyncFilters, extensions, name, opts) {
var c = new Compiler(name, opts.throwOnUndefined);
// Run the extension preprocessors against the source.
if(extensions && extensions.length) {
for(var i=0; i<extensions.length; i++) {
if('preprocess' in extensions[i]) {
src = extensions[i].preprocess(src, name);
}
}
}
c.compile(transformer.transform(
parser.parse(src,
extensions,
opts),
asyncFilters,
name
));
return c.getCode();
},
Compiler: Compiler
};
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lexer = __webpack_require__(9);
var nodes = __webpack_require__(10);
// jshint -W079
var Object = __webpack_require__(6);
var lib = __webpack_require__(1);
var Parser = Object.extend({
init: function (tokens) {
this.tokens = tokens;
this.peeked = null;
this.breakOnBlocks = null;
this.dropLeadingWhitespace = false;
this.extensions = [];
},
nextToken: function (withWhitespace) {
var tok;
if(this.peeked) {
if(!withWhitespace && this.peeked.type === lexer.TOKEN_WHITESPACE) {
this.peeked = null;
}
else {
tok = this.peeked;
this.peeked = null;
return tok;
}
}
tok = this.tokens.nextToken();
if(!withWhitespace) {
while(tok && tok.type === lexer.TOKEN_WHITESPACE) {
tok = this.tokens.nextToken();
}
}
return tok;
},
peekToken: function () {
this.peeked = this.peeked || this.nextToken();
return this.peeked;
},
pushToken: function(tok) {
if(this.peeked) {
throw new Error('pushToken: can only push one token on between reads');
}
this.peeked = tok;
},
fail: function (msg, lineno, colno) {
if((lineno === undefined || colno === undefined) && this.peekToken()) {
var tok = this.peekToken();
lineno = tok.lineno;
colno = tok.colno;
}
if (lineno !== undefined) lineno += 1;
if (colno !== undefined) colno += 1;
throw new lib.TemplateError(msg, lineno, colno);
},
skip: function(type) {
var tok = this.nextToken();
if(!tok || tok.type !== type) {
this.pushToken(tok);
return false;
}
return true;
},
expect: function(type) {
var tok = this.nextToken();
if(tok.type !== type) {
this.fail('expected ' + type + ', got ' + tok.type,
tok.lineno,
tok.colno);
}
return tok;
},
skipValue: function(type, val) {
var tok = this.nextToken();
if(!tok || tok.type !== type || tok.value !== val) {
this.pushToken(tok);
return false;
}
return true;
},
skipSymbol: function(val) {
return this.skipValue(lexer.TOKEN_SYMBOL, val);
},
advanceAfterBlockEnd: function(name) {
var tok;
if(!name) {
tok = this.peekToken();
if(!tok) {
this.fail('unexpected end of file');
}
if(tok.type !== lexer.TOKEN_SYMBOL) {
this.fail('advanceAfterBlockEnd: expected symbol token or ' +
'explicit name to be passed');
}
name = this.nextToken().value;
}
tok = this.nextToken();
if(tok && tok.type === lexer.TOKEN_BLOCK_END) {
if(tok.value.charAt(0) === '-') {
this.dropLeadingWhitespace = true;
}
}
else {
this.fail('expected block end in ' + name + ' statement');
}
return tok;
},
advanceAfterVariableEnd: function() {
if(!this.skip(lexer.TOKEN_VARIABLE_END)) {
this.fail('expected variable end');
}
},
parseFor: function() {
var forTok = this.peekToken();
var node;
var endBlock;
if(this.skipSymbol('for')) {
node = new nodes.For(forTok.lineno, forTok.colno);
endBlock = 'endfor';
}
else if(this.skipSymbol('asyncEach')) {
node = new nodes.AsyncEach(forTok.lineno, forTok.colno);
endBlock = 'endeach';
}
else if(this.skipSymbol('asyncAll')) {
node = new nodes.AsyncAll(forTok.lineno, forTok.colno);
endBlock = 'endall';
}
else {
this.fail('parseFor: expected for{Async}', forTok.lineno, forTok.colno);
}
node.name = this.parsePrimary();
if(!(node.name instanceof nodes.Symbol)) {
this.fail('parseFor: variable name expected for loop');
}
var type = this.peekToken().type;
if(type === lexer.TOKEN_COMMA) {
// key/value iteration
var key = node.name;
node.name = new nodes.Array(key.lineno, key.colno);
node.name.addChild(key);
while(this.skip(lexer.TOKEN_COMMA)) {
var prim = this.parsePrimary();
node.name.addChild(prim);
}
}
if(!this.skipSymbol('in')) {
this.fail('parseFor: expected "in" keyword for loop',
forTok.lineno,
forTok.colno);
}
node.arr = this.parseExpression();
this.advanceAfterBlockEnd(forTok.value);
node.body = this.parseUntilBlocks(endBlock, 'else');
if(this.skipSymbol('else')) {
this.advanceAfterBlockEnd('else');
node.else_ = this.parseUntilBlocks(endBlock);
}
this.advanceAfterBlockEnd();
return node;
},
parseMacro: function() {
var macroTok = this.peekToken();
if(!this.skipSymbol('macro')) {
this.fail('expected macro');
}
var name = this.parsePrimary(true);
var args = this.parseSignature();
var node = new nodes.Macro(macroTok.lineno,
macroTok.colno,
name,
args);
this.advanceAfterBlockEnd(macroTok.value);
node.body = this.parseUntilBlocks('endmacro');
this.advanceAfterBlockEnd();
return node;
},
parseCall: function() {
// a call block is parsed as a normal FunCall, but with an added
// 'caller' kwarg which is a Caller node.
var callTok = this.peekToken();
if(!this.skipSymbol('call')) {
this.fail('expected call');
}
var callerArgs = this.parseSignature(true) || new nodes.NodeList();
var macroCall = this.parsePrimary();
this.advanceAfterBlockEnd(callTok.value);
var body = this.parseUntilBlocks('endcall');
this.advanceAfterBlockEnd();
var callerName = new nodes.Symbol(callTok.lineno,
callTok.colno,
'caller');
var callerNode = new nodes.Caller(callTok.lineno,
callTok.colno,
callerName,
callerArgs,
body);
// add the additional caller kwarg, adding kwargs if necessary
var args = macroCall.args.children;
if (!(args[args.length-1] instanceof nodes.KeywordArgs)) {
args.push(new nodes.KeywordArgs());
}
var kwargs = args[args.length - 1];
kwargs.addChild(new nodes.Pair(callTok.lineno,
callTok.colno,
callerName,
callerNode));
return new nodes.Output(callTok.lineno,
callTok.colno,
[macroCall]);
},
parseWithContext: function() {
var tok = this.peekToken();
var withContext = null;
if(this.skipSymbol('with')) {
withContext = true;
}
else if(this.skipSymbol('without')) {
withContext = false;
}
if(withContext !== null) {
if(!this.skipSymbol('context')) {
this.fail('parseFrom: expected context after with/without',
tok.lineno,
tok.colno);
}
}
return withContext;
},
parseImport: function() {
var importTok = this.peekToken();
if(!this.skipSymbol('import')) {
this.fail('parseImport: expected import',
importTok.lineno,
importTok.colno);
}
var template = this.parseExpression();
if(!this.skipSymbol('as')) {
this.fail('parseImport: expected "as" keyword',
importTok.lineno,
importTok.colno);
}
var target = this.parsePrimary();
var withContext = this.parseWithContext();
var node = new nodes.Import(importTok.lineno,
importTok.colno,
template,
target,
withContext);
this.advanceAfterBlockEnd(importTok.value);
return node;
},
parseFrom: function() {
var fromTok = this.peekToken();
if(!this.skipSymbol('from')) {
this.fail('parseFrom: expected from');
}
var template = this.parsePrimary();
if(!this.skipSymbol('import')) {
this.fail('parseFrom: expected import',
fromTok.lineno,
fromTok.colno);
}
var names = new nodes.NodeList(),
withContext;
while(1) {
var nextTok = this.peekToken();
if(nextTok.type === lexer.TOKEN_BLOCK_END) {
if(!names.children.length) {
this.fail('parseFrom: Expected at least one import name',
fromTok.lineno,
fromTok.colno);
}
// Since we are manually advancing past the block end,
// need to keep track of whitespace control (normally
// this is done in `advanceAfterBlockEnd`
if(nextTok.value.charAt(0) === '-') {
this.dropLeadingWhitespace = true;
}
this.nextToken();
break;
}
if(names.children.length > 0 && !this.skip(lexer.TOKEN_COMMA)) {
this.fail('parseFrom: expected comma',
fromTok.lineno,
fromTok.colno);
}
var name = this.parsePrimary();
if(name.value.charAt(0) === '_') {
this.fail('parseFrom: names starting with an underscore ' +
'cannot be imported',
name.lineno,
name.colno);
}
if(this.skipSymbol('as')) {
var alias = this.parsePrimary();
names.addChild(new nodes.Pair(name.lineno,
name.colno,
name,
alias));
}
else {
names.addChild(name);
}
withContext = this.parseWithContext();
}
return new nodes.FromImport(fromTok.lineno,
fromTok.colno,
template,
names,
withContext);
},
parseBlock: function() {
var tag = this.peekToken();
if(!this.skipSymbol('block')) {
this.fail('parseBlock: expected block', tag.lineno, tag.colno);
}
var node = new nodes.Block(tag.lineno, tag.colno);
node.name = this.parsePrimary();
if(!(node.name instanceof nodes.Symbol)) {
this.fail('parseBlock: variable name expected',
tag.lineno,
tag.colno);
}
this.advanceAfterBlockEnd(tag.value);
node.body = this.parseUntilBlocks('endblock');
if(!this.peekToken()) {
this.fail('parseBlock: expected endblock, got end of file');
}
this.advanceAfterBlockEnd();
return node;
},
parseExtends: function() {
var tagName = 'extends';
var tag = this.peekToken();
if(!this.skipSymbol(tagName)) {
this.fail('parseTemplateRef: expected '+ tagName);
}
var node = new nodes.Extends(tag.lineno, tag.colno);
node.template = this.parseExpression();
this.advanceAfterBlockEnd(tag.value);
return node;
},
parseInclude: function() {
var tagName = 'include';
var tag = this.peekToken();
if(!this.skipSymbol(tagName)) {
this.fail('parseInclude: expected '+ tagName);
}
var node = new nodes.Include(tag.lineno, tag.colno);
node.template = this.parseExpression();
if(this.skipSymbol('ignore') && this.skipSymbol('missing')) {
node.ignoreMissing = true;
}
this.advanceAfterBlockEnd(tag.value);
return node;
},
parseIf: function() {
var tag = this.peekToken();
var node;
if(this.skipSymbol('if') || this.skipSymbol('elif')) {
node = new nodes.If(tag.lineno, tag.colno);
}
else if(this.skipSymbol('ifAsync')) {
node = new nodes.IfAsync(tag.lineno, tag.colno);
}
else {
this.fail('parseIf: expected if or elif',
tag.lineno,
tag.colno);
}
node.cond = this.parseExpression();
this.advanceAfterBlockEnd(tag.value);
node.body = this.parseUntilBlocks('elif', 'else', 'endif');
var tok = this.peekToken();
switch(tok && tok.value) {
case 'elif':
node.else_ = this.parseIf();
break;
case 'else':
this.advanceAfterBlockEnd();
node.else_ = this.parseUntilBlocks('endif');
this.advanceAfterBlockEnd();
break;
case 'endif':
node.else_ = null;
this.advanceAfterBlockEnd();
break;
default:
this.fail('parseIf: expected elif, else, or endif, ' +
'got end of file');
}
return node;
},
parseSet: function() {
var tag = this.peekToken();
if(!this.skipSymbol('set')) {
this.fail('parseSet: expected set', tag.lineno, tag.colno);
}
var node = new nodes.Set(tag.lineno, tag.colno, []);
var target;
while((target = this.parsePrimary())) {
node.targets.push(target);
if(!this.skip(lexer.TOKEN_COMMA)) {
break;
}
}
if(!this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
this.fail('parseSet: expected = in set tag',
tag.lineno,
tag.colno);
}
node.value = this.parseExpression();
this.advanceAfterBlockEnd(tag.value);
return node;
},
parseStatement: function () {
var tok = this.peekToken();
var node;
if(tok.type !== lexer.TOKEN_SYMBOL) {
this.fail('tag name expected', tok.lineno, tok.colno);
}
if(this.breakOnBlocks &&
lib.indexOf(this.breakOnBlocks, tok.value) !== -1) {
return null;
}
switch(tok.value) {
case 'raw': return this.parseRaw();
case 'if':
case 'ifAsync':
return this.parseIf();
case 'for':
case 'asyncEach':
case 'asyncAll':
return this.parseFor();
case 'block': return this.parseBlock();
case 'extends': return this.parseExtends();
case 'include': return this.parseInclude();
case 'set': return this.parseSet();
case 'macro': return this.parseMacro();
case 'call': return this.parseCall();
case 'import': return this.parseImport();
case 'from': return this.parseFrom();
case 'filter': return this.parseFilterStatement();
default:
if (this.extensions.length) {
for (var i = 0; i < this.extensions.length; i++) {
var ext = this.extensions[i];
if (lib.indexOf(ext.tags || [], tok.value) !== -1) {
return ext.parse(this, nodes, lexer);
}
}
}
this.fail('unknown block tag: ' + tok.value, tok.lineno, tok.colno);
}
return node;
},
parseRaw: function() {
// Look for upcoming raw blocks (ignore all other kinds of blocks)
var rawBlockRegex = /([\s\S]*?){%\s*(raw|endraw)\s*(?=%})%}/;
var rawLevel = 1;
var str = '';
var matches = null;
// Skip opening raw token
// Keep this token to track line and column numbers
var begun = this.advanceAfterBlockEnd();
// Exit when there's nothing to match
// or when we've found the matching "endraw" block
while((matches = this.tokens._extractRegex(rawBlockRegex)) && rawLevel > 0) {
var all = matches[0];
var pre = matches[1];
var blockName = matches[2];
// Adjust rawlevel
if(blockName === 'raw') {
rawLevel += 1;
} else if(blockName === 'endraw') {
rawLevel -= 1;
}
// Add to str
if(rawLevel === 0) {
// We want to exclude the last "endraw"
str += pre;
// Move tokenizer to beginning of endraw block
this.tokens.backN(all.length - pre.length);
} else {
str += all;
}
}
return new nodes.Output(
begun.lineno,
begun.colno,
[new nodes.TemplateData(begun.lineno, begun.colno, str)]
);
},
parsePostfix: function(node) {
var lookup, tok = this.peekToken();
while(tok) {
if(tok.type === lexer.TOKEN_LEFT_PAREN) {
// Function call
node = new nodes.FunCall(tok.lineno,
tok.colno,
node,
this.parseSignature());
}
else if(tok.type === lexer.TOKEN_LEFT_BRACKET) {
// Reference
lookup = this.parseAggregate();
if(lookup.children.length > 1) {
this.fail('invalid index');
}
node = new nodes.LookupVal(tok.lineno,
tok.colno,
node,
lookup.children[0]);
}
else if(tok.type === lexer.TOKEN_OPERATOR && tok.value === '.') {
// Reference
this.nextToken();
var val = this.nextToken();
if(val.type !== lexer.TOKEN_SYMBOL) {
this.fail('expected name as lookup value, got ' + val.value,
val.lineno,
val.colno);
}
// Make a literal string because it's not a variable
// reference
lookup = new nodes.Literal(val.lineno,
val.colno,
val.value);
node = new nodes.LookupVal(tok.lineno,
tok.colno,
node,
lookup);
}
else {
break;
}
tok = this.peekToken();
}
return node;
},
parseExpression: function() {
var node = this.parseInlineIf();
return node;
},
parseInlineIf: function() {
var node = this.parseOr();
if(this.skipSymbol('if')) {
var cond_node = this.parseOr();
var body_node = node;
node = new nodes.InlineIf(node.lineno, node.colno);
node.body = body_node;
node.cond = cond_node;
if(this.skipSymbol('else')) {
node.else_ = this.parseOr();
} else {
node.else_ = null;
}
}
return node;
},
parseOr: function() {
var node = this.parseAnd();
while(this.skipSymbol('or')) {
var node2 = this.parseAnd();
node = new nodes.Or(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseAnd: function() {
var node = this.parseNot();
while(this.skipSymbol('and')) {
var node2 = this.parseNot();
node = new nodes.And(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseNot: function() {
var tok = this.peekToken();
if(this.skipSymbol('not')) {
return new nodes.Not(tok.lineno,
tok.colno,
this.parseNot());
}
return this.parseIn();
},
parseIn: function() {
var node = this.parseCompare();
while(1) {
// check if the next token is 'not'
var tok = this.nextToken();
if (!tok) { break; }
var invert = tok.type === lexer.TOKEN_SYMBOL && tok.value === 'not';
// if it wasn't 'not', put it back
if (!invert) { this.pushToken(tok); }
if (this.skipSymbol('in')) {
var node2 = this.parseCompare();
node = new nodes.In(node.lineno,
node.colno,
node,
node2);
if (invert) {
node = new nodes.Not(node.lineno,
node.colno,
node);
}
}
else {
// if we'd found a 'not' but this wasn't an 'in', put back the 'not'
if (invert) { this.pushToken(tok); }
break;
}
}
return node;
},
parseCompare: function() {
var compareOps = ['==', '!=', '<', '>', '<=', '>='];
var expr = this.parseConcat();
var ops = [];
while(1) {
var tok = this.nextToken();
if(!tok) {
break;
}
else if(lib.indexOf(compareOps, tok.value) !== -1) {
ops.push(new nodes.CompareOperand(tok.lineno,
tok.colno,
this.parseConcat(),
tok.value));
}
else {
this.pushToken(tok);
break;
}
}
if(ops.length) {
return new nodes.Compare(ops[0].lineno,
ops[0].colno,
expr,
ops);
}
else {
return expr;
}
},
// finds the '~' for string concatenation
parseConcat: function(){
var node = this.parseAdd();
while(this.skipValue(lexer.TOKEN_TILDE, '~')) {
var node2 = this.parseAdd();
node = new nodes.Concat(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseAdd: function() {
var node = this.parseSub();
while(this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
var node2 = this.parseSub();
node = new nodes.Add(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseSub: function() {
var node = this.parseMul();
while(this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
var node2 = this.parseMul();
node = new nodes.Sub(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseMul: function() {
var node = this.parseDiv();
while(this.skipValue(lexer.TOKEN_OPERATOR, '*')) {
var node2 = this.parseDiv();
node = new nodes.Mul(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseDiv: function() {
var node = this.parseFloorDiv();
while(this.skipValue(lexer.TOKEN_OPERATOR, '/')) {
var node2 = this.parseFloorDiv();
node = new nodes.Div(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseFloorDiv: function() {
var node = this.parseMod();
while(this.skipValue(lexer.TOKEN_OPERATOR, '//')) {
var node2 = this.parseMod();
node = new nodes.FloorDiv(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseMod: function() {
var node = this.parsePow();
while(this.skipValue(lexer.TOKEN_OPERATOR, '%')) {
var node2 = this.parsePow();
node = new nodes.Mod(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parsePow: function() {
var node = this.parseUnary();
while(this.skipValue(lexer.TOKEN_OPERATOR, '**')) {
var node2 = this.parseUnary();
node = new nodes.Pow(node.lineno,
node.colno,
node,
node2);
}
return node;
},
parseUnary: function(noFilters) {
var tok = this.peekToken();
var node;
if(this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
node = new nodes.Neg(tok.lineno,
tok.colno,
this.parseUnary(true));
}
else if(this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
node = new nodes.Pos(tok.lineno,
tok.colno,
this.parseUnary(true));
}
else {
node = this.parsePrimary();
}
if(!noFilters) {
node = this.parseFilter(node);
}
return node;
},
parsePrimary: function (noPostfix) {
var tok = this.nextToken();
var val;
var node = null;
if(!tok) {
this.fail('expected expression, got end of file');
}
else if(tok.type === lexer.TOKEN_STRING) {
val = tok.value;
}
else if(tok.type === lexer.TOKEN_INT) {
val = parseInt(tok.value, 10);
}
else if(tok.type === lexer.TOKEN_FLOAT) {
val = parseFloat(tok.value);
}
else if(tok.type === lexer.TOKEN_BOOLEAN) {
if(tok.value === 'true') {
val = true;
}
else if(tok.value === 'false') {
val = false;
}
else {
this.fail('invalid boolean: ' + tok.value,
tok.lineno,
tok.colno);
}
}
else if(tok.type === lexer.TOKEN_NONE) {
val = null;
}
else if (tok.type === lexer.TOKEN_REGEX) {
val = new RegExp(tok.value.body, tok.value.flags);
}
if(val !== undefined) {
node = new nodes.Literal(tok.lineno, tok.colno, val);
}
else if(tok.type === lexer.TOKEN_SYMBOL) {
node = new nodes.Symbol(tok.lineno, tok.colno, tok.value);
if(!noPostfix) {
node = this.parsePostfix(node);
}
}
else {
// See if it's an aggregate type, we need to push the
// current delimiter token back on
this.pushToken(tok);
node = this.parseAggregate();
}
if(node) {
return node;
}
else {
this.fail('unexpected token: ' + tok.value,
tok.lineno,
tok.colno);
}
},
parseFilterName: function() {
var tok = this.expect(lexer.TOKEN_SYMBOL);
var name = tok.value;
while(this.skipValue(lexer.TOKEN_OPERATOR, '.')) {
name += '.' + this.expect(lexer.TOKEN_SYMBOL).value;
}
return new nodes.Symbol(tok.lineno, tok.colno, name);
},
parseFilterArgs: function(node) {
if(this.peekToken().type === lexer.TOKEN_LEFT_PAREN) {
// Get a FunCall node and add the parameters to the
// filter
var call = this.parsePostfix(node);
return call.args.children;
}
return [];
},
parseFilter: function(node) {
while(this.skip(lexer.TOKEN_PIPE)) {
var name = this.parseFilterName();
node = new nodes.Filter(
name.lineno,
name.colno,
name,
new nodes.NodeList(
name.lineno,
name.colno,
[node].concat(this.parseFilterArgs(node))
)
);
}
return node;
},
parseFilterStatement: function() {
var filterTok = this.peekToken();
if(!this.skipSymbol('filter')) {
this.fail('parseFilterStatement: expected filter');
}
var name = this.parseFilterName();
var args = this.parseFilterArgs(name);
this.advanceAfterBlockEnd(filterTok.value);
var body = this.parseUntilBlocks('endfilter');
this.advanceAfterBlockEnd();
var node = new nodes.Filter(
name.lineno,
name.colno,
name,
new nodes.NodeList(
name.lineno,
name.colno,
// Body is a NodeList with an Output node as a child,
// need to strip those
body.children[0].children.concat(args)
)
);
return new nodes.Output(
name.lineno,
name.colno,
[node]
);
},
parseAggregate: function() {
var tok = this.nextToken();
var node;
switch(tok.type) {
case lexer.TOKEN_LEFT_PAREN:
node = new nodes.Group(tok.lineno, tok.colno); break;
case lexer.TOKEN_LEFT_BRACKET:
node = new nodes.Array(tok.lineno, tok.colno); break;
case lexer.TOKEN_LEFT_CURLY:
node = new nodes.Dict(tok.lineno, tok.colno); break;
default:
return null;
}
while(1) {
var type = this.peekToken().type;
if(type === lexer.TOKEN_RIGHT_PAREN ||
type === lexer.TOKEN_RIGHT_BRACKET ||
type === lexer.TOKEN_RIGHT_CURLY) {
this.nextToken();
break;
}
if(node.children.length > 0) {
if(!this.skip(lexer.TOKEN_COMMA)) {
this.fail('parseAggregate: expected comma after expression',
tok.lineno,
tok.colno);
}
}
if(node instanceof nodes.Dict) {
// TODO: check for errors
var key = this.parsePrimary();
// We expect a key/value pair for dicts, separated by a
// colon
if(!this.skip(lexer.TOKEN_COLON)) {
this.fail('parseAggregate: expected colon after dict key',
tok.lineno,
tok.colno);
}
// TODO: check for errors
var value = this.parseExpression();
node.addChild(new nodes.Pair(key.lineno,
key.colno,
key,
value));
}
else {
// TODO: check for errors
var expr = this.parseExpression();
node.addChild(expr);
}
}
return node;
},
parseSignature: function(tolerant, noParens) {
var tok = this.peekToken();
if(!noParens && tok.type !== lexer.TOKEN_LEFT_PAREN) {
if(tolerant) {
return null;
}
else {
this.fail('expected arguments', tok.lineno, tok.colno);
}
}
if(tok.type === lexer.TOKEN_LEFT_PAREN) {
tok = this.nextToken();
}
var args = new nodes.NodeList(tok.lineno, tok.colno);
var kwargs = new nodes.KeywordArgs(tok.lineno, tok.colno);
var checkComma = false;
while(1) {
tok = this.peekToken();
if(!noParens && tok.type === lexer.TOKEN_RIGHT_PAREN) {
this.nextToken();
break;
}
else if(noParens && tok.type === lexer.TOKEN_BLOCK_END) {
break;
}
if(checkComma && !this.skip(lexer.TOKEN_COMMA)) {
this.fail('parseSignature: expected comma after expression',
tok.lineno,
tok.colno);
}
else {
var arg = this.parseExpression();
if(this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
kwargs.addChild(
new nodes.Pair(arg.lineno,
arg.colno,
arg,
this.parseExpression())
);
}
else {
args.addChild(arg);
}
}
checkComma = true;
}
if(kwargs.children.length) {
args.addChild(kwargs);
}
return args;
},
parseUntilBlocks: function(/* blockNames */) {
var prev = this.breakOnBlocks;
this.breakOnBlocks = lib.toArray(arguments);
var ret = this.parse();
this.breakOnBlocks = prev;
return ret;
},
parseNodes: function () {
var tok;
var buf = [];
while((tok = this.nextToken())) {
if(tok.type === lexer.TOKEN_DATA) {
var data = tok.value;
var nextToken = this.peekToken();
var nextVal = nextToken && nextToken.value;
// If the last token has "-" we need to trim the
// leading whitespace of the data. This is marked with
// the `dropLeadingWhitespace` variable.
if(this.dropLeadingWhitespace) {
// TODO: this could be optimized (don't use regex)
data = data.replace(/^\s*/, '');
this.dropLeadingWhitespace = false;
}
// Same for the succeding block start token
if(nextToken &&
nextToken.type === lexer.TOKEN_BLOCK_START &&
nextVal.charAt(nextVal.length - 1) === '-') {
// TODO: this could be optimized (don't use regex)
data = data.replace(/\s*$/, '');
}
buf.push(new nodes.Output(tok.lineno,
tok.colno,
[new nodes.TemplateData(tok.lineno,
tok.colno,
data)]));
}
else if(tok.type === lexer.TOKEN_BLOCK_START) {
var n = this.parseStatement();
if(!n) {
break;
}
buf.push(n);
}
else if(tok.type === lexer.TOKEN_VARIABLE_START) {
var e = this.parseExpression();
this.advanceAfterVariableEnd();
buf.push(new nodes.Output(tok.lineno, tok.colno, [e]));
}
else if(tok.type !== lexer.TOKEN_COMMENT) {
// Ignore comments, otherwise this should be an error
this.fail('Unexpected token at top-level: ' +
tok.type, tok.lineno, tok.colno);
}
}
return buf;
},
parse: function() {
return new nodes.NodeList(0, 0, this.parseNodes());
},
parseAsRoot: function() {
return new nodes.Root(0, 0, this.parseNodes());
}
});
// var util = require('util');
// var l = lexer.lex('{%- if x -%}\n hello {% endif %}');
// var t;
// while((t = l.nextToken())) {
// console.log(util.inspect(t));
// }
// var p = new Parser(lexer.lex('hello {% filter title %}' +
// 'Hello madam how are you' +
// '{% endfilter %}'));
// var n = p.parseAsRoot();
// nodes.printNodes(n);
module.exports = {
parse: function(src, extensions, opts) {
var p = new Parser(lexer.lex(src, opts));
if (extensions !== undefined) {
p.extensions = extensions;
}
return p.parseAsRoot();
}
};
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lib = __webpack_require__(1);
var whitespaceChars = ' \n\t\r\u00A0';
var delimChars = '()[]{}%*-+~/#,:|.<>=!';
var intChars = '0123456789';
var BLOCK_START = '{%';
var BLOCK_END = '%}';
var VARIABLE_START = '{{';
var VARIABLE_END = '}}';
var COMMENT_START = '{#';
var COMMENT_END = '#}';
var TOKEN_STRING = 'string';
var TOKEN_WHITESPACE = 'whitespace';
var TOKEN_DATA = 'data';
var TOKEN_BLOCK_START = 'block-start';
var TOKEN_BLOCK_END = 'block-end';
var TOKEN_VARIABLE_START = 'variable-start';
var TOKEN_VARIABLE_END = 'variable-end';
var TOKEN_COMMENT = 'comment';
var TOKEN_LEFT_PAREN = 'left-paren';
var TOKEN_RIGHT_PAREN = 'right-paren';
var TOKEN_LEFT_BRACKET = 'left-bracket';
var TOKEN_RIGHT_BRACKET = 'right-bracket';
var TOKEN_LEFT_CURLY = 'left-curly';
var TOKEN_RIGHT_CURLY = 'right-curly';
var TOKEN_OPERATOR = 'operator';
var TOKEN_COMMA = 'comma';
var TOKEN_COLON = 'colon';
var TOKEN_TILDE = 'tilde';
var TOKEN_PIPE = 'pipe';
var TOKEN_INT = 'int';
var TOKEN_FLOAT = 'float';
var TOKEN_BOOLEAN = 'boolean';
var TOKEN_NONE = 'none';
var TOKEN_SYMBOL = 'symbol';
var TOKEN_SPECIAL = 'special';
var TOKEN_REGEX = 'regex';
function token(type, value, lineno, colno) {
return {
type: type,
value: value,
lineno: lineno,
colno: colno
};
}
function Tokenizer(str, opts) {
this.str = str;
this.index = 0;
this.len = str.length;
this.lineno = 0;
this.colno = 0;
this.in_code = false;
opts = opts || {};
var tags = opts.tags || {};
this.tags = {
BLOCK_START: tags.blockStart || BLOCK_START,
BLOCK_END: tags.blockEnd || BLOCK_END,
VARIABLE_START: tags.variableStart || VARIABLE_START,
VARIABLE_END: tags.variableEnd || VARIABLE_END,
COMMENT_START: tags.commentStart || COMMENT_START,
COMMENT_END: tags.commentEnd || COMMENT_END
};
this.trimBlocks = !!opts.trimBlocks;
this.lstripBlocks = !!opts.lstripBlocks;
}
Tokenizer.prototype.nextToken = function() {
var lineno = this.lineno;
var colno = this.colno;
var tok;
if(this.in_code) {
// Otherwise, if we are in a block parse it as code
var cur = this.current();
if(this.is_finished()) {
// We have nothing else to parse
return null;
}
else if(cur === '"' || cur === '\'') {
// We've hit a string
return token(TOKEN_STRING, this.parseString(cur), lineno, colno);
}
else if((tok = this._extract(whitespaceChars))) {
// We hit some whitespace
return token(TOKEN_WHITESPACE, tok, lineno, colno);
}
else if((tok = this._extractString(this.tags.BLOCK_END)) ||
(tok = this._extractString('-' + this.tags.BLOCK_END))) {
// Special check for the block end tag
//
// It is a requirement that start and end tags are composed of
// delimiter characters (%{}[] etc), and our code always
// breaks on delimiters so we can assume the token parsing
// doesn't consume these elsewhere
this.in_code = false;
if(this.trimBlocks) {
cur = this.current();
if(cur === '\n') {
// Skip newline
this.forward();
}else if(cur === '\r'){
// Skip CRLF newline
this.forward();
cur = this.current();
if(cur === '\n'){
this.forward();
}else{
// Was not a CRLF, so go back
this.back();
}
}
}
return token(TOKEN_BLOCK_END, tok, lineno, colno);
}
else if((tok = this._extractString(this.tags.VARIABLE_END))) {
// Special check for variable end tag (see above)
this.in_code = false;
return token(TOKEN_VARIABLE_END, tok, lineno, colno);
}
else if (cur === 'r' && this.str.charAt(this.index + 1) === '/') {
// Skip past 'r/'.
this.forwardN(2);
// Extract until the end of the regex -- / ends it, \/ does not.
var regexBody = '';
while (!this.is_finished()) {
if (this.current() === '/' && this.previous() !== '\\') {
this.forward();
break;
} else {
regexBody += this.current();
this.forward();
}
}
// Check for flags.
// The possible flags are according to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
var POSSIBLE_FLAGS = ['g', 'i', 'm', 'y'];
var regexFlags = '';
while (!this.is_finished()) {
var isCurrentAFlag = POSSIBLE_FLAGS.indexOf(this.current()) !== -1;
if (isCurrentAFlag) {
regexFlags += this.current();
this.forward();
} else {
break;
}
}
return token(TOKEN_REGEX, {body: regexBody, flags: regexFlags}, lineno, colno);
}
else if(delimChars.indexOf(cur) !== -1) {
// We've hit a delimiter (a special char like a bracket)
this.forward();
var complexOps = ['==', '!=', '<=', '>=', '//', '**'];
var curComplex = cur + this.current();
var type;
if(lib.indexOf(complexOps, curComplex) !== -1) {
this.forward();
cur = curComplex;
}
switch(cur) {
case '(': type = TOKEN_LEFT_PAREN; break;
case ')': type = TOKEN_RIGHT_PAREN; break;
case '[': type = TOKEN_LEFT_BRACKET; break;
case ']': type = TOKEN_RIGHT_BRACKET; break;
case '{': type = TOKEN_LEFT_CURLY; break;
case '}': type = TOKEN_RIGHT_CURLY; break;
case ',': type = TOKEN_COMMA; break;
case ':': type = TOKEN_COLON; break;
case '~': type = TOKEN_TILDE; break;
case '|': type = TOKEN_PIPE; break;
default: type = TOKEN_OPERATOR;
}
return token(type, cur, lineno, colno);
}
else {
// We are not at whitespace or a delimiter, so extract the
// text and parse it
tok = this._extractUntil(whitespaceChars + delimChars);
if(tok.match(/^[-+]?[0-9]+$/)) {
if(this.current() === '.') {
this.forward();
var dec = this._extract(intChars);
return token(TOKEN_FLOAT, tok + '.' + dec, lineno, colno);
}
else {
return token(TOKEN_INT, tok, lineno, colno);
}
}
else if(tok.match(/^(true|false)$/)) {
return token(TOKEN_BOOLEAN, tok, lineno, colno);
}
else if(tok === 'none') {
return token(TOKEN_NONE, tok, lineno, colno);
}
else if(tok) {
return token(TOKEN_SYMBOL, tok, lineno, colno);
}
else {
throw new Error('Unexpected value while parsing: ' + tok);
}
}
}
else {
// Parse out the template text, breaking on tag
// delimiters because we need to look for block/variable start
// tags (don't use the full delimChars for optimization)
var beginChars = (this.tags.BLOCK_START.charAt(0) +
this.tags.VARIABLE_START.charAt(0) +
this.tags.COMMENT_START.charAt(0) +
this.tags.COMMENT_END.charAt(0));
if(this.is_finished()) {
return null;
}
else if((tok = this._extractString(this.tags.BLOCK_START + '-')) ||
(tok = this._extractString(this.tags.BLOCK_START))) {
this.in_code = true;
return token(TOKEN_BLOCK_START, tok, lineno, colno);
}
else if((tok = this._extractString(this.tags.VARIABLE_START))) {
this.in_code = true;
return token(TOKEN_VARIABLE_START, tok, lineno, colno);
}
else {
tok = '';
var data;
var in_comment = false;
if(this._matches(this.tags.COMMENT_START)) {
in_comment = true;
tok = this._extractString(this.tags.COMMENT_START);
}
// Continually consume text, breaking on the tag delimiter
// characters and checking to see if it's a start tag.
//
// We could hit the end of the template in the middle of
// our looping, so check for the null return value from
// _extractUntil
while((data = this._extractUntil(beginChars)) !== null) {
tok += data;
if((this._matches(this.tags.BLOCK_START) ||
this._matches(this.tags.VARIABLE_START) ||
this._matches(this.tags.COMMENT_START)) &&
!in_comment) {
if(this.lstripBlocks &&
this._matches(this.tags.BLOCK_START) &&
this.colno > 0 &&
this.colno <= tok.length) {
var lastLine = tok.slice(-this.colno);
if(/^\s+$/.test(lastLine)) {
// Remove block leading whitespace from beginning of the string
tok = tok.slice(0, -this.colno);
if(!tok.length) {
// All data removed, collapse to avoid unnecessary nodes
// by returning next token (block start)
return this.nextToken();
}
}
}
// If it is a start tag, stop looping
break;
}
else if(this._matches(this.tags.COMMENT_END)) {
if(!in_comment) {
throw new Error('unexpected end of comment');
}
tok += this._extractString(this.tags.COMMENT_END);
break;
}
else {
// It does not match any tag, so add the character and
// carry on
tok += this.current();
this.forward();
}
}
if(data === null && in_comment) {
throw new Error('expected end of comment, got end of file');
}
return token(in_comment ? TOKEN_COMMENT : TOKEN_DATA,
tok,
lineno,
colno);
}
}
throw new Error('Could not parse text');
};
Tokenizer.prototype.parseString = function(delimiter) {
this.forward();
var str = '';
while(!this.is_finished() && this.current() !== delimiter) {
var cur = this.current();
if(cur === '\\') {
this.forward();
switch(this.current()) {
case 'n': str += '\n'; break;
case 't': str += '\t'; break;
case 'r': str += '\r'; break;
default:
str += this.current();
}
this.forward();
}
else {
str += cur;
this.forward();
}
}
this.forward();
return str;
};
Tokenizer.prototype._matches = function(str) {
if(this.index + str.length > this.len) {
return null;
}
var m = this.str.slice(this.index, this.index + str.length);
return m === str;
};
Tokenizer.prototype._extractString = function(str) {
if(this._matches(str)) {
this.index += str.length;
return str;
}
return null;
};
Tokenizer.prototype._extractUntil = function(charString) {
// Extract all non-matching chars, with the default matching set
// to everything
return this._extractMatching(true, charString || '');
};
Tokenizer.prototype._extract = function(charString) {
// Extract all matching chars (no default, so charString must be
// explicit)
return this._extractMatching(false, charString);
};
Tokenizer.prototype._extractMatching = function (breakOnMatch, charString) {
// Pull out characters until a breaking char is hit.
// If breakOnMatch is false, a non-matching char stops it.
// If breakOnMatch is true, a matching char stops it.
if(this.is_finished()) {
return null;
}
var first = charString.indexOf(this.current());
// Only proceed if the first character doesn't meet our condition
if((breakOnMatch && first === -1) ||
(!breakOnMatch && first !== -1)) {
var t = this.current();
this.forward();
// And pull out all the chars one at a time until we hit a
// breaking char
var idx = charString.indexOf(this.current());
while(((breakOnMatch && idx === -1) ||
(!breakOnMatch && idx !== -1)) && !this.is_finished()) {
t += this.current();
this.forward();
idx = charString.indexOf(this.current());
}
return t;
}
return '';
};
Tokenizer.prototype._extractRegex = function(regex) {
var matches = this.currentStr().match(regex);
if(!matches) {
return null;
}
// Move forward whatever was matched
this.forwardN(matches[0].length);
return matches;
};
Tokenizer.prototype.is_finished = function() {
return this.index >= this.len;
};
Tokenizer.prototype.forwardN = function(n) {
for(var i=0; i<n; i++) {
this.forward();
}
};
Tokenizer.prototype.forward = function() {
this.index++;
if(this.previous() === '\n') {
this.lineno++;
this.colno = 0;
}
else {
this.colno++;
}
};
Tokenizer.prototype.backN = function(n) {
for(var i=0; i<n; i++) {
this.back();
}
};
Tokenizer.prototype.back = function() {
this.index--;
if(this.current() === '\n') {
this.lineno--;
var idx = this.src.lastIndexOf('\n', this.index-1);
if(idx === -1) {
this.colno = this.index;
}
else {
this.colno = this.index - idx;
}
}
else {
this.colno--;
}
};
// current returns current character
Tokenizer.prototype.current = function() {
if(!this.is_finished()) {
return this.str.charAt(this.index);
}
return '';
};
// currentStr returns what's left of the unparsed string
Tokenizer.prototype.currentStr = function() {
if(!this.is_finished()) {
return this.str.substr(this.index);
}
return '';
};
Tokenizer.prototype.previous = function() {
return this.str.charAt(this.index-1);
};
module.exports = {
lex: function(src, opts) {
return new Tokenizer(src, opts);
},
TOKEN_STRING: TOKEN_STRING,
TOKEN_WHITESPACE: TOKEN_WHITESPACE,
TOKEN_DATA: TOKEN_DATA,
TOKEN_BLOCK_START: TOKEN_BLOCK_START,
TOKEN_BLOCK_END: TOKEN_BLOCK_END,
TOKEN_VARIABLE_START: TOKEN_VARIABLE_START,
TOKEN_VARIABLE_END: TOKEN_VARIABLE_END,
TOKEN_COMMENT: TOKEN_COMMENT,
TOKEN_LEFT_PAREN: TOKEN_LEFT_PAREN,
TOKEN_RIGHT_PAREN: TOKEN_RIGHT_PAREN,
TOKEN_LEFT_BRACKET: TOKEN_LEFT_BRACKET,
TOKEN_RIGHT_BRACKET: TOKEN_RIGHT_BRACKET,
TOKEN_LEFT_CURLY: TOKEN_LEFT_CURLY,
TOKEN_RIGHT_CURLY: TOKEN_RIGHT_CURLY,
TOKEN_OPERATOR: TOKEN_OPERATOR,
TOKEN_COMMA: TOKEN_COMMA,
TOKEN_COLON: TOKEN_COLON,
TOKEN_TILDE: TOKEN_TILDE,
TOKEN_PIPE: TOKEN_PIPE,
TOKEN_INT: TOKEN_INT,
TOKEN_FLOAT: TOKEN_FLOAT,
TOKEN_BOOLEAN: TOKEN_BOOLEAN,
TOKEN_NONE: TOKEN_NONE,
TOKEN_SYMBOL: TOKEN_SYMBOL,
TOKEN_SPECIAL: TOKEN_SPECIAL,
TOKEN_REGEX: TOKEN_REGEX
};
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(process) {'use strict';
var lib = __webpack_require__(1);
// jshint -W079
var Object = __webpack_require__(6);
function traverseAndCheck(obj, type, results) {
if(obj instanceof type) {
results.push(obj);
}
if(obj instanceof Node) {
obj.findAll(type, results);
}
}
var Node = Object.extend('Node', {
init: function(lineno, colno) {
this.lineno = lineno;
this.colno = colno;
var fields = this.fields;
for(var i = 0, l = fields.length; i < l; i++) {
var field = fields[i];
// The first two args are line/col numbers, so offset by 2
var val = arguments[i + 2];
// Fields should never be undefined, but null. It makes
// testing easier to normalize values.
if(val === undefined) {
val = null;
}
this[field] = val;
}
},
findAll: function(type, results) {
results = results || [];
var i, l;
if(this instanceof NodeList) {
var children = this.children;
for(i = 0, l = children.length; i < l; i++) {
traverseAndCheck(children[i], type, results);
}
}
else {
var fields = this.fields;
for(i = 0, l = fields.length; i < l; i++) {
traverseAndCheck(this[fields[i]], type, results);
}
}
return results;
},
iterFields: function(func) {
lib.each(this.fields, function(field) {
func(this[field], field);
}, this);
}
});
// Abstract nodes
var Value = Node.extend('Value', { fields: ['value'] });
// Concrete nodes
var NodeList = Node.extend('NodeList', {
fields: ['children'],
init: function(lineno, colno, nodes) {
this.parent(lineno, colno, nodes || []);
},
addChild: function(node) {
this.children.push(node);
}
});
var Root = NodeList.extend('Root');
var Literal = Value.extend('Literal');
var Symbol = Value.extend('Symbol');
var Group = NodeList.extend('Group');
var Array = NodeList.extend('Array');
var Pair = Node.extend('Pair', { fields: ['key', 'value'] });
var Dict = NodeList.extend('Dict');
var LookupVal = Node.extend('LookupVal', { fields: ['target', 'val'] });
var If = Node.extend('If', { fields: ['cond', 'body', 'else_'] });
var IfAsync = If.extend('IfAsync');
var InlineIf = Node.extend('InlineIf', { fields: ['cond', 'body', 'else_'] });
var For = Node.extend('For', { fields: ['arr', 'name', 'body', 'else_'] });
var AsyncEach = For.extend('AsyncEach');
var AsyncAll = For.extend('AsyncAll');
var Macro = Node.extend('Macro', { fields: ['name', 'args', 'body'] });
var Caller = Macro.extend('Caller');
var Import = Node.extend('Import', { fields: ['template', 'target', 'withContext'] });
var FromImport = Node.extend('FromImport', {
fields: ['template', 'names', 'withContext'],
init: function(lineno, colno, template, names, withContext) {
this.parent(lineno, colno,
template,
names || new NodeList(), withContext);
}
});
var FunCall = Node.extend('FunCall', { fields: ['name', 'args'] });
var Filter = FunCall.extend('Filter');
var FilterAsync = Filter.extend('FilterAsync', {
fields: ['name', 'args', 'symbol']
});
var KeywordArgs = Dict.extend('KeywordArgs');
var Block = Node.extend('Block', { fields: ['name', 'body'] });
var Super = Node.extend('Super', { fields: ['blockName', 'symbol'] });
var TemplateRef = Node.extend('TemplateRef', { fields: ['template'] });
var Extends = TemplateRef.extend('Extends');
var Include = Node.extend('Include', { fields: ['template', 'ignoreMissing'] });
var Set = Node.extend('Set', { fields: ['targets', 'value'] });
var Output = NodeList.extend('Output');
var TemplateData = Literal.extend('TemplateData');
var UnaryOp = Node.extend('UnaryOp', { fields: ['target'] });
var BinOp = Node.extend('BinOp', { fields: ['left', 'right'] });
var In = BinOp.extend('In');
var Or = BinOp.extend('Or');
var And = BinOp.extend('And');
var Not = UnaryOp.extend('Not');
var Add = BinOp.extend('Add');
var Concat = BinOp.extend('Concat');
var Sub = BinOp.extend('Sub');
var Mul = BinOp.extend('Mul');
var Div = BinOp.extend('Div');
var FloorDiv = BinOp.extend('FloorDiv');
var Mod = BinOp.extend('Mod');
var Pow = BinOp.extend('Pow');
var Neg = UnaryOp.extend('Neg');
var Pos = UnaryOp.extend('Pos');
var Compare = Node.extend('Compare', { fields: ['expr', 'ops'] });
var CompareOperand = Node.extend('CompareOperand', {
fields: ['expr', 'type']
});
var CallExtension = Node.extend('CallExtension', {
fields: ['extName', 'prop', 'args', 'contentArgs'],
init: function(ext, prop, args, contentArgs) {
this.extName = ext._name || ext;
this.prop = prop;
this.args = args || new NodeList();
this.contentArgs = contentArgs || [];
this.autoescape = ext.autoescape;
}
});
var CallExtensionAsync = CallExtension.extend('CallExtensionAsync');
// Print the AST in a nicely formatted tree format for debuggin
function printNodes(node, indent) {
indent = indent || 0;
// This is hacky, but this is just a debugging function anyway
function print(str, indent, inline) {
var lines = str.split('\n');
for(var i=0; i<lines.length; i++) {
if(lines[i]) {
if((inline && i > 0) || !inline) {
for(var j=0; j<indent; j++) {
process.stdout.write(' ');
}
}
}
if(i === lines.length-1) {
process.stdout.write(lines[i]);
}
else {
process.stdout.write(lines[i] + '\n');
}
}
}
print(node.typename + ': ', indent);
if(node instanceof NodeList) {
print('\n');
lib.each(node.children, function(n) {
printNodes(n, indent + 2);
});
}
else if(node instanceof CallExtension) {
print(node.extName + '.' + node.prop);
print('\n');
if(node.args) {
printNodes(node.args, indent + 2);
}
if(node.contentArgs) {
lib.each(node.contentArgs, function(n) {
printNodes(n, indent + 2);
});
}
}
else {
var nodes = null;
var props = null;
node.iterFields(function(val, field) {
if(val instanceof Node) {
nodes = nodes || {};
nodes[field] = val;
}
else {
props = props || {};
props[field] = val;
}
});
if(props) {
print(JSON.stringify(props, null, 2) + '\n', null, true);
}
else {
print('\n');
}
if(nodes) {
for(var k in nodes) {
printNodes(nodes[k], indent + 2);
}
}
}
}
// var t = new NodeList(0, 0,
// [new Value(0, 0, 3),
// new Value(0, 0, 10),
// new Pair(0, 0,
// new Value(0, 0, 'key'),
// new Value(0, 0, 'value'))]);
// printNodes(t);
module.exports = {
Node: Node,
Root: Root,
NodeList: NodeList,
Value: Value,
Literal: Literal,
Symbol: Symbol,
Group: Group,
Array: Array,
Pair: Pair,
Dict: Dict,
Output: Output,
TemplateData: TemplateData,
If: If,
IfAsync: IfAsync,
InlineIf: InlineIf,
For: For,
AsyncEach: AsyncEach,
AsyncAll: AsyncAll,
Macro: Macro,
Caller: Caller,
Import: Import,
FromImport: FromImport,
FunCall: FunCall,
Filter: Filter,
FilterAsync: FilterAsync,
KeywordArgs: KeywordArgs,
Block: Block,
Super: Super,
Extends: Extends,
Include: Include,
Set: Set,
LookupVal: LookupVal,
BinOp: BinOp,
In: In,
Or: Or,
And: And,
Not: Not,
Add: Add,
Concat: Concat,
Sub: Sub,
Mul: Mul,
Div: Div,
FloorDiv: FloorDiv,
Mod: Mod,
Pow: Pow,
Neg: Neg,
Pos: Pos,
Compare: Compare,
CompareOperand: CompareOperand,
CallExtension: CallExtension,
CallExtensionAsync: CallExtensionAsync,
printNodes: printNodes
};
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3)))
/***/ },
/* 11 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var nodes = __webpack_require__(10);
var lib = __webpack_require__(1);
var sym = 0;
function gensym() {
return 'hole_' + sym++;
}
// copy-on-write version of map
function mapCOW(arr, func) {
var res = null;
for(var i=0; i<arr.length; i++) {
var item = func(arr[i]);
if(item !== arr[i]) {
if(!res) {
res = arr.slice();
}
res[i] = item;
}
}
return res || arr;
}
function walk(ast, func, depthFirst) {
if(!(ast instanceof nodes.Node)) {
return ast;
}
if(!depthFirst) {
var astT = func(ast);
if(astT && astT !== ast) {
return astT;
}
}
if(ast instanceof nodes.NodeList) {
var children = mapCOW(ast.children, function(node) {
return walk(node, func, depthFirst);
});
if(children !== ast.children) {
ast = new nodes[ast.typename](ast.lineno, ast.colno, children);
}
}
else if(ast instanceof nodes.CallExtension) {
var args = walk(ast.args, func, depthFirst);
var contentArgs = mapCOW(ast.contentArgs, function(node) {
return walk(node, func, depthFirst);
});
if(args !== ast.args || contentArgs !== ast.contentArgs) {
ast = new nodes[ast.typename](ast.extName,
ast.prop,
args,
contentArgs);
}
}
else {
var props = ast.fields.map(function(field) {
return ast[field];
});
var propsT = mapCOW(props, function(prop) {
return walk(prop, func, depthFirst);
});
if(propsT !== props) {
ast = new nodes[ast.typename](ast.lineno, ast.colno);
propsT.forEach(function(prop, i) {
ast[ast.fields[i]] = prop;
});
}
}
return depthFirst ? (func(ast) || ast) : ast;
}
function depthWalk(ast, func) {
return walk(ast, func, true);
}
function _liftFilters(node, asyncFilters, prop) {
var children = [];
var walked = depthWalk(prop ? node[prop] : node, function(node) {
if(node instanceof nodes.Block) {
return node;
}
else if((node instanceof nodes.Filter &&
lib.indexOf(asyncFilters, node.name.value) !== -1) ||
node instanceof nodes.CallExtensionAsync) {
var symbol = new nodes.Symbol(node.lineno,
node.colno,
gensym());
children.push(new nodes.FilterAsync(node.lineno,
node.colno,
node.name,
node.args,
symbol));
return symbol;
}
});
if(prop) {
node[prop] = walked;
}
else {
node = walked;
}
if(children.length) {
children.push(node);
return new nodes.NodeList(
node.lineno,
node.colno,
children
);
}
else {
return node;
}
}
function liftFilters(ast, asyncFilters) {
return depthWalk(ast, function(node) {
if(node instanceof nodes.Output) {
return _liftFilters(node, asyncFilters);
}
else if(node instanceof nodes.Set) {
return _liftFilters(node, asyncFilters, 'value');
}
else if(node instanceof nodes.For) {
return _liftFilters(node, asyncFilters, 'arr');
}
else if(node instanceof nodes.If) {
return _liftFilters(node, asyncFilters, 'cond');
}
else if(node instanceof nodes.CallExtension) {
return _liftFilters(node, asyncFilters, 'args');
}
});
}
function liftSuper(ast) {
return walk(ast, function(blockNode) {
if(!(blockNode instanceof nodes.Block)) {
return;
}
var hasSuper = false;
var symbol = gensym();
blockNode.body = walk(blockNode.body, function(node) {
if(node instanceof nodes.FunCall &&
node.name.value === 'super') {
hasSuper = true;
return new nodes.Symbol(node.lineno, node.colno, symbol);
}
});
if(hasSuper) {
blockNode.body.children.unshift(new nodes.Super(
0, 0, blockNode.name, new nodes.Symbol(0, 0, symbol)
));
}
});
}
function convertStatements(ast) {
return depthWalk(ast, function(node) {
if(!(node instanceof nodes.If) &&
!(node instanceof nodes.For)) {
return;
}
var async = false;
walk(node, function(node) {
if(node instanceof nodes.FilterAsync ||
node instanceof nodes.IfAsync ||
node instanceof nodes.AsyncEach ||
node instanceof nodes.AsyncAll ||
node instanceof nodes.CallExtensionAsync ||
node instanceof nodes.Include) {
async = true;
// Stop iterating by returning the node
return node;
}
});
if(async) {
if(node instanceof nodes.If) {
return new nodes.IfAsync(
node.lineno,
node.colno,
node.cond,
node.body,
node.else_
);
}
else if(node instanceof nodes.For) {
return new nodes.AsyncEach(
node.lineno,
node.colno,
node.arr,
node.name,
node.body,
node.else_
);
}
}
});
}
function cps(ast, asyncFilters) {
return convertStatements(liftSuper(liftFilters(ast, asyncFilters)));
}
function transform(ast, asyncFilters) {
return cps(ast, asyncFilters || []);
}
// var parser = require('./parser');
// var src = 'hello {% foo %}{% endfoo %} end';
// var ast = transform(parser.parse(src, [new FooExtension()]), ['bar']);
// nodes.printNodes(ast);
module.exports = {
transform: transform
};
/***/ },
/* 12 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lib = __webpack_require__(1);
var Obj = __webpack_require__(6);
// Frames keep track of scoping both at compile-time and run-time so
// we know how to access variables. Block tags can introduce special
// variables, for example.
var Frame = Obj.extend({
init: function(parent) {
this.variables = {};
this.parent = parent;
this.topLevel = false;
},
set: function(name, val, resolveUp) {
// Allow variables with dots by automatically creating the
// nested structure
var parts = name.split('.');
var obj = this.variables;
var frame = this;
if(resolveUp) {
if((frame = this.resolve(parts[0]))) {
frame.set(name, val);
return;
}
frame = this;
}
for(var i=0; i<parts.length - 1; i++) {
var id = parts[i];
if(!obj[id]) {
obj[id] = {};
}
obj = obj[id];
}
obj[parts[parts.length - 1]] = val;
},
get: function(name) {
var val = this.variables[name];
if(val !== undefined && val !== null) {
return val;
}
return null;
},
lookup: function(name) {
var p = this.parent;
var val = this.variables[name];
if(val !== undefined && val !== null) {
return val;
}
return p && p.lookup(name);
},
resolve: function(name) {
var p = this.parent;
var val = this.variables[name];
if(val !== undefined && val !== null) {
return this;
}
return p && p.resolve(name);
},
push: function() {
return new Frame(this);
},
pop: function() {
return this.parent;
}
});
function makeMacro(argNames, kwargNames, func) {
return function() {
var argCount = numArgs(arguments);
var args;
var kwargs = getKeywordArgs(arguments);
var i;
if(argCount > argNames.length) {
args = Array.prototype.slice.call(arguments, 0, argNames.length);
// Positional arguments that should be passed in as
// keyword arguments (essentially default values)
var vals = Array.prototype.slice.call(arguments, args.length, argCount);
for(i = 0; i < vals.length; i++) {
if(i < kwargNames.length) {
kwargs[kwargNames[i]] = vals[i];
}
}
args.push(kwargs);
}
else if(argCount < argNames.length) {
args = Array.prototype.slice.call(arguments, 0, argCount);
for(i = argCount; i < argNames.length; i++) {
var arg = argNames[i];
// Keyword arguments that should be passed as
// positional arguments, i.e. the caller explicitly
// used the name of a positional arg
args.push(kwargs[arg]);
delete kwargs[arg];
}
args.push(kwargs);
}
else {
args = arguments;
}
return func.apply(this, args);
};
}
function makeKeywordArgs(obj) {
obj.__keywords = true;
return obj;
}
function getKeywordArgs(args) {
var len = args.length;
if(len) {
var lastArg = args[len - 1];
if(lastArg && lastArg.hasOwnProperty('__keywords')) {
return lastArg;
}
}
return {};
}
function numArgs(args) {
var len = args.length;
if(len === 0) {
return 0;
}
var lastArg = args[len - 1];
if(lastArg && lastArg.hasOwnProperty('__keywords')) {
return len - 1;
}
else {
return len;
}
}
// A SafeString object indicates that the string should not be
// autoescaped. This happens magically because autoescaping only
// occurs on primitive string objects.
function SafeString(val) {
if(typeof val !== 'string') {
return val;
}
this.val = val;
this.length = val.length;
}
SafeString.prototype = Object.create(String.prototype, {
length: { writable: true, configurable: true, value: 0 }
});
SafeString.prototype.valueOf = function() {
return this.val;
};
SafeString.prototype.toString = function() {
return this.val;
};
function copySafeness(dest, target) {
if(dest instanceof SafeString) {
return new SafeString(target);
}
return target.toString();
}
function markSafe(val) {
var type = typeof val;
if(type === 'string') {
return new SafeString(val);
}
else if(type !== 'function') {
return val;
}
else {
return function() {
var ret = val.apply(this, arguments);
if(typeof ret === 'string') {
return new SafeString(ret);
}
return ret;
};
}
}
function suppressValue(val, autoescape) {
val = (val !== undefined && val !== null) ? val : '';
if(autoescape && typeof val === 'string') {
val = lib.escape(val);
}
return val;
}
function ensureDefined(val, lineno, colno) {
if(val === null || val === undefined) {
throw new lib.TemplateError(
'attempted to output null or undefined value',
lineno + 1,
colno + 1
);
}
return val;
}
function memberLookup(obj, val) {
obj = obj || {};
if(typeof obj[val] === 'function') {
return function() {
return obj[val].apply(obj, arguments);
};
}
return obj[val];
}
function callWrap(obj, name, context, args) {
if(!obj) {
throw new Error('Unable to call `' + name + '`, which is undefined or falsey');
}
else if(typeof obj !== 'function') {
throw new Error('Unable to call `' + name + '`, which is not a function');
}
// jshint validthis: true
return obj.apply(context, args);
}
function contextOrFrameLookup(context, frame, name) {
var val = frame.lookup(name);
return (val !== undefined && val !== null) ?
val :
context.lookup(name);
}
function handleError(error, lineno, colno) {
if(error.lineno) {
return error;
}
else {
return new lib.TemplateError(error, lineno, colno);
}
}
function asyncEach(arr, dimen, iter, cb) {
if(lib.isArray(arr)) {
var len = arr.length;
lib.asyncIter(arr, function(item, i, next) {
switch(dimen) {
case 1: iter(item, i, len, next); break;
case 2: iter(item[0], item[1], i, len, next); break;
case 3: iter(item[0], item[1], item[2], i, len, next); break;
default:
item.push(i, next);
iter.apply(this, item);
}
}, cb);
}
else {
lib.asyncFor(arr, function(key, val, i, len, next) {
iter(key, val, i, len, next);
}, cb);
}
}
function asyncAll(arr, dimen, func, cb) {
var finished = 0;
var len, i;
var outputArr;
function done(i, output) {
finished++;
outputArr[i] = output;
if(finished === len) {
cb(null, outputArr.join(''));
}
}
if(lib.isArray(arr)) {
len = arr.length;
outputArr = new Array(len);
if(len === 0) {
cb(null, '');
}
else {
for(i = 0; i < arr.length; i++) {
var item = arr[i];
switch(dimen) {
case 1: func(item, i, len, done); break;
case 2: func(item[0], item[1], i, len, done); break;
case 3: func(item[0], item[1], item[2], i, len, done); break;
default:
item.push(i, done);
// jshint validthis: true
func.apply(this, item);
}
}
}
}
else {
var keys = lib.keys(arr);
len = keys.length;
outputArr = new Array(len);
if(len === 0) {
cb(null, '');
}
else {
for(i = 0; i < keys.length; i++) {
var k = keys[i];
func(k, arr[k], i, len, done);
}
}
}
}
module.exports = {
Frame: Frame,
makeMacro: makeMacro,
makeKeywordArgs: makeKeywordArgs,
numArgs: numArgs,
suppressValue: suppressValue,
ensureDefined: ensureDefined,
memberLookup: memberLookup,
contextOrFrameLookup: contextOrFrameLookup,
callWrap: callWrap,
handleError: handleError,
isArray: lib.isArray,
keys: lib.keys,
SafeString: SafeString,
copySafeness: copySafeness,
markSafe: markSafe,
asyncEach: asyncEach,
asyncAll: asyncAll
};
/***/ },
/* 13 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var lib = __webpack_require__(1);
var r = __webpack_require__(12);
function normalize(value, defaultValue) {
if(value === null || value === undefined || value === false) {
return defaultValue;
}
return value;
}
var filters = {
abs: function(n) {
return Math.abs(n);
},
batch: function(arr, linecount, fill_with) {
var i;
var res = [];
var tmp = [];
for(i = 0; i < arr.length; i++) {
if(i % linecount === 0 && tmp.length) {
res.push(tmp);
tmp = [];
}
tmp.push(arr[i]);
}
if(tmp.length) {
if(fill_with) {
for(i = tmp.length; i < linecount; i++) {
tmp.push(fill_with);
}
}
res.push(tmp);
}
return res;
},
capitalize: function(str) {
str = normalize(str, '');
var ret = str.toLowerCase();
return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1));
},
center: function(str, width) {
str = normalize(str, '');
width = width || 80;
if(str.length >= width) {
return str;
}
var spaces = width - str.length;
var pre = lib.repeat(' ', spaces/2 - spaces % 2);
var post = lib.repeat(' ', spaces/2);
return r.copySafeness(str, pre + str + post);
},
'default': function(val, def, bool) {
if(bool) {
return val ? val : def;
}
else {
return (val !== undefined) ? val : def;
}
},
dictsort: function(val, case_sensitive, by) {
if (!lib.isObject(val)) {
throw new lib.TemplateError('dictsort filter: val must be an object');
}
var array = [];
for (var k in val) {
// deliberately include properties from the object's prototype
array.push([k,val[k]]);
}
var si;
if (by === undefined || by === 'key') {
si = 0;
} else if (by === 'value') {
si = 1;
} else {
throw new lib.TemplateError(
'dictsort filter: You can only sort by either key or value');
}
array.sort(function(t1, t2) {
var a = t1[si];
var b = t2[si];
if (!case_sensitive) {
if (lib.isString(a)) {
a = a.toUpperCase();
}
if (lib.isString(b)) {
b = b.toUpperCase();
}
}
return a > b ? 1 : (a === b ? 0 : -1);
});
return array;
},
dump: function(obj) {
return JSON.stringify(obj);
},
escape: function(str) {
if(typeof str === 'string' ||
str instanceof r.SafeString) {
return lib.escape(str);
}
return str;
},
safe: function(str) {
return r.markSafe(str);
},
first: function(arr) {
return arr[0];
},
groupby: function(arr, attr) {
return lib.groupBy(arr, attr);
},
indent: function(str, width, indentfirst) {
str = normalize(str, '');
if (str === '') return '';
width = width || 4;
var res = '';
var lines = str.split('\n');
var sp = lib.repeat(' ', width);
for(var i=0; i<lines.length; i++) {
if(i === 0 && !indentfirst) {
res += lines[i] + '\n';
}
else {
res += sp + lines[i] + '\n';
}
}
return r.copySafeness(str, res);
},
join: function(arr, del, attr) {
del = del || '';
if(attr) {
arr = lib.map(arr, function(v) {
return v[attr];
});
}
return arr.join(del);
},
last: function(arr) {
return arr[arr.length-1];
},
length: function(val) {
var value = normalize(val, '');
return value !== undefined ? value.length : 0;
},
list: function(val) {
if(lib.isString(val)) {
return val.split('');
}
else if(lib.isObject(val)) {
var keys = [];
if(Object.keys) {
keys = Object.keys(val);
}
else {
for(var k in val) {
keys.push(k);
}
}
return lib.map(keys, function(k) {
return { key: k,
value: val[k] };
});
}
else if(lib.isArray(val)) {
return val;
}
else {
throw new lib.TemplateError('list filter: type not iterable');
}
},
lower: function(str) {
str = normalize(str, '');
return str.toLowerCase();
},
random: function(arr) {
return arr[Math.floor(Math.random() * arr.length)];
},
rejectattr: function(arr, attr) {
return arr.filter(function (item) {
return !item[attr];
});
},
selectattr: function(arr, attr) {
return arr.filter(function (item) {
return !!item[attr];
});
},
replace: function(str, old, new_, maxCount) {
var originalStr = str;
if (old instanceof RegExp) {
return str.replace(old, new_);
}
if(typeof maxCount === 'undefined'){
maxCount = -1;
}
var res = ''; // Output
// Cast Numbers in the search term to string
if(typeof old === 'number'){
old = old + '';
}
else if(typeof old !== 'string') {
// If it is something other than number or string,
// return the original string
return str;
}
// Cast numbers in the replacement to string
if(typeof str === 'number'){
str = str + '';
}
// If by now, we don't have a string, throw it back
if(typeof str !== 'string' && !(str instanceof r.SafeString)){
return str;
}
// ShortCircuits
if(old === ''){
// Mimic the python behaviour: empty string is replaced
// by replacement e.g. "abc"|replace("", ".") -> .a.b.c.
res = new_ + str.split('').join(new_) + new_;
return r.copySafeness(str, res);
}
var nextIndex = str.indexOf(old);
// if # of replacements to perform is 0, or the string to does
// not contain the old value, return the string
if(maxCount === 0 || nextIndex === -1){
return str;
}
var pos = 0;
var count = 0; // # of replacements made
while(nextIndex > -1 && (maxCount === -1 || count < maxCount)){
// Grab the next chunk of src string and add it with the
// replacement, to the result
res += str.substring(pos, nextIndex) + new_;
// Increment our pointer in the src string
pos = nextIndex + old.length;
count++;
// See if there are any more replacements to be made
nextIndex = str.indexOf(old, pos);
}
// We've either reached the end, or done the max # of
// replacements, tack on any remaining string
if(pos < str.length) {
res += str.substring(pos);
}
return r.copySafeness(originalStr, res);
},
reverse: function(val) {
var arr;
if(lib.isString(val)) {
arr = filters.list(val);
}
else {
// Copy it
arr = lib.map(val, function(v) { return v; });
}
arr.reverse();
if(lib.isString(val)) {
return r.copySafeness(val, arr.join(''));
}
return arr;
},
round: function(val, precision, method) {
precision = precision || 0;
var factor = Math.pow(10, precision);
var rounder;
if(method === 'ceil') {
rounder = Math.ceil;
}
else if(method === 'floor') {
rounder = Math.floor;
}
else {
rounder = Math.round;
}
return rounder(val * factor) / factor;
},
slice: function(arr, slices, fillWith) {
var sliceLength = Math.floor(arr.length / slices);
var extra = arr.length % slices;
var offset = 0;
var res = [];
for(var i=0; i<slices; i++) {
var start = offset + i * sliceLength;
if(i < extra) {
offset++;
}
var end = offset + (i + 1) * sliceLength;
var slice = arr.slice(start, end);
if(fillWith && i >= extra) {
slice.push(fillWith);
}
res.push(slice);
}
return res;
},
sort: r.makeMacro(['value', 'reverse', 'case_sensitive', 'attribute'], [], function(arr, reverse, caseSens, attr) {
// Copy it
arr = lib.map(arr, function(v) { return v; });
arr.sort(function(a, b) {
var x, y;
if(attr) {
x = a[attr];
y = b[attr];
}
else {
x = a;
y = b;
}
if(!caseSens && lib.isString(x) && lib.isString(y)) {
x = x.toLowerCase();
y = y.toLowerCase();
}
if(x < y) {
return reverse ? 1 : -1;
}
else if(x > y) {
return reverse ? -1: 1;
}
else {
return 0;
}
});
return arr;
}),
string: function(obj) {
return r.copySafeness(obj, obj);
},
striptags: function(input, preserve_linebreaks) {
input = normalize(input, '');
preserve_linebreaks = preserve_linebreaks || false;
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi;
var trimmedInput = filters.trim(input.replace(tags, ''));
var res = '';
if (preserve_linebreaks) {
res = trimmedInput
.replace(/^ +| +$/gm, '') // remove leading and trailing spaces
.replace(/ +/g, ' ') // squash adjacent spaces
.replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
.replace(/\n\n\n+/g, '\n\n'); // squash abnormal adjacent linebreaks
} else {
res = trimmedInput.replace(/\s+/gi, ' ');
}
return r.copySafeness(input, res);
},
title: function(str) {
str = normalize(str, '');
var words = str.split(' ');
for(var i = 0; i < words.length; i++) {
words[i] = filters.capitalize(words[i]);
}
return r.copySafeness(str, words.join(' '));
},
trim: function(str) {
return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ''));
},
truncate: function(input, length, killwords, end) {
var orig = input;
input = normalize(input, '');
length = length || 255;
if (input.length <= length)
return input;
if (killwords) {
input = input.substring(0, length);
} else {
var idx = input.lastIndexOf(' ', length);
if(idx === -1) {
idx = length;
}
input = input.substring(0, idx);
}
input += (end !== undefined && end !== null) ? end : '...';
return r.copySafeness(orig, input);
},
upper: function(str) {
str = normalize(str, '');
return str.toUpperCase();
},
urlencode: function(obj) {
var enc = encodeURIComponent;
if (lib.isString(obj)) {
return enc(obj);
} else {
var parts;
if (lib.isArray(obj)) {
parts = obj.map(function(item) {
return enc(item[0]) + '=' + enc(item[1]);
});
} else {
parts = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
parts.push(enc(k) + '=' + enc(obj[k]));
}
}
}
return parts.join('&');
}
},
urlize: function(str, length, nofollow) {
if (isNaN(length)) length = Infinity;
var noFollowAttr = (nofollow === true ? ' rel="nofollow"' : '');
// For the jinja regexp, see
// https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
var puncRE = /^(?:\(|<|&lt;)?(.*?)(?:\.|,|\)|\n|&gt;)?$/;
// from http://blog.gerv.net/2011/05/html5_email_address_regexp/
var emailRE = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
var httpHttpsRE = /^https?:\/\/.*$/;
var wwwRE = /^www\./;
var tldRE = /\.(?:org|net|com)(?:\:|\/|$)/;
var words = str.split(/\s+/).filter(function(word) {
// If the word has no length, bail. This can happen for str with
// trailing whitespace.
return word && word.length;
}).map(function(word) {
var matches = word.match(puncRE);
var possibleUrl = matches && matches[1] || word;
// url that starts with http or https
if (httpHttpsRE.test(possibleUrl))
return '<a href="' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
// url that starts with www.
if (wwwRE.test(possibleUrl))
return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
// an email address of the form username@domain.tld
if (emailRE.test(possibleUrl))
return '<a href="mailto:' + possibleUrl + '">' + possibleUrl + '</a>';
// url that ends in .com, .org or .net that is not an email address
if (tldRE.test(possibleUrl))
return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
return word;
});
return words.join(' ');
},
wordcount: function(str) {
str = normalize(str, '');
var words = (str) ? str.match(/\w+/g) : null;
return (words) ? words.length : null;
},
'float': function(val, def) {
var res = parseFloat(val);
return isNaN(res) ? def : res;
},
'int': function(val, def) {
var res = parseInt(val, 10);
return isNaN(res) ? def : res;
}
};
// Aliases
filters.d = filters['default'];
filters.e = filters.escape;
module.exports = filters;
/***/ },
/* 14 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var Loader = __webpack_require__(15);
var PrecompiledLoader = __webpack_require__(16);
var WebLoader = Loader.extend({
init: function(baseURL, opts) {
this.baseURL = baseURL || '.';
opts = opts || {};
// By default, the cache is turned off because there's no way
// to "watch" templates over HTTP, so they are re-downloaded
// and compiled each time. (Remember, PRECOMPILE YOUR
// TEMPLATES in production!)
this.useCache = !!opts.useCache;
// We default `async` to false so that the simple synchronous
// API can be used when you aren't doing anything async in
// your templates (which is most of the time). This performs a
// sync ajax request, but that's ok because it should *only*
// happen in development. PRECOMPILE YOUR TEMPLATES.
this.async = !!opts.async;
},
resolve: function(from, to) { // jshint ignore:line
throw new Error('relative templates not support in the browser yet');
},
getSource: function(name, cb) {
var useCache = this.useCache;
var result;
this.fetch(this.baseURL + '/' + name, function(err, src) {
if(err) {
if(cb) {
cb(err.content);
} else {
if (err.status === 404) {
result = null;
} else {
throw err.content;
}
}
}
else {
result = { src: src,
path: name,
noCache: !useCache };
if(cb) {
cb(null, result);
}
}
});
// if this WebLoader isn't running asynchronously, the
// fetch above would actually run sync and we'll have a
// result here
return result;
},
fetch: function(url, cb) {
// Only in the browser please
var ajax;
var loading = true;
if(window.XMLHttpRequest) { // Mozilla, Safari, ...
ajax = new XMLHttpRequest();
}
else if(window.ActiveXObject) { // IE 8 and older
/* global ActiveXObject */
ajax = new ActiveXObject('Microsoft.XMLHTTP');
}
ajax.onreadystatechange = function() {
if(ajax.readyState === 4 && loading) {
loading = false;
if(ajax.status === 0 || ajax.status === 200) {
cb(null, ajax.responseText);
}
else {
cb({ status: ajax.status, content: ajax.responseText });
}
}
};
url += (url.indexOf('?') === -1 ? '?' : '&') + 's=' +
(new Date().getTime());
ajax.open('GET', url, this.async);
ajax.send();
}
});
module.exports = {
WebLoader: WebLoader,
PrecompiledLoader: PrecompiledLoader
};
/***/ },
/* 15 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var path = __webpack_require__(3);
var Obj = __webpack_require__(6);
var lib = __webpack_require__(1);
var Loader = Obj.extend({
on: function(name, func) {
this.listeners = this.listeners || {};
this.listeners[name] = this.listeners[name] || [];
this.listeners[name].push(func);
},
emit: function(name /*, arg1, arg2, ...*/) {
var args = Array.prototype.slice.call(arguments, 1);
if(this.listeners && this.listeners[name]) {
lib.each(this.listeners[name], function(listener) {
listener.apply(null, args);
});
}
},
resolve: function(from, to) {
return path.resolve(path.dirname(from), to);
},
isRelative: function(filename) {
return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
}
});
module.exports = Loader;
/***/ },
/* 16 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var Loader = __webpack_require__(15);
var PrecompiledLoader = Loader.extend({
init: function(compiledTemplates) {
this.precompiled = compiledTemplates || {};
},
getSource: function(name) {
if (this.precompiled[name]) {
return {
src: { type: 'code',
obj: this.precompiled[name] },
path: name
};
}
return null;
}
});
module.exports = PrecompiledLoader;
/***/ },
/* 17 */
/***/ function(module, exports) {
'use strict';
function cycler(items) {
var index = -1;
return {
current: null,
reset: function() {
index = -1;
this.current = null;
},
next: function() {
index++;
if(index >= items.length) {
index = 0;
}
this.current = items[index];
return this.current;
},
};
}
function joiner(sep) {
sep = sep || ',';
var first = true;
return function() {
var val = first ? '' : sep;
first = false;
return val;
};
}
// Making this a function instead so it returns a new object
// each time it's called. That way, if something like an environment
// uses it, they will each have their own copy.
function globals() {
return {
range: function(start, stop, step) {
if(!stop) {
stop = start;
start = 0;
step = 1;
}
else if(!step) {
step = 1;
}
var arr = [];
var i;
if (step > 0) {
for (i=start; i<stop; i+=step) {
arr.push(i);
}
} else {
for (i=start; i>stop; i+=step) {
arr.push(i);
}
}
return arr;
},
// lipsum: function(n, html, min, max) {
// },
cycler: function() {
return cycler(Array.prototype.slice.call(arguments));
},
joiner: function(sep) {
return joiner(sep);
}
};
}
module.exports = globals;
/***/ },
/* 18 */
/***/ function(module, exports) {
function installCompat() {
'use strict';
// This must be called like `nunjucks.installCompat` so that `this`
// references the nunjucks instance
var runtime = this.runtime; // jshint ignore:line
var lib = this.lib; // jshint ignore:line
var orig_contextOrFrameLookup = runtime.contextOrFrameLookup;
runtime.contextOrFrameLookup = function(context, frame, key) {
var val = orig_contextOrFrameLookup.apply(this, arguments);
if (val === undefined) {
switch (key) {
case 'True':
return true;
case 'False':
return false;
case 'None':
return null;
}
}
return val;
};
var orig_memberLookup = runtime.memberLookup;
var ARRAY_MEMBERS = {
pop: function(index) {
if (index === undefined) {
return this.pop();
}
if (index >= this.length || index < 0) {
throw new Error('KeyError');
}
return this.splice(index, 1);
},
remove: function(element) {
for (var i = 0; i < this.length; i++) {
if (this[i] === element) {
return this.splice(i, 1);
}
}
throw new Error('ValueError');
},
count: function(element) {
var count = 0;
for (var i = 0; i < this.length; i++) {
if (this[i] === element) {
count++;
}
}
return count;
},
index: function(element) {
var i;
if ((i = this.indexOf(element)) === -1) {
throw new Error('ValueError');
}
return i;
},
find: function(element) {
return this.indexOf(element);
},
insert: function(index, elem) {
return this.splice(index, 0, elem);
}
};
var OBJECT_MEMBERS = {
items: function() {
var ret = [];
for(var k in this) {
ret.push([k, this[k]]);
}
return ret;
},
values: function() {
var ret = [];
for(var k in this) {
ret.push(this[k]);
}
return ret;
},
keys: function() {
var ret = [];
for(var k in this) {
ret.push(k);
}
return ret;
},
get: function(key, def) {
var output = this[key];
if (output === undefined) {
output = def;
}
return output;
},
has_key: function(key) {
return this.hasOwnProperty(key);
},
pop: function(key, def) {
var output = this[key];
if (output === undefined && def !== undefined) {
output = def;
} else if (output === undefined) {
throw new Error('KeyError');
} else {
delete this[key];
}
return output;
},
popitem: function() {
for (var k in this) {
// Return the first object pair.
var val = this[k];
delete this[k];
return [k, val];
}
throw new Error('KeyError');
},
setdefault: function(key, def) {
if (key in this) {
return this[key];
}
if (def === undefined) {
def = null;
}
return this[key] = def;
},
update: function(kwargs) {
for (var k in kwargs) {
this[k] = kwargs[k];
}
return null; // Always returns None
}
};
OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items;
OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values;
OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys;
runtime.memberLookup = function(obj, val, autoescape) { // jshint ignore:line
obj = obj || {};
// If the object is an object, return any of the methods that Python would
// otherwise provide.
if (lib.isArray(obj) && ARRAY_MEMBERS.hasOwnProperty(val)) {
return function() {return ARRAY_MEMBERS[val].apply(obj, arguments);};
}
if (lib.isObject(obj) && OBJECT_MEMBERS.hasOwnProperty(val)) {
return function() {return OBJECT_MEMBERS[val].apply(obj, arguments);};
}
return orig_memberLookup.apply(this, arguments);
};
}
module.exports = installCompat;
/***/ }
/******/ ]);