@inweb/markup
Version:
JavaScript 2D markups
1,422 lines (1,407 loc) • 66.4 kB
JavaScript
import Konva from "konva";
class WorldTransform {
screenToWorld(position) {
return {
x: position.x,
y: position.y,
z: 0
};
}
worldToScreen(position) {
return {
x: position.x,
y: position.y
};
}
getScale() {
return {
x: 1,
y: 1,
z: 1
};
}
}
class MarkupColor {
constructor(r, g, b) {
this.setColor(r, g, b);
}
asHex() {
return "#" + this.HEX;
}
asRGB() {
return {
r: this.R,
g: this.G,
b: this.B
};
}
setColor(r, g, b) {
this.R = r;
this.G = g;
this.B = b;
this.HEX = this.rgbToHex(r, g, b);
}
rgbToHex(r, g, b) {
const valueToHex = c => {
const hex = c.toString(16);
return hex === "0" ? "00" : hex;
};
return valueToHex(r) + valueToHex(g) + valueToHex(b);
}
}
const LineTypeSpecs = new Map([ [ "solid", [] ], [ "dot", [ 30, 30, .001, 30 ] ], [ "dash", [ 30, 30 ] ] ]);
class KonvaLine {
constructor(params, ref = null) {
var _a, _b;
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.points) params.points = [ {
x: 0,
y: 0
}, {
x: 100,
y: 100
} ];
const konvaPoints = [];
params.points.forEach((point => konvaPoints.push(point.x, point.y)));
this._ref = new Konva.Line({
stroke: (_a = params.color) !== null && _a !== undefined ? _a : "#ff0000",
strokeWidth: (_b = params.width) !== null && _b !== undefined ? _b : 4,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
points: konvaPoints,
draggable: true,
strokeScaleEnabled: false,
dash: LineTypeSpecs.get(params.type) || []
});
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
}));
this._ref.id(this._ref._id.toString());
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Line";
}
getColor() {
return this._ref.stroke();
}
setColor(hex) {
this._ref.stroke(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getPoints() {
return this._ref.points();
}
setLineWidth(size) {
this._ref.strokeWidth(size);
}
getLineWidth() {
return this._ref.strokeWidth();
}
getLineType() {
const typeSpecs = this._ref.dash() || [];
let type;
switch (typeSpecs) {
case LineTypeSpecs.get("dot"):
type = "dot";
break;
case LineTypeSpecs.get("dash"):
type = "dash";
break;
default:
type = "solid";
break;
}
return type;
}
setLineType(type) {
const specs = LineTypeSpecs.get(type);
if (specs) this._ref.dash(specs);
}
addPoints(points) {
let newPoints = this._ref.points();
points.forEach((point => {
newPoints = newPoints.concat([ point.x, point.y ]);
}));
this._ref.points(newPoints);
}
}
class KonvaText {
constructor(params, ref = null) {
var _a, _b, _c;
this.TEXT_FONT_FAMILY = "Calibri";
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.position) params.position = {
x: 0,
y: 0
};
if (!params.text) params.text = "default";
this._ref = new Konva.Text({
x: params.position.x,
y: params.position.y,
text: params.text,
fontSize: (_a = params.fontSize) !== null && _a !== undefined ? _a : 34,
fontFamily: this.TEXT_FONT_FAMILY,
fill: (_b = params.color) !== null && _b !== undefined ? _b : "#ff0000",
align: "left",
draggable: true,
rotation: (_c = params.rotation) !== null && _c !== undefined ? _c : 0
});
this._ref.width(this._ref.getTextWidth());
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 1e-5;
const scaleByY = Math.abs(attrs.scaleY - 1) > 1e-5;
let newWidth = this._ref.width();
if (scaleByX) newWidth *= attrs.scaleX;
let newHeight = this._ref.height();
if (scaleByY) newHeight *= attrs.scaleY;
const minWidth = 50;
if (newWidth < minWidth) newWidth = minWidth;
if (newHeight < Math.round(this.getFontSize())) newHeight = Math.round(this.getFontSize());
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
this._ref.scale({
x: 1,
y: 1
});
}));
this._ref.id(this._ref._id.toString());
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Text";
}
getColor() {
return this._ref.fill();
}
setColor(hex) {
this._ref.fill(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getText() {
return this._ref.text();
}
setText(text) {
this._ref.text(text);
}
getPosition() {
return this._ref.getPosition();
}
setPosition(x, y) {
this._ref.setPosition({
x: x,
y: y
});
}
getFontSize() {
return this._ref.fontSize();
}
setFontSize(size) {
this._ref.fontSize(size);
}
}
class KonvaRectangle {
constructor(params, ref = null) {
var _a, _b, _c, _d;
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.position) params.position = {
x: 0,
y: 0
};
this._ref = new Konva.Rect({
stroke: (_a = params.color) !== null && _a !== undefined ? _a : "#ff0000",
strokeWidth: (_b = params.lineWidth) !== null && _b !== undefined ? _b : 4,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
x: params.position.x,
y: params.position.y,
width: (_c = params.width) !== null && _c !== undefined ? _c : 200,
height: (_d = params.height) !== null && _d !== undefined ? _d : 200,
draggable: true,
strokeScaleEnabled: false
});
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 1e-5;
const scaleByY = Math.abs(attrs.scaleY - 1) > 1e-5;
let newWidth = this._ref.width();
if (scaleByX) newWidth *= attrs.scaleX;
let newHeight = this._ref.height();
if (scaleByY) newHeight *= attrs.scaleY;
const minWidth = 50;
const minHeight = 50;
if (newWidth < minWidth) newWidth = minWidth;
if (newHeight < minHeight) newHeight = minHeight;
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
this._ref.scale({
x: 1,
y: 1
});
}));
this._ref.id(this._ref._id.toString());
}
getPosition() {
return this._ref.position();
}
getWidth() {
return this._ref.width();
}
getHeigth() {
return this._ref.height();
}
setWidth(w) {
this._ref.width(w);
}
setHeight(h) {
this._ref.height(h);
}
setPosition(x, y) {
this._ref.setPosition({
x: x,
y: y
});
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Rectangle";
}
getColor() {
return this._ref.stroke();
}
setColor(hex) {
this._ref.stroke(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
setLineWidth(size) {
this._ref.strokeWidth(size);
}
getLineWidth() {
return this._ref.strokeWidth();
}
}
class KonvaEllipse {
constructor(params, ref = null) {
var _a, _b;
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.position) params.position = {
x: 0,
y: 0
};
if (!params.radius) params.radius = {
x: 25,
y: 25
};
this._ref = new Konva.Ellipse({
stroke: (_a = params.color) !== null && _a !== undefined ? _a : "#ff0000",
strokeWidth: (_b = params.lineWidth) !== null && _b !== undefined ? _b : 4,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
x: params.position.x,
y: params.position.y,
radiusX: params.radius.x,
radiusY: params.radius.y,
draggable: true,
strokeScaleEnabled: false
});
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 1e-5;
const scaleByY = Math.abs(attrs.scaleY - 1) > 1e-5;
let newRadiusX = this._ref.radiusX();
if (scaleByX) newRadiusX *= attrs.scaleX;
let newRadiusY = this._ref.radiusY();
if (scaleByY) newRadiusY *= attrs.scaleY;
const minRadiusX = 25;
const minRadiusY = 25;
if (newRadiusX < minRadiusX) newRadiusX = minRadiusX;
if (newRadiusY < minRadiusY) newRadiusY = minRadiusY;
if (e.evt.ctrlKey || e.evt.shiftKey) {
if (scaleByX) {
this._ref.radius({
x: newRadiusX,
y: newRadiusX
});
} else {
this._ref.radius({
x: newRadiusY,
y: newRadiusY
});
}
} else {
this._ref.radius({
x: newRadiusX,
y: newRadiusY
});
}
this._ref.scale({
x: 1,
y: 1
});
}));
this._ref.id(this._ref._id.toString());
}
getPosition() {
return this._ref.position();
}
setPosition(x, y) {
this._ref.setPosition({
x: x,
y: y
});
}
getRadiusX() {
return this._ref.radiusX();
}
setRadiusX(r) {
this._ref.radiusX(r);
}
getRadiusY() {
return this._ref.radiusY();
}
setRadiusY(r) {
this._ref.radiusY(r);
}
getLineWidth() {
return this._ref.strokeWidth();
}
setLineWidth(size) {
this._ref.strokeWidth(size);
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Ellipse";
}
getColor() {
return this._ref.stroke();
}
setColor(hex) {
this._ref.stroke(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
}
class KonvaArrow {
constructor(params, ref = null) {
var _a, _b;
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.start) params.start = {
x: 0,
y: 0
};
if (!params.end) params.end = {
x: 100,
y: 100
};
this._ref = new Konva.Arrow({
stroke: (_a = params.color) !== null && _a !== undefined ? _a : "#ff0000",
fill: (_b = params.color) !== null && _b !== undefined ? _b : "#ff0000",
strokeWidth: 4,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
points: [ params.start.x, params.start.y, params.end.x, params.end.y ],
draggable: true,
strokeScaleEnabled: false
});
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
}));
this._ref.id(this._ref._id.toString());
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Arrow";
}
getColor() {
return this._ref.stroke();
}
setColor(hex) {
this._ref.stroke(hex);
this._ref.fill(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getPoints() {
const points = this._ref.points();
return [ {
x: points[0],
y: points[1]
}, {
x: points[2],
y: points[3]
} ];
}
setPoints(points) {
if (points.length === 2) {
this._ref.points([ points[0].x, points[0].y, points[1].x, points[1].y ]);
}
}
getStartPoint() {
const points = this._ref.points();
return {
x: points[0],
y: points[1]
};
}
setStartPoint(x, y) {
const points = this._ref.points();
this._ref.points([ x, y, points[2], points[3] ]);
}
getEndPoint() {
const points = this._ref.points();
return {
x: points[2],
y: points[3]
};
}
setEndPoint(x, y) {
const points = this._ref.points();
this._ref.points([ points[0], points[1], x, y ]);
}
}
class KonvaImage {
constructor(params, ref = null) {
var _a, _b;
this._ratio = 1;
this.EPSILON = 1e-5;
this.BASE64_HEADER_START = "data:image/";
this.BASE64_NOT_FOUND = "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAADsAAAA7AF5KHG9AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAmhJREFUWIXtlr9rVEEQxz+H8RQUJIdeIopYm0vkCg0GBBtbG1NF7Kxt7dR/IGIw/uhTaBNLERURg2kCEUyCYCPi70b0InjGS57FzOZN3r19d+9HJIVfWO52dma/s7Mz8xa2KAaBCWAR+AkECWOmSOIdwC1gtQOpHc+NfQ8wClQ8+1d0vcdH/lQ3bSIRGAZ2pTjAqNovANXIWlXlAXA2zvi2Ln4AjqYgtagYEutENSLvjRoOImFv5iB32Ae8UrLXwFBk3h9ndF0VJnKSO9gTu3yKu5Z1LKnS8YIcABgw5Ks692JZFXcXRJ46Aq6kikCnHNi/mQ50WwVtfaIoBzL3gRk2drSscJ2wrc4VvUoe2wn/41/iBfoVLRnBGnDSY3AAKacy8AmYR+o7K1zCl6wgrgpOAc/MuhvfgMuk+1JGHQgSBcAloKXy78AjYBppJk5/noTulseBMZ23iD/piHFkEdgTQzKk+5wHjmHC3cmBg0BD5xcSTrFXyQPgIWFtDwMvab+2N8DpbhyY1v/3E8gdDgNfVX9SCVZ0/gW4B0wB71S2BpxLcuCM/jaQSHSDEeAX4VMuAG4gTzyHbcAVXXO6GxxwIX+vvxe7JHcYQ07nHqklj96UIW/YhSWzMKcep8VVtf8B1Dw6h4DfhB+sdbgn2R+gnoEc5NR3dZ+3QJ9H74HqXLPCGlJyTfI9y3YCs0owq3OLOpKkLeBI1HhSDT/mdKIPiUCARMTlQx34TMLjtww8IczmO8AJ/N/2JNSQXAiQ671JePePge0+wzJSQq4FFzlaenIvucUAkiQLhC/mLGNZ9xgn5s63BP4CCk0QDtm4BhoAAAAASUVORK5CYII=";
if (ref) {
if (!ref.src || !ref.src.startsWith(this.BASE64_HEADER_START)) ref.src = this.BASE64_NOT_FOUND;
if (ref.height() <= this.EPSILON) ref.height(32);
if (ref.width() <= this.EPSILON) ref.width(32);
this._ref = ref;
this._canvasImage = ref.image();
this._ratio = this._ref.height() <= this.EPSILON || this._ref.width() <= this.EPSILON ? 1 : this._ref.height() / this._ref.width();
return;
}
if (!params) params = {};
if (!params.position) params.position = {
x: 0,
y: 0
};
if (!params.src || !params.src.startsWith(this.BASE64_HEADER_START)) params.src = this.BASE64_NOT_FOUND;
this._canvasImage = new Image;
this._canvasImage.onload = () => {
this._ref.image(this._canvasImage);
if (this._ref.height() <= this.EPSILON) this._ref.height(this._canvasImage.height);
if (this._ref.width() <= this.EPSILON) this._ref.width(this._canvasImage.width);
this._ratio = this._ref.height() <= this.EPSILON || this._ref.width() <= this.EPSILON ? 1 : this._ref.height() / this._ref.width();
if ((params.width <= this.EPSILON || params.height <= this.EPSILON) && (params.maxWidth >= this.EPSILON || params.maxWidth >= this.EPSILON)) {
const heightOutOfCanvas = params.maxHeight - this._canvasImage.height;
const widthOutOfCanvas = params.maxWidth - this._canvasImage.width;
if (heightOutOfCanvas <= this.EPSILON || widthOutOfCanvas <= this.EPSILON) {
if (widthOutOfCanvas <= this.EPSILON && widthOutOfCanvas < heightOutOfCanvas / this._ratio) {
this._ref.height(params.maxWidth * this._ratio);
this._ref.width(params.maxWidth);
} else {
this._ref.width(params.maxHeight / this._ratio);
this._ref.height(params.maxHeight);
}
}
}
};
this._canvasImage.onerror = () => {
this._canvasImage.onerror = function() {};
this._canvasImage.src = this.BASE64_NOT_FOUND;
};
this._canvasImage.src = params.src;
this._ref = new Konva.Image({
x: params.position.x,
y: params.position.y,
image: this._canvasImage,
width: (_a = params.width) !== null && _a !== undefined ? _a : 0,
height: (_b = params.height) !== null && _b !== undefined ? _b : 0,
draggable: true
});
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 1e-5;
const scaleByY = Math.abs(attrs.scaleY - 1) > 1e-5;
let newWidth = this._ref.width();
if (scaleByX) newWidth *= attrs.scaleX;
let newHeight = this._ref.height();
if (scaleByY) newHeight *= attrs.scaleY;
if (e.evt.ctrlKey || e.evt.shiftKey) {
if (scaleByX) {
this._ref.width(newWidth);
this._ref.height(newWidth * this._ratio);
} else {
this._ref.width(newHeight / this._ratio);
this._ref.height(newHeight);
}
} else {
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
}
this._ref.scale({
x: 1,
y: 1
});
}));
this._ref.id(this._ref._id.toString());
}
getSrc() {
return this._canvasImage.src;
}
setSrc(src) {
this._canvasImage.src = src;
}
getWidth() {
return this._ref.width();
}
setWidth(w) {
this._ref.width(w);
this._ref.height(w * this._ratio);
}
getHeight() {
return this._ref.height();
}
setHeight(h) {
this._ref.height(h);
this._ref.width(h / this._ratio);
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Image";
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getPosition() {
return this._ref.getPosition();
}
setPosition(x, y) {
this._ref.setPosition({
x: x,
y: y
});
}
}
class KonvaCloud {
constructor(params, ref = null) {
var _a, _b, _c, _d;
if (ref) {
this._ref = ref;
return;
}
if (!params) params = {};
if (!params.position) params.position = {
x: 0,
y: 0
};
const arcRadius = 16;
this._ref = new Konva.Shape({
x: params.position.x,
y: params.position.y,
width: (_a = params.width) !== null && _a !== undefined ? _a : 200,
height: (_b = params.height) !== null && _b !== undefined ? _b : 200,
stroke: (_c = params.color) !== null && _c !== undefined ? _c : "#ff0000",
strokeWidth: (_d = params.lineWidth) !== null && _d !== undefined ? _d : 4,
draggable: true,
strokeScaleEnabled: false,
globalCompositeOperation: "source-over",
sceneFunc: (context, shape) => {
function calculateMidpoint(position, width, height) {
const midX = position.x + width / 2;
const midY = position.y + height / 2;
return {
x: midX,
y: midY
};
}
const points = [ {
x: 0,
y: 0
}, {
x: 0 + this._ref.width(),
y: 0
}, {
x: 0 + this._ref.width(),
y: 0 + this._ref.height()
}, {
x: 0,
y: 0 + this._ref.height()
}, {
x: 0,
y: 0
} ];
const midPoint = calculateMidpoint({
x: 0,
y: 0
}, this._ref.width(), this._ref.height());
const baseArcLength = 30;
context.beginPath();
for (let iPoint = 0; iPoint < points.length - 1; iPoint++) {
let approxArcLength = baseArcLength;
const dx = points[iPoint + 1].x - points[iPoint].x;
const dy = points[iPoint + 1].y - points[iPoint].y;
const length = Math.sqrt(dx * dx + dy * dy);
const arcCount = Math.floor(length / approxArcLength);
const lengthMod = length % approxArcLength;
approxArcLength = baseArcLength + arcCount / lengthMod;
let pX = points[iPoint].x + dx / arcCount / 2;
let pY = points[iPoint].y + dy / arcCount / 2;
const pEndX = points[iPoint + 1].x;
const pEndY = points[iPoint + 1].y;
const endAngle = Math.atan((pEndY - pY) / (pEndX - pX));
const startAngle = endAngle + Math.PI;
const counterClockwise = pX > midPoint.x && pY > midPoint.y;
for (let iArc = 0; iArc < arcCount; iArc++) {
if (counterClockwise) {
context.arc(pX, pY, arcRadius, endAngle, startAngle);
} else {
context.arc(pX, pY, arcRadius, startAngle, endAngle);
}
pX += dx / arcCount;
pY += dy / arcCount;
}
}
context.closePath();
context.fillStrokeShape(shape);
}
});
this._ref.className = "Cloud";
this._ref.on("transform", (e => {
const attrs = e.target.attrs;
if (attrs.rotation !== this._ref.rotation()) this._ref.rotation(attrs.rotation);
const scaleByX = Math.abs(attrs.scaleX - 1) > 1e-5;
const scaleByY = Math.abs(attrs.scaleY - 1) > 1e-5;
let newWidth = this._ref.width();
if (scaleByX) newWidth *= attrs.scaleX;
let newHeight = this._ref.height();
if (scaleByY) newHeight *= attrs.scaleY;
const minWidth = 100;
const minHeight = 100;
if (newWidth < minWidth) newWidth = minWidth;
if (newHeight < minHeight) newHeight = minHeight;
if (scaleByX) {
this._ref.width(newWidth);
}
if (scaleByY) {
this._ref.height(newHeight);
}
this._ref.scale({
x: 1,
y: 1
});
}));
this._ref.getSelfRect = () => ({
x: 0 - arcRadius,
y: 0 - arcRadius,
width: this._ref.width() + 2 * arcRadius,
height: this._ref.height() + 2 * arcRadius
});
this._ref.id(this._ref._id.toString());
}
ref() {
return this._ref;
}
id() {
return this._ref.id();
}
enableMouseEditing(value) {
this._ref.draggable(value);
}
type() {
return "Cloud";
}
getColor() {
return this._ref.stroke();
}
setColor(hex) {
this._ref.stroke(hex);
}
getRotation() {
return this._ref.rotation();
}
setRotation(degrees) {
this._ref.rotation(degrees);
}
getZIndex() {
return this._ref.zIndex();
}
setZIndex(zIndex) {
this._ref.zIndex(zIndex);
}
delete() {
this._ref.destroy();
this._ref = null;
}
getPosition() {
return this._ref.position();
}
setPosition(x, y) {
this._ref.position({
x: x,
y: y
});
}
getWidth() {
return this._ref.width();
}
setWidth(w) {
this._ref.width(w);
}
getHeigth() {
return this._ref.height();
}
setHeight(h) {
this._ref.height(h);
}
getLineWidth() {
return this._ref.strokeWidth();
}
setLineWidth(size) {
this._ref.strokeWidth(size);
}
}
const MarkupMode2Konva = {
SelectMarkup: {
name: "SelectMarkup",
initializer: null
},
Line: {
name: "Line",
initializer: (ref, params = null) => new KonvaLine(params, ref)
},
Text: {
name: "Text",
initializer: (ref, params = null) => new KonvaText(params, ref)
},
Rectangle: {
name: "Rect",
initializer: (ref, params = null) => new KonvaRectangle(params, ref)
},
Ellipse: {
name: "Ellipse",
initializer: (ref, params = null) => new KonvaEllipse(params, ref)
},
Arrow: {
name: "Arrow",
initializer: (ref, params = null) => new KonvaArrow(params, ref)
},
Image: {
name: "Image",
initializer: (ref, params = null) => new KonvaImage(params, ref)
},
Cloud: {
name: "Cloud",
initializer: (ref, params = null) => new KonvaCloud(params, ref)
}
};
class KonvaMarkup {
constructor() {
this._containerEvents = [];
this._markupIsActive = false;
this._markupColor = new MarkupColor(255, 0, 0);
this.lineWidth = 4;
this.lineType = "solid";
this.fontSize = 34;
this.changeActiveDragger = event => {
const draggerName = event.data;
this._markupContainer.className = this._container.className.split(" ").filter((x => !x.startsWith("oda-cursor-"))).filter((x => x)).concat(`oda-cursor-${draggerName.toLowerCase()}`).join(" ");
this.removeTextInput();
this.removeImageInput();
this.enableEditMode(draggerName);
};
this.resizeContainer = entries => {
const {width: width, height: height} = entries[0].contentRect;
if (!width || !height) return;
if (!this._konvaStage) return;
this._konvaStage.width(width);
this._konvaStage.height(height);
};
this.pan = event => {
const newPos = {
x: this._konvaStage.x() + event.dX,
y: this._konvaStage.y() + event.dY
};
this._konvaStage.position(newPos);
};
this.zoomAt = event => {
const newScale = this._konvaStage.scaleX() * event.data;
this._konvaStage.scale({
x: newScale,
y: newScale
});
const newPos = {
x: event.x - (event.x - this._konvaStage.x()) * event.data,
y: event.y - (event.y - this._konvaStage.y()) * event.data
};
this._konvaStage.position(newPos);
};
this.redirectToViewer = event => {
if (this._viewer) this._viewer.emit(event);
};
this.getRelativePointPosition = (point, node) => {
const transform = node.getAbsoluteTransform().copy();
transform.invert();
return transform.point(point);
};
this.getRelativePointerPosition = node => this.getRelativePointPosition(node.getStage().getPointerPosition(), node);
}
initialize(container, containerEvents, viewer, worldTransformer) {
if (!Konva) throw new Error('Markup error: Konva is not initialized. Forgot to add <script src="https://unpkg.com/konva@9/konva.min.js"><\/script> to your page?');
this._viewer = viewer;
this._worldTransformer = worldTransformer !== null && worldTransformer !== undefined ? worldTransformer : new WorldTransform;
this._container = container;
this._containerEvents = containerEvents !== null && containerEvents !== undefined ? containerEvents : [];
this._markupContainer = document.createElement("div");
this._markupContainer.id = "markup-container";
this._markupContainer.style.position = "absolute";
this._markupContainer.style.top = "0px";
this._markupContainer.style.left = "0px";
this._markupContainer.style.outline = "0px";
this._markupContainer.style.pointerEvents = "none";
const parentDiv = this._container.parentElement;
parentDiv.appendChild(this._markupContainer);
this._resizeObserver = new ResizeObserver(this.resizeContainer);
this._resizeObserver.observe(parentDiv);
this._markupColor.setColor(255, 0, 0);
this.initializeKonva();
if (this._viewer) {
this._viewer.addEventListener("changeactivedragger", this.changeActiveDragger);
this._viewer.addEventListener("pan", this.pan);
this._viewer.addEventListener("zoomat", this.zoomAt);
}
}
dispose() {
var _a, _b;
if (this._viewer) {
this._viewer.removeEventListener("zoomat", this.zoomAt);
this._viewer.removeEventListener("pan", this.pan);
this._viewer.removeEventListener("changeactivedragger", this.changeActiveDragger);
}
this.destroyKonva();
(_a = this._resizeObserver) === null || _a === undefined ? undefined : _a.disconnect();
this._resizeObserver = undefined;
(_b = this._markupContainer) === null || _b === undefined ? undefined : _b.remove();
this._markupContainer = undefined;
this._container = undefined;
this._viewer = undefined;
this._worldTransformer = undefined;
this._markupIsActive = false;
}
syncOverlay() {}
clearOverlay() {
this.removeTextInput();
this.removeImageInput();
this.clearSelected();
this.getObjects().forEach((obj => obj.delete()));
}
getMarkupColor() {
return this._markupColor.asRGB();
}
setMarkupColor(r, g, b) {
this._markupColor.setColor(r, g, b);
this.redirectToViewer({
type: "changemarkupcolor",
data: {
r: r,
g: g,
b: b
}
});
}
colorizeAllMarkup(r, g, b) {
const hexColor = new MarkupColor(r, g, b).asHex();
this.getObjects().filter((obj => {
var _a;
return (_a = obj.setColor) === null || _a === undefined ? undefined : _a.call(obj, hexColor);
}));
}
colorizeSelectedMarkups(r, g, b) {
const hexColor = new MarkupColor(r, g, b).asHex();
this.getSelectedObjects().filter((obj => {
var _a;
return (_a = obj.setColor) === null || _a === undefined ? undefined : _a.call(obj, hexColor);
}));
}
setViewpoint(viewpoint) {
var _a, _b, _c, _d, _e, _f, _g, _h;
this.clearSelected();
this.removeTextInput();
this.removeImageInput();
this._konvaStage.scale({
x: 1,
y: 1
});
this._konvaStage.position({
x: 0,
y: 0
});
const markupColor = ((_a = viewpoint.custom_fields) === null || _a === undefined ? undefined : _a.markup_color) || {
r: 255,
g: 0,
b: 0
};
this.setMarkupColor(markupColor.r, markupColor.g, markupColor.b);
(_b = viewpoint.lines) === null || _b === undefined ? undefined : _b.forEach((line => {
const linePoints = [];
line.points.forEach((point => {
const screenPoint = this._worldTransformer.worldToScreen(point);
linePoints.push(screenPoint.x);
linePoints.push(screenPoint.y);
}));
this.addLine(linePoints, line.color, line.type, line.width, line.id);
}));
(_c = viewpoint.texts) === null || _c === undefined ? undefined : _c.forEach((text => {
const screenPoint = this._worldTransformer.worldToScreen(text.position);
this.addText(text.text, screenPoint, text.angle, text.color, text.text_size, text.font_size, text.id);
}));
(_d = viewpoint.rectangles) === null || _d === undefined ? undefined : _d.forEach((rect => {
const screenPoint = this._worldTransformer.worldToScreen(rect.position);
this.addRectangle(screenPoint, rect.width, rect.height, rect.line_width, rect.color, rect.id);
}));
(_e = viewpoint.ellipses) === null || _e === undefined ? undefined : _e.forEach((ellipse => {
const screenPoint = this._worldTransformer.worldToScreen(ellipse.position);
this.addEllipse(screenPoint, ellipse.radius, ellipse.line_width, ellipse.color, ellipse.id);
}));
(_f = viewpoint.arrows) === null || _f === undefined ? undefined : _f.forEach((arrow => {
const startPoint = this._worldTransformer.worldToScreen(arrow.start);
const endPoint = this._worldTransformer.worldToScreen(arrow.end);
this.addArrow(startPoint, endPoint, arrow.color, arrow.id);
}));
(_g = viewpoint.clouds) === null || _g === undefined ? undefined : _g.forEach((cloud => {
const screenPoint = this._worldTransformer.worldToScreen(cloud.position);
this.addCloud(screenPoint, cloud.width, cloud.height, cloud.line_width, cloud.color, cloud.id);
}));
(_h = viewpoint.images) === null || _h === undefined ? undefined : _h.forEach((image => {
const screenPoint = this._worldTransformer.worldToScreen(image.position);
this.addImage(screenPoint, image.src, image.width, image.height, image.id);
}));
}
getViewpoint(viewpoint) {
if (!viewpoint) viewpoint = {};
viewpoint.lines = this.getMarkupLines();
viewpoint.texts = this.getMarkupTexts();
viewpoint.arrows = this.getMarkupArrows();
viewpoint.clouds = this.getMarkupClouds();
viewpoint.ellipses = this.getMarkupEllipses();
viewpoint.images = this.getMarkupImages();
viewpoint.rectangles = this.getMarkupRectangles();
viewpoint.custom_fields = {
markup_color: this.getMarkupColor()
};
viewpoint.snapshot = {
data: this.combineMarkupWithDrawing()
};
return viewpoint;
}
enableEditMode(mode) {
if (!mode || !MarkupMode2Konva[mode]) {
this.clearSelected();
this.removeTextInput();
this.removeImageInput();
this._markupContainer.style.pointerEvents = "none";
this._markupIsActive = false;
} else {
this._markupMode = mode;
this._markupContainer.style.pointerEvents = "all";
this._markupIsActive = true;
}
return this;
}
createObject(type, params) {
const konvaShape = MarkupMode2Konva[type];
if (!konvaShape || !konvaShape.initializer) throw new Error(`Markup CreateObject - unsupported markup type ${type}`);
const object = konvaShape.initializer(null, params);
this.addObject(object);
return object;
}
getObjects() {
const objects = [];
Object.keys(MarkupMode2Konva).forEach((type => {
const konvaShape = MarkupMode2Konva[type];
this.konvaLayerFind(type).forEach((ref => objects.push(konvaShape.initializer(ref))));
}));
return objects;
}
getSelectedObjects() {
if (!this._konvaTransformer) return [];
return this._konvaTransformer.nodes().map((ref => {
const name = ref.className;
const konvaShape = Object.values(MarkupMode2Konva).find((shape => shape.name === name));
return konvaShape ? konvaShape.initializer(ref) : null;
})).filter((x => x));
}
selectObjects(objects) {
if (!this._konvaTransformer) return;
const selectedObjs = this._konvaTransformer.nodes().concat(objects.map((x => x.ref())));
this._konvaTransformer.nodes(selectedObjs);
}
clearSelected() {
if (this._konvaTransformer) this._konvaTransformer.nodes([]);
}
addObject(object) {
if (object.type() === "Image") this._groupImages.add(object.ref()); else if (object.type() === "Text") this._groupTexts.add(object.ref()); else this._groupGeometry.add(object.ref());
}
konvaLayerFind(type) {
if (!this._konvaLayer) return [];
const konvaShape = MarkupMode2Konva[type];
if (!konvaShape || !konvaShape.initializer) return [];
return this._konvaLayer.find(konvaShape.name).filter((ref => ref.parent === this._konvaLayer || ref.parent === this._groupImages || ref.parent === this._groupGeometry || ref.parent === this._groupTexts));
}
initializeKonva() {
const stage = new Konva.Stage({
container: this._markupContainer,
width: this._container.clientWidth,
height: this._container.clientHeight
});
this._konvaStage = stage;
const layer = new Konva.Layer({
pixelRation: window.devicePixelRatio
});
stage.add(layer);
this._groupImages = new Konva.Group;
layer.add(this._groupImages);
this._groupGeometry = new Konva.Group;
layer.add(this._groupGeometry);
this._groupTexts = new Konva.Group;
layer.add(this._groupTexts);
this._konvaLayer = layer;
const transformer = new Konva.Transformer({
shouldOverdrawWholeArea: false,
keepRatio: false,
flipEnabled: false
});
layer.add(transformer);
this._konvaTransformer = transformer;
let isPaint = false;
let lastLine;
let mouseDownPos;
let lastObj;
stage.on("mousedown touchstart", (e => {
if (!this._markupIsActive || e.target !== stage || this._markupMode === "Text" || this._markupMode === "Image") return;
if (e.target === stage && transformer.nodes().length > 0) {
transformer.nodes([]);
return;
}
const pos = this.getRelativePointerPosition(stage);
mouseDownPos = pos;
isPaint = [ "Arrow", "Cloud", "Ellipse", "Line", "Rectangle" ].some((m => m === this._markupMode));
if (this._markupMode === "Line") {
lastLine = this.addLine([ pos.x, pos.y, pos.x, pos.y ]);
}
}));
stage.on("mouseup touchend", (e => {
if (!this._markupIsActive) return;
if (isPaint) {
const pos = this.getRelativePointerPosition(stage);
const defParams = mouseDownPos && pos.x === mouseDownPos.x && pos.y === mouseDownPos.y;
const startX = defParams ? mouseDownPos.x : Math.min(mouseDownPos.x, pos.x);
const startY = defParams ? mouseDownPos.y : Math.min(mouseDownPos.y, pos.y);
const dX = defParams ? 200 : Math.abs(mouseDownPos.x - pos.x);
const dY = defParams ? 200 : Math.abs(mouseDownPos.y - pos.y);
if (defParams) {
if (this._markupMode === "Rectangle") {
this.addRectangle({
x: startX,
y: startY
}, dX, dY);
} else if (this._markupMode === "Ellipse") {
this.addEllipse({
x: startX,
y: startY
}, {
x: dX / 2,
y: dY / 2
});
} else if (this._markupMode === "Arrow") {
this.addArrow({
x: mouseDownPos.x,
y: mouseDownPos.y
}, {
x: defParams ? mouseDownPos.x + 200 : pos.x,
y: defParams ? startY : pos.y
});
} else if (this._markupMode === "Cloud") {
this.addCloud({
x: startX,
y: startY
}, Math.max(100, dX), Math.max(100, dY));
}
}
}
lastObj = undefined;
isPaint = false;
}));
stage.on("mousemove touchmove", (e => {
if (!this._markupIsActive) return;
if (!isPaint) {
return;
}
const pos = this.getRelativePointerPosition(stage);
const defParams = mouseDownPos && pos.x === mouseDownPos.x && pos.y === mouseDownPos.y;
const startX = defParams ? mouseDownPos.x : Math.min(mouseDownPos.x, pos.x);
const startY = defParams ? mouseDownPos.y : Math.min(mouseDownPos.y, pos.y);
const dX = defParams ? 200 : Math.abs(mouseDownPos.x - pos.x);
const dY = defParams ? 200 : Math.abs(mouseDownPos.y - pos.y);
if (this._markupMode === "Line") {
lastLine.addPoints([ {
x: pos.x,
y: pos.y
} ]);
} else if (this._markupMode === "Arrow") {
if (lastObj) lastObj.setEndPoint(pos.x, pos.y); else lastObj = this.addArrow({
x: mouseDownPos.x,
y: mouseDownPos.y
}, {
x: pos.x,
y: pos.y
});
} else if (this._markupMode === "Rectangle") {
if (lastObj) {
lastObj.setPosition(startX, startY);
lastObj.setWidth(dX);
lastObj.setHeight(dY);
} else lastObj = this.addRectangle({
x: startX,
y: startY
}, dX, dY);
} else if (this._markupMode === "Ellipse") {
if (lastObj) {
lastObj.setPosition(startX, startY);
lastObj.setRadiusX(dX);
lastObj.setRadiusY(dY);
} else lastObj = this.addEllipse({
x: startX,
y: startY
}, {
x: dX,
y: dY
});
} else if (this._markupMode === "Cloud") {
if (lastObj) {
lastObj.setPosition(startX, startY);
lastObj.setWidth(Math.max(100, dX));
lastObj.setHeight(Math.max(100, dY));
} else lastObj = this.addCloud({
x: startX,
y: startY
}, dX, dY);
}
}));
stage.on("click tap", (e => {
if (!this._markupIsActive) return;
if (e.target === stage) {
if (this._markupMode === "Text") {
if (this._textInputRef && this._textInputRef.value) this.addText(this._textInputRef.value, this._textInputPos, this._textInputAngle); else if (transformer.nodes().length === 0) {
const pos = this.getRelativePointerPosition(stage);
this.createTextInput(pos, e.evt.pageX, e.evt.pageY, 0, null);
}
} else if (this._markupMode === "Image") {
if (this._imageInputRef && this._imageInputRef.value) this.addImage({
x: this._imageInputPos.x,
y: this._imageInputPos.y
}, this._imageInputRef.value, 0, 0, this._imageInputRef.value); else if (transformer.nodes().length === 0) {
const pos = this.getRelativePointerPosition(stage);
this.createImageInput(pos);
}
}
transformer.nodes([]);
return;
}
if (this._markupMode === "Text" || this._markupMode === "SelectMarkup") {
if (e.target.className === "Text" && transformer.nodes().length === 1 && transformer.nodes()[0] === e.target) {
if (this._textInputRef && this._textInputRef.value) this.addText(this._textInputRef.value, this._textInputPos, this._textInputAngle); else this.createTextInput({
x: e.target.attrs.x,
y: e.target.attrs.y
}, e.evt.pageX, e.evt.pageY, e.target.attrs.rotation, e.target.attrs.text);
return;
} else {
this.removeTextInput();
}
}
if (this._markupMode === "Image" || this._markupMode === "SelectMarkup") {
if (e.target.className === "Image" && transformer.nodes().length === 1 && transformer.nodes()[0] === e.target) {
if (this._imageInputRef && this._imageInputRef.value) this.addImage(this._imageInputPos, this._imageInputRef.value, 0, 0); else this.createImageInput({
x: e.target.attrs.x,
y: e.target.attrs.y
});
return;
} else {
this.removeImageInput();
}
}
if (transformer.nodes().filter((x => x.className === "Cloud" || x.className === "Image")).length > 0 || e.target.className === "Cloud" || e.target.className === "Image") {
transformer.rotateEnabled(false);
} else {