From f19d9e6fa74c05e431f6d6fae22c972175d289c9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 6 Aug 2024 00:27:12 +0200 Subject: [PATCH] netteForms: removed distribution files (BC break) --- src/assets/netteForms.js | 573 ----------------------------------- src/assets/netteForms.min.js | 7 - 2 files changed, 580 deletions(-) delete mode 100644 src/assets/netteForms.js delete mode 100644 src/assets/netteForms.min.js diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js deleted file mode 100644 index 4dffc008..00000000 --- a/src/assets/netteForms.js +++ /dev/null @@ -1,573 +0,0 @@ -/*! - * NetteForms - simple form validation. - * - * This file is part of the Nette Framework (https://nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Nette?.noInit ? (global.Nette = factory()) : (global.Nette = factory()).initOnLoad()); -})(this, (function () { 'use strict'; - - class Validators { - filled(elem, arg, val) { - return val !== '' && val !== false && val !== null - && (!Array.isArray(val) || val.length > 0) - && (!(val instanceof FileList) || val.length > 0); - } - blank(elem, arg, val) { - return !this.filled(elem, arg, val); - } - valid(elem, arg) { - return arg.validateControl(elem, undefined, true); - } - equal(elem, arg, val) { - if (arg === undefined) { - return null; - } - let toString = (val) => { - if (typeof val === 'number' || typeof val === 'string') { - return '' + val; - } - else { - return val === true ? '1' : ''; - } - }; - let vals = Array.isArray(val) ? val : [val]; - let args = Array.isArray(arg) ? arg : [arg]; - loop: for (let a of vals) { - for (let b of args) { - if (toString(a) === toString(b)) { - continue loop; - } - } - return false; - } - return vals.length > 0; - } - notEqual(elem, arg, val) { - return arg === undefined ? null : !this.equal(elem, arg, val); - } - minLength(elem, arg, val) { - val = typeof val === 'number' ? val.toString() : val; - return val.length >= arg; - } - maxLength(elem, arg, val) { - val = typeof val === 'number' ? val.toString() : val; - return val.length <= arg; - } - length(elem, arg, val) { - val = typeof val === 'number' ? val.toString() : val; - arg = Array.isArray(arg) ? arg : [arg, arg]; - return ((arg[0] === null || val.length >= arg[0]) - && (arg[1] === null || val.length <= arg[1])); - } - email(elem, arg, val) { - return (/^("([ !#-[\]-~]|\\[ -~])+"|[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*)@([0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)+[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?$/i).test(val); - } - url(elem, arg, val, newValue) { - if (!(/^[a-z\d+.-]+:/).test(val)) { - val = 'https://' + val; - } - if ((/^https?:\/\/((([-_0-9a-z\u00C0-\u02FF\u0370-\u1EFF]+\.)*[0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)?[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]{3,39}\])(:\d{1,5})?(\/\S*)?$/i).test(val)) { - newValue.value = val; - return true; - } - return false; - } - regexp(elem, arg, val) { - let parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false; - try { - return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val); - } - catch { - return null; - } - } - pattern(elem, arg, val, newValue, caseInsensitive) { - if (typeof arg !== 'string') { - return null; - } - try { - let regExp; - try { - regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'ui' : 'u'); - } - catch { - regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : ''); - } - return val instanceof FileList - ? Array.from(val).every((file) => regExp.test(file.name)) - : regExp.test(val); - } - catch { - return null; - } - } - patternCaseInsensitive(elem, arg, val) { - return this.pattern(elem, arg, val, null, true); - } - numeric(elem, arg, val) { - return (/^[0-9]+$/).test(val); - } - integer(elem, arg, val, newValue) { - if ((/^-?[0-9]+$/).test(val)) { - newValue.value = parseFloat(val); - return true; - } - return false; - } - float(elem, arg, val, newValue) { - val = val.replace(/ +/g, '').replace(/,/g, '.'); - if ((/^-?[0-9]*\.?[0-9]+$/).test(val)) { - newValue.value = parseFloat(val); - return true; - } - return false; - } - min(elem, arg, val) { - if (Number.isFinite(arg)) { - val = parseFloat(val); - } - return val >= arg; - } - max(elem, arg, val) { - if (Number.isFinite(arg)) { - val = parseFloat(val); - } - return val <= arg; - } - range(elem, arg, val) { - if (!Array.isArray(arg)) { - return null; - } - else if (elem.type === 'time' && arg[0] > arg[1]) { - return val >= arg[0] || val <= arg[1]; - } - return (arg[0] === null || this.min(elem, arg[0], val)) - && (arg[1] === null || this.max(elem, arg[1], val)); - } - submitted(elem) { - return elem.form['nette-submittedBy'] === elem; - } - fileSize(elem, arg, val) { - return Array.from(val).every((file) => file.size <= arg); - } - mimeType(elem, args, val) { - let parts = []; - args = Array.isArray(args) ? args : [args]; - args.forEach((arg) => parts.push('^' + arg.replace(/([^\w])/g, '\\$1').replace('\\*', '.*') + '$')); - let re = new RegExp(parts.join('|')); - return Array.from(val).every((file) => !file.type || re.test(file.type)); - } - image(elem, arg, val) { - return this.mimeType(elem, arg ?? ['image/gif', 'image/png', 'image/jpeg', 'image/webp'], val); - } - static(elem, arg) { - return arg; - } - } - - class FormValidator { - formErrors = []; - validators = new Validators; - #preventFiltering = {}; - #formToggles = {}; - #toggleListeners = new WeakMap; - #getFormElement(form, name) { - let res = form.elements.namedItem(name); - return (res instanceof RadioNodeList ? res[0] : res); - } - #expandRadioElement(elem) { - let res = elem.form.elements.namedItem(elem.name); - return (res instanceof RadioNodeList ? Array.from(res) : [res]); - } - /** - * Function to execute when the DOM is fully loaded. - */ - #onDocumentReady(callback) { - if (document.readyState !== 'loading') { - callback.call(this); - } - else { - document.addEventListener('DOMContentLoaded', callback); - } - } - /** - * Returns the value of form element. - */ - getValue(elem) { - if (elem instanceof HTMLInputElement) { - if (elem.type === 'radio') { - return this.#expandRadioElement(elem) - .find((input) => input.checked) - ?.value ?? null; - } - else if (elem.type === 'file') { - return elem.files; - } - else if (elem.type === 'checkbox') { - return elem.name.endsWith('[]') // checkbox list - ? this.#expandRadioElement(elem) - .filter((input) => input.checked) - .map((input) => input.value) - : elem.checked; - } - else { - return elem.value.trim(); - } - } - else if (elem instanceof HTMLSelectElement) { - return elem.multiple - ? Array.from(elem.selectedOptions, (option) => option.value) - : elem.selectedOptions[0]?.value ?? null; - } - else if (elem instanceof HTMLTextAreaElement) { - return elem.value; - } - else if (elem instanceof RadioNodeList) { - return this.getValue(elem[0]); - } - else { - return null; - } - } - /** - * Returns the effective value of form element. - */ - getEffectiveValue(elem, filter = false) { - let val = this.getValue(elem); - if (val === elem.getAttribute('data-nette-empty-value')) { - val = ''; - } - if (filter && this.#preventFiltering[elem.name] === undefined) { - this.#preventFiltering[elem.name] = true; - let ref = { value: val }; - this.validateControl(elem, undefined, true, ref); - val = ref.value; - delete this.#preventFiltering[elem.name]; - } - return val; - } - /** - * Validates form element against given rules. - */ - validateControl(elem, rules, onlyCheck = false, value, emptyOptional) { - rules ??= JSON.parse(elem.getAttribute('data-nette-rules') ?? '[]'); - value ??= { value: this.getEffectiveValue(elem) }; - emptyOptional ??= !this.validateRule(elem, ':filled', null, value); - for (let rule of rules) { - let op = rule.op.match(/(~)?([^?]+)/), curElem = rule.control ? this.#getFormElement(elem.form, rule.control) : elem; - rule.neg = !!op[1]; - rule.op = op[2]; - rule.condition = !!rule.rules; - if (!curElem) { - continue; - } - else if (emptyOptional && !rule.condition && rule.op !== ':filled') { - continue; - } - let success = this.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); - if (success === null) { - continue; - } - else if (rule.neg) { - success = !success; - } - if (rule.condition && success) { - if (!this.validateControl(elem, rule.rules, onlyCheck, value, rule.op === ':blank' ? false : emptyOptional)) { - return false; - } - } - else if (!rule.condition && !success) { - if (this.isDisabled(curElem)) { - continue; - } - if (!onlyCheck) { - let arr = Array.isArray(rule.arg) ? rule.arg : [rule.arg], message = rule.msg.replace(/%(value|\d+)/g, (foo, m) => this.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control))); - this.addError(curElem, message); - } - return false; - } - } - return true; - } - /** - * Validates whole form. - */ - validateForm(sender, onlyCheck = false) { - let form = sender.form ?? sender, scope; - this.formErrors = []; - if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) { - let scopeArr = JSON.parse(form['nette-submittedBy'].getAttribute('data-nette-validation-scope') ?? '[]'); - if (scopeArr.length) { - scope = new RegExp('^(' + scopeArr.join('-|') + '-)'); - } - else { - this.showFormErrors(form, []); - return true; - } - } - for (let elem of form.elements) { - if (elem.willValidate && elem.validity.badInput) { - elem.reportValidity(); - return false; - } - } - for (let elem of form.elements) { - if (elem.getAttribute('data-nette-rules') - && (!scope || elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) - && !this.isDisabled(elem) - && !this.validateControl(elem, undefined, onlyCheck) - && !this.formErrors.length) { - return false; - } - } - let success = !this.formErrors.length; - this.showFormErrors(form, this.formErrors); - return success; - } - /** - * Check if input is disabled. - */ - isDisabled(elem) { - if (elem.type === 'radio') { - return this.#expandRadioElement(elem) - .every((input) => input.disabled); - } - return elem.disabled; - } - /** - * Adds error message to the queue. - */ - addError(elem, message) { - this.formErrors.push({ - element: elem, - message: message, - }); - } - /** - * Display error messages. - */ - showFormErrors(form, errors) { - let messages = [], focusElem; - for (let error of errors) { - if (messages.indexOf(error.message) < 0) { - messages.push(error.message); - focusElem ??= error.element; - } - } - if (messages.length) { - this.showModal(messages.join('\n'), () => { - focusElem?.focus(); - }); - } - } - /** - * Display modal window. - */ - showModal(message, onclose) { - let dialog = document.createElement('dialog'); - if (!dialog.showModal) { - alert(message); - onclose(); - return; - } - let style = document.createElement('style'); - style.innerText = '.netteFormsModal { text-align: center; margin: auto; border: 2px solid black; padding: 1rem } .netteFormsModal button { padding: .1em 2em }'; - let button = document.createElement('button'); - button.innerText = 'OK'; - button.onclick = () => { - dialog.remove(); - onclose(); - }; - dialog.setAttribute('class', 'netteFormsModal'); - dialog.innerText = message + '\n\n'; - dialog.append(style, button); - document.body.append(dialog); - dialog.showModal(); - } - /** - * Validates single rule. - */ - validateRule(elem, op, arg, value) { - if (elem.validity.badInput) { - return op === ':filled'; - } - value ??= { value: this.getEffectiveValue(elem, true) }; - let method = op.charAt(0) === ':' ? op.substring(1) : op; - method = method.replace('::', '_').replaceAll('\\', ''); - let args = Array.isArray(arg) ? arg : [arg]; - args = args.map((arg) => { - if (arg?.control) { - let control = this.#getFormElement(elem.form, arg.control); - return control === elem ? value.value : this.getEffectiveValue(control, true); - } - return arg; - }); - if (method === 'valid') { - args[0] = this; // todo - } - return this.validators[method] - ? this.validators[method](elem, Array.isArray(arg) ? args : args[0], value.value, value) - : null; - } - /** - * Process all toggles in form. - */ - toggleForm(form, event) { - this.#formToggles = {}; - for (let elem of Array.from(form.elements)) { - if (elem.getAttribute('data-nette-rules')) { - this.toggleControl(elem, undefined, null, !event); - } - } - for (let i in this.#formToggles) { - this.toggle(i, this.#formToggles[i].state, this.#formToggles[i].elem, event); - } - } - /** - * Process toggles on form element. - */ - toggleControl(elem, rules, success = null, firsttime = false, value, emptyOptional) { - rules ??= JSON.parse(elem.getAttribute('data-nette-rules') ?? '[]'); - value ??= { value: this.getEffectiveValue(elem) }; - emptyOptional ??= !this.validateRule(elem, ':filled', null, value); - let has = false, curSuccess; - for (let rule of rules) { - let op = rule.op.match(/(~)?([^?]+)/), curElem = rule.control ? this.#getFormElement(elem.form, rule.control) : elem; - rule.neg = !!op[1]; - rule.op = op[2]; - rule.condition = !!rule.rules; - if (!curElem) { - continue; - } - else if (emptyOptional && !rule.condition && rule.op !== ':filled') { - continue; - } - curSuccess = success; - if (success !== false) { - curSuccess = this.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined); - if (curSuccess === null) { - continue; - } - else if (rule.neg) { - curSuccess = !curSuccess; - } - if (!rule.condition) { - success = curSuccess; - } - } - if ((rule.condition && this.toggleControl(elem, rule.rules, curSuccess, firsttime, value, rule.op === ':blank' ? false : emptyOptional)) || rule.toggle) { - has = true; - if (firsttime) { - this.#expandRadioElement(curElem) - .filter((el) => !this.#toggleListeners.has(el)) - .forEach((el) => { - el.addEventListener('change', (e) => this.toggleForm(elem.form, e)); - this.#toggleListeners.set(el, null); - }); - } - for (let id in rule.toggle ?? {}) { - this.#formToggles[id] ??= { elem: elem, state: false }; - this.#formToggles[id].state ||= rule.toggle[id] ? !!curSuccess : !curSuccess; - } - } - } - return has; - } - /** - * Displays or hides HTML element. - */ - toggle(selector, visible, srcElement, event) { - if (/^\w[\w.:-]*$/.test(selector)) { // id - selector = '#' + selector; - } - Array.from(document.querySelectorAll(selector)) - .forEach((elem) => elem.hidden = !visible); - } - /** - * Compact checkboxes - */ - compactCheckboxes(form, formData) { - let values = {}; - for (let elem of form.elements) { - if (elem instanceof HTMLInputElement && elem.type === 'checkbox' && elem.name.endsWith('[]') && elem.checked && !elem.disabled) { - formData.delete(elem.name); - values[elem.name] ??= []; - values[elem.name].push(elem.value); - } - } - for (let name in values) { - formData.set(name.substring(0, name.length - 2), values[name].join(',')); - } - } - /** - * Setup handlers. - */ - initForm(form) { - if (form.method === 'get' && form.hasAttribute('data-nette-compact')) { - form.addEventListener('formdata', (e) => this.compactCheckboxes(form, e.formData)); - } - if (!Array.from(form.elements).some((elem) => elem.getAttribute('data-nette-rules'))) { - return; - } - this.toggleForm(form); - if (form.noValidate) { - return; - } - form.noValidate = true; - form.addEventListener('submit', (e) => { - if (!this.validateForm(form)) { - e.stopPropagation(); - e.preventDefault(); - } - }); - form.addEventListener('reset', () => { - setTimeout(() => this.toggleForm(form)); - }); - } - initOnLoad() { - this.#onDocumentReady(() => { - Array.from(document.forms) - .forEach((form) => this.initForm(form)); - document.body.addEventListener('click', (e) => { - let target = e.target; - while (target) { - if (target.form && target.type in { submit: 1, image: 1 }) { - target.form['nette-submittedBy'] = target; - break; - } - target = target.parentNode; - } - }); - }); - } - } - - let webalizeTable = { \u00e1: 'a', \u00e4: 'a', \u010d: 'c', \u010f: 'd', \u00e9: 'e', \u011b: 'e', \u00ed: 'i', \u013e: 'l', \u0148: 'n', \u00f3: 'o', \u00f4: 'o', \u0159: 'r', \u0161: 's', \u0165: 't', \u00fa: 'u', \u016f: 'u', \u00fd: 'y', \u017e: 'z' }; - /** - * Converts string to web safe characters [a-z0-9-] text. - * @param {string} s - * @return {string} - */ - function webalize(s) { - s = s.toLowerCase(); - let res = ''; - for (let i = 0; i < s.length; i++) { - let ch = webalizeTable[s.charAt(i)]; - res += ch ? ch : s.charAt(i); - } - return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); - } - - var version = "3.5.0"; - - let nette = new FormValidator; - nette.version = version; - nette.webalize = webalize; - - return nette; - -})); diff --git a/src/assets/netteForms.min.js b/src/assets/netteForms.min.js deleted file mode 100644 index ed3025cd..00000000 --- a/src/assets/netteForms.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * NetteForms - simple form validation. - * - * This file is part of the Nette Framework (https://nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self,e.Nette?.noInit?e.Nette=t():(e.Nette=t()).initOnLoad())}(this,(function(){"use strict";class e{filled(e,t,r){return""!==r&&!1!==r&&null!==r&&(!Array.isArray(r)||r.length>0)&&(!(r instanceof FileList)||r.length>0)}blank(e,t,r){return!this.filled(e,t,r)}valid(e,t){return t.validateControl(e,void 0,!0)}equal(e,t,r){if(void 0===t)return null;let n=e=>"number"==typeof e||"string"==typeof e?""+e:!0===e?"1":"",i=Array.isArray(r)?r:[r],l=Array.isArray(t)?t:[t];e:for(let e of i){for(let t of l)if(n(e)===n(t))continue e;return!1}return i.length>0}notEqual(e,t,r){return void 0===t?null:!this.equal(e,t,r)}minLength(e,t,r){return(r="number"==typeof r?r.toString():r).length>=t}maxLength(e,t,r){return(r="number"==typeof r?r.toString():r).length<=t}length(e,t,r){return r="number"==typeof r?r.toString():r,(null===(t=Array.isArray(t)?t:[t,t])[0]||r.length>=t[0])&&(null===t[1]||r.length<=t[1])}email(e,t,r){return/^("([ !#-[\]-~]|\\[ -~])+"|[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*)@([0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)+[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?$/i.test(r)}url(e,t,r,n){return/^[a-z\d+.-]+:/.test(r)||(r="https://"+r),!!/^https?:\/\/((([-_0-9a-z\u00C0-\u02FF\u0370-\u1EFF]+\.)*[0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)?[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]{3,39}\])(:\d{1,5})?(\/\S*)?$/i.test(r)&&(n.value=r,!0)}regexp(e,t,r){let n="string"==typeof t&&t.match(/^\/(.*)\/([imu]*)$/);try{return n&&new RegExp(n[1],n[2].replace("u","")).test(r)}catch{return null}}pattern(e,t,r,n,i){if("string"!=typeof t)return null;try{let e;try{e=new RegExp("^(?:"+t+")$",i?"ui":"u")}catch{e=new RegExp("^(?:"+t+")$",i?"i":"")}return r instanceof FileList?Array.from(r).every((t=>e.test(t.name))):e.test(r)}catch{return null}}patternCaseInsensitive(e,t,r){return this.pattern(e,t,r,null,!0)}numeric(e,t,r){return/^[0-9]+$/.test(r)}integer(e,t,r,n){return!!/^-?[0-9]+$/.test(r)&&(n.value=parseFloat(r),!0)}float(e,t,r,n){return r=r.replace(/ +/g,"").replace(/,/g,"."),!!/^-?[0-9]*\.?[0-9]+$/.test(r)&&(n.value=parseFloat(r),!0)}min(e,t,r){return Number.isFinite(t)&&(r=parseFloat(r)),r>=t}max(e,t,r){return Number.isFinite(t)&&(r=parseFloat(r)),r<=t}range(e,t,r){return Array.isArray(t)?"time"===e.type&&t[0]>t[1]?r>=t[0]||r<=t[1]:(null===t[0]||this.min(e,t[0],r))&&(null===t[1]||this.max(e,t[1],r)):null}submitted(e){return e.form["nette-submittedBy"]===e}fileSize(e,t,r){return Array.from(r).every((e=>e.size<=t))}mimeType(e,t,r){let n=[];(t=Array.isArray(t)?t:[t]).forEach((e=>n.push("^"+e.replace(/([^\w])/g,"\\$1").replace("\\*",".*")+"$")));let i=new RegExp(n.join("|"));return Array.from(r).every((e=>!e.type||i.test(e.type)))}image(e,t,r){return this.mimeType(e,t??["image/gif","image/png","image/jpeg","image/webp"],r)}static(e,t){return t}}let t={"á":"a","ä":"a","č":"c","ď":"d","é":"e","ě":"e","í":"i","ľ":"l","ň":"n","ó":"o","ô":"o","ř":"r","š":"s","ť":"t","ú":"u","ů":"u","ý":"y","ž":"z"};let r=new class{formErrors=[];validators=new e;#e={};#t={};#r=new WeakMap;#n(e,t){let r=e.elements.namedItem(t);return r instanceof RadioNodeList?r[0]:r}#i(e){let t=e.form.elements.namedItem(e.name);return t instanceof RadioNodeList?Array.from(t):[t]}#l(e){"loading"!==document.readyState?e.call(this):document.addEventListener("DOMContentLoaded",e)}getValue(e){return e instanceof HTMLInputElement?"radio"===e.type?this.#i(e).find((e=>e.checked))?.value??null:"file"===e.type?e.files:"checkbox"===e.type?e.name.endsWith("[]")?this.#i(e).filter((e=>e.checked)).map((e=>e.value)):e.checked:e.value.trim():e instanceof HTMLSelectElement?e.multiple?Array.from(e.selectedOptions,(e=>e.value)):e.selectedOptions[0]?.value??null:e instanceof HTMLTextAreaElement?e.value:e instanceof RadioNodeList?this.getValue(e[0]):null}getEffectiveValue(e,t=!1){let r=this.getValue(e);if(r===e.getAttribute("data-nette-empty-value")&&(r=""),t&&void 0===this.#e[e.name]){this.#e[e.name]=!0;let t={value:r};this.validateControl(e,void 0,!0,t),r=t.value,delete this.#e[e.name]}return r}validateControl(e,t,r=!1,n,i){t??=JSON.parse(e.getAttribute("data-nette-rules")??"[]"),n??={value:this.getEffectiveValue(e)},i??=!this.validateRule(e,":filled",null,n);for(let l of t){let t=l.op.match(/(~)?([^?]+)/),a=l.control?this.#n(e.form,l.control):e;if(l.neg=!!t[1],l.op=t[2],l.condition=!!l.rules,!a)continue;if(i&&!l.condition&&":filled"!==l.op)continue;let o=this.validateRule(a,l.op,l.arg,e===a?n:void 0);if(null!==o)if(l.neg&&(o=!o),l.condition&&o){if(!this.validateControl(e,l.rules,r,n,":blank"!==l.op&&i))return!1}else if(!l.condition&&!o){if(this.isDisabled(a))continue;if(!r){let t=Array.isArray(l.arg)?l.arg:[l.arg],r=l.msg.replace(/%(value|\d+)/g,((r,n)=>this.getValue("value"===n?a:e.form.elements.namedItem(t[n].control))));this.addError(a,r)}return!1}}return!0}validateForm(e,t=!1){let r,n=e.form??e;if(this.formErrors=[],n["nette-submittedBy"]&&null!==n["nette-submittedBy"].getAttribute("formnovalidate")){let e=JSON.parse(n["nette-submittedBy"].getAttribute("data-nette-validation-scope")??"[]");if(!e.length)return this.showFormErrors(n,[]),!0;r=new RegExp("^("+e.join("-|")+"-)")}for(let e of n.elements)if(e.willValidate&&e.validity.badInput)return e.reportValidity(),!1;for(let e of n.elements)if(e.getAttribute("data-nette-rules")&&(!r||e.name.replace(/]\[|\[|]|$/g,"-").match(r))&&!this.isDisabled(e)&&!this.validateControl(e,void 0,t)&&!this.formErrors.length)return!1;let i=!this.formErrors.length;return this.showFormErrors(n,this.formErrors),i}isDisabled(e){return"radio"===e.type?this.#i(e).every((e=>e.disabled)):e.disabled}addError(e,t){this.formErrors.push({element:e,message:t})}showFormErrors(e,t){let r,n=[];for(let e of t)n.indexOf(e.message)<0&&(n.push(e.message),r??=e.element);n.length&&this.showModal(n.join("\n"),(()=>{r?.focus()}))}showModal(e,t){let r=document.createElement("dialog");if(!r.showModal)return alert(e),void t();let n=document.createElement("style");n.innerText=".netteFormsModal { text-align: center; margin: auto; border: 2px solid black; padding: 1rem } .netteFormsModal button { padding: .1em 2em }";let i=document.createElement("button");i.innerText="OK",i.onclick=()=>{r.remove(),t()},r.setAttribute("class","netteFormsModal"),r.innerText=e+"\n\n",r.append(n,i),document.body.append(r),r.showModal()}validateRule(e,t,r,n){if(e.validity.badInput)return":filled"===t;n??={value:this.getEffectiveValue(e,!0)};let i=":"===t.charAt(0)?t.substring(1):t;i=i.replace("::","_").replaceAll("\\","");let l=Array.isArray(r)?r:[r];return l=l.map((t=>{if(t?.control){let r=this.#n(e.form,t.control);return r===e?n.value:this.getEffectiveValue(r,!0)}return t})),"valid"===i&&(l[0]=this),this.validators[i]?this.validators[i](e,Array.isArray(r)?l:l[0],n.value,n):null}toggleForm(e,t){this.#t={};for(let r of Array.from(e.elements))r.getAttribute("data-nette-rules")&&this.toggleControl(r,void 0,null,!t);for(let e in this.#t)this.toggle(e,this.#t[e].state,this.#t[e].elem,t)}toggleControl(e,t,r=null,n=!1,i,l){t??=JSON.parse(e.getAttribute("data-nette-rules")??"[]"),i??={value:this.getEffectiveValue(e)},l??=!this.validateRule(e,":filled",null,i);let a,o=!1;for(let s of t){let t=s.op.match(/(~)?([^?]+)/),u=s.control?this.#n(e.form,s.control):e;if(s.neg=!!t[1],s.op=t[2],s.condition=!!s.rules,u&&(!l||s.condition||":filled"===s.op)){if(a=r,!1!==r){if(a=this.validateRule(u,s.op,s.arg,e===u?i:void 0),null===a)continue;s.neg&&(a=!a),s.condition||(r=a)}if(s.condition&&this.toggleControl(e,s.rules,a,n,i,":blank"!==s.op&&l)||s.toggle){o=!0,n&&this.#i(u).filter((e=>!this.#r.has(e))).forEach((t=>{t.addEventListener("change",(t=>this.toggleForm(e.form,t))),this.#r.set(t,null)}));for(let t in s.toggle??{})this.#t[t]??={elem:e,state:!1},this.#t[t].state||=s.toggle[t]?!!a:!a}}}return o}toggle(e,t,r,n){/^\w[\w.:-]*$/.test(e)&&(e="#"+e),Array.from(document.querySelectorAll(e)).forEach((e=>e.hidden=!t))}compactCheckboxes(e,t){let r={};for(let n of e.elements)n instanceof HTMLInputElement&&"checkbox"===n.type&&n.name.endsWith("[]")&&n.checked&&!n.disabled&&(t.delete(n.name),r[n.name]??=[],r[n.name].push(n.value));for(let e in r)t.set(e.substring(0,e.length-2),r[e].join(","))}initForm(e){"get"===e.method&&e.hasAttribute("data-nette-compact")&&e.addEventListener("formdata",(t=>this.compactCheckboxes(e,t.formData))),Array.from(e.elements).some((e=>e.getAttribute("data-nette-rules")))&&(this.toggleForm(e),e.noValidate||(e.noValidate=!0,e.addEventListener("submit",(t=>{this.validateForm(e)||(t.stopPropagation(),t.preventDefault())})),e.addEventListener("reset",(()=>{setTimeout((()=>this.toggleForm(e)))}))))}initOnLoad(){this.#l((()=>{Array.from(document.forms).forEach((e=>this.initForm(e))),document.body.addEventListener("click",(e=>{let t=e.target;for(;t;){if(t.form&&t.type in{submit:1,image:1}){t.form["nette-submittedBy"]=t;break}t=t.parentNode}}))}))}};return r.version="3.5.0",r.webalize=function(e){e=e.toLowerCase();let r="";for(let n=0;n