1(function () { 2 'use strict'; 3 4 if (typeof aioLoginOtp === 'undefined') { 5 return; 6 } 7 8 var state = { 9 channel: '', 10 token: '', 11 length: 4, 12 resendIn: 0, 13 resendTimer: null, 14 redirectUrl: '', 15 redirectTimer: null, 16 redirectStarted: false, 17 verifyBlocked: false, 18 }; 19 20 var loginEl = document.getElementById('login'); 21 var panel = document.getElementById('aio-login-otp-panel'); 22 23 if (!panel) { 24 return; 25 } 26 27 function mountPanelInsideLoginform() { 28 var body = document.body.classList; 29 return body.contains('aio-login__template-09') 30 || body.contains('aio-login__template-06') 31 || body.contains('aio-login__template-01'); 32 } 33 34 function mountPanelInLogin() { 35 if (!loginEl) { 36 loginEl = document.getElementById('login'); 37 } 38 if (!loginEl || !panel) { 39 return; 40 } 41 var form = document.getElementById('loginform'); 42 if (mountPanelInsideLoginform() && form) { 43 if (panel.parentNode === form) { 44 return; 45 } 46 if (document.body.classList.contains('aio-login__template-09')) { 47 var logo = form.querySelector('h1'); 48 if (logo) { 49 form.insertBefore(panel, logo.nextSibling); 50 return; 51 } 52 } 53 form.insertBefore(panel, form.firstChild); 54 return; 55 } 56 if (form && form.parentNode) { 57 if (panel.parentNode === form.parentNode && panel.nextElementSibling === form) { 58 return; 59 } 60 form.parentNode.insertBefore(panel, form); 61 return; 62 } 63 if (panel.parentNode !== loginEl) { 64 loginEl.insertBefore(panel, loginEl.firstChild); 65 } 66 } 67 68 mountPanelInLogin(); 69 70 /** 71 * Place OTP buttons after username/password and before social login (all templates). 72 */ 73 function positionOtpButtons() { 74 var form = document.getElementById('loginform'); 75 if (!form) { 76 return; 77 } 78 var wrapper = form.querySelector('.aio-login-otp-buttons-wrapper'); 79 if (!wrapper) { 80 return; 81 } 82 var magicLink = form.querySelector('.aio-login-magic-link-buttons-wrapper'); 83 if (magicLink && magicLink.parentNode === form) { 84 form.insertBefore(wrapper, magicLink); 85 return; 86 } 87 var social = form.querySelector('.aio-login-social-login-buttons-wrapper'); 88 if (social && social.parentNode === form) { 89 form.insertBefore(wrapper, social); 90 return; 91 } 92 var remember = form.querySelector('p.forgetmenot'); 93 if (remember && remember.parentNode === form) { 94 form.insertBefore(wrapper, remember); 95 return; 96 } 97 var submit = form.querySelector('p.submit'); 98 if (submit && submit.parentNode === form) { 99 form.insertBefore(wrapper, submit); 100 } 101 } 102 103 positionOtpButtons(); 104 if (typeof window.aioLoginPositionFormCaptchas === 'function') { 105 window.aioLoginPositionFormCaptchas(); 106 } 107 if (document.readyState === 'loading') { 108 document.addEventListener('DOMContentLoaded', function () { 109 mountPanelInLogin(); 110 positionOtpButtons(); 111 if (typeof window.aioLoginPositionFormCaptchas === 'function') { 112 window.aioLoginPositionFormCaptchas(); 113 } 114 }); 115 } 116 117 var steps = { 118 contact: panel.querySelector('[data-step="contact"]'), 119 verify: panel.querySelector('[data-step="verify"]'), 120 success: panel.querySelector('[data-step="success"]'), 121 }; 122 123 var els = { 124 title: panel.querySelector('.aio-login-otp-step--contact .aio-login-otp-panel__title'), 125 contactError: panel.querySelector('.aio-login-otp-step--contact .aio-login-otp-panel__error'), 126 verifyError: panel.querySelector('.aio-login-otp-step--verify .aio-login-otp-panel__error'), 127 emailWrap: panel.querySelector('.aio-login-otp-field--email'), 128 smsWrap: panel.querySelector('.aio-login-otp-field--sms'), 129 email: document.getElementById('aio-login-otp-email'), 130 country: document.getElementById('aio-login-otp-country'), 131 phone: document.getElementById('aio-login-otp-phone'), 132 phoneDial: document.getElementById('aio-login-otp-phone-dial'), 133 captcha: panel.querySelector('.aio-login-otp-captcha'), 134 sendBtn: panel.querySelector('.aio-login-otp-send'), 135 verifyBtn: panel.querySelector('.aio-login-otp-verify'), 136 otpCode: document.getElementById('aio-login-otp-code'), 137 resendBtn: panel.querySelector('.aio-login-otp-resend'), 138 resendTimer: panel.querySelector('.aio-login-otp-resend-timer'), 139 continueBtn: panel.querySelector('.aio-login-otp-continue'), 140 backToLoginWrap: panel.querySelector('.aio-login-otp-panel__back-wrap'), 141 }; 142 143 function showError(el, message, useHtml) { 144 if (!el) { 145 return; 146 } 147 if (useHtml) { 148 el.innerHTML = message || ''; 149 } else { 150 el.textContent = message || ''; 151 } 152 el.hidden = !message; 153 } 154 155 function formatNotRegisteredError(data) { 156 var msg = (data && data.message) ? data.message : ''; 157 var registerUrl = (data && data.register_url) || (aioLoginOtp && aioLoginOtp.registerUrl) || ''; 158 if (!registerUrl) { 159 return msg; 160 } 161 var label = (aioLoginOtp.i18n && aioLoginOtp.i18n.registerLink) ? aioLoginOtp.i18n.registerLink : 'Register'; 162 return msg + ' <a href="' + registerUrl.replace(/"/g, '"') + '">' + label + '</a>'; 163 } 164 165 function showStep(name) {
166 Object.keys(steps).forEach(function (key) { 167 if (!steps[key]) { 168 return; 169 } 170 steps[key].hidden = key !== name; 171 }); 172 if (els.backToLoginWrap) { 173 els.backToLoginWrap.hidden = name === 'success'; 174 } 175 if (name === 'verify' && els.otpCode) { 176 setTimeout(function () { 177 els.otpCode.focus(); 178 }, 0); 179 } 180 } 181 182 function showOtpView() { 183 if (!loginEl) { 184 loginEl = document.getElementById('login'); 185 } 186 if (loginEl) { 187 loginEl.classList.add('aio-login-otp-view--active'); 188 } 189 panel.hidden = false; 190 panel.setAttribute('aria-hidden', 'false'); 191 } 192 193 function showLoginView() { 194 if (loginEl) { 195 loginEl.classList.remove('aio-login-otp-view--active'); 196 } 197 panel.hidden = true; 198 panel.setAttribute('aria-hidden', 'true'); 199 state.channel = ''; 200 state.token = ''; 201 state.verifyBlocked = false; 202 state.redirectUrl = ''; 203 state.redirectStarted = false; 204 if (state.redirectTimer) { 205 clearTimeout(state.redirectTimer); 206 state.redirectTimer = null; 207 } 208 clearResendTimer(); 209 showError(els.contactError, ''); 210 showError(els.verifyError, ''); 211 } 212 213 function clearResendTimer() { 214 if (state.resendTimer) { 215 clearInterval(state.resendTimer); 216 state.resendTimer = null; 217 } 218 } 219 220 function populateCountries() { 221 if (!els.country || !aioLoginOtp.countryCodes) { 222 return; 223 } 224 els.country.innerHTML = ''; 225 var defaultIso = aioLoginOtp.defaultCountryIso || ''; 226 var defaultCode = aioLoginOtp.defaultCountry || ''; 227 aioLoginOtp.countryCodes.forEach(function (c) { 228 var opt = document.createElement('option'); 229 opt.value = c.code; 230 opt.textContent = c.label; 231 opt.dataset.iso = c.iso; 232 if (defaultIso && c.iso === defaultIso) { 233 opt.selected = true; 234 } else if (!defaultIso && c.code === defaultCode) { 235 opt.selected = true; 236 } 237 els.country.appendChild(opt); 238 }); 239 syncDialDisplay(); 240 } 241 242 function configureOtpField(len) { 243 if (!els.otpCode) { 244 return; 245 } 246 state.length = len || 4; 247 els.otpCode.maxLength = state.length; 248 els.otpCode.value = ''; 249 els.otpCode.setAttribute( 250 'aria-label', 251 (aioLoginOtp.i18n && aioLoginOtp.i18n.otpLabel) ? aioLoginOtp.i18n.otpLabel : 'Verification code' 252 ); 253 updateVerifyEnabled(); 254 } 255 256 function bindOtpCodeField() { 257 if (!els.otpCode || els.otpCode.dataset.bound === '1') { 258 return; 259 } 260 els.otpCode.dataset.bound = '1'; 261 els.otpCode.addEventListener('input', function () { 262 els.otpCode.value = els.otpCode.value.replace(/\D/g, '').slice(0, state.length); 263 updateVerifyEnabled(); 264 }); 265 els.otpCode.addEventListener('keydown', function (e) { 266 if (e.key === 'Enter') { 267 e.preventDefault(); 268 tryVerify(); 269 } 270 }); 271 els.otpCode.addEventListener('paste', function (e) { 272 e.preventDefault(); 273 var pasted = (e.clipboardData || window.clipboardData).getData('text').replace(/\D/g, '').slice(0, state.length); 274 els.otpCode.value = pasted; 275 updateVerifyEnabled(); 276 }); 277 } 278 279 function getOtpValue() { 280 if (!els.otpCode) { 281 return ''; 282 } 283 return els.otpCode.value.replace(/\D/g, ''); 284 } 285 286 function clearOtpInputs() { 287 if (!els.otpCode) { 288 return; 289 } 290 els.otpCode.value = ''; 291 els.otpCode.focus(); 292 updateVerifyEnabled(); 293 } 294 295 function digitsOnly(value) { 296 return String(value || '').replace(/\D/g, ''); 297 } 298 299 function currentDialCode() { 300 if (!els.country) { 301 return ''; 302 } 303 return String(els.country.value || ''); 304 } 305 306 function currentCountryIso() { 307 if (!els.country || !els.country.selectedOptions || !els.country.selectedOptions[0]) { 308 return ''; 309 } 310 return els.country.selectedOptions[0].dataset.iso || ''; 311 } 312 313 function syncDialDisplay() { 314 if (els.phoneDial) { 315 els.phoneDial.textContent = currentDialCode(); 316 } 317 } 318 319 function normalizeLocalPhoneDigits(raw) { 320 return digitsOnly(raw); 321 } 322 323 function parsePastedPhone(pasted) { 324 var raw = String(pasted || '').trim(); 325 var ccDigits = digitsOnly(currentDialCode()); 326 var numDigits = digitsOnly(raw); 327 328 if (raw.indexOf('+') === 0 && ccDigits !== '' && numDigits.indexOf(ccDigits) === 0) { 329 return numDigits.slice(ccDigits.length); 330 } 331 332 // Full international paste without "+" (e.g. 16465180948 for +1). 333 if (ccDigits !== '' && numDigits.length > ccDigits.length + 6 && numDigits.indexOf(ccDigits) === 0) { 334 return numDigits.slice(ccDigits.length); 335 } 336 337 return numDigits; 338 } 339 340 function getInvalidPhoneMessage() { 341 return (aioLoginOtp.i18n && aioLoginOtp.i18n.invalidPhone) 342 ? aioLoginOtp.i18n.invalidPhone 343 : 'Please enter a valid phone number.'; 344 } 345 346 function validatePhone(showFieldError) { 347 if (!els.phone) { 348 return { valid: false, digits: '', e164: '' }; 349 } 350 351 var ccDigits = digitsOnly(currentDialCode()); 352 var numDigits = normalizeLocalPhoneDigits(els.phone.value); 353 354 if (numDigits === '') { 355 if (showFieldError) {
356 showError(els.contactError, getInvalidPhoneMessage()); 357 } 358 return { valid: false, digits: '', e164: '' }; 359 } 360 361 var e164 = '+' + ccDigits + numDigits; 362 if (e164.length < 8 || e164.length > 16) { 363 if (showFieldError) { 364 showError(els.contactError, getInvalidPhoneMessage()); 365 } 366 return { valid: false, digits: numDigits, e164: e164 }; 367 } 368 369 return { valid: true, digits: numDigits, e164: e164 }; 370 } 371 372 function sanitizePhoneInput() { 373 if (!els.phone) { 374 return; 375 } 376 var normalized = normalizeLocalPhoneDigits(els.phone.value); 377 if (els.phone.value !== normalized) { 378 els.phone.value = normalized; 379 } 380 } 381 382 function updateSendEnabled() { 383 if (!els.sendBtn) { 384 return; 385 } 386 var ok = false; 387 if (state.channel === 'email') { 388 ok = els.email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(els.email.value.trim()); 389 } else if (state.channel === 'sms') { 390 ok = validatePhone(false).valid; 391 } 392 if (ok && aioLoginOtp.captchaRequired && window.aioLoginPasswordlessCaptcha) { 393 ok = window.aioLoginPasswordlessCaptcha.satisfied(els.captcha, aioLoginOtp.captcha); 394 } 395 els.sendBtn.disabled = !ok; 396 } 397 398 function updateVerifyEnabled() { 399 if (!els.verifyBtn) { 400 return; 401 } 402 if (state.verifyBlocked) { 403 els.verifyBtn.disabled = true; 404 return; 405 } 406 els.verifyBtn.disabled = getOtpValue().length !== state.length; 407 } 408 409 function renderCaptcha() { 410 if (!window.aioLoginPasswordlessCaptcha) { 411 return; 412 } 413 window.aioLoginPasswordlessCaptcha.render(els.captcha, aioLoginOtp.captcha, updateSendEnabled); 414 } 415 416 function appendCaptchaToFormData(fd) { 417 if (window.aioLoginPasswordlessCaptcha) { 418 return window.aioLoginPasswordlessCaptcha.appendToFormDataAsync(els.captcha, aioLoginOtp.captcha, fd); 419 } 420 if (!aioLoginOtp.captcha || !aioLoginOtp.captcha.provider) { 421 return Promise.resolve(); 422 } 423 var p = aioLoginOtp.captcha.provider; 424 if (p === 'recaptcha' && aioLoginOtp.captcha.version === 'v3' && window.aioLoginPasswordlessCaptcha) { 425 return window.aioLoginPasswordlessCaptcha.getRecaptchaV3Token(aioLoginOtp.captcha, 'login').then(function (token) { 426 fd.append('g-recaptcha-response', token); 427 }); 428 } 429 if (p === 'recaptcha') { 430 var resp = ''; 431 if (window.grecaptcha) { 432 resp = window.grecaptcha.getResponse(); 433 } 434 fd.append('g-recaptcha-response', resp); 435 } else if (p === 'hcaptcha' && window.hcaptcha) { 436 fd.append('h-captcha-response', window.hcaptcha.getResponse()); 437 } else if (p === 'turnstile') { 438 var el = els.captcha.querySelector('[name="cf-turnstile-response"]'); 439 if (el) { 440 fd.append('cf-turnstile-response', el.value); 441 } 442 } 443 return Promise.resolve(); 444 } 445 446 function ajax(action, data) { 447 var fd = new FormData(); 448 fd.append('action', action); 449 fd.append('nonce', aioLoginOtp.nonce); 450 Object.keys(data).forEach(function (k) { 451 fd.append(k, data[k]); 452 }); 453 return appendCaptchaToFormData(fd).then(function () { 454 return fetch(aioLoginOtp.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' }).then(function (r) { 455 return r.json(); 456 }); 457 }); 458 } 459 460 function startResendCountdown(seconds) { 461 clearResendTimer(); 462 state.resendIn = seconds; 463 if (els.resendBtn) { 464 els.resendBtn.hidden = true; 465 els.resendBtn.disabled = true; 466 } 467 if (els.resendTimer) { 468 els.resendTimer.hidden = false; 469 els.resendTimer.textContent = aioLoginOtp.i18n.resendIn.replace('%d', String(seconds)); 470 } 471 state.resendTimer = setInterval(function () { 472 state.resendIn -= 1; 473 if (state.resendIn <= 0) { 474 clearResendTimer(); 475 if (els.resendTimer) { 476 els.resendTimer.hidden = true; 477 } 478 if (els.resendBtn) { 479 els.resendBtn.hidden = false; 480 els.resendBtn.disabled = false; 481 } 482 return; 483 } 484 if (els.resendTimer) { 485 els.resendTimer.textContent = aioLoginOtp.i18n.resendIn.replace('%d', String(state.resendIn)); 486 } 487 }, 1000); 488 } 489 490 function openChannel(channel) { 491 if (channel === 'sms' && (!aioLoginOtp.countryCodes || !aioLoginOtp.countryCodes.length)) { 492 showError( 493 els.contactError, 494 (aioLoginOtp.i18n && aioLoginOtp.i18n.noSmsCountries) 495 ? aioLoginOtp.i18n.noSmsCountries 496 : 'SMS login is not available.' 497 ); 498 return; 499 } 500 state.channel = channel; 501 state.token = ''; 502 state.verifyBlocked = false;
503 showError(els.contactError, ''); 504 showError(els.verifyError, ''); 505 if (els.emailWrap) { 506 els.emailWrap.hidden = channel !== 'email'; 507 } 508 if (els.smsWrap) { 509 els.smsWrap.hidden = channel !== 'sms'; 510 } 511 if (els.title) { 512 els.title.textContent = channel === 'email' ? aioLoginOtp.i18n.continueEmail : aioLoginOtp.i18n.continueSms; 513 } 514 showStep('contact'); 515 showOtpView(); 516 renderCaptcha(); 517 if (channel === 'sms') { 518 syncDialDisplay(); 519 } 520 updateSendEnabled(); 521 setTimeout(function () { 522 renderCaptcha(); 523 updateSendEnabled(); 524 if (channel === 'email' && els.email) { 525 els.email.focus(); 526 } 527 if (channel === 'sms' && els.phone) { 528 els.phone.focus(); 529 } 530 }, 80); 531 } 532 533 function showVerifyBlocked(message, length) { 534 state.verifyBlocked = true; 535 showError(els.contactError, ''); 536 configureOtpField(length || 4); 537 showStep('verify'); 538 showError(els.verifyError, message); 539 if (els.verifyBtn) { 540 els.verifyBtn.disabled = true; 541 } 542 if (els.resendBtn) { 543 els.resendBtn.hidden = true; 544 els.resendBtn.disabled = true; 545 } 546 if (els.resendTimer) { 547 els.resendTimer.hidden = true; 548 } 549 } 550 551 function doSend() { 552 var payload = { channel: state.channel }; 553 if (state.channel === 'email') { 554 payload.email = els.email.value.trim(); 555 } else { 556 var phoneData = validatePhone(true); 557 if (!phoneData.valid) { 558 updateSendEnabled(); 559 return; 560 } 561 payload.country_code = els.country.value; 562 payload.phone = phoneData.digits; 563 payload.country_iso = currentCountryIso(); 564 } 565 els.sendBtn.disabled = true; 566 ajax('aio_login_otp_send', payload).then(function (res) { 567 els.sendBtn.disabled = false; 568 if (!res.success) { 569 if (res.data && res.data.code === 'verify_blocked') { 570 showVerifyBlocked(res.data.message, res.data.length); 571 return; 572 } 573 if (res.data && res.data.code === 'not_registered') { 574 showError(els.contactError, formatNotRegisteredError(res.data), true); 575 } else { 576 showError(els.contactError, (res.data && res.data.message) || 'Error'); 577 } 578 return; 579 } 580 state.token = res.data.token; 581 state.length = res.data.length || 4; 582 state.verifyBlocked = false; 583 configureOtpField(state.length); 584 showStep('verify'); 585 startResendCountdown(res.data.resend_in || 60); 586 clearOtpInputs(); 587 }).catch(function () { 588 els.sendBtn.disabled = false; 589 showError(els.contactError, 'Network error'); 590 }); 591 } 592 593 function goToRedirect() { 594 if (!state.redirectUrl || state.redirectStarted) { 595 return; 596 } 597 state.redirectStarted = true; 598 if (state.redirectTimer) { 599 clearTimeout(state.redirectTimer); 600 state.redirectTimer = null; 601 } 602 window.location.href = state.redirectUrl; 603 } 604 605 function enableResendNow() { 606 clearResendTimer(); 607 if (els.resendBtn) { 608 els.resendBtn.hidden = false; 609 els.resendBtn.disabled = false; 610 } 611 if (els.resendTimer) { 612 els.resendTimer.hidden = true; 613 } 614 } 615 616 function isVerifyBlockedResponse(data) { 617 if (!data || !data.code) { 618 return false; 619 } 620 return data.code === 'verify_blocked' || data.code === 'ip_blocked' || data.code === 'max_attempts'; 621 } 622 623 function tryVerify() { 624 if (els.verifyBtn.disabled || state.verifyBlocked) { 625 return; 626 } 627 els.verifyBtn.disabled = true; 628 ajax('aio_login_otp_verify', { token: state.token, otp: getOtpValue() }).then(function (res) { 629 if (!res.success) { 630 if (isVerifyBlockedResponse(res.data)) { 631 showVerifyBlocked((res.data && res.data.message) || 'Error', state.length); 632 return; 633 } 634 if (res.data && res.data.code === 'otp_expired') { 635 enableResendNow(); 636 } 637 els.verifyBtn.disabled = false; 638 updateVerifyEnabled(); 639 showError(els.verifyError, (res.data && res.data.message) || 'Error'); 640 clearOtpInputs(); 641 return; 642 } 643 state.redirectUrl = res.data.redirect || ''; 644 if (res.data.requires_2fa && aioLoginOtp.i18n) { 645 var successTitle = steps.success && steps.success.querySelector('.aio-login-otp-panel__title'); 646 var successMessage = steps.success && steps.success.querySelector('.aio-login-otp-success-message'); 647 if (successTitle && aioLoginOtp.i18n.successTitle2fa) { 648 successTitle.textContent = aioLoginOtp.i18n.successTitle2fa; 649 } 650 if (successMessage && aioLoginOtp.i18n.successMessage2fa) { 651 successMessage.textContent = aioLoginOtp.i18n.successMessage2fa; 652 } 653 } 654 showStep('success'); 655 if (state.redirectTimer) { 656 clearTimeout(state.redirectTimer); 657 } 658 state.redirectStarted = false;
659 state.redirectTimer = setTimeout(function () { 660 goToRedirect(); 661 }, res.data.requires_2fa ? 800 : 1500); 662 }).catch(function () { 663 els.verifyBtn.disabled = false; 664 showError(els.verifyError, 'Network error'); 665 }); 666 } 667 668 function doResend() { 669 els.resendBtn.disabled = true; 670 ajax('aio_login_otp_resend', { token: state.token }).then(function (res) { 671 if (!res.success) { 672 if (res.data && res.data.code === 'verify_blocked') { 673 showVerifyBlocked(res.data.message, res.data.length); 674 return; 675 } 676 if (res.data && res.data.code === 'invalid_session') { 677 showError(els.verifyError, (res.data && res.data.message) || 'Error'); 678 return; 679 } 680 if (res.data && res.data.resend_in) { 681 startResendCountdown(res.data.resend_in); 682 } else { 683 els.resendBtn.disabled = false; 684 } 685 showError(els.verifyError, (res.data && res.data.message) || 'Error'); 686 return; 687 } 688 state.verifyBlocked = false; 689 startResendCountdown(res.data.resend_in || 60); 690 clearOtpInputs(); 691 showError(els.verifyError, ''); 692 updateVerifyEnabled(); 693 }).catch(function () { 694 els.resendBtn.disabled = false; 695 showError(els.verifyError, 'Network error'); 696 }); 697 } 698 699 document.querySelectorAll('.aio-login-otp-launcher').forEach(function (btn) { 700 btn.addEventListener('click', function () { 701 openChannel(btn.getAttribute('data-channel')); 702 }); 703 }); 704 705 panel.querySelectorAll('[data-otp-back-to-login]').forEach(function (btn) { 706 btn.addEventListener('click', showLoginView); 707 }); 708 709 panel.querySelectorAll('[data-otp-back]').forEach(function (btn) { 710 btn.addEventListener('click', function () { 711 if (!steps.verify.hidden) { 712 showStep('contact'); 713 showError(els.verifyError, ''); 714 return; 715 } 716 showLoginView(); 717 }); 718 }); 719 720 if (els.sendBtn) { 721 els.sendBtn.addEventListener('click', doSend); 722 } 723 if (els.verifyBtn) { 724 els.verifyBtn.addEventListener('click', tryVerify); 725 } 726 if (els.resendBtn) { 727 els.resendBtn.addEventListener('click', doResend); 728 } 729 if (els.continueBtn) { 730 els.continueBtn.addEventListener('click', goToRedirect); 731 } 732 if (els.email) { 733 els.email.addEventListener('input', updateSendEnabled); 734 } 735 if (els.phone) { 736 els.phone.addEventListener('input', function () { 737 sanitizePhoneInput(); 738 showError(els.contactError, ''); 739 updateSendEnabled(); 740 }); 741 els.phone.addEventListener('paste', function (e) { 742 e.preventDefault(); 743 var pasted = (e.clipboardData || window.clipboardData).getData('text'); 744 els.phone.value = parsePastedPhone(pasted); 745 showError(els.contactError, ''); 746 updateSendEnabled(); 747 }); 748 els.phone.addEventListener('keydown', function (e) { 749 if (e.key === 'Enter' && els.sendBtn && !els.sendBtn.disabled) { 750 e.preventDefault(); 751 doSend(); 752 } 753 }); 754 } 755 if (els.country) { 756 els.country.addEventListener('change', function () { 757 syncDialDisplay(); 758 sanitizePhoneInput(); 759 showError(els.contactError, ''); 760 updateSendEnabled(); 761 if (els.phone) { 762 els.phone.focus(); 763 } 764 }); 765 } 766 767 populateCountries(); 768 bindOtpCodeField(); 769})();
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.