Merge pull request #628 from SVG-Edit/fix-transformation-issues

master
JFH 2021-08-29 18:21:23 +02:00 committed by GitHub
commit 66a4866656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 408 additions and 1194 deletions

View File

@ -1,14 +1,10 @@
import { NS } from '../../../instrumented/common/namespaces.js';
import * as transformlist from '../../../instrumented/svgcanvas/svgtransformlist.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as hstory from '../../../instrumented/svgcanvas/history.js';
describe('history', function () {
// TODO(codedread): Write tests for handling history events.
// Mocked out methods.
transformlist.changeRemoveElementFromListMap(() => { /* empty fn */ });
utilities.mock({
getHref () { return '#foo'; },
setHref () { /* empty fn */ },

View File

@ -1,330 +0,0 @@
import { NS } from '../../../instrumented/common/namespaces.js';
import * as transformlist from '../../../instrumented/svgcanvas/svgtransformlist.js';
import { disableSupportsNativeTransformLists } from '../../../instrumented/common/browser.js';
import almostEqualsPlugin from '../../support/assert-almostEquals.js';
import expectOutOfBoundsExceptionPlugin from '../../support/assert-expectOutOfBoundsException.js';
chai.use(almostEqualsPlugin);
chai.use(expectOutOfBoundsExceptionPlugin);
describe('svgtransformlist', function () {
disableSupportsNativeTransformLists();
let svgroot; let svgcontent; let rect; let circle;
/**
* Set up tests, adding elements.
* @returns {void}
*/
beforeEach(() => {
document.body.textContent = '';
svgroot = document.createElement('div');
svgroot.id = 'svgroot';
svgroot.style.visibility = 'hidden';
document.body.append(svgroot);
svgcontent = document.createElementNS(NS.SVG, 'svg');
svgroot.append(svgcontent);
rect = document.createElementNS(NS.SVG, 'rect');
svgcontent.append(rect);
rect.id = 'r';
circle = document.createElementNS(NS.SVG, 'circle');
svgcontent.append(circle);
circle.id = 'c';
});
/**
* Tear down tests, emptying SVG root, and resetting list map.
* @returns {void}
*/
afterEach(() => {
transformlist.resetListMap();
while (svgroot.hasChildNodes()) {
svgroot.firstChild.remove();
}
});
it('Test svgedit.transformlist package', function () {
assert.ok(transformlist);
assert.ok(transformlist.getTransformList);
});
it('Test svgedit.transformlist.getTransformList() function', function () {
const rxform = transformlist.getTransformList(rect);
const cxform = transformlist.getTransformList(circle);
assert.ok(rxform);
assert.ok(cxform);
assert.equal(typeof rxform, typeof {});
assert.equal(typeof cxform, typeof {});
});
it('Test SVGTransformList.numberOfItems property', function () {
const rxform = transformlist.getTransformList(rect);
assert.equal(typeof rxform.numberOfItems, typeof 0);
assert.equal(rxform.numberOfItems, 0);
});
it('Test SVGTransformList.initialize()', function () {
const rxform = transformlist.getTransformList(rect);
const cxform = transformlist.getTransformList(circle);
const t = svgcontent.createSVGTransform();
assert.ok(t);
assert.ok(rxform.initialize);
assert.equal(typeof rxform.initialize, typeof function () { /* empty fn */ });
rxform.initialize(t);
assert.equal(rxform.numberOfItems, 1);
assert.equal(cxform.numberOfItems, 0);
// If a transform was already in a transform list, this should
// remove it from its old list and add it to this list.
cxform.initialize(t);
// This also fails in Firefox native.
// assert.equal(rxform.numberOfItems, 0, 'Did not remove transform from list before initializing another transformlist');
assert.equal(cxform.numberOfItems, 1);
});
it('Test SVGTransformList.appendItem() and getItem()', function () {
const rxform = transformlist.getTransformList(rect);
const cxform = transformlist.getTransformList(circle);
const t1 = svgcontent.createSVGTransform();
const t2 = svgcontent.createSVGTransform();
const t3 = svgcontent.createSVGTransform();
assert.ok(rxform.appendItem);
assert.ok(rxform.getItem);
assert.equal(typeof rxform.appendItem, typeof function () { /* empty fn */ });
assert.equal(typeof rxform.getItem, typeof function () { /* empty fn */ });
rxform.appendItem(t1);
rxform.appendItem(t2);
rxform.appendItem(t3);
assert.equal(rxform.numberOfItems, 3);
const rxf = rxform.getItem(0);
assert.equal(rxf, t1);
assert.equal(rxform.getItem(1), t2);
assert.equal(rxform.getItem(2), t3);
assert.expectOutOfBoundsException(rxform, 'getItem', -1);
assert.expectOutOfBoundsException(rxform, 'getItem', 3);
cxform.appendItem(t1);
// These also fail in Firefox native.
// assert.equal(rxform.numberOfItems, 2, 'Did not remove a transform from a list before appending it to a new transformlist');
// assert.equal(rxform.getItem(0), t2, 'Found the wrong transform in a transformlist');
// assert.equal(rxform.getItem(1), t3, 'Found the wrong transform in a transformlist');
assert.equal(cxform.numberOfItems, 1);
assert.equal(cxform.getItem(0), t1);
});
it('Test SVGTransformList.removeItem()', function () {
const rxform = transformlist.getTransformList(rect);
const t1 = svgcontent.createSVGTransform();
const t2 = svgcontent.createSVGTransform();
assert.ok(rxform.removeItem);
assert.equal(typeof rxform.removeItem, typeof function () { /* empty fn */ });
rxform.appendItem(t1);
rxform.appendItem(t2);
const removedTransform = rxform.removeItem(0);
assert.equal(rxform.numberOfItems, 1);
assert.equal(removedTransform, t1);
assert.equal(rxform.getItem(0), t2);
assert.expectOutOfBoundsException(rxform, 'removeItem', -1);
assert.expectOutOfBoundsException(rxform, 'removeItem', 1);
});
it('Test SVGTransformList.replaceItem()', function () {
const rxform = transformlist.getTransformList(rect);
const cxform = transformlist.getTransformList(circle);
assert.ok(rxform.replaceItem);
assert.equal(typeof rxform.replaceItem, typeof function () { /* empty fn */ });
const t1 = svgcontent.createSVGTransform();
const t2 = svgcontent.createSVGTransform();
const t3 = svgcontent.createSVGTransform();
rxform.appendItem(t1);
rxform.appendItem(t2);
cxform.appendItem(t3);
const newItem = rxform.replaceItem(t3, 0);
assert.equal(rxform.numberOfItems, 2);
assert.equal(newItem, t3);
assert.equal(rxform.getItem(0), t3);
assert.equal(rxform.getItem(1), t2);
// Fails in Firefox native
// assert.equal(cxform.numberOfItems, 0);
// test replaceItem within a list
rxform.appendItem(t1);
rxform.replaceItem(t1, 0);
// Fails in Firefox native
// assert.equal(rxform.numberOfItems, 2);
assert.equal(rxform.getItem(0), t1);
assert.equal(rxform.getItem(1), t2);
});
it('Test SVGTransformList.insertItemBefore()', function () {
const rxform = transformlist.getTransformList(rect);
const cxform = transformlist.getTransformList(circle);
assert.ok(rxform.insertItemBefore);
assert.equal(typeof rxform.insertItemBefore, typeof function () { /* empty fn */ });
const t1 = svgcontent.createSVGTransform();
const t2 = svgcontent.createSVGTransform();
const t3 = svgcontent.createSVGTransform();
rxform.appendItem(t1);
rxform.appendItem(t2);
cxform.appendItem(t3);
const newItem = rxform.insertItemBefore(t3, 0);
assert.equal(rxform.numberOfItems, 3);
assert.equal(newItem, t3);
assert.equal(rxform.getItem(0), t3);
assert.equal(rxform.getItem(1), t1);
assert.equal(rxform.getItem(2), t2);
// Fails in Firefox native
// assert.equal(cxform.numberOfItems, 0);
rxform.insertItemBefore(t2, 1);
// Fails in Firefox native (they make copies of the transforms)
// assert.equal(rxform.numberOfItems, 3);
assert.equal(rxform.getItem(0), t3);
assert.equal(rxform.getItem(1), t2);
assert.equal(rxform.getItem(2), t1);
});
it('Test SVGTransformList.init() for translate(200,100)', function () {
rect.setAttribute('transform', 'translate(200,100)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const translate = rxform.getItem(0);
assert.equal(translate.type, 2);
const m = translate.matrix;
assert.equal(m.a, 1);
assert.equal(m.b, 0);
assert.equal(m.c, 0);
assert.equal(m.d, 1);
assert.equal(m.e, 200);
assert.equal(m.f, 100);
});
it('Test SVGTransformList.init() for scale(4)', function () {
rect.setAttribute('transform', 'scale(4)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const scale = rxform.getItem(0);
assert.equal(scale.type, 3);
const m = scale.matrix;
assert.equal(m.a, 4);
assert.equal(m.b, 0);
assert.equal(m.c, 0);
assert.equal(m.d, 4);
assert.equal(m.e, 0);
assert.equal(m.f, 0);
});
it('Test SVGTransformList.init() for scale(4,3)', function () {
rect.setAttribute('transform', 'scale(4,3)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const scale = rxform.getItem(0);
assert.equal(scale.type, 3);
const m = scale.matrix;
assert.equal(m.a, 4);
assert.equal(m.b, 0);
assert.equal(m.c, 0);
assert.equal(m.d, 3);
assert.equal(m.e, 0);
assert.equal(m.f, 0);
});
it('Test SVGTransformList.init() for rotate(45)', function () {
rect.setAttribute('transform', 'rotate(45)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const rotate = rxform.getItem(0);
assert.equal(rotate.type, 4);
assert.equal(rotate.angle, 45);
const m = rotate.matrix;
assert.almostEquals(1 / Math.sqrt(2), m.a);
assert.almostEquals(1 / Math.sqrt(2), m.b);
assert.almostEquals(-1 / Math.sqrt(2), m.c);
assert.almostEquals(1 / Math.sqrt(2), m.d);
assert.equal(m.e, 0);
assert.equal(m.f, 0);
});
it('Test SVGTransformList.init() for rotate(45, 100, 200)', function () {
rect.setAttribute('transform', 'rotate(45, 100, 200)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const rotate = rxform.getItem(0);
assert.equal(rotate.type, 4);
assert.equal(rotate.angle, 45);
const m = rotate.matrix;
assert.almostEquals(m.a, 1 / Math.sqrt(2));
assert.almostEquals(m.b, 1 / Math.sqrt(2));
assert.almostEquals(m.c, -1 / Math.sqrt(2));
assert.almostEquals(m.d, 1 / Math.sqrt(2));
const r = svgcontent.createSVGMatrix();
r.a = 1 / Math.sqrt(2); r.b = 1 / Math.sqrt(2);
r.c = -1 / Math.sqrt(2); r.d = 1 / Math.sqrt(2);
const t = svgcontent.createSVGMatrix();
t.e = -100; t.f = -200;
const t_ = svgcontent.createSVGMatrix();
t_.e = 100; t_.f = 200;
const result = t_.multiply(r).multiply(t);
assert.almostEquals(m.e, result.e);
assert.almostEquals(m.f, result.f);
});
it('Test SVGTransformList.init() for matrix(1, 2, 3, 4, 5, 6)', function () {
rect.setAttribute('transform', 'matrix(1,2,3,4,5,6)');
const rxform = transformlist.getTransformList(rect);
assert.equal(rxform.numberOfItems, 1);
const mt = rxform.getItem(0);
assert.equal(mt.type, 1);
const m = mt.matrix;
assert.equal(m.a, 1);
assert.equal(m.b, 2);
assert.equal(m.c, 3);
assert.equal(m.d, 4);
assert.equal(m.e, 5);
assert.equal(m.f, 6);
});
});

View File

@ -3,7 +3,6 @@ import 'pathseg';
import { NS } from '../../../instrumented/common/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as transformlist from '../../../instrumented/svgcanvas/svgtransformlist.js';
import * as math from '../../../instrumented/svgcanvas/math.js';
import * as path from '../../../instrumented/svgcanvas/path.js';
import setAssertionMethods from '../../support/assert-close.js';
@ -39,7 +38,7 @@ describe('utilities bbox', function () {
const mockPathActions = {
resetOrientation (pth) {
if (utilities.isNullish(pth) || pth.nodeName !== 'path') { return false; }
const tlist = transformlist.getTransformList(pth);
const tlist = pth.transform.baseVal;
const m = math.transformListToTransform(tlist).matrix;
tlist.clear();
pth.removeAttribute('transform');
@ -84,8 +83,6 @@ describe('utilities bbox', function () {
});
sandbox.append(svgroot);
// We're reusing ID's so we need to do this for transforms.
transformlist.resetListMap();
path.init(null);
mockaddSVGElementFromJsonCallCount = 0;
});

View File

@ -3,7 +3,6 @@ import 'pathseg';
import { NS } from '../../../instrumented/common/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as transformlist from '../../../instrumented/svgcanvas/svgtransformlist.js';
import * as math from '../../../instrumented/svgcanvas/math.js';
describe('utilities performance', function () {
@ -118,7 +117,7 @@ describe('utilities performance', function () {
const mockPathActions = {
resetOrientation (path) {
if (utilities.isNullish(path) || path.nodeName !== 'path') { return false; }
const tlist = transformlist.getTransformList(path);
const tlist = path.transform.baseVal;
const m = math.transformListToTransform(tlist).matrix;
tlist.clear();
path.removeAttribute('transform');

View File

@ -113,17 +113,6 @@ describe('utilities', function () {
assert.equal(toXml('\'<&>"'), '&#x27;&lt;&amp;&gt;&quot;');
});
it('Test svgedit.utilities.fromXml() function', function () {
const { fromXml } = utilities;
assert.equal(fromXml('a'), 'a');
assert.equal(fromXml('ABC_'), 'ABC_');
assert.equal(fromXml('PB&amp;J'), 'PB&J');
assert.equal(fromXml('2 &lt; 5'), '2 < 5');
assert.equal(fromXml('5 &gt; 2'), '5 > 2');
assert.equal(fromXml('&lt;&amp;&gt;'), '<&>');
});
it('Test svgedit.utilities.encode64() function', function () {
const { encode64 } = utilities;

518
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -79,7 +79,7 @@
"@babel/polyfill": "7.12.1",
"browser-fs-access": "^0.20.4",
"canvg": "3.0.7",
"core-js": "3.16.2",
"core-js": "3.16.3",
"elix": "15.0.0",
"html2canvas": "1.3.2",
"i18next": "20.4.0",
@ -103,14 +103,14 @@
"@rollup/plugin-node-resolve": "13.0.4",
"@rollup/plugin-replace": "3.0.0",
"@rollup/plugin-url": "6.1.0",
"@web/dev-server": "0.1.21",
"@web/dev-server": "0.1.22",
"@web/dev-server-rollup": "0.3.9",
"axe-core": "4.3.2",
"axe-core": "4.3.3",
"babel-plugin-transform-object-rest-spread": "7.0.0-beta.3",
"copyfiles": "2.4.1",
"core-js-bundle": "3.16.2",
"core-js-bundle": "3.16.3",
"cp-cli": "2.0.0",
"cypress": "8.3.0",
"cypress": "8.3.1",
"cypress-axe": "0.13.0",
"cypress-multi-reporters": "1.5.0",
"cypress-plugin-snapshots": "1.4.4",
@ -120,12 +120,12 @@
"eslint-plugin-chai-expect": "2.2.0",
"eslint-plugin-chai-expect-keywords": "2.1.0",
"eslint-plugin-chai-friendly": "0.7.2",
"eslint-plugin-compat": "3.12.0",
"eslint-plugin-compat": "3.13.0",
"eslint-plugin-cypress": "2.11.3",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-html": "6.1.2",
"eslint-plugin-import": "2.24.0",
"eslint-plugin-jsdoc": "36.0.7",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-jsdoc": "36.0.8",
"eslint-plugin-markdown": "2.2.0",
"eslint-plugin-no-unsanitized": "3.1.5",
"eslint-plugin-no-use-extend-native": "0.5.0",
@ -144,7 +144,7 @@
"remark-cli": "10.0.0",
"remark-lint-ordered-list-marker-value": "3.0.1",
"rimraf": "3.0.2",
"rollup": "2.56.2",
"rollup": "2.56.3",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.1",
"rollup-plugin-node-polyfills": "0.2.1",

View File

@ -11,7 +11,6 @@ import 'pathseg';
import { NS } from './namespaces.js';
const { userAgent } = navigator;
const svg = document.createElementNS(NS.SVG, 'svg');
// Note: Browser sniffing should only be used if no other detection method is possible
const isWebkit_ = userAgent.includes('AppleWebKit');
@ -99,26 +98,6 @@ const supportsNonScalingStroke_ = (function () {
return rect.style.vectorEffect === 'non-scaling-stroke';
}());
let supportsNativeSVGTransformLists_ = (function () {
const rect = document.createElementNS(NS.SVG, 'rect');
const rxform = rect.transform.baseVal;
const t1 = svg.createSVGTransform();
rxform.appendItem(t1);
const r1 = rxform.getItem(0);
const isSVGTransform = (o) => {
// https://developer.mozilla.org/en-US/docs/Web/API/SVGTransform
return o && typeof o === 'object' && typeof o.setMatrix === 'function' && 'angle' in o;
};
return isSVGTransform(r1) && isSVGTransform(t1) &&
r1.type === t1.type && r1.angle === t1.angle &&
r1.matrix.a === t1.matrix.a &&
r1.matrix.b === t1.matrix.b &&
r1.matrix.c === t1.matrix.c &&
r1.matrix.d === t1.matrix.d &&
r1.matrix.e === t1.matrix.e &&
r1.matrix.f === t1.matrix.f;
}());
// Public API
/**
@ -184,17 +163,3 @@ export const supportsGoodTextCharPos = () => supportsGoodTextCharPos_;
*/
export const supportsNonScalingStroke = () => supportsNonScalingStroke_;
/**
* @function module:browser.supportsNativeTransformLists
* @returns {boolean}
*/
export const supportsNativeTransformLists = () => supportsNativeSVGTransformLists_;
/**
* Set `supportsNativeSVGTransformLists_` to `false` (for unit testing).
* @function module:browser.disableSupportsNativeTransformLists
* @returns {void}
*/
export const disableSupportsNativeTransformLists = () => {
supportsNativeSVGTransformLists_ = false;
};

View File

@ -224,14 +224,12 @@ export const convertAttrs = function (element) {
const attrs = attrsToConvert[elName];
if (!attrs) { return; }
const len = attrs.length;
for (let i = 0; i < len; i++) {
const attr = attrs[i];
attrs.forEach( (attr) => {
const cur = element.getAttribute(attr);
if (cur && !isNaN(cur)) {
element.setAttribute(attr, (cur / typeMap_[unit]) + unit);
}
}
});
};
/**

View File

@ -136,11 +136,7 @@ export default {
} catch (err) {
// Should only occur in FF which formats points attr as "n,n n,n", so just split
const ptArr = elem.getAttribute('points').split(' ');
for (let i = 0; i < ptArr.length; i++) {
if (i === pos) {
ptArr[i] = x + ',' + y;
}
}
ptArr[pos] = x + ',' + y;
elem.setAttribute('points', ptArr.join(' '));
}
@ -208,7 +204,7 @@ export default {
let addThis;
// Grab the ends
const parts = [];
[ 'start', 'end' ].forEach(function (pos, i) {
[ 'start', 'end' ].forEach( (pos, i) => {
const key = 'c_' + pos;
let part = dataStorage.get(ethis, key);
if (part === null || part === undefined) { // Does this ever return nullish values?
@ -460,7 +456,7 @@ export default {
if (elem && dataStorage.has(elem, 'c_start')) {
// Remove the "translate" transform given to move
svgCanvas.removeFromSelection([ elem ]);
svgCanvas.getTransformList(elem).clear();
elem.transform.baseVal.clear();
}
}
if (connections.length) {

View File

@ -41,7 +41,7 @@ export default {
$id("sidepanel_content").insertAdjacentHTML( 'beforeend', propsWindowHtml );
// Define dynamic animation of the view box.
const updateViewBox = function () {
const updateViewBox = () => {
const { workarea } = svgEditor;
const portHeight = parseFloat(getComputedStyle(workarea, null).height.replace("px", ""));
const portWidth = parseFloat(getComputedStyle(workarea, null).width.replace("px", ""));

View File

@ -94,8 +94,6 @@ export default {
canv.recalculateDimensions(curShape);
canv.getTransformList(curShape);
lastBBox = curShape.getBBox();
return {
@ -112,7 +110,7 @@ export default {
const x = opts.mouse_x / zoom;
const y = opts.mouse_y / zoom;
const tlist = canv.getTransformList(curShape);
const tlist = curShape.transform.baseVal;
const box = curShape.getBBox();
const left = box.x; const top = box.y;

View File

@ -10,7 +10,6 @@ import {
import {
transformPoint, transformListToTransform, matrixMultiply, transformBox
} from './math.js';
import { getTransformList } from './svgtransformlist.js';
// this is how we map paths to our preferred relative segment types
const pathMap = [
@ -51,11 +50,11 @@ export const init = function (editorContext) {
* @type {module:path.EditorContext#remapElement}
*/
export const remapElement = function (selected, changes, m) {
const remap = function (x, y) { return transformPoint(x, y, m); };
const scalew = function (w) { return m.a * w; };
const scaleh = function (h) { return m.d * h; };
const remap = (x, y) => transformPoint(x, y, m);
const scalew = (w) => m.a * w;
const scaleh = (h) => m.d * h;
const doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg';
const finishUp = function () {
const finishUp = () => {
if (doSnapping) {
Object.entries(changes).forEach(([ o, value ]) => {
changes[o] = snapToGrid(value);
@ -65,8 +64,7 @@ export const remapElement = function (selected, changes, m) {
};
const box = getBBox(selected);
for (let i = 0; i < 2; i++) {
const type = i === 0 ? 'fill' : 'stroke';
[ 'fill', 'stroke' ].forEach( (type) => {
const attrVal = selected.getAttribute(type);
if (attrVal && attrVal.startsWith('url(') && (m.a < 0 || m.d < 0)) {
const grad = getRefElem(attrVal);
@ -90,7 +88,7 @@ export const remapElement = function (selected, changes, m) {
findDefs().append(newgrad);
selected.setAttribute(type, 'url(#' + newgrad.id + ')');
}
}
});
const elName = selected.tagName;
if (elName === 'g' || elName === 'text' || elName === 'tspan' || elName === 'use') {
@ -104,7 +102,7 @@ export const remapElement = function (selected, changes, m) {
changes.y = Number.parseFloat(changes.y) + tNew.f;
} else {
// we just absorb all matrices into the element and don't do any remapping
const chlist = getTransformList(selected);
const chlist = selected.transform.baseVal;
const mt = editorContext_.getSVGRoot().createSVGTransform();
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m));
chlist.clear();
@ -121,7 +119,7 @@ export const remapElement = function (selected, changes, m) {
// Allow images to be inverted (give them matrix when flipped)
if (elName === 'image' && (m.a < 0 || m.d < 0)) {
// Convert to matrix
const chlist = getTransformList(selected);
const chlist = selected.transform.baseVal;
const mt = editorContext_.getSVGRoot().createSVGTransform();
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m));
chlist.clear();
@ -181,20 +179,17 @@ export const remapElement = function (selected, changes, m) {
break;
} case 'polyline':
case 'polygon': {
const len = changes.points.length;
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
changes.points.forEach( (pt) => {
const { x, y } = remap(pt.x, pt.y);
changes.points[i].x = x;
changes.points[i].y = y;
}
pt.x = x;
pt.y = y;
});
// const len = changes.points.length;
let pstr = '';
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
changes.points.forEach( (pt) => {
pstr += pt.x + ',' + pt.y + ' ';
}
});
selected.setAttribute('points', pstr);
break;
} case 'path': {
@ -221,9 +216,12 @@ export const remapElement = function (selected, changes, m) {
len = changes.d.length;
const firstseg = changes.d[0];
const currentpt = remap(firstseg.x, firstseg.y);
changes.d[0].x = currentpt.x;
changes.d[0].y = currentpt.y;
let currentpt;
if (len > 0) {
currentpt = remap(firstseg.x, firstseg.y);
changes.d[0].x = currentpt.x;
changes.d[0].y = currentpt.y;
}
for (let i = 1; i < len; ++i) {
const seg = changes.d[i];
const { type } = seg;
@ -256,9 +254,7 @@ export const remapElement = function (selected, changes, m) {
} // for each segment
let dstr = '';
len = changes.d.length;
for (let i = 0; i < len; ++i) {
const seg = changes.d[i];
changes.d.forEach( (seg) => {
const { type } = seg;
dstr += pathMap[type];
switch (type) {
@ -297,7 +293,7 @@ export const remapElement = function (selected, changes, m) {
dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' ';
break;
}
}
});
selected.setAttribute('d', dstr);
break;

View File

@ -14,9 +14,6 @@ import {
import {
transformPoint, hasMatrixTransform, getMatrix, snapToAngle
} from './math.js';
import {
getTransformList
} from './svgtransformlist.js';
import { supportsNonScalingStroke } from '../common/browser.js';
import * as draw from './draw.js';
import * as pathModule from './path.js';
@ -144,7 +141,7 @@ export const mouseMoveEvent = function (evt) {
// update the dummy transform in our transform list
// to be a translate
const xform = svgRoot.createSVGTransform();
tlist = getTransformList(selected);
tlist = selected.transform?.baseVal;
// Note that if Webkit and there's no ID for this
// element, the dummy transform may have gotten lost.
// This results in unexpected behaviour
@ -209,7 +206,7 @@ export const mouseMoveEvent = function (evt) {
// we track the resize bounding box and translate/scale the selected element
// while the mouse is down, when mouse goes up, we use this to recalculate
// the shape's coordinates
tlist = getTransformList(selected);
tlist = selected.transform.baseVal;
const hasMatrix = hasMatrixTransform(tlist);
box = hasMatrix ? eventContext_.getInitBbox() : utilsGetBBox(selected);
let left = box.x;
@ -1019,7 +1016,7 @@ export const mouseDownEvent = function (evt) {
eventContext_.setStartTransform(mouseTarget.getAttribute('transform'));
const tlist = getTransformList(mouseTarget);
const tlist = mouseTarget.transform.baseVal;
switch (eventContext_.getCurrentMode()) {
case 'select':
eventContext_.setStarted(true);
@ -1046,7 +1043,7 @@ export const mouseDownEvent = function (evt) {
// a transform to use for its translate
for (const selectedElement of selectedElements()) {
if (isNullish(selectedElement)) { continue; }
const slist = getTransformList(selectedElement);
const slist = selectedElement.transform?.baseVal;
if (slist.numberOfItems) {
slist.insertItemBefore(svgRoot.createSVGTransform(), 0);
} else {

View File

@ -7,7 +7,6 @@
*/
import { getHref, setHref, getRotationAngle, isNullish } from './utilities.js';
import { removeElementFromListMap } from './svgtransformlist.js';
/**
* Group: Undo/Redo history management.
@ -215,6 +214,7 @@ export class InsertElementCommand extends Command {
}
}
/**
* History command for an element removed from the DOM.
* @implements {module:history.HistoryCommand}
@ -232,9 +232,6 @@ export class RemoveElementCommand extends Command {
this.text = text || ('Delete ' + elem.tagName);
this.nextSibling = oldNextSibling;
this.parent = oldParent;
// special hack for webkit: remove this element's entry in the svgTransformLists map
removeElementFromListMap(elem);
}
/**
@ -245,7 +242,6 @@ export class RemoveElementCommand extends Command {
*/
apply (handler) {
super.apply(handler, () => {
removeElementFromListMap(this.elem);
this.parent = this.elem.parentNode;
this.elem.remove();
});
@ -259,7 +255,6 @@ export class RemoveElementCommand extends Command {
*/
unapply (handler) {
super.unapply(handler, () => {
removeElementFromListMap(this.elem);
if (isNullish(this.nextSibling) && window.console) {
console.error('Reference element was lost');
}
@ -385,8 +380,6 @@ export class ChangeElementCommand extends Command {
}
}
}
// Remove transformlist to prevent confusion that causes bugs like 575.
removeElementFromListMap(this.elem);
});
}
}

View File

@ -20,7 +20,6 @@
*/
import { NS } from '../common/namespaces.js';
import { getTransformList } from './svgtransformlist.js';
// Constants
const NEAR_ZERO = 1e-14;
@ -178,7 +177,7 @@ export const transformListToTransform = function (tlist, min, max) {
* @returns {SVGMatrix} The matrix object associated with the element's transformlist
*/
export const getMatrix = function (elem) {
const tlist = getTransformList(elem);
const tlist = elem.transform.baseVal;
return transformListToTransform(tlist).matrix;
};

View File

@ -8,7 +8,6 @@
import { NS } from '../common/namespaces.js';
import { shortFloat } from '../common/units.js';
import { getTransformList } from './svgtransformlist.js';
import { ChangeElementCommand, BatchCommand } from './history.js';
import {
transformPoint, snapToAngle, rectsIntersect,
@ -879,7 +878,7 @@ export const pathActionsMethod = (function () {
*/
resetOrientation (pth) {
if (isNullish(pth) || pth.nodeName !== 'path') { return false; }
const tlist = getTransformList(pth);
const tlist = pth.transform.baseVal;
const m = transformListToTransform(tlist).matrix;
tlist.clear();
pth.removeAttribute('transform');

View File

@ -6,7 +6,6 @@
* @copyright 2011 Alexis Deveria, 2011 Jeff Schiller
*/
import { getTransformList } from './svgtransformlist.js';
import { shortFloat } from '../common/units.js';
import { transformPoint } from './math.js';
import {
@ -526,7 +525,7 @@ export const recalcRotatedPath = function () {
// now we must set the new transform to be rotated around the new center
const Rnc = editorContext_.getSVGRoot().createSVGTransform();
const tlist = getTransformList(currentPath);
const tlist = currentPath.transform.baseVal;
Rnc.setRotate((angle * 180.0 / Math.PI), newcx, newcy);
tlist.replaceItem(Rnc, 0);
};

View File

@ -7,7 +7,6 @@
import { NS } from '../common/namespaces.js';
import { convertToNum } from '../common/units.js';
import { isWebkit } from '../common/browser.js';
import { getTransformList } from './svgtransformlist.js';
import { getRotationAngle, getHref, getBBox, getRefElem, isNullish } from './utilities.js';
import { BatchCommand, ChangeElementCommand } from './history.js';
import { remapElement } from './coords.js';
@ -57,7 +56,7 @@ export const init = function (editorContext) {
*/
export const updateClipPath = function (attr, tx, ty) {
const path = getRefElem(attr).firstChild;
const cpXform = getTransformList(path);
const cpXform = path.transform.baseVal;
const newxlate = context_.getSVGRoot().createSVGTransform();
newxlate.setTranslate(tx, ty);
@ -74,17 +73,10 @@ export const updateClipPath = function (attr, tx, ty) {
* @returns {Command} Undo command object with the resulting change
*/
export const recalculateDimensions = function (selected) {
if (isNullish(selected)) { return null; }
// Firefox Issue - 1081
if (selected.nodeName === 'svg' && navigator.userAgent.includes('Firefox/20')) {
return null;
}
if (!selected) return null;
const svgroot = context_.getSVGRoot();
const dataStorage = context_.getDataStorage();
const tlist = getTransformList(selected);
const tlist = selected.transform?.baseVal;
// remove any unnecessary transforms
if (tlist && tlist.numberOfItems > 0) {
let k = tlist.numberOfItems;
@ -307,7 +299,7 @@ export const recalculateDimensions = function (selected) {
tx = 0;
ty = 0;
if (child.nodeType === 1) {
const childTlist = getTransformList(child);
const childTlist = child.transform.baseVal;
// some children might not have a transform (<metadata>, <defs>, etc)
if (!childTlist) { continue; }
@ -387,7 +379,7 @@ export const recalculateDimensions = function (selected) {
// if (href == getHref(useElem)) {
// const usexlate = svgroot.createSVGTransform();
// usexlate.setTranslate(-tx,-ty);
// getTransformList(useElem).insertItemBefore(usexlate,0);
// useElem.transform.baseVal.insertItemBefore(usexlate,0);
// batchCmd.addSubCommand( recalculateDimensions(useElem) );
// }
// }
@ -441,7 +433,7 @@ export const recalculateDimensions = function (selected) {
oldStartTransform = context_.getStartTransform();
context_.setStartTransform(child.getAttribute('transform'));
const childTlist = getTransformList(child);
const childTlist = child.transform?.baseVal;
// some children might not have a transform (<metadata>, <defs>, etc)
if (childTlist) {
const newxlate = svgroot.createSVGTransform();
@ -463,7 +455,7 @@ export const recalculateDimensions = function (selected) {
if (href === getHref(useElem)) {
const usexlate = svgroot.createSVGTransform();
usexlate.setTranslate(-tx, -ty);
getTransformList(useElem).insertItemBefore(usexlate, 0);
useElem.transform.baseVal.insertItemBefore(usexlate, 0);
batchCmd.addSubCommand(recalculateDimensions(useElem));
}
}
@ -485,7 +477,7 @@ export const recalculateDimensions = function (selected) {
if (child.nodeType === 1) {
oldStartTransform = context_.getStartTransform();
context_.setStartTransform(child.getAttribute('transform'));
const childTlist = getTransformList(child);
const childTlist = child.transform?.baseVal;
if (!childTlist) { continue; }
@ -566,7 +558,7 @@ export const recalculateDimensions = function (selected) {
if (child.nodeType === 1) {
oldStartTransform = context_.getStartTransform();
context_.setStartTransform(child.getAttribute('transform'));
const childTlist = getTransformList(child);
const childTlist = child.transform?.baseVal;
const newxlate = svgroot.createSVGTransform();
newxlate.setTranslate(tx, ty);
if (childTlist.numberOfItems) {
@ -649,7 +641,7 @@ export const recalculateDimensions = function (selected) {
if (attrVal === 'userSpaceOnUse') {
// Update the userSpaceOnUse element
m = transformListToTransform(tlist).matrix;
const gtlist = getTransformList(paint);
const gtlist = paint.transform.baseVal;
const gmatrix = transformListToTransform(gtlist).matrix;
m = matrixMultiply(m, gmatrix);
const mStr = 'matrix(' + [ m.a, m.b, m.c, m.d, m.e, m.f ].join(',') + ')';

View File

@ -19,54 +19,55 @@ const REVERSE_NS = getReverseNS();
* @type {PlainObject}
*/
/* eslint-disable max-len */
const svgGenericWhiteList = [ 'class', 'id', 'display', 'transform' ];
const svgWhiteList_ = {
// SVG Elements
a: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'xlink:href', 'xlink:title' ],
circle: [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
clipPath: [ 'class', 'clipPathUnits', 'id' ],
defs: [ 'id' ],
style: [ 'id', 'type' ],
a: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'xlink:href', 'xlink:title' ],
circle: [ 'clip-path', 'clip-rule', 'cx', 'cy', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage' ],
clipPath: [ 'clipPathUnits', 'id' ],
defs: [],
style: [ 'type' ],
desc: [],
ellipse: [ 'class', 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
ellipse: [ 'clip-path', 'clip-rule', 'cx', 'cy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage' ],
feBlend: [ 'in', 'in2' ],
feColorMatrix: [ 'in', 'type', 'value', 'result', 'values', 'id' ],
feComposite: [ 'in', 'id', 'operator', 'result', 'in2' ],
feFlood: [ 'flood-color', 'in', 'id', 'result', 'flood-opacity' ],
feGaussianBlur: [ 'class', 'color-interpolation-filters', 'id', 'in', 'requiredFeatures', 'stdDeviation', 'result' ],
feColorMatrix: [ 'in', 'type', 'value', 'result', 'values' ],
feComposite: [ 'in', 'operator', 'result', 'in2' ],
feFlood: [ 'flood-color', 'in', 'result', 'flood-opacity' ],
feGaussianBlur: [ 'color-interpolation-filters', 'in', 'requiredFeatures', 'stdDeviation', 'result' ],
feMerge: [],
feMergeNode: [ 'in' ],
feMorphology: [ 'class', 'in', 'operator', 'radius' ],
feOffset: [ 'dx', 'id', 'in', 'dy', 'result' ],
filter: [ 'class', 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'id', 'primitiveUnits', 'requiredFeatures', 'style', 'width', 'x', 'xlink:href', 'y' ],
foreignObject: [ 'class', 'font-size', 'height', 'id', 'opacity', 'requiredFeatures', 'style', 'transform', 'width', 'x', 'y' ],
g: [ 'class', 'clip-path', 'clip-rule', 'id', 'display', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor' ],
image: [ 'class', 'clip-path', 'clip-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'transform', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
line: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'x1', 'x2', 'y1', 'y2' ],
linearGradient: [ 'class', 'id', 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
marker: [ 'id', 'class', 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ],
mask: [ 'class', 'height', 'id', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
metadata: [ 'class', 'id' ],
path: [ 'class', 'clip-path', 'clip-rule', 'd', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform' ],
pattern: [ 'class', 'height', 'id', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y' ],
polygon: [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'class', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'sides', 'shape', 'edge', 'point', 'starRadiusMultiplier', 'r', 'radialshift' ],
polyline: [ 'class', 'clip-path', 'clip-rule', 'id', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'se:connector' ],
radialGradient: [ 'class', 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'id', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href' ],
rect: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'width', 'x', 'y' ],
stop: [ 'class', 'id', 'offset', 'requiredFeatures', 'stop-opacity', 'style', 'systemLanguage', 'stop-color', 'gradientUnits', 'gradientTransform' ],
svg: [ 'class', 'clip-path', 'clip-rule', 'filter', 'id', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'version', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'xmlns:oi', 'oi:animations', 'y', 'stroke-linejoin', 'fill-rule', 'aria-label', 'stroke-width', 'fill-rule' ],
switch: [ 'class', 'id', 'requiredFeatures', 'systemLanguage' ],
symbol: [ 'class', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'opacity', 'overflow', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'transform', 'viewBox', 'width', 'height' ],
text: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'transform', 'x', 'xml:space', 'y' ],
textPath: [ 'class', 'id', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'transform', 'xlink:href' ],
feMorphology: [ 'in', 'operator', 'radius' ],
feOffset: [ 'dx', 'in', 'dy', 'result' ],
filter: [ 'color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'primitiveUnits', 'requiredFeatures', 'style', 'width', 'x', 'xlink:href', 'y' ],
foreignObject: [ 'font-size', 'height', 'opacity', 'requiredFeatures', 'style', 'width', 'x', 'y' ],
g: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor' ],
image: [ 'clip-path', 'clip-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'style', 'systemLanguage', 'width', 'x', 'xlink:href', 'xlink:title', 'y' ],
line: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'x1', 'x2', 'y1', 'y2' ],
linearGradient: [ 'gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2' ],
marker: [ 'markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'systemLanguage', 'viewBox' ],
mask: [ 'height', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y' ],
metadata: [ 'id' ],
path: [ 'clip-path', 'clip-rule', 'd', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage' ],
pattern: [ 'height', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'style', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y' ],
polygon: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'sides', 'shape', 'edge', 'point', 'starRadiusMultiplier', 'r', 'radialshift' ],
polyline: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'se:connector' ],
radialGradient: [ 'cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href' ],
rect: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'width', 'x', 'y' ],
stop: [ 'offset', 'requiredFeatures', 'stop-opacity', 'style', 'systemLanguage', 'stop-color', 'gradientUnits', 'gradientTransform' ],
svg: [ 'clip-path', 'clip-rule', 'enable-background', 'filter', 'height', 'mask', 'preserveAspectRatio', 'requiredFeatures', 'style', 'systemLanguage', 'version', 'viewBox', 'width', 'x', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'xmlns:oi', 'oi:animations', 'y', 'stroke-linejoin', 'fill-rule', 'aria-label', 'stroke-width', 'fill-rule', 'xml:space' ],
switch: [ 'requiredFeatures', 'systemLanguage' ],
symbol: [ 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'opacity', 'overflow', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'viewBox', 'width', 'height' ],
text: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'x', 'xml:space', 'y' ],
textPath: [ 'method', 'requiredFeatures', 'spacing', 'startOffset', 'style', 'systemLanguage', 'xlink:href' ],
title: [],
tspan: [ 'class', 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'id', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'transform', 'x', 'xml:space', 'y' ],
use: [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'id', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'transform', 'width', 'x', 'xlink:href', 'y' ],
tspan: [ 'clip-path', 'clip-rule', 'dx', 'dy', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemLanguage', 'text-anchor', 'textLength', 'x', 'xml:space', 'y' ],
use: [ 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'width', 'x', 'xlink:href', 'y', 'overflow' ],
// MathML Elements
annotation: [ 'encoding' ],
'annotation-xml': [ 'encoding' ],
maction: [ 'actiontype', 'other', 'selection' ],
math: [ 'class', 'id', 'display', 'xmlns' ],
math: [ 'xmlns' ],
menclose: [ 'notation' ],
merror: [],
mfrac: [ 'linethickness' ],
@ -96,10 +97,11 @@ const svgWhiteList_ = {
semantics: []
};
/* eslint-enable max-len */
// add generic attributes to all elements of the whitelist
Object.keys(svgWhiteList_).forEach((element) => svgWhiteList_[element] = [ ...svgWhiteList_[element], ...svgGenericWhiteList ]);
// Produce a Namespace-aware version of svgWhitelist
const svgWhiteListNS_ = {};
Object.entries(svgWhiteList_).forEach(function ([ elt, atts ]) {
Object.entries(svgWhiteList_).forEach(([ elt, atts ]) => {
const attNS = {};
Object.entries(atts).forEach(function ([ _i, att ]) {
if (att.includes(':')) {

View File

@ -9,7 +9,6 @@
import { isTouch, isWebkit } from '../common/browser.js'; // , isOpera
import { getRotationAngle, getBBox, getStrokedBBox, isNullish } from './utilities.js';
import { transformListToTransform, transformBox, transformPoint } from './math.js';
import { getTransformList } from './svgtransformlist.js';
let svgFactory_;
let config_;
@ -124,7 +123,7 @@ export class Selector {
}
// loop and transform our bounding box until we reach our first rotation
const tlist = getTransformList(selected);
const tlist = selected.transform.baseVal;
const m = transformListToTransform(tlist).matrix;
// This should probably be handled somewhere else, but for now

View File

@ -16,9 +16,6 @@ import {
import {
transformPoint, matrixMultiply, transformListToTransform
} from './math.js';
import {
getTransformList
} from './svgtransformlist.js';
import {
recalculateDimensions
} from './recalculate.js';
@ -171,7 +168,7 @@ export const moveSelectedElements = function (dx, dy, undoable = true) {
const selected = selectedElements[i];
if (!isNullish(selected)) {
const xform = elementContext_.getSVGRoot().createSVGTransform();
const tlist = getTransformList(selected);
const tlist = selected.transform?.baseVal;
// dx and dy could be arrays
if (Array.isArray(dx)) {
@ -503,7 +500,7 @@ export const pushGroupProperty = function (g, undoable) {
const len = children.length;
const xform = g.getAttribute('transform');
const glist = getTransformList(g);
const glist = g.transform.baseVal;
const m = transformListToTransform(glist).matrix;
const batchCmd = new BatchCommand('Push group properties');
@ -576,7 +573,7 @@ export const pushGroupProperty = function (g, undoable) {
}
}
let chtlist = getTransformList(elem);
let chtlist = elem.transform?.baseVal;
// Don't process gradient transforms
if (elem.tagName.includes('Gradient')) { chtlist = null; }
@ -717,7 +714,7 @@ export const convertToGroup = function (elem) {
}
dataStorage.remove(elem, 'gsvg');
const tlist = getTransformList(elem);
const tlist = elem.transform.baseVal;
const xform = elementContext_.getSVGRoot().createSVGTransform();
xform.setTranslate(pt.x, pt.y);
tlist.appendItem(xform);

View File

@ -10,9 +10,6 @@ import {
isNullish, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible
} from './utilities.js';
import { transformPoint, transformListToTransform, rectsIntersect } from './math.js';
import {
getTransformList
} from './svgtransformlist.js';
import * as hstry from './history.js';
import { getClosest } from '../editor/components/jgraduate/Util.js';
@ -164,7 +161,7 @@ export const getMouseTargetMethod = function (evt) {
return svgCanvas.selectorManager.selectorParentGroup;
}
while (!mouseTarget.parentNode?.isSameNode(selectionContext_.getCurrentGroup() || currentLayer)) {
while (!mouseTarget?.parentNode?.isSameNode(selectionContext_.getCurrentGroup() || currentLayer)) {
mouseTarget = mouseTarget.parentNode;
}
@ -353,7 +350,7 @@ export const setRotationAngle = function (val, preventUndo) {
const oldTransform = elem.getAttribute('transform');
const bbox = utilsGetBBox(elem);
const cx = bbox.x + bbox.width / 2; const cy = bbox.y + bbox.height / 2;
const tlist = getTransformList(elem);
const tlist = elem.transform.baseVal;
// only remove the real rotational transform if present (i.e. at index=0)
if (tlist.numberOfItems > 0) {
@ -411,7 +408,6 @@ export const recalculateAllSelectedDimensions = function () {
let i = selectedElements().length;
while (i--) {
const elem = selectedElements()[i];
// if (getRotationAngle(elem) && !hasMatrixTransform(getTransformList(elem))) { continue; }
const cmd = svgCanvas.recalculateDimensions(elem);
if (cmd) {
batchCmd.addSubCommand(cmd);

View File

@ -17,7 +17,6 @@ import {
import {
transformPoint, transformListToTransform
} from './math.js';
import { resetListMap } from './svgtransformlist.js';
import {
convertUnit, shortFloat, convertToNum
} from '../common/units.js';
@ -491,8 +490,6 @@ export const setSvgString = function (xmlString, preventUndo) {
// reset zoom
svgContext_.setCurrentZoom(1);
// reset transform lists
resetListMap();
svgCanvas.clearSelection();
pathModule.clearData();
svgContext_.getSVGRoot().append(svgCanvas.selectorManager.selectorParentGroup);

View File

@ -87,9 +87,6 @@ import {
import {
isChrome, isWebkit
} from '../common/browser.js'; // , supportsEditableText
import {
getTransformList, SVGTransformList as SVGEditTransformList
} from './svgtransformlist.js';
import {
remapElement,
init as coordsInit
@ -151,7 +148,6 @@ if (window.opera) {
* @borrows module:utilities.getRefElem as #getRefElem
* @borrows module:utilities.assignAttributes as #assignAttributes
*
* @borrows module:SVGTransformList.getTransformList as #getTransformList
* @borrows module:math.matrixMultiply as #matrixMultiply
* @borrows module:math.hasMatrixTransform as #hasMatrixTransform
* @borrows module:math.transformListToTransform as #transformListToTransform
@ -372,7 +368,6 @@ class SvgCanvas {
*/
const addSVGElementFromJson = this.addSVGElementFromJson = addSVGElementsFromJson;
canvas.getTransformList = getTransformList;
canvas.matrixMultiply = matrixMultiply;
canvas.hasMatrixTransform = hasMatrixTransform;
canvas.transformListToTransform = transformListToTransform;
@ -2756,7 +2751,6 @@ class SvgCanvas {
NS,
preventClickDefault,
RemoveElementCommand,
SVGEditTransformList,
text2xml,
transformBox,
transformPoint,

View File

@ -1,393 +0,0 @@
/**
* Partial polyfill of `SVGTransformList`
* @module SVGTransformList
*
* @license MIT
*
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import { NS } from '../common/namespaces.js';
import { supportsNativeTransformLists } from '../common/browser.js';
const svgroot = document.createElementNS(NS.SVG, 'svg');
/**
* Helper function to convert `SVGTransform` to a string.
* @param {SVGTransform} xform
* @returns {string}
*/
function transformToString (xform) {
const m = xform.matrix;
let text = '';
switch (xform.type) {
case 1: // MATRIX
text = 'matrix(' + [ m.a, m.b, m.c, m.d, m.e, m.f ].join(',') + ')';
break;
case 2: // TRANSLATE
text = 'translate(' + m.e + ',' + m.f + ')';
break;
case 3: // SCALE
text = (m.a === m.d) ? `scale(${m.a})` : `scale(${m.a},${m.d})`;
break;
case 4: { // ROTATE
let cx = 0;
let cy = 0;
// this prevents divide by zero
if (xform.angle !== 0) {
const K = 1 - m.a;
cy = (K * m.f + m.b * m.e) / (K * K + m.b * m.b);
cx = (m.e - m.b * cy) / K;
}
text = 'rotate(' + xform.angle + ' ' + cx + ',' + cy + ')';
break;
}
}
return text;
}
/**
* Map of SVGTransformList objects.
*/
let listMap_ = {};
/**
* @interface module:SVGTransformList.SVGEditTransformList
* @property {Integer} numberOfItems unsigned long
*/
/**
* @function module:SVGTransformList.SVGEditTransformList#clear
* @returns {void}
*/
/**
* @function module:SVGTransformList.SVGEditTransformList#initialize
* @param {SVGTransform} newItem
* @returns {SVGTransform}
*/
/**
* DOES NOT THROW DOMException, INDEX_SIZE_ERR.
* @function module:SVGTransformList.SVGEditTransformList#getItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
/**
* DOES NOT THROW DOMException, INDEX_SIZE_ERR.
* @function module:SVGTransformList.SVGEditTransformList#insertItemBefore
* @param {SVGTransform} newItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
/**
* DOES NOT THROW DOMException, INDEX_SIZE_ERR.
* @function module:SVGTransformList.SVGEditTransformList#replaceItem
* @param {SVGTransform} newItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
/**
* DOES NOT THROW DOMException, INDEX_SIZE_ERR.
* @function module:SVGTransformList.SVGEditTransformList#removeItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
/**
* @function module:SVGTransformList.SVGEditTransformList#appendItem
* @param {SVGTransform} newItem
* @returns {SVGTransform}
*/
/**
* NOT IMPLEMENTED.
* @ignore
* @function module:SVGTransformList.SVGEditTransformList#createSVGTransformFromMatrix
* @param {SVGMatrix} matrix
* @returns {SVGTransform}
*/
/**
* NOT IMPLEMENTED.
* @ignore
* @function module:SVGTransformList.SVGEditTransformList#consolidate
* @returns {SVGTransform}
*/
/**
* SVGTransformList implementation for Webkit.
* These methods do not currently raise any exceptions.
* These methods also do not check that transforms are being inserted. This is basically
* implementing as much of SVGTransformList that we need to get the job done.
* @implements {module:SVGTransformList.SVGEditTransformList}
*/
export class SVGTransformList {
/**
* @param {Element} elem
* @returns {SVGTransformList}
*/
constructor (elem) {
this._elem = elem || null;
this._xforms = [];
// TODO: how do we capture the undo-ability in the changed transform list?
this._update = function () {
let tstr = '';
// /* const concatMatrix = */ svgroot.createSVGMatrix();
for (let i = 0; i < this.numberOfItems; ++i) {
const xform = this._list.getItem(i);
tstr += transformToString(xform) + ' ';
}
this._elem.setAttribute('transform', tstr);
};
this._list = this;
this._init = function () {
// Transform attribute parser
let str = this._elem.getAttribute('transform');
if (!str) { return; }
// TODO: Add skew support in future
const re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/;
// const re = /\s*(?<xform>(?:scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/;
let m = true;
while (m) {
m = str.match(re);
str = str.replace(re, '');
if (m && m[1]) {
const x = m[1];
const bits = x.split(/\s*\(/);
const name = bits[0];
const valBits = bits[1].match(/\s*(.*?)\s*\)/);
valBits[1] = valBits[1].replace(/(\d)-/g, '$1 -');
const valArr = valBits[1].split(/[, ]+/);
const letters = 'abcdef'.split('');
/*
if (m && m.groups.xform) {
const x = m.groups.xform;
const [name, bits] = x.split(/\s*\(/);
const valBits = bits.match(/\s*(?<nonWhitespace>.*?)\s*\)/);
valBits.groups.nonWhitespace = valBits.groups.nonWhitespace.replace(
/(?<digit>\d)-/g, '$<digit> -'
);
const valArr = valBits.groups.nonWhitespace.split(/[, ]+/);
const letters = [...'abcdef'];
*/
const mtx = svgroot.createSVGMatrix();
Object.values(valArr).forEach(function (item, i) {
valArr[i] = Number.parseFloat(item);
if (name === 'matrix') {
mtx[letters[i]] = valArr[i];
}
});
const xform = svgroot.createSVGTransform();
const fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1);
const values = name === 'matrix' ? [ mtx ] : valArr;
if (name === 'scale' && values.length === 1) {
values.push(values[0]);
} else if (name === 'translate' && values.length === 1) {
values.push(0);
} else if (name === 'rotate' && values.length === 1) {
values.push(0, 0);
}
xform[fname](...values);
this._list.appendItem(xform);
}
}
};
this._removeFromOtherLists = function (item) {
if (item) {
// Check if this transform is already in a transformlist, and
// remove it if so.
Object.values(listMap_).some((tl) => {
for (let i = 0, len = tl._xforms.length; i < len; ++i) {
if (tl._xforms[i] === item) {
tl.removeItem(i);
return true;
}
}
return false;
});
}
};
this.numberOfItems = 0;
}
/**
* @returns {void}
*/
clear () {
this.numberOfItems = 0;
this._xforms = [];
}
/**
* @param {SVGTransform} newItem
* @returns {void}
*/
initialize (newItem) {
this.numberOfItems = 1;
this._removeFromOtherLists(newItem);
this._xforms = [ newItem ];
}
/**
* @param {Integer} index unsigned long
* @throws {Error}
* @returns {SVGTransform}
*/
getItem (index) {
if (index < this.numberOfItems && index >= 0) {
return this._xforms[index];
}
const err = new Error('DOMException with code=INDEX_SIZE_ERR');
err.code = 1;
throw err;
}
/**
* @param {SVGTransform} newItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
insertItemBefore (newItem, index) {
let retValue = null;
if (index >= 0) {
if (index < this.numberOfItems) {
this._removeFromOtherLists(newItem);
const newxforms = new Array(this.numberOfItems + 1);
// TODO: use array copying and slicing
let i;
for (i = 0; i < index; ++i) {
newxforms[i] = this._xforms[i];
}
newxforms[i] = newItem;
for (let j = i + 1; i < this.numberOfItems; ++j, ++i) {
newxforms[j] = this._xforms[i];
}
this.numberOfItems++;
this._xforms = newxforms;
retValue = newItem;
this._list._update();
} else {
retValue = this._list.appendItem(newItem);
}
}
return retValue;
}
/**
* @param {SVGTransform} newItem
* @param {Integer} index unsigned long
* @returns {SVGTransform}
*/
replaceItem (newItem, index) {
let retValue = null;
if (index < this.numberOfItems && index >= 0) {
this._removeFromOtherLists(newItem);
this._xforms[index] = newItem;
retValue = newItem;
this._list._update();
}
return retValue;
}
/**
* @param {Integer} index unsigned long
* @throws {Error}
* @returns {SVGTransform}
*/
removeItem (index) {
if (index < this.numberOfItems && index >= 0) {
const retValue = this._xforms[index];
const newxforms = new Array(this.numberOfItems - 1);
let i;
for (i = 0; i < index; ++i) {
newxforms[i] = this._xforms[i];
}
for (let j = i; j < this.numberOfItems - 1; ++j, ++i) {
newxforms[j] = this._xforms[i + 1];
}
this.numberOfItems--;
this._xforms = newxforms;
this._list._update();
return retValue;
}
const err = new Error('DOMException with code=INDEX_SIZE_ERR');
err.code = 1;
throw err;
}
/**
* @param {SVGTransform} newItem
* @returns {SVGTransform}
*/
appendItem (newItem) {
this._removeFromOtherLists(newItem);
this._xforms.push(newItem);
this.numberOfItems++;
this._list._update();
return newItem;
}
}
/**
* @function module:SVGTransformList.resetListMap
* @returns {void}
*/
export const resetListMap = function () {
listMap_ = {};
};
/**
* Removes transforms of the given element from the map.
* @function module:SVGTransformList.removeElementFromListMap
* @param {Element} elem - a DOM Element
* @returns {void}
*/
export let removeElementFromListMap = function (elem) {
if (elem.id && listMap_[elem.id]) {
delete listMap_[elem.id];
}
};
/**
* Returns an object that behaves like a `SVGTransformList` for the given DOM element.
* @function module:SVGTransformList.getTransformList
* @param {Element} elem - DOM element to get a transformlist from
* @todo The polyfill should have `SVGAnimatedTransformList` and this should use it
* @returns {SVGAnimatedTransformList|SVGTransformList}
*/
export const getTransformList = function (elem) {
if (!supportsNativeTransformLists()) {
const id = elem.id || 'temp';
let t = listMap_[id];
if (!t || id === 'temp') {
listMap_[id] = new SVGTransformList(elem);
listMap_[id]._init();
t = listMap_[id];
}
return t;
}
if (elem.transform) {
return elem.transform.baseVal;
}
if (elem.gradientTransform) {
return elem.gradientTransform.baseVal;
}
if (elem.patternTransform) {
return elem.patternTransform.baseVal;
}
return null;
};
/**
* @callback module:SVGTransformList.removeElementFromListMap
* @param {Element} elem
* @returns {void}
*/
/**
* Replace `removeElementFromListMap` for unit-testing.
* @function module:SVGTransformList.changeRemoveElementFromListMap
* @param {module:SVGTransformList.removeElementFromListMap} cb Passed a single argument `elem`
* @returns {void}
*/
export const changeRemoveElementFromListMap = function (cb) {
removeElementFromListMap = cb;
};

View File

@ -15,9 +15,6 @@ import {
import {
transformPoint, transformListToTransform
} from './math.js';
import {
getTransformList
} from './svgtransformlist.js';
const {
UndoManager, HistoryEventTypes
@ -84,20 +81,6 @@ export const getUndoManager = function () {
if (values.stdDeviation) {
undoContext_.getCanvas().setBlurOffsets(cmd.elem.parentNode, values.stdDeviation);
}
// This is resolved in later versions of webkit, perhaps we should
// have a featured detection for correct 'use' behavior?
// ——————————
// Remove & Re-add hack for Webkit (issue 775)
// if (cmd.elem.tagName === 'use' && isWebkit()) {
// const {elem} = cmd;
// if (!elem.getAttribute('x') && !elem.getAttribute('y')) {
// const parent = elem.parentNode;
// const sib = elem.nextSibling;
// elem.remove();
// parent.insertBefore(elem, sib);
// // Ok to replace above with this? `sib.before(elem);`
// }
// }
if (cmd.elem.tagName === 'text'){
const [ dx, dy ] = [ cmd.newValues.x - cmd.oldValues.x,
cmd.newValues.y - cmd.oldValues.y ];
@ -251,7 +234,7 @@ export const changeSelectedAttributeNoUndoMethod = function (attr, newValue, ele
// we need to update the rotational transform attribute
const angle = getRotationAngle(elem);
if (angle !== 0 && attr !== 'transform') {
const tlist = getTransformList(elem);
const tlist = elem.transform?.baseVal;
let n = tlist.numberOfItems;
while (n--) {
const xform = tlist.getItem(n);

View File

@ -7,7 +7,6 @@
*/
import { NS } from '../common/namespaces.js';
import { getTransformList } from './svgtransformlist.js';
import { setUnitAttr, getTypeMap } from '../common/units.js';
import {
hasMatrixTransform, transformListToTransform, transformBox
@ -122,20 +121,6 @@ export const toXml = function (str) {
.replace(/'/g, '&#x27;'); // Note: `&apos;` is XML only
};
/**
* Converts XML entities in a string to single characters.
* @function module:utilities.fromXml
* @example `&amp;` becomes `&`
* @param {string} str - The string to be converted
* @returns {string} The converted string
*/
export function fromXml(str) {
const p = document.createElement('p');
// eslint-disable-next-line no-unsanitized/property
p.innerHTML = str;
return p.textContent;
}
// This code was written by Tyler Akins and has been placed in the
// public domain. It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
@ -921,7 +906,7 @@ export const convertToPath = function (
// Reorient if it has a matrix
if (eltrans) {
const tlist = getTransformList(path);
const tlist = path.transform.baseVal;
if (hasMatrixTransform(tlist)) {
pathActions.resetOrientation(path);
}
@ -992,7 +977,7 @@ export const getBBoxWithTransform = function (elem, addSVGElementFromJson, pathA
return null;
}
const tlist = getTransformList(elem);
const tlist = elem.transform.baseVal;
const angle = getRotationAngleFromTransformList(tlist);
const hasMatrixXForm = hasMatrixTransform(tlist);
@ -1127,7 +1112,7 @@ export const getStrokedBBox = function (elems, addSVGElementFromJson, pathAction
export const getVisibleElements = function (parentElement) {
if (!parentElement) {
const svgcontent = editorContext_.getSVGContent();
parentElement = svgcontent.children; // Prevent layers from being included
parentElement = svgcontent.children[0]; // Prevent layers from being included
}
const contentElems = [];
@ -1162,10 +1147,9 @@ export const getStrokedBBoxDefaultVisible = function (elems) {
* @param {boolean} toRad - When true returns the value in radians rather than degrees
* @returns {Float} The angle in degrees or radians
*/
export const getRotationAngleFromTransformList = function (tlist, toRad) {
if (!tlist) { return 0; } // <svg> elements have no tlist
const N = tlist.numberOfItems;
for (let i = 0; i < N; ++i) {
export const getRotationAngleFromTransformList = (tlist, toRad) => {
if (!tlist) { return 0; } // <svg> element have no tlist
for (let i = 0; i < tlist.numberOfItems; ++i) {
const xform = tlist.getItem(i);
if (xform.type === 4) {
return toRad ? xform.angle * Math.PI / 180.0 : xform.angle;
@ -1184,7 +1168,7 @@ export const getRotationAngleFromTransformList = function (tlist, toRad) {
export let getRotationAngle = function (elem, toRad) {
const selected = elem || editorContext_.getSelectedElements()[0];
// find the rotation transform (if any) and set it
const tlist = getTransformList(selected);
const tlist = selected.transform?.baseVal;
return getRotationAngleFromTransformList(tlist, toRad);
};