/** Shared authentication state machine for modal and /sign/. */ (function () { 'use strict'; var config = window.aksrtData || {}; var apiRoot = String(config.apiUrl || config.restUrl || '/wp-json/aksrt/v1').replace(/\/$/, ''); var nonce = config.restNonce || ''; function request(path, data) { return fetch(apiRoot + '/auth/' + path, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce }, body: JSON.stringify(data), }).then(function (response) { return response.json().catch(function () { return {}; }).then(function (body) { if (!response.ok || body.success === false) { throw new Error(body.message || (body.data && body.data.message) || '请求失败,请稍后再试'); } return body.data || body; }); }); } function formData(form) { var data = {}; new FormData(form).forEach(function (value, key) { data[key] = value; }); return data; } function escapeHtml(value) { return String(value || '').replace(/[&<>"']/g, function (char) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]; }); } function showToast(text, type) { if (!text) return; window.notyf(escapeHtml(text), type || 'info'); } function activate(root, tab, updateUrl) { var panel = root.querySelector('[data-auth-panel="' + tab + '"]'); if (!panel) return; root.querySelectorAll('[data-auth-panel]').forEach(function (item) { item.hidden = item !== panel; }); root.dataset.activeTab = tab; if (updateUrl && root.dataset.surface === 'page' && window.history && window.URL) { var url = new URL(window.location.href); url.searchParams.set('tab', tab); window.history.replaceState({}, '', url.toString()); } var focus = panel.querySelector('input:not([type="hidden"])'); if (focus) window.setTimeout(function () { focus.focus(); }, 30); if (tab === 'oauth') loadQr(root); } function loadQr(root) { var box = root.querySelector('[data-auth-qrcode]'); if (!box || box.dataset.loading === '1' || box.dataset.loaded === '1') return; box.dataset.loading = '1'; request('qrcode-login', {}).then(function (result) { box.dataset.loading = '0'; box.dataset.loaded = '1'; box.innerHTML = result.qr_html || ''; if (result.mode === 'scan' && result.token) pollQr(box, result.token, 0); }).catch(function (error) { box.dataset.loading = '0'; box.dataset.loaded = '0'; box.innerHTML = ''; showToast(error.message, 'danger'); }); } function pollQr(box, token, count) { if (!document.documentElement.contains(box) || count >= 100) return; window.setTimeout(function () { request('check-qrcode-login', { token: token }).then(function (result) { if (result.logged_in) { showToast('登录成功', 'success'); window.location.assign(result.redirect || window.location.href); return; } if (result.status === 'expired') { box.dataset.loaded = '0'; box.innerHTML = ''; showToast('二维码已过期,请刷新后重试', 'warning'); return; } pollQr(box, token, count + 1); }).catch(function () { pollQr(box, token, count + 1); }); }, 3000); } function submitQrCode(form) { var data = formData(form); var button = form.querySelector('button[type="submit"]'); if (button) button.disabled = true; request('weixingzh-code', data).then(function (result) { showToast('登录成功', 'success'); window.location.assign(result.redirect || window.location.href); }).catch(function (error) { if (button) button.disabled = false; showToast(error.message, 'danger'); }); } function setBusy(form, busy) { var button = form.querySelector('.sign-submit'); if (!button) return; button.disabled = busy; var spinner = button.querySelector('.fa-spinner'); if (spinner) spinner.hidden = !busy; } function startCountdown(button, seconds) { var original = button.dataset.label || button.textContent; button.dataset.label = original; button.disabled = true; var left = Math.max(1, Number(seconds) || 60); button.textContent = left + 's'; var timer = window.setInterval(function () { left -= 1; if (left <= 0) { window.clearInterval(timer); button.disabled = false; button.textContent = original; } else { button.textContent = left + 's'; } }, 1000); } function captchaScene(scene) { return scene === 'resetpassword' ? 'lostpassword' : scene; } function verifyCaptcha(scene, callback) { var state = window.AKSRTCaptchaState; var captcha = state && state.scenes ? state.scenes[captchaScene(scene)] : null; if (!captcha || typeof captcha.verify !== 'function') { callback(true); return; } captcha.verify(function (verified) { callback(verified !== false); }); } function resetCaptcha(scene) { var state = window.AKSRTCaptchaState; var captcha = state && state.scenes ? state.scenes[captchaScene(scene)] : null; if (captcha && typeof captcha.reset === 'function') captcha.reset(); } function sendCode(button) { var form = button.closest('form'); var target = form && form.querySelector('[name="target"]'); if (!target || !target.value.trim()) { showToast('请先填写邮箱或手机号', 'warning'); return; } button.disabled = true; var payload = formData(form); payload.scene = button.dataset.sendCode; payload.target = target.value.trim(); payload.channel = (form.querySelector('[name="channel"]') || {}).value || ''; verifyCaptcha(payload.scene, function (verified) { if (!verified) { button.disabled = false; showToast('请先完成人机验证', 'warning'); return; } payload = formData(form); payload.scene = button.dataset.sendCode; payload.target = target.value.trim(); payload.channel = (form.querySelector('[name="channel"]') || {}).value || ''; request('code', payload).then(function (result) { showToast(result.message || '验证码已发送', 'success'); startCountdown(button, result.countdown || 60); }).catch(function (error) { button.disabled = false; showToast(error.message, 'danger'); }).finally(function () { resetCaptcha(payload.scene); }); }); } function submit(form) { var action = form.dataset.authForm; var payload = formData(form); if (action === 'register' && payload.channel !== 'phone') { payload.user_email = payload.target; } setBusy(form, true); request(action, payload).then(function (result) { showToast(result.message || '操作成功', 'success'); if (result.redirect) window.setTimeout(function () { window.location.assign(result.redirect); }, 350); }).catch(function (error) { showToast(error.message, 'danger'); setBusy(form, false); }); } function toggleLoginMode(root, button) { var password = root.querySelector('[data-auth-password-fields]'); var code = root.querySelector('[data-auth-code-fields]'); if (!password || !code) return; var showCode = code.hidden; code.hidden = !showCode; password.hidden = showCode; button.textContent = showCode ? '使用账号密码登录' : '使用验证码登录'; } function initRoot(root) { root.querySelectorAll('input').forEach(function (input) { if (!input.hasAttribute('placeholder')) input.setAttribute('placeholder', ' '); }); root.querySelectorAll('form').forEach(function (form) { form.noValidate = true; }); var initial = root.dataset.initialTab || 'signin'; if (initial === 'oauth' && window.matchMedia && window.matchMedia('(max-width: 767px)').matches) initial = 'signin'; activate(root, initial, false); root.addEventListener('click', function (event) { var tab = event.target.closest('[data-auth-tab]'); if (tab) { event.preventDefault(); activate(root, tab.dataset.authTab, true); return; } var password = event.target.closest('[data-password-show]'); if (password) { var input = password.closest('.floating-input').querySelector('input'); input.type = input.type === 'password' ? 'text' : 'password'; password.querySelector('i').className = input.type === 'password' ? 'fa fas fa-eye' : 'fa fas fa-eye-slash'; return; } var send = event.target.closest('[data-send-code]'); if (send) { event.preventDefault(); sendCode(send); return; } var mode = event.target.closest('[data-login-mode]'); if (mode) { event.preventDefault(); toggleLoginMode(root, mode); } var refreshQr = event.target.closest('[data-auth-refresh-qr]'); if (refreshQr) { event.preventDefault(); loadQr(root); } }); root.addEventListener('submit', function (event) { var qrForm = event.target.closest('[data-aksrt-weixingzh-code]'); if (qrForm) { event.preventDefault(); if (validateForm(qrForm)) submitQrCode(qrForm); return; } var form = event.target.closest('[data-auth-form]'); if (!form) return; event.preventDefault(); if (!validateForm(form)) return; submit(form); }); } function validateForm(form) { if (!form.checkValidity || form.checkValidity()) return true; var field = form.querySelector(':invalid'); showToast((field && field.validationMessage) || '请完整填写表单', 'warning'); if (field && typeof field.focus === 'function') field.focus(); return false; } var modal; function authPageUrl(tab, redirect) { var routes = config.routes || {}; var base = tab === 'signup' ? routes.register : routes.login; var url = new URL(base || '/sign/', window.location.href); url.searchParams.set('tab', tab || 'signin'); if (redirect) url.searchParams.set('redirect_to', redirect); return url.toString(); } function openModal(tab, redirect) { if (!modal) { window.location.assign(authPageUrl(tab, redirect)); return; } modal.classList.add('is-open'); modal.setAttribute('aria-hidden', 'false'); document.body.classList.add('auth-modal-open'); var root = modal.querySelector('[data-aksrt-auth]'); if (root) { activate(root, tab || 'signin', false); var redirectInput = root.querySelector('[name="redirect_to"]'); if (redirectInput && redirect) redirectInput.value = redirect; } } function closeModal(storeDismissal) { if (!modal) return; modal.classList.remove('is-open'); modal.setAttribute('aria-hidden', 'true'); document.body.classList.remove('auth-modal-open'); if (storeDismissal && Number(modal.dataset.autoExpires) > 0) { var until = Date.now() + Number(modal.dataset.autoExpires) * 3600000; window.localStorage.setItem('aksrtAuthAutoUntil', String(until)); } } function init() { document.querySelectorAll('[data-aksrt-auth]').forEach(initRoot); modal = document.querySelector('[data-auth-modal]'); document.addEventListener('click', function (event) { var trigger = event.target.closest('[data-aksrt-auth-required], .aksrt-login-trigger, #nav-login-btn, .signin-loader, .signup-loader, .qrcode-signin'); if (!trigger) return; event.preventDefault(); var tab = trigger.classList.contains('signup-loader') ? 'signup' : (trigger.classList.contains('qrcode-signin') ? 'oauth' : 'signin'); openModal(tab, trigger.dataset.authRedirect || ''); }); if (modal) { modal.addEventListener('click', function (event) { if (event.target.closest('[data-auth-close]')) closeModal(true); }); document.addEventListener('keydown', function (event) { if (event.key === 'Escape') closeModal(true); }); if (modal.dataset.auto === '1' && Number(window.localStorage.getItem('aksrtAuthAutoUntil') || 0) < Date.now()) { window.setTimeout(function () { openModal(modal.querySelector('[data-aksrt-auth]').dataset.initialTab || 'signin'); }, Math.max(1, Number(modal.dataset.autoWait) || 5) * 1000); } } } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); }());