anki/ts/html-filter/element.ts

113 lines
2.8 KiB
TypeScript
Raw Normal View History

import { isHTMLElement, isNightMode } from "./helpers";
import { removeNode as removeElement } from "./node";
import {
filterStylingNightMode,
filterStylingLightMode,
filterStylingInternal,
} from "./styling";
interface TagsAllowed {
[tagName: string]: FilterMethod;
}
type FilterMethod = (element: Element) => void;
function filterOutAttributes(
attributePredicate: (attributeName: string) => boolean,
element: Element
): void {
for (const attr of [...element.attributes]) {
const attrName = attr.name.toUpperCase();
if (attributePredicate(attrName)) {
element.removeAttributeNode(attr);
}
}
}
function blockAll(element: Element): void {
filterOutAttributes(() => true, element);
}
const blockExcept = (attrs: string[]): FilterMethod => (element: Element): void =>
filterOutAttributes(
(attributeName: string) => !attrs.includes(attributeName),
element
);
function unwrapElement(element: Element): void {
element.outerHTML = element.innerHTML;
}
function filterSpan(element: Element): void {
const filterAttrs = blockExcept(["STYLE"]);
filterAttrs(element);
const filterStyle = isNightMode() ? filterStylingNightMode : filterStylingLightMode;
filterStyle(element as HTMLSpanElement);
}
const tagsAllowedBasic: TagsAllowed = {
BR: blockAll,
IMG: blockExcept(["SRC"]),
DIV: blockAll,
P: blockAll,
SUB: blockAll,
SUP: blockAll,
TITLE: removeElement,
};
const tagsAllowedExtended: TagsAllowed = {
...tagsAllowedBasic,
A: blockExcept(["HREF"]),
B: blockAll,
BLOCKQUOTE: blockAll,
CODE: blockAll,
DD: blockAll,
DL: blockAll,
DT: blockAll,
EM: blockAll,
FONT: blockExcept(["COLOR"]),
H1: blockAll,
H2: blockAll,
H3: blockAll,
I: blockAll,
LI: blockAll,
OL: blockAll,
PRE: blockAll,
RP: blockAll,
RT: blockAll,
RUBY: blockAll,
SPAN: filterSpan,
STRONG: blockAll,
TABLE: blockAll,
TD: blockExcept(["COLSPAN", "ROWSPAN"]),
TH: blockExcept(["COLSPAN", "ROWSPAN"]),
TR: blockExcept(["ROWSPAN"]),
U: blockAll,
UL: blockAll,
};
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) {
removeElement(element);
} else {
unwrapElement(element);
}
};
export const filterElementBasic = filterElementTagsAllowed(tagsAllowedBasic);
export const filterElementExtended = filterElementTagsAllowed(tagsAllowedExtended);
export function filterElementInternal(element: Element): void {
if (isHTMLElement(element)) {
filterStylingInternal(element);
}
}