{"version":3,"sources":["jsdelivr-header.js","/npm/thymeleaf@0.22.0/source/processors/AttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/AttrAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/processors/ElementProcessor.js","/npm/thymeleaf@0.22.0/source/utilities/Dom.js","/npm/thymeleaf@0.22.0/source/standard/processors/BlockElementProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/CheckedAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/ClassAppendAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/EachAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/EmptyableAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/FragmentAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/IfAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/utilities/Fragments.js","/npm/thymeleaf@0.22.0/source/standard/processors/InsertAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/RemovableAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/RemoveAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/ReplaceAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/utilities/Messages.js","/npm/thymeleaf@0.22.0/source/standard/processors/TextAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/UnlessAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/UTextAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/WithAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/standard/processors/XmlNsAttributeProcessor.js","/npm/thymeleaf@0.22.0/source/dialects/Dialect.js","/npm/thymeleaf@0.22.0/source/standard/StandardDialect.js","/npm/thymeleaf@0.22.0/source/Configurations.js","/npm/thymeleaf@0.22.0/source/processors/Matcher.js","/npm/thymeleaf@0.22.0/source/parser/InputBuffer.js","/npm/thymeleaf@0.22.0/source/parser/Parser.js","/npm/thymeleaf@0.22.0/source/standard/expressions/ExpressionProcessor.js","/npm/thymeleaf@0.22.0/source/standard/expressions/AllInput.js","/npm/thymeleaf@0.22.0/source/parser/Rule.js","/npm/thymeleaf@0.22.0/source/standard/expressions/ThymeleafRule.js","/npm/thymeleaf@0.22.0/source/parser/Grammar.js","/npm/thymeleaf@0.22.0/source/parser/Operators.js","/npm/thymeleaf@0.22.0/source/standard/expressions/FragmentSignatureGrammar.js","/npm/thymeleaf@0.22.0/source/parser/RegularExpression.js","/npm/thymeleaf@0.22.0/source/utilities/getByPath.js","/npm/thymeleaf@0.22.0/source/standard/expressions/ThymeleafExpressionLanguage.js","/npm/thymeleaf@0.22.0/source/TemplateEngine.js"],"names":[],"mappings":"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","file":"/npm/thymeleaf@0.22.0/lib/thymeleaf.browser.cjs.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/thymeleaf@0.22.0/lib/thymeleaf.browser.cjs.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Common class for attribute processors.\n * \n * @author Emanuel Rabina\n */\nexport default class AttributeProcessor {\n\n\t/**\n\t * Constructor, sets this processor's prefix and name.\n\t * \n\t * @param {string} prefix\n\t * @param {string} name\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, name, isomorphic) {\n\n\t\tthis.prefix     = prefix;\n\t\tthis.name       = name;\n\t\tthis.isomorphic = isomorphic;\n\t}\n\n\t/**\n\t * Process the given attribute on the element it appears.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} Whether or not the parent tree needs reprocessing because\n\t *   of modifications made by the processor.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\telement.removeAttribute(attribute);\n\t\tif (this.isomorphic) {\n\t\t\telement.removeAttribute(`${this.prefix}:${this.name}`);\n\t\t}\n\t\treturn false;\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nimport {escapeHtml} from '@ultraq/string-utils';\n\nexport const NAME = 'attr';\n\n/**\n * JS equivalent of Thymeleaf's `th:attr` attribute processor, modifies or sets\n * a target attribute to whatever its associated expression evaluates to.\n * \n * @author Emanuel Rabina\n */\nexport default class AttrAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `attr` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:attr` or `data-th-attr` attribute\n\t * on it, picking out the target attributes and setting them to whatever their\n\t * expressions evaluate to.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\t// TODO: This regex, is this some kind of value list that needs to be\n\t\t//       turned into an expression?\n\t\tif (/(.+=.+,)*.+=.+/.test(attributeValue)) {\n\t\t\tattributeValue.split(',').forEach(attribute => {\n\t\t\t\tlet [name, value] = attribute.split('=');\n\t\t\t\tlet processorResult = context.expressionProcessor.process(value, context);\n\t\t\t\telement.setAttribute(name, escapeHtml(\n\t\t\t\t\ttypeof processorResult === 'string' ?\n\t\t\t\t\t\tprocessorResult :\n\t\t\t\t\t\tJSON.stringify(processorResult))\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\t\t/* istanbul ignore next */\n\t\telse if (process.env.NODE_ENV !== 'test') {\n\t\t\tconsole.warn(`Value to ${attribute}, ${attributeValue}, doesn't seem to contain an attribute assignment expression.  Ignoring.`);\n\t\t}\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parent class for element processors.\n * \n * @author Emanuel Rabina\n */\nexport default class ElementProcessor {\n\n\t/**\n\t * Constructor, sets this processor's prefix and name.\n\t * \n\t * @param {string} prefix\n\t * @param {string} name\n\t */\n\tconstructor(prefix, name) {\n\n\t\tthis.prefix = prefix;\n\t\tthis.name   = name;\n\t}\n\n\t/**\n\t * Processes the given element.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {object} context\n\t * @return {boolean} Whether or not the parent tree needs reprocessing because\n\t *   of modifications made by the processor.\n\t */\n\tprocess(element, context) {\n\n\t\treturn false;\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global ENVIRONMENT */\nimport {\n\tdeserialize as domDeserialize,\n\tserialize as domSerialize\n}              from '@ultraq/dom-utils';\nimport {JSDOM} from 'jsdom';\n\n// Node.nodeType values, not present outside of a JSDOM environment so can't\n// reference them w/ Node.X\n// For a full list of values, see: https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType\nexport const NODE_TYPE_DOCUMENT_TYPE = 10;\nexport const NODE_TYPE_ELEMENT = 1;\n\n/**\n * Create and return a new HTML fragment using JSDOM from the given string.\n * Used for tests.\n * \n * @param {string} htmlString\n * @return {Element}\n */\nexport function createHtml(htmlString) {\n\treturn new JSDOM(htmlString).window.document.body.firstElementChild;\n}\n\n/**\n * Returns the value of a Thymeleaf attribute processor.\n * \n * @param {Element} element\n * @param {string} prefix\n * @param {string} processorName\n * @return {string} The value of the Thymeleaf attribute processor, or `null`\n *   if the attribute processor wasn't present.\n */\nexport function getThymeleafAttributeValue(element, prefix, processorName) {\n\treturn element.getAttribute(`${prefix}:${processorName}`) ||\n\t       element.getAttribute(`data-${prefix}-${processorName}`);\n}\n\n/**\n * Use either JSDOM or the browser's native DOM parsing to deserialize the HTML\n * string into a document fragment.\n * \n * @param {string} htmlString\n * @return {DocumentFragment}\n */\nexport function deserialize(htmlString) {\n\t/* istanbul ignore if */\n\tif (ENVIRONMENT === 'browser') {\n\t\treturn domDeserialize(htmlString);\n\t}\n\telse {\n\t\treturn new JSDOM(htmlString).window.document;\n\t}\n}\n\n/**\n * Use either JSDOM or the browser's native DOM serialization to serialize a\n * document fragment into an HTML string.\n * \n * @param {DocumentFragment} documentFragment\n * @return {string}\n */\nexport function serialize(documentFragment) {\n\t/* istanbul ignore if */\n\tif (ENVIRONMENT === 'browser') {\n\t\treturn domSerialize(documentFragment);\n\t}\n\telse {\n\t\tlet result = '';\n\t\tlet {firstChild, firstElementChild} = documentFragment;\n\t\tif (firstChild.nodeType === NODE_TYPE_DOCUMENT_TYPE) {\n\t\t\tresult += `<!DOCTYPE ${firstChild.name}>`;\n\t\t}\n\t\treturn result + firstElementChild.outerHTML;\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport ElementProcessor    from '../../processors/ElementProcessor.js';\nimport {NODE_TYPE_ELEMENT} from '../../utilities/Dom.js';\n\nexport const NAME = 'block';\n\n/**\n * Equivalent of Thymeleaf's \"synthetic tag\", `th:block`, which removes itself,\n * leaving the body of the tag behind.\n * \n * @author Emanuel Rabina\n */\nexport default class BlockElementProcessor extends ElementProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `block` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t */\n\tconstructor(prefix) {\n\n\t\tsuper(prefix, NAME);\n\t}\n\n\t/**\n\t * Processes an element named `th:block`, removing itself to leave its\n\t * body/contents behind.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {object} context\n\t * @return {boolean} `true` to indicate that the elements need reprocessing.\n\t */\n\tprocess(element, context) {\n\n\t\tlet parent = element.parentElement;\n\t\twhile (element.firstChild) {\n\t\t\tlet child = element.firstChild;\n\t\t\tparent.insertBefore(child, element);\n\n\t\t\tif (child.nodeType === NODE_TYPE_ELEMENT && element.__thymeleafLocalVariables) {\n\t\t\t\tchild.__thymeleafLocalVariables = {\n\t\t\t\t\t...(child.__thymeleafLocalVariables || {}),\n\t\t\t\t\t...element.__thymeleafLocalVariables\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tparent.removeChild(element);\n\n\t\treturn true;\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'checked';\n\n/**\n * Thymeleaf's `th:checked` attribute processor, sets or removes the `checked`\n * attribute from an element based on the result of the expression within it.\n * \n * TODO: This is one of HTML5s \"boolean attributes\", attributes whose values are\n *       true simply by being present in the element, regardless of the value\n *       inside it.  To act as false, the attribute has to be removed.  Find a\n *       way to generate these from some list of boolean attributes so that I\n *       don't need to write a class for each one!\n * \n * @author Emanuel Rabina\n */\nexport default class CheckedAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `checked` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:checked` or `data-th-checked`\n\t * attribute on it, either setting or removing a `checked` attribute to the\n\t * current element based on the result of the inner expression.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet result = context.expressionProcessor.process(attributeValue, context);\n\t\tif (result) {\n\t\t\telement.setAttribute('checked', '');\n\t\t}\n\t\telse {\n\t\t\telement.removeAttribute('checked');\n\t\t}\n\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'classappend';\n\n/**\n * The `th:classappend` is a special attribute that applies the expression to\n * any existing classes already on an element.\n * \n * @author Emanuel Rabina\n */\nexport default class ClassAppendAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `attr` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:classappend` or `data-th-classappend`\n\t * attribute on it, adding the resulting classes to any existing classes on\n\t * the current element.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet classes = context.expressionProcessor.process(attributeValue, context);\n\t\tif (classes) {\n\t\t\telement.className += ` ${classes}`;\n\t\t}\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'each';\n\n/**\n * JS equivalent of Thymeleaf's `th:each` attribute processor, iterates over an\n * [iterable object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols),\n * executing a piece of template for every iteration.\n * \n * @author Emanuel Rabina\n */\nexport default class EachAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `each` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:each`/`data-th-each` attribute,\n\t * repeating the markup for every object in the iterable value.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} Whether or not the parent element needs to do a second\n\t *   pass as its children have been modified by this processor.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tsuper.process(element, attribute, attributeValue, context);\n\n\t\tlet iterationInfo = context.expressionProcessor.process(attributeValue, context);\n\t\tif (iterationInfo) {\n\t\t\tlet {localValueName, iterable} = iterationInfo;\n\t\t\tlet templateNode = element.cloneNode(true);\n\n\t\t\tfor (let value of iterable) {\n\t\t\t\tlet localClone = templateNode.cloneNode(true);\n\t\t\t\tlocalClone.__thymeleafLocalVariables = {\n\t\t\t\t\t[localValueName]: value\n\t\t\t\t};\n\t\t\t\telement.parentElement.appendChild(localClone);\n\t\t\t}\n\t\t}\n\t\telement.parentElement.removeChild(element);\n\n\t\treturn true;\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\n/**\n * Configurable attribute processor that sets or empties an attribute value on\n * an element if the result of its expression is truthy or falsey respectively.\n * \n * @author Emanuel Rabina\n */\nexport default class EmptyableAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set the name of the attribute this processor will operate on.\n\t * \n\t * @param {string} prefix\n\t * @param {string} name\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, name, isomorphic) {\n\n\t\tsuper(prefix, name, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains the configured attribute to be worked\n\t * on, setting it if the expression resolves to a truthy value, or removing it\n\t * if it resolves to a falsey value.\n\t * \n\t * @param {Element} element \n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet value = context.expressionProcessor.process(attributeValue, context);\n\t\telement.setAttribute(this.name, value ? value.toString() : '');\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n\nexport const EMPTYABLE_ATTRIBUTE_NAMES = [\n\t'datetime',\n\t'href',\n\t'src',\n\t'style',\n\t'value'\n];\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'fragment';\n\n/**\n * JS equivalent of Thymeleaf's `th:fragment` attribute processor, marks an\n * element as a template fragment that can be imported by other processors like\n * `th:insert`.\n * \n * @author Emanuel Rabina\n */\nexport default class FragmentAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `fragment` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'if';\n\n/**\n * JS equivalent of Thymeleaf's `th:if` attribute processor, includes or\n * excludes the current element and its children from rendering, depending on\n * the evaluation of the expression in the attribute value.\n * \n * @author Emanuel Rabina\n */\nexport default class IfAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `if` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:if` or `data-th-if` attribute\n\t * on it, evaluating the expression for truthy/falsey, rendering/excluding the\n\t * element and its children based on the result.\n\t * \n\t * @param {Element} element \n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `true` if the element was removed.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet expressionResult = context.expressionProcessor.process(attributeValue, context);\n\t\tif (!expressionResult) {\n\t\t\telement.remove();\n\t\t\treturn true;\n\t\t}\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {deserialize} from './Dom.js';\nimport {NAME}        from '../standard/processors/FragmentAttributeProcessor.js';\n\nimport {$} from 'dumb-query-selector';\n\n/**\n * Extract HTML from the target identified by the given fragment information.\n * \n * @param {string} dialectPrefix\n * @param {object} fragmentInfo\n * @param {object} context\n * @return {Promise<Element>}\n */\nexport async function extractFragment(dialectPrefix, fragmentInfo, context) {\n\tlet {templateResolver} = context;\n\tif (templateResolver) {\n\t\tlet {templateName, fragmentName} = fragmentInfo;\n\t\tlet template = deserialize(await templateResolver(templateName));\n\t\treturn $(`[${dialectPrefix}\\\\:${NAME}^=\"${fragmentName}\"]`, template) ||\n\t\t\t\t\t $(`[data-${dialectPrefix}-${NAME}^=\"${fragmentName}\"]`, template);\n\t}\n\tconsole.log('No template resolver configured');\n\treturn null;\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {NAME as FragmentAttributeProcessorName} from './FragmentAttributeProcessor.js';\nimport AttributeProcessor                       from '../../processors/AttributeProcessor.js';\nimport {getThymeleafAttributeValue}             from '../../utilities/Dom.js';\nimport {extractFragment}                        from '../../utilities/Fragments.js';\n\nimport {clearChildren} from '@ultraq/dom-utils';\n\nexport const NAME = 'insert';\n\n/**\n * JS equivalent of Thymeleaf's `th:insert` attribute processor, inserts the\n * referenced template fragment as a child of the current element.\n * \n * @author Emanuel Rabina\n */\nexport default class InsertAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `insert` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:insert`/`data-th-insert` attribute,\n\t * replacing the current element's children with the DOM in the referenced\n\t * fragment.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} Whether or not the parent element needs to do a second\n\t *   pass as its children have been modified by this processor.\n\t */\n\tasync process(element, attribute, attributeValue, context) {\n\n\t\tsuper.process(element, attribute, attributeValue, context);\n\t\tclearChildren(element);\n\n\t\tlet fragmentInfo = context.expressionProcessor.process(attributeValue, context);\n\t\tif (fragmentInfo) {\n\t\t\tlet fragment = await extractFragment(this.prefix, fragmentInfo, context);\n\t\t\tif (fragment) {\n\t\t\t\tlet fragmentSignature = getThymeleafAttributeValue(fragment, this.prefix, FragmentAttributeProcessorName);\n\t\t\t\tlet {parameterNames} = context.fragmentSignatureProcessor.process(fragmentSignature, context);\n\t\t\t\tif (parameterNames) {\n\t\t\t\t\tlet {parameters} = fragmentInfo;\n\t\t\t\t\tlet localContext = {};\n\t\t\t\t\tparameterNames.forEach((parameterName, index) => {\n\t\t\t\t\t\tlocalContext[parameterName] = parameters[parameterName] || parameters[index] || null;\n\t\t\t\t\t});\n\t\t\t\t\tfragment.__thymeleafLocalVariables = localContext;\n\t\t\t\t}\n\t\t\t\telement.appendChild(fragment);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\n/**\n * Configurable attribute processor that sets or removes an attribute on an\n * element if the result of its expression is truthy or falsey respectively.\n * \n * @author Emanuel Rabina\n */\nexport default class RemovableAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set the name of the attribute this processor will operate on.\n\t * \n\t * @param {string} prefix\n\t * @param {string} name\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, name, isomorphic) {\n\n\t\tsuper(prefix, name, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains the configured attribute to be worked\n\t * on, setting it if the expression resolves to a truthy value, or removing it\n\t * if it resolves to a falsey value.\n\t * \n\t * @param {Element} element \n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet value = context.expressionProcessor.process(attributeValue, context);\n\t\tif (value) {\n\t\t\telement.setAttribute(this.name, value.toString());\n\t\t}\n\t\telse {\n\t\t\telement.removeAttribute(this.name);\n\t\t}\n\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n\nexport const REMOVABLE_ATTRIBUTE_NAMES = [\n\t'alt',\n\t'class'\n];\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'remove';\n\n/**\n * `th:remove`, used to remove the current element or select parts of it (and\n * its children).\n * \n * @author Emanuel Rabina\n */\nexport default class RemoveAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `remove` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:remove`/`data-th-remove`\n\t * attribute, removing the current element or parts of it based on the\n\t * attribute value.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} Whether reprocessing behaviour needs to be applied, only\n\t *   when the current tag has been removed.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tsuper.process(element, attribute, attributeValue, context);\n\n\t\tswitch (attributeValue) {\n\t\t\tcase 'all':\n\t\t\t\telement.parentElement.removeChild(element);\n\t\t\t\treturn true;\n\t\t\tcase 'all-but-first':\n\t\t\t\twhile (element.lastElementChild !== element.firstElementChild) {\n\t\t\t\t\telement.removeChild(element.lastElementChild);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t}\n\t}\n}\n","/*\n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {NAME as FragmentAttributeProcessorName} from './FragmentAttributeProcessor.js';\nimport AttributeProcessor                       from '../../processors/AttributeProcessor.js';\nimport {getThymeleafAttributeValue}             from '../../utilities/Dom.js';\nimport {extractFragment}                        from '../../utilities/Fragments.js';\n\nimport {clearChildren} from '@ultraq/dom-utils';\n\nexport const NAME = 'replace';\n\n/**\n * JS equivalent of Thymeleaf's `th:relace` attribute processor, replaces the\n * current element with the fragment referenced by the processor.\n * \n * @author Emanuel Rabina\n */\nexport default class ReplaceAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `replace` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:replace`/`data-th-replace`\n\t * attribute, replacing the current element with the DOM in the referenced\n\t * fragment.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} Whether or not the parent element needs to do a second\n\t *   pass as its children have been modified by this processor.\n\t */\n\tasync process(element, attribute, attributeValue, context) {\n\n\t\tsuper.process(element, attribute, attributeValue, context);\n\t\tclearChildren(element);\n\n\t\tlet fragmentInfo = context.expressionProcessor.process(attributeValue, context);\n\t\tif (fragmentInfo) {\n\t\t\tlet fragment = await extractFragment(this.prefix, fragmentInfo, context);\n\t\t\tif (fragment) {\n\t\t\t\tlet fragmentSignature = getThymeleafAttributeValue(fragment, this.prefix, FragmentAttributeProcessorName);\n\t\t\t\tlet {parameterNames} = context.fragmentSignatureProcessor.process(fragmentSignature, context);\n\t\t\t\tif (parameterNames) {\n\t\t\t\t\tlet {parameters} = fragmentInfo;\n\t\t\t\t\tlet localContext = {};\n\t\t\t\t\tparameterNames.forEach((parameterName, index) => {\n\t\t\t\t\t\tlocalContext[parameterName] = parameters[parameterName] || parameters[index] || null;\n\t\t\t\t\t});\n\t\t\t\t\tfragment.__thymeleafLocalVariables = localContext;\n\t\t\t\t}\n\n\t\t\t\t// TODO: Can simplify this with insertAdjacent*(), but need to upgrade\n\t\t\t\t//       JSDOM first.\n\t\t\t\telement.parentElement.insertBefore(fragment, element);\n\t\t\t\telement.remove();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\telement.remove();\n\t\treturn false;\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Build a message string from a processed message expression.\n * \n * @param {object} messageInfo\n * @param {Function} messageResolver\n * @return {Promise<string>}\n */\nexport async function buildMessage(messageInfo, messageResolver) {\n\tif (messageResolver) {\n\t\tlet {key, parameters} = messageInfo;\n\t\treturn await messageResolver(key, parameters) || '';\n\t}\n\tconsole.log('No message resolver configured');\n\treturn null;\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\nimport {buildMessage}     from '../../utilities/Messages.js';\n\nexport const NAME = 'text';\n\n/**\n * JS equivalent of Thymeleaf's `th:text` attribute processor, applies the\n * expression in the attribute value to the text content of the element being\n * processed, escaping any unsafe input.\n * \n * @author Emanuel Rabina\n */\nexport default class TextAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `text` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:text` or `data-th-text` attribute\n\t * on it, taking the text expression in the value and applying it to the text\n\t * content of the element.\n\t * \n\t * @param {Element} element \n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tasync process(element, attribute, attributeValue, context) {\n\n\t\t// TODO: Move message constructon to the expression language?  Need to make\n\t\t//       all the executions async!\n\t\tlet messageResult = context.expressionProcessor.process(attributeValue, context);\n\t\telement.textContent =\n\t\t\ttypeof messageResult === 'object' ? await buildMessage(messageResult, context.messageResolver) :\n\t\t\tmessageResult;\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nimport {clearChildren} from '@ultraq/dom-utils';\n\nexport const NAME = 'unless';\n\n/**\n * JS equivalent of Thymeleaf's `th:unless` attribute processor, excludes or\n * includes the current element and its children from rendering, depending on\n * the evaluation of the expression in the attribute value.\n * \n * @author Robbie Bardijn\n */\nexport default class UnlessAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `unless` name and supplied prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:unless` or `data-th-unless` attribute\n\t * on it, evaluating the expression for falsey/truthy, excluding/rendering the\n\t * element and its children based on the result.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `true` if the element was removed.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tlet expressionResult = context.expressionProcessor.process(attributeValue, context);\n\t\tif (expressionResult) {\n\t\t\tclearChildren(element);\n\t\t\t// TODO: element.remove()?\n\t\t\telement.parentNode.removeChild(element);\n\t\t\treturn true;\n\t\t}\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'utext';\n\n/**\n * JS equivalent of Thymeleaf's `th:utext` attribute processor, applies the\n * expression in the attribute value to the text content of the element being\n * processed.\n * \n * @author Emanuel Rabina\n */\nexport default class UTextAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `utext` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:utext` or `data-th-utext`\n\t * attribute on it, taking the text expression in the value and applying it to\n\t * the text content of the element.\n\t *\n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\telement.innerHTML = context.expressionProcessor.process(attributeValue, context);\n\t\treturn super.process(element, attribute, attributeValue, context);\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\nexport const NAME = 'with';\n\n/**\n * `th:with`, used for creating scoped variables, useful for aliasing things.\n * \n * @author Emanuel Rabina\n */\nexport default class WithAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to use the `with` name and supplied\n\t * prefix.\n\t * \n\t * @param {string} prefix\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper(prefix, NAME, isomorphic);\n\t}\n\n\t/**\n\t * Processes an element that contains a `th:with`/`data-th-with` attribute,\n\t * setting a variable scoped to the current element with the given name.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `true` as adding new local variables needs to re-run\n\t *   processing.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\tsuper.process(element, attribute, attributeValue, context);\n\n\t\tlet localVariables = {};\n\t\tlet aliases = context.expressionProcessor.process(attributeValue, context);\n\t\taliases.forEach(({name, value}) => {\n\t\t\tlocalVariables[name] = value;\n\t\t});\n\t\telement.__thymeleafLocalVariables = localVariables;\n\n\t\treturn true;\n\t}\n}\n","/* \n * Copyright 2020, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from '../../processors/AttributeProcessor.js';\n\n/**\n * `xmlns:th`, used for removing the Thymeleaf XML namespace often added to HTML\n * files to satisfy the XML validators that are used to edit them.\n * \n * @author Emanuel Rabina\n */\nexport default class XmlNsAttributeProcessor extends AttributeProcessor {\n\n\t/**\n\t * Constructor, set this processor to operate on the given XML namespace.\n\t * \n\t * @param {string} prefix\n\t * @param {object} isomorphic\n\t */\n\tconstructor(prefix, isomorphic) {\n\n\t\tsuper('xmlns', prefix);\n\t\tthis.isomorphic = isomorphic;\n\t}\n\n\t/**\n\t * Removes the XML namespace from an element.\n\t * \n\t * @param {Element} element\n\t *   Element being processed.\n\t * @param {string} attribute\n\t *   The attribute that was encountered to invoke this processor.\n\t * @param {string} attributeValue\n\t *   The value given by the attribute.\n\t * @param {object} context\n\t * @return {boolean} `false`, as removing the XML namespace never requires\n\t *   repropcessing.\n\t */\n\tprocess(element, attribute, attributeValue, context) {\n\n\t\telement.removeAttribute(attribute);\n\t\tif (this.isomorphic) {\n\t\t\telement.removeAttribute(`xmlns:${this.isomorphic.prefix}`);\n\t\t}\n\t\treturn false;\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Common class for dialects.\n * \n * @author Emanuel Rabina\n */\nexport default class Dialect {\n\n\t/**\n\t * Constructor, sets this dialect's name and optional prefix.\n\t * \n\t * @param {string} name\n\t * @param {string} [prefix]\n\t */\n\tconstructor(name, prefix) {\n\n\t\tthis.name   = name;\n\t\tthis.prefix = prefix;\n\t}\n\n\t/**\n\t * Return an object whose keys are the expression object names, the values the\n\t * expression object available properties and methods.\n\t * \n\t * @return {object}\n\t */\n\tget expressionObjects() {\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return an array of processors.\n\t * \n\t * @return {Array}\n\t */\n\tget processors() {\n\n\t\treturn null;\n\t}\n}\n","/*\n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttrAttributeProcessor        from './processors/AttrAttributeProcessor.js';\nimport BlockElementProcessor         from './processors/BlockElementProcessor.js';\nimport CheckedAttributeProcessor     from './processors/CheckedAttributeProcessor.js';\nimport ClassAppendAttributeProcessor from './processors/ClassAppendAttributeProcessor.js';\nimport EachAttributeProcessor        from './processors/EachAttributeProcessor.js';\nimport EmptyableAttributeProcessor, {\n\tEMPTYABLE_ATTRIBUTE_NAMES\n}                                    from './processors/EmptyableAttributeProcessor.js';\nimport FragmentAttributeProcessor    from './processors/FragmentAttributeProcessor.js';\nimport IfAttributeProcessor          from './processors/IfAttributeProcessor.js';\nimport InsertAttributeProcessor      from './processors/InsertAttributeProcessor.js';\nimport RemovableAttributeProcessor, {\n\tREMOVABLE_ATTRIBUTE_NAMES\n}                                    from './processors/RemovableAttributeProcessor.js';\nimport RemoveAttributeProcessor      from './processors/RemoveAttributeProcessor.js';\nimport ReplaceAttributeProcessor     from './processors/ReplaceAttributeProcessor.js';\nimport TextAttributeProcessor        from './processors/TextAttributeProcessor.js';\nimport UnlessAttributeProcessor      from './processors/UnlessAttributeProcessor.js';\nimport UTextAttributeProcessor       from './processors/UTextAttributeProcessor.js';\nimport WithAttributeProcessor        from './processors/WithAttributeProcessor.js';\nimport XmlNsAttributeProcessor       from './processors/XmlNsAttributeProcessor.js';\nimport Dialect                       from '../dialects/Dialect.js';\n\nexport const NAME = 'Standard';\nexport const DEFAULT_PREFIX = 'thjs';\n\n/**\n * The out-of-the-box dialect for Thymeleaf, the \"Standard Dialect\".\n * \n * @author Emanuel Rabina\n */\nexport default class StandardDialect extends Dialect {\n\n\t/**\n\t * Create an instance of this dialect with the name \"Standard\" and given\n\t * prefix.\n\t * \n\t * @param {string} [prefix]\n\t * @param {object} [isomorphic]\n\t */\n\tconstructor(prefix = DEFAULT_PREFIX, isomorphic) {\n\n\t\tsuper(NAME, prefix);\n\t\tthis.isomorphic = isomorphic;\n\t}\n\n\t/**\n\t * Returns the supported standard processors.\n\t * \n\t * @return {Array} A list of the processors included in this dialect.\n\t */\n\tget processors() {\n\n\t\t// TODO: This is a very basic way of imposing the order of attribute\n\t\t//       processors.  It's currently ordered in the same way as OG\n\t\t//       Thymeleaf.  Figure out a 'proper' way to do the ordering.\n\n\t\t// Order taken from https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#attribute-precedence\n\t\tlet {prefix, isomorphic} = this;\n\t\treturn [\n\t\t\t// Fragment inclusion\n\t\t\tnew InsertAttributeProcessor(prefix, isomorphic),\n\t\t\tnew ReplaceAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Fragment iteration\n\t\t\tnew EachAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Conditional evaluation\n\t\t\tnew IfAttributeProcessor(prefix, isomorphic),\n\t\t\tnew UnlessAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Local variable definition\n\t\t\tnew WithAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// General attribute modification\n\t\t\tnew AttrAttributeProcessor(prefix, isomorphic),\n\t\t\tnew ClassAppendAttributeProcessor(prefix, isomorphic),\n\t\t\t...EMPTYABLE_ATTRIBUTE_NAMES.map(attributeName => {\n\t\t\t\treturn new EmptyableAttributeProcessor(prefix, attributeName, isomorphic);\n\t\t\t}),\n\t\t\t...REMOVABLE_ATTRIBUTE_NAMES.map(attributeName => {\n\t\t\t\treturn new RemovableAttributeProcessor(prefix, attributeName, isomorphic);\n\t\t\t}),\n\n\t\t\t// Specific attribute modification\n\t\t\tnew CheckedAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Text\n\t\t\tnew TextAttributeProcessor(prefix, isomorphic),\n\t\t\tnew UTextAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Fragment specification\n\t\t\tnew FragmentAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Fragment removal\n\t\t\tnew RemoveAttributeProcessor(prefix, isomorphic),\n\n\t\t\t// Element processors\n\t\t\tnew BlockElementProcessor(prefix),\n\n\t\t\t// Misc\n\t\t\tnew XmlNsAttributeProcessor(prefix, isomorphic)\n\t\t];\n\t}\n}\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport StandardDialect from './standard/StandardDialect.js';\n\n/**\n * Configuration object for the template engine.\n * \n * @typedef {object} Configuration\n * @property {Array<Dialect>} dialects\n *   A list of dialects to include with this instance of the template engine.\n * @property {object} [isomorphic]\n *   An object which configures the isomorphic capabilities of the template\n *   engine.\n * @property {Function} messageResolver\n *   A function for building a message string from some external source, given a\n *   message key and optional parameters for that particular message.\n * @property {Function} templateResolver\n *   A function for returning the text of templates named by fragment\n *   expressions in templates.  Is given only 1 argument, the template name from\n *   a fragment expression, and should return a Promise of the template text.\n */\n\n/**\n * Default configuration for the template engine, configures the standard\n * dialect with no options (uses `thjs` as the prefix).\n * \n * @type {Configuration}\n */\nexport const DEFAULT_CONFIGURATION = {\n\tdialects: [\n\t\tnew StandardDialect()\n\t]\n};\n\n/**\n * Standard configuration, configures the standard dialect with the `th` prefix\n * and enables isomorphic mode which enables the ability to use much of the same\n * processors across original Thymeleaf and ThymeleafJS.\n * \n * @type {Configuration}\n */\nexport const STANDARD_CONFIGURATION = {\n\t...DEFAULT_CONFIGURATION,\n\tdialects: [\n\t\tnew StandardDialect('th', {\n\t\t\tprefix: 'thjs'\n\t\t})\n\t]\n};\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport AttributeProcessor from './AttributeProcessor.js';\nimport ElementProcessor   from './ElementProcessor.js';\n\n/**\n * Class for determining if an element contains a processor on it.\n * \n * @author Emanuel Rabina\n */\nexport default class Matcher {\n\n\t/**\n\t * Return the matching attribute or element that a processor can work over.\n\t * \n\t * @param {Element} element\n\t * @param {AttributeProcessor} processor\n\t * @return {string}\n\t *   A match result containing what was matched (either an attribute or an\n\t *   element, relevant to the processor being tested), or `null` if nothing\n\t *   was matched.\n\t */\n\tmatches(element, processor) {\n\n\t\tlet {isomorphic, name, prefix} = processor;\n\n\t\t// Attribute processor matching, can be of the name prefix:name or data-prefix-name\n\t\tif (processor instanceof AttributeProcessor) {\n\t\t\tlet prefixes = [prefix];\n\t\t\tif (isomorphic) {\n\t\t\t\tprefixes.unshift(isomorphic.prefix);\n\t\t\t}\n\t\t\tfor (let prefix of prefixes) {\n\t\t\t\tlet attribute;\n\t\t\t\tattribute = `${prefix}:${name}`;\n\t\t\t\tif (element.hasAttribute(attribute)) {\n\t\t\t\t\treturn attribute;\n\t\t\t\t}\n\t\t\t\tattribute = `data-${prefix}-${name}`;\n\t\t\t\tif (element.hasAttribute(attribute)) {\n\t\t\t\t\treturn attribute;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Element processor, can only be of the name prefix:name\n\t\telse if (processor instanceof ElementProcessor) {\n\t\t\tlet elementName = `${processor.prefix}:${name}`;\n\t\t\tif (element.tagName === elementName.toUpperCase()) {\n\t\t\t\treturn elementName;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Wrapper around the string being parsed, with a position that keeps track of\n * what part of the imput is currently being read/tested.\n * \n * @author Emanuel Rabina\n */\nexport default class InputBuffer {\n\n\t/**\n\t * @private\n\t * @type {number}\n\t */\n\tposition = 0;\n\n\t/**\n\t * @private\n\t * @type {Array<number>}\n\t */\n\tpositionStack = [];\n\n\t/**\n\t * @param {string} input\n\t */\n\tconstructor(input) {\n\n\t\tthis.input = input;\n\t}\n\n\t/**\n\t * Clear the previously {@link #mark}ed position.\n\t */\n\tclear() {\n\n\t\tlet lastPosition = this.positionStack.pop();\n\t\tif (lastPosition === undefined) {\n\t\t\tthrow new Error('Called clear() but no matching mark()');\n\t\t}\n\t}\n\n\t/**\n\t * Returns whether or not the current position is at the end of the input,\n\t * meaning we've exhausted the entire input string.\n\t * \n\t * @return {boolean}\n\t */\n\texhausted() {\n\n\t\treturn this.position === this.input.length;\n\t}\n\n\t/**\n\t * Sets a mark at the current position so that it can be returned to by a\n\t * matching {@link #reset} call.\n\t */\n\tmark() {\n\n\t\tthis.positionStack.push(this.position);\n\t}\n\n\t/**\n\t * Convenience method for surrounding a function with a call to {@link #mark},\n\t * then {@link #clear} if the result of the function is non-null, or\n\t * {@link #reset} if `null`.\n\t * \n\t * @template T\n\t * @param {Function<T>} func\n\t * @return {T}\n\t */\n\tmarkAndClearOrReset(func) {\n\n\t\tthis.mark();\n\t\tlet result = func();\n\t\tif (result !== null) {\n\t\t\tthis.clear();\n\t\t\treturn result;\n\t\t}\n\t\tthis.reset();\n\t\treturn null;\n\t}\n\n\t/**\n\t * Reads as many characters from the current position as satisfies the given\n\t * pattern, returning the read characters and advancing the mark by as many\n\t * characters.\n\t * \n\t * @param {RegExp} pattern\n\t * @return {Array} The array of matched strings, or `null` if the pattern\n\t *   didn't match.\n\t */\n\tread(pattern) {\n\n\t\tlet remaining = this.input.substring(this.position);\n\t\tlet leadingWhitespace = remaining.match(/^\\s+/);\n\t\tif (leadingWhitespace) {\n\t\t\tleadingWhitespace = leadingWhitespace[0];\n\t\t\tremaining = remaining.substring(leadingWhitespace.length);\n\t\t}\n\t\tlet result = new RegExp(pattern.source).exec(remaining);\n\t\tif (result) {\n\t\t\tlet [value] = result;\n\t\t\tif (remaining.startsWith(value)) {\n\t\t\t\tthis.position += (value.length + (leadingWhitespace ? leadingWhitespace.length : 0));\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Revert to the last @{link #mark}ed position.\n\t */\n\treset() {\n\n\t\tlet newPosition = this.positionStack.pop();\n\t\tif (newPosition === undefined) {\n\t\t\tthrow new Error('Called reset() but no matching mark()');\n\t\t}\n\t\tthis.position = newPosition;\n\t}\n}\n","/*\n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport InputBuffer from './InputBuffer';\n\n/**\n * Any one of the objects that can be matched:\n *  - an expression function\n *  - a string that references another rule\n *  - a regular expression\n * \n * @typedef {string | RegExp | Function} Matchable\n */\n\n/**\n * A recursive descent parser for any parsing expression grammar defined by the\n * constructs in this module.\n * \n * TODO: Own module?\n * \n * @author Emanuel Rabina\n */\nexport default class Parser {\n\n\texpressionStack = [];\n\n\t/**\n\t * @param {Grammar} grammar\n\t */\n\tconstructor(grammar) {\n\n\t\tthis.grammar = grammar;\n\t}\n\n\t/**\n\t * Parse a string, attempting to build the parse tree defined by the rules in\n\t * the configured grammar.  Parsing is considered successful when there are no\n\t * more non-terminating symbols in the grammar and all of the input has been\n\t * read.\n\t * \n\t * @param {string} input\n\t * @return {object} The parse tree if the input could be parsed, `null`\n\t *   otherwise.\n\t */\n\tparse(input) {\n\n\t\tlet inputBuffer = new InputBuffer(input);\n\t\tlet matchResult = this.grammar.accept(inputBuffer, this);\n\t\tif (matchResult === null || !inputBuffer.exhausted()) {\n\t\t\tlet errorMessage = `Failed to parse \"${input}\"`;\n\n\t\t\t// Don't bring down the thread if we can't parse\n\t\t\tif (process.env.NODE_ENV === 'production') {\n\t\t\t\tconsole.error(errorMessage);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\t\t}\n\t\treturn matchResult;\n\t}\n\n\t/**\n\t * Parse the input against the given expression.  An expression can either be\n\t * a reference to another rule in the current grammar, or a regular expression\n\t * that consumes input.\n\t * \n\t * @param {InputBuffer} input\n\t * @param {Matchable} expression\n\t * @return {object}\n\t */\n\tparseWithExpression(input, expression) {\n\n\t\t// Name of another rule in the grammar\n\t\tif (typeof expression === 'string') {\n\t\t\tlet rule = this.grammar.findRuleByName(expression);\n\t\t\treturn rule ? rule.accept(input, this) : null;\n\t\t}\n\n\t\t// A regular expression that must be matched\n\t\telse if (expression instanceof RegExp) {\n\t\t\tlet result = input.read(expression);\n\t\t\tif (result) {\n\t\t\t\treturn result[0];\n\t\t\t}\n\t\t}\n\n\t\t// An expression function to be executed\n\t\telse if (typeof expression === 'function') {\n\t\t\treturn expression(input, this);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Surrounds a function that evaluates an expression with tracking it against\n\t * the current stack.  Useful for knowing how the current expression was\n\t * arrived at through the grammar's rules.\n\t * \n\t * @template T\n\t * @param {InputBuffer} input\n\t * @param {Matchable} expression\n\t * @param {string} name\n\t * @param {Function<T>} func\n\t * @return {T}\n\t */\n\ttrackExpression(input, expression, name, func) {\n\n\t\tlet stackSize = this.expressionStack.push({\n\t\t\tname,\n\t\t\texpression: typeof expression !== 'function' ? expression.toString() : null,\n\t\t\tinput: input.input.substring(input.position)\n\t\t});\n\t\tlet result = func();\n\t\tif (result !== null) {\n\t\t\treturn result;\n\t\t}\n\t\tthis.expressionStack.splice(stackSize - 1);\n\t\treturn null;\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Parser from '../../parser/Parser.js';\n\n/**\n * Parses and executes Thymeleaf expressions.\n * \n * TODO: Create a shared instance of this for a processing context so that it\n *       doesn't need to be recreated over and over.\n * \n * @author Emanuel Rabina\n */\nexport default class ExpressionProcessor {\n\n\t/**\n\t * Constructor, create a new processor that can parse/execute a string in the\n\t * given grammar.\n\t * \n\t * @param {Grammar} grammar\n\t */\n\tconstructor(grammar) {\n\n\t\tthis.parser = new Parser(grammar);\n\t}\n\n\t/**\n\t * Parse and execute the given input as a Thymeleaf expression.\n\t * \n\t * @param {string} input\n\t * @param {object} [context]\n\t * @return {*}\n\t */\n\tprocess(input, context = {}) {\n\n\t\tlet expression = this.parser.parse(input);\n\t\treturn expression?.({\n\t\t\t...context,\n\t\t\texpression: input\n\t\t});\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A special kind of expression that requires the referenced rule consume all\n * available input.\n * \n * @param {string} ruleName\n * @return {Matchable}\n */\nexport const AllInput = ruleName => (input, parser) => {\n\tlet matchResult = parser.parseWithExpression(input, ruleName);\n\treturn matchResult !== null && input.exhausted() ? matchResult : null;\n};\n","/*\n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Default processor which returns the result as is.\n * \n * @template T\n * @param {T} result\n * @return {T}\n */\nconst defaultMatchProcessor = result => result;\n\n/**\n * A rule describes a string in the language.\n * \n * @author Emanuel Rabina\n */\nexport default class Rule {\n\n\t/**\n\t * @param {string} name\n\t * @param {object} expression\n\t * @param {Function} [matchProcessor]\n\t */\n\tconstructor(name, expression, matchProcessor = defaultMatchProcessor) {\n\n\t\tthis.name           = name;\n\t\tthis.expression     = expression;\n\t\tthis.matchProcessor = matchProcessor;\n\t}\n\n\t/**\n\t * Given an input string and a parser, return whether or not the input is\n\t * accepted by this rule.\n\t * \n\t * @param {InputBuffer} input\n\t * @param {Parser} parser\n\t * @return {object} If the input is accepted, this will be the non-null result\n\t *   of matching against the rule.\n\t */\n\taccept(input, parser) {\n\n\t\tlet {expression, name} = this;\n\t\treturn parser.trackExpression(input, expression, name, () => {\n\t\t\tlet matchResult = parser.parseWithExpression(input, expression);\n\t\t\treturn matchResult !== null ? this.matchProcessor(matchResult) : null;\n\t\t});\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Rule from '../../parser/Rule.js';\n\n/**\n * A custom rule where the default match processor returns a function to execute\n * against the current context to discover the matched value.\n */\nexport default class ThymeleafRule extends Rule {\n\n\t/**\n\t * @param {string} name\n\t * @param {object} expression\n\t * @param {Function} [matchProcessor]\n\t */\n\tconstructor(name, expression, matchProcessor) {\n\n\t\tconst contextSensitiveMatchProcessor = result => (...args) => {\n\t\t\t// console.log(`Processing rule: ${name}`);\n\t\t\treturn typeof result === 'function' ? result.apply(null, args) : result;\n\t\t};\n\t\tsuper(name, expression, matchProcessor || contextSensitiveMatchProcessor);\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A collection of Rules that describes a language.\n * \n * @author Emanuel Rabina\n */\nexport default class Grammar {\n\n\t/**\n\t * @param {string} name\n\t * @param {Rule} startingRule\n\t * @param {...Rule} additionalRules\n\t */\n\tconstructor(name, startingRule, ...additionalRules) {\n\n\t\tthis.name = name;\n\t\tthis.rules = [].concat(startingRule, additionalRules);\n\t}\n\n\t/**\n\t * Given an input string and a parser, return whether or not the input is\n\t * accepted by this grammar.\n\t * \n\t * @param {InputBuffer} input\n\t * @param {Parser} parser\n\t * @return {object} If the input is accepted, this will be the non-null result\n\t *   of matching against the rules of this grammar.\n\t */\n\taccept(input, parser) {\n\n\t\treturn this.startingRule.accept(input, parser);\n\t}\n\n\t/**\n\t * Return the rule that has the given name.\n\t * \n\t * @param {string} name\n\t * @return {Rule}\n\t */\n\tfindRuleByName(name) {\n\n\t\tlet rule = this.rules.find(rule => rule.name === name);\n\t\tif (!rule) {\n\t\t\tthrow new Error(`Failed to find a rule named \"${name}\" in the grammar`);\n\t\t}\n\t\treturn rule;\n\t}\n\n\t/**\n\t * Returns the grammar's starting rule.\n\t * \n\t * @return {Rule}\n\t */\n\tget startingRule() {\n\n\t\treturn this.rules[0];\n\t}\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Returns an expression function where the underlying expression doesn't need\n * to be matched.  Thus, optional expressions \"always\" match.\n * \n * @param {Matchable} expression\n * @return {Matchable}\n */\nexport const Optional = expression => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tlet result = parser.parseWithExpression(input, expression);\n\t\treturn result !== null ? result : '';\n\t});\n};\n\n/**\n * Returns an expression function where the expression must be matched against\n * at least once to be considered a match.\n * \n * @param {Matchable} expression\n * @return {Matchable}\n */\nexport const OneOrMore = (expression) => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tlet results = [];\n\t\twhile (true) {\n\t\t\tlet result = input.markAndClearOrReset(() => {\n\t\t\t\treturn parser.parseWithExpression(input, expression);\n\t\t\t});\n\t\t\tif (result) {\n\t\t\t\tresults.push(result);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn results.length > 0 ? results : null;\n\t});\n};\n\n/**\n * Returns an expression function where only one of the underlying expressions\n * must be matched in order to consider the expression a match.\n * \n * @param {...Matchable} expressions\n * @return {Matchable}\n */\nexport const OrderedChoice = (...expressions) => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tfor (let expression of expressions) {\n\t\t\tlet result = input.markAndClearOrReset(() => {\n\t\t\t\treturn parser.parseWithExpression(input, expression);\n\t\t\t});\n\t\t\tif (result !== null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t});\n};\n\n/**\n * Returns an expression whose underlying expressions must be matched in order\n * to consider the entire expression a match.\n * \n * @param {...Matchable} expressions\n * @return {Matchable}\n */\nexport const Sequence = (...expressions) => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tlet results = [];\n\t\tfor (let expression of expressions) {\n\t\t\tlet result = input.markAndClearOrReset(() => {\n\t\t\t\treturn parser.parseWithExpression(input, expression);\n\t\t\t});\n\t\t\tif (result === null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tresults.push(result);\n\t\t}\n\t\treturn results;\n\t});\n};\n\n/**\n * Returns an expression function where the expression can be matched a repeated\n * number of times or none at all to be considered a match.\n * \n * @param {Matchable} expression\n * @return {Matchable}\n */\nexport const ZeroOrMore = (expression) => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tlet results = [];\n\t\twhile (true) {\n\t\t\tlet result = input.markAndClearOrReset(() => {\n\t\t\t\treturn parser.parseWithExpression(input, expression);\n\t\t\t});\n\t\t\tif (result) {\n\t\t\t\tresults.push(result);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t});\n};\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {AllInput}                       from './AllInput.js';\nimport ThymeleafRule                    from './ThymeleafRule.js';\nimport Grammar                          from '../../parser/Grammar.js';\nimport {Optional, Sequence, ZeroOrMore} from '../../parser/Operators.js';\n\nimport {flatten} from '@ultraq/array-utils';\n\n/**\n * Grammar for Thymeleaf fragment signatures.  This is separate from the\n * expression language as these are not expressions, but rather marker syntaxes.\n * \n * @author Emanuel Rabina\n */\nexport default new Grammar('Thymeleaf fragment signature',\n\n\tnew ThymeleafRule('ThymeleafFragmentSignatureRule',\n\t\tAllInput('FragmentSignature')\n\t),\n\n\t/**\n\t * The target end of a fragment expression, contains the fragment name and\n\t * optional parameter names.\n\t */\n\tnew ThymeleafRule('FragmentSignature',\n\t\tSequence('FragmentName', Optional('FragmentParameters')),\n\t\t([fragmentName, parameterNames]) => context => {\n\t\t\treturn {\n\t\t\t\tfragmentName: fragmentName(context),\n\t\t\t\tparameterNames: parameterNames ? parameterNames(context) : null\n\t\t\t};\n\t\t}\n\t),\n\tnew ThymeleafRule('FragmentName', /[\\w-._]+/),\n\tnew ThymeleafRule('FragmentParameters',\n\t\tSequence(/\\(/, ZeroOrMore('FragmentParameterNames'), /\\)/),\n\t\t([, [parameterNames]]) => context => {\n\t\t\treturn parameterNames(context);\n\t\t}\n\t),\n\tnew ThymeleafRule('FragmentParameterNames',\n\t\tSequence('Identifier', ZeroOrMore(Sequence(/,/, 'Identifier'))),\n\t\t(namesAndSeparators) => context => {\n\t\t\treturn namesAndSeparators ?\n\t\t\t\tflatten(namesAndSeparators)\n\t\t\t\t\t.filter(item => item !== ',')\n\t\t\t\t\t.map(name => name(context)) :\n\t\t\t\t[];\n\t\t}\n\t),\n\n\n\t// Common language basics\n\t// ======================\n\n\tnew ThymeleafRule('Identifier', /[#a-zA-Z_][\\w]*/)\n);\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport InputBuffer from './InputBuffer';\n\n/**\n * A special kind of expression that understands matched portions of regular\n * expressions to run processing over, which may lead to additional parsing\n * expressions.\n * \n * This expression should be used sparingly as the regexes within need to take\n * care of whitespace between tokens themselves, which can be annoying.\n * \n * @param {RegExp} expression\n * @param {Array<Matchable>} matchers\n * @return {Matchable}\n */\nexport const RegularExpression = (expression, matchers) => (input, parser) => {\n\treturn input.markAndClearOrReset(() => {\n\t\tlet result = input.read(expression);\n\t\tif (result) {\n\t\t\tlet parseResults = [result[0]];\n\t\t\tfor (let i = 1; i < result.length; i++) {\n\t\t\t\tlet match = result[i];\n\t\t\t\tif (match !== undefined) {\n\t\t\t\t\tlet parseResult = parser.parseWithExpression(new InputBuffer(match), matchers[i - 1]);\n\t\t\t\t\tif (parseResult === null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tparseResults.push(parseResult);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parseResults;\n\t\t}\n\t\treturn null;\n\t});\n};\n","/**\n *\n * @param {object|Array} obj\n * @param {string} path\n * @return {*}\n */\nexport function getByPath(obj, path) {\n\tconst paths = getPathArray(path);\n\tlet requestedObject = Array.isArray(obj) ? [...obj] : {...obj};\n\n\tfor (let i = 0; i < paths.length; i++) {\n\t\ttry {\n\t\t\tif (requestedObject[paths[i]] === undefined) {\n\t\t\t\trequestedObject = null;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\trequestedObject = requestedObject[paths[i]];\n\t\t}\n\t\tcatch (e) {\n\t\t\trequestedObject = null;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn requestedObject;\n}\n\n/**\n * Helper function to convert array like path string into array\n *\n * @param {string} path\n * @return {Array<string>}\n */\nexport function getPathArray(path) {\n\treturn path.split('.').reduce((acc, el) => {\n\t\treturn acc.concat(el.replace(/[\"']/g, '')\n\t\t\t.split('[')\n\t\t\t.filter(i => i !== '')\n\t\t\t.map(i => i.replace(']', '')));\n\t}, []);\n}\n","/* \n * Copyright 2018, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {AllInput}          from './AllInput.js';\nimport ExpressionProcessor from './ExpressionProcessor.js';\nimport ThymeleafRule       from './ThymeleafRule.js';\nimport Grammar             from '../../parser/Grammar.js';\nimport {\n\tOptional,\n\tOneOrMore,\n\tOrderedChoice,\n\tSequence,\n\tZeroOrMore\n}                          from '../../parser/Operators.js';\nimport {RegularExpression} from '../../parser/RegularExpression.js';\nimport {getByPath}       from '../../utilities/getByPath';\n\nimport {flatten, remove} from '@ultraq/array-utils';\n\n// For helping identify rules that return objects\nconst METADATA_FRAGMENT  = 'fragment';\nconst METADATA_ITERATION = 'iteration';\nconst METADATA_MESSAGE   = 'message';\n\n/**\n * Grammar for the Thymeleaf expression language.  Describes the language and\n * how to parse it.\n * \n * @author Emanuel Rabina\n */\nconst ThymeleafExpressionLanguage = new Grammar('Thymeleaf Expression Language',\n\n\t// Ordered as at https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax\n\tnew ThymeleafRule('ThymeleafExpression',\n\t\tOrderedChoice(\n\t\t\tAllInput('VariableExpression'),\n\t\t\tAllInput('MessageExpression'),\n\t\t\tAllInput('LinkExpression'),\n\t\t\tAllInput('FragmentExpression'),\n\t\t\tAllInput('Iteration'),\n\t\t\tAllInput('StringConcatenation'),\n\t\t\tAllInput('ScopedVariables'),\n\t\t\tAllInput('Literal'),\n\t\t\tAllInput('LogicalExpression'),\n\t\t\tAllInput('IfThenCondition'),\n\t\t\tAllInput('IfThenElseCondition'),\n\t\t\tAllInput('TokenLiteral'),\n\t\t\tAllInput('Nothing')\n\t\t)\n\t),\n\n\n\t// Simple expressions\n\t// ==================\n\n\t/**\n\t * Variable expressions, `${variable}`.  Represents a value to be retrieved\n\t * from the current context.  Also is an entry into the underlying expression\n\t * language, so this part often extends to do what OGNL (and thus SpEL) can\n\t * do.\n\t */\n\n\tnew ThymeleafRule('VariableExpression',\n\t\tSequence(/\\${/, 'Chain', /\\}/),\n\t\t([, chain]) => context => {\n\t\t\tlet result = chain(context);\n\t\t\treturn result !== null && result !== undefined ? result : '';\n\t\t}\n\t),\n\tnew ThymeleafRule('Chain',\n\t\tSequence(Optional('Negation'), 'ChainLink', ZeroOrMore(Sequence(/\\./, 'ChainLink'))),\n\t\t([negation, ...chain]) => context => {\n\t\t\tlet result = flatten(chain).filter(link => link !== '.').reduce((linkContext, nextLink) => {\n\t\t\t\tif (linkContext === null || linkContext === undefined) {\n\t\t\t\t\treturn linkContext;\n\t\t\t\t}\n\t\t\t\treturn nextLink(linkContext, context);\n\t\t\t}, context);\n\t\t\t// TODO: Need a better way of applying negation - this fails when I\n\t\t\t//       introduce the 'not' keyword\n\t\t\treturn typeof negation === 'function' ? !result : result;\n\t\t}\n\t),\n\tnew ThymeleafRule('ChainLink',\n\t\tOrderedChoice('MethodCall', 'ArrayIndex', 'PropertyName', 'Literal')\n\t),\n\n\t/**\n\t * Message expressions, `#{messageKey(parameters)}`.  Used for referencing\n\t * text from a resource file, often for localization purposes.\n\t */\n\tnew ThymeleafRule('MessageExpression',\n\t\tSequence(/#{/, 'MessageKey', Optional(Sequence(/\\(/, Sequence('Expression', ZeroOrMore(Sequence(/,/, 'Expression'))), /\\)/)), /}/),\n\t\t([, messageKey, [, messageParameters]]) => context => {\n\t\t\treturn {\n\t\t\t\ttype: METADATA_MESSAGE,\n\t\t\t\tkey: messageKey(context),\n\t\t\t\tparameters: messageParameters ?\n\t\t\t\t\tflatten(messageParameters)\n\t\t\t\t\t\t.filter(messageParameter => typeof messageParameter === 'function')\n\t\t\t\t\t\t.map(messageParameter => messageParameter(context)) :\n\t\t\t\t\tnull\n\t\t\t};\n\t\t}\n\t),\n\tnew ThymeleafRule('MessageKey', /[\\w.-]+/),\n\n\t/**\n\t * Link expressions, `@{url(parameters)}`.  Used for generating URLs out of\n\t * context parameters.\n\t * \n\t * TODO: Change this to use the other expression types so I can remove this\n\t *       bespoke operator.\n\t */\n\tnew ThymeleafRule('LinkExpression',\n\t\tRegularExpression(/^@\\{(.+?)(\\(.+\\))?\\}$/, ['Url', 'UrlParameters']),\n\t\t([, urlFunc, parameters]) => context => {\n\n\t\t\tlet url = urlFunc(context);\n\t\t\tif (parameters) {\n\n\t\t\t\t// TODO: Push this parsing of the parameters list back into the grammar\n\t\t\t\tlet expressionProcessor = new ExpressionProcessor(ThymeleafExpressionLanguage);\n\t\t\t\tlet paramsList = parameters(context).slice(1, -1).split(',').map(param => {\n\t\t\t\t\tlet [lhs, rhs] = param.split('=');\n\t\t\t\t\treturn [lhs, expressionProcessor.process(rhs, context)];\n\t\t\t\t});\n\n\t\t\t\t// Fill out any placeholders in the URL from the parameters\n\t\t\t\twhile (true) { // eslint-disable-line\n\t\t\t\t\tlet urlTemplate = /(.*?)\\{(.+?)\\}(.*)/.exec(url);\n\t\t\t\t\tif (urlTemplate) {\n\t\t\t\t\t\tlet [, head, placeholder, tail] = urlTemplate;\n\t\t\t\t\t\tlet paramEntry = remove(paramsList, ([lhs]) => lhs === placeholder);\n\t\t\t\t\t\tif (paramEntry) {\n\t\t\t\t\t\t\turl = `${head}${paramEntry[1]}${tail}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Remaining parameters become search query parameters\n\t\t\t\tif (paramsList.length) {\n\t\t\t\t\turl += `?${paramsList.map(([key, value]) => `${key}=${value}`).join('&')}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn url;\n\t\t}\n\t),\n\tnew ThymeleafRule('Url', /.+/),\n\tnew ThymeleafRule('UrlParameters', /\\((.+)\\)/),\n\n\t/**\n\t * Fragment expressions, `~{template :: fragment(parameters)}`.  A locator for\n\t * a piece of HTML in the same or another template.\n\t */\n\tnew ThymeleafRule('FragmentExpression',\n\t\tSequence(/~{/, 'TemplateName', /::/, 'FragmentName', Optional('FragmentParametersSection'), /}/),\n\t\t([, templateName, , fragmentName, parameters]) => context => {\n\n\t\t\t// TODO: Should executing a fragment expression locate and return the\n\t\t\t//       fragment?  If so, then it'll make expression execution\n\t\t\t//       asynchronous!\n\t\t\treturn {\n\t\t\t\ttype: METADATA_FRAGMENT,\n\t\t\t\ttemplateName: templateName(context),\n\t\t\t\tfragmentName: fragmentName(context),\n\t\t\t\tparameters: parameters ? parameters(context) : null\n\t\t\t};\n\t\t}\n\t),\n\tnew ThymeleafRule('TemplateName',\n\t\tOrderedChoice(\n\t\t\t/[\\w-._/]+/,\n\t\t\t'VariableExpression'\n\t\t)\n\t),\n\tnew ThymeleafRule('FragmentName', /[\\w-._]+/),\n\tnew ThymeleafRule('FragmentParametersSection',\n\t\tSequence(/\\(/, Optional('FragmentParameters'), /\\)/),\n\t\t([, parameters]) => context => {\n\t\t\treturn parameters(context);\n\t\t}\n\t),\n\tnew ThymeleafRule('FragmentParameters',\n\t\tSequence('Expression', ZeroOrMore(Sequence(/,/, 'Expression'))),\n\t\t(expressionsAndSeparators) => context => {\n\t\t\treturn expressionsAndSeparators ?\n\t\t\t\tflatten(expressionsAndSeparators)\n\t\t\t\t\t.filter(item => item !== ',')\n\t\t\t\t\t.map(expressions => expressions(context)) :\n\t\t\t\t[];\n\t\t}\n\t),\n\n\n\t// Complex expressions\n\t// ===================\n\n\t/**\n\t * Iteration, `localVar : ${collection}`.  The name of the variable for each\n\t * loop, followed by the collection being iterated over.\n\t */\n\tnew ThymeleafRule('Iteration',\n\t\tSequence('Identifier', Optional(Sequence(/,/, 'Identifier')), /:/, 'VariableExpression'),\n\t\t([localValueName, [, iterationStatusVariable], , collectionExpressionAction]) => context => ({\n\t\t\ttype: METADATA_ITERATION,\n\t\t\tlocalValueName: localValueName(context),\n\t\t\titerable: collectionExpressionAction(context),\n\t\t\titerationStatusVariable: iterationStatusVariable ? iterationStatusVariable(context) : null\n\t\t})\n\t),\n\n\t/**\n\t * Scoped variable aliases, `key=${expression},...`, describes one or more\n\t * names for scoped variables with the expressions that can be their values.\n\t */\n\tnew ThymeleafRule('ScopedVariables',\n\t\tSequence('ScopedVariable', ZeroOrMore(Sequence(/,/, 'ScopedVariable'))),\n\t\t(aliases) => context => {\n\t\t\treturn flatten(aliases).map(alias => alias(context));\n\t\t}\n\t),\n\tnew ThymeleafRule('ScopedVariable',\n\t\tSequence('Identifier', /=/, 'Expression'),\n\t\t([name, , expression]) => context => ({\n\t\t\tname: name(context),\n\t\t\tvalue: expression(context)\n\t\t})\n\t),\n\n\t// Literals\n\t// ========\n\n\tnew ThymeleafRule('Literal',\n\t\tOrderedChoice(\n\t\t\t'StringLiteral',\n\t\t\t'NumberLiteral',\n\t\t\t'BooleanLiteral',\n\t\t\t'NullLiteral'\n\t\t)\n\t),\n\n\t/**\n\t * String literal, characters surrounded by `'` (single quotes).\n\t * \n\t * The first part is attempting to match an empty string as the second part\n\t * doesn't work on empty strings.\n\t * \n\t * The second part is trying to emulate negative lookbehind so that escaped\n\t * quotes don't get counted as string terminators, but JavaScript only got\n\t * that feature in ES2018, so if I used it it'd leave too many JS engines\n\t * without support.\n\t */\n\tnew ThymeleafRule('StringLiteral', /''|'.*?(?!\\\\').'/, result => () => result.slice(1, -1).replace(/\\\\/g, '')),\n\n\t/**\n\t * A number.\n\t */\n\tnew ThymeleafRule('NumberLiteral', /\\d+(\\.\\d+)?/, result => () => parseFloat(result)),\n\n\t/**\n\t * One of `true` or `false`.\n\t */\n\tnew ThymeleafRule('BooleanLiteral', /(true|false)/, result => () => result === 'true'),\n\n\t/**\n\t * The word `null` to represent the null value.\n\t */\n\t// TODO: The parser uses null to mean 'failed parse', so this might not work?\n\tnew ThymeleafRule('NullLiteral', /null/, () => () => null),\n\n\t/**\n\t * A token literal, which is pretty much anything else that can't be categorized\n\t * by the other literal types.  This is often used as a fallback in the\n\t * expression language so that, for any unknown input, we're still returning\n\t * something.\n\t */\n\tnew ThymeleafRule('TokenLiteral', /[^: ${}]+/, result => () => result),\n\n\n\t// Text operations\n\t// ===============\n\n\n\t/**\n\t * String concatenation, `'...' + '...'` or even `${...} + ${...}`, the\n\t * joining of 2 expressions by way of the `+` operator.\n\t */\n\tnew ThymeleafRule('StringConcatenation',\n\t\tSequence('Concatenatable', OneOrMore(Sequence(/\\+/, 'Concatenatable'))),\n\t\t(values) => context => {\n\t\t\treturn flatten(values).filter(item => item !== '+').reduce((result, value) => {\n\t\t\t\treturn result + (typeof value === 'function' ? value(context) : value);\n\t\t\t}, '');\n\t\t}\n\t),\n\tnew ThymeleafRule('Concatenatable',\n\t\tOrderedChoice(\n\t\t\t'StringLiteral',\n\t\t\t'VariableExpression'\n\t\t)\n\t),\n\n\tnew ThymeleafRule('LiteralSubstitution', Sequence(/^\\|/, OneOrMore(Sequence(/[^$|]*/, 'VariableExpression', /[^$|]*/)), /\\|$/), ([, matchers]) => context => {\n\t\treturn flatten(matchers).reduce((curr, acc) => {\n\t\t\tif (typeof acc === 'string') {\n\t\t\t\treturn curr + acc;\n\t\t\t}\n\t\t\treturn curr + acc(context);\n\t\t}, '');\n\t}),\n\n\n\t// Arithmetic operations\n\t// =====================\n\n\n\t// Boolean operations\n\t// ==================\n\n\tnew ThymeleafRule('LogicalOperation',\n\t\tOrderedChoice('LogicalAndOperation', 'LogicalOrOperation')\n\t),\n\tnew ThymeleafRule('LogicalAndOperation',\n\t\tSequence('Expression', 'LogicalAndOperator', 'Expression'),\n\t\t([leftOperand, , rightOperand]) => context => {\n\t\t\tlet lhs = leftOperand(context);\n\t\t\tlet rhs = rightOperand(context);\n\t\t\treturn lhs && rhs;\n\t\t}\n\t),\n\tnew ThymeleafRule('LogicalAndOperator',\n\t\tOrderedChoice(/&&/, /and/)\n\t),\n\tnew ThymeleafRule('LogicalOrOperation',\n\t\tSequence('Expression', 'LogicalOrOperator', 'Expression'),\n\t\t([leftOperand, , rightOperand]) => context => {\n\t\t\tlet lhs = leftOperand(context);\n\t\t\tlet rhs = rightOperand(context);\n\t\t\treturn lhs || rhs;\n\t\t}\n\t),\n\tnew ThymeleafRule('LogicalOrOperator',\n\t\tOrderedChoice(/\\|\\|/, /or/)\n\t),\n\n\tnew ThymeleafRule('Negation', /!/),\n\n\n\t// Comparisons and equality\n\t// ========================\n\n\t/**\n\t * An operation for comparing the equality of each side of the operator.\n\t */\n\tnew ThymeleafRule('EqualityOperation',\n\t\tSequence('Expression', 'EqualityOperator', 'Expression'),\n\t\t([leftOperand, operator, rightOperand]) => context => {\n\t\t\tlet lhs = leftOperand(context);\n\t\t\tlet rhs = rightOperand(context);\n\t\t\tswitch (operator(context)) {\n\t\t\t\tcase '==':  return lhs == rhs; // eslint-disable-line\n\t\t\t\tcase 'eq':\n\t\t\t\tcase '===': return lhs === rhs;\n\t\t\t\tcase '!=':  return lhs != rhs; // eslint-disable-line\n\t\t\t\tcase 'ne':\n\t\t\t\tcase '!==': return lhs !== rhs;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t),\n\tnew ThymeleafRule('EqualityOperator',\n\t\tOrderedChoice(/[=!]==?/, /eq/, /ne/)\n\t),\n\n\n\t// Conditional operators\n\t// =====================\n\n\t/**\n\t * A logical expression is any expression that resolves to a `true`/`false`\n\t * value.\n\t */\n\tnew ThymeleafRule('LogicalExpression',\n\t\tOrderedChoice(\n\t\t\t'EqualityOperation',\n\t\t\t'LogicalOperation',\n\t\t\t'Expression'\n\t\t)\n\t),\n\n\t/**\n\t * If-then condition, `if ? then`.  This is the truthy branch only of the\n\t * classic ternary operator.  The falsey branch is a no-op.\n\t */\n\tnew ThymeleafRule('IfThenCondition',\n\t\tSequence('LogicalExpression', /\\?/, 'Expression'),\n\t\t([condition, , truthyExpression]) => context => {\n\t\t\treturn condition(context) ? truthyExpression(context) : undefined;\n\t\t}\n\t),\n\n\t/**\n\t * If-then-else condition, `if ? then : else`, the classic ternary operator.\n\t */\n\tnew ThymeleafRule('IfThenElseCondition',\n\t\tSequence('LogicalExpression', /\\?/, 'Expression', /:/, 'Expression'),\n\t\t([condition, , truthyExpression, , falseyExpression]) => context => {\n\t\t\treturn condition(context) ? truthyExpression(context) : falseyExpression(context);\n\t\t}\n\t),\n\n\n\t// Special tokens\n\t// ==============\n\n\t/**\n\t * An expression that matches the empty string.\n\t */\n\tnew ThymeleafRule('Nothing', /^$/),\n\n\n\t// Common language basics\n\t// ======================\n\n\tnew ThymeleafRule('Identifier', /[#a-zA-Z_][\\w]*/),\n\tnew ThymeleafRule('PropertyName', 'Identifier',\n\t\t(propertyName) => context => {\n\t\t\tlet property = propertyName(context);\n\t\t\treturn Object.prototype.hasOwnProperty.call(context, property) ? context[property] : '';\n\t\t}\n\t),\n\tnew ThymeleafRule('ArrayIndex', /([\\w]+)?(\\[[\\d]*])+/,\n\t\t(path) => context => {\n\t\t\treturn getByPath(context, path);\n\t\t}\n\t),\n\tnew ThymeleafRule('MethodCall',\n\t\tSequence('MethodName', /\\(/, Optional('MethodParameters'), /\\)/),\n\t\t([name, , parameters]) => (context, parameterContext) => {\n\t\t\tlet methodName = name(context);\n\t\t\tlet method = context[methodName];\n\t\t\tif (!method) {\n\t\t\t\tconsole.warn(`No method '${methodName}' present on the current context.  Expression: ${context.expression}`);\n\t\t\t\treturn '';\n\t\t\t}\n\t\t\treturn method.apply(context, parameters(parameterContext || context));\n\t\t}\n\t),\n\tnew ThymeleafRule('MethodName', 'Identifier'),\n\tnew ThymeleafRule('MethodParameters',\n\t\tSequence('Chain', ZeroOrMore(Sequence(/,/, 'Chain'))),\n\t\t(parametersAndSeparators) => context => {\n\t\t\treturn parametersAndSeparators ?\n\t\t\t\tflatten(parametersAndSeparators)\n\t\t\t\t\t.filter(item => item !== ',')\n\t\t\t\t\t.map(parameter => parameter(context)) :\n\t\t\t\t[];\n\t\t}\n\t),\n\n\t/**\n\t * Any valid unit of code that resolves to some value.\n\t */\n\tnew ThymeleafRule('Expression',\n\t\tOrderedChoice(\n\t\t\t'LiteralSubstitution',\n\t\t\t'VariableExpression',\n\t\t\t'StringConcatenation',\n\t\t\t'Literal'\n\t\t)\n\t)\n);\n\nexport default ThymeleafExpressionLanguage;\n","/* \n * Copyright 2017, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n *     http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {DEFAULT_CONFIGURATION}     from './Configurations.js';\nimport AttributeProcessor          from './processors/AttributeProcessor.js';\nimport ElementProcessor            from './processors/ElementProcessor.js';\nimport Matcher                     from './processors/Matcher.js';\nimport ExpressionProcessor         from './standard/expressions/ExpressionProcessor.js';\nimport FragmentSignatureGrammar    from './standard/expressions/FragmentSignatureGrammar.js';\nimport ThymeleafExpressionLanguage from './standard/expressions/ThymeleafExpressionLanguage.js';\nimport {deserialize, serialize}    from './utilities/Dom.js';\nimport {promisify}                 from './utilities/Functions.js';\n\nimport {readFile} from 'fs';\n\n/**\n * A highly-configurable class responsible for processing the Thymeleaf\n * directives found in HTML documents and fragments.\n * \n * @author Emanuel Rabina\n */\nexport default class TemplateEngine {\n\n\t/**\n\t * Constructor, set up a new template engine instance.\n\t * \n\t * @param {object} [config]\n\t * @param config.dialects\n\t * @param config.messageResolver\n\t * @param config.templateResolver\n\t */\n\tconstructor({dialects, messageResolver, templateResolver} = DEFAULT_CONFIGURATION) {\n\n\t\tthis.messageResolver  = messageResolver;\n\t\tthis.templateResolver = templateResolver;\n\t\tthis.expressionProcessor = new ExpressionProcessor(ThymeleafExpressionLanguage);\n\t\tthis.fragmentSignatureProcessor = new ExpressionProcessor(FragmentSignatureGrammar);\n\n\t\t// Combine all processors into a unified list\n\t\tthis.processors = dialects.reduce((acc, {processors}) => processors ? [\n\t\t\t...acc,\n\t\t\t...processors\n\t\t] : acc, []);\n\n\t\t// Combine all expression objects into a unified object\n\t\tthis.expressionObjects = dialects.reduce((acc, {expressionObjects}) => expressionObjects ? {\n\t\t\t...acc,\n\t\t\t...expressionObjects\n\t\t} : acc, {});\n\t}\n\n\t/**\n\t * Process the Thymeleaf template data, returning the processed template.\n\t *\n\t * @param {string} template\n\t * @param {object} [context]\n\t * @return {Promise<string>}\n\t *   A promise resolved with the processed template, or rejected with an error\n\t *   message.\n\t */\n\tprocess(template, context = {}) {\n\n\t\tlet documentFragment = deserialize(template);\n\t\tlet rootElement = documentFragment.firstElementChild;\n\t\treturn this.processNode(rootElement, {\n\t\t\t...context,\n\t\t\t...this.expressionObjects,\n\t\t\t// TODO: Is there some way to make these things a dependency of the\n\t\t\t//       processors that need them?  Otherwise it feels like passing\n\t\t\t//       dependencies as part of the context object 🤔\n\t\t\texpressionProcessor: this.expressionProcessor,\n\t\t\tfragmentSignatureProcessor: this.fragmentSignatureProcessor,\n\t\t\tmessageResolver:  this.messageResolver,\n\t\t\ttemplateResolver: this.templateResolver\n\t\t})\n\t\t\t.then(() => {\n\t\t\t\treturn serialize(documentFragment);\n\t\t\t});\n\t}\n\n\t/**\n\t * Process the Thymeleaf template at the given path, returning a promise of\n\t * the processed template.\n\t * \n\t * @param {string} filePath\n\t * @param {object} [context]\n\t * @return {Promise<string>}\n\t *   A promise of the processed template.  The promise is immediately rejected\n\t *   if this method is called in a browser environment.\n\t */\n\tprocessFile(filePath, context = {}) {\n\n\t\t/* global ENVIRONMENT */\n\t\treturn ENVIRONMENT === 'browser' ?\n\t\t\tPromise.reject(new Error('Cannot use TemplateEngine.processFile() inside a browser')) :\n\t\t\tpromisify(readFile)(filePath)\n\t\t\t\t.then(data => {\n\t\t\t\t\treturn this.process(data, context);\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Process a DOM element.\n\t * \n\t * @private\n\t * @param {Element} element\n\t * @param {object} [context]\n\t * @return {Promise<boolean>} Whether or not the parent node needs\n\t *   reprocessing.\n\t */\n\tasync processNode(element, context = {}) {\n\n\t\tlet localVariables = element.__thymeleafLocalVariables || {};\n\t\tlet localContext = {\n\t\t\t...context,\n\t\t\t...localVariables\n\t\t};\n\t\tlet matcher = new Matcher();\n\n\t\t// Run the current element through the gamut of registered processors.  If\n\t\t// one of them sends a reprocessing signal, return from this method to let\n\t\t// the caller re-run everything.\n\t\tfor (let i = 0; i < this.processors.length; i++) {\n\t\t\tlet processor = this.processors[i];\n\t\t\tlet processorResult = false;\n\n\t\t\tlet match = matcher.matches(element, processor);\n\t\t\tif (match) {\n\t\t\t\tif (processor instanceof AttributeProcessor) {\n\t\t\t\t\tprocessorResult = await processor.process(element, match,\n\t\t\t\t\t\telement.getAttribute(match), localContext);\n\t\t\t\t}\n\t\t\t\telse if (processor instanceof ElementProcessor) {\n\t\t\t\t\tprocessorResult = await processor.process(element, localContext);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (processorResult) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// Process this element's children, handling the reprocessing signal to\n\t\t// re-run the 'current' child element (could have been shifted due to being\n\t\t// removed etc).\n\t\tfor (let i = 0; i < element.children.length; ) {\n\t\t\tlet child = element.children[i];\n\t\t\tlet reprocess = await this.processNode(child, localContext);\n\t\t\tif (!reprocess) {\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}