<script>
document.addEventListener('DOMContentLoaded', function() {
(function() {
// ==================== НАСТРОЙКИ ====================
// Конфигурация таймера
const INITIAL_MINUTES = 0;
const INITIAL_SECONDS = 10;
const INITIAL_TOTAL_SECONDS = INITIAL_MINUTES * 60 + INITIAL_SECONDS;
// Время хранения данных в часах
const STORAGE_EXPIRY_HOURS = 1; // Можно менять: 1, 12, 24, 48 и т.д.
// Вычисляем время хранения в миллисекундах
const STORAGE_EXPIRY_TIME = STORAGE_EXPIRY_HOURS * 60 * 60 * 1000;
// Ключи для localStorage
const STORAGE_KEYS = {
START_TIME: 'test_start_timestamp', // Время начала теста
TEST_COMPLETED: 'test_completed',
TIME_EXPIRED: 'test_time_expired',
TIMESTAMP: 'test_timestamp' // Временная метка для отслеживания срока хранения
};
// ===================================================
// Состояние таймера
let timerInterval = null;
let currentTime = {
minutes: INITIAL_MINUTES,
seconds: INITIAL_SECONDS
};
let isTestCompleted = false; // Флаг, чтобы не обрабатывать повторно
let testStartTime = null; // Время начала теста в миллисекундах
let isTimeExpiredHandled = false; // Флаг, чтобы не обрабатывать истечение времени повторно
// Функция для получения общего времени в секундах
function getTotalSeconds(minutes, seconds) {
return minutes * 60 + seconds;
}
// Функция для обновления отображения таймера
function updateTimerDisplay() {
const minuteElements = document.querySelectorAll('.el-minutes .tn-atom');
const secondElements = document.querySelectorAll('.el-seconds .tn-atom');
// Форматируем секунды (добавляем ведущий ноль)
const formattedSeconds = currentTime.seconds < 10 ? '0' + currentTime.seconds : currentTime.seconds;
const formattedMinutes = currentTime.minutes < 10 ? '0' + currentTime.minutes : currentTime.minutes;
minuteElements.forEach(el => {
if (el) el.textContent = formattedMinutes;
});
secondElements.forEach(el => {
if (el) el.textContent = formattedSeconds;
});
}
// Функция для расчета оставшегося времени на основе реального времени
function calculateRemainingTime() {
if (!testStartTime) return false;
const currentRealTime = Date.now();
const elapsedSeconds = Math.floor((currentRealTime - testStartTime) / 1000);
const remainingSeconds = Math.max(0, INITIAL_TOTAL_SECONDS - elapsedSeconds);
currentTime.minutes = Math.floor(remainingSeconds / 60);
currentTime.seconds = remainingSeconds % 60;
updateTimerDisplay();
// Если время истекло и еще не обработано
if (remainingSeconds <= 0 && !isTimeExpiredHandled) {
stopTimer();
handleTimeExpired();
return false;
}
return remainingSeconds;
}
// Функция для проверки актуальности данных (не истек ли срок хранения)
function isStorageDataValid() {
const timestamp = localStorage.getItem(STORAGE_KEYS.TIMESTAMP);
if (!timestamp) return false;
const currentTime = Date.now();
const timePassed = currentTime - parseInt(timestamp);
return timePassed < STORAGE_EXPIRY_TIME;
}
// Функция для очистки устаревших данных
function clearExpiredData() {
if (!isStorageDataValid()) {
Object.values(STORAGE_KEYS).forEach(key => {
localStorage.removeItem(key);
});
return true;
}
return false;
}
// Функция для сохранения времени начала теста
function saveStartTimeToStorage() {
if (testStartTime) {
localStorage.setItem(STORAGE_KEYS.START_TIME, testStartTime.toString());
localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
}
}
// Функция для загрузки времени начала теста из localStorage
function loadStartTimeFromStorage() {
const savedStartTime = localStorage.getItem(STORAGE_KEYS.START_TIME);
if (savedStartTime !== null) {
testStartTime = parseInt(savedStartTime);
return true;
}
return false;
}
// Функция для сохранения статуса завершения теста
function saveTestCompletedToStorage() {
localStorage.setItem(STORAGE_KEYS.TEST_COMPLETED, 'true');
localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
}
// Функция для сохранения статуса истечения времени
function saveTimeExpiredToStorage() {
localStorage.setItem(STORAGE_KEYS.TIME_EXPIRED, 'true');
localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
}
// Функция для остановки таймера
function stopTimer() {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
}
}
// Функция для запуска таймера (на основе реального времени)
function startTimer() {
if (timerInterval) {
stopTimer();
}
// Сразу рассчитываем оставшееся время
calculateRemainingTime();
// Запускаем интервал для обновления каждую секунду
timerInterval = setInterval(() => {
const remainingSeconds = calculateRemainingTime();
if (remainingSeconds <= 0) {
stopTimer();
}
}, 1000);
}
// Функция для обработки истечения времени
function handleTimeExpired() {
// Предотвращаем повторную обработку
if (isTimeExpiredHandled) return;
isTimeExpiredHandled = true;
// Проверяем, сдан ли тест
const testForm = document.querySelector('.uc-test-block form');
const isTestCompletedNow = testForm && testForm.classList.contains('js-send-form-success');
if (isTestCompletedNow) {
// Тест сдан успешно - записываем в хранилище, что тест завершен
saveTestCompletedToStorage();
// Показываем только reload блок
const reloadBlock = document.querySelector('.uc-reload-block');
if (reloadBlock) reloadBlock.style.display = 'block';
} else {
// Тест не сдан - записываем в хранилище, что время истекло
saveTimeExpiredToStorage();
// Скрываем блок теста
const testBlock = document.querySelector('.uc-test-block');
if (testBlock) testBlock.style.display = 'none';
// Показываем блок истекшего времени
const timeOverBlock = document.querySelector('.uc-time-over');
if (timeOverBlock) timeOverBlock.style.display = 'block';
// Показываем блок перезагрузки
const reloadBlock = document.querySelector('.uc-reload-block');
if (reloadBlock) reloadBlock.style.display = 'block';
}
}
// Функция для скрытия всех блоков
function hideAllBlocks() {
const blocks = [
'.uc-timer-block',
'.uc-timer-menu',
'.uc-test-block',
'.uc-time-over',
'.uc-test-finished',
'.uc-reload-block'
];
blocks.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
if (el) el.style.display = 'none';
});
});
}
// Функция для показа блоков
function showBlocks(blocksToShow) {
blocksToShow.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
if (el) el.style.display = 'block';
});
});
}
// Функция для скрытия кнопок start-test (ищем по href)
function hideStartTestButtons() {
const startButtons = document.querySelectorAll('a[href="#start-test"]');
startButtons.forEach(button => {
const parentTrec = button.closest('.t-rec');
if (parentTrec) {
parentTrec.style.display = 'none';
}
});
}
// Функция для проверки статуса теста при загрузке
function checkInitialStatus() {
// Проверяем, не устарели ли данные
const isDataValid = isStorageDataValid();
if (!isDataValid) {
// Если данные устарели, очищаем их и выходим
clearExpiredData();
return;
}
const testCompleted = localStorage.getItem(STORAGE_KEYS.TEST_COMPLETED) === 'true';
const timeExpired = localStorage.getItem(STORAGE_KEYS.TIME_EXPIRED) === 'true';
const savedStartTime = localStorage.getItem(STORAGE_KEYS.START_TIME);
// Если нет сохраненных данных
if (!savedStartTime && !testCompleted && !timeExpired) {
// Ждем нажатия кнопки start-test
return;
}
// Скрываем кнопки start-test
hideStartTestButtons();
// Если тест уже завершен
if (testCompleted) {
// При загрузке показываем блок завершения теста и reload блок
showBlocks(['.uc-test-finished', '.uc-reload-block']);
return;
}
// Если время истекло
if (timeExpired) {
// При загрузке показываем блок истекшего времени и reload блок
showBlocks(['.uc-time-over', '.uc-reload-block']);
return;
}
// Если есть сохраненное время начала
if (savedStartTime) {
testStartTime = parseInt(savedStartTime);
// Рассчитываем оставшееся время
const currentRealTime = Date.now();
const elapsedSeconds = Math.floor((currentRealTime - testStartTime) / 1000);
const remainingSeconds = INITIAL_TOTAL_SECONDS - elapsedSeconds;
if (remainingSeconds > 0) {
// Время еще осталось и тест не сдан - показываем блоки таймера и теста
showBlocks(['.uc-timer-block', '.uc-timer-menu', '.uc-test-block']);
// Обновляем отображение времени
currentTime.minutes = Math.floor(remainingSeconds / 60);
currentTime.seconds = remainingSeconds % 60;
updateTimerDisplay();
// Запускаем таймер
startTimer();
} else {
// Время истекло - обрабатываем истечение времени
handleTimeExpired();
}
}
}
// Функция для запуска теста
function startTest() {
// Скрываем кнопки start-test
hideStartTestButtons();
// Устанавливаем время начала теста (текущее реальное время)
testStartTime = Date.now();
// Сбрасываем флаги
isTimeExpiredHandled = false;
isTestCompleted = false;
// Сбрасываем время к начальному
currentTime.minutes = INITIAL_MINUTES;
currentTime.seconds = INITIAL_SECONDS;
updateTimerDisplay();
// Показываем нужные блоки
showBlocks(['.uc-timer-block', '.uc-timer-menu', '.uc-test-block']);
// Сохраняем время начала в хранилище
saveStartTimeToStorage();
// Удаляем старые статусы, если они были
localStorage.removeItem(STORAGE_KEYS.TEST_COMPLETED);
localStorage.removeItem(STORAGE_KEYS.TIME_EXPIRED);
// Запускаем таймер
startTimer();
}
// Функция для обработки успешной сдачи теста
function handleTestSuccess() {
if (isTestCompleted) return; // Предотвращаем повторную обработку
isTestCompleted = true;
// Записываем в хранилище, что тест сдали
saveTestCompletedToStorage();
// Останавливаем таймер
stopTimer();
// Показываем блок .uc-reload-block
const reloadBlock = document.querySelector('.uc-reload-block');
if (reloadBlock) {
reloadBlock.style.display = 'block';
}
// НЕ СКРЫВАЕМ блоки .uc-timer-block, .uc-timer-menu, .uc-test-block
// Они остаются видимыми
}
// Функция для отслеживания появления класса js-send-form-success
function watchForTestCompletion() {
const testForm = document.querySelector('.uc-test-block form');
if (!testForm) return;
// Используем MutationObserver для отслеживания изменений класса
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
if (testForm.classList.contains('js-send-form-success')) {
// Класс появился, проверяем, не закончилось ли время
const timeExpired = localStorage.getItem(STORAGE_KEYS.TIME_EXPIRED) === 'true';
if (!timeExpired) {
// Время еще есть, тест успешно сдан
handleTestSuccess();
} else {
// Время закончилось, но тест все равно сдан
saveTestCompletedToStorage();
// Показываем блок перезагрузки
const reloadBlock = document.querySelector('.uc-reload-block');
if (reloadBlock) reloadBlock.style.display = 'block';
// НЕ СКРЫВАЕМ блоки
}
}
}
});
});
observer.observe(testForm, { attributes: true });
}
// Функция для сброса всех данных
function resetTimer() {
// Очищаем localStorage
Object.values(STORAGE_KEYS).forEach(key => {
localStorage.removeItem(key);
});
// Останавливаем таймер
stopTimer();
// Обновляем страницу
window.location.reload();
}
// Функция для проверки наличия кнопок с href="#start-test"
function checkForStartButtons() {
const startButtons = document.querySelectorAll('a[href="#start-test"]');
if (startButtons.length > 0) {
return true;
}
return false;
}
// Функция для инициализации всех обработчиков
function init() {
// Проверяем наличие кнопок с href="#start-test"
if (!checkForStartButtons()) {
console.log('Кнопки с href="#start-test" не найдены');
return;
}
// Назначаем обработчики на кнопки start-test (по href)
const startButtons = document.querySelectorAll('a[href="#start-test"]');
startButtons.forEach(button => {
button.addEventListener('click', (e) => {
e.preventDefault();
startTest();
});
});
// Назначаем обработчик на кнопку сброса (по href="#reset-timer")
const resetButton = document.querySelector('a[href="#reset-timer"]');
if (resetButton) {
resetButton.addEventListener('click', (e) => {
e.preventDefault();
resetTimer();
});
}
// Проверяем сохраненное состояние
checkInitialStatus();
// Начинаем отслеживать завершение теста
watchForTestCompletion();
}
// Запускаем инициализацию после загрузки страницы
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
});
</script>
<style>
.uc-timer-block,
.uc-timer-menu,
.uc-test-block,
.uc-time-over,
.uc-test-finished,
.uc-reload-block
{
display: none;
}
.uc-timer-menu {
position: fixed;
bottom: 0;
z-index: 999;
width: 100%;
}
</style>