// Shared browser helpers for web UI escaping and formatting. (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) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatDashboardDate(value, useTwentyFourHour) { if (!value) { return ''; } var date = new Date(value); if (Number.isNaN(date.getTime())) { return ''; } var formatOptions = { month: 'short', day: '2-digit', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }; if (useTwentyFourHour) { formatOptions.hour12 = false; } return new Intl.DateTimeFormat('en-US', formatOptions).format(date); } function getClientRowKey(client) { return String(client && client.id ? client.id : client && client.clientId ? client.clientId : client && client.screen_slug ? client.screen_slug : client && client.deviceId ? client.deviceId : '').trim(); } function getClientDisplayName(client) { if (client && client.client_name) { return String(client.client_name).trim(); } var clientId = String(client && client.clientId ? client.clientId : '').trim(); if (clientId) { return clientId; } return ''; } function setButtonVariant(button, classesToRemove, classToAdd) { if (!button) { return; } if (button.classList) { classesToRemove.forEach(function (className) { button.classList.remove(className); }); if (classToAdd) { button.classList.add(classToAdd); } return; } var className = String(button.className || ''); classesToRemove.forEach(function (removeClass) { className = className.replace(new RegExp('(^|\\s)' + removeClass + '(?=\\s|$)', 'g'), ' '); }); if (classToAdd) { className += ' ' + classToAdd; } button.className = className.replace(/\s+/g, ' ').trim(); } function normalizeDisplayIp(value) { var ip = String(value || '').trim(); if (!ip) { return ''; } if (ip.toLowerCase().indexOf('::ffff:') === 0) { return ip.slice(7).trim(); } return ip; } initializeUrlValidation(); async function blobToText(blob) { if (!blob) { return ''; } if (typeof blob.text === 'function') { return blob.text(); } if (typeof FileReader === 'function') { return await new Promise(function (resolve, reject) { var reader = new FileReader(); reader.onload = function () { resolve(String(reader.result || '')); }; reader.onerror = function () { reject(reader.error || new Error('Failed to read blob.')); }; reader.readAsText(blob); }); } return ''; } window.blobToText = blobToText; window.escapeHtml = escapeHtml; window.pendingByRegionId = window.pendingByRegionId || Object.create(null); window.webUiHelpers = { escapeHtml: escapeHtml, formatDashboardDate: formatDashboardDate, getClientRowKey: getClientRowKey, getClientDisplayName: getClientDisplayName, setButtonVariant: setButtonVariant, normalizeDisplayIp: normalizeDisplayIp }; }());