Release v1.5.12
This commit is contained in:
@@ -1,6 +1,26 @@
|
||||
(function () {
|
||||
// Shared constants used across the playlist schedule editor.
|
||||
var DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
var VIDEO_DURATION_LABEL = 'Use video duration';
|
||||
var VIDEO_DURATION_ACTIVE_LABEL = 'Use Video Duration';
|
||||
var VIDEO_DURATION_LOADING_LABEL = 'Loading...';
|
||||
var PICKER_THUMB_EMPTY_LABEL = 'No thumbnail';
|
||||
var PICKER_ASSIGNED_BADGE_LABEL = 'Already in playlist';
|
||||
var PICKER_CARD_CLASS = 'playlist-slide-picker-card';
|
||||
var PICKER_MEDIA_CLASS = 'playlist-slide-picker-media';
|
||||
var PICKER_TITLE_CLASS = 'playlist-slide-picker-title';
|
||||
var PICKER_CHECK_CLASS = 'playlist-slide-picker-check bi bi-check2-circle';
|
||||
var PICKER_BADGE_CLASS = 'playlist-slide-picker-badge badge text-bg-primary';
|
||||
var SCHEDULE_ROW_FIELD_NAMES = {
|
||||
mode: '[name="schedule_mode[]"]',
|
||||
startDatetime: '[name="schedule_start_datetime[]"]',
|
||||
endDatetime: '[name="schedule_end_datetime[]"]',
|
||||
startTime: '[name="schedule_start_time[]"]',
|
||||
endTime: '[name="schedule_end_time[]"]',
|
||||
daysJson: '[name="schedule_days_json[]"]'
|
||||
};
|
||||
|
||||
// Schedule helpers.
|
||||
function formatDays(daysValue) {
|
||||
var days = [];
|
||||
if (Array.isArray(daysValue)) {
|
||||
@@ -21,6 +41,192 @@
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
function getScheduleRowFields(row) {
|
||||
return {
|
||||
mode: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.mode) : null,
|
||||
startDatetime: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.startDatetime) : null,
|
||||
endDatetime: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.endDatetime) : null,
|
||||
startTime: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.startTime) : null,
|
||||
endTime: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.endTime) : null,
|
||||
daysJson: row ? row.querySelector(SCHEDULE_ROW_FIELD_NAMES.daysJson) : null
|
||||
};
|
||||
}
|
||||
|
||||
// Video duration helpers.
|
||||
function buildVideoDurationButtonMarkup(isPressed, enabled) {
|
||||
if (!enabled) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '<button type="button" class="btn btn-outline-secondary btn-sm playlist-use-video-duration' + (isPressed ? ' active' : '') + '" data-use-video-duration-button aria-pressed="' + (isPressed ? 'true' : 'false') + '">' + (isPressed ? VIDEO_DURATION_ACTIVE_LABEL : VIDEO_DURATION_LABEL) + '</button>';
|
||||
}
|
||||
|
||||
function syncVideoDurationButtonState(button, isPressed, label) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.classList.toggle('active', Boolean(isPressed));
|
||||
button.setAttribute('aria-pressed', isPressed ? 'true' : 'false');
|
||||
button.textContent = label || (isPressed ? VIDEO_DURATION_ACTIVE_LABEL : VIDEO_DURATION_LABEL);
|
||||
}
|
||||
|
||||
// Slide picker helpers.
|
||||
function getSlidePickerEmptyMessage(query, showAssignedSlides, assignedCount, visibleCount) {
|
||||
if (!showAssignedSlides && assignedCount && visibleCount === 0) {
|
||||
return query ? 'No available slides match your search.' : 'No available slides are currently left for this playlist.';
|
||||
}
|
||||
|
||||
return query ? 'No slides match your search.' : 'No slides are currently available for this playlist.';
|
||||
}
|
||||
|
||||
function updateSlidePickerCardState(card, isAssigned, isCanvasMismatch) {
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
card.setAttribute('data-is-assigned', isAssigned ? 'true' : 'false');
|
||||
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');
|
||||
}
|
||||
|
||||
function createElementWithClass(tagName, className, textContent) {
|
||||
var element = document.createElement(tagName);
|
||||
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
if (textContent !== undefined) {
|
||||
element.textContent = textContent;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
function buildSlidePickerThumbPlaceholder() {
|
||||
var placeholder = createElementWithClass('div', 'playlist-slide-picker-thumb-placeholder');
|
||||
var icon = createElementWithClass('i', 'bi bi-image playlist-slide-picker-thumb-placeholder-icon');
|
||||
var label = createElementWithClass('span', 'playlist-slide-picker-thumb-placeholder-label', PICKER_THUMB_EMPTY_LABEL);
|
||||
|
||||
icon.setAttribute('aria-hidden', 'true');
|
||||
placeholder.appendChild(icon);
|
||||
placeholder.appendChild(label);
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
function buildSlidePickerThumbImage(slide) {
|
||||
var image = createElementWithClass('img', '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(buildSlidePickerThumbPlaceholder(), image);
|
||||
}
|
||||
});
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
function buildSlidePickerMedia(slide) {
|
||||
var media = createElementWithClass('div', PICKER_MEDIA_CLASS);
|
||||
var check = createElementWithClass('span', PICKER_CHECK_CLASS);
|
||||
var badge = createElementWithClass('span', PICKER_BADGE_CLASS, PICKER_ASSIGNED_BADGE_LABEL);
|
||||
|
||||
check.setAttribute('aria-hidden', 'true');
|
||||
badge.setAttribute('aria-hidden', 'true');
|
||||
|
||||
media.appendChild(slide && slide.thumbnail_path ? buildSlidePickerThumbImage(slide) : buildSlidePickerThumbPlaceholder());
|
||||
media.appendChild(check);
|
||||
media.appendChild(badge);
|
||||
|
||||
return media;
|
||||
}
|
||||
|
||||
// Playlist row helpers.
|
||||
function getPlaylistSlideRow(target) {
|
||||
return target && target.closest ? target.closest('tr[data-playlist-slide-row]') : null;
|
||||
}
|
||||
|
||||
function getPlaylistSlideRowKey(row) {
|
||||
return String(row && row.getAttribute('data-row-key') || '');
|
||||
}
|
||||
|
||||
function isDurationControl(target) {
|
||||
return Boolean(target && target.closest && target.closest('.playlist-use-video-duration'));
|
||||
}
|
||||
|
||||
function getDurationPointerState(target, type) {
|
||||
var row = getPlaylistSlideRow(target);
|
||||
var pointerType = type === 'button' ? 'button' : 'input';
|
||||
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: pointerType,
|
||||
rowKey: getPlaylistSlideRowKey(row)
|
||||
};
|
||||
}
|
||||
|
||||
function buildPlaylistOrderCellMarkup() {
|
||||
return '' +
|
||||
'<td class="playlist-order-cell" data-label="Order">' +
|
||||
'<div class="playlist-order-cell-inner">' +
|
||||
'<button type="button" class="playlist-drag-handle btn btn-link p-0 text-body-secondary" data-playlist-drag-handle aria-label="Drag to reorder" title="Drag to reorder"><span class="playlist-drag-handle-icon" aria-hidden="true"><svg class="playlist-drag-handle-svg" viewBox="0 0 24 32" focusable="false" aria-hidden="true"><polygon points="12,2 20,9 4,9"></polygon><rect x="4" y="14" width="16" height="4" rx="2"></rect><polygon points="4,23 20,23 12,30"></polygon></svg></span></button>' +
|
||||
'<span class="playlist-order-number"></span>' +
|
||||
'</div>' +
|
||||
'</td>';
|
||||
}
|
||||
|
||||
function buildPlaylistSlideCellMarkup(values, thumbnailStyle, thumbnailMarkup) {
|
||||
return '' +
|
||||
'<td data-label="Slide">' +
|
||||
'<div class="playlist-slide-cell">' +
|
||||
'<div class="playlist-slide-thumb" aria-hidden="true"' + thumbnailStyle + '>' + 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>';
|
||||
}
|
||||
|
||||
function buildPlaylistScheduleCellMarkup(values) {
|
||||
return '' +
|
||||
'<td>' +
|
||||
'<div class="playlist-schedule-summary">' + values.summary + '</div>' +
|
||||
'<input type="hidden" name="schedule_mode[]" value="' + values.schedule_mode + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_start_datetime[]" value="' + values.schedule_start_datetime + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_end_datetime[]" value="' + values.schedule_end_datetime + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_start_time[]" value="' + values.schedule_start_time + '" form="playlist-edit-form" />' +
|
||||
'<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>';
|
||||
}
|
||||
|
||||
function buildPlaylistDurationCellMarkup(values, durationActionMarkup) {
|
||||
return '' +
|
||||
'<td><div class="playlist-duration-field">' +
|
||||
'<input name="duration_seconds[]" type="text" inputmode="decimal" value="' + values.duration_seconds + '" required form="playlist-edit-form" class="form-control form-control-sm playlist-duration-input text-end"' + (values.useVideoDuration ? ' disabled' : '') + ' />' +
|
||||
'<input type="hidden" name="use_video_duration[]" value="' + (values.useVideoDuration ? '1' : '0') + '" form="playlist-edit-form" />' +
|
||||
(values.useVideoDuration ? '<input type="hidden" name="duration_seconds[]" value="' + values.duration_seconds + '" form="playlist-edit-form" data-video-duration-mirror />' : '') +
|
||||
durationActionMarkup +
|
||||
'</div></td>';
|
||||
}
|
||||
|
||||
function buildPlaylistActionsCellMarkup(playlistId, rowKey) {
|
||||
return '' +
|
||||
'<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>' +
|
||||
'</div></td>';
|
||||
}
|
||||
|
||||
// Schedule modal wiring.
|
||||
function initPlaylistScheduleModal() {
|
||||
var dialog = document.getElementById('slide-schedule-dialog');
|
||||
var content = document.getElementById('slide-schedule-content');
|
||||
@@ -38,14 +244,9 @@
|
||||
|
||||
function collectScheduleParams(row, rowKey) {
|
||||
var params = new URLSearchParams();
|
||||
var scheduleFields = [
|
||||
'schedule_mode[]',
|
||||
'schedule_start_datetime[]',
|
||||
'schedule_end_datetime[]',
|
||||
'schedule_start_time[]',
|
||||
'schedule_end_time[]',
|
||||
'schedule_days_json[]'
|
||||
];
|
||||
var scheduleFields = Object.keys(SCHEDULE_ROW_FIELD_NAMES).map(function (key) {
|
||||
return SCHEDULE_ROW_FIELD_NAMES[key].replace(/^\[name="|"\]$/g, '');
|
||||
});
|
||||
|
||||
params.set('row_key', String(rowKey || ''));
|
||||
scheduleFields.forEach(function (fieldName) {
|
||||
@@ -113,6 +314,7 @@
|
||||
|
||||
}
|
||||
|
||||
// Schedule form behavior.
|
||||
function scheduleSummary(values) {
|
||||
var mode = String(values.schedule_mode || 'always');
|
||||
if (mode === 'dates') {
|
||||
@@ -214,10 +416,6 @@
|
||||
});
|
||||
select.addEventListener('change', clearScheduleValidity);
|
||||
|
||||
function notifyParentResize() {
|
||||
return;
|
||||
}
|
||||
|
||||
function updateVisibility() {
|
||||
var mode = select.value;
|
||||
if (datesPanel) {
|
||||
@@ -234,9 +432,6 @@
|
||||
endTimeInput.value = DEFAULT_END_TIME;
|
||||
}
|
||||
}
|
||||
window.requestAnimationFrame(function () {
|
||||
notifyParentResize();
|
||||
});
|
||||
}
|
||||
|
||||
select.addEventListener('change', updateVisibility);
|
||||
@@ -302,6 +497,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Playlist editor behavior.
|
||||
function initPlaylistEditStaging() {
|
||||
var tbody = document.getElementById('playlist-items-body');
|
||||
var form = document.getElementById('playlist-edit-form');
|
||||
@@ -334,13 +530,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getModalInstance() {
|
||||
if (!window.pulseModal) {
|
||||
return null;
|
||||
}
|
||||
return window.pulseModal.getOrCreate(addSlideModal);
|
||||
}
|
||||
|
||||
// Selection state helpers for the picker modal.
|
||||
function setCardSelected(card, selected) {
|
||||
if (!card) {
|
||||
return;
|
||||
@@ -373,50 +563,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// Picker card rendering.
|
||||
function createPickerCard(slide) {
|
||||
var button = document.createElement('button');
|
||||
var media = document.createElement('div');
|
||||
var title = document.createElement('div');
|
||||
var check = document.createElement('span');
|
||||
var badge = document.createElement('span');
|
||||
var searchText = String(slide && slide.title ? slide.title : '').toLowerCase();
|
||||
|
||||
button.type = 'button';
|
||||
button.className = 'playlist-slide-picker-card';
|
||||
button.className = PICKER_CARD_CLASS;
|
||||
button.setAttribute('aria-pressed', 'false');
|
||||
button.setAttribute('data-slide-id', String(slide.id || ''));
|
||||
button.setAttribute('data-search-text', searchText);
|
||||
@@ -427,18 +580,9 @@
|
||||
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);
|
||||
|
||||
badge.className = 'playlist-slide-picker-badge badge text-bg-primary';
|
||||
badge.textContent = 'Already in playlist';
|
||||
badge.setAttribute('aria-hidden', 'true');
|
||||
media.appendChild(badge);
|
||||
|
||||
title.className = 'playlist-slide-picker-title';
|
||||
var media = buildSlidePickerMedia(slide);
|
||||
var title = document.createElement('div');
|
||||
title.className = PICKER_TITLE_CLASS;
|
||||
title.textContent = String(slide && slide.title ? slide.title : 'Slide');
|
||||
|
||||
button.appendChild(media);
|
||||
@@ -447,6 +591,7 @@
|
||||
return button;
|
||||
}
|
||||
|
||||
// Picker filtering and modal controls.
|
||||
function setShowAssignedState(showAssignedSlides) {
|
||||
if (!addSlideShowAssigned) {
|
||||
return;
|
||||
@@ -492,11 +637,7 @@
|
||||
});
|
||||
|
||||
if (addSlideEmpty) {
|
||||
if (!showAssignedSlides && assignedCount && visibleCount === 0) {
|
||||
addSlideEmpty.textContent = query ? 'No available slides match your search.' : 'No available slides are currently left for this playlist.';
|
||||
} else {
|
||||
addSlideEmpty.textContent = query ? 'No slides match your search.' : 'No slides are currently available for this playlist.';
|
||||
}
|
||||
addSlideEmpty.textContent = getSlidePickerEmptyMessage(query, showAssignedSlides, assignedCount, visibleCount);
|
||||
addSlideEmpty.classList.toggle('is-hidden', visibleCount !== 0);
|
||||
}
|
||||
|
||||
@@ -615,24 +756,19 @@
|
||||
}
|
||||
|
||||
function scheduleSummaryForRow(row) {
|
||||
var modeInput = row.querySelector('[name="schedule_mode[]"]');
|
||||
var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]');
|
||||
var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]');
|
||||
var startTime = row.querySelector('[name="schedule_start_time[]"]');
|
||||
var endTime = row.querySelector('[name="schedule_end_time[]"]');
|
||||
var daysJson = row.querySelector('[name="schedule_days_json[]"]');
|
||||
var mode = String(modeInput ? modeInput.value : 'always');
|
||||
var days = formatDays(daysJson && daysJson.value ? daysJson.value : '[]');
|
||||
var fields = getScheduleRowFields(row);
|
||||
var mode = String(fields.mode ? fields.mode.value : 'always');
|
||||
var days = formatDays(fields.daysJson && fields.daysJson.value ? fields.daysJson.value : '[]');
|
||||
|
||||
if (mode === 'dates') {
|
||||
if (startDatetime && endDatetime && startDatetime.value && endDatetime.value) {
|
||||
return 'Dates: ' + startDatetime.value.replace('T', ' ') + ' to ' + endDatetime.value.replace('T', ' ');
|
||||
if (fields.startDatetime && fields.endDatetime && fields.startDatetime.value && fields.endDatetime.value) {
|
||||
return 'Dates: ' + fields.startDatetime.value.replace('T', ' ') + ' to ' + fields.endDatetime.value.replace('T', ' ');
|
||||
}
|
||||
return 'Dates: not set';
|
||||
}
|
||||
if (mode === 'times') {
|
||||
if (days && startTime && endTime && startTime.value && endTime.value) {
|
||||
return 'Times: ' + days + ' ' + startTime.value.slice(0, 5) + '-' + endTime.value.slice(0, 5);
|
||||
if (days && fields.startTime && fields.endTime && fields.startTime.value && fields.endTime.value) {
|
||||
return 'Times: ' + days + ' ' + fields.startTime.value.slice(0, 5) + '-' + fields.endTime.value.slice(0, 5);
|
||||
}
|
||||
return 'Times: not set';
|
||||
}
|
||||
@@ -670,14 +806,7 @@
|
||||
|
||||
slide.isAssigned = isAssigned;
|
||||
|
||||
if (card) {
|
||||
card.setAttribute('data-is-assigned', isAssigned ? 'true' : 'false');
|
||||
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');
|
||||
}
|
||||
updateSlidePickerCardState(card, isAssigned, isCanvasMismatch);
|
||||
if (isAssigned) {
|
||||
slidePickerSelection.delete(slideId);
|
||||
if (card) {
|
||||
@@ -745,31 +874,26 @@
|
||||
}
|
||||
|
||||
function setRowSchedule(row, values) {
|
||||
var modeInput = row.querySelector('[name="schedule_mode[]"]');
|
||||
var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]');
|
||||
var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]');
|
||||
var startTime = row.querySelector('[name="schedule_start_time[]"]');
|
||||
var endTime = row.querySelector('[name="schedule_end_time[]"]');
|
||||
var daysJson = row.querySelector('[name="schedule_days_json[]"]');
|
||||
var fields = getScheduleRowFields(row);
|
||||
var summary = row.querySelector('.playlist-schedule-summary');
|
||||
|
||||
if (modeInput) {
|
||||
modeInput.value = values.schedule_mode || 'always';
|
||||
if (fields.mode) {
|
||||
fields.mode.value = values.schedule_mode || 'always';
|
||||
}
|
||||
if (startDatetime) {
|
||||
startDatetime.value = values.schedule_start_datetime || '';
|
||||
if (fields.startDatetime) {
|
||||
fields.startDatetime.value = values.schedule_start_datetime || '';
|
||||
}
|
||||
if (endDatetime) {
|
||||
endDatetime.value = values.schedule_end_datetime || '';
|
||||
if (fields.endDatetime) {
|
||||
fields.endDatetime.value = values.schedule_end_datetime || '';
|
||||
}
|
||||
if (startTime) {
|
||||
startTime.value = values.schedule_start_time || '';
|
||||
if (fields.startTime) {
|
||||
fields.startTime.value = values.schedule_start_time || '';
|
||||
}
|
||||
if (endTime) {
|
||||
endTime.value = values.schedule_end_time || '';
|
||||
if (fields.endTime) {
|
||||
fields.endTime.value = values.schedule_end_time || '';
|
||||
}
|
||||
if (daysJson) {
|
||||
daysJson.value = values.schedule_days_json || '[]';
|
||||
if (fields.daysJson) {
|
||||
fields.daysJson.value = values.schedule_days_json || '[]';
|
||||
}
|
||||
if (summary) {
|
||||
summary.textContent = values.summary || scheduleSummaryForRow(row);
|
||||
@@ -799,40 +923,16 @@
|
||||
row.setAttribute('data-video-source-path', String(values.videoSourcePath || values.media_path || ''));
|
||||
row.setAttribute('data-video-duration-seconds', String(values.videoDurationSeconds || ''));
|
||||
row.setAttribute('data-use-video-duration', String(Boolean(values.useVideoDuration)));
|
||||
var durationActionMarkup = Boolean(values.showVideoDurationButton) || String(values.media_type || '').trim() === 'video'
|
||||
? '<button type="button" class="btn btn-outline-secondary btn-sm playlist-use-video-duration' + (values.useVideoDuration ? ' active' : '') + '" data-use-video-duration-button aria-pressed="' + (values.useVideoDuration ? 'true' : 'false') + '">' + (values.useVideoDuration ? 'Use Video Duration' : 'Use video duration') + '</button>'
|
||||
: '';
|
||||
var durationActionMarkup = buildVideoDurationButtonMarkup(
|
||||
Boolean(values.useVideoDuration),
|
||||
Boolean(values.showVideoDurationButton) || String(values.media_type || '').trim() === 'video'
|
||||
);
|
||||
row.innerHTML = '' +
|
||||
'<td class="playlist-order-cell" data-label="Order">' +
|
||||
'<div class="playlist-order-cell-inner">' +
|
||||
'<button type="button" class="playlist-drag-handle btn btn-link p-0 text-body-secondary" data-playlist-drag-handle aria-label="Drag to reorder" title="Drag to reorder"><span class="playlist-drag-handle-icon" aria-hidden="true"><svg class="playlist-drag-handle-svg" viewBox="0 0 24 32" focusable="false" aria-hidden="true"><polygon points="12,2 20,9 4,9"></polygon><rect x="4" y="14" width="16" height="4" rx="2"></rect><polygon points="4,23 20,23 12,30"></polygon></svg></span></button>' +
|
||||
'<span class="playlist-order-number"></span>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td data-label="Slide">' +
|
||||
'<div class="playlist-slide-cell">' +
|
||||
'<div class="playlist-slide-thumb" aria-hidden="true"' + thumbnailStyle + '>' + 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" />' +
|
||||
'<input type="hidden" name="schedule_start_datetime[]" value="' + values.schedule_start_datetime + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_end_datetime[]" value="' + values.schedule_end_datetime + '" form="playlist-edit-form" />' +
|
||||
'<input type="hidden" name="schedule_start_time[]" value="' + values.schedule_start_time + '" form="playlist-edit-form" />' +
|
||||
'<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><div class="playlist-duration-field"><input name="duration_seconds[]" type="text" inputmode="decimal" value="' + values.duration_seconds + '" required form="playlist-edit-form" class="form-control form-control-sm playlist-duration-input text-end"' + (values.useVideoDuration ? ' disabled' : '') + ' />' +
|
||||
'<input type="hidden" name="use_video_duration[]" value="' + (values.useVideoDuration ? '1' : '0') + '" form="playlist-edit-form" />' +
|
||||
(values.useVideoDuration ? '<input type="hidden" name="duration_seconds[]" value="' + values.duration_seconds + '" form="playlist-edit-form" data-video-duration-mirror />' : '') +
|
||||
durationActionMarkup +
|
||||
'</div></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>' +
|
||||
'</div></td>';
|
||||
buildPlaylistOrderCellMarkup() +
|
||||
buildPlaylistSlideCellMarkup(values, thumbnailStyle, thumbnailMarkup) +
|
||||
buildPlaylistScheduleCellMarkup(values) +
|
||||
buildPlaylistDurationCellMarkup(values, durationActionMarkup) +
|
||||
buildPlaylistActionsCellMarkup(playlistId, rowKey);
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -919,11 +1019,7 @@
|
||||
row.setAttribute('data-video-duration-previous-value', String(input.value || ''));
|
||||
}
|
||||
|
||||
if (button) {
|
||||
button.classList.toggle('active', Boolean(isPressed));
|
||||
button.setAttribute('aria-pressed', isPressed ? 'true' : 'false');
|
||||
button.textContent = isPressed ? 'Use Video Duration' : 'Use video duration';
|
||||
}
|
||||
syncVideoDurationButtonState(button, isPressed);
|
||||
|
||||
if (input) {
|
||||
if (!isPressed && row) {
|
||||
@@ -997,7 +1093,7 @@
|
||||
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.textContent = 'Loading...';
|
||||
button.textContent = VIDEO_DURATION_LOADING_LABEL;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1018,7 +1114,7 @@
|
||||
} finally {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.textContent = button.getAttribute('aria-pressed') === 'true' ? 'Use Video Duration' : 'Use video duration';
|
||||
syncVideoDurationButtonState(button, button.getAttribute('aria-pressed') === 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,7 +1128,7 @@
|
||||
var removeButton = event.target.closest('[data-playlist-remove-row]');
|
||||
var scheduleButton = event.target.closest('[data-schedule-config]');
|
||||
var durationButton = event.target.closest('[data-use-video-duration]');
|
||||
var row = event.target.closest('tr[data-playlist-slide-row]');
|
||||
var row = getPlaylistSlideRow(event.target);
|
||||
|
||||
if (!row) {
|
||||
return;
|
||||
@@ -1048,13 +1144,13 @@
|
||||
if (scheduleButton) {
|
||||
event.preventDefault();
|
||||
if (typeof window.openScheduleModal === 'function') {
|
||||
window.openScheduleModal(scheduleButton.getAttribute('data-schedule-config') + '?row_key=' + encodeURIComponent(row.getAttribute('data-row-key') || ''));
|
||||
window.openScheduleModal(scheduleButton.getAttribute('data-schedule-config') + '?row_key=' + encodeURIComponent(getPlaylistSlideRowKey(row)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (durationButton) {
|
||||
var rowKey = row.getAttribute('data-row-key');
|
||||
var rowKey = getPlaylistSlideRowKey(row);
|
||||
var pressedOnInput = lastDurationPointerDown && lastDurationPointerDown.type === 'input' && lastDurationPointerDown.rowKey === rowKey;
|
||||
var releasedOnButton = lastDurationPointerUp && lastDurationPointerUp.type === 'button' && lastDurationPointerUp.rowKey === rowKey;
|
||||
|
||||
@@ -1074,29 +1170,23 @@
|
||||
});
|
||||
|
||||
tbody.addEventListener('mousedown', function (event) {
|
||||
var row = event.target && event.target.closest ? event.target.closest('tr[data-playlist-slide-row]') : null;
|
||||
var row = getPlaylistSlideRow(event.target);
|
||||
var dragHandle = event.target && event.target.closest ? event.target.closest('[data-playlist-drag-handle]') : null;
|
||||
var durationInput = event.target && event.target.matches('[name="duration_seconds[]"]') ? event.target : null;
|
||||
var durationToggle = event.target && event.target.closest ? event.target.closest('.playlist-use-video-duration') : null;
|
||||
var durationToggle = isDurationControl(event.target) ? event.target.closest('.playlist-use-video-duration') : null;
|
||||
|
||||
if (row && !dragHandle) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (durationInput) {
|
||||
lastDurationPointerDown = {
|
||||
type: 'input',
|
||||
rowKey: String(event.target.closest('tr[data-playlist-slide-row]') && event.target.closest('tr[data-playlist-slide-row]').getAttribute('data-row-key') || '')
|
||||
};
|
||||
lastDurationPointerDown = getDurationPointerState(event.target, 'input');
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (durationToggle) {
|
||||
lastDurationPointerDown = {
|
||||
type: 'button',
|
||||
rowKey: String(durationToggle.closest('tr[data-playlist-slide-row]') && durationToggle.closest('tr[data-playlist-slide-row]').getAttribute('data-row-key') || '')
|
||||
};
|
||||
lastDurationPointerDown = getDurationPointerState(durationToggle, 'button');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1105,22 +1195,16 @@
|
||||
|
||||
tbody.addEventListener('mouseup', function (event) {
|
||||
var durationInput = event.target && event.target.matches('[name="duration_seconds[]"]') ? event.target : null;
|
||||
var durationToggle = event.target && event.target.closest ? event.target.closest('.playlist-use-video-duration') : null;
|
||||
var row = event.target && event.target.closest ? event.target.closest('tr[data-playlist-slide-row]') : null;
|
||||
var durationToggle = isDurationControl(event.target) ? event.target.closest('.playlist-use-video-duration') : null;
|
||||
var row = getPlaylistSlideRow(event.target);
|
||||
|
||||
if (durationInput) {
|
||||
lastDurationPointerUp = {
|
||||
type: 'input',
|
||||
rowKey: String(row && row.getAttribute('data-row-key') || '')
|
||||
};
|
||||
lastDurationPointerUp = getDurationPointerState(row, 'input');
|
||||
return;
|
||||
}
|
||||
|
||||
if (durationToggle) {
|
||||
lastDurationPointerUp = {
|
||||
type: 'button',
|
||||
rowKey: String(row && row.getAttribute('data-row-key') || '')
|
||||
};
|
||||
lastDurationPointerUp = getDurationPointerState(row, 'button');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1129,7 +1213,7 @@
|
||||
|
||||
tbody.addEventListener('input', function (event) {
|
||||
if (event.target && event.target.matches('[name="duration_seconds[]"]')) {
|
||||
var editedRow = event.target.closest('tr[data-playlist-slide-row]');
|
||||
var editedRow = getPlaylistSlideRow(event.target);
|
||||
var editedButton = editedRow ? editedRow.querySelector('.playlist-use-video-duration') : null;
|
||||
var editedMirror = editedRow ? editedRow.querySelector('[data-video-duration-mirror]') : null;
|
||||
var useVideoDuration = Boolean(editedButton && editedButton.getAttribute('aria-pressed') === 'true');
|
||||
|
||||
Reference in New Issue
Block a user