[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/system/js/fields/ -> validate.js (source)

   1  /** Highest positive signed 32-bit float value */
   2  
   3  const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
   4  
   5  /** Bootstring parameters */
   6  
   7  const base = 36;
   8  const tMin = 1;
   9  const tMax = 26;
  10  const skew = 38;
  11  const damp = 700;
  12  const initialBias = 72;
  13  const initialN = 128; // 0x80
  14  
  15  const delimiter = '-'; // '\x2D'
  16  
  17  /** Regular expressions */
  18  
  19  const regexPunycode = /^xn--/;
  20  const regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars
  21  
  22  const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
  23  
  24  /** Error messages */
  25  
  26  const errors = {
  27    'overflow': 'Overflow: input needs wider integers to process',
  28    'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
  29    'invalid-input': 'Invalid input'
  30  };
  31  /** Convenience shortcuts */
  32  
  33  const baseMinusTMin = base - tMin;
  34  const floor = Math.floor;
  35  const stringFromCharCode = String.fromCharCode;
  36  /*--------------------------------------------------------------------------*/
  37  
  38  /**
  39   * A generic error utility function.
  40   * @private
  41   * @param {String} type The error type.
  42   * @returns {Error} Throws a `RangeError` with the applicable error message.
  43   */
  44  
  45  function error(type) {
  46    throw new RangeError(errors[type]);
  47  }
  48  /**
  49   * A generic `Array#map` utility function.
  50   * @private
  51   * @param {Array} array The array to iterate over.
  52   * @param {Function} callback The function that gets called for every array
  53   * item.
  54   * @returns {Array} A new array of values returned by the callback function.
  55   */
  56  
  57  
  58  function map(array, fn) {
  59    const result = [];
  60    let length = array.length;
  61  
  62    while (length--) {
  63      result[length] = fn(array[length]);
  64    }
  65  
  66    return result;
  67  }
  68  /**
  69   * A simple `Array#map`-like wrapper to work with domain name strings or email
  70   * addresses.
  71   * @private
  72   * @param {String} domain The domain name or email address.
  73   * @param {Function} callback The function that gets called for every
  74   * character.
  75   * @returns {Array} A new string of characters returned by the callback
  76   * function.
  77   */
  78  
  79  
  80  function mapDomain(string, fn) {
  81    const parts = string.split('@');
  82    let result = '';
  83  
  84    if (parts.length > 1) {
  85      // In email addresses, only the domain name should be punycoded. Leave
  86      // the local part (i.e. everything up to `@`) intact.
  87      result = parts[0] + '@';
  88      string = parts[1];
  89    } // Avoid `split(regex)` for IE8 compatibility. See #17.
  90  
  91  
  92    string = string.replace(regexSeparators, '\x2E');
  93    const labels = string.split('.');
  94    const encoded = map(labels, fn).join('.');
  95    return result + encoded;
  96  }
  97  /**
  98   * Creates an array containing the numeric code points of each Unicode
  99   * character in the string. While JavaScript uses UCS-2 internally,
 100   * this function will convert a pair of surrogate halves (each of which
 101   * UCS-2 exposes as separate characters) into a single code point,
 102   * matching UTF-16.
 103   * @see `punycode.ucs2.encode`
 104   * @see <https://mathiasbynens.be/notes/javascript-encoding>
 105   * @memberOf punycode.ucs2
 106   * @name decode
 107   * @param {String} string The Unicode input string (UCS-2).
 108   * @returns {Array} The new array of code points.
 109   */
 110  
 111  
 112  function ucs2decode(string) {
 113    const output = [];
 114    let counter = 0;
 115    const length = string.length;
 116  
 117    while (counter < length) {
 118      const value = string.charCodeAt(counter++);
 119  
 120      if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
 121        // It's a high surrogate, and there is a next character.
 122        const extra = string.charCodeAt(counter++);
 123  
 124        if ((extra & 0xFC00) == 0xDC00) {
 125          // Low surrogate.
 126          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
 127        } else {
 128          // It's an unmatched surrogate; only append this code unit, in case the
 129          // next code unit is the high surrogate of a surrogate pair.
 130          output.push(value);
 131          counter--;
 132        }
 133      } else {
 134        output.push(value);
 135      }
 136    }
 137  
 138    return output;
 139  }
 140  /**
 141   * Creates a string based on an array of numeric code points.
 142   * @see `punycode.ucs2.decode`
 143   * @memberOf punycode.ucs2
 144   * @name encode
 145   * @param {Array} codePoints The array of numeric code points.
 146   * @returns {String} The new Unicode string (UCS-2).
 147   */
 148  
 149  
 150  const ucs2encode = array => String.fromCodePoint(...array);
 151  /**
 152   * Converts a basic code point into a digit/integer.
 153   * @see `digitToBasic()`
 154   * @private
 155   * @param {Number} codePoint The basic numeric code point value.
 156   * @returns {Number} The numeric value of a basic code point (for use in
 157   * representing integers) in the range `0` to `base - 1`, or `base` if
 158   * the code point does not represent a value.
 159   */
 160  
 161  
 162  const basicToDigit = function (codePoint) {
 163    if (codePoint - 0x30 < 0x0A) {
 164      return codePoint - 0x16;
 165    }
 166  
 167    if (codePoint - 0x41 < 0x1A) {
 168      return codePoint - 0x41;
 169    }
 170  
 171    if (codePoint - 0x61 < 0x1A) {
 172      return codePoint - 0x61;
 173    }
 174  
 175    return base;
 176  };
 177  /**
 178   * Converts a digit/integer into a basic code point.
 179   * @see `basicToDigit()`
 180   * @private
 181   * @param {Number} digit The numeric value of a basic code point.
 182   * @returns {Number} The basic code point whose value (when used for
 183   * representing integers) is `digit`, which needs to be in the range
 184   * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
 185   * used; else, the lowercase form is used. The behavior is undefined
 186   * if `flag` is non-zero and `digit` has no uppercase form.
 187   */
 188  
 189  
 190  const digitToBasic = function (digit, flag) {
 191    //  0..25 map to ASCII a..z or A..Z
 192    // 26..35 map to ASCII 0..9
 193    return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
 194  };
 195  /**
 196   * Bias adaptation function as per section 3.4 of RFC 3492.
 197   * https://tools.ietf.org/html/rfc3492#section-3.4
 198   * @private
 199   */
 200  
 201  
 202  const adapt = function (delta, numPoints, firstTime) {
 203    let k = 0;
 204    delta = firstTime ? floor(delta / damp) : delta >> 1;
 205    delta += floor(delta / numPoints);
 206  
 207    for (; delta > baseMinusTMin * tMax >> 1; k += base) {
 208      delta = floor(delta / baseMinusTMin);
 209    }
 210  
 211    return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
 212  };
 213  /**
 214   * Converts a Punycode string of ASCII-only symbols to a string of Unicode
 215   * symbols.
 216   * @memberOf punycode
 217   * @param {String} input The Punycode string of ASCII-only symbols.
 218   * @returns {String} The resulting string of Unicode symbols.
 219   */
 220  
 221  
 222  const decode = function (input) {
 223    // Don't use UCS-2.
 224    const output = [];
 225    const inputLength = input.length;
 226    let i = 0;
 227    let n = initialN;
 228    let bias = initialBias; // Handle the basic code points: let `basic` be the number of input code
 229    // points before the last delimiter, or `0` if there is none, then copy
 230    // the first basic code points to the output.
 231  
 232    let basic = input.lastIndexOf(delimiter);
 233  
 234    if (basic < 0) {
 235      basic = 0;
 236    }
 237  
 238    for (let j = 0; j < basic; ++j) {
 239      // if it's not a basic code point
 240      if (input.charCodeAt(j) >= 0x80) {
 241        error('not-basic');
 242      }
 243  
 244      output.push(input.charCodeAt(j));
 245    } // Main decoding loop: start just after the last delimiter if any basic code
 246    // points were copied; start at the beginning otherwise.
 247  
 248  
 249    for (let index = basic > 0 ? basic + 1 : 0; index < inputLength;) {
 250      // `index` is the index of the next character to be consumed.
 251      // Decode a generalized variable-length integer into `delta`,
 252      // which gets added to `i`. The overflow checking is easier
 253      // if we increase `i` as we go, then subtract off its starting
 254      // value at the end to obtain `delta`.
 255      let oldi = i;
 256  
 257      for (let w = 1, k = base;; k += base) {
 258        if (index >= inputLength) {
 259          error('invalid-input');
 260        }
 261  
 262        const digit = basicToDigit(input.charCodeAt(index++));
 263  
 264        if (digit >= base || digit > floor((maxInt - i) / w)) {
 265          error('overflow');
 266        }
 267  
 268        i += digit * w;
 269        const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
 270  
 271        if (digit < t) {
 272          break;
 273        }
 274  
 275        const baseMinusT = base - t;
 276  
 277        if (w > floor(maxInt / baseMinusT)) {
 278          error('overflow');
 279        }
 280  
 281        w *= baseMinusT;
 282      }
 283  
 284      const out = output.length + 1;
 285      bias = adapt(i - oldi, out, oldi == 0); // `i` was supposed to wrap around from `out` to `0`,
 286      // incrementing `n` each time, so we'll fix that now:
 287  
 288      if (floor(i / out) > maxInt - n) {
 289        error('overflow');
 290      }
 291  
 292      n += floor(i / out);
 293      i %= out; // Insert `n` at position `i` of the output.
 294  
 295      output.splice(i++, 0, n);
 296    }
 297  
 298    return String.fromCodePoint(...output);
 299  };
 300  /**
 301   * Converts a string of Unicode symbols (e.g. a domain name label) to a
 302   * Punycode string of ASCII-only symbols.
 303   * @memberOf punycode
 304   * @param {String} input The string of Unicode symbols.
 305   * @returns {String} The resulting Punycode string of ASCII-only symbols.
 306   */
 307  
 308  
 309  const encode = function (input) {
 310    const output = []; // Convert the input in UCS-2 to an array of Unicode code points.
 311  
 312    input = ucs2decode(input); // Cache the length.
 313  
 314    let inputLength = input.length; // Initialize the state.
 315  
 316    let n = initialN;
 317    let delta = 0;
 318    let bias = initialBias; // Handle the basic code points.
 319  
 320    for (const currentValue of input) {
 321      if (currentValue < 0x80) {
 322        output.push(stringFromCharCode(currentValue));
 323      }
 324    }
 325  
 326    let basicLength = output.length;
 327    let handledCPCount = basicLength; // `handledCPCount` is the number of code points that have been handled;
 328    // `basicLength` is the number of basic code points.
 329    // Finish the basic string with a delimiter unless it's empty.
 330  
 331    if (basicLength) {
 332      output.push(delimiter);
 333    } // Main encoding loop:
 334  
 335  
 336    while (handledCPCount < inputLength) {
 337      // All non-basic code points < n have been handled already. Find the next
 338      // larger one:
 339      let m = maxInt;
 340  
 341      for (const currentValue of input) {
 342        if (currentValue >= n && currentValue < m) {
 343          m = currentValue;
 344        }
 345      } // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
 346      // but guard against overflow.
 347  
 348  
 349      const handledCPCountPlusOne = handledCPCount + 1;
 350  
 351      if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
 352        error('overflow');
 353      }
 354  
 355      delta += (m - n) * handledCPCountPlusOne;
 356      n = m;
 357  
 358      for (const currentValue of input) {
 359        if (currentValue < n && ++delta > maxInt) {
 360          error('overflow');
 361        }
 362  
 363        if (currentValue == n) {
 364          // Represent delta as a generalized variable-length integer.
 365          let q = delta;
 366  
 367          for (let k = base;; k += base) {
 368            const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
 369  
 370            if (q < t) {
 371              break;
 372            }
 373  
 374            const qMinusT = q - t;
 375            const baseMinusT = base - t;
 376            output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)));
 377            q = floor(qMinusT / baseMinusT);
 378          }
 379  
 380          output.push(stringFromCharCode(digitToBasic(q, 0)));
 381          bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
 382          delta = 0;
 383          ++handledCPCount;
 384        }
 385      }
 386  
 387      ++delta;
 388      ++n;
 389    }
 390  
 391    return output.join('');
 392  };
 393  /**
 394   * Converts a Punycode string representing a domain name or an email address
 395   * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
 396   * it doesn't matter if you call it on a string that has already been
 397   * converted to Unicode.
 398   * @memberOf punycode
 399   * @param {String} input The Punycoded domain name or email address to
 400   * convert to Unicode.
 401   * @returns {String} The Unicode representation of the given Punycode
 402   * string.
 403   */
 404  
 405  
 406  const toUnicode = function (input) {
 407    return mapDomain(input, function (string) {
 408      return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string;
 409    });
 410  };
 411  /**
 412   * Converts a Unicode string representing a domain name or an email address to
 413   * Punycode. Only the non-ASCII parts of the domain name will be converted,
 414   * i.e. it doesn't matter if you call it with a domain that's already in
 415   * ASCII.
 416   * @memberOf punycode
 417   * @param {String} input The domain name or email address to convert, as a
 418   * Unicode string.
 419   * @returns {String} The Punycode representation of the given domain name or
 420   * email address.
 421   */
 422  
 423  
 424  const toASCII = function (input) {
 425    return mapDomain(input, function (string) {
 426      return regexNonASCII.test(string) ? 'xn--' + encode(string) : string;
 427    });
 428  };
 429  /*--------------------------------------------------------------------------*/
 430  
 431  /** Define the public API */
 432  
 433  
 434  const punycode = {
 435    /**
 436     * A string representing the current Punycode.js version number.
 437     * @memberOf punycode
 438     * @type String
 439     */
 440    'version': '2.1.0',
 441  
 442    /**
 443     * An object of methods to convert from JavaScript's internal character
 444     * representation (UCS-2) to Unicode code points, and back.
 445     * @see <https://mathiasbynens.be/notes/javascript-encoding>
 446     * @memberOf punycode
 447     * @type Object
 448     */
 449    'ucs2': {
 450      'decode': ucs2decode,
 451      'encode': ucs2encode
 452    },
 453    'decode': decode,
 454    'encode': encode,
 455    'toASCII': toASCII,
 456    'toUnicode': toUnicode
 457  };
 458  
 459  /**
 460   * @copyright  (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
 461   * @license    GNU General Public License version 2 or later; see LICENSE.txt
 462   */
 463  
 464  class JFormValidator {
 465    constructor() {
 466      this.customValidators = {};
 467      this.handlers = [];
 468      this.handlers = {};
 469      this.removeMarking = this.removeMarking.bind(this);
 470  
 471      this.inputEmail = () => {
 472        const input = document.createElement('input');
 473        input.setAttribute('type', 'email');
 474        return input.type !== 'text';
 475      }; // Default handlers
 476  
 477  
 478      this.setHandler('username', value => {
 479        const regex = /[<|>|"|'|%|;|(|)|&]/i;
 480        return !regex.test(value);
 481      });
 482      this.setHandler('password', value => {
 483        const regex = /^\S[\S ]{2,98}\S$/;
 484        return regex.test(value);
 485      });
 486      this.setHandler('numeric', value => {
 487        const regex = /^(\d|-)?(\d|,)*\.?\d*$/;
 488        return regex.test(value);
 489      });
 490      this.setHandler('email', value => {
 491        const newValue = punycode.toASCII(value);
 492        const regex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
 493        return regex.test(newValue);
 494      }); // Attach all forms with a class 'form-validate'
 495  
 496      const forms = [].slice.call(document.querySelectorAll('form'));
 497      forms.forEach(form => {
 498        if (form.classList.contains('form-validate')) {
 499          this.attachToForm(form);
 500        }
 501      });
 502    }
 503  
 504    get custom() {
 505      return this.customValidators;
 506    }
 507  
 508    set custom(value) {
 509      this.customValidators = value;
 510    }
 511  
 512    setHandler(name, func, en) {
 513      const isEnabled = en === '' ? true : en;
 514      this.handlers[name] = {
 515        enabled: isEnabled,
 516        exec: func
 517      };
 518    } // eslint-disable-next-line class-methods-use-this
 519  
 520  
 521    markValid(element) {
 522      // Get a label
 523      const label = element.form.querySelector(`label[for="$element.id}"]`);
 524      let message;
 525  
 526      if (element.classList.contains('required') || element.getAttribute('required')) {
 527        if (label) {
 528          message = label.querySelector('span.form-control-feedback');
 529        }
 530      }
 531  
 532      element.classList.remove('form-control-danger');
 533      element.classList.remove('invalid');
 534      element.classList.add('form-control-success');
 535      element.parentNode.classList.remove('has-danger');
 536      element.parentNode.classList.add('has-success');
 537      element.setAttribute('aria-invalid', 'false'); // Remove message
 538  
 539      if (message) {
 540        message.parentNode.removeChild(message);
 541      } // Restore Label
 542  
 543  
 544      if (label) {
 545        label.classList.remove('invalid');
 546      }
 547    } // eslint-disable-next-line class-methods-use-this
 548  
 549  
 550    markInvalid(element, empty) {
 551      // Get a label
 552      const label = element.form.querySelector(`label[for="$element.id}"]`);
 553      element.classList.remove('form-control-success');
 554      element.classList.remove('valid');
 555      element.classList.add('form-control-danger');
 556      element.classList.add('invalid');
 557      element.parentNode.classList.remove('has-success');
 558      element.parentNode.classList.add('has-danger');
 559      element.setAttribute('aria-invalid', 'true'); // Display custom message
 560  
 561      let mesgCont;
 562      const message = element.getAttribute('data-validation-text');
 563  
 564      if (label) {
 565        mesgCont = label.querySelector('span.form-control-feedback');
 566      }
 567  
 568      if (!mesgCont) {
 569        const elMsg = document.createElement('span');
 570        elMsg.classList.add('form-control-feedback');
 571  
 572        if (empty && empty === 'checkbox') {
 573          elMsg.innerHTML = message !== null ? Joomla.sanitizeHtml(message) : Joomla.sanitizeHtml(Joomla.Text._('JLIB_FORM_FIELD_REQUIRED_CHECK'));
 574        } else if (empty && empty === 'value') {
 575          elMsg.innerHTML = message !== null ? Joomla.sanitizeHtml(message) : Joomla.sanitizeHtml(Joomla.Text._('JLIB_FORM_FIELD_REQUIRED_VALUE'));
 576        } else {
 577          elMsg.innerHTML = message !== null ? Joomla.sanitizeHtml(message) : Joomla.sanitizeHtml(Joomla.Text._('JLIB_FORM_FIELD_INVALID_VALUE'));
 578        }
 579  
 580        if (label) {
 581          label.appendChild(elMsg);
 582        }
 583      } // Mark the Label as well
 584  
 585  
 586      if (label) {
 587        label.classList.add('invalid');
 588      }
 589    } // eslint-disable-next-line class-methods-use-this
 590  
 591  
 592    removeMarking(element) {
 593      // Get the associated label
 594      let message;
 595      const label = element.form.querySelector(`label[for="$element.id}"]`);
 596  
 597      if (label) {
 598        message = label.querySelector('span.form-control-feedback');
 599      }
 600  
 601      element.classList.remove('form-control-danger');
 602      element.classList.remove('form-control-success');
 603      element.classList.remove('invalid');
 604      element.classList.add('valid');
 605      element.parentNode.classList.remove('has-danger');
 606      element.parentNode.classList.remove('has-success'); // Remove message
 607  
 608      if (message) {
 609        if (label) {
 610          label.removeChild(message);
 611        }
 612      } // Restore Label
 613  
 614  
 615      if (label) {
 616        label.classList.remove('invalid');
 617      }
 618    }
 619  
 620    handleResponse(state, element, empty) {
 621      const tagName = element.tagName.toLowerCase(); // Set the element and its label (if exists) invalid state
 622  
 623      if (tagName !== 'button' && element.value !== undefined || tagName === 'fieldset') {
 624        if (state === false) {
 625          this.markInvalid(element, empty);
 626        } else {
 627          this.markValid(element);
 628        }
 629      }
 630    }
 631  
 632    validate(element) {
 633      let tagName; // Ignore the element if its currently disabled,
 634      // because are not submitted for the http-request.
 635      // For those case return always true.
 636  
 637      if (element.getAttribute('disabled') === 'disabled' || element.getAttribute('display') === 'none') {
 638        this.handleResponse(true, element);
 639        return true;
 640      } // If the field is required make sure it has a value
 641  
 642  
 643      if (element.getAttribute('required') || element.classList.contains('required')) {
 644        tagName = element.tagName.toLowerCase();
 645  
 646        if (tagName === 'fieldset' && (element.classList.contains('radio') || element.classList.contains('checkboxes'))) {
 647          // No options are checked.
 648          if (element.querySelector('input:checked') === null) {
 649            this.handleResponse(false, element, 'checkbox');
 650            return false;
 651          }
 652        } else if (element.getAttribute('type') === 'checkbox' && element.checked !== true || tagName === 'select' && !element.value.length) {
 653          this.handleResponse(false, element, 'checkbox');
 654          return false;
 655        } else if (!element.value || element.classList.contains('placeholder')) {
 656          // If element has class placeholder that means it is empty.
 657          this.handleResponse(false, element, 'value');
 658          return false;
 659        }
 660      } // Only validate the field if the validate class is set
 661  
 662  
 663      const handler = element.getAttribute('class') && element.getAttribute('class').match(/validate-([a-zA-Z0-9_-]+)/) ? element.getAttribute('class').match(/validate-([a-zA-Z0-9_-]+)/)[1] : '';
 664  
 665      if (element.getAttribute('pattern') && element.getAttribute('pattern') !== '') {
 666        if (element.value.length) {
 667          const isValid = new RegExp(`^$element.getAttribute('pattern')}$`).test(element.value);
 668          this.handleResponse(isValid, element, 'empty');
 669          return isValid;
 670        }
 671  
 672        if (element.hasAttribute('required') || element.classList.contains('required')) {
 673          this.handleResponse(false, element, 'empty');
 674          return false;
 675        }
 676  
 677        this.handleResponse(true, element);
 678        return true;
 679      }
 680  
 681      if (handler === '') {
 682        this.handleResponse(true, element);
 683        return true;
 684      } // Check the additional validation types
 685  
 686  
 687      if (handler && handler !== 'none' && this.handlers[handler] && element.value) {
 688        // Execute the validation handler and return result
 689        if (this.handlers[handler].exec(element.value, element) !== true) {
 690          this.handleResponse(false, element, 'invalid_value');
 691          return false;
 692        }
 693      } // Return validation state
 694  
 695  
 696      this.handleResponse(true, element);
 697      return true;
 698    }
 699  
 700    isValid(form) {
 701      let valid = true;
 702      let message;
 703      let error;
 704      const invalid = []; // Validate form fields
 705  
 706      const fields = [].slice.call(form.querySelectorAll('input, textarea, select, button, fieldset'));
 707      fields.forEach(field => {
 708        if (this.validate(field) === false) {
 709          valid = false;
 710          invalid.push(field);
 711        }
 712      }); // Run custom form validators if present
 713  
 714      if (Object.keys(this.customValidators).length) {
 715        Object.keys(this.customValidators).foreach(key => {
 716          if (this.customValidators[key].exec() !== true) {
 717            valid = false;
 718          }
 719        });
 720      }
 721  
 722      if (!valid && invalid.length > 0) {
 723        if (form.getAttribute('data-validation-text')) {
 724          message = form.getAttribute('data-validation-text');
 725        } else {
 726          message = Joomla.Text._('JLIB_FORM_CONTAINS_INVALID_FIELDS');
 727        }
 728  
 729        error = {
 730          error: [message]
 731        };
 732        Joomla.renderMessages(error);
 733      }
 734  
 735      return valid;
 736    }
 737  
 738    attachToForm(form) {
 739      const elements = [].slice.call(form.querySelectorAll('input, textarea, select, button, fieldset')); // Iterate through the form object and attach the validate method to all input fields.
 740  
 741      elements.forEach(element => {
 742        const tagName = element.tagName.toLowerCase();
 743  
 744        if (['input', 'textarea', 'select', 'fieldset'].indexOf(tagName) > -1 && element.classList.contains('required')) {
 745          element.setAttribute('required', '');
 746        } // Attach isValid method to submit button
 747  
 748  
 749        if ((tagName === 'input' || tagName === 'button') && (element.getAttribute('type') === 'submit' || element.getAttribute('type') === 'image')) {
 750          if (element.classList.contains('validate')) {
 751            element.addEventListener('click', () => this.isValid(form));
 752          }
 753        } else if (tagName !== 'button' && !(tagName === 'input' && element.getAttribute('type') === 'button')) {
 754          // Attach validate method only to fields
 755          if (tagName !== 'fieldset') {
 756            element.addEventListener('blur', ({
 757              target
 758            }) => this.validate(target));
 759            element.addEventListener('focus', ({
 760              target
 761            }) => this.removeMarking(target));
 762  
 763            if (element.classList.contains('validate-email') && this.inputEmail) {
 764              element.setAttribute('type', 'email');
 765            }
 766          }
 767        }
 768      });
 769    }
 770  
 771  }
 772  
 773  const initialize = () => {
 774    document.formvalidator = new JFormValidator(); // Cleanup
 775  
 776    document.removeEventListener('DOMContentLoaded', initialize);
 777  };
 778  
 779  document.addEventListener('DOMContentLoaded', initialize);


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer