@markup-canvas/core 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/dist/lib/MarkupCanvas.d.ts +1 -0
- package/dist/lib/events/index.d.ts +1 -0
- package/dist/lib/events/postMessage/sendError.d.ts +1 -0
- package/dist/lib/events/postMessage/setupPostMessageEvents.d.ts +2 -0
- package/dist/markup-canvas.cjs.js +135 -7
- package/dist/markup-canvas.esm.js +135 -7
- package/dist/markup-canvas.umd.js +134 -7
- package/dist/markup-canvas.umd.min.js +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/events.d.ts +7 -0
- package/package.json +1 -1
- package/src/lib/MarkupCanvas.ts +14 -1
- package/src/lib/config/constants.ts +5 -4
- package/src/lib/config/presets/editor-preset.ts +1 -0
- package/src/lib/events/index.ts +1 -0
- package/src/lib/events/keyboard/setupKeyboardEvents.ts +3 -3
- package/src/lib/events/keyboard/setupKeyboardNavigation.ts +3 -3
- package/src/lib/events/postMessage/sendError.ts +12 -0
- package/src/lib/events/postMessage/setupPostMessageEvents.ts +96 -0
- package/src/types/config.ts +3 -0
- package/src/types/events.ts +29 -0
package/README.md
CHANGED
|
@@ -66,6 +66,8 @@ const canvas = new MarkupCanvas(container, {
|
|
|
66
66
|
| `enableTouch` | `boolean` | `true` | Enable touch gestures |
|
|
67
67
|
| `enableKeyboard` | `boolean` | `true` | Enable keyboard controls |
|
|
68
68
|
| `limitKeyboardEventsToCanvas` | `boolean` | `true` | Limit keyboard events to when canvas is focused |
|
|
69
|
+
| `bindToWindow` | `boolean` | `false` | Bind canvas instance to window (enables cross-iframe communication) |
|
|
70
|
+
| `enablePostMessageAPI` | `boolean` | `false` | Enable postMessage API for controlling canvas from outside iframe |
|
|
69
71
|
|
|
70
72
|
#### Zoom Behavior
|
|
71
73
|
|
|
@@ -136,6 +138,87 @@ const canvas = new MarkupCanvas(container, {
|
|
|
136
138
|
|--------|------|-------------|
|
|
137
139
|
| `onTransformUpdate` | `(transform: Transform) => void` | Called continuously during transform updates |
|
|
138
140
|
|
|
141
|
+
### PostMessage API - Control from Outside Iframe
|
|
142
|
+
|
|
143
|
+
Control the MarkupCanvas from a parent window or external application. Useful for Sandpack, iframes, and cross-origin scenarios.
|
|
144
|
+
|
|
145
|
+
#### Setup
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const canvas = new MarkupCanvas(container, {
|
|
149
|
+
width: 20000,
|
|
150
|
+
height: 15000,
|
|
151
|
+
bindToWindow: true, // Required: binds canvas to window
|
|
152
|
+
enablePostMessageAPI: true, // Enable postMessage listeners
|
|
153
|
+
name: "canvas", // Canvas instance name
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Send Commands from Parent
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// Get reference to the iframe
|
|
161
|
+
const iframe = document.querySelector('iframe');
|
|
162
|
+
|
|
163
|
+
// Zoom to 2x (200%)
|
|
164
|
+
iframe.contentWindow.postMessage({
|
|
165
|
+
source: "markup-canvas",
|
|
166
|
+
canvasName: "canvas",
|
|
167
|
+
action: "setZoom",
|
|
168
|
+
args: [2.0]
|
|
169
|
+
}, "*");
|
|
170
|
+
|
|
171
|
+
// Pan left by 100 pixels
|
|
172
|
+
iframe.contentWindow.postMessage({
|
|
173
|
+
source: "markup-canvas",
|
|
174
|
+
canvasName: "canvas",
|
|
175
|
+
action: "panLeft",
|
|
176
|
+
args: [100]
|
|
177
|
+
}, "*");
|
|
178
|
+
|
|
179
|
+
// Toggle theme
|
|
180
|
+
iframe.contentWindow.postMessage({
|
|
181
|
+
source: "markup-canvas",
|
|
182
|
+
canvasName: "canvas",
|
|
183
|
+
action: "toggleThemeMode"
|
|
184
|
+
}, "*");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Available PostMessage Actions
|
|
188
|
+
|
|
189
|
+
**View Control:**
|
|
190
|
+
- `zoomIn(factor?)` - Zoom in by factor (default 0.1)
|
|
191
|
+
- `zoomOut(factor?)` - Zoom out by factor (default 0.1)
|
|
192
|
+
- `setZoom(level)` - Set zoom to specific level
|
|
193
|
+
- `resetZoom()` - Reset to 100%
|
|
194
|
+
- `panLeft(distance?)`, `panRight(distance?)`, `panUp(distance?)`, `panDown(distance?)` - Pan operations
|
|
195
|
+
- `fitToScreen()` - Fit content to viewport
|
|
196
|
+
- `centerContent()` - Center content
|
|
197
|
+
- `scrollToPoint(x, y)` - Pan to specific coordinates
|
|
198
|
+
|
|
199
|
+
**Rulers & Grid:**
|
|
200
|
+
- `toggleRulers()`, `showRulers()`, `hideRulers()`
|
|
201
|
+
- `toggleGrid()`, `showGrid()`, `hideGrid()`
|
|
202
|
+
|
|
203
|
+
**Theme:**
|
|
204
|
+
- `updateThemeMode(mode)` - Set theme ("light" or "dark")
|
|
205
|
+
- `toggleThemeMode()` - Toggle between themes
|
|
206
|
+
|
|
207
|
+
#### Error Handling
|
|
208
|
+
|
|
209
|
+
Listen for errors when actions fail:
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
window.addEventListener("message", (event) => {
|
|
213
|
+
if (event.data.source === "markup-canvas-error") {
|
|
214
|
+
console.error(
|
|
215
|
+
`Action failed: ${event.data.action}`,
|
|
216
|
+
event.data.error
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
139
222
|
### Event Handling
|
|
140
223
|
|
|
141
224
|
Listen to various events emitted by the canvas:
|
|
@@ -12,6 +12,7 @@ export declare class MarkupCanvas implements Canvas {
|
|
|
12
12
|
config: Required<MarkupCanvasConfig>;
|
|
13
13
|
private _isReady;
|
|
14
14
|
private listen;
|
|
15
|
+
private postMessageCleanup;
|
|
15
16
|
constructor(container: HTMLElement, options?: MarkupCanvasConfig);
|
|
16
17
|
private setupGlobalBinding;
|
|
17
18
|
private cleanupGlobalBinding;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { setupKeyboardEvents } from "./keyboard/setupKeyboardEvents.js";
|
|
2
2
|
export { setupMouseEvents } from "./mouse/setupMouseEvents.js";
|
|
3
|
+
export { setupPostMessageEvents } from "./postMessage/setupPostMessageEvents.js";
|
|
3
4
|
export { setupTouchEvents } from "./touch/setupTouchEvents.js";
|
|
4
5
|
export { detectTrackpadGesture } from "./trackpad/detectTrackpadGesture.js";
|
|
5
6
|
export { getAdaptiveZoomSpeed } from "./utils/getAdaptiveZoomSpeed.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sendPostMessageError(canvasName: string, action: string, error: string): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.1.
|
|
4
|
+
* @version 1.1.3
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -15,6 +15,7 @@ const EDITOR_PRESET = {
|
|
|
15
15
|
// Global Binding & Instance Access
|
|
16
16
|
bindToWindow: true,
|
|
17
17
|
name: "canvas",
|
|
18
|
+
enablePostMessageAPI: true,
|
|
18
19
|
// Interaction controls
|
|
19
20
|
enableZoom: true,
|
|
20
21
|
enablePan: true,
|
|
@@ -313,6 +314,10 @@ const DEFAULT_CONFIG = {
|
|
|
313
314
|
width: 8000,
|
|
314
315
|
height: 8000,
|
|
315
316
|
enableAcceleration: true,
|
|
317
|
+
// Global Binding & Instance Access
|
|
318
|
+
bindToWindow: false,
|
|
319
|
+
name: "markupCanvas",
|
|
320
|
+
enablePostMessageAPI: false,
|
|
316
321
|
// Interaction controls
|
|
317
322
|
enableZoom: true,
|
|
318
323
|
enablePan: true,
|
|
@@ -364,9 +369,6 @@ const DEFAULT_CONFIG = {
|
|
|
364
369
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
365
370
|
// Theme
|
|
366
371
|
themeMode: "light",
|
|
367
|
-
// Global Binding & Instance Access
|
|
368
|
-
bindToWindow: false,
|
|
369
|
-
name: "markupCanvas",
|
|
370
372
|
// Callbacks
|
|
371
373
|
onTransformUpdate: () => { },
|
|
372
374
|
};
|
|
@@ -842,14 +844,14 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
842
844
|
break;
|
|
843
845
|
case "g":
|
|
844
846
|
case "G":
|
|
845
|
-
if (canvas.toggleGrid) {
|
|
847
|
+
if (event.shiftKey && canvas.toggleGrid) {
|
|
846
848
|
canvas.toggleGrid();
|
|
847
849
|
}
|
|
848
|
-
handled =
|
|
850
|
+
handled = event.shiftKey;
|
|
849
851
|
break;
|
|
850
852
|
case "r":
|
|
851
853
|
case "R":
|
|
852
|
-
if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
854
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
853
855
|
canvas.toggleRulers();
|
|
854
856
|
handled = true;
|
|
855
857
|
}
|
|
@@ -1154,6 +1156,122 @@ function setupMouseEvents(canvas, config, withControls = true) {
|
|
|
1154
1156
|
return cleanup;
|
|
1155
1157
|
}
|
|
1156
1158
|
|
|
1159
|
+
function sendPostMessageError(canvasName, action, error) {
|
|
1160
|
+
window.postMessage({
|
|
1161
|
+
source: "markup-canvas-error",
|
|
1162
|
+
canvasName,
|
|
1163
|
+
action,
|
|
1164
|
+
error,
|
|
1165
|
+
timestamp: Date.now(),
|
|
1166
|
+
}, "*");
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function setupPostMessageEvents(canvas) {
|
|
1170
|
+
const handleMessage = (event) => {
|
|
1171
|
+
const data = event.data;
|
|
1172
|
+
// Validate message structure
|
|
1173
|
+
if (data.source !== "markup-canvas") {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const canvasName = canvas.config.name || "markupCanvas";
|
|
1177
|
+
if (data.canvasName !== canvasName) {
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
const action = data.action;
|
|
1181
|
+
const args = data.args || [];
|
|
1182
|
+
try {
|
|
1183
|
+
// View methods
|
|
1184
|
+
if (action === "zoomIn") {
|
|
1185
|
+
canvas.zoomIn(args[0]);
|
|
1186
|
+
}
|
|
1187
|
+
else if (action === "zoomOut") {
|
|
1188
|
+
canvas.zoomOut(args[0]);
|
|
1189
|
+
}
|
|
1190
|
+
else if (action === "setZoom") {
|
|
1191
|
+
const zoomLevel = args[0];
|
|
1192
|
+
if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
|
|
1193
|
+
throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
|
|
1194
|
+
}
|
|
1195
|
+
canvas.setZoom(zoomLevel);
|
|
1196
|
+
}
|
|
1197
|
+
else if (action === "resetZoom") {
|
|
1198
|
+
canvas.resetZoom();
|
|
1199
|
+
}
|
|
1200
|
+
else if (action === "panLeft") {
|
|
1201
|
+
canvas.panLeft(args[0]);
|
|
1202
|
+
}
|
|
1203
|
+
else if (action === "panRight") {
|
|
1204
|
+
canvas.panRight(args[0]);
|
|
1205
|
+
}
|
|
1206
|
+
else if (action === "panUp") {
|
|
1207
|
+
canvas.panUp(args[0]);
|
|
1208
|
+
}
|
|
1209
|
+
else if (action === "panDown") {
|
|
1210
|
+
canvas.panDown(args[0]);
|
|
1211
|
+
}
|
|
1212
|
+
else if (action === "fitToScreen") {
|
|
1213
|
+
canvas.fitToScreen();
|
|
1214
|
+
}
|
|
1215
|
+
else if (action === "centerContent") {
|
|
1216
|
+
canvas.centerContent();
|
|
1217
|
+
}
|
|
1218
|
+
else if (action === "scrollToPoint") {
|
|
1219
|
+
canvas.scrollToPoint(args[0], args[1]);
|
|
1220
|
+
}
|
|
1221
|
+
else if (action === "resetView") {
|
|
1222
|
+
canvas.resetView();
|
|
1223
|
+
}
|
|
1224
|
+
// Ruler/Grid methods
|
|
1225
|
+
else if (action === "toggleRulers") {
|
|
1226
|
+
canvas.toggleRulers();
|
|
1227
|
+
}
|
|
1228
|
+
else if (action === "showRulers") {
|
|
1229
|
+
canvas.showRulers();
|
|
1230
|
+
}
|
|
1231
|
+
else if (action === "hideRulers") {
|
|
1232
|
+
canvas.hideRulers();
|
|
1233
|
+
}
|
|
1234
|
+
else if (action === "toggleGrid") {
|
|
1235
|
+
canvas.toggleGrid();
|
|
1236
|
+
}
|
|
1237
|
+
else if (action === "showGrid") {
|
|
1238
|
+
canvas.showGrid();
|
|
1239
|
+
}
|
|
1240
|
+
else if (action === "hideGrid") {
|
|
1241
|
+
canvas.hideGrid();
|
|
1242
|
+
}
|
|
1243
|
+
// Config methods
|
|
1244
|
+
else if (action === "updateThemeMode") {
|
|
1245
|
+
const mode = args[0];
|
|
1246
|
+
if (mode !== "light" && mode !== "dark") {
|
|
1247
|
+
throw new Error(`Invalid theme mode: ${mode}`);
|
|
1248
|
+
}
|
|
1249
|
+
canvas.updateThemeMode(mode);
|
|
1250
|
+
}
|
|
1251
|
+
else if (action === "toggleThemeMode") {
|
|
1252
|
+
const currentConfig = canvas.getConfig();
|
|
1253
|
+
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
1254
|
+
canvas.updateThemeMode(newMode);
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
catch (error) {
|
|
1261
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1262
|
+
sendPostMessageError(canvasName, action, errorMessage);
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
if (typeof window !== "undefined") {
|
|
1266
|
+
window.addEventListener("message", handleMessage);
|
|
1267
|
+
}
|
|
1268
|
+
return () => {
|
|
1269
|
+
if (typeof window !== "undefined") {
|
|
1270
|
+
window.removeEventListener("message", handleMessage);
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1157
1275
|
function handleTouchEnd(event, touchState) {
|
|
1158
1276
|
touchState.touches = Array.from(event.touches);
|
|
1159
1277
|
if (touchState.touches.length < 2) {
|
|
@@ -1784,6 +1902,7 @@ class MarkupCanvas {
|
|
|
1784
1902
|
this.dragSetup = null;
|
|
1785
1903
|
this._isReady = false;
|
|
1786
1904
|
this.listen = new EventEmitter();
|
|
1905
|
+
this.postMessageCleanup = null;
|
|
1787
1906
|
if (!container) {
|
|
1788
1907
|
throw new Error("Container element is required");
|
|
1789
1908
|
}
|
|
@@ -1799,6 +1918,10 @@ class MarkupCanvas {
|
|
|
1799
1918
|
this.broadcastEvent(event, data);
|
|
1800
1919
|
});
|
|
1801
1920
|
this.setupGlobalBinding();
|
|
1921
|
+
// Set up postMessage listener
|
|
1922
|
+
if (this.config.enablePostMessageAPI) {
|
|
1923
|
+
this.postMessageCleanup = setupPostMessageEvents(this);
|
|
1924
|
+
}
|
|
1802
1925
|
}
|
|
1803
1926
|
this.setupEventHandlers();
|
|
1804
1927
|
this._isReady = true;
|
|
@@ -2161,6 +2284,11 @@ class MarkupCanvas {
|
|
|
2161
2284
|
// Cleanup method
|
|
2162
2285
|
cleanup() {
|
|
2163
2286
|
this.cleanupGlobalBinding();
|
|
2287
|
+
// Cleanup postMessage listener
|
|
2288
|
+
if (this.postMessageCleanup) {
|
|
2289
|
+
this.postMessageCleanup();
|
|
2290
|
+
this.postMessageCleanup = null;
|
|
2291
|
+
}
|
|
2164
2292
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2165
2293
|
try {
|
|
2166
2294
|
cleanup();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.1.
|
|
4
|
+
* @version 1.1.3
|
|
5
5
|
*/
|
|
6
6
|
const EDITOR_PRESET = {
|
|
7
7
|
// Canvas dimensions
|
|
@@ -11,6 +11,7 @@ const EDITOR_PRESET = {
|
|
|
11
11
|
// Global Binding & Instance Access
|
|
12
12
|
bindToWindow: true,
|
|
13
13
|
name: "canvas",
|
|
14
|
+
enablePostMessageAPI: true,
|
|
14
15
|
// Interaction controls
|
|
15
16
|
enableZoom: true,
|
|
16
17
|
enablePan: true,
|
|
@@ -309,6 +310,10 @@ const DEFAULT_CONFIG = {
|
|
|
309
310
|
width: 8000,
|
|
310
311
|
height: 8000,
|
|
311
312
|
enableAcceleration: true,
|
|
313
|
+
// Global Binding & Instance Access
|
|
314
|
+
bindToWindow: false,
|
|
315
|
+
name: "markupCanvas",
|
|
316
|
+
enablePostMessageAPI: false,
|
|
312
317
|
// Interaction controls
|
|
313
318
|
enableZoom: true,
|
|
314
319
|
enablePan: true,
|
|
@@ -360,9 +365,6 @@ const DEFAULT_CONFIG = {
|
|
|
360
365
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
361
366
|
// Theme
|
|
362
367
|
themeMode: "light",
|
|
363
|
-
// Global Binding & Instance Access
|
|
364
|
-
bindToWindow: false,
|
|
365
|
-
name: "markupCanvas",
|
|
366
368
|
// Callbacks
|
|
367
369
|
onTransformUpdate: () => { },
|
|
368
370
|
};
|
|
@@ -838,14 +840,14 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
838
840
|
break;
|
|
839
841
|
case "g":
|
|
840
842
|
case "G":
|
|
841
|
-
if (canvas.toggleGrid) {
|
|
843
|
+
if (event.shiftKey && canvas.toggleGrid) {
|
|
842
844
|
canvas.toggleGrid();
|
|
843
845
|
}
|
|
844
|
-
handled =
|
|
846
|
+
handled = event.shiftKey;
|
|
845
847
|
break;
|
|
846
848
|
case "r":
|
|
847
849
|
case "R":
|
|
848
|
-
if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
850
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
849
851
|
canvas.toggleRulers();
|
|
850
852
|
handled = true;
|
|
851
853
|
}
|
|
@@ -1150,6 +1152,122 @@ function setupMouseEvents(canvas, config, withControls = true) {
|
|
|
1150
1152
|
return cleanup;
|
|
1151
1153
|
}
|
|
1152
1154
|
|
|
1155
|
+
function sendPostMessageError(canvasName, action, error) {
|
|
1156
|
+
window.postMessage({
|
|
1157
|
+
source: "markup-canvas-error",
|
|
1158
|
+
canvasName,
|
|
1159
|
+
action,
|
|
1160
|
+
error,
|
|
1161
|
+
timestamp: Date.now(),
|
|
1162
|
+
}, "*");
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function setupPostMessageEvents(canvas) {
|
|
1166
|
+
const handleMessage = (event) => {
|
|
1167
|
+
const data = event.data;
|
|
1168
|
+
// Validate message structure
|
|
1169
|
+
if (data.source !== "markup-canvas") {
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
const canvasName = canvas.config.name || "markupCanvas";
|
|
1173
|
+
if (data.canvasName !== canvasName) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const action = data.action;
|
|
1177
|
+
const args = data.args || [];
|
|
1178
|
+
try {
|
|
1179
|
+
// View methods
|
|
1180
|
+
if (action === "zoomIn") {
|
|
1181
|
+
canvas.zoomIn(args[0]);
|
|
1182
|
+
}
|
|
1183
|
+
else if (action === "zoomOut") {
|
|
1184
|
+
canvas.zoomOut(args[0]);
|
|
1185
|
+
}
|
|
1186
|
+
else if (action === "setZoom") {
|
|
1187
|
+
const zoomLevel = args[0];
|
|
1188
|
+
if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
|
|
1189
|
+
throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
|
|
1190
|
+
}
|
|
1191
|
+
canvas.setZoom(zoomLevel);
|
|
1192
|
+
}
|
|
1193
|
+
else if (action === "resetZoom") {
|
|
1194
|
+
canvas.resetZoom();
|
|
1195
|
+
}
|
|
1196
|
+
else if (action === "panLeft") {
|
|
1197
|
+
canvas.panLeft(args[0]);
|
|
1198
|
+
}
|
|
1199
|
+
else if (action === "panRight") {
|
|
1200
|
+
canvas.panRight(args[0]);
|
|
1201
|
+
}
|
|
1202
|
+
else if (action === "panUp") {
|
|
1203
|
+
canvas.panUp(args[0]);
|
|
1204
|
+
}
|
|
1205
|
+
else if (action === "panDown") {
|
|
1206
|
+
canvas.panDown(args[0]);
|
|
1207
|
+
}
|
|
1208
|
+
else if (action === "fitToScreen") {
|
|
1209
|
+
canvas.fitToScreen();
|
|
1210
|
+
}
|
|
1211
|
+
else if (action === "centerContent") {
|
|
1212
|
+
canvas.centerContent();
|
|
1213
|
+
}
|
|
1214
|
+
else if (action === "scrollToPoint") {
|
|
1215
|
+
canvas.scrollToPoint(args[0], args[1]);
|
|
1216
|
+
}
|
|
1217
|
+
else if (action === "resetView") {
|
|
1218
|
+
canvas.resetView();
|
|
1219
|
+
}
|
|
1220
|
+
// Ruler/Grid methods
|
|
1221
|
+
else if (action === "toggleRulers") {
|
|
1222
|
+
canvas.toggleRulers();
|
|
1223
|
+
}
|
|
1224
|
+
else if (action === "showRulers") {
|
|
1225
|
+
canvas.showRulers();
|
|
1226
|
+
}
|
|
1227
|
+
else if (action === "hideRulers") {
|
|
1228
|
+
canvas.hideRulers();
|
|
1229
|
+
}
|
|
1230
|
+
else if (action === "toggleGrid") {
|
|
1231
|
+
canvas.toggleGrid();
|
|
1232
|
+
}
|
|
1233
|
+
else if (action === "showGrid") {
|
|
1234
|
+
canvas.showGrid();
|
|
1235
|
+
}
|
|
1236
|
+
else if (action === "hideGrid") {
|
|
1237
|
+
canvas.hideGrid();
|
|
1238
|
+
}
|
|
1239
|
+
// Config methods
|
|
1240
|
+
else if (action === "updateThemeMode") {
|
|
1241
|
+
const mode = args[0];
|
|
1242
|
+
if (mode !== "light" && mode !== "dark") {
|
|
1243
|
+
throw new Error(`Invalid theme mode: ${mode}`);
|
|
1244
|
+
}
|
|
1245
|
+
canvas.updateThemeMode(mode);
|
|
1246
|
+
}
|
|
1247
|
+
else if (action === "toggleThemeMode") {
|
|
1248
|
+
const currentConfig = canvas.getConfig();
|
|
1249
|
+
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
1250
|
+
canvas.updateThemeMode(newMode);
|
|
1251
|
+
}
|
|
1252
|
+
else {
|
|
1253
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
catch (error) {
|
|
1257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1258
|
+
sendPostMessageError(canvasName, action, errorMessage);
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
if (typeof window !== "undefined") {
|
|
1262
|
+
window.addEventListener("message", handleMessage);
|
|
1263
|
+
}
|
|
1264
|
+
return () => {
|
|
1265
|
+
if (typeof window !== "undefined") {
|
|
1266
|
+
window.removeEventListener("message", handleMessage);
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1153
1271
|
function handleTouchEnd(event, touchState) {
|
|
1154
1272
|
touchState.touches = Array.from(event.touches);
|
|
1155
1273
|
if (touchState.touches.length < 2) {
|
|
@@ -1780,6 +1898,7 @@ class MarkupCanvas {
|
|
|
1780
1898
|
this.dragSetup = null;
|
|
1781
1899
|
this._isReady = false;
|
|
1782
1900
|
this.listen = new EventEmitter();
|
|
1901
|
+
this.postMessageCleanup = null;
|
|
1783
1902
|
if (!container) {
|
|
1784
1903
|
throw new Error("Container element is required");
|
|
1785
1904
|
}
|
|
@@ -1795,6 +1914,10 @@ class MarkupCanvas {
|
|
|
1795
1914
|
this.broadcastEvent(event, data);
|
|
1796
1915
|
});
|
|
1797
1916
|
this.setupGlobalBinding();
|
|
1917
|
+
// Set up postMessage listener
|
|
1918
|
+
if (this.config.enablePostMessageAPI) {
|
|
1919
|
+
this.postMessageCleanup = setupPostMessageEvents(this);
|
|
1920
|
+
}
|
|
1798
1921
|
}
|
|
1799
1922
|
this.setupEventHandlers();
|
|
1800
1923
|
this._isReady = true;
|
|
@@ -2157,6 +2280,11 @@ class MarkupCanvas {
|
|
|
2157
2280
|
// Cleanup method
|
|
2158
2281
|
cleanup() {
|
|
2159
2282
|
this.cleanupGlobalBinding();
|
|
2283
|
+
// Cleanup postMessage listener
|
|
2284
|
+
if (this.postMessageCleanup) {
|
|
2285
|
+
this.postMessageCleanup();
|
|
2286
|
+
this.postMessageCleanup = null;
|
|
2287
|
+
}
|
|
2160
2288
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2161
2289
|
try {
|
|
2162
2290
|
cleanup();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.1.
|
|
4
|
+
* @version 1.1.3
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
@@ -252,6 +252,10 @@
|
|
|
252
252
|
width: 8000,
|
|
253
253
|
height: 8000,
|
|
254
254
|
enableAcceleration: true,
|
|
255
|
+
// Global Binding & Instance Access
|
|
256
|
+
bindToWindow: false,
|
|
257
|
+
name: "markupCanvas",
|
|
258
|
+
enablePostMessageAPI: false,
|
|
255
259
|
// Interaction controls
|
|
256
260
|
enableZoom: true,
|
|
257
261
|
enablePan: true,
|
|
@@ -303,9 +307,6 @@
|
|
|
303
307
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
304
308
|
// Theme
|
|
305
309
|
themeMode: "light",
|
|
306
|
-
// Global Binding & Instance Access
|
|
307
|
-
bindToWindow: false,
|
|
308
|
-
name: "markupCanvas",
|
|
309
310
|
// Callbacks
|
|
310
311
|
onTransformUpdate: () => { },
|
|
311
312
|
};
|
|
@@ -781,14 +782,14 @@
|
|
|
781
782
|
break;
|
|
782
783
|
case "g":
|
|
783
784
|
case "G":
|
|
784
|
-
if (canvas.toggleGrid) {
|
|
785
|
+
if (event.shiftKey && canvas.toggleGrid) {
|
|
785
786
|
canvas.toggleGrid();
|
|
786
787
|
}
|
|
787
|
-
handled =
|
|
788
|
+
handled = event.shiftKey;
|
|
788
789
|
break;
|
|
789
790
|
case "r":
|
|
790
791
|
case "R":
|
|
791
|
-
if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
792
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
792
793
|
canvas.toggleRulers();
|
|
793
794
|
handled = true;
|
|
794
795
|
}
|
|
@@ -1093,6 +1094,122 @@
|
|
|
1093
1094
|
return cleanup;
|
|
1094
1095
|
}
|
|
1095
1096
|
|
|
1097
|
+
function sendPostMessageError(canvasName, action, error) {
|
|
1098
|
+
window.postMessage({
|
|
1099
|
+
source: "markup-canvas-error",
|
|
1100
|
+
canvasName,
|
|
1101
|
+
action,
|
|
1102
|
+
error,
|
|
1103
|
+
timestamp: Date.now(),
|
|
1104
|
+
}, "*");
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
function setupPostMessageEvents(canvas) {
|
|
1108
|
+
const handleMessage = (event) => {
|
|
1109
|
+
const data = event.data;
|
|
1110
|
+
// Validate message structure
|
|
1111
|
+
if (data.source !== "markup-canvas") {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const canvasName = canvas.config.name || "markupCanvas";
|
|
1115
|
+
if (data.canvasName !== canvasName) {
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
const action = data.action;
|
|
1119
|
+
const args = data.args || [];
|
|
1120
|
+
try {
|
|
1121
|
+
// View methods
|
|
1122
|
+
if (action === "zoomIn") {
|
|
1123
|
+
canvas.zoomIn(args[0]);
|
|
1124
|
+
}
|
|
1125
|
+
else if (action === "zoomOut") {
|
|
1126
|
+
canvas.zoomOut(args[0]);
|
|
1127
|
+
}
|
|
1128
|
+
else if (action === "setZoom") {
|
|
1129
|
+
const zoomLevel = args[0];
|
|
1130
|
+
if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
|
|
1131
|
+
throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
|
|
1132
|
+
}
|
|
1133
|
+
canvas.setZoom(zoomLevel);
|
|
1134
|
+
}
|
|
1135
|
+
else if (action === "resetZoom") {
|
|
1136
|
+
canvas.resetZoom();
|
|
1137
|
+
}
|
|
1138
|
+
else if (action === "panLeft") {
|
|
1139
|
+
canvas.panLeft(args[0]);
|
|
1140
|
+
}
|
|
1141
|
+
else if (action === "panRight") {
|
|
1142
|
+
canvas.panRight(args[0]);
|
|
1143
|
+
}
|
|
1144
|
+
else if (action === "panUp") {
|
|
1145
|
+
canvas.panUp(args[0]);
|
|
1146
|
+
}
|
|
1147
|
+
else if (action === "panDown") {
|
|
1148
|
+
canvas.panDown(args[0]);
|
|
1149
|
+
}
|
|
1150
|
+
else if (action === "fitToScreen") {
|
|
1151
|
+
canvas.fitToScreen();
|
|
1152
|
+
}
|
|
1153
|
+
else if (action === "centerContent") {
|
|
1154
|
+
canvas.centerContent();
|
|
1155
|
+
}
|
|
1156
|
+
else if (action === "scrollToPoint") {
|
|
1157
|
+
canvas.scrollToPoint(args[0], args[1]);
|
|
1158
|
+
}
|
|
1159
|
+
else if (action === "resetView") {
|
|
1160
|
+
canvas.resetView();
|
|
1161
|
+
}
|
|
1162
|
+
// Ruler/Grid methods
|
|
1163
|
+
else if (action === "toggleRulers") {
|
|
1164
|
+
canvas.toggleRulers();
|
|
1165
|
+
}
|
|
1166
|
+
else if (action === "showRulers") {
|
|
1167
|
+
canvas.showRulers();
|
|
1168
|
+
}
|
|
1169
|
+
else if (action === "hideRulers") {
|
|
1170
|
+
canvas.hideRulers();
|
|
1171
|
+
}
|
|
1172
|
+
else if (action === "toggleGrid") {
|
|
1173
|
+
canvas.toggleGrid();
|
|
1174
|
+
}
|
|
1175
|
+
else if (action === "showGrid") {
|
|
1176
|
+
canvas.showGrid();
|
|
1177
|
+
}
|
|
1178
|
+
else if (action === "hideGrid") {
|
|
1179
|
+
canvas.hideGrid();
|
|
1180
|
+
}
|
|
1181
|
+
// Config methods
|
|
1182
|
+
else if (action === "updateThemeMode") {
|
|
1183
|
+
const mode = args[0];
|
|
1184
|
+
if (mode !== "light" && mode !== "dark") {
|
|
1185
|
+
throw new Error(`Invalid theme mode: ${mode}`);
|
|
1186
|
+
}
|
|
1187
|
+
canvas.updateThemeMode(mode);
|
|
1188
|
+
}
|
|
1189
|
+
else if (action === "toggleThemeMode") {
|
|
1190
|
+
const currentConfig = canvas.getConfig();
|
|
1191
|
+
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
1192
|
+
canvas.updateThemeMode(newMode);
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
catch (error) {
|
|
1199
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1200
|
+
sendPostMessageError(canvasName, action, errorMessage);
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
if (typeof window !== "undefined") {
|
|
1204
|
+
window.addEventListener("message", handleMessage);
|
|
1205
|
+
}
|
|
1206
|
+
return () => {
|
|
1207
|
+
if (typeof window !== "undefined") {
|
|
1208
|
+
window.removeEventListener("message", handleMessage);
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1096
1213
|
function handleTouchEnd(event, touchState) {
|
|
1097
1214
|
touchState.touches = Array.from(event.touches);
|
|
1098
1215
|
if (touchState.touches.length < 2) {
|
|
@@ -1723,6 +1840,7 @@
|
|
|
1723
1840
|
this.dragSetup = null;
|
|
1724
1841
|
this._isReady = false;
|
|
1725
1842
|
this.listen = new EventEmitter();
|
|
1843
|
+
this.postMessageCleanup = null;
|
|
1726
1844
|
if (!container) {
|
|
1727
1845
|
throw new Error("Container element is required");
|
|
1728
1846
|
}
|
|
@@ -1738,6 +1856,10 @@
|
|
|
1738
1856
|
this.broadcastEvent(event, data);
|
|
1739
1857
|
});
|
|
1740
1858
|
this.setupGlobalBinding();
|
|
1859
|
+
// Set up postMessage listener
|
|
1860
|
+
if (this.config.enablePostMessageAPI) {
|
|
1861
|
+
this.postMessageCleanup = setupPostMessageEvents(this);
|
|
1862
|
+
}
|
|
1741
1863
|
}
|
|
1742
1864
|
this.setupEventHandlers();
|
|
1743
1865
|
this._isReady = true;
|
|
@@ -2100,6 +2222,11 @@
|
|
|
2100
2222
|
// Cleanup method
|
|
2101
2223
|
cleanup() {
|
|
2102
2224
|
this.cleanupGlobalBinding();
|
|
2225
|
+
// Cleanup postMessage listener
|
|
2226
|
+
if (this.postMessageCleanup) {
|
|
2227
|
+
this.postMessageCleanup();
|
|
2228
|
+
this.postMessageCleanup = null;
|
|
2229
|
+
}
|
|
2103
2230
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2104
2231
|
try {
|
|
2105
2232
|
cleanup();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let a=e.querySelector(`.${t}`);a||(a=document.createElement("div"),a.className=t,e.appendChild(a)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(a,r);let s=a.querySelector(`.${n}`);return s||(s=document.createElement("div"),s.className=n,a.appendChild(s),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,s,a)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(s),{transformLayer:a,contentLayer:s}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function a(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function s(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const s=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:s,translateY:s},{scale:l,translateX:c,translateY:u}=i,d=a(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>a(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const a=null!==e.container.querySelector(".canvas-ruler");return o(a?t-r:t,a?n-r:n)}function g(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const f={width:8e3,height:8e3,enableAcceleration:!0,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,bindKeyboardEventsTo:"canvas",zoomSpeed:1.5,minZoom:.05,maxZoom:80,enableTransition:!0,transitionDuration:.2,enableAdaptiveSpeed:!0,enableLeftDrag:!0,enableMiddleDrag:!0,requireSpaceForMouseDrag:!1,keyboardPanStep:50,keyboardFastMultiplier:20,keyboardZoomStep:.2,enableClickToZoom:!0,clickZoomLevel:1,requireOptionForClickZoom:!1,enableRulers:!0,enableGrid:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",bindToWindow:!1,name:"markupCanvas",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},a=t.getBoundingClientRect(),i=a.width||t.clientWidth||0,l=a.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||f.width,m=n.height||f.height,g=function(e,t,n,r,a){const i=o(0,0,s(a.scale,a.translateX,a.translateY)),l=o(e,t,s(a.scale,a.translateX,a.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:g,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{width:0,height:0,contentWidth:0,contentHeight:0,scale:1,translateX:0,translateY:0,visibleArea:{x:0,y:0,width:0,height:0},scaledContentWidth:0,scaledContentHeight:0,canPanLeft:!1,canPanRight:!1,canPanUp:!1,canPanDown:!1,canZoomIn:!1,canZoomOut:!1}}}function v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=g(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.height&&"auto"===n.height&&console.error("MarkupCanvas: Container height is 0. Please set a height on your container element using CSS.","Examples: height: 100vh, height: 500px, or use flexbox/grid layout.",e),0===t.width&&"auto"===n.width&&console.error("MarkupCanvas: Container width is 0. Please set a width on your container element using CSS.","Examples: width: 100vw, width: 800px, or use flexbox/grid layout.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function x(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:a}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,s(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:a,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=s(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,s(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,a=this.config.height*r,s=(e.width-o)/2,i=(e.height-a)/2;return this.updateTransform({scale:r,translateX:s,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function C(e={}){const t={...f,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=f.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=f.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=f.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=f.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=f.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=f.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=f.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=f.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=f.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=f.rulerSize),t}class k{constructor(){this.listeners=new Map}setEmitInterceptor(e){this.emitInterceptor=e}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){this.emitInterceptor?.(e,t);const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function z(e,t){let n=0,r=0;function o(o){const a=e.container.getBoundingClientRect(),s=o.clientX-a.left,i=o.clientY-a.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,s,i,(e,t)=>{n=e,r=t})}function s(o){if(!(o instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;const s=o.shiftKey,l=t.keyboardPanStep*(s?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,a=i(n,r,e.transform,o,t);Object.assign(u,a),c=!0}break;case"g":case"G":e.toggleGrid&&e.toggleGrid(),c=!0;break;case"r":case"R":o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l="canvas"===t.bindKeyboardEventsTo?e.container:document;return l.addEventListener("keydown",s),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",s),e.container.removeEventListener("mousemove",o)}}function M(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),M(e,t,n,r,!1)}function E(e,t,n,r,o,a,s,i,l,c){a&&e.button===s&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,a){const s=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(s<T&&!o&&!a&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{clickX:s,clickY:i}=m(t,o,a,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.canvasToContent(s,i),c=r.width/2,u=r.height/2,d=n.clickZoomLevel,h={scale:d,translateX:c-l.x*d,translateY:u-l.y*d};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,a),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function R(e,t,n=!0){let r=!0,o=!1,a=0,s=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const g={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{a=e},setLastMouseY:e=>{s=e}},f=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!0),M(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!1),M(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed,setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})},v=n=>{!function(e,t,n,r,o,a){const s=0===e.button,i=1===e.button;if(s&&(a.setMouseDownTime(Date.now()),a.setMouseDownX(e.clientX),a.setMouseDownY(e.clientY),a.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(s&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),a.setDragButton(e.button),a.setLastMouseX(e.clientX),a.setLastMouseY(e.clientY),M(t,n,r,o,!1))}(n,e,t,r,l,g)},y=t=>{!function(e,t,n,r,o,a,s,i,l,c){if(o>0){const t=Math.abs(e.clientX-a),o=Math.abs(e.clientY-s);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const a=o.clientX-i,s=o.clientY-l,u={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,a,s,{setHasDragged:g.setHasDragged,setIsDragging:g.setIsDragging,setLastMouseX:g.setLastMouseX,setLastMouseY:g.setLastMouseY})},b=n=>{E(n,e,t,r,l,o,i,c,m,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton,setMouseDownTime:g.setMouseDownTime,setHasDragged:g.setHasDragged})},w=()=>{!function(e,t,n,r,o,a){o&&L(e,t,n,r,a)}(e,t,r,l,o,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",f),document.addEventListener("keyup",p)),M(e,t,r,l,o);const x=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",f),document.removeEventListener("keyup",p))};return n?{cleanup:x,enable:()=>(r=!0,M(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton}),M(e,t,r,l,o),!0),isEnabled:()=>r}:x}function Y(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function X(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function B(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function F(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,a={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(a)}}else if(2===r.length){const e=X(r[0],r[1]),o=Y(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,a=t.container.getBoundingClientRect();let s=o.x-a.left,i=o.y-a.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:s,y:i},e=>e);s=l.x,i=l.y,B(t,r,s,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function $(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=X(t.touches[0],t.touches[1]),t.lastCenter=Y(t.touches[0],t.touches[1]))}(e,t)},r=n=>{F(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function P(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function I(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,a=n.deltaY*r,s={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-a};return y(e.transformLayer,e.config),e.updateTransform(s)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>P(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{mouseX:s,mouseY:i}=m(t,o,a,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=P(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return B(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),s,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const Z=100,O=1e3,_=1001,A=4,G=4,H=100,N=100,q=20,K=200;function V(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${_};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),a=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${Z};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),a&&e.appendChild(a),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:a}}function W(e,t){const n=e/Math.max(5,Math.min(20,t/50)),r=10**Math.floor(Math.log10(n)),o=n/r;let a;return a=o<=1?1:o<=2?2:o<=5?5:10,a*r}function U(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${A}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%H===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${A+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function j(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${G}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%H===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\ttop: ${n-6}px;\n\t\t\tright: ${G+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t\ttransform: rotate(-90deg);\n\t\t\ttransform-origin: right center;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const a=e.getBounds(),s=a.scale||1,i=a.translateX||0,l=a.translateY||0,c=a.width-o.rulerSize,u=a.height-o.rulerSize,d=-i/s,h=-l/s,m=h+u/s;!function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&U(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/s,c,s,o),function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&j(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,s,o),r&&function(e,t,n,r){let o=N*t;for(;o<q;)o*=2;for(;o>K;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,s,i,l)}function Q(e,t){const n=g(t,"rulerBackgroundColor"),r=g(t,"rulerBorderColor"),o=g(t,"rulerTextColor"),a=g(t,"rulerTickColor"),s=g(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",a)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",a)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",s)}function ee(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const a=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&J(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=V(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,a),Q(n,t),a(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:a,updateTheme:e=>{o||Q(n,e)},show:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="block"),n.verticalRuler&&(n.verticalRuler.style.display="block"),n.cornerBox&&(n.cornerBox.style.display="flex"),n.gridOverlay&&(n.gridOverlay.style.display="block")},hide:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="none"),n.verticalRuler&&(n.verticalRuler.style.display="none"),n.cornerBox&&(n.cornerBox.style.display="none"),n.gridOverlay&&(n.gridOverlay.style.display="none")},toggleGrid:()=>{if(n.gridOverlay){const e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"none":"block"}},destroy:()=>{o=!0,r&&r(),n.horizontalRuler?.parentNode&&n.horizontalRuler.parentNode.removeChild(n.horizontalRuler),n.verticalRuler?.parentNode&&n.verticalRuler.parentNode.removeChild(n.verticalRuler),n.cornerBox?.parentNode&&n.cornerBox.parentNode.removeChild(n.cornerBox),n.gridOverlay?.parentNode&&n.gridOverlay.parentNode.removeChild(n.gridOverlay)}}}catch(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,!e)throw new Error("Container element is required");this.config=C(t);const n=x(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(console.log("bindToWindow:","true"),this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding()),this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;console.log(t),t[e]=this,console.log(t[e]),t.__markupCanvasInstances||(t.__markupCanvasInstances=new Map),t.__markupCanvasInstances.set(e,this)}cleanupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;delete t[e],t.__markupCanvasInstances&&t.__markupCanvasInstances.delete(e)}broadcastEvent(e,t){if("undefined"==typeof window)return;let n=t;"ready"===e&&(n={ready:!0}),window.postMessage({source:"markup-canvas",event:e,data:n,timestamp:Date.now(),canvasName:this.config.name},"*")}setupEventHandlers(){try{u(this.config,"enableZoom",()=>{const e=I(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=R(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=z(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=$(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=ee(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}get container(){return this.baseCanvas.container}get transformLayer(){return this.baseCanvas.transformLayer}get contentLayer(){return this.baseCanvas.contentLayer}get transform(){return this.baseCanvas.transform}get isReady(){return this._isReady}get isTransforming(){return this.dragSetup?.isEnabled()||!1}get visibleBounds(){return this.getVisibleArea()}getBounds(){return this.baseCanvas.getBounds()}updateTransform(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};return this.updateTransform(n)}))}resetZoom(){return this.resetView()}enableMouseDrag(){return this.dragSetup?.enable()??!1}disableMouseDrag(){return this.dragSetup?.disable()??!1}isMouseDragEnabled(){return this.dragSetup?.isEnabled()??!1}toggleGrid(){return!!this.rulers?.toggleGrid&&(this.rulers.toggleGrid(),!0)}showGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="block",!0)}hideGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="none",!0)}isGridVisible(){return!!this.rulers?.gridOverlay&&"none"!==this.rulers.gridOverlay.style.display}toggleRulers(){if(this.rulers){return this.areRulersVisible()?this.rulers.hide():this.rulers.show(),!0}return!1}showRulers(){return!!this.rulers&&(this.rulers.show(),!0)}hideRulers(){return!!this.rulers&&(this.rulers.hide(),!0)}areRulersVisible(){return!!this.rulers?.horizontalRuler&&"none"!==this.rulers.horizontalRuler.style.display}centerContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,a=r-e*this.baseCanvas.transform.scale,s=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:a,translateY:s})})}getConfig(){return{...this.config}}updateConfig(e){this.config=C({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=C(t);const n=g(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupGlobalBinding(),this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let s=e.querySelector(`.${t}`);s||(s=document.createElement("div"),s.className=t,e.appendChild(s)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(s,r);let a=s.querySelector(`.${n}`);return a||(a=document.createElement("div"),a.className=n,s.appendChild(a),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,a,s)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(a),{transformLayer:s,contentLayer:a}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function s(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function a(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const a=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:a,translateY:a},{scale:l,translateX:c,translateY:u}=i,d=s(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>s(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const s=null!==e.container.querySelector(".canvas-ruler");return o(s?t-r:t,s?n-r:n)}function f(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const g={width:8e3,height:8e3,enableAcceleration:!0,bindToWindow:!1,name:"markupCanvas",enablePostMessageAPI:!1,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,bindKeyboardEventsTo:"canvas",zoomSpeed:1.5,minZoom:.05,maxZoom:80,enableTransition:!0,transitionDuration:.2,enableAdaptiveSpeed:!0,enableLeftDrag:!0,enableMiddleDrag:!0,requireSpaceForMouseDrag:!1,keyboardPanStep:50,keyboardFastMultiplier:20,keyboardZoomStep:.2,enableClickToZoom:!0,clickZoomLevel:1,requireOptionForClickZoom:!1,enableRulers:!0,enableGrid:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},s=t.getBoundingClientRect(),i=s.width||t.clientWidth||0,l=s.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||g.width,m=n.height||g.height,f=function(e,t,n,r,s){const i=o(0,0,a(s.scale,s.translateX,s.translateY)),l=o(e,t,a(s.scale,s.translateX,s.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:f,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{width:0,height:0,contentWidth:0,contentHeight:0,scale:1,translateX:0,translateY:0,visibleArea:{x:0,y:0,width:0,height:0},scaledContentWidth:0,scaledContentHeight:0,canPanLeft:!1,canPanRight:!1,canPanUp:!1,canPanDown:!1,canZoomIn:!1,canZoomOut:!1}}}function v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=f(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.height&&"auto"===n.height&&console.error("MarkupCanvas: Container height is 0. Please set a height on your container element using CSS.","Examples: height: 100vh, height: 500px, or use flexbox/grid layout.",e),0===t.width&&"auto"===n.width&&console.error("MarkupCanvas: Container width is 0. Please set a width on your container element using CSS.","Examples: width: 100vw, width: 800px, or use flexbox/grid layout.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function C(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:s}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,a(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:s,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=a(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,a(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,s=this.config.height*r,a=(e.width-o)/2,i=(e.height-s)/2;return this.updateTransform({scale:r,translateX:a,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function x(e={}){const t={...g,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=g.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=g.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=g.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=g.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=g.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=g.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=g.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=g.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=g.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=g.rulerSize),t}class k{constructor(){this.listeners=new Map}setEmitInterceptor(e){this.emitInterceptor=e}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){this.emitInterceptor?.(e,t);const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function M(e,t){let n=0,r=0;function o(o){const s=e.container.getBoundingClientRect(),a=o.clientX-s.left,i=o.clientY-s.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,a,i,(e,t)=>{n=e,r=t})}function a(o){if(!(o instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;const a=o.shiftKey,l=t.keyboardPanStep*(a?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=s(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=s(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,s=i(n,r,e.transform,o,t);Object.assign(u,s),c=!0}break;case"g":case"G":o.shiftKey&&e.toggleGrid&&e.toggleGrid(),c=o.shiftKey;break;case"r":case"R":!o.shiftKey||o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l="canvas"===t.bindKeyboardEventsTo?e.container:document;return l.addEventListener("keydown",a),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",a),e.container.removeEventListener("mousemove",o)}}function z(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),z(e,t,n,r,!1)}function R(e,t,n,r,o,s,a,i,l,c){s&&e.button===a&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,s){const a=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(a<T&&!o&&!s&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,s=e.clientY-r.top,{clickX:a,clickY:i}=m(t,o,s,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.canvasToContent(a,i),c=r.width/2,u=r.height/2,d=n.clickZoomLevel,h={scale:d,translateX:c-l.x*d,translateY:u-l.y*d};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,s),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function E(e,t,n=!0){let r=!0,o=!1,s=0,a=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const f={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{s=e},setLastMouseY:e=>{a=e}},g=n=>{!function(e,t,n,r,o,s){n.requireSpaceForMouseDrag&&" "===e.key&&(s.setIsSpacePressed(!0),z(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,s){n.requireSpaceForMouseDrag&&" "===e.key&&(s.setIsSpacePressed(!1),z(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:s.setIsDragging,setDragButton:s.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed,setIsDragging:f.setIsDragging,setDragButton:f.setDragButton})},v=n=>{!function(e,t,n,r,o,s){const a=0===e.button,i=1===e.button;if(a&&(s.setMouseDownTime(Date.now()),s.setMouseDownX(e.clientX),s.setMouseDownY(e.clientY),s.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(a&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),s.setDragButton(e.button),s.setLastMouseX(e.clientX),s.setLastMouseY(e.clientY),z(t,n,r,o,!1))}(n,e,t,r,l,f)},y=t=>{!function(e,t,n,r,o,s,a,i,l,c){if(o>0){const t=Math.abs(e.clientX-s),o=Math.abs(e.clientY-a);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const s=o.clientX-i,a=o.clientY-l,u={translateX:t.transform.translateX+s,translateY:t.transform.translateY+a};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,s,a,{setHasDragged:f.setHasDragged,setIsDragging:f.setIsDragging,setLastMouseX:f.setLastMouseX,setLastMouseY:f.setLastMouseY})},b=n=>{R(n,e,t,r,l,o,i,c,m,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton,setMouseDownTime:f.setMouseDownTime,setHasDragged:f.setHasDragged})},w=()=>{!function(e,t,n,r,o,s){o&&L(e,t,n,r,s)}(e,t,r,l,o,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",g),document.addEventListener("keyup",p)),z(e,t,r,l,o);const C=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",g),document.removeEventListener("keyup",p))};return n?{cleanup:C,enable:()=>(r=!0,z(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:f.setIsDragging,setDragButton:f.setDragButton}),z(e,t,r,l,o),!0),isEnabled:()=>r}:C}function Y(e){const t=t=>{const n=t.data;if("markup-canvas"!==n.source)return;const r=e.config.name||"markupCanvas";if(n.canvasName!==r)return;const o=n.action,s=n.args||[];try{if("zoomIn"===o)e.zoomIn(s[0]);else if("zoomOut"===o)e.zoomOut(s[0]);else if("setZoom"===o){const t=s[0];if("number"!=typeof t||t<=0)throw new Error(`Invalid zoom level: ${t}. Must be a positive number.`);e.setZoom(t)}else if("resetZoom"===o)e.resetZoom();else if("panLeft"===o)e.panLeft(s[0]);else if("panRight"===o)e.panRight(s[0]);else if("panUp"===o)e.panUp(s[0]);else if("panDown"===o)e.panDown(s[0]);else if("fitToScreen"===o)e.fitToScreen();else if("centerContent"===o)e.centerContent();else if("scrollToPoint"===o)e.scrollToPoint(s[0],s[1]);else if("resetView"===o)e.resetView();else if("toggleRulers"===o)e.toggleRulers();else if("showRulers"===o)e.showRulers();else if("hideRulers"===o)e.hideRulers();else if("toggleGrid"===o)e.toggleGrid();else if("showGrid"===o)e.showGrid();else if("hideGrid"===o)e.hideGrid();else if("updateThemeMode"===o){const t=s[0];if("light"!==t&&"dark"!==t)throw new Error(`Invalid theme mode: ${t}`);e.updateThemeMode(t)}else{if("toggleThemeMode"!==o)throw new Error(`Unknown action: ${o}`);{const t="light"===e.getConfig().themeMode?"dark":"light";e.updateThemeMode(t)}}}catch(e){!function(e,t,n){window.postMessage({source:"markup-canvas-error",canvasName:e,action:t,error:n,timestamp:Date.now()},"*")}(r,o,e instanceof Error?e.message:String(e))}};return"undefined"!=typeof window&&window.addEventListener("message",t),()=>{"undefined"!=typeof window&&window.removeEventListener("message",t)}}function X(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function B(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function F(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function $(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,s={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(s)}}else if(2===r.length){const e=B(r[0],r[1]),o=X(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,s=t.container.getBoundingClientRect();let a=o.x-s.left,i=o.y-s.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:a,y:i},e=>e);a=l.x,i=l.y,F(t,r,a,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function P(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=B(t.touches[0],t.touches[1]),t.lastCenter=X(t.touches[0],t.touches[1]))}(e,t)},r=n=>{$(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function I(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function Z(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,s=n.deltaY*r,a={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-s};return y(e.transformLayer,e.config),e.updateTransform(a)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>I(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,s=e.clientY-r.top,{mouseX:a,mouseY:i}=m(t,o,s,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=I(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return F(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),a,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const O=100,A=1e3,_=1001,G=4,H=4,N=100,K=100,q=20,V=200;function U(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${A};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${A};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${_};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),s=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${O};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),s&&e.appendChild(s),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:s}}function W(e,t){const n=e/Math.max(5,Math.min(20,t/50)),r=10**Math.floor(Math.log10(n)),o=n/r;let s;return s=o<=1?1:o<=2?2:o<=5?5:10,s*r}function j(e,t,n,r,o){const s=document.createElement("div");s.className="tick",s.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${G}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(s);if(t%N===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${G+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const s=document.createElement("div");s.className="tick",s.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${H}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(s);if(t%N===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\ttop: ${n-6}px;\n\t\t\tright: ${H+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t\ttransform: rotate(-90deg);\n\t\t\ttransform-origin: right center;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function Q(e,t,n,r,o){const s=e.getBounds(),a=s.scale||1,i=s.translateX||0,l=s.translateY||0,c=s.width-o.rulerSize,u=s.height-o.rulerSize,d=-i/a,h=-l/a,m=h+u/a;!function(e,t,n,r,o,s){const a=r,i=W(n-t,a),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=a+50&&j(l,e,n,0,s)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/a,c,a,o),function(e,t,n,r,o,s){const a=r,i=W(n-t,a),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=a+50&&J(l,e,n,0,s)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,a,o),r&&function(e,t,n,r){let o=K*t;for(;o<q;)o*=2;for(;o>V;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,a,i,l)}function ee(e,t){const n=f(t,"rulerBackgroundColor"),r=f(t,"rulerBorderColor"),o=f(t,"rulerTextColor"),s=f(t,"rulerTickColor"),a=f(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",s)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",s)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",a)}function te(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const s=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&Q(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=U(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,s),ee(n,t),s(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:s,updateTheme:e=>{o||ee(n,e)},show:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="block"),n.verticalRuler&&(n.verticalRuler.style.display="block"),n.cornerBox&&(n.cornerBox.style.display="flex"),n.gridOverlay&&(n.gridOverlay.style.display="block")},hide:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="none"),n.verticalRuler&&(n.verticalRuler.style.display="none"),n.cornerBox&&(n.cornerBox.style.display="none"),n.gridOverlay&&(n.gridOverlay.style.display="none")},toggleGrid:()=>{if(n.gridOverlay){const e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"none":"block"}},destroy:()=>{o=!0,r&&r(),n.horizontalRuler?.parentNode&&n.horizontalRuler.parentNode.removeChild(n.horizontalRuler),n.verticalRuler?.parentNode&&n.verticalRuler.parentNode.removeChild(n.verticalRuler),n.cornerBox?.parentNode&&n.cornerBox.parentNode.removeChild(n.cornerBox),n.gridOverlay?.parentNode&&n.gridOverlay.parentNode.removeChild(n.gridOverlay)}}}catch(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,this.postMessageCleanup=null,!e)throw new Error("Container element is required");this.config=x(t);const n=C(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(console.log("bindToWindow:","true"),this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding(),this.config.enablePostMessageAPI&&(this.postMessageCleanup=Y(this))),this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;console.log(t),t[e]=this,console.log(t[e]),t.__markupCanvasInstances||(t.__markupCanvasInstances=new Map),t.__markupCanvasInstances.set(e,this)}cleanupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;delete t[e],t.__markupCanvasInstances&&t.__markupCanvasInstances.delete(e)}broadcastEvent(e,t){if("undefined"==typeof window)return;let n=t;"ready"===e&&(n={ready:!0}),window.postMessage({source:"markup-canvas",event:e,data:n,timestamp:Date.now(),canvasName:this.config.name},"*")}setupEventHandlers(){try{u(this.config,"enableZoom",()=>{const e=Z(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=E(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=M(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=P(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=te(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}get container(){return this.baseCanvas.container}get transformLayer(){return this.baseCanvas.transformLayer}get contentLayer(){return this.baseCanvas.contentLayer}get transform(){return this.baseCanvas.transform}get isReady(){return this._isReady}get isTransforming(){return this.dragSetup?.isEnabled()||!1}get visibleBounds(){return this.getVisibleArea()}getBounds(){return this.baseCanvas.getBounds()}updateTransform(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};return this.updateTransform(n)}))}resetZoom(){return this.resetView()}enableMouseDrag(){return this.dragSetup?.enable()??!1}disableMouseDrag(){return this.dragSetup?.disable()??!1}isMouseDragEnabled(){return this.dragSetup?.isEnabled()??!1}toggleGrid(){return!!this.rulers?.toggleGrid&&(this.rulers.toggleGrid(),!0)}showGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="block",!0)}hideGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="none",!0)}isGridVisible(){return!!this.rulers?.gridOverlay&&"none"!==this.rulers.gridOverlay.style.display}toggleRulers(){if(this.rulers){return this.areRulersVisible()?this.rulers.hide():this.rulers.show(),!0}return!1}showRulers(){return!!this.rulers&&(this.rulers.show(),!0)}hideRulers(){return!!this.rulers&&(this.rulers.hide(),!0)}areRulersVisible(){return!!this.rulers?.horizontalRuler&&"none"!==this.rulers.horizontalRuler.style.display}centerContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,s=r-e*this.baseCanvas.transform.scale,a=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:s,translateY:a})})}getConfig(){return{...this.config}}updateConfig(e){this.config=x({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=x(t);const n=f(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupGlobalBinding(),this.postMessageCleanup&&(this.postMessageCleanup(),this.postMessageCleanup=null),this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
|
package/dist/types/config.d.ts
CHANGED
package/dist/types/events.d.ts
CHANGED
|
@@ -31,3 +31,10 @@ export interface MouseDragControls {
|
|
|
31
31
|
disable: () => boolean;
|
|
32
32
|
isEnabled: () => boolean;
|
|
33
33
|
}
|
|
34
|
+
export type PostMessageAction = "zoomIn" | "zoomOut" | "setZoom" | "resetZoom" | "panLeft" | "panRight" | "panUp" | "panDown" | "fitToScreen" | "centerContent" | "scrollToPoint" | "resetView" | "toggleRulers" | "showRulers" | "hideRulers" | "toggleGrid" | "showGrid" | "hideGrid" | "updateThemeMode" | "toggleThemeMode";
|
|
35
|
+
export interface PostMessageRequest {
|
|
36
|
+
source: "markup-canvas";
|
|
37
|
+
canvasName: string;
|
|
38
|
+
action: PostMessageAction;
|
|
39
|
+
args?: unknown[];
|
|
40
|
+
}
|
package/package.json
CHANGED
package/src/lib/MarkupCanvas.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createCanvas } from "@/lib/canvas/index.js";
|
|
2
2
|
import { createMarkupCanvasConfig } from "@/lib/config/createMarkupCanvasConfig.js";
|
|
3
3
|
import { EventEmitter } from "@/lib/events/EventEmitter.js";
|
|
4
|
-
import { setupKeyboardEvents, setupMouseEvents, setupTouchEvents, setupWheelEvents } from "@/lib/events/index.js";
|
|
4
|
+
import { setupKeyboardEvents, setupMouseEvents, setupPostMessageEvents, setupTouchEvents, setupWheelEvents } from "@/lib/events/index.js";
|
|
5
5
|
import { getThemeValue, withClampedZoom, withFeatureEnabled } from "@/lib/helpers/index.js";
|
|
6
6
|
import { createRulers } from "@/lib/rulers/index.js";
|
|
7
7
|
import { withTransition } from "@/lib/transition/withTransition.js";
|
|
@@ -29,6 +29,7 @@ export class MarkupCanvas implements Canvas {
|
|
|
29
29
|
public config: Required<MarkupCanvasConfig>;
|
|
30
30
|
private _isReady = false;
|
|
31
31
|
private listen = new EventEmitter<MarkupCanvasEvents>();
|
|
32
|
+
private postMessageCleanup: (() => void) | null = null;
|
|
32
33
|
|
|
33
34
|
constructor(container: HTMLElement, options: MarkupCanvasConfig = {}) {
|
|
34
35
|
if (!container) {
|
|
@@ -50,6 +51,11 @@ export class MarkupCanvas implements Canvas {
|
|
|
50
51
|
this.broadcastEvent(event as string, data);
|
|
51
52
|
});
|
|
52
53
|
this.setupGlobalBinding();
|
|
54
|
+
|
|
55
|
+
// Set up postMessage listener
|
|
56
|
+
if (this.config.enablePostMessageAPI) {
|
|
57
|
+
this.postMessageCleanup = setupPostMessageEvents(this);
|
|
58
|
+
}
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
this.setupEventHandlers();
|
|
@@ -482,6 +488,13 @@ export class MarkupCanvas implements Canvas {
|
|
|
482
488
|
// Cleanup method
|
|
483
489
|
cleanup(): void {
|
|
484
490
|
this.cleanupGlobalBinding();
|
|
491
|
+
|
|
492
|
+
// Cleanup postMessage listener
|
|
493
|
+
if (this.postMessageCleanup) {
|
|
494
|
+
this.postMessageCleanup();
|
|
495
|
+
this.postMessageCleanup = null;
|
|
496
|
+
}
|
|
497
|
+
|
|
485
498
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
486
499
|
try {
|
|
487
500
|
cleanup();
|
|
@@ -6,6 +6,11 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
|
|
|
6
6
|
height: 8000,
|
|
7
7
|
enableAcceleration: true,
|
|
8
8
|
|
|
9
|
+
// Global Binding & Instance Access
|
|
10
|
+
bindToWindow: false,
|
|
11
|
+
name: "markupCanvas",
|
|
12
|
+
enablePostMessageAPI: false,
|
|
13
|
+
|
|
9
14
|
// Interaction controls
|
|
10
15
|
enableZoom: true,
|
|
11
16
|
enablePan: true,
|
|
@@ -67,10 +72,6 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
|
|
|
67
72
|
// Theme
|
|
68
73
|
themeMode: "light",
|
|
69
74
|
|
|
70
|
-
// Global Binding & Instance Access
|
|
71
|
-
bindToWindow: false,
|
|
72
|
-
name: "markupCanvas",
|
|
73
|
-
|
|
74
75
|
// Callbacks
|
|
75
76
|
onTransformUpdate: () => {},
|
|
76
77
|
};
|
package/src/lib/events/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { setupKeyboardEvents } from "./keyboard/setupKeyboardEvents.js";
|
|
2
2
|
export { setupMouseEvents } from "./mouse/setupMouseEvents.js";
|
|
3
|
+
export { setupPostMessageEvents } from "./postMessage/setupPostMessageEvents.js";
|
|
3
4
|
export { setupTouchEvents } from "./touch/setupTouchEvents.js";
|
|
4
5
|
export { detectTrackpadGesture } from "./trackpad/detectTrackpadGesture.js";
|
|
5
6
|
export { getAdaptiveZoomSpeed } from "./utils/getAdaptiveZoomSpeed.js";
|
|
@@ -80,14 +80,14 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
80
80
|
break;
|
|
81
81
|
case "g":
|
|
82
82
|
case "G":
|
|
83
|
-
if (canvas.toggleGrid) {
|
|
83
|
+
if (event.shiftKey && canvas.toggleGrid) {
|
|
84
84
|
canvas.toggleGrid();
|
|
85
85
|
}
|
|
86
|
-
handled =
|
|
86
|
+
handled = event.shiftKey;
|
|
87
87
|
break;
|
|
88
88
|
case "r":
|
|
89
89
|
case "R":
|
|
90
|
-
if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
90
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
91
91
|
canvas.toggleRulers();
|
|
92
92
|
handled = true;
|
|
93
93
|
}
|
|
@@ -81,14 +81,14 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
81
81
|
break;
|
|
82
82
|
case "g":
|
|
83
83
|
case "G":
|
|
84
|
-
if (canvas.toggleGrid) {
|
|
84
|
+
if (event.shiftKey && canvas.toggleGrid) {
|
|
85
85
|
canvas.toggleGrid();
|
|
86
86
|
}
|
|
87
|
-
handled =
|
|
87
|
+
handled = event.shiftKey;
|
|
88
88
|
break;
|
|
89
89
|
case "r":
|
|
90
90
|
case "R":
|
|
91
|
-
if (!event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
91
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
92
92
|
canvas.toggleRulers();
|
|
93
93
|
handled = true;
|
|
94
94
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { MarkupCanvas } from "@/lib/MarkupCanvas.js";
|
|
2
|
+
import type { PostMessageAction, PostMessageRequest } from "@/types/events";
|
|
3
|
+
import { sendPostMessageError } from "./sendError";
|
|
4
|
+
|
|
5
|
+
export function setupPostMessageEvents(canvas: MarkupCanvas): () => void {
|
|
6
|
+
const handleMessage = (event: MessageEvent): void => {
|
|
7
|
+
const data = event.data as PostMessageRequest;
|
|
8
|
+
|
|
9
|
+
// Validate message structure
|
|
10
|
+
if (data.source !== "markup-canvas") {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const canvasName = canvas.config.name || "markupCanvas";
|
|
15
|
+
if (data.canvasName !== canvasName) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const action = data.action as PostMessageAction;
|
|
20
|
+
const args = data.args || [];
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// View methods
|
|
24
|
+
if (action === "zoomIn") {
|
|
25
|
+
canvas.zoomIn(args[0] as number | undefined);
|
|
26
|
+
} else if (action === "zoomOut") {
|
|
27
|
+
canvas.zoomOut(args[0] as number | undefined);
|
|
28
|
+
} else if (action === "setZoom") {
|
|
29
|
+
const zoomLevel = args[0] as number;
|
|
30
|
+
if (typeof zoomLevel !== "number" || zoomLevel <= 0) {
|
|
31
|
+
throw new Error(`Invalid zoom level: ${zoomLevel}. Must be a positive number.`);
|
|
32
|
+
}
|
|
33
|
+
canvas.setZoom(zoomLevel);
|
|
34
|
+
} else if (action === "resetZoom") {
|
|
35
|
+
canvas.resetZoom();
|
|
36
|
+
} else if (action === "panLeft") {
|
|
37
|
+
canvas.panLeft(args[0] as number | undefined);
|
|
38
|
+
} else if (action === "panRight") {
|
|
39
|
+
canvas.panRight(args[0] as number | undefined);
|
|
40
|
+
} else if (action === "panUp") {
|
|
41
|
+
canvas.panUp(args[0] as number | undefined);
|
|
42
|
+
} else if (action === "panDown") {
|
|
43
|
+
canvas.panDown(args[0] as number | undefined);
|
|
44
|
+
} else if (action === "fitToScreen") {
|
|
45
|
+
canvas.fitToScreen();
|
|
46
|
+
} else if (action === "centerContent") {
|
|
47
|
+
canvas.centerContent();
|
|
48
|
+
} else if (action === "scrollToPoint") {
|
|
49
|
+
canvas.scrollToPoint(args[0] as number, args[1] as number);
|
|
50
|
+
} else if (action === "resetView") {
|
|
51
|
+
canvas.resetView();
|
|
52
|
+
}
|
|
53
|
+
// Ruler/Grid methods
|
|
54
|
+
else if (action === "toggleRulers") {
|
|
55
|
+
canvas.toggleRulers();
|
|
56
|
+
} else if (action === "showRulers") {
|
|
57
|
+
canvas.showRulers();
|
|
58
|
+
} else if (action === "hideRulers") {
|
|
59
|
+
canvas.hideRulers();
|
|
60
|
+
} else if (action === "toggleGrid") {
|
|
61
|
+
canvas.toggleGrid();
|
|
62
|
+
} else if (action === "showGrid") {
|
|
63
|
+
canvas.showGrid();
|
|
64
|
+
} else if (action === "hideGrid") {
|
|
65
|
+
canvas.hideGrid();
|
|
66
|
+
}
|
|
67
|
+
// Config methods
|
|
68
|
+
else if (action === "updateThemeMode") {
|
|
69
|
+
const mode = args[0] as "light" | "dark";
|
|
70
|
+
if (mode !== "light" && mode !== "dark") {
|
|
71
|
+
throw new Error(`Invalid theme mode: ${mode}`);
|
|
72
|
+
}
|
|
73
|
+
canvas.updateThemeMode(mode);
|
|
74
|
+
} else if (action === "toggleThemeMode") {
|
|
75
|
+
const currentConfig = canvas.getConfig();
|
|
76
|
+
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
77
|
+
canvas.updateThemeMode(newMode);
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error(`Unknown action: ${action}`);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
83
|
+
sendPostMessageError(canvasName, action, errorMessage);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (typeof window !== "undefined") {
|
|
88
|
+
window.addEventListener("message", handleMessage);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
if (typeof window !== "undefined") {
|
|
93
|
+
window.removeEventListener("message", handleMessage);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
package/src/types/config.ts
CHANGED
package/src/types/events.ts
CHANGED
|
@@ -29,3 +29,32 @@ export interface MouseDragControls {
|
|
|
29
29
|
disable: () => boolean;
|
|
30
30
|
isEnabled: () => boolean;
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
export type PostMessageAction =
|
|
34
|
+
| "zoomIn"
|
|
35
|
+
| "zoomOut"
|
|
36
|
+
| "setZoom"
|
|
37
|
+
| "resetZoom"
|
|
38
|
+
| "panLeft"
|
|
39
|
+
| "panRight"
|
|
40
|
+
| "panUp"
|
|
41
|
+
| "panDown"
|
|
42
|
+
| "fitToScreen"
|
|
43
|
+
| "centerContent"
|
|
44
|
+
| "scrollToPoint"
|
|
45
|
+
| "resetView"
|
|
46
|
+
| "toggleRulers"
|
|
47
|
+
| "showRulers"
|
|
48
|
+
| "hideRulers"
|
|
49
|
+
| "toggleGrid"
|
|
50
|
+
| "showGrid"
|
|
51
|
+
| "hideGrid"
|
|
52
|
+
| "updateThemeMode"
|
|
53
|
+
| "toggleThemeMode";
|
|
54
|
+
|
|
55
|
+
export interface PostMessageRequest {
|
|
56
|
+
source: "markup-canvas";
|
|
57
|
+
canvasName: string;
|
|
58
|
+
action: PostMessageAction;
|
|
59
|
+
args?: unknown[];
|
|
60
|
+
}
|