From f17ca113d0a89def0da651b7a0dd736b66378fad Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 27 Apr 2024 13:37:01 +0200 Subject: [PATCH] netteForms: modernized code --- src/assets/netteForms.js | 220 ++++++++++++++------------------------- 1 file changed, 79 insertions(+), 141 deletions(-) diff --git a/src/assets/netteForms.js b/src/assets/netteForms.js index 9370e3cb..d4947e4c 100644 --- a/src/assets/netteForms.js +++ b/src/assets/netteForms.js @@ -79,56 +79,38 @@ * @return {*} */ Nette.getValue = function(elem) { - if (!elem) { - return null; - - } else if (!elem.tagName) { // RadioNodeList - return elem[0] ? Nette.getValue(elem[0]) : null; - - } else if (elem.type === 'radio') { - for (let input of expandRadioElement(elem)) { - if (input.checked) { - return input.value; - } - } - return null; - - } else if (elem.type === 'file') { - return elem.files || elem.value; - - } else if (elem.tagName.toLowerCase() === 'select') { - let index = elem.selectedIndex, - options = elem.options, - values = []; - - if (elem.type === 'select-one') { - return index < 0 ? null : options[index].value; - } - - for (let i = 0; i < options.length; i++) { - if (options[i].selected) { - values.push(options[i].value); - } - } - return values; - - } else if (elem.name && elem.name.endsWith('[]')) { // multiple elements [] - let values = []; - for (let input of expandRadioElement(elem)) { - if (input.type !== 'checkbox' || input.checked) { - values.push(input.value); - } + if (elem instanceof HTMLInputElement) { + if (elem.type === 'radio') { + return 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 + ? expandRadioElement(elem) + .filter((input) => input.checked) + .map((input) => input.value) + : elem.checked; + } else { + return elem.value.replace('\r', '').replace(/^\s+|\s+$/g, ''); } - return values; - } else if (elem.type === 'checkbox') { - return elem.checked; + } else if (elem instanceof HTMLSelectElement) { + return elem.multiple + ? Array.from(elem.selectedOptions, (option) => option.value) + : elem.selectedOptions[0]?.value ?? null; - } else if (elem.tagName.toLowerCase() === 'textarea') { + } else if (elem instanceof HTMLTextAreaElement) { return elem.value.replace('\r', ''); + } else if (elem instanceof RadioNodeList) { + return Nette.getValue(elem[0]); + } else { - return elem.value.replace('\r', '').replace(/^\s+|\s+$/g, ''); + return null; } }; @@ -139,7 +121,7 @@ * @param {boolean} filter * @return {*} */ - Nette.getEffectiveValue = function(elem, filter) { + Nette.getEffectiveValue = function(elem, filter = false) { let val = Nette.getValue(elem); if (val === elem.getAttribute('data-nette-empty-value')) { val = ''; @@ -164,14 +146,13 @@ * @param {?boolean} emptyOptional * @return {boolean} */ - Nette.validateControl = function(elem, rules, onlyCheck, value, emptyOptional) { - rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); - value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; - emptyOptional = emptyOptional === undefined ? !Nette.validateRule(elem, ':filled', null, value) : emptyOptional; - - for (let id = 0, len = rules.length; id < len; id++) { - let rule = rules[id], - op = rule.op.match(/(~)?([^?]+)/), + Nette.validateControl = function(elem, rules, onlyCheck = false, value= null, emptyOptional = null) { + rules ??= JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); + value ??= {value: Nette.getEffectiveValue(elem)}; + emptyOptional ??= !Nette.validateRule(elem, ':filled', null, value); + + for (const rule of rules) { + let op = rule.op.match(/(~)?([^?]+)/), curElem = rule.control ? getFormElement(elem.form, rule.control) : elem; rule.neg = op[1]; @@ -220,7 +201,7 @@ * @param {boolean} onlyCheck * @return {boolean} */ - Nette.validateForm = function(sender, onlyCheck) { + Nette.validateForm = function(sender, onlyCheck = false) { let form = sender.form || sender, scope = false; @@ -238,9 +219,7 @@ let radios = {}; - for (let i = 0; i < form.elements.length; i++) { - let elem = form.elements[i]; - + for (const elem of form.elements) { if (elem.willValidate && elem.validity.badInput) { elem.reportValidity(); return false; @@ -280,12 +259,8 @@ */ Nette.isDisabled = function(elem) { if (elem.type === 'radio') { - for (let input of expandRadioElement(elem)) { - if (!input.disabled) { - return false; - } - } - return true; + return expandRadioElement(elem) + .every((input) => input.disabled); } return elem.disabled; }; @@ -313,15 +288,12 @@ let messages = [], focusElem; - for (let i = 0; i < errors.length; i++) { - let elem = errors[i].element, - message = errors[i].message; + for (const error of errors) { + if (messages.indexOf(error.message) < 0) { + messages.push(error.message); - if (messages.indexOf(message) < 0) { - messages.push(message); - - if (!focusElem && elem.focus) { - focusElem = elem; + if (!focusElem && error.element.focus) { + focusElem = error.element; } } } @@ -380,7 +352,7 @@ return op === ':filled'; } - value = value === undefined ? {value: Nette.getEffectiveValue(elem, true)} : value; + value ??= {value: Nette.getEffectiveValue(elem, true)}; if (op.charAt(0) === ':') { op = op.substring(1); @@ -388,13 +360,14 @@ op = op.replace('::', '_'); op = op.replace(/\\/g, ''); - let arr = Array.isArray(arg) ? arg.slice(0) : [arg]; - for (let i = 0, len = arr.length; i < len; i++) { - if (arr[i] && arr[i].control) { - let control = getFormElement(elem.form, arr[i].control); - arr[i] = control === elem ? value.value : Nette.getEffectiveValue(control, true); + let arr = Array.isArray(arg) ? arg : [arg]; + arr = arr.map((arg) => { + if (arg?.control) { + let control = getFormElement(elem.form, arg.control); + return control === elem ? value.value : Nette.getEffectiveValue(control, true); } - } + return arg; + }); return Nette.validators[op] ? Nette.validators[op](elem, Array.isArray(arg) ? arr : arr[0], value.value, value) @@ -433,9 +406,9 @@ val = Array.isArray(val) ? val : [val]; arg = Array.isArray(arg) ? arg : [arg]; loop: - for (let i1 = 0, len1 = val.length; i1 < len1; i1++) { - for (let i2 = 0, len2 = arg.length; i2 < len2; i2++) { - if (toString(val[i1]) === toString(arg[i2])) { + for (const a of val) { + for (const b of arg) { + if (toString(a) === toString(b)) { continue loop; } } @@ -499,17 +472,9 @@ regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : ''); } - if (val instanceof FileList) { - for (let i = 0; i < val.length; i++) { - if (!regExp.test(val[i].name)) { - return false; - } - } - - return true; - } - - return regExp.test(val); + return val instanceof FileList + ? Array.from(val).every((file) => regExp.test(file.name)) + : regExp.test(val); } catch (e) {} // eslint-disable-line no-empty }, @@ -567,32 +532,15 @@ }, fileSize: function(elem, arg, val) { - for (let i = 0; i < val.length; i++) { - if (val[i].size > arg) { - return false; - } - } - return true; + return Array.from(val).every((file) => file.size <= arg); }, - mimeType: function (elem, arg, val) { + mimeType: function (elem, args, val) { let re = []; - arg = Array.isArray(arg) ? arg : [arg]; - for (let i = 0, len = arg.length; i < len; i++) { - re.push('^' + arg[i].replace(/([^\w])/g, '\\$1').replace('\\*', '.*') + '$'); - } + args = Array.isArray(args) ? args : [args]; + args.map((arg) => re.push('^' + arg.replace(/([^\w])/g, '\\$1').replace('\\*', '.*') + '$')); re = new RegExp(re.join('|')); - - if (val instanceof FileList) { - for (let i = 0; i < val.length; i++) { - if (val[i].type && !re.test(val[i].type)) { - return false; - } else if (elem.validity.badInput) { - return null; - } - } - } - return true; + return Array.from(val).every((file) => !file.type || re.test(file.type)); }, image: function (elem, arg, val) { @@ -612,9 +560,9 @@ */ Nette.toggleForm = function(form, event) { formToggles = {}; - for (let i = 0; i < form.elements.length; i++) { - if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) { - Nette.toggleControl(form.elements[i], null, null, !event); + for (const elem of form.elements) { + if (elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) { + Nette.toggleControl(elem, null, null, !event); } } @@ -634,10 +582,10 @@ * @param {?boolean} emptyOptional * @return {boolean} */ - Nette.toggleControl = function(elem, rules, success, firsttime, value, emptyOptional) { - rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); - value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value; - emptyOptional = emptyOptional === undefined ? !Nette.validateRule(elem, ':filled', null, value) : emptyOptional; + Nette.toggleControl = function(elem, rules, success, firsttime, value= null, emptyOptional = null) { + rules ??= JSON.parse(elem.getAttribute('data-nette-rules') || '[]'); + value ??= {value: Nette.getEffectiveValue(elem)}; + emptyOptional ??= !Nette.validateRule(elem, ':filled', null, value); let has = false, handler = function (e) { @@ -645,9 +593,8 @@ }, curSuccess; - for (let id = 0, len = rules.length; id < len; id++) { - let rule = rules[id], - op = rule.op.match(/(~)?([^?]+)/), + for (const rule of rules) { + let op = rule.op.match(/(~)?([^?]+)/), curElem = rule.control ? getFormElement(elem.form, rule.control) : elem; rule.neg = op[1]; @@ -677,7 +624,7 @@ if ((rule.condition && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value, rule.op === ':blank' ? false : emptyOptional)) || rule.toggle) { has = true; if (firsttime) { - for (let el of expandRadioElement(curElem)) { + for (const el of expandRadioElement(curElem)) { if (!toggleListeners.has(el)) { el.addEventListener('change', handler); toggleListeners.set(el, null); @@ -705,10 +652,8 @@ if (/^\w[\w.:-]*$/.test(selector)) { // id selector = '#' + selector; } - let elems = document.querySelectorAll(selector); - for (let i = 0; i < elems.length; i++) { - elems[i].hidden = !visible; - } + Array.from(document.querySelectorAll(selector)) + .forEach((elem) => elem.hidden = !visible); }; @@ -719,8 +664,7 @@ Nette.compactCheckboxes = function(form) { let values = {}; - for (let i = 0; i < form.elements.length; i++) { - let elem = form.elements[i]; + for (const elem of form.elements) { if (elem.tagName.toLowerCase() === 'input' && elem.type === 'checkbox' ) { @@ -740,7 +684,7 @@ } } - for (let name in values) { + for (const name in values) { let elem = form.elements.namedItem(name); if (!elem) { elem = document.createElement('input'); @@ -765,12 +709,7 @@ }); } - check: { - for (let i = 0; i < form.elements.length; i++) { - if (form.elements[i].getAttribute('data-nette-rules')) { - break check; - } - } + if (!Array.form(form.elements).some((elem) => elem.getAttribute('data-nette-rules'))) { return; } @@ -801,9 +740,8 @@ */ Nette.initOnLoad = function() { Nette.onDocumentReady(() => { - for (let i = 0; i < document.forms.length; i++) { - Nette.initForm(document.forms[i]); - } + Array.from(document.forms) + .forEach((form) => Nette.initForm(form)); document.body.addEventListener('click', (e) => { let target = e.target;