@leafer-in/editor
Version:
1,112 lines (1,085 loc) • 107 kB
JavaScript
this.LeaferIN = this.LeaferIN || {};
this.LeaferIN.editor = function(exports, draw, core) {
"use strict";
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
function toList(value) {
return value ? draw.isArray(value) ? value : [ value ] : [];
}
class EditorEvent extends draw.Event {
get list() {
return toList(this.value);
}
get oldList() {
return toList(this.oldValue);
}
constructor(type, data) {
super(type);
if (data) Object.assign(this, data);
}
}
EditorEvent.BEFORE_SELECT = "editor.before_select";
EditorEvent.SELECT = "editor.select";
EditorEvent.AFTER_SELECT = "editor.after_select";
EditorEvent.BEFORE_HOVER = "editor.before_hover";
EditorEvent.HOVER = "editor.hover";
function targetAttr(fn) {
return (target, key) => {
const privateKey = "_" + key;
draw.defineKey(target, key, {
get() {
return this[privateKey];
},
set(value) {
const old = this[privateKey];
if (old !== value) {
const t = this;
if (t.config) {
const isSelect = key === "target";
if (isSelect) {
const {beforeSelect: beforeSelect} = t.config;
if (beforeSelect) {
const check = beforeSelect({
target: value
});
if (draw.isObject(check)) value = check; else if (check === false) return;
}
if (t.hasDimOthers) t.cancelDimOthers();
if (draw.isArray(value) && value.length > 1 && value[0].locked) value.splice(0, 1);
if (t.single) {
delete t.element.syncEventer;
delete t.element.__world.ignorePixelSnap;
}
}
const type = isSelect ? EditorEvent.BEFORE_SELECT : EditorEvent.BEFORE_HOVER;
if (this.hasEvent(type)) this.emitEvent(new EditorEvent(type, {
editor: t,
value: value,
oldValue: old
}));
}
this[privateKey] = value, fn(this, old);
}
}
});
};
}
function mergeConfigAttr() {
return (target, key) => {
draw.defineKey(target, key, {
get() {
const {config: config, element: element, dragPoint: dragPoint, editBox: editBox, editTool: editTool, innerEditor: innerEditor, app: app} = this, mergeConfig = Object.assign({}, config);
if (innerEditor) innerEditor.editConfig && Object.assign(mergeConfig, innerEditor.editConfig); else if (editTool) editTool.editConfig && Object.assign(mergeConfig, editTool.editConfig);
if (element && element.editConfig) {
let {editConfig: editConfig} = element;
if (editConfig.hover || editConfig.hoverStyle) {
editConfig = Object.assign({}, editConfig);
delete editConfig.hover;
delete editConfig.hoverStyle;
}
Object.assign(mergeConfig, editConfig);
}
if (editBox.config) Object.assign(mergeConfig, editBox.config);
if (dragPoint) {
if (dragPoint.editConfig) Object.assign(mergeConfig, dragPoint.editConfig);
if (mergeConfig.editSize === "font-size") mergeConfig.lockRatio = true;
if (dragPoint.pointType === "resize-rotate") {
mergeConfig.around || (mergeConfig.around = "center");
draw.isNull(mergeConfig.lockRatio) && (mergeConfig.lockRatio = true);
}
}
if (draw.isUndefined(mergeConfig.dragLimitAnimate)) mergeConfig.dragLimitAnimate = app && app.config.pointer.dragLimitAnimate;
return this.mergedConfig = mergeConfig;
}
});
};
}
const {abs: abs$1} = Math;
const {copy: copy$1} = draw.MatrixHelper;
const {setListWithFn: setListWithFn} = draw.BoundsHelper;
const {worldBounds: worldBounds} = draw.LeafBoundsHelper;
const matrix = draw.getMatrixData();
const bounds$1 = draw.getBoundsData();
class Stroker extends draw.UI {
constructor() {
super();
this.list = [];
this.visible = 0;
this.hittable = false;
this.strokeAlign = "center";
}
setTarget(target, style) {
if (style) this.set(style);
this.target = target;
this.update();
}
update(style) {
const {list: list} = this;
if (list.length) {
setListWithFn(bounds$1, list, worldBounds);
if (style) this.set(style);
this.set(bounds$1);
this.visible = true;
} else this.visible = 0;
}
__draw(canvas, options) {
const {list: list} = this;
if (list.length) {
let leaf;
const data = this.__, {stroke: stroke, strokeWidth: strokeWidth, fill: fill} = data, {bounds: bounds} = options;
for (let i = 0; i < list.length; i++) {
leaf = list[i];
const {worldTransform: worldTransform, worldRenderBounds: worldRenderBounds} = leaf;
if (worldRenderBounds.width && worldRenderBounds.height && (!bounds || bounds.hit(worldRenderBounds, options.matrix))) {
const aScaleX = abs$1(worldTransform.scaleX), aScaleY = abs$1(worldTransform.scaleY);
copy$1(matrix, worldTransform);
matrix.half = strokeWidth % 2;
canvas.setWorld(matrix, options.matrix);
canvas.beginPath();
if (this.strokePathType === "path") {
leaf.__drawPath(canvas);
} else {
if (leaf.__.__useArrow) leaf.__drawPath(canvas); else leaf.__.__pathForRender ? leaf.__drawRenderPath(canvas) : leaf.__drawPathByBox(canvas);
}
data.strokeWidth = strokeWidth / Math.max(aScaleX, aScaleY);
if (data.shadow) {
const shadow = data.shadow[0], {scaleX: scaleX, scaleY: scaleY} = this.getRenderScaleData(true, shadow.scaleFixed);
canvas.save(), canvas.setWorldShadow(shadow.x * scaleX, shadow.y * scaleY, shadow.blur * scaleX, draw.ColorConvert.string(shadow.color));
}
if (stroke) draw.isString(stroke) ? draw.Paint.stroke(stroke, this, canvas, options) : draw.Paint.strokes(stroke, this, canvas, options);
if (fill) draw.isString(fill) ? draw.Paint.fill(fill, this, canvas, options) : draw.Paint.fills(fill, this, canvas, options);
if (data.shadow) canvas.restore();
}
}
data.strokeWidth = strokeWidth;
}
}
destroy() {
this.target = null;
super.destroy();
}
}
__decorate([ targetAttr(onTarget$1) ], Stroker.prototype, "target", void 0);
__decorate([ draw.surfaceType("render-path") ], Stroker.prototype, "strokePathType", void 0);
function onTarget$1(stroker) {
const value = stroker.target;
stroker.list = value ? draw.isArray(value) ? value : [ value ] : [];
}
class SelectArea extends draw.Group {
constructor(data) {
super(data);
this.strokeArea = new draw.Rect({
strokeAlign: "center"
});
this.fillArea = new draw.Rect;
this.visible = 0;
this.hittable = false;
this.addMany(this.fillArea, this.strokeArea);
}
setStyle(style, userStyle) {
const {visible: visible, stroke: stroke, strokeWidth: strokeWidth} = style;
this.visible = visible;
this.strokeArea.reset(Object.assign({
stroke: stroke,
strokeWidth: strokeWidth
}, userStyle || {}));
this.fillArea.reset({
visible: userStyle ? false : true,
fill: stroke,
opacity: .2
});
}
setBounds(bounds) {
this.strokeArea.set(bounds);
this.fillArea.set(bounds);
}
}
const EditSelectHelper = {
findOne(path) {
return path.list.find(leaf => leaf.editable);
},
findByBounds(branch, bounds) {
const list = [];
eachFind([ branch ], list, bounds);
return list;
}
};
function eachFind(children, list, bounds) {
let child, data;
for (let i = 0, len = children.length; i < len; i++) {
child = children[i], data = child.__;
if (data.hittable && data.visible && !data.locked && bounds.hit(child.__world)) {
if (data.editable) {
if (child.isBranch && !data.hitChildren) {
if (data.hitSelf) list.push(child);
continue;
} else if (child.isFrame) {
if (bounds.includes(child.__layout.boxBounds, child.__world)) {
list.push(child);
continue;
}
} else if (bounds.hit(child.__layout.boxBounds, child.__world) && data.hitSelf) list.push(child);
}
if (child.isBranch) eachFind(child.children, list, bounds);
}
}
}
const {findOne: findOne, findByBounds: findByBounds} = EditSelectHelper;
class EditSelect extends draw.Group {
get dragging() {
return !!this.originList;
}
get running() {
const {editor: editor, app: app} = this;
return this.hittable && editor.visible && editor.hittable && editor.mergeConfig.selector && (app && app.mode === "normal");
}
get isMoveMode() {
const {app: app} = this;
return app && app.interaction.moveMode;
}
constructor(editor) {
super();
this.hoverStroker = new Stroker;
this.targetStroker = new Stroker;
this.bounds = new draw.Bounds;
this.selectArea = new SelectArea;
this.__eventIds = [];
this.editor = editor;
this.addMany(this.targetStroker, this.hoverStroker, this.selectArea);
this.__listenEvents();
}
onHover() {
const {editor: editor} = this;
if (this.running && !this.dragging && !editor.dragging) {
const {hoverTarget: hoverTarget, mergeConfig: mergeConfig} = editor, config = Object.assign({}, mergeConfig);
if (hoverTarget && hoverTarget.editConfig) Object.assign(config, hoverTarget.editConfig);
const {stroke: stroke, strokeWidth: strokeWidth, hover: hover, hoverStyle: hoverStyle} = config;
this.hoverStroker.setTarget(hover ? hoverTarget : null, Object.assign({
stroke: stroke,
strokeWidth: strokeWidth
}, hoverStyle || {}));
} else {
this.hoverStroker.target = null;
}
}
onSelect() {
if (this.running) {
this.targetStroker.setTarget(this.editor.list);
this.hoverStroker.target = null;
}
}
update() {
this.hoverStroker.update();
const {stroke: stroke, strokeWidth: strokeWidth, selectedPathType: selectedPathType, selectedStyle: selectedStyle} = this.editor.mergedConfig;
this.targetStroker.update(Object.assign({
stroke: stroke,
strokeWidth: strokeWidth && Math.max(1, strokeWidth / 2),
strokePathType: selectedPathType
}, selectedStyle || {}));
}
onPointerMove(e) {
const {app: app, editor: editor} = this;
if (this.running && !this.isMoveMode && app.interaction.canHover && !app.interaction.dragging) {
const find = this.findUI(e);
editor.hoverTarget = editor.hasItem(find) ? null : find;
}
if (this.isMoveMode) {
editor.hoverTarget = null;
}
}
onBeforeDown(e) {
if (e.multiTouch) return;
const {select: select} = this.editor.mergeConfig;
if (select === "press") {
if (this.app.config.mobile) {
this.waitSelect = () => this.checkAndSelect(e);
} else {
this.checkAndSelect(e);
}
}
}
onTap(e) {
if (e.multiTouch) return;
const {editor: editor} = this;
const {select: select, selectKeep: selectKeep} = editor.mergeConfig;
if (select === "tap") this.checkAndSelect(e); else if (this.waitSelect) this.waitSelect();
if (this.needRemoveItem) {
editor.removeItem(this.needRemoveItem);
} else if (this.isMoveMode) {
if (!selectKeep) editor.target = null;
}
}
checkAndSelect(e) {
this.needRemoveItem = null;
if (this.allowSelect(e)) {
const {editor: editor} = this;
const find = this.findUI(e);
if (find) {
if (this.isMultipleSelect(e)) {
if (editor.hasItem(find)) this.needRemoveItem = find; else editor.addItem(find);
} else {
editor.target = find;
}
e.path.needUpdate = true;
} else if (this.allow(e.target)) {
if (!this.isHoldMultipleSelectKey(e) && !this.editor.mergedConfig.selectKeep) editor.target = null;
}
}
}
onDragStart(e) {
if (e.multiTouch) return;
if (this.waitSelect) this.waitSelect();
if (this.allowDrag(e)) {
const {editor: editor} = this;
const {stroke: stroke, area: area} = editor.mergeConfig;
const {x: x, y: y} = e.getInnerPoint(this);
this.bounds.set(x, y);
this.selectArea.setStyle({
visible: true,
stroke: stroke,
x: x,
y: y
}, area);
this.selectArea.setBounds(this.bounds.get());
this.originList = editor.leafList.clone();
}
}
onDrag(e) {
if (e.multiTouch) return;
if (this.editor.dragging) return this.onDragEnd(e);
if (this.dragging) {
const {editor: editor} = this;
const total = e.getInnerTotal(this);
const dragBounds = this.bounds.clone().unsign();
const list = new draw.LeafList(findByBounds(editor.app, dragBounds));
this.bounds.width = total.x;
this.bounds.height = total.y;
this.selectArea.setBounds(dragBounds.get());
if (list.length) {
const selectList = [];
this.originList.forEach(item => {
if (!list.has(item)) selectList.push(item);
});
list.forEach(item => {
if (!this.originList.has(item)) selectList.push(item);
});
if (selectList.length !== editor.list.length || editor.list.some((child, index) => child !== selectList[index])) {
editor.target = selectList;
}
} else {
editor.target = this.originList.list;
}
}
}
onDragEnd(e) {
if (e.multiTouch) return;
if (this.dragging) this.originList = null, this.selectArea.visible = 0;
}
onAutoMove(e) {
if (this.dragging) {
const {x: x, y: y} = e.getLocalMove(this);
this.bounds.x += x;
this.bounds.y += y;
}
}
allow(target) {
return target.leafer !== this.editor.leafer;
}
allowDrag(e) {
const {boxSelect: boxSelect, multipleSelect: multipleSelect} = this.editor.mergeConfig;
if (this.running && (multipleSelect && boxSelect) && !e.target.draggable) {
return !this.editor.editing && this.allow(e.target) || this.isHoldMultipleSelectKey(e) && !findOne(e.path);
} else {
return false;
}
}
allowSelect(e) {
return this.running && !this.isMoveMode && !e.middle;
}
findDeepOne(e) {
const options = {
exclude: new draw.LeafList(this.editor.editBox.rect)
};
return findOne(e.target.leafer.interaction.findPath(e, options));
}
findUI(e) {
return this.isMultipleSelect(e) ? this.findDeepOne(e) : findOne(e.path);
}
isMultipleSelect(e) {
const {multipleSelect: multipleSelect, continuousSelect: continuousSelect} = this.editor.mergeConfig;
return multipleSelect && (this.isHoldMultipleSelectKey(e) || continuousSelect);
}
isHoldMultipleSelectKey(e) {
const {multipleSelectKey: multipleSelectKey} = this.editor.mergedConfig;
if (multipleSelectKey) return e.isHoldKeys(multipleSelectKey);
return e.shiftKey;
}
__listenEvents() {
const {editor: editor} = this;
editor.waitLeafer(() => {
const {app: app} = editor;
app.selector.proxy = editor;
this.__eventIds = [ editor.on_([ [ EditorEvent.HOVER, this.onHover, this ], [ EditorEvent.SELECT, this.onSelect, this ] ]), app.on_([ [ core.PointerEvent.MOVE, this.onPointerMove, this ], [ core.PointerEvent.BEFORE_DOWN, this.onBeforeDown, this ], [ core.PointerEvent.TAP, this.onTap, this ], [ core.DragEvent.START, this.onDragStart, this, true ], [ core.DragEvent.DRAG, this.onDrag, this ], [ core.DragEvent.END, this.onDragEnd, this ], [ core.MoveEvent.MOVE, this.onAutoMove, this ], [ [ core.ZoomEvent.ZOOM, core.MoveEvent.MOVE ], () => {
this.editor.hoverTarget = null;
} ] ]) ];
});
}
__removeListenEvents() {
this.off_(this.__eventIds);
}
destroy() {
this.editor = this.originList = this.needRemoveItem = null;
this.__removeListenEvents();
super.destroy();
}
}
const {topLeft: topLeft, top: top, topRight: topRight, right: right$1, bottomRight: bottomRight, bottom: bottom, bottomLeft: bottomLeft, left: left$1} = draw.Direction9;
const {toPoint: toPoint} = draw.AroundHelper, {within: within, sign: sign} = draw.MathHelper, {abs: abs} = Math;
const EditDataHelper = {
getScaleData(target, startBounds, direction, totalMoveOrScale, lockRatio, around, flipable, scaleMode) {
let align, origin = {}, scaleX = 1, scaleY = 1, lockScale;
const {boxBounds: boxBounds, widthRange: widthRange, heightRange: heightRange, dragBounds: dragBounds, worldBoxBounds: worldBoxBounds} = target;
const {width: width, height: height} = startBounds;
const originChangedScaleX = target.scaleX / startBounds.scaleX;
const originChangedScaleY = target.scaleY / startBounds.scaleY;
const signX = sign(originChangedScaleX);
const signY = sign(originChangedScaleY);
const changedScaleX = scaleMode ? originChangedScaleX : signX * boxBounds.width / width;
const changedScaleY = scaleMode ? originChangedScaleY : signY * boxBounds.height / height;
if (draw.isNumber(totalMoveOrScale)) {
scaleX = scaleY = Math.sqrt(totalMoveOrScale);
} else {
if (around) {
totalMoveOrScale.x *= 2;
totalMoveOrScale.y *= 2;
}
totalMoveOrScale.x *= scaleMode ? originChangedScaleX : signX;
totalMoveOrScale.y *= scaleMode ? originChangedScaleY : signY;
const topScale = (-totalMoveOrScale.y + height) / height;
const rightScale = (totalMoveOrScale.x + width) / width;
const bottomScale = (totalMoveOrScale.y + height) / height;
const leftScale = (-totalMoveOrScale.x + width) / width;
switch (direction) {
case top:
scaleY = topScale;
align = "bottom";
break;
case right$1:
scaleX = rightScale;
align = "left";
break;
case bottom:
scaleY = bottomScale;
align = "top";
break;
case left$1:
scaleX = leftScale;
align = "right";
break;
case topLeft:
scaleY = topScale;
scaleX = leftScale;
align = "bottom-right";
break;
case topRight:
scaleY = topScale;
scaleX = rightScale;
align = "bottom-left";
break;
case bottomRight:
scaleY = bottomScale;
scaleX = rightScale;
align = "top-left";
break;
case bottomLeft:
scaleY = bottomScale;
scaleX = leftScale;
align = "top-right";
}
if (lockRatio) {
if (lockRatio === "corner" && direction % 2) {
lockRatio = false;
} else {
switch (direction) {
case top:
case bottom:
scaleX = scaleY;
break;
case left$1:
case right$1:
scaleY = scaleX;
break;
default:
lockScale = Math.sqrt(abs(scaleX * scaleY));
scaleX = sign(scaleX) * lockScale;
scaleY = sign(scaleY) * lockScale;
}
}
}
}
const useScaleX = scaleX !== 1, useScaleY = scaleY !== 1;
if (useScaleX) scaleX /= changedScaleX;
if (useScaleY) scaleY /= changedScaleY;
if (!flipable) {
const {worldTransform: worldTransform} = target;
if (scaleX < 0) scaleX = 1 / boxBounds.width / worldTransform.scaleX;
if (scaleY < 0) scaleY = 1 / boxBounds.height / worldTransform.scaleY;
}
toPoint(around || align, boxBounds, origin, true);
if (dragBounds) {
const scaleData = {
x: scaleX,
y: scaleY
};
core.DragBoundsHelper.limitScaleOf(target, origin, scaleData, lockRatio);
scaleX = scaleData.x;
scaleY = scaleData.y;
}
if (useScaleX && widthRange) {
const nowWidth = boxBounds.width * target.scaleX;
scaleX = within(nowWidth * scaleX, widthRange) / nowWidth;
}
if (useScaleY && heightRange) {
const nowHeight = boxBounds.height * target.scaleY;
scaleY = within(nowHeight * scaleY, heightRange) / nowHeight;
}
if (useScaleX && abs(scaleX * worldBoxBounds.width) < 1) scaleX = sign(scaleX) / worldBoxBounds.width;
if (useScaleY && abs(scaleY * worldBoxBounds.height) < 1) scaleY = sign(scaleY) / worldBoxBounds.height;
if (lockRatio && scaleX !== scaleY) {
lockScale = Math.min(abs(scaleX), abs(scaleY));
scaleX = sign(scaleX) * lockScale;
scaleY = sign(scaleY) * lockScale;
}
isFinite(scaleX) || (scaleX = 1);
isFinite(scaleY) || (scaleY = 1);
return {
origin: origin,
scaleX: scaleX,
scaleY: scaleY,
direction: direction,
lockRatio: lockRatio,
around: around
};
},
getRotateData(target, direction, current, last, around) {
let align, origin = {};
switch (direction) {
case topLeft:
align = "bottom-right";
break;
case topRight:
align = "bottom-left";
break;
case bottomRight:
align = "top-left";
break;
case bottomLeft:
align = "top-right";
break;
default:
align = "center";
}
toPoint(around || align, target.boxBounds, origin, true);
return {
origin: origin,
rotation: draw.PointHelper.getRotation(last, target.getWorldPointByBox(origin), current)
};
},
getSkewData(bounds, direction, move, around) {
let align, origin = {}, skewX = 0, skewY = 0;
let last;
switch (direction) {
case top:
case topLeft:
last = {
x: .5,
y: 0
};
align = "bottom";
skewX = 1;
break;
case bottom:
case bottomRight:
last = {
x: .5,
y: 1
};
align = "top";
skewX = 1;
break;
case left$1:
case bottomLeft:
last = {
x: 0,
y: .5
};
align = "right";
skewY = 1;
break;
case right$1:
case topRight:
last = {
x: 1,
y: .5
};
align = "left";
skewY = 1;
}
const {width: width, height: height} = bounds;
last.x = last.x * width;
last.y = last.y * height;
toPoint(around || align, bounds, origin, true);
const rotation = draw.PointHelper.getRotation(last, origin, {
x: last.x + (skewX ? move.x : 0),
y: last.y + (skewY ? move.y : 0)
});
skewX ? skewX = -rotation : skewY = rotation;
return {
origin: origin,
skewX: skewX,
skewY: skewY
};
},
getAround(around, altKey) {
return altKey && !around ? "center" : around;
},
getRotateDirection(direction, rotation, totalDirection = 8) {
direction = (direction + Math.round(rotation / (360 / totalDirection))) % totalDirection;
if (direction < 0) direction += totalDirection;
return direction;
},
getFlipDirection(direction, flipedX, flipedY) {
if (flipedX) {
switch (direction) {
case left$1:
direction = right$1;
break;
case topLeft:
direction = topRight;
break;
case bottomLeft:
direction = bottomRight;
break;
case right$1:
direction = left$1;
break;
case topRight:
direction = topLeft;
break;
case bottomRight:
direction = bottomLeft;
break;
}
}
if (flipedY) {
switch (direction) {
case top:
direction = bottom;
break;
case topLeft:
direction = bottomLeft;
break;
case topRight:
direction = bottomRight;
break;
case bottom:
direction = top;
break;
case bottomLeft:
direction = topLeft;
break;
case bottomRight:
direction = topRight;
break;
}
}
return direction;
}
};
const cacheCursors = {};
function updatePointCursor(editBox, e) {
const {enterPoint: point, dragging: dragging, skewing: skewing, resizing: resizing, flippedX: flippedX, flippedY: flippedY} = editBox;
if (!point || !editBox.editor.editing || !editBox.canUse) return;
if (point.name === "rect") return updateMoveCursor(editBox);
if (point.name === "circle") return;
let {rotation: rotation} = editBox;
const {pointType: pointType} = point, {moveCursor: moveCursor, resizeCursor: resizeCursor, rotateCursor: rotateCursor, skewCursor: skewCursor, moveable: moveable, resizeable: resizeable, rotateable: rotateable, skewable: skewable} = editBox.mergeConfig;
if (pointType === "move") {
point.cursor = moveCursor;
if (!moveable) point.visible = false;
return;
} else if (pointType === "button") {
if (!point.cursor) point.cursor = "pointer";
return;
}
let showResize = pointType.includes("resize");
if (showResize && rotateable && (editBox.isHoldRotateKey(e) || !resizeable)) showResize = false;
const showSkew = skewable && !showResize && (point.name === "resize-line" || pointType === "skew");
const cursor = dragging ? skewing ? skewCursor : resizing ? resizeCursor : rotateCursor : showSkew ? skewCursor : showResize ? resizeCursor : rotateCursor;
rotation += (EditDataHelper.getFlipDirection(point.direction, flippedX, flippedY) + 1) * 45;
rotation = Math.round(draw.MathHelper.formatRotation(rotation, true) / 2) * 2;
const {url: url, x: x, y: y} = cursor;
const key = url + rotation;
if (cacheCursors[key]) {
point.cursor = cacheCursors[key];
} else {
cacheCursors[key] = point.cursor = {
url: toDataURL(url, rotation),
x: x,
y: y
};
}
}
function updateMoveCursor(editBox) {
const {moveCursor: moveCursor, moveable: moveable} = editBox.mergeConfig;
if (editBox.canUse) editBox.rect.cursor = moveable ? moveCursor : undefined;
}
function toDataURL(svg, rotation) {
return '"data:image/svg+xml,' + encodeURIComponent(svg.replace("{{rotation}}", rotation.toString())) + '"';
}
class EditPoint extends draw.Box {
constructor(data) {
super(data);
this.useFastShadow = true;
}
}
const fourDirection = [ "top", "right", "bottom", "left" ], editConfig = undefined;
class EditBox extends draw.Group {
get mergeConfig() {
const {config: config} = this, {mergeConfig: mergeConfig, editBox: editBox} = this.editor;
return this.mergedConfig = config && editBox !== this ? Object.assign(Object.assign({}, mergeConfig), config) : mergeConfig;
}
get target() {
return this._target || this.editor.element;
}
set target(target) {
this._target = target;
}
get single() {
return !!this._target || this.editor.single;
}
get transformTool() {
return this._transformTool || this.editor;
}
set transformTool(tool) {
this._transformTool = tool;
}
get flipped() {
return this.flippedX || this.flippedY;
}
get flippedX() {
return this.scaleX < 0;
}
get flippedY() {
return this.scaleY < 0;
}
get flippedOne() {
return this.scaleX * this.scaleY < 0;
}
get canUse() {
return this.app && this.editor.editing;
}
get canGesture() {
if (!this.canUse) return false;
const {moveable: moveable, resizeable: resizeable, rotateable: rotateable} = this.mergeConfig;
return draw.isString(moveable) || draw.isString(resizeable) || draw.isString(rotateable);
}
get canDragLimitAnimate() {
return this.moving && this.mergeConfig.dragLimitAnimate && this.target.dragBounds;
}
constructor(editor) {
super();
this.view = new draw.Group;
this.rect = new EditPoint({
name: "rect",
hitFill: "all",
hitStroke: "none",
strokeAlign: "center",
hitRadius: 5
});
this.circle = new EditPoint({
name: "circle",
strokeAlign: "center",
around: "center",
cursor: "crosshair",
hitRadius: 5
});
this.buttons = new draw.Group({
around: "center",
hitSelf: false,
visible: 0
});
this.resizePoints = [];
this.rotatePoints = [];
this.resizeLines = [];
this.dragStartData = {};
this.__eventIds = [];
this.editor = editor;
this.visible = false;
this.create();
this.__listenEvents();
}
create() {
let rotatePoint, resizeLine, resizePoint;
const {view: view, resizePoints: resizePoints, rotatePoints: rotatePoints, resizeLines: resizeLines, rect: rect, circle: circle, buttons: buttons} = this;
const arounds = [ "bottom-right", "bottom", "bottom-left", "left", "top-left", "top", "top-right", "right" ];
for (let i = 0; i < 8; i++) {
rotatePoint = new EditPoint({
name: "rotate-point",
around: arounds[i],
width: 15,
height: 15,
hitFill: "all"
});
rotatePoints.push(rotatePoint);
this.listenPointEvents(rotatePoint, "rotate", i);
if (i % 2) {
resizeLine = new EditPoint({
name: "resize-line",
around: "center",
width: 10,
height: 10,
hitFill: "all"
});
resizeLines.push(resizeLine);
this.listenPointEvents(resizeLine, "resize", i);
}
resizePoint = new EditPoint({
name: "resize-point",
hitRadius: 5
});
resizePoints.push(resizePoint);
this.listenPointEvents(resizePoint, "resize", i);
}
this.listenPointEvents(circle, "rotate", 2);
this.listenPointEvents(rect, "move", 8);
view.addMany(...rotatePoints, rect, circle, buttons, ...resizeLines, ...resizePoints);
this.add(view);
}
load() {
const {target: target, mergeConfig: mergeConfig, single: single, rect: rect, circle: circle, resizePoints: resizePoints, resizeLines: resizeLines} = this;
const {stroke: stroke, strokeWidth: strokeWidth, ignorePixelSnap: ignorePixelSnap} = mergeConfig;
const pointsStyle = this.getPointsStyle();
const middlePointsStyle = this.getMiddlePointsStyle();
const resizeLinesStyle = this.getResizeLinesStyle();
this.visible = !target.locked;
let resizeP;
for (let i = 0; i < 8; i++) {
resizeP = resizePoints[i];
resizeP.set(this.getPointStyle(i % 2 ? middlePointsStyle[(i - 1) / 2 % middlePointsStyle.length] : pointsStyle[i / 2 % pointsStyle.length]));
resizeP.rotation = (i - (i % 2 ? 1 : 0)) / 2 * 90;
if (i % 2) resizeLines[(i - 1) / 2].set(Object.assign({
pointType: "resize",
rotation: (i - 1) / 2 * 90
}, resizeLinesStyle[(i - 1) / 2 % resizeLinesStyle.length] || {}));
}
circle.set(this.getPointStyle(mergeConfig.circle || mergeConfig.rotatePoint || pointsStyle[0]));
rect.set(Object.assign({
stroke: stroke,
strokeWidth: strokeWidth,
opacity: 1,
editConfig: editConfig
}, mergeConfig.rect || {}));
const rectThrough = draw.isNull(mergeConfig.rectThrough) ? single : mergeConfig.rectThrough;
rect.hittable = !rectThrough;
if (rectThrough) {
target.syncEventer = rect;
this.app.interaction.bottomList = [ {
target: rect,
proxy: target
} ];
}
if (single) draw.DataHelper.stintSet(target.__world, "ignorePixelSnap", ignorePixelSnap);
updateMoveCursor(this);
}
update() {
const {editor: editor} = this;
const {x: x, y: y, scaleX: scaleX, scaleY: scaleY, rotation: rotation, skewX: skewX, skewY: skewY, width: width, height: height} = this.target.getLayoutBounds("box", editor, true);
this.visible = !this.target.locked;
this.set({
x: x,
y: y,
scaleX: scaleX,
scaleY: scaleY,
rotation: rotation,
skewX: skewX,
skewY: skewY
});
this.updateBounds({
x: 0,
y: 0,
width: width,
height: height
});
}
unload() {
this.visible = false;
if (this.app) this.rect.syncEventer = this.app.interaction.bottomList = null;
}
updateBounds(bounds) {
const {editor: editor, mergeConfig: mergeConfig, single: single, rect: rect, circle: circle, buttons: buttons, resizePoints: resizePoints, rotatePoints: rotatePoints, resizeLines: resizeLines} = this;
const {editMask: editMask} = editor;
const {middlePoint: middlePoint, resizeable: resizeable, rotateable: rotateable, hideOnSmall: hideOnSmall, editBox: editBox, mask: mask, dimOthers: dimOthers, bright: bright, spread: spread, hideRotatePoints: hideRotatePoints, hideResizeLines: hideResizeLines} = mergeConfig;
editMask.visible = mask ? true : 0;
if (!draw.isUndefined(dimOthers) || !draw.isUndefined(bright)) {
editor.setDimOthers(dimOthers);
editor.setBright(!!dimOthers || bright);
editor.hasDimOthers = true;
} else if (editor.hasDimOthers) {
editor.cancelDimOthers();
}
if (spread) draw.BoundsHelper.spread(bounds, spread);
if (this.view.worldOpacity) {
const {width: width, height: height} = bounds;
const smallSize = draw.isNumber(hideOnSmall) ? hideOnSmall : 10;
const showPoints = editBox && !(hideOnSmall && width < smallSize && height < smallSize);
let point = {}, rotateP, resizeP, resizeL;
for (let i = 0; i < 8; i++) {
draw.AroundHelper.toPoint(draw.AroundHelper.directionData[i], bounds, point);
resizeP = resizePoints[i];
rotateP = rotatePoints[i];
resizeP.set(point);
rotateP.set(point);
resizeP.visible = showPoints && !!(resizeable || rotateable);
rotateP.visible = showPoints && rotateable && resizeable && !hideRotatePoints;
if (i % 2) {
resizeL = resizeLines[(i - 1) / 2];
resizeL.set(point);
resizeL.visible = resizeP.visible && !hideResizeLines;
if (resizeP.visible) resizeP.visible = !!middlePoint;
if (rotateP.visible) rotateP.visible = !!middlePoint;
if ((i + 1) / 2 % 2) {
resizeL.width = width + resizeL.height;
if (hideOnSmall && resizeP.width * 2 > width) resizeP.visible = false;
} else {
resizeL.width = height + resizeL.height;
if (hideOnSmall && resizeP.width * 2 > height) resizeP.visible = false;
}
}
}
circle.visible = showPoints && rotateable && !!(mergeConfig.circle || mergeConfig.rotatePoint);
if (circle.visible) this.layoutCircle();
if (rect.path) rect.path = null;
rect.set(Object.assign(Object.assign({}, bounds), {
visible: single ? editBox : true
}));
buttons.visible = showPoints && buttons.children.length > 0 || 0;
if (buttons.visible) this.layoutButtons();
} else rect.set(bounds);
}
layoutCircle() {
const {circleDirection: circleDirection, circleMargin: circleMargin, buttonsMargin: buttonsMargin, buttonsDirection: buttonsDirection, middlePoint: middlePoint} = this.mergedConfig;
const direction = fourDirection.indexOf(circleDirection || (this.buttons.children.length && buttonsDirection === "bottom" ? "top" : "bottom"));
this.setButtonPosition(this.circle, direction, circleMargin || buttonsMargin, !!middlePoint);
}
layoutButtons() {
const {buttons: buttons} = this;
const {buttonsDirection: buttonsDirection, buttonsFixed: buttonsFixed, buttonsMargin: buttonsMargin, middlePoint: middlePoint} = this.mergedConfig;
const {flippedX: flippedX, flippedY: flippedY} = this;
let index = fourDirection.indexOf(buttonsDirection);
if (index % 2 && flippedX || (index + 1) % 2 && flippedY) {
if (buttonsFixed) index = (index + 2) % 4;
}
const direction = buttonsFixed ? EditDataHelper.getRotateDirection(index, this.flippedOne ? this.rotation : -this.rotation, 4) : index;
this.setButtonPosition(buttons, direction, buttonsMargin, !!middlePoint);
if (buttonsFixed) buttons.rotation = (direction - index) * 90;
buttons.scaleX = flippedX ? -1 : 1;
buttons.scaleY = flippedY ? -1 : 1;
}
setButtonPosition(buttons, direction, buttonsMargin, useMiddlePoint) {
const point = this.resizePoints[direction * 2 + 1];
const useX = direction % 2;
const sign = !direction || direction === 3 ? -1 : 1;
const useWidth = direction % 2;
const margin = (buttonsMargin + (useWidth ? (useMiddlePoint ? point.width : 0) + buttons.boxBounds.width : (useMiddlePoint ? point.height : 0) + buttons.boxBounds.height) / 2) * sign;
if (useX) {
buttons.x = point.x + margin;
buttons.y = point.y;
} else {
buttons.x = point.x;
buttons.y = point.y + margin;
}
}
getPointStyle(userStyle) {
const {stroke: stroke, strokeWidth: strokeWidth, pointFill: pointFill, pointSize: pointSize, pointRadius: pointRadius} = this.mergedConfig;
const defaultStyle = {
fill: pointFill,
stroke: stroke,
strokeWidth: strokeWidth,
around: "center",
strokeAlign: "center",
opacity: 1,
width: pointSize,
height: pointSize,
cornerRadius: pointRadius,
offsetX: 0,
offsetY: 0,
editConfig: editConfig
};
return userStyle ? Object.assign(defaultStyle, userStyle) : defaultStyle;
}
getPointsStyle() {
const {point: point} = this.mergedConfig;
return draw.isArray(point) ? point : [ point ];
}
getMiddlePointsStyle() {
const {middlePoint: middlePoint} = this.mergedConfig;
return draw.isArray(middlePoint) ? middlePoint : middlePoint ? [ middlePoint ] : this.getPointsStyle();
}
getResizeLinesStyle() {
const {resizeLine: resizeLine} = this.mergedConfig;
return draw.isArray(resizeLine) ? resizeLine : [ resizeLine ];
}
onDragStart(e) {
this.dragging = true;
const point = this.dragPoint = e.current, {pointType: pointType} = point;
const {moveable: moveable, resizeable: resizeable, rotateable: rotateable, skewable: skewable, onCopy: onCopy} = this.mergeConfig;
if (pointType === "move") {
if (e.altKey && onCopy && onCopy() && this.editor.single) this.app.interaction.replaceDownTarget(this.target);
moveable && (this.moving = true);
} else {
if (pointType.includes("rotate") || this.isHoldRotateKey(e) || !resizeable) {
rotateable && (this.rotating = true);
if (pointType === "resize-rotate") resizeable && (this.resizing = true); else if (point.name === "resize-line") skewable && (this.skewing = true),
this.rotating = false;
} else if (pointType === "resize") resizeable && (this.resizing = true);
if (pointType === "skew") skewable && (this.skewing = true);
}
this.onTransformStart(e);
}
onDrag(e) {
const {transformTool: transformTool, moving: moving, resizing: resizing, rotating: rotating, skewing: skewing} = this;
if (moving) {
transformTool.onMove(e);
} else if (resizing || rotating || skewing) {
const point = e.current;
if (point.pointType) this.enterPoint = point;
if (rotating) transformTool.onRotate(e);
if (resizing) transformTool.onScale(e);
if (skewing) transformTool.onSkew(e);
}
updatePointCursor(this, e);
}
onDragEnd(e) {
this.onTransformEnd(e);
this.dragPoint = null;
}
onTransformStart(e) {
if (this.moving || this.gesturing) this.editor.opacity = this.mergedConfig.hideOnMove ? 0 : 1;
if (this.resizing) draw.ResizeEvent.resizingKeys = this.editor.leafList.keys;
const {dragStartData: dragStartData, target: target} = this;
dragStartData.x = e.x;
dragStartData.y = e.y;
dragStartData.totalOffset = draw.getPointData();
dragStartData.point = {
x: target.x,
y: target.y
};
dragStartData.bounds = Object.assign({}, target.