Compare commits

..
4 Commits
Author SHA1 Message Date
lzstealth 7bd4a792ee Merge remote-tracking branch 'Gitea_SSH/main'
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m50s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s
# Conflicts:
#	CHANGELOG.md
#	build/package.player.json
#	build/package.web.json
#	package-lock.json
#	package.json
2026-08-18 02:25:15 +01:00
lzstealth e1e759f64e Release 2.8.6 2026-08-18 02:23:42 +01:00
lzstealth f071c21219 Release 2.8.5
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m15s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s
2026-08-17 02:15:18 +01:00
lzstealth e984f36875 Release 2.8.5
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m53s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s
2026-08-17 02:07:48 +01:00
15 changed files with 204 additions and 81 deletions
+15
View File
@@ -2,6 +2,21 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 2.8.6 - 2026-08-18
### Fixed
- Added live URL validation with inline feedback and explicit HTTP or HTTPS scheme enforcement for URL fields.
- Prevented Enter in slide editor inputs from implicitly saving the slide.
- Collapsed nested API response JSON sections by default while keeping the root response visible.
## 2.8.5 - 2026-08-17
### Fixed
- Fixed thumbnail capture timing so video assets are ready before the preview canvas is captured.
- Replaced unavailable webpage thumbnails with a subdued placeholder while keeping live webpage previews intact.
## 2.8.4 - 2026-08-17 ## 2.8.4 - 2026-08-17
### Added ### Added
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage-player", "name": "pulse-signage-player",
"version": "2.8.4", "version": "2.8.6",
"private": false, "private": false,
"description": "Pulse Signage player application bundle", "description": "Pulse Signage player application bundle",
"engines": { "engines": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage-web", "name": "pulse-signage-web",
"version": "2.8.4", "version": "2.8.6",
"private": false, "private": false,
"description": "Pulse Signage web and bridge application bundle", "description": "Pulse Signage web and bridge application bundle",
"engines": { "engines": {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.8.4", "version": "2.8.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.8.4", "version": "2.8.6",
"dependencies": { "dependencies": {
"@sparticuz/chromium": "^149.0.0", "@sparticuz/chromium": "^149.0.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.8.4", "version": "2.8.6",
"private": false, "private": false,
"description": "Pulse Signage application with MySQL and media storage", "description": "Pulse Signage application with MySQL and media storage",
"engines": { "engines": {
+5 -2
View File
@@ -32,7 +32,7 @@ function buildHtmlDocument(html) {
return raw; return raw;
} }
return '<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + raw + '</body></html>'; return '<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}html,body{background:transparent !important;}</style></head><body>' + raw + '</body></html>';
} }
function renderHtmlRegionContent(value) { function renderHtmlRegionContent(value) {
@@ -40,7 +40,10 @@ function renderHtmlRegionContent(value) {
if (!html) { if (!html) {
return ''; return '';
} }
return '<iframe class="template-region-html-frame" sandbox="" scrolling="no" srcdoc="' + escapeHtml(buildHtmlDocument(html)) + '" title="HTML region" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>'; if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
return '<iframe class="template-region-html-frame" sandbox="" allowtransparency="true" scrolling="no" srcdoc="' + escapeHtml(html) + '" title="HTML region" loading="eager" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe>';
}
return '<div class="template-region-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div>';
} }
function renderHtmlRegion(region, regionContent) { function renderHtmlRegion(region, regionContent) {
+7 -3
View File
@@ -142,9 +142,13 @@ function buildRegionInnerHtml(region, regionContent, baseUrl) {
if (regionType === 'html') { if (regionType === 'html') {
const html = String(rawValue || '').trim(); const html = String(rawValue || '').trim();
return html if (!html) {
? '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml('<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + html + '</body></html>') + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>' return '';
: ''; }
if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml(html) + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>';
}
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><div class="slide-preview-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div></div>';
} }
if (regionType === 'rtmp') { if (regionType === 'rtmp') {
+42 -37
View File
@@ -203,10 +203,7 @@ function buildRegionInnerHtml(region, regionContent, baseUrl, options) {
} }
if (regionType === 'webpage') { if (regionType === 'webpage') {
const src = resolveAssetUrl(baseUrl, rawValue); return '<div class="slide-preview-region slide-preview-webpage-region" style="' + region.baseStyle + '"><div class="slide-preview-webpage-placeholder" style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.08);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,0.14);box-sizing:border-box;color:rgba(255,255,255,0.72);font-size:24px;font-family:Arial,sans-serif;">Webpage preview unavailable</div></div>';
return src
? '<div class="slide-preview-region slide-preview-webpage-region" style="' + region.baseStyle + '"><iframe src="' + escapeHtml(src) + '" title="' + escapeHtml(region.label || region.region_key || 'webpage') + '" loading="eager" referrerpolicy="no-referrer" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"></iframe></div>'
: '';
} }
if (regionType === 'qr-code') { if (regionType === 'qr-code') {
@@ -220,9 +217,13 @@ function buildRegionInnerHtml(region, regionContent, baseUrl, options) {
if (regionType === 'html') { if (regionType === 'html') {
const html = String(rawValue || '').trim(); const html = String(rawValue || '').trim();
return html if (!html) {
? '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml('<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body>' + html + '</body></html>') + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>' return '';
: ''; }
if (/^<!doctype\b/i.test(html) || /^<html\b/i.test(html)) {
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><iframe sandbox="" allowtransparency="true" srcdoc="' + escapeHtml(html) + '" title="' + escapeHtml(region.label || region.region_key || 'html') + '" loading="eager" scrolling="no" style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"></iframe></div>';
}
return '<div class="slide-preview-region slide-preview-html-region" style="' + region.baseStyle + '"><div class="slide-preview-html-content" style="width:100%;height:100%;overflow:hidden;background:transparent;">' + html + '</div></div>';
} }
if (regionType === 'rtmp') { if (regionType === 'rtmp') {
@@ -301,8 +302,7 @@ async function launchBrowser() {
args: [ args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
'--disable-dev-shm-usage', '--disable-dev-shm-usage'
'--disable-gpu'
], ],
defaultViewport: { width: 1920, height: 1080, deviceScaleFactor: 1 }, defaultViewport: { width: 1920, height: 1080, deviceScaleFactor: 1 },
executablePath: executablePath, executablePath: executablePath,
@@ -401,14 +401,9 @@ async function captureSlideThumbnail(options) {
}, { timeout: 30000 }); }, { timeout: 30000 });
await page.waitForFunction(function () { await page.waitForFunction(function () {
var canvas = document.querySelector('#popup-preview-canvas'); var videos = Array.prototype.slice.call(document.querySelectorAll('#popup-preview-canvas video'));
if (!canvas) { return videos.every(function (video) {
return false; return video.readyState >= 2;
}
var images = Array.prototype.slice.call(canvas.querySelectorAll('img'));
return images.every(function (image) {
return image.complete && typeof image.naturalWidth === 'number';
}); });
}, { timeout: 30000 }); }, { timeout: 30000 });
@@ -417,17 +412,13 @@ async function captureSlideThumbnail(options) {
try { try {
await document.fonts.ready; await document.fonts.ready;
} catch (_error) { } catch (_error) {
// Ignore font readiness failures and fall back to the rendered frame. return null;
} }
} }
}); });
await page.evaluate(function () { await new Promise(function (resolve) {
return new Promise(function (resolve) { setTimeout(resolve, 1000);
window.requestAnimationFrame(function () {
window.requestAnimationFrame(resolve);
});
});
}); });
} }
@@ -435,19 +426,14 @@ async function captureSlideThumbnail(options) {
try { try {
const page = await browser.newPage(); const page = await browser.newPage();
try { try {
const previewPayload = buildThumbnailPreviewPayload(slide, { const previewPath = '/api/internal/slide-thumbnails/' + encodeURIComponent(String(slide.id)) + '/popup-preview';
baseUrl: baseUrl, const previewUrl = baseUrl + previewPath;
fontStylesheetHref: options && options.fontStylesheetHref ? options.fontStylesheetHref : '', const canvasSize = getThumbnailCanvasSize(slide);
getItem: getThumbnailItem, await page.setViewport({
getCachedImagePath: getCachedImagePath width: Math.max(1, Number(canvasSize.width || PLAYER_VIEWPORT.width)),
height: Math.max(1, Number(canvasSize.height || PLAYER_VIEWPORT.height)),
deviceScaleFactor: 1
}); });
const previewPath = '/slides/popup-preview';
const previewUrl = baseUrl + previewPath + '#' + encodeURIComponent(JSON.stringify(previewPayload));
await page.setViewport({
width: Math.max(1, Number(previewPayload.canvasWidth || PLAYER_VIEWPORT.width)),
height: Math.max(1, Number(previewPayload.canvasHeight || PLAYER_VIEWPORT.height)),
deviceScaleFactor: 1
});
await page.setExtraHTTPHeaders(Object.assign({}, createRequestAuthHeaders({ await page.setExtraHTTPHeaders(Object.assign({}, createRequestAuthHeaders({
method: 'GET', method: 'GET',
pathname: previewPath pathname: previewPath
@@ -463,7 +449,26 @@ async function captureSlideThumbnail(options) {
if (!canvas) { if (!canvas) {
throw new Error('Popup preview did not produce a slide canvas.'); throw new Error('Popup preview did not produce a slide canvas.');
} }
await canvas.screenshot({ path: fullSizePath }); await page.evaluate(function () {
var canvasElement = document.querySelector('#popup-preview-canvas');
if (canvasElement) {
canvasElement.style.transform = 'none';
canvasElement.style.transformOrigin = 'top left';
}
});
const canvasBounds = await canvas.boundingBox();
if (!canvasBounds) {
throw new Error('Popup preview canvas has no screenshot bounds.');
}
await page.screenshot({
path: fullSizePath,
clip: {
x: Math.max(0, canvasBounds.x),
y: Math.max(0, canvasBounds.y),
width: Math.max(1, canvasBounds.width),
height: Math.max(1, canvasBounds.height)
}
});
} finally { } finally {
await page.close().catch(function () { await page.close().catch(function () {
return null; return null;
+5
View File
@@ -2383,6 +2383,11 @@ html[data-bs-theme='dark'] .template-field-card .tox .tox-collection {
margin-bottom: 0; margin-bottom: 0;
} }
.template-field-card .form-label,
.region-item .form-label {
margin-bottom: 0.35rem;
}
.template-field-card .form-control, .template-field-card .form-control,
.template-field-card .form-select, .template-field-card .form-select,
.template-field-card textarea, .template-field-card textarea,
+14 -10
View File
@@ -886,7 +886,7 @@
var summaryLabel = isRoot ? (isArray ? 'Array' : 'Object') : String(key); var summaryLabel = isRoot ? (isArray ? 'Array' : 'Object') : String(key);
var summaryMeta = isArray ? '[' + value.length + ']' : '{' + Object.keys(value).length + '}'; var summaryMeta = isArray ? '[' + value.length + ']' : '{' + Object.keys(value).length + '}';
return '' + return '' +
'<details class="json-tree-node" open>' + '<details class="json-tree-node"' + (isRoot ? ' open' : '') + '>' +
'<summary><span class="json-tree-key">' + escapeHtml(summaryLabel) + '</span><span class="mx-1">:</span><span class="text-body-secondary">' + escapeHtml(summaryMeta) + '</span></summary>' + '<summary><span class="json-tree-key">' + escapeHtml(summaryLabel) + '</span><span class="mx-1">:</span><span class="text-body-secondary">' + escapeHtml(summaryMeta) + '</span></summary>' +
'<div class="ms-3 ps-3 border-start">' + entries + '</div>' + '<div class="ms-3 ps-3 border-start">' + entries + '</div>' +
'</details>'; '</details>';
@@ -945,26 +945,30 @@
detail.open = expanded; detail.open = expanded;
}); });
if (collapseButton) { syncButtons();
collapseButton.disabled = !details.length || !expanded;
}
if (expandButton) {
expandButton.disabled = !details.length || expanded;
}
} }
function syncButtons() { function syncButtons() {
var details = output.querySelectorAll('details');
var allExpanded = details.length > 0 && Array.prototype.every.call(details, function (detail) { return detail.open; });
var allCollapsed = details.length > 0 && Array.prototype.every.call(details, function (detail) { return !detail.open; });
if (collapseButton) { if (collapseButton) {
collapseButton.setAttribute('aria-pressed', 'false'); collapseButton.disabled = !details.length || allCollapsed;
collapseButton.setAttribute('aria-pressed', String(allCollapsed));
} }
if (expandButton) { if (expandButton) {
expandButton.setAttribute('aria-pressed', 'true'); expandButton.disabled = !details.length || allExpanded;
expandButton.setAttribute('aria-pressed', String(allExpanded));
} }
} }
output.innerHTML = formattedTree; output.innerHTML = formattedTree;
setAllSectionsExpanded(false);
var rootDetails = output.querySelector('details');
if (rootDetails) {
rootDetails.open = true;
}
syncButtons(); syncButtons();
setAllSectionsExpanded(true);
if (collapseButton) { if (collapseButton) {
collapseButton.addEventListener('click', function () { collapseButton.addEventListener('click', function () {
+1 -1
View File
@@ -1029,7 +1029,7 @@
'</div>' + '</div>' +
'<div class="card-body p-3 d-grid gap-2">' + '<div class="card-body p-3 d-grid gap-2">' +
'<label class="form-label mb-1" for="region_qr_code_' + region.id + '">URL</label>' + '<label class="form-label mb-1" for="region_qr_code_' + region.id + '">URL</label>' +
'<input id="region_qr_code_' + region.id + '" type="url" name="region_qr_code_' + region.id + '" class="form-control" value="' + escapeHtml(current) + '" placeholder="https://example.com" />' + '<input id="region_qr_code_' + region.id + '" type="url" name="region_qr_code_' + region.id + '" class="form-control" value="' + escapeHtml(current) + '" placeholder="https://example.com" required />' +
'<textarea name="region_qr_svg_' + region.id + '" class="visually-hidden" hidden>' + escapeHtml(currentSvg) + '</textarea>' + '<textarea name="region_qr_svg_' + region.id + '" class="visually-hidden" hidden>' + escapeHtml(currentSvg) + '</textarea>' +
'<input type="hidden" name="region_qr_preview_' + region.id + '" value="' + escapeHtml(String(context.qr_preview || '')) + '" />' + '<input type="hidden" name="region_qr_preview_' + region.id + '" value="' + escapeHtml(String(context.qr_preview || '')) + '" />' +
'<div class="card card-outline card-secondary overflow-hidden mt-2">' + '<div class="card card-outline card-secondary overflow-hidden mt-2">' +
+1 -1
View File
@@ -50,7 +50,7 @@
headerActions: '<span class="chip">Webpage</span>', headerActions: '<span class="chip">Webpage</span>',
bodyHtml: '' + bodyHtml: '' +
'<div class="input-group flex-nowrap">' + '<div class="input-group flex-nowrap">' +
'<input type="url" name="region_webpage_' + region.id + '" class="form-control" value="' + escapeHtml(current) + '" placeholder="https://example.com" />' + '<input type="url" name="region_webpage_' + region.id + '" class="form-control" value="' + escapeHtml(current) + '" placeholder="https://example.com" required />' +
'<button type="button" class="btn btn-outline-secondary text-nowrap" data-webpage-preview-update data-region-id="' + region.id + '">Update</button>' + '<button type="button" class="btn btn-outline-secondary text-nowrap" data-webpage-preview-update data-region-id="' + region.id + '">Update</button>' +
'</div>' '</div>'
}); });
+10 -20
View File
@@ -305,31 +305,12 @@ import { createSlideFormPreviewHelpers } from '/assets/js/slides/slide-form-prev
previewBox.innerHTML = value previewBox.innerHTML = value
? '<div class="slide-image-region-preview-shell" data-remove-' + (isVideo ? 'region-video' : isQrImage ? 'region-qr-image' : 'region-image') + '="' + escapeHtml(card.getAttribute('data-region-id') || '') + '" role="button" tabindex="0" aria-label="Remove ' + (isVideo ? 'video' : isQrImage ? 'QR image' : 'image') + '">' + ? '<div class="slide-image-region-preview-shell" data-remove-' + (isVideo ? 'region-video' : isQrImage ? 'region-qr-image' : 'region-image') + '="' + escapeHtml(card.getAttribute('data-region-id') || '') + '" role="button" tabindex="0" aria-label="Remove ' + (isVideo ? 'video' : isQrImage ? 'QR image' : 'image') + '">' +
(isVideo (isVideo
? '<video class="slide-image-region-preview" src="' + escapeHtml(value) + '" autoplay loop muted playsinline preload="metadata"></video>' ? '<video class="slide-image-region-preview" src="' + escapeHtml(value) + '" muted playsinline preload="metadata"></video>'
: '<img class="slide-image-region-preview" src="' + escapeHtml(value) + '" alt="' + (isQrImage ? 'Current QR image preview' : 'Current image preview') + '" />') + : '<img class="slide-image-region-preview" src="' + escapeHtml(value) + '" alt="' + (isQrImage ? 'Current QR image preview' : 'Current image preview') + '" />') +
'<span class="slide-image-region-preview-remove" aria-hidden="true"><i class="bi bi-trash3" aria-hidden="true"></i></span>' + '<span class="slide-image-region-preview-remove" aria-hidden="true"><i class="bi bi-trash3" aria-hidden="true"></i></span>' +
'</div>' '</div>'
: '<div class="slide-image-region-preview slide-image-region-preview-empty">No ' + (isVideo ? 'video' : 'image') + '</div>'; : '<div class="slide-image-region-preview slide-image-region-preview-empty">No ' + (isVideo ? 'video' : 'image') + '</div>';
if (isVideo) {
window.requestAnimationFrame(function () {
var video = previewBox.querySelector('video.slide-image-region-preview');
if (!video) {
return;
}
try {
video.load();
} catch (_error) {
// Ignore load failures; play() will retry if the browser allows it.
}
var playPromise = video.play && video.play();
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(function () {
return null;
});
}
});
}
} }
function loadVideoDuration(mediaPath) { function loadVideoDuration(mediaPath) {
@@ -861,6 +842,15 @@ import { createSlideFormPreviewHelpers } from '/assets/js/slides/slide-form-prev
requestPreviewRender(); requestPreviewRender();
} }
slideForm.addEventListener('keydown', function (event) {
var target = event.target;
if (event.key !== 'Enter' || !target || !target.matches || !target.matches('input:not([type="submit"]):not([type="button"]):not([type="reset"]), select')) {
return;
}
event.preventDefault();
});
slideForm.addEventListener('submit', function (event) { slideForm.addEventListener('submit', function (event) {
if (!window.webAsyncSaveForm || typeof window.webAsyncSaveForm.submit !== 'function') { if (!window.webAsyncSaveForm || typeof window.webAsyncSaveForm.submit !== 'function') {
return; return;
+97
View File
@@ -1,6 +1,102 @@
// Shared browser helpers for web UI escaping and formatting. // Shared browser helpers for web UI escaping and formatting.
(function () { (function () {
var urlValidationMessage = 'Please enter a URL.';
function isUrlInput(input) {
return input && input.matches && input.matches('input[type="url"]');
}
function isValidHttpUrl(input) {
input.setCustomValidity('');
var value = String(input.value || '').trim();
var hasValidNativeUrl = input.validity.valid;
var hasExplicitHttpScheme = /^https?:\/\/[^\s]+$/i.test(value);
var isValid = hasValidNativeUrl && hasExplicitHttpScheme;
if (!isValid) {
input.setCustomValidity(urlValidationMessage);
}
return isValid;
}
function getUrlFeedback(input) {
if (input._urlFeedback && input._urlFeedback.parentNode) {
return input._urlFeedback;
}
var feedbackId = input.id ? input.id + '-url-feedback' : '';
var feedback = feedbackId ? document.getElementById(feedbackId) : null;
if (feedback) {
var inputGroup = input.closest ? input.closest('.input-group') : null;
var feedbackAnchor = inputGroup || input;
feedbackAnchor.parentNode.insertBefore(feedback, feedbackAnchor.nextSibling);
input._urlFeedback = feedback;
return feedback;
}
feedback = document.createElement('div');
if (feedbackId) {
feedback.id = feedbackId;
}
feedback.className = 'invalid-feedback';
feedback.setAttribute('role', 'alert');
feedback.textContent = urlValidationMessage;
var inputGroup = input.closest ? input.closest('.input-group') : null;
var feedbackAnchor = inputGroup || input;
feedbackAnchor.parentNode.insertBefore(feedback, feedbackAnchor.nextSibling);
if (input.id) {
input.setAttribute('aria-describedby', feedback.id);
}
input._urlFeedback = feedback;
return feedback;
}
function updateUrlValidation(input, showFeedback) {
if (!isUrlInput(input)) {
return true;
}
var isValid = isValidHttpUrl(input);
if (!isValid && showFeedback) {
var feedback = getUrlFeedback(input);
feedback.hidden = false;
feedback.classList.add('d-block');
input.classList.add('is-invalid');
} else if (isValid) {
if (input.id) {
document.querySelectorAll('[id="' + input.id + '-url-feedback"]').forEach(function (feedback) {
feedback.hidden = true;
feedback.classList.remove('d-block');
});
} else if (input._urlFeedback) {
input._urlFeedback.hidden = true;
input._urlFeedback.classList.remove('d-block');
}
input.classList.remove('is-invalid');
}
return isValid;
}
function initializeUrlValidation() {
if (typeof document === 'undefined') {
return;
}
document.addEventListener('input', function (event) {
if (isUrlInput(event.target)) {
updateUrlValidation(event.target, true);
}
});
document.addEventListener('invalid', function (event) {
if (isUrlInput(event.target)) {
event.preventDefault();
updateUrlValidation(event.target, true);
}
}, true);
}
function escapeHtml(value) { function escapeHtml(value) {
return String(value ?? '') return String(value ?? '')
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
@@ -97,6 +193,7 @@
} }
window.escapeHtml = escapeHtml; window.escapeHtml = escapeHtml;
initializeUrlValidation();
var defaultQrOptions = window.defaultQrOptions || { var defaultQrOptions = window.defaultQrOptions || {
width: 1000, width: 1000,
+2 -2
View File
@@ -99,9 +99,9 @@ test('shared iframe renderers size preview content explicitly', () => {
assert.ok(playerHtmlRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"')); assert.ok(playerHtmlRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
assert.ok(playerWebpageRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"')); assert.ok(playerWebpageRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"'));
assert.ok(playerRenderHelpersSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"')); assert.ok(playerRenderHelpersSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"')); assert.ok(slideThumbnailPreviewSource.includes('slide-preview-webpage-region'));
assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"')); assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"')); assert.ok(slideThumbnailsSource.includes('slide-preview-webpage-placeholder'));
assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"')); assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
}); });