Files
pulse-signage/src/web/public/js/slides/slide-form-editor.js
T
lzstealth 7bb34f40fe
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m14s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s
Release 2.8.3
2026-08-17 00:08:05 +01:00

816 lines
28 KiB
JavaScript

export function createSlideFormEditorController(options) {
var settings = options || {};
var templateFields = settings.templateFields || null;
var templateSelectorLock = settings.templateSelectorLock || null;
var requestPreviewRender = typeof settings.requestPreviewRender === 'function' ? settings.requestPreviewRender : function () {};
var markFormDirty = typeof settings.markFormDirty === 'function' ? settings.markFormDirty : function () {};
var defaultFontSize = Math.max(1, Number(settings.defaultFontSize || 32));
var fontFamilyFormats = String(settings.fontFamilyFormats || '').trim();
var fontStylesheetHref = String(settings.fontStylesheetHref || '').trim();
var defaultEditorFontFamily = 'Arial, Helvetica, sans-serif';
var editorInstances = new Map();
var editorHeights = new Map();
var editorSizeControlsBound = false;
var getRegionTypeModule = typeof settings.getRegionTypeModule === 'function' ? settings.getRegionTypeModule : function () {
return null;
};
var imageUploadUrl = String(settings.imageUploadUrl || '/slides/uploads').trim() || '/slides/uploads';
var imageUploadMaxBytes = Math.max(1, Number(settings.imageUploadMaxBytes || 2 * 1024 * 1024));
var imageUploadLimitLabel = String(settings.imageUploadLimitLabel || '').trim() || Math.max(1, Math.round(imageUploadMaxBytes / (1024 * 1024))) + ' MB';
var imageUploadContext = String(settings.imageUploadContext || 'wysiwyg').trim() || 'wysiwyg';
var configuredImageMimeTypes = Array.isArray(settings.uploadMimeTypes)
? settings.uploadMimeTypes.filter(function (mimeType) { return String(mimeType || '').indexOf('image/') === 0; })
: [];
var imageUploadAllowedMimeTypes = configuredImageMimeTypes.length
? configuredImageMimeTypes
: ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml'];
var imageUploadAllowedExtensions = imageUploadAllowedMimeTypes.map(function (mimeType) {
var subtype = String(mimeType).split('/')[1] || '';
return subtype === 'jpeg' ? 'jpg' : subtype === 'svg+xml' ? 'svg' : subtype;
});
var imageUploadFileTypes = imageUploadAllowedExtensions.join(',');
var wysiwygEditorHeightStep = Math.max(1, Number(settings.wysiwygEditorHeightStep || 80));
var wysiwygEditorHeightMin = Math.max(1, Number(settings.wysiwygEditorHeightMin || 240));
var wysiwygEditorHeightMax = Math.max(wysiwygEditorHeightMin, Number(settings.wysiwygEditorHeightMax || 960));
var wysiwygEditorHeightDefault = Math.max(wysiwygEditorHeightMin, Number(settings.wysiwygEditorHeightDefault || 360));
var editorImageUploadPaths = new Set();
var committedEditorImageUploadPaths = new Set();
var pendingEditorImageUploadCleanupPaths = new Set();
var getEditorBackgroundColor = typeof settings.getEditorBackgroundColor === 'function' ? settings.getEditorBackgroundColor : function () {
return '#111111';
};
function normalizeEditorData(value) {
return String(value || '');
}
function isEmptyRichTextValue(value) {
var raw = String(value === undefined || value === null ? '' : value).trim();
if (!raw) {
return true;
}
if (/<img\b/i.test(raw)) {
return false;
}
var stripped = raw
.replace(/<\s*br\s*\/?>/gi, '')
.replace(/<p[^>]*>(?:\s|&nbsp;|<br\s*\/?>)*<\/p>/gi, '')
.replace(/<[^>]+>/g, '')
.replace(/&nbsp;/gi, '')
.trim();
return !stripped;
}
function normalizeFontSizeValue(value) {
var raw = String(value || '').trim().toLowerCase();
if (/^\d+(?:\.\d+)?px$/.test(raw)) {
return String(Math.max(1, Math.round(Number(raw.replace(/px$/, '')))));
}
if (/^\d+(?:\.\d+)?$/.test(raw)) {
return String(Math.max(1, Math.round(Number(raw))));
}
return '';
}
function normalizeEditorHeightValue(value) {
var numericValue = Number(value);
if (!Number.isFinite(numericValue) || numericValue <= 0) {
numericValue = wysiwygEditorHeightDefault;
}
return Math.min(wysiwygEditorHeightMax, Math.max(wysiwygEditorHeightMin, Math.round(numericValue)));
}
function getEditorHolder(regionId) {
if (!templateFields) {
return null;
}
return templateFields.querySelector('.editor-holder[data-region-id="' + regionId + '"]');
}
function getEditorHeight(regionId) {
var key = String(regionId || '');
var storedHeight = editorHeights.get(key);
if (storedHeight !== undefined && storedHeight !== null) {
return normalizeEditorHeightValue(storedHeight);
}
var holder = getEditorHolder(key);
var holderHeight = holder && holder.dataset ? holder.dataset.editorHeight : '';
var normalizedHeight = normalizeEditorHeightValue(holderHeight || wysiwygEditorHeightDefault);
editorHeights.set(key, normalizedHeight);
return normalizedHeight;
}
function applyEditorHeight(regionId, height, options) {
var key = String(regionId || '');
var normalizedHeight = normalizeEditorHeightValue(height);
var holder = getEditorHolder(key);
var editor = editorInstances.get(key);
var container = editor && typeof editor.getContainer === 'function' ? editor.getContainer() : null;
var shouldRequestPreview = !(options && options.silent);
editorHeights.set(key, normalizedHeight);
if (holder) {
holder.dataset.editorHeight = String(normalizedHeight);
holder.style.height = normalizedHeight + 'px';
holder.style.minHeight = normalizedHeight + 'px';
}
if (container) {
container.style.height = normalizedHeight + 'px';
container.style.minHeight = normalizedHeight + 'px';
}
if (editor && typeof editor.dispatch === 'function') {
editor.dispatch('ResizeEditor');
}
if (shouldRequestPreview) {
requestPreviewRender();
}
}
function adjustEditorHeight(regionId, delta) {
applyEditorHeight(regionId, getEditorHeight(regionId) + Number(delta || 0));
}
function bindEditorSizeControls() {
if (editorSizeControlsBound || !templateFields) {
return;
}
editorSizeControlsBound = true;
templateFields.addEventListener('click', function (event) {
var button = event.target && typeof event.target.closest === 'function' ? event.target.closest('[data-editor-size-action]') : null;
if (!button) {
return;
}
var action = String(button.getAttribute('data-editor-size-action') || '').trim();
if (action !== 'increase' && action !== 'decrease') {
return;
}
var card = typeof button.closest === 'function' ? button.closest('[data-region-id]') : null;
var regionId = card ? card.getAttribute('data-region-id') : '';
if (!regionId) {
return;
}
event.preventDefault();
adjustEditorHeight(regionId, action === 'increase' ? wysiwygEditorHeightStep : -wysiwygEditorHeightStep);
});
}
function getEditorHiddenInput(regionId) {
if (!templateFields) {
return null;
}
var card = templateFields.querySelector('[data-region-id="' + regionId + '"]');
return card ? card.querySelector('input[type="hidden"][name="region_text_' + regionId + '"]') : null;
}
function getEditorFontSizeHiddenInput(regionId) {
if (!templateFields) {
return null;
}
var card = templateFields.querySelector('[data-region-id="' + regionId + '"]');
return card ? card.querySelector('input[name="region_font_size_' + regionId + '"]') : null;
}
function getTinymce() {
return window.tinymce || null;
}
function getEditorContentSafely(editor, fallbackValue) {
if (!editor || typeof editor.getContent !== 'function') {
return String(fallbackValue || '');
}
try {
return String(editor.getContent({ format: 'html' }));
} catch (_error) {
return String(fallbackValue || '');
}
}
function getThemeName() {
var theme = String(document.documentElement && document.documentElement.dataset && document.documentElement.dataset.bsTheme || 'light').trim().toLowerCase();
if (theme === 'auto') {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return theme === 'dark' ? 'dark' : 'light';
}
function getTinyMceThemeAssets() {
var themeName = getThemeName();
var skinName = themeName === 'dark' ? 'oxide-dark' : 'oxide';
var skinBase = '/assets/vendor/tinymce/skins/ui/' + skinName;
return {
themeName: themeName,
skinName: skinName,
skinUrl: skinBase,
contentCss: skinBase + '/content.css',
bodyClass: themeName === 'dark' ? 'tinymce-theme-dark' : 'tinymce-theme-light'
};
}
function syncEditorState(regionId, editor, shouldLock, hydrated) {
var hidden = getEditorHiddenInput(regionId);
var sourceElm = editor && editor.targetElm ? editor.targetElm : null;
var fallbackContent = hidden && hidden.value !== undefined ? hidden.value : (sourceElm && sourceElm.value !== undefined ? sourceElm.value : '');
if (editor && typeof editor.save === 'function') {
if (typeof editor.blur === 'function') {
editor.blur();
}
editor.save();
}
var content = sourceElm && sourceElm.value !== undefined ? String(sourceElm.value || '') : getEditorContentSafely(editor, fallbackContent);
content = isEmptyRichTextValue(content) ? '' : content;
if (hidden) {
hidden.value = content;
}
if (sourceElm) {
sourceElm.value = content;
}
if (typeof editor.nodeChanged === 'function') {
editor.nodeChanged();
}
if (typeof editor.setDirty === 'function') {
editor.setDirty(true);
}
if (shouldLock && templateSelectorLock && typeof templateSelectorLock.arm === 'function') {
templateSelectorLock.arm();
}
if (shouldLock && templateSelectorLock && typeof templateSelectorLock.markEdited === 'function') {
templateSelectorLock.markEdited();
}
if (hydrated) {
requestPreviewRender();
}
}
function getEditorRegionType(card) {
if (!card) {
return '';
}
if (card.dataset && String(card.dataset.regionMediaType || '').trim()) {
return String(card.dataset.regionMediaType || '').trim();
}
var regionTypeInput = card.querySelector ? card.querySelector('input[type="hidden"][name="region_type[]"]') : null;
return String(regionTypeInput && regionTypeInput.value || '').trim();
}
function getEditorContentStyle(regionType) {
var module = getRegionTypeModule(regionType);
if (module && typeof module.getEditorContentStyle === 'function') {
return String(module.getEditorContentStyle() || '').trim();
}
return '';
}
function syncEditorFontSizeHidden(regionId) {
var fontSizeHidden = getEditorFontSizeHiddenInput(regionId);
if (!fontSizeHidden) {
return;
}
fontSizeHidden.value = normalizeFontSizeValue(fontSizeHidden.value) || String(defaultFontSize);
}
function getEditorBackgroundColorValue() {
var value = String(getEditorBackgroundColor() || '').trim();
return value || '#111111';
}
function getFontFamilyFormats() {
if (fontFamilyFormats) {
return fontFamilyFormats;
}
return [
'Default=inherit',
'Arial=Arial,Helvetica,sans-serif',
'Comic Sans MS=Comic Sans MS,cursive,sans-serif',
'Courier New=Courier New,Courier,monospace',
'Georgia=Georgia,serif',
'Helvetica=Helvetica,Arial,sans-serif',
'Impact=Impact,Charcoal,sans-serif',
'Lucida Sans Unicode=Lucida Sans Unicode,Lucida Grande,sans-serif',
'Palatino Linotype=Palatino Linotype,Book Antiqua,Palatino,serif',
'Tahoma=Tahoma,Geneva,sans-serif',
'Times New Roman=Times New Roman,Times,serif',
'Trebuchet MS=Trebuchet MS,Helvetica,sans-serif',
'Verdana=Verdana,Geneva,sans-serif'
].join(';');
}
function getContentCss() {
var themeAssets = getTinyMceThemeAssets();
return fontStylesheetHref
? [themeAssets.contentCss, fontStylesheetHref]
: themeAssets.contentCss;
}
function normalizeUploadPath(value) {
return String(value || '').trim();
}
function collectEditorImageUploadPaths(html) {
var matches = String(html || '').match(/\/media\/uploads\/[^^\s"'<>]+/g);
return matches ? Array.from(new Set(matches.map(normalizeUploadPath).filter(Boolean))) : [];
}
function collectCurrentEditorImageUploadPaths() {
var currentPaths = new Set();
editorInstances.forEach(function (editor, regionId) {
var hidden = getEditorHiddenInput(regionId);
var sourceElm = editor && editor.targetElm ? editor.targetElm : null;
var fallbackContent = hidden && hidden.value !== undefined ? hidden.value : (sourceElm && sourceElm.value !== undefined ? sourceElm.value : '');
collectEditorImageUploadPaths(getEditorContentSafely(editor, fallbackContent)).forEach(function (path) {
currentPaths.add(path);
});
});
return currentPaths;
}
function getImageUploadCleanupPaths() {
var currentPaths = collectCurrentEditorImageUploadPaths();
return Array.from(editorImageUploadPaths).filter(function (path) {
return !currentPaths.has(path);
});
}
function getCommittedImageUploadCleanupPaths() {
var currentPaths = collectCurrentEditorImageUploadPaths();
return Array.from(committedEditorImageUploadPaths).filter(function (path) {
return !currentPaths.has(path);
});
}
function getPendingImageUploadPaths() {
return Array.from(editorImageUploadPaths).filter(function (path) {
return !committedEditorImageUploadPaths.has(path);
});
}
function queueImageUploadCleanupPaths(paths) {
Array.from(new Set((paths || []).map(normalizeUploadPath).filter(Boolean))).forEach(function (path) {
pendingEditorImageUploadCleanupPaths.add(path);
});
}
function getPendingImageUploadCleanupPaths() {
var currentPaths = collectCurrentEditorImageUploadPaths();
return Array.from(pendingEditorImageUploadCleanupPaths).filter(function (path) {
return !currentPaths.has(path);
});
}
function markImageUploadsCommitted() {
committedEditorImageUploadPaths = collectCurrentEditorImageUploadPaths();
}
function getAllImageUploadPaths() {
return Array.from(editorImageUploadPaths);
}
function clearImageUploadPaths() {
editorImageUploadPaths.clear();
committedEditorImageUploadPaths.clear();
pendingEditorImageUploadCleanupPaths.clear();
}
function getFileExtension(fileName) {
var match = String(fileName || '').toLowerCase().match(/\.([a-z0-9]+)$/);
return match ? String(match[1] || '') : '';
}
function getImageUploadValidationMessage(blobInfo) {
var blob = blobInfo && typeof blobInfo.blob === 'function' ? blobInfo.blob() : null;
var fileName = blobInfo && typeof blobInfo.filename === 'function' ? String(blobInfo.filename() || '') : '';
var mimeType = blob && blob.type ? String(blob.type || '').trim().toLowerCase() : '';
var extension = getFileExtension(fileName);
if (!blob) {
return 'No image file was provided.';
}
if (Number(blob.size || 0) > imageUploadMaxBytes) {
return 'Image must be ' + imageUploadLimitLabel + ' or smaller. Larger images should use the dedicated Image region.';
}
if (mimeType && imageUploadAllowedMimeTypes.indexOf(mimeType) === -1) {
return 'This editor accepts PNG, JPG, GIF, WebP, or SVG images.';
}
if (!mimeType && extension && imageUploadAllowedExtensions.indexOf(extension) === -1) {
return 'This editor accepts PNG, JPG, GIF, WebP, or SVG images.';
}
if (!mimeType && !extension) {
return 'This editor accepts PNG, JPG, GIF, WebP, or SVG images.';
}
return '';
}
function uploadEditorImage(blobInfo, progress) {
var validationError = getImageUploadValidationMessage(blobInfo);
if (validationError) {
return Promise.reject(new Error(validationError));
}
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
var formData = new FormData();
var blob = blobInfo.blob();
var fileName = typeof blobInfo.filename === 'function' ? String(blobInfo.filename() || 'image') : 'image';
formData.append('file', blob, fileName);
xhr.open('POST', imageUploadUrl, true);
xhr.responseType = 'text';
xhr.withCredentials = true;
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
xhr.setRequestHeader('X-Upload-Context', imageUploadContext);
xhr.upload.onprogress = function (event) {
if (!progress) {
return;
}
if (!event || !event.lengthComputable || !event.total) {
progress(0);
return;
}
progress(Math.round((event.loaded / event.total) * 100));
};
xhr.onload = function () {
var responseText = String(xhr.responseText || '');
if (xhr.status < 200 || xhr.status >= 300) {
reject(new Error(responseText || 'Unable to upload image.'));
return;
}
if (responseText.trim().toLowerCase().indexOf('<!doctype html') === 0 || responseText.toLowerCase().indexOf('<html') !== -1) {
reject(new Error('Upload redirected to an HTML page. Please sign in again and retry.'));
return;
}
var payload = {};
try {
payload = JSON.parse(responseText || '{}') || {};
} catch (_error) {
reject(new Error('Unable to parse the upload response.'));
return;
}
if (!payload.path) {
reject(new Error('Unable to upload image.'));
return;
}
if (progress) {
progress(100);
}
editorImageUploadPaths.add(String(payload.path || '').trim());
resolve(String(payload.path || ''));
};
xhr.onerror = function () {
reject(new Error('Unable to upload image.'));
};
xhr.ontimeout = function () {
reject(new Error('Upload timed out. Please try again.'));
};
xhr.send(formData);
});
}
function attachEditorEvents(regionId, editor) {
var hidden = getEditorHiddenInput(regionId);
var source = editor && editor.targetElm ? editor.targetElm : null;
var hydrated = false;
function syncState(shouldLock) {
var currentEditor = editorInstances.get(regionId);
if (currentEditor !== editor) {
return;
}
syncEditorState(regionId, editor, shouldLock, hydrated);
if (!hydrated) {
requestPreviewRender();
return;
}
}
editor.on('init', function () {
var content = getEditorContentSafely(editor, hidden && hidden.value !== undefined ? hidden.value : (source && source.value !== undefined ? source.value : ''));
content = isEmptyRichTextValue(content) ? '' : content;
if (hidden) {
hidden.value = content;
}
if (source) {
source.value = content;
}
syncEditorFontSizeHidden(regionId);
hydrated = true;
requestPreviewRender();
});
['change', 'keyup', 'undo', 'redo', 'Paste', 'input'].forEach(function (eventName) {
editor.on(eventName, function () {
syncState(true);
if (hydrated) {
markFormDirty();
}
});
});
}
function createEditorForHolder(holder) {
if (!holder) {
return Promise.resolve(null);
}
var regionId = holder.getAttribute('data-region-id');
var card = holder.closest ? holder.closest('[data-region-id]') : null;
var source = holder.querySelector('.editor-source');
var hidden = getEditorHiddenInput(regionId);
var tinymce = getTinymce();
var themeAssets = getTinyMceThemeAssets();
var editorContentStyle = getEditorContentStyle(getEditorRegionType(card));
if (!source || !tinymce || typeof tinymce.init !== 'function') {
return Promise.resolve(null);
}
source.value = normalizeEditorData(source && source.value !== undefined && source.value !== null ? source.value : (hidden ? hidden.value : ''));
if (!source.id) {
source.id = 'slide-editor-region-' + regionId;
}
applyEditorHeight(regionId, getEditorHeight(regionId), { silent: true });
function registerInlineFormat(editor, formatName, styles) {
editor.formatter.register(formatName, {
inline: 'span',
styles: styles
});
}
function applyInlineFormat(regionId, editor, formatName) {
if (editor && editor.undoManager && typeof editor.undoManager.transact === 'function') {
editor.undoManager.transact(function () {
editor.formatter.apply(formatName);
});
} else {
editor.formatter.apply(formatName);
}
syncEditorState(regionId, editor, false, true);
}
function applyEditorCommand(regionId, editor, commandName) {
if (editor && editor.undoManager && typeof editor.undoManager.transact === 'function') {
editor.undoManager.transact(function () {
editor.execCommand(commandName);
});
} else {
editor.execCommand(commandName);
}
syncEditorState(regionId, editor, false, true);
}
return tinymce.init({
target: source,
menubar: false,
branding: false,
promotion: false,
relative_urls: false,
remove_script_host: false,
convert_urls: true,
paste_data_images: false,
automatic_uploads: true,
images_file_types: imageUploadFileTypes,
images_upload_handler: uploadEditorImage,
plugins: 'lists code advlist fullscreen table image',
toolbar: 'undo redo | fontfamily fontsizeinput | forecolor backcolor bold italic underlineformats removeformat | align lineheight indent outdent bullist numlist table image chip | fullscreen',
toolbar_mode: 'sliding',
license_key: 'gpl',
height: getEditorHeight(regionId),
min_height: wysiwygEditorHeightMin,
table_default_attributes: {
border: '1',
cellpadding: '0',
cellspacing: '0'
},
skin: themeAssets.skinName,
skin_url: themeAssets.skinUrl,
content_css: getContentCss(),
body_class: themeAssets.bodyClass,
content_style: 'body { font-family: ' + defaultEditorFontFamily + '; font-size: 32px; line-height: 1.5; background-color: ' + getEditorBackgroundColorValue() + '; } p { margin: 1em 0; } p:first-child { margin-top: 0; } p:last-child { margin-bottom: 1em; } table { border-collapse: collapse; border-spacing: 0; width: 100%; } td, th { border: 1px solid currentColor; padding: 0; vertical-align: top; } th { font-weight: 700; }' + (editorContentStyle ? ' ' + editorContentStyle : ''),
font_family_formats: getFontFamilyFormats(),
font_size_input_default_unit: 'px',
invalid_elements: 'a',
forced_root_block: 'p',
force_br_newlines: false,
newline_behavior: 'default',
placeholder: String(source.getAttribute('placeholder') || '').trim(),
setup: function (editor) {
editorInstances.set(regionId, editor);
editor.ui.registry.addButton('chip', {
icon: 'addtag',
tooltip: 'Chip style',
onAction: function () {
applyInlineFormat(regionId, editor, 'chip');
}
});
editor.ui.registry.addSplitButton('underlineformats', {
icon: 'underline',
tooltip: 'Text formats',
onAction: function () {
applyEditorCommand(regionId, editor, 'Underline');
},
onItemAction: function (_api, value) {
if (value === 'strikethrough') {
applyEditorCommand(regionId, editor, 'Strikethrough');
return;
}
if (value === 'subscript') {
applyEditorCommand(regionId, editor, 'Subscript');
return;
}
if (value === 'superscript') {
applyEditorCommand(regionId, editor, 'Superscript');
}
},
fetch: function (callback) {
callback([
{
type: 'choiceitem',
value: 'strikethrough',
icon: 'strikethrough',
text: 'Strikethrough',
},
{
type: 'choiceitem',
value: 'subscript',
icon: 'subscript',
text: 'Subscript',
},
{
type: 'choiceitem',
value: 'superscript',
icon: 'superscript',
text: 'Superscript',
}
]);
}
});
editor.on('init', function () {
registerInlineFormat(editor, 'chip', {
display: 'inline-block',
padding: '0.2em 0.6em',
borderRadius: '999px',
fontWeight: '700',
letterSpacing: '0.02em'
});
});
attachEditorEvents(regionId, editor);
}
}).then(function (editors) {
var editor = Array.isArray(editors) && editors.length ? editors[0] : tinymce.get(source.id);
if (!editor) {
return null;
}
editorInstances.set(regionId, editor);
applyEditorHeight(regionId, getEditorHeight(regionId), { silent: true });
if (editor.targetElm) {
editor.targetElm.value = editor.getContent({ format: 'html' });
}
markImageUploadsCommitted();
return editor;
}).catch(function (error) {
console.error('Failed to initialize TinyMCE.', error);
return null;
});
}
function renderEditors() {
if (!templateFields) {
return Promise.resolve([]);
}
bindEditorSizeControls();
watchThemeChanges();
var holders = Array.prototype.slice.call(templateFields.querySelectorAll('.editor-holder'));
return Promise.all(holders.map(function (holder) {
return createEditorForHolder(holder);
}));
}
function syncEditors() {
if (!templateFields) {
return Promise.resolve([]);
}
var saves = Array.prototype.map.call(templateFields.querySelectorAll('.editor-holder'), function (holder) {
var regionId = holder.getAttribute('data-region-id');
var editor = editorInstances.get(regionId);
if (!editor) {
return Promise.resolve();
}
syncEditorState(regionId, editor, false, true);
return Promise.resolve();
});
return Promise.all(saves).then(function (results) {
getCommittedImageUploadCleanupPaths();
return results;
});
}
function destroyEditors() {
editorInstances.forEach(function (editor) {
if (editor && typeof editor.remove === 'function') {
editor.remove();
}
});
editorInstances.clear();
var tinymce = getTinymce();
if (tinymce && typeof tinymce.remove === 'function') {
tinymce.remove();
}
}
var themeObserver = null;
function watchThemeChanges() {
if (themeObserver || !window.MutationObserver || !templateFields) {
return;
}
themeObserver = new MutationObserver(function () {
destroyEditors();
renderEditors();
});
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-bs-theme']
});
}
return {
renderEditors: renderEditors,
syncEditors: syncEditors,
getImageUploadCleanupPaths: getImageUploadCleanupPaths,
getCommittedImageUploadCleanupPaths: getCommittedImageUploadCleanupPaths,
getPendingImageUploadPaths: getPendingImageUploadPaths,
getPendingImageUploadCleanupPaths: getPendingImageUploadCleanupPaths,
getAllImageUploadPaths: getAllImageUploadPaths,
queueImageUploadCleanupPaths: queueImageUploadCleanupPaths,
markImageUploadsCommitted: markImageUploadsCommitted,
clearImageUploadPaths: clearImageUploadPaths,
destroyEditors: function () {
if (themeObserver) {
themeObserver.disconnect();
themeObserver = null;
}
destroyEditors();
},
watchThemeChanges: watchThemeChanges
};
}