Preserve background-color when pasting external content in light mode

Closes #1964
This commit is contained in:
Damien Elmes 2022-08-19 12:31:26 +10:00
parent 92171e25e6
commit 0809812c1d
2 changed files with 71 additions and 53 deletions

View file

@ -9,4 +9,35 @@ describe("filterHTML", () => {
expect(filterHTML("", true, false)).toBe(""); expect(filterHTML("", true, false)).toBe("");
expect(filterHTML("", false, false)).toBe(""); expect(filterHTML("", false, false)).toBe("");
}); });
test("internal filtering", () => {
// font-size is filtered, weight is not
expect(
filterHTML(
'<div style="font-weight: bold; font-size: 10px;"></div>',
true,
true,
),
).toBe('<div style="font-weight: bold;"></div>');
});
test("background color", () => {
// transparent is stripped, other colors are not
expect(
filterHTML(
'<span style="background-color: transparent;"></span>',
false,
true,
),
).toBe('<span style=""></span>');
expect(
filterHTML('<span style="background-color: blue;"></span>', false, true),
).toBe('<span style="background-color: blue;"></span>');
// except if extended mode is off
expect(
filterHTML('<span style="background-color: blue;">x</span>', false, false),
).toBe("x");
// or if it's an internal paste
expect(
filterHTML('<span style="background-color: blue;"></span>', true, true),
).toBe('<span style=""></span>');
});
}); });

View file

@ -1,63 +1,50 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// 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
interface AllowPropertiesBlockValues { /// Keep property if true.
[property: string]: string[];
}
type BlockProperties = string[];
type StylingPredicate = (property: string, value: string) => boolean; type StylingPredicate = (property: string, value: string) => boolean;
const stylingNightMode: AllowPropertiesBlockValues = { const keep = (_key: string, _value: string) => true;
"font-weight": [], const discard = (_key: string, _value: string) => false;
"font-style": [],
"text-decoration-line": [],
};
const stylingLightMode: AllowPropertiesBlockValues = { /// Return a function that filters out certain styles.
color: [], /// - If the style is listed in `exceptions`, the provided predicate is used.
"background-color": ["transparent"], /// - If the style is not listed, the default predicate is used instead.
...stylingNightMode, function filterStyling(
}; defaultPredicate: StylingPredicate,
exceptions: Record<string, StylingPredicate>,
const stylingInternal: BlockProperties = [ ): (element: HTMLElement) => void {
"background-color", return (element: HTMLElement): void => {
"font-size", // jsdom does not support @@iterator, so manually iterate
"font-family", for (let i = 0; i < element.style.length; i++) {
"width", const key = element.style.item(i);
"height", const value = element.style.getPropertyValue(key);
"max-width", const predicate = exceptions[key] ?? defaultPredicate;
"max-height", if (!predicate(key, value)) {
]; element.style.removeProperty(key);
const allowPropertiesBlockValues =
(allowBlock: AllowPropertiesBlockValues): StylingPredicate =>
(property: string, value: string): boolean =>
Object.prototype.hasOwnProperty.call(allowBlock, property) &&
!allowBlock[property].includes(value);
const blockProperties =
(block: BlockProperties): StylingPredicate =>
(property: string): boolean =>
!block.includes(property);
const filterStyling =
(predicate: (property: string, value: string) => boolean) =>
(element: HTMLElement): void => {
for (const property of [...element.style]) {
const value = element.style.getPropertyValue(property);
if (!predicate(property, value)) {
element.style.removeProperty(property);
} }
} }
}; };
}
export const filterStylingNightMode = filterStyling( const nightModeExceptions = {
allowPropertiesBlockValues(stylingNightMode), "font-weight": keep,
); "font-style": keep,
export const filterStylingLightMode = filterStyling( "text-decoration-line": keep,
allowPropertiesBlockValues(stylingLightMode), };
);
export const filterStylingInternal = filterStyling(blockProperties(stylingInternal)); export const filterStylingNightMode = filterStyling(discard, nightModeExceptions);
export const filterStylingLightMode = filterStyling(discard, {
color: keep,
"background-color": (_key: string, value: string) => value != "transparent",
...nightModeExceptions,
});
export const filterStylingInternal = filterStyling(keep, {
"background-color": discard,
"font-size": discard,
"font-family": discard,
width: discard,
height: discard,
"max-width": discard,
"max-height": discard,
});