Merge pull request #628 from SVG-Edit/fix-transformation-issues
commit
66a4866656
|
@ -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 */ },
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -113,17 +113,6 @@ describe('utilities', function () {
|
|||
assert.equal(toXml('\'<&>"'), ''<&>"');
|
||||
});
|
||||
|
||||
it('Test svgedit.utilities.fromXml() function', function () {
|
||||
const { fromXml } = utilities;
|
||||
|
||||
assert.equal(fromXml('a'), 'a');
|
||||
assert.equal(fromXml('ABC_'), 'ABC_');
|
||||
assert.equal(fromXml('PB&J'), 'PB&J');
|
||||
assert.equal(fromXml('2 < 5'), '2 < 5');
|
||||
assert.equal(fromXml('5 > 2'), '5 > 2');
|
||||
assert.equal(fromXml('<&>'), '<&>');
|
||||
});
|
||||
|
||||
it('Test svgedit.utilities.encode64() function', function () {
|
||||
const { encode64 } = utilities;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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", ""));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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(',') + ')';
|
||||
|
|
|
@ -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(':')) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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, '''); // Note: `'` is XML only
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts XML entities in a string to single characters.
|
||||
* @function module:utilities.fromXml
|
||||
* @example `&` 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);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue