diff --git a/mix-manifest.json b/mix-manifest.json
index ccf5629..83a95b5 100644
--- a/mix-manifest.json
+++ b/mix-manifest.json
@@ -1,7 +1,7 @@
{
"/public/js/spritemap.js": "/public/js/spritemap.js",
- "/public/js/index.js": "/public/js/index.js",
"/public/js/critical.js": "/public/js/critical.js",
+ "/public/js/create-bucket.js": "/public/js/create-bucket.js",
"/public/js/dashboard.js": "/public/js/dashboard.js",
"/public/css/index.css": "/public/css/index.css",
"/public/css/demo.html": "/public/css/demo.html",
diff --git a/public/css/index.css b/public/css/index.css
index 50b06af..03ad9c6 100644
--- a/public/css/index.css
+++ b/public/css/index.css
@@ -14919,15 +14919,19 @@ input[type=checkbox].field-choice:checked ~ .field-switch:after {
color: var(--text);
}
+.field-error ul {
+ list-style: none;
+ padding: 0;
+}
+
.container--app {
max-width: 100%;
padding: 15px 30px;
}
.turbolinks-progress-bar {
- width: 5px;
- height: 100%;
- background-color: blue;
+ height: 5px;
+ background-color: var(--danger);
}
.app-header .logo {
diff --git a/public/js/create-bucket.js b/public/js/create-bucket.js
new file mode 100644
index 0000000..b33303f
--- /dev/null
+++ b/public/js/create-bucket.js
@@ -0,0 +1,4556 @@
+/******/ (() => { // webpackBootstrap
+/******/ var __webpack_modules__ = ({
+
+/***/ "./resources/js/components/field-error.riot":
+/*!**************************************************!*\
+ !*** ./resources/js/components/field-error.riot ***!
+ \**************************************************/
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
+/* harmony export */ });
+/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
+ 'css': null,
+
+ 'exports': {
+ state: {
+ errors: [
+
+ ],
+ },
+
+ /**
+ *
+ *
+ * @param {Object} props [description]
+ * @param {Object} state [description]
+ *
+ */
+ onMounted(props, state)
+ {
+ // getting parent element for entire field
+ const parent = this.root.closest('.field-group')
+
+ // getting current element by name
+ const element = document.querySelector('[name="' + props.name + '"]')
+
+ // getting form
+ const form = element.closest('form')
+
+ // element, form are exists and nofieldupdate is not set
+ // each change of the element dispatch a event to form validation
+ if (element && form && !props.nofieldupdate) {
+ element.addEventListener('input', (event) => {
+ this.dispatchCustomEvent(event, form, props.name)
+ })
+ }
+
+ // add custom event to listen to form-validation
+ this.root.addEventListener('form-validation', (event) => {
+ this.onFormValidation(event, parent)
+ })
+ },
+
+ /**
+ * process form validation triggered by form
+ *
+ * @param {Event} event
+ * @param {Element} parent
+ *
+ */
+ onFormValidation(event, parent)
+ {
+ // if detail is a value, set to errors
+ if (event.detail) {
+ this.state.errors = event.detail
+
+ parent.classList.add('field--error')
+ parent.classList.remove('field--valid')
+ } else {
+ this.state.errors = []
+
+ parent.classList.remove('field--error')
+ parent.classList.add('field--valid')
+ }
+
+ this.update()
+ },
+
+ /**
+ * create event to send to form validation
+ *
+ * @param {Event} event
+ * @param {Element} form
+ * @param {string} name
+ *
+ */
+ dispatchCustomEvent(event, form, name)
+ {
+ const fieldUpdateEvent = new CustomEvent('field-update', {
+ 'detail': {
+ 'name': name,
+ 'value': event.target.value
+ }
+ })
+
+ form.dispatchEvent(fieldUpdateEvent)
+ }
+ },
+
+ 'template': function(
+ template,
+ expressionTypes,
+ bindingTypes,
+ getComponent
+ ) {
+ return template(
+ '
',
+ [
+ {
+ 'type': bindingTypes.IF,
+
+ 'evaluate': function(
+ _scope
+ ) {
+ return _scope.state.errors.length > 0;
+ },
+
+ 'redundantAttribute': 'expr6',
+ 'selector': '[expr6]',
+
+ 'template': template(
+ '',
+ [
+ {
+ 'type': bindingTypes.EACH,
+ 'getKey': null,
+ 'condition': null,
+
+ 'template': template(
+ ' ',
+ [
+ {
+ 'expressions': [
+ {
+ 'type': expressionTypes.TEXT,
+ 'childNodeIndex': 0,
+
+ 'evaluate': function(
+ _scope
+ ) {
+ return [
+ _scope.error
+ ].join(
+ ''
+ );
+ }
+ }
+ ]
+ }
+ ]
+ ),
+
+ 'redundantAttribute': 'expr7',
+ 'selector': '[expr7]',
+ 'itemName': 'error',
+ 'indexName': null,
+
+ 'evaluate': function(
+ _scope
+ ) {
+ return _scope.state.errors;
+ }
+ }
+ ]
+ )
+ }
+ ]
+ );
+ },
+
+ 'name': 'field-error'
+});
+
+/***/ }),
+
+/***/ "./node_modules/@tentakelfabrik/tiny-validator/src/FormValidator.js":
+/*!**************************************************************************!*\
+ !*** ./node_modules/@tentakelfabrik/tiny-validator/src/FormValidator.js ***!
+ \**************************************************************************/
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
+/* harmony export */ });
+/* harmony import */ var validate_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! validate.js */ "./node_modules/validate.js/validate.js");
+/* harmony import */ var validate_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(validate_js__WEBPACK_IMPORTED_MODULE_0__);
+/* harmony import */ var form_serialize__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! form-serialize */ "./node_modules/form-serialize/index.js");
+/* harmony import */ var form_serialize__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(form_serialize__WEBPACK_IMPORTED_MODULE_1__);
+
+
+
+/**
+ * Form Validator with RiotJS Components
+ *
+ *
+ *
+ *
+ */
+class FormValidator
+{
+ /**
+ *
+ * @param {[type]} formSelector [description]
+ * @param {[type]} constraits [description]
+ */
+ constructor(formSelector, constraits)
+ {
+ // getting selector to find form-element
+ this.formSelector = formSelector
+
+ // constraits for validate.js
+ this.constraits = constraits
+
+ // get form and elements
+ this.form = document.querySelector(this.formSelector)
+ this.elements = this.form.querySelectorAll('field-error')
+
+ // adding submit event
+ this.form.addEventListener('submit', (event) => {
+ this.onSubmit(event)
+ })
+
+ // adding event if a element is updated
+ this.form.addEventListener('field-update', (event) => {
+ this.onFieldUpdate(event)
+ })
+ }
+
+ /**
+ *
+ * @param {[type]} event [description]
+ * @return {[type]} [description]
+ */
+ onSubmit(event)
+ {
+ let errors = validate_js__WEBPACK_IMPORTED_MODULE_0___default()(form_serialize__WEBPACK_IMPORTED_MODULE_1___default()(event.target, {
+ hash: true
+ }), this.constraits, {
+ fullMessages: false
+ })
+
+ if (errors) {
+ event.preventDefault()
+
+ // send each element a event
+ this.elements.forEach((element) => {
+ let elementErrors = false
+
+ // check for errors by name
+ if (errors[element.attributes.name.nodeValue]) {
+ elementErrors = errors[element.attributes.name.nodeValue]
+ }
+
+ this.dispatchCustomEvent(elementErrors, element)
+ })
+ }
+ }
+
+ /**
+ *
+ *
+ * @param {Event} event
+ *
+ */
+ onFieldUpdate(event)
+ {
+ // workaround, make sure that value for single is undefined if it is empty
+ if (event.detail.value == '') {
+ event.detail.value = undefined
+ }
+
+ let errors = validate_js__WEBPACK_IMPORTED_MODULE_0___default().single(event.detail.value, this.constraits[event.detail.name])
+
+ // search for element by name and dispatch event
+ this.elements.forEach((element) => {
+ if (element.attributes.name.nodeValue == event.detail.name) {
+ this.dispatchCustomEvent(errors, element)
+ }
+ })
+ }
+
+ /**
+ * dispatch event to single element
+ *
+ * @param {Array} errors
+ * @param {Element} element
+ *
+ */
+ dispatchCustomEvent(errors, element)
+ {
+ let detail = false
+
+ if (errors) {
+ detail = errors
+ }
+
+ const formValidationEvent = new CustomEvent('form-validation', {
+ 'detail': detail
+ })
+
+ element.dispatchEvent(formValidationEvent)
+ }
+}
+
+/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FormValidator);
+
+/***/ }),
+
+/***/ "./node_modules/form-serialize/index.js":
+/*!**********************************************!*\
+ !*** ./node_modules/form-serialize/index.js ***!
+ \**********************************************/
+/***/ ((module) => {
+
+// get successful control from form and assemble into object
+// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
+
+// types which indicate a submit action and are not successful controls
+// these will be ignored
+var k_r_submitter = /^(?:submit|button|image|reset|file)$/i;
+
+// node names which could be successful controls
+var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i;
+
+// Matches bracket notation.
+var brackets = /(\[[^\[\]]*\])/g;
+
+// serializes form fields
+// @param form MUST be an HTMLForm element
+// @param options is an optional argument to configure the serialization. Default output
+// with no options specified is a url encoded string
+// - hash: [true | false] Configure the output type. If true, the output will
+// be a js object.
+// - serializer: [function] Optional serializer function to override the default one.
+// The function takes 3 arguments (result, key, value) and should return new result
+// hash and url encoded str serializers are provided with this module
+// - disabled: [true | false]. If true serialize disabled fields.
+// - empty: [true | false]. If true serialize empty fields
+function serialize(form, options) {
+ if (typeof options != 'object') {
+ options = { hash: !!options };
+ }
+ else if (options.hash === undefined) {
+ options.hash = true;
+ }
+
+ var result = (options.hash) ? {} : '';
+ var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize);
+
+ var elements = form && form.elements ? form.elements : [];
+
+ //Object store each radio and set if it's empty or not
+ var radio_store = Object.create(null);
+
+ for (var i=0 ; i {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ "__": () => (/* binding */ __),
+/* harmony export */ "component": () => (/* binding */ component),
+/* harmony export */ "install": () => (/* binding */ install),
+/* harmony export */ "mount": () => (/* binding */ mount),
+/* harmony export */ "pure": () => (/* binding */ pure),
+/* harmony export */ "register": () => (/* binding */ register),
+/* harmony export */ "uninstall": () => (/* binding */ uninstall),
+/* harmony export */ "unmount": () => (/* binding */ unmount),
+/* harmony export */ "unregister": () => (/* binding */ unregister),
+/* harmony export */ "version": () => (/* binding */ version),
+/* harmony export */ "withTypes": () => (/* binding */ withTypes)
+/* harmony export */ });
+/* Riot v6.0.1, @license MIT */
+/**
+ * Convert a string from camel case to dash-case
+ * @param {string} string - probably a component tag name
+ * @returns {string} component name normalized
+ */
+function camelToDashCase(string) {
+ return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
+}
+/**
+ * Convert a string containing dashes to camel case
+ * @param {string} string - input string
+ * @returns {string} my-string -> myString
+ */
+
+function dashToCamelCase(string) {
+ return string.replace(/-(\w)/g, (_, c) => c.toUpperCase());
+}
+
+/**
+ * Get all the element attributes as object
+ * @param {HTMLElement} element - DOM node we want to parse
+ * @returns {Object} all the attributes found as a key value pairs
+ */
+
+function DOMattributesToObject(element) {
+ return Array.from(element.attributes).reduce((acc, attribute) => {
+ acc[dashToCamelCase(attribute.name)] = attribute.value;
+ return acc;
+ }, {});
+}
+/**
+ * Move all the child nodes from a source tag to another
+ * @param {HTMLElement} source - source node
+ * @param {HTMLElement} target - target node
+ * @returns {undefined} it's a void method ¯\_(ツ)_/¯
+ */
+// Ignore this helper because it's needed only for svg tags
+
+function moveChildren(source, target) {
+ if (source.firstChild) {
+ target.appendChild(source.firstChild);
+ moveChildren(source, target);
+ }
+}
+/**
+ * Remove the child nodes from any DOM node
+ * @param {HTMLElement} node - target node
+ * @returns {undefined}
+ */
+
+function cleanNode(node) {
+ clearChildren(node.childNodes);
+}
+/**
+ * Clear multiple children in a node
+ * @param {HTMLElement[]} children - direct children nodes
+ * @returns {undefined}
+ */
+
+function clearChildren(children) {
+ Array.from(children).forEach(removeChild);
+}
+/**
+ * Remove a node
+ * @param {HTMLElement}node - node to remove
+ * @returns {undefined}
+ */
+
+const removeChild = node => node && node.parentNode && node.parentNode.removeChild(node);
+/**
+ * Insert before a node
+ * @param {HTMLElement} newNode - node to insert
+ * @param {HTMLElement} refNode - ref child
+ * @returns {undefined}
+ */
+
+const insertBefore = (newNode, refNode) => refNode && refNode.parentNode && refNode.parentNode.insertBefore(newNode, refNode);
+/**
+ * Replace a node
+ * @param {HTMLElement} newNode - new node to add to the DOM
+ * @param {HTMLElement} replaced - node to replace
+ * @returns {undefined}
+ */
+
+const replaceChild = (newNode, replaced) => replaced && replaced.parentNode && replaced.parentNode.replaceChild(newNode, replaced);
+
+// Riot.js constants that can be used accross more modules
+const COMPONENTS_IMPLEMENTATION_MAP$1 = new Map(),
+ DOM_COMPONENT_INSTANCE_PROPERTY$1 = Symbol('riot-component'),
+ PLUGINS_SET$1 = new Set(),
+ IS_DIRECTIVE = 'is',
+ VALUE_ATTRIBUTE = 'value',
+ MOUNT_METHOD_KEY = 'mount',
+ UPDATE_METHOD_KEY = 'update',
+ UNMOUNT_METHOD_KEY = 'unmount',
+ SHOULD_UPDATE_KEY = 'shouldUpdate',
+ ON_BEFORE_MOUNT_KEY = 'onBeforeMount',
+ ON_MOUNTED_KEY = 'onMounted',
+ ON_BEFORE_UPDATE_KEY = 'onBeforeUpdate',
+ ON_UPDATED_KEY = 'onUpdated',
+ ON_BEFORE_UNMOUNT_KEY = 'onBeforeUnmount',
+ ON_UNMOUNTED_KEY = 'onUnmounted',
+ PROPS_KEY = 'props',
+ STATE_KEY = 'state',
+ SLOTS_KEY = 'slots',
+ ROOT_KEY = 'root',
+ IS_PURE_SYMBOL = Symbol('pure'),
+ IS_COMPONENT_UPDATING = Symbol('is_updating'),
+ PARENT_KEY_SYMBOL = Symbol('parent'),
+ ATTRIBUTES_KEY_SYMBOL = Symbol('attributes'),
+ TEMPLATE_KEY_SYMBOL = Symbol('template');
+
+var globals = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ COMPONENTS_IMPLEMENTATION_MAP: COMPONENTS_IMPLEMENTATION_MAP$1,
+ DOM_COMPONENT_INSTANCE_PROPERTY: DOM_COMPONENT_INSTANCE_PROPERTY$1,
+ PLUGINS_SET: PLUGINS_SET$1,
+ IS_DIRECTIVE: IS_DIRECTIVE,
+ VALUE_ATTRIBUTE: VALUE_ATTRIBUTE,
+ MOUNT_METHOD_KEY: MOUNT_METHOD_KEY,
+ UPDATE_METHOD_KEY: UPDATE_METHOD_KEY,
+ UNMOUNT_METHOD_KEY: UNMOUNT_METHOD_KEY,
+ SHOULD_UPDATE_KEY: SHOULD_UPDATE_KEY,
+ ON_BEFORE_MOUNT_KEY: ON_BEFORE_MOUNT_KEY,
+ ON_MOUNTED_KEY: ON_MOUNTED_KEY,
+ ON_BEFORE_UPDATE_KEY: ON_BEFORE_UPDATE_KEY,
+ ON_UPDATED_KEY: ON_UPDATED_KEY,
+ ON_BEFORE_UNMOUNT_KEY: ON_BEFORE_UNMOUNT_KEY,
+ ON_UNMOUNTED_KEY: ON_UNMOUNTED_KEY,
+ PROPS_KEY: PROPS_KEY,
+ STATE_KEY: STATE_KEY,
+ SLOTS_KEY: SLOTS_KEY,
+ ROOT_KEY: ROOT_KEY,
+ IS_PURE_SYMBOL: IS_PURE_SYMBOL,
+ IS_COMPONENT_UPDATING: IS_COMPONENT_UPDATING,
+ PARENT_KEY_SYMBOL: PARENT_KEY_SYMBOL,
+ ATTRIBUTES_KEY_SYMBOL: ATTRIBUTES_KEY_SYMBOL,
+ TEMPLATE_KEY_SYMBOL: TEMPLATE_KEY_SYMBOL
+});
+
+const EACH = 0;
+const IF = 1;
+const SIMPLE = 2;
+const TAG = 3;
+const SLOT = 4;
+var bindingTypes = {
+ EACH,
+ IF,
+ SIMPLE,
+ TAG,
+ SLOT
+};
+
+const ATTRIBUTE = 0;
+const EVENT = 1;
+const TEXT = 2;
+const VALUE = 3;
+var expressionTypes = {
+ ATTRIBUTE,
+ EVENT,
+ TEXT,
+ VALUE
+};
+
+const HEAD_SYMBOL = Symbol('head');
+const TAIL_SYMBOL = Symbol('tail');
+
+/**
+ * Create the fragments text nodes
+ * @return {Object} {{head: Text, tail: Text}}
+ */
+
+function createHeadTailPlaceholders() {
+ const head = document.createTextNode('');
+ const tail = document.createTextNode('');
+ head[HEAD_SYMBOL] = true;
+ tail[TAIL_SYMBOL] = true;
+ return {
+ head,
+ tail
+ };
+}
+
+/**
+ * Create the template meta object in case of fragments
+ * @param {TemplateChunk} componentTemplate - template chunk object
+ * @returns {Object} the meta property that will be passed to the mount function of the TemplateChunk
+ */
+
+function createTemplateMeta(componentTemplate) {
+ const fragment = componentTemplate.dom.cloneNode(true);
+ const {
+ head,
+ tail
+ } = createHeadTailPlaceholders();
+ return {
+ avoidDOMInjection: true,
+ fragment,
+ head,
+ tail,
+ children: [head, ...Array.from(fragment.childNodes), tail]
+ };
+}
+
+/**
+ * Helper function to set an immutable property
+ * @param {Object} source - object where the new property will be set
+ * @param {string} key - object key where the new property will be stored
+ * @param {*} value - value of the new property
+ * @param {Object} options - set the propery overriding the default options
+ * @returns {Object} - the original object modified
+ */
+function defineProperty(source, key, value, options) {
+ if (options === void 0) {
+ options = {};
+ }
+
+ /* eslint-disable fp/no-mutating-methods */
+ Object.defineProperty(source, key, Object.assign({
+ value,
+ enumerable: false,
+ writable: false,
+ configurable: true
+ }, options));
+ /* eslint-enable fp/no-mutating-methods */
+
+ return source;
+}
+/**
+ * Define multiple properties on a target object
+ * @param {Object} source - object where the new properties will be set
+ * @param {Object} properties - object containing as key pair the key + value properties
+ * @param {Object} options - set the propery overriding the default options
+ * @returns {Object} the original object modified
+ */
+
+function defineProperties(source, properties, options) {
+ Object.entries(properties).forEach(_ref => {
+ let [key, value] = _ref;
+ defineProperty(source, key, value, options);
+ });
+ return source;
+}
+/**
+ * Define default properties if they don't exist on the source object
+ * @param {Object} source - object that will receive the default properties
+ * @param {Object} defaults - object containing additional optional keys
+ * @returns {Object} the original object received enhanced
+ */
+
+function defineDefaults(source, defaults) {
+ Object.entries(defaults).forEach(_ref2 => {
+ let [key, value] = _ref2;
+ if (!source[key]) source[key] = value;
+ });
+ return source;
+}
+
+/**
+ * Get the current fragment children located in between the head and tail comments
+ * @param {Comment} head - head comment node
+ * @param {Comment} tail - tail comment node
+ * @return {Array[]} children list of the nodes found in this template fragment
+ */
+
+function getFragmentChildren(_ref) {
+ let {
+ head,
+ tail
+ } = _ref;
+ const nodes = walkNodes([head], head.nextSibling, n => n === tail, false);
+ nodes.push(tail);
+ return nodes;
+}
+/**
+ * Recursive function to walk all the children nodes
+ * @param {Array[]} children - children nodes collection
+ * @param {ChildNode} node - current node
+ * @param {Function} check - exit function check
+ * @param {boolean} isFilterActive - filter flag to skip nodes managed by other bindings
+ * @returns {Array[]} children list of the nodes found in this template fragment
+ */
+
+function walkNodes(children, node, check, isFilterActive) {
+ const {
+ nextSibling
+ } = node; // filter tail and head nodes together with all the nodes in between
+ // this is needed only to fix a really ugly edge case https://github.com/riot/riot/issues/2892
+
+ if (!isFilterActive && !node[HEAD_SYMBOL] && !node[TAIL_SYMBOL]) {
+ children.push(node);
+ }
+
+ if (!nextSibling || check(node)) return children;
+ return walkNodes(children, nextSibling, check, // activate the filters to skip nodes between fragments that will be managed by other bindings
+ isFilterActive && !node[TAIL_SYMBOL] || nextSibling[HEAD_SYMBOL]);
+}
+
+/**
+ * Quick type checking
+ * @param {*} element - anything
+ * @param {string} type - type definition
+ * @returns {boolean} true if the type corresponds
+ */
+function checkType(element, type) {
+ return typeof element === type;
+}
+/**
+ * Check if an element is part of an svg
+ * @param {HTMLElement} el - element to check
+ * @returns {boolean} true if we are in an svg context
+ */
+
+function isSvg(el) {
+ const owner = el.ownerSVGElement;
+ return !!owner || owner === null;
+}
+/**
+ * Check if an element is a template tag
+ * @param {HTMLElement} el - element to check
+ * @returns {boolean} true if it's a
+ */
+
+function isTemplate(el) {
+ return el.tagName.toLowerCase() === 'template';
+}
+/**
+ * Check that will be passed if its argument is a function
+ * @param {*} value - value to check
+ * @returns {boolean} - true if the value is a function
+ */
+
+function isFunction(value) {
+ return checkType(value, 'function');
+}
+/**
+ * Check if a value is a Boolean
+ * @param {*} value - anything
+ * @returns {boolean} true only for the value is a boolean
+ */
+
+function isBoolean(value) {
+ return checkType(value, 'boolean');
+}
+/**
+ * Check if a value is an Object
+ * @param {*} value - anything
+ * @returns {boolean} true only for the value is an object
+ */
+
+function isObject(value) {
+ return !isNil(value) && value.constructor === Object;
+}
+/**
+ * Check if a value is null or undefined
+ * @param {*} value - anything
+ * @returns {boolean} true only for the 'undefined' and 'null' types
+ */
+
+function isNil(value) {
+ return value === null || value === undefined;
+}
+
+/**
+ * ISC License
+ *
+ * Copyright (c) 2020, Andrea Giammarchi, @WebReflection
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+// fork of https://github.com/WebReflection/udomdiff version 1.1.0
+// due to https://github.com/WebReflection/udomdiff/pull/2
+
+/* eslint-disable */
+
+/**
+ * @param {Node[]} a The list of current/live children
+ * @param {Node[]} b The list of future children
+ * @param {(entry: Node, action: number) => Node} get
+ * The callback invoked per each entry related DOM operation.
+ * @param {Node} [before] The optional node used as anchor to insert before.
+ * @returns {Node[]} The same list of future children.
+ */
+
+var udomdiff = ((a, b, get, before) => {
+ const bLength = b.length;
+ let aEnd = a.length;
+ let bEnd = bLength;
+ let aStart = 0;
+ let bStart = 0;
+ let map = null;
+
+ while (aStart < aEnd || bStart < bEnd) {
+ // append head, tail, or nodes in between: fast path
+ if (aEnd === aStart) {
+ // we could be in a situation where the rest of nodes that
+ // need to be added are not at the end, and in such case
+ // the node to `insertBefore`, if the index is more than 0
+ // must be retrieved, otherwise it's gonna be the first item.
+ const node = bEnd < bLength ? bStart ? get(b[bStart - 1], -0).nextSibling : get(b[bEnd - bStart], 0) : before;
+
+ while (bStart < bEnd) insertBefore(get(b[bStart++], 1), node);
+ } // remove head or tail: fast path
+ else if (bEnd === bStart) {
+ while (aStart < aEnd) {
+ // remove the node only if it's unknown or not live
+ if (!map || !map.has(a[aStart])) removeChild(get(a[aStart], -1));
+ aStart++;
+ }
+ } // same node: fast path
+ else if (a[aStart] === b[bStart]) {
+ aStart++;
+ bStart++;
+ } // same tail: fast path
+ else if (a[aEnd - 1] === b[bEnd - 1]) {
+ aEnd--;
+ bEnd--;
+ } // The once here single last swap "fast path" has been removed in v1.1.0
+ // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85
+ // reverse swap: also fast path
+ else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
+ // this is a "shrink" operation that could happen in these cases:
+ // [1, 2, 3, 4, 5]
+ // [1, 4, 3, 2, 5]
+ // or asymmetric too
+ // [1, 2, 3, 4, 5]
+ // [1, 2, 3, 5, 6, 4]
+ const node = get(a[--aEnd], -1).nextSibling;
+ insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
+ insertBefore(get(b[--bEnd], 1), node); // mark the future index as identical (yeah, it's dirty, but cheap 👍)
+ // The main reason to do this, is that when a[aEnd] will be reached,
+ // the loop will likely be on the fast path, as identical to b[bEnd].
+ // In the best case scenario, the next loop will skip the tail,
+ // but in the worst one, this node will be considered as already
+ // processed, bailing out pretty quickly from the map index check
+
+ a[aEnd] = b[bEnd];
+ } // map based fallback, "slow" path
+ else {
+ // the map requires an O(bEnd - bStart) operation once
+ // to store all future nodes indexes for later purposes.
+ // In the worst case scenario, this is a full O(N) cost,
+ // and such scenario happens at least when all nodes are different,
+ // but also if both first and last items of the lists are different
+ if (!map) {
+ map = new Map();
+ let i = bStart;
+
+ while (i < bEnd) map.set(b[i], i++);
+ } // if it's a future node, hence it needs some handling
+
+
+ if (map.has(a[aStart])) {
+ // grab the index of such node, 'cause it might have been processed
+ const index = map.get(a[aStart]); // if it's not already processed, look on demand for the next LCS
+
+ if (bStart < index && index < bEnd) {
+ let i = aStart; // counts the amount of nodes that are the same in the future
+
+ let sequence = 1;
+
+ while (++i < aEnd && i < bEnd && map.get(a[i]) === index + sequence) sequence++; // effort decision here: if the sequence is longer than replaces
+ // needed to reach such sequence, which would brings again this loop
+ // to the fast path, prepend the difference before a sequence,
+ // and move only the future list index forward, so that aStart
+ // and bStart will be aligned again, hence on the fast path.
+ // An example considering aStart and bStart are both 0:
+ // a: [1, 2, 3, 4]
+ // b: [7, 1, 2, 3, 6]
+ // this would place 7 before 1 and, from that time on, 1, 2, and 3
+ // will be processed at zero cost
+
+
+ if (sequence > index - bStart) {
+ const node = get(a[aStart], 0);
+
+ while (bStart < index) insertBefore(get(b[bStart++], 1), node);
+ } // if the effort wasn't good enough, fallback to a replace,
+ // moving both source and target indexes forward, hoping that some
+ // similar node will be found later on, to go back to the fast path
+ else {
+ replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
+ }
+ } // otherwise move the source forward, 'cause there's nothing to do
+ else aStart++;
+ } // this node has no meaning in the future list, so it's more than safe
+ // to remove it, and check the next live node out instead, meaning
+ // that only the live list index should be forwarded
+ else removeChild(get(a[aStart++], -1));
+ }
+ }
+
+ return b;
+});
+
+const UNMOUNT_SCOPE = Symbol('unmount');
+const EachBinding = {
+ // dynamic binding properties
+ // childrenMap: null,
+ // node: null,
+ // root: null,
+ // condition: null,
+ // evaluate: null,
+ // template: null,
+ // isTemplateTag: false,
+ nodes: [],
+
+ // getKey: null,
+ // indexName: null,
+ // itemName: null,
+ // afterPlaceholder: null,
+ // placeholder: null,
+ // API methods
+ mount(scope, parentScope) {
+ return this.update(scope, parentScope);
+ },
+
+ update(scope, parentScope) {
+ const {
+ placeholder,
+ nodes,
+ childrenMap
+ } = this;
+ const collection = scope === UNMOUNT_SCOPE ? null : this.evaluate(scope);
+ const items = collection ? Array.from(collection) : []; // prepare the diffing
+
+ const {
+ newChildrenMap,
+ batches,
+ futureNodes
+ } = createPatch(items, scope, parentScope, this); // patch the DOM only if there are new nodes
+
+ udomdiff(nodes, futureNodes, patch(Array.from(childrenMap.values()), parentScope), placeholder); // trigger the mounts and the updates
+
+ batches.forEach(fn => fn()); // update the children map
+
+ this.childrenMap = newChildrenMap;
+ this.nodes = futureNodes; // make sure that the loop edge nodes are marked
+
+ markEdgeNodes(this.nodes);
+ return this;
+ },
+
+ unmount(scope, parentScope) {
+ this.update(UNMOUNT_SCOPE, parentScope);
+ return this;
+ }
+
+};
+/**
+ * Patch the DOM while diffing
+ * @param {any[]} redundant - list of all the children (template, nodes, context) added via each
+ * @param {*} parentScope - scope of the parent template
+ * @returns {Function} patch function used by domdiff
+ */
+
+function patch(redundant, parentScope) {
+ return (item, info) => {
+ if (info < 0) {
+ // get the last element added to the childrenMap saved previously
+ const element = redundant[redundant.length - 1];
+
+ if (element) {
+ // get the nodes and the template in stored in the last child of the childrenMap
+ const {
+ template,
+ nodes,
+ context
+ } = element; // remove the last node (notice tags might have more children nodes)
+
+ nodes.pop(); // notice that we pass null as last argument because
+ // the root node and its children will be removed by domdiff
+
+ if (!nodes.length) {
+ // we have cleared all the children nodes and we can unmount this template
+ redundant.pop();
+ template.unmount(context, parentScope, null);
+ }
+ }
+ }
+
+ return item;
+ };
+}
+/**
+ * Check whether a template must be filtered from a loop
+ * @param {Function} condition - filter function
+ * @param {Object} context - argument passed to the filter function
+ * @returns {boolean} true if this item should be skipped
+ */
+
+
+function mustFilterItem(condition, context) {
+ return condition ? !condition(context) : false;
+}
+/**
+ * Extend the scope of the looped template
+ * @param {Object} scope - current template scope
+ * @param {Object} options - options
+ * @param {string} options.itemName - key to identify the looped item in the new context
+ * @param {string} options.indexName - key to identify the index of the looped item
+ * @param {number} options.index - current index
+ * @param {*} options.item - collection item looped
+ * @returns {Object} enhanced scope object
+ */
+
+
+function extendScope(scope, _ref) {
+ let {
+ itemName,
+ indexName,
+ index,
+ item
+ } = _ref;
+ defineProperty(scope, itemName, item);
+ if (indexName) defineProperty(scope, indexName, index);
+ return scope;
+}
+/**
+ * Mark the first and last nodes in order to ignore them in case we need to retrieve the fragment nodes
+ * @param {Array[]} nodes - each binding nodes list
+ * @returns {undefined} void function
+ */
+
+
+function markEdgeNodes(nodes) {
+ const first = nodes[0];
+ const last = nodes[nodes.length - 1];
+ if (first) first[HEAD_SYMBOL] = true;
+ if (last) last[TAIL_SYMBOL] = true;
+}
+/**
+ * Loop the current template items
+ * @param {Array} items - expression collection value
+ * @param {*} scope - template scope
+ * @param {*} parentScope - scope of the parent template
+ * @param {EachBinding} binding - each binding object instance
+ * @returns {Object} data
+ * @returns {Map} data.newChildrenMap - a Map containing the new children template structure
+ * @returns {Array} data.batches - array containing the template lifecycle functions to trigger
+ * @returns {Array} data.futureNodes - array containing the nodes we need to diff
+ */
+
+
+function createPatch(items, scope, parentScope, binding) {
+ const {
+ condition,
+ template,
+ childrenMap,
+ itemName,
+ getKey,
+ indexName,
+ root,
+ isTemplateTag
+ } = binding;
+ const newChildrenMap = new Map();
+ const batches = [];
+ const futureNodes = [];
+ items.forEach((item, index) => {
+ const context = extendScope(Object.create(scope), {
+ itemName,
+ indexName,
+ index,
+ item
+ });
+ const key = getKey ? getKey(context) : index;
+ const oldItem = childrenMap.get(key);
+ const nodes = [];
+
+ if (mustFilterItem(condition, context)) {
+ return;
+ }
+
+ const mustMount = !oldItem;
+ const componentTemplate = oldItem ? oldItem.template : template.clone();
+ const el = componentTemplate.el || root.cloneNode();
+ const meta = isTemplateTag && mustMount ? createTemplateMeta(componentTemplate) : componentTemplate.meta;
+
+ if (mustMount) {
+ batches.push(() => componentTemplate.mount(el, context, parentScope, meta));
+ } else {
+ batches.push(() => componentTemplate.update(context, parentScope));
+ } // create the collection of nodes to update or to add
+ // in case of template tags we need to add all its children nodes
+
+
+ if (isTemplateTag) {
+ nodes.push(...(mustMount ? meta.children : getFragmentChildren(meta)));
+ } else {
+ nodes.push(el);
+ } // delete the old item from the children map
+
+
+ childrenMap.delete(key);
+ futureNodes.push(...nodes); // update the children map
+
+ newChildrenMap.set(key, {
+ nodes,
+ template: componentTemplate,
+ context,
+ index
+ });
+ });
+ return {
+ newChildrenMap,
+ batches,
+ futureNodes
+ };
+}
+
+function create$6(node, _ref2) {
+ let {
+ evaluate,
+ condition,
+ itemName,
+ indexName,
+ getKey,
+ template
+ } = _ref2;
+ const placeholder = document.createTextNode('');
+ const root = node.cloneNode();
+ insertBefore(placeholder, node);
+ removeChild(node);
+ return Object.assign({}, EachBinding, {
+ childrenMap: new Map(),
+ node,
+ root,
+ condition,
+ evaluate,
+ isTemplateTag: isTemplate(root),
+ template: template.createDOM(node),
+ getKey,
+ indexName,
+ itemName,
+ placeholder
+ });
+}
+
+/**
+ * Binding responsible for the `if` directive
+ */
+
+const IfBinding = {
+ // dynamic binding properties
+ // node: null,
+ // evaluate: null,
+ // isTemplateTag: false,
+ // placeholder: null,
+ // template: null,
+ // API methods
+ mount(scope, parentScope) {
+ return this.update(scope, parentScope);
+ },
+
+ update(scope, parentScope) {
+ const value = !!this.evaluate(scope);
+ const mustMount = !this.value && value;
+ const mustUnmount = this.value && !value;
+
+ const mount = () => {
+ const pristine = this.node.cloneNode();
+ insertBefore(pristine, this.placeholder);
+ this.template = this.template.clone();
+ this.template.mount(pristine, scope, parentScope);
+ };
+
+ switch (true) {
+ case mustMount:
+ mount();
+ break;
+
+ case mustUnmount:
+ this.unmount(scope);
+ break;
+
+ default:
+ if (value) this.template.update(scope, parentScope);
+ }
+
+ this.value = value;
+ return this;
+ },
+
+ unmount(scope, parentScope) {
+ this.template.unmount(scope, parentScope, true);
+ return this;
+ }
+
+};
+function create$5(node, _ref) {
+ let {
+ evaluate,
+ template
+ } = _ref;
+ const placeholder = document.createTextNode('');
+ insertBefore(placeholder, node);
+ removeChild(node);
+ return Object.assign({}, IfBinding, {
+ node,
+ evaluate,
+ placeholder,
+ template: template.createDOM(node)
+ });
+}
+
+/**
+ * Throw an error with a descriptive message
+ * @param { string } message - error message
+ * @returns { undefined } hoppla.. at this point the program should stop working
+ */
+
+function panic(message) {
+ throw new Error(message);
+}
+/**
+ * Returns the memoized (cached) function.
+ * // borrowed from https://www.30secondsofcode.org/js/s/memoize
+ * @param {Function} fn - function to memoize
+ * @returns {Function} memoize function
+ */
+
+function memoize(fn) {
+ const cache = new Map();
+
+ const cached = val => {
+ return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
+ };
+
+ cached.cache = cache;
+ return cached;
+}
+/**
+ * Evaluate a list of attribute expressions
+ * @param {Array} attributes - attribute expressions generated by the riot compiler
+ * @returns {Object} key value pairs with the result of the computation
+ */
+
+function evaluateAttributeExpressions(attributes) {
+ return attributes.reduce((acc, attribute) => {
+ const {
+ value,
+ type
+ } = attribute;
+
+ switch (true) {
+ // spread attribute
+ case !attribute.name && type === ATTRIBUTE:
+ return Object.assign({}, acc, value);
+ // value attribute
+
+ case type === VALUE:
+ acc.value = attribute.value;
+ break;
+ // normal attributes
+
+ default:
+ acc[dashToCamelCase(attribute.name)] = attribute.value;
+ }
+
+ return acc;
+ }, {});
+}
+
+const ElementProto = typeof Element === 'undefined' ? {} : Element.prototype;
+const isNativeHtmlProperty = memoize(name => ElementProto.hasOwnProperty(name)); // eslint-disable-line
+
+/**
+ * Add all the attributes provided
+ * @param {HTMLElement} node - target node
+ * @param {Object} attributes - object containing the attributes names and values
+ * @returns {undefined} sorry it's a void function :(
+ */
+
+function setAllAttributes(node, attributes) {
+ Object.entries(attributes).forEach(_ref => {
+ let [name, value] = _ref;
+ return attributeExpression(node, {
+ name
+ }, value);
+ });
+}
+/**
+ * Remove all the attributes provided
+ * @param {HTMLElement} node - target node
+ * @param {Object} newAttributes - object containing all the new attribute names
+ * @param {Object} oldAttributes - object containing all the old attribute names
+ * @returns {undefined} sorry it's a void function :(
+ */
+
+
+function removeAllAttributes(node, newAttributes, oldAttributes) {
+ const newKeys = newAttributes ? Object.keys(newAttributes) : [];
+ Object.keys(oldAttributes).filter(name => !newKeys.includes(name)).forEach(attribute => node.removeAttribute(attribute));
+}
+/**
+ * Check whether the attribute value can be rendered
+ * @param {*} value - expression value
+ * @returns {boolean} true if we can render this attribute value
+ */
+
+
+function canRenderAttribute(value) {
+ return value === true || ['string', 'number'].includes(typeof value);
+}
+/**
+ * Check whether the attribute should be removed
+ * @param {*} value - expression value
+ * @returns {boolean} boolean - true if the attribute can be removed}
+ */
+
+
+function shouldRemoveAttribute(value) {
+ return !value && value !== 0;
+}
+/**
+ * This methods handles the DOM attributes updates
+ * @param {HTMLElement} node - target node
+ * @param {Object} expression - expression object
+ * @param {string} expression.name - attribute name
+ * @param {*} value - new expression value
+ * @param {*} oldValue - the old expression cached value
+ * @returns {undefined}
+ */
+
+
+function attributeExpression(node, _ref2, value, oldValue) {
+ let {
+ name
+ } = _ref2;
+
+ // is it a spread operator? {...attributes}
+ if (!name) {
+ if (oldValue) {
+ // remove all the old attributes
+ removeAllAttributes(node, value, oldValue);
+ } // is the value still truthy?
+
+
+ if (value) {
+ setAllAttributes(node, value);
+ }
+
+ return;
+ } // handle boolean attributes
+
+
+ if (!isNativeHtmlProperty(name) && (isBoolean(value) || isObject(value) || isFunction(value))) {
+ node[name] = value;
+ }
+
+ if (shouldRemoveAttribute(value)) {
+ node.removeAttribute(name);
+ } else if (canRenderAttribute(value)) {
+ node.setAttribute(name, normalizeValue(name, value));
+ }
+}
+/**
+ * Get the value as string
+ * @param {string} name - attribute name
+ * @param {*} value - user input value
+ * @returns {string} input value as string
+ */
+
+function normalizeValue(name, value) {
+ // be sure that expressions like selected={ true } will be always rendered as selected='selected'
+ return value === true ? name : value;
+}
+
+const RE_EVENTS_PREFIX = /^on/;
+
+const getCallbackAndOptions = value => Array.isArray(value) ? value : [value, false]; // see also https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
+
+
+const EventListener = {
+ handleEvent(event) {
+ this[event.type](event);
+ }
+
+};
+const ListenersWeakMap = new WeakMap();
+
+const createListener = node => {
+ const listener = Object.create(EventListener);
+ ListenersWeakMap.set(node, listener);
+ return listener;
+};
+/**
+ * Set a new event listener
+ * @param {HTMLElement} node - target node
+ * @param {Object} expression - expression object
+ * @param {string} expression.name - event name
+ * @param {*} value - new expression value
+ * @returns {value} the callback just received
+ */
+
+
+function eventExpression(node, _ref, value) {
+ let {
+ name
+ } = _ref;
+ const normalizedEventName = name.replace(RE_EVENTS_PREFIX, '');
+ const eventListener = ListenersWeakMap.get(node) || createListener(node);
+ const [callback, options] = getCallbackAndOptions(value);
+ const handler = eventListener[normalizedEventName];
+ const mustRemoveEvent = handler && !callback;
+ const mustAddEvent = callback && !handler;
+
+ if (mustRemoveEvent) {
+ node.removeEventListener(normalizedEventName, eventListener);
+ }
+
+ if (mustAddEvent) {
+ node.addEventListener(normalizedEventName, eventListener, options);
+ }
+
+ eventListener[normalizedEventName] = callback;
+}
+
+/**
+ * Normalize the user value in order to render a empty string in case of falsy values
+ * @param {*} value - user input value
+ * @returns {string} hopefully a string
+ */
+
+function normalizeStringValue(value) {
+ return isNil(value) ? '' : value;
+}
+
+/**
+ * Get the the target text node to update or create one from of a comment node
+ * @param {HTMLElement} node - any html element containing childNodes
+ * @param {number} childNodeIndex - index of the text node in the childNodes list
+ * @returns {Text} the text node to update
+ */
+
+const getTextNode = (node, childNodeIndex) => {
+ const target = node.childNodes[childNodeIndex];
+
+ if (target.nodeType === Node.COMMENT_NODE) {
+ const textNode = document.createTextNode('');
+ node.replaceChild(textNode, target);
+ return textNode;
+ }
+
+ return target;
+};
+/**
+ * This methods handles a simple text expression update
+ * @param {HTMLElement} node - target node
+ * @param {Object} data - expression object
+ * @param {*} value - new expression value
+ * @returns {undefined}
+ */
+
+function textExpression(node, data, value) {
+ node.data = normalizeStringValue(value);
+}
+
+/**
+ * This methods handles the input fileds value updates
+ * @param {HTMLElement} node - target node
+ * @param {Object} expression - expression object
+ * @param {*} value - new expression value
+ * @returns {undefined}
+ */
+
+function valueExpression(node, expression, value) {
+ node.value = normalizeStringValue(value);
+}
+
+var expressions = {
+ [ATTRIBUTE]: attributeExpression,
+ [EVENT]: eventExpression,
+ [TEXT]: textExpression,
+ [VALUE]: valueExpression
+};
+
+const Expression = {
+ // Static props
+ // node: null,
+ // value: null,
+ // API methods
+
+ /**
+ * Mount the expression evaluating its initial value
+ * @param {*} scope - argument passed to the expression to evaluate its current values
+ * @returns {Expression} self
+ */
+ mount(scope) {
+ // hopefully a pure function
+ this.value = this.evaluate(scope); // IO() DOM updates
+
+ apply(this, this.value);
+ return this;
+ },
+
+ /**
+ * Update the expression if its value changed
+ * @param {*} scope - argument passed to the expression to evaluate its current values
+ * @returns {Expression} self
+ */
+ update(scope) {
+ // pure function
+ const value = this.evaluate(scope);
+
+ if (this.value !== value) {
+ // IO() DOM updates
+ apply(this, value);
+ this.value = value;
+ }
+
+ return this;
+ },
+
+ /**
+ * Expression teardown method
+ * @returns {Expression} self
+ */
+ unmount() {
+ // unmount only the event handling expressions
+ if (this.type === EVENT) apply(this, null);
+ return this;
+ }
+
+};
+/**
+ * IO() function to handle the DOM updates
+ * @param {Expression} expression - expression object
+ * @param {*} value - current expression value
+ * @returns {undefined}
+ */
+
+function apply(expression, value) {
+ return expressions[expression.type](expression.node, expression, value, expression.value);
+}
+
+function create$4(node, data) {
+ return Object.assign({}, Expression, data, {
+ node: data.type === TEXT ? getTextNode(node, data.childNodeIndex) : node
+ });
+}
+
+/**
+ * Create a flat object having as keys a list of methods that if dispatched will propagate
+ * on the whole collection
+ * @param {Array} collection - collection to iterate
+ * @param {Array} methods - methods to execute on each item of the collection
+ * @param {*} context - context returned by the new methods created
+ * @returns {Object} a new object to simplify the the nested methods dispatching
+ */
+function flattenCollectionMethods(collection, methods, context) {
+ return methods.reduce((acc, method) => {
+ return Object.assign({}, acc, {
+ [method]: scope => {
+ return collection.map(item => item[method](scope)) && context;
+ }
+ });
+ }, {});
+}
+
+function create$3(node, _ref) {
+ let {
+ expressions
+ } = _ref;
+ return Object.assign({}, flattenCollectionMethods(expressions.map(expression => create$4(node, expression)), ['mount', 'update', 'unmount']));
+}
+
+function extendParentScope(attributes, scope, parentScope) {
+ if (!attributes || !attributes.length) return parentScope;
+ const expressions = attributes.map(attr => Object.assign({}, attr, {
+ value: attr.evaluate(scope)
+ }));
+ return Object.assign(Object.create(parentScope || null), evaluateAttributeExpressions(expressions));
+} // this function is only meant to fix an edge case
+// https://github.com/riot/riot/issues/2842
+
+
+const getRealParent = (scope, parentScope) => scope[PARENT_KEY_SYMBOL] || parentScope;
+
+const SlotBinding = {
+ // dynamic binding properties
+ // node: null,
+ // name: null,
+ attributes: [],
+
+ // template: null,
+ getTemplateScope(scope, parentScope) {
+ return extendParentScope(this.attributes, scope, parentScope);
+ },
+
+ // API methods
+ mount(scope, parentScope) {
+ const templateData = scope.slots ? scope.slots.find(_ref => {
+ let {
+ id
+ } = _ref;
+ return id === this.name;
+ }) : false;
+ const {
+ parentNode
+ } = this.node;
+ const realParent = getRealParent(scope, parentScope);
+ this.template = templateData && create(templateData.html, templateData.bindings).createDOM(parentNode);
+
+ if (this.template) {
+ cleanNode(this.node);
+ this.template.mount(this.node, this.getTemplateScope(scope, realParent), realParent);
+ this.template.children = Array.from(this.node.childNodes);
+ }
+
+ moveSlotInnerContent(this.node);
+ removeChild(this.node);
+ return this;
+ },
+
+ update(scope, parentScope) {
+ if (this.template) {
+ const realParent = getRealParent(scope, parentScope);
+ this.template.update(this.getTemplateScope(scope, realParent), realParent);
+ }
+
+ return this;
+ },
+
+ unmount(scope, parentScope, mustRemoveRoot) {
+ if (this.template) {
+ this.template.unmount(this.getTemplateScope(scope, parentScope), null, mustRemoveRoot);
+ }
+
+ return this;
+ }
+
+};
+/**
+ * Move the inner content of the slots outside of them
+ * @param {HTMLElement} slot - slot node
+ * @returns {undefined} it's a void method ¯\_(ツ)_/¯
+ */
+
+function moveSlotInnerContent(slot) {
+ const child = slot && slot.firstChild;
+ if (!child) return;
+ insertBefore(child, slot);
+ moveSlotInnerContent(slot);
+}
+/**
+ * Create a single slot binding
+ * @param {HTMLElement} node - slot node
+ * @param {string} name - slot id
+ * @param {AttributeExpressionData[]} attributes - slot attributes
+ * @returns {Object} Slot binding object
+ */
+
+
+function createSlot(node, _ref2) {
+ let {
+ name,
+ attributes
+ } = _ref2;
+ return Object.assign({}, SlotBinding, {
+ attributes,
+ node,
+ name
+ });
+}
+
+/**
+ * Create a new tag object if it was registered before, otherwise fallback to the simple
+ * template chunk
+ * @param {Function} component - component factory function
+ * @param {Array