@markup-canvas/core 1.1.6 → 1.1.7
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/dist/lib/MarkupCanvas.d.ts +1 -0
- package/dist/lib/events/utils/getViewportCenter.d.ts +5 -0
- package/dist/markup-canvas.cjs.js +58 -53
- package/dist/markup-canvas.esm.js +58 -53
- package/dist/markup-canvas.umd.js +56 -51
- package/dist/markup-canvas.umd.min.js +1 -1
- package/dist/types/canvas.d.ts +1 -0
- package/package.json +1 -1
- package/src/lib/MarkupCanvas.ts +30 -16
- package/src/lib/config/presets/editor-preset.ts +2 -2
- package/src/lib/events/keyboard/setupKeyboardEvents.ts +15 -36
- package/src/lib/events/utils/getViewportCenter.ts +14 -0
- package/src/types/canvas.ts +1 -0
- package/dist/lib/events/keyboard/setupKeyboardNavigation.d.ts +0 -2
- package/src/lib/events/keyboard/setupKeyboardNavigation.ts +0 -115
|
@@ -42,6 +42,7 @@ export declare class MarkupCanvas implements Canvas {
|
|
|
42
42
|
};
|
|
43
43
|
zoomToPoint(x: number, y: number, targetScale: number): boolean;
|
|
44
44
|
resetView(): boolean;
|
|
45
|
+
resetViewToCenter(): boolean;
|
|
45
46
|
zoomToFitContent(): boolean;
|
|
46
47
|
panLeft(distance?: number): boolean;
|
|
47
48
|
panRight(distance?: number): boolean;
|
|
@@ -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.7
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -20,8 +20,8 @@ const EDITOR_PRESET = {
|
|
|
20
20
|
enableZoom: true,
|
|
21
21
|
enablePan: true,
|
|
22
22
|
enableTouch: true,
|
|
23
|
-
enableKeyboard:
|
|
24
|
-
bindKeyboardEventsTo: "
|
|
23
|
+
enableKeyboard: true,
|
|
24
|
+
bindKeyboardEventsTo: "document",
|
|
25
25
|
// Zoom behavior
|
|
26
26
|
zoomSpeed: 1.5,
|
|
27
27
|
minZoom: 0.05,
|
|
@@ -276,13 +276,6 @@ function withRulerSize(canvas, rulerSize, operation) {
|
|
|
276
276
|
const finalRulerSize = hasRulers ? rulerSize : 0;
|
|
277
277
|
return operation(finalRulerSize);
|
|
278
278
|
}
|
|
279
|
-
function withRulerOffsets(canvas, rulerSize, x, y, operation) {
|
|
280
|
-
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
281
|
-
const adjustedX = x - rulerSize;
|
|
282
|
-
const adjustedY = y - rulerSize;
|
|
283
|
-
return operation(adjustedX, adjustedY);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
279
|
function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
|
|
287
280
|
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
288
281
|
const adjusted = {
|
|
@@ -776,42 +769,28 @@ function getAdaptiveZoomSpeed(canvas, baseSpeed) {
|
|
|
776
769
|
}
|
|
777
770
|
|
|
778
771
|
function setupKeyboardEvents(canvas, config) {
|
|
779
|
-
// Track mouse position
|
|
780
|
-
let lastMouseX = 0;
|
|
781
|
-
let lastMouseY = 0;
|
|
782
|
-
function handleMouseMove(event) {
|
|
783
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
784
|
-
const rawMouseX = event.clientX - rect.left;
|
|
785
|
-
const rawMouseY = event.clientY - rect.top;
|
|
786
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
787
|
-
lastMouseX = adjustedX;
|
|
788
|
-
lastMouseY = adjustedY;
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
772
|
function handleKeyDown(event) {
|
|
792
773
|
if (!(event instanceof KeyboardEvent))
|
|
793
774
|
return;
|
|
794
775
|
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
795
776
|
return;
|
|
796
|
-
const isFastPan = event.shiftKey;
|
|
797
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
798
777
|
let handled = false;
|
|
799
778
|
const newTransform = {};
|
|
800
779
|
switch (event.key) {
|
|
801
780
|
case "ArrowLeft":
|
|
802
|
-
newTransform.translateX = canvas.transform.translateX +
|
|
781
|
+
newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
|
|
803
782
|
handled = true;
|
|
804
783
|
break;
|
|
805
784
|
case "ArrowRight":
|
|
806
|
-
newTransform.translateX = canvas.transform.translateX -
|
|
785
|
+
newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
|
|
807
786
|
handled = true;
|
|
808
787
|
break;
|
|
809
788
|
case "ArrowUp":
|
|
810
|
-
newTransform.translateY = canvas.transform.translateY +
|
|
789
|
+
newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
|
|
811
790
|
handled = true;
|
|
812
791
|
break;
|
|
813
792
|
case "ArrowDown":
|
|
814
|
-
newTransform.translateY = canvas.transform.translateY -
|
|
793
|
+
newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
|
|
815
794
|
handled = true;
|
|
816
795
|
break;
|
|
817
796
|
case "=":
|
|
@@ -820,7 +799,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
820
799
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
821
800
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
822
801
|
: config.keyboardZoomStep;
|
|
823
|
-
|
|
802
|
+
canvas.zoomIn(adaptiveZoomStep);
|
|
824
803
|
handled = true;
|
|
825
804
|
}
|
|
826
805
|
break;
|
|
@@ -829,16 +808,21 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
829
808
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
830
809
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
831
810
|
: config.keyboardZoomStep;
|
|
832
|
-
|
|
811
|
+
canvas.zoomOut(adaptiveZoomStep);
|
|
833
812
|
handled = true;
|
|
834
813
|
}
|
|
835
814
|
break;
|
|
836
815
|
case "0":
|
|
837
|
-
if (event.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
816
|
+
if (event.ctrlKey) {
|
|
817
|
+
if (canvas.resetView) {
|
|
818
|
+
canvas.resetView();
|
|
819
|
+
}
|
|
820
|
+
handled = true;
|
|
821
|
+
}
|
|
822
|
+
else if (event.metaKey || event.ctrlKey) {
|
|
823
|
+
if (canvas.resetViewToCenter) {
|
|
824
|
+
canvas.resetViewToCenter();
|
|
825
|
+
}
|
|
842
826
|
handled = true;
|
|
843
827
|
}
|
|
844
828
|
break;
|
|
@@ -866,10 +850,8 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
866
850
|
}
|
|
867
851
|
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
868
852
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
869
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
870
853
|
return () => {
|
|
871
854
|
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
872
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
873
855
|
};
|
|
874
856
|
}
|
|
875
857
|
|
|
@@ -1490,6 +1472,20 @@ function setupWheelEvents(canvas, config) {
|
|
|
1490
1472
|
};
|
|
1491
1473
|
}
|
|
1492
1474
|
|
|
1475
|
+
function getViewportCenter(canvas) {
|
|
1476
|
+
try {
|
|
1477
|
+
const bounds = canvas.getBounds();
|
|
1478
|
+
return {
|
|
1479
|
+
x: bounds.width / 2,
|
|
1480
|
+
y: bounds.height / 2,
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
catch (error) {
|
|
1484
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
1485
|
+
return { x: 0, y: 0 };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1493
1489
|
// Rulers
|
|
1494
1490
|
const RULER_Z_INDEX = {
|
|
1495
1491
|
GRID: 100,
|
|
@@ -2088,12 +2084,23 @@ class MarkupCanvas {
|
|
|
2088
2084
|
});
|
|
2089
2085
|
}
|
|
2090
2086
|
resetView() {
|
|
2087
|
+
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
2088
|
+
if (result) {
|
|
2089
|
+
this.emitTransformEvents();
|
|
2090
|
+
}
|
|
2091
|
+
return result;
|
|
2092
|
+
}
|
|
2093
|
+
resetViewToCenter() {
|
|
2091
2094
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
this
|
|
2095
|
-
|
|
2096
|
-
|
|
2095
|
+
return withClampedZoom(this.config, (clamp) => {
|
|
2096
|
+
const newScale = clamp(1.0);
|
|
2097
|
+
const center = getViewportCenter(this);
|
|
2098
|
+
const result = this.zoomToPoint(center.x, center.y, newScale);
|
|
2099
|
+
if (result) {
|
|
2100
|
+
this.emitTransformEvents();
|
|
2101
|
+
}
|
|
2102
|
+
return result;
|
|
2103
|
+
});
|
|
2097
2104
|
});
|
|
2098
2105
|
}
|
|
2099
2106
|
zoomToFitContent() {
|
|
@@ -2135,30 +2142,28 @@ class MarkupCanvas {
|
|
|
2135
2142
|
return this.updateTransform(newTransform);
|
|
2136
2143
|
}
|
|
2137
2144
|
// Zoom methods
|
|
2138
|
-
zoomIn(factor = 0.
|
|
2145
|
+
zoomIn(factor = 0.5) {
|
|
2139
2146
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2140
2147
|
return withClampedZoom(this.config, (clamp) => {
|
|
2141
2148
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
return this.updateTransform(newTransform);
|
|
2149
|
+
// Get the center of the viewport
|
|
2150
|
+
const center = getViewportCenter(this);
|
|
2151
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2146
2152
|
});
|
|
2147
2153
|
});
|
|
2148
2154
|
}
|
|
2149
|
-
zoomOut(factor = 0.
|
|
2155
|
+
zoomOut(factor = 0.5) {
|
|
2150
2156
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2151
2157
|
return withClampedZoom(this.config, (clamp) => {
|
|
2152
2158
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
return this.updateTransform(newTransform);
|
|
2159
|
+
// Get the center of the viewport
|
|
2160
|
+
const center = getViewportCenter(this);
|
|
2161
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2157
2162
|
});
|
|
2158
2163
|
});
|
|
2159
2164
|
}
|
|
2160
2165
|
resetZoom() {
|
|
2161
|
-
return this.
|
|
2166
|
+
return this.resetViewToCenter();
|
|
2162
2167
|
}
|
|
2163
2168
|
// Mouse drag control methods
|
|
2164
2169
|
enableMouseDrag() {
|
|
@@ -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.7
|
|
5
5
|
*/
|
|
6
6
|
const EDITOR_PRESET = {
|
|
7
7
|
// Canvas dimensions
|
|
@@ -16,8 +16,8 @@ const EDITOR_PRESET = {
|
|
|
16
16
|
enableZoom: true,
|
|
17
17
|
enablePan: true,
|
|
18
18
|
enableTouch: true,
|
|
19
|
-
enableKeyboard:
|
|
20
|
-
bindKeyboardEventsTo: "
|
|
19
|
+
enableKeyboard: true,
|
|
20
|
+
bindKeyboardEventsTo: "document",
|
|
21
21
|
// Zoom behavior
|
|
22
22
|
zoomSpeed: 1.5,
|
|
23
23
|
minZoom: 0.05,
|
|
@@ -272,13 +272,6 @@ function withRulerSize(canvas, rulerSize, operation) {
|
|
|
272
272
|
const finalRulerSize = hasRulers ? rulerSize : 0;
|
|
273
273
|
return operation(finalRulerSize);
|
|
274
274
|
}
|
|
275
|
-
function withRulerOffsets(canvas, rulerSize, x, y, operation) {
|
|
276
|
-
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
277
|
-
const adjustedX = x - rulerSize;
|
|
278
|
-
const adjustedY = y - rulerSize;
|
|
279
|
-
return operation(adjustedX, adjustedY);
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
275
|
function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
|
|
283
276
|
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
284
277
|
const adjusted = {
|
|
@@ -772,42 +765,28 @@ function getAdaptiveZoomSpeed(canvas, baseSpeed) {
|
|
|
772
765
|
}
|
|
773
766
|
|
|
774
767
|
function setupKeyboardEvents(canvas, config) {
|
|
775
|
-
// Track mouse position
|
|
776
|
-
let lastMouseX = 0;
|
|
777
|
-
let lastMouseY = 0;
|
|
778
|
-
function handleMouseMove(event) {
|
|
779
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
780
|
-
const rawMouseX = event.clientX - rect.left;
|
|
781
|
-
const rawMouseY = event.clientY - rect.top;
|
|
782
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
783
|
-
lastMouseX = adjustedX;
|
|
784
|
-
lastMouseY = adjustedY;
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
768
|
function handleKeyDown(event) {
|
|
788
769
|
if (!(event instanceof KeyboardEvent))
|
|
789
770
|
return;
|
|
790
771
|
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
791
772
|
return;
|
|
792
|
-
const isFastPan = event.shiftKey;
|
|
793
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
794
773
|
let handled = false;
|
|
795
774
|
const newTransform = {};
|
|
796
775
|
switch (event.key) {
|
|
797
776
|
case "ArrowLeft":
|
|
798
|
-
newTransform.translateX = canvas.transform.translateX +
|
|
777
|
+
newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
|
|
799
778
|
handled = true;
|
|
800
779
|
break;
|
|
801
780
|
case "ArrowRight":
|
|
802
|
-
newTransform.translateX = canvas.transform.translateX -
|
|
781
|
+
newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
|
|
803
782
|
handled = true;
|
|
804
783
|
break;
|
|
805
784
|
case "ArrowUp":
|
|
806
|
-
newTransform.translateY = canvas.transform.translateY +
|
|
785
|
+
newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
|
|
807
786
|
handled = true;
|
|
808
787
|
break;
|
|
809
788
|
case "ArrowDown":
|
|
810
|
-
newTransform.translateY = canvas.transform.translateY -
|
|
789
|
+
newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
|
|
811
790
|
handled = true;
|
|
812
791
|
break;
|
|
813
792
|
case "=":
|
|
@@ -816,7 +795,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
816
795
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
817
796
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
818
797
|
: config.keyboardZoomStep;
|
|
819
|
-
|
|
798
|
+
canvas.zoomIn(adaptiveZoomStep);
|
|
820
799
|
handled = true;
|
|
821
800
|
}
|
|
822
801
|
break;
|
|
@@ -825,16 +804,21 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
825
804
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
826
805
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
827
806
|
: config.keyboardZoomStep;
|
|
828
|
-
|
|
807
|
+
canvas.zoomOut(adaptiveZoomStep);
|
|
829
808
|
handled = true;
|
|
830
809
|
}
|
|
831
810
|
break;
|
|
832
811
|
case "0":
|
|
833
|
-
if (event.
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
812
|
+
if (event.ctrlKey) {
|
|
813
|
+
if (canvas.resetView) {
|
|
814
|
+
canvas.resetView();
|
|
815
|
+
}
|
|
816
|
+
handled = true;
|
|
817
|
+
}
|
|
818
|
+
else if (event.metaKey || event.ctrlKey) {
|
|
819
|
+
if (canvas.resetViewToCenter) {
|
|
820
|
+
canvas.resetViewToCenter();
|
|
821
|
+
}
|
|
838
822
|
handled = true;
|
|
839
823
|
}
|
|
840
824
|
break;
|
|
@@ -862,10 +846,8 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
862
846
|
}
|
|
863
847
|
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
864
848
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
865
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
866
849
|
return () => {
|
|
867
850
|
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
868
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
869
851
|
};
|
|
870
852
|
}
|
|
871
853
|
|
|
@@ -1486,6 +1468,20 @@ function setupWheelEvents(canvas, config) {
|
|
|
1486
1468
|
};
|
|
1487
1469
|
}
|
|
1488
1470
|
|
|
1471
|
+
function getViewportCenter(canvas) {
|
|
1472
|
+
try {
|
|
1473
|
+
const bounds = canvas.getBounds();
|
|
1474
|
+
return {
|
|
1475
|
+
x: bounds.width / 2,
|
|
1476
|
+
y: bounds.height / 2,
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
catch (error) {
|
|
1480
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
1481
|
+
return { x: 0, y: 0 };
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1489
1485
|
// Rulers
|
|
1490
1486
|
const RULER_Z_INDEX = {
|
|
1491
1487
|
GRID: 100,
|
|
@@ -2084,12 +2080,23 @@ class MarkupCanvas {
|
|
|
2084
2080
|
});
|
|
2085
2081
|
}
|
|
2086
2082
|
resetView() {
|
|
2083
|
+
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
2084
|
+
if (result) {
|
|
2085
|
+
this.emitTransformEvents();
|
|
2086
|
+
}
|
|
2087
|
+
return result;
|
|
2088
|
+
}
|
|
2089
|
+
resetViewToCenter() {
|
|
2087
2090
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
this
|
|
2091
|
-
|
|
2092
|
-
|
|
2091
|
+
return withClampedZoom(this.config, (clamp) => {
|
|
2092
|
+
const newScale = clamp(1.0);
|
|
2093
|
+
const center = getViewportCenter(this);
|
|
2094
|
+
const result = this.zoomToPoint(center.x, center.y, newScale);
|
|
2095
|
+
if (result) {
|
|
2096
|
+
this.emitTransformEvents();
|
|
2097
|
+
}
|
|
2098
|
+
return result;
|
|
2099
|
+
});
|
|
2093
2100
|
});
|
|
2094
2101
|
}
|
|
2095
2102
|
zoomToFitContent() {
|
|
@@ -2131,30 +2138,28 @@ class MarkupCanvas {
|
|
|
2131
2138
|
return this.updateTransform(newTransform);
|
|
2132
2139
|
}
|
|
2133
2140
|
// Zoom methods
|
|
2134
|
-
zoomIn(factor = 0.
|
|
2141
|
+
zoomIn(factor = 0.5) {
|
|
2135
2142
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2136
2143
|
return withClampedZoom(this.config, (clamp) => {
|
|
2137
2144
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
return this.updateTransform(newTransform);
|
|
2145
|
+
// Get the center of the viewport
|
|
2146
|
+
const center = getViewportCenter(this);
|
|
2147
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2142
2148
|
});
|
|
2143
2149
|
});
|
|
2144
2150
|
}
|
|
2145
|
-
zoomOut(factor = 0.
|
|
2151
|
+
zoomOut(factor = 0.5) {
|
|
2146
2152
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2147
2153
|
return withClampedZoom(this.config, (clamp) => {
|
|
2148
2154
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
return this.updateTransform(newTransform);
|
|
2155
|
+
// Get the center of the viewport
|
|
2156
|
+
const center = getViewportCenter(this);
|
|
2157
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2153
2158
|
});
|
|
2154
2159
|
});
|
|
2155
2160
|
}
|
|
2156
2161
|
resetZoom() {
|
|
2157
|
-
return this.
|
|
2162
|
+
return this.resetViewToCenter();
|
|
2158
2163
|
}
|
|
2159
2164
|
// Mouse drag control methods
|
|
2160
2165
|
enableMouseDrag() {
|
|
@@ -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.7
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
@@ -214,13 +214,6 @@
|
|
|
214
214
|
const finalRulerSize = hasRulers ? rulerSize : 0;
|
|
215
215
|
return operation(finalRulerSize);
|
|
216
216
|
}
|
|
217
|
-
function withRulerOffsets(canvas, rulerSize, x, y, operation) {
|
|
218
|
-
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
219
|
-
const adjustedX = x - rulerSize;
|
|
220
|
-
const adjustedY = y - rulerSize;
|
|
221
|
-
return operation(adjustedX, adjustedY);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
217
|
function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
|
|
225
218
|
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
226
219
|
const adjusted = {
|
|
@@ -714,42 +707,28 @@
|
|
|
714
707
|
}
|
|
715
708
|
|
|
716
709
|
function setupKeyboardEvents(canvas, config) {
|
|
717
|
-
// Track mouse position
|
|
718
|
-
let lastMouseX = 0;
|
|
719
|
-
let lastMouseY = 0;
|
|
720
|
-
function handleMouseMove(event) {
|
|
721
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
722
|
-
const rawMouseX = event.clientX - rect.left;
|
|
723
|
-
const rawMouseY = event.clientY - rect.top;
|
|
724
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
725
|
-
lastMouseX = adjustedX;
|
|
726
|
-
lastMouseY = adjustedY;
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
710
|
function handleKeyDown(event) {
|
|
730
711
|
if (!(event instanceof KeyboardEvent))
|
|
731
712
|
return;
|
|
732
713
|
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
733
714
|
return;
|
|
734
|
-
const isFastPan = event.shiftKey;
|
|
735
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
736
715
|
let handled = false;
|
|
737
716
|
const newTransform = {};
|
|
738
717
|
switch (event.key) {
|
|
739
718
|
case "ArrowLeft":
|
|
740
|
-
newTransform.translateX = canvas.transform.translateX +
|
|
719
|
+
newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
|
|
741
720
|
handled = true;
|
|
742
721
|
break;
|
|
743
722
|
case "ArrowRight":
|
|
744
|
-
newTransform.translateX = canvas.transform.translateX -
|
|
723
|
+
newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
|
|
745
724
|
handled = true;
|
|
746
725
|
break;
|
|
747
726
|
case "ArrowUp":
|
|
748
|
-
newTransform.translateY = canvas.transform.translateY +
|
|
727
|
+
newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
|
|
749
728
|
handled = true;
|
|
750
729
|
break;
|
|
751
730
|
case "ArrowDown":
|
|
752
|
-
newTransform.translateY = canvas.transform.translateY -
|
|
731
|
+
newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
|
|
753
732
|
handled = true;
|
|
754
733
|
break;
|
|
755
734
|
case "=":
|
|
@@ -758,7 +737,7 @@
|
|
|
758
737
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
759
738
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
760
739
|
: config.keyboardZoomStep;
|
|
761
|
-
|
|
740
|
+
canvas.zoomIn(adaptiveZoomStep);
|
|
762
741
|
handled = true;
|
|
763
742
|
}
|
|
764
743
|
break;
|
|
@@ -767,16 +746,21 @@
|
|
|
767
746
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
768
747
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
769
748
|
: config.keyboardZoomStep;
|
|
770
|
-
|
|
749
|
+
canvas.zoomOut(adaptiveZoomStep);
|
|
771
750
|
handled = true;
|
|
772
751
|
}
|
|
773
752
|
break;
|
|
774
753
|
case "0":
|
|
775
|
-
if (event.
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
754
|
+
if (event.ctrlKey) {
|
|
755
|
+
if (canvas.resetView) {
|
|
756
|
+
canvas.resetView();
|
|
757
|
+
}
|
|
758
|
+
handled = true;
|
|
759
|
+
}
|
|
760
|
+
else if (event.metaKey || event.ctrlKey) {
|
|
761
|
+
if (canvas.resetViewToCenter) {
|
|
762
|
+
canvas.resetViewToCenter();
|
|
763
|
+
}
|
|
780
764
|
handled = true;
|
|
781
765
|
}
|
|
782
766
|
break;
|
|
@@ -804,10 +788,8 @@
|
|
|
804
788
|
}
|
|
805
789
|
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
806
790
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
807
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
808
791
|
return () => {
|
|
809
792
|
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
810
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
811
793
|
};
|
|
812
794
|
}
|
|
813
795
|
|
|
@@ -1428,6 +1410,20 @@
|
|
|
1428
1410
|
};
|
|
1429
1411
|
}
|
|
1430
1412
|
|
|
1413
|
+
function getViewportCenter(canvas) {
|
|
1414
|
+
try {
|
|
1415
|
+
const bounds = canvas.getBounds();
|
|
1416
|
+
return {
|
|
1417
|
+
x: bounds.width / 2,
|
|
1418
|
+
y: bounds.height / 2,
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
catch (error) {
|
|
1422
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
1423
|
+
return { x: 0, y: 0 };
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1431
1427
|
// Rulers
|
|
1432
1428
|
const RULER_Z_INDEX = {
|
|
1433
1429
|
GRID: 100,
|
|
@@ -2026,12 +2022,23 @@
|
|
|
2026
2022
|
});
|
|
2027
2023
|
}
|
|
2028
2024
|
resetView() {
|
|
2025
|
+
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
2026
|
+
if (result) {
|
|
2027
|
+
this.emitTransformEvents();
|
|
2028
|
+
}
|
|
2029
|
+
return result;
|
|
2030
|
+
}
|
|
2031
|
+
resetViewToCenter() {
|
|
2029
2032
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
this
|
|
2033
|
-
|
|
2034
|
-
|
|
2033
|
+
return withClampedZoom(this.config, (clamp) => {
|
|
2034
|
+
const newScale = clamp(1.0);
|
|
2035
|
+
const center = getViewportCenter(this);
|
|
2036
|
+
const result = this.zoomToPoint(center.x, center.y, newScale);
|
|
2037
|
+
if (result) {
|
|
2038
|
+
this.emitTransformEvents();
|
|
2039
|
+
}
|
|
2040
|
+
return result;
|
|
2041
|
+
});
|
|
2035
2042
|
});
|
|
2036
2043
|
}
|
|
2037
2044
|
zoomToFitContent() {
|
|
@@ -2073,30 +2080,28 @@
|
|
|
2073
2080
|
return this.updateTransform(newTransform);
|
|
2074
2081
|
}
|
|
2075
2082
|
// Zoom methods
|
|
2076
|
-
zoomIn(factor = 0.
|
|
2083
|
+
zoomIn(factor = 0.5) {
|
|
2077
2084
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2078
2085
|
return withClampedZoom(this.config, (clamp) => {
|
|
2079
2086
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
return this.updateTransform(newTransform);
|
|
2087
|
+
// Get the center of the viewport
|
|
2088
|
+
const center = getViewportCenter(this);
|
|
2089
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2084
2090
|
});
|
|
2085
2091
|
});
|
|
2086
2092
|
}
|
|
2087
|
-
zoomOut(factor = 0.
|
|
2093
|
+
zoomOut(factor = 0.5) {
|
|
2088
2094
|
return withTransition(this.transformLayer, this.config, () => {
|
|
2089
2095
|
return withClampedZoom(this.config, (clamp) => {
|
|
2090
2096
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
return this.updateTransform(newTransform);
|
|
2097
|
+
// Get the center of the viewport
|
|
2098
|
+
const center = getViewportCenter(this);
|
|
2099
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2095
2100
|
});
|
|
2096
2101
|
});
|
|
2097
2102
|
}
|
|
2098
2103
|
resetZoom() {
|
|
2099
|
-
return this.
|
|
2104
|
+
return this.resetViewToCenter();
|
|
2100
2105
|
}
|
|
2101
2106
|
// Mouse drag control methods
|
|
2102
2107
|
enableMouseDrag() {
|
|
@@ -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 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},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||g.width,m=n.height||g.height,f=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: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: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 k(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 x{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 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":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",s),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",s),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,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 E(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 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=>{a=e},setLastMouseY:e=>{s=e}},g=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!0),z(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!1),z(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:f.setIsSpacePressed,setIsDragging:f.setIsDragging,setDragButton:f.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),z(t,n,r,o,!1))}(n,e,t,r,l,f)},y=n=>{!function(e,t,n,r,o,a,s,i,l,c,u,h){if(s>0){const t=Math.abs(e.clientX-i),s=Math.abs(e.clientY-l);if(t>S||s>S){h.setHasDragged(!0);const e=!n.requireSpaceForMouseDrag||a;!o&&r&&e&&h.setIsDragging(!0)}}if(!o||!r)return;e.preventDefault(),d((...e)=>{const n=e[0];if(!o||!r)return;const a=n.clientX-c,s=n.clientY-u,i={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(i),h.setLastMouseX(n.clientX),h.setLastMouseY(n.clientY)})(e)}(n,e,t,r,o,l,c,u,h,a,s,{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,a){o&&L(e,t,n,r,a)}(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,a=n.args||[];try{if("zoomIn"===o)e.zoomIn(a[0]);else if("zoomOut"===o)e.zoomOut(a[0]);else if("setZoom"===o){const t=a[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(a[0]);else if("panRight"===o)e.panRight(a[0]);else if("panUp"===o)e.panUp(a[0]);else if("panDown"===o)e.panDown(a[0]);else if("fitToScreen"===o)e.fitToScreen();else if("centerContent"===o)e.centerContent();else if("scrollToPoint"===o)e.scrollToPoint(a[0],a[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=a[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,a={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(a)}}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,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,F(t,r,s,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,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=>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,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=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),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 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),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: ${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),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 j(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: ${G}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);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 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: ${H}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);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 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&&j(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=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,s,i,l)}function ee(e,t){const n=f(t,"rulerBackgroundColor"),r=f(t,"rulerBorderColor"),o=f(t,"rulerTextColor"),a=f(t,"rulerTickColor"),s=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",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 te(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&&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,a),ee(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||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 x,this.postMessageCleanup=null,!e)throw new Error("Container element is required");this.config=k(t);const n=C(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(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;t[e]=this,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},"*"),window.parent&&window.parent.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 b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(e)};return this.updateTransform(n)}))}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=k({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=k(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)}}});
|
|
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,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},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 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 z(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),M(e,t,n,r,!1)}function L(e,t,n,r,o,a,s,i,l,c){a&&e.button===s&&z(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&&z(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=n=>{!function(e,t,n,r,o,a,s,i,l,c,u,h){if(s>0){const t=Math.abs(e.clientX-i),s=Math.abs(e.clientY-l);if(t>S||s>S){h.setHasDragged(!0);const e=!n.requireSpaceForMouseDrag||a;!o&&r&&e&&h.setIsDragging(!0)}}if(!o||!r)return;e.preventDefault(),d((...e)=>{const n=e[0];if(!o||!r)return;const a=n.clientX-c,s=n.clientY-u,i={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(i),h.setLastMouseX(n.clientX),h.setLastMouseY(n.clientY)})(e)}(n,e,t,r,o,l,c,u,h,a,s,{setHasDragged:g.setHasDragged,setIsDragging:g.setIsDragging,setLastMouseX:g.setLastMouseX,setLastMouseY:g.setLastMouseY})},b=n=>{L(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&&z(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&&z(e,t,r,l,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton}),M(e,t,r,l,o),!0),isEnabled:()=>r}:x}function E(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,a=n.args||[];try{if("zoomIn"===o)e.zoomIn(a[0]);else if("zoomOut"===o)e.zoomOut(a[0]);else if("setZoom"===o){const t=a[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(a[0]);else if("panRight"===o)e.panRight(a[0]);else if("panUp"===o)e.panUp(a[0]);else if("panDown"===o)e.panDown(a[0]);else if("fitToScreen"===o)e.fitToScreen();else if("centerContent"===o)e.centerContent();else if("scrollToPoint"===o)e.scrollToPoint(a[0],a[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=a[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 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 P(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 F(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=>{P(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 $(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=>$(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=$(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)}}function Z(e){try{const t=e.getBounds();return{x:t.width/2,y:t.height/2}}catch(e){return console.warn("Failed to calculate viewport center:",e),{x:0,y:0}}}const O=100,A=1e3,_=1001,G=4,H=4,N=100,V=100,K=20,q=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),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: ${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),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 j(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: ${G}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);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 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: ${H}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);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 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&&j(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=V*t;for(;o<K;)o*=2;for(;o>q;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,s,i,l)}function ee(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 te(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&&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,a),ee(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||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=C(t);const n=x(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding(),this.config.enablePostMessageAPI&&(this.postMessageCleanup=E(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;t[e]=this,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},"*"),window.parent&&window.parent.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=function(e,t){function n(n){if(!(n instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;let r=!1;const o={};switch(n.key){case"ArrowLeft":o.translateX=e.transform.translateX+t.keyboardPanStep,r=!0;break;case"ArrowRight":o.translateX=e.transform.translateX-t.keyboardPanStep,r=!0;break;case"ArrowUp":o.translateY=e.transform.translateY+t.keyboardPanStep,r=!0;break;case"ArrowDown":o.translateY=e.transform.translateY-t.keyboardPanStep,r=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;e.zoomIn(n),r=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;e.zoomOut(n),r=!0}break;case"0":n.ctrlKey?(e.resetView&&e.resetView(),r=!0):(n.metaKey||n.ctrlKey)&&(e.resetViewToCenter&&e.resetViewToCenter(),r=!0);break;case"g":case"G":n.shiftKey&&e.toggleGrid&&e.toggleGrid(),r=n.shiftKey;break;case"r":case"R":!n.shiftKey||n.metaKey||n.ctrlKey||n.altKey||!e.toggleRulers||(e.toggleRulers(),r=!0)}r&&(n.preventDefault(),Object.keys(o).length>0&&e.updateTransform(o))}const r="canvas"===t.bindKeyboardEventsTo?e.container:document;return r.addEventListener("keydown",n),()=>{r.removeEventListener("keydown",n)}}(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=F(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 b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(e)};return this.updateTransform(n)}))}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(){const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e}resetViewToCenter(){return b(this.transformLayer,this.config,()=>l(this.config,e=>{const t=e(1),n=Z(this),r=this.zoomToPoint(n.x,n.y,t);return r&&this.emitTransformEvents(),r}))}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=.5){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n=t(this.baseCanvas.transform.scale*(1+e)),r=Z(this);return this.zoomToPoint(r.x,r.y,n)}))}zoomOut(e=.5){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n=t(this.baseCanvas.transform.scale*(1-e)),r=Z(this);return this.zoomToPoint(r.x,r.y,n)}))}resetZoom(){return this.resetViewToCenter()}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.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/canvas.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface Canvas extends BaseCanvas {
|
|
|
62
62
|
zoomIn: (factor?: number) => boolean;
|
|
63
63
|
zoomOut: (factor?: number) => boolean;
|
|
64
64
|
resetZoom: (duration?: number) => boolean;
|
|
65
|
+
resetViewToCenter: () => boolean;
|
|
65
66
|
enableMouseDrag: () => boolean;
|
|
66
67
|
disableMouseDrag: () => boolean;
|
|
67
68
|
isMouseDragEnabled: () => boolean;
|
package/package.json
CHANGED
package/src/lib/MarkupCanvas.ts
CHANGED
|
@@ -2,6 +2,7 @@ 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
4
|
import { setupKeyboardEvents, setupMouseEvents, setupPostMessageEvents, setupTouchEvents, setupWheelEvents } from "@/lib/events/index.js";
|
|
5
|
+
import { getViewportCenter } from "@/lib/events/utils/getViewportCenter.js";
|
|
5
6
|
import { getThemeValue, withClampedZoom, withFeatureEnabled } from "@/lib/helpers/index.js";
|
|
6
7
|
import { createRulers } from "@/lib/rulers/index.js";
|
|
7
8
|
import { withTransition } from "@/lib/transition/withTransition.js";
|
|
@@ -260,12 +261,25 @@ export class MarkupCanvas implements Canvas {
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
resetView(): boolean {
|
|
264
|
+
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
265
|
+
if (result) {
|
|
266
|
+
this.emitTransformEvents();
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
resetViewToCenter(): boolean {
|
|
263
272
|
return withTransition(this.transformLayer, this.config, () => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
273
|
+
return withClampedZoom(this.config, (clamp) => {
|
|
274
|
+
const newScale = clamp(1.0);
|
|
275
|
+
|
|
276
|
+
const center = getViewportCenter(this);
|
|
277
|
+
const result = this.zoomToPoint(center.x, center.y, newScale);
|
|
278
|
+
if (result) {
|
|
279
|
+
this.emitTransformEvents();
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
});
|
|
269
283
|
});
|
|
270
284
|
}
|
|
271
285
|
|
|
@@ -313,32 +327,32 @@ export class MarkupCanvas implements Canvas {
|
|
|
313
327
|
}
|
|
314
328
|
|
|
315
329
|
// Zoom methods
|
|
316
|
-
zoomIn(factor: number = 0.
|
|
330
|
+
zoomIn(factor: number = 0.5): boolean {
|
|
317
331
|
return withTransition(this.transformLayer, this.config, () => {
|
|
318
332
|
return withClampedZoom(this.config, (clamp) => {
|
|
319
333
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return this.
|
|
334
|
+
|
|
335
|
+
// Get the center of the viewport
|
|
336
|
+
const center = getViewportCenter(this);
|
|
337
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
324
338
|
});
|
|
325
339
|
});
|
|
326
340
|
}
|
|
327
341
|
|
|
328
|
-
zoomOut(factor: number = 0.
|
|
342
|
+
zoomOut(factor: number = 0.5): boolean {
|
|
329
343
|
return withTransition(this.transformLayer, this.config, () => {
|
|
330
344
|
return withClampedZoom(this.config, (clamp) => {
|
|
331
345
|
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return this.
|
|
346
|
+
|
|
347
|
+
// Get the center of the viewport
|
|
348
|
+
const center = getViewportCenter(this);
|
|
349
|
+
return this.zoomToPoint(center.x, center.y, newScale);
|
|
336
350
|
});
|
|
337
351
|
});
|
|
338
352
|
}
|
|
339
353
|
|
|
340
354
|
resetZoom(): boolean {
|
|
341
|
-
return this.
|
|
355
|
+
return this.resetViewToCenter();
|
|
342
356
|
}
|
|
343
357
|
|
|
344
358
|
// Mouse drag control methods
|
|
@@ -15,8 +15,8 @@ export const EDITOR_PRESET: Required<MarkupCanvasConfig> = {
|
|
|
15
15
|
enableZoom: true,
|
|
16
16
|
enablePan: true,
|
|
17
17
|
enableTouch: true,
|
|
18
|
-
enableKeyboard:
|
|
19
|
-
bindKeyboardEventsTo: "
|
|
18
|
+
enableKeyboard: true,
|
|
19
|
+
bindKeyboardEventsTo: "document",
|
|
20
20
|
|
|
21
21
|
// Zoom behavior
|
|
22
22
|
zoomSpeed: 1.5,
|
|
@@ -1,51 +1,30 @@
|
|
|
1
1
|
import { getAdaptiveZoomSpeed } from "@/lib/events/utils/getAdaptiveZoomSpeed.js";
|
|
2
|
-
import { withRulerOffsets } from "@/lib/helpers/index.js";
|
|
3
|
-
import { clampZoom } from "@/lib/matrix/clampZoom.js";
|
|
4
|
-
import { getZoomToMouseTransform } from "@/lib/matrix/getZoomToMouseTransform.js";
|
|
5
2
|
import type { Canvas, MarkupCanvasConfig, Transform } from "@/types/index.js";
|
|
6
3
|
|
|
7
4
|
export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>): () => void {
|
|
8
|
-
// Track mouse position
|
|
9
|
-
let lastMouseX = 0;
|
|
10
|
-
let lastMouseY = 0;
|
|
11
|
-
|
|
12
|
-
function handleMouseMove(event: MouseEvent): void {
|
|
13
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
14
|
-
const rawMouseX = event.clientX - rect.left;
|
|
15
|
-
const rawMouseY = event.clientY - rect.top;
|
|
16
|
-
|
|
17
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
18
|
-
lastMouseX = adjustedX;
|
|
19
|
-
lastMouseY = adjustedY;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
5
|
function handleKeyDown(event: Event): void {
|
|
24
6
|
if (!(event instanceof KeyboardEvent)) return;
|
|
25
7
|
|
|
26
8
|
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container) return;
|
|
27
9
|
|
|
28
|
-
const isFastPan = event.shiftKey;
|
|
29
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
30
|
-
|
|
31
10
|
let handled = false;
|
|
32
11
|
const newTransform: Partial<Transform> = {};
|
|
33
12
|
|
|
34
13
|
switch (event.key) {
|
|
35
14
|
case "ArrowLeft":
|
|
36
|
-
newTransform.translateX = canvas.transform.translateX +
|
|
15
|
+
newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
|
|
37
16
|
handled = true;
|
|
38
17
|
break;
|
|
39
18
|
case "ArrowRight":
|
|
40
|
-
newTransform.translateX = canvas.transform.translateX -
|
|
19
|
+
newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
|
|
41
20
|
handled = true;
|
|
42
21
|
break;
|
|
43
22
|
case "ArrowUp":
|
|
44
|
-
newTransform.translateY = canvas.transform.translateY +
|
|
23
|
+
newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
|
|
45
24
|
handled = true;
|
|
46
25
|
break;
|
|
47
26
|
case "ArrowDown":
|
|
48
|
-
newTransform.translateY = canvas.transform.translateY -
|
|
27
|
+
newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
|
|
49
28
|
handled = true;
|
|
50
29
|
break;
|
|
51
30
|
case "=":
|
|
@@ -54,7 +33,7 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
54
33
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
55
34
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
56
35
|
: config.keyboardZoomStep;
|
|
57
|
-
|
|
36
|
+
canvas.zoomIn(adaptiveZoomStep);
|
|
58
37
|
handled = true;
|
|
59
38
|
}
|
|
60
39
|
break;
|
|
@@ -63,18 +42,20 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
63
42
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
64
43
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
65
44
|
: config.keyboardZoomStep;
|
|
66
|
-
|
|
45
|
+
canvas.zoomOut(adaptiveZoomStep);
|
|
67
46
|
handled = true;
|
|
68
47
|
}
|
|
69
48
|
break;
|
|
70
49
|
case "0":
|
|
71
|
-
if (event.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
50
|
+
if (event.ctrlKey) {
|
|
51
|
+
if (canvas.resetView) {
|
|
52
|
+
canvas.resetView();
|
|
53
|
+
}
|
|
54
|
+
handled = true;
|
|
55
|
+
} else if (event.metaKey || event.ctrlKey) {
|
|
56
|
+
if (canvas.resetViewToCenter) {
|
|
57
|
+
canvas.resetViewToCenter();
|
|
58
|
+
}
|
|
78
59
|
handled = true;
|
|
79
60
|
}
|
|
80
61
|
break;
|
|
@@ -105,10 +86,8 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
105
86
|
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
106
87
|
|
|
107
88
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
108
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
109
89
|
|
|
110
90
|
return () => {
|
|
111
91
|
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
112
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
113
92
|
};
|
|
114
93
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Canvas } from "@/types/index.js";
|
|
2
|
+
|
|
3
|
+
export function getViewportCenter(canvas: Canvas): { x: number; y: number } {
|
|
4
|
+
try {
|
|
5
|
+
const bounds = canvas.getBounds();
|
|
6
|
+
return {
|
|
7
|
+
x: bounds.width / 2,
|
|
8
|
+
y: bounds.height / 2,
|
|
9
|
+
};
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
12
|
+
return { x: 0, y: 0 };
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/types/canvas.ts
CHANGED
|
@@ -66,6 +66,7 @@ export interface Canvas extends BaseCanvas {
|
|
|
66
66
|
zoomIn: (factor?: number) => boolean;
|
|
67
67
|
zoomOut: (factor?: number) => boolean;
|
|
68
68
|
resetZoom: (duration?: number) => boolean;
|
|
69
|
+
resetViewToCenter: () => boolean;
|
|
69
70
|
// Mouse drag control functions
|
|
70
71
|
enableMouseDrag: () => boolean;
|
|
71
72
|
disableMouseDrag: () => boolean;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { getAdaptiveZoomSpeed } from "@/lib/events/utils/getAdaptiveZoomSpeed.js";
|
|
2
|
-
import { withRulerOffsets } from "@/lib/helpers/index.js";
|
|
3
|
-
import { clampZoom } from "@/lib/matrix/clampZoom.js";
|
|
4
|
-
import { getZoomToMouseTransform } from "@/lib/matrix/getZoomToMouseTransform.js";
|
|
5
|
-
import type { Canvas, MarkupCanvasConfig, Transform } from "@/types/index.js";
|
|
6
|
-
|
|
7
|
-
export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>): () => void {
|
|
8
|
-
// Track mouse position
|
|
9
|
-
let lastMouseX = 0;
|
|
10
|
-
let lastMouseY = 0;
|
|
11
|
-
|
|
12
|
-
function handleMouseMove(event: MouseEvent): void {
|
|
13
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
14
|
-
const rawMouseX = event.clientX - rect.left;
|
|
15
|
-
const rawMouseY = event.clientY - rect.top;
|
|
16
|
-
|
|
17
|
-
// Use the new helper to adjust for ruler offset
|
|
18
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
19
|
-
lastMouseX = adjustedX;
|
|
20
|
-
lastMouseY = adjustedY;
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function handleKeyDown(event: Event): void {
|
|
25
|
-
if (!(event instanceof KeyboardEvent)) return;
|
|
26
|
-
|
|
27
|
-
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container) return;
|
|
28
|
-
|
|
29
|
-
const isFastPan = event.shiftKey;
|
|
30
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
31
|
-
|
|
32
|
-
let handled = false;
|
|
33
|
-
const newTransform: Partial<Transform> = {};
|
|
34
|
-
|
|
35
|
-
switch (event.key) {
|
|
36
|
-
case "ArrowLeft":
|
|
37
|
-
newTransform.translateX = canvas.transform.translateX + panDistance;
|
|
38
|
-
handled = true;
|
|
39
|
-
break;
|
|
40
|
-
case "ArrowRight":
|
|
41
|
-
newTransform.translateX = canvas.transform.translateX - panDistance;
|
|
42
|
-
handled = true;
|
|
43
|
-
break;
|
|
44
|
-
case "ArrowUp":
|
|
45
|
-
newTransform.translateY = canvas.transform.translateY + panDistance;
|
|
46
|
-
handled = true;
|
|
47
|
-
break;
|
|
48
|
-
case "ArrowDown":
|
|
49
|
-
newTransform.translateY = canvas.transform.translateY - panDistance;
|
|
50
|
-
handled = true;
|
|
51
|
-
break;
|
|
52
|
-
case "=":
|
|
53
|
-
case "+":
|
|
54
|
-
{
|
|
55
|
-
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
56
|
-
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
57
|
-
: config.keyboardZoomStep;
|
|
58
|
-
newTransform.scale = clampZoom(canvas.transform.scale * (1 + adaptiveZoomStep), config);
|
|
59
|
-
handled = true;
|
|
60
|
-
}
|
|
61
|
-
break;
|
|
62
|
-
case "-":
|
|
63
|
-
{
|
|
64
|
-
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
65
|
-
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
66
|
-
: config.keyboardZoomStep;
|
|
67
|
-
newTransform.scale = clampZoom(canvas.transform.scale * (1 - adaptiveZoomStep), config);
|
|
68
|
-
handled = true;
|
|
69
|
-
}
|
|
70
|
-
break;
|
|
71
|
-
case "0":
|
|
72
|
-
if (event.metaKey || event.ctrlKey) {
|
|
73
|
-
const targetScale = 1.0;
|
|
74
|
-
const zoomFactor = targetScale / canvas.transform.scale;
|
|
75
|
-
|
|
76
|
-
const zoomTransform = getZoomToMouseTransform(lastMouseX, lastMouseY, canvas.transform, zoomFactor, config);
|
|
77
|
-
|
|
78
|
-
Object.assign(newTransform, zoomTransform);
|
|
79
|
-
handled = true;
|
|
80
|
-
}
|
|
81
|
-
break;
|
|
82
|
-
case "g":
|
|
83
|
-
case "G":
|
|
84
|
-
if (event.shiftKey && canvas.toggleGrid) {
|
|
85
|
-
canvas.toggleGrid();
|
|
86
|
-
}
|
|
87
|
-
handled = event.shiftKey;
|
|
88
|
-
break;
|
|
89
|
-
case "r":
|
|
90
|
-
case "R":
|
|
91
|
-
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && canvas.toggleRulers) {
|
|
92
|
-
canvas.toggleRulers();
|
|
93
|
-
handled = true;
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (handled) {
|
|
99
|
-
event.preventDefault();
|
|
100
|
-
if (Object.keys(newTransform).length > 0) {
|
|
101
|
-
canvas.updateTransform(newTransform);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const keyboardTarget = config.bindKeyboardEventsTo ? canvas.container : document;
|
|
107
|
-
|
|
108
|
-
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
109
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
110
|
-
|
|
111
|
-
return () => {
|
|
112
|
-
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
113
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
114
|
-
};
|
|
115
|
-
}
|