various refactor

master
JFH 2021-10-16 16:25:17 +02:00
parent 0a6170bcff
commit 2fad5d93a9
8 changed files with 254 additions and 313 deletions

View File

@ -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_;

View File

@ -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,15 +628,14 @@ 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'); walkTree(elem, function (el) {
walkTree(elem, function (el) { 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;

View File

@ -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) &&

View File

@ -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.

View File

@ -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));
} }

View File

@ -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(
bb[o] = rubberBBox[o] / currentZoom; (o) => {
}); bb[o] = rubberBBox[o] / currentZoom;
}
);
rubberBBox = bb; rubberBBox = bb;
} else { } else {
rubberBBox = selectionContext_.getSVGContent().createSVGRect(); rubberBBox = selectionContext_.getSVGContent().createSVGRect();
@ -271,22 +303,20 @@ 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') { // Cache all bboxes
resultList = []; console.log("setbb");
selectionContext_.setCurBBoxes(getVisibleElementsAndBBoxes(parent));
if (!selectionContext_.getCurBBoxes().length) { }
// Cache all bboxes let i = selectionContext_.getCurBBoxes().length;
selectionContext_.setCurBBoxes(getVisibleElementsAndBBoxes(parent)); while (i--) {
const curBBoxes = selectionContext_.getCurBBoxes();
if (!rubberBBox.width) {
continue;
} }
let i = selectionContext_.getCurBBoxes().length; if (rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
while (i--) { resultList.push(curBBoxes[i].elem);
const curBBoxes = selectionContext_.getCurBBoxes();
if (!rubberBBox.width) { continue; }
if (rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
resultList.push(curBBoxes[i].elem);
}
} }
} }
@ -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);
} }
}; };

View File

@ -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; }

View File

@ -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);
} }