PageSourceSearch

https://www.hochheim.de/fileadmin/templates/js/parts/hw_forms_validation.js?1710839295

js hochheim.de collected 2026-10-02 16:32:41 UTC 20,027 bytes, 572 lines download raw bytes

1class Form {
2    'use strict';
3
4    /**
5     * Form element (filled via constructor)
6     */
7    #form;
8    #formtype;
9
10    /**
11     * Has form any errors?
12     *
13     * @type {boolean}
14     */
15    #error = false;
16
17    /**
18     * List all fieldnames and errors
19     * @type {{}}
20     */
21    #errorFields = {};
22
23    #formErrorClass = 'hw_form--error';
24    #fieldsetErrorClass = 'hw_fieldset--error';
25    #fieldErrorClass = 'hw_field--error';
26    #errorContainerClass = 'data-' + this.#formtype + '-class-handler';
27    #errorMessageContainerClass = this.#formtype + '_errors_list';
28
29    /**
30     * Validator configuration
31     *
32     * Add possibility to add own validators from outside like:
33     *
34     *    const forms = document.querySelectorAll('.powermail_form');
35     *    forms.forEach(function(form) {
36     *      let formValidation = form.powermailFormValidation;
37     *
38     *      formValidation.addValidator('custom100', function(field) {
39     *        if (field.hasAttribute('data-' + this.#formtype + '-custom100')) {
40     *          // return true means validation has failed
41     *          return field.value < parseInt(field.getAttribute('data-' + this.#formtype + '-custom100'));
42     *        }
43     *        return false;
44     *      });
45     *    });
46     *
47     * @type {{name: function(*=, *): boolean}}
48     */
49    #validators = {
50        'required': (field) => {
51            return this.#isRequiredField(field) && this.#isValidationRequiredConfirmed(field) === false;
52        },
53        'email': (field) => {
54            return this.#isEmailField(field) && this.#isValidationEmailConfirmed(field) === false;
55        },
56        'url': (field) => {
57            return this.#isUrlField(field) && this.#isValidationUrlConfirmed(field) === false;
58        },
59        'pattern': (field) => {
60            return this.#isPatternField(field) && this.#isValidationPatternConfirmed(field) === false;
61        },
62        'number': (field) => {
63            return this.#isNumberField(field) && this.#isValidationNumberConfirmed(field) === false;
64        },
65        'minimum': (field) => {
66            return this.#isMinimumField(field) && this.#isValidationMinimumConfirmed(field) === false;
67        },
68        'maximum': (field) => {
69            return this.#isMaximumField(field) && this.#isValidationMaximumConfirmed(field) === false;
70        },
71        'length': (field) => {
72            return this.#isLengthField(field) && this.#isValidationLengthConfirmed(field) === false;
73        },
74        'equalto': (field) => {
75            return this.#isEqualtoField(field) && this.#isValidationEqualtoConfirmed(field) === false;
76        },
77        'powermailfilesize': (field) => {
78            return this.#isUploadField(field) && this.#isValidationUploadFieldSizeConfirmed(field) === false;
79        },
80        'powermailfileextensions': (field) => {
81            return this.#isUploadField(field) && this.#isValidationUploadFieldExtensionConfirmed(field) === false;
82        },
83    };
84
85    /**
86     * Submit error callback configuration
87     *
88     * Add possibility to add own callbacks when a user submits a form and an error happens from outside like:
89     *
90     *    const forms = document.querySelectorAll('.powermail_form');
91     *    forms.forEach(function(form) {
92     *      let formValidation = form.powermailFormValidation;
93     *
94     *      formValidation.addSubmitErrorCallback('custom100', function() {
95     *        // error happens, do something
96     *      });
97     *    });
98     *
99     * @type {{name: function(*=, *): boolean}}
100     */
101    #submitErrorCallbacks = {
102        'openTabWithError': () => {
103            const firstFieldWithError = this.#form.querySelector('.powermail_field_error');
104            if (firstFieldWithError !== null) {
105                let fieldsetError = firstFieldWithError.closest('.powermail_fieldset');
106                const fieldsetErrorIndex = [...this.#form.querySelectorAll('.powermail_fieldset')].indexOf(fieldsetError);
107                let moreStepForm = new MoreStepForm();
108                moreStepForm.showFieldset(fieldsetErrorIndex, this.#form);
109            }
110        },
111        'scrollToFirstError': () => {
112            try {
113                const fieldsWithError = this.#form.querySelectorAll('.powermail_field_error');
114                fieldsWithError.forEach((field) => {
115                    if (Utility.isElementVisible(field)) {
116                        field.scrollIntoView({behavior:'smooth'});
117                        throw 'StopException';
118                    }
119                });
120            } catch (exception) {
121                // stop, do nothing
122            }
123        }
124    };
125
126    constructor(form, formtype) {
127        this.#form = form;
128        this.#formtype = formtype;
129        this.#form.powermailFormValidation = this;
130        this.#errorContainerClass = 'data-' + this.#formtype + '-class-handler';
131        this.#errorMessageContainerClass = this.#formtype + '_errors_list';
132    }
133
134    validate() {
135        this.#validateFormSubmit();
136        this.#validateFieldListener();
137    }
138
139    /**
140     * @param name
141     * @param validator
142     */
143    addValidator(name, validator) {
144        this.#validators[name] = validator;
145    }
146
147    /**
148     * @param name
149     * @param callback
150     */
151    addSubmitErrorCallback(name, callback) {
152        this.#submitErrorCallbacks[name] = callback;
153    }
154
155    #validateFormSubmit() {
156        const that = this;
157        this.#form.setAttribute('novalidate', 'novalidate')
158        this.#form.addEventListener('submit', function(event) {
159            that.#validateForm();
160            if (that.#hasFormErrors() === true) {
161                that.#runSubmitErrorCallbacks();
162                event.preventDefault();
163            }
164        })
165    };
166
167    #validateFieldListener() {
168        const fields = this.#getFieldsFromForm();
169        fields.forEach((field) => {
170            field.addEventListener('input', () => {
171                // When user types something in a field
172                this.#validateField(field);
173            });
174            field.addEventListener('blur', () => {
175                // When field focus gets lost
176                this.#validateField(field);
177            });
178            field.addEventListener('change', () => {
179                // When a checkbox, radiobutton or option in a select was chosen
180                this.#validateField(field);
181            });
182        });
183    };
184
185    #validateForm() {
186        let fields = this.#getFieldsFromForm();
187        for (let i = 0; i < fields.length; i++) {
188            this.#validateField(fields[i]);
189        }
190    };
191
192    #validateField(field) {
193        let error = false;
194        field = this.#getValidationField(field);
195
196        for (let validator in this.#validators) {
197            if (this.#validators.hasOwnProperty(validator) === false) {
198                continue;
199            }
200            error = this.#runValidator(validator, this.#validators[validator], field, error);
201        }
202
203        this.#addFieldErrorStatus(field, error);
204        this.#calculateError();
205        this.#updateErrorClassesForFormAndFieldsets(field);
206    };
207
208    #runValidator(name, validator, field, error) {
209        if (error === true) {
210            return error;
211        }
212        error = validator(field);
213        error ? this.#setError(name, field) : this.#removeError(name, field);
214        return error;
215    }
216
217    #runSubmitErrorCallbacks() {
218        for (let callback in this.#submitErrorCallbacks) {
219            if (this.#submitErrorCallbacks.hasOwnProperty(callback) === false) {
220                continue;
221            }
222            this.#submitErrorCallbacks[callback]();
223        }
224    }
225
226    /*
227     * Check for validations
228     */
229
230    #isRequiredField(field) {
231        return field.hasAttribute('required') || field.getAttribute('data-' + this.#formtype + '-required') === 'true';
232    };
233
234    #isEmailField(field) {
235        return field.getAttribute('type') === 'email' || field.getAttribute('data-' + this.#formtype + '-type') === 'email';
236    };
237
238    #isUrlField(field) {
239        return field.getAttribute('type') === 'url' || field.getAttribute('data-' + this.#formtype + '-type') === 'url';
240    };
241
242    #isPatternField(field) {
243        return field.hasAttribute('pattern') || field.hasAttribute('data-' + this.#formtype + '-pattern');
244    };
245
246    #isNumberField(field) {
247        return field.getAttribute('type') === 'number' || field.getAttribute('data-' + this.#formtype + '-type') === 'integer';
248    };
249
250    #isMinimumField(field) {
251        return field.hasAttribute('min') || field.hasAttribute('data-' + this.#formtype + '-min');
252    };
253
254    #isMaximumField(field) {
255        return field.hasAttribute('max') || field.hasAttribute('data-' + this.#formtype + '-max');
256    };
257
258    #isLengthField(field) {
259        return field.hasAttribute('data-' + this.#formtype + '-length');
260    };
261
262    #isEqualtoField(field) {
263        return field.hasAttribute('data-' + this.#formtype + '-equalto');
264    };
265
266    #isUploadField(field) {
267        return field.getAttribute('type') === 'file';
268    };
269
270    /*
271     * Single validators
272     */
273
274    #isValidationRequiredConfirmed(field) {
275        return this.#getFieldValue(field) !== '';
276    };
277
278    #isValidationEmailConfirmed(field) {
279        if (field.value === '') {
280            return true;
281        }
282        let pattern = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
283        let constraint = new RegExp(pattern, '');
284        return constraint.test(this.#getFieldValue(field));
285    };
286
287    #isValidationUrlConfirmed(field) {
288        if (field.value === '') {
289            return true;
290        }
291        let pattern = '^(https?:\\/\\/)?'+ // protocol
292            '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
293            '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
294            '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
295            '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
296            '(\\#[-a-z\\d_]*)?$'
297        let constraint = new RegExp(pattern, '');
298        return constraint.test(this.#getFieldValue(field));
299    };
300
301    #isValidationPatternConfirmed(field) {
302        if (field.value === '') {
303            return true;
304        }
305        let pattern = field.getAttribute('data-' + this.#formtype + '-pattern') || field.getAttribute('pattern');
306        let constraint = new RegExp(pattern, '');
307        return constraint.test(this.#getFieldValue(field));
308    };
309
310    #isValidationNumberConfirmed(field) {
311        if (field.value === '') {
312            return true;
313        }
314        return isNaN(field.value) === false;
315    };
316
317    #isValidationMinimumConfirmed(field) {
318        if (field.value === '') {
319            return true;
320        }
321        let minimum = field.getAttribute('min') || field.getAttribute('data-' + this.#formtype + '-min');
322        return parseInt(field.value) >= parseInt(minimum);
323    };
324
325    #isValidationMaximumConfirmed(field) {
326        if (field.value === '') {
327            return true;
328        }
329        let maximum = field.getAttribute('max') || field.getAttribute('data-' + this.#formtype + '-max');
330        return parseInt(field.value) <= parseInt(maximum);
331    };
332
333    #isValidationLengthConfirmed(field) {
334        if (field.value === '') {
335            return true;
336        }
337        let lengthConfiguration = field.getAttribute('data-' + this.#formtype + '-length');
338        let length = lengthConfiguration.replace('[', '').replace(']', '').split(',');
339        let minimum = length[0].trim();
340        let maximum = length[1].trim();
341        return parseInt(field.value.length) >= parseInt(minimum) && parseInt(field.value.length) <= parseInt(maximum);
342    };
343
344    #isValidationEqualtoConfirmed(field) {
345        const comparisonSelector = field.getAttribute('data-' + this.#formtype + '-equalto');
346        const comparisonField = this.#form.querySelector(comparisonSelector);
347        if (comparisonField !== null) {
348            return comparisonField.value === field.value;
349        }
350        return false;
351    };
352
353    #isValidationUploadFieldSizeConfirmed(field) {
354        if (field.value === '') {
355            return true;
356        }
357        let size = Utility.getLargestFileSize(field);
358        let sizeConfiguration = field.getAttribute('data-' + this.#formtype + '-powermailfilesize').split(',');
359        return size <= parseInt(sizeConfiguration[0]);
360    };
361
362    #isValidationUploadFieldExtensionConfirmed(field) {
363        if (field.value === '') {
364            return true;
365        }
366        return Utility.isFileExtensionInList(Utility.getExtensionFromFileName(field.value), field.getAttribute('accept'));
367    };
368
369    /**
370     * @param type like "required" or "pattern"
371     * @param field
372     */
373    #setError(type, field) {
374        this.#removeError(type, field);
375        this.#addErrorClass(field);
376        let message = field.getAttribute('data-' + this.#formtype + '-' + type + '-message') ||
377            field.getAttribute('data-' + this.#formtype + '-error-message') || 'Validation error';
378        this.#addErrorMessage(message, field);
379    };
380
381    /**
382     * @param type like "required" or "pattern"
383     * @param field
384     */
385    #removeError(type, field) {
386        this.#removeErrorClass(field);
387        this.#removeErrorMessages(field);
388    };
389
390    #addErrorClass(field) {
391        if (field.getAttribute(this.#errorContainerClass)) {
392            let elements = document.querySelectorAll(field.getAttribute(this.#errorContainerClass));
393            for (let i = 0; i < elements.length; i++) {
394                elements[i].classList.add(this.#fieldErrorClass);
395            }
396        } else {
397            field.classList.add(this.#fieldErrorClass);
398        }
399    };
400
401    #removeErrorClass(field) {
402        if (field.getAttribute(this.#errorContainerClass)) {
403            let elements = document.querySelectorAll(field.getAttribute(this.#errorContainerClass));
404            for (let i = 0; i < elements.length; i++) {
405                elements[i].classList.remove(this.#fieldErrorClass);
406            }
407        } else {
408            field.classList.remove(this.#fieldErrorClass);
409        }
410    };
411
412    #addErrorMessage(message, field) {
413        let errorContainer = document.createElement('ul');
414        errorContainer.classList.add(this.#errorMessageContainerClass);
415        errorContainer.classList.add('filled');
416        errorContainer.setAttribute('data-' + this.#formtype + '-error', this.#getFieldIdentifier(field));
417        let errorElement = document.createElement('li');
418        errorContainer.appendChild(errorElement);
419        let textNode = document.createTextNode(message);
420        errorElement.appendChild(textNode);
421
422        if (field.getAttribute('data-' + this.#formtype + '-errors-container') !== null) {
423            let parentContainer = document.querySelector(field.getAttribute('data-' + this.#formtype + '-errors-container'));
424            if (parentContainer !== null) {
425                parentContainer.appendChild(errorContainer);
426            }
427        } else {
428            field.parentNode.appendChild(errorContainer);
429        }
430    };
431
432    #removeErrorMessages(field) {
433        let errorMessageContainer = document.querySelector(
434            '[data-' + this.#formtype + '-error="' + this.#getFieldIdentifier(field) + '"]'
435        );
436        if (errorMessageContainer !== null) {
437            errorMessageContainer.remove();
438        }
439    };
440
441    #getFieldValue(field) {
442        let value = field.value;
443
444        // Special case radiobuttons & checkboxes: take value from selected field
445        if (field.getAttribute('type') === 'radio' || field.getAttribute('type') === 'checkbox') {
446            value = '';
447            let name = field.getAttribute('name');
448            let form = field.closest('form');
449            let selectedField = form.querySelector('input[name="' + name + '"]:checked');
450            if (selectedField !== null) {
451                value = selectedField.value;
452            }
453        }
454        return value;
455    };
456
457    #getFieldIdentifier(field) {
458        let name = field.getAttribute('name');
459        return name.replace(/[^\w\s]/gi, '');
460    };
461
462    #getFieldsFromForm() {
463        return this.#form.querySelectorAll(
464            'input:not([data-' + this.#formtype + '-validation="disabled"]):not([type="hidden"]):not([type="reset"]):not([type="submit"])'
465            + ', textarea:not([data-' + this.#formtype + '-validation="disabled"])'
466            + ', select:not([data-' + this.#formtype + '-validation="disabled"])'
467        );
468    };
469
470    /**
471     * Special case for radiobuttons & checkboxes: take first field
472     *
473     * @param field
474     * @returns {*}
475     */
476    #getValidationField(field) {
477        if (field.getAttribute('type') === 'radio' || field.getAttribute('type') === 'checkbox') {
478            let name = field.getAttribute('name');
479            let form = field.closest('form');
480            let fieldNew = form.querySelector('[name="' + name + '"][type="radio"], [name="' + name + '"][type="checkbox"]');
481            if (fieldNew !== null) {
482                field = fieldNew;
483            }
484        }
485        return field;
486    };
487
488    #hasFormErrors() {
489        return this.#error;
490    }
491
492    #addFieldErrorStatus(field, error) {
493        this.#errorFields[field.getAttribute('name')] = error;
494    }
495
496    #calculateError() {
497        let error = false;
498        for (let property in this.#errorFields) {
499            if (this.#errorFields.hasOwnProperty(property) === false) {
500                continue;
501            }
502            if (this.#errorFields[property] === true) {
503                error = true;
504            }
505        }
506        this.#error = error;
507    }
508
509    #updateErrorClassesForFormAndFieldsets(field) {
510        const fieldset = field.closest('fieldset.powermail_fieldset');
511        if (this.#hasFormErrors()) {
512            this.#form.classList.add(this.#formErrorClass);
513            if (fieldset !== null) {
514                fieldset.classList.add(this.#fieldsetErrorClass);
515            }
516        } else {
517            this.#form.classList.remove(this.#formErrorClass);
518            if (fieldset !== null) {
519                fieldset.classList.remove(this.#fieldsetErrorClass);
520            }
521        }
522    }
523}
524
525
526
527
528const forms = document.querySelectorAll(".hw_form");
529forms.forEach(function(form) {
530    if(form.hasAttribute("data-powermail-validate")) {
531        form = new Form(form,"powermail");
532    } else if(form.hasAttribute("data-hw-form-validate")) {
533        form = new Form(form,"hw-form");
534    } else {
535        return;
536    }
537    form.validate();
538});
539
540
541
542// let inputsRequired = document.querySelectorAll("input[required='required']");
543// inputsRequired.forEach((inputRequired) => {
544//     inputRequired.addEventListener("focusout", (event) => {
545//         console.log("inputRequired focusout");
546//         console.log("inputRequired.validity.valid");
547//         console.log(inputRequired.validity.valid);
548//         console.log("inputRequired.validity.valueMissing");
549//         console.log(inputRequired.validity.valueMissing);
550//         console.log("inputRequired.validity.typeMismatch");
551//         console.log(inputRequired.validity.typeMismatch);
552//         console.log("inputRequired.reportValidity();");
553//         console.log(inputRequired.reportValidity());
554//         if (inputRequired.validity.valueMissing) {
555//             if(!inputRequired.classList.contains("hw_error")) {
556//                 inputRequired.classList.add("hw_error");
557//             }
558//         } else {
559//             if(inputRequired.classList.contains("hw_error")) {
560//                 inputRequired.classList.remove("hw_error");
561//             }
562//         }
563//     });
564//     inputRequired.addEventListener("input", (event) => {
565//         if (inputRequired.validity.valueMissing) {
566//         } else {
567//             if(inputRequired.classList.contains("hw_error")) {
568//                 inputRequired.classList.remove("hw_error");
569//             }
570//         }
571//     });
572// });

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.