Files
minicrm/public/loading-start.js

848 lines
38 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// loading-start.js - Загружается первым в <head>
(function() {
// Конфигурация
const CONFIG = {
maxRetries: 3,
retryDelay: 5000,
timeout: 10000,
showWarnings: true,
maxLoadingTime: 15000, // Максимум 15 секунд
minLoadingTime: 1000 // Минимум 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: 98 },
{ emoji: '✅', text: 'Завершено!', weight: 100 }
];
// Дополнительные случайные статусы
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) / 3000).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);
});
})();