Release v2.11.1
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m47s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 31s

This commit is contained in:
2026-09-04 15:43:55 +01:00
parent 2c150b5b2e
commit 98f969ca0f
104 changed files with 3559 additions and 447 deletions
+223 -7
View File
@@ -27,9 +27,30 @@
return current === undefined || current === null ? '' : current;
}
function splitExpressionSegments(value) {
var segments = [];
var current = '';
var depth = 0;
String(value || '').split('').forEach(function (character) {
if (character === '(') {
depth += 1;
} else if (character === ')' && depth > 0) {
depth -= 1;
}
if (character === '.' && depth === 0) {
segments.push(current);
current = '';
return;
}
current += character;
});
segments.push(current);
return segments;
}
function parsePlaceholderExpression(expression) {
var raw = String(expression || '').trim();
var segments = raw ? raw.split('.') : [];
var segments = raw ? splitExpressionSegments(raw) : [];
var transforms = [];
while (segments.length) {
@@ -57,13 +78,33 @@
if (!source) {
return [];
}
if ((source[0] === '"' && source[source.length - 1] === '"') || (source[0] === '\'' && source[source.length - 1] === '\'')) {
return [source.slice(1, -1)];
var args = [];
var current = '';
var quote = '';
source.split('').forEach(function (character) {
if ((character === '"' || character === '\'') && (!quote || quote === character)) {
quote = quote ? '' : character;
current += character;
return;
}
if (character === ',' && !quote) {
if (current.trim()) {
args.push(current.trim());
}
current = '';
return;
}
current += character;
});
if (current.trim()) {
args.push(current.trim());
}
return source.split(',').map(function (item) {
return String(item || '').trim();
return args.map(function (item) {
var normalized = String(item || '').trim();
if ((normalized[0] === '"' && normalized[normalized.length - 1] === '"') || (normalized[0] === '\'' && normalized[normalized.length - 1] === '\'')) {
return normalized.slice(1, -1);
}
return normalized;
}).filter(Boolean);
}
@@ -253,6 +294,26 @@
return resolveTimeZone((args && args[0]) || (options && options.timeZone) || '');
}
function toNumericValue(value) {
if (value && typeof value === 'object') {
var numericKeys = ['value', 'amount', 'current', 'total', 'goal', 'raised'];
for (var keyIndex = 0; keyIndex < numericKeys.length; keyIndex += 1) {
var nestedValue = value[numericKeys[keyIndex]];
if (nestedValue !== undefined && nestedValue !== null && nestedValue !== value) {
var nestedNumber = toNumericValue(nestedValue);
if (Number.isFinite(nestedNumber)) {
return nestedNumber;
}
}
}
return NaN;
}
var normalized = String(value === undefined || value === null ? '' : value).replace(/[^0-9.eE+-]/g, '');
var number = Number(normalized);
return Number.isFinite(number) ? number : NaN;
}
function applyTransform(value, transform, options) {
var text = String(value === undefined || value === null ? '' : value);
var name = String(transform && transform.name || '').trim().toLowerCase();
@@ -284,6 +345,18 @@
return getTimeZoneLongName(getTransformTimeZone(args, options));
}
if (name === 'add' || name === 'subtract' || name === 'multiply' || name === 'divide') {
var arithmeticValue = toNumericValue(value);
var operand = toNumericValue(args[0]);
if (!Number.isFinite(arithmeticValue) || !Number.isFinite(operand) || (name === 'divide' && operand === 0)) {
return value;
}
if (name === 'add') return arithmeticValue + operand;
if (name === 'subtract') return arithmeticValue - operand;
if (name === 'multiply') return arithmeticValue * operand;
return arithmeticValue / operand;
}
return text;
}
@@ -319,6 +392,147 @@
};
}
function isProgressPlaceholderExpression(expression) {
var parsed = parsePlaceholderExpression(expression);
return parsed.transforms.some(function (transform) {
return transform && transform.name === 'progress';
});
}
function renderProgressPlaceholder(value, expression) {
var parsed = parsePlaceholderExpression(expression);
var transform = parsed.transforms.find(function (candidate) {
return candidate && candidate.name === 'progress';
});
if (!transform || !value || typeof value !== 'object' || transform.args.length < 2) {
return '';
}
function toNumber(raw) {
if (raw && typeof raw === 'object') {
var numericKeys = ['value', 'amount', 'current', 'total', 'goal', 'raised'];
for (var keyIndex = 0; keyIndex < numericKeys.length; keyIndex += 1) {
var nestedValue = raw[numericKeys[keyIndex]];
if (nestedValue !== undefined && nestedValue !== null && nestedValue !== raw) {
var nestedNumber = toNumber(nestedValue);
if (Number.isFinite(nestedNumber)) {
return nestedNumber;
}
}
}
return 0;
}
var normalized = String(raw === undefined || raw === null ? '' : raw).replace(/[^0-9.eE+-]/g, '');
var number = Number(normalized);
return Number.isFinite(number) ? number : 0;
}
function resolveProgressValue(argument) {
var raw = String(argument === undefined || argument === null ? '' : argument).trim();
var literal = raw.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, '$1$2');
if (/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(literal)) {
return literal;
}
return resolvePath(value, raw);
}
var startValue = resolveProgressValue(transform.args[0]);
var endValue = resolveProgressValue(transform.args[1]);
var startDate = startValue instanceof Date ? startValue : new Date(startValue);
var endDate = endValue instanceof Date ? endValue : new Date(endValue);
var percentage;
if (typeof startValue === 'string' && typeof endValue === 'string' && /[-T]/.test(startValue) && /[-T]/.test(endValue) && !Number.isNaN(startDate.getTime()) && !Number.isNaN(endDate.getTime()) && endDate.getTime() > startDate.getTime()) {
percentage = Math.max(0, Math.min(100, ((Date.now() - startDate.getTime()) / (endDate.getTime() - startDate.getTime())) * 100));
} else {
var current = toNumber(startValue);
var goal = toNumber(endValue);
percentage = goal > 0 ? Math.max(0, Math.min(100, (current / goal) * 100)) : 0;
}
var roundedPercentage = Math.round(percentage * 10) / 10;
var label = roundedPercentage + '%';
var variants = ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'];
var variantColors = {
primary: '#0d6efd',
secondary: '#6c757d',
success: '#198754',
danger: '#dc3545',
warning: '#ffc107',
info: '#0dcaf0',
light: '#f8f9fa',
dark: '#212529'
};
var announcementColors = {
orange: '#c84e10',
amber: '#a56710',
olive: '#5f7f0f',
teal: '#12827d',
sky: '#127caf',
indigo: '#6f60ea',
violet: '#9553db',
fuchsia: '#b347be',
pink: '#cd388d',
navy: '#1d2d4c',
steel: '#3a4860',
slate: '#566577',
graphite: '#32363c',
midnight: '#1e1d2d'
};
var variant = 'primary';
var barVariant = '';
var customColor = '';
var backgroundColor = '';
var colorCount = 0;
var modifiers = [];
var textless = false;
var borderRadius = 'var(--bs-border-radius)';
transform.args.slice(2).forEach(function (argument) {
var option = String(argument || '').trim().toLowerCase();
var isNamedColor = variants.indexOf(option) !== -1 || Object.prototype.hasOwnProperty.call(announcementColors, option);
var isHexColor = /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(option);
if (isNamedColor || isHexColor) {
var color = isHexColor ? option : (variantColors[option] || announcementColors[option]);
if (colorCount === 0) {
if (variants.indexOf(option) !== -1) {
variant = option;
barVariant = option;
}
customColor = color;
} else if (colorCount === 1) {
backgroundColor = color;
}
colorCount += 1;
}
if (option === 'striped' || option === 'animated') {
modifiers.push('progress-bar-' + option);
}
if (option === 'textless') {
textless = true;
}
if (option === 'square') {
borderRadius = '0';
} else if (option === 'pill') {
borderRadius = '50rem';
} else if (option === 'rounded') {
borderRadius = 'var(--bs-border-radius)';
} else {
var radiusMatch = option.match(/^radius\((0|[0-9]+(?:\.[0-9]+)?(?:px|rem|em|%)?)\)$/);
if (radiusMatch) {
borderRadius = radiusMatch[1];
}
}
});
var progressClass = 'progress';
var barClass = 'progress-bar' + (barVariant ? ' bg-' + barVariant : '') + (modifiers.length ? ' ' + modifiers.join(' ') : '');
var barStyle = 'width:' + roundedPercentage + '%;color:inherit;';
if (customColor) {
barStyle += 'background-color:' + customColor + ';';
}
var progressStyleValue = (backgroundColor ? 'background-color:' + backgroundColor + ';' : '') + 'font-size:inherit;--bs-progress-font-size:inherit;--bs-progress-height:1em;height:1em;border-radius:' + borderRadius + ';';
var progressStyle = progressStyleValue ? ' style="' + progressStyleValue + '"' : '';
return '<span class="' + progressClass + ' api-progress"' + progressStyle + ' role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="' + roundedPercentage + '" aria-label="' + label + '">' +
'<span class="' + barClass + '" style="' + barStyle + '">' + (textless ? '' : label) + '</span></span>';
}
function formatPlaceholderValue(value) {
if (value === undefined || value === null) {
return '';
@@ -372,6 +586,8 @@
resolvePlaceholderExpression: resolvePlaceholderExpression,
isImagePlaceholderExpression: isImagePlaceholderExpression,
getImagePlaceholderConfig: getImagePlaceholderConfig,
isProgressPlaceholderExpression: isProgressPlaceholderExpression,
renderProgressPlaceholder: renderProgressPlaceholder,
formatPlaceholderValue: formatPlaceholderValue,
collectPlaceholderFieldPaths: collectPlaceholderFieldPaths
};