From ac7d08252bf9901793d0e2bf9899af46f7ee6031 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 3 Nov 2020 23:32:23 +0100 Subject: [PATCH] Convert `Ui/Redactor/Metacode` to TypeScript --- .../WoltLabSuite/Core/Ui/Redactor/Metacode.js | 282 +++++++----------- .../WoltLabSuite/Core/Ui/Redactor/Metacode.js | 215 ------------- .../WoltLabSuite/Core/Ui/Redactor/Metacode.ts | 147 +++++++++ 3 files changed, 262 insertions(+), 382 deletions(-) delete mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.js create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.ts diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/Metacode.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/Metacode.js index c6bd329b27..5a580996ae 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/Metacode.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Redactor/Metacode.js @@ -1,185 +1,133 @@ /** * Converts `` into the bbcode representation. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Ui/Redactor/Metacode + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Redactor/Metacode */ -define(['EventHandler', 'Dom/Util'], function (EventHandler, DomUtil) { +define(["require", "exports", "tslib", "../../Event/Handler", "../../Dom/Util"], function (require, exports, tslib_1, EventHandler, Util_1) { "use strict"; - if (!COMPILER_TARGET_DEFAULT) { - var Fake = function () { }; - Fake.prototype = { - convert: function () { }, - convertFromHtml: function () { }, - _getOpeningTag: function () { }, - _getClosingTag: function () { }, - _getFirstParagraph: function () { }, - _getLastParagraph: function () { }, - _parseAttributes: function () { } - }; - return Fake; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.convertFromHtml = void 0; + EventHandler = tslib_1.__importStar(EventHandler); + Util_1 = tslib_1.__importDefault(Util_1); + /** + * Returns a text node representing the opening bbcode tag. + */ + function getOpeningTag(name, attributes) { + let buffer = "[" + name; + if (attributes.length) { + buffer += "="; + buffer += attributes.map((attribute) => `'${attribute}'`).join(","); + } + return document.createTextNode(buffer + "]"); } /** - * @exports WoltLabSuite/Core/Ui/Redactor/Metacode + * Returns a text node representing the closing bbcode tag. */ - return { - /** - * Converts `` into the bbcode representation. - * - * @param {Element} element textarea element - */ - convert: function (element) { - element.textContent = this.convertFromHtml(element.textContent); - }, - convertFromHtml: function (editorId, html) { - var div = elCreate('div'); - div.innerHTML = html; - var attributes, data, metacode, metacodes = elByTag('woltlab-metacode', div), name, tagClose, tagOpen; - while (metacodes.length) { - metacode = metacodes[0]; - name = elData(metacode, 'name'); - attributes = this._parseAttributes(elData(metacode, 'attributes')); - data = { - attributes: attributes, - cancel: false, - metacode: metacode - }; - EventHandler.fire('com.woltlab.wcf.redactor2', 'metacode_' + name + '_' + editorId, data); - if (data.cancel === true) { - continue; - } - tagOpen = this._getOpeningTag(name, attributes); - tagClose = this._getClosingTag(name); - if (metacode.parentNode === div) { - DomUtil.prepend(tagOpen, this._getFirstParagraph(metacode)); - this._getLastParagraph(metacode).appendChild(tagClose); - } - else { - DomUtil.prepend(tagOpen, metacode); - metacode.appendChild(tagClose); - } - DomUtil.unwrapChildNodes(metacode); - } - // convert `…` to `[tt]…[/tt]` - var inlineCode, inlineCodes = elByTag('kbd', div); - while (inlineCodes.length) { - inlineCode = inlineCodes[0]; - inlineCode.insertBefore(document.createTextNode('[tt]'), inlineCode.firstChild); - inlineCode.appendChild(document.createTextNode('[/tt]')); - DomUtil.unwrapChildNodes(inlineCode); - } - return div.innerHTML; - }, - /** - * Returns a text node representing the opening bbcode tag. - * - * @param {string} name bbcode tag - * @param {Array} attributes list of attributes - * @returns {Text} text node containing the opening bbcode tag - * @protected - */ - _getOpeningTag: function (name, attributes) { - var buffer = '[' + name; - if (attributes.length) { - buffer += '='; - for (var i = 0, length = attributes.length; i < length; i++) { - if (i > 0) - buffer += ","; - buffer += "'" + attributes[i] + "'"; - } - } - return document.createTextNode(buffer + ']'); - }, - /** - * Returns a text node representing the closing bbcode tag. - * - * @param {string} name bbcode tag - * @returns {Text} text node containing the closing bbcode tag - * @protected - */ - _getClosingTag: function (name) { - return document.createTextNode('[/' + name + ']'); - }, - /** - * Returns the first paragraph of provided element. If there are no children or - * the first child is not a paragraph, a new paragraph is created and inserted - * as first child. - * - * @param {Element} element metacode element - * @returns {Element} paragraph that is the first child of provided element - * @protected - */ - _getFirstParagraph: function (element) { - var firstChild, paragraph; - if (element.childElementCount === 0) { - paragraph = elCreate('p'); - element.appendChild(paragraph); + function getClosingTag(name) { + return document.createTextNode(`[/${name}]`); + } + /** + * Returns the first paragraph of provided element. If there are no children or + * the first child is not a paragraph, a new paragraph is created and inserted + * as first child. + */ + function getFirstParagraph(element) { + let paragraph; + if (element.childElementCount === 0) { + paragraph = document.createElement("p"); + element.appendChild(paragraph); + } + else { + const firstChild = element.children[0]; + if (firstChild.nodeName === "P") { + paragraph = firstChild; } else { - firstChild = element.children[0]; - if (firstChild.nodeName === 'P') { - paragraph = firstChild; - } - else { - paragraph = elCreate('p'); - element.insertBefore(paragraph, firstChild); - } + paragraph = document.createElement("p"); + element.insertBefore(paragraph, firstChild); } - return paragraph; - }, - /** - * Returns the last paragraph of provided element. If there are no children or - * the last child is not a paragraph, a new paragraph is created and inserted - * as last child. - * - * @param {Element} element metacode element - * @returns {Element} paragraph that is the last child of provided element - * @protected - */ - _getLastParagraph: function (element) { - var count = element.childElementCount, lastChild, paragraph; - if (count === 0) { - paragraph = elCreate('p'); - element.appendChild(paragraph); + } + return paragraph; + } + /** + * Returns the last paragraph of provided element. If there are no children or + * the last child is not a paragraph, a new paragraph is created and inserted + * as last child. + */ + function getLastParagraph(element) { + const count = element.childElementCount; + let paragraph; + if (count === 0) { + paragraph = document.createElement("p"); + element.appendChild(paragraph); + } + else { + const lastChild = element.children[count - 1]; + if (lastChild.nodeName === "P") { + paragraph = lastChild; } else { - lastChild = element.children[count - 1]; - if (lastChild.nodeName === 'P') { - paragraph = lastChild; - } - else { - paragraph = elCreate('p'); - element.appendChild(paragraph); - } + paragraph = document.createElement("p"); + element.appendChild(paragraph); } - return paragraph; - }, - /** - * Parses the attributes string. - * - * @param {string} attributes base64- and JSON-encoded attributes - * @return {Array} list of parsed attributes - * @protected - */ - _parseAttributes: function (attributes) { - try { - attributes = JSON.parse(atob(attributes)); + } + return paragraph; + } + /** + * Parses the attributes string. + */ + function parseAttributes(attributes) { + try { + attributes = JSON.parse(atob(attributes)); + } + catch (e) { + /* invalid base64 data or invalid json */ + } + if (!Array.isArray(attributes)) { + return []; + } + return attributes.map((attribute) => { + return attribute.toString().replace(/^'(.*)'$/, "$1"); + }); + } + function convertFromHtml(editorId, html) { + const div = document.createElement("div"); + div.innerHTML = html; + div.querySelectorAll("woltlab-metacode").forEach((metacode) => { + const name = metacode.dataset.name; + const attributes = parseAttributes(metacode.dataset.attributes || ""); + const data = { + attributes: attributes, + cancel: false, + metacode: metacode, + }; + EventHandler.fire("com.woltlab.wcf.redactor2", `metacode_${name}_${editorId}`, data); + if (data.cancel) { + return; } - catch (e) { /* invalid base64 data or invalid json */ } - if (!Array.isArray(attributes)) { - return []; + const tagOpen = getOpeningTag(name, attributes); + const tagClose = getClosingTag(name); + if (metacode.parentElement === div) { + const paragraph = getFirstParagraph(metacode); + paragraph.insertBefore(tagOpen, paragraph.firstChild); + getLastParagraph(metacode).appendChild(tagClose); } - var attribute, parsedAttributes = []; - for (var i = 0, length = attributes.length; i < length; i++) { - attribute = attributes[i]; - if (typeof attribute === 'string') { - attribute = attribute.replace(/^'(.*)'$/, '$1'); - } - parsedAttributes.push(attribute); + else { + metacode.insertBefore(tagOpen, metacode.firstChild); + metacode.appendChild(tagClose); } - return parsedAttributes; - } - }; + Util_1.default.unwrapChildNodes(metacode); + }); + // convert `…` to `[tt]…[/tt]` + div.querySelectorAll("kbd").forEach((inlineCode) => { + inlineCode.insertBefore(document.createTextNode("[tt]"), inlineCode.firstChild); + inlineCode.appendChild(document.createTextNode("[/tt]")); + Util_1.default.unwrapChildNodes(inlineCode); + }); + return div.innerHTML; + } + exports.convertFromHtml = convertFromHtml; }); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.js deleted file mode 100644 index 2cf8796404..0000000000 --- a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.js +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Converts `` into the bbcode representation. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Ui/Redactor/Metacode - */ -define(['EventHandler', 'Dom/Util'], function(EventHandler, DomUtil) { - "use strict"; - - if (!COMPILER_TARGET_DEFAULT) { - var Fake = function() {}; - Fake.prototype = { - convert: function() {}, - convertFromHtml: function() {}, - _getOpeningTag: function() {}, - _getClosingTag: function() {}, - _getFirstParagraph: function() {}, - _getLastParagraph: function() {}, - _parseAttributes: function() {} - }; - return Fake; - } - - /** - * @exports WoltLabSuite/Core/Ui/Redactor/Metacode - */ - return { - /** - * Converts `` into the bbcode representation. - * - * @param {Element} element textarea element - */ - convert: function(element) { - element.textContent = this.convertFromHtml(element.textContent); - }, - - convertFromHtml: function (editorId, html) { - var div = elCreate('div'); - div.innerHTML = html; - - var attributes, data, metacode, metacodes = elByTag('woltlab-metacode', div), name, tagClose, tagOpen; - while (metacodes.length) { - metacode = metacodes[0]; - name = elData(metacode, 'name'); - attributes = this._parseAttributes(elData(metacode, 'attributes')); - - data = { - attributes: attributes, - cancel: false, - metacode: metacode - }; - - EventHandler.fire('com.woltlab.wcf.redactor2', 'metacode_' + name + '_' + editorId, data); - if (data.cancel === true) { - continue; - } - - tagOpen = this._getOpeningTag(name, attributes); - tagClose = this._getClosingTag(name); - - if (metacode.parentNode === div) { - DomUtil.prepend(tagOpen, this._getFirstParagraph(metacode)); - this._getLastParagraph(metacode).appendChild(tagClose); - } - else { - DomUtil.prepend(tagOpen, metacode); - metacode.appendChild(tagClose); - } - - DomUtil.unwrapChildNodes(metacode); - } - - // convert `…` to `[tt]…[/tt]` - var inlineCode, inlineCodes = elByTag('kbd', div); - while (inlineCodes.length) { - inlineCode = inlineCodes[0]; - - inlineCode.insertBefore(document.createTextNode('[tt]'), inlineCode.firstChild); - inlineCode.appendChild(document.createTextNode('[/tt]')); - - DomUtil.unwrapChildNodes(inlineCode); - } - - return div.innerHTML; - }, - - /** - * Returns a text node representing the opening bbcode tag. - * - * @param {string} name bbcode tag - * @param {Array} attributes list of attributes - * @returns {Text} text node containing the opening bbcode tag - * @protected - */ - _getOpeningTag: function(name, attributes) { - var buffer = '[' + name; - if (attributes.length) { - buffer += '='; - - for (var i = 0, length = attributes.length; i < length; i++) { - if (i > 0) buffer += ","; - buffer += "'" + attributes[i] + "'"; - } - } - - return document.createTextNode(buffer + ']'); - }, - - /** - * Returns a text node representing the closing bbcode tag. - * - * @param {string} name bbcode tag - * @returns {Text} text node containing the closing bbcode tag - * @protected - */ - _getClosingTag: function(name) { - return document.createTextNode('[/' + name + ']'); - }, - - /** - * Returns the first paragraph of provided element. If there are no children or - * the first child is not a paragraph, a new paragraph is created and inserted - * as first child. - * - * @param {Element} element metacode element - * @returns {Element} paragraph that is the first child of provided element - * @protected - */ - _getFirstParagraph: function (element) { - var firstChild, paragraph; - - if (element.childElementCount === 0) { - paragraph = elCreate('p'); - element.appendChild(paragraph); - } - else { - firstChild = element.children[0]; - - if (firstChild.nodeName === 'P') { - paragraph = firstChild; - } - else { - paragraph = elCreate('p'); - element.insertBefore(paragraph, firstChild); - } - } - - return paragraph; - }, - - /** - * Returns the last paragraph of provided element. If there are no children or - * the last child is not a paragraph, a new paragraph is created and inserted - * as last child. - * - * @param {Element} element metacode element - * @returns {Element} paragraph that is the last child of provided element - * @protected - */ - _getLastParagraph: function (element) { - var count = element.childElementCount, lastChild, paragraph; - - if (count === 0) { - paragraph = elCreate('p'); - element.appendChild(paragraph); - } - else { - lastChild = element.children[count - 1]; - - if (lastChild.nodeName === 'P') { - paragraph = lastChild; - } - else { - paragraph = elCreate('p'); - element.appendChild(paragraph); - } - } - - return paragraph; - }, - - /** - * Parses the attributes string. - * - * @param {string} attributes base64- and JSON-encoded attributes - * @return {Array} list of parsed attributes - * @protected - */ - _parseAttributes: function(attributes) { - try { - attributes = JSON.parse(atob(attributes)); - } - catch (e) { /* invalid base64 data or invalid json */ } - - if (!Array.isArray(attributes)) { - return []; - } - - var attribute, parsedAttributes = []; - for (var i = 0, length = attributes.length; i < length; i++) { - attribute = attributes[i]; - - if (typeof attribute === 'string') { - attribute = attribute.replace(/^'(.*)'$/, '$1'); - } - - parsedAttributes.push(attribute); - } - - return parsedAttributes; - } - }; -}); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.ts new file mode 100644 index 0000000000..94d30bb0b9 --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Redactor/Metacode.ts @@ -0,0 +1,147 @@ +/** + * Converts `` into the bbcode representation. + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Redactor/Metacode + */ + +import * as EventHandler from "../../Event/Handler"; +import DomUtil from "../../Dom/Util"; + +type Attributes = string[]; + +/** + * Returns a text node representing the opening bbcode tag. + */ +function getOpeningTag(name: string, attributes: Attributes): Text { + let buffer = "[" + name; + if (attributes.length) { + buffer += "="; + buffer += attributes.map((attribute) => `'${attribute}'`).join(","); + } + + return document.createTextNode(buffer + "]"); +} + +/** + * Returns a text node representing the closing bbcode tag. + */ +function getClosingTag(name: string): Text { + return document.createTextNode(`[/${name}]`); +} + +/** + * Returns the first paragraph of provided element. If there are no children or + * the first child is not a paragraph, a new paragraph is created and inserted + * as first child. + */ +function getFirstParagraph(element: HTMLElement): HTMLElement { + let paragraph: HTMLElement; + if (element.childElementCount === 0) { + paragraph = document.createElement("p"); + element.appendChild(paragraph); + } else { + const firstChild = element.children[0] as HTMLElement; + + if (firstChild.nodeName === "P") { + paragraph = firstChild; + } else { + paragraph = document.createElement("p"); + element.insertBefore(paragraph, firstChild); + } + } + + return paragraph; +} + +/** + * Returns the last paragraph of provided element. If there are no children or + * the last child is not a paragraph, a new paragraph is created and inserted + * as last child. + */ +function getLastParagraph(element: HTMLElement): HTMLElement { + const count = element.childElementCount; + + let paragraph: HTMLElement; + if (count === 0) { + paragraph = document.createElement("p"); + element.appendChild(paragraph); + } else { + const lastChild = element.children[count - 1] as HTMLElement; + + if (lastChild.nodeName === "P") { + paragraph = lastChild; + } else { + paragraph = document.createElement("p"); + element.appendChild(paragraph); + } + } + + return paragraph; +} + +/** + * Parses the attributes string. + */ +function parseAttributes(attributes: string): Attributes { + try { + attributes = JSON.parse(atob(attributes)); + } catch (e) { + /* invalid base64 data or invalid json */ + } + + if (!Array.isArray(attributes)) { + return []; + } + + return attributes.map((attribute: string | number) => { + return attribute.toString().replace(/^'(.*)'$/, "$1"); + }); +} + +export function convertFromHtml(editorId: string, html: string): string { + const div = document.createElement("div"); + div.innerHTML = html; + + div.querySelectorAll("woltlab-metacode").forEach((metacode: HTMLElement) => { + const name = metacode.dataset.name!; + const attributes = parseAttributes(metacode.dataset.attributes || ""); + + const data = { + attributes: attributes, + cancel: false, + metacode: metacode, + }; + + EventHandler.fire("com.woltlab.wcf.redactor2", `metacode_${name}_${editorId}`, data); + if (data.cancel) { + return; + } + + const tagOpen = getOpeningTag(name, attributes); + const tagClose = getClosingTag(name); + + if (metacode.parentElement === div) { + const paragraph = getFirstParagraph(metacode); + paragraph.insertBefore(tagOpen, paragraph.firstChild); + getLastParagraph(metacode).appendChild(tagClose); + } else { + metacode.insertBefore(tagOpen, metacode.firstChild); + metacode.appendChild(tagClose); + } + + DomUtil.unwrapChildNodes(metacode); + }); + + // convert `…` to `[tt]…[/tt]` + div.querySelectorAll("kbd").forEach((inlineCode) => { + inlineCode.insertBefore(document.createTextNode("[tt]"), inlineCode.firstChild); + inlineCode.appendChild(document.createTextNode("[/tt]")); + + DomUtil.unwrapChildNodes(inlineCode); + }); + + return div.innerHTML; +} -- 2.20.1