загрузка сервиса и рандом

This commit is contained in:
Калугин Олег Александрович
2026-02-22 08:26:12 +00:00
committed by GitVerse
parent 01238e93d9
commit 97609b3ba7
3 changed files with 896 additions and 18 deletions

848
public/loading-start.js Normal file
View File

@@ -0,0 +1,848 @@
// loading-start.js - Загружается первым в <head>
(function() {
// Конфигурация
const CONFIG = {
maxRetries: 3,
retryDelay: 2000,
timeout: 10000,
showWarnings: true,
maxLoadingTime: 15000, // Максимум 15 секунд
minLoadingTime: 5000 // Минимум 5 секунд
};
// Генерация случайных цветов для градиента
function getRandomGradient() {
// Базовая палитра цветов для школы/образования
const colorPalettes = [
// Сине-фиолетовая гамма
{ start: '#667eea', end: '#764ba2' },
{ start: '#6B73FF', end: '#000DFF' },
{ start: '#5f72bd', end: '#9b23ea' },
// Зелено-голубая гамма
{ start: '#11998e', end: '#38ef7d' },
{ start: '#0BA360', end: '#3CBBB2' },
{ start: '#1D976C', end: '#93F9B9' },
// Оранжево-розовая гамма
{ start: '#f46b45', end: '#eea849' },
{ start: '#FF512F', end: '#DD2476' },
{ start: '#F09819', end: '#EDDE5D' },
// Красная гамма
{ start: '#cb2d3e', end: '#ef473a' },
{ start: '#FF416C', end: '#FF4B2B' },
{ start: '#DC2424', end: '#4A569D' },
// Фиолетовая гамма
{ start: '#8E2DE2', end: '#4A00E0' },
{ start: '#A770EF', end: '#CF8BF3' },
{ start: '#7F00FF', end: '#E100FF' },
// Морская гамма
{ start: '#00B4DB', end: '#0083B0' },
{ start: '#00C9FF', end: '#92FE9D' },
{ start: '#1FA2FF', end: '#12D8FA' },
// Осенняя гамма
{ start: '#FC4A1A', end: '#F7B733' },
{ start: '#ED213A', end: '#93291E' },
{ start: '#F12711', end: '#F5AF19' },
// Ночная гамма
{ start: '#141E30', end: '#243B55' },
{ start: '#0F2027', end: '#203A43' },
{ start: '#1c1c1c', end: '#3a3a3a' },
// Пастельная гамма
{ start: '#a8c0ff', end: '#3f2b96' },
{ start: '#c84e89', end: '#F15F79' },
{ start: '#b993d0', end: '#8ca6db' },
// Школьная гамма
{ start: '#56ab2f', end: '#a8e063' },
{ start: '#1D976C', end: '#93F9B9' },
{ start: '#4CA1AF', end: '#2C3E50' }
];
// Случайная палитра
const palette = colorPalettes[Math.floor(Math.random() * colorPalettes.length)];
// Случайное смещение градиента
const directions = [
'135deg', '90deg', '45deg', '180deg', '225deg', '270deg', '315deg'
];
const direction = directions[Math.floor(Math.random() * directions.length)];
return {
background: `linear-gradient(${direction}, ${palette.start}, ${palette.end})`,
start: palette.start,
end: palette.end,
direction: direction
};
}
// Генерация случайного цвета для прогресс-бара
function getRandomProgressColor() {
const colors = [
'#fff', '#ffd700', '#00ff87', '#00ffff', '#ff69b4', '#ffa500',
'#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dfe6e9'
];
return colors[Math.floor(Math.random() * colors.length)];
}
// Генерация случайного цвета для текста
function getRandomTextColor() {
const colors = [
'white', '#f0f0f0', '#ffeaa7', '#dfe6e9', '#b2bec3', '#ffffff'
];
return colors[Math.floor(Math.random() * colors.length)];
}
// Получаем случайные цвета для этой загрузки
const gradientColors = getRandomGradient();
const progressColor = getRandomProgressColor();
const textColor = getRandomTextColor();
const accentColor = getRandomProgressColor();
// Статусы для динамического отображения
const LOADING_STATUSES = [
{ emoji: '📦', text: 'Загрузка компонентов...', weight: 10 },
{ emoji: '🔧', text: 'Настройка модулей...', weight: 15 },
{ emoji: '⚙️', text: 'Инициализация системы...', weight: 20 },
{ emoji: '🔌', text: 'Подключение к серверу...', weight: 25 },
{ emoji: '📊', text: 'Загрузка данных...', weight: 30 },
{ emoji: '🧩', text: 'Сборка интерфейса...', weight: 35 },
{ emoji: '🎨', text: 'Отрисовка элементов...', weight: 40 },
{ emoji: '🔐', text: 'Проверка безопасности...', weight: 45 },
{ emoji: '📁', text: 'Загрузка файлов...', weight: 50 },
{ emoji: '🔄', text: 'Синхронизация...', weight: 55 },
{ emoji: '🧪', text: 'Тестирование модулей...', weight: 60 },
{ emoji: '📝', text: 'Подготовка задач...', weight: 65 },
{ emoji: '👥', text: 'Загрузка пользователей...', weight: 70 },
{ emoji: '📅', text: 'Обновление календаря...', weight: 75 },
{ emoji: '🔔', text: 'Настройка уведомлений...', weight: 80 },
{ emoji: '💾', text: 'Сохранение кэша...', weight: 85 },
{ emoji: '✨', text: 'Финальная обработка...', weight: 90 },
{ emoji: '🚀', text: 'Запуск приложения...', weight: 95 },
{ emoji: '✅', text: 'Почти готово...', weight: 98 }
];
// Дополнительные случайные статусы
const RANDOM_STATUSES = [
{ emoji: '☕', text: 'Наливаем кофе...' },
{ emoji: '🧹', text: 'Подметаем баги...' },
{ emoji: '🎮', text: 'Играем в косынку...' },
{ emoji: '📚', text: 'Читаем документацию...' },
{ emoji: '🎵', text: 'Включаем музыку...' },
{ emoji: '🧘', text: 'Медитируем...' },
{ emoji: '🏋️', text: 'Качаем бицепс...' },
{ emoji: '🍕', text: 'Заказываем пиццу...' },
{ emoji: '🌴', text: 'Улетаем в отпуск...' },
{ emoji: '🎪', text: 'Устраиваем цирк...' },
{ emoji: '🎭', text: 'Играем спектакль...' },
{ emoji: '🎯', text: 'Целимся в баги...' },
{ emoji: '🎲', text: 'Бросаем кубик...' },
{ emoji: '🎰', text: 'Крутим барабан...' },
{ emoji: '🦄', text: 'Ловим единорога...' },
{ emoji: '🌈', text: 'Красим радугу...' },
{ emoji: '🪄', text: 'Колдуем...' },
{ emoji: '🎩', text: 'Достаем кролика...' },
{ emoji: '🕯️', text: 'Зажигаем свечи...' },
{ emoji: '🔮', text: 'Гадаем на баги...' }
];
// Функция для инициализации после загрузки DOM
function initializeLoadingOverlay() {
// Проверяем, существует ли уже оверлей
if (document.getElementById('loading-overlay')) {
return;
}
// Создаем контейнер для анимации загрузки
const loadingOverlay = document.createElement('div');
loadingOverlay.id = 'loading-overlay';
loadingOverlay.innerHTML = `
<div class="loading-container">
<div class="loading-spinner">
<div class="spinner" id="loading-spinner"></div>
</div>
<div class="loading-text">
<h2 id="loading-title">School CRM</h2>
<p>Загрузка сервиса управления задачами...</p>
<div class="loading-progress">
<div class="progress-bar" id="loading-progress-bar"></div>
</div>
<div class="loading-status" id="loading-status">
<span class="status-icon">📦</span> Инициализация...
</div>
<div class="loading-percent" id="loading-percent">0%</div>
<div class="loading-details" id="loading-details"></div>
</div>
<div class="loading-footer">
<p>МАОУ - СОШ № 25 | Версия 0.9</p>
<p class="loading-tip" id="loading-tip"></p>
</div>
</div>
`;
// Добавляем стили для анимации со случайными цветами
const styles = document.createElement('style');
styles.textContent = `
#loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: ${gradientColors.background};
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
transition: opacity 0.5s ease-out;
}
#loading-overlay.fade-out {
opacity: 0;
}
.loading-container {
text-align: center;
color: ${textColor};
max-width: 500px;
width: 90%;
padding: 40px;
background: rgba(255, 255, 255, 0.15);
border-radius: 20px;
backdrop-filter: blur(10px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
animation: slideUp 0.8s ease-out;
border: 1px solid rgba(255, 255, 255, 0.2);
}
@keyframes slideUp {
from {
transform: translateY(50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.loading-spinner {
margin-bottom: 30px;
}
.spinner {
width: 80px;
height: 80px;
margin: 0 auto;
border: 5px solid rgba(255, 255, 255, 0.2);
border-radius: 50%;
border-top-color: ${progressColor};
border-left-color: ${accentColor};
border-right-color: ${accentColor};
animation: spin 1s ease-in-out infinite;
box-shadow: 0 0 20px ${progressColor}40;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text h2 {
font-size: 32px;
margin: 0 0 15px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
letter-spacing: 2px;
background: linear-gradient(135deg, ${textColor}, ${progressColor});
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.loading-text p {
font-size: 18px;
margin: 0 0 30px;
opacity: 0.9;
color: ${textColor};
}
.loading-progress {
width: 100%;
height: 12px;
background: rgba(255, 255, 255, 0.15);
border-radius: 6px;
margin: 20px 0;
overflow: hidden;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
}
.progress-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, ${progressColor}, ${accentColor}, ${progressColor});
border-radius: 6px;
transition: width 0.3s ease;
animation: progressPulse 2s ease-in-out infinite;
background-size: 200% 100%;
}
@keyframes progressPulse {
0%, 100% { opacity: 1; background-position: 0% 50%; }
50% { opacity: 0.8; background-position: 100% 50%; }
}
.loading-status {
font-size: 18px;
margin: 15px 0;
min-height: 28px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
color: ${textColor};
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.loading-percent {
font-size: 42px;
font-weight: bold;
margin: 10px 0;
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
background: linear-gradient(135deg, ${textColor}, ${progressColor});
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: percentPulse 1.5s ease-in-out infinite;
}
@keyframes percentPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.loading-details {
font-size: 13px;
margin-top: 15px;
opacity: 0.8;
min-height: 20px;
color: ${textColor};
background: rgba(255, 255, 255, 0.1);
padding: 8px 15px;
border-radius: 20px;
display: inline-block;
}
.loading-footer {
margin-top: 30px;
font-size: 14px;
opacity: 0.8;
color: ${textColor};
}
.loading-footer p {
margin: 5px 0;
color: ${textColor};
}
.loading-tip {
font-size: 13px;
font-style: italic;
color: ${textColor};
animation: fadeInOut 3s ease-in-out infinite;
background: rgba(255, 255, 255, 0.1);
padding: 5px 15px;
border-radius: 20px;
margin-top: 10px;
}
@keyframes fadeInOut {
0%, 100% { opacity: 0.7; transform: translateY(0); }
50% { opacity: 1; transform: translateY(-2px); }
}
.status-icon {
display: inline-block;
animation: bounce 1s ease infinite;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.retry-button {
background: rgba(255, 255, 255, 0.2);
border: 2px solid ${textColor};
color: ${textColor};
padding: 12px 30px;
border-radius: 30px;
cursor: pointer;
font-size: 16px;
margin-top: 20px;
transition: all 0.3s ease;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
.retry-button:hover {
background: ${progressColor};
color: ${gradientColors.start};
transform: scale(1.05);
border-color: ${progressColor};
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.warning {
color: #ffd700;
text-shadow: 0 0 10px #ffd700;
}
.error {
color: #ff6b6b;
text-shadow: 0 0 10px #ff6b6b;
}
.progress-dots {
display: inline-block;
min-width: 30px;
}
.progress-dots:after {
content: '...';
animation: dots 1.5s steps(4, end) infinite;
font-weight: bold;
letter-spacing: 2px;
}
@keyframes dots {
0%, 20% { content: ''; }
40% { content: '.'; }
60% { content: '..'; }
80%, 100% { content: '...'; }
}
/* Добавляем случайные частицы для эффекта */
.loading-container::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, ${progressColor}20 1px, transparent 1px);
background-size: 50px 50px;
animation: particles 20s linear infinite;
opacity: 0.1;
pointer-events: none;
}
@keyframes particles {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
`;
// Добавляем элементы в DOM с проверкой
if (document.head) {
document.head.appendChild(styles);
} else {
document.addEventListener('DOMContentLoaded', () => {
document.head.appendChild(styles);
});
}
if (document.body) {
document.body.appendChild(loadingOverlay);
} else {
document.addEventListener('DOMContentLoaded', () => {
document.body.appendChild(loadingOverlay);
});
}
// Логируем использованные цвета
console.log('🎨 Случайные цвета загрузки:', {
gradient: gradientColors,
progress: progressColor,
accent: accentColor,
text: textColor
});
}
// Инициализируем оверлей после загрузки DOM
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeLoadingOverlay);
} else {
initializeLoadingOverlay();
}
// Массив полезных советов
const TIPS = [
'💡 Совет: Используйте фильтры для быстрого поиска задач',
'💡 Совет: Можно прикреплять до 15 файлов к задаче',
'💡 Совет: Настраивайте уведомления в личном кабинете',
'💡 Совет: Используйте Канбан-доску для визуализации задач',
'💡 Совет: Отмечайте важные задачи цветными метками',
'💡 Совет: Следите за просроченными задачами в отдельной вкладке',
'💡 Совет: Создавайте шаблоны для частых типов задач',
'💡 Совет: Используйте поиск для быстрого доступа к задачам',
'💡 Совет: Комментируйте задачи для уточнения деталей',
'💡 Совет: Прикрепляйте скриншоты к задачам в ИТ-отдел',
'💡 Совет: Проверяйте уведомления на email ежедневно',
'💡 Совет: Архивируйте выполненные задачи для чистоты списка',
'🌟 Факт: Сегодняшний градиент - один из 30 возможных',
'🎨 Факт: Цвета загрузки уникальны для каждого обновления',
'🌈 Факт: У вас сегодня особенная цветовая схема'
];
// Счетчик загруженных скриптов
window.loadingScripts = {
total: 0,
loaded: 0,
errors: [],
retryCounts: {},
startTime: Date.now(),
scripts: [],
animationInterval: null,
tipInterval: null,
loadingComplete: false,
minTimeElapsed: false,
updateProgress: function() {
const percent = Math.min(Math.round((this.loaded / this.total) * 100), 100);
const progressBar = document.getElementById('loading-progress-bar');
const percentDisplay = document.getElementById('loading-percent');
const statusDisplay = document.getElementById('loading-status');
const detailsDisplay = document.getElementById('loading-details');
if (progressBar) progressBar.style.width = percent + '%';
if (percentDisplay) percentDisplay.textContent = percent + '%';
// Обновляем статус в зависимости от процента загрузки
if (statusDisplay) {
if (this.errors.length > 0) {
statusDisplay.innerHTML = '<span class="status-icon warning">⚠️</span> Проблемы с загрузкой. Пытаемся восстановить...';
} else {
// Находим подходящий статус по проценту загрузки
const currentStatus = LOADING_STATUSES.find(s => s.weight >= percent) || LOADING_STATUSES[LOADING_STATUSES.length - 1];
statusDisplay.innerHTML = `<span class="status-icon">${currentStatus.emoji}</span> ${currentStatus.text} <span class="progress-dots"></span>`;
}
}
// Обновляем детальную информацию
if (detailsDisplay) {
const loadedScripts = this.loaded;
const totalScripts = this.total;
const errors = this.errors.length;
const timeElapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
let detailsHtml = `📊 ${loadedScripts}/${totalScripts} модулей • ⏱️ ${timeElapsed}с`;
if (errors > 0) {
detailsHtml += `<br><span class="warning">⚠️ Ошибок: ${errors}</span>`;
}
detailsDisplay.innerHTML = detailsHtml;
}
// Проверяем, все ли скрипты загружены
if (this.loaded >= this.total && this.total > 0 && !this.loadingComplete) {
this.loadingComplete = true;
this.checkAndFinishLoading();
}
},
checkAndFinishLoading: function() {
const elapsed = Date.now() - this.startTime;
// Если прошло минимум 5 секунд, завершаем загрузку
if (elapsed >= CONFIG.minLoadingTime) {
this.finishLoading();
} else {
// Иначе ждем оставшееся время
const remainingTime = CONFIG.minLoadingTime - elapsed;
// Показываем сообщение о финальной подготовке
const statusDisplay = document.getElementById('loading-status');
if (statusDisplay) {
statusDisplay.innerHTML = '<span class="status-icon">✨</span> Финальная подготовка интерфейса... <span class="progress-dots"></span>';
}
setTimeout(() => {
this.finishLoading();
}, remainingTime);
}
},
startDynamicLoading: function() {
// Запускаем анимацию прогресса
let fakePercent = 0;
const startTime = Date.now();
this.animationInterval = setInterval(() => {
const elapsed = Date.now() - startTime;
const realPercent = Math.min(Math.round((this.loaded / this.total) * 100), 100);
// Рассчитываем фейковый процент на основе реального и времени
if (elapsed < CONFIG.maxLoadingTime && !this.loadingComplete) {
// Фейковый процент растет медленнее к концу
const timePercent = (elapsed / CONFIG.maxLoadingTime) * 100;
fakePercent = Math.min(
Math.max(realPercent, Math.floor(timePercent * 0.7 + Math.random() * 10)),
99 // Никогда не достигаем 100% фейком
);
} else {
// Если загрузка завершена или прошло 15 секунд, показываем реальный процент
fakePercent = realPercent;
}
// Обновляем прогресс бар с фейковым процентом
const progressBar = document.getElementById('loading-progress-bar');
const percentDisplay = document.getElementById('loading-percent');
if (progressBar) progressBar.style.width = fakePercent + '%';
if (percentDisplay) percentDisplay.textContent = fakePercent + '%';
// Случайно меняем статус для динамики
if (Math.random() > 0.7 && !this.loadingComplete) {
const statusDisplay = document.getElementById('loading-status');
if (statusDisplay && this.errors.length === 0) {
const randomStatus = RANDOM_STATUSES[Math.floor(Math.random() * RANDOM_STATUSES.length)];
statusDisplay.innerHTML = `<span class="status-icon">${randomStatus.emoji}</span> ${randomStatus.text} <span class="progress-dots"></span>`;
// Возвращаем нормальный статус через 2 секунды
setTimeout(() => {
const currentStatus = LOADING_STATUSES.find(s => s.weight >= fakePercent) || LOADING_STATUSES[LOADING_STATUSES.length - 1];
if (statusDisplay && !this.loadingComplete) {
statusDisplay.innerHTML = `<span class="status-icon">${currentStatus.emoji}</span> ${currentStatus.text} <span class="progress-dots"></span>`;
}
}, 2000);
}
}
// Проверяем, не пора ли закончить по времени
if (elapsed >= CONFIG.maxLoadingTime && !this.loadingComplete) {
this.loadingComplete = true;
this.finishDynamicLoading();
}
}, 200);
// Запускаем смену советов
this.tipInterval = setInterval(() => {
const tipElement = document.getElementById('loading-tip');
if (tipElement) {
const randomTip = TIPS[Math.floor(Math.random() * TIPS.length)];
tipElement.textContent = randomTip;
}
}, 3000);
},
finishDynamicLoading: function() {
if (this.animationInterval) {
clearInterval(this.animationInterval);
this.animationInterval = null;
}
// Показываем реальный прогресс
const realPercent = Math.min(Math.round((this.loaded / this.total) * 100), 100);
const progressBar = document.getElementById('loading-progress-bar');
const percentDisplay = document.getElementById('loading-percent');
if (progressBar) progressBar.style.width = realPercent + '%';
if (percentDisplay) percentDisplay.textContent = realPercent + '%';
// Завершаем загрузку
this.finishLoading();
},
finishLoading: function() {
// Останавливаем интервалы
if (this.animationInterval) {
clearInterval(this.animationInterval);
this.animationInterval = null;
}
if (this.tipInterval) {
clearInterval(this.tipInterval);
this.tipInterval = null;
}
const loadingTime = ((Date.now() - this.startTime) / 1000).toFixed(1);
const statusDisplay = document.getElementById('loading-status');
const percentDisplay = document.getElementById('loading-percent');
if (this.errors.length > 0) {
if (statusDisplay) {
statusDisplay.innerHTML = `<span class="status-icon warning">⚠️</span> Загружено с ошибками (${this.errors.length}) за ${loadingTime}с`;
}
if (percentDisplay) {
percentDisplay.innerHTML = '⚠️';
}
// Показываем кнопку перезагрузки
const detailsDisplay = document.getElementById('loading-details');
if (detailsDisplay) {
const retryButton = document.createElement('button');
retryButton.className = 'retry-button';
retryButton.innerHTML = '🔄 Перезагрузить страницу';
retryButton.onclick = () => window.location.reload();
detailsDisplay.appendChild(retryButton);
}
// Автоматическая перезагрузка через 5 секунд
setTimeout(() => {
if (confirm('Некоторые компоненты не загрузились. Перезагрузить страницу?')) {
window.location.reload();
}
}, 5000);
} else {
if (statusDisplay) {
statusDisplay.innerHTML = `<span class="status-icon">✅</span> Загрузка завершена за ${loadingTime}с`;
}
if (percentDisplay) {
percentDisplay.innerHTML = '100%';
}
// Плавно скрываем оверлей
setTimeout(() => {
const overlay = document.getElementById('loading-overlay');
if (overlay) {
overlay.classList.add('fade-out');
setTimeout(() => {
if (overlay && overlay.parentNode) {
overlay.parentNode.removeChild(overlay);
}
}, 500);
}
}, 800);
}
},
retryScript: function(scriptSrc) {
if (!this.retryCounts[scriptSrc]) {
this.retryCounts[scriptSrc] = 0;
}
if (this.retryCounts[scriptSrc] >= CONFIG.maxRetries) {
console.error(`Не удалось загрузить скрипт после ${CONFIG.maxRetries} попыток: ${scriptSrc}`);
return false;
}
this.retryCounts[scriptSrc]++;
this.loaded--; // Уменьшаем счетчик загруженных
const statusDisplay = document.getElementById('loading-status');
if (statusDisplay) {
statusDisplay.innerHTML = `<span class="status-icon">🔄</span> Перезагрузка: ${scriptSrc.split('/').pop()} (попытка ${this.retryCounts[scriptSrc]})`;
}
// Создаем новый script элемент
const script = document.createElement('script');
script.src = scriptSrc;
script.async = false;
script.onload = () => {
this.loaded++;
this.errors = this.errors.filter(e => e !== scriptSrc);
this.updateProgress();
};
script.onerror = () => {
setTimeout(() => this.retryScript(scriptSrc), CONFIG.retryDelay);
};
document.head.appendChild(script);
return true;
}
};
// Перехватываем загрузку скриптов
const originalAppendChild = document.head ? document.head.appendChild : null;
if (originalAppendChild) {
document.head.appendChild = function(node) {
if (node.tagName === 'SCRIPT' && node.src) {
window.loadingScripts.total++;
window.loadingScripts.scripts.push(node.src);
// Устанавливаем таймаут для скрипта
const timeoutId = setTimeout(() => {
console.warn(`Таймаут загрузки скрипта: ${node.src}`);
if (window.loadingScripts.retryScript(node.src)) {
// Удаляем старый скрипт
if (node.parentNode) {
node.parentNode.removeChild(node);
}
}
}, CONFIG.timeout);
// Обновляем обработчики событий
const originalOnLoad = node.onload;
node.onload = function() {
clearTimeout(timeoutId);
window.loadingScripts.loaded++;
window.loadingScripts.updateProgress();
if (originalOnLoad) originalOnLoad.apply(this, arguments);
};
const originalOnError = node.onerror;
node.onerror = function() {
clearTimeout(timeoutId);
window.loadingScripts.errors.push(node.src);
window.loadingScripts.updateProgress();
// Пытаемся перезагрузить скрипт
if (window.loadingScripts.retryScript(node.src)) {
// Удаляем старый скрипт
if (node.parentNode) {
node.parentNode.removeChild(node);
}
}
if (originalOnError) originalOnError.apply(this, arguments);
};
}
return originalAppendChild.call(this, node);
};
}
// Добавляем обработчик для DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
// Запускаем динамическую загрузку
window.loadingScripts.startDynamicLoading();
// Устанавливаем максимальное время загрузки 15 секунд
setTimeout(() => {
if (!window.loadingScripts.loadingComplete && window.loadingScripts.loaded < window.loadingScripts.total) {
window.loadingScripts.loadingComplete = true;
window.loadingScripts.finishDynamicLoading();
}
}, CONFIG.maxLoadingTime);
// Проверяем, не зависла ли загрузка
setTimeout(() => {
if (window.loadingScripts.loaded < window.loadingScripts.total) {
const unloadedScripts = window.loadingScripts.scripts.filter(src =>
!window.loadingScripts.retryCounts[src] ||
window.loadingScripts.retryCounts[src] < CONFIG.maxRetries
);
unloadedScripts.forEach(src => {
window.loadingScripts.retryScript(src);
});
}
}, 5000);
});
})();