Help
RSS
API
Feed
Maltego
Contact
Domain > aquiz.pro
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2026-02-18
104.21.46.35
(
ClassC
)
Port 443
HTTP/1.1 200 OKDate: Wed, 18 Feb 2026 13:41:08 GMTContent-Type: text/html; charsetutf-8Transfer-Encoding: chunkedConnection: keep-aliveServer: cloudflareNel: {report_to:cf-nel,success_fraction:0.0,max_age:604800}Report-To: {group:cf-nel,max_age:604800,endpoints:{url:https://a.nel.cloudflare.com/report/v4?sIkatvqx4MBomyrzE7vgignc8duUauUZVODO3vJ%2Fr%2Faetp%2BDAIWHrN%2BwHPRyZ%2BeBzPEJldsTEFZmIP7zqqD%2BfHH9d2TxDWo8%3D}}cf-cache-status: DYNAMICvary: accept-encodingCF-RAY: 9cfdf0b7aaf1b642-PDXalt-svc: h3:443; ma86400 !DOCTYPE html>html langru>head>meta charsetUTF-8 />meta nameviewport contentwidthdevice-width, initial-scale1 />title>Угадай аниме по картинке: Аниме квиз онлайн | Anime Quiz/title>!-- Google tag (gtag.js) -->script async srchttps://www.googletagmanager.com/gtag/js?idG-4DK6WGS0ZF>/script>script> window.dataLayer window.dataLayer || ; function gtag(){dataLayer.push(arguments);} gtag(js, new Date()); gtag(config, G-4DK6WGS0ZF);/script>!-- Yandex.Metrika counter -->script typetext/javascript> (function(m,e,t,r,i,k,a){ mimi||function(){(mi.ami.a||).push(arguments)}; mi.l1*new Date(); for (var j 0; j document.scripts.length; j++) {if (document.scriptsj.src r) { return; }} ke.createElement(t),ae.getElementsByTagName(t)0,k.async1,k.srcr,a.parentNode.insertBefore(k,a) })(window, document,script,https://mc.yandex.ru/metrika/tag.js?id104776755, ym); ym(104776755, init, {ssr:true, webvisor:true, clickmap:true, ecommerce:dataLayer, accurateTrackBounce:true, trackLinks:true});/script>noscript>div>img srchttps://mc.yandex.ru/watch/104776755 styleposition:absolute; left:-9999px; alt />/div>/noscript>!-- /Yandex.Metrika counter -->!-- Favicon -->link relicon typeimage/x-icon href/static/favicon.ico>link relicon typeimage/png sizes16x16 href/static/favicon-16x16.png>link relicon typeimage/png sizes32x32 href/static/favicon-32x32.png>link relapple-touch-icon sizes180x180 href/static/apple-touch-icon.png>link relicon typeimage/png sizes192x192 href/static/android-chrome-192x192.png>link relicon typeimage/png sizes512x512 href/static/android-chrome-512x512.png>link relmanifest href/static/site.webmanifest>!-- Meta tags -->meta namedescription contentУгадай аниме по картинке в Anime Quiz! Пройди аниме викторину: угадай тайтлы по скриншотам из популярных или редких аниме. Играй онлайн бесплатно!>meta namekeywords contentаниме, викторина, квиз, угадай аниме, скриншоты>meta nameauthor contentAnime Quiz>meta nametheme-color content#2b65ff>!-- Open Graph / Facebook -->meta propertyog:type contentwebsite>meta propertyog:url contenthttps://aquiz.pro/>meta propertyog:title contentУгадай аниме по картинке: Аниме квиз онлайн | Anime Quiz>meta propertyog:description contentУгадай аниме по картинке в Anime Quiz! Пройди аниме викторину: угадай тайтлы по скриншотам из популярных или редких аниме. Играй онлайн бесплатно!>meta propertyog:image contenthttps://aquiz.pro/static/android-chrome-512x512.png>!-- Twitter -->meta propertytwitter:card contentsummary_large_image>meta propertytwitter:url contenthttps://aquiz.pro/>meta propertytwitter:title contentУгадай аниме по картинке: Аниме квиз онлайн | Anime Quiz>meta propertytwitter:description contentУгадай аниме по картинке в Anime Quiz! Пройди аниме викторину: угадай тайтлы по скриншотам из популярных или редких аниме. Играй онлайн бесплатно!>meta propertytwitter:image contenthttps://aquiz.pro/static/android-chrome-512x512.png>link relpreconnect hrefhttps://fonts.gstatic.com crossorigin>link hrefhttps://fonts.googleapis.com/css2?familyInter:wght@400;600&displayswap relstylesheet>style> :root { --bg:#0b0f17; --card:#111826; --muted:#8b93a7; --text:#e6e9f2; --accent:#6ea8fe; --ok:#38b000; --bad:#ff4d6d; } * { box-sizing: border-box } body { margin:0; font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; background:linear-gradient(180deg,#0b0f17,#0f1626); color:var(--text); min-height: 100vh; display: flex; flex-direction: column; } /* Header закреплен вверху */ .header-container { position: sticky; top: 0; z-index: 100; background: rgba(11, 15, 23, 0.95); backdrop-filter: blur(10px); border-bottom: 1px solid #1b2436; } .header-wrapper { max-width: 980px; margin: 0 auto; padding: 16px 24px; } /* Основной контент по центру */ .container { max-width: 980px; margin: 0 auto; padding: 24px; flex: 1; display: flex; flex-direction: column; justify-content: center; } header { display:flex; align-items:center; } .header-content { max-width: 720px; width: 100%; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; } h1 { font-size: 22px; margin:0; } .row { display:flex; gap:16px; flex-wrap:wrap; } .card { background:var(--card); border:1px solid #1b2436; border-radius:12px; padding:0; flex:1; min-width:280px; position: relative; } /* Padding для элементов внутри card */ .timer-container { padding: 16px 16px 0 16px; } .card h2 { margin: 0 0 10px 0; font-size:16px; } input, button, textarea { font: inherit; } inputtypetext, inputtypepassword, inputtypeemail { width:100%; padding:10px 12px; border-radius:8px; border:1px solid #26324a; background:#0f1422; color:var(--text); } button { padding:10px 14px; border-radius:8px; border:1px solid #26324a; background:#152036; color:var(--text); cursor:pointer; } button.primary { background:linear-gradient(180deg,#2b65ff,#1b4ed6); border:0; } button:disabled { opacity: 0.6; cursor:not-allowed; } .muted { color: var(--muted); font-size: 13px; } .imgbox { display: flex; align-items: center; justify-content: center; background: #0f1422; border: 1px dashed #2a3858; border-radius: 10px; overflow: hidden; position: relative; /* Абсолютно статический размер - никогда не меняется */ width: 720px; height: 405px; /* 16:9 пропорция для 720px ширины */ margin: 0 auto; flex-shrink: 0; /* Не сжимается */ box-sizing: border-box; /* padding включается в общий размер */ } /* Блок выбора сложности заполняет весь imgbox с отступами */ #start-section { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; padding: 20px; box-sizing: border-box; } .imgbox img { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; object-fit: contain; /* Изображение не должно влиять на размер контейнера */ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .imgbox-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); display: none; align-items: center; justify-content: center; border-radius: 10px; } .next-button-overlay { padding: 16px 32px; font-size: 18px; font-weight: 600; background: linear-gradient(180deg,#2b65ff,#1b4ed6); border: none; border-radius: 12px; color: white; cursor: pointer; transition: all 0.2s; } .next-button-overlay:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(43, 101, 255, 0.3); } /* Toggle Switch Styles */ .toggle-switch { position: relative; display: inline-block; width: 50px; height: 24px; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #4a5568; transition: 0.3s; border-radius: 24px; } .toggle-slider:before { position: absolute; content: ; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: 0.3s; border-radius: 50%; } .toggle-switch input:checked + .toggle-slider { background: linear-gradient(180deg,#2b65ff,#1b4ed6); } .toggle-switch input:checked + .toggle-slider:before { transform: translateX(26px); } .toggle-container { position: absolute; bottom: 16px; right: 16px; display: flex; align-items: center; gap: 8px; background: rgba(17, 24, 38, 0.9); padding: 8px 12px; border-radius: 20px; backdrop-filter: blur(10px); } /* Hamburger Menu Styles */ .hamburger-menu { display: flex; flex-direction: column; justify-content: space-around; width: 20px; height: 20px; cursor: pointer; } .hamburger-line { width: 100%; height: 2px; background-color: var(--text); transition: all 0.3s; } .hamburger-menu:hover .hamburger-line { background-color: var(--accent); } .user-menu { display: none; } .user-menu.active { display: block; } /* Стили для кнопок в пользовательском меню */ .user-menu .quiz-option { display: block; width: 100%; text-align: left; } /* Стили для кнопок пользовательского меню */ .user-menu-btn { display: block; width: 100%; padding: 12px 16px; margin: 0; background: #152036; border: 1px solid #26324a; border-radius: 8px; cursor: pointer; transition: all 0.2s; color: var(--text); text-align: left; font: inherit; } .user-menu-btn:hover { background: #1a2a4a; border-color: #3a4a6a; } .user-menu-btn.small { padding: 8px 12px; font-size: 13px; } /* Start Button Styles */ .start-button { padding: 20px 40px; font-size: 24px; font-weight: 600; background: linear-gradient(180deg,#2b65ff,#1b4ed6); border: none; border-radius: 16px; color: white; cursor: pointer; transition: all 0.3s; } .start-button:hover { transform: translateY(-3px); box-shadow: 0 12px 32px rgba(43, 101, 255, 0.4); } /* Back Button Styles */ .back-button { padding: 8px 16px; border-radius: 8px; border: 1px solid #26324a; background: rgba(17, 24, 38, 0.9); color: var(--text); font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s; backdrop-filter: blur(10px); min-width: 80px; } .back-button:hover { background: rgba(43, 101, 255, 0.2); border-color: #2b65ff; transform: translateY(-1px); } /* Difficulty Button Styles */ .difficulty-btn { padding: 12px 20px; border-radius: 8px; border: 2px solid; background: transparent; cursor: pointer; transition: all 0.2s; font-weight: 600; font-size: 14px; font-family: inherit; } .difficulty-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.3); } .difficulty-btn.selected { background: inherit !important; color: white !important; } .difficulty-btndata-difficultyeasy { border-color: #38b000 !important; color: #38b000 !important; } .difficulty-btndata-difficultyeasy:hover { background: #38b000 !important; color: white !important; } .difficulty-btndata-difficultyeasy.selected { background: #38b000 !important; color: white !important; } .difficulty-btndata-difficultynormal { border-color: #2b65ff !important; color: #2b65ff !important; } .difficulty-btndata-difficultynormal:hover { background: #2b65ff !important; color: white !important; } .difficulty-btndata-difficultynormal.selected { background: #2b65ff !important; color: white !important; } .difficulty-btndata-difficultyhard { border-color: #ff4d6d !important; color: #ff4d6d !important; } .difficulty-btndata-difficultyhard:hover { background: #ff4d6d !important; color: white !important; } .difficulty-btndata-difficultyhard.selected { background: #ff4d6d !important; color: white !important; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Защита от копирования изображений */ #q-image { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-user-drag: none; -khtml-user-drag: none; -moz-user-drag: none; -o-user-drag: none; user-drag: none; pointer-events: none; } /* Скрываем секции по умолчанию до инициализации JavaScript */ #auth-section { display: none; } #user-section { display: none; } .status { margin-top: 8px; font-size: 14px; min-height: 20px; /* Фиксированная минимальная высота */ display: flex; align-items: center; justify-content: center; transition: opacity 0.3s ease, color 0.3s ease; /* Плавные переходы */ } .status.ok { color: var(--ok) } .status.bad { color: var(--bad) } .grid-2 { display:grid; grid-template-columns: 1fr 1fr; gap:16px; } @media (max-width: 820px){ .grid-2 { grid-template-columns: 1fr } } .dropdown { position: relative; } .dropdown-menu { position:absolute; right:0; top:42px; width:280px; background:var(--card); border:1px solid #1b2436; border-radius:12px; padding:12px; box-shadow:0 8px 24px rgba(0,0,0,0.35); z-index: 5; } #quiz-options { min-height: 200px; /* Фиксированная минимальная высота для контейнера ответов */ } .quiz-option { padding:12px 16px; margin:8px 0; background:#152036; border:1px solid #26324a; border-radius:8px; cursor:pointer; transition:all 0.2s; } .quiz-option:hover { background:#1a2a4a; border-color:#3a4a6a; } .quiz-option.selected { background:linear-gradient(180deg,#2b65ff,#1b4ed6); border-color:#2b65ff; } .quiz-option.correct { background:linear-gradient(180deg,#38b000,#2a8a00); border-color:#38b000; } .quiz-option.wrong { background:linear-gradient(180deg,#ff4d6d,#d63384); border-color:#ff4d6d; } .timer { transition: color 0.3s; } .timer.warning { color: #ff6b35; } .timer.danger { color: #ff4d6d; animation: pulse 0.5s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes shimmer { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Footer styles */ footer { margin-top: auto; padding: 20px 0; border-top: 1px solid #1b2436; text-align: center; color: var(--muted); font-size: 14px; } .footer-content { max-width: 720px; width: 100%; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; } .footer-links { display: flex; gap: 24px; align-items: center; } .footer-links a { color: var(--muted); text-decoration: none; transition: color 0.2s ease; } .footer-links a:hover { color: var(--accent); } .footer-copyright { color: var(--muted); font-size: 13px; } @media (max-width: 768px) { .header-wrapper { padding: 12px 16px; } .container { padding: 16px; justify-content: flex-start; padding-top: 20px; } .imgbox { width: 100%; max-width: 720px; height: 300px; /* Меньшая высота на планшетах */ } #start-section { padding: 16px; } .footer-content { flex-direction: column; gap: 12px; } .footer-links { gap: 16px; } } @media (max-width: 480px) { .imgbox { width: 100%; max-width: 720px; height: 250px; /* Еще меньшая высота на мобильных */ } #start-section { padding: 12px; } } @media (max-height: 600px) { .container { justify-content: flex-start; padding-top: 20px; } }/style>/head>body>script>// Автоматическое перенаправление на локализованную версию(function() { console.log(Redirect script disabled for debugging); // const savedLang localStorage.getItem(lang); // if (savedLang en) { // window.location.href /en/; // } else { // window.location.href /ru/; // }})();/script> !-- Header закреплен вверху --> div classheader-container> div classheader-wrapper> header> div classheader-content> div styledisplay: flex; align-items: center; gap: 12px;> h1 data-i18ntitle>Anime Quiz/h1> img src/static/logo.png altAnime Quiz: Угадай аниме styleheight: 22px; width: auto; object-fit: contain;> /div> !-- Группа элементов справа --> div styledisplay:flex; gap:12px; align-items:center;> div classlang-switch> button idbtn-lang titleLanguage stylepadding:8px 10px>RU/button> /div> button idbtn-leaderboard-top data-i18nsvc.leaderboard stylepadding:8px 10px>Лидерборд/button> a href# data-header-faq data-i18nsvc.faq stylepadding:8px 10px; text-decoration:none; color:var(--text); border:1px solid #26324a; border-radius:8px; background:#152036; transition:all 0.2s; onmouseoverthis.style.background#1a2a4a; this.style.borderColor#3a4a6a onmouseoutthis.style.background#152036; this.style.borderColor#26324a>FAQ/a> !-- Меню для неавторизованных пользователей --> div idauth-section classdropdown> button idauth-toggle classprimary data-i18nauth.menu>Login / Register/button> div idauth-menu classdropdown-menu styledisplay:none> div classmuted data-i18nauth.register>Регистрация/div> input idreg-username typetext placeholderЛогин /> div styleheight:8px>/div> input idreg-email typeemail placeholderEmail /> div styleheight:8px>/div> input idreg-password typepassword placeholderПароль /> div styleheight:8px>/div> button idbtn-register classprimary data-i18nauth.register_btn>Зарегистрироваться/button> div idreg-status classstatus muted>/div> hr styleborder-color:#26324a; margin:10px 0 /> div classmuted data-i18nauth.login>Вход/div> input idlogin-username typetext placeholderЛогин /> div styleheight:8px>/div> input idlogin-password typepassword placeholderПароль /> div styleheight:8px>/div> button idbtn-login classprimary data-i18nauth.login_btn>Войти/button> div idlogin-status classstatus muted>/div> /div> /div> !-- Меню для авторизованных пользователей --> div iduser-section styledisplay:none; align-items:center; gap:8px;> div idwho classmuted stylefont-size: 14px;>/div> div classdropdown> div iduser-menu-toggle classhamburger-menu> div classhamburger-line>/div> div classhamburger-line>/div> div classhamburger-line>/div> /div> div iduser-menu classdropdown-menu user-menu styledisplay:none; width: 200px;> button idbtn-profile classuser-menu-btn stylemargin-bottom: 8px; data-i18nuser.profile>Мой профиль/button> button idbtn-logout classuser-menu-btn small data-i18nuser.logout>Выход/button> /div> /div> /div> /div> /div> /header> /div> /div> !-- Основной контент по центру --> div classcontainer> div classrow stylegap: 0;> div classcard stylemax-width:752px; width:100%; margin: 0 auto;> div classtimer-container styledisplay: flex; align-items: center; margin-bottom: 16px; display: none;> !-- Кнопка Назад в левом углу --> button idback-button classback-button onclickgoBackToDifficultySelection() data-i18nquiz.back>Назад/button> !-- Таймер по центру --> div styleflex: 1; text-align: center;> div idtimer classtimer stylefont-size: 24px; font-weight: bold; color: var(--accent);>10/div> /div> /div> div classimgbox idquiz-content> !-- Кнопка Start (показывается до начала квиза) --> div idstart-section> !-- Выбор уровня сложности --> div styletext-align: center;> h3 stylemargin: 0 0 16px 0; font-size: 18px; color: var(--text); data-i18nquiz.difficulty_title>Выберите уровень сложности/h3> div styledisplay: flex; gap: 12px; justify-content: center; flex-wrap: wrap;> button classdifficulty-btn data-difficultyeasy> span data-i18nquiz.difficulty.easy>Легкий/span> /button> button classdifficulty-btn selected data-difficultynormal> span data-i18nquiz.difficulty.normal>Обычный/span> /button> button classdifficulty-btn data-difficultyhard> span data-i18nquiz.difficulty.hard>Сложный/span> /button> /div> div stylemargin-top: 12px; font-size: 13px; color: var(--muted);> div data-i18nquiz.difficulty.easy_desc>Современные популярные аниме (после 2010, рейтинг >8)/div> div data-i18nquiz.difficulty.normal_desc>Классические аниме (после 2000, рейтинг >7)/div> div data-i18nquiz.difficulty.hard_desc>Старые или менее известные тайтлы/div> /div> /div> !-- Кнопка Start --> button idstart-button classstart-button data-i18nquiz.start_btn>Начать/button> /div> !-- Индикатор загрузки --> div idloading-section styledisplay: none; flex-direction: column; align-items: center; justify-content: center; height: 100%;> div styletext-align: center; color: var(--text);> div style width: 40px; height: 40px; border: 4px solid #26324a; border-top: 4px solid #2b65ff; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 16px; >/div> div data-i18nquiz.loading>Загрузка.../div> /div> /div> !-- Контент квиза (скрыт до начала) --> div idquiz-section styledisplay: none; width: 100%; height: 100%; position: relative;> div styledisplay: flex; align-items: center; justify-content: center; height: 100%;> img idq-image altКартинка вопроса stylemax-width:100%; max-height:100%; width:auto; height:auto; display:block; object-fit: contain; oncontextmenureturn false; ondragstartreturn false; /> /div> div idimgbox-overlay classimgbox-overlay> button idnext-button-overlay classnext-button-overlay data-i18nquiz.next_btn>Next/button> /div> /div> /div> div styleheight:16px>/div> div idquiz-options styledisplay:none> div classquiz-option data-option0 onclickselectOption(0)>/div> div classquiz-option data-option1 onclickselectOption(1)>/div> div classquiz-option data-option2 onclickselectOption(2)>/div> div classquiz-option data-option3 onclickselectOption(3)>/div> /div> div styleheight:16px>/div> div idq-status classstatus muted>/div> !-- Toggle switch в правом нижнем углу --> div classtoggle-container> span data-i18nquiz.auto_next>Auto next/span> label classtoggle-switch> input typecheckbox idauto-next-toggle checked> span classtoggle-slider>/span> /label> /div> /div> /div> /div> !-- Footer --> footer> div classfooter-content> div classfooter-links> a href# data-footer-home>Home/a> a href# data-footer-faq>FAQ/a> /div> div classfooter-copyright> © aquiz.pro 2025 /div> /div> /footer>script>const api (path) > `${location.origin}${path}`;let token localStorage.getItem(token) || ;let currentQuestion null;let currentLang localStorage.getItem(lang) || ru;// Определяем язык из URLconst pathLang window.location.pathname.split(/)1;if (pathLang en) { currentLang en;} else if (pathLang ru) { currentLang ru;}localStorage.setItem(lang, currentLang);let selectedOption -1;let timerInterval null;let timeLeft 10;let autoNext localStorage.getItem(autoNext) ! false; // по умолчанию включеноlet answerSubmitted false;let currentDifficulty localStorage.getItem(difficulty) || normal; // текущий уровень сложности// Предзагрузка следующего вопросаlet preloadedQuestion null;let preloadedImage null;async function preloadNextQuestion() { try { const res await fetch(api(`/quiz/preload-next?lang${currentLang}&difficulty${currentDifficulty}`)); if (res.ok) { preloadedQuestion await res.json(); // Предзагружаем изображение preloadedImage new Image(); preloadedImage.src preloadedQuestion.image_url; console.log(Следующий вопрос предзагружен); } } catch (error) { console.log(Ошибка предзагрузки:, error); }}function startTimer() { timeLeft 10; updateTimerDisplay(); if (timerInterval) { clearInterval(timerInterval); } timerInterval setInterval(() > { timeLeft--; updateTimerDisplay(); if (timeLeft 0) { clearInterval(timerInterval); // Время вышло - показываем кнопку Next независимо от настройки Auto Next if (!answerSubmitted) { const overlay document.getElementById(imgbox-overlay); if (overlay) { overlay.style.display flex; } // Показываем Время вышло! в таймере вместо 0 const timerElement document.getElementById(timer); if (timerElement) { const dict i18ncurrentLang; timerElement.textContent dict.time_up; timerElement.className timer danger; } // Делаем кнопки неактивными const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); options.forEach(opt > { opt.style.opacity 0.5; opt.style.pointerEvents none; opt.onclick null; }); } else if (autoNext) { nextQuestion(); } } }, 1000);}function stopTimer() { if (timerInterval) { clearInterval(timerInterval); timerInterval null; }}function updateTimerDisplay() { const timerElement document.getElementById(timer); if (timerElement) { timerElement.textContent timeLeft; // Меняем цвет в зависимости от оставшегося времени timerElement.className timer; if (timeLeft 3) { timerElement.classList.add(danger); } else if (timeLeft 5) { timerElement.classList.add(warning); } }}const i18n { ru: { title: Anime Quiz, auth.menu: Login / Register, auth.register: Регистрация, auth.register_btn: Зарегистрироваться, auth.login: Вход, auth.login_btn: Войти, import.title: Импорт данных, import.anime_hint: Импорт аниме (JSON массив {title, image_url}), import.anime_btn: Импорт аниме, import.quiz_hint: Импорт вопросов (JSON массив {answer_title, image_url}), import.quiz_btn: Импорт вопросов, quiz.title: Квиз: вопрос, quiz.answer_ph: Название аниме, quiz.answer_btn: Ответить, quiz.next_btn: Далее, quiz.auto_next: Авто переход, quiz.start_btn: Начать, quiz.loading: Загрузка..., quiz.difficulty_title: Выберите уровень сложности, quiz.difficulty.easy: Легкий, quiz.difficulty.normal: Обычный, quiz.difficulty.hard: Сложный, quiz.difficulty.easy_desc: Современные популярные аниме (после 2010, рейтинг >8), quiz.difficulty.normal_desc: Классические аниме (после 2000, рейтинг >7), quiz.difficulty.hard_desc: Старые или менее известные тайтлы, quiz.back: Назад, svc.title: Сервисные действия, svc.list_anime: Список аниме, svc.leaderboard: Лидерборд, svc.faq: FAQ, user.profile: Мой профиль, user.logout: Выход, profile.title: Мой профиль, profile.description: Описание:, profile.edit: Edit, profile.save: Сохранить, profile.cancel: Отмена, profile.points: Очков, profile.rank: Место, profile.registration: Регистрация, profile.stats.title: Статистика по режимам, profile.stats.easy: Легкий, profile.stats.normal: Обычный, profile.stats.hard: Сложный, profile.stats.correct: Правильно, profile.stats.total: Всего, profile.stats.accuracy: Точность, profile.no_description: Описание не указано, profile.avatar_placeholder: Введите URL аватарки:, profile.avatar_updated: Аватарка обновлена!, profile.saved: Профиль сохранен!, profile.avatar_uploaded: Аватарка успешно загружена!, profile.avatar_error: Ошибка загрузки аватарки:, leaderboard.title: Таблица лидеров, leaderboard.rank: Место, leaderboard.username: Пользователь, leaderboard.points: Очки, leaderboard.registration: Регистрация, leaderboard.no_users: Пользователи не найдены, ok_register: Регистрация успешна, err_register: Ошибка регистрации, ok_login: Вход выполнен, err_login: Ошибка входа, need_login: Для участия в рейтинге войдите в аккаунт, need_question: Сначала получите вопрос, loaded_question: Вопрос загружен, correct: Верно! +, wrong: Неверно, time_up: Время вышло!, }, en: { title: Anime Quiz, auth.menu: Login / Register, auth.register: Register, auth.register_btn: Sign up, auth.login: Login, auth.login_btn: Sign in, import.title: Import data, import.anime_hint: Import anime (JSON array {title, image_url}), import.anime_btn: Import anime, import.quiz_hint: Import questions (JSON array {answer_title, image_url}), import.quiz_btn: Import questions, quiz.title: Quiz: question, quiz.answer_ph: Anime title, quiz.answer_btn: Answer, quiz.next_btn: Next, quiz.auto_next: Auto next, quiz.start_btn: Start, quiz.loading: Loading..., quiz.difficulty_title: Choose difficulty level, quiz.difficulty.easy: Easy, quiz.difficulty.normal: Normal, quiz.difficulty.hard: Hard, quiz.difficulty.easy_desc: Modern popular anime (after 2010, rating >8), quiz.difficulty.normal_desc: Classic anime (after 2000, rating >7), quiz.difficulty.hard_desc: Old or less known titles, quiz.back: Back, svc.title: Service actions, svc.list_anime: List anime, svc.leaderboard: Leaderboard, svc.faq: FAQ, user.profile: My Profile, user.logout: Log Out, profile.title: My Profile, profile.description: Description:, profile.edit: Edit, profile.save: Save, profile.cancel: Cancel, profile.points: Points, profile.rank: Rank, profile.registration: Registration, profile.stats.title: Difficulty Statistics, profile.stats.easy: Easy, profile.stats.normal: Normal, profile.stats.hard: Hard, profile.stats.correct: Correct, profile.stats.total: Total, profile.stats.accuracy: Accuracy, profile.no_description: No description provided, profile.avatar_placeholder: Enter avatar URL:, profile.avatar_updated: Avatar updated!, profile.saved: Profile saved!, profile.avatar_uploaded: Avatar uploaded successfully!, profile.avatar_error: Error uploading avatar:, leaderboard.title: Leaderboard, leaderboard.rank: Rank, leaderboard.username: User, leaderboard.points: Points, leaderboard.registration: Registration, leaderboard.no_users: No users found, ok_register: Registration successful, err_register: Registration error, ok_login: Signed in, err_login: Sign in error, need_login: Log in to join the leaderboard, need_question: Get a question first, loaded_question: Question loaded, correct: Correct! +, wrong: Wrong, time_up: Time\s up!, }};function applyI18n(){ const dict i18ncurrentLang || i18n.ru; document.querySelectorAll(data-i18n).forEach(el > { const key el.getAttribute(data-i18n); if(dictkey) el.textContent dictkey; }); document.querySelectorAll(data-i18n-ph).forEach(el > { const key el.getAttribute(data-i18n-ph); if(dictkey) el.setAttribute(placeholder, dictkey); }); const langBtn document.getElementById(btn-lang); if (langBtn) { langBtn.textContent currentLang.toUpperCase(); }}function setStatus(el, text, okfalse, badfalse){ el.textContent text || ; el.className status + (ok? ok:) + (bad? bad:);}function setUser(u){ const authSection document.getElementById(auth-section); const userSection document.getElementById(user-section); const whoElement document.getElementById(who); if (u) { // Пользователь авторизован authSection.style.display none; userSection.style.display flex; whoElement.textContent u.username; } else { // Пользователь не авторизован authSection.style.display block; userSection.style.display none; whoElement.textContent Гость; }}async function register(){ const username document.getElementById(reg-username).value.trim(); const email document.getElementById(reg-email).value.trim(); const password document.getElementById(reg-password).value; const st document.getElementById(reg-status); setStatus(st,); try{ const res await fetch(api(/auth/register),{method:POST, headers:{Content-Type:application/json}, body:JSON.stringify({username,email,password})}); const body await res.json().catch(()>({})); const dict i18ncurrentLang; if(res.ok){ setStatus(st,dict.ok_register,true); } else setStatus(st, body.detail || dict.err_register, false, true); }catch(e){ setStatus(st, Сеть: +e.message, false, true); }}async function login(){ const username document.getElementById(login-username).value.trim(); const password document.getElementById(login-password).value; const st document.getElementById(login-status); setStatus(st,); try{ const res await fetch(api(/auth/login-json), { method:POST, headers:{Content-Type:application/json}, body: JSON.stringify({username,password})}); const body await res.json().catch(()>({})); const dict i18ncurrentLang; if(res.ok && body.access_token){ token body.access_token; localStorage.setItem(token, token); setStatus(st,dict.ok_login, true); await me(); } else setStatus(st, body.detail || dict.err_login, false, true); }catch(e){ setStatus(st, Сеть: +e.message, false, true); }}async function me(){ if(!token){ setUser(null); return; } try{ const res await fetch(api(/auth/me), { headers:{ Authorization: Bearer +token } }); const body await res.json().catch(()>({})); if(res.ok){ setUser(body); } else { setUser(null); } }catch{ setUser(null); }}async function nextQuestion(){ const st document.getElementById(q-status); setStatus(st,); selectedOption -1; answerSubmitted false; stopTimer(); // Останавливаем предыдущий таймер // Скрываем оверлей с кнопкой Next const overlay document.getElementById(imgbox-overlay); if (overlay) { overlay.style.display none; } // Восстанавливаем активность кнопок ответов const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); options.forEach(opt > { opt.style.opacity 1; opt.style.pointerEvents auto; }); try{ let body; const dict i18ncurrentLang; // Используем предзагруженный вопрос, если он есть if (preloadedQuestion) { body preloadedQuestion; preloadedQuestion null; preloadedImage null; console.log(Используем предзагруженный вопрос); } else { // Если предзагрузки нет, загружаем обычным способом const res await fetch(api(`/quiz/random?lang${currentLang}&difficulty${currentDifficulty}`)); body await res.json(); if (!res.ok) { setStatus(st, body.detail || Error loading question, false, true); return; } } if(body){ currentQuestion body; document.getElementById(q-image).src body.image_url; // Показываем варианты ответа const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); body.options.forEach((option, index) > { // Если сервер уже прислал строку со скобками — используем как есть let displayText option; const jp body.options_original_jp && body.options_original_jpindex; if (jp && jp.trim()) { const needsAppend !option.includes(`(${jp})`); displayText needsAppend ? `${option} (${jp})` : option; } optionsindex.textContent displayText; optionsindex.className quiz-option; optionsindex.onclick () > selectOption(index); }); optionsContainer.style.display block; setStatus(st, dict.loaded_question, true); // Запускаем таймер startTimer(); } else setStatus(st, body.detail || No questions, false, true); }catch(e){ setStatus(st, Сеть: +e.message, false, true); }}async function reloadQuestionWithNewLanguage(){ const st document.getElementById(q-status); setStatus(st,); selectedOption -1; answerSubmitted false; stopTimer(); // Останавливаем предыдущий таймер // Скрываем оверлей с кнопкой Next const overlay document.getElementById(imgbox-overlay); if (overlay) { overlay.style.display none; } // Восстанавливаем активность кнопок ответов const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); options.forEach(opt > { opt.style.opacity 1; opt.style.pointerEvents auto; }); if (!currentQuestion) return; try{ const res await fetch(api(`/quiz/question/${currentQuestion.question_id}?lang${currentLang}`)); const body await res.json(); const dict i18ncurrentLang; if(res.ok){ // Обновляем текущий вопрос с новыми названиями currentQuestion body; // Показываем варианты ответа const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); body.options.forEach((option, index) > { let displayText option; const jp body.options_original_jp && body.options_original_jpindex; if (jp && jp.trim()) { const needsAppend !option.includes(`(${jp})`); displayText needsAppend ? `${option} (${jp})` : option; } optionsindex.textContent displayText; optionsindex.className quiz-option; optionsindex.onclick () > selectOption(index); }); optionsContainer.style.display block; setStatus(st, dict.loaded_question, true); // Запускаем таймер startTimer(); // Предзагружаем следующий вопрос в фоне preloadNextQuestion(); } else setStatus(st, body.detail || Error loading question, false, true); }catch(e){ setStatus(st, Сеть: +e.message, false, true); }}function selectOption(index) { if (selectedOption ! -1 || answerSubmitted) return; // Уже ответили selectedOption index; answerSubmitted true; stopTimer(); // Останавливаем таймер при выборе ответа const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); // Убираем выделение со всех вариантов options.forEach(opt > opt.classList.remove(selected)); // Выделяем выбранный вариант optionsindex.classList.add(selected); // Автоматически отправляем ответ sendAnswer();}async function sendAnswer(){ const st document.getElementById(q-status); setStatus(st,); const dict i18ncurrentLang; if(!token){ // Локальная проверка ответа для неавторизованных пользователей if(!currentQuestion){ setStatus(st,dict.need_question, false, true); return; } if(selectedOption -1){ setStatus(st,Выберите вариант ответа, false, true); return; } const answer currentQuestion.optionsselectedOption; // Отмечаем правильный/неправильный варианты локально const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); // Определяем правильный ответ из текущего вопроса const correctAnswerRu currentQuestion.correct_answer_ru; const correctAnswerEn currentQuestion.correct_answer_en; options.forEach((opt, index) > { opt.onclick null; // Извлекаем базовое название без скобок для сравнения const baseOption currentQuestion.optionsindex.split( (, 1)0.trim(); // Проверяем совпадение с правильным ответом const isCorrect (correctAnswerRu && baseOption correctAnswerRu) || (correctAnswerEn && baseOption correctAnswerEn); if (isCorrect) { opt.classList.add(correct); } else if (index selectedOption) { opt.classList.add(wrong); } }); // Показываем информационное сообщение о рейтинге setStatus(st, dict.need_login, false, false); // Предзагружаем следующий вопрос сразу после ответа preloadNextQuestion(); // Переход к следующему вопросу по настройке if (autoNext) { setTimeout(() > { nextQuestion(); }, 2000); } else { const overlay document.getElementById(imgbox-overlay); if (overlay) overlay.style.display flex; } return; } if(!currentQuestion){ setStatus(st,dict.need_question, false, true); return; } if(selectedOption -1){ setStatus(st,Выберите вариант ответа, false, true); return; } const answer currentQuestion.optionsselectedOption; try{ const res await fetch(api(/quiz/answer), { method:POST, headers:{ Content-Type:application/json, Authorization:Bearer +token }, body: JSON.stringify({ question_id: currentQuestion.question_id, answer }) }); const body await res.json(); if(res.ok){ // Показываем правильный/неправильный ответ const optionsContainer document.getElementById(quiz-options); const options optionsContainer.querySelectorAll(.quiz-option); options.forEach((opt, index) > { opt.onclick null; // Убираем возможность клика // Определяем правильный ответ с оригинальным названием const correctAnswerWithOriginal `${body.correct_answer_ru} (${body.correct_answer_jp})`; const correctAnswerEnWithOriginal `${body.correct_answer_en} (${body.correct_answer_jp})`; if (currentQuestion.optionsindex body.correct_answer_ru || currentQuestion.optionsindex body.correct_answer_en || opt.textContent.includes(body.correct_answer_jp)) { opt.classList.add(correct); } else if (index selectedOption && !body.correct) { opt.classList.add(wrong); } }); setStatus(st, body.correct ? `${dict.correct}${body.awarded_points}` : dict.wrong, body.correct, !body.correct); // Отправляем статистику на сервер try { await fetch(api(/quiz/update-stats), { method: POST, headers: { Content-Type: application/json, Authorization: Bearer + token }, body: JSON.stringify({ difficulty: currentDifficulty, correct: body.correct }) }); } catch (error) { console.error(Failed to update stats:, error); } // Предзагружаем следующий вопрос сразу после ответа preloadNextQuestion(); // Показываем кнопку Next или автоматически переходим к следующему вопросу if (autoNext) { // Автоматически переходим к следующему вопросу через 2 секунды setTimeout(() > { nextQuestion(); }, 2000); } else { // Показываем кнопку Next поверх скриншота const overlay document.getElementById(imgbox-overlay); if (overlay) { overlay.style.display flex; } } } else { setStatus(st, body.detail || Ошибка, false, true); } }catch(e){ setStatus(st, Сеть: +e.message, false, true); }}async function importAnime(){ const out document.getElementById(import-status); try{ const payload JSON.parse(document.getElementById(anime-json).value); const res await fetch(api(/anime/import), { method:POST, headers:{ Content-Type:application/json, Authorization:Bearer +token }, body: JSON.stringify(payload) }); const body await res.json(); out.textContent res.ok? `Импортировано: ${body.length}` : (body.detail || Ошибка импорта аниме); }catch(e){ out.textContent Ошибка формата/сети: +e.message; }}async function importQuiz(){ const out document.getElementById(import-status); try{ const payload JSON.parse(document.getElementById(quiz-json).value); const res await fetch(api(/quiz/import), { method:POST, headers:{ Content-Type:application/json, Authorization:Bearer +token }, body: JSON.stringify(payload) }); const body await res.json(); out.textContent res.ok? `Вопросов: ${body.length}` : (body.detail || Ошибка импорта вопросов); }catch(e){ out.textContent Ошибка формата/сети: +e.message; }}async function listAnime(){ const out document.getElementById(output); const res await fetch(api(/anime/)); const body await res.json().catch(()>); out.textContent JSON.stringify(body, null, 2);}async function leaderboard(){ try { const res await fetch(/profile/); if (!res.ok) { throw new Error(Ошибка загрузки лидерборда); } const users await res.json(); displayLeaderboard(users); } catch (error) { console.error(Ошибка:, error); alert(Ошибка загрузки лидерборда); }}async function openUserProfile(userId) { try { const response await fetch(`/profile/${userId}`); if (!response.ok) { throw new Error(Ошибка загрузки профиля пользователя); } const profile await response.json(); displayProfile(profile, false); // false чужой профиль } catch (error) { console.error(Ошибка:, error); alert(Ошибка загрузки профиля пользователя); }}function displayLeaderboard(users) { // Получаем текущий язык const currentLang localStorage.getItem(lang) || ru; // Создаем модальное окно лидерборда const modal document.createElement(div); modal.style.cssText ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; `; modal.innerHTML ` div style background: #152036; border-radius: 16px; padding: 32px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto; border: 1px solid #26324a; > div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;> h2 stylemargin: 0; color: var(--text); data-i18nleaderboard.title>Таблица лидеров/h2> button idclose-leaderboard style background: none; border: none; color: var(--text); font-size: 24px; cursor: pointer; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; >×/button> /div> div styleoverflow-x: auto;> table style width: 100%; border-collapse: collapse; background: #0f1422; border-radius: 8px; overflow: hidden; > thead> tr stylebackground: #26324a;> th style padding: 16px 12px; text-align: left; color: var(--text); font-weight: 600; border-bottom: 2px solid #2b65ff; data-i18nleaderboard.rank>Место/th> th style padding: 16px 12px; text-align: left; color: var(--text); font-weight: 600; border-bottom: 2px solid #2b65ff; data-i18nleaderboard.username>Пользователь/th> th style padding: 16px 12px; text-align: center; color: var(--text); font-weight: 600; border-bottom: 2px solid #2b65ff; data-i18nleaderboard.points>Очки/th> /tr> /thead> tbody> ${users.length > 0 ? users.map((user, index) > ` tr style border-bottom: 1px solid #26324a; transition: background-color 0.2s; cursor: pointer; onmouseoverthis.style.backgroundColor#1a2332 onmouseoutthis.style.backgroundColortransparent onclickopenUserProfile(${user.id})> td style padding: 16px 12px; color: var(--text); font-weight: bold; text-align: center; > div style display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: ${index 0 ? linear-gradient(135deg, #ffd700, #ffed4e) : index 1 ? linear-gradient(135deg, #c0c0c0, #e5e5e5) : index 2 ? linear-gradient(135deg, #cd7f32, #daa520) : linear-gradient(135deg, #2b65ff, #1b4ed6)}; color: ${index 3 ? #000 : #fff}; font-weight: bold; font-size: 14px; > ${user.rank || index + 1} /div> /td> td stylepadding: 16px 12px; color: var(--text);> div styledisplay: flex; align-items: center; gap: 12px;> div style width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, #2b65ff, #1b4ed6); display: flex; align-items: center; justify-content: center; font-size: 16px; color: white; font-weight: bold; flex-shrink: 0; > ${user.avatar_url ? `img src${escapeHtml(user.avatar_url)} stylewidth: 100%; height: 100%; border-radius: 50%; object-fit: cover; />` : user.username.charAt(0).toUpperCase()} /div> div> div stylefont-weight: 600; color: var(--text); ${index 3 ? background: linear-gradient(90deg, + (index 0 ? #ffd700, #ffffff, #ffd700 : index 1 ? #c0c0c0, #ffffff, #c0c0c0 : #cd7f32, #ffffff, #cd7f32) + ); background-size: 200% 100%; animation: shimmer 3s ease-in-out infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; : }>${escapeHtml(user.username)}/div> /div> /div> /td> td style padding: 16px 12px; color: #2b65ff; font-weight: bold; text-align: center; font-size: 18px; > ${user.total_points} /td> /tr> `).join() : ` tr> td colspan3 style padding: 32px; text-align: center; color: var(--muted); font-style: italic; data-i18nleaderboard.no_users>Пользователи не найдены/td> /tr> `} /tbody> /table> /div> /div> `; document.body.appendChild(modal); // Применяем переводы к модальному окну applyI18n(); // Обработчик закрытия document.getElementById(close-leaderboard).onclick () > modal.remove();}function toggleAutoNext() { autoNext document.getElementById(auto-next-toggle).checked; localStorage.setItem(autoNext, autoNext);}function handleNextButton() { nextQuestion();}// Функции для работы с уровнями сложностиfunction selectDifficulty(difficulty) { // Проверяем, что кнопки существуют const allButtons document.querySelectorAll(.difficulty-btn); // Убираем класс selected со всех кнопок allButtons.forEach((btn) > { btn.classList.remove(selected); }); // Добавляем класс selected к выбранной кнопке const selectedBtn document.querySelector(`data-difficulty${difficulty}`); if (selectedBtn) { selectedBtn.classList.add(selected); } // Сохраняем выбранный уровень сложности currentDifficulty difficulty; localStorage.setItem(difficulty, difficulty);}function initializeDifficultySelection() { // Проверяем, что кнопки существуют const buttons document.querySelectorAll(.difficulty-btn); if (buttons.length 0) { return; } // Устанавливаем сохраненный уровень сложности selectDifficulty(currentDifficulty); // Добавляем обработчики событий для кнопок buttons.forEach((btn) > { btn.addEventListener(click, () > { const difficulty btn.getAttribute(data-difficulty); selectDifficulty(difficulty); }); });}async function startQuiz() { // Скрываем кнопку Start и показываем индикатор загрузки const startSection document.getElementById(start-section); const loadingSection document.getElementById(loading-section); startSection.style.display none; loadingSection.style.display flex; loadingSection.style.flexDirection column; try { // Сначала загружаем первый вопрос await nextQuestion(); // Предзагружаем второй вопрос preloadNextQuestion(); // Только после загрузки показываем контент квиза const quizSection document.getElementById(quiz-section); const timerContainer document.querySelector(.timer-container); loadingSection.style.display none; quizSection.style.display block; timerContainer.style.display block; } catch (error) { console.error(Ошибка загрузки вопроса:, error); // В случае ошибки возвращаемся к кнопке Start loadingSection.style.display none; startSection.style.display flex; }}function goBackToDifficultySelection() { // Останавливаем таймер stopTimer(); // Сбрасываем состояние квиза currentQuestion null; selectedOption -1; answerSubmitted false; preloadedQuestion null; // Скрываем секции квиза const quizSection document.getElementById(quiz-section); const quizOptions document.getElementById(quiz-options); const timerContainer document.querySelector(.timer-container); const statusElement document.getElementById(q-status); const backButton document.getElementById(back-button); quizSection.style.display none; quizOptions.style.display none; timerContainer.style.display none; statusElement.textContent ; // Показываем секцию выбора сложности const startSection document.getElementById(start-section); startSection.style.display flex; // Сбрасываем выбор сложности (можно оставить текущий или сбросить) // initializeDifficultySelection(); // Раскомментируйте, если хотите сбросить выбор}function logout() { token ; localStorage.removeItem(token); setUser(null); // Скрываем пользовательское меню const userMenu document.getElementById(user-menu); if (userMenu) { userMenu.style.display none; }}// Функция для экранирования HTMLfunction escapeHtml(text) { if (!text) return ; const div document.createElement(div); div.textContent text; return div.innerHTML;}async function showProfile() { try { const response await fetch(/profile/me, { headers: { Authorization: `Bearer ${localStorage.getItem(token)}` } }); if (!response.ok) { throw new Error(Ошибка загрузки профиля); } const profile await response.json(); displayProfile(profile, true); // true свой профиль } catch (error) { console.error(Ошибка:, error); alert(Ошибка загрузки профиля); }}function displayProfile(profile, isOwnProfile false) { // Получаем текущий язык const currentLang localStorage.getItem(lang) || ru; // Создаем модальное окно профиля const modal document.createElement(div); modal.style.cssText ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; `; modal.innerHTML ` div style background: #152036; border-radius: 16px; padding: 32px; max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; border: 1px solid #26324a; > div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;> h2 stylemargin: 0; color: var(--text);>${isOwnProfile ? (currentLang en ? My Profile : Мой профиль) : (currentLang en ? User Profile : Профиль пользователя)}/h2> button idclose-profile style background: none; border: none; color: var(--text); font-size: 24px; cursor: pointer; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; >×/button> /div> div styletext-align: center; margin-bottom: 24px;> div idavatar-container style width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #2b65ff, #1b4ed6); display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; font-size: 32px; color: white; font-weight: bold; cursor: pointer; position: relative; transition: transform 0.2s; onmouseoverthis.style.transformscale(1.05) onmouseoutthis.style.transformscale(1)> ${profile.avatar_url ? `img src${profile.avatar_url} stylewidth: 100%; height: 100%; border-radius: 50%; object-fit: cover; />` : profile.username.charAt(0).toUpperCase()} div style position: absolute; bottom: -2px; right: -2px; width: 24px; height: 24px; background: #2b65ff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; color: white; border: 2px solid #152036; >✏️/div> /div> h3 stylemargin: 0; color: var(--text); ${profile.rank && profile.rank 3 ? background: linear-gradient(90deg, + (profile.rank 1 ? #ffd700, #ffffff, #ffd700 : profile.rank 2 ? #c0c0c0, #ffffff, #c0c0c0 : #cd7f32, #ffffff, #cd7f32) + ); background-size: 200% 100%; animation: shimmer 3s ease-in-out infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; : }>${profile.username}/h3> /div> div stylemargin-bottom: 24px;> div styledisplay: flex; justify-content: space-between; margin-bottom: 16px;> div styletext-align: center; flex: 1;> div stylefont-size: 24px; font-weight: bold; color: #2b65ff;>${profile.total_points}/div> div stylefont-size: 12px; color: var(--muted); data-i18nprofile.points>Очков/div> /div> div styletext-align: center; flex: 1;> div stylefont-size: 24px; font-weight: bold; color: #2b65ff;>${profile.rank || —}/div> div stylefont-size: 12px; color: var(--muted); data-i18nprofile.rank>Место/div> /div> div styletext-align: center; flex: 1;> div stylefont-size: 24px; font-weight: bold; color: #2b65ff;>${new Date(profile.created_at).toLocaleDateString(currentLang en ? en-US : ru-RU)}/div> div stylefont-size: 12px; color: var(--muted); data-i18nprofile.registration>Регистрация/div> /div> /div> /div> div stylemargin-bottom: 24px;> h3 stylemargin: 0 0 16px 0; color: var(--text); font-size: 16px; data-i18nprofile.stats.title>Статистика по режимам/h3> div styledisplay: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px;> div styletext-align: center; padding: 16px; background: #0f1422; border-radius: 8px; border: 1px solid #26324a;> div stylefont-size: 18px; font-weight: bold; color: #38b000; data-i18nprofile.stats.easy>Легкий/div> div stylemargin-top: 8px;> div stylefont-size: 20px; font-weight: bold; color: #2b65ff;>${profile.easy_correct || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.correct>Правильно/div> /div> div stylemargin-top: 4px;> div stylefont-size: 16px; color: var(--text);>${profile.easy_total || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.total>Всего/div> /div> div stylemargin-top: 8px; font-size: 14px; font-weight: bold; color: #38b000;> ${profile.easy_accuracy || 0}% /div> div stylefont-size: 10px; color: var(--muted); data-i18nprofile.stats.accuracy>Точность/div> /div> div styletext-align: center; padding: 16px; background: #0f1422; border-radius: 8px; border: 1px solid #26324a;> div stylefont-size: 18px; font-weight: bold; color: #2b65ff; data-i18nprofile.stats.normal>Обычный/div> div stylemargin-top: 8px;> div stylefont-size: 20px; font-weight: bold; color: #2b65ff;>${profile.normal_correct || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.correct>Правильно/div> /div> div stylemargin-top: 4px;> div stylefont-size: 16px; color: var(--text);>${profile.normal_total || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.total>Всего/div> /div> div stylemargin-top: 8px; font-size: 14px; font-weight: bold; color: #2b65ff;> ${profile.normal_accuracy || 0}% /div> div stylefont-size: 10px; color: var(--muted); data-i18nprofile.stats.accuracy>Точность/div> /div> div styletext-align: center; padding: 16px; background: #0f1422; border-radius: 8px; border: 1px solid #26324a;> div stylefont-size: 18px; font-weight: bold; color: #ff4d6d; data-i18nprofile.stats.hard>Сложный/div> div stylemargin-top: 8px;> div stylefont-size: 20px; font-weight: bold; color: #2b65ff;>${profile.hard_correct || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.correct>Правильно/div> /div> div stylemargin-top: 4px;> div stylefont-size: 16px; color: var(--text);>${profile.hard_total || 0}/div> div stylefont-size: 11px; color: var(--muted); data-i18nprofile.stats.total>Всего/div> /div> div stylemargin-top: 8px; font-size: 14px; font-weight: bold; color: #ff4d6d;> ${profile.hard_accuracy || 0}% /div> div stylefont-size: 10px; color: var(--muted); data-i18nprofile.stats.accuracy>Точность/div> /div> /div> /div> div stylemargin-bottom: 24px;> div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;> label stylecolor: var(--text); font-weight: 500; data-i18nprofile.description>Описание:/label> button idedit-profile style padding: 6px 12px; border-radius: 6px; border: 1px solid #26324a; background: #152036; color: var(--text); font-size: 12px; cursor: pointer; display: ${isOwnProfile ? block : none}; data-i18nprofile.edit>Edit/button> /div> div iddescription-display style padding: 12px; border-radius: 8px; border: 1px solid #26324a; background: #0f1422; color: var(--text); min-height: 60px; white-space: pre-wrap; >${escapeHtml(profile.description) || (currentLang en ? No description provided : Описание не указано)}/div> div iddescription-edit styledisplay: none;> textarea idprofile-description style width: 100%; height: 80px; padding: 12px; border-radius: 8px; border: 1px solid #26324a; background: #0f1422; color: var(--text); font-family: inherit; resize: vertical; placeholder${currentLang en ? Tell us about yourself... : Расскажите о себе...}>${escapeHtml(profile.description) || }/textarea> div styledisplay: flex; gap: 8px; margin-top: 12px;> button idsave-profile style padding: 8px 16px; border-radius: 6px; border: none; background: linear-gradient(180deg, #2b65ff, #1b4ed6); color: white; font-size: 12px; cursor: pointer; data-i18nprofile.save>Сохранить/button> button idcancel-edit style padding: 8px 16px; border-radius: 6px; border: 1px solid #26324a; background: #152036; color: var(--text); font-size: 12px; cursor: pointer; data-i18nprofile.cancel>Отмена/button> /div> /div> /div> /div> `; document.body.appendChild(modal); // Применяем переводы к модальному окну applyI18n(); // Обработчики событий document.getElementById(close-profile).onclick () > modal.remove(); // Обработчик клика на аватарку (только для собственного профиля) if (isOwnProfile) { document.getElementById(avatar-container).onclick () > { const input document.createElement(input); input.type file; input.accept image/*; input.onchange async (e) > { const file e.target.files0; if (!file) return; // Проверяем размер файла (5MB максимум) if (file.size > 5 * 1024 * 1024) { const currentLang localStorage.getItem(lang) || ru; const errorMessage currentLang en ? File size must be less than 5MB : Размер файла должен быть меньше 5MB; alert(errorMessage); return; } // Проверяем тип файла if (!file.type.startsWith(image/)) { const currentLang localStorage.getItem(lang) || ru; const errorMessage currentLang en ? Please select an image file : Пожалуйста, выберите файл изображения; alert(errorMessage); return; } // Показываем индикатор загрузки const avatarContainer document.getElementById(avatar-container); const originalContent avatarContainer.innerHTML; avatarContainer.innerHTML ` div style width: 100%; height: 100%; border-radius: 50%; background: var(--muted); display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--text); >Загрузка.../div> `; try { // Создаем FormData для загрузки файла const formData new FormData(); formData.append(file, file); const response await fetch(/profile/upload-avatar, { method: POST, headers: { Authorization: `Bearer ${localStorage.getItem(token)}` }, body: formData }); const result await response.json(); if (!response.ok) { throw new Error(result.detail || Ошибка загрузки аватарки); } // Обновляем аватарку avatarContainer.innerHTML ` img src${result.avatar_url} stylewidth: 100%; height: 100%; border-radius: 50%; object-fit: cover; /> div style position: absolute; bottom: -2px; right: -2px; width: 24px; height: 24px; background: #2b65ff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; color: white; border: 2px solid #152036; >✏️/div> `; const currentLang localStorage.getItem(lang) || ru; const successMessage currentLang en ? Avatar uploaded successfully! : Аватарка успешно загружена!; alert(successMessage); } catch (error) { console.error(Ошибка:, error); // Восстанавливаем оригинальное содержимое avatarContainer.innerHTML originalContent; const currentLang localStorage.getItem(lang) || ru; const errorMessage currentLang en ? Error uploading avatar: + error.message : Ошибка загрузки аватарки: + error.message; alert(errorMessage); } }; input.click(); }; } // Обработчик кнопки Edit (только для собственного профиля) if (isOwnProfile) { document.getElementById(edit-profile).onclick () > { document.getElementById(description-display).style.display none; document.getElementById(description-edit).style.display block; document.getElementById(edit-profile).style.display none; }; // Обработчик кнопки Cancel в режиме редактирования document.getElementById(cancel-edit).onclick () > { document.getElementById(description-display).style.display block; document.getElementById(description-edit).style.display none; document.getElementById(edit-profile).style.display block; }; // Обработчик кнопки Save document.getElementById(save-profile).onclick async () > { const description document.getElementById(profile-description).value; try { const response await fetch(/profile/me, { method: PUT, headers: { Content-Type: application/json, Authorization: `Bearer ${localStorage.getItem(token)}` }, body: JSON.stringify({ description }) }); if (!response.ok) { throw new Error(Ошибка сохранения профиля); } // Обновляем отображение описания const currentLang localStorage.getItem(lang) || ru; const noDescriptionText currentLang en ? No description provided : Описание не указано; document.getElementById(description-display).textContent description || noDescriptionText; // Возвращаемся к режиму просмотра document.getElementById(description-display).style.display block; document.getElementById(description-edit).style.display none; document.getElementById(edit-profile).style.display block; const successMessage currentLang en ? Profile saved! : Профиль сохранен!; alert(successMessage); } catch (error) { console.error(Ошибка:, error); alert(Ошибка сохранения профиля); } }; }}// eventsaddEventListener(DOMContentLoaded, () > { document.getElementById(btn-register).onclick register; document.getElementById(btn-login).onclick login; // Инициализируем переключатель Auto next const autoNextToggle document.getElementById(auto-next-toggle); if (autoNextToggle) { autoNextToggle.checked autoNext; autoNextToggle.addEventListener(change, toggleAutoNext); } // Инициализируем выбор уровня сложности initializeDifficultySelection(); // Обработчик для кнопки Next в оверлее const nextButtonOverlay document.getElementById(next-button-overlay); if (nextButtonOverlay) { nextButtonOverlay.addEventListener(click, handleNextButton); } // Защита от правого клика и контекстного меню const qImage document.getElementById(q-image); if (qImage) { // Блокируем контекстное меню qImage.addEventListener(contextmenu, (e) > { e.preventDefault(); return false; }); // Блокируем перетаскивание qImage.addEventListener(dragstart, (e) > { e.preventDefault(); return false; }); // Блокируем выделение текста qImage.addEventListener(selectstart, (e) > { e.preventDefault(); return false; }); // Дополнительная защита от копирования qImage.addEventListener(copy, (e) > { e.preventDefault(); return false; }); } // Глобальная защита от правого клика (только для изображений квиза) document.addEventListener(contextmenu, (e) > { // Блокируем контекстное меню только для изображений квиза if (e.target && e.target.id q-image) { e.preventDefault(); return false; } }); // Блокируем горячие клавиши для копирования/сохранения изображений document.addEventListener(keydown, (e) > { // Ctrl+S, Ctrl+C, F12 (DevTools) if ((e.ctrlKey && (e.key s || e.key c)) || e.key F12) { // Блокируем только если фокус на изображении квиза if (document.activeElement && document.activeElement.id q-image) { e.preventDefault(); return false; } } }); // Обработчики для пользовательского меню const userMenuToggle document.getElementById(user-menu-toggle); const userMenu document.getElementById(user-menu); const btnProfile document.getElementById(btn-profile); const btnLogout document.getElementById(btn-logout); if (userMenuToggle && userMenu) { userMenuToggle.addEventListener(click, (e) > { e.stopPropagation(); userMenu.style.display userMenu.style.display none || userMenu.style.display ? block : none; }); // Не закрывать при кликах внутри меню userMenu.addEventListener(click, (e) > { e.stopPropagation(); }); } if (btnProfile) { btnProfile.addEventListener(click, showProfile); } if (btnLogout) { btnLogout.addEventListener(click, logout); } // Обработчик для кнопки Start const startButton document.getElementById(start-button); if (startButton) { startButton.addEventListener(click, startQuiz); } const btnList document.getElementById(btn-list-anime); if (btnList) btnList.onclick listAnime; const lbTop document.getElementById(btn-leaderboard-top); if (lbTop) lbTop.onclick leaderboard; // auth dropdown const toggle document.getElementById(auth-toggle); const menu document.getElementById(auth-menu); if (toggle && menu) { toggle.addEventListener(click, (e)>{ e.stopPropagation(); menu.style.display menu.style.display none || menu.style.display ? block : none; }); // Не закрывать при кликах внутри меню menu.addEventListener(click, (e)>{ e.stopPropagation(); }); document.addEventListener(click, ()>{ menu.style.display none; // Также закрываем пользовательское меню const userMenu document.getElementById(user-menu); if (userMenu) { userMenu.style.display none; } }); } // language switch const btnLang document.getElementById(btn-lang); if (btnLang) { btnLang.addEventListener(click, ()>{ const newLang currentLang ru ? en : ru; const currentPath window.location.pathname; // Сохраняем новый язык в localStorage localStorage.setItem(lang, newLang); // Определяем новый URL в зависимости от текущего пути let newPath; if (currentPath / || currentPath /ru/) { newPath newLang en ? /en/ : /ru/; } else if (currentPath /faq || currentPath /ru/faq) { newPath newLang en ? /en/faq : /ru/faq; } else if (currentPath.startsWith(/en/)) { newPath currentPath.replace(/en/, newLang ru ? /ru/ : /en/); } else { newPath newLang en ? /en/ : /ru/; } // Переходим на новую страницу window.location.href newPath; }); } // Footer localized links const footerHome document.querySelector(data-footer-home); const footerFaq document.querySelector(data-footer-faq); if (footerHome) { footerHome.textContent currentLang en ? Home : Главная; footerHome.href currentLang en ? /en/ : /ru/; } if (footerFaq) { footerFaq.textContent FAQ; footerFaq.href currentLang en ? /en/faq : /ru/faq; } const headerFaq document.querySelector(data-header-faq); if (headerFaq) { headerFaq.href currentLang en ? /en/faq : /ru/faq; } applyI18n(); // Инициализируем состояние авторизации сразу if (token) { // Если есть токен, показываем секцию пользователя document.getElementById(auth-section).style.display none; document.getElementById(user-section).style.display flex; } else { // Если нет токена, показываем секцию авторизации document.getElementById(auth-section).style.display block; document.getElementById(user-section).style.display none; } me();});/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]