@vonage/vivid 5.10.0 → 5.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/definition2.cjs +1 -1
- package/bundled/definition2.js +1 -1
- package/bundled/vivid-element.cjs +1 -1
- package/bundled/vivid-element.js +1 -1
- package/custom-elements.json +2515 -2405
- package/icon/definition.cjs +1 -1
- package/icon/definition.js +1 -1
- package/lib/rich-text-editor/rte/utils/sanitization.d.ts +7 -0
- package/package.json +5 -5
- package/rich-text-editor/definition.cjs +52 -11
- package/rich-text-editor/definition.js +52 -11
- package/rich-text-editor/index.cjs +14 -14
- package/rich-text-editor/index.js +1393 -1365
- package/styles/core/all.css +2 -2
- package/styles/core/theme.css +2 -2
- package/styles/core/typography.css +1 -1
- package/styles/tokens/theme-dark.css +4 -4
- package/styles/tokens/theme-light.css +4 -4
- package/styles/tokens/vivid-2-compat.css +1 -1
- package/unbundled/vivid-element.cjs +1 -1
- package/unbundled/vivid-element.js +1 -1
package/icon/definition.cjs
CHANGED
|
@@ -10,7 +10,7 @@ const fastWebUtilities = require('@microsoft/fast-web-utilities');
|
|
|
10
10
|
const styles = ":host{display:inline-block;vertical-align:sub}.control.connotation-accent{--_connotation-color-primary: var(--vvd-icon-accent-primary, var(--vvd-color-canvas-text))}.control.connotation-announcement{--_connotation-color-primary: var(--vvd-icon-announcement-primary, var(--vvd-color-announcement-500))}.control.connotation-cta{--_connotation-color-primary: var(--vvd-icon-cta-primary, var(--vvd-color-cta-500))}.control.connotation-success{--_connotation-color-primary: var(--vvd-icon-success-primary, var(--vvd-color-success-500))}.control.connotation-warning{--_connotation-color-primary: var(--vvd-icon-warning-primary, var(--vvd-color-warning-300))}.control.connotation-alert{--_connotation-color-primary: var(--vvd-icon-alert-primary, var(--vvd-color-alert-500))}.control.connotation-information{--_connotation-color-primary: var(--vvd-icon-information-primary, var(--vvd-color-information-500))}.control.size--6{--_icon-block-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2)) - (1px*(24 + 4*clamp(-1, var(--vvd-size-density, 0), 2)))) }.control.size--5{--_icon-block-size: calc(1px*(20 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--4{--_icon-block-size: calc(1px*(24 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--3{--_icon-block-size: calc(1px*(28 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--2{--_icon-block-size: calc(1px*(32 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--1{--_icon-block-size: calc(1px*(36 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-0{--_icon-block-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-1{--_icon-block-size: calc(1px*(44 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-2{--_icon-block-size: calc(1px*(48 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-3{--_icon-block-size: calc(1px*(52 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-4{--_icon-block-size: calc(1px*(56 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-5{--_icon-block-size: calc(1px*(60 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control:not(.size--6,.size--5,.size--4,.size--3,.size--2,.size--1,.size-0,.size-1,.size-2,.size-3,.size-4,.size-5){--_icon-block-size: 1em}.control{display:flex;margin:unset;block-size:var(--_icon-block-size);color:currentColor;contain:strict;inline-size:var(--_icon-block-size)}.control[class*=connotation]{color:var(--_connotation-color-primary)}slot,svg,::slotted(:where(svg,img)){margin:auto;block-size:inherit;inline-size:inherit}";
|
|
11
11
|
|
|
12
12
|
const ICONS_BASE_URL = 'https://icon.resources.vonage.com';
|
|
13
|
-
const ICONS_VERSION = '4.7.
|
|
13
|
+
const ICONS_VERSION = '4.7.1';
|
|
14
14
|
|
|
15
15
|
const numberConverter = {
|
|
16
16
|
toView(value) {
|
package/icon/definition.js
CHANGED
|
@@ -6,7 +6,7 @@ import { classNames } from '@microsoft/fast-web-utilities';
|
|
|
6
6
|
const styles = ":host{display:inline-block;vertical-align:sub}.control.connotation-accent{--_connotation-color-primary: var(--vvd-icon-accent-primary, var(--vvd-color-canvas-text))}.control.connotation-announcement{--_connotation-color-primary: var(--vvd-icon-announcement-primary, var(--vvd-color-announcement-500))}.control.connotation-cta{--_connotation-color-primary: var(--vvd-icon-cta-primary, var(--vvd-color-cta-500))}.control.connotation-success{--_connotation-color-primary: var(--vvd-icon-success-primary, var(--vvd-color-success-500))}.control.connotation-warning{--_connotation-color-primary: var(--vvd-icon-warning-primary, var(--vvd-color-warning-300))}.control.connotation-alert{--_connotation-color-primary: var(--vvd-icon-alert-primary, var(--vvd-color-alert-500))}.control.connotation-information{--_connotation-color-primary: var(--vvd-icon-information-primary, var(--vvd-color-information-500))}.control.size--6{--_icon-block-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2)) - (1px*(24 + 4*clamp(-1, var(--vvd-size-density, 0), 2)))) }.control.size--5{--_icon-block-size: calc(1px*(20 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--4{--_icon-block-size: calc(1px*(24 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--3{--_icon-block-size: calc(1px*(28 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--2{--_icon-block-size: calc(1px*(32 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size--1{--_icon-block-size: calc(1px*(36 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-0{--_icon-block-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-1{--_icon-block-size: calc(1px*(44 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-2{--_icon-block-size: calc(1px*(48 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-3{--_icon-block-size: calc(1px*(52 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-4{--_icon-block-size: calc(1px*(56 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control.size-5{--_icon-block-size: calc(1px*(60 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) }.control:not(.size--6,.size--5,.size--4,.size--3,.size--2,.size--1,.size-0,.size-1,.size-2,.size-3,.size-4,.size-5){--_icon-block-size: 1em}.control{display:flex;margin:unset;block-size:var(--_icon-block-size);color:currentColor;contain:strict;inline-size:var(--_icon-block-size)}.control[class*=connotation]{color:var(--_connotation-color-primary)}slot,svg,::slotted(:where(svg,img)){margin:auto;block-size:inherit;inline-size:inherit}";
|
|
7
7
|
|
|
8
8
|
const ICONS_BASE_URL = 'https://icon.resources.vonage.com';
|
|
9
|
-
const ICONS_VERSION = '4.7.
|
|
9
|
+
const ICONS_VERSION = '4.7.1';
|
|
10
10
|
|
|
11
11
|
const numberConverter = {
|
|
12
12
|
toView(value) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const ATTR_WHITESPACE: RegExp;
|
|
2
|
+
export declare const domPurifyConfig: {
|
|
3
|
+
ALLOWED_URI_REGEXP: RegExp;
|
|
4
|
+
};
|
|
5
|
+
export declare const sanitizeLinkHref: (url: string) => string;
|
|
6
|
+
export declare const sanitizeImageSrc: (url: string) => string;
|
|
7
|
+
export declare const escapeCssProperty: (value: string) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonage/vivid",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"homepage": "https://vivid.deno.dev",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/Vonage/vivid-3/issues"
|
|
@@ -89,16 +89,16 @@
|
|
|
89
89
|
"vitest-fetch-mock": "^0.4.5",
|
|
90
90
|
"wait-on": "^8.0.5",
|
|
91
91
|
"@repo/cem-analyzer-plugins": "1.0.0",
|
|
92
|
-
"@repo/eslint-config": "1.0.0",
|
|
93
|
-
"@repo/consts": "1.0.0",
|
|
94
92
|
"@repo/eslint-plugin-repo": "1.0.0",
|
|
93
|
+
"@repo/consts": "1.0.0",
|
|
95
94
|
"@repo/shared": "1.0.0",
|
|
95
|
+
"@repo/eslint-config": "1.0.0",
|
|
96
96
|
"@repo/stylelint-config": "1.0.0",
|
|
97
97
|
"@repo/styles": "1.0.0",
|
|
98
98
|
"@repo/tokens": "1.0.0",
|
|
99
|
-
"@repo/
|
|
99
|
+
"@repo/typescript-config": "1.0.0",
|
|
100
100
|
"@repo/tools": "1.0.0",
|
|
101
|
-
"@repo/
|
|
101
|
+
"@repo/vitest-config": "1.0.0"
|
|
102
102
|
},
|
|
103
103
|
"customElements": "custom-elements.json",
|
|
104
104
|
"scripts": {
|
|
@@ -11575,6 +11575,37 @@ class RteCoreImpl extends slottableRequest.RteFeatureImpl {
|
|
|
11575
11575
|
}
|
|
11576
11576
|
slottableRequest.featureFacade(RteCoreImpl);
|
|
11577
11577
|
|
|
11578
|
+
const DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
11579
|
+
const ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
11580
|
+
const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
11581
|
+
const domPurifyConfig = {
|
|
11582
|
+
// Allow blobs
|
|
11583
|
+
ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB
|
|
11584
|
+
};
|
|
11585
|
+
const sanitizeLinkHref = (url) => {
|
|
11586
|
+
if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) {
|
|
11587
|
+
return "";
|
|
11588
|
+
}
|
|
11589
|
+
const anchor = document.createElement("a");
|
|
11590
|
+
anchor.setAttribute("href", url);
|
|
11591
|
+
const sanitizedAnchor = DOMPurify.sanitize(anchor, {
|
|
11592
|
+
RETURN_DOM: true,
|
|
11593
|
+
...domPurifyConfig
|
|
11594
|
+
}).querySelector("a");
|
|
11595
|
+
/* v8 ignore next -- since href is already validated it's probably always present @preserve */
|
|
11596
|
+
return sanitizedAnchor.getAttribute("href") ?? "";
|
|
11597
|
+
};
|
|
11598
|
+
const sanitizeImageSrc = (url) => {
|
|
11599
|
+
const img = document.createElement("img");
|
|
11600
|
+
img.setAttribute("src", url);
|
|
11601
|
+
const sanitizedImg = DOMPurify.sanitize(img, {
|
|
11602
|
+
RETURN_DOM: true,
|
|
11603
|
+
...domPurifyConfig
|
|
11604
|
+
}).querySelector("img");
|
|
11605
|
+
return sanitizedImg.getAttribute("src") ?? "";
|
|
11606
|
+
};
|
|
11607
|
+
const escapeCssProperty = (value) => value.replace(/[;!].*/, "");
|
|
11608
|
+
|
|
11578
11609
|
const copy = (obj) => ({ ...obj });
|
|
11579
11610
|
const parseRulesFromSchema = (schema) => ({
|
|
11580
11611
|
nodes: Object.fromEntries(
|
|
@@ -11612,7 +11643,10 @@ class RteHtmlParser {
|
|
|
11612
11643
|
}).content.toJSON() ?? [];
|
|
11613
11644
|
}
|
|
11614
11645
|
parseHtml(html, options) {
|
|
11615
|
-
const dom = DOMPurify.sanitize(html, {
|
|
11646
|
+
const dom = DOMPurify.sanitize(html, {
|
|
11647
|
+
RETURN_DOM: true,
|
|
11648
|
+
...domPurifyConfig
|
|
11649
|
+
});
|
|
11616
11650
|
const container = document.createDocumentFragment();
|
|
11617
11651
|
container.appendChild(dom);
|
|
11618
11652
|
options?.modifyDom?.(container);
|
|
@@ -12771,7 +12805,7 @@ class RteFontSizePickerFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
12771
12805
|
toDOM: (mark) => {
|
|
12772
12806
|
return [
|
|
12773
12807
|
"span",
|
|
12774
|
-
{ style: `font-size: ${mark.attrs.size};` },
|
|
12808
|
+
{ style: `font-size: ${escapeCssProperty(mark.attrs.size)};` },
|
|
12775
12809
|
0
|
|
12776
12810
|
];
|
|
12777
12811
|
},
|
|
@@ -13441,7 +13475,7 @@ class RteTextColorPickerFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
13441
13475
|
return [
|
|
13442
13476
|
"span",
|
|
13443
13477
|
{
|
|
13444
|
-
style: `color: ${color}
|
|
13478
|
+
style: `color: ${escapeCssProperty(color)};`,
|
|
13445
13479
|
"data-text-color": color
|
|
13446
13480
|
},
|
|
13447
13481
|
0
|
|
@@ -13951,6 +13985,7 @@ const alignments = [
|
|
|
13951
13985
|
label: "right"
|
|
13952
13986
|
}
|
|
13953
13987
|
];
|
|
13988
|
+
const validTextAlign = (value) => alignments.find((a) => a.value === value)?.value ?? "left";
|
|
13954
13989
|
class RteAlignmentFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
13955
13990
|
constructor() {
|
|
13956
13991
|
super(...arguments);
|
|
@@ -13962,10 +13997,10 @@ class RteAlignmentFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
13962
13997
|
name: "textAlign",
|
|
13963
13998
|
default: "left",
|
|
13964
13999
|
fromDOM(dom) {
|
|
13965
|
-
return dom.style.textAlign
|
|
14000
|
+
return validTextAlign(dom.style.textAlign);
|
|
13966
14001
|
},
|
|
13967
14002
|
toStyles(node) {
|
|
13968
|
-
return [`text-align: ${node.attrs.textAlign}`];
|
|
14003
|
+
return [`text-align: ${validTextAlign(node.attrs.textAlign)}`];
|
|
13969
14004
|
}
|
|
13970
14005
|
})
|
|
13971
14006
|
];
|
|
@@ -14121,7 +14156,7 @@ class RteLinkFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
14121
14156
|
],
|
|
14122
14157
|
toDOM(node) {
|
|
14123
14158
|
const { href } = node.attrs;
|
|
14124
|
-
return ["a", { href }, 0];
|
|
14159
|
+
return ["a", { href: sanitizeLinkHref(href) }, 0];
|
|
14125
14160
|
}
|
|
14126
14161
|
}
|
|
14127
14162
|
}
|
|
@@ -14526,7 +14561,7 @@ class InlineImageView {
|
|
|
14526
14561
|
});
|
|
14527
14562
|
}
|
|
14528
14563
|
renderImg(src) {
|
|
14529
|
-
this.img.src = src;
|
|
14564
|
+
this.img.src = sanitizeImageSrc(src);
|
|
14530
14565
|
this.setContent(this.img, { allowPopover: true });
|
|
14531
14566
|
}
|
|
14532
14567
|
update(node) {
|
|
@@ -14564,13 +14599,13 @@ class RteInlineImageFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
14564
14599
|
},
|
|
14565
14600
|
parseDOM: [
|
|
14566
14601
|
{
|
|
14567
|
-
tag: "img[src]",
|
|
14602
|
+
tag: "img[src],img[data-src]",
|
|
14568
14603
|
getAttrs: (dom) => {
|
|
14569
14604
|
const parseDimension = (dim) => {
|
|
14570
14605
|
const value = parseInt(dim ?? "", 10);
|
|
14571
14606
|
return isNaN(value) ? null : value;
|
|
14572
14607
|
};
|
|
14573
|
-
const srcAttr = dom.getAttribute("src");
|
|
14608
|
+
const srcAttr = dom.getAttribute("data-src") ?? dom.getAttribute("src");
|
|
14574
14609
|
const imageUrl = this.config.parseUrlFromHtml ? this.config.parseUrlFromHtml(srcAttr) : srcAttr;
|
|
14575
14610
|
if (imageUrl === null) {
|
|
14576
14611
|
return false;
|
|
@@ -14592,8 +14627,14 @@ class RteInlineImageFeatureImpl extends slottableRequest.RteFeatureImpl {
|
|
|
14592
14627
|
if (resolvedUrl === null) {
|
|
14593
14628
|
return document.createTextNode("");
|
|
14594
14629
|
}
|
|
14595
|
-
const attrs = {
|
|
14596
|
-
|
|
14630
|
+
const attrs = {
|
|
14631
|
+
src: sanitizeImageSrc(resolvedUrl),
|
|
14632
|
+
"data-src": resolvedUrl,
|
|
14633
|
+
// Preserve src if it is dropped by sanitization
|
|
14634
|
+
alt
|
|
14635
|
+
};
|
|
14636
|
+
if (size)
|
|
14637
|
+
attrs.style = `max-width: ${escapeCssProperty(size)}; height: auto;`;
|
|
14597
14638
|
if (naturalWidth) attrs.width = naturalWidth;
|
|
14598
14639
|
if (naturalHeight) attrs.height = naturalHeight;
|
|
14599
14640
|
return ["img", attrs];
|
|
@@ -11571,6 +11571,37 @@ class RteCoreImpl extends RteFeatureImpl {
|
|
|
11571
11571
|
}
|
|
11572
11572
|
featureFacade(RteCoreImpl);
|
|
11573
11573
|
|
|
11574
|
+
const DEFAULT_ALLOWED_URI_REGEXP = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
11575
|
+
const ALLOWED_URI_REGEXP_WITH_BLOB = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
|
|
11576
|
+
const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
|
|
11577
|
+
const domPurifyConfig = {
|
|
11578
|
+
// Allow blobs
|
|
11579
|
+
ALLOWED_URI_REGEXP: ALLOWED_URI_REGEXP_WITH_BLOB
|
|
11580
|
+
};
|
|
11581
|
+
const sanitizeLinkHref = (url) => {
|
|
11582
|
+
if (!DEFAULT_ALLOWED_URI_REGEXP.test(url.replace(ATTR_WHITESPACE, ""))) {
|
|
11583
|
+
return "";
|
|
11584
|
+
}
|
|
11585
|
+
const anchor = document.createElement("a");
|
|
11586
|
+
anchor.setAttribute("href", url);
|
|
11587
|
+
const sanitizedAnchor = DOMPurify.sanitize(anchor, {
|
|
11588
|
+
RETURN_DOM: true,
|
|
11589
|
+
...domPurifyConfig
|
|
11590
|
+
}).querySelector("a");
|
|
11591
|
+
/* v8 ignore next -- since href is already validated it's probably always present @preserve */
|
|
11592
|
+
return sanitizedAnchor.getAttribute("href") ?? "";
|
|
11593
|
+
};
|
|
11594
|
+
const sanitizeImageSrc = (url) => {
|
|
11595
|
+
const img = document.createElement("img");
|
|
11596
|
+
img.setAttribute("src", url);
|
|
11597
|
+
const sanitizedImg = DOMPurify.sanitize(img, {
|
|
11598
|
+
RETURN_DOM: true,
|
|
11599
|
+
...domPurifyConfig
|
|
11600
|
+
}).querySelector("img");
|
|
11601
|
+
return sanitizedImg.getAttribute("src") ?? "";
|
|
11602
|
+
};
|
|
11603
|
+
const escapeCssProperty = (value) => value.replace(/[;!].*/, "");
|
|
11604
|
+
|
|
11574
11605
|
const copy = (obj) => ({ ...obj });
|
|
11575
11606
|
const parseRulesFromSchema = (schema) => ({
|
|
11576
11607
|
nodes: Object.fromEntries(
|
|
@@ -11608,7 +11639,10 @@ class RteHtmlParser {
|
|
|
11608
11639
|
}).content.toJSON() ?? [];
|
|
11609
11640
|
}
|
|
11610
11641
|
parseHtml(html, options) {
|
|
11611
|
-
const dom = DOMPurify.sanitize(html, {
|
|
11642
|
+
const dom = DOMPurify.sanitize(html, {
|
|
11643
|
+
RETURN_DOM: true,
|
|
11644
|
+
...domPurifyConfig
|
|
11645
|
+
});
|
|
11612
11646
|
const container = document.createDocumentFragment();
|
|
11613
11647
|
container.appendChild(dom);
|
|
11614
11648
|
options?.modifyDom?.(container);
|
|
@@ -12767,7 +12801,7 @@ class RteFontSizePickerFeatureImpl extends RteFeatureImpl {
|
|
|
12767
12801
|
toDOM: (mark) => {
|
|
12768
12802
|
return [
|
|
12769
12803
|
"span",
|
|
12770
|
-
{ style: `font-size: ${mark.attrs.size};` },
|
|
12804
|
+
{ style: `font-size: ${escapeCssProperty(mark.attrs.size)};` },
|
|
12771
12805
|
0
|
|
12772
12806
|
];
|
|
12773
12807
|
},
|
|
@@ -13437,7 +13471,7 @@ class RteTextColorPickerFeatureImpl extends RteFeatureImpl {
|
|
|
13437
13471
|
return [
|
|
13438
13472
|
"span",
|
|
13439
13473
|
{
|
|
13440
|
-
style: `color: ${color}
|
|
13474
|
+
style: `color: ${escapeCssProperty(color)};`,
|
|
13441
13475
|
"data-text-color": color
|
|
13442
13476
|
},
|
|
13443
13477
|
0
|
|
@@ -13947,6 +13981,7 @@ const alignments = [
|
|
|
13947
13981
|
label: "right"
|
|
13948
13982
|
}
|
|
13949
13983
|
];
|
|
13984
|
+
const validTextAlign = (value) => alignments.find((a) => a.value === value)?.value ?? "left";
|
|
13950
13985
|
class RteAlignmentFeatureImpl extends RteFeatureImpl {
|
|
13951
13986
|
constructor() {
|
|
13952
13987
|
super(...arguments);
|
|
@@ -13958,10 +13993,10 @@ class RteAlignmentFeatureImpl extends RteFeatureImpl {
|
|
|
13958
13993
|
name: "textAlign",
|
|
13959
13994
|
default: "left",
|
|
13960
13995
|
fromDOM(dom) {
|
|
13961
|
-
return dom.style.textAlign
|
|
13996
|
+
return validTextAlign(dom.style.textAlign);
|
|
13962
13997
|
},
|
|
13963
13998
|
toStyles(node) {
|
|
13964
|
-
return [`text-align: ${node.attrs.textAlign}`];
|
|
13999
|
+
return [`text-align: ${validTextAlign(node.attrs.textAlign)}`];
|
|
13965
14000
|
}
|
|
13966
14001
|
})
|
|
13967
14002
|
];
|
|
@@ -14117,7 +14152,7 @@ class RteLinkFeatureImpl extends RteFeatureImpl {
|
|
|
14117
14152
|
],
|
|
14118
14153
|
toDOM(node) {
|
|
14119
14154
|
const { href } = node.attrs;
|
|
14120
|
-
return ["a", { href }, 0];
|
|
14155
|
+
return ["a", { href: sanitizeLinkHref(href) }, 0];
|
|
14121
14156
|
}
|
|
14122
14157
|
}
|
|
14123
14158
|
}
|
|
@@ -14522,7 +14557,7 @@ class InlineImageView {
|
|
|
14522
14557
|
});
|
|
14523
14558
|
}
|
|
14524
14559
|
renderImg(src) {
|
|
14525
|
-
this.img.src = src;
|
|
14560
|
+
this.img.src = sanitizeImageSrc(src);
|
|
14526
14561
|
this.setContent(this.img, { allowPopover: true });
|
|
14527
14562
|
}
|
|
14528
14563
|
update(node) {
|
|
@@ -14560,13 +14595,13 @@ class RteInlineImageFeatureImpl extends RteFeatureImpl {
|
|
|
14560
14595
|
},
|
|
14561
14596
|
parseDOM: [
|
|
14562
14597
|
{
|
|
14563
|
-
tag: "img[src]",
|
|
14598
|
+
tag: "img[src],img[data-src]",
|
|
14564
14599
|
getAttrs: (dom) => {
|
|
14565
14600
|
const parseDimension = (dim) => {
|
|
14566
14601
|
const value = parseInt(dim ?? "", 10);
|
|
14567
14602
|
return isNaN(value) ? null : value;
|
|
14568
14603
|
};
|
|
14569
|
-
const srcAttr = dom.getAttribute("src");
|
|
14604
|
+
const srcAttr = dom.getAttribute("data-src") ?? dom.getAttribute("src");
|
|
14570
14605
|
const imageUrl = this.config.parseUrlFromHtml ? this.config.parseUrlFromHtml(srcAttr) : srcAttr;
|
|
14571
14606
|
if (imageUrl === null) {
|
|
14572
14607
|
return false;
|
|
@@ -14588,8 +14623,14 @@ class RteInlineImageFeatureImpl extends RteFeatureImpl {
|
|
|
14588
14623
|
if (resolvedUrl === null) {
|
|
14589
14624
|
return document.createTextNode("");
|
|
14590
14625
|
}
|
|
14591
|
-
const attrs = {
|
|
14592
|
-
|
|
14626
|
+
const attrs = {
|
|
14627
|
+
src: sanitizeImageSrc(resolvedUrl),
|
|
14628
|
+
"data-src": resolvedUrl,
|
|
14629
|
+
// Preserve src if it is dropped by sanitization
|
|
14630
|
+
alt
|
|
14631
|
+
};
|
|
14632
|
+
if (size)
|
|
14633
|
+
attrs.style = `max-width: ${escapeCssProperty(size)}; height: auto;`;
|
|
14593
14634
|
if (naturalWidth) attrs.width = naturalWidth;
|
|
14594
14635
|
if (naturalHeight) attrs.height = naturalHeight;
|
|
14595
14636
|
return ["img", attrs];
|