diff --git a/dist/formulate.esm.js b/dist/formulate.esm.js index 51d2372..76bf251 100644 --- a/dist/formulate.esm.js +++ b/dist/formulate.esm.js @@ -1,6 +1,6 @@ import isUrl from 'is-url'; -import isPlainObject from 'is-plain-object'; import nanoid from 'nanoid'; +import isPlainObject from 'is-plain-object'; var library = { // === SINGLE LINE TEXT STYLE INPUTS @@ -97,9 +97,129 @@ var library = { select: { classification: 'select', component: 'FormulateInputSelect' + }, + + // === FILE TYPE + + file: { + classification: 'file', + component: 'FormulateInputFile' + }, + image: { + classification: 'file', + component: 'FormulateInputFile' } }; +/** + * The file upload class holds and represents a file’s upload state durring + * the upload flow. + */ +var FileUpload = function FileUpload (fileList, context, options) { + this.fileList = fileList; + this.files = []; + this.options = options; + this.setFileList(fileList); + this.context = context; +}; + +/** + * Produce an array of files and alert the callback. + * @param {FileList} + */ +FileUpload.prototype.setFileList = function setFileList (fileList) { + for (var i = 0; i < fileList.length; i++) { + var file = fileList.item(i); + this.files.push({ + progress: 0, + name: file.name || 'file-upload', + file: file, + uuid: nanoid() + }); + } +}; + +/** + * Check if the file has an. + */ +FileUpload.prototype.hasUploader = function hasUploader () { + return !!this.context.uploader +}; + +FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () { + if ( + this.hasUploader && + typeof this.hasUploader.request === 'function' && + typeof this.hasUploader.get === 'function' && + typeof this.hasUploader.delete === 'function' && + typeof this.hasUploader.post === 'function' + ) { + return true + } + return false +}; + +/** + * Get a new uploader function. + */ +FileUpload.prototype.getUploader = function getUploader () { + var ref; + + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + if (this.uploaderIsAxios()) { + var formData = new FormData(); + formData.append(this.context.name || 'file', args[0]); + return this.uploader.post(this.context.uploadUrl, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: function (progressEvent) { + args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total)); + } + }) + .catch(function (err) { return args[2](err); }) + } + return (ref = this.context).uploader.apply(ref, args) +}; + +/** + * Perform the file upload. + */ +FileUpload.prototype.upload = function upload () { + var this$1 = this; + + return new Promise(function (resolve, reject) { + if (!this$1.hasUploader) { + return reject(new Error('No uploader has been defined')) + } + Promise.all(this$1.files.map(function (file) { + return this$1.getUploader( + file.file, + function (progress) { file.progress = progress; }, + function (error) { return reject(new Error(error)); }, + this$1.options + ) + })) + .then(function (results) { return resolve(results); }) + .catch(function (err) { throw new Error(err) }); + }) +}; + +/** + * Get the files. + */ +FileUpload.prototype.getFileList = function getFileList () { + return this.fileList +}; + +/** + * Get the files. + */ +FileUpload.prototype.getFiles = function getFiles () { + return this.files +}; + /** * Function to map over an object. * @param {Object} obj An object to map over @@ -389,19 +509,20 @@ var rules = { /** * Check the maximum value of a particular. */ - max: function (value, minimum) { + max: function (value, minimum, force) { if ( minimum === void 0 ) minimum = 10; return Promise.resolve((function () { - minimum = Number(minimum); - if (!isNaN(value)) { - value = Number(value); - return value <= minimum - } - if (typeof value === 'string') { + if (Array.isArray(value)) { + minimum = !isNaN(minimum) ? Number(minimum) : minimum; return value.length <= minimum } - if (Array.isArray(value)) { + if ((!isNaN(value) && force !== 'length') || force === 'value') { + value = !isNaN(value) ? Number(value) : value; + return value <= minimum + } + if (typeof value === 'string' || (force === 'length')) { + value = !isNaN(value) ? value.toString() : value; return value.length <= minimum } return false @@ -416,6 +537,12 @@ var rules = { while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ]; return Promise.resolve((function () { + if (files instanceof FileUpload) { + if (files.hasUploader()) { + return false + } + files = files.getFiles(); + } if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') { for (var i in files) { if (!types.includes(files[i].type)) { @@ -639,10 +766,14 @@ var en = { var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be less than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You may only select " + (args[0]) + " " + name + ".") } - return (name + " must be less than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be less than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.") }, /** @@ -653,10 +784,14 @@ var en = { var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be more than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You must select at least " + (args[0]) + " " + name + ".") } - return (name + " must be more than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be more than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.") }, /** @@ -697,6 +832,35 @@ var en = { } }; +/** + * A fake uploader used by default. + * + * @param {File} file + * @param {function} progress + * @param {function} error + * @param {object} options + */ +function fauxUploader (file, progress, error, options) { + return new Promise(function (resolve, reject) { + var totalTime = options.fauxUploaderDuration || 2000; + var start = performance.now(); + var advance = function () { return setTimeout(function () { + var elapsed = performance.now() - start; + var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100)); + progress(currentProgress); + if (currentProgress >= 100) { + resolve({ + url: 'http://via.placeholder.com/350x150.png', + name: file.name + }); + } else { + advance(); + } + }, 20); }; + advance(); + }) +} + /** * For a single instance of an input, export all of the context needed to fully * render that element. @@ -713,7 +877,11 @@ var context = { label: this.label, labelPosition: this.logicalLabelPosition, attributes: this.elementAttributes, - blurHandler: blurHandler.bind(this)}, + blurHandler: blurHandler.bind(this), + showImage: this.showImage, + uploadUrl: this.uploadUrl, + uploader: this.uploader || this.$formulate.getUploader(), + immediateUpload: this.immediateUpload}, this.typeContext)) }, nameOrFallback: nameOrFallback, @@ -1001,6 +1169,22 @@ var script = { showErrors: { type: Boolean, default: false + }, + showImage: { + type: Boolean, + default: true + }, + uploadUrl: { + type: [String, Boolean], + default: false + }, + uploader: { + type: [Function, Object, Boolean], + default: false + }, + immediateUpload: { + type: Boolean, + default: true } }, data: function data () { @@ -1989,6 +2173,219 @@ __vue_render__$5._withStripped = true; // var script$6 = { + name: 'FormulateFiles', + props: { + files: { + type: FileUpload, + required: true + } + }, + computed: { + fileUploads: function fileUploads () { + return this.files.files || [] + } + } +}; + +/* script */ +var __vue_script__$6 = script$6; + +/* template */ +var __vue_render__$6 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _vm.fileUploads.length + ? _c( + "ul", + { staticClass: "formulate-files" }, + _vm._l(_vm.fileUploads, function(file) { + return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [ + _c("span", { + staticClass: "formualte-file-name", + domProps: { textContent: _vm._s(file.name) } + }), + _vm._v(" "), + file.progress > 0 && file.progress < 100 + ? _c("span", { + domProps: { textContent: _vm._s(file.progress + "%") } + }) + : _vm._e() + ]) + }), + 0 + ) + : _vm._e() +}; +var __vue_staticRenderFns__$6 = []; +__vue_render__$6._withStripped = true; + + /* style */ + var __vue_inject_styles__$6 = undefined; + /* scoped */ + var __vue_scope_id__$6 = undefined; + /* module identifier */ + var __vue_module_identifier__$6 = undefined; + /* functional template */ + var __vue_is_functional_template__$6 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateFiles = normalizeComponent( + { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, + __vue_inject_styles__$6, + __vue_script__$6, + __vue_scope_id__$6, + __vue_is_functional_template__$6, + __vue_module_identifier__$6, + false, + undefined, + undefined, + undefined + ); + +// + +var script$7 = { + name: 'FormulateInputFile', + components: { + FormulateFiles: FormulateFiles + }, + mixins: [FormulateInputMixin], + data: function data () { + return { + isOver: false + } + }, + computed: { + hasFiles: function hasFiles () { + return (this.context.model instanceof FileUpload && this.context.model.files.length) + } + }, + methods: { + handleFile: function handleFile () { + var input = this.$refs.file; + if (input.files.length) { + this.context.model = this.$formulate.createUpload(input.files, this.context); + } + if (this.context.immediateUpload && this.context.model instanceof FileUpload) { + this.context.model.upload(); + } + }, + handleDragOver: function handleDragOver (e) { + e.preventDefault(); + this.isOver = true; + }, + handleDragLeave: function handleDragLeave (e) { + e.preventDefault(); + this.isOver = false; + } + } +}; + +/* script */ +var __vue_script__$7 = script$7; + +/* template */ +var __vue_render__$7 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c( + "div", + { + class: + "formulate-input-element formulate-input-element--" + _vm.context.type, + attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles } + }, + [ + _c( + "div", + { + staticClass: "formulate-input-upload-area", + attrs: { "data-has-files": _vm.hasFiles } + }, + [ + _c( + "input", + _vm._b( + { + ref: "file", + attrs: { "data-is-drag-hover": _vm.isOver, type: "file" }, + on: { + blur: _vm.context.blurHandler, + change: _vm.handleFile, + dragover: _vm.handleDragOver, + dragleave: _vm.handleDragLeave + } + }, + "input", + _vm.attributes, + false + ) + ), + _vm._v(" "), + _c("div", { + directives: [ + { + name: "show", + rawName: "v-show", + value: !_vm.hasFiles, + expression: "!hasFiles" + } + ], + staticClass: "formulate-input-upload-area-mask" + }), + _vm._v(" "), + _vm.hasFiles + ? _c("FormulateFiles", { attrs: { files: _vm.context.model } }) + : _vm._e() + ], + 1 + ) + ] + ) +}; +var __vue_staticRenderFns__$7 = []; +__vue_render__$7._withStripped = true; + + /* style */ + var __vue_inject_styles__$7 = undefined; + /* scoped */ + var __vue_scope_id__$7 = undefined; + /* module identifier */ + var __vue_module_identifier__$7 = undefined; + /* functional template */ + var __vue_is_functional_template__$7 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateInputFile = normalizeComponent( + { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, + __vue_inject_styles__$7, + __vue_script__$7, + __vue_scope_id__$7, + __vue_is_functional_template__$7, + __vue_module_identifier__$7, + false, + undefined, + undefined, + undefined + ); + +// + +var script$8 = { name: 'FormulateInputSelect', mixins: [FormulateInputMixin], computed: { @@ -2005,10 +2402,10 @@ var script$6 = { }; /* script */ -var __vue_script__$6 = script$6; +var __vue_script__$8 = script$8; /* template */ -var __vue_render__$6 = function() { +var __vue_render__$8 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2120,17 +2517,17 @@ var __vue_render__$6 = function() { ] ) }; -var __vue_staticRenderFns__$6 = []; -__vue_render__$6._withStripped = true; +var __vue_staticRenderFns__$8 = []; +__vue_render__$8._withStripped = true; /* style */ - var __vue_inject_styles__$6 = undefined; + var __vue_inject_styles__$8 = undefined; /* scoped */ - var __vue_scope_id__$6 = undefined; + var __vue_scope_id__$8 = undefined; /* module identifier */ - var __vue_module_identifier__$6 = undefined; + var __vue_module_identifier__$8 = undefined; /* functional template */ - var __vue_is_functional_template__$6 = false; + var __vue_is_functional_template__$8 = false; /* style inject */ /* style inject SSR */ @@ -2140,12 +2537,12 @@ __vue_render__$6._withStripped = true; var FormulateInputSelect = normalizeComponent( - { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, - __vue_inject_styles__$6, - __vue_script__$6, - __vue_scope_id__$6, - __vue_is_functional_template__$6, - __vue_module_identifier__$6, + { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, + __vue_inject_styles__$8, + __vue_script__$8, + __vue_scope_id__$8, + __vue_is_functional_template__$8, + __vue_module_identifier__$8, false, undefined, undefined, @@ -2154,16 +2551,16 @@ __vue_render__$6._withStripped = true; // -var script$7 = { - name: 'FormulateInputText', +var script$9 = { + name: 'FormulateInputSlider', mixins: [FormulateInputMixin] }; /* script */ -var __vue_script__$7 = script$7; +var __vue_script__$9 = script$9; /* template */ -var __vue_render__$7 = function() { +var __vue_render__$9 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2284,17 +2681,17 @@ var __vue_render__$7 = function() { ] ) }; -var __vue_staticRenderFns__$7 = []; -__vue_render__$7._withStripped = true; +var __vue_staticRenderFns__$9 = []; +__vue_render__$9._withStripped = true; /* style */ - var __vue_inject_styles__$7 = undefined; + var __vue_inject_styles__$9 = undefined; /* scoped */ - var __vue_scope_id__$7 = undefined; + var __vue_scope_id__$9 = undefined; /* module identifier */ - var __vue_module_identifier__$7 = undefined; + var __vue_module_identifier__$9 = undefined; /* functional template */ - var __vue_is_functional_template__$7 = false; + var __vue_is_functional_template__$9 = false; /* style inject */ /* style inject SSR */ @@ -2304,12 +2701,12 @@ __vue_render__$7._withStripped = true; var FormulateInputSlider = normalizeComponent( - { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, - __vue_inject_styles__$7, - __vue_script__$7, - __vue_scope_id__$7, - __vue_is_functional_template__$7, - __vue_module_identifier__$7, + { render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 }, + __vue_inject_styles__$9, + __vue_script__$9, + __vue_scope_id__$9, + __vue_is_functional_template__$9, + __vue_module_identifier__$9, false, undefined, undefined, @@ -2318,16 +2715,16 @@ __vue_render__$7._withStripped = true; // -var script$8 = { +var script$a = { name: 'FormulateInputTextArea', mixins: [FormulateInputMixin] }; /* script */ -var __vue_script__$8 = script$8; +var __vue_script__$a = script$a; /* template */ -var __vue_render__$8 = function() { +var __vue_render__$a = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2369,17 +2766,17 @@ var __vue_render__$8 = function() { ] ) }; -var __vue_staticRenderFns__$8 = []; -__vue_render__$8._withStripped = true; +var __vue_staticRenderFns__$a = []; +__vue_render__$a._withStripped = true; /* style */ - var __vue_inject_styles__$8 = undefined; + var __vue_inject_styles__$a = undefined; /* scoped */ - var __vue_scope_id__$8 = undefined; + var __vue_scope_id__$a = undefined; /* module identifier */ - var __vue_module_identifier__$8 = undefined; + var __vue_module_identifier__$a = undefined; /* functional template */ - var __vue_is_functional_template__$8 = false; + var __vue_is_functional_template__$a = false; /* style inject */ /* style inject SSR */ @@ -2389,12 +2786,12 @@ __vue_render__$8._withStripped = true; var FormulateInputTextArea = normalizeComponent( - { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, - __vue_inject_styles__$8, - __vue_script__$8, - __vue_scope_id__$8, - __vue_is_functional_template__$8, - __vue_module_identifier__$8, + { render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a }, + __vue_inject_styles__$a, + __vue_script__$a, + __vue_scope_id__$a, + __vue_is_functional_template__$a, + __vue_module_identifier__$a, false, undefined, undefined, @@ -2412,6 +2809,7 @@ var Formulate = function Formulate () { FormulateInputErrors: FormulateInputErrors, FormulateInputBox: FormulateInputBox, FormulateInputText: FormulateInputText, + FormulateInputFile: FormulateInputFile, FormulateInputGroup: FormulateInputGroup, FormulateInputSelect: FormulateInputSelect, FormulateInputSlider: FormulateInputSlider, @@ -2420,6 +2818,7 @@ var Formulate = function Formulate () { library: library, rules: rules, locale: 'en', + uploader: fauxUploader, locales: { en: en } @@ -2508,6 +2907,20 @@ Formulate.prototype.validationMessage = function validationMessage (rule, valida return 'This field does not have a valid value' }; +/** + * Get the file uploader. + */ +Formulate.prototype.getUploader = function getUploader () { + return this.options.uploader || false +}; + +/** + * Create a new instance of an upload. + */ +Formulate.prototype.createUpload = function createUpload (fileList, context) { + return new FileUpload(fileList, context, this.options) +}; + var Formulate$1 = new Formulate(); export default Formulate$1; diff --git a/dist/formulate.min.js b/dist/formulate.min.js index d32166f..dbc0489 100644 --- a/dist/formulate.min.js +++ b/dist/formulate.min.js @@ -1,9 +1,9 @@ -var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { +var Formulate = (function (exports, isUrl, nanoid, isPlainObject) { 'use strict'; isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl; - isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject; nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid; + isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject; var library = { // === SINGLE LINE TEXT STYLE INPUTS @@ -100,9 +100,129 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { select: { classification: 'select', component: 'FormulateInputSelect' + }, + + // === FILE TYPE + + file: { + classification: 'file', + component: 'FormulateInputFile' + }, + image: { + classification: 'file', + component: 'FormulateInputFile' } }; + /** + * The file upload class holds and represents a file’s upload state durring + * the upload flow. + */ + var FileUpload = function FileUpload (fileList, context, options) { + this.fileList = fileList; + this.files = []; + this.options = options; + this.setFileList(fileList); + this.context = context; + }; + + /** + * Produce an array of files and alert the callback. + * @param {FileList} + */ + FileUpload.prototype.setFileList = function setFileList (fileList) { + for (var i = 0; i < fileList.length; i++) { + var file = fileList.item(i); + this.files.push({ + progress: 0, + name: file.name || 'file-upload', + file: file, + uuid: nanoid() + }); + } + }; + + /** + * Check if the file has an. + */ + FileUpload.prototype.hasUploader = function hasUploader () { + return !!this.context.uploader + }; + + FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () { + if ( + this.hasUploader && + typeof this.hasUploader.request === 'function' && + typeof this.hasUploader.get === 'function' && + typeof this.hasUploader.delete === 'function' && + typeof this.hasUploader.post === 'function' + ) { + return true + } + return false + }; + + /** + * Get a new uploader function. + */ + FileUpload.prototype.getUploader = function getUploader () { + var ref; + + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + if (this.uploaderIsAxios()) { + var formData = new FormData(); + formData.append(this.context.name || 'file', args[0]); + return this.uploader.post(this.context.uploadUrl, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: function (progressEvent) { + args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total)); + } + }) + .catch(function (err) { return args[2](err); }) + } + return (ref = this.context).uploader.apply(ref, args) + }; + + /** + * Perform the file upload. + */ + FileUpload.prototype.upload = function upload () { + var this$1 = this; + + return new Promise(function (resolve, reject) { + if (!this$1.hasUploader) { + return reject(new Error('No uploader has been defined')) + } + Promise.all(this$1.files.map(function (file) { + return this$1.getUploader( + file.file, + function (progress) { file.progress = progress; }, + function (error) { return reject(new Error(error)); }, + this$1.options + ) + })) + .then(function (results) { return resolve(results); }) + .catch(function (err) { throw new Error(err) }); + }) + }; + + /** + * Get the files. + */ + FileUpload.prototype.getFileList = function getFileList () { + return this.fileList + }; + + /** + * Get the files. + */ + FileUpload.prototype.getFiles = function getFiles () { + return this.files + }; + /** * Function to map over an object. * @param {Object} obj An object to map over @@ -392,19 +512,20 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { /** * Check the maximum value of a particular. */ - max: function (value, minimum) { + max: function (value, minimum, force) { if ( minimum === void 0 ) minimum = 10; return Promise.resolve((function () { - minimum = Number(minimum); - if (!isNaN(value)) { - value = Number(value); - return value <= minimum - } - if (typeof value === 'string') { + if (Array.isArray(value)) { + minimum = !isNaN(minimum) ? Number(minimum) : minimum; return value.length <= minimum } - if (Array.isArray(value)) { + if ((!isNaN(value) && force !== 'length') || force === 'value') { + value = !isNaN(value) ? Number(value) : value; + return value <= minimum + } + if (typeof value === 'string' || (force === 'length')) { + value = !isNaN(value) ? value.toString() : value; return value.length <= minimum } return false @@ -419,6 +540,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ]; return Promise.resolve((function () { + if (files instanceof FileUpload) { + if (files.hasUploader()) { + return false + } + files = files.getFiles(); + } if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') { for (var i in files) { if (!types.includes(files[i].type)) { @@ -642,10 +769,14 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be less than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You may only select " + (args[0]) + " " + name + ".") } - return (name + " must be less than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be less than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.") }, /** @@ -656,10 +787,14 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be more than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You must select at least " + (args[0]) + " " + name + ".") } - return (name + " must be more than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be more than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.") }, /** @@ -700,6 +835,35 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { } }; + /** + * A fake uploader used by default. + * + * @param {File} file + * @param {function} progress + * @param {function} error + * @param {object} options + */ + function fauxUploader (file, progress, error, options) { + return new Promise(function (resolve, reject) { + var totalTime = options.fauxUploaderDuration || 2000; + var start = performance.now(); + var advance = function () { return setTimeout(function () { + var elapsed = performance.now() - start; + var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100)); + progress(currentProgress); + if (currentProgress >= 100) { + resolve({ + url: 'http://via.placeholder.com/350x150.png', + name: file.name + }); + } else { + advance(); + } + }, 20); }; + advance(); + }) + } + /** * For a single instance of an input, export all of the context needed to fully * render that element. @@ -716,7 +880,11 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { label: this.label, labelPosition: this.logicalLabelPosition, attributes: this.elementAttributes, - blurHandler: blurHandler.bind(this)}, + blurHandler: blurHandler.bind(this), + showImage: this.showImage, + uploadUrl: this.uploadUrl, + uploader: this.uploader || this.$formulate.getUploader(), + immediateUpload: this.immediateUpload}, this.typeContext)) }, nameOrFallback: nameOrFallback, @@ -1004,6 +1172,22 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { showErrors: { type: Boolean, default: false + }, + showImage: { + type: Boolean, + default: true + }, + uploadUrl: { + type: [String, Boolean], + default: false + }, + uploader: { + type: [Function, Object, Boolean], + default: false + }, + immediateUpload: { + type: Boolean, + default: true } }, data: function data () { @@ -1992,6 +2176,219 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { // var script$6 = { + name: 'FormulateFiles', + props: { + files: { + type: FileUpload, + required: true + } + }, + computed: { + fileUploads: function fileUploads () { + return this.files.files || [] + } + } + }; + + /* script */ + var __vue_script__$6 = script$6; + + /* template */ + var __vue_render__$6 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _vm.fileUploads.length + ? _c( + "ul", + { staticClass: "formulate-files" }, + _vm._l(_vm.fileUploads, function(file) { + return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [ + _c("span", { + staticClass: "formualte-file-name", + domProps: { textContent: _vm._s(file.name) } + }), + _vm._v(" "), + file.progress > 0 && file.progress < 100 + ? _c("span", { + domProps: { textContent: _vm._s(file.progress + "%") } + }) + : _vm._e() + ]) + }), + 0 + ) + : _vm._e() + }; + var __vue_staticRenderFns__$6 = []; + __vue_render__$6._withStripped = true; + + /* style */ + var __vue_inject_styles__$6 = undefined; + /* scoped */ + var __vue_scope_id__$6 = undefined; + /* module identifier */ + var __vue_module_identifier__$6 = undefined; + /* functional template */ + var __vue_is_functional_template__$6 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateFiles = normalizeComponent( + { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, + __vue_inject_styles__$6, + __vue_script__$6, + __vue_scope_id__$6, + __vue_is_functional_template__$6, + __vue_module_identifier__$6, + false, + undefined, + undefined, + undefined + ); + + // + + var script$7 = { + name: 'FormulateInputFile', + components: { + FormulateFiles: FormulateFiles + }, + mixins: [FormulateInputMixin], + data: function data () { + return { + isOver: false + } + }, + computed: { + hasFiles: function hasFiles () { + return (this.context.model instanceof FileUpload && this.context.model.files.length) + } + }, + methods: { + handleFile: function handleFile () { + var input = this.$refs.file; + if (input.files.length) { + this.context.model = this.$formulate.createUpload(input.files, this.context); + } + if (this.context.immediateUpload && this.context.model instanceof FileUpload) { + this.context.model.upload(); + } + }, + handleDragOver: function handleDragOver (e) { + e.preventDefault(); + this.isOver = true; + }, + handleDragLeave: function handleDragLeave (e) { + e.preventDefault(); + this.isOver = false; + } + } + }; + + /* script */ + var __vue_script__$7 = script$7; + + /* template */ + var __vue_render__$7 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c( + "div", + { + class: + "formulate-input-element formulate-input-element--" + _vm.context.type, + attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles } + }, + [ + _c( + "div", + { + staticClass: "formulate-input-upload-area", + attrs: { "data-has-files": _vm.hasFiles } + }, + [ + _c( + "input", + _vm._b( + { + ref: "file", + attrs: { "data-is-drag-hover": _vm.isOver, type: "file" }, + on: { + blur: _vm.context.blurHandler, + change: _vm.handleFile, + dragover: _vm.handleDragOver, + dragleave: _vm.handleDragLeave + } + }, + "input", + _vm.attributes, + false + ) + ), + _vm._v(" "), + _c("div", { + directives: [ + { + name: "show", + rawName: "v-show", + value: !_vm.hasFiles, + expression: "!hasFiles" + } + ], + staticClass: "formulate-input-upload-area-mask" + }), + _vm._v(" "), + _vm.hasFiles + ? _c("FormulateFiles", { attrs: { files: _vm.context.model } }) + : _vm._e() + ], + 1 + ) + ] + ) + }; + var __vue_staticRenderFns__$7 = []; + __vue_render__$7._withStripped = true; + + /* style */ + var __vue_inject_styles__$7 = undefined; + /* scoped */ + var __vue_scope_id__$7 = undefined; + /* module identifier */ + var __vue_module_identifier__$7 = undefined; + /* functional template */ + var __vue_is_functional_template__$7 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateInputFile = normalizeComponent( + { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, + __vue_inject_styles__$7, + __vue_script__$7, + __vue_scope_id__$7, + __vue_is_functional_template__$7, + __vue_module_identifier__$7, + false, + undefined, + undefined, + undefined + ); + + // + + var script$8 = { name: 'FormulateInputSelect', mixins: [FormulateInputMixin], computed: { @@ -2008,10 +2405,10 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { }; /* script */ - var __vue_script__$6 = script$6; + var __vue_script__$8 = script$8; /* template */ - var __vue_render__$6 = function() { + var __vue_render__$8 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2123,17 +2520,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { ] ) }; - var __vue_staticRenderFns__$6 = []; - __vue_render__$6._withStripped = true; + var __vue_staticRenderFns__$8 = []; + __vue_render__$8._withStripped = true; /* style */ - var __vue_inject_styles__$6 = undefined; + var __vue_inject_styles__$8 = undefined; /* scoped */ - var __vue_scope_id__$6 = undefined; + var __vue_scope_id__$8 = undefined; /* module identifier */ - var __vue_module_identifier__$6 = undefined; + var __vue_module_identifier__$8 = undefined; /* functional template */ - var __vue_is_functional_template__$6 = false; + var __vue_is_functional_template__$8 = false; /* style inject */ /* style inject SSR */ @@ -2143,12 +2540,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { var FormulateInputSelect = normalizeComponent( - { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, - __vue_inject_styles__$6, - __vue_script__$6, - __vue_scope_id__$6, - __vue_is_functional_template__$6, - __vue_module_identifier__$6, + { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, + __vue_inject_styles__$8, + __vue_script__$8, + __vue_scope_id__$8, + __vue_is_functional_template__$8, + __vue_module_identifier__$8, false, undefined, undefined, @@ -2157,16 +2554,16 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { // - var script$7 = { - name: 'FormulateInputText', + var script$9 = { + name: 'FormulateInputSlider', mixins: [FormulateInputMixin] }; /* script */ - var __vue_script__$7 = script$7; + var __vue_script__$9 = script$9; /* template */ - var __vue_render__$7 = function() { + var __vue_render__$9 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2287,17 +2684,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { ] ) }; - var __vue_staticRenderFns__$7 = []; - __vue_render__$7._withStripped = true; + var __vue_staticRenderFns__$9 = []; + __vue_render__$9._withStripped = true; /* style */ - var __vue_inject_styles__$7 = undefined; + var __vue_inject_styles__$9 = undefined; /* scoped */ - var __vue_scope_id__$7 = undefined; + var __vue_scope_id__$9 = undefined; /* module identifier */ - var __vue_module_identifier__$7 = undefined; + var __vue_module_identifier__$9 = undefined; /* functional template */ - var __vue_is_functional_template__$7 = false; + var __vue_is_functional_template__$9 = false; /* style inject */ /* style inject SSR */ @@ -2307,12 +2704,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { var FormulateInputSlider = normalizeComponent( - { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, - __vue_inject_styles__$7, - __vue_script__$7, - __vue_scope_id__$7, - __vue_is_functional_template__$7, - __vue_module_identifier__$7, + { render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 }, + __vue_inject_styles__$9, + __vue_script__$9, + __vue_scope_id__$9, + __vue_is_functional_template__$9, + __vue_module_identifier__$9, false, undefined, undefined, @@ -2321,16 +2718,16 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { // - var script$8 = { + var script$a = { name: 'FormulateInputTextArea', mixins: [FormulateInputMixin] }; /* script */ - var __vue_script__$8 = script$8; + var __vue_script__$a = script$a; /* template */ - var __vue_render__$8 = function() { + var __vue_render__$a = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2372,17 +2769,17 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { ] ) }; - var __vue_staticRenderFns__$8 = []; - __vue_render__$8._withStripped = true; + var __vue_staticRenderFns__$a = []; + __vue_render__$a._withStripped = true; /* style */ - var __vue_inject_styles__$8 = undefined; + var __vue_inject_styles__$a = undefined; /* scoped */ - var __vue_scope_id__$8 = undefined; + var __vue_scope_id__$a = undefined; /* module identifier */ - var __vue_module_identifier__$8 = undefined; + var __vue_module_identifier__$a = undefined; /* functional template */ - var __vue_is_functional_template__$8 = false; + var __vue_is_functional_template__$a = false; /* style inject */ /* style inject SSR */ @@ -2392,12 +2789,12 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { var FormulateInputTextArea = normalizeComponent( - { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, - __vue_inject_styles__$8, - __vue_script__$8, - __vue_scope_id__$8, - __vue_is_functional_template__$8, - __vue_module_identifier__$8, + { render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a }, + __vue_inject_styles__$a, + __vue_script__$a, + __vue_scope_id__$a, + __vue_is_functional_template__$a, + __vue_module_identifier__$a, false, undefined, undefined, @@ -2415,6 +2812,7 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { FormulateInputErrors: FormulateInputErrors, FormulateInputBox: FormulateInputBox, FormulateInputText: FormulateInputText, + FormulateInputFile: FormulateInputFile, FormulateInputGroup: FormulateInputGroup, FormulateInputSelect: FormulateInputSelect, FormulateInputSlider: FormulateInputSlider, @@ -2423,6 +2821,7 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { library: library, rules: rules, locale: 'en', + uploader: fauxUploader, locales: { en: en } @@ -2511,10 +2910,24 @@ var Formulate = (function (exports, isUrl, isPlainObject, nanoid) { return 'This field does not have a valid value' }; + /** + * Get the file uploader. + */ + Formulate.prototype.getUploader = function getUploader () { + return this.options.uploader || false + }; + + /** + * Create a new instance of an upload. + */ + Formulate.prototype.createUpload = function createUpload (fileList, context) { + return new FileUpload(fileList, context, this.options) + }; + var Formulate$1 = new Formulate(); exports.default = Formulate$1; return exports; -}({}, isUrl, isPlainObject, nanoid)); +}({}, isUrl, nanoid, isPlainObject)); diff --git a/dist/formulate.umd.js b/dist/formulate.umd.js index bc698c8..2f1fb26 100644 --- a/dist/formulate.umd.js +++ b/dist/formulate.umd.js @@ -1,12 +1,12 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-url'), require('is-plain-object'), require('nanoid')) : - typeof define === 'function' && define.amd ? define(['exports', 'is-url', 'is-plain-object', 'nanoid'], factory) : - (global = global || self, factory(global.Formulate = {}, global.isUrl, global.isPlainObject, global.nanoid)); -}(this, (function (exports, isUrl, isPlainObject, nanoid) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-url'), require('nanoid'), require('is-plain-object')) : + typeof define === 'function' && define.amd ? define(['exports', 'is-url', 'nanoid', 'is-plain-object'], factory) : + (global = global || self, factory(global.Formulate = {}, global.isUrl, global.nanoid, global.isPlainObject)); +}(this, (function (exports, isUrl, nanoid, isPlainObject) { 'use strict'; isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl; - isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject; nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid; + isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject; var library = { // === SINGLE LINE TEXT STYLE INPUTS @@ -103,9 +103,129 @@ select: { classification: 'select', component: 'FormulateInputSelect' + }, + + // === FILE TYPE + + file: { + classification: 'file', + component: 'FormulateInputFile' + }, + image: { + classification: 'file', + component: 'FormulateInputFile' } }; + /** + * The file upload class holds and represents a file’s upload state durring + * the upload flow. + */ + var FileUpload = function FileUpload (fileList, context, options) { + this.fileList = fileList; + this.files = []; + this.options = options; + this.setFileList(fileList); + this.context = context; + }; + + /** + * Produce an array of files and alert the callback. + * @param {FileList} + */ + FileUpload.prototype.setFileList = function setFileList (fileList) { + for (var i = 0; i < fileList.length; i++) { + var file = fileList.item(i); + this.files.push({ + progress: 0, + name: file.name || 'file-upload', + file: file, + uuid: nanoid() + }); + } + }; + + /** + * Check if the file has an. + */ + FileUpload.prototype.hasUploader = function hasUploader () { + return !!this.context.uploader + }; + + FileUpload.prototype.uploaderIsAxios = function uploaderIsAxios () { + if ( + this.hasUploader && + typeof this.hasUploader.request === 'function' && + typeof this.hasUploader.get === 'function' && + typeof this.hasUploader.delete === 'function' && + typeof this.hasUploader.post === 'function' + ) { + return true + } + return false + }; + + /** + * Get a new uploader function. + */ + FileUpload.prototype.getUploader = function getUploader () { + var ref; + + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + if (this.uploaderIsAxios()) { + var formData = new FormData(); + formData.append(this.context.name || 'file', args[0]); + return this.uploader.post(this.context.uploadUrl, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: function (progressEvent) { + args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total)); + } + }) + .catch(function (err) { return args[2](err); }) + } + return (ref = this.context).uploader.apply(ref, args) + }; + + /** + * Perform the file upload. + */ + FileUpload.prototype.upload = function upload () { + var this$1 = this; + + return new Promise(function (resolve, reject) { + if (!this$1.hasUploader) { + return reject(new Error('No uploader has been defined')) + } + Promise.all(this$1.files.map(function (file) { + return this$1.getUploader( + file.file, + function (progress) { file.progress = progress; }, + function (error) { return reject(new Error(error)); }, + this$1.options + ) + })) + .then(function (results) { return resolve(results); }) + .catch(function (err) { throw new Error(err) }); + }) + }; + + /** + * Get the files. + */ + FileUpload.prototype.getFileList = function getFileList () { + return this.fileList + }; + + /** + * Get the files. + */ + FileUpload.prototype.getFiles = function getFiles () { + return this.files + }; + /** * Function to map over an object. * @param {Object} obj An object to map over @@ -395,19 +515,20 @@ /** * Check the maximum value of a particular. */ - max: function (value, minimum) { + max: function (value, minimum, force) { if ( minimum === void 0 ) minimum = 10; return Promise.resolve((function () { - minimum = Number(minimum); - if (!isNaN(value)) { - value = Number(value); - return value <= minimum - } - if (typeof value === 'string') { + if (Array.isArray(value)) { + minimum = !isNaN(minimum) ? Number(minimum) : minimum; return value.length <= minimum } - if (Array.isArray(value)) { + if ((!isNaN(value) && force !== 'length') || force === 'value') { + value = !isNaN(value) ? Number(value) : value; + return value <= minimum + } + if (typeof value === 'string' || (force === 'length')) { + value = !isNaN(value) ? value.toString() : value; return value.length <= minimum } return false @@ -422,6 +543,12 @@ while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ]; return Promise.resolve((function () { + if (files instanceof FileUpload) { + if (files.hasUploader()) { + return false + } + files = files.getFiles(); + } if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') { for (var i in files) { if (!types.includes(files[i].type)) { @@ -645,10 +772,14 @@ var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be less than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You may only select " + (args[0]) + " " + name + ".") } - return (name + " must be less than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be less than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be less than " + (args[0]) + " characters long.") }, /** @@ -659,10 +790,14 @@ var value = ref.value; var args = ref.args; - if (!isNaN(value)) { - return (name + " must be more than " + (args[0]) + ".") + if (Array.isArray(value)) { + return ("You must select at least " + (args[0]) + " " + name + ".") } - return (name + " must be more than " + (args[0]) + " characters long.") + var force = Array.isArray(args) && args[1] ? args[1] : false; + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return ((sentence(name)) + " must be more than " + (args[0]) + ".") + } + return ((sentence(name)) + " must be more than " + (args[0]) + " characters long.") }, /** @@ -703,6 +838,35 @@ } }; + /** + * A fake uploader used by default. + * + * @param {File} file + * @param {function} progress + * @param {function} error + * @param {object} options + */ + function fauxUploader (file, progress, error, options) { + return new Promise(function (resolve, reject) { + var totalTime = options.fauxUploaderDuration || 2000; + var start = performance.now(); + var advance = function () { return setTimeout(function () { + var elapsed = performance.now() - start; + var currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100)); + progress(currentProgress); + if (currentProgress >= 100) { + resolve({ + url: 'http://via.placeholder.com/350x150.png', + name: file.name + }); + } else { + advance(); + } + }, 20); }; + advance(); + }) + } + /** * For a single instance of an input, export all of the context needed to fully * render that element. @@ -719,7 +883,11 @@ label: this.label, labelPosition: this.logicalLabelPosition, attributes: this.elementAttributes, - blurHandler: blurHandler.bind(this)}, + blurHandler: blurHandler.bind(this), + showImage: this.showImage, + uploadUrl: this.uploadUrl, + uploader: this.uploader || this.$formulate.getUploader(), + immediateUpload: this.immediateUpload}, this.typeContext)) }, nameOrFallback: nameOrFallback, @@ -1007,6 +1175,22 @@ showErrors: { type: Boolean, default: false + }, + showImage: { + type: Boolean, + default: true + }, + uploadUrl: { + type: [String, Boolean], + default: false + }, + uploader: { + type: [Function, Object, Boolean], + default: false + }, + immediateUpload: { + type: Boolean, + default: true } }, data: function data () { @@ -1995,6 +2179,219 @@ // var script$6 = { + name: 'FormulateFiles', + props: { + files: { + type: FileUpload, + required: true + } + }, + computed: { + fileUploads: function fileUploads () { + return this.files.files || [] + } + } + }; + + /* script */ + var __vue_script__$6 = script$6; + + /* template */ + var __vue_render__$6 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _vm.fileUploads.length + ? _c( + "ul", + { staticClass: "formulate-files" }, + _vm._l(_vm.fileUploads, function(file) { + return _c("li", { key: file.uuid, staticClass: "formulate-file" }, [ + _c("span", { + staticClass: "formualte-file-name", + domProps: { textContent: _vm._s(file.name) } + }), + _vm._v(" "), + file.progress > 0 && file.progress < 100 + ? _c("span", { + domProps: { textContent: _vm._s(file.progress + "%") } + }) + : _vm._e() + ]) + }), + 0 + ) + : _vm._e() + }; + var __vue_staticRenderFns__$6 = []; + __vue_render__$6._withStripped = true; + + /* style */ + var __vue_inject_styles__$6 = undefined; + /* scoped */ + var __vue_scope_id__$6 = undefined; + /* module identifier */ + var __vue_module_identifier__$6 = undefined; + /* functional template */ + var __vue_is_functional_template__$6 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateFiles = normalizeComponent( + { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, + __vue_inject_styles__$6, + __vue_script__$6, + __vue_scope_id__$6, + __vue_is_functional_template__$6, + __vue_module_identifier__$6, + false, + undefined, + undefined, + undefined + ); + + // + + var script$7 = { + name: 'FormulateInputFile', + components: { + FormulateFiles: FormulateFiles + }, + mixins: [FormulateInputMixin], + data: function data () { + return { + isOver: false + } + }, + computed: { + hasFiles: function hasFiles () { + return (this.context.model instanceof FileUpload && this.context.model.files.length) + } + }, + methods: { + handleFile: function handleFile () { + var input = this.$refs.file; + if (input.files.length) { + this.context.model = this.$formulate.createUpload(input.files, this.context); + } + if (this.context.immediateUpload && this.context.model instanceof FileUpload) { + this.context.model.upload(); + } + }, + handleDragOver: function handleDragOver (e) { + e.preventDefault(); + this.isOver = true; + }, + handleDragLeave: function handleDragLeave (e) { + e.preventDefault(); + this.isOver = false; + } + } + }; + + /* script */ + var __vue_script__$7 = script$7; + + /* template */ + var __vue_render__$7 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c( + "div", + { + class: + "formulate-input-element formulate-input-element--" + _vm.context.type, + attrs: { "data-type": _vm.context.type, "data-has-files": _vm.hasFiles } + }, + [ + _c( + "div", + { + staticClass: "formulate-input-upload-area", + attrs: { "data-has-files": _vm.hasFiles } + }, + [ + _c( + "input", + _vm._b( + { + ref: "file", + attrs: { "data-is-drag-hover": _vm.isOver, type: "file" }, + on: { + blur: _vm.context.blurHandler, + change: _vm.handleFile, + dragover: _vm.handleDragOver, + dragleave: _vm.handleDragLeave + } + }, + "input", + _vm.attributes, + false + ) + ), + _vm._v(" "), + _c("div", { + directives: [ + { + name: "show", + rawName: "v-show", + value: !_vm.hasFiles, + expression: "!hasFiles" + } + ], + staticClass: "formulate-input-upload-area-mask" + }), + _vm._v(" "), + _vm.hasFiles + ? _c("FormulateFiles", { attrs: { files: _vm.context.model } }) + : _vm._e() + ], + 1 + ) + ] + ) + }; + var __vue_staticRenderFns__$7 = []; + __vue_render__$7._withStripped = true; + + /* style */ + var __vue_inject_styles__$7 = undefined; + /* scoped */ + var __vue_scope_id__$7 = undefined; + /* module identifier */ + var __vue_module_identifier__$7 = undefined; + /* functional template */ + var __vue_is_functional_template__$7 = false; + /* style inject */ + + /* style inject SSR */ + + /* style inject shadow dom */ + + + + var FormulateInputFile = normalizeComponent( + { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, + __vue_inject_styles__$7, + __vue_script__$7, + __vue_scope_id__$7, + __vue_is_functional_template__$7, + __vue_module_identifier__$7, + false, + undefined, + undefined, + undefined + ); + + // + + var script$8 = { name: 'FormulateInputSelect', mixins: [FormulateInputMixin], computed: { @@ -2011,10 +2408,10 @@ }; /* script */ - var __vue_script__$6 = script$6; + var __vue_script__$8 = script$8; /* template */ - var __vue_render__$6 = function() { + var __vue_render__$8 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2126,17 +2523,17 @@ ] ) }; - var __vue_staticRenderFns__$6 = []; - __vue_render__$6._withStripped = true; + var __vue_staticRenderFns__$8 = []; + __vue_render__$8._withStripped = true; /* style */ - var __vue_inject_styles__$6 = undefined; + var __vue_inject_styles__$8 = undefined; /* scoped */ - var __vue_scope_id__$6 = undefined; + var __vue_scope_id__$8 = undefined; /* module identifier */ - var __vue_module_identifier__$6 = undefined; + var __vue_module_identifier__$8 = undefined; /* functional template */ - var __vue_is_functional_template__$6 = false; + var __vue_is_functional_template__$8 = false; /* style inject */ /* style inject SSR */ @@ -2146,12 +2543,12 @@ var FormulateInputSelect = normalizeComponent( - { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, - __vue_inject_styles__$6, - __vue_script__$6, - __vue_scope_id__$6, - __vue_is_functional_template__$6, - __vue_module_identifier__$6, + { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, + __vue_inject_styles__$8, + __vue_script__$8, + __vue_scope_id__$8, + __vue_is_functional_template__$8, + __vue_module_identifier__$8, false, undefined, undefined, @@ -2160,16 +2557,16 @@ // - var script$7 = { - name: 'FormulateInputText', + var script$9 = { + name: 'FormulateInputSlider', mixins: [FormulateInputMixin] }; /* script */ - var __vue_script__$7 = script$7; + var __vue_script__$9 = script$9; /* template */ - var __vue_render__$7 = function() { + var __vue_render__$9 = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2290,17 +2687,17 @@ ] ) }; - var __vue_staticRenderFns__$7 = []; - __vue_render__$7._withStripped = true; + var __vue_staticRenderFns__$9 = []; + __vue_render__$9._withStripped = true; /* style */ - var __vue_inject_styles__$7 = undefined; + var __vue_inject_styles__$9 = undefined; /* scoped */ - var __vue_scope_id__$7 = undefined; + var __vue_scope_id__$9 = undefined; /* module identifier */ - var __vue_module_identifier__$7 = undefined; + var __vue_module_identifier__$9 = undefined; /* functional template */ - var __vue_is_functional_template__$7 = false; + var __vue_is_functional_template__$9 = false; /* style inject */ /* style inject SSR */ @@ -2310,12 +2707,12 @@ var FormulateInputSlider = normalizeComponent( - { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 }, - __vue_inject_styles__$7, - __vue_script__$7, - __vue_scope_id__$7, - __vue_is_functional_template__$7, - __vue_module_identifier__$7, + { render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 }, + __vue_inject_styles__$9, + __vue_script__$9, + __vue_scope_id__$9, + __vue_is_functional_template__$9, + __vue_module_identifier__$9, false, undefined, undefined, @@ -2324,16 +2721,16 @@ // - var script$8 = { + var script$a = { name: 'FormulateInputTextArea', mixins: [FormulateInputMixin] }; /* script */ - var __vue_script__$8 = script$8; + var __vue_script__$a = script$a; /* template */ - var __vue_render__$8 = function() { + var __vue_render__$a = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; @@ -2375,17 +2772,17 @@ ] ) }; - var __vue_staticRenderFns__$8 = []; - __vue_render__$8._withStripped = true; + var __vue_staticRenderFns__$a = []; + __vue_render__$a._withStripped = true; /* style */ - var __vue_inject_styles__$8 = undefined; + var __vue_inject_styles__$a = undefined; /* scoped */ - var __vue_scope_id__$8 = undefined; + var __vue_scope_id__$a = undefined; /* module identifier */ - var __vue_module_identifier__$8 = undefined; + var __vue_module_identifier__$a = undefined; /* functional template */ - var __vue_is_functional_template__$8 = false; + var __vue_is_functional_template__$a = false; /* style inject */ /* style inject SSR */ @@ -2395,12 +2792,12 @@ var FormulateInputTextArea = normalizeComponent( - { render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 }, - __vue_inject_styles__$8, - __vue_script__$8, - __vue_scope_id__$8, - __vue_is_functional_template__$8, - __vue_module_identifier__$8, + { render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a }, + __vue_inject_styles__$a, + __vue_script__$a, + __vue_scope_id__$a, + __vue_is_functional_template__$a, + __vue_module_identifier__$a, false, undefined, undefined, @@ -2418,6 +2815,7 @@ FormulateInputErrors: FormulateInputErrors, FormulateInputBox: FormulateInputBox, FormulateInputText: FormulateInputText, + FormulateInputFile: FormulateInputFile, FormulateInputGroup: FormulateInputGroup, FormulateInputSelect: FormulateInputSelect, FormulateInputSlider: FormulateInputSlider, @@ -2426,6 +2824,7 @@ library: library, rules: rules, locale: 'en', + uploader: fauxUploader, locales: { en: en } @@ -2514,6 +2913,20 @@ return 'This field does not have a valid value' }; + /** + * Get the file uploader. + */ + Formulate.prototype.getUploader = function getUploader () { + return this.options.uploader || false + }; + + /** + * Create a new instance of an upload. + */ + Formulate.prototype.createUpload = function createUpload (fileList, context) { + return new FileUpload(fileList, context, this.options) + }; + var Formulate$1 = new Formulate(); exports.default = Formulate$1; diff --git a/dist/snow.css b/dist/snow.css index 5d1f918..4f9360b 100644 --- a/dist/snow.css +++ b/dist/snow.css @@ -40,12 +40,37 @@ padding: .75em; display: block; width: 100%; - font-weight: 400; } + font-weight: 400; + line-height: 1.1em; + margin: 0; } .formulate-input[data-classification='text'] input::placeholder { color: #a8a8a8; } .formulate-input[data-classification='text'] input:focus { outline: 0; border: 1px solid #41b883; } + .formulate-input[data-classification='text'] input[type="color"] { + height: 1.1em; + box-sizing: content-box; } + .formulate-input[data-classification='text'] input[type="color"]::-webkit-color-swatch-wrapper { + padding: 0 0 0 1.5em; + display: flex; + align-items: center; + background-image: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-size: .9em .9em; + background-position: left .1em; } + .formulate-input[data-classification='text'] input[type="color"]::-webkit-color-swatch { + display: block; + height: 1em; + border-radius: .2em; + border: 0; + flex: auto; } + .formulate-input[data-classification='text'] input[type="color"]::-moz-color-swatch { + display: block; + height: 1em; + border-radius: .2em; + border: 0; + flex: auto; } .formulate-input[data-classification='slider'] input { appearance: none; width: 100%; @@ -96,7 +121,9 @@ padding: .75em; display: block; width: 100%; - font-weight: 400; } + font-weight: 400; + line-height: 1.1em; + margin: 0; } .formulate-input[data-classification='textarea'] textarea::placeholder { color: #a8a8a8; } .formulate-input[data-classification='textarea'] textarea:focus { @@ -127,6 +154,8 @@ display: block; width: 100%; font-weight: 400; + line-height: 1.1em; + margin: 0; padding-right: 2em; } .formulate-input[data-classification='select'] select::placeholder { color: #a8a8a8; } @@ -174,7 +203,8 @@ .formulate-input[data-classification='box'] .formulate-input-element input[type="checkbox"]:checked ~ .formulate-input-element-decorator { border-color: #41b883; } .formulate-input[data-classification='box'] .formulate-input-element input[type="checkbox"]:checked ~ .formulate-input-element-decorator::before { - background-image: url('data:image/svg+xml;utf8,'); } + background-color: #41b883; + mask-image: url('data:image/svg+xml;utf8,'); } .formulate-input[data-classification='box'] .formulate-input-element input[type="radio"]:checked ~ .formulate-input-element-decorator { border-color: #41b883; } .formulate-input[data-classification='box'] .formulate-input-element input[type="radio"]:checked ~ .formulate-input-element-decorator::before { @@ -187,3 +217,83 @@ margin-right: .5em; } .formulate-input[data-classification="group"] > .formulate-input-wrapper > .formulate-input-label { margin-bottom: .5em; } + .formulate-input[data-classification="file"] .formulate-input-upload-area { + width: 100%; + position: relative; + padding: 2em; } + .formulate-input[data-classification="file"] .formulate-input-upload-area[data-has-files] { + padding: 0; } + .formulate-input[data-classification="file"] .formulate-input-upload-area input { + cursor: pointer; + appearance: none; + opacity: 0; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 5; } + .formulate-input[data-classification="file"] .formulate-input-upload-area-mask { + border-radius: .4em; + position: absolute; + pointer-events: none; + position: absolute; + display: flex; + justify-content: center; + align-items: center; + left: 0; + right: 0; + top: 0; + bottom: 0; + border: 2px dashed #a8a8a8; + z-index: 2; } + .formulate-input[data-classification="file"] .formulate-input-upload-area-mask::before { + content: ''; + background-color: #a8a8a8; + mask-image: url('data:image/svg+xml;utf8,'); + width: 2em; + height: 2em; + position: absolute; + pointer-events: none; } + .formulate-input[data-classification="file"] .formulate-input-upload-area input:focus ~ .formulate-input-upload-area-mask, + .formulate-input[data-classification="file"] .formulate-input-upload-area input:hover ~ .formulate-input-upload-area-mask, + .formulate-input[data-classification="file"] .formulate-input-upload-area input[data-is-drag-hover] ~ .formulate-input-upload-area-mask { + border-color: #41b883; } + .formulate-input[data-classification="file"] .formulate-input-upload-area input:focus ~ .formulate-input-upload-area-mask::before, + .formulate-input[data-classification="file"] .formulate-input-upload-area input:hover ~ .formulate-input-upload-area-mask::before, + .formulate-input[data-classification="file"] .formulate-input-upload-area input[data-is-drag-hover] ~ .formulate-input-upload-area-mask::before { + background-color: #41b883; } + .formulate-input[data-classification="file"] .formulate-files { + list-style-type: none; + margin: 0; + padding: 0; } + .formulate-input[data-classification="file"] .formulate-files li { + appearance: none; + border-radius: .3em; + border: 1px solid #cecece; + box-sizing: border-box; + background-color: transparent; + font-size: .9em; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + padding: .75em; + display: block; + width: 100%; + font-weight: 400; + line-height: 1.1em; + margin: 0; + display: block; + width: 100%; + display: flex; + justify-content: space-between; } + .formulate-input[data-classification="file"] .formulate-files li::placeholder { + color: #a8a8a8; } + .formulate-input[data-classification="file"] .formulate-files li:focus { + outline: 0; + border: 1px solid #41b883; } + .formulate-input[data-classification="file"] .formulate-files li ::-webkit-progress-bar { + appearance: none; + height: .5em; + border-radius: .5em; + overflow: hidden; } diff --git a/dist/snow.min.css b/dist/snow.min.css index b8e8664..8532588 100644 --- a/dist/snow.min.css +++ b/dist/snow.min.css @@ -1,2 +1,2 @@ -.formulate-input{margin-bottom:2em}.formulate-input .formulate-input-label{display:block;line-height:1.5;font-size:.9em;font-weight:600;margin-bottom:.1em}.formulate-input .formulate-input-element{max-width:20em;margin-bottom:.1em}.formulate-input .formulate-input-help{color:#6d6d6d;font-size:.7em;font-weight:400;line-height:1.5;margin-bottom:.25em}.formulate-input .formulate-input-errors{list-style-type:none;padding:0;margin:0}.formulate-input .formulate-input-error{color:#960505;font-size:.8em;font-weight:300;line-height:1.5;margin-bottom:.25em}.formulate-input .formulate-input-group-item{margin-bottom:.5em}.formulate-input:last-child{margin-bottom:0}.formulate-input[data-classification=text] input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;padding:.75em;display:block;width:100%;font-weight:400}.formulate-input[data-classification=text] input::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=slider] input{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;font-size:1em;padding:.5em 0}.formulate-input[data-classification=slider] input:focus{outline:0}.formulate-input[data-classification=slider] input::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-moz-range-thumb{cursor:pointer;-moz-appearance:none;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-ms-thumb{cursor:pointer;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-webkit-slider-runnable-track{-webkit-appearance:none;appearance:none;width:100%;height:4px;background-color:#efefef;border-radius:3px;margin:0;padding:0}.formulate-input[data-classification=textarea] textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;padding:.75em;display:block;width:100%;font-weight:400}.formulate-input[data-classification=textarea] textarea::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=select] .formulate-input-element{position:relative}.formulate-input[data-classification=select] .formulate-input-element:before{content:"";width:0;height:0;border-color:#cecece transparent transparent;border-style:solid;border-width:.3em .3em 0;top:50%;margin-top:-.1em;right:1em;position:absolute}.formulate-input[data-classification=select] select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;display:block;width:100%;font-weight:400;padding:.75em 2em .75em .75em}.formulate-input[data-classification=select] select::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=select] select[data-placeholder-selected]{color:#a8a8a8}.formulate-input[data-classification=box] .formulate-input-element,.formulate-input[data-classification=box] .formulate-input-wrapper{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center}.formulate-input[data-classification=box] .formulate-input-element{overflow:hidden}.formulate-input[data-classification=box] .formulate-input-element input{position:absolute;left:-999px}.formulate-input[data-classification=box] .formulate-input-element-decorator{display:block;width:1em;height:1em;border-radius:.25em;border:1px solid #cecece;position:relative}.formulate-input[data-classification=box] .formulate-input-element-decorator:before{content:"";display:block;background-size:contain;background-position:100%;width:calc(100% - .125em);height:calc(100% - .125em);box-sizing:border-box;position:absolute;top:.0625em;left:.0625em}.formulate-input[data-classification=box] .formulate-input-element[data-type=radio] .formulate-input-element-decorator{border-radius:1em}.formulate-input[data-classification=box] .formulate-input-element[data-type=radio] .formulate-input-element-decorator:before{border-radius:1em;width:calc(100% - .5em);height:calc(100% - .5em);top:.25em;left:.25em}.formulate-input[data-classification=box] .formulate-input-element input[type=checkbox]:checked~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input[type=checkbox]:checked~.formulate-input-element-decorator:before{background-image:url('data:image/svg+xml;utf8,')}.formulate-input[data-classification=box] .formulate-input-element input[type=radio]:checked~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input[type=radio]:checked~.formulate-input-element-decorator:before{background-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input:focus~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-label--after{margin-left:.5em}.formulate-input[data-classification=box] .formulate-input-label--before{margin-right:.5em}.formulate-input[data-classification=group]>.formulate-input-wrapper>.formulate-input-label{margin-bottom:.5em} -/*# sourceMappingURL=data:application/json;base64,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QUNsQiwwREFDRSxTQUFVLENBQ1Ysd0JBQTJCLENBQzdCLCtFQUNFLGFBQWdCLENBSXBCLHNJQUZFLG1CQUFhLENBQWIsWUFBYSxDQUNiLHdCQUFtQixDQUFuQixrQkFJcUIsQ0FIdkIsbUVBQ0UsZUFFcUIsQ0FDckIseUVBQ0UsaUJBQWtCLENBQ2xCLFdBQWMsQ0FDaEIsNkVBQ0UsYUFBYyxDQUNkLFNBQVUsQ0FDVixVQUFXLENBQ1gsbUJBQW9CLENBQ3BCLHdCQUF5QixDQUN6QixpQkFBb0IsQ0FDcEIsb0ZBQ0UsVUFBVyxDQUNYLGFBQWMsQ0FDZCx1QkFBd0IsQ0FDeEIsd0JBQTBCLENBQzFCLHlCQUEwQixDQUMxQiwwQkFBMkIsQ0FDM0IscUJBQXNCLENBQ3RCLGlCQUFrQixDQUNsQixXQUFZLENBQ1osWUFBZSxDQUNuQix1SEFDRSxpQkFBb0IsQ0FDcEIsOEhBQ0UsaUJBQWtCLENBQ2xCLHVCQUF3QixDQUN4Qix3QkFBeUIsQ0FDekIsU0FBVSxDQUNWLFVBQWEsQ0FDakIsbUlBQ0Usb0JBQXVCLENBQ3ZCLDBJQUNFLGdSQUFtUixDQUN2UixnSUFDRSxvQkFBdUIsQ0FDdkIsdUlBQ0Usd0JBQTJCLENBQy9CLGtIQUNFLG9CQUF1QixDQUMzQix3RUFDRSxnQkFBbUIsQ0FDckIseUVBQ0UsaUJBQW9CLENBQ3RCLDRGQUNFLGtCQUFxQiIsImZpbGUiOiJzdGRpbiIsInNvdXJjZXNDb250ZW50IjpbIi5mb3JtdWxhdGUtaW5wdXQge1xuICBtYXJnaW4tYm90dG9tOiAyZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dCAuZm9ybXVsYXRlLWlucHV0LWxhYmVsIHtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICBsaW5lLWhlaWdodDogMS41O1xuICAgIGZvbnQtc2l6ZTogLjllbTtcbiAgICBmb250LXdlaWdodDogNjAwO1xuICAgIG1hcmdpbi1ib3R0b206IC4xZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dCAuZm9ybXVsYXRlLWlucHV0LWVsZW1lbnQge1xuICAgIG1heC13aWR0aDogMjBlbTtcbiAgICBtYXJnaW4tYm90dG9tOiAuMWVtOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXQgLmZvcm11bGF0ZS1pbnB1dC1oZWxwIHtcbiAgICBjb2xvcjogIzZkNmQ2ZDtcbiAgICBmb250LXNpemU6IC43ZW07XG4gICAgZm9udC13ZWlnaHQ6IDQwMDtcbiAgICBsaW5lLWhlaWdodDogMS41O1xuICAgIG1hcmdpbi1ib3R0b206IC4yNWVtOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXQgLmZvcm11bGF0ZS1pbnB1dC1lcnJvcnMge1xuICAgIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTtcbiAgICBwYWRkaW5nOiAwO1xuICAgIG1hcmdpbjogMDsgfVxuICAuZm9ybXVsYXRlLWlucHV0IC5mb3JtdWxhdGUtaW5wdXQtZXJyb3Ige1xuICAgIGNvbG9yOiAjOTYwNTA1O1xuICAgIGZvbnQtc2l6ZTogLjhlbTtcbiAgICBmb250LXdlaWdodDogMzAwO1xuICAgIGxpbmUtaGVpZ2h0OiAxLjU7XG4gICAgbWFyZ2luLWJvdHRvbTogLjI1ZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dCAuZm9ybXVsYXRlLWlucHV0LWdyb3VwLWl0ZW0ge1xuICAgIG1hcmdpbi1ib3R0b206IC41ZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dDpsYXN0LWNoaWxkIHtcbiAgICBtYXJnaW4tYm90dG9tOiAwOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0ndGV4dCddIGlucHV0IHtcbiAgICBhcHBlYXJhbmNlOiBub25lO1xuICAgIGJvcmRlci1yYWRpdXM6IC4zZW07XG4gICAgYm9yZGVyOiAxcHggc29saWQgI2NlY2VjZTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICAgIGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAgIGZvbnQtc2l6ZTogLjllbTtcbiAgICBmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCBcIlNlZ29lIFVJXCIsIFJvYm90bywgSGVsdmV0aWNhLCBBcmlhbCwgc2Fucy1zZXJpZiwgXCJBcHBsZSBDb2xvciBFbW9qaVwiLCBcIlNlZ29lIFVJIEVtb2ppXCIsIFwiU2Vnb2UgVUkgU3ltYm9sXCI7XG4gICAgcGFkZGluZzogLjc1ZW07XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgd2lkdGg6IDEwMCU7XG4gICAgZm9udC13ZWlnaHQ6IDQwMDsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0ndGV4dCddIGlucHV0OjpwbGFjZWhvbGRlciB7XG4gICAgICBjb2xvcjogI2E4YThhODsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0ndGV4dCddIGlucHV0OmZvY3VzIHtcbiAgICAgIG91dGxpbmU6IDA7XG4gICAgICBib3JkZXI6IDFweCBzb2xpZCAjNDFiODgzOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQge1xuICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgd2lkdGg6IDEwMCU7XG4gICAgZm9udC1zaXplOiAxZW07XG4gICAgcGFkZGluZzogLjVlbSAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDpmb2N1cyB7XG4gICAgICBvdXRsaW5lOiAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDo6LXdlYmtpdC1zbGlkZXItdGh1bWIge1xuICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxZW07XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IDFlbTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7XG4gICAgICBtYXJnaW4tdG9wOiBjYWxjKC0uNWVtICsgMnB4KTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQ6Oi1tb3otcmFuZ2UtdGh1bWIge1xuICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxZW07XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IDFlbTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7XG4gICAgICBtYXJnaW4tdG9wOiBjYWxjKC0uNWVtICsgMnB4KTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQ6Oi1tcy10aHVtYiB7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICBhcHBlYXJhbmNlOiBub25lO1xuICAgICAgd2lkdGg6IDFlbTtcbiAgICAgIGhlaWdodDogMWVtO1xuICAgICAgYm9yZGVyLXJhZGl1czogMWVtO1xuICAgICAgYmFja2dyb3VuZC1jb2xvcjogIzQxYjg4MztcbiAgICAgIG1hcmdpbi10b3A6IGNhbGMoLS41ZW0gKyAycHgpOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDo6LXdlYmtpdC1zbGlkZXItcnVubmFibGUtdHJhY2sge1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgaGVpZ2h0OiA0cHg7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZWZlZmVmO1xuICAgICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgICAgbWFyZ2luOiAwO1xuICAgICAgcGFkZGluZzogMDsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHRhcmVhJ10gdGV4dGFyZWEge1xuICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgYm9yZGVyLXJhZGl1czogLjNlbTtcbiAgICBib3JkZXI6IDFweCBzb2xpZCAjY2VjZWNlO1xuICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgZm9udC1zaXplOiAuOWVtO1xuICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmLCBcIkFwcGxlIENvbG9yIEVtb2ppXCIsIFwiU2Vnb2UgVUkgRW1vamlcIiwgXCJTZWdvZSBVSSBTeW1ib2xcIjtcbiAgICBwYWRkaW5nOiAuNzVlbTtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICB3aWR0aDogMTAwJTtcbiAgICBmb250LXdlaWdodDogNDAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSd0ZXh0YXJlYSddIHRleHRhcmVhOjpwbGFjZWhvbGRlciB7XG4gICAgICBjb2xvcjogI2E4YThhODsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0ndGV4dGFyZWEnXSB0ZXh0YXJlYTpmb2N1cyB7XG4gICAgICBvdXRsaW5lOiAwO1xuICAgICAgYm9yZGVyOiAxcHggc29saWQgIzQxYjg4MzsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3NlbGVjdCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzZWxlY3QnXSAuZm9ybXVsYXRlLWlucHV0LWVsZW1lbnQ6OmJlZm9yZSB7XG4gICAgICBjb250ZW50OiAnJztcbiAgICAgIHdpZHRoOiAwO1xuICAgICAgaGVpZ2h0OiAwO1xuICAgICAgYm9yZGVyOiAuM2VtIHNvbGlkIHRyYW5zcGFyZW50O1xuICAgICAgYm9yZGVyLXRvcC1jb2xvcjogI2NlY2VjZTtcbiAgICAgIGJvcmRlci1ib3R0b20td2lkdGg6IDA7XG4gICAgICB0b3A6IDUwJTtcbiAgICAgIG1hcmdpbi10b3A6IC0uMWVtO1xuICAgICAgcmlnaHQ6IDFlbTtcbiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3NlbGVjdCddIHNlbGVjdCB7XG4gICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICBib3JkZXItcmFkaXVzOiAuM2VtO1xuICAgIGJvcmRlcjogMXB4IHNvbGlkICNjZWNlY2U7XG4gICAgYm94LXNpemluZzogYm9yZGVyLWJveDtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgICBmb250LXNpemU6IC45ZW07XG4gICAgZm9udC1mYW1pbHk6IC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgXCJTZWdvZSBVSVwiLCBSb2JvdG8sIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWYsIFwiQXBwbGUgQ29sb3IgRW1vamlcIiwgXCJTZWdvZSBVSSBFbW9qaVwiLCBcIlNlZ29lIFVJIFN5bWJvbFwiO1xuICAgIHBhZGRpbmc6IC43NWVtO1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIHdpZHRoOiAxMDAlO1xuICAgIGZvbnQtd2VpZ2h0OiA0MDA7XG4gICAgcGFkZGluZy1yaWdodDogMmVtOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzZWxlY3QnXSBzZWxlY3Q6OnBsYWNlaG9sZGVyIHtcbiAgICAgIGNvbG9yOiAjYThhOGE4OyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzZWxlY3QnXSBzZWxlY3Q6Zm9jdXMge1xuICAgICAgb3V0bGluZTogMDtcbiAgICAgIGJvcmRlcjogMXB4IHNvbGlkICM0MWI4ODM7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3NlbGVjdCddIHNlbGVjdFtkYXRhLXBsYWNlaG9sZGVyLXNlbGVjdGVkXSB7XG4gICAgICBjb2xvcjogI2E4YThhODsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtd3JhcHBlciB7XG4gICAgZGlzcGxheTogZmxleDtcbiAgICBhbGlnbi1pdGVtczogY2VudGVyOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50IHtcbiAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50IGlucHV0IHtcbiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgIGxlZnQ6IC05OTlweDsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50LWRlY29yYXRvciB7XG4gICAgICBkaXNwbGF5OiBibG9jaztcbiAgICAgIHdpZHRoOiAxZW07XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IC4yNWVtO1xuICAgICAgYm9yZGVyOiAxcHggc29saWQgI2NlY2VjZTtcbiAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTsgfVxuICAgICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdib3gnXSAuZm9ybXVsYXRlLWlucHV0LWVsZW1lbnQtZGVjb3JhdG9yOjpiZWZvcmUge1xuICAgICAgICBjb250ZW50OiAnJztcbiAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjtcbiAgICAgICAgYmFja2dyb3VuZC1wb3NpdGlvbjogcmlnaHQ7XG4gICAgICAgIHdpZHRoOiBjYWxjKDEwMCUgLSAuMTI1ZW0pO1xuICAgICAgICBoZWlnaHQ6IGNhbGMoMTAwJSAtIC4xMjVlbSk7XG4gICAgICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgdG9wOiAuMDYyNWVtO1xuICAgICAgICBsZWZ0OiAuMDYyNWVtOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdib3gnXSAuZm9ybXVsYXRlLWlucHV0LWVsZW1lbnRbZGF0YS10eXBlPVwicmFkaW9cIl0gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50LWRlY29yYXRvciB7XG4gICAgICBib3JkZXItcmFkaXVzOiAxZW07IH1cbiAgICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50W2RhdGEtdHlwZT1cInJhZGlvXCJdIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3I6OmJlZm9yZSB7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDFlbTtcbiAgICAgICAgd2lkdGg6IGNhbGMoMTAwJSAtIC41ZW0pO1xuICAgICAgICBoZWlnaHQ6IGNhbGMoMTAwJSAtIC41ZW0pO1xuICAgICAgICB0b3A6IC4yNWVtO1xuICAgICAgICBsZWZ0OiAuMjVlbTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50IGlucHV0W3R5cGU9XCJjaGVja2JveFwiXTpjaGVja2VkIH4gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50LWRlY29yYXRvciB7XG4gICAgICBib3JkZXItY29sb3I6ICM0MWI4ODM7IH1cbiAgICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50IGlucHV0W3R5cGU9XCJjaGVja2JveFwiXTpjaGVja2VkIH4gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50LWRlY29yYXRvcjo6YmVmb3JlIHtcbiAgICAgICAgYmFja2dyb3VuZC1pbWFnZTogdXJsKCdkYXRhOmltYWdlL3N2Zyt4bWw7dXRmOCw8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB2aWV3Qm94PVwiMCAwIDEwMCAxMDBcIiBmaWxsPVwiJTIzNDFiODgzXCI+PHBhdGggZD1cIk04Ljc2LDU2LjJjLTYuMzgtNi4zNCwzLjI2LTE2LDkuNjQtOS42OUwzOCw2NS44OCw4MC41NiwyMy4yOWM2LjM4LTYuMzgsMTYuMDcsMy4zMiw5LjY5LDkuNjlMNDIuODQsODAuMzdhNi44Myw2LjgzLDAsMCwxLTkuNjUsMFpcIi8+PC9zdmc+Jyk7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLWNvbG9yOiAjNDFiODgzOyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3I6OmJlZm9yZSB7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dDpmb2N1cyB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLWNvbG9yOiAjNDFiODgzOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1sYWJlbC0tYWZ0ZXIge1xuICAgIG1hcmdpbi1sZWZ0OiAuNWVtOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1sYWJlbC0tYmVmb3JlIHtcbiAgICBtYXJnaW4tcmlnaHQ6IC41ZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZ3JvdXBcIl0gPiAuZm9ybXVsYXRlLWlucHV0LXdyYXBwZXIgPiAuZm9ybXVsYXRlLWlucHV0LWxhYmVsIHtcbiAgICBtYXJnaW4tYm90dG9tOiAuNWVtOyB9XG4iXX0= */ \ No newline at end of file +.formulate-input{margin-bottom:2em}.formulate-input .formulate-input-label{display:block;line-height:1.5;font-size:.9em;font-weight:600;margin-bottom:.1em}.formulate-input .formulate-input-element{max-width:20em;margin-bottom:.1em}.formulate-input .formulate-input-help{color:#6d6d6d;font-size:.7em;font-weight:400;line-height:1.5;margin-bottom:.25em}.formulate-input .formulate-input-errors{list-style-type:none;padding:0;margin:0}.formulate-input .formulate-input-error{color:#960505;font-size:.8em;font-weight:300;line-height:1.5;margin-bottom:.25em}.formulate-input .formulate-input-group-item{margin-bottom:.5em}.formulate-input:last-child{margin-bottom:0}.formulate-input[data-classification=text] input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;padding:.75em;display:block;width:100%;font-weight:400;line-height:1.1em;margin:0}.formulate-input[data-classification=text] input::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input::placeholder{color:#a8a8a8}.formulate-input[data-classification=text] input:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=text] input[type=color]{height:1.1em;box-sizing:content-box}.formulate-input[data-classification=text] input[type=color]::-webkit-color-swatch-wrapper{padding:0 0 0 1.5em;display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;background-image:url('data:image/svg+xml;utf8,');background-repeat:no-repeat;background-size:.9em .9em;background-position:left .1em}.formulate-input[data-classification=text] input[type=color]::-webkit-color-swatch{display:block;height:1em;border-radius:.2em;border:0;-webkit-box-flex:1;flex:auto}.formulate-input[data-classification=text] input[type=color]::-moz-color-swatch{display:block;height:1em;border-radius:.2em;border:0;flex:auto}.formulate-input[data-classification=slider] input{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;font-size:1em;padding:.5em 0}.formulate-input[data-classification=slider] input:focus{outline:0}.formulate-input[data-classification=slider] input::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-moz-range-thumb{cursor:pointer;-moz-appearance:none;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-ms-thumb{cursor:pointer;appearance:none;width:1em;height:1em;border-radius:1em;background-color:#41b883;margin-top:calc(-.5em + 2px)}.formulate-input[data-classification=slider] input::-webkit-slider-runnable-track{-webkit-appearance:none;appearance:none;width:100%;height:4px;background-color:#efefef;border-radius:3px;margin:0;padding:0}.formulate-input[data-classification=textarea] textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;padding:.75em;display:block;width:100%;font-weight:400;line-height:1.1em;margin:0}.formulate-input[data-classification=textarea] textarea::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea::placeholder{color:#a8a8a8}.formulate-input[data-classification=textarea] textarea:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=select] .formulate-input-element{position:relative}.formulate-input[data-classification=select] .formulate-input-element:before{content:"";width:0;height:0;border-color:#cecece transparent transparent;border-style:solid;border-width:.3em .3em 0;top:50%;margin-top:-.1em;right:1em;position:absolute}.formulate-input[data-classification=select] select{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;display:block;width:100%;font-weight:400;line-height:1.1em;margin:0;padding:.75em 2em .75em .75em}.formulate-input[data-classification=select] select::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select::placeholder{color:#a8a8a8}.formulate-input[data-classification=select] select:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=select] select[data-placeholder-selected]{color:#a8a8a8}.formulate-input[data-classification=box] .formulate-input-element,.formulate-input[data-classification=box] .formulate-input-wrapper{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center}.formulate-input[data-classification=box] .formulate-input-element{overflow:hidden}.formulate-input[data-classification=box] .formulate-input-element input{position:absolute;left:-999px}.formulate-input[data-classification=box] .formulate-input-element-decorator{display:block;width:1em;height:1em;border-radius:.25em;border:1px solid #cecece;position:relative}.formulate-input[data-classification=box] .formulate-input-element-decorator:before{content:"";display:block;background-size:contain;background-position:100%;width:calc(100% - .125em);height:calc(100% - .125em);box-sizing:border-box;position:absolute;top:.0625em;left:.0625em}.formulate-input[data-classification=box] .formulate-input-element[data-type=radio] .formulate-input-element-decorator{border-radius:1em}.formulate-input[data-classification=box] .formulate-input-element[data-type=radio] .formulate-input-element-decorator:before{border-radius:1em;width:calc(100% - .5em);height:calc(100% - .5em);top:.25em;left:.25em}.formulate-input[data-classification=box] .formulate-input-element input[type=checkbox]:checked~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input[type=checkbox]:checked~.formulate-input-element-decorator:before{background-color:#41b883;-webkit-mask-image:url('data:image/svg+xml;utf8,');mask-image:url('data:image/svg+xml;utf8,')}.formulate-input[data-classification=box] .formulate-input-element input[type=radio]:checked~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input[type=radio]:checked~.formulate-input-element-decorator:before{background-color:#41b883}.formulate-input[data-classification=box] .formulate-input-element input:focus~.formulate-input-element-decorator{border-color:#41b883}.formulate-input[data-classification=box] .formulate-input-label--after{margin-left:.5em}.formulate-input[data-classification=box] .formulate-input-label--before{margin-right:.5em}.formulate-input[data-classification=group]>.formulate-input-wrapper>.formulate-input-label{margin-bottom:.5em}.formulate-input[data-classification=file] .formulate-input-upload-area{width:100%;position:relative;padding:2em}.formulate-input[data-classification=file] .formulate-input-upload-area[data-has-files]{padding:0}.formulate-input[data-classification=file] .formulate-input-upload-area input{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0;position:absolute;left:0;right:0;bottom:0;top:0;width:100%;height:100%;z-index:5}.formulate-input[data-classification=file] .formulate-input-upload-area-mask{border-radius:.4em;pointer-events:none;position:absolute;display:-webkit-box;display:flex;-webkit-box-pack:center;justify-content:center;-webkit-box-align:center;align-items:center;left:0;right:0;top:0;bottom:0;border:2px dashed #a8a8a8;z-index:2}.formulate-input[data-classification=file] .formulate-input-upload-area-mask:before{content:"";background-color:#a8a8a8;-webkit-mask-image:url('data:image/svg+xml;utf8,');mask-image:url('data:image/svg+xml;utf8,');width:2em;height:2em;position:absolute;pointer-events:none}.formulate-input[data-classification=file] .formulate-input-upload-area input:focus~.formulate-input-upload-area-mask,.formulate-input[data-classification=file] .formulate-input-upload-area input:hover~.formulate-input-upload-area-mask,.formulate-input[data-classification=file] .formulate-input-upload-area input[data-is-drag-hover]~.formulate-input-upload-area-mask{border-color:#41b883}.formulate-input[data-classification=file] .formulate-input-upload-area input:focus~.formulate-input-upload-area-mask:before,.formulate-input[data-classification=file] .formulate-input-upload-area input:hover~.formulate-input-upload-area-mask:before,.formulate-input[data-classification=file] .formulate-input-upload-area input[data-is-drag-hover]~.formulate-input-upload-area-mask:before{background-color:#41b883}.formulate-input[data-classification=file] .formulate-files{list-style-type:none;margin:0;padding:0}.formulate-input[data-classification=file] .formulate-files li{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.3em;border:1px solid #cecece;box-sizing:border-box;background-color:transparent;font-size:.9em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;padding:.75em;font-weight:400;line-height:1.1em;margin:0;display:block;width:100%;display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between}.formulate-input[data-classification=file] .formulate-files li::-webkit-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=file] .formulate-files li::-moz-placeholder{color:#a8a8a8}.formulate-input[data-classification=file] .formulate-files li:-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=file] .formulate-files li::-ms-input-placeholder{color:#a8a8a8}.formulate-input[data-classification=file] .formulate-files li::placeholder{color:#a8a8a8}.formulate-input[data-classification=file] .formulate-files li:focus{outline:0;border:1px solid #41b883}.formulate-input[data-classification=file] .formulate-files li ::-webkit-progress-bar{-webkit-appearance:none;appearance:none;height:.5em;border-radius:.5em;overflow:hidden} +/*# sourceMappingURL=data:application/json;base64,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QUFnQixDQURsQixzRkFDRSxhQUFnQixDQURsQiw0RUFDRSxhQUFnQixDQUNsQixxRUFDRSxTQUFVLENBQ1Ysd0JBQTJCLENBQzdCLHNGQUNFLHVCQUFnQixDQUFoQixlQUFnQixDQUNoQixXQUFZLENBQ1osa0JBQW1CLENBQ25CLGVBQWtCIiwiZmlsZSI6InN0ZGluIiwic291cmNlc0NvbnRlbnQiOlsiLmZvcm11bGF0ZS1pbnB1dCB7XG4gIG1hcmdpbi1ib3R0b206IDJlbTsgfVxuICAuZm9ybXVsYXRlLWlucHV0IC5mb3JtdWxhdGUtaW5wdXQtbGFiZWwge1xuICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgIGxpbmUtaGVpZ2h0OiAxLjU7XG4gICAgZm9udC1zaXplOiAuOWVtO1xuICAgIGZvbnQtd2VpZ2h0OiA2MDA7XG4gICAgbWFyZ2luLWJvdHRvbTogLjFlbTsgfVxuICAuZm9ybXVsYXRlLWlucHV0IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCB7XG4gICAgbWF4LXdpZHRoOiAyMGVtO1xuICAgIG1hcmdpbi1ib3R0b206IC4xZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dCAuZm9ybXVsYXRlLWlucHV0LWhlbHAge1xuICAgIGNvbG9yOiAjNmQ2ZDZkO1xuICAgIGZvbnQtc2l6ZTogLjdlbTtcbiAgICBmb250LXdlaWdodDogNDAwO1xuICAgIGxpbmUtaGVpZ2h0OiAxLjU7XG4gICAgbWFyZ2luLWJvdHRvbTogLjI1ZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dCAuZm9ybXVsYXRlLWlucHV0LWVycm9ycyB7XG4gICAgbGlzdC1zdHlsZS10eXBlOiBub25lO1xuICAgIHBhZGRpbmc6IDA7XG4gICAgbWFyZ2luOiAwOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXQgLmZvcm11bGF0ZS1pbnB1dC1lcnJvciB7XG4gICAgY29sb3I6ICM5NjA1MDU7XG4gICAgZm9udC1zaXplOiAuOGVtO1xuICAgIGZvbnQtd2VpZ2h0OiAzMDA7XG4gICAgbGluZS1oZWlnaHQ6IDEuNTtcbiAgICBtYXJnaW4tYm90dG9tOiAuMjVlbTsgfVxuICAuZm9ybXVsYXRlLWlucHV0IC5mb3JtdWxhdGUtaW5wdXQtZ3JvdXAtaXRlbSB7XG4gICAgbWFyZ2luLWJvdHRvbTogLjVlbTsgfVxuICAuZm9ybXVsYXRlLWlucHV0Omxhc3QtY2hpbGQge1xuICAgIG1hcmdpbi1ib3R0b206IDA7IH1cbiAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSd0ZXh0J10gaW5wdXQge1xuICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgYm9yZGVyLXJhZGl1czogLjNlbTtcbiAgICBib3JkZXI6IDFweCBzb2xpZCAjY2VjZWNlO1xuICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgZm9udC1zaXplOiAuOWVtO1xuICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmLCBcIkFwcGxlIENvbG9yIEVtb2ppXCIsIFwiU2Vnb2UgVUkgRW1vamlcIiwgXCJTZWdvZSBVSSBTeW1ib2xcIjtcbiAgICBwYWRkaW5nOiAuNzVlbTtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICB3aWR0aDogMTAwJTtcbiAgICBmb250LXdlaWdodDogNDAwO1xuICAgIGxpbmUtaGVpZ2h0OiAxLjFlbTtcbiAgICBtYXJnaW46IDA7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHQnXSBpbnB1dDo6cGxhY2Vob2xkZXIge1xuICAgICAgY29sb3I6ICNhOGE4YTg7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHQnXSBpbnB1dDpmb2N1cyB7XG4gICAgICBvdXRsaW5lOiAwO1xuICAgICAgYm9yZGVyOiAxcHggc29saWQgIzQxYjg4MzsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHQnXSBpbnB1dFt0eXBlPVwiY29sb3JcIl0ge1xuICAgIGhlaWdodDogMS4xZW07XG4gICAgYm94LXNpemluZzogY29udGVudC1ib3g7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHQnXSBpbnB1dFt0eXBlPVwiY29sb3JcIl06Oi13ZWJraXQtY29sb3Itc3dhdGNoLXdyYXBwZXIge1xuICAgICAgcGFkZGluZzogMCAwIDAgMS41ZW07XG4gICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgIGJhY2tncm91bmQtaW1hZ2U6IHVybCgnZGF0YTppbWFnZS9zdmcreG1sO3V0ZjgsPHN2ZyB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIgdmlld0JveD1cIjAgMCA3OS4xNyA3OS4xN1wiPjxwYXRoIGZpbGw9XCIlMjM2ZDZkNmRcIiBkPVwiTTQwLjgsMjIuOTJjLTMuNC0zLjQtNC43Ni04LjQ0LTEtMTIuMjRzOC44NC0yLjQ0LDEyLjI0LDFjNS01LDEwLjY5LTEzLjMzLDE4LjgxLTExLjMxYTExLDExLDAsMCwxLDcuNjIsMTQuMzRjLTEuMjYsMy40NS00LjYzLDYuMDYtNy4xNiw4LjU5LS45Mi45My0zLDIuMjYtMy40NiwzLjQ2LS40MiwxLDEuODIsMi42MywyLjM2LDRhOCw4LDAsMCwxLTEyLjQyLDkuMTljLS4yMS0uMTYtMS4zNS0xLjUxLTEuNTktMS41MXMtLjgzLjgzLTEsMUw0OS43MSw0NC45LDMyLjQzLDYyLjE4Yy0zLjM3LDMuMzgtNi42LDcuMTQtMTAuMjYsMTAuMjFhMTEsMTEsMCwwLDEtNC40OCwyLjI4Yy0xLjI1LjMtMy4xMS0uMjItNC4xOC4xOC0xLjI4LjQ4LTIuNDIsMi42NS0zLjY4LDMuNC02LjA1LDMuNjEtMTIuNjQtNC04LjQ2LTkuNTcuNzMtMSwyLjUzLTEuOTIsMy0zYTE0LjQ2LDE0LjQ2LDAsMCwwLS4wOS0yLjUyLDEwLjc1LDEwLjc1LDAsMCwxLDMuMTQtNi43N2MuOTItMSwxLjkzLTEuOTMsMi44OS0yLjlabTQuNC0xLjVjNC4xOSw0LDguMjQsOC4yNCwxMi4zNiwxMi4zNiwyLjA2LDIuMDYsNSw1LjU5LDgsMi42MSw0LjY1LTQuNjItNS02LjgtMi40Mi0xMC43OEM2Ni4zLDIwLjcsNzYuNCwxNi40OCw3NC44NCw5LjQ1LDczLjYyLDQsNjcuMTIsMi43OCw2My4yOSw2LjMyYy0yLjU1LDIuMzYtNC45Myw0Ljk0LTcuMzksNy40LS43OS43OC0xLjgsMi4yOC0yLjg4LDIuNzMtMi4xNC44OC0zLjQtMS42Mi00Ljc5LTIuNzctMi41OC0yLjE0LTYuODktLjgyLTYuNTMsM0M0MS44OSwxOC42OCw0My44NywyMC4wOSw0NS4yLDIxLjQyWm0tMS40NSw0LjQ0TDI3LjgyLDQxLjc5QzIyLDQ3LjU3LDE1Ljg5LDUzLjE0LDEwLjQxLDU5LjJhOC4yMyw4LjIzLDAsMCwwLTEuNDQsMmMtLjkzLDIsLjI1LDQuMTQtLjUsNlM0LjkyLDY5Ljk0LDQuMyw3MmEyLjM0LDIuMzQsMCwwLDAsMi41NiwzYzEuMTEtLjE3LDItMS4zMywyLjcxLTIuMDdhMTEuMTcsMTEuMTcsMCwwLDEsMi4wOC0yYzEuNjgtLjk0LDQsLjE3LDUuOTMtLjU3QzIwLDY5LjQxLDIyLDY2LjczLDIzLjc2LDY1TDM0LjQyLDU0LjMsNTMuMywzNS40MlpcIi8+PC9zdmc+Jyk7XG4gICAgICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0O1xuICAgICAgYmFja2dyb3VuZC1zaXplOiAuOWVtIC45ZW07XG4gICAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiBsZWZ0IC4xZW07IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHQnXSBpbnB1dFt0eXBlPVwiY29sb3JcIl06Oi13ZWJraXQtY29sb3Itc3dhdGNoIHtcbiAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgaGVpZ2h0OiAxZW07XG4gICAgICBib3JkZXItcmFkaXVzOiAuMmVtO1xuICAgICAgYm9yZGVyOiAwO1xuICAgICAgZmxleDogYXV0bzsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0ndGV4dCddIGlucHV0W3R5cGU9XCJjb2xvclwiXTo6LW1vei1jb2xvci1zd2F0Y2gge1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IC4yZW07XG4gICAgICBib3JkZXI6IDA7XG4gICAgICBmbGV4OiBhdXRvOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQge1xuICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgd2lkdGg6IDEwMCU7XG4gICAgZm9udC1zaXplOiAxZW07XG4gICAgcGFkZGluZzogLjVlbSAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDpmb2N1cyB7XG4gICAgICBvdXRsaW5lOiAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDo6LXdlYmtpdC1zbGlkZXItdGh1bWIge1xuICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxZW07XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IDFlbTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7XG4gICAgICBtYXJnaW4tdG9wOiBjYWxjKC0uNWVtICsgMnB4KTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQ6Oi1tb3otcmFuZ2UtdGh1bWIge1xuICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxZW07XG4gICAgICBoZWlnaHQ6IDFlbTtcbiAgICAgIGJvcmRlci1yYWRpdXM6IDFlbTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7XG4gICAgICBtYXJnaW4tdG9wOiBjYWxjKC0uNWVtICsgMnB4KTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2xpZGVyJ10gaW5wdXQ6Oi1tcy10aHVtYiB7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICBhcHBlYXJhbmNlOiBub25lO1xuICAgICAgd2lkdGg6IDFlbTtcbiAgICAgIGhlaWdodDogMWVtO1xuICAgICAgYm9yZGVyLXJhZGl1czogMWVtO1xuICAgICAgYmFja2dyb3VuZC1jb2xvcjogIzQxYjg4MztcbiAgICAgIG1hcmdpbi10b3A6IGNhbGMoLS41ZW0gKyAycHgpOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzbGlkZXInXSBpbnB1dDo6LXdlYmtpdC1zbGlkZXItcnVubmFibGUtdHJhY2sge1xuICAgICAgYXBwZWFyYW5jZTogbm9uZTtcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgaGVpZ2h0OiA0cHg7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZWZlZmVmO1xuICAgICAgYm9yZGVyLXJhZGl1czogM3B4O1xuICAgICAgbWFyZ2luOiAwO1xuICAgICAgcGFkZGluZzogMDsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHRhcmVhJ10gdGV4dGFyZWEge1xuICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgYm9yZGVyLXJhZGl1czogLjNlbTtcbiAgICBib3JkZXI6IDFweCBzb2xpZCAjY2VjZWNlO1xuICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gICAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gICAgZm9udC1zaXplOiAuOWVtO1xuICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmLCBcIkFwcGxlIENvbG9yIEVtb2ppXCIsIFwiU2Vnb2UgVUkgRW1vamlcIiwgXCJTZWdvZSBVSSBTeW1ib2xcIjtcbiAgICBwYWRkaW5nOiAuNzVlbTtcbiAgICBkaXNwbGF5OiBibG9jaztcbiAgICB3aWR0aDogMTAwJTtcbiAgICBmb250LXdlaWdodDogNDAwO1xuICAgIGxpbmUtaGVpZ2h0OiAxLjFlbTtcbiAgICBtYXJnaW46IDA7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3RleHRhcmVhJ10gdGV4dGFyZWE6OnBsYWNlaG9sZGVyIHtcbiAgICAgIGNvbG9yOiAjYThhOGE4OyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSd0ZXh0YXJlYSddIHRleHRhcmVhOmZvY3VzIHtcbiAgICAgIG91dGxpbmU6IDA7XG4gICAgICBib3JkZXI6IDFweCBzb2xpZCAjNDFiODgzOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2VsZWN0J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50IHtcbiAgICBwb3NpdGlvbjogcmVsYXRpdmU7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J3NlbGVjdCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudDo6YmVmb3JlIHtcbiAgICAgIGNvbnRlbnQ6ICcnO1xuICAgICAgd2lkdGg6IDA7XG4gICAgICBoZWlnaHQ6IDA7XG4gICAgICBib3JkZXI6IC4zZW0gc29saWQgdHJhbnNwYXJlbnQ7XG4gICAgICBib3JkZXItdG9wLWNvbG9yOiAjY2VjZWNlO1xuICAgICAgYm9yZGVyLWJvdHRvbS13aWR0aDogMDtcbiAgICAgIHRvcDogNTAlO1xuICAgICAgbWFyZ2luLXRvcDogLS4xZW07XG4gICAgICByaWdodDogMWVtO1xuICAgICAgcG9zaXRpb246IGFic29sdXRlOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2VsZWN0J10gc2VsZWN0IHtcbiAgICBhcHBlYXJhbmNlOiBub25lO1xuICAgIGJvcmRlci1yYWRpdXM6IC4zZW07XG4gICAgYm9yZGVyOiAxcHggc29saWQgI2NlY2VjZTtcbiAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICAgIGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAgIGZvbnQtc2l6ZTogLjllbTtcbiAgICBmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCBcIlNlZ29lIFVJXCIsIFJvYm90bywgSGVsdmV0aWNhLCBBcmlhbCwgc2Fucy1zZXJpZiwgXCJBcHBsZSBDb2xvciBFbW9qaVwiLCBcIlNlZ29lIFVJIEVtb2ppXCIsIFwiU2Vnb2UgVUkgU3ltYm9sXCI7XG4gICAgcGFkZGluZzogLjc1ZW07XG4gICAgZGlzcGxheTogYmxvY2s7XG4gICAgd2lkdGg6IDEwMCU7XG4gICAgZm9udC13ZWlnaHQ6IDQwMDtcbiAgICBsaW5lLWhlaWdodDogMS4xZW07XG4gICAgbWFyZ2luOiAwO1xuICAgIHBhZGRpbmctcmlnaHQ6IDJlbTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2VsZWN0J10gc2VsZWN0OjpwbGFjZWhvbGRlciB7XG4gICAgICBjb2xvcjogI2E4YThhODsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nc2VsZWN0J10gc2VsZWN0OmZvY3VzIHtcbiAgICAgIG91dGxpbmU6IDA7XG4gICAgICBib3JkZXI6IDFweCBzb2xpZCAjNDFiODgzOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdzZWxlY3QnXSBzZWxlY3RbZGF0YS1wbGFjZWhvbGRlci1zZWxlY3RlZF0ge1xuICAgICAgY29sb3I6ICNhOGE4YTg7IH1cbiAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPSdib3gnXSAuZm9ybXVsYXRlLWlucHV0LXdyYXBwZXIge1xuICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjsgfVxuICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCB7XG4gICAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dCB7XG4gICAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgICBsZWZ0OiAtOTk5cHg7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB3aWR0aDogMWVtO1xuICAgICAgaGVpZ2h0OiAxZW07XG4gICAgICBib3JkZXItcmFkaXVzOiAuMjVlbTtcbiAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNjZWNlY2U7XG4gICAgICBwb3NpdGlvbjogcmVsYXRpdmU7IH1cbiAgICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50LWRlY29yYXRvcjo6YmVmb3JlIHtcbiAgICAgICAgY29udGVudDogJyc7XG4gICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47XG4gICAgICAgIGJhY2tncm91bmQtcG9zaXRpb246IHJpZ2h0O1xuICAgICAgICB3aWR0aDogY2FsYygxMDAlIC0gLjEyNWVtKTtcbiAgICAgICAgaGVpZ2h0OiBjYWxjKDEwMCUgLSAuMTI1ZW0pO1xuICAgICAgICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICAgICAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgICAgIHRvcDogLjA2MjVlbTtcbiAgICAgICAgbGVmdDogLjA2MjVlbTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1lbGVtZW50W2RhdGEtdHlwZT1cInJhZGlvXCJdIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLXJhZGl1czogMWVtOyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudFtkYXRhLXR5cGU9XCJyYWRpb1wiXSAuZm9ybXVsYXRlLWlucHV0LWVsZW1lbnQtZGVjb3JhdG9yOjpiZWZvcmUge1xuICAgICAgICBib3JkZXItcmFkaXVzOiAxZW07XG4gICAgICAgIHdpZHRoOiBjYWxjKDEwMCUgLSAuNWVtKTtcbiAgICAgICAgaGVpZ2h0OiBjYWxjKDEwMCUgLSAuNWVtKTtcbiAgICAgICAgdG9wOiAuMjVlbTtcbiAgICAgICAgbGVmdDogLjI1ZW07IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwiY2hlY2tib3hcIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLWNvbG9yOiAjNDFiODgzOyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwiY2hlY2tib3hcIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3I6OmJlZm9yZSB7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7XG4gICAgICAgIG1hc2staW1hZ2U6IHVybCgnZGF0YTppbWFnZS9zdmcreG1sO3V0ZjgsPHN2ZyB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIgdmlld0JveD1cIjAgMCAxMDAgMTAwXCI+PHBhdGggZD1cIk04Ljc2LDU2LjJjLTYuMzgtNi4zNCwzLjI2LTE2LDkuNjQtOS42OUwzOCw2NS44OCw4MC41NiwyMy4yOWM2LjM4LTYuMzgsMTYuMDcsMy4zMiw5LjY5LDkuNjlMNDIuODQsODAuMzdhNi44Myw2LjgzLDAsMCwxLTkuNjUsMFpcIi8+PC9zdmc+Jyk7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLWNvbG9yOiAjNDFiODgzOyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dFt0eXBlPVwicmFkaW9cIl06Y2hlY2tlZCB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3I6OmJlZm9yZSB7XG4gICAgICAgIGJhY2tncm91bmQtY29sb3I6ICM0MWI4ODM7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249J2JveCddIC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudCBpbnB1dDpmb2N1cyB+IC5mb3JtdWxhdGUtaW5wdXQtZWxlbWVudC1kZWNvcmF0b3Ige1xuICAgICAgYm9yZGVyLWNvbG9yOiAjNDFiODgzOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1sYWJlbC0tYWZ0ZXIge1xuICAgIG1hcmdpbi1sZWZ0OiAuNWVtOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj0nYm94J10gLmZvcm11bGF0ZS1pbnB1dC1sYWJlbC0tYmVmb3JlIHtcbiAgICBtYXJnaW4tcmlnaHQ6IC41ZW07IH1cbiAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZ3JvdXBcIl0gPiAuZm9ybXVsYXRlLWlucHV0LXdyYXBwZXIgPiAuZm9ybXVsYXRlLWlucHV0LWxhYmVsIHtcbiAgICBtYXJnaW4tYm90dG9tOiAuNWVtOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYSB7XG4gICAgd2lkdGg6IDEwMCU7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIHBhZGRpbmc6IDJlbTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYVtkYXRhLWhhcy1maWxlc10ge1xuICAgICAgcGFkZGluZzogMDsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYSBpbnB1dCB7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICBhcHBlYXJhbmNlOiBub25lO1xuICAgICAgb3BhY2l0eTogMDtcbiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgIGxlZnQ6IDA7XG4gICAgICByaWdodDogMDtcbiAgICAgIGJvdHRvbTogMDtcbiAgICAgIHRvcDogMDtcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgaGVpZ2h0OiAxMDAlO1xuICAgICAgei1pbmRleDogNTsgfVxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrIHtcbiAgICAgIGJvcmRlci1yYWRpdXM6IC40ZW07XG4gICAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgICBwb2ludGVyLWV2ZW50czogbm9uZTtcbiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICBsZWZ0OiAwO1xuICAgICAgcmlnaHQ6IDA7XG4gICAgICB0b3A6IDA7XG4gICAgICBib3R0b206IDA7XG4gICAgICBib3JkZXI6IDJweCBkYXNoZWQgI2E4YThhODtcbiAgICAgIHotaW5kZXg6IDI7IH1cbiAgICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrOjpiZWZvcmUge1xuICAgICAgICBjb250ZW50OiAnJztcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2E4YThhODtcbiAgICAgICAgbWFzay1pbWFnZTogdXJsKCdkYXRhOmltYWdlL3N2Zyt4bWw7dXRmOCw8c3ZnIHhtbG5zPVwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiB2aWV3Qm94PVwiMCAwIDU4IDU4XCI+PHBhdGggZD1cIk0yOSw1OEEyOSwyOSwwLDEsMCwwLDI5LDI5LDI5LDAsMCwwLDI5LDU4Wk0yOSw0QTI1LDI1LDAsMSwxLDQsMjksMjUsMjUsMCwwLDEsMjksNFpcIi8+PHBvbHlnb24gcG9pbnRzPVwiMjcgMjIgMjcgNDQuNCAzMSA0NC40IDMxIDIyIDQxLjcgMzEuMSA0NC4zIDI4LjEgMjkgMTUgMTMuNyAyOC4xIDE2LjMgMzEuMSAyNyAyMlwiLz48L3N2Zz4nKTtcbiAgICAgICAgd2lkdGg6IDJlbTtcbiAgICAgICAgaGVpZ2h0OiAyZW07XG4gICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICAgICAgcG9pbnRlci1ldmVudHM6IG5vbmU7IH1cbiAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249XCJmaWxlXCJdIC5mb3JtdWxhdGUtaW5wdXQtdXBsb2FkLWFyZWEgaW5wdXQ6Zm9jdXMgfiAuZm9ybXVsYXRlLWlucHV0LXVwbG9hZC1hcmVhLW1hc2ssXG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZmlsZVwiXSAuZm9ybXVsYXRlLWlucHV0LXVwbG9hZC1hcmVhIGlucHV0OmhvdmVyIH4gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrLFxuICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYSBpbnB1dFtkYXRhLWlzLWRyYWctaG92ZXJdIH4gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrIHtcbiAgICAgIGJvcmRlci1jb2xvcjogIzQxYjg4MzsgfVxuICAgICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZmlsZVwiXSAuZm9ybXVsYXRlLWlucHV0LXVwbG9hZC1hcmVhIGlucHV0OmZvY3VzIH4gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrOjpiZWZvcmUsXG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249XCJmaWxlXCJdIC5mb3JtdWxhdGUtaW5wdXQtdXBsb2FkLWFyZWEgaW5wdXQ6aG92ZXIgfiAuZm9ybXVsYXRlLWlucHV0LXVwbG9hZC1hcmVhLW1hc2s6OmJlZm9yZSxcbiAgICAgIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYSBpbnB1dFtkYXRhLWlzLWRyYWctaG92ZXJdIH4gLmZvcm11bGF0ZS1pbnB1dC11cGxvYWQtYXJlYS1tYXNrOjpiZWZvcmUge1xuICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjNDFiODgzOyB9XG4gIC5mb3JtdWxhdGUtaW5wdXRbZGF0YS1jbGFzc2lmaWNhdGlvbj1cImZpbGVcIl0gLmZvcm11bGF0ZS1maWxlcyB7XG4gICAgbGlzdC1zdHlsZS10eXBlOiBub25lO1xuICAgIG1hcmdpbjogMDtcbiAgICBwYWRkaW5nOiAwOyB9XG4gICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZmlsZVwiXSAuZm9ybXVsYXRlLWZpbGVzIGxpIHtcbiAgICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgICBib3JkZXItcmFkaXVzOiAuM2VtO1xuICAgICAgYm9yZGVyOiAxcHggc29saWQgI2NlY2VjZTtcbiAgICAgIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gICAgICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgICAgIGZvbnQtc2l6ZTogLjllbTtcbiAgICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmLCBcIkFwcGxlIENvbG9yIEVtb2ppXCIsIFwiU2Vnb2UgVUkgRW1vamlcIiwgXCJTZWdvZSBVSSBTeW1ib2xcIjtcbiAgICAgIHBhZGRpbmc6IC43NWVtO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICB3aWR0aDogMTAwJTtcbiAgICAgIGZvbnQtd2VpZ2h0OiA0MDA7XG4gICAgICBsaW5lLWhlaWdodDogMS4xZW07XG4gICAgICBtYXJnaW46IDA7XG4gICAgICBkaXNwbGF5OiBibG9jaztcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgfVxuICAgICAgLmZvcm11bGF0ZS1pbnB1dFtkYXRhLWNsYXNzaWZpY2F0aW9uPVwiZmlsZVwiXSAuZm9ybXVsYXRlLWZpbGVzIGxpOjpwbGFjZWhvbGRlciB7XG4gICAgICAgIGNvbG9yOiAjYThhOGE4OyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249XCJmaWxlXCJdIC5mb3JtdWxhdGUtZmlsZXMgbGk6Zm9jdXMge1xuICAgICAgICBvdXRsaW5lOiAwO1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjNDFiODgzOyB9XG4gICAgICAuZm9ybXVsYXRlLWlucHV0W2RhdGEtY2xhc3NpZmljYXRpb249XCJmaWxlXCJdIC5mb3JtdWxhdGUtZmlsZXMgbGkgOjotd2Via2l0LXByb2dyZXNzLWJhciB7XG4gICAgICAgIGFwcGVhcmFuY2U6IG5vbmU7XG4gICAgICAgIGhlaWdodDogLjVlbTtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogLjVlbTtcbiAgICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsgfVxuIl19 */ \ No newline at end of file diff --git a/src/FileUpload.js b/src/FileUpload.js new file mode 100644 index 0000000..b13befe --- /dev/null +++ b/src/FileUpload.js @@ -0,0 +1,113 @@ +import nanoid from 'nanoid' + +/** + * The file upload class holds and represents a file’s upload state durring + * the upload flow. + */ +class FileUpload { + /** + * Create a file upload object. + * @param {FileList} fileList + * @param {object} context + */ + constructor (fileList, context, options) { + this.fileList = fileList + this.files = [] + this.options = options + this.setFileList(fileList) + this.context = context + } + + /** + * Produce an array of files and alert the callback. + * @param {FileList} + */ + setFileList (fileList) { + for (let i = 0; i < fileList.length; i++) { + const file = fileList.item(i) + this.files.push({ + progress: 0, + name: file.name || 'file-upload', + file: file, + uuid: nanoid() + }) + } + } + + /** + * Check if the file has an. + */ + hasUploader () { + return !!this.context.uploader + } + + uploaderIsAxios () { + if ( + this.hasUploader && + typeof this.hasUploader.request === 'function' && + typeof this.hasUploader.get === 'function' && + typeof this.hasUploader.delete === 'function' && + typeof this.hasUploader.post === 'function' + ) { + return true + } + return false + } + + /** + * Get a new uploader function. + */ + getUploader (...args) { + if (this.uploaderIsAxios()) { + const formData = new FormData() + formData.append(this.context.name || 'file', args[0]) + return this.uploader.post(this.context.uploadUrl, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress: progressEvent => { + args[1](Math.round((progressEvent.loaded * 100) / progressEvent.total)) + } + }) + .catch(err => args[2](err)) + } + return this.context.uploader(...args) + } + + /** + * Perform the file upload. + */ + upload () { + return new Promise((resolve, reject) => { + if (!this.hasUploader) { + return reject(new Error('No uploader has been defined')) + } + Promise.all(this.files.map(file => { + return this.getUploader( + file.file, + (progress) => { file.progress = progress }, + (error) => reject(new Error(error)), + this.options + ) + })) + .then(results => resolve(results)) + .catch(err => { throw new Error(err) }) + }) + } + + /** + * Get the files. + */ + getFileList () { + return this.fileList + } + + /** + * Get the files. + */ + getFiles () { + return this.files + } +} + +export default FileUpload diff --git a/src/Formulate.js b/src/Formulate.js index 91f03e0..1fb38a1 100644 --- a/src/Formulate.js +++ b/src/Formulate.js @@ -1,13 +1,16 @@ import library from './libs/library' import rules from './libs/rules' import en from './locales/en' +import FileUpload from './FileUpload' import isPlainObject from 'is-plain-object' +import fauxUploader from './libs/faux-uploader' import FormulateInput from './FormulateInput.vue' import FormulateForm from './FormulateForm.vue' import FormulateInputErrors from './FormulateInputErrors.vue' import FormulateInputGroup from './FormulateInputGroup.vue' import FormulateInputBox from './inputs/FormulateInputBox.vue' import FormulateInputText from './inputs/FormulateInputText.vue' +import FormulateInputFile from './inputs/FormulateInputFile.vue' import FormulateInputSelect from './inputs/FormulateInputSelect.vue' import FormulateInputSlider from './inputs/FormulateInputSlider.vue' import FormulateInputTextArea from './inputs/FormulateInputTextArea.vue' @@ -27,6 +30,7 @@ class Formulate { FormulateInputErrors, FormulateInputBox, FormulateInputText, + FormulateInputFile, FormulateInputGroup, FormulateInputSelect, FormulateInputSlider, @@ -35,6 +39,7 @@ class Formulate { library, rules, locale: 'en', + uploader: fauxUploader, locales: { en } @@ -122,6 +127,20 @@ class Formulate { } return 'This field does not have a valid value' } + + /** + * Get the file uploader. + */ + getUploader () { + return this.options.uploader || false + } + + /** + * Create a new instance of an upload. + */ + createUpload (fileList, context) { + return new FileUpload(fileList, context, this.options) + } } export default new Formulate() diff --git a/src/FormulateFiles.vue b/src/FormulateFiles.vue new file mode 100644 index 0000000..b7392d5 --- /dev/null +++ b/src/FormulateFiles.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/FormulateInput.vue b/src/FormulateInput.vue index ad24005..8f85a2a 100644 --- a/src/FormulateInput.vue +++ b/src/FormulateInput.vue @@ -136,6 +136,22 @@ export default { showErrors: { type: Boolean, default: false + }, + showImage: { + type: Boolean, + default: true + }, + uploadUrl: { + type: [String, Boolean], + default: false + }, + uploader: { + type: [Function, Object, Boolean], + default: false + }, + immediateUpload: { + type: Boolean, + default: true } }, data () { diff --git a/src/inputs/FormulateInputFile.vue b/src/inputs/FormulateInputFile.vue new file mode 100644 index 0000000..078a2a9 --- /dev/null +++ b/src/inputs/FormulateInputFile.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/inputs/FormulateInputSlider.vue b/src/inputs/FormulateInputSlider.vue index 6cc3e68..8b61fd8 100644 --- a/src/inputs/FormulateInputSlider.vue +++ b/src/inputs/FormulateInputSlider.vue @@ -16,7 +16,7 @@ import FormulateInputMixin from '../FormulateInputMixin' export default { - name: 'FormulateInputText', + name: 'FormulateInputSlider', mixins: [FormulateInputMixin] } diff --git a/src/libs/context.js b/src/libs/context.js index b2ff591..d45aeb0 100644 --- a/src/libs/context.js +++ b/src/libs/context.js @@ -19,6 +19,10 @@ export default { labelPosition: this.logicalLabelPosition, attributes: this.elementAttributes, blurHandler: blurHandler.bind(this), + showImage: this.showImage, + uploadUrl: this.uploadUrl, + uploader: this.uploader || this.$formulate.getUploader(), + immediateUpload: this.immediateUpload, ...this.typeContext }) }, diff --git a/src/libs/faux-uploader.js b/src/libs/faux-uploader.js new file mode 100644 index 0000000..672631c --- /dev/null +++ b/src/libs/faux-uploader.js @@ -0,0 +1,28 @@ +/** + * A fake uploader used by default. + * + * @param {File} file + * @param {function} progress + * @param {function} error + * @param {object} options + */ +export default function (file, progress, error, options) { + return new Promise((resolve, reject) => { + const totalTime = options.fauxUploaderDuration || 2000 + const start = performance.now() + const advance = () => setTimeout(() => { + const elapsed = performance.now() - start + const currentProgress = Math.min(100, Math.round(elapsed / totalTime * 100)) + progress(currentProgress) + if (currentProgress >= 100) { + resolve({ + url: 'http://via.placeholder.com/350x150.png', + name: file.name + }) + } else { + advance() + } + }, 20) + advance() + }) +} diff --git a/src/libs/library.js b/src/libs/library.js index 8596ab8..5596885 100644 --- a/src/libs/library.js +++ b/src/libs/library.js @@ -93,5 +93,16 @@ export default { select: { classification: 'select', component: 'FormulateInputSelect' + }, + + // === FILE TYPE + + file: { + classification: 'file', + component: 'FormulateInputFile' + }, + image: { + classification: 'file', + component: 'FormulateInputFile' } } diff --git a/src/libs/rules.js b/src/libs/rules.js index 7bb9567..4be177a 100644 --- a/src/libs/rules.js +++ b/src/libs/rules.js @@ -1,4 +1,5 @@ import isUrl from 'is-url' +import FileUpload from '../FileUpload' import { shallowEqualObjects, regexForFormat } from './utils' /** @@ -124,17 +125,18 @@ export default { /** * Check the maximum value of a particular. */ - max: function (value, minimum = 10) { + max: function (value, minimum = 10, force) { return Promise.resolve((() => { - minimum = Number(minimum) - if (!isNaN(value)) { - value = Number(value) - return value <= minimum - } - if (typeof value === 'string') { + if (Array.isArray(value)) { + minimum = !isNaN(minimum) ? Number(minimum) : minimum return value.length <= minimum } - if (Array.isArray(value)) { + if ((!isNaN(value) && force !== 'length') || force === 'value') { + value = !isNaN(value) ? Number(value) : value + return value <= minimum + } + if (typeof value === 'string' || (force === 'length')) { + value = !isNaN(value) ? value.toString() : value return value.length <= minimum } return false @@ -146,6 +148,12 @@ export default { */ mime: function (files, ...types) { return Promise.resolve((() => { + if (files instanceof FileUpload) { + if (files.hasUploader()) { + return false + } + files = files.getFiles() + } if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') { for (const i in files) { if (!types.includes(files[i].type)) { diff --git a/src/locales/en.js b/src/locales/en.js index 890d804..bbc3310 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -103,20 +103,28 @@ export default { * The maximum value allowed. */ max: function ({ name, value, args }) { - if (!isNaN(value)) { - return `${name} must be less than ${args[0]}.` + if (Array.isArray(value)) { + return `You may only select ${args[0]} ${name}.` } - return `${name} must be less than ${args[0]} characters long.` + const force = Array.isArray(args) && args[1] ? args[1] : false + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return `${s(name)} must be less than ${args[0]}.` + } + return `${s(name)} must be less than ${args[0]} characters long.` }, /** * The maximum value allowed. */ min: function ({ name, value, args }) { - if (!isNaN(value)) { - return `${name} must be more than ${args[0]}.` + if (Array.isArray(value)) { + return `You must select at least ${args[0]} ${name}.` } - return `${name} must be more than ${args[0]} characters long.` + const force = Array.isArray(args) && args[1] ? args[1] : false + if ((!isNaN(value) && force !== 'length') || force === 'value') { + return `${s(name)} must be more than ${args[0]}.` + } + return `${s(name)} must be more than ${args[0]} characters long.` }, /** diff --git a/test/Formulate.test.js b/test/Formulate.test.js index a0e6770..94d8dff 100644 --- a/test/Formulate.test.js +++ b/test/Formulate.test.js @@ -53,6 +53,7 @@ test('installs on vue instance', () => { 'FormulateInputErrors', 'FormulateInputBox', 'FormulateInputText', + 'FormulateInputFile', 'FormulateInputGroup', 'FormulateInputSelect', 'FormulateInputSlider', diff --git a/test/rules.test.js b/test/rules.test.js index 3679967..3e6bb91 100644 --- a/test/rules.test.js +++ b/test/rules.test.js @@ -331,11 +331,19 @@ describe('max', () => { it('passes when a array length', async () => expect(await rules.max(Array(6), '6')).toBe(true)) + it('passes when forced to validate on length', async () => expect(await rules.max(10, 3, 'length')).toBe(true)) + + it('passes when forced to validate string on value', async () => expect(await rules.max('b', 'e', 'value')).toBe(true)) + it('fails when a array length', async () => expect(await rules.max(Array(6), '5')).toBe(false)) it('fails when a string length', async () => expect(await rules.max('bar', 2)).toBe(false)) it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false)) + + it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false)) + + it('fails when forced to validate on length', async () => expect(await rules.max(-10, '1', 'length')).toBe(false)) }) /** diff --git a/themes/snow/_inputs.scss b/themes/snow/_inputs.scss index b8b0a7d..ccde311 100644 --- a/themes/snow/_inputs.scss +++ b/themes/snow/_inputs.scss @@ -55,6 +55,37 @@ input { @include baseinput; } + + input[type="color"] { + height: 1.1em; + box-sizing: content-box; + + @mixin color-swatch { + display: block; + height: 1em; + border-radius: .2em; + border: 0; + flex: auto; + } + + &::-webkit-color-swatch-wrapper { + padding: 0 0 0 1.5em; + display: flex; + align-items: center; + background-image: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-size: .9em .9em; + background-position: left .1em; + } + + &::-webkit-color-swatch { + @include color-swatch; + } + + &::-moz-color-swatch { + @include color-swatch; + } + } } // Slider inputs @@ -207,7 +238,8 @@ border-color: $formulate-green; &::before { - background-image: url('data:image/svg+xml;utf8,'); + background-color: $formulate-green; + mask-image: url('data:image/svg+xml;utf8,'); } } } @@ -245,4 +277,97 @@ } } } + + + // File inputs +// ----------------------------------------------------------------------------- + + &[data-classification="file"] { + .formulate-input-upload-area { + width: 100%; + position: relative; + padding: 2em; + + &[data-has-files] { + padding: 0; + } + + input { + cursor: pointer; + appearance: none; + opacity: 0; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 5; + } + + &-mask { + border-radius: .4em; + position: absolute; + pointer-events: none; + position: absolute; + display: flex; + justify-content: center; + align-items: center; + left: 0; + right: 0; + top: 0; + bottom: 0; + border: 2px dashed $formulate-gray-dd; + z-index: 2; + + &::before { + content: ''; + background-color: $formulate-gray-dd; + mask-image: url('data:image/svg+xml;utf8,'); + width: 2em; + height: 2em; + position: absolute; + pointer-events: none; + } + } + + input:focus, + input:hover, + input[data-is-drag-hover] { + & ~ .formulate-input-upload-area-mask { + border-color: $formulate-green; + + &::before { + background-color: $formulate-green; + } + } + } + } + + .formulate-files { + list-style-type: none; + margin: 0; + padding: 0; + + li { + @include baseinput; + display: block; + width: 100%; + display: flex; + justify-content: space-between; + + @mixin progress { + appearance: none; + height: .5em; + border-radius: .5em; + overflow: hidden; + } + + ::-webkit-progress-bar { + @include progress; + } + } + } + } } diff --git a/themes/snow/_variables.scss b/themes/snow/_variables.scss index 4b4ad35..80c6c7a 100644 --- a/themes/snow/_variables.scss +++ b/themes/snow/_variables.scss @@ -37,6 +37,8 @@ $formulate-yellow-l: #fff8d2; display: block; width: 100%; font-weight: 400; + line-height: 1.1em; + margin: 0; &::placeholder { color: $formulate-gray-dd;