/* * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2019-2024 Mike Tzou (Chocobo1) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * In addition, as a special exception, the copyright holders give permission to * link this program with the OpenSSL project's "OpenSSL" library (or with * modified versions of it that use the same license as the "OpenSSL" library), * and distribute the linked executables. You must obey the GNU General Public * License in all respects for all of the code used other than "OpenSSL". If you * modify file(s), you may extend this exception to your version of the file(s), * but you are not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ 'use strict'; async function setupI18n() { const languages = (() => { const langs = new Set(); // list of available languages: https://github.com/qbittorrent/qBittorrent/tree/master/src/webui/www/public/lang const queryLang = new URLSearchParams(window.location.search).get('lang'); if (queryLang !== null) { // use the fallback lang if `queryLang` is present but empty // limit the length of the language string to prevent Client-side Request Forgery if ((queryLang.length > 0) && (queryLang.length <= 8)) langs.add(queryLang.replace('-', '_')); } else { for (const lang of navigator.languages) { langs.add(lang.replace('-', '_')); const idx = lang.indexOf('-'); if (idx > 0) langs.add(lang.slice(0, idx)); } } langs.add('en'); // fallback return Array.from(langs); })(); // it is faster to fetch all translation files at once than one by one const fetches = languages.map(lang => fetch(`lang/${lang}.json`)); const fetchResults = await Promise.allSettled(fetches); const translations = fetchResults .map((value, idx) => ({ lang: languages[idx], result: value })) .filter(v => (v.result.value.status === 200)); const translation = { lang: (translations.length > 0) ? translations[0].lang.replace('_', '-') : undefined, data: (translations.length > 0) ? (await translations[0].result.value.json()) : {} }; // present it to i18next const i18nextOptions = { lng: translation.lang, fallbackLng: false, load: 'currentOnly', resources: { [translation.lang]: { translation: translation.data } }, returnEmptyString: false }; i18next.init(i18nextOptions, replaceI18nText); } function replaceI18nText() { const tr = i18next.t; // workaround for warnings from i18next-parser for (const element of document.getElementsByClassName('qbt-translatable')) { const translationKey = element.getAttribute('data-i18n'); const translatedValue = tr(translationKey); switch (element.nodeName) { case 'INPUT': element.value = translatedValue; break; case 'LABEL': element.textContent = translatedValue; break; default: console.error(`Unhandled element: ${element}`); break; } } document.documentElement.lang = i18next.language.split('-')[0]; } function submitLoginForm(event) { event.preventDefault(); const errorMsgElement = document.getElementById('error_msg'); const xhr = new XMLHttpRequest(); xhr.open('POST', 'api/v2/auth/login', true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); xhr.addEventListener('readystatechange', () => { if (xhr.readyState === 4) { // DONE state if ((xhr.status === 200) && (xhr.responseText === "Ok.")) location.replace(location); else errorMsgElement.textContent = i18next.t('Invalid Username or Password.'); } }); xhr.addEventListener('error', () => { errorMsgElement.textContent = (xhr.responseText !== "") ? xhr.responseText : i18next.t('Unable to log in, server is probably unreachable.'); }); const usernameElement = document.getElementById('username'); const passwordElement = document.getElementById('password'); const queryString = "username=" + encodeURIComponent(usernameElement.value) + "&password=" + encodeURIComponent(passwordElement.value); xhr.send(queryString); // clear the field passwordElement.value = ''; } document.addEventListener('DOMContentLoaded', () => { const loginForm = document.getElementById('loginform'); loginForm.setAttribute('method', 'POST'); loginForm.addEventListener('submit', submitLoginForm); setupI18n(); });