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.