diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-macro/plugin.js b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-macro/plugin.js index 77a16133e061ea6e9255dffca78eb87b653ca670..3c0ae0df23c4a7574b5bcb9ed647be9cfda0627d 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-macro/plugin.js +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-macro/plugin.js @@ -916,37 +916,49 @@ // The passed element is of type CKEDITOR.htmlParser.element isMacroElement: function(element) { return (element.name == 'div' || element.name == 'span') && - element.hasClass('macro') && element.attributes['data-macro']; + // The passed element doesn't always have the CKEDITOR.htmlParser.element API. This happens for instance when + // CKEditor checks which plugins to disable (based on the HTML elements they require). In this case the passed + // element only has the data (attributes, children, etc.). + element.hasClass?.('macro') && element.attributes['data-macro']; }, // The passed element is of type CKEDITOR.htmlParser.element isMacroOutput: function(element) { - return element.getAscendant && element.getAscendant(function(ancestor) { - // The macro marker comments might have been already processed (e.g. in the case of nested editables) so we need - // to look for a macro output wrapper also. - if (CKEDITOR.plugins.xwikiMacro.isMacroElement(ancestor)) { - return true; - } - // Look for macro marker comments otherwise, taking into account that macro markers can be "nested". - var nestingLevel = 0; - var previousSibling = ancestor; - while (previousSibling.previous && nestingLevel <= 0) { - previousSibling = previousSibling.previous; - if (previousSibling.type === CKEDITOR.NODE_COMMENT) { - if (previousSibling.value === 'stopmacro') { - // Macro output end. - nestingLevel--; - } else if (previousSibling.value.substr(0, 11) === 'startmacro:') { - // Macro output start. - nestingLevel++; - } - } - } - return nestingLevel > 0; - }); + // CKEDITOR.htmlParser.element#getAscendant() doesn't check the element itself, so we have to do it. + return isMacroElementOrBetweenMacroMarkers(element) || + element.getAscendant?.(isMacroElementOrBetweenMacroMarkers); } }; + /** + * @param {CKEDITOR.htmlParser.element} element the element to check + * @returns {@code true} if the given element is a macro output wrapper or is between macro marker comments, + * {@code false} otherwise + */ + function isMacroElementOrBetweenMacroMarkers(element) { + // The macro marker comments might have been already processed (e.g. in the case of nested editables) so we need to + // look for a macro output wrapper also. + if (CKEDITOR.plugins.xwikiMacro.isMacroElement(element)) { + return true; + } + // Look for macro marker comments otherwise, taking into account that macro markers can be "nested". + var nestingLevel = 0; + var previousSibling = element; + while (previousSibling.previous && nestingLevel <= 0) { + previousSibling = previousSibling.previous; + if (previousSibling.type === CKEDITOR.NODE_COMMENT) { + if (previousSibling.value === 'stopmacro') { + // Macro output end. + nestingLevel--; + } else if (previousSibling.value.substr(0, 11) === 'startmacro:') { + // Macro output start. + nestingLevel++; + } + } + } + return nestingLevel > 0; + } + // Overwrite CKEditor's HTML parser in order to prevent it from removing empty elements from generated macro output. // It's important to preserve the generated macro output as is, because these empty elements are often used: // * to display font icons (even on the default wiki home page)