V13: Rich text editor does not show its toolbar on grid layout rte's (#15595)
* synchronize normal rte with grid-rte * restore pinToolbar and unpinToolbar from v10 and update to tinymce v6 and apply to grid-rte * linting * Reverting `pinToolbar` from v8 * remove unused variable --------- Co-authored-by: leekelleher <leekelleher@gmail.com>
This commit is contained in:
@@ -12,22 +12,18 @@ angular.module("umbraco.directives")
|
|||||||
replace: true,
|
replace: true,
|
||||||
link: function (scope, element, attrs) {
|
link: function (scope, element, attrs) {
|
||||||
|
|
||||||
// TODO: A lot of the code below should be shared between the grid rte and the normal rte
|
|
||||||
|
|
||||||
scope.isLoading = true;
|
scope.isLoading = true;
|
||||||
|
|
||||||
var promises = [];
|
|
||||||
|
|
||||||
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
|
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
|
||||||
// because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because
|
// because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because
|
||||||
// we have this mini content editor panel that can be launched with MNTP.
|
// we have this mini content editor panel that can be launched with MNTP.
|
||||||
scope.textAreaHtmlId = scope.uniqueId + "_" + String.CreateGuid();
|
scope.textAreaHtmlId = scope.uniqueId + "_" + String.CreateGuid();
|
||||||
|
|
||||||
var editorConfig = scope.configuration ? scope.configuration : null;
|
let editorConfig = scope.configuration ? scope.configuration : null;
|
||||||
if (!editorConfig || Utilities.isString(editorConfig)) {
|
if (!editorConfig || Utilities.isString(editorConfig)) {
|
||||||
editorConfig = tinyMceService.defaultPrevalues();
|
editorConfig = tinyMceService.defaultPrevalues();
|
||||||
//for the grid by default, we don't want to include the macro or the block-picker toolbar
|
//for the grid by default, we don't want to include the macro or the block-picker toolbar
|
||||||
editorConfig.toolbar = _.without(editorConfig, "umbmacro", "umbblockpicker");
|
editorConfig.toolbar = _.without(editorConfig.toolbar, "umbmacro", "umbblockpicker");
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure the grid's global config is being passed up to the RTE, these 2 properties need to be in this format
|
//ensure the grid's global config is being passed up to the RTE, these 2 properties need to be in this format
|
||||||
@@ -39,46 +35,50 @@ angular.module("umbraco.directives")
|
|||||||
scope.dataTypeKey = scope.datatypeKey; //Yes - this casing is rediculous, but it's because the var starts with `data` so it can't be `data-type-id` :/
|
scope.dataTypeKey = scope.datatypeKey; //Yes - this casing is rediculous, but it's because the var starts with `data` so it can't be `data-type-id` :/
|
||||||
|
|
||||||
//stores a reference to the editor
|
//stores a reference to the editor
|
||||||
var tinyMceEditor = null;
|
let tinyMceEditor = null;
|
||||||
|
|
||||||
|
const assetPromises = [];
|
||||||
|
|
||||||
//queue file loading
|
//queue file loading
|
||||||
tinyMceAssets.forEach(function (tinyJsAsset) {
|
tinyMceAssets.forEach(function (tinyJsAsset) {
|
||||||
promises.push(assetsService.loadJs(tinyJsAsset, scope));
|
assetPromises.push(assetsService.loadJs(tinyJsAsset, scope));
|
||||||
});
|
});
|
||||||
promises.push(tinyMceService.getTinyMceEditorConfig({
|
|
||||||
htmlId: scope.textAreaHtmlId,
|
|
||||||
stylesheets: editorConfig.stylesheets,
|
|
||||||
toolbar: editorConfig.toolbar,
|
|
||||||
mode: editorConfig.mode
|
|
||||||
}));
|
|
||||||
|
|
||||||
$q.all(promises).then(function (result) {
|
//wait for assets to load before proceeding
|
||||||
|
$q.all(assetPromises)
|
||||||
|
.then(function () {
|
||||||
|
return tinyMceService.getTinyMceEditorConfig({
|
||||||
|
htmlId: scope.textAreaHtmlId,
|
||||||
|
stylesheets: editorConfig.stylesheets,
|
||||||
|
toolbar: editorConfig.toolbar,
|
||||||
|
mode: editorConfig.mode
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
var standardConfig = result[promises.length - 1];
|
// Handle additional assets loading depending on the configuration before initializing the editor
|
||||||
|
.then(function (tinyMceConfig) {
|
||||||
|
// Load the plugins.min.js file from the TinyMCE Cloud if a Cloud Api Key is specified
|
||||||
|
if (tinyMceConfig.cloudApiKey) {
|
||||||
|
return assetsService.loadJs(`https://cdn.tiny.cloud/1/${tinyMceConfig.cloudApiKey}/tinymce/${tinymce.majorVersion}.${tinymce.minorVersion}/plugins.min.js`)
|
||||||
|
.then(() => tinyMceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tinyMceConfig;
|
||||||
|
})
|
||||||
|
|
||||||
|
//wait for config to be ready after assets have loaded
|
||||||
|
.then(function (standardConfig) {
|
||||||
|
|
||||||
//create a baseline Config to extend upon
|
//create a baseline Config to extend upon
|
||||||
var baseLineConfigObj = {
|
let baseLineConfigObj = {
|
||||||
maxImageSize: editorConfig.maxImageSize,
|
maxImageSize: editorConfig.maxImageSize
|
||||||
toolbar_sticky: true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Utilities.extend(baseLineConfigObj, standardConfig);
|
|
||||||
|
|
||||||
baseLineConfigObj.setup = function (editor) {
|
baseLineConfigObj.setup = function (editor) {
|
||||||
|
|
||||||
//set the reference
|
//set the reference
|
||||||
tinyMceEditor = editor;
|
tinyMceEditor = editor;
|
||||||
|
|
||||||
//initialize the standard editor functionality for Umbraco
|
|
||||||
tinyMceService.initializeEditor({
|
|
||||||
editor: editor,
|
|
||||||
toolbar: editorConfig.toolbar,
|
|
||||||
model: scope,
|
|
||||||
// Form is found in the scope of the grid controller above us, not in our isolated scope
|
|
||||||
// https://github.com/umbraco/Umbraco-CMS/issues/7461
|
|
||||||
currentForm: angularHelper.getCurrentForm(scope.$parent)
|
|
||||||
});
|
|
||||||
|
|
||||||
//custom initialization for this editor within the grid
|
//custom initialization for this editor within the grid
|
||||||
editor.on('init', function (e) {
|
editor.on('init', function (e) {
|
||||||
|
|
||||||
@@ -96,49 +96,52 @@ angular.module("umbraco.directives")
|
|||||||
}, 400);
|
}, 400);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//initialize the standard editor functionality for Umbraco
|
||||||
|
tinyMceService.initializeEditor({
|
||||||
|
editor: editor,
|
||||||
|
toolbar: editorConfig.toolbar,
|
||||||
|
model: scope,
|
||||||
|
// Form is found in the scope of the grid controller above us, not in our isolated scope
|
||||||
|
// https://github.com/umbraco/Umbraco-CMS/issues/7461
|
||||||
|
currentForm: angularHelper.getCurrentForm(scope.$parent)
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Loads in the editor */
|
Utilities.extend(baseLineConfigObj, standardConfig);
|
||||||
function loadTinyMce() {
|
|
||||||
|
|
||||||
//we need to add a timeout here, to force a redraw so TinyMCE can find
|
|
||||||
//the elements needed
|
|
||||||
$timeout(function () {
|
|
||||||
tinymce.init(baseLineConfigObj);
|
|
||||||
}, 150, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadTinyMce();
|
|
||||||
|
|
||||||
// TODO: This should probably be in place for all RTE, not just for the grid, which means
|
|
||||||
// this code can live in tinyMceService.initializeEditor
|
|
||||||
var tabShownListener = eventsService.on("app.tabChange", function (e, args) {
|
|
||||||
|
|
||||||
var tabId = args.id;
|
|
||||||
var myTabId = element.closest(".umb-tab-pane").attr("rel");
|
|
||||||
|
|
||||||
if (String(tabId) === myTabId) {
|
|
||||||
//the tab has been shown, trigger the mceAutoResize (as it could have timed out before the tab was shown)
|
|
||||||
if (tinyMceEditor !== undefined && tinyMceEditor != null) {
|
|
||||||
tinyMceEditor.execCommand('mceAutoResize', false, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//when the element is disposed we need to unsubscribe!
|
|
||||||
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
|
|
||||||
// element might still be there even after the modal has been hidden.
|
|
||||||
scope.$on('$destroy', function () {
|
|
||||||
eventsService.unsubscribe(tabShownListener);
|
|
||||||
//ensure we unbind this in case the blur doesn't fire above
|
|
||||||
if (tinyMceEditor !== undefined && tinyMceEditor != null) {
|
|
||||||
tinyMceEditor.destroy()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
//we need to add a timeout here, to force a redraw so TinyMCE can find
|
||||||
|
//the elements needed
|
||||||
|
$timeout(function () {
|
||||||
|
tinymce.init(baseLineConfigObj);
|
||||||
|
}, 150);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tabShownListener = eventsService.on("app.tabChange", function (e, args) {
|
||||||
|
|
||||||
|
const tabId = String(args.id);
|
||||||
|
const myTabId = element.closest(".umb-tab-pane").attr("rel");
|
||||||
|
|
||||||
|
if (tabId === myTabId) {
|
||||||
|
//the tab has been shown, trigger the mceAutoResize (as it could have timed out before the tab was shown)
|
||||||
|
if (tinyMceEditor !== undefined && tinyMceEditor != null) {
|
||||||
|
tinyMceEditor.execCommand('mceAutoResize', false, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//when the element is disposed we need to unsubscribe!
|
||||||
|
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
|
||||||
|
// element might still be there even after the modal has been hidden.
|
||||||
|
scope.$on('$destroy', function () {
|
||||||
|
eventsService.unsubscribe(tabShownListener);
|
||||||
|
|
||||||
|
//ensure we unbind this in case the blur doesn't fire above
|
||||||
|
if (tinyMceEditor) {
|
||||||
|
tinyMceEditor.destroy();
|
||||||
|
tinyMceEditor = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -797,6 +797,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Do not add any further controls if the block editor is not available
|
||||||
|
if (!blockEditorApi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
editor.ui.registry.addToggleButton('umbblockpicker', {
|
editor.ui.registry.addToggleButton('umbblockpicker', {
|
||||||
icon: 'visualblocks',
|
icon: 'visualblocks',
|
||||||
tooltip: 'Insert Block',
|
tooltip: 'Insert Block',
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
configuration="model.config.rte"
|
configuration="model.config.rte"
|
||||||
value="control.value"
|
value="control.value"
|
||||||
unique-id="control.$uniqueId"
|
unique-id="control.$uniqueId"
|
||||||
datatype-key="{{model.dataTypeKey}}"
|
datatype-key="{{model.dataTypeKey}}"
|
||||||
ignore-user-start-nodes="{{model.config.ignoreUserStartNodes}}">
|
ignore-user-start-nodes="{{model.config.ignoreUserStartNodes}}">
|
||||||
</grid-rte>
|
</grid-rte>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -195,162 +195,148 @@
|
|||||||
assetPromises.push(assetsService.loadJs(tinyJsAsset, $scope));
|
assetPromises.push(assetsService.loadJs(tinyJsAsset, $scope));
|
||||||
});
|
});
|
||||||
|
|
||||||
const tinyMceConfigDeferred = $q.defer();
|
|
||||||
|
|
||||||
//wait for assets to load before proceeding
|
//wait for assets to load before proceeding
|
||||||
$q.all(assetPromises).then(function () {
|
$q.all(assetPromises)
|
||||||
|
.then(function () {
|
||||||
tinyMceService.getTinyMceEditorConfig({
|
return tinyMceService.getTinyMceEditorConfig({
|
||||||
htmlId: vm.textAreaHtmlId,
|
htmlId: vm.textAreaHtmlId,
|
||||||
stylesheets: editorConfig.stylesheets,
|
stylesheets: editorConfig.stylesheets,
|
||||||
toolbar: editorConfig.toolbar,
|
toolbar: editorConfig.toolbar,
|
||||||
mode: editorConfig.mode
|
mode: editorConfig.mode
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.then(function (tinyMceConfig) {
|
|
||||||
// Load the plugins.min.js file from the TinyMCE Cloud if a Cloud Api Key is specified
|
// Handle additional assets loading depending on the configuration before initializing the editor
|
||||||
if (tinyMceConfig.cloudApiKey) {
|
.then(function (tinyMceConfig) {
|
||||||
return assetsService.loadJs(`https://cdn.tiny.cloud/1/${tinyMceConfig.cloudApiKey}/tinymce/${tinymce.majorVersion}.${tinymce.minorVersion}/plugins.min.js`)
|
// Load the plugins.min.js file from the TinyMCE Cloud if a Cloud Api Key is specified
|
||||||
.then(() => tinyMceConfig);
|
if (tinyMceConfig.cloudApiKey) {
|
||||||
|
return assetsService.loadJs(`https://cdn.tiny.cloud/1/${tinyMceConfig.cloudApiKey}/tinymce/${tinymce.majorVersion}.${tinymce.minorVersion}/plugins.min.js`)
|
||||||
|
.then(() => tinyMceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tinyMceConfig;
|
||||||
|
})
|
||||||
|
|
||||||
|
//wait for config to be ready after assets have loaded
|
||||||
|
.then(function (standardConfig) {
|
||||||
|
|
||||||
|
if (height !== null) {
|
||||||
|
standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tinyMceConfig;
|
//create a baseline Config to extend upon
|
||||||
})
|
let baseLineConfigObj = {
|
||||||
.then(function (tinyMceConfig) {
|
maxImageSize: editorConfig.maxImageSize,
|
||||||
tinyMceConfigDeferred.resolve(tinyMceConfig);
|
width: width,
|
||||||
});
|
height: height
|
||||||
});
|
};
|
||||||
|
|
||||||
//wait for config to be ready after assets have loaded
|
baseLineConfigObj.setup = function (editor) {
|
||||||
tinyMceConfigDeferred.promise.then(function (standardConfig) {
|
|
||||||
|
|
||||||
if (height !== null) {
|
//set the reference
|
||||||
standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1);
|
vm.tinyMceEditor = editor;
|
||||||
}
|
|
||||||
|
|
||||||
//create a baseline Config to extend upon
|
vm.tinyMceEditor.on('init', function (e) {
|
||||||
var baseLineConfigObj = {
|
$timeout(function () {
|
||||||
maxImageSize: editorConfig.maxImageSize,
|
vm.rteLoading = false;
|
||||||
width: width,
|
vm.updateLoading();
|
||||||
height: height
|
|
||||||
};
|
|
||||||
|
|
||||||
baseLineConfigObj.setup = function (editor) {
|
|
||||||
|
|
||||||
//set the reference
|
|
||||||
vm.tinyMceEditor = editor;
|
|
||||||
|
|
||||||
vm.tinyMceEditor.on('init', function (e) {
|
|
||||||
$timeout(function () {
|
|
||||||
vm.rteLoading = false;
|
|
||||||
vm.updateLoading();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
vm.tinyMceEditor.on("focus", function () {
|
|
||||||
$element[0].dispatchEvent(new CustomEvent('umb-rte-focus', {composed: true, bubbles: true}));
|
|
||||||
});
|
|
||||||
vm.tinyMceEditor.on("blur", function () {
|
|
||||||
$element[0].dispatchEvent(new CustomEvent('umb-rte-blur', {composed: true, bubbles: true}));
|
|
||||||
});
|
|
||||||
|
|
||||||
//initialize the standard editor functionality for Umbraco
|
|
||||||
tinyMceService.initializeEditor({
|
|
||||||
//scope: $scope,
|
|
||||||
editor: editor,
|
|
||||||
toolbar: editorConfig.toolbar,
|
|
||||||
model: vm.model,
|
|
||||||
getValue: function () {
|
|
||||||
return vm.model.value.markup;
|
|
||||||
},
|
|
||||||
setValue: function (newVal) {
|
|
||||||
vm.model.value.markup = newVal;
|
|
||||||
$scope.$evalAsync();
|
|
||||||
},
|
|
||||||
culture: vm.umbProperty?.culture ?? null,
|
|
||||||
segment: vm.umbProperty?.segment ?? null,
|
|
||||||
blockEditorApi: vm.noBlocksMode ? undefined : vm.blockEditorApi,
|
|
||||||
parentForm: vm.propertyForm,
|
|
||||||
valFormManager: vm.valFormManager,
|
|
||||||
currentFormInput: $scope.rteForm.modelValue
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Utilities.extend(baseLineConfigObj, standardConfig);
|
|
||||||
|
|
||||||
// Readonly mode
|
|
||||||
baseLineConfigObj.toolbar = vm.readonly ? false : baseLineConfigObj.toolbar;
|
|
||||||
baseLineConfigObj.readonly = vm.readonly ? 1 : baseLineConfigObj.readonly;
|
|
||||||
|
|
||||||
// We need to wait for DOM to have rendered before we can find the element by ID.
|
|
||||||
$timeout(function () {
|
|
||||||
tinymce.init(baseLineConfigObj);
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
//listen for formSubmitting event (the result is callback used to remove the event subscription)
|
|
||||||
unsubscribe.push($scope.$on("formSubmitting", function () {
|
|
||||||
if (vm.tinyMceEditor != null && !vm.rteLoading) {
|
|
||||||
|
|
||||||
// Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup.
|
|
||||||
var blockElements = vm.tinyMceEditor.dom.select(`umb-rte-block, umb-rte-block-inline`);
|
|
||||||
const usedContentUdis = blockElements.map(blockElement => blockElement.getAttribute('data-content-udi'));
|
|
||||||
|
|
||||||
const unusedBlocks = vm.layout.filter(x => usedContentUdis.indexOf(x.contentUdi) === -1);
|
|
||||||
unusedBlocks.forEach(blockLayout => {
|
|
||||||
deleteBlock(blockLayout.$block);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Remove Angular Classes from markup:
|
|
||||||
var parser = new DOMParser();
|
|
||||||
var doc = parser.parseFromString(vm.model.value.markup, 'text/html');
|
|
||||||
|
|
||||||
// Get all elements in the parsed document
|
|
||||||
var elements = doc.querySelectorAll('*[class]');
|
|
||||||
elements.forEach(element => {
|
|
||||||
var classAttribute = element.getAttribute("class");
|
|
||||||
if (classAttribute) {
|
|
||||||
// Split the class attribute by spaces and remove "ng-scope" and "ng-isolate-scope"
|
|
||||||
var classes = classAttribute.split(" ");
|
|
||||||
var newClasses = classes.filter(function (className) {
|
|
||||||
return className !== "ng-scope" && className !== "ng-isolate-scope";
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
// Update the class attribute with the remaining classes
|
vm.tinyMceEditor.on("focus", function () {
|
||||||
if (newClasses.length > 0) {
|
$element[0].dispatchEvent(new CustomEvent('umb-rte-focus', {composed: true, bubbles: true}));
|
||||||
element.setAttribute('class', newClasses.join(' '));
|
});
|
||||||
} else {
|
vm.tinyMceEditor.on("blur", function () {
|
||||||
// If no remaining classes, remove the class attribute
|
$element[0].dispatchEvent(new CustomEvent('umb-rte-blur', {composed: true, bubbles: true}));
|
||||||
element.removeAttribute('class');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.model.value.markup = doc.body.innerHTML;
|
//initialize the standard editor functionality for Umbraco
|
||||||
|
tinyMceService.initializeEditor({
|
||||||
|
//scope: $scope,
|
||||||
|
editor: editor,
|
||||||
|
toolbar: editorConfig.toolbar,
|
||||||
|
model: vm.model,
|
||||||
|
getValue: function () {
|
||||||
|
return vm.model.value.markup;
|
||||||
|
},
|
||||||
|
setValue: function (newVal) {
|
||||||
|
vm.model.value.markup = newVal;
|
||||||
|
$scope.$evalAsync();
|
||||||
|
},
|
||||||
|
culture: vm.umbProperty?.culture ?? null,
|
||||||
|
segment: vm.umbProperty?.segment ?? null,
|
||||||
|
blockEditorApi: vm.noBlocksMode ? undefined : vm.blockEditorApi,
|
||||||
|
parentForm: vm.propertyForm,
|
||||||
|
valFormManager: vm.valFormManager,
|
||||||
|
currentFormInput: $scope.rteForm.modelValue
|
||||||
|
});
|
||||||
|
|
||||||
}
|
};
|
||||||
}));
|
|
||||||
|
|
||||||
vm.focusRTE = function () {
|
Utilities.extend(baseLineConfigObj, standardConfig);
|
||||||
vm.tinyMceEditor.focus();
|
|
||||||
}
|
// Readonly mode
|
||||||
|
baseLineConfigObj.toolbar = vm.readonly ? false : baseLineConfigObj.toolbar;
|
||||||
|
baseLineConfigObj.readonly = vm.readonly ? 1 : baseLineConfigObj.readonly;
|
||||||
|
|
||||||
|
// We need to wait for DOM to have rendered before we can find the element by ID.
|
||||||
|
$timeout(function () {
|
||||||
|
tinymce.init(baseLineConfigObj);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
//listen for formSubmitting event (the result is callback used to remove the event subscription)
|
||||||
|
unsubscribe.push($scope.$on("formSubmitting", function () {
|
||||||
|
if (vm.tinyMceEditor != null && !vm.rteLoading) {
|
||||||
|
|
||||||
|
// Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup.
|
||||||
|
var blockElements = vm.tinyMceEditor.dom.select(`umb-rte-block, umb-rte-block-inline`);
|
||||||
|
const usedContentUdis = blockElements.map(blockElement => blockElement.getAttribute('data-content-udi'));
|
||||||
|
|
||||||
|
const unusedBlocks = vm.layout.filter(x => usedContentUdis.indexOf(x.contentUdi) === -1);
|
||||||
|
unusedBlocks.forEach(blockLayout => {
|
||||||
|
deleteBlock(blockLayout.$block);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Remove Angular Classes from markup:
|
||||||
|
var parser = new DOMParser();
|
||||||
|
var doc = parser.parseFromString(vm.model.value.markup, 'text/html');
|
||||||
|
|
||||||
|
// Get all elements in the parsed document
|
||||||
|
var elements = doc.querySelectorAll('*[class]');
|
||||||
|
elements.forEach(element => {
|
||||||
|
var classAttribute = element.getAttribute("class");
|
||||||
|
if (classAttribute) {
|
||||||
|
// Split the class attribute by spaces and remove "ng-scope" and "ng-isolate-scope"
|
||||||
|
var classes = classAttribute.split(" ");
|
||||||
|
var newClasses = classes.filter(function (className) {
|
||||||
|
return className !== "ng-scope" && className !== "ng-isolate-scope";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the class attribute with the remaining classes
|
||||||
|
if (newClasses.length > 0) {
|
||||||
|
element.setAttribute('class', newClasses.join(' '));
|
||||||
|
} else {
|
||||||
|
// If no remaining classes, remove the class attribute
|
||||||
|
element.removeAttribute('class');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
vm.model.value.markup = doc.body.innerHTML;
|
||||||
|
|
||||||
// When the element is disposed we need to unsubscribe!
|
|
||||||
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
|
|
||||||
// element might still be there even after the modal has been hidden.
|
|
||||||
$scope.$on('$destroy', function () {
|
|
||||||
if (vm.tinyMceEditor != null) {
|
|
||||||
if($element) {
|
|
||||||
$element[0]?.dispatchEvent(new CustomEvent('blur', {composed: true, bubbles: true}));
|
|
||||||
}
|
}
|
||||||
vm.tinyMceEditor.destroy();
|
}));
|
||||||
vm.tinyMceEditor = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.focusRTE = function () {
|
||||||
|
if (vm.tinyMceEditor) {
|
||||||
|
vm.tinyMceEditor.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Called when we save the value, the server may return an updated data and our value is re-synced
|
// Called when we save the value, the server may return an updated data and our value is re-synced
|
||||||
// we need to deal with that here so that our model values are all in sync so we basically re-initialize.
|
// we need to deal with that here so that our model values are all in sync so we basically re-initialize.
|
||||||
function onServerValueChanged(newVal, oldVal) {
|
function onServerValueChanged(newVal, oldVal) {
|
||||||
@@ -978,6 +964,17 @@
|
|||||||
for (const subscription of unsubscribe) {
|
for (const subscription of unsubscribe) {
|
||||||
subscription();
|
subscription();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the element is disposed we need to unsubscribe!
|
||||||
|
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
|
||||||
|
// element might still be there even after the modal has been hidden.
|
||||||
|
if (vm.tinyMceEditor != null) {
|
||||||
|
if($element) {
|
||||||
|
$element[0]?.dispatchEvent(new CustomEvent('blur', {composed: true, bubbles: true}));
|
||||||
|
}
|
||||||
|
vm.tinyMceEditor.destroy();
|
||||||
|
vm.tinyMceEditor = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user