Merge pull request #4332 from umbraco/temp8-macros-dont-render-and-other-macro-bugs
v8 Macros don't render in the RTE and various
This commit is contained in:
6
src/Umbraco.Web.UI.Client/package-lock.json
generated
6
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -15598,9 +15598,9 @@
|
||||
}
|
||||
},
|
||||
"tinymce": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.0.tgz",
|
||||
"integrity": "sha512-hrPeCLXY/sVCo3i64CTW8P5xbDiEI8Uii/vWpcmQWAMhex6GWWd2U+L8WIMj5tKKGdfcIQAJfpfQthc/92bcKw=="
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.2.tgz",
|
||||
"integrity": "sha512-ZRoTGG4GAsOI73QPSNkabO7nkoYw9H6cglRB44W2mMkxSiqxYi8WJlgkUphk0fDqo6ZD6r3E+NSP4UHxF2lySg=="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"npm": "^6.4.1",
|
||||
"signalr": "2.4.0",
|
||||
"spectrum-colorpicker": "1.8.0",
|
||||
"tinymce": "4.9.0",
|
||||
"tinymce": "4.9.2",
|
||||
"typeahead.js": "0.11.1",
|
||||
"underscore": "1.9.1"
|
||||
},
|
||||
|
||||
@@ -30,7 +30,8 @@ angular.module("umbraco.directives")
|
||||
promises.push(tinyMceService.getTinyMceEditorConfig({
|
||||
htmlId: scope.uniqueId,
|
||||
stylesheets: scope.configuration ? scope.configuration.stylesheets : null,
|
||||
toolbar: toolbar
|
||||
toolbar: toolbar,
|
||||
mode: scope.configuration.mode
|
||||
}));
|
||||
|
||||
// pin toolbar to top of screen if we have focus and it scrolls off the screen
|
||||
|
||||
@@ -232,7 +232,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
|
||||
body_class: 'umb-rte',
|
||||
//see http://archive.tinymce.com/wiki.php/Configuration:cache_suffix
|
||||
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster
|
||||
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster,
|
||||
|
||||
//this is used to style the inline macro bits, sorry hard coding this form now since we don't have a standalone
|
||||
//stylesheet to load in for this with only these styles (the color is @pinkLight)
|
||||
content_style: ".mce-content-body .umb-macro-holder { border: 3px dotted #f5c1bc; padding: 7px; display: block; margin: 3px; } .umb-rte .mce-content-body .umb-macro-holder.loading {background: url(assets/img/loader.gif) right no-repeat; background-size: 18px; background-position-x: 99%;}"
|
||||
};
|
||||
|
||||
if (tinyMceConfig.customConfig) {
|
||||
@@ -458,7 +462,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
*/
|
||||
createInsertMacro: function (editor, callback) {
|
||||
|
||||
var createInsertMacroScope = this;
|
||||
let self = this;
|
||||
let activeMacroElement = null; //track an active macro element
|
||||
|
||||
/** Adds custom rules for the macro plugin and custom serialization */
|
||||
editor.on('preInit', function (args) {
|
||||
@@ -474,6 +479,16 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/** when the contents load we need to find any macros declared and load in their content */
|
||||
editor.on("SetContent", function (o) {
|
||||
|
||||
//get all macro divs and load their content
|
||||
$(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () {
|
||||
self.loadMacroContent($(this), null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -501,203 +516,26 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
tooltip: 'Insert macro',
|
||||
onPostRender: function () {
|
||||
|
||||
var ctrl = this;
|
||||
var isOnMacroElement = false;
|
||||
|
||||
let ctrl = this;
|
||||
|
||||
/**
|
||||
if the selection comes from a different element that is not the macro's
|
||||
we need to check if the selection includes part of the macro, if so we'll force the selection
|
||||
to clear to the next element since if people can select part of the macro markup they can then modify it.
|
||||
*/
|
||||
function handleSelectionChange() {
|
||||
|
||||
if (!editor.selection.isCollapsed()) {
|
||||
var endSelection = tinymce.activeEditor.selection.getEnd();
|
||||
var startSelection = tinymce.activeEditor.selection.getStart();
|
||||
//don't proceed if it's an entire element selected
|
||||
if (endSelection !== startSelection) {
|
||||
|
||||
//if the end selection is a macro then move the cursor
|
||||
//NOTE: we don't have to handle when the selection comes from a previous parent because
|
||||
// that is automatically taken care of with the normal onNodeChanged logic since the
|
||||
// evt.element will be the macro once it becomes part of the selection.
|
||||
var $testForMacro = $(endSelection).closest(".umb-macro-holder");
|
||||
if ($testForMacro.length > 0) {
|
||||
|
||||
//it came from before so move after, if there is no after then select ourselves
|
||||
var next = $testForMacro.next();
|
||||
if (next.length > 0) {
|
||||
editor.selection.setCursorLocation($testForMacro.next().get(0));
|
||||
} else {
|
||||
selectMacroElement($testForMacro.get(0));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to select the macro element */
|
||||
function selectMacroElement(macroElement) {
|
||||
|
||||
// move selection to top element to ensure we can't edit this
|
||||
editor.selection.select(macroElement);
|
||||
|
||||
// check if the current selection *is* the element (ie bug)
|
||||
var currentSelection = editor.selection.getStart();
|
||||
if (tinymce.isIE) {
|
||||
if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) {
|
||||
while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) {
|
||||
currentSelection = currentSelection.parentNode;
|
||||
}
|
||||
editor.selection.select(currentSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag.
|
||||
* If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves
|
||||
* from the event listener before changing selection, however, it seems that putting a break point in this method
|
||||
* will always cause an 'infinite' loop as the caret keeps changing.
|
||||
*
|
||||
* TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/
|
||||
* Check if the macro is currently selected and toggle the menu button
|
||||
*/
|
||||
function onNodeChanged(evt) {
|
||||
|
||||
//set our macro button active when on a node of class umb-macro-holder
|
||||
var $macroElement = $(evt.element).closest(".umb-macro-holder");
|
||||
|
||||
handleSelectionChange();
|
||||
|
||||
//set the button active
|
||||
ctrl.active($macroElement.length !== 0);
|
||||
|
||||
if ($macroElement.length > 0) {
|
||||
var macroElement = $macroElement.get(0);
|
||||
|
||||
//remove the event listener before re-selecting
|
||||
editor.off('NodeChange', onNodeChanged);
|
||||
|
||||
selectMacroElement(macroElement);
|
||||
|
||||
//set the flag
|
||||
isOnMacroElement = true;
|
||||
|
||||
//re-add the event listener
|
||||
editor.on('NodeChange', onNodeChanged);
|
||||
} else {
|
||||
isOnMacroElement = false;
|
||||
}
|
||||
activeMacroElement = getRealMacroElem(evt.element);
|
||||
|
||||
//set the button active/inactive
|
||||
ctrl.active(activeMacroElement !== null);
|
||||
}
|
||||
|
||||
/** when the contents load we need to find any macros declared and load in their content */
|
||||
editor.on("LoadContent", function (o) {
|
||||
|
||||
//get all macro divs and load their content
|
||||
$(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () {
|
||||
createInsertMacroScope.loadMacroContent($(this), null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* This prevents any other commands from executing when the current element is the macro so the content cannot be edited
|
||||
*
|
||||
* TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/
|
||||
*/
|
||||
editor.on('BeforeExecCommand', function (o) {
|
||||
if (isOnMacroElement) {
|
||||
if (o.preventDefault) {
|
||||
o.preventDefault();
|
||||
}
|
||||
if (o.stopImmediatePropagation) {
|
||||
o.stopImmediatePropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This double checks and ensures you can't paste content into the rendered macro
|
||||
*
|
||||
* TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/
|
||||
*/
|
||||
editor.on("Paste", function (o) {
|
||||
if (isOnMacroElement) {
|
||||
if (o.preventDefault) {
|
||||
o.preventDefault();
|
||||
}
|
||||
if (o.stopImmediatePropagation) {
|
||||
o.stopImmediatePropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
//NOTE: This could be another way to deal with the active/inactive state
|
||||
//editor.on('ObjectSelected', function (e) {});
|
||||
|
||||
//set onNodeChanged event listener
|
||||
editor.on('NodeChange', onNodeChanged);
|
||||
|
||||
/**
|
||||
* Listen for the keydown in the editor, we'll check if we are currently on a macro element, if so
|
||||
* we'll check if the key down is a supported key which requires an action, otherwise we ignore the request
|
||||
* so the macro cannot be edited.
|
||||
*
|
||||
* TODO: I don't think we need this anymore with recent tinymce fixes: https://www.tiny.cloud/docs/plugins/noneditable/
|
||||
*/
|
||||
editor.on('KeyDown', function (e) {
|
||||
if (isOnMacroElement) {
|
||||
var macroElement = editor.selection.getNode();
|
||||
|
||||
//get the 'real' element (either p or the real one)
|
||||
macroElement = getRealMacroElem(macroElement);
|
||||
|
||||
//prevent editing
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var moveSibling = function (element, isNext) {
|
||||
var $e = $(element);
|
||||
var $sibling = isNext ? $e.next() : $e.prev();
|
||||
if ($sibling.length > 0) {
|
||||
editor.selection.select($sibling.get(0));
|
||||
editor.selection.collapse(true);
|
||||
} else {
|
||||
//if we're moving previous and there is no sibling, then lets recurse and just select the next one
|
||||
if (!isNext) {
|
||||
moveSibling(element, true);
|
||||
return;
|
||||
}
|
||||
|
||||
//if there is no sibling we'll generate a new p at the end and select it
|
||||
editor.setContent(editor.getContent() + "<p> </p>");
|
||||
editor.selection.select($(editor.dom.getRoot()).children().last().get(0));
|
||||
editor.selection.collapse(true);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//supported keys to move to the next or prev element (13-enter, 27-esc, 38-up, 40-down, 39-right, 37-left)
|
||||
//supported keys to remove the macro (8-backspace, 46-delete)
|
||||
// TODO: Should we make the enter key insert a line break before or leave it as moving to the next element?
|
||||
if ($.inArray(e.keyCode, [13, 40, 39]) !== -1) {
|
||||
//move to next element
|
||||
moveSibling(macroElement, true);
|
||||
} else if ($.inArray(e.keyCode, [27, 38, 37]) !== -1) {
|
||||
//move to prev element
|
||||
moveSibling(macroElement, false);
|
||||
} else if ($.inArray(e.keyCode, [8, 46]) !== -1) {
|
||||
//delete macro element
|
||||
|
||||
//move first, then delete
|
||||
moveSibling(macroElement, false);
|
||||
editor.dom.remove(macroElement);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/** The insert macro button click event handler */
|
||||
@@ -710,11 +548,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
|
||||
//when we click we could have a macro already selected and in that case we'll want to edit the current parameters
|
||||
//so we'll need to extract them and submit them to the dialog.
|
||||
var macroElement = editor.selection.getNode();
|
||||
macroElement = getRealMacroElem(macroElement);
|
||||
if (macroElement) {
|
||||
if (activeMacroElement) {
|
||||
//we have a macro selected so we'll need to parse it's alias and parameters
|
||||
var contents = $(macroElement).contents();
|
||||
var contents = $(activeMacroElement).contents();
|
||||
var comment = _.find(contents, function (item) {
|
||||
return item.nodeType === 8;
|
||||
});
|
||||
@@ -724,7 +560,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
var syntax = comment.textContent.trim();
|
||||
var parsed = macroService.parseMacroSyntax(syntax);
|
||||
dialogData = {
|
||||
macroData: parsed
|
||||
macroData: parsed,
|
||||
activeMacroElement: activeMacroElement //pass the active element along so we can retrieve it later
|
||||
};
|
||||
}
|
||||
|
||||
@@ -737,7 +574,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
});
|
||||
},
|
||||
|
||||
insertMacroInEditor: function (editor, macroObject) {
|
||||
insertMacroInEditor: function (editor, macroObject, activeMacroElement) {
|
||||
|
||||
//Important note: the TinyMce plugin "noneditable" is used here so that the macro cannot be edited,
|
||||
// for this to work the mceNonEditable class needs to come last and we also need to use the attribute contenteditable = false
|
||||
// (even though all the docs and examples say that is not necessary)
|
||||
|
||||
//put the macro syntax in comments, we will parse this out on the server side to be used
|
||||
//for persisting.
|
||||
@@ -746,11 +587,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
var uniqueId = "umb-macro-" + editor.dom.uniqueId();
|
||||
var macroDiv = editor.dom.create('div',
|
||||
{
|
||||
'class': 'umb-macro-holder ' + macroObject.macroAlias + ' mceNonEditable ' + uniqueId
|
||||
'class': 'umb-macro-holder ' + macroObject.macroAlias + " " + uniqueId + ' mceNonEditable',
|
||||
'contenteditable': 'false'
|
||||
},
|
||||
macroSyntaxComment + '<ins>Macro alias: <strong>' + macroObject.macroAlias + '</strong></ins>');
|
||||
|
||||
editor.selection.setNode(macroDiv);
|
||||
//if there's an activeMacroElement then replace it, otherwise set the contents of the selected node
|
||||
if (activeMacroElement) {
|
||||
activeMacroElement.replaceWith(macroDiv); //directly replaces the html node
|
||||
}
|
||||
else {
|
||||
editor.selection.setNode(macroDiv);
|
||||
}
|
||||
|
||||
var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId));
|
||||
|
||||
@@ -1167,10 +1015,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
|
||||
pinToolbar : function (editor) {
|
||||
|
||||
//we can't pin the toolbar if this doesn't exist (i.e. when in distraction free mode)
|
||||
if (!editor.editorContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tinyMce = $(editor.editorContainer);
|
||||
var toolbar = tinyMce.find(".mce-toolbar");
|
||||
var toolbarHeight = toolbar.height();
|
||||
var tinyMceRect = tinyMce[0].getBoundingClientRect();
|
||||
var tinyMceRect = editor.editorContainer.getBoundingClientRect();
|
||||
var tinyMceTop = tinyMceRect.top;
|
||||
var tinyMceBottom = tinyMceRect.bottom;
|
||||
var tinyMceWidth = tinyMceRect.width;
|
||||
@@ -1288,7 +1141,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
}
|
||||
});
|
||||
|
||||
var self = this;
|
||||
let self = this;
|
||||
|
||||
//create link picker
|
||||
self.createLinkPicker(args.editor, function (currentTarget, anchorElement) {
|
||||
@@ -1346,7 +1199,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
dialogData: dialogData,
|
||||
submit: function (model) {
|
||||
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
|
||||
self.insertMacroInEditor(args.editor, macroObject);
|
||||
self.insertMacroInEditor(args.editor, macroObject, dialogData.activeMacroElement);
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
|
||||
@@ -25,13 +25,6 @@
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
/* loader for macro loading in tinymce*/
|
||||
.umb-rte .mce-content-body .umb-macro-holder.loading {
|
||||
background: url(img/loader.gif) right no-repeat;
|
||||
background-size: 18px;
|
||||
background-position-x: 99%;
|
||||
}
|
||||
|
||||
.umb-rte .mce-container {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user