Release v2.5.6
This commit is contained in:
@@ -50,6 +50,8 @@
|
||||
var selectedIndex = -1;
|
||||
var activeAnimationCard = null;
|
||||
var overlayRenderFrame = 0;
|
||||
var stageResizeObserver = null;
|
||||
var regionNameValidationToastShown = false;
|
||||
var previewState = {
|
||||
active: false,
|
||||
mode: '',
|
||||
@@ -192,6 +194,40 @@
|
||||
card.querySelector('[name="region_label[]"]').value = next;
|
||||
}
|
||||
|
||||
function normalizeRegionNamePrefix(regionType) {
|
||||
var prefix = String(regionType || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
||||
return prefix || 'region';
|
||||
}
|
||||
|
||||
function buildDefaultRegionName(regionType, index) {
|
||||
return normalizeRegionNamePrefix(regionType) + '_' + index;
|
||||
}
|
||||
|
||||
function getNextDefaultRegionName(regionType) {
|
||||
var normalizedType = normalizeRegionNamePrefix(regionType);
|
||||
var nextIndex = getCards().reduce(function (count, card) {
|
||||
var currentType = normalizeRegionNamePrefix(card.querySelector('[name="region_type[]"]').value || '');
|
||||
if (currentType !== normalizedType) {
|
||||
return count;
|
||||
}
|
||||
|
||||
var currentName = getRegionName(card);
|
||||
var expectedPrefix = normalizedType + '_';
|
||||
if (currentName.indexOf(expectedPrefix) !== 0) {
|
||||
return count;
|
||||
}
|
||||
|
||||
var suffix = Number(currentName.slice(expectedPrefix.length));
|
||||
if (!Number.isFinite(suffix) || suffix < 1) {
|
||||
return count;
|
||||
}
|
||||
|
||||
return Math.max(count, suffix);
|
||||
}, 0) + 1;
|
||||
|
||||
return buildDefaultRegionName(normalizedType, nextIndex);
|
||||
}
|
||||
|
||||
function validateRegionNames() {
|
||||
var cards = getCards();
|
||||
var names = {};
|
||||
@@ -230,6 +266,17 @@
|
||||
return !hasDuplicate;
|
||||
}
|
||||
|
||||
function notifyRegionNameValidationError(message) {
|
||||
if (regionNameValidationToastShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
regionNameValidationToastShown = true;
|
||||
if (typeof showToast === 'function') {
|
||||
showToast(message || 'Region names must be unique on this template.', 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
function readCard(card) {
|
||||
return utils.readCard ? utils.readCard(card) : {
|
||||
region_key: getRegionName(card),
|
||||
@@ -304,6 +351,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
function updatePreviewButtonIcon(button, iconClass) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
var icon = button.querySelector('[data-preview-icon]');
|
||||
if (!icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
icon.className = 'bi ' + iconClass + ' me-1';
|
||||
}
|
||||
|
||||
function updatePreviewControlState() {
|
||||
var activeMode = previewState.active ? previewState.mode : '';
|
||||
|
||||
@@ -315,7 +375,9 @@
|
||||
var available = hasPreviewAnimationForMode(entry.mode);
|
||||
var disabled = !available || (previewState.active && activeMode !== entry.mode);
|
||||
entry.button.disabled = disabled;
|
||||
entry.button.classList.toggle('active', previewState.active && activeMode === entry.mode);
|
||||
entry.button.classList.toggle('btn-outline-secondary', disabled);
|
||||
entry.button.classList.toggle('btn-info', !disabled);
|
||||
updatePreviewButtonIcon(entry.button, previewState.active && activeMode === entry.mode ? 'bi-stop-fill' : 'bi-play-fill');
|
||||
entry.button.setAttribute('aria-pressed', previewState.active && activeMode === entry.mode ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
@@ -380,6 +442,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
var MAX_ANIMATION_REPEAT_COUNT = 999;
|
||||
|
||||
function getPreviewAnimationStepTimingMs(step, mode, animateCssMode) {
|
||||
var normalizedMode = String(mode || '').trim().toLowerCase();
|
||||
var durationMs = Number(step && step.duration_ms);
|
||||
@@ -416,7 +480,9 @@
|
||||
normalized.preset = normalizePreviewAnimation(value.preset !== undefined ? value.preset : fallbackPreset || 'none');
|
||||
normalized.duration_ms = Number.isFinite(Number(value.duration_ms)) && Number(value.duration_ms) > 0 ? Math.round(Number(value.duration_ms)) : null;
|
||||
normalized.delay_ms = Number.isFinite(Number(value.delay_ms)) && Number(value.delay_ms) >= 0 ? Math.round(Number(value.delay_ms)) : null;
|
||||
normalized.iterations = Number.isFinite(Number(value.iterations)) && Number(value.iterations) > 0 ? Math.round(Number(value.iterations)) : null;
|
||||
normalized.iterations = Number.isFinite(Number(value.iterations)) && Number(value.iterations) > 0
|
||||
? Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, Math.round(Number(value.iterations))))
|
||||
: null;
|
||||
normalized.fill_mode = String(value.fill_mode || '').trim();
|
||||
return normalized;
|
||||
}
|
||||
@@ -599,11 +665,11 @@
|
||||
var delayInput = getAnimationModalField(stepName, 'delay_ms');
|
||||
|
||||
if (presetInput) { presetInput.value = step.preset; }
|
||||
if (durationInput) { durationInput.value = step.duration_ms === null || step.duration_ms === undefined || Number(step.duration_ms) <= 0 ? '1000' : String(step.duration_ms); }
|
||||
if (durationInput) { durationInput.value = String(getAnimationDurationPercent(step.duration_ms)); }
|
||||
if (delayInput) { delayInput.value = step.delay_ms === null || step.delay_ms === undefined ? '0' : String(step.delay_ms); }
|
||||
if (stepName === 'loop') {
|
||||
var iterationsInput = getAnimationModalField(stepName, 'iterations');
|
||||
if (iterationsInput) { iterationsInput.value = step.iterations === null || step.iterations === undefined ? '1' : String(step.iterations); }
|
||||
if (iterationsInput) { iterationsInput.value = step.iterations === null || step.iterations === undefined ? '1' : String(Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, Number(step.iterations) || 1))); }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -617,6 +683,44 @@
|
||||
return Number.isFinite(next) ? Math.round(next) : null;
|
||||
}
|
||||
|
||||
function clampAnimationModalField(field) {
|
||||
if (!field || !field.matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isLoopIterationsField = field.matches('[data-animation-modal-step="loop"][data-animation-modal-field="iterations"]');
|
||||
var isDurationField = field.matches('[data-animation-modal-field="duration_ms"]');
|
||||
if (!isLoopIterationsField && !isDurationField) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fieldValue = readNumberField(field.value);
|
||||
if (fieldValue === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedValue = isLoopIterationsField
|
||||
? Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, fieldValue))
|
||||
: Math.min(200, Math.max(1, fieldValue));
|
||||
if (String(normalizedValue) !== String(field.value)) {
|
||||
field.value = String(normalizedValue);
|
||||
}
|
||||
}
|
||||
|
||||
function getAnimationDurationPercent(durationMs) {
|
||||
var value = Number(durationMs);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return Math.min(200, Math.max(1, Math.round(value / 10)));
|
||||
}
|
||||
|
||||
function getAnimationDurationMs(durationPercent) {
|
||||
var percent = Math.min(200, Math.max(1, readNumberField(durationPercent) || 100));
|
||||
return percent * 10;
|
||||
}
|
||||
|
||||
function readAnimationModalConfig() {
|
||||
if (!animationAdvancedModal) {
|
||||
return null;
|
||||
@@ -631,11 +735,11 @@
|
||||
|
||||
var nextStep = Object.assign({}, config[stepName] || {});
|
||||
nextStep.preset = presetInput ? presetInput.value : nextStep.preset;
|
||||
nextStep.duration_ms = readNumberField(durationInput ? durationInput.value : '') || 1000;
|
||||
nextStep.duration_ms = getAnimationDurationMs(durationInput ? durationInput.value : '');
|
||||
nextStep.delay_ms = readNumberField(delayInput ? delayInput.value : '') || 0;
|
||||
if (stepName === 'loop') {
|
||||
var iterationsInput = getAnimationModalField(stepName, 'iterations');
|
||||
nextStep.iterations = readNumberField(iterationsInput ? iterationsInput.value : '') || 1;
|
||||
nextStep.iterations = Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, readNumberField(iterationsInput ? iterationsInput.value : '') || 1));
|
||||
}
|
||||
|
||||
config[stepName] = normalizeAnimationStep(nextStep, 'none');
|
||||
@@ -892,6 +996,104 @@
|
||||
canvasHeightInput.value = Math.max(1, Number(option.dataset.height || canvasHeightInput.value || 1080));
|
||||
updateAspectRatio();
|
||||
updateCanvasSizeSummary();
|
||||
getCards().forEach(function (card) {
|
||||
normalizeRegionCard(card, 'width');
|
||||
});
|
||||
}
|
||||
|
||||
function fitRegionToCanvas(region, aspect) {
|
||||
var size = getCanvasSize();
|
||||
var minSize = 12;
|
||||
var next = {
|
||||
x: Math.round(Number(region && region.x !== undefined ? region.x : 0)),
|
||||
y: Math.round(Number(region && region.y !== undefined ? region.y : 0)),
|
||||
width: Math.max(minSize, Math.round(Number(region && region.width !== undefined ? region.width : minSize))),
|
||||
height: Math.max(minSize, Math.round(Number(region && region.height !== undefined ? region.height : minSize)))
|
||||
};
|
||||
|
||||
if (aspect && Number.isFinite(aspect.ratio) && aspect.ratio > 0) {
|
||||
if (next.width > size.width || next.height > size.height) {
|
||||
var canvasRatio = size.width / Math.max(1, size.height);
|
||||
if (canvasRatio >= aspect.ratio) {
|
||||
next.height = Math.max(minSize, Math.min(size.height, next.height, Math.round(size.height)));
|
||||
next.width = Math.max(minSize, Math.round(next.height * aspect.ratio));
|
||||
if (next.width > size.width) {
|
||||
next.width = size.width;
|
||||
next.height = Math.max(minSize, Math.round(next.width / aspect.ratio));
|
||||
}
|
||||
} else {
|
||||
next.width = Math.max(minSize, Math.min(size.width, next.width, Math.round(size.width)));
|
||||
next.height = Math.max(minSize, Math.round(next.width / aspect.ratio));
|
||||
if (next.height > size.height) {
|
||||
next.height = size.height;
|
||||
next.width = Math.max(minSize, Math.round(next.height * aspect.ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (next.width > size.width) {
|
||||
next.width = size.width;
|
||||
next.x = 0;
|
||||
}
|
||||
if (next.height > size.height) {
|
||||
next.height = size.height;
|
||||
next.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (next.x + next.width > size.width) {
|
||||
next.x = size.width - next.width;
|
||||
}
|
||||
if (next.y + next.height > size.height) {
|
||||
next.y = size.height - next.height;
|
||||
}
|
||||
|
||||
next.x = clamp(next.x, 0, Math.max(0, size.width - next.width));
|
||||
next.y = clamp(next.y, 0, Math.max(0, size.height - next.height));
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
function normalizeRegionCard(card, changedField) {
|
||||
if (!card) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var current = readCard(card);
|
||||
var next = {
|
||||
x: current.x,
|
||||
y: current.y,
|
||||
width: Math.max(12, Math.round(current.width || 0)),
|
||||
height: Math.max(12, Math.round(current.height || 0))
|
||||
};
|
||||
var lockRatio = normalizeLockRatio(current.lock_ratio);
|
||||
var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
|
||||
var size = getCanvasSize();
|
||||
|
||||
if (aspect) {
|
||||
if (changedField === 'height') {
|
||||
next.width = Math.max(12, Math.round(next.height * aspect.ratio));
|
||||
} else {
|
||||
next.height = Math.max(12, Math.round(next.width / aspect.ratio));
|
||||
}
|
||||
|
||||
if (next.width > size.width || next.height > size.height) {
|
||||
var scale = Math.min(size.width / Math.max(1, next.width), size.height / Math.max(1, next.height), 1);
|
||||
if (scale < 1) {
|
||||
next.width = Math.max(12, Math.round(next.width * scale));
|
||||
next.height = Math.max(12, Math.round(next.height * scale));
|
||||
if (changedField === 'height') {
|
||||
next.width = Math.max(12, Math.round(next.height * aspect.ratio));
|
||||
} else {
|
||||
next.height = Math.max(12, Math.round(next.width / aspect.ratio));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next = fitRegionToCanvas(next, aspect);
|
||||
writeCard(card, next);
|
||||
return next;
|
||||
}
|
||||
|
||||
function updateBackgroundPreview(file) {
|
||||
@@ -932,6 +1134,25 @@
|
||||
lockChip.textContent = 'Locked ' + lockRatio;
|
||||
}
|
||||
|
||||
function updateRegionLockRatioState(card) {
|
||||
var lockRatioInput = card.querySelector('[name="region_lock_ratio[]"]');
|
||||
var regionTypeInput = card.querySelector('[name="region_type[]"]');
|
||||
if (!lockRatioInput || !regionTypeInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isQrCode = String(regionTypeInput.value || '').trim() === 'qr-code';
|
||||
lockRatioInput.disabled = isQrCode;
|
||||
lockRatioInput.setAttribute('aria-disabled', isQrCode ? 'true' : 'false');
|
||||
|
||||
if (isQrCode) {
|
||||
lockRatioInput.value = '1:1';
|
||||
lockRatioInput.title = 'QR code regions are fixed to 1:1.';
|
||||
} else {
|
||||
lockRatioInput.removeAttribute('title');
|
||||
}
|
||||
}
|
||||
|
||||
function getRegionLockRatio(card) {
|
||||
return normalizeLockRatio(card.querySelector('[name="region_lock_ratio[]"]').value);
|
||||
}
|
||||
@@ -941,22 +1162,10 @@
|
||||
}
|
||||
|
||||
function syncLockedDimensions(card, changedField) {
|
||||
var lockRatio = getRegionLockRatio(card);
|
||||
var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
|
||||
if (!aspect) {
|
||||
return;
|
||||
}
|
||||
|
||||
var widthInput = card.querySelector('[name="region_width[]"]');
|
||||
var heightInput = card.querySelector('[name="region_height[]"]');
|
||||
var width = Math.max(1, Number(widthInput.value || 0));
|
||||
var height = Math.max(1, Number(heightInput.value || 0));
|
||||
|
||||
if (changedField === 'width') {
|
||||
heightInput.value = Math.max(1, Math.round(width / aspect.ratio));
|
||||
} else if (changedField === 'height') {
|
||||
widthInput.value = Math.max(1, Math.round(height * aspect.ratio));
|
||||
}
|
||||
normalizeRegionCard(card, changedField);
|
||||
updateRegionLockBadge(card);
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
}
|
||||
|
||||
function getRegionChipLabel(regionType) {
|
||||
@@ -964,7 +1173,7 @@
|
||||
return window.pulseRegionTypes.getRegionChipLabel(regionType);
|
||||
}
|
||||
|
||||
return regionType === 'image' ? 'Image' : regionType === 'video' ? 'Video' : regionType === 'webpage' ? 'Webpage' : regionType === 'html' ? 'HTML' : regionType === 'rtmp' ? 'RTMP' : regionType === 'rss' ? 'RSS' : 'Text';
|
||||
return 'Text';
|
||||
}
|
||||
|
||||
function populateRegionCard(card, region) {
|
||||
@@ -995,7 +1204,7 @@
|
||||
}
|
||||
var regionLockRatioInput = card.querySelector('[name="region_lock_ratio[]"]');
|
||||
if (regionLockRatioInput) {
|
||||
regionLockRatioInput.value = normalizeLockRatio(region.lock_ratio);
|
||||
regionLockRatioInput.value = normalizeLockRatio(region.lock_ratio || (String(region.region_type || '').trim() === 'qr-code' ? '1:1' : ''));
|
||||
}
|
||||
writeAnimationConfig(card, region.animation_json);
|
||||
card.querySelector('[name="region_x[]"]').value = valueOrDefault(region.x, 80);
|
||||
@@ -1003,6 +1212,7 @@
|
||||
card.querySelector('[name="region_z[]"]').value = valueOrDefault(region.z_index, 1);
|
||||
card.querySelector('[name="region_width[]"]').value = valueOrDefault(region.width, 300);
|
||||
card.querySelector('[name="region_height[]"]').value = valueOrDefault(region.height, 120);
|
||||
updateRegionLockRatioState(card);
|
||||
updateRegionLockBadge(card);
|
||||
}
|
||||
|
||||
@@ -1053,6 +1263,7 @@
|
||||
var animationResetButtons = card.querySelectorAll('[data-animation-reset-button]');
|
||||
var animationAdvancedButton = card.querySelector('[data-animation-advanced-button]');
|
||||
nameInput.addEventListener('input', function () {
|
||||
regionNameValidationToastShown = false;
|
||||
syncRegionIdentity(card, nameInput.value);
|
||||
updateRegionLabel(card);
|
||||
validateRegionNames();
|
||||
@@ -1062,6 +1273,7 @@
|
||||
Array.prototype.forEach.call(animationPresetInputs, function (input) {
|
||||
input.addEventListener('change', function () {
|
||||
setAnimationPreset(card, input.getAttribute('data-animation-preset'), input.value);
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
});
|
||||
});
|
||||
@@ -1069,6 +1281,7 @@
|
||||
button.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
setAnimationPreset(card, button.getAttribute('data-animation-reset-button'), 'none');
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
});
|
||||
});
|
||||
@@ -1083,26 +1296,17 @@
|
||||
}
|
||||
if (lockRatioInput) {
|
||||
lockRatioInput.addEventListener('input', function () {
|
||||
updateRegionLockBadge(card);
|
||||
requestOverlayRender();
|
||||
syncLockedDimensions(card, 'width');
|
||||
});
|
||||
}
|
||||
if (widthInput) {
|
||||
widthInput.addEventListener('input', function () {
|
||||
if (isRegionLocked(card)) {
|
||||
syncLockedDimensions(card, 'width');
|
||||
updateRegionLockBadge(card);
|
||||
}
|
||||
requestOverlayRender();
|
||||
syncLockedDimensions(card, 'width');
|
||||
});
|
||||
}
|
||||
if (heightInput) {
|
||||
heightInput.addEventListener('input', function () {
|
||||
if (isRegionLocked(card)) {
|
||||
syncLockedDimensions(card, 'height');
|
||||
updateRegionLockBadge(card);
|
||||
}
|
||||
requestOverlayRender();
|
||||
syncLockedDimensions(card, 'height');
|
||||
});
|
||||
}
|
||||
card.addEventListener('click', function (event) {
|
||||
@@ -1114,6 +1318,7 @@
|
||||
card.querySelector('[data-region-remove-button]').addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
card.remove();
|
||||
markTemplateFormDirty();
|
||||
if (!getCards().length) {
|
||||
selectedIndex = -1;
|
||||
} else if (selectedIndex >= getCards().length) {
|
||||
@@ -1217,6 +1422,29 @@
|
||||
renderOverlay();
|
||||
}
|
||||
|
||||
function observeStageResize() {
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.ResizeObserver) {
|
||||
stageResizeObserver = new window.ResizeObserver(function () {
|
||||
requestOverlayRender();
|
||||
});
|
||||
stageResizeObserver.observe(stage);
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('resize', requestOverlayRender);
|
||||
}
|
||||
|
||||
function markTemplateFormDirty() {
|
||||
if (!templateForm) {
|
||||
return;
|
||||
}
|
||||
templateForm.dataset.dirty = 'true';
|
||||
}
|
||||
|
||||
function setSelected(index) {
|
||||
if (previewState.active) {
|
||||
return;
|
||||
@@ -1246,6 +1474,7 @@
|
||||
var card = makeRegionCard(region);
|
||||
setRegionRemovalState(card);
|
||||
regionList.appendChild(card);
|
||||
markTemplateFormDirty();
|
||||
setSelected(getCards().length - 1);
|
||||
}
|
||||
|
||||
@@ -1257,9 +1486,9 @@
|
||||
}
|
||||
|
||||
function createDefaultRegion(type) {
|
||||
var count = getCards().length + 1;
|
||||
var name = 'region_' + count;
|
||||
var size = getDefaultRegionSize(type, '');
|
||||
var name = getNextDefaultRegionName(type);
|
||||
var lockRatio = String(type || '').trim() === 'qr-code' ? '1:1' : '';
|
||||
var size = getDefaultRegionSize(type, lockRatio);
|
||||
return {
|
||||
region_key: name,
|
||||
label: name,
|
||||
@@ -1269,7 +1498,7 @@
|
||||
y: 80,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
lock_ratio: '',
|
||||
lock_ratio: lockRatio,
|
||||
z_index: 1
|
||||
};
|
||||
}
|
||||
@@ -1307,8 +1536,9 @@
|
||||
var width = Math.abs(draft.end.x - draft.start.x);
|
||||
var height = Math.abs(draft.end.y - draft.start.y);
|
||||
if (width >= 8 && height >= 8) {
|
||||
var region = clampRegion({ x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: width, height: height });
|
||||
addRegion({ region_key: 'region_' + (getCards().length + 1), label: 'Region ' + (getCards().length + 1), region_type: 'text', x: region.x, y: region.y, width: region.width, height: region.height, z_index: 1 });
|
||||
var region = fitRegionToCanvas({ x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: width, height: height });
|
||||
var name = getNextDefaultRegionName('text');
|
||||
addRegion({ region_key: name, label: name, region_type: 'text', x: region.x, y: region.y, width: region.width, height: region.height, z_index: 1 });
|
||||
}
|
||||
draft = null;
|
||||
requestOverlayRender();
|
||||
@@ -1336,6 +1566,7 @@
|
||||
var nextX = clamp(startRegion.x + dx, 0, maxX);
|
||||
var nextY = clamp(startRegion.y + dy, 0, maxY);
|
||||
writeCard(cardAt(index), { x: nextX, y: nextY, width: startRegion.width, height: startRegion.height });
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
}
|
||||
function upHandler() {
|
||||
@@ -1439,8 +1670,9 @@
|
||||
}
|
||||
if (next.width < 12) { if (dir.indexOf('w') !== -1) { next.x -= 12 - next.width; } next.width = 12; }
|
||||
if (next.height < 12) { if (dir.indexOf('n') !== -1) { next.y -= 12 - next.height; } next.height = 12; }
|
||||
next = clampRegion(next);
|
||||
next = fitRegionToCanvas(next, aspect);
|
||||
writeCard(cardAt(index), { x: next.x, y: next.y, width: next.width, height: next.height });
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
}
|
||||
function upHandler() {
|
||||
@@ -1492,11 +1724,19 @@
|
||||
animationAdvancedModal.addEventListener('hidden.bs.modal', function () {
|
||||
activeAnimationCard = null;
|
||||
});
|
||||
animationAdvancedModal.addEventListener('input', function (event) {
|
||||
clampAnimationModalField(event.target);
|
||||
markTemplateFormDirty();
|
||||
});
|
||||
animationAdvancedModal.addEventListener('change', function () {
|
||||
markTemplateFormDirty();
|
||||
});
|
||||
var animationModalApplyButton = animationAdvancedModal.querySelector('[data-animation-modal-apply]');
|
||||
var animationModalResetButton = animationAdvancedModal.querySelector('[data-animation-modal-reset]');
|
||||
if (animationModalApplyButton) {
|
||||
animationModalApplyButton.addEventListener('click', function () {
|
||||
saveAnimationModal();
|
||||
markTemplateFormDirty();
|
||||
if (window.pulseModal) {
|
||||
window.pulseModal.hide(animationAdvancedModal);
|
||||
}
|
||||
@@ -1508,6 +1748,7 @@
|
||||
return;
|
||||
}
|
||||
writeAnimationConfig(activeAnimationCard, normalizeAnimationConfig('{}'));
|
||||
markTemplateFormDirty();
|
||||
populateAnimationModal(activeAnimationCard);
|
||||
requestOverlayRender();
|
||||
});
|
||||
@@ -1541,6 +1782,19 @@
|
||||
setSelected(-1);
|
||||
});
|
||||
if (templateForm) {
|
||||
templateForm.addEventListener('invalid', function (event) {
|
||||
var target = event.target;
|
||||
if (!target || !target.validationMessage || !target.validationMessage.match(/Region names must be unique on this template\.|Region name is required\./)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.closest && !target.closest('.region-item')) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyRegionNameValidationError(target.validationMessage);
|
||||
}, true);
|
||||
|
||||
templateForm.addEventListener('formdata', function (event) {
|
||||
if (previewState.active) {
|
||||
event.preventDefault();
|
||||
@@ -1552,16 +1806,13 @@
|
||||
return;
|
||||
}
|
||||
getCards().forEach(function (card) {
|
||||
if (isRegionLocked(card)) {
|
||||
syncLockedDimensions(card, 'width');
|
||||
updateRegionLockBadge(card);
|
||||
}
|
||||
normalizeRegionCard(card, 'width');
|
||||
});
|
||||
syncCanvasSizeSelection();
|
||||
event.formData.set('regions_json', JSON.stringify(getCards().map(readCard)));
|
||||
});
|
||||
|
||||
templateForm.addEventListener('submit', function () {
|
||||
templateForm.addEventListener('submit', function (event) {
|
||||
if (previewState.active) {
|
||||
return;
|
||||
}
|
||||
@@ -1570,10 +1821,7 @@
|
||||
return;
|
||||
}
|
||||
getCards().forEach(function (card) {
|
||||
if (isRegionLocked(card)) {
|
||||
syncLockedDimensions(card, 'width');
|
||||
updateRegionLockBadge(card);
|
||||
}
|
||||
normalizeRegionCard(card, 'width');
|
||||
});
|
||||
syncCanvasSizeSelection();
|
||||
regionsJsonInput.value = JSON.stringify(getCards().map(readCard));
|
||||
@@ -1613,5 +1861,6 @@
|
||||
renderRegionList(existingRegions);
|
||||
syncCanvasSizeSelection();
|
||||
updateStageBackgroundColor();
|
||||
observeStageResize();
|
||||
render();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user