bp/public/assets/js/base.js

230 lines
8.9 KiB
JavaScript
Raw Permalink 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.

// Скрипт тоггла сайдбара с улучшенной обработкой
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();
}
});
});
})();