// Скрипт тоггла сайдбара с улучшенной обработкой document.addEventListener("DOMContentLoaded", function () { const sidebarToggle = document.getElementById('sidebarToggle'); const sidebarWrapper = document.getElementById('sidebar-wrapper'); const body = document.body; // Базовая функция для получения base URL (объявляем ДО использования) const baseUrl = '/'.replace(/\/+$/, ''); if (sidebarToggle) { // Обработчик клика sidebarToggle.addEventListener('click', function (event) { event.preventDefault(); event.stopPropagation(); // Переключаем класс body.classList.toggle('sb-sidenav-toggled'); // Сохраняем состояние в localStorage const isToggled = body.classList.contains('sb-sidenav-toggled'); localStorage.setItem('sidebar-toggled', isToggled); }); // Закрытие сайдбара при клике вне его (для мобильных) document.addEventListener('click', function (event) { if (window.innerWidth <= 768 && body.classList.contains('sb-sidenav-toggled') && !sidebarWrapper.contains(event.target) && event.target !== sidebarToggle && !sidebarToggle.contains(event.target)) { body.classList.remove('sb-sidenav-toggled'); localStorage.setItem('sidebar-toggled', false); } }); // Восстановление состояния из localStorage const sidebarToggled = localStorage.getItem('sidebar-toggled'); if (sidebarToggled === 'true' && window.innerWidth > 768) { body.classList.add('sb-sidenav-toggled'); } } // Адаптивное поведение при изменении размера окна window.addEventListener('resize', function () { if (window.innerWidth > 768) { // На десктопе всегда показываем сайдбар body.classList.remove('sb-sidenav-toggled'); } }); // Подсветка активного пункта меню при загрузке highlightActiveMenuItem(); function highlightActiveMenuItem() { const currentPath = window.location.pathname; const menuItems = document.querySelectorAll('.sidebar-link'); menuItems.forEach(item => { const href = item.getAttribute('href'); if (href && href !== '#' && currentPath.includes(href.replace(baseUrl, ''))) { item.classList.add('active'); } }); } }); (function () { const cookieName = 'rl_token'; // Проверяем, есть ли кука const hasCookie = document.cookie.split('; ').find(function (row) { return row.indexOf(cookieName + '=') === 0; }); if (!hasCookie) { // Генерируем UUID v4 на клиенте function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } const token = generateUUID(); const date = new Date(); date.setTime(date.getTime() + (365 * 24 * 60 * 60 * 1000)); const cookieString = cookieName + '=' + token + '; expires=' + date.toUTCString() + '; path=/' + '; SameSite=Lax' + (location.protocol === 'https:' ? '; Secure' : ''); document.cookie = cookieString; } })(); (function() { 'use strict'; /** * Получение CSRF токена из нескольких источников (с fallback) */ function getCsrfToken() { // 1. Пробуем из мета-тега const meta = document.querySelector('meta[name="csrf-token"]'); if (meta && meta.getAttribute('content')) { return meta.getAttribute('content'); } // 2. Пробуем из data-атрибута body if (document.body && document.body.dataset.csrfToken) { return document.body.dataset.csrfToken; } // 3. Пробуем из скрытого input на странице const csrfInput = document.querySelector('input[name*="csrf"]'); if (csrfInput && csrfInput.value) { return csrfInput.value; } // 4. Пробуем из cookie - CodeIgniter 4 использует 'csrf_cookie_name' const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'csrf_cookie_name' && value) { return decodeURIComponent(value); } } console.warn('CSRF token not found anywhere'); return ''; } /** * Обновление CSRF токена во всех источниках */ function updateCsrfToken(token, hash) { // Обновляем мета-теги const tokenMeta = document.querySelector('meta[name="csrf-token"]'); const hashMeta = document.querySelector('meta[name="csrf-hash"]'); if (tokenMeta) tokenMeta.setAttribute('content', token); if (hashMeta) hashMeta.setAttribute('content', hash); // Обновляем data-атрибут body if (document.body) { document.body.dataset.csrfToken = token; } // Обновляем все input поля с CSRF document.querySelectorAll('input[name*="csrf"]').forEach(input => { input.value = token; }); // Обновляем cookie document.cookie = 'csrf_cookie_name=' + encodeURIComponent(token) + '; path=/; SameSite=Lax'; } // Перехват fetch() const originalFetch = window.fetch; window.fetch = function(url, options = {}) { // Добавляем CSRF токен в заголовки if (!options.headers) { options.headers = {}; } // Если headers это объект - добавляем токен if (options.headers instanceof Headers) { options.headers.set('X-CSRF-TOKEN', getCsrfToken()); } else if (typeof options.headers === 'object') { options.headers['X-CSRF-TOKEN'] = getCsrfToken(); } // Выполняем запрос return originalFetch(url, options).then(response => { // Проверяем, пришёл ли новый токен в ответе const newToken = response.headers.get('X-CSRF-TOKEN'); const newHash = response.headers.get('X-CSRF-HASH'); if (newToken && newHash) { updateCsrfToken(newToken, newHash); } return response; }); }; // Перехват XMLHttpRequest (для jQuery и других библиотек) const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { this._csrfToken = getCsrfToken(); return originalOpen.apply(this, arguments); }; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(body) { // Добавляем CSRF токен в заголовки this.setRequestHeader('X-CSRF-TOKEN', this._csrfToken || getCsrfToken()); // Слушаем ответ для обновления токена this.addEventListener('load', function() { const newToken = this.getResponseHeader('X-CSRF-TOKEN'); const newHash = this.getResponseHeader('X-CSRF-HASH'); if (newToken && newHash) { updateCsrfToken(newToken, newHash); } }); return originalSend.apply(this, arguments); }; // Автоматическое обновление CSRF токена в AJAX формах перед отправкой document.addEventListener('submit', function(e) { const form = e.target; if (form.dataset.ajax === 'true') { const tokenInput = form.querySelector('input[name="csrf_token"]'); if (tokenInput) { tokenInput.value = getCsrfToken(); } } }); // Обновление CSRF токена в скрытых полях при загрузке страницы (только для AJAX форм) document.addEventListener('DOMContentLoaded', function() { const forms = document.querySelectorAll('form[data-ajax="true"]'); forms.forEach(function(form) { const tokenInput = form.querySelector('input[name="csrf_token"]'); if (tokenInput) { tokenInput.value = getCsrfToken(); } }); }); })();