(function () {
function escapeHtml(value) {
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function valueOrDefault(value, fallback) {
return value === undefined || value === null || value === '' ? fallback : value;
}
function normalizeLockRatio(value) {
var raw = String(value || '').trim();
if (!raw) {
return '';
}
if (!/^\d+\s*:\s*\d+$/.test(raw)) {
return '';
}
return raw.replace(/\s+/g, '');
}
function parseLockRatio(value) {
var normalized = normalizeLockRatio(value);
if (!normalized) {
return null;
}
var parts = normalized.split(':');
var width = Number(parts[0]);
var height = Number(parts[1]);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return null;
}
return {
label: normalized,
ratio: width / height
};
}
function getDefaultRegionSize(regionType, lockRatio) {
var ratio = parseLockRatio(lockRatio);
var locked = regionType === 'image' || regionType === 'webpage' || regionType === 'html' || regionType === 'rtmp' || regionType === 'rss' || regionType === 'api';
if (ratio) {
if (ratio.ratio >= 1) {
var baseWidth = locked ? 420 : 300;
return {
width: Math.max(12, Math.round(baseWidth)),
height: Math.max(12, Math.round(baseWidth / ratio.ratio))
};
}
var baseHeight = locked ? 300 : 240;
return {
width: Math.max(12, Math.round(baseHeight * ratio.ratio)),
height: Math.max(12, Math.round(baseHeight))
};
}
return {
width: locked ? 420 : 300,
height: locked ? 240 : 120
};
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function getRegionName(card) {
return String(card.querySelector('[name="region_name[]"]').value || '').trim();
}
function syncRegionIdentity(card, value) {
var next = String(value || '').trim();
card.querySelector('[name="region_name[]"]').value = next;
card.querySelector('[name="region_key[]"]').value = next;
card.querySelector('[name="region_label[]"]').value = next;
}
function readCard(card) {
var name = getRegionName(card);
return {
region_key: name,
label: name,
region_type: card.querySelector('[name="region_type[]"]').value,
font_family: card.querySelector('[name="font_family[]"]').value,
lock_ratio: normalizeLockRatio(card.querySelector('[name="region_lock_ratio[]"]').value),
x: Number(card.querySelector('[name="region_x[]"]').value || 0),
y: Number(card.querySelector('[name="region_y[]"]').value || 0),
width: Number(card.querySelector('[name="region_width[]"]').value || 0),
height: Number(card.querySelector('[name="region_height[]"]').value || 0),
z_index: Number(card.querySelector('[name="region_z[]"]').value || 0)
};
}
function writeCard(card, values) {
if (values.region_name !== undefined) {
syncRegionIdentity(card, values.region_name);
} else if (values.region_key !== undefined) {
syncRegionIdentity(card, values.region_key);
} else if (values.label !== undefined) {
syncRegionIdentity(card, values.label);
}
if (values.region_type !== undefined) { card.querySelector('[name="region_type[]"]').value = values.region_type; }
if (values.font_family !== undefined) { card.querySelector('[name="font_family[]"]').value = values.font_family; }
if (values.lock_ratio !== undefined) { card.querySelector('[name="region_lock_ratio[]"]').value = normalizeLockRatio(values.lock_ratio); }
if (values.x !== undefined) { card.querySelector('[name="region_x[]"]').value = Math.round(values.x); }
if (values.y !== undefined) { card.querySelector('[name="region_y[]"]').value = Math.round(values.y); }
if (values.width !== undefined) { card.querySelector('[name="region_width[]"]').value = Math.round(values.width); }
if (values.height !== undefined) { card.querySelector('[name="region_height[]"]').value = Math.round(values.height); }
if (values.z_index !== undefined) { card.querySelector('[name="region_z[]"]').value = Math.round(values.z_index); }
}
function clampRegion(region, size, minSize) {
var bounds = size || { width: 1920, height: 1080 };
var minimum = minSize || 12;
var x = clamp(region.x, 0, bounds.width - minimum);
var y = clamp(region.y, 0, bounds.height - minimum);
var width = Math.max(minimum, region.width);
var height = Math.max(minimum, region.height);
if (x + width > bounds.width) {
width = bounds.width - x;
}
if (y + height > bounds.height) {
height = bounds.height - y;
}
return { x: Math.round(x), y: Math.round(y), width: Math.round(Math.max(minimum, width)), height: Math.round(Math.max(minimum, height)) };
}
function getOverlayRect(overlay) {
return overlay.getBoundingClientRect();
}
function toCanvasPoint(event, overlay, size) {
var rect = getOverlayRect(overlay);
var canvasSize = size || { width: 1920, height: 1080 };
var x = clamp(event.clientX - rect.left, 0, rect.width);
var y = clamp(event.clientY - rect.top, 0, rect.height);
return {
x: Math.round((x / Math.max(rect.width, 1)) * canvasSize.width),
y: Math.round((y / Math.max(rect.height, 1)) * canvasSize.height)
};
}
function canvasRectToPixels(region, overlay, size) {
var rect = getOverlayRect(overlay);
var canvasSize = size || { width: 1920, height: 1080 };
return {
left: (region.x / canvasSize.width) * rect.width,
top: (region.y / canvasSize.height) * rect.height,
width: (region.width / canvasSize.width) * rect.width,
height: (region.height / canvasSize.height) * rect.height
};
}
window.templateDesignerUtils = {
escapeHtml: escapeHtml,
valueOrDefault: valueOrDefault,
clamp: clamp,
getRegionName: getRegionName,
syncRegionIdentity: syncRegionIdentity,
readCard: readCard,
writeCard: writeCard,
clampRegion: clampRegion,
getOverlayRect: getOverlayRect,
toCanvasPoint: toCanvasPoint,
canvasRectToPixels: canvasRectToPixels,
normalizeLockRatio: normalizeLockRatio,
parseLockRatio: parseLockRatio,
getDefaultRegionSize: getDefaultRegionSize
};
}());