mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-10 07:45:20 +01:00
Compare commits
2 Commits
net-stats
...
devolved-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b20fe944e7 | ||
|
|
1156ef6b96 |
@@ -11,7 +11,6 @@
|
||||
<img src="https://badges.crowdin.net/excalidraw/localized.svg">
|
||||
</a>
|
||||
</p>
|
||||
<p>Ask questions or hang out on our <a target="_blank" href="https://discord.gg/UexuTaE">discord.gg/UexuTaE</a>.</p>
|
||||
</div>
|
||||
|
||||
## Try it now
|
||||
@@ -20,14 +19,6 @@ Go to [excalidraw.com](https://excalidraw.com) to start sketching.
|
||||
|
||||
Read the latest news and updates on our [blog](https://blog.excalidraw.com). A good start is to see all the updates of [One Year of Excalidraw](https://blog.excalidraw.com/one-year-of-excalidraw/).
|
||||
|
||||
## We accept donations
|
||||
|
||||
If you like the project, you can become a sponsor at [Open Collective](https://opencollective.com/excalidraw).
|
||||
|
||||
<a href="https://opencollective.com/excalidraw#category-CONTRIBUTE" target="_blank"><img src="https://opencollective.com/excalidraw/tiers/sponsors.svg?avatarHeight=64"/></a>
|
||||
|
||||
<a href="https://opencollective.com/excalidraw#category-CONTRIBUTE" target="_blank"><img src="https://opencollective.com/excalidraw/tiers/backers.svg?avatarHeight=32"/></a>
|
||||
|
||||
## Documentation
|
||||
|
||||
### Shortcuts
|
||||
|
||||
@@ -17,6 +17,6 @@ export const actionToggleGridMode = register({
|
||||
};
|
||||
},
|
||||
checked: (appState: AppState) => appState.gridSize !== null,
|
||||
contextItemLabel: "labels.showGrid",
|
||||
contextItemLabel: "labels.gridMode",
|
||||
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.code === CODES.QUOTE,
|
||||
});
|
||||
|
||||
@@ -73,8 +73,6 @@ export const getDefaultAppState = (): Omit<
|
||||
zenModeEnabled: false,
|
||||
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
||||
viewModeEnabled: false,
|
||||
networkSpeed: 0,
|
||||
networkPing: 0,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -155,8 +153,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
zenModeEnabled: { browser: true, export: false },
|
||||
zoom: { browser: true, export: false },
|
||||
viewModeEnabled: { browser: false, export: false },
|
||||
networkSpeed: { browser: false, export: false },
|
||||
networkPing: { browser: false, export: false },
|
||||
});
|
||||
|
||||
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
||||
|
||||
@@ -48,10 +48,8 @@ import {
|
||||
ELEMENT_TRANSLATE_AMOUNT,
|
||||
ENV,
|
||||
EVENT,
|
||||
GRID_SIZE,
|
||||
LINE_CONFIRM_THRESHOLD,
|
||||
MIME_TYPES,
|
||||
NETWORK_TIMEOUT_MS,
|
||||
POINTER_BUTTON,
|
||||
TAP_TWICE_TIMEOUT,
|
||||
TEXT_TO_CENTER_SNAP_THRESHOLD,
|
||||
@@ -184,7 +182,6 @@ import LayerUI from "./LayerUI";
|
||||
import { Stats } from "./Stats";
|
||||
import { Toast } from "./Toast";
|
||||
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||
import { getNetworkSpeed, getNetworkPing } from "../networkStats";
|
||||
|
||||
const { history } = createHistory();
|
||||
|
||||
@@ -207,9 +204,6 @@ const gesture: Gesture = {
|
||||
initialScale: null,
|
||||
};
|
||||
|
||||
const shouldEnableNetworkStats = !!(
|
||||
typeof process !== "undefined" && process.env?.REACT_APP_SOCKET_SERVER_URL
|
||||
);
|
||||
export type PointerDownState = Readonly<{
|
||||
// The first position at which pointerDown happened
|
||||
origin: Readonly<{ x: number; y: number }>;
|
||||
@@ -294,8 +288,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
height: window.innerHeight,
|
||||
};
|
||||
private scene: Scene;
|
||||
private networkSpeedIntervalId?: any;
|
||||
private networkPingIntervalId?: any;
|
||||
constructor(props: ExcalidrawProps) {
|
||||
super(props);
|
||||
const defaultAppState = getDefaultAppState();
|
||||
@@ -307,8 +299,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
offsetTop,
|
||||
excalidrawRef,
|
||||
viewModeEnabled = false,
|
||||
zenModeEnabled = false,
|
||||
gridModeEnabled = false,
|
||||
} = props;
|
||||
this.state = {
|
||||
...defaultAppState,
|
||||
@@ -317,8 +307,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
height,
|
||||
...this.getCanvasOffsets({ offsetLeft, offsetTop }),
|
||||
viewModeEnabled,
|
||||
zenModeEnabled,
|
||||
gridSize: gridModeEnabled ? GRID_SIZE : null,
|
||||
};
|
||||
if (excalidrawRef) {
|
||||
const readyPromise =
|
||||
@@ -465,9 +453,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
onExportToBackend={onExportToBackend}
|
||||
renderCustomFooter={renderFooter}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
showExitZenModeBtn={
|
||||
typeof this.props?.zenModeEnabled === "undefined" && zenModeEnabled
|
||||
}
|
||||
/>
|
||||
<div className="excalidraw-textEditorContainer" />
|
||||
{this.state.showStats && (
|
||||
@@ -476,8 +461,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
setAppState={this.setAppState}
|
||||
elements={this.scene.getElements()}
|
||||
onClose={this.toggleStats}
|
||||
isCollaborating={this.props.isCollaborating}
|
||||
shouldEnableNetworkStats={shouldEnableNetworkStats}
|
||||
/>
|
||||
)}
|
||||
{this.state.toastMessage !== null && (
|
||||
@@ -528,21 +511,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
|
||||
let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
|
||||
let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
|
||||
let gridSize = actionResult?.appState?.gridSize || null;
|
||||
|
||||
if (typeof this.props.viewModeEnabled !== "undefined") {
|
||||
viewModeEnabled = this.props.viewModeEnabled;
|
||||
}
|
||||
|
||||
if (typeof this.props.zenModeEnabled !== "undefined") {
|
||||
zenModeEnabled = this.props.zenModeEnabled;
|
||||
}
|
||||
|
||||
if (typeof this.props.gridModeEnabled !== "undefined") {
|
||||
gridSize = this.props.gridModeEnabled ? GRID_SIZE : null;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
(state) => ({
|
||||
...actionResult.appState,
|
||||
@@ -553,8 +526,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
offsetTop: state.offsetTop,
|
||||
offsetLeft: state.offsetLeft,
|
||||
viewModeEnabled,
|
||||
zenModeEnabled,
|
||||
gridSize,
|
||||
}),
|
||||
() => {
|
||||
if (actionResult.syncHistory) {
|
||||
@@ -757,7 +728,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.removeEventListeners();
|
||||
this.scene.destroy();
|
||||
clearTimeout(touchTimeout);
|
||||
clearTimeout(this.networkSpeedIntervalId);
|
||||
touchTimeout = 0;
|
||||
}
|
||||
|
||||
@@ -875,43 +845,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
if (prevProps.zenModeEnabled !== this.props.zenModeEnabled) {
|
||||
this.setState({ zenModeEnabled: !!this.props.zenModeEnabled });
|
||||
}
|
||||
|
||||
if (prevProps.gridModeEnabled !== this.props.gridModeEnabled) {
|
||||
this.setState({
|
||||
gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
|
||||
});
|
||||
}
|
||||
if (
|
||||
shouldEnableNetworkStats &&
|
||||
(prevState.showStats !== this.state.showStats ||
|
||||
prevProps.isCollaborating !== this.props.isCollaborating)
|
||||
) {
|
||||
const navigator: Navigator & {
|
||||
connection?: {
|
||||
addEventListener: Function;
|
||||
removeEventListener: Function;
|
||||
};
|
||||
} = window.navigator;
|
||||
|
||||
if (this.state.showStats && this.props.isCollaborating) {
|
||||
this.calculateNetStats();
|
||||
|
||||
navigator?.connection?.addEventListener(
|
||||
"change",
|
||||
this.calculateNetStats,
|
||||
);
|
||||
} else {
|
||||
navigator?.connection?.removeEventListener(
|
||||
"change",
|
||||
this.calculateNetStats,
|
||||
);
|
||||
clearTimeout(this.networkSpeedIntervalId);
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector(".excalidraw")
|
||||
?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
|
||||
@@ -1037,42 +970,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
}
|
||||
|
||||
private calculateNetStats = () => {
|
||||
this.checkNetworkPing();
|
||||
this.checkNetworkSpeed();
|
||||
};
|
||||
|
||||
private checkNetworkPing = async () => {
|
||||
if (!this.state.showStats || !this.props.isCollaborating) {
|
||||
clearTimeout(this.networkPingIntervalId);
|
||||
return;
|
||||
}
|
||||
const networkPing = await getNetworkPing();
|
||||
this.setState({ networkPing });
|
||||
if (this.networkPingIntervalId) {
|
||||
clearTimeout(this.networkPingIntervalId);
|
||||
}
|
||||
this.networkPingIntervalId = setTimeout(
|
||||
this.checkNetworkPing,
|
||||
NETWORK_TIMEOUT_MS,
|
||||
);
|
||||
};
|
||||
|
||||
private checkNetworkSpeed = async () => {
|
||||
if (!this.state.showStats || !this.props.isCollaborating) {
|
||||
clearTimeout(this.networkSpeedIntervalId);
|
||||
return;
|
||||
}
|
||||
const networkSpeed = await getNetworkSpeed();
|
||||
this.setState({ networkSpeed });
|
||||
if (this.networkSpeedIntervalId) {
|
||||
clearTimeout(this.networkSpeedIntervalId);
|
||||
}
|
||||
this.networkSpeedIntervalId = setTimeout(
|
||||
this.checkNetworkSpeed,
|
||||
NETWORK_TIMEOUT_MS,
|
||||
);
|
||||
};
|
||||
// Copy/paste
|
||||
|
||||
private onCut = withBatchedUpdates((event: ClipboardEvent) => {
|
||||
@@ -3556,11 +3453,40 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
}
|
||||
};
|
||||
|
||||
private handleCanvasImageDrop = async (
|
||||
event: React.DragEvent<HTMLCanvasElement>,
|
||||
file: File,
|
||||
) => {
|
||||
try {
|
||||
const shapes = await (
|
||||
await import(
|
||||
/* webpackChunkName: "pixelated-image" */ "../data/pixelated-image"
|
||||
)
|
||||
).pixelateImage(file, 20, 1200, event.clientX, event.clientY);
|
||||
|
||||
const nextElements = [
|
||||
...this.scene.getElementsIncludingDeleted(),
|
||||
...shapes,
|
||||
];
|
||||
|
||||
this.scene.replaceAllElements(nextElements);
|
||||
} catch (error) {
|
||||
return this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private handleCanvasOnDrop = async (
|
||||
event: React.DragEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
let imageFile: File | null = null;
|
||||
try {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (file?.type.indexOf("image/") === 0) {
|
||||
imageFile = file;
|
||||
}
|
||||
if (file?.type === "image/png" || file?.type === "image/svg+xml") {
|
||||
const { elements, appState } = await loadFromBlob(file, this.state);
|
||||
this.syncActionResult({
|
||||
@@ -3572,8 +3498,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
commitToHistory: true,
|
||||
});
|
||||
return;
|
||||
} else if (imageFile) {
|
||||
return await this.handleCanvasImageDrop(event, imageFile);
|
||||
}
|
||||
} catch (error) {
|
||||
if (imageFile) {
|
||||
return await this.handleCanvasImageDrop(event, imageFile);
|
||||
}
|
||||
return this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: error.message,
|
||||
@@ -3820,10 +3751,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
||||
separator,
|
||||
actionSelectAll,
|
||||
separator,
|
||||
typeof this.props.gridModeEnabled === "undefined" &&
|
||||
actionToggleGridMode,
|
||||
typeof this.props.zenModeEnabled === "undefined" &&
|
||||
actionToggleZenMode,
|
||||
actionToggleGridMode,
|
||||
actionToggleZenMode,
|
||||
typeof this.props.viewModeEnabled === "undefined" &&
|
||||
actionToggleViewMode,
|
||||
actionToggleStats,
|
||||
|
||||
@@ -224,7 +224,7 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
shortcuts={[getShortcutKey("Alt+Z")]}
|
||||
/>
|
||||
<Shortcut
|
||||
label={t("labels.showGrid")}
|
||||
label={t("labels.gridMode")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+'")]}
|
||||
/>
|
||||
<Shortcut
|
||||
|
||||
@@ -52,7 +52,6 @@ interface LayerUIProps {
|
||||
onLockToggle: () => void;
|
||||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
||||
zenModeEnabled: boolean;
|
||||
showExitZenModeBtn: boolean;
|
||||
toggleZenMode: () => void;
|
||||
langCode: Language["code"];
|
||||
isCollaborating: boolean;
|
||||
@@ -297,7 +296,6 @@ const LayerUI = ({
|
||||
onLockToggle,
|
||||
onInsertElements,
|
||||
zenModeEnabled,
|
||||
showExitZenModeBtn,
|
||||
toggleZenMode,
|
||||
isCollaborating,
|
||||
onExportToBackend,
|
||||
@@ -515,18 +513,17 @@ const LayerUI = ({
|
||||
"transition-right": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
{appState.collaborators.size > 0 &&
|
||||
Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(([_, client]) => Object.keys(client).length !== 0)
|
||||
.map(([clientId, client]) => (
|
||||
<Tooltip
|
||||
label={client.username || "Unknown user"}
|
||||
key={clientId}
|
||||
>
|
||||
{actionManager.renderAction("goToCollaborator", clientId)}
|
||||
</Tooltip>
|
||||
))}
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(([_, client]) => Object.keys(client).length !== 0)
|
||||
.map(([clientId, client]) => (
|
||||
<Tooltip
|
||||
label={client.username || "Unknown user"}
|
||||
key={clientId}
|
||||
>
|
||||
{actionManager.renderAction("goToCollaborator", clientId)}
|
||||
</Tooltip>
|
||||
))}
|
||||
</UserList>
|
||||
</div>
|
||||
</FixedSideContainer>
|
||||
@@ -581,7 +578,7 @@ const LayerUI = ({
|
||||
</div>
|
||||
<button
|
||||
className={clsx("disable-zen-mode", {
|
||||
"disable-zen-mode--visible": showExitZenModeBtn,
|
||||
"disable-zen-mode--visible": zenModeEnabled,
|
||||
})}
|
||||
onClick={toggleZenMode}
|
||||
>
|
||||
|
||||
@@ -152,26 +152,24 @@ export const MobileMenu = ({
|
||||
<Stack.Col gap={4}>
|
||||
{renderCanvasActions()}
|
||||
{renderCustomFooter?.(true)}
|
||||
{appState.collaborators.size > 0 && (
|
||||
<fieldset>
|
||||
<legend>{t("labels.collaborators")}</legend>
|
||||
<UserList mobile>
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(
|
||||
([_, client]) => Object.keys(client).length !== 0,
|
||||
)
|
||||
.map(([clientId, client]) => (
|
||||
<React.Fragment key={clientId}>
|
||||
{actionManager.renderAction(
|
||||
"goToCollaborator",
|
||||
clientId,
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</UserList>
|
||||
</fieldset>
|
||||
)}
|
||||
<fieldset>
|
||||
<legend>{t("labels.collaborators")}</legend>
|
||||
<UserList mobile>
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
.filter(
|
||||
([_, client]) => Object.keys(client).length !== 0,
|
||||
)
|
||||
.map(([clientId, client]) => (
|
||||
<React.Fragment key={clientId}>
|
||||
{actionManager.renderAction(
|
||||
"goToCollaborator",
|
||||
clientId,
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</UserList>
|
||||
</fieldset>
|
||||
</Stack.Col>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
@@ -11,13 +11,7 @@ import { t } from "../i18n";
|
||||
import useIsMobile from "../is-mobile";
|
||||
import { getTargetElements } from "../scene";
|
||||
import { AppState } from "../types";
|
||||
import {
|
||||
debounce,
|
||||
formatSpeedBits,
|
||||
formatTime,
|
||||
getVersion,
|
||||
nFormatter,
|
||||
} from "../utils";
|
||||
import { debounce, getVersion, nFormatter } from "../utils";
|
||||
import { close } from "./icons";
|
||||
import { Island } from "./Island";
|
||||
import "./Stats.scss";
|
||||
@@ -36,8 +30,6 @@ export const Stats = (props: {
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
onClose: () => void;
|
||||
isCollaborating?: boolean;
|
||||
shouldEnableNetworkStats: boolean;
|
||||
}) => {
|
||||
const isMobile = useIsMobile();
|
||||
const [storageSizes, setStorageSizes] = useState<StorageSizes>({
|
||||
@@ -178,37 +170,6 @@ export const Stats = (props: {
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{props.shouldEnableNetworkStats && props.isCollaborating ? (
|
||||
<>
|
||||
<tr>
|
||||
<th colSpan={2}>{t("stats.collaboration")}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("stats.collaborators")}</td>
|
||||
<td>{props.appState.collaborators.size}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("stats.ping")}</td>
|
||||
<td>
|
||||
{props.appState.networkPing === 0
|
||||
? "…"
|
||||
: props.appState.networkPing > 0
|
||||
? formatTime(props.appState.networkPing)
|
||||
: t("stats.error")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t("stats.speed")}</td>
|
||||
<td>
|
||||
{props.appState.networkSpeed === 0
|
||||
? "…"
|
||||
: props.appState.networkSpeed > 0
|
||||
? formatSpeedBits(props.appState.networkSpeed)
|
||||
: t("stats.error")}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
) : null}
|
||||
<tr>
|
||||
<th colSpan={2}>{t("stats.version")}</th>
|
||||
</tr>
|
||||
|
||||
@@ -8,8 +8,6 @@ export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
|
||||
export const ELEMENT_TRANSLATE_AMOUNT = 1;
|
||||
export const TEXT_TO_CENTER_SNAP_THRESHOLD = 30;
|
||||
export const SHIFT_LOCKING_ANGLE = Math.PI / 12;
|
||||
export const NETWORK_TIMEOUT_MS = 4000;
|
||||
|
||||
export const CURSOR_TYPE = {
|
||||
TEXT: "text",
|
||||
CROSSHAIR: "crosshair",
|
||||
|
||||
@@ -227,8 +227,7 @@
|
||||
.App-top-bar {
|
||||
z-index: var(--zIndex-layerUI);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.App-bottom-bar {
|
||||
|
||||
106
src/data/pixelated-image.ts
Normal file
106
src/data/pixelated-image.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { ExcalidrawGenericElement, NonDeleted } from "../element/types";
|
||||
import { newElement } from "../element";
|
||||
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
|
||||
import { randomId } from "../random";
|
||||
|
||||
const loadImage = async (url: string): Promise<HTMLImageElement> => {
|
||||
const image = new Image();
|
||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
image.onload = () => resolve(image);
|
||||
image.onerror = (err) =>
|
||||
reject(
|
||||
new Error(
|
||||
`Failed to load image: ${err ? err.toString : "unknown error"}`,
|
||||
),
|
||||
);
|
||||
image.onabort = () =>
|
||||
reject(new Error(`Failed to load image: image load aborted`));
|
||||
image.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
const commonProps = {
|
||||
fillStyle: "solid",
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
opacity: 100,
|
||||
roughness: 1,
|
||||
strokeColor: "transparent",
|
||||
strokeSharpness: "sharp",
|
||||
strokeStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
verticalAlign: "middle",
|
||||
} as const;
|
||||
|
||||
export const pixelateImage = async (
|
||||
blob: Blob,
|
||||
cellSize: number,
|
||||
suggestedMaxShapeCount: number,
|
||||
x: number,
|
||||
y: number,
|
||||
) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
try {
|
||||
const image = await loadImage(url);
|
||||
|
||||
// initialize canvas for pixelation
|
||||
const { width, height } = image;
|
||||
let canvasWidth = Math.floor(width / cellSize);
|
||||
let canvasHeight = Math.floor(height / cellSize);
|
||||
const shapeCount = canvasHeight * canvasWidth;
|
||||
if (shapeCount > suggestedMaxShapeCount) {
|
||||
canvasWidth = Math.floor(
|
||||
canvasWidth * (suggestedMaxShapeCount / shapeCount),
|
||||
);
|
||||
canvasHeight = Math.floor(
|
||||
canvasHeight * (suggestedMaxShapeCount / shapeCount),
|
||||
);
|
||||
}
|
||||
const xOffset = x - (canvasWidth * cellSize) / 2;
|
||||
const yOffset = y - (canvasHeight * cellSize) / 2;
|
||||
|
||||
const canvas =
|
||||
"OffscreenCanvas" in window
|
||||
? new OffscreenCanvas(canvasWidth, canvasHeight)
|
||||
: document.createElement("canvas");
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
|
||||
// Draw image on canvas
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.drawImage(image, 0, 0, width, height, 0, 0, canvasWidth, canvasHeight);
|
||||
const imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
|
||||
const buffer = imageData.data;
|
||||
|
||||
const groupId = randomId();
|
||||
const shapes: NonDeleted<ExcalidrawGenericElement>[] = [];
|
||||
|
||||
for (let row = 0; row < canvasHeight; row++) {
|
||||
for (let col = 0; col < canvasWidth; col++) {
|
||||
const offset = row * canvasWidth * 4 + col * 4;
|
||||
const r = buffer[offset];
|
||||
const g = buffer[offset + 1];
|
||||
const b = buffer[offset + 2];
|
||||
const alpha = buffer[offset + 3];
|
||||
if (alpha) {
|
||||
const color = `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
const rectangle = newElement({
|
||||
backgroundColor: color,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
x: xOffset + col * cellSize,
|
||||
y: yOffset + row * cellSize,
|
||||
width: cellSize,
|
||||
height: cellSize,
|
||||
});
|
||||
shapes.push(rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shapes;
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
@@ -80,14 +80,7 @@ const initializeScene = async (opts: {
|
||||
let roomLinkData = getCollaborationLinkData(window.location.href);
|
||||
const isExternalScene = !!(id || jsonMatch || roomLinkData);
|
||||
if (isExternalScene) {
|
||||
if (
|
||||
// don't prompt if scene is empty
|
||||
!scene.elements.length ||
|
||||
// don't prompt for collab scenes because we don't override local storage
|
||||
roomLinkData ||
|
||||
// otherwise, prompt whether user wants to override current scene
|
||||
window.confirm(t("alerts.loadSceneOverridePrompt"))
|
||||
) {
|
||||
if (roomLinkData || window.confirm(t("alerts.loadSceneOverridePrompt"))) {
|
||||
// Backwards compatibility with legacy url format
|
||||
if (id) {
|
||||
scene = await loadScene(id, null, initialData);
|
||||
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "Speicher",
|
||||
"title": "Statistiken für Nerds",
|
||||
"total": "Gesamt",
|
||||
"version": "Version",
|
||||
"versionCopy": "Zum Kopieren klicken",
|
||||
"versionNotAvailable": "Version nicht verfügbar",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Breite"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Formatierung kopiert.",
|
||||
"copyToClipboard": "In die Zwischenablage kopiert.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "In die Zwischenablage als PNG kopiert."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"group": "Group selection",
|
||||
"ungroup": "Ungroup selection",
|
||||
"collaborators": "Collaborators",
|
||||
"showGrid": "Show grid",
|
||||
"gridMode": "Grid mode",
|
||||
"addToLibrary": "Add to library",
|
||||
"removeFromLibrary": "Remove from library",
|
||||
"libraryLoadingMessage": "Loading library…",
|
||||
@@ -227,16 +227,11 @@
|
||||
},
|
||||
"stats": {
|
||||
"angle": "Angle",
|
||||
"collaboration": "Collaboration",
|
||||
"collaborators": "Collaborators",
|
||||
"element": "Element",
|
||||
"elements": "Elements",
|
||||
"error": "Error",
|
||||
"height": "Height",
|
||||
"ping": "Ping",
|
||||
"scene": "Scene",
|
||||
"selected": "Selected",
|
||||
"speed": "Speed",
|
||||
"storage": "Storage",
|
||||
"title": "Stats for nerds",
|
||||
"total": "Total",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"gridMode": "Modo cuadrícula",
|
||||
"addToLibrary": "Añadir a la biblioteca",
|
||||
"removeFromLibrary": "Eliminar de la biblioteca",
|
||||
"libraryLoadingMessage": "Cargando librería…",
|
||||
"libraryLoadingMessage": "Cargando biblioteca…",
|
||||
"libraries": "Explorar bibliotecas",
|
||||
"loadingScene": "Cargando escena…",
|
||||
"align": "Alinear",
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "Almacenamiento",
|
||||
"title": "Estadísticas para nerds",
|
||||
"total": "Total",
|
||||
"version": "Versión",
|
||||
"versionCopy": "Clic para copiar",
|
||||
"versionNotAvailable": "Versión no disponible",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Ancho"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Estilos copiados.",
|
||||
"copyToClipboard": "Copiado en el portapapeles.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Copiado al portapapeles como PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "Tallennustila",
|
||||
"title": "Nörttien tilastot",
|
||||
"total": "Yhteensä",
|
||||
"version": "Versio",
|
||||
"versionCopy": "Klikkaa kopioidaksesi",
|
||||
"versionNotAvailable": "Versio ei saatavilla",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Leveys"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Tyylit kopioitu.",
|
||||
"copyToClipboard": "Kopioitu leikepöydälle.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Kopioitu leikepöydälle PNG-tiedostona."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,9 +235,9 @@
|
||||
"storage": "Aḥraz",
|
||||
"title": "",
|
||||
"total": "Aɣrud",
|
||||
"version": "Alqem",
|
||||
"versionCopy": "Sit ad tneɣleḍ",
|
||||
"versionNotAvailable": "Ur inuḥ ulqem",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Tehri"
|
||||
},
|
||||
"toast": {
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"centerHorizontally": "수평으로 중앙 정렬",
|
||||
"distributeHorizontally": "수평으로 분배",
|
||||
"distributeVertically": "수직으로 분배",
|
||||
"viewMode": "보기 모드"
|
||||
"viewMode": ""
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "캔버스 초기화",
|
||||
@@ -136,7 +136,7 @@
|
||||
"decryptFailed": "데이터를 복호화하지 못했습니다.",
|
||||
"uploadedSecurly": "업로드는 종단 간 암호화로 보호되므로 Excalidraw 서버 및 타사가 콘텐츠를 읽을 수 없습니다.",
|
||||
"loadSceneOverridePrompt": "외부 파일을 불러 오면 기존 콘텐츠가 대체됩니다. 계속 진행할까요?",
|
||||
"collabStopOverridePrompt": "협업 세션을 종료하면 로컬 저장소에 있는 그림이 협업 세션의 그림으로 대체됩니다. 진행하겠습니까?\n\n(로컬 저장소에 있는 그림을 유지하려면 현재 브라우저 탭을 닫아주세요.)",
|
||||
"collabStopOverridePrompt": "",
|
||||
"errorLoadingLibrary": "외부 라이브러리를 불러오는 중에 문제가 발생했습니다.",
|
||||
"confirmAddLibrary": "{{numShapes}}개의 모양이 라이브러리에 추가됩니다. 계속하시겠어요?",
|
||||
"imageDoesNotContainScene": "이미지에서 불러오기는 현재 지원되지 않습니다.\n\n화면을 불러오려고 하셨나요? 이미지에 화면 정보가 없는 것 같습니다. 내보낼 때 화면을 포함했나요?",
|
||||
@@ -202,25 +202,25 @@
|
||||
"title": "오류"
|
||||
},
|
||||
"helpDialog": {
|
||||
"blog": "블로그 읽어보기",
|
||||
"click": "클릭",
|
||||
"curvedArrow": "곡선 화살표",
|
||||
"curvedLine": "곡선",
|
||||
"documentation": "설명서",
|
||||
"drag": "드래그",
|
||||
"editor": "에디터",
|
||||
"github": "문제 제보하기",
|
||||
"howto": "가이드 참고하기",
|
||||
"or": "또는",
|
||||
"preventBinding": "화살표가 붙지 않게 하기",
|
||||
"shapes": "도형",
|
||||
"shortcuts": "키보드 단축키",
|
||||
"textFinish": "편집 완료 (텍스트)",
|
||||
"textNewLine": "줄바꿈 (텍스트)",
|
||||
"title": "도움말",
|
||||
"view": "보기",
|
||||
"zoomToFit": "모든 요소가 보이도록 확대/축소",
|
||||
"zoomToSelection": "선택 영역으로 확대/축소"
|
||||
"blog": "",
|
||||
"click": "",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"drag": "",
|
||||
"editor": "",
|
||||
"github": "",
|
||||
"howto": "",
|
||||
"or": "",
|
||||
"preventBinding": "",
|
||||
"shapes": "",
|
||||
"shortcuts": "",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"title": "",
|
||||
"view": "",
|
||||
"zoomToFit": "",
|
||||
"zoomToSelection": ""
|
||||
},
|
||||
"encrypted": {
|
||||
"tooltip": "그림은 종단 간 암호화되므로 Excalidraw의 서버는 절대로 내용을 알 수 없습니다."
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "저장공간",
|
||||
"title": "덕후들을 위한 통계",
|
||||
"total": "합계",
|
||||
"version": "버전",
|
||||
"versionCopy": "복사하려면 클릭",
|
||||
"versionNotAvailable": "해당 버전 사용 불가능",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "너비"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "스타일 복사.",
|
||||
"copyToClipboard": "클립보드로 복사.",
|
||||
"copyToClipboardAsPng": "클립보드로 PNG 이미지 복사."
|
||||
"copyStyles": "",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "Opslag",
|
||||
"title": "Statistieken voor nerds",
|
||||
"total": "Totaal",
|
||||
"version": "Versie",
|
||||
"versionCopy": "Klik om te kopiëren",
|
||||
"versionNotAvailable": "Versie niet beschikbaar",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Breedte"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Stijlen gekopieerd.",
|
||||
"copyToClipboard": "Gekopieerd naar het klembord.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Gekopieerd naar klembord als PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"ar-SA": 87,
|
||||
"bg-BG": 91,
|
||||
"ca-ES": 87,
|
||||
"de-DE": 100,
|
||||
"de-DE": 98,
|
||||
"el-GR": 99,
|
||||
"en": 100,
|
||||
"es-ES": 100,
|
||||
"es-ES": 98,
|
||||
"fa-IR": 95,
|
||||
"fi-FI": 100,
|
||||
"fi-FI": 98,
|
||||
"fr-FR": 100,
|
||||
"he-IL": 87,
|
||||
"hi-IN": 98,
|
||||
@@ -15,20 +15,20 @@
|
||||
"id-ID": 97,
|
||||
"it-IT": 100,
|
||||
"ja-JP": 79,
|
||||
"kab-KAB": 96,
|
||||
"ko-KR": 100,
|
||||
"kab-KAB": 94,
|
||||
"ko-KR": 87,
|
||||
"my-MM": 81,
|
||||
"nb-NO": 100,
|
||||
"nl-NL": 99,
|
||||
"nl-NL": 97,
|
||||
"nn-NO": 90,
|
||||
"pa-IN": 100,
|
||||
"pl-PL": 88,
|
||||
"pt-BR": 100,
|
||||
"pt-PT": 97,
|
||||
"ro-RO": 100,
|
||||
"ru-RU": 98,
|
||||
"ru-RU": 97,
|
||||
"sk-SK": 100,
|
||||
"sv-SE": 100,
|
||||
"sv-SE": 98,
|
||||
"tr-TR": 87,
|
||||
"uk-UA": 97,
|
||||
"zh-CN": 98,
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
"title": "Статистика для ботаников",
|
||||
"total": "Всего",
|
||||
"version": "",
|
||||
"versionCopy": "Копировать",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Ширина"
|
||||
},
|
||||
|
||||
@@ -235,14 +235,14 @@
|
||||
"storage": "Lagring",
|
||||
"title": "Statistik för nördar",
|
||||
"total": "Totalt",
|
||||
"version": "Version",
|
||||
"versionCopy": "Klicka för att kopiera",
|
||||
"versionNotAvailable": "Versionen är inte tillgänglig",
|
||||
"version": "",
|
||||
"versionCopy": "",
|
||||
"versionNotAvailable": "",
|
||||
"width": "Bredd"
|
||||
},
|
||||
"toast": {
|
||||
"copyStyles": "Kopierade stilar.",
|
||||
"copyToClipboard": "Kopierad till urklipp.",
|
||||
"copyToClipboard": "",
|
||||
"copyToClipboardAsPng": "Kopierat till urklipp som PNG."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import { getAverage } from "./utils";
|
||||
|
||||
const IMAGE_URL = `${process.env.REACT_APP_SOCKET_SERVER_URL}/test256.png`;
|
||||
const IMAGE_SIZE_BITS = 141978 * 8;
|
||||
const AVERAGE_MAX = 4;
|
||||
|
||||
const speedHistory: number[] = [];
|
||||
|
||||
const pushSpeed = (speed: number): void => {
|
||||
speedHistory.push(speed);
|
||||
if (speedHistory.length > AVERAGE_MAX) {
|
||||
speedHistory.shift();
|
||||
}
|
||||
};
|
||||
|
||||
const getSpeedBits = (
|
||||
imageSize: number,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
): number => {
|
||||
const duration = (endTime - startTime) / 1000;
|
||||
if (duration > 0) {
|
||||
return imageSize / duration;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const processImage = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
let endTime: number;
|
||||
image.onload = () => {
|
||||
endTime = new Date().getTime();
|
||||
const speed = getSpeedBits(IMAGE_SIZE_BITS, startTime, endTime);
|
||||
pushSpeed(speed);
|
||||
resolve(getAverage(speedHistory));
|
||||
};
|
||||
|
||||
image.onerror = () => {
|
||||
resolve(-1);
|
||||
};
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
image.src = `${IMAGE_URL}?t=${startTime}`;
|
||||
});
|
||||
};
|
||||
|
||||
export const getNetworkSpeed = async (): Promise<number> => {
|
||||
return await processImage();
|
||||
};
|
||||
|
||||
export const getNetworkPing = async () => {
|
||||
const startTime = new Date().getTime();
|
||||
try {
|
||||
await fetch(process.env.REACT_APP_SOCKET_SERVER_URL, {
|
||||
mode: "no-cors",
|
||||
method: "HEAD",
|
||||
});
|
||||
const endTime = new Date().getTime();
|
||||
return endTime - startTime;
|
||||
} catch (error) {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
@@ -18,7 +18,6 @@ Please add the latest change on the top under the correct section.
|
||||
|
||||
### Features
|
||||
|
||||
- Add `zenModeEnabled` and `gridModeEnabled` prop which enables zen mode and grid mode respectively [#2901](https://github.com/excalidraw/excalidraw/pull/2901). When this prop is used, the zen mode / grid mode will be fully controlled by the host app.
|
||||
- Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host.
|
||||
- Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834).
|
||||
|
||||
|
||||
@@ -138,9 +138,7 @@ export default function App() {
|
||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
||||
| [`langCode`](#langCode) | string | `en` | Language code string |
|
||||
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
|
||||
| [`viewModeEnabled`](#viewModeEnabled) | boolean | | This implies if the app is in view mode. |
|
||||
| [`zenModeEnabled`](#zenModeEnabled) | boolean | | This implies if the zen mode is enabled |
|
||||
| [`gridModeEnabled`](#gridModeEnabled) | boolean | | This implies if the grid mode is enabled |
|
||||
| [`viewModeEnabled`](#viewModeEnabled) | boolean | false | This implies if the app is in view mode. |
|
||||
|
||||
### `Extra API's`
|
||||
|
||||
@@ -336,12 +334,4 @@ A function that renders (returns JSX) custom UI footer. For example, you can use
|
||||
|
||||
#### `viewModeEnabled`
|
||||
|
||||
This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over `intialData.appState.viewModeEnabled`, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
|
||||
#### `zenModeEnabled`
|
||||
|
||||
This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over `intialData.appState.zenModeEnabled`, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
|
||||
#### `gridModeEnabled`
|
||||
|
||||
This prop indicates whether the shows the grid. When supplied, the value takes precedence over `intialData.appState.gridModeEnabled`, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||
This prop indicates if the app is in `view mode`. When this prop is used, the `view mode` will not show up in context menu is so it is fully controlled by host. Also the value of this prop if passed will be used over the value of `intialData.appState.viewModeEnabled`
|
||||
|
||||
@@ -27,8 +27,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
||||
renderFooter,
|
||||
langCode = defaultLang.code,
|
||||
viewModeEnabled,
|
||||
zenModeEnabled,
|
||||
gridModeEnabled,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -68,8 +66,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
||||
renderFooter={renderFooter}
|
||||
langCode={langCode}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
gridModeEnabled={gridModeEnabled}
|
||||
/>
|
||||
</IsMobileProvider>
|
||||
</InitializeApp>
|
||||
|
||||
@@ -47,11 +47,9 @@ import {
|
||||
TransformHandles,
|
||||
TransformHandleType,
|
||||
} from "../element/transformHandles";
|
||||
import { viewportCoordsToSceneCoords, supportsEmoji } from "../utils";
|
||||
import { viewportCoordsToSceneCoords } from "../utils";
|
||||
import { UserIdleState } from "../excalidraw-app/collab/types";
|
||||
|
||||
const hasEmojiSupport = supportsEmoji();
|
||||
|
||||
const strokeRectWithRotation = (
|
||||
context: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
@@ -451,7 +449,7 @@ export const renderScene = (
|
||||
|
||||
const userState = sceneState.remotePointerUserStates[clientId];
|
||||
if (isOutOfBounds || userState === UserIdleState.AWAY) {
|
||||
context.globalAlpha = 0.48;
|
||||
context.globalAlpha = 0.2;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -483,24 +481,13 @@ export const renderScene = (
|
||||
context.stroke();
|
||||
|
||||
const username = sceneState.remotePointerUsernames[clientId];
|
||||
let usernameAndIdleState;
|
||||
if (hasEmojiSupport) {
|
||||
usernameAndIdleState = `${username ? `${username} ` : ""}${
|
||||
userState === UserIdleState.AWAY
|
||||
? "⚫️"
|
||||
: userState === UserIdleState.IDLE
|
||||
? "💤"
|
||||
: "🟢"
|
||||
}`;
|
||||
} else {
|
||||
usernameAndIdleState = `${username ? `${username}` : ""}${
|
||||
userState === UserIdleState.AWAY
|
||||
? ` (${UserIdleState.AWAY})`
|
||||
: userState === UserIdleState.IDLE
|
||||
? ` (${UserIdleState.IDLE})`
|
||||
: ""
|
||||
}`;
|
||||
}
|
||||
const usernameAndIdleState = `${username ? `${username} ` : ""}${
|
||||
userState === UserIdleState.AWAY
|
||||
? "⚫️"
|
||||
: userState === UserIdleState.IDLE
|
||||
? "💤"
|
||||
: "🟢"
|
||||
}`;
|
||||
|
||||
if (!isOutOfBounds && usernameAndIdleState) {
|
||||
const offsetX = x + width;
|
||||
|
||||
@@ -40,8 +40,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -502,8 +500,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -970,8 +966,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -1747,8 +1741,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -1952,8 +1944,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -2411,8 +2401,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -2665,8 +2653,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -2830,8 +2816,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -3308,8 +3292,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -3617,8 +3599,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -3822,8 +3802,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -4067,8 +4045,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -4320,8 +4296,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -4704,8 +4678,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -5000,8 +4972,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -5308,8 +5278,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -5517,8 +5485,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -5682,8 +5648,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -6136,8 +6100,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -6455,8 +6417,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -8490,8 +8450,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -8853,8 +8811,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -9109,8 +9065,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -9363,8 +9317,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -9679,8 +9631,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -9844,8 +9794,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10009,8 +9957,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10174,8 +10120,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10369,8 +10313,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10564,8 +10506,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10759,8 +10699,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -10954,8 +10892,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -11119,8 +11055,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -11284,8 +11218,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -11479,8 +11411,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -11644,8 +11574,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -11839,8 +11767,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -12556,8 +12482,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -12810,8 +12734,6 @@ Object {
|
||||
"lastPointerDownWith": "touch",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -12913,8 +12835,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -13014,8 +12934,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -13179,8 +13097,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -13488,8 +13404,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -13797,8 +13711,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -13962,8 +13874,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -14159,8 +14069,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -14409,8 +14317,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -14734,8 +14640,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -15574,8 +15478,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -15883,8 +15785,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -16192,8 +16092,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -16572,8 +16470,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -16740,8 +16636,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -17062,8 +16956,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -17302,8 +17194,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -17558,8 +17448,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -17886,8 +17774,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -17987,8 +17873,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -18152,8 +18036,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -18974,8 +18856,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -19075,8 +18955,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -19830,8 +19708,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -20236,8 +20112,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -20510,8 +20384,6 @@ Object {
|
||||
"lastPointerDownWith": "touch",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -20613,8 +20485,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -21112,8 +20982,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
@@ -21213,8 +21081,6 @@ Object {
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "Untitled-201933152653",
|
||||
"networkPing": 0,
|
||||
"networkSpeed": 0,
|
||||
"offsetLeft": 0,
|
||||
"offsetTop": 0,
|
||||
"openMenu": null,
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import React from "react";
|
||||
import { fireEvent, GlobalTestState, render } from "./test-utils";
|
||||
import Excalidraw from "../packages/excalidraw/index";
|
||||
import { queryByText } from "@testing-library/react";
|
||||
import { GRID_SIZE } from "../constants";
|
||||
|
||||
const { h } = window;
|
||||
|
||||
describe("<Excalidraw/>", () => {
|
||||
describe("Test zenModeEnabled prop", () => {
|
||||
it('should show exit zen mode button when zen mode is set and zen mode option in context menu when zenModeEnabled is "undefined"', async () => {
|
||||
const { container } = await render(<Excalidraw />);
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(0);
|
||||
expect(h.state.zenModeEnabled).toBe(false);
|
||||
|
||||
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||
button: 2,
|
||||
clientX: 1,
|
||||
clientY: 1,
|
||||
});
|
||||
const contextMenu = document.querySelector(".context-menu");
|
||||
fireEvent.click(queryByText(contextMenu as HTMLElement, "Zen mode")!);
|
||||
expect(h.state.zenModeEnabled).toBe(true);
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
it("should not show exit zen mode button and zen mode option in context menu when zenModeEnabled is set", async () => {
|
||||
const { container } = await render(<Excalidraw zenModeEnabled={true} />);
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(0);
|
||||
expect(h.state.zenModeEnabled).toBe(true);
|
||||
|
||||
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||
button: 2,
|
||||
clientX: 1,
|
||||
clientY: 1,
|
||||
});
|
||||
const contextMenu = document.querySelector(".context-menu");
|
||||
expect(queryByText(contextMenu as HTMLElement, "Zen mode")).toBe(null);
|
||||
expect(h.state.zenModeEnabled).toBe(true);
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test gridModeEnabled prop", () => {
|
||||
it('should show grid mode in context menu when gridModeEnabled is "undefined"', async () => {
|
||||
const { container } = await render(<Excalidraw />);
|
||||
expect(h.state.gridSize).toBe(null);
|
||||
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(0);
|
||||
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||
button: 2,
|
||||
clientX: 1,
|
||||
clientY: 1,
|
||||
});
|
||||
const contextMenu = document.querySelector(".context-menu");
|
||||
fireEvent.click(queryByText(contextMenu as HTMLElement, "Show grid")!);
|
||||
expect(h.state.gridSize).toBe(GRID_SIZE);
|
||||
});
|
||||
|
||||
it('should not show grid mode in context menu when gridModeEnabled is not "undefined"', async () => {
|
||||
const { container } = await render(
|
||||
<Excalidraw gridModeEnabled={false} />,
|
||||
);
|
||||
expect(h.state.gridSize).toBe(null);
|
||||
|
||||
expect(
|
||||
container.getElementsByClassName("disable-zen-mode--visible").length,
|
||||
).toBe(0);
|
||||
fireEvent.contextMenu(GlobalTestState.canvas, {
|
||||
button: 2,
|
||||
clientX: 1,
|
||||
clientY: 1,
|
||||
});
|
||||
const contextMenu = document.querySelector(".context-menu");
|
||||
expect(queryByText(contextMenu as HTMLElement, "Show grid")).toBe(null);
|
||||
expect(h.state.gridSize).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -88,8 +88,6 @@ export type AppState = {
|
||||
appearance: "light" | "dark";
|
||||
gridSize: number | null;
|
||||
viewModeEnabled: boolean;
|
||||
networkSpeed: number;
|
||||
networkPing: number;
|
||||
|
||||
/** top-most selected groups (i.e. does not include nested groups) */
|
||||
selectedGroupIds: { [groupId: string]: boolean };
|
||||
@@ -187,8 +185,6 @@ export interface ExcalidrawProps {
|
||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
||||
langCode?: Language["code"];
|
||||
viewModeEnabled?: boolean;
|
||||
zenModeEnabled?: boolean;
|
||||
gridModeEnabled?: boolean;
|
||||
}
|
||||
|
||||
export type SceneData = {
|
||||
|
||||
38
src/utils.ts
38
src/utils.ts
@@ -363,47 +363,9 @@ export const nFormatter = (num: number, digits: number): string => {
|
||||
);
|
||||
};
|
||||
|
||||
export const formatSpeedBits = (speed: number): string => {
|
||||
// source: https://en.wikipedia.org/wiki/Data-rate_units#Conversion_table
|
||||
const suffix = ["bps", "kbps", "Mbps", "Gbps"];
|
||||
let index = 0;
|
||||
while (speed > 1000) {
|
||||
index++;
|
||||
speed = speed / 1000;
|
||||
}
|
||||
return `${speed.toFixed(index > 1 ? 1 : 0)} ${suffix[index]}`;
|
||||
};
|
||||
|
||||
export const getVersion = () => {
|
||||
return (
|
||||
document.querySelector<HTMLMetaElement>('meta[name="version"]')?.content ||
|
||||
DEFAULT_VERSION
|
||||
);
|
||||
};
|
||||
|
||||
export const formatTime = (mseconds: number): string => {
|
||||
return mseconds < 1000
|
||||
? `${mseconds} ms`
|
||||
: `${(mseconds / 1000).toFixed(1)} s`;
|
||||
};
|
||||
|
||||
export const getAverage = (arr: Array<number>): number => {
|
||||
return arr.reduce((sum, currentVal) => sum + currentVal) / arr.length;
|
||||
};
|
||||
|
||||
// Adapted from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/emoji.js
|
||||
export const supportsEmoji = () => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
const offset = 12;
|
||||
ctx.fillStyle = "#f00";
|
||||
ctx.textBaseline = "top";
|
||||
ctx.font = "32px Arial";
|
||||
// Modernizr used 🐨, but it is sort of supported on Windows 7.
|
||||
// Luckily 😀 isn't supported.
|
||||
ctx.fillText("😀", 0, 0);
|
||||
return ctx.getImageData(offset, offset, 1, 1).data[0] !== 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user