307 lines
9.1 KiB
JavaScript
307 lines
9.1 KiB
JavaScript
// Shared browser helpers for web UI escaping and formatting.
|
|
|
|
(function () {
|
|
function escapeHtml(value) {
|
|
return String(value ?? '')
|
|
.replace(/&/g, '&')
|
|
.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) {
|
|
var clientName = String(client && client.client_name ? client.client_name : '').trim();
|
|
if (clientName) {
|
|
return clientName;
|
|
}
|
|
|
|
return String(client && client.screen_slug ? client.screen_slug : client && client.deviceId ? client.deviceId : client && client.id ? client.id : client && client.clientId ? client.clientId : '').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;
|
|
}
|
|
|
|
window.escapeHtml = escapeHtml;
|
|
|
|
var defaultQrOptions = window.defaultQrOptions || {
|
|
width: 1000,
|
|
height: 1000,
|
|
type: 'canvas',
|
|
margin: 10,
|
|
qrOptions: {},
|
|
dotsOptions: {
|
|
color: '#000000',
|
|
type: 'square'
|
|
},
|
|
cornersSquareOptions: {
|
|
color: '#000000',
|
|
type: 'square'
|
|
},
|
|
cornersDotOptions: {
|
|
color: '#000000',
|
|
type: 'square'
|
|
},
|
|
backgroundOptions: {
|
|
color: '#ffffff'
|
|
}
|
|
};
|
|
var qrDotTypes = window.qrDotTypes || [
|
|
{ value: 'square', label: 'Square' },
|
|
{ value: 'dots', label: 'Dots' },
|
|
{ value: 'rounded', label: 'Rounded' },
|
|
{ value: 'extra-rounded', label: 'Extra rounded' },
|
|
{ value: 'classy', label: 'Classy' },
|
|
{ value: 'classy-rounded', label: 'Classy rounded' }
|
|
];
|
|
var qrCornerSquareTypes = window.qrCornerSquareTypes || [
|
|
{ value: 'square', label: 'Square' },
|
|
{ value: 'dot', label: 'Dot' },
|
|
{ value: 'rounded', label: 'Rounded' },
|
|
{ value: 'extra-rounded', label: 'Extra rounded' },
|
|
{ value: 'dots', label: 'Dots' },
|
|
{ value: 'classy', label: 'Classy' },
|
|
{ value: 'classy-rounded', label: 'Classy rounded' }
|
|
];
|
|
var qrCornerDotTypes = window.qrCornerDotTypes || [
|
|
{ value: 'square', label: 'Square' },
|
|
{ value: 'dot', label: 'Dot' },
|
|
{ value: 'rounded', label: 'Rounded' },
|
|
{ value: 'extra-rounded', label: 'Extra rounded' },
|
|
{ value: 'classy', label: 'Classy' },
|
|
{ value: 'classy-rounded', label: 'Classy rounded' }
|
|
];
|
|
|
|
function getOptionValue(option) {
|
|
if (option && typeof option === 'object' && option.value !== undefined && option.value !== null) {
|
|
return String(option.value).trim();
|
|
}
|
|
|
|
return String(option === undefined || option === null ? '' : option).trim();
|
|
}
|
|
|
|
function normalizeMarginValue(value, fallback) {
|
|
var parsed = Number(value);
|
|
return Number.isFinite(parsed) && parsed >= 0 ? Math.round(parsed) : fallback;
|
|
}
|
|
|
|
function normalizeHexColorValue(value, fallback) {
|
|
var raw = String(value === undefined || value === null ? '' : value).trim();
|
|
return raw || fallback;
|
|
}
|
|
|
|
function normalizeBooleanValue(value, fallback) {
|
|
if (value === undefined || value === null || value === '') {
|
|
return Boolean(fallback);
|
|
}
|
|
if (typeof value === 'boolean') {
|
|
return value;
|
|
}
|
|
var text = String(value).trim().toLowerCase();
|
|
if (text === 'true' || text === '1' || text === 'yes' || text === 'on') {
|
|
return true;
|
|
}
|
|
if (text === 'false' || text === '0' || text === 'no' || text === 'off') {
|
|
return false;
|
|
}
|
|
return Boolean(value);
|
|
}
|
|
|
|
function normalizeOptionValue(value, options, fallback) {
|
|
var text = String(value === undefined || value === null ? '' : value).trim();
|
|
for (var index = 0; index < options.length; index += 1) {
|
|
if (getOptionValue(options[index]) === text) {
|
|
return text;
|
|
}
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
function normalizeRatioValue(value, fallback) {
|
|
var parsed = Number(value);
|
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
}
|
|
|
|
function normalizeGradientTypeValue(value, fallback) {
|
|
var text = String(value === undefined || value === null ? '' : value).trim().toLowerCase();
|
|
return text === 'radial' ? 'radial' : 'linear';
|
|
}
|
|
|
|
function normalizeGradientRotationValue(value, fallback) {
|
|
var parsed = Number(value);
|
|
return Number.isFinite(parsed) ? Math.round(parsed) : fallback;
|
|
}
|
|
|
|
function normalizeColorModeValue(value, fallback) {
|
|
var text = String(value === undefined || value === null ? '' : value).trim().toLowerCase();
|
|
return text === 'gradient' ? 'gradient' : 'single';
|
|
}
|
|
|
|
function normalizeColorValue(value, fallback) {
|
|
return normalizeHexColorValue(value, fallback);
|
|
}
|
|
|
|
function renderSelectOptions(options, currentValue) {
|
|
return options.map(function (option) {
|
|
var optionValue = getOptionValue(option);
|
|
var optionLabel = option && typeof option === 'object' && option.label !== undefined ? option.label : optionValue;
|
|
return '<option value="' + escapeHtml(optionValue) + '"' + (String(optionValue) === String(currentValue) ? ' selected' : '') + '>' + escapeHtml(optionLabel) + '</option>';
|
|
}).join('');
|
|
}
|
|
|
|
function normalizeSvgMarkup(svg) {
|
|
var markup = String(svg || '').trim();
|
|
if (!markup || markup.charAt(0) !== '<') {
|
|
return markup;
|
|
}
|
|
|
|
return markup.replace(/^<svg\b([^>]*)>/i, function (_match, attrText) {
|
|
var attrs = String(attrText || '');
|
|
if (!/\bwidth\s*=\s*/i.test(attrs)) {
|
|
attrs += ' width="95%"';
|
|
}
|
|
if (!/\bheight\s*=\s*/i.test(attrs)) {
|
|
attrs += ' height="95%"';
|
|
}
|
|
if (!/\bpreserveAspectRatio\s*=\s*/i.test(attrs)) {
|
|
attrs += ' preserveAspectRatio="xMidYMid meet"';
|
|
}
|
|
if (!/\bstyle\s*=\s*/i.test(attrs)) {
|
|
attrs += ' style="display:block;width:95%;height:95%;"';
|
|
}
|
|
return '<svg' + attrs + '>';
|
|
});
|
|
}
|
|
|
|
function normalizeQrSvg(svg) {
|
|
return normalizeSvgMarkup(svg);
|
|
}
|
|
|
|
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.defaultQrOptions = defaultQrOptions;
|
|
window.qrDotTypes = qrDotTypes;
|
|
window.qrCornerSquareTypes = qrCornerSquareTypes;
|
|
window.qrCornerDotTypes = qrCornerDotTypes;
|
|
window.normalizeMarginValue = normalizeMarginValue;
|
|
window.normalizeHexColorValue = normalizeHexColorValue;
|
|
window.normalizeBooleanValue = normalizeBooleanValue;
|
|
window.normalizeOptionValue = normalizeOptionValue;
|
|
window.normalizeRatioValue = normalizeRatioValue;
|
|
window.normalizeGradientTypeValue = normalizeGradientTypeValue;
|
|
window.normalizeGradientRotationValue = normalizeGradientRotationValue;
|
|
window.normalizeColorModeValue = normalizeColorModeValue;
|
|
window.normalizeColorValue = normalizeColorValue;
|
|
window.renderSelectOptions = renderSelectOptions;
|
|
window.normalizeSvgMarkup = normalizeSvgMarkup;
|
|
window.normalizeQrSvg = normalizeQrSvg;
|
|
window.blobToText = blobToText;
|
|
window.pendingByRegionId = window.pendingByRegionId || Object.create(null);
|
|
|
|
window.webUiHelpers = {
|
|
escapeHtml: escapeHtml,
|
|
formatDashboardDate: formatDashboardDate,
|
|
getClientRowKey: getClientRowKey,
|
|
getClientDisplayName: getClientDisplayName,
|
|
setButtonVariant: setButtonVariant,
|
|
normalizeDisplayIp: normalizeDisplayIp
|
|
};
|
|
}()); |