Release 1.5.13
This commit is contained in:
@@ -164,9 +164,6 @@
|
||||
if (typeof window.initTableSearches === 'function') {
|
||||
window.initTableSearches(targetElement);
|
||||
}
|
||||
if (typeof window.initTablePaginations === 'function') {
|
||||
window.initTablePaginations(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceRefreshTargetFromPage(refreshTargetSelector, sequenceId) {
|
||||
@@ -201,7 +198,7 @@
|
||||
|
||||
currentTarget.outerHTML = nextTarget.outerHTML;
|
||||
rebindRefreshTarget(document.querySelector(refreshTargetSelector));
|
||||
}).catch(function (_error) {
|
||||
}).catch(function () {
|
||||
// Ignore refresh replacement failures and leave the existing content in place.
|
||||
});
|
||||
}
|
||||
@@ -515,303 +512,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initTablePaginations(root) {
|
||||
var scope = root && root.querySelectorAll ? root : document;
|
||||
|
||||
Array.prototype.forEach.call(scope.querySelectorAll('[data-table-pagination="auto"]'), function (container) {
|
||||
if (container.getAttribute('data-table-pagination-bound') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
var table = container.querySelector('[data-table-searchable]');
|
||||
var cardHeader = container.querySelector('.card-header');
|
||||
var cardBody = container.querySelector('.card-body.table-responsive') || container.querySelector('.card-body');
|
||||
var footer = container.querySelector('[data-table-pagination-controls]');
|
||||
var footerSummary = null;
|
||||
var footerList = null;
|
||||
var syncFrame = 0;
|
||||
var state = {
|
||||
currentPage: 1,
|
||||
pageSize: 1,
|
||||
totalPages: 1
|
||||
};
|
||||
|
||||
if (!table || !cardBody || !table.tBodies.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.setAttribute('data-table-pagination-bound', 'true');
|
||||
|
||||
function getRows() {
|
||||
return Array.prototype.slice.call(table.querySelectorAll('tbody tr[data-table-search-row]'));
|
||||
}
|
||||
|
||||
function isSearchMatched(row) {
|
||||
return String(row.getAttribute('data-table-search-match') || 'true') !== 'false';
|
||||
}
|
||||
|
||||
function ensureFooter() {
|
||||
if (footer) {
|
||||
footerSummary = footer.querySelector('[data-table-pagination-summary]');
|
||||
footerList = footer.querySelector('[data-table-pagination-list]');
|
||||
return footer;
|
||||
}
|
||||
|
||||
footer = document.createElement('div');
|
||||
footer.className = 'card-footer d-none';
|
||||
footer.setAttribute('data-table-pagination-controls', 'true');
|
||||
footer.innerHTML = '' +
|
||||
'<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">' +
|
||||
'<div class="text-muted small" data-table-pagination-summary></div>' +
|
||||
'<nav aria-label="Table pages">' +
|
||||
'<ul class="pagination pagination-sm mb-0" data-table-pagination-list></ul>' +
|
||||
'</nav>' +
|
||||
'</div>';
|
||||
cardBody.parentNode.insertBefore(footer, cardBody.nextSibling);
|
||||
footerSummary = footer.querySelector('[data-table-pagination-summary]');
|
||||
footerList = footer.querySelector('[data-table-pagination-list]');
|
||||
return footer;
|
||||
}
|
||||
|
||||
function setRowVisible(row, visible) {
|
||||
row.hidden = !visible;
|
||||
row.classList.toggle('d-none', !visible);
|
||||
}
|
||||
|
||||
function getAvailableHeight() {
|
||||
var containerRect = container.getBoundingClientRect();
|
||||
var headerHeight = cardHeader ? cardHeader.getBoundingClientRect().height : 0;
|
||||
var footerEstimate = footer && !footer.classList.contains('d-none') ? footer.getBoundingClientRect().height : 56;
|
||||
return Math.max(0, window.innerHeight - Math.max(0, containerRect.top) - headerHeight - footerEstimate - 24);
|
||||
}
|
||||
|
||||
function renderFooter(totalItems) {
|
||||
var pageButtons = [];
|
||||
|
||||
ensureFooter();
|
||||
|
||||
if (state.totalPages <= 1 || totalItems <= 0) {
|
||||
footer.classList.add('d-none');
|
||||
footerSummary.textContent = '';
|
||||
footerList.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
footer.classList.remove('d-none');
|
||||
footerSummary.textContent = 'Showing ' + (((state.currentPage - 1) * state.pageSize) + 1) + '-' + Math.min(totalItems, state.currentPage * state.pageSize) + ' of ' + totalItems;
|
||||
|
||||
pageButtons.push('<li class="page-item' + (state.currentPage <= 1 ? ' disabled' : '') + '"><a class="page-link" href="#" data-table-pagination-page="' + (state.currentPage - 1) + '" aria-label="Previous page">Previous</a></li>');
|
||||
for (var pageNumber = 1; pageNumber <= state.totalPages; pageNumber += 1) {
|
||||
pageButtons.push('<li class="page-item' + (pageNumber === state.currentPage ? ' active' : '') + '"><a class="page-link" href="#" data-table-pagination-page="' + pageNumber + '">' + pageNumber + '</a></li>');
|
||||
}
|
||||
pageButtons.push('<li class="page-item' + (state.currentPage >= state.totalPages ? ' disabled' : '') + '"><a class="page-link" href="#" data-table-pagination-page="' + (state.currentPage + 1) + '" aria-label="Next page">Next</a></li>');
|
||||
footerList.innerHTML = pageButtons.join('');
|
||||
}
|
||||
|
||||
function applyPagination(resetPage) {
|
||||
var rows = getRows();
|
||||
var matchedRows = rows.filter(isSearchMatched);
|
||||
var totalItems = matchedRows.length;
|
||||
var rowHeight = 48;
|
||||
var pageSize;
|
||||
var startIndex;
|
||||
var endIndex;
|
||||
|
||||
if (resetPage) {
|
||||
state.currentPage = 1;
|
||||
}
|
||||
|
||||
rows.forEach(function (row) {
|
||||
if (!isSearchMatched(row)) {
|
||||
setRowVisible(row, false);
|
||||
}
|
||||
});
|
||||
|
||||
if (!totalItems) {
|
||||
state.pageSize = 1;
|
||||
state.totalPages = 1;
|
||||
state.currentPage = 1;
|
||||
renderFooter(0);
|
||||
return;
|
||||
}
|
||||
|
||||
matchedRows.forEach(function (row) {
|
||||
setRowVisible(row, true);
|
||||
});
|
||||
|
||||
if (matchedRows[0]) {
|
||||
rowHeight = Math.max(24, matchedRows[0].getBoundingClientRect().height || matchedRows[0].offsetHeight || 48);
|
||||
}
|
||||
|
||||
pageSize = Math.max(1, Math.floor(getAvailableHeight() / rowHeight));
|
||||
|
||||
if (pageSize >= totalItems) {
|
||||
state.pageSize = totalItems;
|
||||
state.totalPages = 1;
|
||||
state.currentPage = 1;
|
||||
matchedRows.forEach(function (row) {
|
||||
setRowVisible(row, true);
|
||||
});
|
||||
renderFooter(totalItems);
|
||||
return;
|
||||
}
|
||||
|
||||
state.pageSize = pageSize;
|
||||
state.totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
||||
state.currentPage = Math.min(Math.max(1, state.currentPage), state.totalPages);
|
||||
startIndex = (state.currentPage - 1) * pageSize;
|
||||
endIndex = startIndex + pageSize;
|
||||
|
||||
matchedRows.forEach(function (row, index) {
|
||||
setRowVisible(row, index >= startIndex && index < endIndex);
|
||||
});
|
||||
|
||||
renderFooter(totalItems);
|
||||
}
|
||||
|
||||
function scheduleSync(options) {
|
||||
if (syncFrame) {
|
||||
window.cancelAnimationFrame(syncFrame);
|
||||
}
|
||||
|
||||
syncFrame = window.requestAnimationFrame(function () {
|
||||
syncFrame = 0;
|
||||
applyPagination(Boolean(options && options.resetPage));
|
||||
});
|
||||
}
|
||||
|
||||
ensureFooter();
|
||||
|
||||
if (footer) {
|
||||
footer.addEventListener('click', function (event) {
|
||||
var target = event.target && event.target.closest ? event.target.closest('[data-table-pagination-page]') : null;
|
||||
var pageValue;
|
||||
|
||||
if (!target || target.closest('.disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
pageValue = Math.max(1, Math.floor(Number(target.getAttribute('data-table-pagination-page')) || 1));
|
||||
state.currentPage = pageValue;
|
||||
scheduleSync();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
scheduleSync();
|
||||
});
|
||||
|
||||
if (typeof ResizeObserver === 'function') {
|
||||
try {
|
||||
new ResizeObserver(function () {
|
||||
scheduleSync();
|
||||
}).observe(container);
|
||||
} catch (_error) {
|
||||
// Ignore observer setup failures and rely on window resize.
|
||||
}
|
||||
}
|
||||
|
||||
container.syncTablePagination = scheduleSync;
|
||||
scheduleSync({ resetPage: true });
|
||||
});
|
||||
}
|
||||
|
||||
function initTableSearches(root) {
|
||||
var scope = root && root.querySelectorAll ? root : document;
|
||||
|
||||
Array.prototype.forEach.call(scope.querySelectorAll('[data-table-search]'), function (input) {
|
||||
if (input.getAttribute('data-table-search-bound') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
var container = input.closest('[data-table-search-container]') || input.closest('.card') || document;
|
||||
var table = container.querySelector('[data-table-searchable]');
|
||||
var emptyRow = null;
|
||||
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
input.setAttribute('data-table-search-bound', 'true');
|
||||
|
||||
function getSearchableRows() {
|
||||
return Array.prototype.slice.call(table.querySelectorAll('tbody tr[data-table-search-row]'));
|
||||
}
|
||||
|
||||
function getDefaultEmptyRow() {
|
||||
return table.querySelector('tbody tr[data-table-search-empty-default]');
|
||||
}
|
||||
|
||||
function removeGeneratedEmptyRow() {
|
||||
if (emptyRow && emptyRow.parentNode) {
|
||||
emptyRow.parentNode.removeChild(emptyRow);
|
||||
}
|
||||
emptyRow = null;
|
||||
}
|
||||
|
||||
function ensureGeneratedEmptyRow(message) {
|
||||
var tbody = table.tBodies[0] || table.querySelector('tbody');
|
||||
var columnCount = 1;
|
||||
|
||||
if (!tbody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!emptyRow) {
|
||||
emptyRow = document.createElement('tr');
|
||||
emptyRow.setAttribute('data-table-search-empty-row', 'true');
|
||||
var cell = document.createElement('td');
|
||||
cell.className = 'empty';
|
||||
cell.setAttribute('data-table-search-empty-cell', 'true');
|
||||
emptyRow.appendChild(cell);
|
||||
}
|
||||
|
||||
columnCount = table.tHead && table.tHead.rows && table.tHead.rows[0] ? table.tHead.rows[0].cells.length : (getSearchableRows()[0] ? getSearchableRows()[0].cells.length : 1);
|
||||
emptyRow.firstElementChild.colSpan = Math.max(1, columnCount);
|
||||
emptyRow.firstElementChild.textContent = message;
|
||||
|
||||
if (!emptyRow.parentNode) {
|
||||
tbody.appendChild(emptyRow);
|
||||
}
|
||||
}
|
||||
|
||||
function syncSearch() {
|
||||
var query = String(input.value || '').trim().toLowerCase();
|
||||
var rows = getSearchableRows();
|
||||
var visibleCount = 0;
|
||||
var defaultEmptyRow = getDefaultEmptyRow();
|
||||
|
||||
removeGeneratedEmptyRow();
|
||||
|
||||
rows.forEach(function (row) {
|
||||
var haystack = String(row.getAttribute('data-search-text') || row.textContent || '').toLowerCase();
|
||||
var visible = !query || haystack.indexOf(query) !== -1;
|
||||
row.setAttribute('data-table-search-match', visible ? 'true' : 'false');
|
||||
row.classList.toggle('d-none', !visible);
|
||||
row.hidden = !visible;
|
||||
if (visible) {
|
||||
visibleCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (defaultEmptyRow) {
|
||||
defaultEmptyRow.classList.toggle('d-none', Boolean(query));
|
||||
defaultEmptyRow.hidden = Boolean(query);
|
||||
}
|
||||
|
||||
if (query && visibleCount === 0) {
|
||||
ensureGeneratedEmptyRow('No results match your search.');
|
||||
}
|
||||
|
||||
if (typeof window.syncTablePagination === 'function') {
|
||||
window.syncTablePagination(container, { resetPage: true });
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('input', syncSearch);
|
||||
syncSearch();
|
||||
});
|
||||
if (typeof window.initTableSearches === 'function') {
|
||||
window.initTableSearches(root);
|
||||
}
|
||||
}
|
||||
|
||||
function createSlideThumbPlaceholder() {
|
||||
@@ -841,10 +545,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (window.initSortableTables) {
|
||||
window.initSortableTables();
|
||||
}
|
||||
|
||||
initConfirmForms();
|
||||
initDirtyTracking();
|
||||
initCancelConfirm();
|
||||
@@ -854,19 +554,9 @@
|
||||
initJsonTogglePanels();
|
||||
initLocalDateTimes();
|
||||
initTableSearches();
|
||||
initTablePaginations();
|
||||
attachSlideThumbFallbacks(document);
|
||||
window.initJsonTogglePanels = initJsonTogglePanels;
|
||||
window.initLocalDateTimes = initLocalDateTimes;
|
||||
window.initTableSearches = initTableSearches;
|
||||
window.initTablePaginations = initTablePaginations;
|
||||
window.syncTablePagination = function (container, options) {
|
||||
if (!container || !container.syncTablePagination) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.syncTablePagination(options);
|
||||
};
|
||||
window.createSlideThumbPlaceholder = createSlideThumbPlaceholder;
|
||||
window.attachSlideThumbFallbacks = attachSlideThumbFallbacks;
|
||||
}());
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
(function () {
|
||||
function getGroupCheckboxes(group) {
|
||||
return Array.prototype.slice.call(group.querySelectorAll('input[name="permission_keys[]"]'));
|
||||
}
|
||||
|
||||
function getPermissionKey(checkbox) {
|
||||
return String((checkbox && (checkbox.getAttribute('data-permission-key') || checkbox.value)) || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function getActionKey(checkbox) {
|
||||
var permissionKey = getPermissionKey(checkbox);
|
||||
var parts = permissionKey.split('.');
|
||||
if (parts.length !== 2) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return String(parts[1] || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function syncPermissionGroup(group) {
|
||||
var checkboxes = getGroupCheckboxes(group);
|
||||
if (!checkboxes.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var readCheckbox = null;
|
||||
var nonReadChecked = false;
|
||||
|
||||
checkboxes.forEach(function (checkbox) {
|
||||
if (getActionKey(checkbox) === 'read') {
|
||||
readCheckbox = checkbox;
|
||||
return;
|
||||
}
|
||||
if (checkbox.checked) {
|
||||
nonReadChecked = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!readCheckbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!readCheckbox.checked && nonReadChecked) {
|
||||
readCheckbox.checked = true;
|
||||
}
|
||||
|
||||
if (!readCheckbox.checked) {
|
||||
checkboxes.forEach(function (checkbox) {
|
||||
if (getActionKey(checkbox) !== 'read') {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleGroupChange(event) {
|
||||
var checkbox = event.target && event.target.matches ? event.target : null;
|
||||
if (!checkbox || checkbox.tagName !== 'INPUT' || checkbox.type !== 'checkbox') {
|
||||
return;
|
||||
}
|
||||
|
||||
var actionKey = getActionKey(checkbox);
|
||||
var group = checkbox.closest('.accordion-item');
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
var checkboxes = getGroupCheckboxes(group);
|
||||
var readCheckbox = checkboxes.find(function (candidate) {
|
||||
return getActionKey(candidate) === 'read';
|
||||
}) || null;
|
||||
|
||||
if (!readCheckbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionKey === 'read' && !checkbox.checked) {
|
||||
checkboxes.forEach(function (candidate) {
|
||||
if (candidate !== checkbox) {
|
||||
candidate.checked = false;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionKey !== 'read' && checkbox.checked) {
|
||||
readCheckbox.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
function initPermissionGroups() {
|
||||
document.querySelectorAll('.accordion-item').forEach(function (group) {
|
||||
syncPermissionGroup(group);
|
||||
});
|
||||
|
||||
document.addEventListener('change', function (event) {
|
||||
var checkbox = event.target;
|
||||
if (!checkbox || checkbox.tagName !== 'INPUT' || checkbox.type !== 'checkbox') {
|
||||
return;
|
||||
}
|
||||
if (String(checkbox.getAttribute('name') || '') !== 'permission_keys[]') {
|
||||
return;
|
||||
}
|
||||
|
||||
handleGroupChange(event);
|
||||
syncPermissionGroup(checkbox.closest('.accordion-item'));
|
||||
});
|
||||
}
|
||||
|
||||
initPermissionGroups();
|
||||
}());
|
||||
@@ -1,102 +0,0 @@
|
||||
(function () {
|
||||
function updateSidebarStatus(status, label) {
|
||||
var dot = document.getElementById('sidebar-status-dot');
|
||||
var text = document.getElementById('sidebar-status-text');
|
||||
var pill = document.getElementById('sidebar-status-pill');
|
||||
if (!dot && !text && !pill) {
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedStatus = status === 'online' || status === 'offline' || status === 'unknown'
|
||||
? status
|
||||
: (status ? 'online' : 'offline');
|
||||
var statusLabel = normalizedStatus === 'online'
|
||||
? (label || 'Connected to player feed')
|
||||
: normalizedStatus === 'offline'
|
||||
? 'Disconnected from player feed'
|
||||
: (label || 'Connecting to player feed');
|
||||
var pillLabel = normalizedStatus === 'online' ? 'Connected' : normalizedStatus === 'offline' ? 'Disconnected' : 'Connecting';
|
||||
|
||||
dot.classList.remove('status-dot--unknown', 'status-dot--online', 'status-dot--offline');
|
||||
dot.classList.add(normalizedStatus === 'online' ? 'status-dot--online' : normalizedStatus === 'offline' ? 'status-dot--offline' : 'status-dot--unknown');
|
||||
dot.setAttribute('aria-label', statusLabel);
|
||||
dot.setAttribute('title', statusLabel);
|
||||
|
||||
if (text) {
|
||||
text.textContent = statusLabel;
|
||||
}
|
||||
|
||||
if (pill) {
|
||||
pill.classList.remove('status-pill--unknown', 'status-pill--online', 'status-pill--offline');
|
||||
pill.classList.add(normalizedStatus === 'online' ? 'status-pill--online' : normalizedStatus === 'offline' ? 'status-pill--offline' : 'status-pill--unknown');
|
||||
pill.textContent = pillLabel;
|
||||
}
|
||||
}
|
||||
|
||||
function connectDashboardSocket() {
|
||||
if (!window.WebSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hasStatusIndicators = document.getElementById('sidebar-status-dot') || document.getElementById('sidebar-status-text') || document.getElementById('sidebar-status-pill');
|
||||
if (!hasStatusIndicators) {
|
||||
return;
|
||||
}
|
||||
|
||||
var socket = null;
|
||||
var reconnectTimer = null;
|
||||
|
||||
function scheduleReconnect() {
|
||||
if (reconnectTimer) {
|
||||
return;
|
||||
}
|
||||
reconnectTimer = window.setTimeout(function () {
|
||||
reconnectTimer = null;
|
||||
connect();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
socket = new WebSocket(protocol + '//' + window.location.host + '/ws/dashboard');
|
||||
updateSidebarStatus('unknown', 'Connecting to player feed');
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
try {
|
||||
var payload = JSON.parse(String(event.data || '{}'));
|
||||
if (payload && payload.type === 'dashboard-state') {
|
||||
if (typeof window.webHandleDashboardState === 'function') {
|
||||
window.webHandleDashboardState(payload.state);
|
||||
}
|
||||
updateSidebarStatus(Boolean(payload.state && payload.state.playerServiceConnected), 'Live player feed');
|
||||
}
|
||||
} catch (_error) {
|
||||
// Ignore malformed dashboard payloads.
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = function () {
|
||||
updateSidebarStatus('offline');
|
||||
scheduleReconnect();
|
||||
};
|
||||
|
||||
socket.onerror = function () {
|
||||
updateSidebarStatus('offline');
|
||||
try {
|
||||
socket.close();
|
||||
} catch (_error) {
|
||||
// ignore close errors
|
||||
}
|
||||
};
|
||||
|
||||
socket.onopen = function () {
|
||||
updateSidebarStatus('unknown', 'Connecting to player feed');
|
||||
};
|
||||
}
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
connectDashboardSocket();
|
||||
}());
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
(function () {
|
||||
var THEME_STORAGE_KEY = 'web-theme';
|
||||
var CKEDITOR_THEME_STYLE_ID = 'ckeditor-dark-theme-overrides';
|
||||
|
||||
function getPreferredTheme() {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
}
|
||||
|
||||
function getStoredTheme() {
|
||||
try {
|
||||
var storedTheme = window.localStorage.getItem(THEME_STORAGE_KEY);
|
||||
if (storedTheme === 'dark' || storedTheme === 'light') {
|
||||
return storedTheme;
|
||||
}
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function setStoredTheme(theme) {
|
||||
try {
|
||||
window.localStorage.setItem(THEME_STORAGE_KEY, theme);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getOrCreateCkeditorThemeStyleElement() {
|
||||
var styleElement = document.getElementById(CKEDITOR_THEME_STYLE_ID);
|
||||
|
||||
if (styleElement) {
|
||||
return styleElement;
|
||||
}
|
||||
|
||||
styleElement = document.createElement('style');
|
||||
styleElement.id = CKEDITOR_THEME_STYLE_ID;
|
||||
document.head.appendChild(styleElement);
|
||||
|
||||
return styleElement;
|
||||
}
|
||||
|
||||
function syncCkeditorTheme(theme) {
|
||||
var styleElement = getOrCreateCkeditorThemeStyleElement();
|
||||
|
||||
if (theme !== 'dark') {
|
||||
styleElement.textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
styleElement.textContent = [
|
||||
'.ck-body-wrapper .ck.ck-dropdown__panel,',
|
||||
'.ck-body-wrapper .ck.ck-list__panel,',
|
||||
'.ck-body-wrapper .ck.ck-list,',
|
||||
'.ck-body-wrapper .ck.ck-balloon-panel {',
|
||||
' background: var(--bs-body-bg) !important;',
|
||||
' background-color: var(--bs-body-bg) !important;',
|
||||
' border-color: var(--bs-border-color) !important;',
|
||||
' color: var(--bs-body-color) !important;',
|
||||
'}',
|
||||
'.ck-body-wrapper .ck.ck-list .ck-list-item-button {',
|
||||
' background: transparent !important;',
|
||||
' background-color: transparent !important;',
|
||||
' color: var(--bs-body-color) !important;',
|
||||
'}',
|
||||
'.ck-body-wrapper .ck.ck-list .ck-list-item-button:hover {',
|
||||
' background: var(--bs-secondary-bg) !important;',
|
||||
' background-color: var(--bs-secondary-bg) !important;',
|
||||
'}',
|
||||
'.ck-body-wrapper .ck.ck-color-grid,',
|
||||
'.ck-body-wrapper .ck.ck-color-grid__tile {',
|
||||
' color: var(--bs-body-color) !important;',
|
||||
'}',
|
||||
'.ck-body-wrapper .ck.ck-color-grid__tile {',
|
||||
' border-color: var(--bs-border-color) !important;',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
var normalizedTheme = theme === 'dark' ? 'dark' : 'light';
|
||||
var nextTheme = normalizedTheme === 'dark' ? 'light' : 'dark';
|
||||
var nextThemeLabel = nextTheme === 'dark' ? 'Dark mode' : 'Light mode';
|
||||
|
||||
document.documentElement.dataset.bsTheme = normalizedTheme;
|
||||
document.documentElement.style.colorScheme = normalizedTheme;
|
||||
syncCkeditorTheme(normalizedTheme);
|
||||
|
||||
Array.prototype.forEach.call(document.querySelectorAll('[data-theme-toggle]'), function (toggleButton) {
|
||||
var icon = toggleButton.querySelector('.theme-toggle__icon');
|
||||
toggleButton.setAttribute('aria-pressed', normalizedTheme === 'dark' ? 'true' : 'false');
|
||||
toggleButton.setAttribute('aria-label', 'Switch to ' + nextThemeLabel.toLowerCase());
|
||||
if (icon) {
|
||||
icon.classList.remove('theme-toggle__icon--moon', 'theme-toggle__icon--sun');
|
||||
icon.classList.add(normalizedTheme === 'dark' ? 'theme-toggle__icon--sun' : 'theme-toggle__icon--moon');
|
||||
}
|
||||
});
|
||||
|
||||
return normalizedTheme;
|
||||
}
|
||||
|
||||
function initThemeToggle() {
|
||||
var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-theme-toggle]'));
|
||||
var storedTheme = getStoredTheme();
|
||||
var theme = storedTheme || getPreferredTheme();
|
||||
|
||||
applyTheme(theme);
|
||||
|
||||
if (!toggleButtons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
||||
toggleButton.addEventListener('click', function () {
|
||||
var nextTheme = document.documentElement.dataset.bsTheme === 'dark' ? 'light' : 'dark';
|
||||
setStoredTheme(nextTheme);
|
||||
applyTheme(nextTheme);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initSidebarToggle() {
|
||||
var body = document.body;
|
||||
var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-sidebar-toggle]'));
|
||||
var backdrop = document.querySelector('[data-sidebar-backdrop]');
|
||||
|
||||
if (!toggleButtons.length || !backdrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
function setSidebarOpen(isOpen) {
|
||||
body.classList.toggle('is-sidebar-open', isOpen);
|
||||
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
||||
toggleButton.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
setSidebarOpen(!body.classList.contains('is-sidebar-open'));
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(toggleButtons, function (toggleButton) {
|
||||
toggleButton.addEventListener('click', function () {
|
||||
toggleSidebar();
|
||||
});
|
||||
});
|
||||
|
||||
backdrop.addEventListener('click', function () {
|
||||
setSidebarOpen(false);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Escape') {
|
||||
setSidebarOpen(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.getPreferredTheme = getPreferredTheme;
|
||||
window.getStoredTheme = getStoredTheme;
|
||||
window.setStoredTheme = setStoredTheme;
|
||||
window.applyTheme = applyTheme;
|
||||
window.initThemeToggle = initThemeToggle;
|
||||
window.initSidebarToggle = initSidebarToggle;
|
||||
|
||||
initThemeToggle();
|
||||
initSidebarToggle();
|
||||
}());
|
||||
@@ -1,134 +0,0 @@
|
||||
(function () {
|
||||
function getBootstrapToast(toast) {
|
||||
if (!toast || !window.bootstrap || !window.bootstrap.Toast) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.bootstrap.Toast.getOrCreateInstance(toast, {
|
||||
autohide: true,
|
||||
delay: 4000
|
||||
});
|
||||
}
|
||||
|
||||
function removeToast(toast) {
|
||||
if (toast && toast.parentNode) {
|
||||
toast.parentNode.removeChild(toast);
|
||||
}
|
||||
}
|
||||
|
||||
function dismissToast(toast) {
|
||||
var instance = getBootstrapToast(toast);
|
||||
if (instance) {
|
||||
instance.hide();
|
||||
return;
|
||||
}
|
||||
removeToast(toast);
|
||||
}
|
||||
|
||||
function setToastVariant(toast, variant) {
|
||||
if (!toast || !toast.classList) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nextVariant = String(variant || 'info').trim().toLowerCase();
|
||||
var variants = ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'];
|
||||
variants.forEach(function (value) {
|
||||
toast.classList.remove('text-bg-' + value);
|
||||
});
|
||||
toast.classList.add('text-bg-' + (variants.indexOf(nextVariant) === -1 ? 'info' : nextVariant));
|
||||
}
|
||||
|
||||
function getMessageVariant(message, fallbackVariant) {
|
||||
var text = String(message || '').trim();
|
||||
if (/\b(?:unable to|cannot|can't|could not|failed to)\s+delete\b/i.test(text) || /\bdelete\b.*\b(?:before|first)\b/i.test(text) || /\bstill (?:in use|linked|assigned|used)\b/i.test(text)) {
|
||||
return 'danger';
|
||||
}
|
||||
if (/\b(?:already exists|already exist|already taken|duplicate|must be unique|name already exists)\b/i.test(text)) {
|
||||
return 'warning';
|
||||
}
|
||||
return String(fallbackVariant || 'info').trim().toLowerCase() || 'info';
|
||||
}
|
||||
|
||||
function showToast(message, variant) {
|
||||
var text = String(message || '').trim();
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
var container = document.getElementById('app-toast-container');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
var existingToast = document.getElementById('app-toast');
|
||||
if (existingToast) {
|
||||
var existingBody = existingToast.querySelector('.toast-body');
|
||||
if (existingBody) {
|
||||
existingBody.textContent = text;
|
||||
}
|
||||
var nextVariant = getMessageVariant(text, variant);
|
||||
existingToast.setAttribute('data-toast-variant', nextVariant);
|
||||
setToastVariant(existingToast, nextVariant);
|
||||
var existingInstance = getBootstrapToast(existingToast);
|
||||
if (existingInstance) {
|
||||
existingInstance.show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var toast = document.createElement('div');
|
||||
toast.className = 'toast align-items-center border-0';
|
||||
toast.id = 'app-toast';
|
||||
toast.setAttribute('role', 'status');
|
||||
toast.setAttribute('aria-live', 'polite');
|
||||
toast.setAttribute('aria-atomic', 'true');
|
||||
toast.setAttribute('data-bs-autohide', 'true');
|
||||
toast.setAttribute('data-bs-delay', '4000');
|
||||
toast.innerHTML = '<div class="d-flex"><div class="toast-body"></div><button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Dismiss notification"></button></div>';
|
||||
var toastVariant = getMessageVariant(text, variant);
|
||||
toast.setAttribute('data-toast-variant', toastVariant);
|
||||
setToastVariant(toast, toastVariant);
|
||||
toast.querySelector('.toast-body').textContent = text;
|
||||
|
||||
toast.addEventListener('hidden.bs.toast', function () {
|
||||
removeToast(toast);
|
||||
});
|
||||
|
||||
container.appendChild(toast);
|
||||
var instance = getBootstrapToast(toast);
|
||||
if (instance) {
|
||||
instance.show();
|
||||
}
|
||||
}
|
||||
|
||||
function initToast() {
|
||||
var toast = document.getElementById('app-toast');
|
||||
if (!toast) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var url = new URL(window.location.href);
|
||||
if (url.searchParams.has('message')) {
|
||||
url.searchParams.delete('message');
|
||||
window.history.replaceState({}, document.title, url.pathname + url.search + url.hash);
|
||||
}
|
||||
} catch (_error) {
|
||||
// ignore URL cleanup failures
|
||||
}
|
||||
|
||||
var existingVariant = String(toast.getAttribute('data-toast-variant') || '').trim().toLowerCase() || getMessageVariant((toast.querySelector('.toast-body') && toast.querySelector('.toast-body').textContent) || '', 'info');
|
||||
setToastVariant(toast, existingVariant);
|
||||
|
||||
var instance = getBootstrapToast(toast);
|
||||
if (instance) {
|
||||
instance.show();
|
||||
}
|
||||
}
|
||||
|
||||
window.dismissToast = dismissToast;
|
||||
window.showToast = showToast;
|
||||
window.initToast = initToast;
|
||||
|
||||
initToast();
|
||||
}());
|
||||
Reference in New Issue
Block a user