190 lines
7.4 KiB
JavaScript
190 lines
7.4 KiB
JavaScript
// Скрипт тоггла сайдбара с улучшенной обработкой
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const sidebarToggle = document.getElementById('sidebarToggle');
|
||
const sidebarWrapper = document.getElementById('sidebar-wrapper');
|
||
const body = document.body;
|
||
|
||
// Базовая функция для получения base URL (объявляем ДО использования)
|
||
const baseUrl = '{{ base_url("/") }}'.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 токена из мета-тега
|
||
*/
|
||
function getCsrfToken() {
|
||
const meta = document.querySelector('meta[name="csrf-token"]');
|
||
return meta ? meta.getAttribute('content') : '';
|
||
}
|
||
|
||
/**
|
||
* Обновление 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);
|
||
}
|
||
|
||
// Перехват 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();
|
||
}
|
||
});
|
||
});
|
||
})(); |