/* * IntlTelInput_cb_validation_practical.jQuery * * Validation of International Phone Field Input * extending Joomla/Community Builder (CB) validation * * * Modified: * 02 May 2026: Bob Briscoe * * Derived from https://intl-tel-input.com/examples/validation-practical * (the HTML source not the code snippet on the page) * with integration into Joomla/Community Builder added * * MIT Licence : https://github.com/jackocnr/intl-tel-input#MIT-1-ov-file * * Copyright (c) 2014-2016 Jack O'Connor * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ // Initialize jQuery validation on the form cbjQuery.validator.addMethod("tel", function(value, element) { // If a config error was already flagged during init, return it and block submit if (element.dataset.validationText && element.dataset.validationText.includes('Configuration Error')) { return false; } // intl-tel-input integration const iti = element.itiInstance; if (!iti) return true; // Fallback to default validation if iti not initialized if (iti.isValidNumber()) { // SUCCESS // Sync the E.164 value if the storage field was found during init const e164Field = element.e164Field; if (e164Field) e164Field.value = iti.getNumber(); // jQuery CB validation handles validation success // Then is-valid class + intlTelInput turn border green and add green tick return true; } else { // Check if the field is optional AND empty to override a 'too short' error if (this.optional(element) && value.trim().length === 0) return true; // FAILURE: Standard Error Mapping const errorMap = [ "Invalid number", "Invalid country code", "Too short", "Too long", "Invalid number" ]; const errorCode = iti ? iti.getValidationError() : 0; // Store the error message on the element so the message function can see it element.dataset.validationText = errorMap[errorCode] || "Invalid phone number"; // jQuery CB validation handles validation failure // turns border red, adds red '!' and preventDefault() submit behaviour return false; } }, function(params, element) { return cbjQuery.validator.format(element.dataset.validationText); }); // Toggle the is-valid class on CB highlight / unhighlight events cbjQuery('.cbValidation').on('cbvalidate.highlight', (e, cbvalidate, element) => { cbjQuery(element).removeClass('is-valid'); }).on('cbvalidate.unhighlight', (e, cbvalidate, element) => { cbjQuery(element).addClass('is-valid'); }); /* Attach iti-related properties to each HTML input element within * containers of the validate-tel class */ // Requires: "validate-tel" class on each input element that is to be validated with the tel method. const valTelContainers = document.querySelectorAll(".validate-tel"); valTelContainers.forEach(valTelContainer => { const valTelField = valTelContainer.querySelector('input[type="tel"], input[type="text"]'); const validator = cbjQuery(valTelField.form).data("validator"); if (valTelField) { // Add data-rule-tel attr (not class) for CB validation valTelField.setAttribute('data-rule-tel', 'true'); /* * intl-tel-input initialization * Requires: intlTelInputWithUtils.min.js or similar */ const iti = window.intlTelInput(valTelField, { initialCountry: "gb", }); valTelField.itiInstance = iti; // Attach iti to element to pass it to the validator valTelField.showInteractiveErrors = false; // Mute interactive error messages /* * Find the associated hidden field to store the E.164-formatted international phone number * Requires: "displays-" class on the visible input element */ const targetClass = Array.from(valTelContainer.classList).find(c => c.startsWith('displays-')); if (targetClass) { const e164FieldId = targetClass.replace('displays-', ''); const e164Field = document.getElementById(e164FieldId); if (e164Field) { valTelField.e164Field = e164Field; // Success: Cache it if (e164Field.value) iti.setNumber(e164Field.value); // Reformat E.164 to national } else { const msg = `Configuration Error: Please tell the site operator: Field with ID '${e164FieldId}' not found`; console.error(`[CB Error] ${msg} in DOM, for #${valTelField.id}`); valTelField.dataset.validationText = msg; validator.element(valTelField); } } else { const msg = "Configuration Error: Please tell the site operator: Missing 'displays-' class"; console.error(`[CB Error] ${msg} #${valTelField.id}`); valTelField.dataset.validationText = msg; validator.element(valTelField); } /* * Mute/unmute of interactive validation */ valTelField.addEventListener('countrychange', (e) => { if (e.target.showInteractiveErrors) { // Only trigger CB validation if unmuted validator.element(e.target); } }); // No need for input listener - CB already validates on input after first blur valTelField.addEventListener('blur', (e) => { // Unmute verbosity once user leaves field e.target.showInteractiveErrors = true; // CB validation (for just this field) already triggers on blur }); } else { const msg = `Configuration Error: Please tell the site operator: Nested tel or text input not found`; console.error("[CB Error] ${msg} inside .validate-tel container", valTelContainer); valTelField.dataset.validationText = msg; validator.element(valTelField); return; // Can't set up any phone fields } }); if (valTelContainers.length === 0) { console.log("[CB Configuration Warning] No .validate-tel fields found - exiting early"); return; // Script has been assigned to a page with no tel fields to validate. }