2021-04-13 10:57:08 +02:00
|
|
|
// Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
2021-03-25 00:43:01 +01:00
|
|
|
import { isHTMLElement, isNightMode } from "./helpers";
|
|
|
|
import { removeNode as removeElement } from "./node";
|
2021-03-24 23:35:51 +01:00
|
|
|
import {
|
|
|
|
filterStylingNightMode,
|
|
|
|
filterStylingLightMode,
|
|
|
|
filterStylingInternal,
|
2021-03-25 00:43:01 +01:00
|
|
|
} from "./styling";
|
2021-03-24 21:04:12 +01:00
|
|
|
|
|
|
|
interface TagsAllowed {
|
2021-03-24 21:18:16 +01:00
|
|
|
[tagName: string]: FilterMethod;
|
2021-03-24 21:04:12 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 21:18:16 +01:00
|
|
|
type FilterMethod = (element: Element) => void;
|
|
|
|
|
2021-03-25 01:23:54 +01:00
|
|
|
function filterAttributes(
|
2021-03-24 21:04:12 +01:00
|
|
|
attributePredicate: (attributeName: string) => boolean,
|
2021-10-19 01:06:00 +02:00
|
|
|
element: Element,
|
2021-03-24 21:04:12 +01:00
|
|
|
): void {
|
|
|
|
for (const attr of [...element.attributes]) {
|
|
|
|
const attrName = attr.name.toUpperCase();
|
|
|
|
|
2021-03-25 01:23:54 +01:00
|
|
|
if (!attributePredicate(attrName)) {
|
2021-03-24 21:04:12 +01:00
|
|
|
element.removeAttributeNode(attr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 01:23:54 +01:00
|
|
|
function allowNone(element: Element): void {
|
|
|
|
filterAttributes(() => false, element);
|
2021-03-24 21:18:16 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 01:21:33 +02:00
|
|
|
const allow =
|
|
|
|
(attrs: string[]): FilterMethod =>
|
|
|
|
(element: Element): void =>
|
|
|
|
filterAttributes(
|
|
|
|
(attributeName: string) => attrs.includes(attributeName),
|
2021-10-19 01:06:00 +02:00
|
|
|
element,
|
2021-05-26 01:21:33 +02:00
|
|
|
);
|
2021-03-24 21:04:12 +01:00
|
|
|
|
2021-03-24 21:18:16 +01:00
|
|
|
function unwrapElement(element: Element): void {
|
2021-03-25 01:47:47 +01:00
|
|
|
element.replaceWith(...element.childNodes);
|
2021-03-24 21:18:16 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 23:35:51 +01:00
|
|
|
function filterSpan(element: Element): void {
|
2021-03-25 01:23:54 +01:00
|
|
|
const filterAttrs = allow(["STYLE"]);
|
2021-03-24 23:35:51 +01:00
|
|
|
filterAttrs(element);
|
|
|
|
|
|
|
|
const filterStyle = isNightMode() ? filterStylingNightMode : filterStylingLightMode;
|
|
|
|
filterStyle(element as HTMLSpanElement);
|
|
|
|
}
|
|
|
|
|
2021-03-24 21:18:16 +01:00
|
|
|
const tagsAllowedBasic: TagsAllowed = {
|
2021-03-25 01:23:54 +01:00
|
|
|
BR: allowNone,
|
|
|
|
IMG: allow(["SRC"]),
|
|
|
|
DIV: allowNone,
|
|
|
|
P: allowNone,
|
|
|
|
SUB: allowNone,
|
|
|
|
SUP: allowNone,
|
2021-03-24 21:18:16 +01:00
|
|
|
TITLE: removeElement,
|
2021-03-24 21:04:12 +01:00
|
|
|
};
|
|
|
|
|
2021-03-24 21:18:16 +01:00
|
|
|
const tagsAllowedExtended: TagsAllowed = {
|
2021-03-24 21:04:12 +01:00
|
|
|
...tagsAllowedBasic,
|
2021-03-25 01:23:54 +01:00
|
|
|
A: allow(["HREF"]),
|
|
|
|
B: allowNone,
|
|
|
|
BLOCKQUOTE: allowNone,
|
|
|
|
CODE: allowNone,
|
|
|
|
DD: allowNone,
|
|
|
|
DL: allowNone,
|
|
|
|
DT: allowNone,
|
|
|
|
EM: allowNone,
|
|
|
|
FONT: allow(["COLOR"]),
|
|
|
|
H1: allowNone,
|
|
|
|
H2: allowNone,
|
|
|
|
H3: allowNone,
|
|
|
|
I: allowNone,
|
|
|
|
LI: allowNone,
|
|
|
|
OL: allowNone,
|
|
|
|
PRE: allowNone,
|
|
|
|
RP: allowNone,
|
|
|
|
RT: allowNone,
|
|
|
|
RUBY: allowNone,
|
2021-03-24 21:04:12 +01:00
|
|
|
SPAN: filterSpan,
|
2021-03-25 01:23:54 +01:00
|
|
|
STRONG: allowNone,
|
|
|
|
TABLE: allowNone,
|
|
|
|
TD: allow(["COLSPAN", "ROWSPAN"]),
|
|
|
|
TH: allow(["COLSPAN", "ROWSPAN"]),
|
|
|
|
TR: allow(["ROWSPAN"]),
|
|
|
|
U: allowNone,
|
|
|
|
UL: allowNone,
|
2021-03-24 21:04:12 +01:00
|
|
|
};
|
2021-03-24 21:18:16 +01:00
|
|
|
|
2021-05-26 01:21:33 +02:00
|
|
|
const filterElementTagsAllowed =
|
|
|
|
(tagsAllowed: TagsAllowed) =>
|
|
|
|
(element: Element): void => {
|
|
|
|
const tagName = element.tagName;
|
|
|
|
|
|
|
|
if (Object.prototype.hasOwnProperty.call(tagsAllowed, tagName)) {
|
|
|
|
tagsAllowed[tagName](element);
|
|
|
|
} else if (element.innerHTML) {
|
|
|
|
unwrapElement(element);
|
|
|
|
} else {
|
|
|
|
removeElement(element);
|
|
|
|
}
|
|
|
|
};
|
2021-03-24 23:35:51 +01:00
|
|
|
|
|
|
|
export const filterElementBasic = filterElementTagsAllowed(tagsAllowedBasic);
|
|
|
|
export const filterElementExtended = filterElementTagsAllowed(tagsAllowedExtended);
|
|
|
|
|
|
|
|
export function filterElementInternal(element: Element): void {
|
|
|
|
if (isHTMLElement(element)) {
|
|
|
|
filterStylingInternal(element);
|
|
|
|
}
|
2021-03-24 21:18:16 +01:00
|
|
|
}
|