Compare commits

...

10 Commits

Author SHA1 Message Date
dwelle
0dbd2a3931 v0.17.6 2024-04-17 21:57:24 +02:00
David Luzar
988f81911c fix: allow same origin for all necessary domains (#7889) 2024-04-17 21:56:52 +02:00
dwelle
db4770ed83 v0.17.5 2024-04-12 20:11:07 +02:00
dwelle
58338e54c9 fix: parse embeddable srcdoc urls strictly & escape attribute url html 2024-04-12 20:08:04 +02:00
David Luzar
640caba739 v0.17.4 2024-04-12 14:57:03 +02:00
David Luzar
2879c9d852 fix: Gist embed allowing unsafe html (#7883) 2024-04-12 13:58:38 +02:00
YuBin, Hsu
81046ccd6b fix: keep customData when converting to ExcalidrawElement (#7656)
* feat: keep customData when converting to ExcalidrawElement (#7654)

* docs: add changelog for keeping customData when converting to ExcalidrawElement
2024-02-09 19:01:52 +05:30
Aakansha Doshi
207a0bcc6e fix: umd build so it can be used in browser (#7349)
* fix: umd build so it can be used in browser

* fix lint

* increase size limit

* update changelog

* use json.stringify for env preact variable so its accessible as string

* update changelog
2024-02-09 19:00:43 +05:30
David Luzar
f53edb7437 fix: disable caching bounds for arrow labels (#7343) 2024-02-09 17:08:28 +05:30
David Luzar
8d0a8ce65b fix: bounds cached prematurely resulting in incorrectly rendered labels (#7339) 2024-02-09 17:08:12 +05:30
20 changed files with 694 additions and 84 deletions

View File

@ -39,7 +39,7 @@ Since Vite removes env variables by default, you can update the vite config to e
``` ```
define: { define: {
"process.env.IS_PREACT": process.env.IS_PREACT, "process.env.IS_PREACT": JSON.stringify("true"),
}, },
``` ```

View File

@ -93,7 +93,7 @@ Since Vite removes env variables by default, you can update the vite config to e
``` ```
define: { define: {
"process.env.IS_PREACT": process.env.IS_PREACT, "process.env.IS_PREACT": JSON.stringify("true"),
}, },
``` ```
::: :::

View File

@ -946,7 +946,11 @@ class App extends React.Component<AppProps, AppState> {
title="Excalidraw Embedded Content" title="Excalidraw Embedded Content"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen={true} allowFullScreen={true}
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads" sandbox={`${
embedLink?.sandbox?.allowSameOrigin
? "allow-same-origin"
: ""
} allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads`}
/> />
)} )}
</div> </div>

View File

@ -14,6 +14,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -49,6 +50,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -79,6 +81,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "ellipse-1", "elementId": "ellipse-1",
@ -132,6 +135,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "ellipse-1", "elementId": "ellipse-1",
@ -190,6 +194,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -227,6 +232,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
}, },
], ],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -271,6 +277,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
}, },
], ],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -313,6 +320,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "text-2", "elementId": "text-2",
@ -368,6 +376,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id48", "containerId": "id48",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -410,6 +419,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "id40", "elementId": "id40",
@ -465,6 +475,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id37", "containerId": "id37",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -507,6 +518,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -542,6 +554,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -577,6 +590,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": { "endBinding": {
"elementId": "id44", "elementId": "id44",
@ -632,6 +646,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id41", "containerId": "id41",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -676,6 +691,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
}, },
], ],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -720,6 +736,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
}, },
], ],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -757,6 +774,7 @@ exports[`Test Transform > should not allow duplicate ids 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -787,6 +805,7 @@ exports[`Test Transform > should transform linear elements 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -832,6 +851,7 @@ exports[`Test Transform > should transform linear elements 2`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "triangle", "endArrowhead": "triangle",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -877,6 +897,7 @@ exports[`Test Transform > should transform linear elements 3`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -922,6 +943,7 @@ exports[`Test Transform > should transform linear elements 4`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -967,6 +989,7 @@ exports[`Test Transform > should transform regular shapes 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -997,6 +1020,7 @@ exports[`Test Transform > should transform regular shapes 2`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1027,6 +1051,7 @@ exports[`Test Transform > should transform regular shapes 3`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1057,6 +1082,7 @@ exports[`Test Transform > should transform regular shapes 4`] = `
"angle": 0, "angle": 0,
"backgroundColor": "#c0eb75", "backgroundColor": "#c0eb75",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1087,6 +1113,7 @@ exports[`Test Transform > should transform regular shapes 5`] = `
"angle": 0, "angle": 0,
"backgroundColor": "#ffc9c9", "backgroundColor": "#ffc9c9",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1117,6 +1144,7 @@ exports[`Test Transform > should transform regular shapes 6`] = `
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1149,6 +1177,7 @@ exports[`Test Transform > should transform text element 1`] = `
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1188,6 +1217,7 @@ exports[`Test Transform > should transform text element 2`] = `
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1230,6 +1260,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -1280,6 +1311,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -1330,6 +1362,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -1380,6 +1413,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -1427,6 +1461,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id25", "containerId": "id25",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1466,6 +1501,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id26", "containerId": "id26",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1505,6 +1541,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id27", "containerId": "id27",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1545,6 +1582,7 @@ exports[`Test Transform > should transform to labelled arrows when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id28", "containerId": "id28",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1588,6 +1626,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1623,6 +1662,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1658,6 +1698,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1693,6 +1734,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1728,6 +1770,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1763,6 +1806,7 @@ exports[`Test Transform > should transform to text containers when label provide
"type": "text", "type": "text",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1795,6 +1839,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id13", "containerId": "id13",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1834,6 +1879,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id14", "containerId": "id14",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1874,6 +1920,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id15", "containerId": "id15",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1916,6 +1963,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id16", "containerId": "id16",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1956,6 +2004,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id17", "containerId": "id17",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,
@ -1997,6 +2046,7 @@ exports[`Test Transform > should transform to text containers when label provide
"baseline": 0, "baseline": 0,
"boundElements": null, "boundElements": null,
"containerId": "id18", "containerId": "id18",
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 20, "fontSize": 20,

View File

@ -822,4 +822,22 @@ describe("Test Transform", () => {
"Duplicate id found for rect-1", "Duplicate id found for rect-1",
); );
}); });
it("should contains customData if provided", () => {
const rawData = [
{
type: "rectangle",
x: 100,
y: 100,
customData: { createdBy: "user01" },
},
];
const convertedElements = convertToExcalidrawElements(
rawData as ExcalidrawElementSkeleton[],
opts,
);
expect(convertedElements[0].customData).toStrictEqual({
createdBy: "user01",
});
});
}); });

View File

@ -1,11 +1,15 @@
import { sanitizeUrl } from "@braintree/sanitize-url"; import { sanitizeUrl } from "@braintree/sanitize-url";
export const sanitizeHTMLAttribute = (html: string) => {
return html.replace(/"/g, "&quot;");
};
export const normalizeLink = (link: string) => { export const normalizeLink = (link: string) => {
link = link.trim(); link = link.trim();
if (!link) { if (!link) {
return link; return link;
} }
return sanitizeUrl(link); return sanitizeUrl(sanitizeHTMLAttribute(link));
}; };
export const isLocalLink = (link: string | null) => { export const isLocalLink = (link: string | null) => {

View File

@ -13,6 +13,7 @@ import { Point } from "../types";
import { generateRoughOptions } from "../scene/Shape"; import { generateRoughOptions } from "../scene/Shape";
import { import {
isArrowElement, isArrowElement,
isBoundToContainer,
isFreeDrawElement, isFreeDrawElement,
isLinearElement, isLinearElement,
isTextElement, isTextElement,
@ -22,6 +23,7 @@ import { getBoundTextElement, getContainerElement } from "./textElement";
import { LinearElementEditor } from "./linearElementEditor"; import { LinearElementEditor } from "./linearElementEditor";
import { Mutable } from "../utility-types"; import { Mutable } from "../utility-types";
import { ShapeCache } from "../scene/ShapeCache"; import { ShapeCache } from "../scene/ShapeCache";
import Scene from "../scene/Scene";
export type RectangleBox = { export type RectangleBox = {
x: number; x: number;
@ -53,16 +55,29 @@ export class ElementBounds {
static getBounds(element: ExcalidrawElement) { static getBounds(element: ExcalidrawElement) {
const cachedBounds = ElementBounds.boundsCache.get(element); const cachedBounds = ElementBounds.boundsCache.get(element);
if (cachedBounds?.version && cachedBounds.version === element.version) { if (
cachedBounds?.version &&
cachedBounds.version === element.version &&
// we don't invalidate cache when we update containers and not labels,
// which is causing problems down the line. Fix TBA.
!isBoundToContainer(element)
) {
return cachedBounds.bounds; return cachedBounds.bounds;
} }
const bounds = ElementBounds.calculateBounds(element); const bounds = ElementBounds.calculateBounds(element);
ElementBounds.boundsCache.set(element, { // hack to ensure that downstream checks could retrieve element Scene
version: element.version, // so as to have correctly calculated bounds
bounds, // FIXME remove when we get rid of all the id:Scene / element:Scene mapping
}); const shouldCache = Scene.getScene(element);
if (shouldCache) {
ElementBounds.boundsCache.set(element, {
version: element.version,
bounds,
});
}
return bounds; return bounds;
} }

View File

@ -13,11 +13,13 @@ import {
NonDeletedExcalidrawElement, NonDeletedExcalidrawElement,
Theme, Theme,
} from "./types"; } from "./types";
import { sanitizeHTMLAttribute } from "../data/url";
type EmbeddedLink = type EmbeddedLink =
| ({ | ({
aspectRatio: { w: number; h: number }; aspectRatio: { w: number; h: number };
warning?: string; warning?: string;
sandbox: { allowSameOrigin?: boolean };
} & ( } & (
| { type: "video" | "generic"; link: string } | { type: "video" | "generic"; link: string }
| { type: "document"; srcdoc: (theme: Theme) => string } | { type: "document"; srcdoc: (theme: Theme) => string }
@ -30,20 +32,21 @@ const RE_YOUTUBE =
/^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/; /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
const RE_VIMEO = const RE_VIMEO =
/^(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/; /^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/; const RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
const RE_GH_GIST = /^https:\/\/gist\.github\.com/; const RE_GH_GIST = /^https:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)/;
const RE_GH_GIST_EMBED = const RE_GH_GIST_EMBED =
/^<script[\s\S]*?\ssrc=["'](https:\/\/gist.github.com\/.*?)\.js["']/i; /^<script[\s\S]*?\ssrc=["'](https:\/\/gist\.github\.com\/.*?)\.js["']/i;
// not anchored to start to allow <blockquote> twitter embeds // not anchored to start to allow <blockquote> twitter embeds
const RE_TWITTER = /(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?twitter.com/; const RE_TWITTER =
/(?:https?:\/\/)?(?:(?:w){3}\.)?(?:twitter|x)\.com\/[^/]+\/status\/(\d+)/;
const RE_TWITTER_EMBED = const RE_TWITTER_EMBED =
/^<blockquote[\s\S]*?\shref=["'](https:\/\/twitter.com\/[^"']*)/i; /^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:twitter|x)\.com\/[^"']*)/i;
const RE_VALTOWN = const RE_VALTOWN =
/^https:\/\/(?:www\.)?val.town\/(v|embed)\/[a-zA-Z_$][0-9a-zA-Z_$]+\.[a-zA-Z_$][0-9a-zA-Z_$]+/; /^https:\/\/(?:www\.)?val\.town\/(v|embed)\/[a-zA-Z_$][0-9a-zA-Z_$]+\.[a-zA-Z_$][0-9a-zA-Z_$]+/;
const RE_GENERIC_EMBED = const RE_GENERIC_EMBED =
/^<(?:iframe|blockquote)[\s\S]*?\s(?:src|href)=["']([^"']*)["'][\s\S]*?>$/i; /^<(?:iframe|blockquote)[\s\S]*?\s(?:src|href)=["']([^"']*)["'][\s\S]*?>$/i;
@ -64,7 +67,18 @@ const ALLOWED_DOMAINS = new Set([
"stackblitz.com", "stackblitz.com",
"val.town", "val.town",
"giphy.com", "giphy.com",
"dddice.com", ]);
const ALLOW_SAME_ORIGIN = new Set([
"youtube.com",
"youtu.be",
"vimeo.com",
"player.vimeo.com",
"figma.com",
"twitter.com",
"x.com",
"*.simplepdf.eu",
"stackblitz.com",
]); ]);
const createSrcDoc = (body: string) => { const createSrcDoc = (body: string) => {
@ -82,6 +96,10 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
const originalLink = link; const originalLink = link;
const allowSameOrigin = ALLOW_SAME_ORIGIN.has(
matchHostname(link, ALLOW_SAME_ORIGIN) || "",
);
let type: "video" | "generic" = "generic"; let type: "video" | "generic" = "generic";
let aspectRatio = { w: 560, h: 840 }; let aspectRatio = { w: 560, h: 840 };
const ytLink = link.match(RE_YOUTUBE); const ytLink = link.match(RE_YOUTUBE);
@ -104,8 +122,18 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
break; break;
} }
aspectRatio = isPortrait ? { w: 315, h: 560 } : { w: 560, h: 315 }; aspectRatio = isPortrait ? { w: 315, h: 560 } : { w: 560, h: 315 };
embeddedLinkCache.set(originalLink, { link, aspectRatio, type }); embeddedLinkCache.set(originalLink, {
return { link, aspectRatio, type }; link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
});
return {
link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
};
} }
const vimeoLink = link.match(RE_VIMEO); const vimeoLink = link.match(RE_VIMEO);
@ -119,8 +147,13 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
aspectRatio = { w: 560, h: 315 }; aspectRatio = { w: 560, h: 315 };
//warning deliberately ommited so it is displayed only once per link //warning deliberately ommited so it is displayed only once per link
//same link next time will be served from cache //same link next time will be served from cache
embeddedLinkCache.set(originalLink, { link, aspectRatio, type }); embeddedLinkCache.set(originalLink, {
return { link, aspectRatio, type, warning }; link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
});
return { link, aspectRatio, type, warning, sandbox: { allowSameOrigin } };
} }
const figmaLink = link.match(RE_FIGMA); const figmaLink = link.match(RE_FIGMA);
@ -130,75 +163,81 @@ export const getEmbedLink = (link: string | null | undefined): EmbeddedLink => {
link, link,
)}`; )}`;
aspectRatio = { w: 550, h: 550 }; aspectRatio = { w: 550, h: 550 };
embeddedLinkCache.set(originalLink, { link, aspectRatio, type }); embeddedLinkCache.set(originalLink, {
return { link, aspectRatio, type }; link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
});
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
} }
const valLink = link.match(RE_VALTOWN); const valLink = link.match(RE_VALTOWN);
if (valLink) { if (valLink) {
link = link =
valLink[1] === "embed" ? valLink[0] : valLink[0].replace("/v", "/embed"); valLink[1] === "embed" ? valLink[0] : valLink[0].replace("/v", "/embed");
embeddedLinkCache.set(originalLink, { link, aspectRatio, type }); embeddedLinkCache.set(originalLink, {
return { link, aspectRatio, type }; link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
});
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
} }
if (RE_TWITTER.test(link)) { if (RE_TWITTER.test(link)) {
let ret: EmbeddedLink; const postId = link.match(RE_TWITTER)![1];
// assume embed code // the embed srcdoc still supports twitter.com domain only.
if (/<blockquote/.test(link)) { // Note that we don't attempt to parse the username as it can consist of
const srcDoc = createSrcDoc(link); // non-latin1 characters, and the username in the url can be set to anything
ret = { // without affecting the embed.
type: "document", const safeURL = sanitizeHTMLAttribute(
srcdoc: () => srcDoc, `https://twitter.com/x/status/${postId}`,
aspectRatio: { w: 480, h: 480 }, );
};
// assume regular tweet url const ret: EmbeddedLink = {
} else { type: "document",
ret = { srcdoc: (theme: string) =>
type: "document", createSrcDoc(
srcdoc: (theme: string) => `<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${safeURL}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`,
createSrcDoc( ),
`<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>`, aspectRatio: { w: 480, h: 480 },
), sandbox: { allowSameOrigin },
aspectRatio: { w: 480, h: 480 }, };
};
}
embeddedLinkCache.set(originalLink, ret); embeddedLinkCache.set(originalLink, ret);
return ret; return ret;
} }
if (RE_GH_GIST.test(link)) { if (RE_GH_GIST.test(link)) {
let ret: EmbeddedLink; const [, user, gistId] = link.match(RE_GH_GIST)!;
// assume embed code const safeURL = sanitizeHTMLAttribute(
if (/<script>/.test(link)) { `https://gist.github.com/${user}/${gistId}`,
const srcDoc = createSrcDoc(link); );
ret = { const ret: EmbeddedLink = {
type: "document", type: "document",
srcdoc: () => srcDoc, srcdoc: () =>
aspectRatio: { w: 550, h: 720 }, createSrcDoc(`
}; <script src="${safeURL}.js"></script>
// assume regular url
} else {
ret = {
type: "document",
srcdoc: () =>
createSrcDoc(`
<script src="${link}.js"></script>
<style type="text/css"> <style type="text/css">
* { margin: 0px; } * { margin: 0px; }
table, .gist { height: 100%; } table, .gist { height: 100%; }
.gist .gist-file { height: calc(100vh - 2px); padding: 0px; display: grid; grid-template-rows: 1fr auto; } .gist .gist-file { height: calc(100vh - 2px); padding: 0px; display: grid; grid-template-rows: 1fr auto; }
</style> </style>
`), `),
aspectRatio: { w: 550, h: 720 }, aspectRatio: { w: 550, h: 720 },
}; sandbox: { allowSameOrigin },
} };
embeddedLinkCache.set(link, ret); embeddedLinkCache.set(link, ret);
return ret; return ret;
} }
embeddedLinkCache.set(link, { link, aspectRatio, type }); embeddedLinkCache.set(link, {
return { link, aspectRatio, type }; link,
aspectRatio,
type,
sandbox: { allowSameOrigin },
});
return { link, aspectRatio, type, sandbox: { allowSameOrigin } };
}; };
export const isEmbeddableOrLabel = ( export const isEmbeddableOrLabel = (
@ -273,34 +312,39 @@ export const actionSetEmbeddableAsActiveTool = register({
}, },
}); });
const validateHostname = ( const matchHostname = (
url: string, url: string,
/** using a Set assumes it already contains normalized bare domains */ /** using a Set assumes it already contains normalized bare domains */
allowedHostnames: Set<string> | string, allowedHostnames: Set<string> | string,
): boolean => { ): string | null => {
try { try {
const { hostname } = new URL(url); const { hostname } = new URL(url);
const bareDomain = hostname.replace(/^www\./, ""); const bareDomain = hostname.replace(/^www\./, "");
const bareDomainWithFirstSubdomainWildcarded = bareDomain.replace(
/^([^.]+)/,
"*",
);
if (allowedHostnames instanceof Set) { if (allowedHostnames instanceof Set) {
return ( if (ALLOWED_DOMAINS.has(bareDomain)) {
ALLOWED_DOMAINS.has(bareDomain) || return bareDomain;
ALLOWED_DOMAINS.has(bareDomainWithFirstSubdomainWildcarded) }
const bareDomainWithFirstSubdomainWildcarded = bareDomain.replace(
/^([^.]+)/,
"*",
); );
if (ALLOWED_DOMAINS.has(bareDomainWithFirstSubdomainWildcarded)) {
return bareDomainWithFirstSubdomainWildcarded;
}
return null;
} }
if (bareDomain === allowedHostnames.replace(/^www\./, "")) { const bareAllowedHostname = allowedHostnames.replace(/^www\./, "");
return true; if (bareDomain === bareAllowedHostname) {
return bareAllowedHostname;
} }
} catch (error) { } catch (error) {
// ignore // ignore
} }
return false; return null;
}; };
export const extractSrc = (htmlString: string): string => { export const extractSrc = (htmlString: string): string => {
@ -349,7 +393,7 @@ export const embeddableURLValidator = (
if (url.match(domain)) { if (url.match(domain)) {
return true; return true;
} }
} else if (validateHostname(url, domain)) { } else if (matchHostname(url, domain)) {
return true; return true;
} }
} }
@ -357,5 +401,5 @@ export const embeddableURLValidator = (
} }
} }
return validateHostname(url, ALLOWED_DOMAINS); return !!matchHostname(url, ALLOWED_DOMAINS);
}; };

