{"version":3,"file":"liquid.js","sources":["../node_modules/regenerator-runtime/runtime.js","../src/util/underscore.js","../src/lexical.js","../src/util/error.js","../src/util/assert.js","../src/scope.js","../src/template-browser.js","../src/whitespace-ctrl.js","../src/tokenizer.js","../src/operators.js","../src/syntax.js","../src/render.js","../src/tag.js","../src/filter.js","../src/parser.js","../src/util/promise.js","../src/tags/for.js","../src/tags/assign.js","../src/tags/capture.js","../src/tags/case.js","../src/tags/comment.js","../src/tags/include.js","../src/tags/decrement.js","../src/tags/cycle.js","../src/tags/if.js","../src/tags/increment.js","../src/tags/layout.js","../src/tags/raw.js","../src/tags/tablerow.js","../src/tags/unless.js","../src/tags/index.js","../src/util/strftime.js","../src/filters.js","../src/index.js"],"sourcesContent":["/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n!(function(global) {\n \"use strict\";\n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n var inModule = typeof module === \"object\";\n var runtime = global.regeneratorRuntime;\n if (runtime) {\n if (inModule) {\n // If regeneratorRuntime is defined globally and we're in a module,\n // make the exports object identical to regeneratorRuntime.\n module.exports = runtime;\n }\n // Don't bother evaluating the rest of this file if the runtime was\n // already defined globally.\n return;\n }\n\n // Define the runtime globally (as expected by generated code) as either\n // module.exports (if we're in a module) or a new, empty object.\n runtime = global.regeneratorRuntime = inModule ? module.exports : {};\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n runtime.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n IteratorPrototype[iteratorSymbol] = function () {\n return this;\n };\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;\n GeneratorFunctionPrototype.constructor = GeneratorFunction;\n GeneratorFunctionPrototype[toStringTagSymbol] =\n GeneratorFunction.displayName = \"GeneratorFunction\";\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function(method) {\n prototype[method] = function(arg) {\n return this._invoke(method, arg);\n };\n });\n }\n\n runtime.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n runtime.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n if (!(toStringTagSymbol in genFun)) {\n genFun[toStringTagSymbol] = \"GeneratorFunction\";\n }\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n runtime.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return Promise.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return Promise.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new Promise(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n AsyncIterator.prototype[asyncIteratorSymbol] = function () {\n return this;\n };\n runtime.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n runtime.async = function(innerFn, outerFn, self, tryLocsList) {\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList)\n );\n\n return runtime.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n if (delegate.iterator.return) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n Gp[toStringTagSymbol] = \"Generator\";\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n Gp[iteratorSymbol] = function() {\n return this;\n };\n\n Gp.toString = function() {\n return \"[object Generator]\";\n };\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n runtime.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n runtime.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n})(\n // In sloppy mode, unbound `this` refers to the global object, fallback to\n // Function constructor if we're in global strict mode. That is sadly a form\n // of indirect eval which violates Content Security Policy.\n (function() {\n return this || (typeof self === \"object\" && self);\n })() || Function(\"return this\")()\n);\n","const toStr = Object.prototype.toString\nconst arrToStr = Array.prototype.toString\n\n/*\n * Checks if value is classified as a String primitive or object.\n * @param {any} value The value to check.\n * @return {Boolean} Returns true if value is a string, else false.\n */\nexport function isString (value) {\n return toStr.call(value) === '[object String]'\n}\n\nexport function isFunction (value) {\n return typeof value === 'function'\n}\n\nexport function promisify (fn) {\n return function () {\n return new Promise((resolve, reject) => {\n fn(...arguments, (err, result) => {\n err ? reject(err) : resolve(result)\n })\n })\n }\n}\n\nexport function stringify (value) {\n if (isNil(value)) return String(value)\n if (isFunction(value.to_liquid)) return stringify(value.to_liquid())\n if (isFunction(value.toLiquid)) return stringify(value.toLiquid())\n if (isFunction(value.to_s)) return value.to_s()\n if ([toStr, arrToStr].indexOf(value.toString) > -1) return defaultToString(value)\n if (isFunction(value.toString)) return value.toString()\n return toStr.call(value)\n}\n\nfunction defaultToString (value) {\n const cache = []\n return JSON.stringify(value, (key, value) => {\n if (isObject(value)) {\n if (cache.indexOf(value) !== -1) {\n return\n }\n cache.push(value)\n }\n return value\n })\n}\n\nexport function create (proto) {\n return Object.create(proto)\n}\n\nexport function isNil (value) {\n return value === null || value === undefined\n}\n\nexport function isArray (value) {\n // be compatible with IE 8\n return toStr.call(value) === '[object Array]'\n}\n\nexport function isError (value) {\n const signature = toStr.call(value)\n // [object XXXError]\n return signature.substr(-6, 5) === 'Error' ||\n (typeof value.message === 'string' && typeof value.name === 'string')\n}\n\n/*\n * Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property.\n * The iteratee is invoked with three arguments: (value, key, object).\n * Iteratee functions may exit iteration early by explicitly returning false.\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @return {Object} Returns object.\n */\nexport function forOwn (object, iteratee) {\n object = object || {}\n for (const k in object) {\n if (object.hasOwnProperty(k)) {\n if (iteratee(object[k], k, object) === false) break\n }\n }\n return object\n}\n\n/*\n * Assigns own enumerable string keyed properties of source objects to the destination object.\n * Source objects are applied from left to right.\n * Subsequent sources overwrite property assignments of previous sources.\n *\n * Note: This method mutates object and is loosely based on Object.assign.\n *\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @return {Object} Returns object.\n */\nexport function assign (object) {\n object = isObject(object) ? object : {}\n const srcs = Array.prototype.slice.call(arguments, 1)\n srcs.forEach((src) => Object.assign(object, src))\n return object\n}\n\nexport function last (arr) {\n return arr[arr.length - 1]\n}\n\nexport function uniq (arr) {\n const u = {}\n const a = []\n for (let i = 0, l = arr.length; i < l; ++i) {\n if (u.hasOwnProperty(arr[i])) {\n continue\n }\n a.push(arr[i])\n u[arr[i]] = 1\n }\n return a\n}\n\n/*\n * Checks if value is the language type of Object.\n * (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))\n * @param {any} value The value to check.\n * @return {Boolean} Returns true if value is an object, else false.\n */\nexport function isObject (value) {\n const type = typeof value\n return value !== null && (type === 'object' || type === 'function')\n}\n\n/*\n * A function to create flexibly-numbered lists of integers,\n * handy for each and map loops. start, if omitted, defaults to 0; step defaults to 1.\n * Returns a list of integers from start (inclusive) to stop (exclusive),\n * incremented (or decremented) by step, exclusive.\n * Note that ranges that stop before they start are considered to be zero-length instead of\n * negative — if you'd like a negative range, use a negative step.\n */\nexport function range (start, stop, step) {\n if (arguments.length === 1) {\n stop = start\n start = 0\n }\n step = step || 1\n\n const arr = []\n for (let i = start; i < stop; i += step) {\n arr.push(i)\n }\n return arr\n}\n","// quote related\nconst singleQuoted = /'[^']*'/\nconst doubleQuoted = /\"[^\"]*\"/\nexport const quoted = new RegExp(`${singleQuoted.source}|${doubleQuoted.source}`)\nexport const quoteBalanced = new RegExp(`(?:${quoted.source}|[^'\"])*`)\n\n// basic types\nexport const integer = /-?\\d+/\nexport const number = /-?\\d+\\.?\\d*|\\.?\\d+/\nexport const bool = /true|false/\n\n// property access\nexport const identifier = /[\\w-]+[?]?/\nexport const subscript = new RegExp(`\\\\[(?:${quoted.source}|[\\\\w-\\\\.]+)\\\\]`)\nexport const literal = new RegExp(`(?:${quoted.source}|${bool.source}|${number.source})`)\nexport const variable = new RegExp(`${identifier.source}(?:\\\\.${identifier.source}|${subscript.source})*`)\n\n// range related\nexport const rangeLimit = new RegExp(`(?:${variable.source}|${number.source})`)\nexport const range = new RegExp(`\\\\(${rangeLimit.source}\\\\.\\\\.${rangeLimit.source}\\\\)`)\nexport const rangeCapture = new RegExp(`\\\\((${rangeLimit.source})\\\\.\\\\.(${rangeLimit.source})\\\\)`)\n\nexport const value = new RegExp(`(?:${variable.source}|${literal.source}|${range.source})`)\n\n// hash related\nexport const hash = new RegExp(`(?:${identifier.source})\\\\s*:\\\\s*(?:${value.source})`)\nexport const hashCapture = new RegExp(`(${identifier.source})\\\\s*:\\\\s*(${value.source})`, 'g')\n\n// full match\nexport const tagLine = new RegExp(`^\\\\s*(${identifier.source})\\\\s*([\\\\s\\\\S]*)\\\\s*$`)\nexport const literalLine = new RegExp(`^${literal.source}$`, 'i')\nexport const variableLine = new RegExp(`^${variable.source}$`)\nexport const numberLine = new RegExp(`^${number.source}$`)\nexport const boolLine = new RegExp(`^${bool.source}$`, 'i')\nexport const quotedLine = new RegExp(`^${quoted.source}$`)\nexport const rangeLine = new RegExp(`^${rangeCapture.source}$`)\nexport const integerLine = new RegExp(`^${integer.source}$`)\n\n// filter related\nexport const valueDeclaration = new RegExp(`(?:${identifier.source}\\\\s*:\\\\s*)?${value.source}`)\nexport const valueList = new RegExp(`${valueDeclaration.source}(\\\\s*,\\\\s*${valueDeclaration.source})*`)\nexport const filter = new RegExp(`${identifier.source}(?:\\\\s*:\\\\s*${valueList.source})?`, 'g')\nexport const filterCapture = new RegExp(`(${identifier.source})(?:\\\\s*:\\\\s*(${valueList.source}))?`)\nexport const filterLine = new RegExp(`^${filterCapture.source}$`)\n\nexport const operators = [\n /\\s+or\\s+/,\n /\\s+and\\s+/,\n /==|!=|<=|>=|<|>|\\s+contains\\s+/\n]\n\nexport function isInteger (str) {\n return integerLine.test(str)\n}\n\nexport function isLiteral (str) {\n return literalLine.test(str)\n}\n\nexport function isRange (str) {\n return rangeLine.test(str)\n}\n\nexport function isVariable (str) {\n return variableLine.test(str)\n}\n\nexport function matchValue (str) {\n return value.exec(str)\n}\n\nexport function parseLiteral (str) {\n let res = str.match(numberLine)\n if (res) {\n return Number(str)\n }\n res = str.match(boolLine)\n if (res) {\n return str.toLowerCase() === 'true'\n }\n res = str.match(quotedLine)\n if (res) {\n return str.slice(1, -1)\n }\n throw new TypeError(`cannot parse '${str}' as literal`)\n}\n","import * as _ from './underscore.js'\n\nfunction initError () {\n this.name = this.constructor.name\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor)\n }\n}\n\nfunction initLiquidError (err, token) {\n initError.call(this)\n\n this.input = token.input\n this.line = token.line\n this.file = token.file\n\n const context = mkContext(token.input, token.line)\n this.message = mkMessage(err.message, token)\n this.stack = context +\n '\\n' + (this.stack || this.message) +\n (err.stack ? '\\nFrom ' + err.stack : '')\n}\n\nexport function TokenizationError (message, token) {\n initLiquidError.call(this, { message: message }, token)\n}\nTokenizationError.prototype = _.create(Error.prototype)\nTokenizationError.prototype.constructor = TokenizationError\n\nexport function ParseError (e, token) {\n _.assign(this, e)\n this.originalError = e\n\n initLiquidError.call(this, e, token)\n}\nParseError.prototype = _.create(Error.prototype)\nParseError.prototype.constructor = ParseError\n\nexport function RenderError (e, tpl) {\n // return the original render error\n if (e instanceof RenderError) {\n return e\n }\n _.assign(this, e)\n this.originalError = e\n\n initLiquidError.call(this, e, tpl.token)\n}\nRenderError.prototype = _.create(Error.prototype)\nRenderError.prototype.constructor = RenderError\n\nexport function RenderBreakError (message) {\n initError.call(this)\n this.message = message + ''\n}\nRenderBreakError.prototype = _.create(Error.prototype)\nRenderBreakError.prototype.constructor = RenderBreakError\n\nexport function AssertionError (message) {\n initError.call(this)\n this.message = message + ''\n}\nAssertionError.prototype = _.create(Error.prototype)\nAssertionError.prototype.constructor = AssertionError\n\nfunction mkContext (input, line) {\n const lines = input.split('\\n')\n const begin = Math.max(line - 2, 1)\n const end = Math.min(line + 3, lines.length)\n\n const context = _\n .range(begin, end + 1)\n .map(l => [\n (l === line) ? '>> ' : ' ',\n align(l, end),\n '| ',\n lines[l - 1]\n ].join(''))\n .join('\\n')\n\n return context\n}\n\nfunction align (n, max) {\n const length = (max + '').length\n const str = n + ''\n const blank = Array(length - str.length).join(' ')\n return blank + str\n}\n\nfunction mkMessage (msg, token) {\n msg = msg || ''\n if (token.file) {\n msg += ', file:' + token.file\n }\n if (token.line) {\n msg += ', line:' + token.line\n }\n return msg\n}\n","import { AssertionError } from './error.js'\n\nexport default function (predicate, message) {\n if (!predicate) {\n message = message || `expect ${predicate} to be true`\n throw new AssertionError(message)\n }\n}\n","import * as _ from './util/underscore.js'\nimport * as lexical from './lexical.js'\nimport assert from './util/assert.js'\n\nconst Scope = {\n getAll: function () {\n return this.contexts.reduce((ctx, val) => _.assign(ctx, val), _.create(null))\n },\n get: function (path) {\n const paths = this.propertyAccessSeq(path)\n const scope = this.findContextFor(paths[0]) || _.last(this.contexts)\n return paths.reduce((value, key) => this.readProperty(value, key), scope)\n },\n set: function (path, v) {\n const paths = this.propertyAccessSeq(path)\n let scope = this.findContextFor(paths[0]) || _.last(this.contexts)\n paths.some((key, i) => {\n if (!_.isObject(scope)) {\n return true\n }\n if (i === paths.length - 1) {\n scope[key] = v\n return true\n }\n if (undefined === scope[key]) {\n scope[key] = {}\n }\n scope = scope[key]\n })\n },\n unshift: function (ctx) {\n return this.contexts.unshift(ctx)\n },\n push: function (ctx) {\n return this.contexts.push(ctx)\n },\n pop: function (ctx) {\n if (!arguments.length) {\n return this.contexts.pop()\n }\n const i = this.contexts.findIndex(scope => scope === ctx)\n if (i === -1) {\n throw new TypeError('scope not found, cannot pop')\n }\n return this.contexts.splice(i, 1)[0]\n },\n findContextFor: function (key, filter) {\n filter = filter || (() => true)\n for (let i = this.contexts.length - 1; i >= 0; i--) {\n const candidate = this.contexts[i]\n if (!filter(candidate)) continue\n if (key in candidate) {\n return candidate\n }\n }\n return null\n },\n readProperty: function (obj, key) {\n let val\n if (_.isNil(obj)) {\n val = undefined\n } else {\n obj = toLiquid(obj)\n val = key === 'size' ? readSize(obj) : obj[key]\n if (_.isFunction(obj.liquid_method_missing)) {\n val = obj.liquid_method_missing(key)\n }\n }\n if (_.isNil(val) && this.opts.strict_variables) {\n throw new TypeError(`undefined variable: ${key}`)\n }\n return val\n },\n\n /*\n * Parse property access sequence from access string\n * @example\n * accessSeq(\"foo.bar\") // ['foo', 'bar']\n * accessSeq(\"foo['bar']\") // ['foo', 'bar']\n * accessSeq(\"foo['b]r']\") // ['foo', 'b]r']\n * accessSeq(\"foo[bar.coo]\") // ['foo', 'bar'], for bar.coo == 'bar'\n */\n propertyAccessSeq: function (str) {\n str = String(str)\n const seq = []\n let name = ''\n let j\n let i = 0\n while (i < str.length) {\n switch (str[i]) {\n case '[':\n push()\n\n const delemiter = str[i + 1]\n if (/['\"]/.test(delemiter)) { // foo[\"bar\"]\n j = str.indexOf(delemiter, i + 2)\n assert(j !== -1, `unbalanced ${delemiter}: ${str}`)\n name = str.slice(i + 2, j)\n push()\n i = j + 2\n } else { // foo[bar.coo]\n j = matchRightBracket(str, i + 1)\n assert(j !== -1, `unbalanced []: ${str}`)\n name = str.slice(i + 1, j)\n if (!lexical.isInteger(name)) { // foo[bar] vs. foo[1]\n name = String(this.get(name))\n }\n push()\n i = j + 1\n }\n break\n case '.':// foo.bar, foo[0].bar\n push()\n i++\n break\n default:// foo.bar\n name += str[i]\n i++\n }\n }\n push()\n\n if (!seq.length) {\n throw new TypeError(`invalid path:\"${str}\"`)\n }\n return seq\n\n function push () {\n if (name.length) seq.push(name)\n name = ''\n }\n }\n}\n\nfunction toLiquid (obj) {\n if (_.isFunction(obj.to_liquid)) {\n return obj.to_liquid()\n }\n if (_.isFunction(obj.toLiquid)) {\n return obj.toLiquid()\n }\n return obj\n}\n\nfunction readSize (obj) {\n if (!_.isNil(obj.size)) return obj.size\n if (_.isArray(obj) || _.isString(obj)) return obj.length\n return obj.size\n}\n\nfunction matchRightBracket (str, begin) {\n let stack = 1 // count of '[' - count of ']'\n for (let i = begin; i < str.length; i++) {\n if (str[i] === '[') {\n stack++\n }\n if (str[i] === ']') {\n stack--\n if (stack === 0) {\n return i\n }\n }\n }\n return -1\n}\n\nexport function factory (ctx, opts) {\n const defaultOptions = {\n dynamicPartials: true,\n strict_variables: false,\n strict_filters: false,\n blocks: {},\n root: []\n }\n const scope = _.create(Scope)\n scope.opts = _.assign(defaultOptions, opts)\n scope.contexts = [ctx || {}]\n return scope\n}\n","import { last, isArray } from './util/underscore'\n\nfunction domResolve (root, path) {\n const base = document.createElement('base')\n base.href = root\n\n const head = document.getElementsByTagName('head')[0]\n head.insertBefore(base, head.firstChild)\n\n const a = document.createElement('a')\n a.href = path\n const resolved = a.href\n head.removeChild(base)\n\n return resolved\n}\n\nexport function resolve (filepath, root, options) {\n root = root || options.root\n if (isArray(root)) {\n root = root[0]\n }\n if (root.length && last(root) !== '/') {\n root += '/'\n }\n const url = domResolve(root, filepath)\n return url.replace(/^(\\w+:\\/\\/[^/]+)(\\/[^?]+)/, (str, origin, path) => {\n const last = path.split('/').pop()\n if (/\\.\\w+$/.test(last)) {\n return str\n }\n return origin + path + options.extname\n })\n}\n\nexport async function read (url) {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest()\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(xhr.responseText)\n } else {\n reject(new Error(xhr.statusText))\n }\n }\n xhr.onerror = () => {\n reject(new Error('An error occurred whilst receiving the response.'))\n }\n xhr.open('GET', url)\n xhr.send()\n })\n}\n","import { assign } from './util/underscore.js'\n\nexport default function whiteSpaceCtrl (tokens, options) {\n options = assign({ greedy: true }, options)\n let inRaw = false\n\n tokens.forEach((token, i) => {\n if (shouldTrimLeft(token, inRaw, options)) {\n trimLeft(tokens[i - 1], options.greedy)\n }\n\n if (token.type === 'tag' && token.name === 'raw') inRaw = true\n if (token.type === 'tag' && token.name === 'endraw') inRaw = false\n\n if (shouldTrimRight(token, inRaw, options)) {\n trimRight(tokens[i + 1], options.greedy)\n }\n })\n}\n\nfunction shouldTrimLeft (token, inRaw, options) {\n if (inRaw) return false\n if (token.type === 'tag') return token.trim_left || options.trim_tag_left\n if (token.type === 'value') return token.trim_left || options.trim_value_left\n}\n\nfunction shouldTrimRight (token, inRaw, options) {\n if (inRaw) return false\n if (token.type === 'tag') return token.trim_right || options.trim_tag_right\n if (token.type === 'value') return token.trim_right || options.trim_value_right\n}\n\nfunction trimLeft (token, greedy) {\n if (!token || token.type !== 'html') return\n\n const rLeft = greedy ? /\\s+$/g : /[\\t\\r ]*$/g\n token.value = token.value.replace(rLeft, '')\n}\n\nfunction trimRight (token, greedy) {\n if (!token || token.type !== 'html') return\n\n const rRight = greedy ? /^\\s+/g : /^[\\t\\r ]*\\n?/g\n token.value = token.value.replace(rRight, '')\n}\n","import * as lexical from './lexical.js'\nimport { TokenizationError } from './util/error.js'\nimport * as _ from './util/underscore.js'\nimport assert from './util/assert.js'\nimport whiteSpaceCtrl from './whitespace-ctrl.js'\n\nexport { default as whiteSpaceCtrl } from './whitespace-ctrl.js'\n\nexport function parse (input, file, options) {\n assert(_.isString(input), 'illegal input')\n\n const rLiquid = /({%-?([\\s\\S]*?)-?%})|({{-?([\\s\\S]*?)-?}})/g\n let currIndent = 0\n const lineNumber = LineNumber(input)\n let lastMatchEnd = 0\n const tokens = []\n\n for (let match; (match = rLiquid.exec(input)); lastMatchEnd = rLiquid.lastIndex) {\n if (match.index > lastMatchEnd) {\n tokens.push(parseHTMLToken(lastMatchEnd, match.index))\n }\n tokens.push(match[1]\n ? parseTagToken(match[1], match[2].trim(), match.index)\n : parseValueToken(match[3], match[4].trim(), match.index))\n }\n if (input.length > lastMatchEnd) {\n tokens.push(parseHTMLToken(lastMatchEnd, input.length))\n }\n whiteSpaceCtrl(tokens, options)\n return tokens\n\n function parseTagToken (raw, value, pos) {\n const match = value.match(lexical.tagLine)\n const token = {\n type: 'tag',\n indent: currIndent,\n line: lineNumber.get(pos),\n trim_left: raw.slice(0, 3) === '{%-',\n trim_right: raw.slice(-3) === '-%}',\n raw,\n value,\n input,\n file\n }\n if (!match) {\n throw new TokenizationError(`illegal tag syntax`, token)\n }\n token.name = match[1]\n token.args = match[2]\n return token\n }\n\n function parseValueToken (raw, value, pos) {\n return {\n type: 'value',\n line: lineNumber.get(pos),\n trim_left: raw.slice(0, 3) === '{{-',\n trim_right: raw.slice(-3) === '-}}',\n raw,\n value,\n input,\n file\n }\n }\n\n function parseHTMLToken (begin, end) {\n const htmlFragment = input.slice(begin, end)\n currIndent = _.last((htmlFragment).split('\\n')).length\n\n return {\n type: 'html',\n raw: htmlFragment,\n value: htmlFragment\n }\n }\n}\n\nfunction LineNumber (html) {\n let parsedLinesCount = 0\n let lastMatchBegin = -1\n\n return {\n get: function (pos) {\n const lines = html.slice(lastMatchBegin + 1, pos).split('\\n')\n parsedLinesCount += lines.length - 1\n lastMatchBegin = pos\n return parsedLinesCount + 1\n }\n }\n}\n","export default function (isTruthy) {\n return {\n '==': (l, r) => l === r,\n '!=': (l, r) => l !== r,\n '>': (l, r) => l !== null && r !== null && l > r,\n '<': (l, r) => l !== null && r !== null && l < r,\n '>=': (l, r) => l !== null && r !== null && l >= r,\n '<=': (l, r) => l !== null && r !== null && l <= r,\n 'contains': (l, r) => {\n if (!l) return false\n if (typeof l.indexOf !== 'function') return false\n return l.indexOf(r) > -1\n },\n 'and': (l, r) => isTruthy(l) && isTruthy(r),\n 'or': (l, r) => isTruthy(l) || isTruthy(r)\n }\n}\n","import Operators from './operators.js'\nimport * as lexical from './lexical.js'\nimport assert from './util/assert.js'\n\nconst operators = Operators(isTruthy)\n\nexport function evalExp (exp, scope) {\n assert(scope, 'unable to evalExp: scope undefined')\n const operatorREs = lexical.operators\n let match\n for (let i = 0; i < operatorREs.length; i++) {\n const operatorRE = operatorREs[i]\n const expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`)\n if ((match = exp.match(expRE))) {\n const l = evalExp(match[1], scope)\n const op = operators[match[2].trim()]\n const r = evalExp(match[3], scope)\n return op(l, r)\n }\n }\n\n if ((match = exp.match(lexical.rangeLine))) {\n const low = evalValue(match[1], scope)\n const high = evalValue(match[2], scope)\n const range = []\n for (let j = low; j <= high; j++) {\n range.push(j)\n }\n return range\n }\n\n return evalValue(exp, scope)\n}\n\nexport function evalValue (str, scope) {\n str = str && str.trim()\n if (!str) return undefined\n\n if (lexical.isLiteral(str)) {\n return lexical.parseLiteral(str)\n }\n if (lexical.isVariable(str)) {\n return scope.get(str)\n }\n throw new TypeError(`cannot eval '${str}' as value`)\n}\n\nexport function isTruthy (val) {\n return !isFalsy(val)\n}\n\nexport function isFalsy (val) {\n return val === false || undefined === val || val === null\n}\n","import { evalExp } from './syntax.js'\nimport { RenderBreakError, RenderError } from './util/error.js'\nimport { stringify, create } from './util/underscore.js'\nimport assert from './util/assert.js'\n\nconst render = {\n renderTemplates: async function (templates, scope) {\n assert(scope, 'unable to evalTemplates: scope undefined')\n\n let html = ''\n for (const tpl of templates) {\n try {\n html += await renderTemplate.call(this, tpl)\n } catch (e) {\n if (e instanceof RenderBreakError) {\n e.resolvedHTML = html\n throw e\n }\n throw new RenderError(e, tpl)\n }\n }\n return html\n\n async function renderTemplate (template) {\n if (template.type === 'tag') {\n const partial = await this.renderTag(template, scope)\n return partial === undefined ? '' : partial\n }\n if (template.type === 'value') {\n return this.renderValue(template, scope)\n }\n return template.value\n }\n },\n\n renderTag: async function (template, scope) {\n if (template.name === 'continue') {\n throw new RenderBreakError('continue')\n }\n if (template.name === 'break') {\n throw new RenderBreakError('break')\n }\n return template.render(scope)\n },\n\n renderValue: async function (template, scope) {\n const partial = this.evalValue(template, scope)\n return partial === undefined ? '' : stringify(partial)\n },\n\n evalValue: function (template, scope) {\n assert(scope, 'unable to evalValue: scope undefined')\n return template.filters.reduce(\n (prev, filter) => filter.render(prev, scope),\n evalExp(template.initial, scope))\n }\n}\n\nexport default function () {\n const instance = create(render)\n return instance\n}\n","import { hashCapture } from './lexical.js'\nimport { create } from './util/underscore.js'\nimport { evalValue } from './syntax.js'\nimport assert from './util/assert.js'\n\nfunction hash (markup, scope) {\n const obj = {}\n let match\n hashCapture.lastIndex = 0\n while ((match = hashCapture.exec(markup))) {\n const k = match[1]\n const v = match[2]\n obj[k] = evalValue(v, scope)\n }\n return obj\n}\n\nexport default function () {\n let tagImpls = {}\n\n const _tagInstance = {\n render: async function (scope) {\n const obj = hash(this.token.args, scope)\n const impl = this.tagImpl\n if (typeof impl.render !== 'function') {\n return ''\n }\n return impl.render(scope, obj)\n },\n parse: function (token, tokens) {\n this.type = 'tag'\n this.token = token\n this.name = token.name\n\n const tagImpl = tagImpls[this.name]\n assert(tagImpl, `tag ${this.name} not found`)\n this.tagImpl = create(tagImpl)\n if (this.tagImpl.parse) {\n this.tagImpl.parse(token, tokens)\n }\n }\n }\n\n function register (name, tag) {\n tagImpls[name] = tag\n }\n\n function construct (token, tokens) {\n const instance = create(_tagInstance)\n instance.parse(token, tokens)\n return instance\n }\n\n function clear () {\n tagImpls = {}\n }\n\n return {\n construct,\n register,\n clear\n }\n}\n","import * as lexical from './lexical.js'\nimport { evalValue } from './syntax.js'\nimport assert from './util/assert.js'\nimport { assign, create } from './util/underscore.js'\n\nconst valueRE = new RegExp(`${lexical.value.source}`, 'g')\n\nexport default function (options) {\n options = assign({}, options)\n let filters = {}\n\n const _filterInstance = {\n render: function (output, scope) {\n const args = this.args.map(arg => evalValue(arg, scope))\n args.unshift(output)\n return this.filter.apply(null, args)\n },\n parse: function (str) {\n let match = lexical.filterLine.exec(str)\n assert(match, 'illegal filter: ' + str)\n\n const name = match[1]\n const argList = match[2] || ''\n const filter = filters[name]\n if (typeof filter !== 'function') {\n if (options.strict_filters) {\n throw new TypeError(`undefined filter: ${name}`)\n }\n this.name = name\n this.filter = x => x\n this.args = []\n return this\n }\n\n const args = []\n while ((match = valueRE.exec(argList.trim()))) {\n const v = match[0]\n const re = new RegExp(`${v}\\\\s*:`, 'g')\n const keyMatch = re.exec(match.input)\n const currentMatchIsKey = keyMatch && keyMatch.index === match.index\n currentMatchIsKey ? args.push(`'${v}'`) : args.push(v)\n }\n\n this.name = name\n this.filter = filter\n this.args = args\n\n return this\n }\n }\n\n function construct (str) {\n const instance = create(_filterInstance)\n return instance.parse(str)\n }\n\n function register (name, filter) {\n filters[name] = filter\n }\n\n function clear () {\n filters = {}\n }\n\n return {\n construct, register, clear\n }\n}\n","import * as lexical from './lexical.js'\nimport { create } from './util/underscore.js'\nimport { ParseError } from './util/error.js'\nimport assert from './util/assert.js'\n\nexport default function (Tag, Filter) {\n const stream = {\n init: function (tokens) {\n this.tokens = tokens\n this.handlers = {}\n return this\n },\n on: function (name, cb) {\n this.handlers[name] = cb\n return this\n },\n trigger: function (event, arg) {\n const h = this.handlers[event]\n if (typeof h === 'function') {\n h(arg)\n return true\n }\n },\n start: function () {\n this.trigger('start')\n let token\n while (!this.stopRequested && (token = this.tokens.shift())) {\n if (this.trigger('token', token)) continue\n if (token.type === 'tag' &&\n this.trigger(`tag:${token.name}`, token)) {\n continue\n }\n const template = parseToken(token, this.tokens)\n this.trigger('template', template)\n }\n if (!this.stopRequested) this.trigger('end')\n return this\n },\n stop: function () {\n this.stopRequested = true\n return this\n }\n }\n\n function parse (tokens) {\n let token\n const templates = []\n while ((token = tokens.shift())) {\n templates.push(parseToken(token, tokens))\n }\n return templates\n }\n\n function parseToken (token, tokens) {\n try {\n let tpl = null\n if (token.type === 'tag') {\n tpl = parseTag(token, tokens)\n } else if (token.type === 'value') {\n tpl = parseValue(token.value)\n } else { // token.type === 'html'\n tpl = token\n }\n tpl.token = token\n return tpl\n } catch (e) {\n throw new ParseError(e, token)\n }\n }\n\n function parseTag (token, tokens) {\n if (token.name === 'continue' || token.name === 'break') return token\n return Tag.construct(token, tokens)\n }\n\n function parseValue (str) {\n let match = lexical.matchValue(str)\n assert(match, `illegal value string: ${str}`)\n\n const initial = match[0]\n str = str.substr(match.index + match[0].length)\n\n const filters = []\n while ((match = lexical.filter.exec(str))) {\n filters.push([match[0].trim()])\n }\n\n return {\n type: 'value',\n initial: initial,\n filters: filters.map(str => Filter.construct(str))\n }\n }\n\n function parseStream (tokens) {\n const s = create(stream)\n return s.init(tokens)\n }\n\n return {\n parse,\n parseTag,\n parseStream,\n parseValue\n }\n}\n","/*\n * Call functions in serial until someone resolved.\n * @param {Array} iterable the array to iterate with.\n * @param {Array} iteratee returns a new promise.\n * The iteratee is invoked with three arguments: (value, index, iterable).\n */\nexport function anySeries (iterable, iteratee) {\n let ret = Promise.reject(new Error('init'))\n iterable.forEach(function (item, idx) {\n ret = ret.catch(e => iteratee(item, idx, iterable))\n })\n return ret\n}\n\n/*\n * Call functions in serial until someone rejected.\n * @param {Array} iterable the array to iterate with.\n * @param {Array} iteratee returns a new promise.\n * The iteratee is invoked with three arguments: (value, index, iterable).\n */\nexport function mapSeries (iterable, iteratee) {\n let ret = Promise.resolve('init')\n const result = []\n iterable.forEach(function (item, idx) {\n ret = ret\n .then(() => iteratee(item, idx, iterable))\n .then(x => result.push(x))\n })\n return ret.then(() => result)\n}\n","import { mapSeries } from '../util/promise.js'\nimport { isString, isObject, isArray } from '../util/underscore.js'\nimport assert from '../util/assert.js'\nimport { identifier, value, hash } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const RenderBreakError = Liquid.Types.RenderBreakError\n const re = new RegExp(`^(${identifier.source})\\\\s+in\\\\s+` +\n `(${value.source})` +\n `(?:\\\\s+${hash.source})*` +\n `(?:\\\\s+(reversed))?` +\n `(?:\\\\s+${hash.source})*$`)\n\n liquid.registerTag('for', { parse, render })\n\n function parse (tagToken, remainTokens) {\n const match = re.exec(tagToken.args)\n assert(match, `illegal tag: ${tagToken.raw}`)\n this.variable = match[1]\n this.collection = match[2]\n this.reversed = !!match[3]\n\n this.templates = []\n this.elseTemplates = []\n\n let p\n const stream = liquid.parser.parseStream(remainTokens)\n .on('start', () => (p = this.templates))\n .on('tag:else', () => (p = this.elseTemplates))\n .on('tag:endfor', () => stream.stop())\n .on('template', tpl => p.push(tpl))\n .on('end', () => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n\n stream.start()\n }\n async function render (scope, hash) {\n let collection = Liquid.evalExp(this.collection, scope)\n\n if (!isArray(collection)) {\n if (isString(collection) && collection.length > 0) {\n collection = [collection]\n } else if (isObject(collection)) {\n collection = Object.keys(collection).map((key) => [key, collection[key]])\n }\n }\n if (!isArray(collection) || !collection.length) {\n return liquid.renderer.renderTemplates(this.elseTemplates, scope)\n }\n\n const offset = hash.offset || 0\n const limit = (hash.limit === undefined) ? collection.length : hash.limit\n\n collection = collection.slice(offset, offset + limit)\n if (this.reversed) collection.reverse()\n\n const contexts = collection.map((item, i) => {\n const ctx = {}\n ctx[this.variable] = item\n ctx.forloop = {\n first: i === 0,\n index: i + 1,\n index0: i,\n last: i === collection.length - 1,\n length: collection.length,\n rindex: collection.length - i,\n rindex0: collection.length - i - 1\n }\n return ctx\n })\n\n let html = ''\n let finished = false\n await mapSeries(contexts, async context => {\n if (finished) return\n\n scope.push(context)\n try {\n html += await liquid.renderer.renderTemplates(this.templates, scope)\n } catch (e) {\n if (e instanceof RenderBreakError) {\n html += e.resolvedHTML\n if (e.message === 'break') {\n finished = true\n }\n } else throw e\n }\n scope.pop(context)\n })\n return html\n }\n}\n","import assert from '../util/assert.js'\nimport { identifier } from '../lexical.js'\nimport { create } from '../util/underscore.js'\n\nexport default function (liquid, Liquid) {\n const re = new RegExp(`(${identifier.source})\\\\s*=([^]*)`)\n const { AssignScope } = Liquid.Types\n\n liquid.registerTag('assign', {\n parse: function (token) {\n const match = token.args.match(re)\n assert(match, `illegal token ${token.raw}`)\n this.key = match[1]\n this.value = match[2]\n },\n render: function (scope) {\n const ctx = create(AssignScope)\n ctx[this.key] = liquid.evalValue(this.value, scope)\n scope.push(ctx)\n return Promise.resolve('')\n }\n })\n}\n","import assert from '../util/assert.js'\nimport { create } from '../util/underscore.js'\nimport { identifier } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const re = new RegExp(`(${identifier.source})`)\n const { CaptureScope } = Liquid.Types\n\n liquid.registerTag('capture', {\n parse: function (tagToken, remainTokens) {\n const match = tagToken.args.match(re)\n assert(match, `${tagToken.args} not valid identifier`)\n\n this.variable = match[1]\n this.templates = []\n\n const stream = liquid.parser.parseStream(remainTokens)\n stream.on('tag:endcapture', token => stream.stop())\n .on('template', tpl => this.templates.push(tpl))\n .on('end', x => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n stream.start()\n },\n render: async function (scope, hash) {\n const html = await liquid.renderer.renderTemplates(this.templates, scope)\n const ctx = create(CaptureScope)\n ctx[this.variable] = html\n scope.push(ctx)\n }\n })\n}\n","export default function (liquid, Liquid) {\n liquid.registerTag('case', {\n\n parse: function (tagToken, remainTokens) {\n this.cond = tagToken.args\n this.cases = []\n this.elseTemplates = []\n\n let p = []\n const stream = liquid.parser.parseStream(remainTokens)\n .on('tag:when', token => {\n this.cases.push({\n val: token.args,\n templates: p = []\n })\n })\n .on('tag:else', () => (p = this.elseTemplates))\n .on('tag:endcase', token => stream.stop())\n .on('template', tpl => p.push(tpl))\n .on('end', x => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n\n stream.start()\n },\n\n render: function (scope, hash) {\n for (let i = 0; i < this.cases.length; i++) {\n const branch = this.cases[i]\n const val = Liquid.evalExp(branch.val, scope)\n const cond = Liquid.evalExp(this.cond, scope)\n if (val === cond) {\n return liquid.renderer.renderTemplates(branch.templates, scope)\n }\n }\n return liquid.renderer.renderTemplates(this.elseTemplates, scope)\n }\n })\n}\n","export default function (liquid) {\n liquid.registerTag('comment', {\n parse: function (tagToken, remainTokens) {\n const stream = liquid.parser.parseStream(remainTokens)\n stream\n .on('token', token => {\n if (token.name === 'endcomment') stream.stop()\n })\n .on('end', x => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n stream.start()\n }\n })\n}\n","import assert from '../util/assert.js'\nimport { value, quotedLine } from '../lexical.js'\n\nconst staticFileRE = /[^\\s,]+/\n\nexport default function (liquid, Liquid) {\n const withRE = new RegExp(`with\\\\s+(${value.source})`)\n\n liquid.registerTag('include', {\n parse: function (token) {\n let match = staticFileRE.exec(token.args)\n if (match) {\n this.staticValue = match[0]\n }\n\n match = value.exec(token.args)\n if (match) {\n this.value = match[0]\n }\n\n match = withRE.exec(token.args)\n if (match) {\n this.with = match[1]\n }\n },\n render: async function (scope, hash) {\n let filepath\n if (scope.opts.dynamicPartials) {\n if (quotedLine.exec(this.value)) {\n const template = this.value.slice(1, -1)\n filepath = await liquid.parseAndRender(template, scope.getAll(), scope.opts)\n } else {\n filepath = Liquid.evalValue(this.value, scope)\n }\n } else {\n filepath = this.staticValue\n }\n assert(filepath, `cannot include with empty filename`)\n\n const originBlocks = scope.opts.blocks\n const originBlockMode = scope.opts.blockMode\n\n scope.opts.blocks = {}\n scope.opts.blockMode = 'output'\n if (this.with) {\n hash[filepath] = Liquid.evalValue(this.with, scope)\n }\n const templates = await liquid.getTemplate(filepath, scope.opts.root)\n scope.push(hash)\n const html = await liquid.renderer.renderTemplates(templates, scope)\n scope.pop(hash)\n scope.opts.blocks = originBlocks\n scope.opts.blockMode = originBlockMode\n return html\n }\n })\n}\n","import { create } from '../util/underscore.js'\nimport assert from '../util/assert.js'\nimport { identifier } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const { CaptureScope, AssignScope, DecrementScope } = Liquid.Types\n\n liquid.registerTag('decrement', {\n parse: function (token) {\n const match = token.args.match(identifier)\n assert(match, `illegal identifier ${token.args}`)\n this.variable = match[0]\n },\n render: function (scope, hash) {\n let context = scope.findContextFor(\n this.variable,\n ctx => {\n const proto = Object.getPrototypeOf(ctx)\n return proto !== CaptureScope && proto !== AssignScope\n }\n )\n if (!context) {\n context = create(DecrementScope)\n scope.unshift(context)\n }\n if (typeof context[this.variable] !== 'number') {\n context[this.variable] = 0\n }\n return --context[this.variable]\n }\n })\n}\n","import assert from '../util/assert.js'\nimport { value as rValue } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const groupRE = new RegExp(`^(?:(${rValue.source})\\\\s*:\\\\s*)?(.*)$`)\n const candidatesRE = new RegExp(rValue.source, 'g')\n\n liquid.registerTag('cycle', {\n\n parse: function (tagToken, remainTokens) {\n let match = groupRE.exec(tagToken.args)\n assert(match, `illegal tag: ${tagToken.raw}`)\n\n this.group = match[1] || ''\n const candidates = match[2]\n\n this.candidates = []\n\n while ((match = candidatesRE.exec(candidates))) {\n this.candidates.push(match[0])\n }\n assert(this.candidates.length, `empty candidates: ${tagToken.raw}`)\n },\n\n render: function (scope, hash) {\n const group = Liquid.evalValue(this.group, scope)\n const fingerprint = `cycle:${group}:` + this.candidates.join(',')\n\n const groups = scope.opts.groups = scope.opts.groups || {}\n let idx = groups[fingerprint]\n\n if (idx === undefined) {\n idx = groups[fingerprint] = 0\n }\n\n const candidate = this.candidates[idx]\n idx = (idx + 1) % this.candidates.length\n groups[fingerprint] = idx\n\n return Liquid.evalValue(candidate, scope)\n }\n })\n}\n","export default function (liquid, Liquid) {\n liquid.registerTag('if', {\n\n parse: function (tagToken, remainTokens) {\n this.branches = []\n this.elseTemplates = []\n\n let p\n const stream = liquid.parser.parseStream(remainTokens)\n .on('start', () => this.branches.push({\n cond: tagToken.args,\n templates: (p = [])\n }))\n .on('tag:elsif', token => {\n this.branches.push({\n cond: token.args,\n templates: p = []\n })\n })\n .on('tag:else', () => (p = this.elseTemplates))\n .on('tag:endif', token => stream.stop())\n .on('template', tpl => p.push(tpl))\n .on('end', x => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n\n stream.start()\n },\n\n render: function (scope, hash) {\n for (const branch of this.branches) {\n const cond = Liquid.evalExp(branch.cond, scope)\n if (Liquid.isTruthy(cond)) {\n return liquid.renderer.renderTemplates(branch.templates, scope)\n }\n }\n return liquid.renderer.renderTemplates(this.elseTemplates, scope)\n }\n })\n}\n","import assert from '../util/assert.js'\nimport { create } from '../util/underscore.js'\nimport { identifier } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const { CaptureScope, AssignScope, IncrementScope } = Liquid.Types\n\n liquid.registerTag('increment', {\n parse: function (token) {\n const match = token.args.match(identifier)\n assert(match, `illegal identifier ${token.args}`)\n this.variable = match[0]\n },\n render: function (scope, hash) {\n let context = scope.findContextFor(\n this.variable,\n ctx => {\n const proto = Object.getPrototypeOf(ctx)\n return proto !== CaptureScope && proto !== AssignScope\n }\n )\n if (!context) {\n context = create(IncrementScope)\n scope.unshift(context)\n }\n if (typeof context[this.variable] !== 'number') {\n context[this.variable] = 0\n }\n const val = context[this.variable]\n context[this.variable]++\n return val\n }\n })\n}\n","import assert from '../util/assert.js'\nimport { value as rValue } from '../lexical.js'\n\n/*\n * blockMode:\n * * \"store\": store rendered html into blocks\n * * \"output\": output rendered html\n */\n\nexport default function (liquid, Liquid) {\n const staticFileRE = /\\S+/\n\n liquid.registerTag('layout', {\n parse: function (token, remainTokens) {\n let match = staticFileRE.exec(token.args)\n if (match) {\n this.staticLayout = match[0]\n }\n\n match = rValue.exec(token.args)\n if (match) {\n this.layout = match[0]\n }\n\n this.tpls = liquid.parser.parse(remainTokens)\n },\n render: async function (scope, hash) {\n const layout = scope.opts.dynamicPartials\n ? Liquid.evalValue(this.layout, scope)\n : this.staticLayout\n assert(layout, `cannot apply layout with empty filename`)\n\n // render the remaining tokens immediately\n scope.opts.blockMode = 'store'\n const html = await liquid.renderer.renderTemplates(this.tpls, scope)\n if (scope.opts.blocks[''] === undefined) {\n scope.opts.blocks[''] = html\n }\n const templates = await liquid.getTemplate(layout, scope.opts.root)\n scope.push(hash)\n scope.opts.blockMode = 'output'\n const partial = await liquid.renderer.renderTemplates(templates, scope)\n scope.pop(hash)\n return partial\n }\n })\n\n liquid.registerTag('block', {\n parse: function (token, remainTokens) {\n const match = /\\w+/.exec(token.args)\n this.block = match ? match[0] : ''\n\n this.tpls = []\n const stream = liquid.parser.parseStream(remainTokens)\n .on('tag:endblock', () => stream.stop())\n .on('template', tpl => this.tpls.push(tpl))\n .on('end', () => {\n throw new Error(`tag ${token.raw} not closed`)\n })\n stream.start()\n },\n render: async function (scope) {\n const childDefined = scope.opts.blocks[this.block]\n const html = childDefined !== undefined\n ? childDefined\n : await liquid.renderer.renderTemplates(this.tpls, scope)\n\n if (scope.opts.blockMode === 'store') {\n scope.opts.blocks[this.block] = html\n return ''\n }\n return html\n }\n })\n}\n","export default function (liquid) {\n liquid.registerTag('raw', {\n parse: function (tagToken, remainTokens) {\n this.tokens = []\n\n const stream = liquid.parser.parseStream(remainTokens)\n stream\n .on('token', token => {\n if (token.name === 'endraw') stream.stop()\n else this.tokens.push(token)\n })\n .on('end', () => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n stream.start()\n },\n render: function (scope, hash) {\n return this.tokens.map(token => token.raw).join('')\n }\n })\n}\n","import { mapSeries } from '../util/promise.js'\nimport assert from '../util/assert.js'\nimport { identifier, value, hash } from '../lexical.js'\n\nexport default function (liquid, Liquid) {\n const re = new RegExp(`^(${identifier.source})\\\\s+in\\\\s+` +\n `(${value.source})` +\n `(?:\\\\s+${hash.source})*$`)\n\n liquid.registerTag('tablerow', {\n\n parse: function (tagToken, remainTokens) {\n const match = re.exec(tagToken.args)\n assert(match, `illegal tag: ${tagToken.raw}`)\n\n this.variable = match[1]\n this.collection = match[2]\n this.templates = []\n\n let p\n const stream = liquid.parser.parseStream(remainTokens)\n .on('start', () => (p = this.templates))\n .on('tag:endtablerow', token => stream.stop())\n .on('template', tpl => p.push(tpl))\n .on('end', () => {\n throw new Error(`tag ${tagToken.raw} not closed`)\n })\n\n stream.start()\n },\n\n render: async function (scope, hash) {\n let collection = Liquid.evalExp(this.collection, scope) || []\n const offset = hash.offset || 0\n const limit = (hash.limit === undefined) ? collection.length : hash.limit\n\n collection = collection.slice(offset, offset + limit)\n const cols = hash.cols || collection.length\n const contexts = collection.map((item, i) => {\n const ctx = {}\n ctx[this.variable] = item\n return ctx\n })\n\n let row\n let html = ''\n await mapSeries(contexts, async (context, idx) => {\n row = Math.floor(idx / cols) + 1\n const col = (idx % cols) + 1\n if (col === 1) {\n if (row !== 1) {\n html += ''\n }\n html += `