diff --git a/demo/browser/date.html b/demo/browser/date.html
new file mode 100644
index 000000000..1684b6db4
--- /dev/null
+++ b/demo/browser/date.html
@@ -0,0 +1 @@
+access time: {{date|date: "%Y-%m-%d %H:%M:%S"}}
\ No newline at end of file
diff --git a/demo/browser/hello.html b/demo/browser/hello.html
new file mode 100644
index 000000000..215e851fb
--- /dev/null
+++ b/demo/browser/hello.html
@@ -0,0 +1 @@
+
Welcome to {{ name | capitalize}}, {% include 'date.html' %}
\ No newline at end of file
diff --git a/demo/browser/index.html b/demo/browser/index.html
index 06a2bc227..5d7b69279 100644
--- a/demo/browser/index.html
+++ b/demo/browser/index.html
@@ -8,19 +8,25 @@
-
diff --git a/dist/liquid.js b/dist/liquid.js
index a194ade4f..9bda73c8f 100644
--- a/dist/liquid.js
+++ b/dist/liquid.js
@@ -215,7 +215,7 @@ function isValidDate(date) {
registerAll.filters = filters;
module.exports = registerAll;
-},{"./src/syntax.js":14,"./src/util/strftime.js":21,"./src/util/underscore.js":22}],2:[function(require,module,exports){
+},{"./src/syntax.js":15,"./src/util/strftime.js":22,"./src/util/underscore.js":23}],2:[function(require,module,exports){
'use strict';
var Scope = require('./src/scope');
@@ -225,6 +225,7 @@ var tokenizer = require('./src/tokenizer.js');
var statFileAsync = require('./src/util/fs.js').statFileAsync;
var readFileAsync = require('./src/util/fs.js').readFileAsync;
var path = require('path');
+var url = require('./src/util/url.js');
var Render = require('./src/render.js');
var lexical = require('./src/lexical.js');
var Tag = require('./src/tag.js');
@@ -305,6 +306,9 @@ var _engine = {
});
},
getTemplate: function getTemplate(filepath, root) {
+ return typeof XMLHttpRequest === 'undefined' ? this.getTemplateFromFile(filepath, root) : this.getTemplateFromUrl(filepath, root);
+ },
+ getTemplateFromFile: function getTemplateFromFile(filepath, root) {
var _this3 = this;
if (!path.extname(filepath)) {
@@ -328,6 +332,44 @@ var _engine = {
}
});
},
+ getTemplateFromUrl: function getTemplateFromUrl(filepath, root) {
+ var _this4 = this;
+
+ var fullUrl;
+ if (url.valid(filepath)) {
+ fullUrl = filepath;
+ } else {
+ if (!url.extname(filepath)) {
+ filepath += this.options.extname;
+ }
+ fullUrl = url.resolve(root || this.options.root, filepath);
+ }
+ if (this.options.cache) {
+ var tpl = this.cache[filepath];
+ if (tpl) {
+ return Promise.resolve(tpl);
+ }
+ }
+ return new Promise(function (resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function () {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ var tpl = _this4.parse(xhr.responseText);
+ if (_this4.options.cache) {
+ _this4.cache[filepath] = tpl;
+ }
+ resolve(tpl);
+ } else {
+ reject(new Error(xhr.statusText));
+ }
+ };
+ xhr.onerror = function () {
+ reject(new Error(xhr.statusText));
+ };
+ xhr.open('GET', fullUrl);
+ xhr.send();
+ });
+ },
express: function express(opts) {
opts = opts || {};
var self = this;
@@ -384,7 +426,7 @@ factory.Types = {
module.exports = factory;
-},{"./filters":1,"./src/filter.js":8,"./src/lexical.js":9,"./src/parser":11,"./src/render.js":12,"./src/scope":13,"./src/syntax.js":14,"./src/tag.js":15,"./src/tokenizer.js":16,"./src/util/assert.js":17,"./src/util/error.js":18,"./src/util/fs.js":19,"./src/util/promise.js":20,"./src/util/underscore.js":22,"./tags":34,"any-promise":3,"path":7}],3:[function(require,module,exports){
+},{"./filters":1,"./src/filter.js":9,"./src/lexical.js":10,"./src/parser":12,"./src/render.js":13,"./src/scope":14,"./src/syntax.js":15,"./src/tag.js":16,"./src/tokenizer.js":17,"./src/util/assert.js":18,"./src/util/error.js":19,"./src/util/fs.js":20,"./src/util/promise.js":21,"./src/util/underscore.js":23,"./src/util/url.js":24,"./tags":36,"any-promise":3,"path":7}],3:[function(require,module,exports){
'use strict';
module.exports = require('./register')().Promise;
@@ -494,6 +536,58 @@ function loadImplementation() {
"use strict";
},{}],8:[function(require,module,exports){
+"use strict";
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+// Copyright 2014 Simon Lydell
+// X11 (“MIT”) Licensed. (See LICENSE.)
+
+void function (root, factory) {
+ if (typeof define === "function" && define.amd) {
+ define(factory);
+ } else if ((typeof exports === "undefined" ? "undefined" : _typeof(exports)) === "object") {
+ module.exports = factory();
+ } else {
+ root.resolveUrl = factory();
+ }
+}(undefined, function () {
+
+ function resolveUrl() /* ...urls */{
+ var numUrls = arguments.length;
+
+ if (numUrls === 0) {
+ throw new Error("resolveUrl requires at least one argument; got none.");
+ }
+
+ var base = document.createElement("base");
+ base.href = arguments[0];
+
+ if (numUrls === 1) {
+ return base.href;
+ }
+
+ var head = document.getElementsByTagName("head")[0];
+ head.insertBefore(base, head.firstChild);
+
+ var a = document.createElement("a");
+ var resolved;
+
+ for (var index = 1; index < numUrls; index++) {
+ a.href = arguments[index];
+ resolved = a.href;
+ base.href = resolved;
+ }
+
+ head.removeChild(base);
+
+ return resolved;
+ }
+
+ return resolveUrl;
+});
+
+},{}],9:[function(require,module,exports){
'use strict';
var lexical = require('./lexical.js');
@@ -567,7 +661,7 @@ module.exports = function (options) {
};
};
-},{"./lexical.js":9,"./syntax.js":14,"./util/assert.js":17,"./util/underscore.js":22}],9:[function(require,module,exports){
+},{"./lexical.js":10,"./syntax.js":15,"./util/assert.js":18,"./util/underscore.js":23}],10:[function(require,module,exports){
'use strict';
// quote related
@@ -683,7 +777,7 @@ module.exports = {
isInteger: isInteger
};
-},{}],10:[function(require,module,exports){
+},{}],11:[function(require,module,exports){
'use strict';
module.exports = function (isTruthy) {
@@ -720,7 +814,7 @@ module.exports = function (isTruthy) {
};
};
-},{}],11:[function(require,module,exports){
+},{}],12:[function(require,module,exports){
'use strict';
var lexical = require('./lexical.js');
@@ -831,7 +925,7 @@ module.exports = function (Tag, Filter) {
};
};
-},{"./lexical.js":9,"./util/assert.js":17,"./util/error.js":18}],12:[function(require,module,exports){
+},{"./lexical.js":10,"./util/assert.js":18,"./util/error.js":19}],13:[function(require,module,exports){
'use strict';
var Syntax = require('./syntax.js');
@@ -913,7 +1007,7 @@ function stringify(val) {
module.exports = factory;
-},{"./syntax.js":14,"./util/assert.js":17,"./util/error.js":18,"./util/promise.js":20,"any-promise":3}],13:[function(require,module,exports){
+},{"./syntax.js":15,"./util/assert.js":18,"./util/error.js":19,"./util/promise.js":21,"any-promise":3}],14:[function(require,module,exports){
'use strict';
var _ = require('./util/underscore.js');
@@ -1106,7 +1200,7 @@ exports.factory = function (ctx, opts) {
return scope;
};
-},{"./lexical.js":9,"./util/assert.js":17,"./util/underscore.js":22}],14:[function(require,module,exports){
+},{"./lexical.js":10,"./util/assert.js":18,"./util/underscore.js":23}],15:[function(require,module,exports){
'use strict';
var operators = require('./operators.js')(isTruthy);
@@ -1166,7 +1260,7 @@ module.exports = {
evalExp: evalExp, evalValue: evalValue, isTruthy: isTruthy, isFalsy: isFalsy
};
-},{"../src/util/assert.js":17,"./lexical.js":9,"./operators.js":10}],15:[function(require,module,exports){
+},{"../src/util/assert.js":18,"./lexical.js":10,"./operators.js":11}],16:[function(require,module,exports){
'use strict';
var lexical = require('./lexical.js');
@@ -1235,7 +1329,7 @@ module.exports = function () {
};
};
-},{"./lexical.js":9,"./syntax.js":14,"./util/assert.js":17,"any-promise":3}],16:[function(require,module,exports){
+},{"./lexical.js":10,"./syntax.js":15,"./util/assert.js":18,"any-promise":3}],17:[function(require,module,exports){
'use strict';
var lexical = require('./lexical.js');
@@ -1328,7 +1422,7 @@ function LineNumber(html) {
exports.parse = parse;
exports.whiteSpaceCtrl = whiteSpaceCtrl;
-},{"./lexical.js":9,"./util/assert.js":17,"./util/error.js":18,"./util/underscore.js":22,"./whitespace-ctrl.js":23}],17:[function(require,module,exports){
+},{"./lexical.js":10,"./util/assert.js":18,"./util/error.js":19,"./util/underscore.js":23,"./whitespace-ctrl.js":25}],18:[function(require,module,exports){
'use strict';
var AssertionError = require('./error.js').AssertionError;
@@ -1342,7 +1436,7 @@ function assert(predicate, message) {
module.exports = assert;
-},{"./error.js":18}],18:[function(require,module,exports){
+},{"./error.js":19}],19:[function(require,module,exports){
'use strict';
var _ = require('./underscore.js');
@@ -1446,7 +1540,7 @@ module.exports = {
RenderError: RenderError
};
-},{"./underscore.js":22}],19:[function(require,module,exports){
+},{"./underscore.js":23}],20:[function(require,module,exports){
'use strict';
var fs = require('fs');
@@ -1472,7 +1566,7 @@ module.exports = {
statFileAsync: statFileAsync
};
-},{"fs":6}],20:[function(require,module,exports){
+},{"fs":6}],21:[function(require,module,exports){
'use strict';
var Promise = require('any-promise');
@@ -1517,7 +1611,7 @@ function mapSeries(iterable, iteratee) {
exports.anySeries = anySeries;
exports.mapSeries = mapSeries;
-},{"any-promise":3}],21:[function(require,module,exports){
+},{"any-promise":3}],22:[function(require,module,exports){
'use strict';
var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
@@ -1711,7 +1805,7 @@ var strftime = function strftime(d, format) {
module.exports = strftime;
-},{}],22:[function(require,module,exports){
+},{}],23:[function(require,module,exports){
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
@@ -1853,7 +1947,34 @@ exports.forOwn = forOwn;
exports.assign = assign;
exports.uniq = uniq;
-},{}],23:[function(require,module,exports){
+},{}],24:[function(require,module,exports){
+'use strict';
+
+var resolve = require('resolve-url');
+var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^/]+?|)(\.[^./]*|))(?:[/]*)$/;
+var urlRe = /^(?:\w+:)?\/\/([^\s.]+\.\S{2}|localhost[:?\d]*)\S*$/;
+
+// https://github.com/jinder/path/blob/master/path.js#L567
+exports.extname = function (path) {
+ return splitPathRe.exec(path).slice(1)[3];
+};
+
+// https://www.npmjs.com/package/is-url
+exports.valid = function (path) {
+ return urlRe.test(path);
+};
+
+exports.resolve = function (root, path) {
+ if (Object.prototype.toString.call(root) === '[object Array]') {
+ root = root[0];
+ }
+ if (root && root.charAt(root.length - 1) !== '/') {
+ root += '/';
+ }
+ return resolve(root, path);
+};
+
+},{"resolve-url":8}],25:[function(require,module,exports){
'use strict';
var _ = require('./util/underscore.js');
@@ -1904,7 +2025,7 @@ function trimRight(token, greedy) {
module.exports = whiteSpaceCtrl;
-},{"./util/underscore.js":22}],24:[function(require,module,exports){
+},{"./util/underscore.js":23}],26:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -1928,7 +2049,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17,"any-promise":3}],25:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18,"any-promise":3}],27:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -1937,38 +2058,37 @@ var re = new RegExp('(' + lexical.identifier.source + ')');
var assert = require('../src/util/assert.js');
module.exports = function (liquid) {
+ liquid.registerTag('capture', {
+ parse: function parse(tagToken, remainTokens) {
+ var _this = this;
- liquid.registerTag('capture', {
- parse: function parse(tagToken, remainTokens) {
- var _this = this;
+ var match = tagToken.args.match(re);
+ assert(match, tagToken.args + ' not valid identifier');
- var match = tagToken.args.match(re);
- assert(match, tagToken.args + ' not valid identifier');
+ this.variable = match[1];
+ this.templates = [];
- this.variable = match[1];
- this.templates = [];
+ var stream = liquid.parser.parseStream(remainTokens);
+ stream.on('tag:endcapture', function (token) {
+ return stream.stop();
+ }).on('template', function (tpl) {
+ return _this.templates.push(tpl);
+ }).on('end', function (x) {
+ throw new Error('tag ' + tagToken.raw + ' not closed');
+ });
+ stream.start();
+ },
+ render: function render(scope, hash) {
+ var _this2 = this;
- var stream = liquid.parser.parseStream(remainTokens);
- stream.on('tag:endcapture', function (token) {
- return stream.stop();
- }).on('template', function (tpl) {
- return _this.templates.push(tpl);
- }).on('end', function (x) {
- throw new Error('tag ' + tagToken.raw + ' not closed');
- });
- stream.start();
- },
- render: function render(scope, hash) {
- var _this2 = this;
-
- return liquid.renderer.renderTemplates(this.templates, scope).then(function (html) {
- scope.set(_this2.variable, html);
- });
- }
- });
+ return liquid.renderer.renderTemplates(this.templates, scope).then(function (html) {
+ scope.set(_this2.variable, html);
+ });
+ }
+ });
};
-},{"..":2,"../src/util/assert.js":17}],26:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18}],28:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2016,25 +2136,24 @@ module.exports = function (liquid) {
});
};
-},{"..":2}],27:[function(require,module,exports){
+},{"..":2}],29:[function(require,module,exports){
'use strict';
module.exports = function (liquid) {
-
- liquid.registerTag('comment', {
- parse: function parse(tagToken, remainTokens) {
- var stream = liquid.parser.parseStream(remainTokens);
- stream.on('token', function (token) {
- if (token.name === 'endcomment') stream.stop();
- }).on('end', function (x) {
- throw new Error('tag ' + tagToken.raw + ' not closed');
- });
- stream.start();
- }
- });
+ liquid.registerTag('comment', {
+ parse: function parse(tagToken, remainTokens) {
+ var stream = liquid.parser.parseStream(remainTokens);
+ stream.on('token', function (token) {
+ if (token.name === 'endcomment') stream.stop();
+ }).on('end', function (x) {
+ throw new Error('tag ' + tagToken.raw + ' not closed');
+ });
+ stream.start();
+ }
+ });
};
-},{}],28:[function(require,module,exports){
+},{}],30:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2082,7 +2201,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17,"any-promise":3}],29:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18,"any-promise":3}],31:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2090,22 +2209,21 @@ var lexical = Liquid.lexical;
var assert = require('../src/util/assert.js');
module.exports = function (liquid) {
-
- liquid.registerTag('decrement', {
- parse: function parse(token) {
- var match = token.args.match(lexical.identifier);
- assert(match, 'illegal identifier ' + token.args);
- this.variable = match[0];
- },
- render: function render(scope, hash) {
- var v = scope.get(this.variable);
- if (typeof v !== 'number') v = 0;
- scope.set(this.variable, v - 1);
- }
- });
+ liquid.registerTag('decrement', {
+ parse: function parse(token) {
+ var match = token.args.match(lexical.identifier);
+ assert(match, 'illegal identifier ' + token.args);
+ this.variable = match[0];
+ },
+ render: function render(scope, hash) {
+ var v = scope.get(this.variable);
+ if (typeof v !== 'number') v = 0;
+ scope.set(this.variable, v - 1);
+ }
+ });
};
-},{"..":2,"../src/util/assert.js":17}],30:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18}],32:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2216,7 +2334,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17,"../src/util/promise.js":20,"../src/util/underscore.js":22}],31:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18,"../src/util/promise.js":21,"../src/util/underscore.js":23}],33:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2267,7 +2385,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2}],32:[function(require,module,exports){
+},{"..":2}],34:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2314,7 +2432,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17}],33:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18}],35:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2322,22 +2440,21 @@ var assert = require('../src/util/assert.js');
var lexical = Liquid.lexical;
module.exports = function (liquid) {
-
- liquid.registerTag('increment', {
- parse: function parse(token) {
- var match = token.args.match(lexical.identifier);
- assert(match, 'illegal identifier ' + token.args);
- this.variable = match[0];
- },
- render: function render(scope, hash) {
- var v = scope.get(this.variable);
- if (typeof v !== 'number') v = 0;
- scope.set(this.variable, v + 1);
- }
- });
+ liquid.registerTag('increment', {
+ parse: function parse(token) {
+ var match = token.args.match(lexical.identifier);
+ assert(match, 'illegal identifier ' + token.args);
+ this.variable = match[0];
+ },
+ render: function render(scope, hash) {
+ var v = scope.get(this.variable);
+ if (typeof v !== 'number') v = 0;
+ scope.set(this.variable, v + 1);
+ }
+ });
};
-},{"..":2,"../src/util/assert.js":17}],34:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18}],36:[function(require,module,exports){
'use strict';
module.exports = function (engine) {
@@ -2357,7 +2474,7 @@ module.exports = function (engine) {
require('./unless.js')(engine);
};
-},{"./assign.js":24,"./capture.js":25,"./case.js":26,"./comment.js":27,"./cycle.js":28,"./decrement.js":29,"./for.js":30,"./if.js":31,"./include.js":32,"./increment.js":33,"./layout.js":35,"./raw.js":36,"./tablerow.js":37,"./unless.js":38}],35:[function(require,module,exports){
+},{"./assign.js":26,"./capture.js":27,"./case.js":28,"./comment.js":29,"./cycle.js":30,"./decrement.js":31,"./for.js":32,"./if.js":33,"./include.js":34,"./increment.js":35,"./layout.js":37,"./raw.js":38,"./tablerow.js":39,"./unless.js":40}],37:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2441,7 +2558,7 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17,"any-promise":3}],36:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18,"any-promise":3}],38:[function(require,module,exports){
'use strict';
var Promise = require('any-promise');
@@ -2470,7 +2587,7 @@ module.exports = function (liquid) {
});
};
-},{"any-promise":3}],37:[function(require,module,exports){
+},{"any-promise":3}],39:[function(require,module,exports){
'use strict';
var Liquid = require('..');
@@ -2558,40 +2675,40 @@ module.exports = function (liquid) {
});
};
-},{"..":2,"../src/util/assert.js":17,"../src/util/promise.js":20}],38:[function(require,module,exports){
+},{"..":2,"../src/util/assert.js":18,"../src/util/promise.js":21}],40:[function(require,module,exports){
'use strict';
var Liquid = require('..');
module.exports = function (liquid) {
- liquid.registerTag('unless', {
- parse: function parse(tagToken, remainTokens) {
- var _this = this;
+ liquid.registerTag('unless', {
+ parse: function parse(tagToken, remainTokens) {
+ var _this = this;
- this.templates = [];
- this.elseTemplates = [];
- var p,
- stream = liquid.parser.parseStream(remainTokens).on('start', function (x) {
- p = _this.templates;
- _this.cond = tagToken.args;
- }).on('tag:else', function (token) {
- return p = _this.elseTemplates;
- }).on('tag:endunless', function (token) {
- return stream.stop();
- }).on('template', function (tpl) {
- return p.push(tpl);
- }).on('end', function (x) {
- throw new Error('tag ' + tagToken.raw + ' not closed');
- });
+ this.templates = [];
+ this.elseTemplates = [];
+ var p;
+ var stream = liquid.parser.parseStream(remainTokens).on('start', function (x) {
+ p = _this.templates;
+ _this.cond = tagToken.args;
+ }).on('tag:else', function () {
+ return p = _this.elseTemplates;
+ }).on('tag:endunless', function (token) {
+ return stream.stop();
+ }).on('template', function (tpl) {
+ return p.push(tpl);
+ }).on('end', function (x) {
+ throw new Error('tag ' + tagToken.raw + ' not closed');
+ });
- stream.start();
- },
+ stream.start();
+ },
- render: function render(scope, hash) {
- var cond = Liquid.evalExp(this.cond, scope);
- return Liquid.isFalsy(cond) ? liquid.renderer.renderTemplates(this.templates, scope) : liquid.renderer.renderTemplates(this.elseTemplates, scope);
- }
- });
+ render: function render(scope, hash) {
+ var cond = Liquid.evalExp(this.cond, scope);
+ return Liquid.isFalsy(cond) ? liquid.renderer.renderTemplates(this.templates, scope) : liquid.renderer.renderTemplates(this.elseTemplates, scope);
+ }
+ });
};
},{"..":2}]},{},[2])(2)
diff --git a/dist/liquid.min.js b/dist/liquid.min.js
index 0050c291e..0facfb24a 100644
--- a/dist/liquid.min.js
+++ b/dist/liquid.min.js
@@ -1 +1 @@
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Liquid=e()}}(function(){return function e(t,r,n){function s(o,u){if(!r[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var c=new Error("Cannot find module '"+o+"'");throw c.code="MODULE_NOT_FOUND",c}var l=r[o]={exports:{}};t[o][0].call(l.exports,function(e){var r=t[o][1][e];return s(r||e)},l,l.exports,e,t,r,n)}return r[o].exports}for(var i="function"==typeof require&&require,o=0;o|"|'/g,function(e){return f[e]})}function s(e){var t=(e+"").split(".");return t.length>1?t[1].length:0}function i(e){return e+""}function o(e){return function(t,r){var n=function(e,t){return Math.max(s(e),s(t))}(t,r);return e(t,r).toFixed(n)}}function u(e){return c.forOwn(h,function(t,r){return e.registerFilter(r,t)})}var a=e("./src/util/strftime.js"),c=e("./src/util/underscore.js"),l=e("./src/syntax.js").isTruthy,f={"&":"&","<":"<",">":">",'"':""","'":"'"},p={"&":"&","<":"<",">":">",""":'"',"'":"'"},h={abs:function(e){return Math.abs(e)},append:function(e,t){return e+t},capitalize:function(e){return i(e).charAt(0).toUpperCase()+e.slice(1)},ceil:function(e){return Math.ceil(e)},concat:function(e,t){return Array.prototype.concat.call(e,t)},date:function(e,t){var r=e;return"now"===e?r=new Date:c.isString(e)&&(r=new Date(e)),function(e){return e instanceof Date&&!isNaN(e.getTime())}(r)?a(r,t):e},default:function(e,t){return l(e)?e:t},divided_by:function(e,t){return Math.floor(e/t)},downcase:function(e){return e.toLowerCase()},escape:n,escape_once:function(e){return n(function(e){return i(e).replace(/&(amp|lt|gt|#34|#39);/g,function(e){return p[e]})}(e))},first:function(e){return e[0]},floor:function(e){return Math.floor(e)},join:function(e,t){return e.join(t)},last:function(e){return e[e.length-1]},lstrip:function(e){return i(e).replace(/^\s+/,"")},map:function(e,t){return e.map(function(e){return e[t]})},minus:o(function(e,t){return e-t}),modulo:o(function(e,t){return e%t}),newline_to_br:function(e){return e.replace(/\n/g,"
")},plus:o(function(e,t){return Number(e)+Number(t)}),prepend:function(e,t){return t+e},remove:function(e,t){return e.split(t).join("")},remove_first:function(e,t){return e.replace(t,"")},replace:function(e,t,r){return i(e).split(t).join(r)},replace_first:function(e,t,r){return i(e).replace(t,r)},reverse:function(e){return e.reverse()},round:function(e,t){var r=Math.pow(10,t||0);return Math.round(e*r,t)/r},rstrip:function(e){return i(e).replace(/\s+$/,"")},size:function(e){return e.length},slice:function(e,t,r){return e.substr(t,void 0===r?1:r)},sort:function(e,t){return e.sort(t)},split:function(e,t){return i(e).split(t)},strip:function(e){return i(e).trim()},strip_html:function(e){return i(e).replace(/<\/?\s*\w+\s*\/?>/g,"")},strip_newlines:function(e){return i(e).replace(/\n/g,"")},times:function(e,t){return e*t},truncate:function(e,t,r){return e=i(e),r=void 0===r?"...":r,t=t||16,e.length<=t?e:e.substr(0,t-r.length)+r},truncatewords:function(e,t,r){void 0===r&&(r="...");var n=e.split(" "),s=n.slice(0,t).join(" ");return n.length>t&&(s+=r),s},uniq:function(e){var t={};return(e||[]).filter(function(e){return!t.hasOwnProperty(e)&&(t[e]=!0,!0)})},upcase:function(e){return i(e).toUpperCase()},url_encode:encodeURIComponent};u.filters=h,t.exports=u},{"./src/syntax.js":14,"./src/util/strftime.js":21,"./src/util/underscore.js":22}],2:[function(e,t,r){"use strict";function n(e){(e=i.assign({root:["."],cache:!1,extname:"",dynamicPartials:!0,trim_tag_right:!1,trim_tag_left:!1,trim_value_right:!1,trim_value_left:!1,greedy:!0,strict_filters:!1,strict_variables:!1},e)).root=function(e){return Array.isArray(e)?e:i.isString(e)?[e]:[]}(e.root);var t=Object.create(b);return t.init(h(),g(e),e),t}var s=e("./src/scope"),i=e("./src/util/underscore.js"),o=e("./src/util/assert.js"),u=e("./src/tokenizer.js"),a=e("./src/util/fs.js").statFileAsync,c=e("./src/util/fs.js").readFileAsync,l=e("path"),f=e("./src/render.js"),p=e("./src/lexical.js"),h=e("./src/tag.js"),g=e("./src/filter.js"),d=e("./src/parser"),v=e("./src/syntax.js"),m=e("./tags"),y=e("./filters"),j=e("any-promise"),w=e("./src/util/promise.js").anySeries,x=e("./src/util/error.js"),b={init:function(e,t,r){return r.cache&&(this.cache={}),this.options=r,this.tag=e,this.filter=t,this.parser=d(e,t),this.renderer=f(),m(this),y(this),this},parse:function(e,t){var r=u.parse(e,t,this.options);return this.parser.parse(r)},render:function(e,t,r){r=i.assign({},this.options,r);var n=s.factory(t,r);return this.renderer.renderTemplates(e,n)},parseAndRender:function(e,t,r){var n=this;return j.resolve().then(function(){return n.parse(e)}).then(function(e){return n.render(e,t,r)})},renderFile:function(e,t,r){var n=this;return r=i.assign({},r),this.getTemplate(e,r.root).then(function(e){return n.render(e,t,r)})},evalValue:function(e,t){var r=this.parser.parseValue(e.trim());return this.renderer.evalValue(r,t)},registerFilter:function(e,t){return this.filter.register(e,t)},registerTag:function(e,t){return this.tag.register(e,t)},lookup:function(e,t){t=this.options.root.concat(t||[]);var r=(t=i.uniq(t)).map(function(t){return l.resolve(t,e)});return w(r,function(e){return a(e).then(function(){return e})}).catch(function(r){throw r.message=r.code+": Failed to lookup "+e+" in: "+t,r})},getTemplate:function(e,t){var r=this;return l.extname(e)||(e+=this.options.extname),this.lookup(e,t).then(function(e){if(r.options.cache){var t=r.cache[e];return t?j.resolve(t):c(e).then(function(e){return r.parse(e)}).then(function(t){return r.cache[e]=t})}return c(e).then(function(t){return r.parse(t,e)})})},express:function(e){e=e||{};var t=this;return function(r,n,s){o(Array.isArray(this.root)||i.isString(this.root),"illegal views root, are you using express.js?"),e.root=this.root,t.renderFile(r,n,e).then(function(e){return s(null,e)}).catch(function(e){return s(e)})}}};n.lexical=p,n.isTruthy=v.isTruthy,n.isFalsy=v.isFalsy,n.evalExp=v.evalExp,n.evalValue=v.evalValue,n.Types={ParseError:x.ParseError,TokenizationEroor:x.TokenizationError,RenderBreakError:x.RenderBreakError,AssertionError:x.AssertionError},t.exports=n},{"./filters":1,"./src/filter.js":8,"./src/lexical.js":9,"./src/parser":11,"./src/render.js":12,"./src/scope":13,"./src/syntax.js":14,"./src/tag.js":15,"./src/tokenizer.js":16,"./src/util/assert.js":17,"./src/util/error.js":18,"./src/util/fs.js":19,"./src/util/promise.js":20,"./src/util/underscore.js":22,"./tags":34,"any-promise":3,path:7}],3:[function(e,t,r){"use strict";t.exports=e("./register")().Promise},{"./register":5}],4:[function(e,t,r){"use strict";var n="@@any-promise/REGISTRATION",s=null;t.exports=function(e,t){return function(r,i){r=r||null;var o=!1!==(i=i||{}).global;if(null===s&&o&&(s=e[n]||null),null!==s&&null!==r&&s.implementation!==r)throw new Error('any-promise already defined as "'+s.implementation+'". You can only register an implementation before the first call to require("any-promise") and an implementation cannot be changed');return null===s&&(s=null!==r&&void 0!==i.Promise?{Promise:i.Promise,implementation:r}:t(r),o&&(e[n]=s)),s}}},{}],5:[function(e,t,r){"use strict";t.exports=e("./loader")(window,function(){if(void 0===window.Promise)throw new Error("any-promise browser requires a polyfill or explicit registration e.g: require('any-promise/register/bluebird')");return{Promise:window.Promise,implementation:"window.Promise"}})},{"./loader":4}],6:[function(e,t,r){"use strict"},{}],7:[function(e,t,r){"use strict"},{}],8:[function(e,t,r){"use strict";var n=e("./lexical.js"),s=e("./syntax.js"),i=e("./util/assert.js"),o=e("./util/underscore.js"),u=new RegExp(""+n.value.source,"g");t.exports=function(e){e=o.assign({},e);var t={},r={render:function(e,t){var r=this.args.map(function(e){return s.evalValue(e,t)});return r.unshift(e),this.filter.apply(null,r)},parse:function(r){var s=n.filterLine.exec(r);i(s,"illegal filter: "+r);var o=s[1],a=s[2]||"",c=t[o];if("function"!=typeof c){if(e.strict_filters)throw new TypeError("undefined filter: "+o);return this.name=o,this.filter=function(e){return e},this.args=[],this}for(var l=[];s=u.exec(a.trim());){var f=s[0];new RegExp(f+"\\s*:","g").test(s.input)?l.push("'"+f+"'"):l.push(f)}return this.name=o,this.filter=c,this.args=l,this}};return{construct:function(e){return Object.create(r).parse(e)},register:function(e,r){t[e]=r},clear:function(){t={}}}}},{"./lexical.js":9,"./syntax.js":14,"./util/assert.js":17,"./util/underscore.js":22}],9:[function(e,t,r){"use strict";var n=new RegExp(/'[^']*'/.source+"|"+/"[^"]*"/.source),s=new RegExp("(?:"+n.source+"|[^'\"])*"),i=/-?\d+/,o=/-?\d+\.?\d*|\.?\d+/,u=/true|false/,a=/[\w-]+/,c=new RegExp("\\[(?:"+n.source+"|[\\w-\\.]+)\\]"),l=new RegExp("(?:"+n.source+"|"+u.source+"|"+o.source+")"),f=new RegExp(a.source+"(?:\\."+a.source+"|"+c.source+")*"),p=new RegExp("(?:"+f.source+"|"+o.source+")"),h=new RegExp("\\("+p.source+"\\.\\."+p.source+"\\)"),g=new RegExp("\\(("+p.source+")\\.\\.("+p.source+")\\)"),d=new RegExp("(?:"+f.source+"|"+l.source+"|"+h.source+")"),v=new RegExp("(?:"+a.source+")\\s*:\\s*(?:"+d.source+")"),m=new RegExp("("+a.source+")\\s*:\\s*("+d.source+")","g"),y=new RegExp("^\\s*("+a.source+")\\s*([\\s\\S]*)\\s*$"),j=new RegExp("^"+l.source+"$","i"),w=new RegExp("^"+f.source+"$"),x=new RegExp("^"+o.source+"$"),b=new RegExp("^"+u.source+"$","i"),E=new RegExp("^"+n.source+"$"),T=new RegExp("^"+g.source+"$"),k=new RegExp("^"+i.source+"$"),S=new RegExp("(?:"+a.source+"\\s*:\\s*)?"+d.source),R=new RegExp(S.source+"(\\s*,\\s*"+S.source+")*"),O=new RegExp(a.source+"(?:\\s*:\\s*"+R.source+")?","g"),_=new RegExp("("+a.source+")(?:\\s*:\\s*("+R.source+"))?"),M=new RegExp("^"+_.source+"$");t.exports={quoted:n,number:o,bool:u,literal:l,filter:O,integer:i,hash:v,hashCapture:m,range:h,rangeCapture:g,identifier:a,value:d,quoteBalanced:s,operators:[/\s+or\s+/,/\s+and\s+/,/==|!=|<=|>=|<|>|\s+contains\s+/],quotedLine:E,numberLine:x,boolLine:b,rangeLine:T,literalLine:j,filterLine:M,tagLine:y,isLiteral:function(e){return j.test(e)},isVariable:function(e){return w.test(e)},parseLiteral:function(e){var t=e.match(x);if(t)return Number(e);if(t=e.match(b))return"true"===e.toLowerCase();if(t=e.match(E))return e.slice(1,-1);throw new TypeError("cannot parse '"+e+"' as literal")},isRange:function(e){return T.test(e)},matchValue:function(e){return d.exec(e)},isInteger:function(e){return k.test(e)}}},{}],10:[function(e,t,r){"use strict";t.exports=function(e){return{"==":function(e,t){return e===t},"!=":function(e,t){return e!==t},">":function(e,t){return null!==e&&null!==t&&e>t},"<":function(e,t){return null!==e&&null!==t&&e=":function(e,t){return null!==e&&null!==t&&e>=t},"<=":function(e,t){return null!==e&&null!==t&&e<=t},contains:function(e,t){return!!e&&("function"==typeof e.indexOf&&e.indexOf(t)>-1)},and:function(t,r){return e(t)&&e(r)},or:function(t,r){return e(t)||e(r)}}}},{}],11:[function(e,t,r){"use strict";var n=e("./lexical.js"),s=e("./util/error.js").ParseError,i=e("./util/assert.js");t.exports=function(e,t){function r(e,t){try{var r=null;return r="tag"===e.type?o(e,t):"value"===e.type?u(e.value):e,r.token=e,r}catch(t){throw new s(t,e)}}function o(t,r){return"continue"===t.name||"break"===t.name?t:e.construct(t,r)}function u(e){var r=n.matchValue(e);i(r,"illegal value string: "+e);var s=r[0];e=e.substr(r.index+r[0].length);for(var o=[];r=n.filter.exec(e);)o.push([r[0].trim()]);return{type:"value",initial:s,filters:o.map(function(e){return t.construct(e)})}}var a={init:function(e){return this.tokens=e,this.handlers={},this},on:function(e,t){return this.handlers[e]=t,this},trigger:function(e,t){var r=this.handlers[e];if("function"==typeof r)return r(t),!0},start:function(){this.trigger("start");for(var e;!this.stopRequested&&(e=this.tokens.shift());)if(!(this.trigger("token",e)||"tag"===e.type&&this.trigger("tag:"+e.name,e))){var t=r(e,this.tokens);this.trigger("template",t)}return this.stopRequested||this.trigger("end"),this},stop:function(){return this.stopRequested=!0,this}};return{parse:function(e){for(var t,n=[];t=e.shift();)n.push(r(t,e));return n},parseTag:o,parseStream:function(e){return Object.create(a).init(e)},parseValue:u}}},{"./lexical.js":9,"./util/assert.js":17,"./util/error.js":18}],12:[function(e,t,r){"use strict";var n=e("./syntax.js"),s=e("any-promise"),i=e("./util/promise.js").mapSeries,o=e("./util/error.js").RenderBreakError,u=e("./util/error.js").RenderError,a=e("./util/assert.js"),c={renderTemplates:function(e,t){function r(e){var r=this;return"tag"===e.type?this.renderTag(e,t).then(function(e){return void 0===e?"":e}):"value"===e.type?s.resolve().then(function(){return r.evalValue(e,t)}).then(function(e){return void 0===e?"":function(e){return"string"==typeof e?e:JSON.stringify(e)}(e)}):s.resolve(e.value)}var n=this;a(t,"unable to evalTemplates: scope undefined");var c="";return i(e,function(e){return r.call(n,e).then(function(e){return c+=e}).catch(function(t){if(t instanceof o)throw t.resolvedHTML=c,t;throw new u(t,e)})}).then(function(){return c})},renderTag:function(e,t){return"continue"===e.name?s.reject(new o("continue")):"break"===e.name?s.reject(new o("break")):e.render(t)},evalValue:function(e,t){return a(t,"unable to evalValue: scope undefined"),e.filters.reduce(function(e,r){return r.render(e,t)},n.evalExp(e.initial,t))}};t.exports=function(){return Object.create(c)}},{"./syntax.js":14,"./util/assert.js":17,"./util/error.js":18,"./util/promise.js":20,"any-promise":3}],13:[function(e,t,r){"use strict";var n=e("./util/underscore.js"),s=e("./lexical.js"),i=e("./util/assert.js"),o={getAll:function(){for(var e={},t=this.scopes.length-1;t>=0;t--)n.assign(e,this.scopes[t]);return e},get:function(e){try{return this.getPropertyByPath(this.scopes,e)}catch(e){if(!/undefined variable/.test(e.message)||this.opts.strict_variables)throw e}},set:function(e,t){return function(e,t,r){for(var s=(t+"").replace(/\[/g,".").replace(/\]/g,"").split("."),i=0;i=0&&!(e in this.scopes[t]);)t--;return t<0&&(t=this.scopes.length-1),this.scopes[t]},unshift:function(e){return i(e,"trying to push "+e+" into scopes"),this.scopes.unshift(e)},shift:function(){return this.scopes.shift()},getPropertyByPath:function(e,t){var r=this.propertyAccessSeq(t+"");if(!r.length)throw new TypeError("undefined variable: "+t);var s=function(e,t){for(var r=t.length-1;r>-1;r--){var n=t[r];if(n.hasOwnProperty(e))return n[e]}throw new TypeError("undefined variable: "+e)}(r.shift(),e);return r.reduce(function(e,t){if(n.isNil(e))throw new TypeError("undefined variable: "+t);return function(e,t){return"size"===e&&(n.isArray(t)||n.isString(t))?t.length:t[e]}(t,e)},s)},propertyAccessSeq:function(e){function t(){o.length&&n.push(o),o=""}for(var r,n=[],o="",u=0;uh&&g.push(a(h,c.index)),g.push(c[1]?function(r,i,o){var u=i.match(n.tagLine),a={type:"tag",indent:f,line:p.get(o),trim_left:"{%-"===r.slice(0,3),trim_right:"-%}"===r.slice(-3),raw:r,value:i,input:e,file:t};if(!u)throw new s("illegal tag syntax",a);return a.name=u[1],a.args=u[2],a}(c[1],c[2].trim(),c.index):function(r,n,s){return{type:"value",line:p.get(s),trim_left:"{{-"===r.slice(0,3),trim_right:"-}}"===r.slice(-3),raw:r,value:n,input:e,file:t}}(c[3],c[4].trim(),c.index));return e.length>h&&g.push(a(h,e.length)),o(g,r),g},r.whiteSpaceCtrl=o},{"./lexical.js":9,"./util/assert.js":17,"./util/error.js":18,"./util/underscore.js":22,"./whitespace-ctrl.js":23}],17:[function(e,t,r){"use strict";var n=e("./error.js").AssertionError;t.exports=function(e,t){if(!e)throw t=t||"expect "+e+" to be true",new n(t)}},{"./error.js":18}],18:[function(e,t,r){"use strict";function n(){this.name=this.constructor.name,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}function s(e,t){n.call(this),this.input=t.input,this.line=t.line,this.file=t.file;var r=function(e,t){var r=e.split("\n"),n=Math.max(t-2,1),s=Math.min(t+3,r.length);return l.range(n,s+1).map(function(e){return[e===t?">> ":" ",function(e,t){var r=(t+"").length,n=e+"";return Array(r-n.length).join(" ")+n}(e,s),"| ",r[e-1]].join("")}).join("\n")}(t.input,t.line);this.message=function(e,t){e=e||"",t.file&&(e+=", file:"+t.file);t.line&&(e+=", line:"+t.line);return e}(e.message,t),this.stack=r+"\n"+(this.stack||this.message)+(e.stack?"\nFrom "+e.stack:"")}function i(e,t){s.call(this,{message:e},t)}function o(e,t){l.assign(this,e),this.originalError=e,s.call(this,e,t)}function u(e,t){if(e instanceof u)return e;l.assign(this,e),this.originalError=e,s.call(this,e,t.token)}function a(e){n.call(this),this.message=e+""}function c(e){n.call(this),this.message=e+""}var l=e("./underscore.js");(i.prototype=Object.create(Error.prototype)).constructor=i,(o.prototype=Object.create(Error.prototype)).constructor=o,(u.prototype=Object.create(Error.prototype)).constructor=u,(a.prototype=Object.create(Error.prototype)).constructor=a,(c.prototype=Object.create(Error.prototype)).constructor=c,t.exports={TokenizationError:i,ParseError:o,RenderBreakError:a,AssertionError:c,RenderError:u}},{"./underscore.js":22}],19:[function(e,t,r){"use strict";var n=e("fs");t.exports={readFileAsync:function(e){return new Promise(function(t,r){n.readFile(e,"utf8",function(e,n){e?r(e):t(n)})})},statFileAsync:function(e){return new Promise(function(t,r){n.stat(e,function(e,n){return e?r(e):t(n)})})}}},{fs:6}],20:[function(e,t,r){"use strict";var n=e("any-promise");r.anySeries=function(e,t){var r=n.reject(new Error("init"));return e.forEach(function(n,s){r=r.catch(function(r){return t(n,s,e)})}),r},r.mapSeries=function(e,t){var r=n.resolve("init"),s=[];return e.forEach(function(n,i){r=r.then(function(){return t(n,i,e)}).then(function(e){return s.push(e)})}),r.then(function(){return s})}},{"any-promise":3}],21:[function(e,t,r){"use strict";var n=["January","February","March","April","May","June","July","August","September","October","November","December"],s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],i=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],o=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u={1:"st",2:"nd",3:"rd",default:"th"},a={daysInMonth:function(e){return[31,a.isLeapYear(e)?29:28,31,30,31,30,31,31,30,31,30,31]},getDayOfYear:function(e){for(var t=0,r=0;r0;)n=r+n;return n}},l={a:function(e){return o[e.getDay()]},A:function(e){return i[e.getDay()]},b:function(e){return s[e.getMonth()]},B:function(e){return n[e.getMonth()]},c:function(e){return e.toLocaleString()},C:function(e){return a.century(e)},d:function(e){return c.pad(e.getDate(),2)},e:function(e){return c.pad(e.getDate(),2," ")},H:function(e){return c.pad(e.getHours(),2)},I:function(e){return c.pad(e.getHours()%12||12,2)},j:function(e){return c.pad(a.getDayOfYear(e),3)},k:function(e){return c.pad(e.getHours(),2," ")},l:function(e){return c.pad(e.getHours()%12||12,2," ")},L:function(e){return c.pad(e.getMilliseconds(),3)},m:function(e){return c.pad(e.getMonth()+1,2)},M:function(e){return c.pad(e.getMinutes(),2)},p:function(e){return e.getHours()<12?"AM":"PM"},P:function(e){return e.getHours()<12?"am":"pm"},q:function(e){return a.getSuffix(e)},s:function(e){return Math.round(e.valueOf()/1e3)},S:function(e){return c.pad(e.getSeconds(),2)},u:function(e){return e.getDay()||7},U:function(e){return a.getWeekOfYear(e,0)},w:function(e){return e.getDay()},W:function(e){return a.getWeekOfYear(e,1)},x:function(e){return e.toLocaleDateString()},X:function(e){return e.toLocaleTimeString()},y:function(e){return e.getFullYear().toString().substring(2,4)},Y:function(e){return e.getFullYear()},z:function(e){var t=e.getTimezoneOffset()/60*100;return(t>0?"-":"+")+c.pad(Math.abs(t),4)},"%":function(){return"%"}};l.h=l.b,l.N=l.L;t.exports=function(e,t){for(var r="",n=t;;){var s=/%./g,i=s.exec(n);if(!i)return r+n;r+=n.slice(0,s.lastIndex-2),n=n.slice(s.lastIndex);var o=i[0].charAt(1),u=l[o];r+=u?u.call(this,e):"%"+o}}},{}],22:[function(e,t,r){"use strict";function n(e,t){e=e||{};for(var r in e)if(e.hasOwnProperty(r)&&!1===t(e[r],r,e))break;return e}function s(e){var t=void 0===e?"undefined":i(e);return null!=e&&("object"===t||"function"===t)}var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=Object.prototype.toString;r.isString=function(e){return e instanceof String||"string"==typeof e},r.isObject=s,r.isArray=function(e){return"[object Array]"===o.call(e)},r.isNil=function(e){return null===e||void 0===e},r.isError=function(e){return"Error"===Object.prototype.toString.call(e).substr(-6,5)||"string"==typeof e.message&&"string"==typeof e.name},r.range=function(e,t,r){1===arguments.length&&(t=e,e=0),r=r||1;for(var n=[],s=e;s0?a=[a]:o.isObject(a)&&(a=Object.keys(a).map(function(e){return[e,a[e]]}))),!Array.isArray(a)||!a.length)return e.renderer.renderTemplates(this.elseTemplates,t);var c=a.length,l=r.offset||0,f=void 0===r.limit?a.length:r.limit;a=a.slice(l,l+f),this.reversed&&a.reverse();var p=a.map(function(e,t){var r={};return r[s.variable]=e,r.forloop={first:0===t,index:t+1,index0:t,last:t===c-1,length:c,rindex:c-t,rindex0:c-t-1},r}),h="";return i(p,function(r){return Promise.resolve().then(function(){return t.push(r)}).then(function(){return e.renderer.renderTemplates(s.templates,t)}).then(function(e){return h+=e}).catch(function(e){if(!(e instanceof u&&(h+=e.resolvedHTML,"continue"===e.message)))throw e}).then(function(){return t.pop()})}).catch(function(e){if(!(e instanceof u&&"break"===e.message))throw e}).then(function(){return h})}})}},{"..":2,"../src/util/assert.js":17,"../src/util/promise.js":20,"../src/util/underscore.js":22}],31:[function(e,t,r){"use strict";var n=e("..");t.exports=function(e){e.registerTag("if",{parse:function(t,r){var n=this;this.branches=[],this.elseTemplates=[];var s,i=e.parser.parseStream(r).on("start",function(){return n.branches.push({cond:t.args,templates:s=[]})}).on("tag:elsif",function(e){n.branches.push({cond:e.args,templates:s=[]})}).on("tag:else",function(){return s=n.elseTemplates}).on("tag:endif",function(e){return i.stop()}).on("template",function(e){return s.push(e)}).on("end",function(e){throw new Error("tag "+t.raw+" not closed")});i.start()},render:function(t,r){for(var s=0;s",l=r.offset||0,f=void 0===r.limit?a.length:r.limit,p=r.cols;if(!p)throw new Error("illegal cols: "+p);var h=[];return(a=a.slice(l,l+f)).some(function(e,t){var r={};r[u.variable]=e,h.push(r)}),s(h,function(r,n){return i=Math.floor(n/p)+1,1===(o=n%p+1)&&(1!==i&&(c+=""),c+=''),c+='| ',t.push(r),e.renderer.renderTemplates(u.templates,t).then(function(e){return t.pop(r),c+=e,c+=" | "})}).then(function(){return i>0&&(c+="
"),c+=""})}})}},{"..":2,"../src/util/assert.js":17,"../src/util/promise.js":20}],38:[function(e,t,r){"use strict";var n=e("..");t.exports=function(e){e.registerTag("unless",{parse:function(t,r){var n=this;this.templates=[],this.elseTemplates=[];var s,i=e.parser.parseStream(r).on("start",function(e){s=n.templates,n.cond=t.args}).on("tag:else",function(e){return s=n.elseTemplates}).on("tag:endunless",function(e){return i.stop()}).on("template",function(e){return s.push(e)}).on("end",function(e){throw new Error("tag "+t.raw+" not closed")});i.start()},render:function(t,r){var s=n.evalExp(this.cond,t);return n.isFalsy(s)?e.renderer.renderTemplates(this.templates,t):e.renderer.renderTemplates(this.elseTemplates,t)}})}},{"..":2}]},{},[2])(2)});
\ No newline at end of file
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Liquid=e()}}(function(){return function e(t,r,n){function s(o,u){if(!r[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var c=new Error("Cannot find module '"+o+"'");throw c.code="MODULE_NOT_FOUND",c}var l=r[o]={exports:{}};t[o][0].call(l.exports,function(e){var r=t[o][1][e];return s(r||e)},l,l.exports,e,t,r,n)}return r[o].exports}for(var i="function"==typeof require&&require,o=0;o":">",'"':""","'":"'"},u={"&":"&","<":"<",">":">",""":'"',"'":"'"},a={abs:function(e){return Math.abs(e)},append:function(e,t){return e+t},capitalize:function(e){return f(e).charAt(0).toUpperCase()+e.slice(1)},ceil:function(e){return Math.ceil(e)},concat:function(e,t){return Array.prototype.concat.call(e,t)},date:function(e,t){var r,i=e;return"now"===e?i=new Date:s.isString(e)&&(i=new Date(e)),(r=i)instanceof Date&&!isNaN(r.getTime())?n(i,t):e},default:function(e,t){return i(e)?e:t},divided_by:function(e,t){return Math.floor(e/t)},downcase:function(e){return e.toLowerCase()},escape:c,escape_once:function(e){return c(f(e).replace(/&(amp|lt|gt|#34|#39);/g,function(e){return u[e]}))},first:function(e){return e[0]},floor:function(e){return Math.floor(e)},join:function(e,t){return e.join(t)},last:function(e){return e[e.length-1]},lstrip:function(e){return f(e).replace(/^\s+/,"")},map:function(e,t){return e.map(function(e){return e[t]})},minus:p(function(e,t){return e-t}),modulo:p(function(e,t){return e%t}),newline_to_br:function(e){return e.replace(/\n/g,"
")},plus:p(function(e,t){return Number(e)+Number(t)}),prepend:function(e,t){return t+e},remove:function(e,t){return e.split(t).join("")},remove_first:function(e,t){return e.replace(t,"")},replace:function(e,t,r){return f(e).split(t).join(r)},replace_first:function(e,t,r){return f(e).replace(t,r)},reverse:function(e){return e.reverse()},round:function(e,t){var r=Math.pow(10,t||0);return Math.round(e*r,t)/r},rstrip:function(e){return f(e).replace(/\s+$/,"")},size:function(e){return e.length},slice:function(e,t,r){return e.substr(t,void 0===r?1:r)},sort:function(e,t){return e.sort(t)},split:function(e,t){return f(e).split(t)},strip:function(e){return f(e).trim()},strip_html:function(e){return f(e).replace(/<\/?\s*\w+\s*\/?>/g,"")},strip_newlines:function(e){return f(e).replace(/\n/g,"")},times:function(e,t){return e*t},truncate:function(e,t,r){return e=f(e),r=void 0===r?"...":r,t=t||16,e.length<=t?e:e.substr(0,t-r.length)+r},truncatewords:function(e,t,r){void 0===r&&(r="...");var n=e.split(" "),s=n.slice(0,t).join(" ");return n.length>t&&(s+=r),s},uniq:function(e){var t={};return(e||[]).filter(function(e){return!t.hasOwnProperty(e)&&(t[e]=!0,!0)})},upcase:function(e){return f(e).toUpperCase()},url_encode:encodeURIComponent};function c(e){return f(e).replace(/&|<|>|"|'/g,function(e){return o[e]})}function l(e){var t=(e+"").split(".");return t.length>1?t[1].length:0}function f(e){return e+""}function p(e){return function(t,r){var n,s,i=(n=t,s=r,Math.max(l(n),l(s)));return e(t,r).toFixed(i)}}function h(e){return s.forOwn(a,function(t,r){return e.registerFilter(r,t)})}h.filters=a,t.exports=h},{"./src/syntax.js":15,"./src/util/strftime.js":22,"./src/util/underscore.js":23}],2:[function(e,t,r){"use strict";var n=e("./src/scope"),s=e("./src/util/underscore.js"),i=e("./src/util/assert.js"),o=e("./src/tokenizer.js"),u=e("./src/util/fs.js").statFileAsync,a=e("./src/util/fs.js").readFileAsync,c=e("path"),l=e("./src/util/url.js"),f=e("./src/render.js"),p=e("./src/lexical.js"),h=e("./src/tag.js"),g=e("./src/filter.js"),d=e("./src/parser"),v=e("./src/syntax.js"),m=e("./tags"),y=e("./filters"),j=e("any-promise"),w=e("./src/util/promise.js").anySeries,x=e("./src/util/error.js"),b={init:function(e,t,r){return r.cache&&(this.cache={}),this.options=r,this.tag=e,this.filter=t,this.parser=d(e,t),this.renderer=f(),m(this),y(this),this},parse:function(e,t){var r=o.parse(e,t,this.options);return this.parser.parse(r)},render:function(e,t,r){r=s.assign({},this.options,r);var i=n.factory(t,r);return this.renderer.renderTemplates(e,i)},parseAndRender:function(e,t,r){var n=this;return j.resolve().then(function(){return n.parse(e)}).then(function(e){return n.render(e,t,r)})},renderFile:function(e,t,r){var n=this;return r=s.assign({},r),this.getTemplate(e,r.root).then(function(e){return n.render(e,t,r)})},evalValue:function(e,t){var r=this.parser.parseValue(e.trim());return this.renderer.evalValue(r,t)},registerFilter:function(e,t){return this.filter.register(e,t)},registerTag:function(e,t){return this.tag.register(e,t)},lookup:function(e,t){t=this.options.root.concat(t||[]);var r=(t=s.uniq(t)).map(function(t){return c.resolve(t,e)});return w(r,function(e){return u(e).then(function(){return e})}).catch(function(r){throw r.message=r.code+": Failed to lookup "+e+" in: "+t,r})},getTemplate:function(e,t){return"undefined"==typeof XMLHttpRequest?this.getTemplateFromFile(e,t):this.getTemplateFromUrl(e,t)},getTemplateFromFile:function(e,t){var r=this;return c.extname(e)||(e+=this.options.extname),this.lookup(e,t).then(function(e){if(r.options.cache){var t=r.cache[e];return t?j.resolve(t):a(e).then(function(e){return r.parse(e)}).then(function(t){return r.cache[e]=t})}return a(e).then(function(t){return r.parse(t,e)})})},getTemplateFromUrl:function(e,t){var r,n=this;if(l.valid(e)?r=e:(l.extname(e)||(e+=this.options.extname),r=l.resolve(t||this.options.root,e)),this.options.cache){var s=this.cache[e];if(s)return j.resolve(s)}return new j(function(t,s){var i=new XMLHttpRequest;i.onload=function(){if(i.status>=200&&i.status<300){var r=n.parse(i.responseText);n.options.cache&&(n.cache[e]=r),t(r)}else s(new Error(i.statusText))},i.onerror=function(){s(new Error(i.statusText))},i.open("GET",r),i.send()})},express:function(e){e=e||{};var t=this;return function(r,n,o){i(Array.isArray(this.root)||s.isString(this.root),"illegal views root, are you using express.js?"),e.root=this.root,t.renderFile(r,n,e).then(function(e){return o(null,e)}).catch(function(e){return o(e)})}}};function E(e){var t;(e=s.assign({root:["."],cache:!1,extname:"",dynamicPartials:!0,trim_tag_right:!1,trim_tag_left:!1,trim_value_right:!1,trim_value_left:!1,greedy:!0,strict_filters:!1,strict_variables:!1},e)).root=(t=e.root,Array.isArray(t)?t:s.isString(t)?[t]:[]);var r=Object.create(b);return r.init(h(),g(e),e),r}E.lexical=p,E.isTruthy=v.isTruthy,E.isFalsy=v.isFalsy,E.evalExp=v.evalExp,E.evalValue=v.evalValue,E.Types={ParseError:x.ParseError,TokenizationEroor:x.TokenizationError,RenderBreakError:x.RenderBreakError,AssertionError:x.AssertionError},t.exports=E},{"./filters":1,"./src/filter.js":9,"./src/lexical.js":10,"./src/parser":12,"./src/render.js":13,"./src/scope":14,"./src/syntax.js":15,"./src/tag.js":16,"./src/tokenizer.js":17,"./src/util/assert.js":18,"./src/util/error.js":19,"./src/util/fs.js":20,"./src/util/promise.js":21,"./src/util/underscore.js":23,"./src/util/url.js":24,"./tags":36,"any-promise":3,path:7}],3:[function(e,t,r){"use strict";t.exports=e("./register")().Promise},{"./register":5}],4:[function(e,t,r){"use strict";var n="@@any-promise/REGISTRATION",s=null;t.exports=function(e,t){return function(r,i){r=r||null;var o=!1!==(i=i||{}).global;if(null===s&&o&&(s=e[n]||null),null!==s&&null!==r&&s.implementation!==r)throw new Error('any-promise already defined as "'+s.implementation+'". You can only register an implementation before the first call to require("any-promise") and an implementation cannot be changed');return null===s&&(s=null!==r&&void 0!==i.Promise?{Promise:i.Promise,implementation:r}:t(r),o&&(e[n]=s)),s}}},{}],5:[function(e,t,r){"use strict";t.exports=e("./loader")(window,function(){if(void 0===window.Promise)throw new Error("any-promise browser requires a polyfill or explicit registration e.g: require('any-promise/register/bluebird')");return{Promise:window.Promise,implementation:"window.Promise"}})},{"./loader":4}],6:[function(e,t,r){"use strict"},{}],7:[function(e,t,r){"use strict"},{}],8:[function(e,t,r){"use strict";var n,s,i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};n=void 0,s=function(){return function(){var e=arguments.length;if(0===e)throw new Error("resolveUrl requires at least one argument; got none.");var t=document.createElement("base");if(t.href=arguments[0],1===e)return t.href;var r=document.getElementsByTagName("head")[0];r.insertBefore(t,r.firstChild);for(var n,s=document.createElement("a"),i=1;i=|<|>|\s+contains\s+/],quotedLine:E,numberLine:x,boolLine:b,rangeLine:T,literalLine:j,filterLine:_,tagLine:y,isLiteral:function(e){return j.test(e)},isVariable:function(e){return w.test(e)},parseLiteral:function(e){var t=e.match(x);if(t)return Number(e);if(t=e.match(b))return"true"===e.toLowerCase();if(t=e.match(E))return e.slice(1,-1);throw new TypeError("cannot parse '"+e+"' as literal")},isRange:function(e){return T.test(e)},matchValue:function(e){return d.exec(e)},isInteger:function(e){return k.test(e)}}},{}],11:[function(e,t,r){"use strict";t.exports=function(e){return{"==":function(e,t){return e===t},"!=":function(e,t){return e!==t},">":function(e,t){return null!==e&&null!==t&&e>t},"<":function(e,t){return null!==e&&null!==t&&e=":function(e,t){return null!==e&&null!==t&&e>=t},"<=":function(e,t){return null!==e&&null!==t&&e<=t},contains:function(e,t){return!!e&&("function"==typeof e.indexOf&&e.indexOf(t)>-1)},and:function(t,r){return e(t)&&e(r)},or:function(t,r){return e(t)||e(r)}}}},{}],12:[function(e,t,r){"use strict";var n=e("./lexical.js"),s=e("./util/error.js").ParseError,i=e("./util/assert.js");t.exports=function(e,t){var r={init:function(e){return this.tokens=e,this.handlers={},this},on:function(e,t){return this.handlers[e]=t,this},trigger:function(e,t){var r=this.handlers[e];if("function"==typeof r)return r(t),!0},start:function(){var e;for(this.trigger("start");!this.stopRequested&&(e=this.tokens.shift());)if(!(this.trigger("token",e)||"tag"===e.type&&this.trigger("tag:"+e.name,e))){var t=o(e,this.tokens);this.trigger("template",t)}return this.stopRequested||this.trigger("end"),this},stop:function(){return this.stopRequested=!0,this}};function o(e,t){try{var r=null;return(r="tag"===e.type?u(e,t):"value"===e.type?a(e.value):e).token=e,r}catch(t){throw new s(t,e)}}function u(t,r){return"continue"===t.name||"break"===t.name?t:e.construct(t,r)}function a(e){var r=n.matchValue(e);i(r,"illegal value string: "+e);var s=r[0];e=e.substr(r.index+r[0].length);for(var o=[];r=n.filter.exec(e);)o.push([r[0].trim()]);return{type:"value",initial:s,filters:o.map(function(e){return t.construct(e)})}}return{parse:function(e){for(var t,r=[];t=e.shift();)r.push(o(t,e));return r},parseTag:u,parseStream:function(e){return Object.create(r).init(e)},parseValue:a}}},{"./lexical.js":10,"./util/assert.js":18,"./util/error.js":19}],13:[function(e,t,r){"use strict";var n=e("./syntax.js"),s=e("any-promise"),i=e("./util/promise.js").mapSeries,o=e("./util/error.js").RenderBreakError,u=e("./util/error.js").RenderError,a=e("./util/assert.js"),c={renderTemplates:function(e,t){var r=this;a(t,"unable to evalTemplates: scope undefined");var n="";return i(e,function(e){return function(e){var r=this;return"tag"===e.type?this.renderTag(e,t).then(function(e){return void 0===e?"":e}):"value"===e.type?s.resolve().then(function(){return r.evalValue(e,t)}).then(function(e){return void 0===e?"":"string"==typeof(t=e)?t:JSON.stringify(t);var t}):s.resolve(e.value)}.call(r,e).then(function(e){return n+=e}).catch(function(t){if(t instanceof o)throw t.resolvedHTML=n,t;throw new u(t,e)})}).then(function(){return n})},renderTag:function(e,t){return"continue"===e.name?s.reject(new o("continue")):"break"===e.name?s.reject(new o("break")):e.render(t)},evalValue:function(e,t){return a(t,"unable to evalValue: scope undefined"),e.filters.reduce(function(e,r){return r.render(e,t)},n.evalExp(e.initial,t))}};t.exports=function(){return Object.create(c)}},{"./syntax.js":15,"./util/assert.js":18,"./util/error.js":19,"./util/promise.js":21,"any-promise":3}],14:[function(e,t,r){"use strict";var n=e("./util/underscore.js"),s=e("./lexical.js"),i=e("./util/assert.js"),o={getAll:function(){for(var e={},t=this.scopes.length-1;t>=0;t--)n.assign(e,this.scopes[t]);return e},get:function(e){try{return this.getPropertyByPath(this.scopes,e)}catch(e){if(!/undefined variable/.test(e.message)||this.opts.strict_variables)throw e}},set:function(e,t){return function(e,t,r){for(var s=(t+"").replace(/\[/g,".").replace(/\]/g,"").split("."),i=0;i=0&&!(e in this.scopes[t]);)t--;return t<0&&(t=this.scopes.length-1),this.scopes[t]},unshift:function(e){return i(e,"trying to push "+e+" into scopes"),this.scopes.unshift(e)},shift:function(){return this.scopes.shift()},getPropertyByPath:function(e,t){var r=this.propertyAccessSeq(t+"");if(!r.length)throw new TypeError("undefined variable: "+t);var s=function(e,t){for(var r=t.length-1;r>-1;r--){var n=t[r];if(n.hasOwnProperty(e))return n[e]}throw new TypeError("undefined variable: "+e)}(r.shift(),e);return r.reduce(function(e,t){if(n.isNil(e))throw new TypeError("undefined variable: "+t);return s=e,"size"===(r=t)&&(n.isArray(s)||n.isString(s))?s.length:s[r];var r,s},s)},propertyAccessSeq:function(e){for(var t,r=[],n="",o=0;oy&&j.push(x(y,f.index)),j.push(f[1]?w(f[1],f[2].trim(),f.index):(p=f[3],h=f[4].trim(),g=f.index,{type:"value",line:m.get(g),trim_left:"{{-"===p.slice(0,3),trim_right:"-}}"===p.slice(-3),raw:p,value:h,input:e,file:t}));return e.length>y&&j.push(x(y,e.length)),o(j,r),j;function w(r,i,o){var u=i.match(n.tagLine),a={type:"tag",indent:v,line:m.get(o),trim_left:"{%-"===r.slice(0,3),trim_right:"-%}"===r.slice(-3),raw:r,value:i,input:e,file:t};if(!u)throw new s("illegal tag syntax",a);return a.name=u[1],a.args=u[2],a}function x(t,r){var n=e.slice(t,r);return v=i.last(n.split("\n")).length,{type:"html",raw:n,value:n}}},r.whiteSpaceCtrl=o},{"./lexical.js":10,"./util/assert.js":18,"./util/error.js":19,"./util/underscore.js":23,"./whitespace-ctrl.js":25}],18:[function(e,t,r){"use strict";var n=e("./error.js").AssertionError;t.exports=function(e,t){if(!e)throw new n(t=t||"expect "+e+" to be true")}},{"./error.js":19}],19:[function(e,t,r){"use strict";var n=e("./underscore.js");function s(){this.name=this.constructor.name,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}function i(e,t){s.call(this),this.input=t.input,this.line=t.line,this.file=t.file;var r,i,o,u,a,c=(r=t.input,i=t.line,o=r.split("\n"),u=Math.max(i-2,1),a=Math.min(i+3,o.length),n.range(u,a+1).map(function(e){return[e===i?">> ":" ",(t=e,r=a,n=t+"",Array((r+"").length-n.length).join(" ")+n),"| ",o[e-1]].join("");var t,r,n}).join("\n"));this.message=function(e,t){e=e||"",t.file&&(e+=", file:"+t.file);t.line&&(e+=", line:"+t.line);return e}(e.message,t),this.stack=c+"\n"+(this.stack||this.message)+(e.stack?"\nFrom "+e.stack:"")}function o(e,t){i.call(this,{message:e},t)}function u(e,t){n.assign(this,e),this.originalError=e,i.call(this,e,t)}function a(e,t){if(e instanceof a)return e;n.assign(this,e),this.originalError=e,i.call(this,e,t.token)}function c(e){s.call(this),this.message=e+""}function l(e){s.call(this),this.message=e+""}o.prototype=Object.create(Error.prototype),o.prototype.constructor=o,u.prototype=Object.create(Error.prototype),u.prototype.constructor=u,a.prototype=Object.create(Error.prototype),a.prototype.constructor=a,c.prototype=Object.create(Error.prototype),c.prototype.constructor=c,l.prototype=Object.create(Error.prototype),l.prototype.constructor=l,t.exports={TokenizationError:o,ParseError:u,RenderBreakError:c,AssertionError:l,RenderError:a}},{"./underscore.js":23}],20:[function(e,t,r){"use strict";var n=e("fs");t.exports={readFileAsync:function(e){return new Promise(function(t,r){n.readFile(e,"utf8",function(e,n){e?r(e):t(n)})})},statFileAsync:function(e){return new Promise(function(t,r){n.stat(e,function(e,n){return e?r(e):t(n)})})}}},{fs:6}],21:[function(e,t,r){"use strict";var n=e("any-promise");r.anySeries=function(e,t){var r=n.reject(new Error("init"));return e.forEach(function(n,s){r=r.catch(function(r){return t(n,s,e)})}),r},r.mapSeries=function(e,t){var r=n.resolve("init"),s=[];return e.forEach(function(n,i){r=r.then(function(){return t(n,i,e)}).then(function(e){return s.push(e)})}),r.then(function(){return s})}},{"any-promise":3}],22:[function(e,t,r){"use strict";var n=["January","February","March","April","May","June","July","August","September","October","November","December"],s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],i=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],o=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u={1:"st",2:"nd",3:"rd",default:"th"},a={daysInMonth:function(e){return[31,a.isLeapYear(e)?29:28,31,30,31,30,31,31,30,31,30,31]},getDayOfYear:function(e){for(var t=0,r=0;r0;)n=r+n;return n}},l={a:function(e){return o[e.getDay()]},A:function(e){return i[e.getDay()]},b:function(e){return s[e.getMonth()]},B:function(e){return n[e.getMonth()]},c:function(e){return e.toLocaleString()},C:function(e){return a.century(e)},d:function(e){return c.pad(e.getDate(),2)},e:function(e){return c.pad(e.getDate(),2," ")},H:function(e){return c.pad(e.getHours(),2)},I:function(e){return c.pad(e.getHours()%12||12,2)},j:function(e){return c.pad(a.getDayOfYear(e),3)},k:function(e){return c.pad(e.getHours(),2," ")},l:function(e){return c.pad(e.getHours()%12||12,2," ")},L:function(e){return c.pad(e.getMilliseconds(),3)},m:function(e){return c.pad(e.getMonth()+1,2)},M:function(e){return c.pad(e.getMinutes(),2)},p:function(e){return e.getHours()<12?"AM":"PM"},P:function(e){return e.getHours()<12?"am":"pm"},q:function(e){return a.getSuffix(e)},s:function(e){return Math.round(e.valueOf()/1e3)},S:function(e){return c.pad(e.getSeconds(),2)},u:function(e){return e.getDay()||7},U:function(e){return a.getWeekOfYear(e,0)},w:function(e){return e.getDay()},W:function(e){return a.getWeekOfYear(e,1)},x:function(e){return e.toLocaleDateString()},X:function(e){return e.toLocaleTimeString()},y:function(e){return e.getFullYear().toString().substring(2,4)},Y:function(e){return e.getFullYear()},z:function(e){var t=e.getTimezoneOffset()/60*100;return(t>0?"-":"+")+c.pad(Math.abs(t),4)},"%":function(){return"%"}};l.h=l.b,l.N=l.L;t.exports=function(e,t){for(var r="",n=t;;){var s=/%./g,i=s.exec(n);if(!i)return r+n;r+=n.slice(0,s.lastIndex-2),n=n.slice(s.lastIndex);var o=i[0].charAt(1),u=l[o];r+=u?u.call(this,e):"%"+o}}},{}],23:[function(e,t,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s=Object.prototype.toString;function i(e,t){e=e||{};for(var r in e)if(e.hasOwnProperty(r)&&!1===t(e[r],r,e))break;return e}function o(e){var t=void 0===e?"undefined":n(e);return null!=e&&("object"===t||"function"===t)}r.isString=function(e){return e instanceof String||"string"==typeof e},r.isObject=o,r.isArray=function(e){return"[object Array]"===s.call(e)},r.isNil=function(e){return null===e||void 0===e},r.isError=function(e){return"Error"===Object.prototype.toString.call(e).substr(-6,5)||"string"==typeof e.message&&"string"==typeof e.name},r.range=function(e,t,r){1===arguments.length&&(t=e,e=0),r=r||1;for(var n=[],s=e;s0?a=[a]:o.isObject(a)&&(a=Object.keys(a).map(function(e){return[e,a[e]]}))),!Array.isArray(a)||!a.length)return e.renderer.renderTemplates(this.elseTemplates,t);var c=a.length,l=r.offset||0,f=void 0===r.limit?a.length:r.limit;a=a.slice(l,l+f),this.reversed&&a.reverse();var p=a.map(function(e,t){var r={};return r[s.variable]=e,r.forloop={first:0===t,index:t+1,index0:t,last:t===c-1,length:c,rindex:c-t,rindex0:c-t-1},r}),h="";return i(p,function(r){return Promise.resolve().then(function(){return t.push(r)}).then(function(){return e.renderer.renderTemplates(s.templates,t)}).then(function(e){return h+=e}).catch(function(e){if(!(e instanceof u&&(h+=e.resolvedHTML,"continue"===e.message)))throw e}).then(function(){return t.pop()})}).catch(function(e){if(!(e instanceof u&&"break"===e.message))throw e}).then(function(){return h})}})}},{"..":2,"../src/util/assert.js":18,"../src/util/promise.js":21,"../src/util/underscore.js":23}],33:[function(e,t,r){"use strict";var n=e("..");t.exports=function(e){e.registerTag("if",{parse:function(t,r){var n,s=this;this.branches=[],this.elseTemplates=[];var i=e.parser.parseStream(r).on("start",function(){return s.branches.push({cond:t.args,templates:n=[]})}).on("tag:elsif",function(e){s.branches.push({cond:e.args,templates:n=[]})}).on("tag:else",function(){return n=s.elseTemplates}).on("tag:endif",function(e){return i.stop()}).on("template",function(e){return n.push(e)}).on("end",function(e){throw new Error("tag "+t.raw+" not closed")});i.start()},render:function(t,r){for(var s=0;s",l=r.offset||0,f=void 0===r.limit?a.length:r.limit,p=r.cols;if(!p)throw new Error("illegal cols: "+p);var h=[];return(a=a.slice(l,l+f)).some(function(e,t){var r={};r[u.variable]=e,h.push(r)}),s(h,function(r,n){return i=Math.floor(n/p)+1,1===(o=n%p+1)&&(1!==i&&(c+=""),c+=''),c+='| ',t.push(r),e.renderer.renderTemplates(u.templates,t).then(function(e){return t.pop(r),c+=e,c+=" | "})}).then(function(){return i>0&&(c+="
"),c+=""})}})}},{"..":2,"../src/util/assert.js":18,"../src/util/promise.js":21}],40:[function(e,t,r){"use strict";var n=e("..");t.exports=function(e){e.registerTag("unless",{parse:function(t,r){var n,s=this;this.templates=[],this.elseTemplates=[];var i=e.parser.parseStream(r).on("start",function(e){n=s.templates,s.cond=t.args}).on("tag:else",function(){return n=s.elseTemplates}).on("tag:endunless",function(e){return i.stop()}).on("template",function(e){return n.push(e)}).on("end",function(e){throw new Error("tag "+t.raw+" not closed")});i.start()},render:function(t,r){var s=n.evalExp(this.cond,t);return n.isFalsy(s)?e.renderer.renderTemplates(this.templates,t):e.renderer.renderTemplates(this.elseTemplates,t)}})}},{"..":2}]},{},[2])(2)});
\ No newline at end of file
diff --git a/index.js b/index.js
index 56f8bcf64..897f28496 100644
--- a/index.js
+++ b/index.js
@@ -5,6 +5,7 @@ const tokenizer = require('./src/tokenizer.js')
const statFileAsync = require('./src/util/fs.js').statFileAsync
const readFileAsync = require('./src/util/fs.js').readFileAsync
const path = require('path')
+const url = require('./src/util/url.js')
const Render = require('./src/render.js')
const lexical = require('./src/lexical.js')
const Tag = require('./src/tag.js')
@@ -73,6 +74,11 @@ var _engine = {
})
},
getTemplate: function (filepath, root) {
+ return typeof XMLHttpRequest === 'undefined'
+ ? this.getTemplateFromFile(filepath, root)
+ : this.getTemplateFromUrl(filepath, root)
+ },
+ getTemplateFromFile: function (filepath, root) {
if (!path.extname(filepath)) {
filepath += this.options.extname
}
@@ -92,6 +98,42 @@ var _engine = {
}
})
},
+ getTemplateFromUrl: function (filepath, root) {
+ var fullUrl
+ if (url.valid(filepath)) {
+ fullUrl = filepath
+ } else {
+ if (!url.extname(filepath)) {
+ filepath += this.options.extname
+ }
+ fullUrl = url.resolve(root || this.options.root, filepath)
+ }
+ if (this.options.cache) {
+ var tpl = this.cache[filepath]
+ if (tpl) {
+ return Promise.resolve(tpl)
+ }
+ }
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest()
+ xhr.onload = () => {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ var tpl = this.parse(xhr.responseText)
+ if (this.options.cache) {
+ this.cache[filepath] = tpl
+ }
+ resolve(tpl)
+ } else {
+ reject(new Error(xhr.statusText))
+ }
+ }
+ xhr.onerror = () => {
+ reject(new Error(xhr.statusText))
+ }
+ xhr.open('GET', fullUrl)
+ xhr.send()
+ })
+ },
express: function (opts) {
opts = opts || {}
var self = this
diff --git a/package.json b/package.json
index 59a3b2e49..a6d4f971a 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,9 @@
"type": "git",
"url": "git+https://github.com/harttle/liquidjs.git"
},
- "engines": { "node" : ">=4.8.7" },
+ "engines": {
+ "node": ">=4.8.7"
+ },
"keywords": [
"liquid",
"template engine",
@@ -32,14 +34,15 @@
},
"homepage": "https://github.com/harttle/liquidjs#readme",
"dependencies": {
- "any-promise": "^1.3.0"
+ "any-promise": "^1.3.0",
+ "resolve-url": "^0.2.1"
},
"browser": {
"fs": false
},
"devDependencies": {
- "babel-preset-es2015": "^6.24.1",
"babel-core": "^6.26.0",
+ "babel-preset-es2015": "^6.24.1",
"babelify": "^8.0.0",
"browserify": "^14.4.0",
"chai": "^4.1.2",
@@ -54,6 +57,7 @@
"eslint-plugin-standard": "^3.0.1",
"express": "^4.16.1",
"istanbul": "^0.4.5",
+ "jsdom": "^11.5.1",
"mocha": "^4.0.0",
"mock-fs": "^4.4.1",
"sinon": "^4.0.1",
diff --git a/src/util/url.js b/src/util/url.js
new file mode 100644
index 000000000..a7a6057e9
--- /dev/null
+++ b/src/util/url.js
@@ -0,0 +1,23 @@
+const resolve = require('resolve-url')
+const splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^/]+?|)(\.[^./]*|))(?:[/]*)$/
+const urlRe = /^(?:\w+:)?\/\/([^\s.]+\.\S{2}|localhost[:?\d]*)\S*$/
+
+// https://github.com/jinder/path/blob/master/path.js#L567
+exports.extname = function (path) {
+ return splitPathRe.exec(path).slice(1)[3]
+}
+
+// https://www.npmjs.com/package/is-url
+exports.valid = function (path) {
+ return urlRe.test(path)
+}
+
+exports.resolve = function (root, path) {
+ if (Object.prototype.toString.call(root) === '[object Array]') {
+ root = root[0]
+ }
+ if (root && root.charAt(root.length - 1) !== '/') {
+ root += '/'
+ }
+ return resolve(root, path)
+}
diff --git a/test/util/url.js b/test/util/url.js
new file mode 100644
index 000000000..93289c814
--- /dev/null
+++ b/test/util/url.js
@@ -0,0 +1,61 @@
+const chai = require('chai')
+const expect = chai.expect
+const url = require('../../src/util/url.js')
+const { JSDOM } = require('jsdom')
+
+describe('util/url', function () {
+ var dom
+ beforeEach(function () {
+ dom = new JSDOM(``, {
+ url: 'https://example.com/foo/bar/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ })
+ afterEach(function () {
+ delete global.document
+ })
+ describe('resolve', function () {
+ describe('root', function () {
+ it('should support width relative path', function () {
+ expect(url.resolve('./views', 'foo'))
+ .to.equal('https://example.com/foo/bar/views/foo')
+ expect(url.resolve('./views/', 'foo'))
+ .to.equal('https://example.com/foo/bar/views/foo')
+ })
+ it('should support width absolute path', function () {
+ expect(url.resolve('/views', 'foo'))
+ .to.equal('https://example.com/views/foo')
+ expect(url.resolve('/views/', 'foo'))
+ .to.equal('https://example.com/views/foo')
+ })
+ it('should support with empty', function () {
+ expect(url.resolve('', 'page.html'))
+ .to.equal('https://example.com/foo/bar/page.html')
+ })
+ it('should support with url', function () {
+ expect(url.resolve('https://example.com/views', 'page.html'))
+ .to.equal('https://example.com/views/page.html')
+ expect(url.resolve('https://example.com/views/', 'page.html'))
+ .to.equal('https://example.com/views/page.html')
+ })
+ it('should get the first value when argument is array', function () {
+ expect(url.resolve(['https://example.com/views', 'https://google.com/views'], 'page.html'))
+ .to.equal('https://example.com/views/page.html')
+ expect(url.resolve(['https://example.com/views/', 'https://google.com/views'], 'page.html'))
+ .to.equal('https://example.com/views/page.html')
+ })
+ })
+ describe('path', function () {
+ it('should support width relative path', function () {
+ expect(url.resolve('./views/', 'page.html'))
+ .to.equal('https://example.com/foo/bar/views/page.html')
+ })
+ it('should support with absolute path', function () {
+ expect(url.resolve('/views/', '/page.html'))
+ .to.equal('https://example.com/page.html')
+ })
+ })
+ })
+})
diff --git a/test/xhr.js b/test/xhr.js
new file mode 100644
index 000000000..5e5ac027e
--- /dev/null
+++ b/test/xhr.js
@@ -0,0 +1,146 @@
+const Liquid = require('..')
+const sinon = require('sinon')
+const { JSDOM } = require('jsdom')
+const chai = require('chai')
+const expect = chai.expect
+
+describe('xhr', () => {
+ var server, engine, dom
+
+ beforeEach(() => {
+ server = sinon.fakeServer.create()
+ server.autoRespond = true
+ server.respondWith('GET', 'https://example.com/views/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ global.XMLHttpRequest = sinon.useFakeXMLHttpRequest()
+ dom = new JSDOM(``, {
+ url: 'https://example.com/',
+ referrer: 'https://example.com/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ engine = Liquid({
+ root: 'https://example.com/views',
+ extname: '.html'
+ })
+ })
+
+ afterEach(() => {
+ server.restore()
+ delete global.XMLHttpRequest
+ delete global.document
+ })
+
+ describe('#renderFile()', () => {
+ it('should support without extname', () => {
+ return expect(engine.renderFile('hello', {name: 'alice1'}))
+ .to.eventually.equal('hello alice1')
+ })
+ it('should support with extname', () => {
+ return expect(engine.renderFile('hello.html', {name: 'alice2'}))
+ .to.eventually.equal('hello alice2')
+ })
+ it('should support with absolute path', () => {
+ server.respondWith('GET', 'https://example.com/foo.html',
+ [200, {'Content-Type': 'text/plain'}, 'foo'])
+ return expect(engine.renderFile('/foo.html'))
+ .to.eventually.equal('foo')
+ })
+ it('should support with url', () => {
+ return expect(engine.renderFile('https://example.com/views/hello.html', {name: 'alice4'}))
+ .to.eventually.equal('hello alice4')
+ })
+ it('should support include', () => {
+ server.respondWith('GET', 'https://example.com/views/hello.html',
+ [200, {'Content-Type': 'text/plain'}, "hello {% include 'name.html' %}"])
+ server.respondWith('GET', 'https://example.com/views/name.html',
+ [200, {'Content-Type': 'text/plain'}, '{{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ it('should throw 404', () => {
+ return expect(engine.renderFile('/not/exist.html'))
+ .to.be.rejectedWith('Not Found')
+ })
+ })
+ describe('root', () => {
+ it('should support with null', () => {
+ dom = new JSDOM(``, {
+ url: 'https://example.com/bar/',
+ referrer: 'https://example.com/bar/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ engine = Liquid({
+ extname: '.html'
+ })
+ server.respondWith('GET', 'https://example.com/bar/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ it('should support with empty', () => {
+ dom = new JSDOM(``, {
+ url: 'https://example.com/foo/',
+ referrer: 'https://example.com/foo/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ engine = Liquid({
+ root: '',
+ extname: '.html'
+ })
+ server.respondWith('GET', 'https://example.com/foo/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ it('should support with relative path', () => {
+ dom = new JSDOM(``, {
+ url: 'https://example.com/foo/',
+ referrer: 'https://example.com/foo/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ engine = Liquid({
+ root: './views/',
+ extname: '.html'
+ })
+ server.respondWith('GET', 'https://example.com/foo/views/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ it('should support with absolute path', () => {
+ dom = new JSDOM(``, {
+ url: 'https://example.com/foo/',
+ referrer: 'https://example.com/foo/',
+ contentType: 'text/html',
+ includeNodeLocations: true
+ })
+ global.document = dom.window.document
+ engine = Liquid({
+ root: '/views/',
+ extname: '.html'
+ })
+ server.respondWith('GET', 'https://example.com/views/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ it('should support with url', () => {
+ engine = Liquid({
+ root: 'https://foo.com/bar/',
+ extname: '.html'
+ })
+ server.respondWith('GET', 'https://foo.com/bar/hello.html',
+ [200, {'Content-Type': 'text/plain'}, 'hello {{name}}'])
+ return expect(engine.renderFile('hello.html', {name: 'alice5'}))
+ .to.eventually.equal('hello alice5')
+ })
+ })
+})