From f3cc6f3e90520818c207988316d4cbb5a7d5039f Mon Sep 17 00:00:00 2001 From: HerrHase Date: Thu, 7 Apr 2022 22:47:50 +0200 Subject: [PATCH] adding --- mix-manifest.json | 15 +- package-lock.json | 13 +- package.json | 3 +- packages/frontend/js/app.js | 8 +- packages/frontend/js/components/loading.riot | 15 + .../frontend/js/components/notification.riot | 115 + packages/frontend/js/components/sidebar.riot | 12 +- .../frontend/js/components/task-form.riot | 53 +- packages/frontend/js/components/task-new.riot | 10 +- packages/frontend/js/mixins/sidebar.js | 3 +- packages/frontend/js/stores/notification.js | 49 + packages/frontend/js/stores/sidebar.js | 21 - packages/frontend/package.json | 6 +- .../scss/components/_field-error.scss | 7 + .../frontend/scss/components/_loading.scss | 22 + packages/frontend/scss/styles.scss | 6 +- packages/frontend/views/index.liquid | 3 + packages/frontend/views/layout.liquid | 1 + packages/frontend/webpack.mix.js | 18 +- packages/server/http/index.js | 7 +- public/css/styles.css | 26 + public/js/app.js | 9521 +++++++------ public/symbol-defs.svg | 2 +- yarn.lock | 11681 ++++++++-------- 24 files changed, 11234 insertions(+), 10383 deletions(-) create mode 100644 packages/frontend/js/components/loading.riot create mode 100644 packages/frontend/js/components/notification.riot create mode 100644 packages/frontend/js/stores/notification.js delete mode 100644 packages/frontend/js/stores/sidebar.js create mode 100644 packages/frontend/scss/components/_field-error.scss create mode 100644 packages/frontend/scss/components/_loading.scss diff --git a/mix-manifest.json b/mix-manifest.json index 4ed0b80..79b4870 100644 --- a/mix-manifest.json +++ b/mix-manifest.json @@ -1,16 +1,5 @@ { "/public/js/spritemap.js": "/public/js/spritemap.js?id=2dda73ecee3bb668b395026efda6524c", - "/public/js/app.js": "/public/js/app.js?id=3af721e60a47e6147d71c98308c2b6e1", - "/public/css/styles.css": "/public/css/styles.css?id=d8096bd2ff84ae29cb0ecf810064b218", - "/public/css/demo.html": "/public/css/demo.html?id=a54a5d205e3152fb64b33dda63ffa555", - "/public/css/IBMPlexMono-Bold.eot": "/public/css/IBMPlexMono-Bold.eot?id=ef1fadf711db80a00542b202ab14f7ee", - "/public/css/IBMPlexMono-Bold.ttf": "/public/css/IBMPlexMono-Bold.ttf?id=e46cace25a93f48a2ec32800717827cb", - "/public/css/IBMPlexMono-Bold.woff": "/public/css/IBMPlexMono-Bold.woff?id=8864bd7cb954c4646045e3fc0bdec90c", - "/public/css/IBMPlexMono-Bold.woff2": "/public/css/IBMPlexMono-Bold.woff2?id=c6d3f08fe7a9fecab40d748b98c87cc5", - "/public/css/IBMPlexMono.eot": "/public/css/IBMPlexMono.eot?id=d68f064d6b86ff47b38ad486a3362d82", - "/public/css/IBMPlexMono.ttf": "/public/css/IBMPlexMono.ttf?id=60d8ae961dba3289c1d2d54e0b85c9b7", - "/public/css/IBMPlexMono.woff": "/public/css/IBMPlexMono.woff?id=18a7a5a76b4176759e2e1b3e674a7f82", - "/public/css/IBMPlexMono.woff2": "/public/css/IBMPlexMono.woff2?id=428bd06c5eb0362494016994c26188b4", - "/public/css/OFL.txt": "/public/css/OFL.txt?id=5c7bb1d9d37e52d30b53224261c955b2", - "/public/css/stylesheet.css": "/public/css/stylesheet.css?id=a3e561da46246c3c582cddec544ad25f" + "/public/js/app.js": "/public/js/app.js?id=1c9307a192e1817119e543e388f2a43b", + "/public/css/styles.css": "/public/css/styles.css?id=72a43d98017bdbcb83928c41f6289210" } diff --git a/package-lock.json b/package-lock.json index da6b69e..ed876e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2054,7 +2054,8 @@ "@tiny-components/plain-ui": "^0.5.0", "@tiny-components/validator": "^0.1.0", "got": "^12.0.1", - "riot": "^6.1.2" + "riot": "^6.1.2", + "uuid": "^8.3.2" }, "devDependencies": { "@riotjs/webpack-loader": "^6.0.0", @@ -12125,8 +12126,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -14423,7 +14422,7 @@ "frontend": { "version": "file:packages/frontend", "requires": { - "@riotjs/observable": "*", + "@riotjs/observable": "^4.1.1", "@riotjs/webpack-loader": "^6.0.0", "@tiny-components/plain-ui": "^0.5.0", "@tiny-components/validator": "^0.1.0", @@ -14433,7 +14432,8 @@ "riot": "^6.1.2", "sass": "^1.49.9", "sass-loader": "^12.6.0", - "svg-spritemap-webpack-plugin": "^4.4.0" + "svg-spritemap-webpack-plugin": "^4.4.0", + "uuid": "^8.3.2" }, "dependencies": { "@ampproject/remapping": { @@ -21489,8 +21489,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "vary": { "version": "1.1.2", diff --git a/package.json b/package.json index 3593205..d4eed0e 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "packages/*" ], "scripts": { - "start": "yarn workspace runner run start" + "start": "yarn workspace server start", + "build": "yarn workspace frontend build" } } diff --git a/packages/frontend/js/app.js b/packages/frontend/js/app.js index e8db547..ed9ef11 100644 --- a/packages/frontend/js/app.js +++ b/packages/frontend/js/app.js @@ -6,14 +6,20 @@ import AppTasks from './components/tasks.riot' import AppTaskButton from './components/task-new.riot' import AppTaskForm from './components/task-form.riot' +import AppNotification from './components/notification.riot' + // register components riot.register('app-sidebar', AppSidebar) riot.register('app-tasks', AppTasks) riot.register('app-task-button', AppTaskButton) riot.register('app-task-form', AppTaskForm) +riot.register('app-notification', AppNotification) + // mount components riot.mount('app-sidebar') riot.mount('app-tasks') riot.mount('app-task-button') -riot.mount('app-task-form') \ No newline at end of file +riot.mount('app-task-form') + +riot.mount('app-notification') \ No newline at end of file diff --git a/packages/frontend/js/components/loading.riot b/packages/frontend/js/components/loading.riot new file mode 100644 index 0000000..62b5ff2 --- /dev/null +++ b/packages/frontend/js/components/loading.riot @@ -0,0 +1,15 @@ + +
+
+ + + +
+
+ + +
\ No newline at end of file diff --git a/packages/frontend/js/components/notification.riot b/packages/frontend/js/components/notification.riot new file mode 100644 index 0000000..08d019e --- /dev/null +++ b/packages/frontend/js/components/notification.riot @@ -0,0 +1,115 @@ + +
0 }> +
{ this.handleClick(event, item) } }> + +
+ { item.message } +
+ +
+
+ +
\ No newline at end of file diff --git a/packages/frontend/js/components/sidebar.riot b/packages/frontend/js/components/sidebar.riot index 980d9a5..6d90fa3 100644 --- a/packages/frontend/js/components/sidebar.riot +++ b/packages/frontend/js/components/sidebar.riot @@ -9,7 +9,7 @@
@@ -19,17 +19,18 @@ +
+ +
+
+ +
@@ -58,7 +72,11 @@ import FormValidator from '@tiny-components/validator/src/formValidator.js' + // stores import taskFormStore from './../stores/taskForm.js' + import notificationStore from './../stores/notification' + + // mixins import sidebar from './../mixins/sidebar.js' import FieldError from '@tiny-components/validator/src/fieldError.riot' @@ -72,13 +90,20 @@ export default () => { return { - ...sidebar, + ...sidebar, // adding basic funtion for sidebar state: { requestHandlers: [], validator: undefined, current: { + name: '', + url: '', + requestHandler: '', + action: '', + options: { + }, + timer: '' } }, @@ -98,6 +123,12 @@ }, 'requestHandler': { 'presence': true + }, + 'action': { + 'presence': true + }, + 'timer': { + 'presence': true } }) @@ -106,6 +137,14 @@ this.handleSuccess(event, data) }) + // adding on success + this.state.validator.onError((event, data) => { + this.state.isLoading = false + notificationStore.danger('Error! Check your input!') + + this.update() + }) + taskFormStore.on('open', (data) => { this.state.isOpen = true this.update() diff --git a/packages/frontend/js/components/task-new.riot b/packages/frontend/js/components/task-new.riot index 8c26881..e9f6a05 100644 --- a/packages/frontend/js/components/task-new.riot +++ b/packages/frontend/js/components/task-new.riot @@ -1,6 +1,6 @@
', + [ + { + redundantAttribute: 'expr12', + selector: '[expr12]', -// urlform encoding serializer -function str_serialize(result, key, value) { - // encode newlines as \r\n cause the html spec says so - value = value.replace(/(\r)?\n/g, '\r\n'); - value = encodeURIComponent(value); + expressions: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'class', + evaluate: _scope => _scope.getCssClasses() + } + ] + }, + { + type: bindingTypes.SLOT, + attributes: [], + name: 'title', + redundantAttribute: 'expr13', + selector: '[expr13]' + }, + { + redundantAttribute: 'expr14', + selector: '[expr14]', - // spaces should be '+' rather than '%20'. - value = value.replace(/%20/g, '+'); - return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; -} + expressions: [ + { + type: expressionTypes.EVENT, + name: 'onclick', + evaluate: _scope => (event) => { _scope.handleClose(event) } + } + ] + }, + { + type: bindingTypes.SLOT, + attributes: [], + name: 'form', + redundantAttribute: 'expr15', + selector: '[expr15]' + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'app-loading', + slots: [], -module.exports = serialize; + attributes: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'loading', + evaluate: _scope => _scope.props.loading + } + ], + + redundantAttribute: 'expr16', + selector: '[expr16]' + }, + { + redundantAttribute: 'expr17', + selector: '[expr17]', + + expressions: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'form', + evaluate: _scope => _scope.props.formId + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'disabled', + evaluate: _scope => _scope.props.loading + } + ] + }, + { + redundantAttribute: 'expr18', + selector: '[expr18]', + + expressions: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'form', + evaluate: _scope => _scope.props.formId + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'disabled', + evaluate: _scope => _scope.props.loading + } + ] + } + ] + ), + name: 'app-sidebar' +}); /***/ }), -/***/ "../../node_modules/validate.js/validate.js": -/*!**************************************************!*\ - !*** ../../node_modules/validate.js/validate.js ***! - \**************************************************/ -/***/ (function(module, exports, __webpack_require__) { +/***/ "./js/components/task-form.riot": +/*!**************************************!*\ + !*** ./js/components/task-form.riot ***! + \**************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -/* module decorator */ module = __webpack_require__.nmd(module); -/*! - * validate.js 0.13.1 - * - * (c) 2013-2019 Nicklas Ansman, 2013 Wrapp - * Validate.js may be freely distributed under the MIT license. - * For all details and documentation: - * http://validatejs.org/ - */ +"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 riot__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! riot */ "../../node_modules/riot/riot.esm.js"); +/* harmony import */ var _tiny_components_validator_src_formValidator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tiny-components/validator/src/formValidator.js */ "../../node_modules/@tiny-components/validator/src/formValidator.js"); +/* harmony import */ var _stores_taskForm_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./../stores/taskForm.js */ "./js/stores/taskForm.js"); +/* harmony import */ var _stores_notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./../stores/notification */ "./js/stores/notification.js"); +/* harmony import */ var _mixins_sidebar_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./../mixins/sidebar.js */ "./js/mixins/sidebar.js"); +/* harmony import */ var _tiny_components_validator_src_fieldError_riot__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @tiny-components/validator/src/fieldError.riot */ "../../node_modules/@tiny-components/validator/src/fieldError.riot"); -(function(exports, module, define) { - "use strict"; - // The main function that calls the validators specified by the constraints. - // The options are the following: - // - format (string) - An option that controls how the returned value is formatted - // * flat - Returns a flat array of just the error messages - // * grouped - Returns the messages grouped by attribute (default) - // * detailed - Returns an array of the raw validation data - // - fullMessages (boolean) - If `true` (default) the attribute name is prepended to the error. - // - // Please note that the options are also passed to each validator. - var validate = function(attributes, constraints, options) { - options = v.extend({}, v.options, options); - var results = v.runValidations(attributes, constraints, options) - , attr - , validator; - if (results.some(function(r) { return v.isPromise(r.error); })) { - throw new Error("Use validate.async if you want support for promises"); - } - return validate.processValidationResults(results, options); - }; +// stores - var v = validate; - // Copies over attributes from one or more sources to a single destination. - // Very much similar to underscore's extend. - // The first argument is the target object and the remaining arguments will be - // used as sources. - v.extend = function(obj) { - [].slice.call(arguments, 1).forEach(function(source) { - for (var attr in source) { - obj[attr] = source[attr]; - } - }); - return obj; - }; - v.extend(validate, { - // This is the version of the library as a semver. - // The toString function will allow it to be coerced into a string - version: { - major: 0, - minor: 13, - patch: 1, - metadata: null, - toString: function() { - var version = v.format("%{major}.%{minor}.%{patch}", v.version); - if (!v.isEmpty(v.version.metadata)) { - version += "+" + v.version.metadata; - } - return version; - } - }, +// mixins - // Below is the dependencies that are used in validate.js - - // The constructor of the Promise implementation. - // If you are using Q.js, RSVP or any other A+ compatible implementation - // override this attribute to be the constructor of that promise. - // Since jQuery promises aren't A+ compatible they won't work. - Promise: typeof Promise !== "undefined" ? Promise : /* istanbul ignore next */ null, - - EMPTY_STRING_REGEXP: /^\s*$/, - // Runs the validators specified by the constraints object. - // Will return an array of the format: - // [{attribute: "", error: ""}, ...] - runValidations: function(attributes, constraints, options) { - var results = [] - , attr - , validatorName - , value - , validators - , validator - , validatorOptions - , error; - if (v.isDomElement(attributes) || v.isJqueryElement(attributes)) { - attributes = v.collectFormValues(attributes); - } +riot__WEBPACK_IMPORTED_MODULE_5__.register('field-error', _tiny_components_validator_src_fieldError_riot__WEBPACK_IMPORTED_MODULE_4__["default"]) - // Loops through each constraints, finds the correct validator and run it. - for (attr in constraints) { - value = v.getDeepObjectValue(attributes, attr); - // This allows the constraints for an attribute to be a function. - // The function will be called with the value, attribute name, the complete dict of - // attributes as well as the options and constraints passed in. - // This is useful when you want to have different - // validations depending on the attribute value. - validators = v.result(constraints[attr], value, attributes, attr, options, constraints); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ + css: null, - for (validatorName in validators) { - validator = v.validators[validatorName]; + exports: () => { + return { - if (!validator) { - error = v.format("Unknown validator %{name}", {name: validatorName}); - throw new Error(error); - } + ..._mixins_sidebar_js__WEBPACK_IMPORTED_MODULE_3__["default"], // adding basic funtion for sidebar - validatorOptions = validators[validatorName]; - // This allows the options to be a function. The function will be - // called with the value, attribute name, the complete dict of - // attributes as well as the options and constraints passed in. - // This is useful when you want to have different - // validations depending on the attribute value. - validatorOptions = v.result(validatorOptions, value, attributes, attr, options, constraints); - if (!validatorOptions) { - continue; - } - results.push({ - attribute: attr, - value: value, - validator: validatorName, - globalOptions: options, - attributes: attributes, - options: validatorOptions, - error: validator.call(validator, - value, - validatorOptions, - attr, - attributes, - options) - }); - } - } + state: { + requestHandlers: [], + validator: undefined, + current: { + name: '', + url: '', + requestHandler: '', + action: '', + options: { - return results; - }, + }, + timer: '' + } + }, - // Takes the output from runValidations and converts it to the correct - // output format. - processValidationResults: function(errors, options) { - errors = v.pruneEmptyErrors(errors, options); - errors = v.expandMultipleErrors(errors, options); - errors = v.convertErrorMessages(errors, options); + /** + * + * + */ + onMounted() + { + // creating formValidator + this.state.validator = new _tiny_components_validator_src_formValidator_js__WEBPACK_IMPORTED_MODULE_0__["default"](this.$('.form'), { + 'name': { + 'presence': true + }, + 'url': { + 'presence': true + }, + 'requestHandler': { + 'presence': true + }, + 'action': { + 'presence': true + }, + 'timer': { + 'presence': true + } + }) - var format = options.format || "grouped"; + // adding on success + this.state.validator.onSuccess((event, data) => { + this.handleSuccess(event, data) + }) - if (typeof v.formatters[format] === 'function') { - errors = v.formatters[format](errors); - } else { - throw new Error(v.format("Unknown format %{format}", options)); - } + // adding on success + this.state.validator.onError((event, data) => { + this.state.isLoading = false + _stores_notification__WEBPACK_IMPORTED_MODULE_2__["default"].danger('Error! Check your input!') - return v.isEmpty(errors) ? undefined : errors; - }, + this.update() + }) - // Runs the validations with support for promises. - // This function will return a promise that is settled when all the - // validation promises have been completed. - // It can be called even if no validations returned a promise. - async: function(attributes, constraints, options) { - options = v.extend({}, v.async.options, options); + _stores_taskForm_js__WEBPACK_IMPORTED_MODULE_1__["default"].on('open', (data) => { + this.state.isOpen = true + this.update() + }) + }, - var WrapErrors = options.wrapErrors || function(errors) { - return errors; - }; + /** + * + * + * + */ + handleSuccess(event, data) + { - // Removes unknown attributes - if (options.cleanAttributes !== false) { - attributes = v.cleanAttributes(attributes, constraints); + } } + }, - var results = v.runValidations(attributes, constraints, options); + template: ( + template, + expressionTypes, + bindingTypes, + getComponent + ) => template( + '', + [ + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'app-sidebar', - return new v.Promise(function(resolve, reject) { - v.waitForResults(results).then(function() { - var errors = v.processValidationResults(results, options); - if (errors) { - reject(new WrapErrors(errors, options, attributes, constraints)); - } else { - resolve(attributes); - } - }, function(err) { - reject(err); - }); - }); - }, + slots: [ + { + id: 'title', + html: '', - single: function(value, constraints, options) { - options = v.extend({}, v.single.options, options, { - format: "flat", - fullMessages: false - }); - return v({single: value}, {single: constraints}, options); - }, + bindings: [ + { + type: bindingTypes.IF, + evaluate: _scope => _scope.state.current.name, + redundantAttribute: 'expr23', + selector: '[expr23]', - // Returns a promise that is resolved when all promises in the results array - // are settled. The promise returned from this function is always resolved, - // never rejected. - // This function modifies the input argument, it replaces the promises - // with the value returned from the promise. - waitForResults: function(results) { - // Create a sequence of all the results starting with a resolved promise. - return results.reduce(function(memo, result) { - // If this result isn't a promise skip it in the sequence. - if (!v.isPromise(result.error)) { - return memo; - } + template: template( + null, + [ + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'virtual', - return memo.then(function() { - return result.error.then(function(error) { - result.error = error || null; - }); - }); - }, new v.Promise(function(r) { r(); })); // A resolved promise - }, + slots: [ + { + id: 'default', + html: ' ', - // If the given argument is a call: function the and: function return the value - // otherwise just return the value. Additional arguments will be passed as - // arguments to the function. - // Example: - // ``` - // result('foo') // 'foo' - // result(Math.max, 1, 2) // 2 - // ``` - result: function(value) { - var args = [].slice.call(arguments, 1); - if (typeof value === 'function') { - value = value.apply(null, args); - } - return value; - }, + bindings: [ + { + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - // Checks if the value is a number. This function does not consider NaN a - // number like many other `isNumber` functions do. - isNumber: function(value) { - return typeof value === 'number' && !isNaN(value); - }, - - // Returns false if the object is not a function - isFunction: function(value) { - return typeof value === 'function'; - }, + evaluate: _scope => [ + 'Edit Task ', + _scope.state.current.name + ].join( + '' + ) + } + ] + } + ] + } + ], - // A simple check to verify that the value is an integer. Uses `isNumber` - // and a simple modulo check. - isInteger: function(value) { - return v.isNumber(value) && value % 1 === 0; - }, + attributes: [] + } + ] + ) + }, + { + type: bindingTypes.IF, + evaluate: _scope => !_scope.state.current.name, + redundantAttribute: 'expr24', + selector: '[expr24]', - // Checks if the value is a boolean - isBoolean: function(value) { - return typeof value === 'boolean'; - }, + template: template( + null, + [ + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'virtual', - // Uses the `Object` function to check if the given argument is an object. - isObject: function(obj) { - return obj === Object(obj); - }, + slots: [ + { + id: 'default', + html: '\n New Task\n ', + bindings: [] + } + ], - // Simply checks if the object is an instance of a date - isDate: function(obj) { - return obj instanceof Date; - }, + attributes: [] + } + ] + ) + } + ] + }, + { + id: 'form', + html: '
', - // Returns false if the object is `null` of `undefined` - isDefined: function(obj) { - return obj !== null && obj !== undefined; - }, + bindings: [ + { + redundantAttribute: 'expr25', + selector: '[expr25]', - // Checks if the given argument is a promise. Anything with a `then` - // function is considered a promise. - isPromise: function(p) { - return !!p && v.isFunction(p.then); - }, + expressions: [ + { + type: expressionTypes.EVENT, + name: 'onsubmit', + evaluate: _scope => (event) => { _scope.state.isLoading = true; _scope.update(); _scope.state.validator.submit(event) } + } + ] + }, + { + redundantAttribute: 'expr26', + selector: '[expr26]', - isJqueryElement: function(o) { - return o && v.isString(o.jquery); - }, + expressions: [ + { + type: expressionTypes.VALUE, + evaluate: _scope => _scope.state.current.name + } + ] + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr27', + selector: '[expr27]' + }, + { + redundantAttribute: 'expr28', + selector: '[expr28]', - isDomElement: function(o) { - if (!o) { - return false; - } + expressions: [ + { + type: expressionTypes.VALUE, + evaluate: _scope => _scope.state.current.url + } + ] + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr29', + selector: '[expr29]' + }, + { + type: bindingTypes.EACH, + getKey: null, + condition: null, - if (!o.querySelectorAll || !o.querySelector) { - return false; - } + template: template( + null, + [ + { + expressions: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'value', + evaluate: _scope => _scope.handler + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'selected', + evaluate: _scope => _scope.action === _scope.state.current.requestHandler + } + ] + } + ] + ), - if (v.isObject(document) && o === document) { - return true; - } + redundantAttribute: 'expr30', + selector: '[expr30]', + itemName: 'handler', + indexName: null, + evaluate: _scope => _scope.state.requestHandlers + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr31', + selector: '[expr31]' + }, + { + type: bindingTypes.EACH, + getKey: null, + condition: null, - // http://stackoverflow.com/a/384380/699304 - /* istanbul ignore else */ - if (typeof HTMLElement === "object") { - return o instanceof HTMLElement; - } else { - return o && - typeof o === "object" && - o !== null && - o.nodeType === 1 && - typeof o.nodeName === "string"; - } - }, + template: template( + null, + [ + { + expressions: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'value', + evaluate: _scope => _scope.action + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'selected', + evaluate: _scope => _scope.action === _scope.state.current.action + } + ] + } + ] + ), - isEmpty: function(value) { - var attr; + redundantAttribute: 'expr32', + selector: '[expr32]', + itemName: 'action', + indexName: null, + evaluate: _scope => _scope.state.actions + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr33', + selector: '[expr33]' + }, + { + redundantAttribute: 'expr34', + selector: '[expr34]', - // Null and undefined are empty - if (!v.isDefined(value)) { - return true; - } + expressions: [ + { + type: expressionTypes.VALUE, + evaluate: _scope => JSON.stringify(_scope.state.current.options) + } + ] + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr35', + selector: '[expr35]' + }, + { + redundantAttribute: 'expr36', + selector: '[expr36]', - // functions are non empty - if (v.isFunction(value)) { - return false; - } + expressions: [ + { + type: expressionTypes.VALUE, + evaluate: _scope => _scope.state.current.timer + } + ] + }, + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'field-error', + slots: [], + attributes: [], + redundantAttribute: 'expr37', + selector: '[expr37]' + } + ] + } + ], - // Whitespace only strings are empty - if (v.isString(value)) { - return v.EMPTY_STRING_REGEXP.test(value); - } + attributes: [ + { + type: expressionTypes.ATTRIBUTE, + name: 'open', + evaluate: _scope => _scope.state.isOpen + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'close', + evaluate: _scope => () => { _scope.handleClose() } + }, + { + type: expressionTypes.ATTRIBUTE, + name: 'loading', + evaluate: _scope => _scope.state.isLoading + } + ], - // For arrays we use the length property - if (v.isArray(value)) { - return value.length === 0; + redundantAttribute: 'expr22', + selector: '[expr22]' } + ] + ), - // Dates have no attributes but aren't empty - if (v.isDate(value)) { - return false; - } + name: 'app-task-form' +}); - // If we find at least one property we consider it non empty - if (v.isObject(value)) { - for (attr in value) { - return false; - } - return true; - } +/***/ }), - return false; +/***/ "./js/components/task-new.riot": +/*!*************************************!*\ + !*** ./js/components/task-new.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 import */ var _stores_taskForm_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./../stores/taskForm.js */ "./js/stores/taskForm.js"); + + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ + css: null, + + exports: { + state: { + text: 'new' }, - // Formats the specified strings with the given values like so: - // ``` - // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar" - // ``` - // If you want to write %{...} without having it replaced simply - // prefix it with % like this `Foo: %%{foo}` and it will be returned - // as `"Foo: %{foo}"` - format: v.extend(function(str, vals) { - if (!v.isString(str)) { - return str; - } - return str.replace(v.format.FORMAT_REGEXP, function(m0, m1, m2) { - if (m1 === '%') { - return "%{" + m2 + "}"; - } else { - return String(vals[m2]); + /** + * + * + */ + onMounted() + { + // if props for text is set + if (this.props.text) { + this.state.text = this.props.text } - }); - }, { - // Finds %{key} style patterns in the given string - FORMAT_REGEXP: /(%?)%\{([^\}]+)\}/g - }), + }, - // "Prettifies" the given string. - // Prettifying means replacing [.\_-] with spaces as well as splitting - // camel case words. - prettify: function(str) { - if (v.isNumber(str)) { - // If there are more than 2 decimals round it to two - if ((str * 100) % 1 === 0) { - return "" + str; - } else { - return parseFloat(Math.round(str * 100) / 100).toFixed(2); - } - } + /** + * trigger open in storage + * + * + * @param {object} event + * + */ + handleOpen(event, data = undefined) + { + _stores_taskForm_js__WEBPACK_IMPORTED_MODULE_0__["default"].open(data) + } + }, - if (v.isArray(str)) { - return str.map(function(s) { return v.prettify(s); }).join(", "); - } + template: ( + template, + expressionTypes, + bindingTypes, + getComponent + ) => template( + '', + [ + { + redundantAttribute: 'expr11', + selector: '[expr11]', - if (v.isObject(str)) { - if (!v.isDefined(str.toString)) { - return JSON.stringify(str); - } + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 1, - return str.toString(); + evaluate: _scope => [ + _scope.state.text + ].join( + '' + ) + }, + { + type: expressionTypes.EVENT, + name: 'onclick', + evaluate: _scope => (event) => { _scope.handleOpen(event) } + } + ] } + ] + ), - // Ensure the string is actually a string - str = "" + str; + name: 'app-task-button' +}); - return str - // Splits keys separated by periods - .replace(/([^\s])\.([^\s])/g, '$1 $2') - // Removes backslashes - .replace(/\\+/g, '') - // Replaces - and - with space - .replace(/[_-]/g, ' ') - // Splits camel cased words - .replace(/([a-z])([A-Z])/g, function(m0, m1, m2) { - return "" + m1 + " " + m2.toLowerCase(); - }) - .toLowerCase(); - }, +/***/ }), - stringifyValue: function(value, options) { - var prettify = options && options.prettify || v.prettify; - return prettify(value); - }, +/***/ "./js/components/tasks.riot": +/*!**********************************!*\ + !*** ./js/components/tasks.riot ***! + \**********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - isString: function(value) { - return typeof value === 'string'; - }, +"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, - isArray: function(value) { - return {}.toString.call(value) === '[object Array]'; + exports: { + state: { + tasks: [] }, - // Checks if the object is a hash, which is equivalent to an object that - // is neither an array nor a function. - isHash: function(value) { - return v.isObject(value) && !v.isArray(value) && !v.isFunction(value); + /** + * + * + */ + onMounted() + { + this.getTasks() }, - contains: function(obj, value) { - if (!v.isDefined(obj)) { - return false; - } - if (v.isArray(obj)) { - return obj.indexOf(value) !== -1; - } - return value in obj; + /** + * + * + */ + getTasks() + { + fetch('/api/task', (response) => { + this.state.tasks = response.data + }) }, - unique: function(array) { - if (!v.isArray(array)) { - return array; - } - return array.filter(function(el, index, array) { - return array.indexOf(el) == index; - }); + /** + * + * + */ + handleDelete(event, action) + { + }, - forEachKeyInKeypath: function(object, keypath, callback) { - if (!v.isString(keypath)) { - return undefined; - } + /** + * + * + */ + handleEdit(event, action) + { - var key = "" - , i - , escape = false; + } + }, - for (i = 0; i < keypath.length; ++i) { - switch (keypath[i]) { - case '.': - if (escape) { - escape = false; - key += '.'; - } else { - object = callback(object, key, false); - key = ""; - } - break; + template: ( + template, + expressionTypes, + bindingTypes, + getComponent + ) => template( + '
\n name\n \n url\n \n requestHandler\n \n actions\n \n cron\n
', + [ + { + type: bindingTypes.EACH, + getKey: null, + condition: _scope => _scope.state.tasks.length > 0, - case '\\': - if (escape) { - escape = false; - key += '\\'; - } else { - escape = true; - } - break; + template: template( + ' ', + [ + { + redundantAttribute: 'expr1', + selector: '[expr1]', - default: - escape = false; - key += keypath[i]; - break; - } - } + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - return callback(object, key, true); - }, + evaluate: _scope => [ + _scope.task.name + ].join( + '' + ) + } + ] + }, + { + redundantAttribute: 'expr2', + selector: '[expr2]', - getDeepObjectValue: function(obj, keypath) { - if (!v.isObject(obj)) { - return undefined; - } + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - return v.forEachKeyInKeypath(obj, keypath, function(obj, key) { - if (v.isObject(obj)) { - return obj[key]; - } - }); - }, + evaluate: _scope => [ + _scope.task.url + ].join( + '' + ) + } + ] + }, + { + redundantAttribute: 'expr3', + selector: '[expr3]', - // This returns an object with all the values of the form. - // It uses the input name as key and the value as value - // So for example this: - // - // would return: - // {email: "foo@bar.com"} - collectFormValues: function(form, options) { - var values = {} - , i - , j - , input - , inputs - , option - , value; + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - if (v.isJqueryElement(form)) { - form = form[0]; - } + evaluate: _scope => [ + _scope.task.requestHandler + ].join( + '' + ) + } + ] + }, + { + type: bindingTypes.EACH, + getKey: null, + condition: null, - if (!form) { - return values; - } + template: template( + null, + [ + { + type: bindingTypes.TAG, + getComponent: getComponent, + evaluate: _scope => 'virtual', - options = options || {}; + slots: [ + { + id: 'default', + html: ' ', - inputs = form.querySelectorAll("input[name], textarea[name]"); - for (i = 0; i < inputs.length; ++i) { - input = inputs.item(i); + bindings: [ + { + redundantAttribute: 'expr5', + selector: '[expr5]', - if (v.isDefined(input.getAttribute("data-ignored"))) { - continue; - } + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - var name = input.name.replace(/\./g, "\\\\."); - value = v.sanitizeFormValue(input.value, options); - if (input.type === "number") { - value = value ? +value : null; - } else if (input.type === "checkbox") { - if (input.attributes.value) { - if (!input.checked) { - value = values[name] || null; - } - } else { - value = input.checked; - } - } else if (input.type === "radio") { - if (!input.checked) { - value = values[name] || null; - } - } - values[name] = value; - } + evaluate: _scope => [ + _scope.action.className + ].join( + '' + ) + } + ] + }, + { + type: bindingTypes.IF, + evaluate: _scope => _scope.actions.options, + redundantAttribute: 'expr6', + selector: '[expr6]', - inputs = form.querySelectorAll("select[name]"); - for (i = 0; i < inputs.length; ++i) { - input = inputs.item(i); - if (v.isDefined(input.getAttribute("data-ignored"))) { - continue; - } + template: template( + ' ', + [ + { + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, - if (input.multiple) { - value = []; - for (j in input.options) { - option = input.options[j]; - if (option && option.selected) { - value.push(v.sanitizeFormValue(option.value, options)); + evaluate: _scope => [ + JSON.stringify(_scope.action.options) + ].join( + '' + ) + } + ] + } + ] + ) + } + ] + } + ], + + attributes: [] + } + ] + ), + + redundantAttribute: 'expr4', + selector: '[expr4]', + itemName: 'action', + indexName: null, + evaluate: _scope => _scope.task.actions + }, + { + redundantAttribute: 'expr7', + selector: '[expr7]', + + expressions: [ + { + type: expressionTypes.TEXT, + childNodeIndex: 0, + + evaluate: _scope => [ + _scope.task.cron + ].join( + '' + ) + } + ] + }, + { + redundantAttribute: 'expr8', + selector: '[expr8]', + + expressions: [ + { + type: expressionTypes.EVENT, + name: 'onclick', + evaluate: _scope => (event) => { _scope.handleEdit(event, _scope.task) } + } + ] + }, + { + redundantAttribute: 'expr9', + selector: '[expr9]', + + expressions: [ + { + type: expressionTypes.EVENT, + name: 'onclick', + evaluate: _scope => (event) => { _scope.handleDelete(event, _scope.task) } + } + ] } - } - } else { - var _val = typeof input.options[input.selectedIndex] !== 'undefined' ? input.options[input.selectedIndex].value : /* istanbul ignore next */ ''; - value = v.sanitizeFormValue(_val, options); - } - values[input.name] = value; - } + ] + ), - return values; - }, + redundantAttribute: 'expr0', + selector: '[expr0]', + itemName: 'task', + indexName: null, + evaluate: _scope => _scope.state.tasks + }, + { + type: bindingTypes.IF, + evaluate: _scope => _scope.state.tasks.length === 0, + redundantAttribute: 'expr10', + selector: '[expr10]', - sanitizeFormValue: function(value, options) { - if (options.trim && v.isString(value)) { - value = value.trim(); + template: template( + '\n Nothing found\n ', + [] + ) } + ] + ), - if (options.nullify !== false && value === "") { - return null; - } - return value; - }, + name: 'app-tasks' +}); - capitalize: function(str) { - if (!v.isString(str)) { - return str; - } - return str[0].toUpperCase() + str.slice(1); - }, +/***/ }), - // Remove all errors who's error attribute is empty (null or undefined) - pruneEmptyErrors: function(errors) { - return errors.filter(function(error) { - return !v.isEmpty(error.error); - }); - }, +/***/ "../../node_modules/@tiny-components/validator/src/formValidator.js": +/*!**************************************************************************!*\ + !*** ../../node_modules/@tiny-components/validator/src/formValidator.js ***! + \**************************************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - // In - // [{error: ["err1", "err2"], ...}] - // Out - // [{error: "err1", ...}, {error: "err2", ...}] - // - // All attributes in an error with multiple messages are duplicated - // when expanding the errors. - expandMultipleErrors: function(errors) { - var ret = []; - errors.forEach(function(error) { - // Removes errors without a message - if (v.isArray(error.error)) { - error.error.forEach(function(msg) { - ret.push(v.extend({}, error, {error: msg})); - }); - } else { - ret.push(error); - } - }); - return ret; - }, +"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__); - // Converts the error mesages by prepending the attribute name unless the - // message is prefixed by ^ - convertErrorMessages: function(errors, options) { - options = options || {}; - var ret = [] - , prettify = options.prettify || v.prettify; - errors.forEach(function(errorInfo) { - var error = v.result(errorInfo.error, - errorInfo.value, - errorInfo.attribute, - errorInfo.options, - errorInfo.attributes, - errorInfo.globalOptions); - if (!v.isString(error)) { - ret.push(errorInfo); - return; - } +/** + * Form Validator with RiotJS Components + * + * + * @author HerrHase + * + */ - if (error[0] === '^') { - error = error.slice(1); - } else if (options.fullMessages !== false) { - error = v.capitalize(prettify(errorInfo.attribute)) + " " + error; - } - error = error.replace(/\\\^/g, "^"); - error = v.format(error, { - value: v.stringifyValue(errorInfo.value, options) - }); - ret.push(v.extend({}, errorInfo, {error: error})); - }); - return ret; - }, +class FormValidator +{ + /** + * + * @param {[type]} formSelector [description] + * @param {[type]} constraits [description] + */ + constructor(formElement, constraits, addSubmitEvent = false) + { + // constraits for validate.js + this.constraits = constraits - // In: - // [{attribute: "", ...}] - // Out: - // {"": [{attribute: "", ...}]} - groupErrorsByAttribute: function(errors) { - var ret = {}; - errors.forEach(function(error) { - var list = ret[error.attribute]; - if (list) { - list.push(error); - } else { - ret[error.attribute] = [error]; + // get form and elements + this.formElement = formElement + + // if form not found + if (!this.formElement) { + console.error('FormValidator: form not found!') } - }); - return ret; - }, - // In: - // [{error: "", ...}, {error: "", ...}] - // Out: - // ["", ""] - flattenErrorsToArray: function(errors) { - return errors - .map(function(error) { return error.error; }) - .filter(function(value, index, self) { - return self.indexOf(value) === index; - }); - }, + this.elements = this.formElement.querySelectorAll('field-error') - cleanAttributes: function(attributes, whitelist) { - function whitelistCreator(obj, key, last) { - if (v.isObject(obj[key])) { - return obj[key]; - } - return (obj[key] = last ? true : {}); - } + // adding event if a element is updated + this.formElement.addEventListener('field-update', (event) => { + this._onFieldUpdate(event) + }) - function buildObjectWhitelist(whitelist) { - var ow = {} - , lastObject - , attr; - for (attr in whitelist) { - if (!whitelist[attr]) { - continue; - } - v.forEachKeyInKeypath(ow, attr, whitelistCreator); + // adding submit event + if (addSubmitEvent) { + this.formElement.addEventListener('submit', (event) => { + this._onSubmit(event) + }) } - return ow; - } + } - function cleanRecursive(attributes, whitelist) { - if (!v.isObject(attributes)) { - return attributes; - } + /** + * trigger submit + * + * @param {object} event + * + */ + submit(event) + { + this._onSubmit(event) + } - var ret = v.extend({}, attributes) - , w - , attribute; + /** + * + * @param {function} onError + * + */ + onError(onError) + { + this._onError = onError + } - for (attribute in attributes) { - w = whitelist[attribute]; + /** + * settin onSuccess callback and add submit-event on form + * + * @param {function} onSuccess + * + */ + onSuccess(onSuccess) + { + // adding onSuccess + this._onSuccess = onSuccess + } - if (v.isObject(w)) { - ret[attribute] = cleanRecursive(ret[attribute], w); - } else if (!w) { - delete ret[attribute]; - } + /** + * handle submit + * + * + * @param {Event} event + * + */ + _onSubmit(event) + { + // getting data from target of submit event + const data = form_serialize__WEBPACK_IMPORTED_MODULE_1___default()(event.target, { + hash: true + }) + + // options for validate.js + const options = { + fullMessages: false } - return ret; - } - if (!v.isObject(whitelist) || !v.isObject(attributes)) { - return {}; - } + // check form and getting errors + validate_js__WEBPACK_IMPORTED_MODULE_0___default().async(data, this.constraits, options).then( + () => { + this._onSuccess(event, data) + }, - whitelist = buildObjectWhitelist(whitelist); - return cleanRecursive(attributes, whitelist); - }, + (errors) => { + event.preventDefault() - exposeModule: function(validate, root, exports, module, define) { - if (exports) { - if (module && module.exports) { - exports = module.exports = validate; - } - exports.validate = validate; - } else { - root.validate = validate; - if (validate.isFunction(define) && define.amd) { - define([], function () { return validate; }); - } - } - }, + // if onError is set, tha + if (this._onError) { + this._onError(event, errors, data) + } - warn: function(msg) { - if (typeof console !== "undefined" && console.warn) { - console.warn("[validate.js] " + msg); - } - }, + // send each element a event + this.elements.forEach((element) => { + let elementErrors = false - error: function(msg) { - if (typeof console !== "undefined" && console.error) { - console.error("[validate.js] " + msg); - } - } - }); + // check for errors by name + if (errors[element.attributes.name.nodeValue]) { + elementErrors = errors[element.attributes.name.nodeValue] + } - validate.validators = { - // Presence validates that the value isn't empty - presence: function(value, options) { - options = v.extend({}, this.options, options); - if (options.allowEmpty !== false ? !v.isDefined(value) : v.isEmpty(value)) { - return options.message || this.message || "can't be blank"; - } - }, - length: function(value, options, attribute) { - // Empty values are allowed - if (!v.isDefined(value)) { - return; - } + this._dispatchCustomEvent(elementErrors, element) + }) + } + ) + } - options = v.extend({}, this.options, options); + /** + * send update to fields + * + * + * @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 + } - var is = options.is - , maximum = options.maximum - , minimum = options.minimum - , tokenizer = options.tokenizer || function(val) { return val; } - , err - , errors = []; + let errors = validate_js__WEBPACK_IMPORTED_MODULE_0___default().single(event.detail.value, this.constraits[event.detail.name]) - value = tokenizer(value); - var length = value.length; - if(!v.isNumber(length)) { - return options.message || this.notValid || "has an incorrect length"; - } + // search for element by name and dispatch event + this.elements.forEach((element) => { + if (element.attributes.name.nodeValue == event.detail.name) { + this._dispatchCustomEvent(errors, element) + } + }) + } - // Is checks - if (v.isNumber(is) && length !== is) { - err = options.wrongLength || - this.wrongLength || - "is the wrong length (should be %{count} characters)"; - errors.push(v.format(err, {count: is})); - } + /** + * dispatch event to single element + * + * @param {Array} errors + * @param {Element} element + * + */ + _dispatchCustomEvent(errors, element) + { + let detail = false - if (v.isNumber(minimum) && length < minimum) { - err = options.tooShort || - this.tooShort || - "is too short (minimum is %{count} characters)"; - errors.push(v.format(err, {count: minimum})); - } + if (errors) { + detail = errors + } - if (v.isNumber(maximum) && length > maximum) { - err = options.tooLong || - this.tooLong || - "is too long (maximum is %{count} characters)"; - errors.push(v.format(err, {count: maximum})); - } + const formValidationEvent = new CustomEvent('form-validation', { + 'detail': detail + }) - if (errors.length > 0) { - return options.message || errors; - } - }, - numericality: function(value, options, attribute, attributes, globalOptions) { - // Empty values are fine - if (!v.isDefined(value)) { - return; - } + element.dispatchEvent(formValidationEvent) + } +} - options = v.extend({}, this.options, options); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FormValidator); - var errors = [] - , name - , count - , checks = { - greaterThan: function(v, c) { return v > c; }, - greaterThanOrEqualTo: function(v, c) { return v >= c; }, - equalTo: function(v, c) { return v === c; }, - lessThan: function(v, c) { return v < c; }, - lessThanOrEqualTo: function(v, c) { return v <= c; }, - divisibleBy: function(v, c) { return v % c === 0; } - } - , prettify = options.prettify || - (globalOptions && globalOptions.prettify) || - v.prettify; +/***/ }), - // Strict will check that it is a valid looking number - if (v.isString(value) && options.strict) { - var pattern = "^-?(0|[1-9]\\d*)"; - if (!options.onlyInteger) { - pattern += "(\\.\\d+)?"; - } - pattern += "$"; +/***/ "./js/app.js": +/*!*******************!*\ + !*** ./js/app.js ***! + \*******************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - if (!(new RegExp(pattern).test(value))) { - return options.message || - options.notValid || - this.notValid || - this.message || - "must be a valid number"; - } - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var riot__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! riot */ "../../node_modules/riot/riot.esm.js"); +/* harmony import */ var _components_sidebar_riot__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./components/sidebar.riot */ "./js/components/sidebar.riot"); +/* harmony import */ var _components_tasks_riot__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/tasks.riot */ "./js/components/tasks.riot"); +/* harmony import */ var _components_task_new_riot__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/task-new.riot */ "./js/components/task-new.riot"); +/* harmony import */ var _components_task_form_riot__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/task-form.riot */ "./js/components/task-form.riot"); +/* harmony import */ var _components_notification_riot__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/notification.riot */ "./js/components/notification.riot"); - // Coerce the value to a number unless we're being strict. - if (options.noStrings !== true && v.isString(value) && !v.isEmpty(value)) { - value = +value; - } - // If it's not a number we shouldn't continue since it will compare it. - if (!v.isNumber(value)) { - return options.message || - options.notValid || - this.notValid || - this.message || - "is not a number"; - } - // Same logic as above, sort of. Don't bother with comparisons if this - // doesn't pass. - if (options.onlyInteger && !v.isInteger(value)) { - return options.message || - options.notInteger || - this.notInteger || - this.message || - "must be an integer"; - } - for (name in checks) { - count = options[name]; - if (v.isNumber(count) && !checks[name](value, count)) { - // This picks the default message if specified - // For example the greaterThan check uses the message from - // this.notGreaterThan so we capitalize the name and prepend "not" - var key = "not" + v.capitalize(name); - var msg = options[key] || - this[key] || - this.message || - "must be %{type} %{count}"; - errors.push(v.format(msg, { - count: count, - type: prettify(name) - })); - } - } + // register components - if (options.odd && value % 2 !== 1) { - errors.push(options.notOdd || - this.notOdd || - this.message || - "must be odd"); - } - if (options.even && value % 2 !== 0) { - errors.push(options.notEven || - this.notEven || - this.message || - "must be even"); - } +riot__WEBPACK_IMPORTED_MODULE_5__.register('app-sidebar', _components_sidebar_riot__WEBPACK_IMPORTED_MODULE_0__["default"]); +riot__WEBPACK_IMPORTED_MODULE_5__.register('app-tasks', _components_tasks_riot__WEBPACK_IMPORTED_MODULE_1__["default"]); +riot__WEBPACK_IMPORTED_MODULE_5__.register('app-task-button', _components_task_new_riot__WEBPACK_IMPORTED_MODULE_2__["default"]); +riot__WEBPACK_IMPORTED_MODULE_5__.register('app-task-form', _components_task_form_riot__WEBPACK_IMPORTED_MODULE_3__["default"]); +riot__WEBPACK_IMPORTED_MODULE_5__.register('app-notification', _components_notification_riot__WEBPACK_IMPORTED_MODULE_4__["default"]); // mount components - if (errors.length) { - return options.message || errors; - } - }, - datetime: v.extend(function(value, options) { - if (!v.isFunction(this.parse) || !v.isFunction(this.format)) { - throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator"); - } +riot__WEBPACK_IMPORTED_MODULE_5__.mount('app-sidebar'); +riot__WEBPACK_IMPORTED_MODULE_5__.mount('app-tasks'); +riot__WEBPACK_IMPORTED_MODULE_5__.mount('app-task-button'); +riot__WEBPACK_IMPORTED_MODULE_5__.mount('app-task-form'); +riot__WEBPACK_IMPORTED_MODULE_5__.mount('app-notification'); - // Empty values are fine - if (!v.isDefined(value)) { - return; - } +/***/ }), - options = v.extend({}, this.options, options); +/***/ "./js/mixins/sidebar.js": +/*!******************************!*\ + !*** ./js/mixins/sidebar.js ***! + \******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - var err - , errors = [] - , earliest = options.earliest ? this.parse(options.earliest, options) : NaN - , latest = options.latest ? this.parse(options.latest, options) : NaN; +"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__ = ({ + state: { + isOpen: false, + isLoading: false + }, - value = this.parse(value, options); + /** + * + * + */ + handleClose: function handleClose() { + this.state.isOpen = false; + this.update(); + } +}); - // 86400000 is the number of milliseconds in a day, this is used to remove - // the time from the date - if (isNaN(value) || options.dateOnly && value % 86400000 !== 0) { - err = options.notValid || - options.message || - this.notValid || - "must be a valid date"; - return v.format(err, {value: arguments[0]}); - } +/***/ }), - if (!isNaN(earliest) && value < earliest) { - err = options.tooEarly || - options.message || - this.tooEarly || - "must be no earlier than %{date}"; - err = v.format(err, { - value: this.format(value, options), - date: this.format(earliest, options) - }); - errors.push(err); - } +/***/ "./js/stores/notification.js": +/*!***********************************!*\ + !*** ./js/stores/notification.js ***! + \***********************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - if (!isNaN(latest) && value > latest) { - err = options.tooLate || - options.message || - this.tooLate || - "must be no later than %{date}"; - err = v.format(err, { - date: this.format(latest, options), - value: this.format(value, options) - }); - errors.push(err); - } +"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 _riotjs_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @riotjs/observable */ "../../node_modules/@riotjs/observable/dist/observable.js"); +/* harmony import */ var _riotjs_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_riotjs_observable__WEBPACK_IMPORTED_MODULE_0__); - if (errors.length) { - return v.unique(errors); - } - }, { - parse: null, - format: null - }), - date: function(value, options) { - options = v.extend({}, options, {dateOnly: true}); - return v.validators.datetime.call(v.validators.datetime, value, options); - }, - format: function(value, options) { - if (v.isString(options) || (options instanceof RegExp)) { - options = {pattern: options}; - } +/** + * NotificationService + * + * + */ - options = v.extend({}, this.options, options); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_riotjs_observable__WEBPACK_IMPORTED_MODULE_0___default()({ + SUCCESS: 'success', + DANGER: 'danger', + INFO: 'info', - var message = options.message || this.message || "is invalid" - , pattern = options.pattern - , match; + /** + * + * + */ + success: function success(message) { + this._add(message, this.SUCCESS); + }, - // Empty values are allowed - if (!v.isDefined(value)) { - return; - } - if (!v.isString(value)) { - return message; - } + /** + * + * + */ + danger: function danger(message) { + this._add(message, this.DANGER); + }, - if (v.isString(pattern)) { - pattern = new RegExp(options.pattern, options.flags); - } - match = pattern.exec(value); - if (!match || match[0].length != value.length) { - return message; - } - }, - inclusion: function(value, options) { - // Empty values are fine - if (!v.isDefined(value)) { - return; - } - if (v.isArray(options)) { - options = {within: options}; - } - options = v.extend({}, this.options, options); - if (v.contains(options.within, value)) { - return; - } - var message = options.message || - this.message || - "^%{value} is not included in the list"; - return v.format(message, {value: value}); - }, - exclusion: function(value, options) { - // Empty values are fine - if (!v.isDefined(value)) { - return; - } - if (v.isArray(options)) { - options = {within: options}; - } - options = v.extend({}, this.options, options); - if (!v.contains(options.within, value)) { - return; - } - var message = options.message || this.message || "^%{value} is restricted"; - if (v.isString(options.within[value])) { - value = options.within[value]; - } - return v.format(message, {value: value}); - }, - email: v.extend(function(value, options) { - options = v.extend({}, this.options, options); - var message = options.message || this.message || "is not a valid email"; - // Empty values are fine - if (!v.isDefined(value)) { - return; - } - if (!v.isString(value)) { - return message; - } - if (!this.PATTERN.exec(value)) { - return message; - } - }, { - PATTERN: /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i - }), - equality: function(value, options, attribute, attributes, globalOptions) { - if (!v.isDefined(value)) { - return; - } + /** + * + * + */ + info: function info(message) { + this._add(message, this.INFO); + }, - if (v.isString(options)) { - options = {attribute: options}; - } - options = v.extend({}, this.options, options); - var message = options.message || - this.message || - "is not equal to %{attribute}"; + /** + * + * @param {[type]} message [description] + * @param {[type]} type [description] + */ + _add: function _add(message, type) { + this.trigger('update', { + message: message, + type: type + }); + } +})); - if (v.isEmpty(options.attribute) || !v.isString(options.attribute)) { - throw new Error("The attribute must be a non empty string"); - } +/***/ }), - var otherValue = v.getDeepObjectValue(attributes, options.attribute) - , comparator = options.comparator || function(v1, v2) { - return v1 === v2; - } - , prettify = options.prettify || - (globalOptions && globalOptions.prettify) || - v.prettify; +/***/ "./js/stores/taskForm.js": +/*!*******************************!*\ + !*** ./js/stores/taskForm.js ***! + \*******************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - if (!comparator(value, otherValue, options, attribute, attributes)) { - return v.format(message, {attribute: prettify(options.attribute)}); - } - }, - // A URL validator that is used to validate URLs with the ability to - // restrict schemes and some domains. - url: function(value, options) { - if (!v.isDefined(value)) { - return; - } +"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 _riotjs_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @riotjs/observable */ "../../node_modules/@riotjs/observable/dist/observable.js"); +/* harmony import */ var _riotjs_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_riotjs_observable__WEBPACK_IMPORTED_MODULE_0__); +/** + * + * + * + * @author Björn Hase + * + * + */ - options = v.extend({}, this.options, options); +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_riotjs_observable__WEBPACK_IMPORTED_MODULE_0___default()({ + /** + * + * @param {object} data + * + */ + open: function open(data) { + this.trigger('open', data); + } +})); - var message = options.message || this.message || "is not a valid url" - , schemes = options.schemes || this.schemes || ['http', 'https'] - , allowLocal = options.allowLocal || this.allowLocal || false - , allowDataUrl = options.allowDataUrl || this.allowDataUrl || false; - if (!v.isString(value)) { - return message; - } +/***/ }), - // https://gist.github.com/dperini/729294 - var regex = - "^" + - // protocol identifier - "(?:(?:" + schemes.join("|") + ")://)" + - // user:pass authentication - "(?:\\S+(?::\\S*)?@)?" + - "(?:"; +/***/ "../../node_modules/form-serialize/index.js": +/*!**************************************************!*\ + !*** ../../node_modules/form-serialize/index.js ***! + \**************************************************/ +/***/ ((module) => { - var tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))"; +// get successful control from form and assemble into object +// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2 - if (allowLocal) { - tld += "?"; - } else { - regex += - // IP address exclusion - // private & local networks - "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + - "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + - "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){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; - regex += - // IP address dotted notation octets - // excludes loopback network 0.0.0.0 - // excludes reserved space >= 224.0.0.0 - // excludes network & broacast addresses - // (first & last IP address of each class) - "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + - "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + - "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + - "|" + - // host name - "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + - // domain name - "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + - tld + - ")" + - // port number - "(?::\\d{2,5})?" + - // resource path - "(?:[/?#]\\S*)?" + - "$"; +// node names which could be successful controls +var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i; - if (allowDataUrl) { - // RFC 2397 - var mediaType = "\\w+\\/[-+.\\w]+(?:;[\\w=]+)*"; - var urlchar = "[A-Za-z0-9-_.!~\\*'();\\/?:@&=+$,%]*"; - var dataurl = "data:(?:"+mediaType+")?(?:;base64)?,"+urlchar; - regex = "(?:"+regex+")|(?:^"+dataurl+"$)"; - } +// Matches bracket notation. +var brackets = /(\[[^\[\]]*\])/g; - var PATTERN = new RegExp(regex, 'i'); - if (!PATTERN.exec(value)) { - return message; - } - }, - type: v.extend(function(value, originalOptions, attribute, attributes, globalOptions) { - if (v.isString(originalOptions)) { - originalOptions = {type: originalOptions}; - } +// 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; + } - if (!v.isDefined(value)) { - return; - } + var result = (options.hash) ? {} : ''; + var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize); - var options = v.extend({}, this.options, originalOptions); + var elements = form && form.elements ? form.elements : []; - var type = options.type; - if (!v.isDefined(type)) { - throw new Error("No type was specified"); - } + //Object store each radio and set if it's empty or not + var radio_store = Object.create(null); - var check; - if (v.isFunction(type)) { - check = type; - } else { - check = this.types[type]; - } + for (var i=0 ; i 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