From: Alexander Ebert Date: Thu, 4 May 2023 17:44:35 +0000 (+0200) Subject: Clean up the markup passed to CKEditor X-Git-Tag: 6.0.0_Alpha_1~136^2~13 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=4b5af983cbb2e8c14c41765ae4e208c5d8e2255a;p=GitHub%2FWoltLab%2FWCF.git Clean up the markup passed to CKEditor --- diff --git a/ts/WoltLabSuite/Core/Component/Ckeditor/Cleanup.ts b/ts/WoltLabSuite/Core/Component/Ckeditor/Cleanup.ts index b5b0d5adbb..88edda2f39 100644 --- a/ts/WoltLabSuite/Core/Component/Ckeditor/Cleanup.ts +++ b/ts/WoltLabSuite/Core/Component/Ckeditor/Cleanup.ts @@ -1,3 +1,74 @@ +/** + * Cleans up the markup of legacy messages. + * + * Messages created in the previous editor used empty paragraphs to create empty + * lines. In addition, Firefox kept trailing
in lines with content, which + * causes issues with CKEditor. + * + * @author Alexander Ebert + * @copyright 2001-2023 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.0 + */ + +import DomUtil from "../../Dom/Util"; + +function unwrapBr(div: HTMLElement): void { + div.querySelectorAll("br").forEach((br) => { + if (br.previousSibling || br.nextSibling) { + return; + } + + let parent: HTMLElement | null = br; + while ((parent = parent.parentElement) !== null) { + switch (parent.tagName) { + case "B": + case "EM": + case "I": + case "STRONG": + case "SUB": + case "SUP": + case "SPAN": + case "U": + if (br.previousSibling || br.nextSibling) { + return; + } + + parent.insertAdjacentElement("afterend", br); + parent.remove(); + parent = br; + break; + + default: + return; + } + } + }); +} + +function removeTrailingBr(div: HTMLElement): void { + div.querySelectorAll("br").forEach((br) => { + if (br.dataset.ckeFiller === "true") { + return; + } + + const paragraph = br.closest("p"); + if (paragraph === null) { + return; + } + + if (!DomUtil.isAtNodeEnd(br, paragraph)) { + return; + } + + if (paragraph.innerHTML === "
") { + paragraph.remove(); + } else { + br.remove(); + } + }); +} + function stripLegacySpacerParagraphs(div: HTMLElement): void { div.querySelectorAll("p").forEach((paragraph) => { if (paragraph.childElementCount === 1) { @@ -19,6 +90,8 @@ export function normalizeLegacyMessage(element: HTMLElement): void { const div = document.createElement("div"); div.innerHTML = element.value; + unwrapBr(div); + removeTrailingBr(div); stripLegacySpacerParagraphs(div); element.value = div.innerHTML; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Cleanup.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Cleanup.js index 31fa4715f3..a40ec4aa1f 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Cleanup.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Ckeditor/Cleanup.js @@ -1,7 +1,69 @@ -define(["require", "exports"], function (require, exports) { +/** + * Cleans up the markup of legacy messages. + * + * Messages created in the previous editor used empty paragraphs to create empty + * lines. In addition, Firefox kept trailing
in lines with content, which + * causes issues with CKEditor. + * + * @author Alexander Ebert + * @copyright 2001-2023 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.0 + */ +define(["require", "exports", "tslib", "../../Dom/Util"], function (require, exports, tslib_1, Util_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizeLegacyMessage = void 0; + Util_1 = tslib_1.__importDefault(Util_1); + function unwrapBr(div) { + div.querySelectorAll("br").forEach((br) => { + if (br.previousSibling || br.nextSibling) { + return; + } + let parent = br; + while ((parent = parent.parentElement) !== null) { + switch (parent.tagName) { + case "B": + case "EM": + case "I": + case "STRONG": + case "SUB": + case "SUP": + case "SPAN": + case "U": + if (br.previousSibling || br.nextSibling) { + return; + } + parent.insertAdjacentElement("afterend", br); + parent.remove(); + parent = br; + break; + default: + return; + } + } + }); + } + function removeTrailingBr(div) { + div.querySelectorAll("br").forEach((br) => { + if (br.dataset.ckeFiller === "true") { + return; + } + const paragraph = br.closest("p"); + if (paragraph === null) { + return; + } + if (!Util_1.default.isAtNodeEnd(br, paragraph)) { + return; + } + if (paragraph.innerHTML === "
") { + paragraph.remove(); + } + else { + br.remove(); + } + }); + } function stripLegacySpacerParagraphs(div) { div.querySelectorAll("p").forEach((paragraph) => { if (paragraph.childElementCount === 1) { @@ -20,6 +82,8 @@ define(["require", "exports"], function (require, exports) { } const div = document.createElement("div"); div.innerHTML = element.value; + unwrapBr(div); + removeTrailingBr(div); stripLegacySpacerParagraphs(div); element.value = div.innerHTML; }