various refactor
parent
0a6170bcff
commit
2fad5d93a9
|
@ -54,12 +54,6 @@ const supportsHVLineContainerBBox_ = (function () {
|
||||||
return (bbox.width === 15);
|
return (bbox.width === 15);
|
||||||
}());
|
}());
|
||||||
|
|
||||||
const supportsNonScalingStroke_ = (function () {
|
|
||||||
const rect = document.createElementNS(NSSVG, 'rect');
|
|
||||||
rect.setAttribute('style', 'vector-effect:non-scaling-stroke');
|
|
||||||
return rect.style.vectorEffect === 'non-scaling-stroke';
|
|
||||||
}());
|
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,10 +94,3 @@ export const supportsHVLineContainerBBox = () => supportsHVLineContainerBBox_;
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export const supportsGoodTextCharPos = () => supportsGoodTextCharPos_;
|
export const supportsGoodTextCharPos = () => supportsGoodTextCharPos_;
|
||||||
|
|
||||||
/**
|
|
||||||
* @function module:browser.supportsNonScalingStroke
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export const supportsNonScalingStroke = () => supportsNonScalingStroke_;
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
assignAttributes, cleanupElement, getElem, getRotationAngle, snapToGrid, walkTree,
|
assignAttributes, cleanupElement, getElem, getRotationAngle, snapToGrid, walkTree,
|
||||||
getBBox as utilsGetBBox, isNullish, preventClickDefault, setHref
|
isNullish, preventClickDefault, setHref
|
||||||
} from './utilities.js';
|
} from './utilities.js';
|
||||||
import {
|
import {
|
||||||
convertAttrs
|
convertAttrs
|
||||||
|
@ -14,7 +14,6 @@ import {
|
||||||
import {
|
import {
|
||||||
transformPoint, hasMatrixTransform, getMatrix, snapToAngle
|
transformPoint, hasMatrixTransform, getMatrix, snapToAngle
|
||||||
} from './math.js';
|
} from './math.js';
|
||||||
import { supportsNonScalingStroke } from '../common/browser.js';
|
|
||||||
import * as draw from './draw.js';
|
import * as draw from './draw.js';
|
||||||
import * as pathModule from './path.js';
|
import * as pathModule from './path.js';
|
||||||
import * as hstry from './history.js';
|
import * as hstry from './history.js';
|
||||||
|
@ -79,7 +78,7 @@ export const getBsplinePoint = function (t) {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const mouseMoveEvent = function (evt) {
|
export const mouseMoveEvent = function (evt) {
|
||||||
const selectedElements = eventContext_.getSelectedElements;
|
const selectedElements = eventContext_.getSelectedElements();
|
||||||
const currentZoom = eventContext_.getCurrentZoom();
|
const currentZoom = eventContext_.getCurrentZoom();
|
||||||
const svgRoot = eventContext_.getSVGRoot();
|
const svgRoot = eventContext_.getSVGRoot();
|
||||||
const svgCanvas = eventContext_.getCanvas();
|
const svgCanvas = eventContext_.getCanvas();
|
||||||
|
@ -96,7 +95,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
let len;
|
let len;
|
||||||
let angle;
|
let angle;
|
||||||
let box;
|
let box;
|
||||||
let selected = selectedElements()[0];
|
let selected = selectedElements[0];
|
||||||
const pt = transformPoint(evt.clientX, evt.clientY, eventContext_.getrootSctm());
|
const pt = transformPoint(evt.clientX, evt.clientY, eventContext_.getrootSctm());
|
||||||
const mouseX = pt.x * currentZoom;
|
const mouseX = pt.x * currentZoom;
|
||||||
const mouseY = pt.y * currentZoom;
|
const mouseY = pt.y * currentZoom;
|
||||||
|
@ -119,7 +118,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
// we temporarily use a translate on the element(s) being dragged
|
// we temporarily use a translate on the element(s) being dragged
|
||||||
// this transform is removed upon mousing up and the element is
|
// this transform is removed upon mousing up and the element is
|
||||||
// relocated to the new location
|
// relocated to the new location
|
||||||
if (selectedElements()[0] !== null) {
|
if (selectedElements[0] !== null) {
|
||||||
dx = x - eventContext_.getStartX();
|
dx = x - eventContext_.getStartX();
|
||||||
dy = y - eventContext_.getStartY();
|
dy = y - eventContext_.getStartY();
|
||||||
if (eventContext_.getCurConfig().gridSnapping) {
|
if (eventContext_.getCurConfig().gridSnapping) {
|
||||||
|
@ -128,16 +127,10 @@ export const mouseMoveEvent = function (evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dx !== 0 || dy !== 0) {
|
if (dx !== 0 || dy !== 0) {
|
||||||
len = selectedElements().length;
|
len = selectedElements.length;
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
selected = selectedElements()[i];
|
selected = selectedElements[i];
|
||||||
if (isNullish(selected)) { break; }
|
if (isNullish(selected)) { break; }
|
||||||
// if (i === 0) {
|
|
||||||
// const box = utilsGetBBox(selected);
|
|
||||||
// selectedBBoxes[i].x = box.x + dx;
|
|
||||||
// selectedBBoxes[i].y = box.y + dy;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// update the dummy transform in our transform list
|
// update the dummy transform in our transform list
|
||||||
// to be a translate
|
// to be a translate
|
||||||
const xform = svgRoot.createSVGTransform();
|
const xform = svgRoot.createSVGTransform();
|
||||||
|
@ -157,7 +150,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
svgCanvas.selectorManager.requestSelector(selected).resize();
|
svgCanvas.selectorManager.requestSelector(selected).resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
svgCanvas.call('transition', selectedElements());
|
svgCanvas.call('transition', selectedElements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -175,7 +168,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
// - if newList contains selected, do nothing
|
// - if newList contains selected, do nothing
|
||||||
// - if newList doesn't contain selected, remove it from selected
|
// - if newList doesn't contain selected, remove it from selected
|
||||||
// - for any newList that was not in selectedElements, add it to selected
|
// - for any newList that was not in selectedElements, add it to selected
|
||||||
const elemsToRemove = selectedElements().slice(); const elemsToAdd = [];
|
const elemsToRemove = selectedElements.slice(); const elemsToAdd = [];
|
||||||
const newList = eventContext_.getIntersectionList();
|
const newList = eventContext_.getIntersectionList();
|
||||||
|
|
||||||
// For every element in the intersection, add if not present in selectedElements.
|
// For every element in the intersection, add if not present in selectedElements.
|
||||||
|
@ -183,7 +176,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
const intElem = newList[i];
|
const intElem = newList[i];
|
||||||
// Found an element that was not selected before, so we should add it.
|
// Found an element that was not selected before, so we should add it.
|
||||||
if (!selectedElements().includes(intElem)) {
|
if (!selectedElements.includes(intElem)) {
|
||||||
elemsToAdd.push(intElem);
|
elemsToAdd.push(intElem);
|
||||||
}
|
}
|
||||||
// Found an element that was already selected, so we shouldn't remove it.
|
// Found an element that was already selected, so we shouldn't remove it.
|
||||||
|
@ -208,7 +201,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
// the shape's coordinates
|
// the shape's coordinates
|
||||||
tlist = selected.transform.baseVal;
|
tlist = selected.transform.baseVal;
|
||||||
const hasMatrix = hasMatrixTransform(tlist);
|
const hasMatrix = hasMatrixTransform(tlist);
|
||||||
box = hasMatrix ? eventContext_.getInitBbox() : utilsGetBBox(selected);
|
box = hasMatrix ? eventContext_.getInitBbox() : selected.getBBox();
|
||||||
let left = box.x;
|
let left = box.x;
|
||||||
let top = box.y;
|
let top = box.y;
|
||||||
let { width, height } = box;
|
let { width, height } = box;
|
||||||
|
@ -290,7 +283,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
svgCanvas.selectorManager.requestSelector(selected).resize();
|
svgCanvas.selectorManager.requestSelector(selected).resize();
|
||||||
svgCanvas.call('transition', selectedElements());
|
svgCanvas.call('transition', selectedElements);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} case 'zoom': {
|
} case 'zoom': {
|
||||||
|
@ -485,7 +478,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} case 'rotate': {
|
} case 'rotate': {
|
||||||
box = utilsGetBBox(selected);
|
box = selected.getBBox();
|
||||||
cx = box.x + box.width / 2;
|
cx = box.x + box.width / 2;
|
||||||
cy = box.y + box.height / 2;
|
cy = box.y + box.height / 2;
|
||||||
const m = getMatrix(selected);
|
const m = getMatrix(selected);
|
||||||
|
@ -502,7 +495,7 @@ export const mouseMoveEvent = function (evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
svgCanvas.setRotationAngle(angle < -180 ? (360 + angle) : angle, true);
|
svgCanvas.setRotationAngle(angle < -180 ? (360 + angle) : angle, true);
|
||||||
svgCanvas.call('transition', selectedElements());
|
svgCanvas.call('transition', selectedElements);
|
||||||
break;
|
break;
|
||||||
} default:
|
} default:
|
||||||
break;
|
break;
|
||||||
|
@ -579,16 +572,16 @@ export const mouseUpEvent = function (evt) {
|
||||||
// intentionally fall-through to select here
|
// intentionally fall-through to select here
|
||||||
case 'resize':
|
case 'resize':
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
if (!isNullish(eventContext_.getRubberBox())) {
|
if (eventContext_.getRubberBox()) {
|
||||||
eventContext_.getRubberBox().setAttribute('display', 'none');
|
eventContext_.getRubberBox().setAttribute('display', 'none');
|
||||||
eventContext_.setCurBBoxes([]);
|
eventContext_.setCurBBoxes([]);
|
||||||
}
|
}
|
||||||
eventContext_.setCurrentMode('select');
|
eventContext_.setCurrentMode('select');
|
||||||
// Fallthrough
|
// Fallthrough
|
||||||
case 'select':
|
case 'select':
|
||||||
if (!isNullish(selectedElements[0])) {
|
if (selectedElements[0]) {
|
||||||
// if we only have one selected element
|
// if we only have one selected element
|
||||||
if (isNullish(selectedElements[1])) {
|
if (!selectedElements[1]) {
|
||||||
// set our current stroke/fill properties to the element's
|
// set our current stroke/fill properties to the element's
|
||||||
const selected = selectedElements[0];
|
const selected = selectedElements[0];
|
||||||
switch (selected.tagName) {
|
switch (selected.tagName) {
|
||||||
|
@ -613,9 +606,6 @@ export const mouseUpEvent = function (evt) {
|
||||||
eventContext_.setCurText('font_family', selected.getAttribute('font-family'));
|
eventContext_.setCurText('font_family', selected.getAttribute('font-family'));
|
||||||
}
|
}
|
||||||
svgCanvas.selectorManager.requestSelector(selected).showGrips(true);
|
svgCanvas.selectorManager.requestSelector(selected).showGrips(true);
|
||||||
|
|
||||||
// This shouldn't be necessary as it was done on mouseDown...
|
|
||||||
// svgCanvas.call('selected', [selected]);
|
|
||||||
}
|
}
|
||||||
// always recalculate dimensions to strip off stray identity transforms
|
// always recalculate dimensions to strip off stray identity transforms
|
||||||
svgCanvas.recalculateAllSelectedDimensions();
|
svgCanvas.recalculateAllSelectedDimensions();
|
||||||
|
@ -624,10 +614,6 @@ export const mouseUpEvent = function (evt) {
|
||||||
const len = selectedElements.length;
|
const len = selectedElements.length;
|
||||||
for (let i = 0; i < len; ++i) {
|
for (let i = 0; i < len; ++i) {
|
||||||
if (isNullish(selectedElements[i])) { break; }
|
if (isNullish(selectedElements[i])) { break; }
|
||||||
if (!selectedElements[i].firstChild) {
|
|
||||||
// Not needed for groups (incorrectly resizes elems), possibly not needed at all?
|
|
||||||
svgCanvas.selectorManager.requestSelector(selectedElements[i]).resize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// no change in position/size, so maybe we should move to pathedit
|
// no change in position/size, so maybe we should move to pathedit
|
||||||
} else {
|
} else {
|
||||||
|
@ -642,7 +628,6 @@ export const mouseUpEvent = function (evt) {
|
||||||
} // no change in mouse position
|
} // no change in mouse position
|
||||||
|
|
||||||
// Remove non-scaling stroke
|
// Remove non-scaling stroke
|
||||||
if (supportsNonScalingStroke()) {
|
|
||||||
const elem = selectedElements[0];
|
const elem = selectedElements[0];
|
||||||
if (elem) {
|
if (elem) {
|
||||||
elem.removeAttribute('style');
|
elem.removeAttribute('style');
|
||||||
|
@ -650,7 +635,7 @@ export const mouseUpEvent = function (evt) {
|
||||||
el.removeAttribute('style');
|
el.removeAttribute('style');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'zoom': {
|
case 'zoom': {
|
||||||
|
@ -823,7 +808,7 @@ export const mouseUpEvent = function (evt) {
|
||||||
// if this element is in a group, go up until we reach the top-level group
|
// if this element is in a group, go up until we reach the top-level group
|
||||||
// just below the layer groups
|
// just below the layer groups
|
||||||
// TODO: once we implement links, we also would have to check for <a> elements
|
// TODO: once we implement links, we also would have to check for <a> elements
|
||||||
while (t && t.parentNode && t.parentNode.parentNode && t.parentNode.parentNode.tagName === 'g') {
|
while (t?.parentNode?.parentNode?.tagName === 'g') {
|
||||||
t = t.parentNode;
|
t = t.parentNode;
|
||||||
}
|
}
|
||||||
// if we are not in the middle of creating a path, and we've clicked on some shape,
|
// if we are not in the middle of creating a path, and we've clicked on some shape,
|
||||||
|
@ -1089,7 +1074,7 @@ export const mouseDownEvent = function (evt) {
|
||||||
|
|
||||||
// Getting the BBox from the selection box, since we know we
|
// Getting the BBox from the selection box, since we know we
|
||||||
// want to orient around it
|
// want to orient around it
|
||||||
eventContext_.setInitBbox(utilsGetBBox($id('selectedBox0')));
|
eventContext_.setInitBbox($id('selectedBox0').getBBox());
|
||||||
const bb = {};
|
const bb = {};
|
||||||
for (const [ key, val ] of Object.entries(eventContext_.getInitBbox())) {
|
for (const [ key, val ] of Object.entries(eventContext_.getInitBbox())) {
|
||||||
bb[key] = val / currentZoom;
|
bb[key] = val / currentZoom;
|
||||||
|
|
|
@ -176,7 +176,7 @@ export const transformListToTransform = function (tlist, min, max) {
|
||||||
* @param {Element} elem - The DOM element to check
|
* @param {Element} elem - The DOM element to check
|
||||||
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
|
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
|
||||||
*/
|
*/
|
||||||
export const getMatrix = function (elem) {
|
export const getMatrix = (elem) => {
|
||||||
const tlist = elem.transform.baseVal;
|
const tlist = elem.transform.baseVal;
|
||||||
return transformListToTransform(tlist).matrix;
|
return transformListToTransform(tlist).matrix;
|
||||||
};
|
};
|
||||||
|
@ -191,7 +191,7 @@ export const getMatrix = function (elem) {
|
||||||
* @param {Integer} y2 - Second coordinate's y value
|
* @param {Integer} y2 - Second coordinate's y value
|
||||||
* @returns {module:math.AngleCoord45}
|
* @returns {module:math.AngleCoord45}
|
||||||
*/
|
*/
|
||||||
export const snapToAngle = function (x1, y1, x2, y2) {
|
export const snapToAngle = (x1, y1, x2, y2) => {
|
||||||
const snap = Math.PI / 4; // 45 degrees
|
const snap = Math.PI / 4; // 45 degrees
|
||||||
const dx = x2 - x1;
|
const dx = x2 - x1;
|
||||||
const dy = y2 - y1;
|
const dy = y2 - y1;
|
||||||
|
@ -213,7 +213,7 @@ export const snapToAngle = function (x1, y1, x2, y2) {
|
||||||
* @param {SVGRect} r2 - The second BBox-like object
|
* @param {SVGRect} r2 - The second BBox-like object
|
||||||
* @returns {boolean} True if rectangles intersect
|
* @returns {boolean} True if rectangles intersect
|
||||||
*/
|
*/
|
||||||
export const rectsIntersect = function (r1, r2) {
|
export const rectsIntersect = (r1, r2) => {
|
||||||
return r2.x < (r1.x + r1.width) &&
|
return r2.x < (r1.x + r1.width) &&
|
||||||
(r2.x + r2.width) > r1.x &&
|
(r2.x + r2.width) > r1.x &&
|
||||||
r2.y < (r1.y + r1.height) &&
|
r2.y < (r1.y + r1.height) &&
|
||||||
|
|
|
@ -110,9 +110,9 @@ export class Selector {
|
||||||
const mgr = selectorManager_;
|
const mgr = selectorManager_;
|
||||||
const selectedGrips = mgr.selectorGrips;
|
const selectedGrips = mgr.selectorGrips;
|
||||||
const selected = this.selectedElement;
|
const selected = this.selectedElement;
|
||||||
const sw = selected.getAttribute('stroke-width');
|
|
||||||
const currentZoom = svgFactory_.getCurrentZoom();
|
const currentZoom = svgFactory_.getCurrentZoom();
|
||||||
let offset = 1 / currentZoom;
|
let offset = 1 / currentZoom;
|
||||||
|
const sw = selected.getAttribute('stroke-width');
|
||||||
if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) {
|
if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) {
|
||||||
offset += (sw / 2);
|
offset += (sw / 2);
|
||||||
}
|
}
|
||||||
|
@ -200,10 +200,8 @@ export class Selector {
|
||||||
' L' + (nbax + nbaw) + ',' + nbay +
|
' L' + (nbax + nbaw) + ',' + nbay +
|
||||||
' ' + (nbax + nbaw) + ',' + (nbay + nbah) +
|
' ' + (nbax + nbaw) + ',' + (nbay + nbah) +
|
||||||
' ' + nbax + ',' + (nbay + nbah) + 'z';
|
' ' + nbax + ',' + (nbay + nbah) + 'z';
|
||||||
selectedBox.setAttribute('d', dstr);
|
|
||||||
|
|
||||||
const xform = angle ? 'rotate(' + [ angle, cx, cy ].join(',') + ')' : '';
|
const xform = angle ? 'rotate(' + [ angle, cx, cy ].join(',') + ')' : '';
|
||||||
this.selectorGroup.setAttribute('transform', xform);
|
|
||||||
|
|
||||||
// TODO(codedread): Is this needed?
|
// TODO(codedread): Is this needed?
|
||||||
// if (selected === selectedElements[0]) {
|
// if (selected === selectedElements[0]) {
|
||||||
|
@ -217,6 +215,8 @@ export class Selector {
|
||||||
e: [ nbax + nbaw, nbay + (nbah) / 2 ],
|
e: [ nbax + nbaw, nbay + (nbah) / 2 ],
|
||||||
s: [ nbax + (nbaw) / 2, nbay + nbah ]
|
s: [ nbax + (nbaw) / 2, nbay + nbah ]
|
||||||
};
|
};
|
||||||
|
selectedBox.setAttribute('d', dstr);
|
||||||
|
this.selectorGroup.setAttribute('transform', xform);
|
||||||
Object.entries(this.gripCoords).forEach(([ dir, coords ]) => {
|
Object.entries(this.gripCoords).forEach(([ dir, coords ]) => {
|
||||||
selectedGrips[dir].setAttribute('cx', coords[0]);
|
selectedGrips[dir].setAttribute('cx', coords[0]);
|
||||||
selectedGrips[dir].setAttribute('cy', coords[1]);
|
selectedGrips[dir].setAttribute('cy', coords[1]);
|
||||||
|
@ -415,7 +415,7 @@ export class SelectorManager {
|
||||||
* @returns {Selector} The selector based on the given element
|
* @returns {Selector} The selector based on the given element
|
||||||
*/
|
*/
|
||||||
requestSelector(elem, bbox) {
|
requestSelector(elem, bbox) {
|
||||||
if (isNullish(elem)) { return null; }
|
if (!elem) { return null; }
|
||||||
|
|
||||||
const N = this.selectors.length;
|
const N = this.selectors.length;
|
||||||
// If we've already acquired one for this element, return it.
|
// If we've already acquired one for this element, return it.
|
||||||
|
|
|
@ -108,7 +108,6 @@ export const moveUpDownSelected = function (dir) {
|
||||||
if (!selected) { return; }
|
if (!selected) { return; }
|
||||||
|
|
||||||
elementContext_.setCurBBoxes([]);
|
elementContext_.setCurBBoxes([]);
|
||||||
// curBBoxes = [];
|
|
||||||
let closest; let foundCur;
|
let closest; let foundCur;
|
||||||
// jQuery sorts this list
|
// jQuery sorts this list
|
||||||
const list = elementContext_.getIntersectionList(getStrokedBBoxDefaultVisible([ selected ]));
|
const list = elementContext_.getIntersectionList(getStrokedBBoxDefaultVisible([ selected ]));
|
||||||
|
@ -163,10 +162,8 @@ export const moveSelectedElements = function (dx, dy, undoable = true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchCmd = new BatchCommand('position');
|
const batchCmd = new BatchCommand('position');
|
||||||
let i = selectedElements.length;
|
selectedElements.forEach((selected, i) => {
|
||||||
while (i--) {
|
if (selected) {
|
||||||
const selected = selectedElements[i];
|
|
||||||
if (!isNullish(selected)) {
|
|
||||||
const xform = elementContext_.getSVGRoot().createSVGTransform();
|
const xform = elementContext_.getSVGRoot().createSVGTransform();
|
||||||
const tlist = selected.transform?.baseVal;
|
const tlist = selected.transform?.baseVal;
|
||||||
|
|
||||||
|
@ -190,7 +187,7 @@ export const moveSelectedElements = function (dx, dy, undoable = true) {
|
||||||
|
|
||||||
elementContext_.gettingSelectorManager().requestSelector(selected).resize();
|
elementContext_.gettingSelectorManager().requestSelector(selected).resize();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
if (!batchCmd.isEmpty()) {
|
if (!batchCmd.isEmpty()) {
|
||||||
if (undoable) {
|
if (undoable) {
|
||||||
elementContext_.addCommandToHistory(batchCmd);
|
elementContext_.addCommandToHistory(batchCmd);
|
||||||
|
@ -893,12 +890,7 @@ export const ungroupSelectedElement = function () {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anchor) {
|
children[i++] = parent.insertBefore(elem, anchor);
|
||||||
anchor.before(elem);
|
|
||||||
} else {
|
|
||||||
g.after(elem);
|
|
||||||
}
|
|
||||||
children[i++] = elem;
|
|
||||||
batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));
|
batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,142 +5,159 @@
|
||||||
* @copyright 2011 Jeff Schiller
|
* @copyright 2011 Jeff Schiller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NS } from './namespaces.js';
|
import { NS } from "./namespaces.js";
|
||||||
import {
|
import {
|
||||||
isNullish, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible
|
getBBox as utilsGetBBox,
|
||||||
} from './utilities.js';
|
getStrokedBBoxDefaultVisible
|
||||||
import { transformPoint, transformListToTransform, rectsIntersect } from './math.js';
|
} from "./utilities.js";
|
||||||
import * as hstry from './history.js';
|
import {
|
||||||
import { getClosest } from '../editor/components/jgraduate/Util.js';
|
transformPoint,
|
||||||
|
transformListToTransform,
|
||||||
|
rectsIntersect
|
||||||
|
} from "./math.js";
|
||||||
|
import * as hstry from "./history.js";
|
||||||
|
import { getClosest } from "../editor/components/jgraduate/Util.js";
|
||||||
|
|
||||||
const { BatchCommand } = hstry;
|
const { BatchCommand } = hstry;
|
||||||
let selectionContext_ = null;
|
let selectionContext_ = null;
|
||||||
let svgCanvas = null;
|
let svgCanvas = null;
|
||||||
let selectedElements;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function module:selection.init
|
* @function module:selection.init
|
||||||
* @param {module:selection.selectionContext} selectionContext
|
* @param {module:selection.selectionContext} selectionContext
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const init = function (selectionContext) {
|
export const init = function (selectionContext) {
|
||||||
selectionContext_ = selectionContext;
|
selectionContext_ = selectionContext;
|
||||||
svgCanvas = selectionContext_.getCanvas();
|
svgCanvas = selectionContext_.getCanvas();
|
||||||
selectedElements = selectionContext_.getSelectedElements;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the selection. The 'selected' handler is then optionally called.
|
* Clears the selection. The 'selected' handler is then optionally called.
|
||||||
* This should really be an intersection applying to all types rather than a union.
|
* This should really be an intersection applying to all types rather than a union.
|
||||||
* @name module:selection.SvgCanvas#clearSelection
|
* @name module:selection.SvgCanvas#clearSelection
|
||||||
* @type {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection}
|
* @type {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection}
|
||||||
* @fires module:selection.SvgCanvas#event:selected
|
* @fires module:selection.SvgCanvas#event:selected
|
||||||
*/
|
*/
|
||||||
export const clearSelectionMethod = function (noCall) {
|
export const clearSelectionMethod = function (noCall) {
|
||||||
selectedElements().forEach((elem) => {
|
const selectedElements = selectionContext_.getSelectedElements();
|
||||||
if (isNullish(elem)) {
|
selectedElements.forEach((elem) => {
|
||||||
|
if (!elem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
svgCanvas.selectorManager.releaseSelector(elem);
|
svgCanvas.selectorManager.releaseSelector(elem);
|
||||||
});
|
});
|
||||||
svgCanvas.setEmptySelectedElements();
|
svgCanvas.setEmptySelectedElements;
|
||||||
|
|
||||||
if (!noCall) { svgCanvas.call('selected', selectedElements()); }
|
if (!noCall) {
|
||||||
|
svgCanvas.call("selected", selectedElements);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a list of elements to the selection. The 'selected' handler is then called.
|
* Adds a list of elements to the selection. The 'selected' handler is then called.
|
||||||
* @name module:selection.SvgCanvas#addToSelection
|
* @name module:selection.SvgCanvas#addToSelection
|
||||||
* @type {module:path.EditorContext#addToSelection}
|
* @type {module:path.EditorContext#addToSelection}
|
||||||
* @fires module:selection.SvgCanvas#event:selected
|
* @fires module:selection.SvgCanvas#event:selected
|
||||||
*/
|
*/
|
||||||
export const addToSelectionMethod = function (elemsToAdd, showGrips) {
|
export const addToSelectionMethod = function (elemsToAdd, showGrips) {
|
||||||
if (!elemsToAdd.length) { return; }
|
const selectedElements = selectionContext_.getSelectedElements();
|
||||||
|
if (!elemsToAdd.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// find the first null in our selectedElements array
|
// find the first null in our selectedElements array
|
||||||
|
|
||||||
let j = 0;
|
let firstNull = 0;
|
||||||
while (j < selectedElements().length) {
|
while (firstNull < selectedElements.length) {
|
||||||
if (isNullish(selectedElements()[j])) {
|
if (selectedElements[firstNull] === null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++j;
|
++firstNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// now add each element consecutively
|
// now add each element consecutively
|
||||||
let i = elemsToAdd.length;
|
let i = elemsToAdd.length;
|
||||||
while (i--) {
|
while (i--) {
|
||||||
let elem = elemsToAdd[i];
|
let elem = elemsToAdd[i];
|
||||||
if (!elem) { continue; }
|
if (!elem || !elem.getBBox) {
|
||||||
const bbox = utilsGetBBox(elem);
|
continue;
|
||||||
if (!bbox) { continue; }
|
}
|
||||||
|
|
||||||
if (elem.tagName === 'a' && elem.childNodes.length === 1) {
|
if (elem.tagName === "a" && elem.childNodes.length === 1) {
|
||||||
// Make "a" element's child be the selected element
|
// Make "a" element's child be the selected element
|
||||||
elem = elem.firstChild;
|
elem = elem.firstChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it's not already there, add it
|
// if it's not already there, add it
|
||||||
if (!selectedElements().includes(elem)) {
|
if (!selectedElements.includes(elem)) {
|
||||||
selectedElements()[j] = elem;
|
selectedElements[firstNull] = elem;
|
||||||
|
|
||||||
// only the first selectedBBoxes element is ever used in the codebase these days
|
// only the first selectedBBoxes element is ever used in the codebase these days
|
||||||
// if (j === 0) selectedBBoxes[0] = utilsGetBBox(elem);
|
// if (j === 0) selectedBBoxes[0] = utilsGetBBox(elem);
|
||||||
j++;
|
firstNull++;
|
||||||
const sel = svgCanvas.selectorManager.requestSelector(elem, bbox);
|
const sel = svgCanvas.selectorManager.requestSelector(elem);
|
||||||
|
|
||||||
if (selectedElements().length > 1) {
|
if (selectedElements.length > 1) {
|
||||||
sel.showGrips(false);
|
sel.showGrips(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!selectedElements().length) {
|
if (!selectedElements.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
svgCanvas.call('selected', selectedElements());
|
svgCanvas.call("selected", selectedElements);
|
||||||
|
|
||||||
if (selectedElements().length === 1) {
|
if (selectedElements.length === 1) {
|
||||||
svgCanvas.selectorManager.requestSelector(selectedElements()[0]).showGrips(showGrips);
|
svgCanvas.selectorManager
|
||||||
|
.requestSelector(selectedElements[0])
|
||||||
|
.showGrips(showGrips);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the elements are in the correct order
|
// make sure the elements are in the correct order
|
||||||
// See: https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition
|
// See: https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition
|
||||||
|
|
||||||
selectedElements().sort(function (a, b) {
|
selectedElements.sort(function (a, b) {
|
||||||
if (a && b && a.compareDocumentPosition) {
|
if (a && b && a.compareDocumentPosition) {
|
||||||
return 3 - (b.compareDocumentPosition(a) & 6); // eslint-disable-line no-bitwise
|
return 3 - (b.compareDocumentPosition(a) & 6); // eslint-disable-line no-bitwise
|
||||||
}
|
}
|
||||||
if (isNullish(a)) {
|
if (!a) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure first elements are not null
|
// Make sure first elements are not null
|
||||||
while (isNullish(selectedElements())[0]) {
|
while (!selectedElements[0]) {
|
||||||
selectedElements().shift(0);
|
selectedElements.shift(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @name module:svgcanvas.SvgCanvas#getMouseTarget
|
* @name module:svgcanvas.SvgCanvas#getMouseTarget
|
||||||
* @type {module:path.EditorContext#getMouseTarget}
|
* @type {module:path.EditorContext#getMouseTarget}
|
||||||
*/
|
*/
|
||||||
export const getMouseTargetMethod = function (evt) {
|
export const getMouseTargetMethod = function (evt) {
|
||||||
if (isNullish(evt)) {
|
if (!evt) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let mouseTarget = evt.target;
|
let mouseTarget = evt.target;
|
||||||
|
|
||||||
// if it was a <use>, Opera and WebKit return the SVGElementInstance
|
// if it was a <use>, Opera and WebKit return the SVGElementInstance
|
||||||
if (mouseTarget.correspondingUseElement) { mouseTarget = mouseTarget.correspondingUseElement; }
|
if (mouseTarget.correspondingUseElement) {
|
||||||
|
mouseTarget = mouseTarget.correspondingUseElement;
|
||||||
|
}
|
||||||
|
|
||||||
// for foreign content, go up until we find the foreignObject
|
// for foreign content, go up until we find the foreignObject
|
||||||
// WebKit browsers set the mouse target to the svgcanvas div
|
// WebKit browsers set the mouse target to the svgcanvas div
|
||||||
if ([ NS.MATH, NS.HTML ].includes(mouseTarget.namespaceURI) &&
|
if (
|
||||||
mouseTarget.id !== 'svgcanvas'
|
[ NS.MATH, NS.HTML ].includes(mouseTarget.namespaceURI) &&
|
||||||
|
mouseTarget.id !== "svgcanvas"
|
||||||
) {
|
) {
|
||||||
while (mouseTarget.nodeName !== 'foreignObject') {
|
while (mouseTarget.nodeName !== "foreignObject") {
|
||||||
mouseTarget = mouseTarget.parentNode;
|
mouseTarget = mouseTarget.parentNode;
|
||||||
if (!mouseTarget) { return selectionContext_.getSVGRoot(); }
|
if (!mouseTarget) {
|
||||||
|
return selectionContext_.getSVGRoot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,52 +172,61 @@ export const getMouseTargetMethod = function (evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a selection grip, return the grip parent
|
// If it's a selection grip, return the grip parent
|
||||||
if (getClosest(mouseTarget.parentNode, '#selectorParentGroup')) {
|
if (getClosest(mouseTarget.parentNode, "#selectorParentGroup")) {
|
||||||
// While we could instead have just returned mouseTarget,
|
// While we could instead have just returned mouseTarget,
|
||||||
// this makes it easier to indentify as being a selector grip
|
// this makes it easier to indentify as being a selector grip
|
||||||
return svgCanvas.selectorManager.selectorParentGroup;
|
return svgCanvas.selectorManager.selectorParentGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!mouseTarget?.parentNode?.isSameNode(selectionContext_.getCurrentGroup() || currentLayer)) {
|
while (
|
||||||
|
!mouseTarget?.parentNode?.isSameNode(
|
||||||
|
selectionContext_.getCurrentGroup() || currentLayer
|
||||||
|
)
|
||||||
|
) {
|
||||||
mouseTarget = mouseTarget.parentNode;
|
mouseTarget = mouseTarget.parentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mouseTarget;
|
return mouseTarget;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @typedef {module:svgcanvas.ExtensionMouseDownStatus|module:svgcanvas.ExtensionMouseUpStatus|module:svgcanvas.ExtensionIDsUpdatedStatus|module:locale.ExtensionLocaleData[]|void} module:svgcanvas.ExtensionStatus
|
* @typedef {module:svgcanvas.ExtensionMouseDownStatus|module:svgcanvas.ExtensionMouseUpStatus|module:svgcanvas.ExtensionIDsUpdatedStatus|module:locale.ExtensionLocaleData[]|void} module:svgcanvas.ExtensionStatus
|
||||||
* @tutorial ExtensionDocs
|
* @tutorial ExtensionDocs
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @callback module:svgcanvas.ExtensionVarBuilder
|
* @callback module:svgcanvas.ExtensionVarBuilder
|
||||||
* @param {string} name The name of the extension
|
* @param {string} name The name of the extension
|
||||||
* @returns {module:svgcanvas.SvgCanvas#event:ext_addLangData}
|
* @returns {module:svgcanvas.SvgCanvas#event:ext_addLangData}
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @callback module:svgcanvas.ExtensionNameFilter
|
* @callback module:svgcanvas.ExtensionNameFilter
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
/**
|
/**
|
||||||
* @todo Consider: Should this return an array by default, so extension results aren't overwritten?
|
* @todo Consider: Should this return an array by default, so extension results aren't overwritten?
|
||||||
* @todo Would be easier to document if passing in object with key of action and vars as value; could then define an interface which tied both together
|
* @todo Would be easier to document if passing in object with key of action and vars as value; could then define an interface which tied both together
|
||||||
* @function module:svgcanvas.SvgCanvas#runExtensions
|
* @function module:svgcanvas.SvgCanvas#runExtensions
|
||||||
* @param {"mouseDown"|"mouseMove"|"mouseUp"|"zoomChanged"|"IDsUpdated"|"canvasUpdated"|"toolButtonStateUpdate"|"selectedChanged"|"elementTransition"|"elementChanged"|"langReady"|"langChanged"|"addLangData"|"onNewDocument"|"workareaResized"} action
|
* @param {"mouseDown"|"mouseMove"|"mouseUp"|"zoomChanged"|"IDsUpdated"|"canvasUpdated"|"toolButtonStateUpdate"|"selectedChanged"|"elementTransition"|"elementChanged"|"langReady"|"langChanged"|"addLangData"|"onNewDocument"|"workareaResized"} action
|
||||||
* @param {module:svgcanvas.SvgCanvas#event:ext_mouseDown|module:svgcanvas.SvgCanvas#event:ext_mouseMove|module:svgcanvas.SvgCanvas#event:ext_mouseUp|module:svgcanvas.SvgCanvas#event:ext_zoomChanged|module:svgcanvas.SvgCanvas#event:ext_IDsUpdated|module:svgcanvas.SvgCanvas#event:ext_canvasUpdated|module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate|module:svgcanvas.SvgCanvas#event:ext_selectedChanged|module:svgcanvas.SvgCanvas#event:ext_elementTransition|module:svgcanvas.SvgCanvas#event:ext_elementChanged|module:svgcanvas.SvgCanvas#event:ext_langReady|module:svgcanvas.SvgCanvas#event:ext_langChanged|module:svgcanvas.SvgCanvas#event:ext_addLangData|module:svgcanvas.SvgCanvas#event:ext_onNewDocument|module:svgcanvas.SvgCanvas#event:ext_workareaResized|module:svgcanvas.ExtensionVarBuilder} [vars]
|
* @param {module:svgcanvas.SvgCanvas#event:ext_mouseDown|module:svgcanvas.SvgCanvas#event:ext_mouseMove|module:svgcanvas.SvgCanvas#event:ext_mouseUp|module:svgcanvas.SvgCanvas#event:ext_zoomChanged|module:svgcanvas.SvgCanvas#event:ext_IDsUpdated|module:svgcanvas.SvgCanvas#event:ext_canvasUpdated|module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate|module:svgcanvas.SvgCanvas#event:ext_selectedChanged|module:svgcanvas.SvgCanvas#event:ext_elementTransition|module:svgcanvas.SvgCanvas#event:ext_elementChanged|module:svgcanvas.SvgCanvas#event:ext_langReady|module:svgcanvas.SvgCanvas#event:ext_langChanged|module:svgcanvas.SvgCanvas#event:ext_addLangData|module:svgcanvas.SvgCanvas#event:ext_onNewDocument|module:svgcanvas.SvgCanvas#event:ext_workareaResized|module:svgcanvas.ExtensionVarBuilder} [vars]
|
||||||
* @param {boolean} [returnArray]
|
* @param {boolean} [returnArray]
|
||||||
* @param {module:svgcanvas.ExtensionNameFilter} nameFilter
|
* @param {module:svgcanvas.ExtensionNameFilter} nameFilter
|
||||||
* @returns {GenericArray<module:svgcanvas.ExtensionStatus>|module:svgcanvas.ExtensionStatus|false} See {@tutorial ExtensionDocs} on the ExtensionStatus.
|
* @returns {GenericArray<module:svgcanvas.ExtensionStatus>|module:svgcanvas.ExtensionStatus|false} See {@tutorial ExtensionDocs} on the ExtensionStatus.
|
||||||
*/
|
*/
|
||||||
/* eslint-enable max-len */
|
/* eslint-enable max-len */
|
||||||
export const runExtensionsMethod = function (action, vars, returnArray, nameFilter) {
|
export const runExtensionsMethod = function (
|
||||||
|
action,
|
||||||
|
vars,
|
||||||
|
returnArray,
|
||||||
|
nameFilter
|
||||||
|
) {
|
||||||
let result = returnArray ? [] : false;
|
let result = returnArray ? [] : false;
|
||||||
for (const [ name, ext ] of Object.entries(selectionContext_.getExtensions())) {
|
for (const [ name, ext ] of Object.entries(selectionContext_.getExtensions())) {
|
||||||
if (nameFilter && !nameFilter(name)) {
|
if (nameFilter && !nameFilter(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ext && action in ext) {
|
if (ext && action in ext) {
|
||||||
if (typeof vars === 'function') {
|
if (typeof vars === "function") {
|
||||||
vars = vars(name); // ext, action
|
vars = vars(name); // ext, action
|
||||||
}
|
}
|
||||||
if (returnArray) {
|
if (returnArray) {
|
||||||
|
@ -214,13 +240,13 @@ export const runExtensionsMethod = function (action, vars, returnArray, nameFilt
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all elements that have a BBox (excludes `<defs>`, `<title>`, etc).
|
* Get all elements that have a BBox (excludes `<defs>`, `<title>`, etc).
|
||||||
* Note that 0-opacity, off-screen etc elements are still considered "visible"
|
* Note that 0-opacity, off-screen etc elements are still considered "visible"
|
||||||
* for this function.
|
* for this function.
|
||||||
* @function module:svgcanvas.SvgCanvas#getVisibleElementsAndBBoxes
|
* @function module:svgcanvas.SvgCanvas#getVisibleElementsAndBBoxes
|
||||||
* @param {Element} parent - The parent DOM element to search within
|
* @param {Element} parent - The parent DOM element to search within
|
||||||
* @returns {ElementAndBBox[]} An array with objects that include:
|
* @returns {ElementAndBBox[]} An array with objects that include:
|
||||||
*/
|
*/
|
||||||
export const getVisibleElementsAndBBoxes = function (parent) {
|
export const getVisibleElementsAndBBoxes = function (parent) {
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
const svgcontent = selectionContext_.getSVGContent();
|
const svgcontent = selectionContext_.getSVGContent();
|
||||||
|
@ -228,7 +254,7 @@ export const getVisibleElementsAndBBoxes = function (parent) {
|
||||||
}
|
}
|
||||||
const contentElems = [];
|
const contentElems = [];
|
||||||
const elements = parent.children;
|
const elements = parent.children;
|
||||||
Array.prototype.forEach.call(elements, function (elem) {
|
Array.from(elements).forEach((elem) => {
|
||||||
if (elem.getBBox) {
|
if (elem.getBBox) {
|
||||||
contentElems.push({ elem, bbox: getStrokedBBoxDefaultVisible([ elem ]) });
|
contentElems.push({ elem, bbox: getStrokedBBoxDefaultVisible([ elem ]) });
|
||||||
}
|
}
|
||||||
|
@ -237,31 +263,37 @@ export const getVisibleElementsAndBBoxes = function (parent) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method sends back an array or a NodeList full of elements that
|
* This method sends back an array or a NodeList full of elements that
|
||||||
* intersect the multi-select rubber-band-box on the currentLayer only.
|
* intersect the multi-select rubber-band-box on the currentLayer only.
|
||||||
*
|
*
|
||||||
* We brute-force `getIntersectionList` for browsers that do not support it (Firefox).
|
* We brute-force `getIntersectionList` for browsers that do not support it (Firefox).
|
||||||
*
|
*
|
||||||
* Reference:
|
* Reference:
|
||||||
* Firefox does not implement `getIntersectionList()`, see {@link https://bugzilla.mozilla.org/show_bug.cgi?id=501421}.
|
* Firefox does not implement `getIntersectionList()`, see {@link https://bugzilla.mozilla.org/show_bug.cgi?id=501421}.
|
||||||
* @function module:svgcanvas.SvgCanvas#getIntersectionList
|
* @function module:svgcanvas.SvgCanvas#getIntersectionList
|
||||||
* @param {SVGRect} rect
|
* @param {SVGRect} rect
|
||||||
* @returns {Element[]|NodeList} Bbox elements
|
* @returns {Element[]|NodeList} Bbox elements
|
||||||
*/
|
*/
|
||||||
export const getIntersectionListMethod = function (rect) {
|
export const getIntersectionListMethod = function (rect) {
|
||||||
const currentZoom = selectionContext_.getCurrentZoom();
|
const currentZoom = selectionContext_.getCurrentZoom();
|
||||||
if (isNullish(selectionContext_.getRubberBox())) { return null; }
|
if (!selectionContext_.getRubberBox()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const parent = selectionContext_.getCurrentGroup() || svgCanvas.getCurrentDrawing().getCurrentLayer();
|
const parent =
|
||||||
|
selectionContext_.getCurrentGroup() ||
|
||||||
|
svgCanvas.getCurrentDrawing().getCurrentLayer();
|
||||||
|
|
||||||
let rubberBBox;
|
let rubberBBox;
|
||||||
if (!rect) {
|
if (!rect) {
|
||||||
rubberBBox = selectionContext_.getRubberBox().getBBox();
|
rubberBBox = selectionContext_.getRubberBox().getBBox();
|
||||||
const bb = selectionContext_.getSVGContent().createSVGRect();
|
const bb = selectionContext_.getSVGContent().createSVGRect();
|
||||||
|
|
||||||
[ 'x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left' ].forEach((o) => {
|
[ "x", "y", "width", "height", "top", "right", "bottom", "left" ].forEach(
|
||||||
|
(o) => {
|
||||||
bb[o] = rubberBBox[o] / currentZoom;
|
bb[o] = rubberBBox[o] / currentZoom;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
rubberBBox = bb;
|
rubberBBox = bb;
|
||||||
} else {
|
} else {
|
||||||
rubberBBox = selectionContext_.getSVGContent().createSVGRect();
|
rubberBBox = selectionContext_.getSVGContent().createSVGRect();
|
||||||
|
@ -271,24 +303,22 @@ export const getIntersectionListMethod = function (rect) {
|
||||||
rubberBBox.height = rect.height;
|
rubberBBox.height = rect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resultList = null;
|
const resultList = [];
|
||||||
|
if (selectionContext_.getCurBBoxes().length === 0) {
|
||||||
if (isNullish(resultList) || typeof resultList.item !== 'function') {
|
|
||||||
resultList = [];
|
|
||||||
|
|
||||||
if (!selectionContext_.getCurBBoxes().length) {
|
|
||||||
// Cache all bboxes
|
// Cache all bboxes
|
||||||
|
console.log("setbb");
|
||||||
selectionContext_.setCurBBoxes(getVisibleElementsAndBBoxes(parent));
|
selectionContext_.setCurBBoxes(getVisibleElementsAndBBoxes(parent));
|
||||||
}
|
}
|
||||||
let i = selectionContext_.getCurBBoxes().length;
|
let i = selectionContext_.getCurBBoxes().length;
|
||||||
while (i--) {
|
while (i--) {
|
||||||
const curBBoxes = selectionContext_.getCurBBoxes();
|
const curBBoxes = selectionContext_.getCurBBoxes();
|
||||||
if (!rubberBBox.width) { continue; }
|
if (!rubberBBox.width) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
|
if (rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
|
||||||
resultList.push(curBBoxes[i].elem);
|
resultList.push(curBBoxes[i].elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// addToSelection expects an array, but it's ok to pass a NodeList
|
// addToSelection expects an array, but it's ok to pass a NodeList
|
||||||
// because using square-bracket notation is allowed:
|
// because using square-bracket notation is allowed:
|
||||||
|
@ -297,61 +327,62 @@ export const getIntersectionListMethod = function (rect) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {PlainObject} ElementAndBBox
|
* @typedef {PlainObject} ElementAndBBox
|
||||||
* @property {Element} elem - The element
|
* @property {Element} elem - The element
|
||||||
* @property {module:utilities.BBoxObject} bbox - The element's BBox as retrieved from `getStrokedBBoxDefaultVisible`
|
* @property {module:utilities.BBoxObject} bbox - The element's BBox as retrieved from `getStrokedBBoxDefaultVisible`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap an SVG element into a group element, mark the group as 'gsvg'.
|
* Wrap an SVG element into a group element, mark the group as 'gsvg'.
|
||||||
* @function module:svgcanvas.SvgCanvas#groupSvgElem
|
* @function module:svgcanvas.SvgCanvas#groupSvgElem
|
||||||
* @param {Element} elem - SVG element to wrap
|
* @param {Element} elem - SVG element to wrap
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const groupSvgElem = function (elem) {
|
export const groupSvgElem = function (elem) {
|
||||||
const dataStorage = selectionContext_.getDataStorage();
|
const dataStorage = selectionContext_.getDataStorage();
|
||||||
const g = document.createElementNS(NS.SVG, 'g');
|
const g = document.createElementNS(NS.SVG, "g");
|
||||||
elem.replaceWith(g);
|
elem.replaceWith(g);
|
||||||
g.appendChild(elem);
|
g.appendChild(elem);
|
||||||
dataStorage.put(g, 'gsvg', elem);
|
dataStorage.put(g, "gsvg", elem);
|
||||||
g.id = svgCanvas.getNextId();
|
g.id = svgCanvas.getNextId();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the SVG Document through the sanitizer and then updates its paths.
|
* Runs the SVG Document through the sanitizer and then updates its paths.
|
||||||
* @function module:svgcanvas.SvgCanvas#prepareSvg
|
* @function module:svgcanvas.SvgCanvas#prepareSvg
|
||||||
* @param {XMLDocument} newDoc - The SVG DOM document
|
* @param {XMLDocument} newDoc - The SVG DOM document
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const prepareSvg = function (newDoc) {
|
export const prepareSvg = function (newDoc) {
|
||||||
|
|
||||||
svgCanvas.sanitizeSvg(newDoc.documentElement);
|
svgCanvas.sanitizeSvg(newDoc.documentElement);
|
||||||
|
|
||||||
// convert paths into absolute commands
|
// convert paths into absolute commands
|
||||||
const paths = [ ...newDoc.getElementsByTagNameNS(NS.SVG, 'path') ];
|
const paths = [ ...newDoc.getElementsByTagNameNS(NS.SVG, "path") ];
|
||||||
paths.forEach((path) => {
|
paths.forEach((path) => {
|
||||||
const convertedPath = svgCanvas.pathActions.convertPath(path);
|
const convertedPath = svgCanvas.pathActions.convertPath(path);
|
||||||
path.setAttribute('d', convertedPath);
|
path.setAttribute("d", convertedPath);
|
||||||
svgCanvas.pathActions.fixEnd(path);
|
svgCanvas.pathActions.fixEnd(path);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes any old rotations if present, prepends a new rotation at the
|
* Removes any old rotations if present, prepends a new rotation at the
|
||||||
* transformed center.
|
* transformed center.
|
||||||
* @function module:svgcanvas.SvgCanvas#setRotationAngle
|
* @function module:svgcanvas.SvgCanvas#setRotationAngle
|
||||||
* @param {string|Float} val - The new rotation angle in degrees
|
* @param {string|Float} val - The new rotation angle in degrees
|
||||||
* @param {boolean} preventUndo - Indicates whether the action should be undoable or not
|
* @param {boolean} preventUndo - Indicates whether the action should be undoable or not
|
||||||
* @fires module:svgcanvas.SvgCanvas#event:changed
|
* @fires module:svgcanvas.SvgCanvas#event:changed
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const setRotationAngle = function (val, preventUndo) {
|
export const setRotationAngle = function (val, preventUndo) {
|
||||||
|
const selectedElements = selectionContext_.getSelectedElements();
|
||||||
// ensure val is the proper type
|
// ensure val is the proper type
|
||||||
val = Number.parseFloat(val);
|
val = Number.parseFloat(val);
|
||||||
const elem = selectedElements()[0];
|
const elem = selectedElements[0];
|
||||||
const oldTransform = elem.getAttribute('transform');
|
const oldTransform = elem.getAttribute("transform");
|
||||||
const bbox = utilsGetBBox(elem);
|
const bbox = utilsGetBBox(elem);
|
||||||
const cx = bbox.x + bbox.width / 2; const cy = bbox.y + bbox.height / 2;
|
const cx = bbox.x + bbox.width / 2;
|
||||||
|
const cy = bbox.y + bbox.height / 2;
|
||||||
const tlist = elem.transform.baseVal;
|
const tlist = elem.transform.baseVal;
|
||||||
|
|
||||||
// only remove the real rotational transform if present (i.e. at index=0)
|
// only remove the real rotational transform if present (i.e. at index=0)
|
||||||
|
@ -363,7 +394,11 @@ export const setRotationAngle = function (val, preventUndo) {
|
||||||
}
|
}
|
||||||
// find Rnc and insert it
|
// find Rnc and insert it
|
||||||
if (val !== 0) {
|
if (val !== 0) {
|
||||||
const center = transformPoint(cx, cy, transformListToTransform(tlist).matrix);
|
const center = transformPoint(
|
||||||
|
cx,
|
||||||
|
cy,
|
||||||
|
transformListToTransform(tlist).matrix
|
||||||
|
);
|
||||||
const Rnc = selectionContext_.getSVGRoot().createSVGTransform();
|
const Rnc = selectionContext_.getSVGRoot().createSVGTransform();
|
||||||
Rnc.setRotate(val, center.x, center.y);
|
Rnc.setRotate(val, center.x, center.y);
|
||||||
if (tlist.numberOfItems) {
|
if (tlist.numberOfItems) {
|
||||||
|
@ -372,52 +407,58 @@ export const setRotationAngle = function (val, preventUndo) {
|
||||||
tlist.appendItem(Rnc);
|
tlist.appendItem(Rnc);
|
||||||
}
|
}
|
||||||
} else if (tlist.numberOfItems === 0) {
|
} else if (tlist.numberOfItems === 0) {
|
||||||
elem.removeAttribute('transform');
|
elem.removeAttribute("transform");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preventUndo) {
|
if (!preventUndo) {
|
||||||
// we need to undo it, then redo it so it can be undo-able! :)
|
// we need to undo it, then redo it so it can be undo-able! :)
|
||||||
// TODO: figure out how to make changes to transform list undo-able cross-browser?
|
// TODO: figure out how to make changes to transform list undo-able cross-browser?
|
||||||
const newTransform = elem.getAttribute('transform');
|
const newTransform = elem.getAttribute("transform");
|
||||||
if (oldTransform) {
|
if (oldTransform) {
|
||||||
elem.setAttribute('transform', oldTransform);
|
elem.setAttribute("transform", oldTransform);
|
||||||
} else {
|
} else {
|
||||||
elem.removeAttribute('transform');
|
elem.removeAttribute("transform");
|
||||||
}
|
}
|
||||||
svgCanvas.changeSelectedAttribute('transform', newTransform, selectedElements());
|
svgCanvas.changeSelectedAttribute(
|
||||||
svgCanvas.call('changed', selectedElements());
|
"transform",
|
||||||
|
newTransform,
|
||||||
|
selectedElements
|
||||||
|
);
|
||||||
|
svgCanvas.call("changed", selectedElements);
|
||||||
}
|
}
|
||||||
// const pointGripContainer = getElem('pathpointgrip_container');
|
// const pointGripContainer = getElem('pathpointgrip_container');
|
||||||
// if (elem.nodeName === 'path' && pointGripContainer) {
|
// if (elem.nodeName === 'path' && pointGripContainer) {
|
||||||
// pathActions.setPointContainerTransform(elem.getAttribute('transform'));
|
// pathActions.setPointContainerTransform(elem.getAttribute('transform'));
|
||||||
// }
|
// }
|
||||||
const selector = svgCanvas.selectorManager.requestSelector(selectedElements()[0]);
|
const selector = svgCanvas.selectorManager.requestSelector(
|
||||||
|
selectedElements[0]
|
||||||
|
);
|
||||||
selector.resize();
|
selector.resize();
|
||||||
selectionContext_.getSelector().updateGripCursors(val);
|
selectionContext_.getSelector().updateGripCursors(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs `recalculateDimensions` on the selected elements,
|
* Runs `recalculateDimensions` on the selected elements,
|
||||||
* adding the changes to a single batch command.
|
* adding the changes to a single batch command.
|
||||||
* @function module:svgcanvas.SvgCanvas#recalculateAllSelectedDimensions
|
* @function module:svgcanvas.SvgCanvas#recalculateAllSelectedDimensions
|
||||||
* @fires module:svgcanvas.SvgCanvas#event:changed
|
* @fires module:svgcanvas.SvgCanvas#event:changed
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export const recalculateAllSelectedDimensions = function () {
|
export const recalculateAllSelectedDimensions = function () {
|
||||||
const text = (selectionContext_.getCurrentResizeMode() === 'none' ? 'position' : 'size');
|
const text =
|
||||||
|
selectionContext_.getCurrentResizeMode() === "none" ? "position" : "size";
|
||||||
const batchCmd = new BatchCommand(text);
|
const batchCmd = new BatchCommand(text);
|
||||||
|
const selectedElements = selectionContext_.getSelectedElements();
|
||||||
|
|
||||||
let i = selectedElements().length;
|
selectedElements.forEach((elem) => {
|
||||||
while (i--) {
|
|
||||||
const elem = selectedElements()[i];
|
|
||||||
const cmd = svgCanvas.recalculateDimensions(elem);
|
const cmd = svgCanvas.recalculateDimensions(elem);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
batchCmd.addSubCommand(cmd);
|
batchCmd.addSubCommand(cmd);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (!batchCmd.isEmpty()) {
|
if (!batchCmd.isEmpty()) {
|
||||||
selectionContext_.addCommandToHistory(batchCmd);
|
selectionContext_.addCommandToHistory(batchCmd);
|
||||||
svgCanvas.call('changed', selectedElements());
|
svgCanvas.call("changed", selectedElements);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -625,7 +625,7 @@ class SvgCanvas {
|
||||||
getCurrentZoom,
|
getCurrentZoom,
|
||||||
getRubberBox() { return rubberBox; },
|
getRubberBox() { return rubberBox; },
|
||||||
setCurBBoxes(value) { curBBoxes = value; },
|
setCurBBoxes(value) { curBBoxes = value; },
|
||||||
getCurBBoxes(_value) { return curBBoxes; },
|
getCurBBoxes() { return curBBoxes; },
|
||||||
getCurrentResizeMode() { return currentResizeMode; },
|
getCurrentResizeMode() { return currentResizeMode; },
|
||||||
addCommandToHistory,
|
addCommandToHistory,
|
||||||
getSelector() { return Selector; }
|
getSelector() { return Selector; }
|
||||||
|
|
|
@ -11,9 +11,6 @@ import { setUnitAttr, getTypeMap } from '../common/units.js';
|
||||||
import {
|
import {
|
||||||
hasMatrixTransform, transformListToTransform, transformBox
|
hasMatrixTransform, transformListToTransform, transformBox
|
||||||
} from './math.js';
|
} from './math.js';
|
||||||
import {
|
|
||||||
isWebkit, supportsHVLineContainerBBox
|
|
||||||
} from '../common/browser.js';
|
|
||||||
import { getClosest, mergeDeep } from '../editor/components/jgraduate/Util.js';
|
import { getClosest, mergeDeep } from '../editor/components/jgraduate/Util.js';
|
||||||
|
|
||||||
// Much faster than running getBBox() every time
|
// Much faster than running getBBox() every time
|
||||||
|
@ -467,61 +464,6 @@ export const getPathBBox = function (path) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the given/selected element's bounding box object, checking for
|
|
||||||
* horizontal/vertical lines (see issue 717)
|
|
||||||
* Note that performance is currently terrible, so some way to improve would
|
|
||||||
* be great.
|
|
||||||
* @param {Element} selected - Container or `<use>` DOM element
|
|
||||||
* @returns {DOMRect} Bounding box object
|
|
||||||
*/
|
|
||||||
function groupBBFix(selected) {
|
|
||||||
if (supportsHVLineContainerBBox()) {
|
|
||||||
try { return selected.getBBox(); } catch (e) {/* empty */ }
|
|
||||||
}
|
|
||||||
const ref = editorContext_.getDataStorage().get(selected, 'ref');
|
|
||||||
let matched = null;
|
|
||||||
let ret; let copy;
|
|
||||||
|
|
||||||
if (ref) {
|
|
||||||
const elements = [];
|
|
||||||
Array.prototype.forEach.call(ref.children, function (el) {
|
|
||||||
const elem = el.cloneNode(true);
|
|
||||||
elem.setAttribute('visibility', 'hidden');
|
|
||||||
svgroot_.appendChild(elem);
|
|
||||||
copy.push(elem);
|
|
||||||
if ([ 'line', 'path' ].indexOf(elem.tagName) !== -1) {
|
|
||||||
elements.push(elem);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
matched = (elements.length) ? elements : null;
|
|
||||||
} else {
|
|
||||||
matched = selected.querySelectorAll('line, path');
|
|
||||||
}
|
|
||||||
|
|
||||||
let issue = false;
|
|
||||||
if (matched.length) {
|
|
||||||
Array.prototype.forEach.call(matched, function (match) {
|
|
||||||
const bb = match.getBBox();
|
|
||||||
if (!bb.width || !bb.height) {
|
|
||||||
issue = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (issue) {
|
|
||||||
const elems = ref ? copy : selected.children;
|
|
||||||
ret = getStrokedBBox(elems);
|
|
||||||
} else {
|
|
||||||
ret = selected.getBBox();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = selected.getBBox();
|
|
||||||
}
|
|
||||||
if (ref) {
|
|
||||||
copy.remove();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the given/selected element's bounding box object, convert it to be more
|
* Get the given/selected element's bounding box object, convert it to be more
|
||||||
* usable when necessary.
|
* usable when necessary.
|
||||||
|
@ -546,22 +488,16 @@ export const getBBox = function (elem) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'path':
|
case 'path':
|
||||||
|
case 'g':
|
||||||
|
case 'a':
|
||||||
if (selected.getBBox) {
|
if (selected.getBBox) {
|
||||||
ret = selected.getBBox();
|
ret = selected.getBBox();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'g':
|
|
||||||
case 'a':
|
|
||||||
ret = groupBBFix(selected);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if (elname === 'use') {
|
if (elname === 'use') {
|
||||||
ret = groupBBFix(selected); // , true);
|
ret = selected.getBBox(); // , true);
|
||||||
}
|
|
||||||
if (elname === 'use' || (elname === 'foreignObject' && isWebkit())) {
|
|
||||||
if (!ret) { ret = selected.getBBox(); }
|
|
||||||
|
|
||||||
} else if (visElemsArr.includes(elname)) {
|
} else if (visElemsArr.includes(elname)) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
try {
|
try {
|
||||||
|
@ -1030,8 +966,8 @@ export const getVisibleElements = function (parentElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentElems = [];
|
const contentElems = [];
|
||||||
const childrens = parentElement.children;
|
const children = parentElement.children;
|
||||||
Array.prototype.forEach.call(childrens, function (elem) {
|
Array.from(children, function (elem) {
|
||||||
if (elem.getBBox) {
|
if (elem.getBBox) {
|
||||||
contentElems.push(elem);
|
contentElems.push(elem);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue