From b1fd143bc1f8ab7ce28cf4081088090383c2c9bc Mon Sep 17 00:00:00 2001 From: Haolin Wang Date: Sat, 6 Sep 2025 13:28:03 +0000 Subject: [PATCH] Refactor login.js: extract helpers to reduce init() complexity --- public/src/client/login.js | 174 ++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 68 deletions(-) diff --git a/public/src/client/login.js b/public/src/client/login.js index f7508b8ec2..17d669323b 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -11,22 +11,61 @@ define('forum/login', ['hooks', 'translator', 'jquery-form'], function (hooks, t const submitEl = $('#login'); const formEl = $('#login-form'); + bindSubmitHandler({ formEl, submitEl, errorEl }); + + + // Guard against caps lock + Login.capsLockCheck(document.querySelector('#password'), document.querySelector('#caps-lock-warning')); + + if ($('#content #username').val()) { + $('#content #password').val('').focus(); + } else { + $('#content #username').focus(); + } + $('#content #noscript').val('false'); + }; + + Login.capsLockCheck = (inputEl, warningEl) => { + const toggle = (state) => { + warningEl.classList[state ? 'remove' : 'add']('hidden'); + warningEl.parentNode.classList[state ? 'add' : 'remove']('has-warning'); + }; + if (!inputEl) { + return; + } + inputEl.addEventListener('keyup', function (e) { + if (Login._capsState && e.key === 'CapsLock') { + toggle(false); + Login._capsState = !Login._capsState; + return; + } + Login._capsState = e.getModifierState && e.getModifierState('CapsLock'); + toggle(Login._capsState); + }); + + if (Login._capsState) { + toggle(true); + } + }; + + function bindSubmitHandler({ formEl, submitEl, errorEl }) { submitEl.on('click', async function (e) { e.preventDefault(); + const username = $('#username').val(); const password = $('#password').val(); - errorEl.addClass('hidden').find('p').text(''); - if (!username || !password) { - errorEl.find('p').translateText('[[error:invalid-username-or-password]]'); - errorEl.removeClass('hidden'); + + clearError(errorEl); + + if (!isFilled(username, password)) { + showError(errorEl, '[[error:invalid-username-or-password]]'); return; } - if (submitEl.hasClass('disabled')) { + if (isDisabled(submitEl)) { return; } - - submitEl.addClass('disabled'); + setDisabled(submitEl, true); try { const hookData = await hooks.fire('filter:app.login', { @@ -35,89 +74,88 @@ define('forum/login', ['hooks', 'translator', 'jquery-form'], function (hooks, t cancel: false, }); if (hookData.cancel) { - submitEl.removeClass('disabled'); + setDisabled(submitEl, false); return; } } catch (err) { - errorEl.find('p').translateText(err.message); - errorEl.removeClass('hidden'); - submitEl.removeClass('disabled'); + showError(errorEl, err.message); + setDisabled(submitEl, false); return; } hooks.fire('action:app.login'); formEl.ajaxSubmit({ - headers: { - 'x-csrf-token': config.csrf_token, - }, + headers: { 'x-csrf-token': config.csrf_token }, beforeSend: function () { app.flags._login = true; }, - success: function (data) { - hooks.fire('action:app.loggedIn', data); - const pathname = utils.urlToLocation(data.next).pathname; - const params = utils.params({ url: data.next }); - params.loggedin = true; - delete params.register; // clear register message incase it exists - const qs = $.param(params); - - window.location.href = pathname + '?' + qs; - }, + success: handleSuccessRedirect, error: function (data) { - let message = data.responseText; - const errInfo = data.responseJSON; - if (data.status === 403 && data.responseText === 'Forbidden') { - window.location.href = config.relative_path + '/login?error=csrf-invalid'; - } else if (errInfo && errInfo.hasOwnProperty('banned_until')) { - message = errInfo.banned_until ? - translator.compile('error:user-banned-reason-until', (new Date(errInfo.banned_until).toLocaleString()), errInfo.reason) : - '[[error:user-banned-reason, ' + errInfo.reason + ']]'; - } - errorEl.find('p').translateText(message); - errorEl.removeClass('hidden'); - submitEl.removeClass('disabled'); - - // Select the entire password if that field has focus - if ($('#password:focus').length) { - $('#password').select(); - } + handleAjaxError({ data, errorEl, submitEl }); }, }); }); + } - // Guard against caps lock - Login.capsLockCheck(document.querySelector('#password'), document.querySelector('#caps-lock-warning')); + function isFilled(username, password) { + return Boolean(username && password); + } - if ($('#content #username').val()) { - $('#content #password').val('').focus(); - } else { - $('#content #username').focus(); - } - $('#content #noscript').val('false'); - }; + function isDisabled($el) { + return $el.hasClass('disabled'); + } - Login.capsLockCheck = (inputEl, warningEl) => { - const toggle = (state) => { - warningEl.classList[state ? 'remove' : 'add']('hidden'); - warningEl.parentNode.classList[state ? 'add' : 'remove']('has-warning'); - }; - if (!inputEl) { + function setDisabled($el, state) { + $el.toggleClass('disabled', !!state); + } + + function clearError($errorEl) { + $errorEl.addClass('hidden').find('p').text(''); + } + + function showError($errorEl, message) { + $errorEl.find('p').translateText(message); + $errorEl.removeClass('hidden'); + } + + function handleSuccessRedirect(data) { + hooks.fire('action:app.loggedIn', data); + const pathname = utils.urlToLocation(data.next).pathname; + const params = utils.params({ url: data.next }); + params.loggedin = true; + delete params.register; // clear register message incase it exists + const qs = $.param(params); + window.location.href = pathname + '?' + qs; + } + + function handleAjaxError({ data, errorEl, submitEl }) { + let message = data.responseText; + const errInfo = data.responseJSON; + + if (data.status === 403 && data.responseText === 'Forbidden') { + window.location.href = config.relative_path + '/login?error=csrf-invalid'; return; } - inputEl.addEventListener('keyup', function (e) { - if (Login._capsState && e.key === 'CapsLock') { - toggle(false); - Login._capsState = !Login._capsState; - return; - } - Login._capsState = e.getModifierState && e.getModifierState('CapsLock'); - toggle(Login._capsState); - }); - if (Login._capsState) { - toggle(true); + if (errInfo && Object.prototype.hasOwnProperty.call(errInfo, 'banned_until')) { + message = errInfo.banned_until ? + translator.compile( + 'error:user-banned-reason-until', + (new Date(errInfo.banned_until).toLocaleString()), + errInfo.reason + ) : + '[[error:user-banned-reason, ' + errInfo.reason + ']]'; } - }; + + showError(errorEl, message); + setDisabled(submitEl, false); + + // Select the entire password if that field has focus + if ($('#password:focus').length) { + $('#password').select(); + } + } + return Login; });