import './lib/gt.js'; import './lib/tom-select.complete.min.js'; import { PhoneProviderGeetestCaptcha } from './lib/gtutils.js'; class SmsSenderForm { constructor(type, formSelector) { this.type = type; this.formElem = document.querySelector(formSelector); this.realmUrl = config.get('realmUrl'); this.selectAreaCode = document.querySelector("#areaCodeInput"); this.inputPhoneNumber = document.querySelector("#phoneNumberInput"); this.inputCode = document.querySelector("#smsCodeInput"); this.btnSend = document.querySelector("#btnSendSmsCode"); this.countdown = null; this.resendTimeout = 120; this.areaCodeSelect = null; this.bindEvents(); this.initConfig(); this.initCaptcha(); } bindEvents() { this.btnSend.addEventListener('click', this.onBtnSendClick.bind(this)); this.inputPhoneNumber.addEventListener('input', () => { // Hide error message when user types const errorMsg = this.formElem.querySelector('.pf-v5-c-form__helper-text--error'); if (errorMsg) { errorMsg.style.display = 'none'; } }); this.formElem.addEventListener('submit', () => { if (this.areaCodeSelect) { this.areaCodeSelect.enable(); } }); } initCaptcha() { this.captcha = new PhoneProviderGeetestCaptcha(); } initConfig() { fetch(this.realmUrl + '/sms').then((res) => { if (res.ok) { return res.json(); } else { throw new Error(msg.localize('cannotGetConfig')); } }).then((res) => { this.setAreaCodeList(res.areaCodeList || []); // Initialize TomSelect after data is loaded this.areaCodeSelect = new TomSelect('#areaCodeInput', { maxItems: 1, valueField: 'value', labelField: 'text', searchField: ['text', 'value'], create: false, }); if (res.areaLocked) { this.areaCodeSelect.disable(); } const defaultAreaCode = this.selectAreaCode.getAttribute('data-value') || res.defaultAreaCode || '86'; this.areaCodeSelect.setValue(defaultAreaCode.toString()); }).catch((err) => { console.error(err); this.showError(err.message); }); } getFilledAreaCode(areaCode) { const maxLen = 4; let areaCodeStr = areaCode.toString(); return '+' + areaCodeStr + ('\u2002'.repeat(maxLen - areaCodeStr.length)); } getLocalizedName(nameList, langCode) { if (langCode in nameList) { return nameList[langCode]; } else { return nameList['en']; } } showError(errorMessage) { // Find or create error message element let errorElement = this.formElem.querySelector('.phone-error-message'); if (!errorElement) { errorElement = document.createElement('div'); errorElement.className = 'pf-v5-c-form__helper-text pf-v5-c-form__helper-text--error phone-error-message'; const phoneGroup = this.formElem.querySelector('[name="phoneNumber"]').closest('.pf-v5-c-form__group'); if (phoneGroup) { phoneGroup.appendChild(errorElement); } } errorElement.textContent = errorMessage; errorElement.style.display = 'block'; } setAreaCodeList(areaCodeList) { const langCode = msg.localize('countryNameLangCode'); // Clear existing options this.selectAreaCode.innerHTML = ''; // Add new options areaCodeList.forEach((info) => { const option = document.createElement('option'); option.value = info.areaCode; const displayName = this.getFilledAreaCode(info.areaCode) + '\u2002\u2002' + this.getLocalizedName(info.name, langCode); option.textContent = displayName; this.selectAreaCode.appendChild(option); }); } startCountdown() { let currentTimeout = this.resendTimeout; const resendMsg = msg.localize('resendVerificationCode'); const secondMsg = msg.localize('second'); this.countdown = setInterval(() => { currentTimeout -= 1; if (currentTimeout <= 0) { this.stopCountdown(); return; } this.btnSend.textContent = resendMsg + ' (' + currentTimeout.toString() + secondMsg + ')'; }, 1000); } stopCountdown() { if (this.countdown !== null) { clearInterval(this.countdown); this.countdown = null; this.btnSend.textContent = msg.localize('resendVerificationCode'); this.btnSend.disabled = false; } } checkInput() { if (this.inputPhoneNumber.value.trim() === "") { this.showError(msg.localize('phoneNumberIsEmpty')); return false; } return true; } onBtnSendClick() { if (!this.checkInput()) return; this.captcha.verify().then((data) => { this.btnSend.disabled = true; this.btnSend.textContent = msg.localize('sending'); data.areaCode = this.areaCodeSelect.getValue(); data.phoneNumber = this.inputPhoneNumber.value; fetch(this.realmUrl + '/sms/' + this.type + '-code', { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' }, }).then((res) => { if (res.ok) { return res.json(); } else { throw new Error(msg.localize('sendVerificationError', [res.status + ' ' + res.statusText])); } }).then((res) => { if (res.status == 1) { this.startCountdown(); } else if (res.status == 0) { throw new Error(res.errormsg ? msg.localize(res.errormsg, [res.error]) : res.error); } }).catch((err) => { console.error(err); this.showError(err.message); this.btnSend.disabled = false; this.btnSend.textContent = msg.localize('sendVerificationCode'); }); }).catch((err) => { if (err !== 'close') { // User closed captcha console.error(err); this.showError(typeof err === 'string' ? err : err.message); this.btnSend.disabled = false; this.btnSend.textContent = msg.localize('sendVerificationCode'); } }); } } document.addEventListener('DOMContentLoaded', function () { // Initialize SMS sender form for login window.SmsSenderForm = new SmsSenderForm("login", "#kc-form-login"); // Handle tab switching to set the login type document.addEventListener('pf.tab.activate', function(event) { const tabId = event.detail.tabId; let loginTypeInput = document.querySelector('#loginType'); if (!loginTypeInput) { loginTypeInput = document.createElement('input'); loginTypeInput.type = 'hidden'; loginTypeInput.name = 'loginType'; loginTypeInput.id = 'loginType'; document.querySelector('#kc-form-login').appendChild(loginTypeInput); } loginTypeInput.value = tabId; // 'password' or 'phone' }); // Set initial login type to password (default active tab) const initialLoginType = document.createElement('input'); initialLoginType.type = 'hidden'; initialLoginType.name = 'loginType'; initialLoginType.id = 'loginType'; initialLoginType.value = 'password'; document.querySelector('#kc-form-login').appendChild(initialLoginType); });