PageSourceSearch

https://caosemfome.org.br/wp-content/plugins/mask-form-elementor/assets/js/custom-mask-script.js?ver=4.4.5

js caosemfome.org.br collected 2026-10-10 02:31:20 UTC 39,648 bytes, 1,041 lines download raw bytes

1(function($) {
2    $(document).ready(function() {
3        // ----------------- Global Settings -----------------
4        let currencySymbol = "$"; // Change to any currency symbol dynamically
5        let decimalPlaces = 2; // Adjust between 2 or 3 dynamically
6      
7        // ----------------- Formatting Functions -----------------
8        function formatString(digits, pattern) {
9          let formatted = "";
10          let index = 0;
11          for (let char of pattern) {
12              if (char === "#") {
13                  if (index < digits.length) {
14                      formatted += digits[index++];
15                  } else {
16                      break;
17                  }
18              } else {
19                  formatted += char;
20              }
21          }
22          return formatted;
23        }
24
25        function stripCNPJ(value) {
26          var s = String(value).toUpperCase().replace(/[^A-Z0-9]/g, "");
27          if (s.length <= 12) return s;
28          return s.slice(0, 12) + s.slice(12).replace(/\D/g, "").slice(0, 2);
29        }
30      
31        // ----------------- Money Mask Formatting -----------------
32        function formatMoneyInput(value, type, prefix,input) {
33          let decimalSeparator = type === "C" ? "." : ",";
34          let thousandSeparator = type === "C" ? "," : ".";
35          let rawDigits = value.replace(/\D/g, "");
36          decimalPlaces = 2;
37          if(input !== ''){
38            decimalPlaces = Number(input.dataset.decimalPlaces)
39          }
40      
41          if (rawDigits.length === 0) {
42              return `${prefix}0${decimalSeparator}${"0".repeat(decimalPlaces)}`;
43          }
44      
45          while (rawDigits.length < decimalPlaces + 1) {
46              rawDigits = "0" + rawDigits;
47          }
48      
49          let cents = rawDigits.slice(-decimalPlaces);
50          let wholeNumber = rawDigits.slice(0, -decimalPlaces).replace(/^0+/, "") || "0";
51      
52          wholeNumber = wholeNumber.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
53          return `${prefix}${wholeNumber}${decimalSeparator}${cents}`;
54        }
55      
56        function handleMoneyInput(event) {
57          let input = event.target;
58          let oldValue = input.value;
59          let oldCursorPos = input.selectionStart;
60      
61          let moneyPrefix = input.dataset.moneymaskPrefix
62          let moneymaskFormat = input.dataset.moneymaskFormat
63          let type = (moneymaskFormat === 'dot' ? 'D' : 'C')
64          let newValue = '';
65
66          if(input.value != '$0,00'){
67
68            newValue = formatMoneyInput(input.value, type, moneyPrefix, input);
69          }else{
70            newValue = "";
71          }
72          
73          if (input.value === newValue) return;
74      
75          input.value = newValue;
76          let newCursorPos = oldCursorPos + (newValue.length - oldValue.length);
77          setCaretPosition(input, newCursorPos);
78        }
79      
80        // ----------------- Handling Focus & Blur for Money Mask -----------------
81        $(document).on("focus", ".mask-moneyc", function() {
82          let moneymaskFormat = $(this)[0].dataset.moneymaskFormat
83          let type = (moneymaskFormat === 'dot' ? 'D' : 'C')
84          let decimalSeparator = type === "C" ? "." : ",";
85          let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(decimalPlaces)}`;
86      
87          if ($(this).val().trim() === "") {
88              $(this).val(baseFormat);
89              // setCaretPosition(this, $(this).val().length - (decimalPlaces + 1));
90          }
91        });
92      
93        $(document).on("blur", ".mask-moneyc", function() {
94          let moneymaskFormat = $(this)[0].dataset.moneymaskFormat
95          let type = (moneymaskFormat === 'dot' ? 'D' : 'C')
96          let decimalSeparator = type === "C" ? "." : ",";
97          let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(decimalPlaces)}`;
98      
99          let val = $(this).val().trim();
100      
101          let numericValue = val.replace(new RegExp(`[^0-9${decimalSeparator}]`, "g"), "").replace(decimalSeparator, ".");
102      
103          if (parseFloat(numericValue) === 0 || val === baseFormat) {
104              $(this).val("");
105          }
106      });
107      
108      
109        // ----------------- Formatting Credit Card -----------------
110        function formatCreditCard(digits, formatType) {
111          let cardType = detectCardType(digits);
112          
113          if (cardType === "American Express") {
114              digits = digits.slice(0, 15);
115              return formatType === "space" 
116                  ? formatString(digits, "#### ###### #####")
117                  : formatString(digits, "####-######-#####");
118          } else {
119              digits = digits.slice(0, 16);
120              return formatType === "space" 
121                  ? formatString(digits, "#### #### #### ####")
122                  : formatString(digits, "####-####-####-####");
123          }
124        }
125      
126        // ----------------- Card Logos -----------------
127        const cardLogos = {
128            "Visa":  fmeData.pluginUrl+ "assets/svg-icons/visa-logo.svg",
129            "MasterCard": fmeData.pluginUrl+ "assets/svg-icons/mastercard-logo.svg",
130            "American Express": fmeData.pluginUrl+ "assets/svg-icons/amex-logo.svg",
131            "Discover": fmeData.pluginUrl+ "assets/svg-icons/discover-logo.svg",
132            "JCB": fmeData.pluginUrl+ "assets/svg-icons/jcb-logo.svg",
133            "Diners Club": fmeData.pluginUrl+ "assets/svg-icons/cc-logo.svg",
134            "Maestro": fmeData.pluginUrl+ "assets/svg-icons/maestro-logo.svg",
135            "UnionPay": fmeData.pluginUrl+ "assets/svg-icons/cc-logo.svg",
136            "RuPay": fmeData.pluginUrl+ "assets/svg-icons/repay-logo.svg",
137            "Unknown": fmeData.pluginUrl+ "assets/svg-icons/cc-logo.svg"
138        };
139      
140        // ----------------- Function to Detect Card Type -----------------
141        function detectCardType(number) {
142          const cleaned = number.replace(/\D/g, ""); // Remove non-numeric characters
143      
144          const cardPatterns = {
145              "Visa": /^4/,
146              "MasterCard": /^5[1-5]/, // MasterCard starts with 51-55
147              "American Express": /^3[47]/, // AmEx starts with 34 or 37
148              "Discover": /^6(?:011|5)/, // Discover starts with 6011 or 65
149              "JCB": /^(?:2131|1800|35)/, // JCB starts with 2131, 1800, or 35
150              "Diners Club": /^3(?:0[0-5]|[689])/, // Diners Club starts with 300-305, 36, or 38-39
151              "UnionPay": /^(62|81)/, // UnionPay starts with 62 or 81
152              "RuPay": /^(60|65|81|82|508)/, // RuPay starts with 60, 65, 81, 82, or 508
153              "Maestro": /^(50|5[6-9]|6[0-9])/ // Maestro starts with 50, 56-59, 60-69
154          };
155      
156          for (let card in cardPatterns) {
157              if (cardPatterns[card].test(cleaned)) {
158                  return card;
159              }
160          }
161      
162          return "Unknown"; // Default if no match is found
163        }
164      
165        // ----------------- Function to Update Card Logo Dynamically -----------------
166        function updateCardLogo(inputSelector) {
167          let input = $(inputSelector);
168          let logo = input.siblings(".card-logo"); // Select logo next to input
169          let cardNumber = input.val().replace(/\D/g, ''); // Remove non-digit characters
170      
171          if (cardNumber === "") {
172              logo.hide(); // Hide the logo if the input is empty
173          } else {
174              let cardType = detectCardType(cardNumber);
175              if (cardType in cardLogos) {
176                  logo.attr("src", cardLogos[cardType]).show();
177              } else {
178                  logo.hide(); // Hide logo if card type is unknown
179              }
180          }
181        }
182      
183        // ----------------- Universal Field Formatting -----------------
184        const formatFunctions = {
185          ".mask-cnpj": (digits) => formatString(digits, "##.###.###/####-##"),
186          ".mask-cpf": (digits) => formatString(digits, "###.###.###-##"),
187          ".mask-cep": (digits) => formatString(digits, "#####-###"),
188          ".mask-phus": (digits) => formatString(digits, "(###) ###-####"),
189          ".mask-ph8": (digits) => formatString(digits, "####-####"),
190          ".mask-ddd8": (digits) => formatString(digits, "(##) ####-####"),
191          ".mask-ddd9": (digits) => formatString(digits, "(##) #####-####"),
192          ".mask-dmy": (digits) => formatString(digits, "##/##/####"),
193          ".mask-mdy": (digits) => formatString(digits, "##/##/####"),
194          ".mask-hms": (digits) => formatString(digits, "##:##:##"),
195          ".mask-hm": (digits) => formatString(digits, "##:##"),
196          ".mask-dmyhm": (digits) => formatString(digits, "##/##/#### ##:##"),
197          ".mask-mdyhm": (digits) => formatString(digits, "##/##/#### ##:##"),
198          ".mask-my": (digits) => formatString(digits, "##/####"),
199          ".mask-ccs": (digits) => formatCreditCard(digits, "space"),
200          ".mask-cch": (digits) => formatCreditCard(digits, "hyphen"),
201          ".mask-ccmy": (digits) => formatString(digits, "##/##"),
202          ".mask-ccmyy": (digits) => formatString(digits, "##/####"),
203          ".mask-moneyc": (digits) => formatMoneyInput(digits, "C",'$',''),
204          ".mask-moneyd": (digits) => formatMoneyInput(digits, "D",'$',''),
205          ".mask-ipv4": (digits) => formatString(digits, "###.###.###.###") // New IPv4 Masking
206        };
207      
208        // Apply formatting dynamically for all fields, including money input
208s
209        Object.entries(formatFunctions).forEach(([selector, formatFunction]) => {
210          $(document).on("input focus", selector, function (event) {
211              var input = this;
212              var oldCaret = getCaretPosition(input);
213      
214              // Handle money input separately
215              if ($(input).hasClass("mask-moneyc") || $(input).hasClass("mask-moneyd")) {
216                  handleMoneyInput(event);
217                  return;
218              }
219
220              var isCnpj = $(input).hasClass("mask-cnpj");
221              var rawDigits = isCnpj ? stripCNPJ(input.value) : input.value.replace(/\D/g, "");
222              var digitIndex = getDigitIndexFromCaret(input.value, oldCaret, isCnpj);
223              var newVal = formatFunction(rawDigits);
224              var newCaret = mapDigitIndexToCaret(newVal, digitIndex, isCnpj);
225      
226              if(newVal != '('){
227
228                input.value = newVal;
229              }else{
230                input.value = "";
231              }
232              
233              setCaretPosition(input, newCaret || 0); // Keep caret in place
234      
235              // Update card logo dynamically for credit card fields
236              if ($(input).hasClass("mask-ccs") || $(input).hasClass("mask-cch")) {
237                  updateCardLogo(input);
238              }
239          });
240        });
241      
242        // ----------------- Helper Functions for Caret Management -----------------
243        function getCaretPosition(input) {
244          return input.selectionStart;
245        }
246        function getDigitIndexFromCaret(formattedStr, caretPos, alphanumeric) {
247          var count = 0;
248          var pattern = alphanumeric ? /[A-Za-z0-9]/ : /\d/;
249          for (var i = 0; i < caretPos; i++) {
250            if (pattern.test(formattedStr.charAt(i))) {
251              count++;
252            }
253          }
254          return count;
255        }
256        function mapDigitIndexToCaret(formattedStr, digitIndex, alphanumeric) {
257          var count = 0;
258          var pattern = alphanumeric ? /[A-Za-z0-9]/ : /\d/;
259          for (var i = 0; i < formattedStr.length; i++) {
260            if (pattern.test(formattedStr.charAt(i))) {
261              if (count === digitIndex) {
262                return i;
263              }
264              count++;
265            }
266          }
267          return formattedStr.length;
268        }
269        function setCaretPosition(elem, pos) {
270          if (elem.setSelectionRange) {
271            elem.focus();
272            elem.setSelectionRange(pos, pos);
273          } else if (elem.createTextRange) {
274            var range = elem.createTextRange();
275            range.collapse(true);
276            range.moveEnd('character', pos);
277            range.moveStart('character', pos);
278            range.select();
279          }
280        }
281      
282        // ----------------- Backspace Handling -----------------
283        $(document).on("keydown", "input", function (e) {
284          if (e.key === "Backspace") {
285              var input = this;
286      
287              // Special handling for money inputs
288              if ($(input).hasClass("mask-moneyc") || $(input).hasClass("mask-moneyd")) {
289                  let decimalSeparator = $(input).hasClass("mask-moneyc") ? "." : ",";
290                  let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(decimalPlaces)}`;
291      
292                  if (input.value === baseFormat) {
293                      e.preventDefault(); // Prevent deletion beyond the base format
294                      return;
295                  }
296      
297                  // Handle money input dynamically and prevent default formatting conflict
298                  handleMoneyInput(e);
299                  return;
300              }
301
302              // Standard backspace handling for all other masked inputs
303              if (input.selectionStart !== input.selectionEnd) {
304                  e.preventDefault();
305                  input.value = "";
306                  setCaretPosition(input, 0);
307                  return;
308              }
309      
310              var caretPos = getCaretPosition(input);
311              var isCnpj = $(input).hasClass("mask-cnpj");
312              var digitIndex = getDigitIndexFromCaret(input.value, caretPos, isCnpj);
313              if (digitIndex === 0) return;
314      
315              var rawDigits = isCnpj ? stripCNPJ(input.value) : input.value.replace(/\D/g, "");
316              var newDigits = rawDigits.slice(0, digitIndex - 1) + rawDigits.slice(digitIndex);
317      
318              // Find matching class for format function
319              let matchedClass = Object.keys(formatFunctions).find(cls => $(input).hasClass(cls.substring(1)));
320      
321              if (matchedClass) {
322                  var formatted = formatFunctions[matchedClass](newDigits);
323                  input.value = formatted;
324                  var newCaretPos = mapDigitIndexToCaret(formatted, digitIndex - 1, isCnpj);
325                  setCaretPosition(input, newCaretPos);
326              }
327          }
328        });
329        
330        // ----------------- Error Validation -----------------
331        function validateInput(selector, errorClass, validationFunction, errorMessage) {
332          $(document).on("blur", selector, function () {
333              var input = $(this);
334              if(input.hasClass("hide-fme-mask-input")){
335                return;
336              }
337              var val = input.val();
338              var errorElement = input.closest('.elementor-field-group').find("." + errorClass);
339      
340              // Remove predefined symbol if it's the ONLY character left
341              if (val.length === 1 && !/\d/.test(val)) {
342                  input.val(""); 
343                  errorElement.hide().text(""); 
344                  return;
345              }
346      
347              // Show error message if validation fails
348              if (val !== "" && !validationFunction(val)) {
349                  errorElement.text(errorMessage).css("display", "flex").hide().fadeIn(200);
350              } else {
351                  errorElement.fadeOut(100, function () {
352                      $(this).css("display", "none"); 
353                  });
354              }
355          });
356      
357          $(document).on("input", selector, function () {
358              var input = $(this);
359              nextbtnVisibility(errorClass, input, validationFunction);
360              var errorElement = input.closest('.elementor-field-group').find("." + errorClass);
361      
362              if (errorElement.is(":visible")) { 
363                  var val = input.val();
364                  if (validationFunction(val)) {
365                      errorElement.fadeOut(100, function () {
366                          $(this).css("display", "none"); 
367                      });
368                  }
369              }
370          });
371        }
372                
373         // Apply validation dynamically for all fields
374        const validations = {
375          ".mask-cnpj": { errorClass: "error-cnpj", validate: isValidCNPJ, msg: fmeData.errorMessages["mask-cnpj"] },
376          ".mask-cpf": { errorClass: "error-cpf", validate: isValidCPF, msg: fmeData.errorMessages["mask-cpf"] },
377          ".mask-cep": { errorClass: "error-cep", validate: isValidCEP, msg: fmeData.errorMessages["mask-cep"] },
378          ".mask-phus": { errorClass: "error-phus", validate: isValidPhoneUSA, msg: fmeData.errorMessages["mask-phus"] },
379          ".mask-ph8": { errorClass: "error-ph8", validate: isValidPhone8, msg: fmeData.errorMessages["mask-ph8"] },
380          ".mask-ddd8": { errorClass: "error-ddd8", validate: isValidPhoneDDD8, msg: fmeData.errorMessages["mask-ddd8"] },
381          ".mask-ddd9": { errorClass: "error-ddd9", validate: isValidPhoneDDD9, msg: fmeData.errorMessages["mask-ddd9"] },
382          ".mask-dmy": { errorClass: "error-dmy", validate: isValidDateDMY, msg: fmeData.errorMessages["mask-dmy"] },
383          ".mask-mdy": { errorClass: "error-mdy", validate: isValidDateMDY, msg: fmeData.errorMessages["mask-mdy"] },
384          ".mask-hms": { errorClass: "error-hms", validate: isValidTimeHMS, msg: fmeData.errorMessages["mask-hms"] },
385          ".mask-hm": { errorClass: "error-hm", validate: isValidTimeHM, msg: fmeData.errorMessages["mask-hm"] },
386          ".mask-dmyhm": { errorClass: "error-dmyhm", validate: isValidDateDMYHM, msg: fmeData.errorMessages["mask-dmyhm"] },
387          ".mask-mdyhm": { errorClass: "error-mdyhm", validate: isValidDateMDYHM, msg: fmeData.errorMessages["mask-mdyhm"] },
388          ".mask-my": { errorClass: "error-my", validate: isValidDateMY, msg: fmeData.errorMessages["mask-my"] },
389          ".mask-ccs": { errorClass: "error-ccs", validate: isValidCreditCard, msg: fmeData.errorMessages["mask-ccs"] },
390          ".mask-cch": { errorClass: "error-cch", validate: isValidCreditCard, msg: fmeData.errorMessages["mask-cch"] },
391          ".mask-ccmy": { errorClass: "error-ccmy", validate: isValidExpiryMMYY, msg: fmeData.errorMessages["mask-ccmy"] },
392          ".mask-ccmyy": { errorClass: "error-ccmyy", validate: isValidExpiryMMYYYY, msg: fmeData.errorMessages["mask-ccmyy"] },
393          ".mask-ipv4": { errorClass: "error-ipv4", validate: isValidIPv4, msg: fmeData.errorMessages["mask-ipv4"] }
394      };
395      
396      
397        for (const [selector, { errorClass, validate, msg }] of Object.entries(validations)) {
398            validateInput(selector, errorClass, validate, msg);
399        }
400      
401        // ----------------- Validation Functions -----------------
402        function isValidPhoneUSA(phoneStr) {
403          return /^\(\d{3}\) \d{3}-\d{4}$/.test(phoneStr);
404        }
405        function isValidPhone8(phoneStr) {
406          return /^\d{4}-\d{4}$/.test(phoneStr);
407        }
408        function isValidPhoneDDD8(phoneStr) {
409            return /^\(\d{2}\) \d{4}-\d{4}$/.test(phoneStr);
410          }
411        function isValidPhoneDDD9(phoneStr) {
412            return /^\(\d{2}\) 9\d{4}-\d{4}$/.test(phoneStr);
413        }
414      
415        // ----------------- Universal Date & Time Validation Function -----------------
416        function isValidDateTime(value, format) {
417          let regexPattern;
418          let expectedParts;
419      
420          switch (format) {
421              case "DMY": // dd/mm/yyyy
422                  regexPattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
423                  expectedParts = ["day", "month", "year"];
424                  break;
425              case "MDY": // mm/dd/yyyy
426                  regexPattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
427                  expectedParts = ["month", "day", "year"];
428                  break;
429              case "HMS": // hh:mm:ss
430                  regexPattern = /^(\d{2}):(\d{2}):(\d{2})$/;
431                  expectedParts = ["hour", "minute", "second"];
432                  break;
433              case "HM": // hh:mm
434                  regexPattern = /^(\d{2}):(\d{2})$/;
435                  expectedParts = ["hour", "minute"];
436                  break;
437              case "DMY-HM": // dd/mm/yyyy hh:mm
438                  regexPattern = /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})$/;
439                  expectedParts = ["day", "month", "year", "hour", "minute"];
440                  break;
441              case "MDY-HM": // mm/dd/yyyy hh:mm
442                  regexPattern = /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})$/;
443                  expectedParts = ["month", "day", "year", "hour", "minute"];
444                  break;
445              case "MY": // mm/yyyy
446                  regexPattern = /^(\d{2})\/(\d{4})$/;
447                  expectedParts = ["month", "year"];
448                  break;
449              default:
450                  return false; // Unsupported format
451          }
452      
453          // Match input with regex
454          let match = value.match(regexPattern);
455          if (!match) return false;
456      
457          // Extract values dynamically
458          let parts = {};
459          expectedParts.forEach((part, index) => {
460              parts[part] = parseInt(match[index + 1], 10);
461          });
462      
463          // Validate Date (Day, Month, Year)
464          if (parts.year && (parts.year < 1500 || parts.year > 3000)) return false;
465          if (parts.month && (parts.month < 1 || parts.month > 12)) return false;
466          if (parts.day) {
467              let daysInMonth = new Date(parts.year, parts.month, 0).getDate();
468              if (parts.day < 1 || parts.day > daysInMonth) return false;
469          }
470      
471          // Validate Time (Hour, Minute, Second)
472          if (parts.hour && (parts.hour < 0 || parts.hour >= 24)) return false;
473          if (parts.minute && (parts.minute < 0 || parts.minute >= 60)) return false;
474          if (parts.second && (parts.second < 0 || parts.second >= 60)) return false;
475      
476          return true;
477        }
478      
479        // ----------------- Expiry Date Validation -----------------
480        function isValidExpiryDate(value, format) {
481          let regexPattern = format === "MM/YY" ? /^(\d{2})\/(\d{2})$/ : /^(\d{2})\/(\d{4})$/;
482          let match = value.match(regexPattern);
483      
484          if (!match) return false; // Invalid format
485      
486          let month = parseInt(match[1], 10);
487          let year = parseInt(match[2], 10);
488      
489          let currentYear = new Date().getFullYear();
490          let currentMonth = new Date().getMonth() + 1; // JS months are 0-based
491      
492          // Adjust year format if MM/YY (Assume 20XX for past/future dates)
493          if (format === "MM/YY") {
494              year += 2000;
495          }
496      
497          // Validate month (01-12)
498          if (month < 1 || month > 12) return false;
499      
500          // Validate expiry date (must be in the future)
501          if (year < currentYear || (year === currentYear && month < currentMonth)) {
502              return false;
503          }
504      
505          return true;
506        }
507      
508        // ----------------- Exportable Functions Using Universal Validator -----------------
509        function isValidDateDMY(dateStr) { return isValidDateTime(dateStr, "DMY"); }
510        function isValidDateMDY(dateStr) { return isValidDateTime(dateStr, "MDY"); }
511        function isValidTimeHMS(timeStr) { return isValidDateTime(timeStr, "HMS"); }
512        function isValidTimeHM(timeStr) { return isValidDateTime(timeStr, "HM"); }
513        function isValidDateDMYHM(dateTimeStr) { return isValidDateTime(dateTimeStr, "DMY-HM"); }
514        function isValidDateMDYHM(dateTimeStr) { return isValidDateTime(dateTimeStr, "MDY-HM"); }
515        function isValidDateMY(dateStr) { return isValidDateTime(dateStr, "MY"); }
516        function isValidExpiryMMYY(dateStr) { return isValidExpiryDate(dateStr, "MM/YY"); }
517        function isValidExpiryMMYYYY(dateStr) { return isValidExpiryDate(dateStr, "MM/YYYY"); }
518      
519        // Apply validation for credit card fields
520        function isValidCreditCard(cardNumber) {
521          const cleaned = cardNumber.replace(/\D/g, ''); // Remove non-digits
522          if (cleaned.length < 15 || cleaned.length > 16) return false; // AMEX: 15, Others: 16
523          let sum = 0;
524          let shouldDouble = false;
525          // Luhn Algorithm to validate card number
526          for (let i = cleaned.length - 1; i >= 0; i--) {
527              let digit = parseInt(cleaned.charAt(i), 10);
528      
529              if (shouldDouble) {
530                  digit *= 2;
531                  if (digit > 9) digit -= 9;
532              }
533      
534              sum += digit;
535              shouldDouble = !shouldDouble;
536          }
537          return sum % 10 === 0;
538        }
539      
540        // Validate CNPJ (Brazilian Business ID - 14 chars, numeric or alphanumeric)
541        function isValidCNPJ(cnpj) {
542          cnpj = cnpj.toUpperCase().replace(/[.\-\/]/g, '');
543          if (!/^[A-Z0-9]{12}\d{2}$/.test(cnpj)) return false;
544      
545          // Eliminate obvious invalid CNPJs (repeated characters)
546          if (/^(.)\1{13}$/.test(cnpj)) return false;
547      
548          let calcCheckDigit = (cnpj, length) => {
549              let weights = length === 12 ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
550              let sum = 0;
551      
552              for (let i = 0; i < weights.length; i++) {
553                  sum += (cnpj.charCodeAt(i) - 48) * weights[i];
554              }
555      
556              let remainder = sum % 11;
557              return remainder < 2 ? 0 : 11 - remainder;
558          };
559      
560          // Validate both check digits
561          let firstCheck = calcCheckDigit(cnpj, 12);
562          let secondCheck = calcCheckDigit(cnpj.slice(0, 12) + firstCheck, 13);
563      
564          return firstCheck === parseInt(cnpj.charAt(12), 10) && secondCheck === parseInt(cnpj.charAt(13), 10);
565        }
566      
567        // Validate CPF (Brazilian Individual ID - 11 digits)
568        function isValidCPF(cpf) {
569          cpf = cpf.replace(/\D/g, ''); // Remove non-numeric characters
570          if (cpf.length !== 11) return false;
571      
572          // Eliminate obvious invalid CPFs (repeated digits)
573          if (/^(\d)\1+$/.test(cpf)) return false;
574      
575          let calculateDigit = (cpf, length) => {
576              let sum = 0;
577              for (let i = 0; i < length; i++) {
578                  sum += parseInt(cpf.charAt(i)) * (length + 1 - i);
579              }
580              let result = (sum * 10) % 11;
581              return result === 10 ? 0 : result;
582          };
583      
584          let firstDigit = calculateDigit(cpf, 9);
585          let secondDigit = calculateDigit(cpf, 10); // Now correctly includes first digit in the calculation
586      
587          return firstDigit === parseInt(cpf.charAt(9)) && secondDigit === parseInt(cpf.charAt(10));
588        }      
589
590        // Validate CEP (Brazilian Postal Code - XXXXX-XXX)
591        function isValidCEP(cep) {
592          return /^\d{5}
592-\d{3}$/.test(cep); // Matches format XXXXX-XXX
593        }
594      
595        // Validate IPv4 (Format: 192.168.001.001)
596        function isValidIPv4(ip) {
597          // Ensure correct format (###.###.###.###)
598          const ipv4Pattern = /^(?:\d{1,3}\.){3}\d{1,3}$/;
599      
600          if (!ipv4Pattern.test(ip)) return false; // Check general format
601      
602          // Split by dots and validate each octet (0-255)
603          const octets = ip.split(".");
604          return octets.every(octet => {
605              let num = parseInt(octet, 10); // Convert to number
606              return num >= 0 && num <= 255; // Each octet must be between 0-255
607          });
608        }
609      
610        // ----------------- Form Submit -----------------
611
612
613    // start - step field mask error handling
614
615    let maskErrorArr = {};
616    let nextBtnOriginalClicks = {};
617    let recaptchaEvent = {};
618    let submitBtnEvent = {};
619
620
621    const nextbtnVisibility = (errorClass, input, validationFunction) => {
622
623      const closesWidget = input.closest(".elementor-widget-form");
624      const widgetId = closesWidget.data('id');
625      const inuptId = input.attr('id');
626      const fieldStep=input.closest(".elementor-field-type-step");
627      const form = input.closest(".elementor-form");
628
629      const currectStepFields = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)").find('input, textarea, select');
630
631      const submtBtnTag = form.find("button[type='submit']");
632
633      if (!closesWidget.length || !fieldStep.length) {
634        return;
635      }
636
637      const nextBtn = fieldStep.find(".e-form__buttons__wrapper__button[data-direction='next']");
638
639      
640
641      if ((nextBtn.length == 0 && submtBtnTag.length > 0 ) && (submtBtnTag.length == 0 && nextBtn.length == 0) ) {
642        return;
643      }
644
645
646
647      let val = input.val();
648
649      if (val.length === 1 && !/\d/.test(val)) {
650        val = '';
651      }
652
653      // Show error message if validation fails
654      if (  val !== "" && !validationFunction(val)) {
655
656
657        if (closesWidget.length > 0) {
658
659
660          if (!maskErrorArr[widgetId]) {
661            maskErrorArr[widgetId] = [];
662          }
663
664          if (!maskErrorArr[widgetId].includes(inuptId)) {
665            maskErrorArr[widgetId].push(inuptId);
666          }
667        }
668
669        if (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length) {
670
671
672
673          if(nextBtn.length > 0){
674
675            const origninalClicks = jQuery._data(nextBtn[0], "events");
676  
677            if (origninalClicks && (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length === 0)) {
678              nextBtnOriginalClicks[widgetId] = origninalClicks && origninalClicks.click ? origninalClicks.click.map(h => h.handler) : [];
679  
680            }
681          }
682
683
684        }
685      } else {
686        if (maskErrorArr[widgetId] && maskErrorArr[widgetId].includes(inuptId)) {
687          maskErrorArr[widgetId] = maskErrorArr[widgetId].filter(item => item !== inuptId);
688        }
689      }
690
691
692      if (maskErrorArr[widgetId] && maskErrorArr[widgetId].length > 0) {
693
694        if(nextBtn.length > 0){
695
696          const origninalClicks = jQuery._data(nextBtn[0], "events");
697
698        if (origninalClicks && (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length)) {
699
700
701          nextBtnOriginalClicks[widgetId] = origninalClicks && origninalClicks.click ? origninalClicks.click.map(h => h.handler) : [];
702
703        }
704        
705        if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
706          nextBtn.off("click");
707        }
708
709
710        }
711
712
713        
714      } else {
715
716
717        // when no error in mask validation
718
719        if(recaptchaEvent[widgetId]){
720              submtBtnTag.on("click", recaptchaEvent[widgetId]);
721        }
722
723        if(submitBtnEvent[widgetId]){
724          form.on("submit", submitBtnEvent[widgetId]);
725        }
726
727
728
729        if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
730
731
732
733
734          let isfieldsValid = true
735
736          for (let i = 0; i < currectStepFields.length; i++) {
737
738
739            if (currectStepFields[i].checkValidity() == false) {
740
741              isfieldsValid = false;
742              break;
743
744            }
745
746          }
747
748          if (isfieldsValid) {
749
750
751
752
753
754            if(nextBtn.length > 0){
755
756              // 2️⃣ First, clear existing handlers to avoid duplication
757              nextBtn.off("click");
758              
759              // Re-attach original click handlers only once
760              nextBtnOriginalClicks[widgetId].forEach(fn => {
761                nextBtn.one("click", fn); // use .one instead of .on
762              });
763            }
764
765
766          }
767            
768          }
769
770      }
771    }
772
773
774    
775
776    // handle next button event before click
777
778     $(document).on("mousedown", ".e-form__buttons__wrapper__button[data-direction='next']", function(e){
779
780      const form = $(this).closest(".elementor-form");
781
782      const mask_error_div = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)").find('div.mask-error');
783
784      const closesWidget = $(this).closest(".elementor-widget-form");
785      const widgetId = closesWidget.data('id');
786
787      let mask_error = false;
788
789      // finding mask error
790      for(let i=0; i<mask_error_div.length; i++){
791        if(mask_error_div[i].value != "" && mask_error_div[i].style.display == 'flex'){
792          mask_error = true;
793          break;
794        }
795      }
796
797      // reattcach next button event when no mask error
798
799       if (maskErrorArr[widgetId] && !maskErrorArr[widgetId].length > 0 && !mask_error) {
800          // Re-attach original click handlers only once
801            
802          if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
803              $(this).off("click");
804
805              nextBtnOriginalClicks[widgetId].forEach(fn => {
806                $(this).one("click", fn); // use .one instead of .on
807              });
808            }
809
810
811       }
812
813     })
814
815
816     // handling next button event when previous button click
817
818    $(document).on("click", ".e-form__buttons__wrapper__button[data-direction='previous']", function(e){
819
820      const form = $(this).closest(".elementor-form");
821      
822      const closesWidget = form.closest(".elementor-widget-form");
823      const widgetId = closesWidget.data('id');
824      maskErrorArr[widgetId] = [];
825
826      const currectStepFields = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)")
827
828      const nextBtn = currectStepFields.find(".e-form__buttons__wrapper__button[data-direction='next']");
829
830      // attaching events to next button
831      
832      if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
833        nextBtn.off("click");
834
835        nextBtnOriginalClicks[widgetId].forEach(fn => {
836                nextBtn.one("click", fn); // use .one instead of .on
837          });
838
839      }
840
841    })
842
843     // handle mask validation on submit button of step field form
844     $(document).on("mousedown", ".elementor-field-type-submit", function (e){
845
846      var $submitBtn = $(this);
847
848       var $form = $submitBtn.closest("form");
849
850       const closesWidget = $form.closest(".elementor-widget-form");
851      const widgetId = closesWidget.data('id');
852
853      const currectStepFields = $form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)")
854
855      const previousBtn = currectStepFields.find(".e-form__buttons__wrapper__button[data-direction='previous']");
856
857      const inputMaskFields = currectStepFields.find("input.fme-mask-input");
858
859      // run only first time when click on submit button 
860      // if maskerror found in the form widget this will remove submit button and form events to prevent submit
861
862
863      if(previousBtn && previousBtn.length && inputMaskFields && inputMaskFields.length && maskErrorArr[widgetId] && maskErrorArr[widgetId].length){
864
865        var $subBtnTag = $submitBtn.find("button");
866
867      
868      // getting submit button recaptcha and click events
869
870      var $form = $submitBtn.closest("form");
871
872      const origninalclick = jQuery._data($subBtnTag[0], "events");
873
874
875      if(origninalclick && origninalclick.click){
876
877            origninalclick.click.forEach((ele) => {
878            if(ele.handler.toString().trim().includes("onV3FormSubmit")){
879
880              if(!recaptchaEvent[widgetId]){
881
882                recaptchaEvent[widgetId] = ele.handler;
883
884              }
885
886            }
887          })
888
889      }
890
891
892
893
894      // getting form apply step events
895
896      const origninalSubmit = jQuery._data($form[0], "events");
897
898      if(origninalSubmit && origninalSubmit.submit){
899
900          origninalSubmit.submit.forEach((ele) => {
901          if(ele.handler.toString().trim().includes("resetForm")){
902
903            if(!submitBtnEvent[widgetId]){
904
905              submitBtnEvent[widgetId] = ele.handler;
906
907            }
908
909          }
910        })
911
912      }
913
914
915      if(submitBtnEvent[widgetId]){
916          // removing form apply step event
917          $form.off("submit", submitBtnEvent[widgetId]);
918      }
919
920
921      if(recaptchaEvent[widgetId]){
922          // removing submit button recaptcha click events
923          $subBtnTag.off("click", recaptchaEvent[widgetId]);
924      }
925
926
927      }
928    })
929
930    // end - step field mask error handling
931
932
933    // handling form submit of mask field in elementor form
934
935    $(document).on("click", ".elementor-field-type-submit", function (e) {
936
937      var $submitBtn = $(this);
938
939      if ($submitBtn.find('button').hasClass('cfkef-prevent-submit') || $submitBtn.find('button').hasClass('confirmation-pending')
940      ) {
941        return
942      }
943
944      var $form = $submitBtn.closest("form");
945
946      const closesWidget = $form.closest(".elementor-widget-form");
947      const widgetId = closesWidget.data('id');
948      const submtBtnTag = $form.find("button[type='submit']");
949
950      // Prevent double-clicks
951      if ($submitBtn.data("clicked")) {
952        e.preventDefault();
953        return;
954      }
955      $submitBtn.data("clicked", true); // Mark as clicked
956
957      // Trigger blur on inputs to ensure validation runs
958      $form.find("input").trigger("blur");
959
960      // Add Elementor waiting class
961      $form[0].classList.add("elementor-form-waiting");
962
963      // Wait for mask errors or blur logic to complete
964      setTimeout(() => {
965        let hasVisibleMaskError = false;
966
967        // Check for visible mask error messages
968        const $errors = $form.find(".mask-error").filter(function () {
969          return $(this).text().trim() !== "" && $(this).css("display") == "flex";
970      });
971
972      if ($errors.length > 0) {
973        hasVisibleMaskError = true;
974        const $firstError = $errors.first();
975        $("html, body").animate({
976          scrollTop: $firstError.offset().top - 200
977        }, 300);
978      }
979
980      // ✅ Check for empty required masked fields
981      const $emptyRequiredMasked = $form.find("input[required]").filter(function () {
982        if(!$(this).hasClass('hide-fme-mask-input')){
983          const val = $(this).val().trim();
984          const isVisible = $(this).css("display") == "flex";
985          return (val === "" || /^[\s_\-\(\)\.:/]+$/.test(val));
986        }
987      });
988
989      if ($emptyRequiredMasked.length > 0) {
990        hasVisibleMaskError = true;
991        const $firstEmpty = $emptyRequiredMasked.first();
992        $("html, body").animate({
993          scrollTop: $firstEmpty.offset().top - 200
994        }, 300);
995        $firstEmpty.focus();
996      }
997
998      // ❌ Validation failed
999      if (hasVisibleMaskError) {
1000        // $form[0].classList.remove("elementor-form-waiting");
1001        $submitBtn.data("clicked", false);
1002        e.preventDefault();
1003        return;
1004      }
1005
1006      if (!hasVisibleMaskError) {
1007        // ✅ All good — submit the form
1008
1009
1010        if(recaptchaEvent[widgetId]){
1011          submtBtnTag.on("click", recaptchaEvent[widgetId]);
1012          submtBtnTag.trigger("click");
1013        }
1014
1015        if(submitBtnEvent[widgetId]){
1016          $form.on("submit", submitBtnEvent[widgetId]);
1017          submtBtnTag.trigger("click");
1018
1019        }
1020
1021        $form[0].classList.remove("elementor-form-waiting");
1022          $submitBtn.data("clicked", false);
1023          if(!recaptchaEvent[widgetId]){
1024
1025            let error_messages = $form.find('.elementor-form-fields-wrapper').find('.elementor-message');
1026
1027            if(error_messages && error_messages.length == 0){
1028
1029              $form[0].requestSubmit();
1030            }
1031
1032
1033          }
1034      }
1035
1036      }, 500);
1037    });
1038
1039      
1040    });
1041})(jQuery);

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.