View File

@ -67,6 +67,7 @@ export type ElementConstructorOpts = MarkOptional<
| "roundness" | "roundness"
| "locked" | "locked"
| "opacity" | "opacity"
| "customData"
>; >;
const _newElementBase = <T extends ExcalidrawElement>( const _newElementBase = <T extends ExcalidrawElement>(
@ -120,6 +121,7 @@ const _newElementBase = <T extends ExcalidrawElement>(
updated: getUpdatedTimestamp(), updated: getUpdatedTimestamp(),
link, link,
locked, locked,
customData: rest.customData,
}; };
return element; return element;
}; };

View File

@ -1,7 +1,7 @@
[ [
{ {
"path": "dist/excalidraw.production.min.js", "path": "dist/excalidraw.production.min.js",
"limit": "325 kB" "limit": "335 kB"
}, },
{ {
"path": "dist/excalidraw-assets/locales", "path": "dist/excalidraw-assets/locales",

View File

@ -11,6 +11,72 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section. Please add the latest change on the top under the correct section.
--> -->
## Unreleased
### Features
- Add `onPointerUp` prop [#7638](https://github.com/excalidraw/excalidraw/pull/7638).
- Expose `getVisibleSceneBounds` helper to get scene bounds of visible canvas area. [#7450](https://github.com/excalidraw/excalidraw/pull/7450)
### Fixes
- Keep customData when converting to ExcalidrawElement. [#7656](https://github.com/excalidraw/excalidraw/pull/7656)
### Breaking Changes
- `ExcalidrawEmbeddableElement.validated` was removed and moved to private editor state. This should largely not affect your apps unless you were reading from this attribute. We keep validating embeddable urls internally, and the public [`props.validateEmbeddable`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props#validateembeddable) still applies. [#7539](https://github.com/excalidraw/excalidraw/pull/7539)
- Create an `ESM` build for `@excalidraw/excalidraw`. The API is in progress and subject to change before stable release. There are some changes on how the package will be consumed
#### Bundler
- CSS needs to be imported so you will need to import the css along with the excalidraw component
```js
import { Excalidraw } from "@excalidraw/excalidraw";
import "@excalidraw/excalidraw/index.css";
```
- The `types` path is updated
Instead of importing from `@excalidraw/excalidraw/types/`, you will need to import from `@excalidraw/excalidraw/dist/excalidraw` or `@excalidraw/excalidraw/dist/utils` depending on the types you are using.
However this we will be fixing before stable release, so in case you want to try it out you will need to update the types for now.
#### Browser
- Since its `ESM` so now script type `module` can be used to load it and css needs to be loaded as well.
```html
<link
rel="stylesheet"
href="https://unpkg.com/@excalidraw/excalidraw@next/dist/browser/dev/index.css"
/>
<script type="module">
import * as ExcalidrawLib from "https://unpkg.com/@excalidraw/excalidraw@next/dist/browser/dev/index.js";
window.ExcalidrawLib = ExcalidrawLib;
</script>
```
- `appState.openDialog` type was changed from `null | string` to `null | { name: string }`. [#7336](https://github.com/excalidraw/excalidraw/pull/7336)
## 0.17.1 (2023-11-28)
### Fixes
- Umd build for browser since it was breaking in v0.17.0 [#7349](https://github.com/excalidraw/excalidraw/pull/7349). Also make sure that when using `Vite`, the `process.env.IS_PREACT` is set as `"true"` (string) and not a boolean.
```
define: {
"process.env.IS_PREACT": JSON.stringify("true"),
}
```
### Breaking Changes
- `appState.openDialog` type was changed from `null | string` to `null | { name: string }`. [#7336](https://github.com/excalidraw/excalidraw/pull/7336)
## 0.17.0 (2023-11-14) ## 0.17.0 (2023-11-14)
### Features ### Features

View File

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.17.0", "version": "0.17.6",
"main": "main.js", "main": "main.js",
"types": "types/packages/excalidraw/index.d.ts", "types": "types/packages/excalidraw/index.d.ts",
"files": [ "files": [

View File

@ -1,5 +1,3 @@
const { merge } = require("webpack-merge");
const prodConfig = require("./webpack.prod.config"); const prodConfig = require("./webpack.prod.config");
const devConfig = require("./webpack.dev.config"); const devConfig = require("./webpack.dev.config");
@ -11,6 +9,7 @@ const outputFile = isProd
: "excalidraw-with-preact.development"; : "excalidraw-with-preact.development";
const preactWebpackConfig = { const preactWebpackConfig = {
...config,
entry: { entry: {
[outputFile]: "./entry.js", [outputFile]: "./entry.js",
}, },
@ -30,4 +29,4 @@ const preactWebpackConfig = {
}, },
}, },
}; };
module.exports = merge(config, preactWebpackConfig); module.exports = preactWebpackConfig;

View File

@ -385,6 +385,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
"angle": 0, "angle": 0,
"backgroundColor": "red", "backgroundColor": "red",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -419,6 +420,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
"angle": 0, "angle": 0,
"backgroundColor": "red", "backgroundColor": "red",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -580,6 +582,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -639,6 +642,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -780,6 +784,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -812,6 +817,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -871,6 +877,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -914,6 +921,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -943,6 +951,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -986,6 +995,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1015,6 +1025,7 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1156,6 +1167,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1188,6 +1200,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1247,6 +1260,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1290,6 +1304,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1319,6 +1334,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1362,6 +1378,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1391,6 +1408,7 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1534,6 +1552,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1593,6 +1612,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1732,6 +1752,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1791,6 +1812,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1832,6 +1854,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -1973,6 +1996,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2005,6 +2029,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2064,6 +2089,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2107,6 +2133,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2136,6 +2163,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2282,6 +2310,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -2316,6 +2345,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -2377,6 +2407,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2420,6 +2451,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2449,6 +2481,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2495,6 +2528,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -2526,6 +2560,7 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -2671,6 +2706,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2703,6 +2739,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2762,6 +2799,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2805,6 +2843,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2834,6 +2873,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2877,6 +2917,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2906,6 +2947,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2949,6 +2991,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -2978,6 +3021,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3021,6 +3065,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3050,6 +3095,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3093,6 +3139,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3122,6 +3169,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3165,6 +3213,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3194,6 +3243,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3237,6 +3287,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3266,6 +3317,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3309,6 +3361,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3338,6 +3391,7 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
"angle": 0, "angle": 0,
"backgroundColor": "#a5d8ff", "backgroundColor": "#a5d8ff",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3479,6 +3533,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3511,6 +3566,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3570,6 +3626,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3613,6 +3670,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3642,6 +3700,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3685,6 +3744,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3714,6 +3774,7 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3855,6 +3916,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3887,6 +3949,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3946,6 +4009,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -3989,6 +4053,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4018,6 +4083,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4061,6 +4127,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4090,6 +4157,7 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4234,6 +4302,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4266,6 +4335,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4325,6 +4395,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4368,6 +4439,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4397,6 +4469,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4443,6 +4516,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -4474,6 +4548,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -4520,6 +4595,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4549,6 +4625,7 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4966,6 +5043,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -4998,6 +5076,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5057,6 +5136,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5100,6 +5180,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5129,6 +5210,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5548,6 +5630,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -5582,6 +5665,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -5643,6 +5727,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5686,6 +5771,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5715,6 +5801,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -5761,6 +5848,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -5792,6 +5880,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -6838,6 +6927,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -6870,6 +6960,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el
"angle": 0, "angle": 0,
"backgroundColor": "red", "backgroundColor": "red",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -6902,6 +6993,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el
"angle": 0, "angle": 0,
"backgroundColor": "red", "backgroundColor": "red",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -6961,6 +7053,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] hi
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],

View File

@ -7,6 +7,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -56,6 +57,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -90,6 +92,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -122,6 +125,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -171,6 +175,7 @@ exports[`Test dragCreate > add element to the scene when pointer dragging long e
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],

View File

@ -5,6 +5,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -37,6 +38,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 2`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -69,6 +71,7 @@ exports[`move element > rectangle 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -106,6 +109,7 @@ exports[`move element > rectangles with binding arrow 1`] = `
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -143,6 +147,7 @@ exports[`move element > rectangles with binding arrow 2`] = `
"type": "arrow", "type": "arrow",
}, },
], ],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -175,6 +180,7 @@ exports[`move element > rectangles with binding arrow 3`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": { "endBinding": {
"elementId": "id1", "elementId": "id1",

View File

@ -5,6 +5,7 @@ exports[`multi point mode in linear elements > arrow 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -59,6 +60,7 @@ exports[`multi point mode in linear elements > line 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ exports[`select single element on the scene > arrow 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": "arrow", "endArrowhead": "arrow",
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -52,6 +53,7 @@ exports[`select single element on the scene > arrow escape 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -99,6 +101,7 @@ exports[`select single element on the scene > diamond 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -131,6 +134,7 @@ exports[`select single element on the scene > ellipse 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -163,6 +167,7 @@ exports[`select single element on the scene > rectangle 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": null, "boundElements": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],

View File

@ -5,6 +5,7 @@ exports[`restoreElements > should restore arrow element correctly 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [], "boundElements": [],
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -52,6 +53,7 @@ exports[`restoreElements > should restore correctly with rectangle, ellipse and
"angle": 0, "angle": 0,
"backgroundColor": "blue", "backgroundColor": "blue",
"boundElements": [], "boundElements": [],
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -88,6 +90,7 @@ exports[`restoreElements > should restore correctly with rectangle, ellipse and
"angle": 0, "angle": 0,
"backgroundColor": "blue", "backgroundColor": "blue",
"boundElements": [], "boundElements": [],
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -124,6 +127,7 @@ exports[`restoreElements > should restore correctly with rectangle, ellipse and
"angle": 0, "angle": 0,
"backgroundColor": "blue", "backgroundColor": "blue",
"boundElements": [], "boundElements": [],
"customData": undefined,
"fillStyle": "cross-hatch", "fillStyle": "cross-hatch",
"frameId": null, "frameId": null,
"groupIds": [ "groupIds": [
@ -160,6 +164,7 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = `
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [], "boundElements": [],
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"frameId": null, "frameId": null,
"groupIds": [], "groupIds": [],
@ -196,6 +201,7 @@ exports[`restoreElements > should restore line and draw elements correctly 1`] =
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [], "boundElements": [],
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -243,6 +249,7 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] =
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
"boundElements": [], "boundElements": [],
"customData": undefined,
"endArrowhead": null, "endArrowhead": null,
"endBinding": null, "endBinding": null,
"fillStyle": "solid", "fillStyle": "solid",
@ -292,6 +299,7 @@ exports[`restoreElements > should restore text element correctly passing value f
"baseline": 0, "baseline": 0,
"boundElements": [], "boundElements": [],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 14, "fontSize": 14,
@ -333,6 +341,7 @@ exports[`restoreElements > should restore text element correctly with unknown fo
"baseline": 0, "baseline": 0,
"boundElements": [], "boundElements": [],
"containerId": null, "containerId": null,
"customData": undefined,
"fillStyle": "solid", "fillStyle": "solid",
"fontFamily": 1, "fontFamily": 1,
"fontSize": 10, "fontSize": 10,