Convert serialize and deserialize to TypeScript

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2020-04-15 00:49:08 +01:00
parent 95eaf94cd8
commit c72139fc3f
2 changed files with 32 additions and 23 deletions

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2019 New Vector Ltd Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,11 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { walkDOMDepthFirst } from "./dom"; import { walkDOMDepthFirst } from "./dom";
import { checkBlockNode } from "../HtmlUtils"; import { checkBlockNode } from "../HtmlUtils";
import {getPrimaryPermalinkEntity} from "../utils/permalinks/Permalinks"; import { getPrimaryPermalinkEntity } from "../utils/permalinks/Permalinks";
import { PartCreator } from "./parts";
function parseAtRoomMentions(text, partCreator) { function parseAtRoomMentions(text: string, partCreator: PartCreator) {
const ATROOM = "@room"; const ATROOM = "@room";
const parts = []; const parts = [];
text.split(ATROOM).forEach((textPart, i, arr) => { text.split(ATROOM).forEach((textPart, i, arr) => {
@ -37,7 +40,7 @@ function parseAtRoomMentions(text, partCreator) {
return parts; return parts;
} }
function parseLink(a, partCreator) { function parseLink(a: HTMLAnchorElement, partCreator: PartCreator) {
const {href} = a; const {href} = a;
const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID
const prefix = resourceId ? resourceId[0] : undefined; // First character of ID const prefix = resourceId ? resourceId[0] : undefined; // First character of ID
@ -56,11 +59,11 @@ function parseLink(a, partCreator) {
} }
} }
function parseCodeBlock(n, partCreator) { function parseCodeBlock(n: HTMLElement, partCreator: PartCreator) {
const parts = []; const parts = [];
let language = ""; let language = "";
if (n.firstChild && n.firstChild.nodeName === "CODE") { if (n.firstChild && n.firstChild.nodeName === "CODE") {
for (const className of n.firstChild.classList) { for (const className of (<HTMLElement>n.firstChild).classList) {
if (className.startsWith("language-")) { if (className.startsWith("language-")) {
language = className.substr("language-".length); language = className.substr("language-".length);
break; break;
@ -77,12 +80,17 @@ function parseCodeBlock(n, partCreator) {
return parts; return parts;
} }
function parseHeader(el, partCreator) { function parseHeader(el: HTMLElement, partCreator: PartCreator) {
const depth = parseInt(el.nodeName.substr(1), 10); const depth = parseInt(el.nodeName.substr(1), 10);
return partCreator.plain("#".repeat(depth) + " "); return partCreator.plain("#".repeat(depth) + " ");
} }
function parseElement(n, partCreator, lastNode, state) { interface IState {
listIndex: number[];
listDepth?: number;
}
function parseElement(n: HTMLElement, partCreator: PartCreator, lastNode: HTMLElement | undefined, state: IState) {
switch (n.nodeName) { switch (n.nodeName) {
case "H1": case "H1":
case "H2": case "H2":
@ -92,7 +100,7 @@ function parseElement(n, partCreator, lastNode, state) {
case "H6": case "H6":
return parseHeader(n, partCreator); return parseHeader(n, partCreator);
case "A": case "A":
return parseLink(n, partCreator); return parseLink(<HTMLAnchorElement>n, partCreator);
case "BR": case "BR":
return partCreator.newline(); return partCreator.newline();
case "EM": case "EM":
@ -123,7 +131,7 @@ function parseElement(n, partCreator, lastNode, state) {
break; break;
} }
case "OL": case "OL":
state.listIndex.push(n.start || 1); state.listIndex.push((<HTMLOListElement>n).start || 1);
// fallthrough // fallthrough
case "UL": case "UL":
state.listDepth = (state.listDepth || 0) + 1; state.listDepth = (state.listDepth || 0) + 1;
@ -174,7 +182,7 @@ function prefixQuoteLines(isFirstNode, parts, partCreator) {
} }
} }
function parseHtmlMessage(html, partCreator, isQuotedMessage) { function parseHtmlMessage(html: string, partCreator: PartCreator, isQuotedMessage: boolean) {
// no nodes from parsing here should be inserted in the document, // no nodes from parsing here should be inserted in the document,
// as scripts in event handlers, etc would be executed then. // as scripts in event handlers, etc would be executed then.
// we're only taking text, so that is fine // we're only taking text, so that is fine
@ -182,7 +190,7 @@ function parseHtmlMessage(html, partCreator, isQuotedMessage) {
const parts = []; const parts = [];
let lastNode; let lastNode;
let inQuote = isQuotedMessage; let inQuote = isQuotedMessage;
const state = { const state: IState = {
listIndex: [], listIndex: [],
}; };
@ -249,7 +257,7 @@ function parseHtmlMessage(html, partCreator, isQuotedMessage) {
return parts; return parts;
} }
export function parsePlainTextMessage(body, partCreator, isQuotedMessage) { export function parsePlainTextMessage(body: string, partCreator: PartCreator, isQuotedMessage: boolean) {
const lines = body.split(/\r\n|\r|\n/g); // split on any new-line combination not just \n, collapses \r\n const lines = body.split(/\r\n|\r|\n/g); // split on any new-line combination not just \n, collapses \r\n
const parts = lines.reduce((parts, line, i) => { const parts = lines.reduce((parts, line, i) => {
if (isQuotedMessage) { if (isQuotedMessage) {
@ -265,7 +273,7 @@ export function parsePlainTextMessage(body, partCreator, isQuotedMessage) {
return parts; return parts;
} }
export function parseEvent(event, partCreator, {isQuotedMessage = false} = {}) { export function parseEvent(event: MatrixEvent, partCreator: PartCreator, {isQuotedMessage = false} = {}) {
const content = event.getContent(); const content = event.getContent();
let parts; let parts;
if (content.format === "org.matrix.custom.html") { if (content.format === "org.matrix.custom.html") {

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2019 New Vector Ltd Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,8 +17,9 @@ limitations under the License.
import Markdown from '../Markdown'; import Markdown from '../Markdown';
import {makeGenericPermalink} from "../utils/permalinks/Permalinks"; import {makeGenericPermalink} from "../utils/permalinks/Permalinks";
import EditorModel from "./model";
export function mdSerialize(model) { export function mdSerialize(model: EditorModel) {
return model.parts.reduce((html, part) => { return model.parts.reduce((html, part) => {
switch (part.type) { switch (part.type) {
case "newline": case "newline":
@ -35,7 +36,7 @@ export function mdSerialize(model) {
}, ""); }, "");
} }
export function htmlSerializeIfNeeded(model, {forceHTML = false} = {}) { export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} = {}) {
const md = mdSerialize(model); const md = mdSerialize(model);
const parser = new Markdown(md); const parser = new Markdown(md);
if (!parser.isPlainText() || forceHTML) { if (!parser.isPlainText() || forceHTML) {
@ -43,7 +44,7 @@ export function htmlSerializeIfNeeded(model, {forceHTML = false} = {}) {
} }
} }
export function textSerialize(model) { export function textSerialize(model: EditorModel) {
return model.parts.reduce((text, part) => { return model.parts.reduce((text, part) => {
switch (part.type) { switch (part.type) {
case "newline": case "newline":
@ -60,11 +61,11 @@ export function textSerialize(model) {
}, ""); }, "");
} }
export function containsEmote(model) { export function containsEmote(model: EditorModel) {
return startsWith(model, "/me "); return startsWith(model, "/me ");
} }
export function startsWith(model, prefix) { export function startsWith(model: EditorModel, prefix: string) {
const firstPart = model.parts[0]; const firstPart = model.parts[0];
// part type will be "plain" while editing, // part type will be "plain" while editing,
// and "command" while composing a message. // and "command" while composing a message.
@ -73,18 +74,18 @@ export function startsWith(model, prefix) {
firstPart.text.startsWith(prefix); firstPart.text.startsWith(prefix);
} }
export function stripEmoteCommand(model) { export function stripEmoteCommand(model: EditorModel) {
// trim "/me " // trim "/me "
return stripPrefix(model, "/me "); return stripPrefix(model, "/me ");
} }
export function stripPrefix(model, prefix) { export function stripPrefix(model: EditorModel, prefix: string) {
model = model.clone(); model = model.clone();
model.removeText({index: 0, offset: 0}, prefix.length); model.removeText({index: 0, offset: 0}, prefix.length);
return model; return model;
} }
export function unescapeMessage(model) { export function unescapeMessage(model: EditorModel) {
const {parts} = model; const {parts} = model;
if (parts.length) { if (parts.length) {
const firstPart = parts[0]; const firstPart = parts[0];