Convert serialize and deserialize to TypeScript
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
95eaf94cd8
commit
c72139fc3f
2 changed files with 32 additions and 23 deletions
|
@ -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") {
|
|
@ -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];
|
Loading…
Reference in a new issue