Release 1.5.3
This commit is contained in:
@@ -168,6 +168,32 @@
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
.card-header .card-tools > .btn,
|
||||
.card-header .card-tools > a.btn {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-header .card-tools .input-group {
|
||||
flex: 1 1 11rem;
|
||||
min-width: 8rem;
|
||||
max-width: 14rem;
|
||||
}
|
||||
|
||||
.background-tasks-task-tools {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.background-tasks-task-search {
|
||||
flex: 0 1 12rem;
|
||||
min-width: 9rem;
|
||||
max-width: 12rem;
|
||||
}
|
||||
|
||||
.background-tasks-task-status {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.admin-form-card {
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -1182,6 +1208,149 @@ html[data-bs-theme='dark'] .ck-body-wrapper .ck.ck-color-grid__tile {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-modal-body {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding-bottom: 0.25rem;
|
||||
background: var(--bs-modal-bg, var(--bs-body-bg));
|
||||
}
|
||||
|
||||
.playlist-slide-picker-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 1.25rem;
|
||||
max-height: min(64vh, 48rem);
|
||||
overflow: auto;
|
||||
padding: 0.25rem 0.5rem 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card {
|
||||
appearance: none;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border-radius: 1.1rem;
|
||||
background: var(--bs-body-bg);
|
||||
color: var(--bs-body-color);
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0.3rem;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card:hover,
|
||||
.playlist-slide-picker-card:focus-visible {
|
||||
border-color: var(--bs-primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb), 0.15);
|
||||
transform: translateY(-1px);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card.is-selected {
|
||||
border-color: var(--bs-primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb), 0.2);
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card.is-assigned,
|
||||
.playlist-slide-picker-card:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.55;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card.is-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-media {
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
overflow: hidden;
|
||||
border-radius: 1.05rem 1.05rem 0 0;
|
||||
background: rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-thumb-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-thumb-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.35rem;
|
||||
color: var(--bs-secondary-color, #6c757d);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0.12) 100%),
|
||||
repeating-linear-gradient(45deg, rgba(108, 117, 125, 0.08), rgba(108, 117, 125, 0.08) 12px, rgba(108, 117, 125, 0.04) 12px, rgba(108, 117, 125, 0.04) 24px);
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-thumb-placeholder-icon {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-thumb-placeholder-label {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-title {
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
min-height: 0;
|
||||
font-size: 0.98rem;
|
||||
padding: 0 0.9rem 0.6rem;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-meta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-check {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
z-index: 1;
|
||||
color: var(--bs-primary);
|
||||
opacity: 0;
|
||||
background: rgba(var(--bs-body-bg-rgb), 0.88);
|
||||
border-radius: 999px;
|
||||
padding: 0.2rem 0.3rem;
|
||||
box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.playlist-slide-picker-card.is-selected .playlist-slide-picker-check {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-badge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playlist-slide-picker-empty.is-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.schedule-day-grid .schedule-day-button {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
@@ -1242,10 +1411,60 @@ td[data-label="Actions"] > div {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.playlist-duration-input {
|
||||
width: 6rem;
|
||||
min-width: 6rem;
|
||||
}
|
||||
|
||||
table.table > :not(caption) > * > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.playlist-slide-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.playlist-slide-cell-content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.playlist-slide-title {
|
||||
display: block;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.playlist-slide-thumb {
|
||||
flex: 0 0 auto;
|
||||
width: 5.5rem;
|
||||
height: 3.5rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
background: rgba(15, 23, 42, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.playlist-slide-thumb-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.playlist-slide-thumb-placeholder {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--bs-secondary-color, #6c757d);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.playlist-order-cell {
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -434,6 +434,11 @@
|
||||
window.location.replace(response.url);
|
||||
return;
|
||||
}
|
||||
if (form.hasAttribute('data-async-save-reload-on-success')) {
|
||||
clearFormDirty(form);
|
||||
window.location.replace(response.url || window.location.href);
|
||||
return;
|
||||
}
|
||||
clearFormDirty(form);
|
||||
return response.text().then(function (text) {
|
||||
var savedMessage = '';
|
||||
|
||||
@@ -234,6 +234,12 @@
|
||||
if (typeof window.initLocalDateTimes === 'function') {
|
||||
window.initLocalDateTimes(targetElement);
|
||||
}
|
||||
if (typeof window.initTableSearches === 'function') {
|
||||
window.initTableSearches(targetElement);
|
||||
}
|
||||
if (typeof window.initTablePaginations === 'function') {
|
||||
window.initTablePaginations(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceRefreshTargetFromPage(refreshTargetSelector, sequenceId) {
|
||||
@@ -434,6 +440,11 @@
|
||||
window.location.replace(response.url);
|
||||
return;
|
||||
}
|
||||
if (form.hasAttribute('data-async-save-reload-on-success')) {
|
||||
clearFormDirty(form);
|
||||
window.location.replace(response.url || window.location.href);
|
||||
return;
|
||||
}
|
||||
clearFormDirty(form);
|
||||
return response.text().then(function (text) {
|
||||
var savedMessage = '';
|
||||
@@ -577,6 +588,332 @@
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
function createSlideThumbPlaceholder() {
|
||||
var placeholder = document.createElement('span');
|
||||
var icon = document.createElement('i');
|
||||
|
||||
placeholder.className = 'playlist-slide-thumb-placeholder';
|
||||
icon.className = 'bi bi-image';
|
||||
icon.setAttribute('aria-hidden', 'true');
|
||||
placeholder.appendChild(icon);
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
function attachSlideThumbFallbacks(root) {
|
||||
var scope = root && root.querySelectorAll ? root : document;
|
||||
|
||||
Array.prototype.forEach.call(scope.querySelectorAll('.playlist-slide-thumb-image'), function (image) {
|
||||
if (image.getAttribute('data-slide-thumb-fallback-bound') === 'true') {
|
||||
return;
|
||||
}
|
||||
image.setAttribute('data-slide-thumb-fallback-bound', 'true');
|
||||
image.addEventListener('error', function () {
|
||||
if (image.parentNode) {
|
||||
image.parentNode.replaceChild(createSlideThumbPlaceholder(), image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (window.initSortableTables) {
|
||||
window.initSortableTables();
|
||||
}
|
||||
@@ -589,6 +926,20 @@
|
||||
initSubmitOnChange();
|
||||
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;
|
||||
}());
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
(function () {
|
||||
function getOrCreate(modalElement) {
|
||||
if (!modalElement || !window.bootstrap || !window.bootstrap.Modal) {
|
||||
return null;
|
||||
}
|
||||
return window.bootstrap.Modal.getOrCreateInstance(modalElement);
|
||||
}
|
||||
|
||||
function show(modalElement) {
|
||||
var modal = getOrCreate(modalElement);
|
||||
if (modal) {
|
||||
modal.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hide(modalElement) {
|
||||
var modal = getOrCreate(modalElement);
|
||||
if (modal) {
|
||||
modal.hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
window.pulseModal = {
|
||||
getOrCreate: getOrCreate,
|
||||
show: show,
|
||||
hide: hide
|
||||
};
|
||||
}());
|
||||
@@ -305,16 +305,239 @@
|
||||
function initPlaylistEditStaging() {
|
||||
var tbody = document.getElementById('playlist-items-body');
|
||||
var form = document.getElementById('playlist-edit-form');
|
||||
var addSection = document.getElementById('playlist-add-section');
|
||||
var addSlideForm = document.getElementById('playlist-add-slide-form');
|
||||
var addSlideButton = document.getElementById('playlist-add-slide-button');
|
||||
var addSlideSelect = document.getElementById('playlist-add-slide-select');
|
||||
var addSlideEmpty = document.getElementById('playlist-add-slide-empty');
|
||||
var addSlideModal = document.getElementById('playlist-add-slide-modal');
|
||||
var addSlideGrid = document.getElementById('playlist-slide-picker-grid');
|
||||
var addSlideSearch = document.getElementById('playlist-slide-picker-search');
|
||||
var addSlideEmpty = document.getElementById('playlist-slide-picker-empty');
|
||||
var addSlideConfirm = document.getElementById('playlist-confirm-add-slides');
|
||||
var addSlideCount = document.getElementById('playlist-slide-picker-selected-count');
|
||||
var addSlideData = document.getElementById('playlist-add-slide-data');
|
||||
var addSlideOpenButton = document.getElementById('playlist-open-slide-modal');
|
||||
var slidePickerCards = [];
|
||||
var slidePickerSelection = new Set();
|
||||
var slidePickerSlides = [];
|
||||
|
||||
if (!tbody || !form) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addSlideData) {
|
||||
try {
|
||||
var parsedSlides = JSON.parse(addSlideData.textContent || '[]');
|
||||
slidePickerSlides = Array.isArray(parsedSlides) ? parsedSlides : [];
|
||||
} catch (_error) {
|
||||
slidePickerSlides = [];
|
||||
}
|
||||
}
|
||||
|
||||
function getModalInstance() {
|
||||
if (!window.pulseModal) {
|
||||
return null;
|
||||
}
|
||||
return window.pulseModal.getOrCreate(addSlideModal);
|
||||
}
|
||||
|
||||
function setCardSelected(card, selected) {
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
card.classList.toggle('is-selected', Boolean(selected));
|
||||
card.setAttribute('aria-pressed', Boolean(selected) ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function setCardVisible(card, visible) {
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
card.classList.toggle('is-hidden', !visible);
|
||||
}
|
||||
|
||||
function clearModalSelection() {
|
||||
slidePickerSelection.clear();
|
||||
slidePickerCards.forEach(function (card) {
|
||||
setCardSelected(card, false);
|
||||
});
|
||||
if (addSlideSearch) {
|
||||
addSlideSearch.value = '';
|
||||
}
|
||||
syncSlidePickerState();
|
||||
}
|
||||
|
||||
function attachSlideThumbFallbacks(root) {
|
||||
if (typeof window.attachSlideThumbFallbacks === 'function') {
|
||||
window.attachSlideThumbFallbacks(root || tbody);
|
||||
}
|
||||
}
|
||||
|
||||
function createPickerThumb(slide) {
|
||||
function createPlaceholder() {
|
||||
var placeholder = document.createElement('div');
|
||||
var icon = document.createElement('i');
|
||||
var label = document.createElement('span');
|
||||
|
||||
placeholder.className = 'playlist-slide-picker-thumb-placeholder';
|
||||
icon.className = 'bi bi-image playlist-slide-picker-thumb-placeholder-icon';
|
||||
icon.setAttribute('aria-hidden', 'true');
|
||||
label.className = 'playlist-slide-picker-thumb-placeholder-label';
|
||||
label.textContent = 'No thumbnail';
|
||||
|
||||
placeholder.appendChild(icon);
|
||||
placeholder.appendChild(label);
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
if (slide && slide.thumbnail_path) {
|
||||
var image = document.createElement('img');
|
||||
image.className = 'playlist-slide-picker-thumb-image';
|
||||
image.loading = 'lazy';
|
||||
image.alt = String(slide.title || 'Slide');
|
||||
image.src = String(slide.thumbnail_path || '');
|
||||
image.addEventListener('error', function () {
|
||||
if (image.parentNode) {
|
||||
image.parentNode.replaceChild(createPlaceholder(), image);
|
||||
}
|
||||
});
|
||||
return image;
|
||||
}
|
||||
|
||||
return createPlaceholder();
|
||||
}
|
||||
|
||||
function createPickerCard(slide) {
|
||||
var button = document.createElement('button');
|
||||
var media = document.createElement('div');
|
||||
var title = document.createElement('div');
|
||||
var check = document.createElement('span');
|
||||
var searchText = String(slide && slide.title ? slide.title : '').toLowerCase();
|
||||
|
||||
button.type = 'button';
|
||||
button.className = 'playlist-slide-picker-card';
|
||||
button.setAttribute('aria-pressed', 'false');
|
||||
button.setAttribute('data-slide-id', String(slide.id || ''));
|
||||
button.setAttribute('data-search-text', searchText);
|
||||
|
||||
if (slide && slide.isAssigned) {
|
||||
button.disabled = true;
|
||||
button.classList.add('is-assigned');
|
||||
}
|
||||
|
||||
media.className = 'playlist-slide-picker-media';
|
||||
media.appendChild(createPickerThumb(slide));
|
||||
check.className = 'playlist-slide-picker-check bi bi-check2-circle';
|
||||
check.setAttribute('aria-hidden', 'true');
|
||||
media.appendChild(check);
|
||||
|
||||
title.className = 'playlist-slide-picker-title';
|
||||
title.textContent = String(slide && slide.title ? slide.title : 'Slide');
|
||||
|
||||
button.appendChild(media);
|
||||
button.appendChild(title);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function renderSlidePicker() {
|
||||
if (!addSlideGrid) {
|
||||
return;
|
||||
}
|
||||
addSlideGrid.innerHTML = '';
|
||||
slidePickerCards = slidePickerSlides.map(function (slide) {
|
||||
var card = createPickerCard(slide);
|
||||
addSlideGrid.appendChild(card);
|
||||
return card;
|
||||
});
|
||||
syncSlidePickerState();
|
||||
}
|
||||
|
||||
function syncSlidePickerState() {
|
||||
var query = String(addSlideSearch && addSlideSearch.value ? addSlideSearch.value : '').trim().toLowerCase();
|
||||
var visibleCount = 0;
|
||||
|
||||
slidePickerCards.forEach(function (card) {
|
||||
var searchText = String(card.getAttribute('data-search-text') || '');
|
||||
var matches = !query || searchText.indexOf(query) !== -1;
|
||||
setCardVisible(card, matches);
|
||||
if (matches) {
|
||||
visibleCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (addSlideEmpty) {
|
||||
addSlideEmpty.textContent = query ? 'No slides match your search.' : 'No slides are currently available for this playlist.';
|
||||
addSlideEmpty.classList.toggle('is-hidden', visibleCount !== 0);
|
||||
}
|
||||
|
||||
if (addSlideCount) {
|
||||
addSlideCount.textContent = String(slidePickerSelection.size);
|
||||
}
|
||||
|
||||
if (addSlideConfirm) {
|
||||
addSlideConfirm.disabled = slidePickerSelection.size === 0;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCardSelection(card) {
|
||||
var slideId = String(card && card.getAttribute('data-slide-id') ? card.getAttribute('data-slide-id') : '');
|
||||
var selected;
|
||||
|
||||
if (!slideId || card.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
selected = !slidePickerSelection.has(slideId);
|
||||
if (selected) {
|
||||
slidePickerSelection.add(slideId);
|
||||
} else {
|
||||
slidePickerSelection.delete(slideId);
|
||||
}
|
||||
setCardSelected(card, selected);
|
||||
syncSlidePickerState();
|
||||
}
|
||||
|
||||
function addSelectedSlides() {
|
||||
var selectedSlides = slidePickerSlides.filter(function (slide) {
|
||||
return slidePickerSelection.has(String(slide.id || '')) && !slide.isAssigned;
|
||||
});
|
||||
|
||||
if (!selectedSlides.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tbody.querySelector('.playlist-empty-row')) {
|
||||
tbody.querySelector('.playlist-empty-row').remove();
|
||||
}
|
||||
|
||||
selectedSlides.forEach(function (slide) {
|
||||
var row = createRow({
|
||||
row_key: 'new-' + Date.now() + '-' + slide.id,
|
||||
slide_id: String(slide.id),
|
||||
thumbnail_path: String(slide.thumbnail_path || ''),
|
||||
canvas_signature: String(slide.canvasSignature || ''),
|
||||
title: slide.title || 'Slide',
|
||||
duration_seconds: 10,
|
||||
schedule_mode: 'always',
|
||||
schedule_start_datetime: '',
|
||||
schedule_end_datetime: '',
|
||||
schedule_start_time: '',
|
||||
schedule_end_time: '',
|
||||
schedule_days_json: '[]',
|
||||
summary: 'Always visible'
|
||||
});
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
attachSlideThumbFallbacks(tbody);
|
||||
|
||||
updateRowOrder(true);
|
||||
|
||||
if (!window.pulseModal || !window.pulseModal.hide(addSlideModal)) {
|
||||
addSlideModal.classList.remove('show');
|
||||
addSlideModal.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
attachSlideThumbFallbacks(tbody);
|
||||
|
||||
function getRows() {
|
||||
return Array.prototype.slice.call(tbody.querySelectorAll('tr[data-playlist-slide-row]'));
|
||||
}
|
||||
@@ -375,9 +598,6 @@
|
||||
}
|
||||
|
||||
function syncAddSlideOptions() {
|
||||
if (!addSlideSelect) {
|
||||
return;
|
||||
}
|
||||
var activeSlideIds = {};
|
||||
var activeCanvasSignatures = {};
|
||||
getRows().forEach(function (row) {
|
||||
@@ -397,51 +617,37 @@
|
||||
allowedCanvasSignature = canvasKeys[0];
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(addSlideSelect.options, function (option) {
|
||||
if (!option.value) {
|
||||
return;
|
||||
slidePickerSlides.forEach(function (slide) {
|
||||
var slideId = String(slide.id || '');
|
||||
var card = slidePickerCards.find(function (item) {
|
||||
return String(item.getAttribute('data-slide-id') || '') === slideId;
|
||||
});
|
||||
var canvasSignature = String(slide.canvasSignature || '');
|
||||
var isAssigned = Boolean(activeSlideIds[slideId]);
|
||||
var isCanvasMismatch = Boolean(allowedCanvasSignature && canvasSignature && canvasSignature !== allowedCanvasSignature);
|
||||
|
||||
if (card) {
|
||||
card.disabled = isAssigned || isCanvasMismatch;
|
||||
card.classList.toggle('is-assigned', isAssigned);
|
||||
card.classList.toggle('is-mismatch', isCanvasMismatch);
|
||||
card.classList.toggle('is-hidden', false);
|
||||
card.setAttribute('aria-disabled', card.disabled ? 'true' : 'false');
|
||||
}
|
||||
var optionCanvasSignature = String(option.getAttribute('data-canvas-signature') || '');
|
||||
var isActive = Boolean(activeSlideIds[String(option.value)]);
|
||||
var isCanvasMismatch = Boolean(allowedCanvasSignature && optionCanvasSignature && optionCanvasSignature !== allowedCanvasSignature);
|
||||
option.disabled = isActive || isCanvasMismatch;
|
||||
option.hidden = isActive || isCanvasMismatch;
|
||||
});
|
||||
if (
|
||||
addSlideSelect.options[addSlideSelect.selectedIndex]
|
||||
&& (addSlideSelect.options[addSlideSelect.selectedIndex].disabled || addSlideSelect.options[addSlideSelect.selectedIndex].hidden)
|
||||
) {
|
||||
for (var i = 0; i < addSlideSelect.options.length; i += 1) {
|
||||
if (!addSlideSelect.options[i].disabled && !addSlideSelect.options[i].hidden) {
|
||||
addSlideSelect.selectedIndex = i;
|
||||
break;
|
||||
if (isAssigned) {
|
||||
slidePickerSelection.delete(slideId);
|
||||
if (card) {
|
||||
setCardSelected(card, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var availableCount = 0;
|
||||
Array.prototype.forEach.call(addSlideSelect.options, function (option) {
|
||||
if (!option.value) {
|
||||
return;
|
||||
}
|
||||
if (!option.disabled && !option.hidden) {
|
||||
availableCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (addSlideButton) {
|
||||
addSlideButton.disabled = availableCount === 0;
|
||||
}
|
||||
if (addSlideSelect) {
|
||||
addSlideSelect.disabled = availableCount === 0;
|
||||
}
|
||||
if (addSlideEmpty) {
|
||||
addSlideEmpty.classList.toggle('is-hidden', availableCount !== 0);
|
||||
}
|
||||
if (addSection && addSlideForm && addSlideSelect && addSlideButton) {
|
||||
var controlsVisible = availableCount > 0;
|
||||
addSlideForm.classList.toggle('is-hidden', !controlsVisible);
|
||||
if (addSlideOpenButton) {
|
||||
addSlideOpenButton.disabled = slidePickerCards.filter(function (card) {
|
||||
return !card.disabled;
|
||||
}).length === 0;
|
||||
}
|
||||
|
||||
syncSlidePickerState();
|
||||
}
|
||||
|
||||
function updateRowOrder(markDirty) {
|
||||
@@ -530,6 +736,9 @@
|
||||
var row = document.createElement('tr');
|
||||
var rowKey = values.row_key || ('new-' + Date.now() + '-' + Math.random().toString(36).slice(2));
|
||||
var playlistId = tbody.getAttribute('data-playlist-id') || '';
|
||||
var thumbnailMarkup = values.thumbnail_path
|
||||
? '<img class="playlist-slide-thumb-image" src="' + values.thumbnail_path + '" alt="" loading="lazy" />'
|
||||
: '<span class="playlist-slide-thumb-placeholder"><i class="bi bi-image" aria-hidden="true"></i></span>';
|
||||
row.setAttribute('data-playlist-slide-row', '');
|
||||
row.setAttribute('data-row-key', rowKey);
|
||||
row.setAttribute('data-slide-id', String(values.slide_id));
|
||||
@@ -541,7 +750,12 @@
|
||||
'<span class="playlist-order-number"></span>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' + values.title + '<input type="hidden" name="slide_id[]" value="' + values.slide_id + '" form="playlist-edit-form" /></td>' +
|
||||
'<td data-label="Slide">' +
|
||||
'<div class="playlist-slide-cell">' +
|
||||
'<div class="playlist-slide-thumb" aria-hidden="true">' + thumbnailMarkup + '</div>' +
|
||||
'<div class="playlist-slide-cell-content"><span class="playlist-slide-title">' + values.title + '</span></div>' +
|
||||
'</div>' +
|
||||
'<input type="hidden" name="slide_id[]" value="' + values.slide_id + '" form="playlist-edit-form" /></td>' +
|
||||
'<td>' +
|
||||
'<div class="playlist-schedule-summary">' + values.summary + '</div>' +
|
||||
'<input type="hidden" name="schedule_mode[]" value="' + values.schedule_mode + '" form="playlist-edit-form" />' +
|
||||
@@ -551,7 +765,7 @@
|
||||
'<input type="hidden" name="schedule_end_time[]" value="' + values.schedule_end_time + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_days_json[]" value="' + values.schedule_days_json + '" form="playlist-edit-form" />' +
|
||||
'</td>' +
|
||||
'<td><input name="duration_seconds[]" type="number" min="1" value="' + values.duration_seconds + '" required form="playlist-edit-form" /></td>' +
|
||||
'<td><input name="duration_seconds[]" type="number" min="1" value="' + values.duration_seconds + '" required form="playlist-edit-form" class="form-control form-control-sm playlist-duration-input text-end" /></td>' +
|
||||
'<td><div class="actions playlist-item-actions">' +
|
||||
'<button type="button" class="btn btn-sm btn-primary" data-schedule-config="/playlists/' + encodeURIComponent(playlistId) + '/slides/0/config" data-schedule-config-row="' + rowKey + '">Schedule</button>' +
|
||||
'<button type="button" class="btn btn-sm btn-danger" data-playlist-remove-row>Remove</button>' +
|
||||
@@ -609,32 +823,37 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (addSlideButton && addSlideSelect) {
|
||||
addSlideButton.addEventListener('click', function () {
|
||||
var selectedOption = addSlideSelect.options[addSlideSelect.selectedIndex];
|
||||
if (!selectedOption || selectedOption.disabled) {
|
||||
if (addSlideModal && addSlideGrid) {
|
||||
renderSlidePicker();
|
||||
|
||||
addSlideGrid.addEventListener('click', function (event) {
|
||||
var card = event.target.closest('.playlist-slide-picker-card');
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
var placeholder = tbody.querySelector('.playlist-empty-row');
|
||||
if (placeholder) {
|
||||
placeholder.remove();
|
||||
}
|
||||
var row = createRow({
|
||||
row_key: 'new-' + Date.now(),
|
||||
slide_id: String(selectedOption.value),
|
||||
canvas_signature: String(selectedOption.getAttribute('data-canvas-signature') || ''),
|
||||
title: selectedOption.textContent || 'Slide',
|
||||
duration_seconds: 10,
|
||||
schedule_mode: 'always',
|
||||
schedule_start_datetime: '',
|
||||
schedule_end_datetime: '',
|
||||
schedule_start_time: '',
|
||||
schedule_end_time: '',
|
||||
schedule_days_json: '[]',
|
||||
summary: 'Always visible'
|
||||
event.preventDefault();
|
||||
toggleCardSelection(card);
|
||||
});
|
||||
|
||||
if (addSlideSearch) {
|
||||
addSlideSearch.addEventListener('input', syncSlidePickerState);
|
||||
}
|
||||
|
||||
if (addSlideConfirm) {
|
||||
addSlideConfirm.addEventListener('click', function () {
|
||||
addSelectedSlides();
|
||||
});
|
||||
tbody.appendChild(row);
|
||||
updateRowOrder(true);
|
||||
}
|
||||
|
||||
addSlideModal.addEventListener('shown.bs.modal', function () {
|
||||
syncSlidePickerState();
|
||||
if (addSlideSearch) {
|
||||
addSlideSearch.focus();
|
||||
}
|
||||
});
|
||||
|
||||
addSlideModal.addEventListener('hidden.bs.modal', function () {
|
||||
clearModalSelection();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,133 @@
|
||||
return;
|
||||
}
|
||||
|
||||
function getPageUrl(href) {
|
||||
try {
|
||||
return new URL(href, window.location.href);
|
||||
} catch (_error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateLocalDateTimes(root) {
|
||||
if (typeof window.initLocalDateTimes === 'function') {
|
||||
window.initLocalDateTimes(root || document);
|
||||
}
|
||||
}
|
||||
|
||||
function replaceSectionFromDocument(sectionName, responseDocument) {
|
||||
var currentCard = document.querySelector('[data-pagination-card="' + sectionName + '"]');
|
||||
var nextCard = responseDocument.querySelector('[data-pagination-card="' + sectionName + '"]');
|
||||
|
||||
if (!currentCard || !nextCard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentCard.outerHTML = nextCard.outerHTML;
|
||||
updateLocalDateTimes(document.querySelector('[data-pagination-card="' + sectionName + '"]'));
|
||||
return true;
|
||||
}
|
||||
|
||||
function replaceSummaryFromDocument(responseDocument) {
|
||||
var currentSummary = document.getElementById('background-tasks-summary');
|
||||
var nextSummary = responseDocument.getElementById('background-tasks-summary');
|
||||
|
||||
if (!currentSummary || !nextSummary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentSummary.outerHTML = nextSummary.outerHTML;
|
||||
return true;
|
||||
}
|
||||
|
||||
function replaceFiltersFromDocument(responseDocument) {
|
||||
var currentFilters = document.getElementById('background-tasks-filters');
|
||||
var nextFilters = responseDocument.getElementById('background-tasks-filters');
|
||||
|
||||
if (!currentFilters || !nextFilters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentFilters.outerHTML = nextFilters.outerHTML;
|
||||
return true;
|
||||
}
|
||||
|
||||
function replaceStateFromDocument(responseDocument) {
|
||||
var currentState = document.getElementById('background-tasks-state');
|
||||
var nextState = responseDocument.getElementById('background-tasks-state');
|
||||
|
||||
if (!currentState || !nextState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentState.outerHTML = nextState.outerHTML;
|
||||
stateNode = document.getElementById('background-tasks-state');
|
||||
currentVersion = stateNode ? String(stateNode.getAttribute('data-state-version') || '') : currentVersion;
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyPageResponse(responseDocument, sectionNames) {
|
||||
var updated = false;
|
||||
|
||||
if (replaceStateFromDocument(responseDocument)) {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (replaceSummaryFromDocument(responseDocument)) {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (replaceFiltersFromDocument(responseDocument)) {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
sectionNames.forEach(function (sectionName) {
|
||||
if (replaceSectionFromDocument(sectionName, responseDocument)) {
|
||||
updated = true;
|
||||
}
|
||||
});
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
function loadPage(nextUrl, options) {
|
||||
var url = getPageUrl(nextUrl);
|
||||
var sectionNames = options && options.sectionNames ? options.sectionNames : [];
|
||||
var replaceHistory = Boolean(options && options.replaceHistory);
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(url.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'text/html, application/xhtml+xml',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
cache: 'no-store'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('Unable to load background tasks page.');
|
||||
}
|
||||
return response.text();
|
||||
}).then(function (text) {
|
||||
var responseDocument = new DOMParser().parseFromString(text || '', 'text/html');
|
||||
if (!applyPageResponse(responseDocument, sectionNames.length ? sectionNames : ['tasks', 'recurring'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (replaceHistory) {
|
||||
window.history.replaceState({}, document.title, url.pathname + url.search + url.hash);
|
||||
} else {
|
||||
window.history.pushState({}, document.title, url.pathname + url.search + url.hash);
|
||||
}
|
||||
}).catch(function (_error) {
|
||||
window.location.assign(url.pathname + url.search + url.hash);
|
||||
});
|
||||
}
|
||||
|
||||
function stripMessageParameter() {
|
||||
try {
|
||||
var url = new URL(window.location.href);
|
||||
@@ -21,6 +148,100 @@
|
||||
}
|
||||
}
|
||||
|
||||
function findPaginationLink(target) {
|
||||
if (!target || !target.closest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var link = target.closest('.pagination .page-link');
|
||||
if (!link) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var href = String(link.getAttribute('href') || '').trim();
|
||||
if (!href || href === '#') {
|
||||
return null;
|
||||
}
|
||||
|
||||
var card = link.closest('[data-pagination-card]');
|
||||
if (!card) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
href: href,
|
||||
sectionName: String(card.getAttribute('data-pagination-card') || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function findFilterLink(target) {
|
||||
if (!target || !target.closest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var link = target.closest('a[data-background-tasks-nav]');
|
||||
if (!link) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var href = String(link.getAttribute('href') || '').trim();
|
||||
if (!href || href === '#') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
href: href
|
||||
};
|
||||
}
|
||||
|
||||
function initPaginationNavigation() {
|
||||
document.addEventListener('click', function (event) {
|
||||
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paginationLink = findPaginationLink(event.target);
|
||||
if (paginationLink && paginationLink.sectionName) {
|
||||
var currentUrl = getPageUrl(window.location.href);
|
||||
var nextUrl = getPageUrl(paginationLink.href);
|
||||
if (!currentUrl || !nextUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentUrl.pathname === nextUrl.pathname && currentUrl.search === nextUrl.search && currentUrl.hash === nextUrl.hash) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
loadPage(nextUrl.toString(), { sectionNames: [paginationLink.sectionName] });
|
||||
return;
|
||||
}
|
||||
|
||||
var filterLink = findFilterLink(event.target);
|
||||
if (!filterLink) {
|
||||
return;
|
||||
}
|
||||
|
||||
var filterUrl = getPageUrl(filterLink.href);
|
||||
if (!filterUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filterUrl.pathname === window.location.pathname && filterUrl.search === window.location.search && filterUrl.hash === window.location.hash) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
loadPage(filterUrl.toString(), { sectionNames: ['tasks'] });
|
||||
}, true);
|
||||
|
||||
window.addEventListener('popstate', function () {
|
||||
loadPage(window.location.href, { replaceHistory: true });
|
||||
});
|
||||
}
|
||||
|
||||
function refreshPage() {
|
||||
if (document.visibilityState !== 'visible') {
|
||||
return;
|
||||
@@ -59,5 +280,6 @@
|
||||
}
|
||||
|
||||
stripMessageParameter();
|
||||
initPaginationNavigation();
|
||||
window.setInterval(refreshPage, REFRESH_INTERVAL_MS);
|
||||
}());
|
||||
@@ -543,7 +543,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
if (!src) {
|
||||
return '<div class="slide-preview-placeholder">Webpage</div>';
|
||||
}
|
||||
return '<iframe class="slide-preview-webpage-frame" src="' + escapeHtml(src) + '" title="Webpage preview" loading="eager" referrerpolicy="no-referrer" scrolling="no"></iframe>';
|
||||
return '<iframe class="slide-preview-webpage-frame" src="' + escapeHtml(src) + '" title="Webpage preview" loading="eager" referrerpolicy="no-referrer" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"></iframe>';
|
||||
}
|
||||
|
||||
function renderPreviewHtmlRegion(value) {
|
||||
@@ -551,7 +551,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
if (!html) {
|
||||
return '<div class="slide-preview-placeholder">HTML</div>';
|
||||
}
|
||||
return '<iframe class="slide-preview-html-frame" sandbox="" scrolling="no" srcdoc="<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + escapeHtml(html) + '</body></html>" title="HTML preview" loading="eager"></iframe>';
|
||||
return '<iframe class="slide-preview-html-frame" sandbox="" scrolling="no" srcdoc="<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + escapeHtml(html) + '</body></html>" title="HTML preview" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
|
||||
}
|
||||
|
||||
function destroyEditors() {
|
||||
|
||||
@@ -67,6 +67,34 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
var sidebarTopOffset = 16;
|
||||
var templateSelectorLock = createTemplateSelectorLockController(templateSelect);
|
||||
|
||||
function applyBackdropStyle(element, backgroundColor, backgroundImagePath) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
var color = String(backgroundColor || '#111111').trim() || '#111111';
|
||||
element.style.backgroundColor = color;
|
||||
element.style.backgroundImage = backgroundImagePath
|
||||
? 'linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)), url("' + encodeURI(String(backgroundImagePath)) + '")'
|
||||
: 'linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42))';
|
||||
element.style.backgroundPosition = backgroundImagePath ? 'center, center' : 'center';
|
||||
element.style.backgroundSize = backgroundImagePath ? '100% 100%, cover' : '100% 100%';
|
||||
element.style.backgroundRepeat = backgroundImagePath ? 'no-repeat, no-repeat' : 'no-repeat';
|
||||
}
|
||||
|
||||
function applyPopupBackdropStyle(element, backgroundColor) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
var color = String(backgroundColor || '#111111').trim() || '#111111';
|
||||
element.style.backgroundColor = color;
|
||||
element.style.backgroundImage = 'linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42))';
|
||||
element.style.backgroundPosition = 'center';
|
||||
element.style.backgroundSize = '100% 100%';
|
||||
element.style.backgroundRepeat = 'no-repeat';
|
||||
}
|
||||
|
||||
class FontSizeInputUI extends Plugin {
|
||||
static get pluginName() {
|
||||
return 'FontSizeInputUI';
|
||||
@@ -408,7 +436,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
if (!src) {
|
||||
return '<div class="slide-preview-placeholder">Webpage</div>';
|
||||
}
|
||||
return '<iframe class="slide-preview-webpage-frame" src="' + escapeHtml(src) + '" title="Webpage preview" loading="eager" referrerpolicy="no-referrer" scrolling="no"></iframe>';
|
||||
return '<iframe class="slide-preview-webpage-frame" src="' + escapeHtml(src) + '" title="Webpage preview" loading="eager" referrerpolicy="no-referrer" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"></iframe>';
|
||||
}
|
||||
|
||||
function renderPreviewRtmpRegion(value, disableAudio) {
|
||||
@@ -425,7 +453,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
if (!html) {
|
||||
return '<div class="slide-preview-placeholder">HTML</div>';
|
||||
}
|
||||
return '<iframe class="slide-preview-html-frame" sandbox="" scrolling="no" srcdoc="<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + escapeHtml(html) + '</body></html>" title="HTML preview" loading="eager"></iframe>';
|
||||
return '<iframe class="slide-preview-html-frame" sandbox="" scrolling="no" srcdoc="<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + escapeHtml(html) + '</body></html>" title="HTML preview" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
|
||||
}
|
||||
|
||||
function destroyEditors() {
|
||||
@@ -814,7 +842,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
}
|
||||
|
||||
if (!template) {
|
||||
popupStage.style.backgroundColor = '#111111';
|
||||
applyBackdropStyle(popupCanvas, '#111111');
|
||||
popupCanvas.style.width = '';
|
||||
popupCanvas.style.height = '';
|
||||
popupCanvas.style.transform = '';
|
||||
@@ -837,7 +865,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
|
||||
var canvasWidth = currentPreviewCanvasWidth || Math.max(1, Number(template.canvas_size_width || 1920));
|
||||
var canvasHeight = currentPreviewCanvasHeight || Math.max(1, Number(template.canvas_size_height || 1080));
|
||||
popupStage.style.backgroundColor = template.background_color || '#111111';
|
||||
applyBackdropStyle(popupCanvas, template.background_color || '#111111', template.background_image_path);
|
||||
popupCanvas.style.width = canvasWidth + 'px';
|
||||
popupCanvas.style.height = canvasHeight + 'px';
|
||||
popupCanvas.innerHTML = slidePreviewCanvas.innerHTML;
|
||||
@@ -949,7 +977,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
slidePreviewOverlay.style.height = '';
|
||||
slidePreviewOverlay.style.transform = '';
|
||||
slidePreviewStage.style.aspectRatio = '16 / 9';
|
||||
slidePreviewStage.style.backgroundColor = '#111111';
|
||||
applyBackdropStyle(slidePreviewStage, '#111111');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -959,7 +987,7 @@ import { createTemplateSelectorLockController } from '/assets/js/slides/slide-fo
|
||||
currentPreviewCanvasWidth = canvasWidth;
|
||||
currentPreviewCanvasHeight = canvasHeight;
|
||||
slidePreviewStage.style.aspectRatio = canvasWidth + ' / ' + canvasHeight;
|
||||
slidePreviewStage.style.backgroundColor = template.background_color || '#111111';
|
||||
applyBackdropStyle(slidePreviewStage, template.background_color || '#111111', template.background_image_path);
|
||||
slidePreviewMeta.textContent = canvasWidth + 'x' + canvasHeight;
|
||||
if (!(template.regions || []).length) {
|
||||
slidePreviewEmpty.style.display = 'flex';
|
||||
|
||||
@@ -528,10 +528,10 @@
|
||||
}
|
||||
|
||||
function openAddRegionModal() {
|
||||
if (!regionAddModal || !window.bootstrap || !window.bootstrap.Modal) {
|
||||
if (!window.pulseModal) {
|
||||
return;
|
||||
}
|
||||
window.bootstrap.Modal.getOrCreateInstance(regionAddModal).show();
|
||||
window.pulseModal.show(regionAddModal);
|
||||
}
|
||||
|
||||
function createDefaultRegion(type) {
|
||||
@@ -701,8 +701,8 @@
|
||||
button.addEventListener('click', function () {
|
||||
var regionType = button.getAttribute('data-add-region-type');
|
||||
addRegion(createDefaultRegion(regionType));
|
||||
if (window.bootstrap && window.bootstrap.Modal) {
|
||||
window.bootstrap.Modal.getOrCreateInstance(regionAddModal).hide();
|
||||
if (window.pulseModal) {
|
||||
window.pulseModal.hide(regionAddModal);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user