Move most of tags allowed to its own file
This commit is contained in:
parent
592d73c344
commit
1d4d7fabec
@ -77,3 +77,7 @@ export function caretToEnd(currentField: EditingArea): void {
|
|||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
selection.addRange(range);
|
selection.addRange(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNightMode(): boolean {
|
||||||
|
return document.body.classList.contains("nightMode");
|
||||||
|
}
|
||||||
|
@ -2,92 +2,9 @@
|
|||||||
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
||||||
|
|
||||||
import { nodeIsElement } from "./helpers";
|
import { nodeIsElement } from "./helpers";
|
||||||
|
import { tagsAllowedBasic, tagsAllowedExtended } from "./htmlFilterTagsAllowed";
|
||||||
|
|
||||||
const allowedTagsBasic = {};
|
////////////////////// //////////////////// ////////////////////
|
||||||
const allowedTagsExtended = {};
|
|
||||||
|
|
||||||
let TAGS_WITHOUT_ATTRS = ["P", "DIV", "BR", "SUB", "SUP"];
|
|
||||||
for (const tag of TAGS_WITHOUT_ATTRS) {
|
|
||||||
allowedTagsBasic[tag] = { attrs: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
TAGS_WITHOUT_ATTRS = [
|
|
||||||
"B",
|
|
||||||
"BLOCKQUOTE",
|
|
||||||
"CODE",
|
|
||||||
"DD",
|
|
||||||
"DL",
|
|
||||||
"DT",
|
|
||||||
"EM",
|
|
||||||
"H1",
|
|
||||||
"H2",
|
|
||||||
"H3",
|
|
||||||
"I",
|
|
||||||
"LI",
|
|
||||||
"OL",
|
|
||||||
"PRE",
|
|
||||||
"RP",
|
|
||||||
"RT",
|
|
||||||
"RUBY",
|
|
||||||
"STRONG",
|
|
||||||
"TABLE",
|
|
||||||
"U",
|
|
||||||
"UL",
|
|
||||||
];
|
|
||||||
for (const tag of TAGS_WITHOUT_ATTRS) {
|
|
||||||
allowedTagsExtended[tag] = { attrs: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
allowedTagsBasic["IMG"] = { attrs: ["SRC"] };
|
|
||||||
|
|
||||||
allowedTagsExtended["A"] = { attrs: ["HREF"] };
|
|
||||||
allowedTagsExtended["TR"] = { attrs: ["ROWSPAN"] };
|
|
||||||
allowedTagsExtended["TD"] = { attrs: ["COLSPAN", "ROWSPAN"] };
|
|
||||||
allowedTagsExtended["TH"] = { attrs: ["COLSPAN", "ROWSPAN"] };
|
|
||||||
allowedTagsExtended["FONT"] = { attrs: ["COLOR"] };
|
|
||||||
|
|
||||||
const allowedStyling = {
|
|
||||||
color: true,
|
|
||||||
"background-color": true,
|
|
||||||
"font-weight": true,
|
|
||||||
"font-style": true,
|
|
||||||
"text-decoration-line": true,
|
|
||||||
};
|
|
||||||
|
|
||||||
function isNightMode(): boolean {
|
|
||||||
return document.body.classList.contains("nightMode");
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterExternalSpan(elem: HTMLElement): void {
|
|
||||||
// filter out attributes
|
|
||||||
for (const attr of [...elem.attributes]) {
|
|
||||||
const attrName = attr.name.toUpperCase();
|
|
||||||
|
|
||||||
if (attrName !== "STYLE") {
|
|
||||||
elem.removeAttributeNode(attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter styling
|
|
||||||
for (const name of [...elem.style]) {
|
|
||||||
const value = elem.style.getPropertyValue(name);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!allowedStyling.hasOwnProperty(name) ||
|
|
||||||
// google docs adds this unnecessarily
|
|
||||||
(name === "background-color" && value === "transparent") ||
|
|
||||||
// ignore coloured text in night mode for now
|
|
||||||
(isNightMode() && (name === "background-color" || name === "color"))
|
|
||||||
) {
|
|
||||||
elem.style.removeProperty(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allowedTagsExtended["SPAN"] = filterExternalSpan;
|
|
||||||
|
|
||||||
// add basic tags to extended
|
|
||||||
Object.assign(allowedTagsExtended, allowedTagsBasic);
|
|
||||||
|
|
||||||
function isHTMLElement(elem: Element): elem is HTMLElement {
|
function isHTMLElement(elem: Element): elem is HTMLElement {
|
||||||
return elem instanceof HTMLElement;
|
return elem instanceof HTMLElement;
|
||||||
@ -127,29 +44,16 @@ function filterNode(node: Node, extendedMode: boolean): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag = extendedMode
|
const tagsAllowed = extendedMode ? tagsAllowedExtended : tagsAllowedBasic;
|
||||||
? allowedTagsExtended[node.tagName]
|
|
||||||
: allowedTagsBasic[node.tagName];
|
|
||||||
|
|
||||||
if (!tag) {
|
if (tagsAllowed.hasOwnProperty(node.tagName)) {
|
||||||
|
tagsAllowed[node.tagName](node);
|
||||||
|
} else {
|
||||||
if (!node.innerHTML || node.tagName === "TITLE") {
|
if (!node.innerHTML || node.tagName === "TITLE") {
|
||||||
node.parentNode.removeChild(node);
|
node.parentNode.removeChild(node);
|
||||||
} else {
|
} else {
|
||||||
node.outerHTML = node.innerHTML;
|
node.outerHTML = node.innerHTML;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (typeof tag === "function") {
|
|
||||||
// filtering function provided
|
|
||||||
tag(node);
|
|
||||||
} else {
|
|
||||||
// allowed, filter out attributes
|
|
||||||
for (const attr of [...node.attributes]) {
|
|
||||||
const attrName = attr.name.toUpperCase();
|
|
||||||
if (tag.attrs.indexOf(attrName) === -1) {
|
|
||||||
node.removeAttributeNode(attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
ts/editor/htmlFilterSpan.ts
Normal file
44
ts/editor/htmlFilterSpan.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { isNightMode } from "./helpers";
|
||||||
|
|
||||||
|
/* keys are allowed properties, values are blocked values */
|
||||||
|
const stylingAllowListNightMode = {
|
||||||
|
"font-weight": [],
|
||||||
|
"font-style": [],
|
||||||
|
"text-decoration-line": [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const stylingAllowList = {
|
||||||
|
color: [],
|
||||||
|
"background-color": ["transparent"],
|
||||||
|
...stylingAllowListNightMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
function isStylingAllowed(property: string, value: string): boolean {
|
||||||
|
const allowList = isNightMode() ? stylingAllowListNightMode : stylingAllowList;
|
||||||
|
|
||||||
|
return allowList.hasOwnProperty(property) && !allowList[property].includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowedAttrs = ["STYLE"];
|
||||||
|
|
||||||
|
export function filterSpan(element: Element): void {
|
||||||
|
// filter out attributes
|
||||||
|
for (const attr of [...element.attributes]) {
|
||||||
|
const attrName = attr.name.toUpperCase();
|
||||||
|
|
||||||
|
if (!allowedAttrs.includes(attrName)) {
|
||||||
|
element.removeAttributeNode(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter styling
|
||||||
|
const elementStyle = (element as HTMLSpanElement).style;
|
||||||
|
|
||||||
|
for (const property of [...elementStyle]) {
|
||||||
|
const value = elementStyle.getPropertyValue(name);
|
||||||
|
|
||||||
|
if (!isStylingAllowed(property, value)) {
|
||||||
|
elementStyle.removeProperty(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
ts/editor/htmlFilterTagsAllowed.ts
Normal file
72
ts/editor/htmlFilterTagsAllowed.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { filterSpan } from "./htmlFilterSpan";
|
||||||
|
|
||||||
|
type FilterMethod = (element: Element) => void;
|
||||||
|
|
||||||
|
interface TagsAllowed {
|
||||||
|
[key: string]: FilterMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 blockExcept(attrs: string[]): FilterMethod {
|
||||||
|
return (element: Element) =>
|
||||||
|
filterOutAttributes(
|
||||||
|
(attributeName: string) => !attrs.includes(attributeName),
|
||||||
|
element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockAll(element: Element): void {
|
||||||
|
filterOutAttributes(() => true, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tagsAllowedBasic: TagsAllowed = {
|
||||||
|
BR: blockAll,
|
||||||
|
IMG: blockExcept(["SRC"]),
|
||||||
|
DIV: blockAll,
|
||||||
|
P: blockAll,
|
||||||
|
SUB: blockAll,
|
||||||
|
SUP: blockAll,
|
||||||
|
};
|
||||||
|
|
||||||
|
export 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,
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user