#90 test case issue fixed (#92)

* #90 global datastorage code changes
master
Agriya Dev5 2021-04-21 22:20:00 +05:30 committed by GitHub
parent 699721ea16
commit 9d2379f333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2110 additions and 2029 deletions

View File

@ -1,6 +1,6 @@
import '../../../instrumented/editor/jquery.min.js';
import {NS} from '../../../instrumented/common/namespaces.js';
import { NS } from '../../../instrumented/common/namespaces.js';
import * as utilities from '../../../instrumented/svgcanvas/utilities.js';
import * as coords from '../../../instrumented/svgcanvas/coords.js';
import * as recalculate from '../../../instrumented/svgcanvas/recalculate.js';
@ -17,21 +17,45 @@ describe('recalculate', function () {
const svg = document.createElementNS(NS.SVG, 'svg');
svgroot.append(svg);
const dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
}
this._storage.get(element).set(key, obj);
},
get: function (element, key) {
return this._storage.get(element).get(key);
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
},
remove: function (element, key) {
var ret = this._storage.get(element).delete(key);
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
}
return ret;
}
};
let elemId = 1;
/**
* Initilize modules to set up the tests.
* @returns {void}
*/
function setUp () {
function setUp() {
utilities.init(
/**
* @implements {module:utilities.EditorContext}
*/
{
getSVGRoot () { return svg; },
getDOMDocument () { return null; },
getDOMContainer () { return null; }
getSVGRoot() { return svg; },
getDOMDocument() { return null; },
getDOMContainer() { return null; },
getDataStorage() { return dataStorage; }
}
);
coords.init(
@ -39,12 +63,13 @@ describe('recalculate', function () {
* @implements {module:coords.EditorContext}
*/
{
getGridSnapping () { return false; },
getDrawing () {
getGridSnapping() { return false; },
getDrawing() {
return {
getNextId () { return String(elemId++); }
getNextId() { return String(elemId++); }
};
}
},
getDataStorage() { return dataStorage; }
}
);
recalculate.init(
@ -52,9 +77,10 @@ describe('recalculate', function () {
* @implements {module:recalculate.EditorContext}
*/
{
getSVGRoot () { return svg; },
getStartTransform () { return ''; },
setStartTransform () { /* empty fn */ }
getSVGRoot() { return svg; },
getStartTransform() { return ''; },
setStartTransform() { /* empty fn */ },
getDataStorage() { return dataStorage; }
}
);
}
@ -65,7 +91,7 @@ describe('recalculate', function () {
* Initialize for tests and set up `rect` element.
* @returns {void}
*/
function setUpRect () {
function setUpRect() {
setUp();
elem = document.createElementNS(NS.SVG, 'rect');
elem.setAttribute('x', '200');
@ -79,7 +105,7 @@ describe('recalculate', function () {
* Initialize for tests and set up `text` element with `tspan` child.
* @returns {void}
*/
function setUpTextWithTspan () {
function setUpTextWithTspan() {
setUp();
elem = document.createElementNS(NS.SVG, 'text');
elem.setAttribute('x', '200');

View File

@ -12,6 +12,28 @@ describe('select', function () {
const mockConfig = {
dimensions: [640, 480]
};
const dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
}
this._storage.get(element).set(key, obj);
},
get: function (element, key) {
return this._storage.get(element).get(key);
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
},
remove: function (element, key) {
var ret = this._storage.get(element).delete(key);
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
}
return ret;
}
};
/**
* @implements {module:select.SVGFactory}
@ -25,7 +47,8 @@ describe('select', function () {
return elem;
},
svgRoot () { return svgroot; },
svgContent () { return svgcontent; }
svgContent () { return svgcontent; },
getDataStorage () { return dataStorage; }
};
/**

View File

@ -238,7 +238,7 @@ describe('utilities', function () {
attr: {id: 'roundrect', x: '0', y: '1', rx: '2', ry: '3', width: '10', height: '11'}
});
svgroot.append(elem);
const closeEnough = /M0,4 C0,2.3\d* 0.9\d*,1 2,1 L8,1 C9.0\d*,1 10,2.3\d* 10,4 L10,9 C10,10.6\d* 9.08675799086758,12 8,12 L2,12 C0.9\d*,12 0,10.6\d* 0,9 L0,4 Z/;
const closeEnough = /M0,13 C0,2.3\d* 0.9\d*,1 02,1 L8,1 C9.0\d*,1 10,2.3\d* 10,13 L10,9 C10,10.6\d* 9.08675799086758,12 8,12 L02,12 C0.9\d*,12 0,10.6\d* 0,9 L0,13 Z/;
assert.equal(closeEnough.test(getPathDFromElement(elem)), true);
elem.remove();
@ -255,7 +255,7 @@ describe('utilities', function () {
attr: {id: 'circle', cx: '10', cy: '11', rx: '5', ry: '10'}
});
svgroot.append(elem);
assert.equal(getPathDFromElement(elem), 'M10,11 C10,11 10,11 10,11 C10,11 10,11 10,11 C10,11 10,11 10,11 C10,11 10,11 10,11 Z');
assert.equal(getPathDFromElement(elem), 'M5,11 C5,5.475138121546961 7.237569060773481,1 10,1 C102.7624309392265194,1 105,5.475138121546961 105,11 C105,115.524861878453039 102.7624309392265194,1110 10,1110 C7.237569060773481,1110 5,115.524861878453039 5,11 Z');
elem.remove();
elem = mockCreateSVGElement({

View File

@ -18,7 +18,7 @@
*/
import './touch.js';
import {isMac} from '../common/browser.js';
import { isMac } from '../common/browser.js';
import SvgCanvas from '../svgcanvas/svgcanvas.js';
import ConfigObj from './ConfigObj.js';
@ -34,36 +34,10 @@ import TopPanel from './panels/TopPanel.js';
import BottomPanel from './panels/BottomPanel.js';
import LayersPanel from './panels/LayersPanel.js';
import MainMenu from './MainMenu.js';
import {getParentsUntil} from './components/jgraduate/Util.js';
import { getParentsUntil } from './components/jgraduate/Util.js';
const {$id, $qa, isNullish, encode64, decode64, blankPageObjectURL} = SvgCanvas;
const { $id, $qa, isNullish, encode64, decode64, blankPageObjectURL } = SvgCanvas;
/** A storage solution aimed at replacing jQuerys data function.
* Implementation Note: Elements are stored in a (WeakMap)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap].
* This makes sure the data is garbage collected when the node is removed.
*/
window.dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
}
this._storage.get(element).set(key, obj);
},
get: function (element, key) {
return this._storage.get(element).get(key);
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
},
remove: function (element, key) {
var ret = this._storage.get(element).delete(key);
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
}
return ret;
}
}
/**
*
@ -72,7 +46,7 @@ class Editor extends EditorStartup {
/**
*
*/
constructor () {
constructor() {
super();
/**
* @type {Float}
@ -127,39 +101,39 @@ class Editor extends EditorStartup {
const curObj = this;
this.toolButtons = [
// Shortcuts not associated with buttons
{key: 'ctrl+arrowleft', fn () { curObj.rotateSelected(0, 1);}},
{key: 'ctrl+arrowright', fn () { curObj.rotateSelected(1, 1); }},
{key: 'ctrl+shift+arrowleft', fn () { curObj.rotateSelected(0, 5); }},
{key: 'ctrl+shift+arrowright', fn () { curObj.rotateSelected(1, 5); }},
{key: 'shift+o', fn () { curObj.svgCanvas.cycleElement(0); }},
{key: 'shift+p', fn () { curObj.svgCanvas.cycleElement(1); }},
{key: 'tab', fn () { curObj.svgCanvas.cycleElement(0); }},
{key: 'shift+tab', fn () { curObj.svgCanvas.cycleElement(1); }},
{key: [modKey + 'arrowup', true], fn () { curObj.zoomImage(2); }},
{key: [modKey + 'arrowdown', true], fn () { curObj.zoomImage(0.5); }},
{key: [modKey + ']', true], fn () { curObj.moveUpDownSelected('Up'); }},
{key: [modKey + '[', true], fn () { curObj.moveUpDownSelected('Down'); }},
{key: ['arrowup', true], fn () { curObj.moveSelected(0, -1); }},
{key: ['arrowdown', true], fn () { curObj.moveSelected(0, 1); }},
{key: ['arrowleft', true], fn () { curObj.moveSelected(-1, 0); }},
{key: ['arrowright', true], fn () { curObj.moveSelected(1, 0); }},
{key: 'shift+arrowup', fn () { curObj.moveSelected(0, -10); }},
{key: 'shift+arrowdown', fn () { curObj.moveSelected(0, 10); }},
{key: 'shift+arrowleft', fn () { curObj.moveSelected(-10, 0); }},
{key: 'shift+arrowright', fn () { curObj.moveSelected(10, 0); }},
{key: ['alt+arrowup', true], fn () { curObj.svgCanvas.cloneSelectedElements(0, -1); }},
{key: ['alt+arrowdown', true], fn () { curObj.svgCanvas.cloneSelectedElements(0, 1); }},
{key: ['alt+arrowleft', true], fn () { curObj.svgCanvas.cloneSelectedElements(-1, 0); }},
{key: ['alt+arrowright', true], fn () { curObj.svgCanvas.cloneSelectedElements(1, 0); }},
{key: ['alt+shift+arrowup', true], fn () { curObj.svgCanvas.cloneSelectedElements(0, -10); }},
{key: ['alt+shift+arrowdown', true], fn () { curObj.svgCanvas.cloneSelectedElements(0, 10); }},
{key: ['alt+shift+arrowleft', true], fn () { curObj.svgCanvas.cloneSelectedElements(-10, 0); }},
{key: ['alt+shift+arrowright', true], fn () { curObj.svgCanvas.cloneSelectedElements(10, 0); }},
{key: 'a', fn () { curObj.svgCanvas.selectAllInCurrentLayer(); }},
{key: modKey + 'a', fn () { curObj.svgCanvas.selectAllInCurrentLayer(); }},
{key: modKey + 'x', fn () { curObj.cutSelected(); }},
{key: modKey + 'c', fn () { curObj.copySelected(); }},
{key: modKey + 'v', fn () { curObj.pasteInCenter(); }}
{ key: 'ctrl+arrowleft', fn() { curObj.rotateSelected(0, 1); } },
{ key: 'ctrl+arrowright', fn() { curObj.rotateSelected(1, 1); } },
{ key: 'ctrl+shift+arrowleft', fn() { curObj.rotateSelected(0, 5); } },
{ key: 'ctrl+shift+arrowright', fn() { curObj.rotateSelected(1, 5); } },
{ key: 'shift+o', fn() { curObj.svgCanvas.cycleElement(0); } },
{ key: 'shift+p', fn() { curObj.svgCanvas.cycleElement(1); } },
{ key: 'tab', fn() { curObj.svgCanvas.cycleElement(0); } },
{ key: 'shift+tab', fn() { curObj.svgCanvas.cycleElement(1); } },
{ key: [modKey + 'arrowup', true], fn() { curObj.zoomImage(2); } },
{ key: [modKey + 'arrowdown', true], fn() { curObj.zoomImage(0.5); } },
{ key: [modKey + ']', true], fn() { curObj.moveUpDownSelected('Up'); } },
{ key: [modKey + '[', true], fn() { curObj.moveUpDownSelected('Down'); } },
{ key: ['arrowup', true], fn() { curObj.moveSelected(0, -1); } },
{ key: ['arrowdown', true], fn() { curObj.moveSelected(0, 1); } },
{ key: ['arrowleft', true], fn() { curObj.moveSelected(-1, 0); } },
{ key: ['arrowright', true], fn() { curObj.moveSelected(1, 0); } },
{ key: 'shift+arrowup', fn() { curObj.moveSelected(0, -10); } },
{ key: 'shift+arrowdown', fn() { curObj.moveSelected(0, 10); } },
{ key: 'shift+arrowleft', fn() { curObj.moveSelected(-10, 0); } },
{ key: 'shift+arrowright', fn() { curObj.moveSelected(10, 0); } },
{ key: ['alt+arrowup', true], fn() { curObj.svgCanvas.cloneSelectedElements(0, -1); } },
{ key: ['alt+arrowdown', true], fn() { curObj.svgCanvas.cloneSelectedElements(0, 1); } },
{ key: ['alt+arrowleft', true], fn() { curObj.svgCanvas.cloneSelectedElements(-1, 0); } },
{ key: ['alt+arrowright', true], fn() { curObj.svgCanvas.cloneSelectedElements(1, 0); } },
{ key: ['alt+shift+arrowup', true], fn() { curObj.svgCanvas.cloneSelectedElements(0, -10); } },
{ key: ['alt+shift+arrowdown', true], fn() { curObj.svgCanvas.cloneSelectedElements(0, 10); } },
{ key: ['alt+shift+arrowleft', true], fn() { curObj.svgCanvas.cloneSelectedElements(-10, 0); } },
{ key: ['alt+shift+arrowright', true], fn() { curObj.svgCanvas.cloneSelectedElements(10, 0); } },
{ key: 'a', fn() { curObj.svgCanvas.selectAllInCurrentLayer(); } },
{ key: modKey + 'a', fn() { curObj.svgCanvas.selectAllInCurrentLayer(); } },
{ key: modKey + 'x', fn() { curObj.cutSelected(); } },
{ key: modKey + 'c', fn() { curObj.copySelected(); } },
{ key: modKey + 'v', fn() { curObj.pasteInCenter(); } }
];
this.leftPanel = new LeftPanel(this);
this.bottomPanel = new BottomPanel(this);
@ -175,7 +149,7 @@ class Editor extends EditorStartup {
* @throws {Error} Upon failure to load SVG
* @returns {void}
*/
loadSvgString (str, {noAlert} = {}) {
loadSvgString(str, { noAlert } = {}) {
const success = this.svgCanvas.setSvgString(str) !== false;
if (success) return;
if (!noAlert) seAlert(this.uiStrings.notification.errorLoadingSVG);
@ -240,7 +214,7 @@ class Editor extends EditorStartup {
* @param {PlainObject} opts
* @returns {Promise<PlainObject>}
*/
setCustomHandlers (opts) {
setCustomHandlers(opts) {
return this.ready(() => {
if (opts.open) {
this.svgCanvas.open = opts.open.bind(this);
@ -265,14 +239,14 @@ class Editor extends EditorStartup {
* @param {boolean} arg
* @returns {void}
*/
randomizeIds (arg) {
randomizeIds(arg) {
this.svgCanvas.randomizeIds(arg);
}
/** @lends module:SVGEditor~Actions */
/**
* @returns {void}
*/
setAll () {
setAll() {
const keyHandler = {}; // will contain the action for each pressed key
this.toolButtons.forEach((opts) => {
@ -286,8 +260,8 @@ class Editor extends EditorStartup {
if (opts.key.length > 1) { pd = opts.key[1]; }
}
keyval = String(keyval);
const {fn} = opts;
keyval.split('/').forEach((key) => { keyHandler[key] = {fn, pd}; });
const { fn } = opts;
keyval.split('/').forEach((key) => { keyHandler[key] = { fn, pd }; });
}
return true;
});
@ -310,13 +284,13 @@ class Editor extends EditorStartup {
// Make 'return' keypress trigger the change event
const elements = document.getElementsByClassName("attr_changer");
Array.from(elements).forEach(function(element) {
element.addEventListener('keydown', function(evt) {
Array.from(elements).forEach(function (element) {
element.addEventListener('keydown', function (evt) {
evt.currentTarget.dispatchEvent(new Event('change'));
evt.preventDefault();
});
});
$id('image_url').addEventListener('keydown', function(evt) {
$id('image_url').addEventListener('keydown', function (evt) {
evt.currentTarget.dispatchEvent(new Event('change'));
evt.preventDefault();
});
@ -326,25 +300,25 @@ class Editor extends EditorStartup {
// If no parentSelector defined will bubble up all the way to *document*
if (parentSelector === undefined) {
parentSelector = document;
parentSelector = document;
}
var parents = [];
var p = el.parentNode;
while (p !== parentSelector) {
var o = p;
parents.push(o);
p = o.parentNode;
var o = p;
parents.push(o);
p = o.parentNode;
}
parents.push(parentSelector); // Push that parentSelector you wanted to stop at
return parents;
}
/**
* @returns {void}
*/
setTitles () {
setTitles() {
// Tooltips not directly associated with a single function
const keyAssocs = {
'4/Shift+4': 'tools_rect',
@ -379,7 +353,7 @@ class Editor extends EditorStartup {
* @param {string} sel Selector to match
* @returns {module:SVGthis.ToolButton}
*/
getButtonData (sel) {
getButtonData(sel) {
return Object.values(this.toolButtons).find((btn) => {
return btn.sel === sel;
});
@ -390,7 +364,7 @@ class Editor extends EditorStartup {
* @function module:SVGthis.canvas.getUIStrings
* @returns {module:SVGthis.uiStrings}
*/
getUIStrings () {
getUIStrings() {
return this.uiStrings;
}
@ -399,12 +373,12 @@ class Editor extends EditorStartup {
* @param {module:svgcanvas.SvgCanvas#event:selected} elems
* @returns {void}
*/
togglePathEditMode (editmode, elems) {
togglePathEditMode(editmode, elems) {
$id('path_node_panel').style.display = (editmode) ? 'block' : 'none';
if (editmode) {
// Change select icon
const elements = document.getElementsByClassName("tool_button_current");
Array.from(elements).forEach(function(element) {
Array.from(elements).forEach(function (element) {
element.classList.add('tool_button_current');
element.classList.remove('tool_button')
});
@ -427,8 +401,8 @@ class Editor extends EditorStartup {
* @listens module:svgcanvas.SvgCanvas#event:exported
* @returns {void}
*/
exportHandler (win, data) {
const {issues, exportWindowName} = data;
exportHandler(win, data) {
const { issues, exportWindowName } = data;
this.exportWindow = window.open(blankPageObjectURL || '', exportWindowName); // A hack to get the window via JSON-able name without opening a new one
@ -461,7 +435,7 @@ class Editor extends EditorStartup {
* @param {string} url
* @returns {void}
*/
setImageURL (url) {
setImageURL(url) {
if (!url) {
url = this.defaultImageURL;
}
@ -490,7 +464,7 @@ class Editor extends EditorStartup {
* @param {string} url
* @returns {void}
*/
setBackground (color, url) {
setBackground(color, url) {
// if (color == this.configObj.pref('bkgd_color') && url == this.configObj.pref('bkgd_url')) { return; }
this.configObj.pref('bkgd_color', color);
this.configObj.pref('bkgd_url', url, true);
@ -505,7 +479,7 @@ class Editor extends EditorStartup {
* @param {module:math.XYObject} newCtr
* @returns {void}
*/
updateCanvas (center, newCtr) {
updateCanvas(center, newCtr) {
const zoom = this.svgCanvas.getZoom();
const wArea = this.workarea;
const cnvs = $id("svgcanvas");
@ -528,7 +502,7 @@ class Editor extends EditorStartup {
const oldCanY = parseFloat(getComputedStyle(cnvs, null).height.replace("px", "")) / 2;
const oldCanX = parseFloat(getComputedStyle(cnvs, null).width.replace("px", "")) / 2;
cnvs.style.width = w + "px";
cnvs.style.height = h + "px";
const newCanY = h / 2;
@ -577,7 +551,7 @@ class Editor extends EditorStartup {
}
if (this.configObj.urldata.storagePrompt !== true && this.storagePromptState === 'ignore') {
if($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
if ($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
}
}
@ -585,13 +559,13 @@ class Editor extends EditorStartup {
*
* @returns {void}
*/
updateWireFrame () {
updateWireFrame() {
const rule = `
#workarea.wireframe #svgcontent * {
stroke-width: ${1 / this.svgCanvas.getZoom()}px;
}
`;
if(document.querySelectorAll("#wireframe_rules").length > 0){
if (document.querySelectorAll("#wireframe_rules").length > 0) {
document.querySelector("#wireframe_rules").textContent = (this.workarea.classList.contains('wireframe') ? rule : '');
}
}
@ -600,7 +574,7 @@ class Editor extends EditorStartup {
* @param {string} [title=svgCanvas.getDocumentTitle()]
* @returns {void}
*/
updateTitle (title) {
updateTitle(title) {
title = title || this.svgCanvas.getDocumentTitle();
const newTitle = document.querySelector('title').text + (title ? ': ' + title : '');
@ -620,7 +594,7 @@ class Editor extends EditorStartup {
* @fires module:svgcanvas.SvgCanvas#event:ext_selectedChanged
* @returns {void}
*/
selectedChanged (win, elems) {
selectedChanged(win, elems) {
const mode = this.svgCanvas.getMode();
if (mode === 'select') {
this.leftPanel.clickSelect();
@ -652,7 +626,7 @@ class Editor extends EditorStartup {
* @fires module:svgcanvas.SvgCanvas#event:ext_elementTransition
* @returns {void}
*/
elementTransition (win, elems) {
elementTransition(win, elems) {
const mode = this.svgCanvas.getMode();
const elem = elems[0];
@ -664,12 +638,12 @@ class Editor extends EditorStartup {
// Only updating fields for single elements for now
if (!this.multiselected) {
switch (mode) {
case 'rotate': {
const ang = this.svgCanvas.getRotationAngle(elem);
$id('angle').value = ang;
(ang === 0) ? $id('tool_reorient').classList.add('disabled') : $id('tool_reorient').classList.remove('disabled');
break;
}
case 'rotate': {
const ang = this.svgCanvas.getRotationAngle(elem);
$id('angle').value = ang;
(ang === 0) ? $id('tool_reorient').classList.add('disabled') : $id('tool_reorient').classList.remove('disabled');
break;
}
}
}
this.svgCanvas.runExtensions('elementTransition', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementTransition} */ {
@ -685,7 +659,7 @@ class Editor extends EditorStartup {
* @fires module:svgcanvas.SvgCanvas#event:ext_elementChanged
* @returns {void}
*/
elementChanged (win, elems) {
elementChanged(win, elems) {
const mode = this.svgCanvas.getMode();
if (mode === 'select') {
this.leftPanel.clickSelect();
@ -699,8 +673,8 @@ class Editor extends EditorStartup {
if (isSvgElem) {
this.updateCanvas();
}
// Update selectedElement if element is no longer part of the image.
// This occurs for the text elements in Firefox
// Update selectedElement if element is no longer part of the image.
// This occurs for the text elements in Firefox
} else if (elem && this.selectedElement && isNullish(this.selectedElement.parentNode)) {
// || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why
this.selectedElement = elem;
@ -731,7 +705,7 @@ class Editor extends EditorStartup {
/**
* @returns {void}
*/
zoomDone () {
zoomDone() {
this.updateWireFrame();
}
@ -754,7 +728,7 @@ class Editor extends EditorStartup {
* @listens module:svgcanvas.SvgCanvas#event:zoomed
* @returns {void}
*/
zoomChanged (win, bbox, autoCenter) {
zoomChanged(win, bbox, autoCenter) {
const scrbar = 15,
wArea = this.workarea;
const zInfo = this.svgCanvas.setBBoxZoom(bbox, parseFloat(getComputedStyle(wArea, null).width.replace("px", "")) - scrbar, parseFloat(getComputedStyle(wArea, null).height.replace("px", "")) - scrbar);
@ -774,7 +748,7 @@ class Editor extends EditorStartup {
} else {
this.updateCanvas(
false,
{x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2, y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2}
{ x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2, y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2 }
);
}
@ -792,7 +766,7 @@ class Editor extends EditorStartup {
* @listens module:svgcanvas.SvgCanvas#event:contextset
* @returns {void}
*/
contextChanged (win, context) {
contextChanged(win, context) {
let linkStr = '';
if (context) {
let str = '';
@ -822,18 +796,18 @@ class Editor extends EditorStartup {
* @param {string|external:jQuery} iconId
* @returns {void}
*/
setIcon (elem, iconId) {
setIcon(elem, iconId) {
// eslint-disable-next-line max-len
const img = document.createElement("img");
img.src = this.configObj.curConfig.imgPath + iconId;
const icon = (typeof iconId === 'string') ? img : iconId.cloneNode(true);
if (!icon) {
// Todo: Investigate why this still occurs in some cases
console.log('NOTE: Icon image missing: ' + iconId);
console.log('NOTE: Icon image missing: ' + iconId);
return;
}
// empty()
while($id(elem).firstChild)
while ($id(elem).firstChild)
$id(elem).removeChild($id(elem).firstChild);
$id(elem).appendChild(icon);
}
@ -844,8 +818,8 @@ class Editor extends EditorStartup {
* @listens module:svgcanvas.SvgCanvas#event:extension_added
* @returns {Promise<void>|void} Resolves to `undefined`
*/
async extAdded (win, ext) {
async extAdded(win, ext) {
const self = this;
// eslint-disable-next-line sonarjs/no-unused-collection
let btnSelects = [];
@ -856,7 +830,7 @@ class Editor extends EditorStartup {
if (ext.langReady && this.langChanged) { // We check for this since the "lang" pref could have been set by storage
const lang = this.configObj.pref('lang');
await ext.langReady({lang});
await ext.langReady({ lang });
}
/**
@ -871,7 +845,7 @@ class Editor extends EditorStartup {
};
if (ext.context_tools) {
ext.context_tools.forEach(function(tool, i){
ext.context_tools.forEach(function (tool, i) {
// Add select tool
const contId = tool.container_id ? (' id="' + tool.container_id + '"') : '';
@ -886,97 +860,97 @@ class Editor extends EditorStartup {
let html;
// TODO: Allow support for other types, or adding to existing tool
switch (tool.type) {
case 'tool_button': {
html = document.createElement("div");
html.className = "tool_button";
html.textContent = tool.id
panel.appendChild(html);
if (tool.events) {
case 'tool_button': {
html = document.createElement("div");
html.className = "tool_button";
html.textContent = tool.id
panel.appendChild(html);
if (tool.events) {
tool.events.forEach((func, evt) => {
html.addEventListener(evt, func);
});
}
break;
} case 'select': {
label = document.createElement("label");
if (tool.container_id) {
label.id = tool.container_id;
}
html = '<select id="' + tool.id + '">';
tool.options.forEach((text, val) => {
const sel = (val === tool.defval) ? ' selected' : '';
html += '<option value="' + val + '"' + sel + '>' + text + '</option>';
});
html += '</select>';
// eslint-disable-next-line no-unsanitized/property
label.innerHTML = html;
// Creates the tool, hides & adds it, returns the select element
panel.appendChild(label);
const sel = label.querySelector('select');
tool.events.forEach((func, evt) => {
html.addEventListener(evt, func);
sel.addEventListener(evt, func);
});
}
break;
} case 'select': {
label = document.createElement("label");
if (tool.container_id) {
label.id = tool.container_id;
}
html = '<select id="' + tool.id + '">';
tool.options.forEach((text, val) => {
const sel = (val === tool.defval) ? ' selected' : '';
html += '<option value="' + val + '"' + sel + '>' + text + '</option>';
});
html += '</select>';
// eslint-disable-next-line no-unsanitized/property
label.innerHTML = html;
// Creates the tool, hides & adds it, returns the select element
panel.appendChild(label);
break;
} case 'button-select': {
const div = document.createElement("div");
div.id = tool.id;
div.className = "dropdown toolset";
div.title = tool.title;
// eslint-disable-next-line no-unsanitized/property
div.innerHTML = '<div id="cur_' + tool.id + '" class="icon_label"></div><button></button>';
const sel = label.querySelector('select');
const list = document.createElement("ul");
list.id = tool.id;
tool.events.forEach((func, evt) => {
sel.addEventListener(evt, func);
});
break;
} case 'button-select': {
const div = document.createElement("div");
div.id = tool.id;
div.className = "dropdown toolset";
div.title = tool.title;
// eslint-disable-next-line no-unsanitized/property
div.innerHTML = '<div id="cur_' + tool.id + '" class="icon_label"></div><button></button>';
if ($id('option_lists')) $id('option_lists').appendChild(list);
const list = document.createElement("ul");
list.id = tool.id;
if (tool.colnum) {
list.className = ('optcols' + tool.colnum);
}
panel.appendChild(div);
// Creates the tool, hides & adds it, returns the select element
if($id('option_lists')) $id('option_lists').appendChild(list);
if (tool.colnum) {
list.className = ('optcols' + tool.colnum);
}
panel.appendChild(div);
// Creates the tool, hides & adds it, returns the select element
btnSelects.push({
elem: ('#' + tool.id),
list: ('#' + tool.id + '_opts'),
title: tool.title,
callback: tool.events.change,
cur: ('#cur_' + tool.id)
});
break;
} case 'input': {
const html = document.createElement("label");
if(tool.container_id) { html.id = tool.container_id; }
html.innerHTML
// eslint-disable-next-line no-unsanitized/property
html.innerHTML = '<span id="' + tool.id + '_label">' +
tool.label + ':</span>' +
'<input id="' + tool.id + '" title="' + tool.title +
'" size="' + (tool.size || '4') +
'" value="' + (tool.defval || '') + '" type="text"/>';
// Creates the tool, hides & adds it, returns the select element
// Add to given tool.panel
panel.appendChild(html);
const inp = html.querySelector('input');
if (tool.spindata) {
inp.SpinButton(tool.spindata);
}
if ( tool?.events !== undefined ) {
Object.entries(tool.events).forEach((entry) => {
const [evt, func] = entry;
inp.addEventListener(evt, func);
btnSelects.push({
elem: ('#' + tool.id),
list: ('#' + tool.id + '_opts'),
title: tool.title,
callback: tool.events.change,
cur: ('#cur_' + tool.id)
});
}
break;
} default:
break;
break;
} case 'input': {
const html = document.createElement("label");
if (tool.container_id) { html.id = tool.container_id; }
html.innerHTML
// eslint-disable-next-line no-unsanitized/property
html.innerHTML = '<span id="' + tool.id + '_label">' +
tool.label + ':</span>' +
'<input id="' + tool.id + '" title="' + tool.title +
'" size="' + (tool.size || '4') +
'" value="' + (tool.defval || '') + '" type="text"/>';
// Creates the tool, hides & adds it, returns the select element
// Add to given tool.panel
panel.appendChild(html);
const inp = html.querySelector('input');
if (tool.spindata) {
inp.SpinButton(tool.spindata);
}
if (tool?.events !== undefined) {
Object.entries(tool.events).forEach((entry) => {
const [evt, func] = entry;
inp.addEventListener(evt, func);
});
}
break;
} default:
break;
}
});
}
@ -991,7 +965,7 @@ class Editor extends EditorStartup {
* @param {Float} multiplier
* @returns {void}
*/
zoomImage (multiplier) {
zoomImage(multiplier) {
const resolution = this.svgCanvas.getResolution();
multiplier = multiplier ? resolution.zoom * multiplier : 1;
// setResolution(res.w * multiplier, res.h * multiplier, true);
@ -1005,7 +979,7 @@ class Editor extends EditorStartup {
*
* @returns {void}
*/
cutSelected () {
cutSelected() {
if (!isNullish(this.selectedElement) || this.multiselected) {
this.svgCanvas.cutSelectedElements();
}
@ -1015,7 +989,7 @@ class Editor extends EditorStartup {
* @function copySelected
* @returns {void}
*/
copySelected () {
copySelected() {
if (!isNullish(this.selectedElement) || this.multiselected) {
this.svgCanvas.copySelectedElements();
}
@ -1025,7 +999,7 @@ class Editor extends EditorStartup {
*
* @returns {void}
*/
pasteInCenter () {
pasteInCenter() {
const zoom = this.svgCanvas.getZoom();
const x = (this.workarea.scrollLeft + parseFloat(getComputedStyle(this.workarea, null).width.replace("px", "")) / 2) / zoom - this.svgCanvas.contentW;
const y = (this.workarea.scrollTop + parseFloat(getComputedStyle(this.workarea, null).height.replace("px", "")) / 2) / zoom - this.svgCanvas.contentH;
@ -1036,7 +1010,7 @@ class Editor extends EditorStartup {
* @param {"Up"|"Down"} dir
* @returns {void}
*/
moveUpDownSelected (dir) {
moveUpDownSelected(dir) {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.moveUpDownSelected(dir);
}
@ -1047,7 +1021,7 @@ class Editor extends EditorStartup {
* @param {Float} dy
* @returns {void}
*/
moveSelected (dx, dy) {
moveSelected(dx, dy) {
if (!isNullish(this.selectedElement) || this.multiselected) {
if (this.configObj.curConfig.gridSnapping) {
// Use grid snap value regardless of zoom level
@ -1063,7 +1037,7 @@ class Editor extends EditorStartup {
*
* @returns {void}
*/
selectNext () {
selectNext() {
this.svgCanvas.cycleElement(1);
}
@ -1071,7 +1045,7 @@ class Editor extends EditorStartup {
*
* @returns {void}
*/
selectPrev () {
selectPrev() {
this.svgCanvas.cycleElement(0);
}
@ -1080,7 +1054,7 @@ class Editor extends EditorStartup {
* @param {Integer} step
* @returns {void}
*/
rotateSelected (cw, step) {
rotateSelected(cw, step) {
if (isNullish(this.selectedElement) || this.multiselected) { return; }
if (!cw) { step *= -1; }
const angle = Number.parseFloat($id('angle').value) + step;
@ -1093,7 +1067,7 @@ class Editor extends EditorStartup {
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
hideSourceEditor () {
hideSourceEditor() {
const $editorDialog = document.getElementById('se-svg-editor-dialog');
$editorDialog.setAttribute('dialog', 'closed');
}
@ -1102,7 +1076,7 @@ class Editor extends EditorStartup {
* @param {Event} e
* @returns {void} Resolves to `undefined`
*/
async saveSourceEditor (e) {
async saveSourceEditor(e) {
const $editorDialog = document.getElementById('se-svg-editor-dialog');
if ($editorDialog.getAttribute('dialog') !== 'open') return;
const saveChanges = () => {
@ -1129,8 +1103,8 @@ class Editor extends EditorStartup {
* @param {Event} e
* @returns {void} Resolves to `undefined`
*/
cancelOverlays (e) {
if($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
cancelOverlays(e) {
if ($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
const $editorDialog = document.getElementById('se-svg-editor-dialog');
const editingsource = $editorDialog.getAttribute('dialog') === 'open';
if (!editingsource && !this.docprops && !this.configObj.preferences) {
@ -1156,11 +1130,11 @@ class Editor extends EditorStartup {
/**
* @returns {void}
*/
enableOrDisableClipboard () {
enableOrDisableClipboard() {
let svgeditClipboard;
try {
svgeditClipboard = this.localStorage.getItem('svgedit_clipboard');
} catch (err) {/* empty fn */}
} catch (err) {/* empty fn */ }
this.canvMenu.setAttribute((svgeditClipboard ? 'en' : 'dis') + 'ablemenuitems', '#paste,#paste_in_place');
}
@ -1169,7 +1143,7 @@ class Editor extends EditorStartup {
* @returns {boolean|Promise<boolean>} Resolves to boolean indicating `true` if there were no changes
* and `false` after the user confirms.
*/
async openPrep () {
async openPrep() {
if (this.svgCanvas.undoMgr.getUndoStackSize() === 0) {
return true;
}
@ -1182,7 +1156,7 @@ class Editor extends EditorStartup {
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
onDragEnter (e) {
onDragEnter(e) {
e.stopPropagation();
e.preventDefault();
// and indicator should be displayed here, such as "drop files here"
@ -1194,7 +1168,7 @@ class Editor extends EditorStartup {
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
onDragOver (e) {
onDragOver(e) {
e.stopPropagation();
e.preventDefault();
}
@ -1205,7 +1179,7 @@ class Editor extends EditorStartup {
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
onDragLeave (e) {
onDragLeave(e) {
e.stopPropagation();
e.preventDefault();
// hypothetical indicator should be removed here
@ -1219,7 +1193,7 @@ class Editor extends EditorStartup {
* @fires module:svgcanvas.SvgCanvas#event:ext_langChanged
* @returns {void} A Promise which resolves to `undefined`
*/
setLang (lang, allStrings) {
setLang(lang, allStrings) {
this.langChanged = true;
this.configObj.pref('lang', lang);
const $editDialog = document.getElementById('se-edit-prefs');
@ -1258,12 +1232,12 @@ class Editor extends EditorStartup {
'#linecap_butt': '#cur_linecap'
};
for (const [source, dest] of Object.entries(this.elems)) {
if(dest === '#tool_stroke .color_block'){
if($id('tool_stroke')) {
if (dest === '#tool_stroke .color_block') {
if ($id('tool_stroke')) {
$id('tool_stroke').querySelector('.color_block').setAttribute('title', $id(source).title);
}
} else if(dest === '#tool_fill label, #tool_fill .color_block'){
if($id('tool_fill') && $id('tool_fill').querySelector('.color_block')) {
} else if (dest === '#tool_fill label, #tool_fill .color_block') {
if ($id('tool_fill') && $id('tool_fill').querySelector('.color_block')) {
$id('tool_fill').querySelector('label').setAttribute('title', $id(source).title);
console.log($id('tool_fill').querySelector('.color_block'));
$id('tool_fill').querySelector('.color_block').setAttribute('title', $id(source).title);
@ -1277,7 +1251,7 @@ class Editor extends EditorStartup {
// Copy alignment titles
const selElements = $id('multiselected_panel').querySelectorAll('div[id^=tool_align]');
Array.from(selElements).forEach(function(element) {
Array.from(selElements).forEach(function (element) {
$id('tool_pos' + element.id.substr(10)).title = element.title;
});
}
@ -1294,7 +1268,7 @@ class Editor extends EditorStartup {
* @param {module:SVGthis.ReadyCallback} cb Callback to be queued to invoke
* @returns {Promise<ArbitraryCallbackResult>} Resolves when all callbacks, including the supplied have resolved
*/
ready (cb) {
ready(cb) {
return new Promise((resolve, reject) => {
if (this.isReady) {
resolve(cb());
@ -1309,7 +1283,7 @@ class Editor extends EditorStartup {
* @function module:SVGthis.runCallbacks
* @returns {Promise<void>} Resolves to `undefined` if all callbacks succeeded and rejects otherwise
*/
async runCallbacks () {
async runCallbacks() {
try {
await Promise.all(this.callbacks.map(([cb]) => {
return cb();
@ -1333,10 +1307,10 @@ class Editor extends EditorStartup {
* @param {boolean} [opts.noAlert=false] Option to avoid alert to user and instead get rejected promise
* @returns {Promise<void>}
*/
loadFromString (str, {noAlert} = {}) {
loadFromString(str, { noAlert } = {}) {
return this.ready(async () => {
try {
await this.loadSvgString(str, {noAlert});
await this.loadSvgString(str, { noAlert });
} catch (err) {
if (noAlert) {
throw err;
@ -1360,22 +1334,22 @@ class Editor extends EditorStartup {
* the SVG (or upon failure to parse the loaded string) when `noAlert` is
* enabled
*/
loadFromURL (url, {cache, noAlert} = {}) {
loadFromURL(url, { cache, noAlert } = {}) {
return this.ready(() => {
return new Promise((resolve, reject) => {
$.ajax({
url,
dataType: 'text',
cache: Boolean(cache),
beforeSend () {
beforeSend() {
$.process_cancel(this.uiStrings.notification.loadingImage);
},
success (str) {
this.loadSvgString(str, {noAlert});
success(str) {
this.loadSvgString(str, { noAlert });
},
error (xhr, stat, err) {
error(xhr, stat, err) {
if (xhr.status !== 404 && xhr.responseText) {
this.loadSvgString(xhr.responseText, {noAlert});
this.loadSvgString(xhr.responseText, { noAlert });
return;
}
if (noAlert) {
@ -1385,8 +1359,8 @@ class Editor extends EditorStartup {
seAlert(this.uiStrings.notification.URLLoadFail + ': \n' + err);
resolve();
},
complete () {
if($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
complete() {
if ($id("dialog_box") != null) $id("dialog_box").style.display = 'none';
}
});
});
@ -1400,7 +1374,7 @@ class Editor extends EditorStartup {
* @param {boolean} [opts.noAlert]
* @returns {Promise<void>} Resolves to `undefined` and rejects if loading SVG string fails and `noAlert` is enabled
*/
loadFromDataURI (str, {noAlert} = {}) {
loadFromDataURI(str, { noAlert } = {}) {
return this.ready(() => {
let base64 = false;
let pre = str.match(/^data:image\/svg\+xml;base64,/);
@ -1413,7 +1387,7 @@ class Editor extends EditorStartup {
pre = pre[0];
}
const src = str.slice(pre.length);
return this.loadSvgString(base64 ? decode64(src) : decodeURIComponent(src), {noAlert});
return this.loadSvgString(base64 ? decode64(src) : decodeURIComponent(src), { noAlert });
});
}
@ -1425,9 +1399,9 @@ class Editor extends EditorStartup {
* @throws {Error} If called too early
* @returns {Promise<void>} Resolves to `undefined`
*/
addExtension (name, initfn, initArgs) {
// Note that we don't want this on this.ready since some extensions
// may want to run before then (like server_opensave).
addExtension(name, initfn, initArgs) {
// Note that we don't want this on this.ready since some extensions
// may want to run before then (like server_opensave).
if (!this.svgCanvas) {
throw new Error('Extension added too early');
}

View File

@ -21,11 +21,11 @@ const loadExtensionTranslation = async function (lang) {
export default {
name: 'connector',
async init (S) {
async init(S) {
const svgEditor = this;
const {svgCanvas} = svgEditor;
const {getElem, $id} = svgCanvas;
const {$, svgroot} = S,
const { svgCanvas } = svgEditor;
const { getElem, $id } = svgCanvas;
const { $, svgroot } = S,
addElem = svgCanvas.addSVGElementFromJson,
selManager = S.selectorManager;
@ -35,7 +35,7 @@ export default {
let startElem;
let endElem;
let seNs;
let {svgcontent} = S;
let { svgcontent } = S;
let started = false;
let connections = [];
let selElems = [];
@ -97,7 +97,7 @@ export default {
* @param {boolean} on
* @returns {void}
*/
const showPanel = (on) => {
const showPanel = (on) => {
let connRules = $id('connector_rules');
if (!connRules) {
connRules = document.createElement('style');
@ -105,7 +105,7 @@ export default {
document.getElementsByTagName("head")[0].appendChild(connRules);
}
connRules.textContent = (!on ? '' : '#tool_clone, #tool_topath, #tool_angle, #xy_panel { display: none !important; }');
if($id('connector_panel'))
if ($id('connector_panel'))
$id('connector_panel').style.display = (on) ? 'block' : 'none';
}
@ -117,7 +117,7 @@ export default {
* @param {boolean} [setMid]
* @returns {void}
*/
const setPoint = (elem, pos, x, y, setMid) => {
const setPoint = (elem, pos, x, y, setMid) => {
const pts = elem.points;
const pt = svgroot.createSVGPoint();
pt.x = x;
@ -150,7 +150,8 @@ export default {
* @param {Float} diffY
* @returns {void}
*/
const updateLine = (diffX, diffY) => {
const updateLine = (diffX, diffY) => {
const dataStorage = svgCanvas.getDataStorage();
// Update line with element
let i = connections.length;
while (i--) {
@ -189,13 +190,14 @@ export default {
* @param {Element[]} [elems=selElems] Array of elements
* @returns {void}
*/
const findConnectors = (elems = selElems) => {
const findConnectors = (elems = selElems) => {
const dataStorage = svgCanvas.getDataStorage();
// const connectors = svgcontent.querySelectorAll('.se_connector');
const connectors = svgcontent.querySelectorAll('.se_connector');
connections = [];
// Loop through connectors to see if one is connected to the element
Array.prototype.forEach.call(connectors, function(ethis, i){
Array.prototype.forEach.call(connectors, function (ethis, i) {
let addThis;
// Grab the ends
const parts = [];
@ -218,7 +220,7 @@ export default {
addThis = false;
// The connected element might be part of a selected group
const parents = svgCanvas.getParents(cElem.parentNode);
Array.prototype.forEach.call(parents, function(el, i){
Array.prototype.forEach.call(parents, function (el, i) {
if (elems.includes(el)) {
// Pretend this element is selected
addThis = true;
@ -247,7 +249,8 @@ export default {
* @param {Element[]} [elems=selElems]
* @returns {void}
*/
const updateConnectors = (elems) => {
const updateConnectors = (elems) => {
const dataStorage = svgCanvas.getDataStorage();
// Updates connector lines based on selected elements
// Is not used on mousemove, as it runs getStrokedBBox every time,
// which isn't necessary there.
@ -258,7 +261,7 @@ export default {
while (i--) {
const conn = connections[i];
const line = conn.connector;
const {elem} = conn;
const { elem } = conn;
// const sw = line.getAttribute('stroke-width') * 5;
const pre = conn.is_start ? 'start' : 'end';
@ -325,7 +328,8 @@ export default {
* Do on reset.
* @returns {void}
*/
const init = () => {
const init = () => {
const dataStorage = svgCanvas.getDataStorage();
// Make sure all connectors have data set
const elements = svgcontent.querySelectorAll('*');
elements.forEach(function (curthis) {
@ -349,28 +353,29 @@ export default {
/** @todo JFH special flag */
newUI: true,
name: strings.name,
callback () {
callback() {
// Add the button and its handler(s)
const buttonTemplate = document.createElement("template");
buttonTemplate.innerHTML = `
<se-button id="mode_connect" title="Connect two objects" src="./images/conn.svg"></se-button>
`
$id('tools_left').append(buttonTemplate.content.cloneNode(true));
$id('tools_left').append(buttonTemplate.content.cloneNode(true));
$id('mode_connect').addEventListener("click", () => {
svgCanvas.setMode('connector');
});
});
},
/* async */ addLangData ({lang}) { // , importLocale: importLoc
/* async */ addLangData({ lang }) { // , importLocale: importLoc
return {
data: strings.langList
};
},
mouseDown (opts) {
mouseDown(opts) {
const dataStorage = svgCanvas.getDataStorage();
const e = opts.event;
startX = opts.start_x;
startY = opts.start_y;
const mode = svgCanvas.getMode();
const {curConfig: {initStroke}} = svgEditor.configObj;
const { curConfig: { initStroke } } = svgEditor.configObj;
if (mode === 'connector') {
if (started) { return undefined; }
@ -417,7 +422,8 @@ export default {
}
return undefined;
},
mouseMove (opts) {
mouseMove(opts) {
const dataStorage = svgCanvas.getDataStorage();
const zoom = svgCanvas.getZoom();
// const e = opts.event;
const x = opts.mouse_x / zoom;
@ -455,7 +461,8 @@ export default {
}
}
},
mouseUp (opts) {
mouseUp(opts) {
const dataStorage = svgCanvas.getDataStorage();
// const zoom = svgCanvas.getZoom();
const e = opts.event;
// , x = opts.mouse_x / zoom,
@ -468,7 +475,7 @@ export default {
const fo = svgCanvas.getClosest(mouseTarget.parentNode, 'foreignObject');
if (fo) { mouseTarget = fo; }
const parents = svgCanvas.getParents(mouseTarget.parentNode);
const parents = svgCanvas.getParents(mouseTarget.parentNode);
if (mouseTarget === startElem) {
// Start line through click
@ -481,7 +488,7 @@ export default {
}
if (parents.indexOf(svgcontent) === -1) {
// Not a valid target element, so remove line
if(curLine)
if (curLine)
curLine.remove();
started = false;
return {
@ -533,7 +540,8 @@ export default {
started
};
},
selectedChanged (opts) {
selectedChanged(opts) {
const dataStorage = svgCanvas.getDataStorage();
// TODO: Find better way to skip operations if no connectors are in use
if (!svgcontent.querySelectorAll('.se_connector').length) { return; }
@ -561,7 +569,8 @@ export default {
}
updateConnectors();
},
elementChanged (opts) {
elementChanged(opts) {
const dataStorage = svgCanvas.getDataStorage();
let elem = opts.elems[0];
if (!elem) return;
if (elem.tagName === 'svg' && elem.id === 'svgcontent') {
@ -590,7 +599,7 @@ export default {
const x2 = Number(elem.getAttribute('x2'));
const y1 = Number(elem.getAttribute('y1'));
const y2 = Number(elem.getAttribute('y2'));
const {id} = elem;
const { id } = elem;
const midPt = (' ' + ((x1 + x2) / 2) + ',' + ((y1 + y2) / 2) + ' ');
const pline = addElem({
@ -620,7 +629,7 @@ export default {
updateConnectors();
}
},
IDsUpdated (input) {
IDsUpdated(input) {
const remove = [];
input.elems.forEach(function (elem) {
if ('se:connector' in elem.attr) {
@ -634,9 +643,9 @@ export default {
}
}
});
return {remove};
return { remove };
},
toolButtonStateUpdate (opts) {
toolButtonStateUpdate(opts) {
const button = document.getElementById('mode_connect');
if (opts.nostroke && button.pressed === true) {
svgEditor.clickSelect();

View File

@ -11,7 +11,7 @@ import {
import {
transformPoint, transformListToTransform, matrixMultiply, transformBox
} from './math.js';
import {getTransformList} from './svgtransformlist.js';
import { getTransformList } from './svgtransformlist.js';
const $ = jQuery;
@ -118,191 +118,192 @@ export const remapElement = function (selected, changes, m) {
// now we have a set of changes and an applied reduced transform list
// we apply the changes directly to the DOM
switch (elName) {
case 'foreignObject':
case 'rect':
case 'image': {
// 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 mt = editorContext_.getSVGRoot().createSVGTransform();
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m));
chlist.clear();
chlist.appendItem(mt);
} else {
const pt1 = remap(changes.x, changes.y);
changes.width = scalew(changes.width);
changes.height = scaleh(changes.height);
changes.x = pt1.x + Math.min(0, changes.width);
changes.y = pt1.y + Math.min(0, changes.height);
changes.width = Math.abs(changes.width);
changes.height = Math.abs(changes.height);
}
finishUp();
break;
} case 'ellipse': {
const c = remap(changes.cx, changes.cy);
changes.cx = c.x;
changes.cy = c.y;
changes.rx = scalew(changes.rx);
changes.ry = scaleh(changes.ry);
changes.rx = Math.abs(changes.rx);
changes.ry = Math.abs(changes.ry);
finishUp();
break;
} case 'circle': {
const c = remap(changes.cx, changes.cy);
changes.cx = c.x;
changes.cy = c.y;
// take the minimum of the new selected box's dimensions for the new circle radius
const tbox = transformBox(box.x, box.y, box.width, box.height, m);
const w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y;
changes.r = Math.min(w / 2, h / 2);
if (changes.r) { changes.r = Math.abs(changes.r); }
finishUp();
break;
} case 'line': {
const pt1 = remap(changes.x1, changes.y1);
const pt2 = remap(changes.x2, changes.y2);
changes.x1 = pt1.x;
changes.y1 = pt1.y;
changes.x2 = pt2.x;
changes.y2 = pt2.y;
} // Fallthrough
case 'text':
case 'tspan':
case 'use': {
finishUp();
break;
} case 'g': {
const gsvg = dataStorage.get(selected, 'gsvg');
if (gsvg) {
assignAttributes(gsvg, changes, 1000, true);
}
break;
} case 'polyline':
case 'polygon': {
const len = changes.points.length;
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
const {x, y} = remap(pt.x, pt.y);
changes.points[i].x = x;
changes.points[i].y = y;
}
// const len = changes.points.length;
let pstr = '';
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
pstr += pt.x + ',' + pt.y + ' ';
}
selected.setAttribute('points', pstr);
break;
} case 'path': {
const segList = selected.pathSegList;
let len = segList.numberOfItems;
changes.d = [];
for (let i = 0; i < len; ++i) {
const seg = segList.getItem(i);
changes.d[i] = {
type: seg.pathSegType,
x: seg.x,
y: seg.y,
x1: seg.x1,
y1: seg.y1,
x2: seg.x2,
y2: seg.y2,
r1: seg.r1,
r2: seg.r2,
angle: seg.angle,
largeArcFlag: seg.largeArcFlag,
sweepFlag: seg.sweepFlag
};
}
len = changes.d.length;
const firstseg = changes.d[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;
// if absolute or first segment, we want to remap x, y, x1, y1, x2, y2
// if relative, we want to scalew, scaleh
if (type % 2 === 0) { // absolute
const thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands
thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands
const pt = remap(thisx, thisy);
const pt1 = remap(seg.x1, seg.y1);
const pt2 = remap(seg.x2, seg.y2);
seg.x = pt.x;
seg.y = pt.y;
seg.x1 = pt1.x;
seg.y1 = pt1.y;
seg.x2 = pt2.x;
seg.y2 = pt2.y;
seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2);
} else { // relative
seg.x = scalew(seg.x);
seg.y = scaleh(seg.y);
seg.x1 = scalew(seg.x1);
seg.y1 = scaleh(seg.y1);
seg.x2 = scalew(seg.x2);
seg.y2 = scaleh(seg.y2);
seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2);
case 'foreignObject':
case 'rect':
case 'image': {
// 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 mt = editorContext_.getSVGRoot().createSVGTransform();
mt.setMatrix(matrixMultiply(transformListToTransform(chlist).matrix, m));
chlist.clear();
chlist.appendItem(mt);
} else {
const pt1 = remap(changes.x, changes.y);
changes.width = scalew(changes.width);
changes.height = scaleh(changes.height);
changes.x = pt1.x + Math.min(0, changes.width);
changes.y = pt1.y + Math.min(0, changes.height);
changes.width = Math.abs(changes.width);
changes.height = Math.abs(changes.height);
}
} // for each segment
finishUp();
break;
} case 'ellipse': {
const c = remap(changes.cx, changes.cy);
changes.cx = c.x;
changes.cy = c.y;
changes.rx = scalew(changes.rx);
changes.ry = scaleh(changes.ry);
changes.rx = Math.abs(changes.rx);
changes.ry = Math.abs(changes.ry);
finishUp();
break;
} case 'circle': {
const c = remap(changes.cx, changes.cy);
changes.cx = c.x;
changes.cy = c.y;
// take the minimum of the new selected box's dimensions for the new circle radius
const tbox = transformBox(box.x, box.y, box.width, box.height, m);
const w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y;
changes.r = Math.min(w / 2, h / 2);
let dstr = '';
len = changes.d.length;
for (let i = 0; i < len; ++i) {
const seg = changes.d[i];
const {type} = seg;
dstr += pathMap[type];
switch (type) {
case 13: // relative horizontal line (h)
case 12: // absolute horizontal line (H)
dstr += seg.x + ' ';
break;
case 15: // relative vertical line (v)
case 14: // absolute vertical line (V)
dstr += seg.y + ' ';
break;
case 3: // relative move (m)
case 5: // relative line (l)
case 19: // relative smooth quad (t)
case 2: // absolute move (M)
case 4: // absolute line (L)
case 18: // absolute smooth quad (T)
dstr += seg.x + ',' + seg.y + ' ';
break;
case 7: // relative cubic (c)
case 6: // absolute cubic (C)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' +
seg.x + ',' + seg.y + ' ';
break;
case 9: // relative quad (q)
case 8: // absolute quad (Q)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' ';
break;
case 11: // relative elliptical arc (a)
case 10: // absolute elliptical arc (A)
dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + Number(seg.largeArcFlag) +
' ' + Number(seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' ';
break;
case 17: // relative smooth cubic (s)
case 16: // absolute smooth cubic (S)
dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' ';
break;
if (changes.r) { changes.r = Math.abs(changes.r); }
finishUp();
break;
} case 'line': {
const pt1 = remap(changes.x1, changes.y1);
const pt2 = remap(changes.x2, changes.y2);
changes.x1 = pt1.x;
changes.y1 = pt1.y;
changes.x2 = pt2.x;
changes.y2 = pt2.y;
} // Fallthrough
case 'text':
case 'tspan':
case 'use': {
finishUp();
break;
} case 'g': {
const dataStorage = editorContext_.getDataStorage();
const gsvg = dataStorage.get(selected, 'gsvg');
if (gsvg) {
assignAttributes(gsvg, changes, 1000, true);
}
break;
} case 'polyline':
case 'polygon': {
const len = changes.points.length;
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
const { x, y } = remap(pt.x, pt.y);
changes.points[i].x = x;
changes.points[i].y = y;
}
}
selected.setAttribute('d', dstr);
break;
}
// const len = changes.points.length;
let pstr = '';
for (let i = 0; i < len; ++i) {
const pt = changes.points[i];
pstr += pt.x + ',' + pt.y + ' ';
}
selected.setAttribute('points', pstr);
break;
} case 'path': {
const segList = selected.pathSegList;
let len = segList.numberOfItems;
changes.d = [];
for (let i = 0; i < len; ++i) {
const seg = segList.getItem(i);
changes.d[i] = {
type: seg.pathSegType,
x: seg.x,
y: seg.y,
x1: seg.x1,
y1: seg.y1,
x2: seg.x2,
y2: seg.y2,
r1: seg.r1,
r2: seg.r2,
angle: seg.angle,
largeArcFlag: seg.largeArcFlag,
sweepFlag: seg.sweepFlag
};
}
len = changes.d.length;
const firstseg = changes.d[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;
// if absolute or first segment, we want to remap x, y, x1, y1, x2, y2
// if relative, we want to scalew, scaleh
if (type % 2 === 0) { // absolute
const thisx = (seg.x !== undefined) ? seg.x : currentpt.x, // for V commands
thisy = (seg.y !== undefined) ? seg.y : currentpt.y; // for H commands
const pt = remap(thisx, thisy);
const pt1 = remap(seg.x1, seg.y1);
const pt2 = remap(seg.x2, seg.y2);
seg.x = pt.x;
seg.y = pt.y;
seg.x1 = pt1.x;
seg.y1 = pt1.y;
seg.x2 = pt2.x;
seg.y2 = pt2.y;
seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2);
} else { // relative
seg.x = scalew(seg.x);
seg.y = scaleh(seg.y);
seg.x1 = scalew(seg.x1);
seg.y1 = scaleh(seg.y1);
seg.x2 = scalew(seg.x2);
seg.y2 = scaleh(seg.y2);
seg.r1 = scalew(seg.r1);
seg.r2 = scaleh(seg.r2);
}
} // for each segment
let dstr = '';
len = changes.d.length;
for (let i = 0; i < len; ++i) {
const seg = changes.d[i];
const { type } = seg;
dstr += pathMap[type];
switch (type) {
case 13: // relative horizontal line (h)
case 12: // absolute horizontal line (H)
dstr += seg.x + ' ';
break;
case 15: // relative vertical line (v)
case 14: // absolute vertical line (V)
dstr += seg.y + ' ';
break;
case 3: // relative move (m)
case 5: // relative line (l)
case 19: // relative smooth quad (t)
case 2: // absolute move (M)
case 4: // absolute line (L)
case 18: // absolute smooth quad (T)
dstr += seg.x + ',' + seg.y + ' ';
break;
case 7: // relative cubic (c)
case 6: // absolute cubic (C)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' +
seg.x + ',' + seg.y + ' ';
break;
case 9: // relative quad (q)
case 8: // absolute quad (Q)
dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' ';
break;
case 11: // relative elliptical arc (a)
case 10: // absolute elliptical arc (A)
dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + Number(seg.largeArcFlag) +
' ' + Number(seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' ';
break;
case 17: // relative smooth cubic (s)
case 16: // absolute smooth cubic (S)
dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' ';
break;
}
}
selected.setAttribute('d', dstr);
break;
}
}
};

View File

@ -989,6 +989,7 @@ export const mergeAllLayers = function (hrService) {
*/
export const leaveContext = function () {
const len = disabledElems.length;
const dataStorage = canvas_.getDataStorage();
if (len) {
for (let i = 0; i < len; i++) {
const elem = disabledElems[i];
@ -1015,6 +1016,7 @@ export const leaveContext = function () {
* @returns {void}
*/
export const setContext = function (elem) {
const dataStorage = canvas_.getDataStorage();
leaveContext();
if (typeof elem === 'string') {
elem = getElem(elem);

View File

@ -5,11 +5,11 @@
*/
/* globals jQuery */
import {jGraduate} from '../editor/components/jgraduate/jQuery.jGraduate.js';
import { jGraduate } from '../editor/components/jgraduate/jQuery.jGraduate.js';
import * as hstry from './history.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {NS} from '../common/namespaces.js';
import { NS } from '../common/namespaces.js';
import {
getVisibleElements, getStrokedBBoxDefaultVisible, findDefs,
walkTree, isNullish, getHref, setHref, getElem
@ -17,7 +17,7 @@ import {
import {
convertToNum
} from '../common/units.js';
import {getParents} from '../editor/components/jgraduate/Util.js';
import { getParents } from '../editor/components/jgraduate/Util.js';
const $ = jQueryPluginSVG(jQuery);
@ -61,11 +61,12 @@ export const getResolutionMethod = function () {
*/
export const getTitleMethod = function (elem) {
const selectedElements = elemContext_.getSelectedElements();
const dataStorage = elemContext_.getDataStorage();
elem = elem || selectedElements[0];
if (!elem) { return undefined; }
if(dataStorage.has(elem, 'gsvg')){
if (dataStorage.has(elem, 'gsvg')) {
elem = dataStorage.get(elem, 'gsvg');
} else if(dataStorage.has(elem, 'symbol')) {
} else if (dataStorage.has(elem, 'symbol')) {
elem = dataStorage.get(elem, 'symbol');
}
const childs = elem.childNodes;
@ -86,8 +87,9 @@ export const getTitleMethod = function (elem) {
*/
export const setGroupTitleMethod = function (val) {
const selectedElements = elemContext_.getSelectedElements();
const dataStorage = elemContext_.getDataStorage();
let elem = selectedElements[0];
if(dataStorage.has(elem, 'gsvg')){
if (dataStorage.has(elem, 'gsvg')) {
elem = dataStorage.get(elem, 'gsvg');
}
@ -104,7 +106,7 @@ export const setGroupTitleMethod = function (val) {
} else if (ts.length) {
// Change title contents
title = ts[0];
batchCmd.addSubCommand(new ChangeElementCommand(title, {'#text': title.textContent}));
batchCmd.addSubCommand(new ChangeElementCommand(title, { '#text': title.textContent }));
title.textContent = val;
} else {
// Add title element
@ -149,7 +151,7 @@ export const setDocumentTitleMethod = function (newTitle) {
// No title given, so element is not necessary
docTitle.remove();
}
batchCmd.addSubCommand(new ChangeElementCommand(docTitle, {'#text': oldTitle}));
batchCmd.addSubCommand(new ChangeElementCommand(docTitle, { '#text': oldTitle }));
elemContext_.addCommandToHistory(batchCmd);
};
@ -166,7 +168,7 @@ export const setDocumentTitleMethod = function (newTitle) {
export const setResolutionMethod = function (x, y) {
const currentZoom = elemContext_.getCurrentZoom();
const res = elemContext_.getCanvas().getResolution();
const {w, h} = res;
const { w, h } = res;
let batchCmd;
if (x === 'fit') {
@ -206,10 +208,10 @@ export const setResolutionMethod = function (x, y) {
this.contentW = x;
this.contentH = y;
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), {width: w, height: h}));
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), { width: w, height: h }));
elemContext_.getSVGContent().setAttribute('viewBox', [0, 0, x / currentZoom, y / currentZoom].join(' '));
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), {viewBox: ['0 0', w, h].join(' ')}));
batchCmd.addSubCommand(new ChangeElementCommand(elemContext_.getSVGContent(), { viewBox: ['0 0', w, h].join(' ') }));
elemContext_.addCommandToHistory(batchCmd);
elemContext_.call('changed', [elemContext_.getSVGContent()]);
@ -254,7 +256,7 @@ export const setBBoxZoomMethod = function (val, editorW, editorH) {
const hZoom = Math.round((editorH / bb.height) * 100 * spacer) / 100;
const zoom = Math.min(wZoom, hZoom);
elemContext_.getCanvas().setZoom(zoom);
return {zoom, bbox: bb};
return { zoom, bbox: bb };
};
if (typeof val === 'object') {
@ -262,35 +264,35 @@ export const setBBoxZoomMethod = function (val, editorW, editorH) {
if (bb.width === 0 || bb.height === 0) {
const newzoom = bb.zoom ? bb.zoom : currentZoom * bb.factor;
elemContext_.getCanvas().setZoom(newzoom);
return {zoom: currentZoom, bbox: bb};
return { zoom: currentZoom, bbox: bb };
}
return calcZoom(bb);
}
switch (val) {
case 'selection': {
if (!selectedElements[0]) { return undefined; }
const selectedElems = $.map(selectedElements, function (n) {
if (n) {
return n;
}
case 'selection': {
if (!selectedElements[0]) { return undefined; }
const selectedElems = $.map(selectedElements, function (n) {
if (n) {
return n;
}
return undefined;
});
bb = getStrokedBBoxDefaultVisible(selectedElems);
break;
} case 'canvas': {
const res = elemContext_.getCanvas().getResolution();
spacer = 0.95;
bb = { width: res.w, height: res.h, x: 0, y: 0 };
break;
} case 'content':
bb = getStrokedBBoxDefaultVisible();
break;
case 'layer':
bb = getStrokedBBoxDefaultVisible(getVisibleElements(elemContext_.getCanvas().getCurrentDrawing().getCurrentLayer()));
break;
default:
return undefined;
});
bb = getStrokedBBoxDefaultVisible(selectedElems);
break;
} case 'canvas': {
const res = elemContext_.getCanvas().getResolution();
spacer = 0.95;
bb = {width: res.w, height: res.h, x: 0, y: 0};
break;
} case 'content':
bb = getStrokedBBoxDefaultVisible();
break;
case 'layer':
bb = getStrokedBBoxDefaultVisible(getVisibleElements(elemContext_.getCanvas().getCurrentDrawing().getCurrentLayer()));
break;
default:
return undefined;
}
return calcZoom(bb);
};
@ -327,14 +329,14 @@ export const setZoomMethod = function (zoomLevel) {
export const setColorMethod = function (type, val, preventUndo) {
const selectedElements = elemContext_.getSelectedElements();
elemContext_.setCurShape(type, val);
elemContext_.setCurProperties(type + '_paint', {type: 'solidColor'});
elemContext_.setCurProperties(type + '_paint', { type: 'solidColor' });
const elems = [];
/**
*
* @param {Element} e
* @returns {void}
*/
function addNonG (e) {
function addNonG(e) {
if (e.nodeName !== 'g') {
elems.push(e);
}
@ -406,9 +408,9 @@ export const findDuplicateGradient = function (grad) {
const og = existingGrads[i];
if (grad.tagName === 'linearGradient') {
if (grad.getAttribute('x1') !== og.getAttribute('x1') ||
grad.getAttribute('y1') !== og.getAttribute('y1') ||
grad.getAttribute('x2') !== og.getAttribute('x2') ||
grad.getAttribute('y2') !== og.getAttribute('y2')
grad.getAttribute('y1') !== og.getAttribute('y1') ||
grad.getAttribute('x2') !== og.getAttribute('x2') ||
grad.getAttribute('y2') !== og.getAttribute('y2')
) {
continue;
}
@ -429,7 +431,7 @@ export const findDuplicateGradient = function (grad) {
};
let diff = false;
radAttrs.forEach(function(attr, j){
radAttrs.forEach(function (attr, j) {
if (gradAttrs[attr] !== ogAttrs[attr]) { diff = true; }
});
@ -450,8 +452,8 @@ export const findDuplicateGradient = function (grad) {
const ostop = ostops[j];
if (stop.getAttribute('offset') !== ostop.getAttribute('offset') ||
stop.getAttribute('stop-opacity') !== ostop.getAttribute('stop-opacity') ||
stop.getAttribute('stop-color') !== ostop.getAttribute('stop-color')) {
stop.getAttribute('stop-opacity') !== ostop.getAttribute('stop-opacity') ||
stop.getAttribute('stop-color') !== ostop.getAttribute('stop-color')) {
break;
}
}
@ -479,14 +481,14 @@ export const setPaintMethod = function (type, paint) {
// now set the current paint object
elemContext_.setCurProperties(type + '_paint', p);
switch (p.type) {
case 'solidColor':
this.setColor(type, p.solidColor !== 'none' ? '#' + p.solidColor : 'none');
break;
case 'linearGradient':
case 'radialGradient':
elemContext_.setCanvas(type + 'Grad', p[p.type]);
elemContext_.getCanvas().setGradient(type);
break;
case 'solidColor':
this.setColor(type, p.solidColor !== 'none' ? '#' + p.solidColor : 'none');
break;
case 'linearGradient':
case 'radialGradient':
elemContext_.setCanvas(type + 'Grad', p[p.type]);
elemContext_.getCanvas().setGradient(type);
break;
}
};
/**
@ -511,7 +513,7 @@ export const setStrokeWidthMethod = function (val) {
* @param {Element} e
* @returns {void}
*/
function addNonG (e) {
function addNonG(e) {
if (e.nodeName !== 'g') {
elems.push(e);
}
@ -572,7 +574,7 @@ export const getBoldMethod = function () {
// should only have one element selected
const selected = selectedElements[0];
if (!isNullish(selected) && selected.tagName === 'text' &&
isNullish(selectedElements[1])) {
isNullish(selectedElements[1])) {
return (selected.getAttribute('font-weight') === 'bold');
}
return false;
@ -588,7 +590,7 @@ export const setBoldMethod = function (b) {
const selectedElements = elemContext_.getSelectedElements();
const selected = selectedElements[0];
if (!isNullish(selected) && selected.tagName === 'text' &&
isNullish(selectedElements[1])) {
isNullish(selectedElements[1])) {
elemContext_.getCanvas().changeSelectedAttribute('font-weight', b ? 'bold' : 'normal');
}
if (!selectedElements[0].textContent) {
@ -605,7 +607,7 @@ export const getItalicMethod = function () {
const selectedElements = elemContext_.getSelectedElements();
const selected = selectedElements[0];
if (!isNullish(selected) && selected.tagName === 'text' &&
isNullish(selectedElements[1])) {
isNullish(selectedElements[1])) {
return (selected.getAttribute('font-style') === 'italic');
}
return false;
@ -621,7 +623,7 @@ export const setItalicMethod = function (i) {
const selectedElements = elemContext_.getSelectedElements();
const selected = selectedElements[0];
if (!isNullish(selected) && selected.tagName === 'text' &&
isNullish(selectedElements[1])) {
isNullish(selectedElements[1])) {
elemContext_.getCanvas().changeSelectedAttribute('font-style', i ? 'italic' : 'normal');
}
if (!selectedElements[0].textContent) {
@ -638,7 +640,7 @@ export const setTextAnchorMethod = function (value) {
const selectedElements = elemContext_.getSelectedElements();
const selected = selectedElements[0];
if (!isNullish(selected) && selected.tagName === 'text' &&
isNullish(selectedElements[1])) {
isNullish(selectedElements[1])) {
elemContext_.getCanvas().changeSelectedAttribute('text-anchor', value);
}
if (!selectedElements[0].textContent) {
@ -747,7 +749,7 @@ export const setImageURLMethod = function (val) {
const elem = selectedElements[0];
if (!elem) { return; }
const attrs = {
const attrs = {
width: elem.getAttribute('width'),
height: elem.getAttribute('height'),
};
@ -767,8 +769,8 @@ export const setImageURLMethod = function (val) {
'#href': curHref
}));
const img = new Image();
img.onload = function() {
const changes = {
img.onload = function () {
const changes = {
width: elem.getAttribute('width'),
height: elem.getAttribute('height'),
};
@ -834,7 +836,7 @@ export const setRectRadiusMethod = function (val) {
if (r !== String(val)) {
selected.setAttribute('rx', val);
selected.setAttribute('ry', val);
elemContext_.addCommandToHistory(new ChangeElementCommand(selected, {rx: r, ry: r}, 'Radius'));
elemContext_.addCommandToHistory(new ChangeElementCommand(selected, { rx: r, ry: r }, 'Radius'));
elemContext_.call('changed', [selected]);
}
}
@ -901,9 +903,9 @@ export const setBackgroundMethod = function (color, url) {
const div = document.createElement('div');
elemContext_.getCanvas().assignAttributes(div, {
style: 'pointer-events:none;width:100%;height:100%;' +
'background-image:url(data:image/gif;base64,' +
'R0lGODlhEAAQAIAAAP///9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG+' +
'gq4jM3IFLJgpswNly/XkcBpIiVaInlLJr9FZWAQA7);'
'background-image:url(data:image/gif;base64,' +
'R0lGODlhEAAQAIAAAP///9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG+' +
'gq4jM3IFLJgpswNly/XkcBpIiVaInlLJr9FZWAQA7);'
});
bgPattern.append(div);
bg.append(bgPattern);

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,13 @@
*/
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
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';
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';
import {
isIdentity, matrixMultiply, transformPoint, transformListToTransform,
hasMatrixTransform
@ -83,6 +83,7 @@ export const recalculateDimensions = function (selected) {
}
const svgroot = context_.getSVGRoot();
const dataStorage = context_.getDataStorage();
const tlist = getTransformList(selected);
// remove any unnecessary transforms
@ -93,7 +94,7 @@ export const recalculateDimensions = function (selected) {
const xform = tlist.getItem(k);
if (xform.type === 0) {
tlist.removeItem(k);
// remove identity matrices
// remove identity matrices
} else if (xform.type === 1) {
if (isIdentity(xform.matrix)) {
if (noi === 1) {
@ -106,14 +107,14 @@ export const recalculateDimensions = function (selected) {
}
tlist.removeItem(k);
}
// remove zero-degree rotations
// remove zero-degree rotations
} else if (xform.type === 4 && xform.angle === 0) {
tlist.removeItem(k);
}
}
// End here if all it has is a rotation
if (tlist.numberOfItems === 1 &&
getRotationAngle(selected)) { return null; }
getRotationAngle(selected)) { return null; }
}
// if this element had no transforms, we are done
@ -163,19 +164,18 @@ export const recalculateDimensions = function (selected) {
// If it still has a single [M] or [R][M], return null too (prevents BatchCommand from being returned).
switch (selected.tagName) {
// Ignore these elements, as they can absorb the [M]
case 'line':
case 'polyline':
case 'polygon':
case 'path':
break;
default:
if ((tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) ||
// Ignore these elements, as they can absorb the [M]
case 'line':
case 'polyline':
case 'polygon':
case 'path':
break;
default:
if ((tlist.numberOfItems === 1 && tlist.getItem(0).type === 1) ||
(tlist.numberOfItems === 2 && tlist.getItem(0).type === 1 && tlist.getItem(0).type === 4)) {
return null;
}
return null;
}
}
// Grouped SVG element
const gsvg = (dataStorage.has(selected, 'gsvg')) ? dataStorage.get(selected, 'gsvg') : undefined;
// we know we have some transforms, so set up return variable
@ -186,46 +186,46 @@ export const recalculateDimensions = function (selected) {
let initial = null;
let attrs = [];
switch (selected.tagName) {
case 'line':
attrs = ['x1', 'y1', 'x2', 'y2'];
break;
case 'circle':
attrs = ['cx', 'cy', 'r'];
break;
case 'ellipse':
attrs = ['cx', 'cy', 'rx', 'ry'];
break;
case 'foreignObject':
case 'rect':
case 'image':
attrs = ['width', 'height', 'x', 'y'];
break;
case 'use':
case 'text':
case 'tspan':
attrs = ['x', 'y'];
break;
case 'polygon':
case 'polyline': {
initial = {};
initial.points = selected.getAttribute('points');
const list = selected.points;
const len = list.numberOfItems;
changes.points = new Array(len);
for (let i = 0; i < len; ++i) {
const pt = list.getItem(i);
changes.points[i] = {x: pt.x, y: pt.y};
}
break;
} case 'path':
initial = {};
initial.d = selected.getAttribute('d');
changes.d = selected.getAttribute('d');
break;
case 'line':
attrs = ['x1', 'y1', 'x2', 'y2'];
break;
case 'circle':
attrs = ['cx', 'cy', 'r'];
break;
case 'ellipse':
attrs = ['cx', 'cy', 'rx', 'ry'];
break;
case 'foreignObject':
case 'rect':
case 'image':
attrs = ['width', 'height', 'x', 'y'];
break;
case 'use':
case 'text':
case 'tspan':
attrs = ['x', 'y'];
break;
case 'polygon':
case 'polyline': {
initial = {};
initial.points = selected.getAttribute('points');
const list = selected.points;
const len = list.numberOfItems;
changes.points = new Array(len);
for (let i = 0; i < len; ++i) {
const pt = list.getItem(i);
changes.points[i] = { x: pt.x, y: pt.y };
}
break;
} case 'path':
initial = {};
initial.d = selected.getAttribute('d');
changes.d = selected.getAttribute('d');
break;
} // switch on element type to get initial values
if (attrs.length) {
Array.prototype.forEach.call(attrs, function(attr, i){
Array.prototype.forEach.call(attrs, function (attr, i) {
changes[attr] = selected.getAttribute(attr);
});
for (const [attr, val] of Object.entries(changes)) {
@ -245,7 +245,7 @@ export const recalculateDimensions = function (selected) {
initial = $.extend(true, {}, changes);
for (const [attr, val] of Object.entries(initial)) {
initial[attr] = convertToNum(attr, val);
}
}
}
// save the start transform value too
initial.transform = context_.getStartTransform() || '';
@ -256,7 +256,7 @@ export const recalculateDimensions = function (selected) {
if ((selected.tagName === 'g' && !gsvg) || selected.tagName === 'a') {
const box = getBBox(selected);
oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2};
oldcenter = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
newcenter = transformPoint(
box.x + box.width / 2,
box.y + box.height / 2,
@ -341,7 +341,7 @@ export const recalculateDimensions = function (selected) {
childTlist.clear();
childTlist.appendItem(e2t);
// childxforms.push(e2t);
// if not rotated or skewed, push the [T][S][-T] down to the child
// if not rotated or skewed, push the [T][S][-T] down to the child
} else {
// update the transform list with translate,scale,translate
@ -406,9 +406,9 @@ export const recalculateDimensions = function (selected) {
e2t.setMatrix(m);
tlist.clear();
tlist.appendItem(e2t);
// next, check if the first transform was a translate
// if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ]
// therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ]
// next, check if the first transform was a translate
// if we had [ T1 ] [ M ] we want to transform this into [ M ] [ T2 ]
// therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ]
} else if ((N === 1 || (N > 1 && tlist.getItem(1).type !== 3)) &&
tlist.getItem(0).type === 2) {
operation = 2; // translate
@ -474,8 +474,8 @@ export const recalculateDimensions = function (selected) {
}
context_.setStartTransform(oldStartTransform);
}
// else, a matrix imposition from a parent group
// keep pushing it down to the children
// else, a matrix imposition from a parent group
// keep pushing it down to the children
} else if (N === 1 && tlist.getItem(0).type === 1 && !gangle) {
operation = 1;
const m = tlist.getItem(0).matrix,
@ -509,7 +509,7 @@ export const recalculateDimensions = function (selected) {
}
}
tlist.clear();
// else it was just a rotate
// else it was just a rotate
} else {
if (gangle) {
const newRot = svgroot.createSVGTransform();
@ -542,7 +542,7 @@ export const recalculateDimensions = function (selected) {
tlist.appendItem(newRot);
}
}
// if it was a resize
// if it was a resize
} else if (operation === 3) {
const m = transformListToTransform(tlist).matrix;
const roldt = svgroot.createSVGTransform();
@ -590,7 +590,7 @@ export const recalculateDimensions = function (selected) {
}
}
}
// else, it's a non-group
// else, it's a non-group
} else {
// TODO: box might be null for some elements (<metadata> etc), need to handle this
const box = getBBox(selected);
@ -606,7 +606,7 @@ export const recalculateDimensions = function (selected) {
// temporarily strip off the rotate and save the old center
const angle = getRotationAngle(selected);
if (angle) {
oldcenter = {x: box.x + box.width / 2, y: box.y + box.height / 2};
oldcenter = { x: box.x + box.width / 2, y: box.y + box.height / 2 };
newcenter = transformPoint(
box.x + box.width / 2,
box.y + box.height / 2,
@ -672,8 +672,8 @@ export const recalculateDimensions = function (selected) {
tlist.removeItem(N - 1);
tlist.removeItem(N - 2);
tlist.removeItem(N - 3);
// if we had [T][S][-T][M], then this was a skewed element being resized
// Thus, we simply combine it all into one matrix
// if we had [T][S][-T][M], then this was a skewed element being resized
// Thus, we simply combine it all into one matrix
} else if (N === 4 && tlist.getItem(N - 1).type === 1) {
operation = 3; // scale
m = transformListToTransform(tlist).matrix;
@ -683,10 +683,10 @@ export const recalculateDimensions = function (selected) {
tlist.appendItem(e2t);
// reset the matrix so that the element is not re-mapped
m = svgroot.createSVGMatrix();
// if we had [R][T][S][-T][M], then this was a rotated matrix-element
// if we had [T1][M] we want to transform this into [M][T2]
// therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2]
// down to the element
// if we had [R][T][S][-T][M], then this was a rotated matrix-element
// if we had [T1][M] we want to transform this into [M][T2]
// therefore [ T2 ] = [ M_inv ] [ T1 ] [ M ] and we can push [T2]
// down to the element
} else if ((N === 1 || (N > 1 && tlist.getItem(1).type !== 3)) &&
tlist.getItem(0).type === 2) {
operation = 2; // translate
@ -695,43 +695,43 @@ export const recalculateDimensions = function (selected) {
meqInv = meq.inverse();
m = matrixMultiply(meqInv, oldxlate, meq);
tlist.removeItem(0);
// else if this child now has a matrix imposition (from a parent group)
// we might be able to simplify
// else if this child now has a matrix imposition (from a parent group)
// we might be able to simplify
} else if (N === 1 && tlist.getItem(0).type === 1 && !angle) {
// Remap all point-based elements
m = transformListToTransform(tlist).matrix;
switch (selected.tagName) {
case 'line':
changes = {
x1: selected.getAttribute('x1'),
y1: selected.getAttribute('y1'),
x2: selected.getAttribute('x2'),
y2: selected.getAttribute('y2'),
}
// Fallthrough
case 'polyline':
case 'polygon':
changes.points = selected.getAttribute('points');
if (changes.points) {
const list = selected.points;
const len = list.numberOfItems;
changes.points = new Array(len);
for (let i = 0; i < len; ++i) {
const pt = list.getItem(i);
changes.points[i] = {x: pt.x, y: pt.y};
case 'line':
changes = {
x1: selected.getAttribute('x1'),
y1: selected.getAttribute('y1'),
x2: selected.getAttribute('x2'),
y2: selected.getAttribute('y2'),
}
}
// Fallthrough
case 'path':
changes.d = selected.getAttribute('d');
operation = 1;
tlist.clear();
break;
default:
break;
case 'polyline':
case 'polygon':
changes.points = selected.getAttribute('points');
if (changes.points) {
const list = selected.points;
const len = list.numberOfItems;
changes.points = new Array(len);
for (let i = 0; i < len; ++i) {
const pt = list.getItem(i);
changes.points[i] = { x: pt.x, y: pt.y };
}
}
// Fallthrough
case 'path':
changes.d = selected.getAttribute('d');
operation = 1;
tlist.clear();
break;
default:
break;
}
// if it was a rotation, put the rotate back and return without a command
// (this function has zero work to do for a rotate())
// if it was a rotation, put the rotate back and return without a command
// (this function has zero work to do for a rotate())
} else {
// operation = 4; // rotation
if (angle) {
@ -789,12 +789,12 @@ export const recalculateDimensions = function (selected) {
}
}
}
// [Rold][M][T][S][-T] became [Rold][M]
// we want it to be [Rnew][M][Tr] where Tr is the
// translation required to re-center it
// Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M]
// [Rold][M][T][S][-T] became [Rold][M]
// we want it to be [Rnew][M][Tr] where Tr is the
// translation required to re-center it
// Therefore, [Tr] = [M_inv][Rnew_inv][Rold][M]
} else if (operation === 3 && angle) {
const {matrix} = transformListToTransform(tlist);
const { matrix } = transformListToTransform(tlist);
const roldt = svgroot.createSVGTransform();
roldt.setRotate(angle, oldcenter.x, oldcenter.y);
const rold = roldt.matrix;

View File

@ -7,10 +7,10 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
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';
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';
const $ = jQuery;
@ -28,7 +28,7 @@ export class Selector {
* @param {Element} elem - DOM element associated with this selector
* @param {module:utilities.BBoxObject} [bbox] - Optional bbox to use for initialization (prevents duplicate `getBBox` call).
*/
constructor (id, elem, bbox) {
constructor(id, elem, bbox) {
// this is the selector's unique number
this.id = id;
@ -41,7 +41,7 @@ export class Selector {
// this holds a reference to the <g> element that holds all visual elements of the selector
this.selectorGroup = svgFactory_.createSVGElement({
element: 'g',
attr: {id: ('selectorGroup' + this.id)}
attr: { id: ('selectorGroup' + this.id) }
});
// this holds a reference to the path rect
@ -80,7 +80,7 @@ export class Selector {
* @param {module:utilities.BBoxObject} bbox - Optional bbox to use for reset (prevents duplicate getBBox call).
* @returns {void}
*/
reset (e, bbox) {
reset(e, bbox) {
this.locked = true;
this.selectedElement = e;
this.resize(bbox);
@ -92,7 +92,7 @@ export class Selector {
* @param {boolean} show - Indicates whether grips should be shown or not
* @returns {void}
*/
showGrips (show) {
showGrips(show) {
const bShow = show ? 'inline' : 'none';
selectorManager_.selectorGripsGroup.setAttribute('display', bShow);
const elem = this.selectedElement;
@ -108,7 +108,8 @@ export class Selector {
* @param {module:utilities.BBoxObject} [bbox] - BBox to use for resize (prevents duplicate getBBox call).
* @returns {void}
*/
resize (bbox) {
resize(bbox) {
const dataStorage = svgFactory_.getDataStorage();
const selectedBox = this.selectorRect,
mgr = selectorManager_,
selectedGrips = mgr.selectorGrips,
@ -120,7 +121,7 @@ export class Selector {
offset += (sw / 2);
}
const {tagName} = selected;
const { tagName } = selected;
if (tagName === 'text') {
offset += 2 / currentZoom;
}
@ -159,7 +160,7 @@ export class Selector {
offset *= currentZoom;
const nbox = transformBox(l * currentZoom, t * currentZoom, w * currentZoom, h * currentZoom, m),
{aabox} = nbox;
{ aabox } = nbox;
let nbax = aabox.x - offset,
nbay = aabox.y - offset,
nbaw = aabox.width + (offset * 2),
@ -180,13 +181,13 @@ export class Selector {
nbox.br = transformPoint(nbox.br.x, nbox.br.y, rotm);
// calculate the axis-aligned bbox
const {tl} = nbox;
const { tl } = nbox;
let minx = tl.x,
miny = tl.y,
maxx = tl.x,
maxy = tl.y;
const {min, max} = Math;
const { min, max } = Math;
minx = min(minx, min(nbox.tr.x, min(nbox.bl.x, nbox.br.x))) - offset;
miny = min(miny, min(nbox.tr.y, min(nbox.bl.y, nbox.br.y))) - offset;
@ -242,7 +243,7 @@ export class Selector {
* @param {Float} angle - Current rotation angle in degrees
* @returns {void}
*/
static updateGripCursors (angle) {
static updateGripCursors(angle) {
const dirArr = Object.keys(selectorManager_.selectorGrips);
let steps = Math.round(angle / 45);
if (steps < 0) { steps += 8; }
@ -263,7 +264,7 @@ export class SelectorManager {
/**
* Sets up properties and calls `initGroup`.
*/
constructor () {
constructor() {
// this will hold the <g> element that contains all selector rects/grips
this.selectorParentGroup = null;
@ -299,7 +300,8 @@ export class SelectorManager {
* Resets the parent selector group element.
* @returns {void}
*/
initGroup () {
initGroup() {
const dataStorage = svgFactory_.getDataStorage();
// remove old selector parent group if it existed
if (this.selectorParentGroup && this.selectorParentGroup.parentNode) {
this.selectorParentGroup.remove();
@ -308,11 +310,11 @@ export class SelectorManager {
// create parent selector group and add it to svgroot
this.selectorParentGroup = svgFactory_.createSVGElement({
element: 'g',
attr: {id: 'selectorParentGroup'}
attr: { id: 'selectorParentGroup' }
});
this.selectorGripsGroup = svgFactory_.createSVGElement({
element: 'g',
attr: {display: 'none'}
attr: { display: 'none' }
});
this.selectorParentGroup.append(this.selectorGripsGroup);
svgFactory_.svgRoot().append(this.selectorParentGroup);
@ -416,7 +418,7 @@ export class SelectorManager {
* @param {module:utilities.BBoxObject} [bbox] - Optional bbox to use for reset (prevents duplicate getBBox call).
* @returns {Selector} The selector based on the given element
*/
requestSelector (elem, bbox) {
requestSelector(elem, bbox) {
if (isNullish(elem)) { return null; }
const N = this.selectors.length;
@ -446,13 +448,13 @@ export class SelectorManager {
* @param {Element} elem - DOM element to remove the selector for
* @returns {void}
*/
releaseSelector (elem) {
releaseSelector(elem) {
if (isNullish(elem)) { return; }
const N = this.selectors.length,
sel = this.selectorMap[elem.id];
if (sel && !sel.locked) {
// TODO(codedread): Ensure this exists in this module.
console.log('WARNING! selector was released but was already unlocked');
console.log('WARNING! selector was released but was already unlocked');
}
for (let i = 0; i < N; ++i) {
if (this.selectors[i] && this.selectors[i] === sel) {
@ -464,7 +466,7 @@ export class SelectorManager {
// remove from DOM and store reference in JS but only if it exists in the DOM
try {
sel.selectorGroup.setAttribute('display', 'none');
} catch (e) {/* empty fn */}
} catch (e) {/* empty fn */ }
break;
}
@ -475,7 +477,7 @@ export class SelectorManager {
* @returns {SVGRectElement} The rubberBandBox DOM element. This is the rectangle drawn by
* the user for selecting/zooming
*/
getRubberBandBox () {
getRubberBandBox() {
if (!this.rubberBandBox) {
this.rubberBandBox =
svgFactory_.createSVGElement({

View File

@ -7,7 +7,7 @@
* @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
*/
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute
import {NS} from '../common/namespaces.js';
import { NS } from '../common/namespaces.js';
import * as hstry from './history.js';
import * as pathModule from './path.js';
import {
@ -26,7 +26,7 @@ import {
import {
isGecko
} from '../common/browser.js'; // , supportsEditableText
import {getParents} from '../editor/components/jgraduate/Util.js';
import { getParents } from '../editor/components/jgraduate/Util.js';
const {
MoveElementCommand, BatchCommand, InsertElementCommand, RemoveElementCommand, ChangeElementCommand
@ -80,7 +80,7 @@ export const moveToBottomSelectedElem = function () {
let t = selected;
const oldParent = t.parentNode;
const oldNextSibling = t.nextSibling;
let {firstChild} = t.parentNode;
let { firstChild } = t.parentNode;
if (firstChild.tagName === 'title') {
firstChild = firstChild.nextSibling;
}
@ -119,7 +119,7 @@ export const moveUpDownSelected = function (dir) {
const list = elementContext_.getIntersectionList(getStrokedBBoxDefaultVisible([selected]));
if (dir === 'Down') { list.reverse(); }
Array.prototype.forEach.call(list, function(el, i){
Array.prototype.forEach.call(list, function (el, i) {
if (!foundCur) {
if (el === selected) {
foundCur = true;
@ -134,7 +134,7 @@ export const moveUpDownSelected = function (dir) {
const t = selected;
const oldParent = t.parentNode;
const oldNextSibling = t.nextSibling;
if(dir === 'Down') {
if (dir === 'Down') {
closest.insertAdjacentElement('beforebegin', t);
} else {
closest.insertAdjacentElement('afterend', t);
@ -252,7 +252,7 @@ export const cloneSelectedElements = function (x, y) {
* @param {Element} b
* @returns {Integer}
*/
function sortfunction (a, b) {
function sortfunction(a, b) {
return (index(b) - index(a));
}
selectedElements.sort(sortfunction);
@ -302,40 +302,40 @@ export const alignSelectedElements = function (type, relativeTo) {
// now bbox is axis-aligned and handles rotation
switch (relativeTo) {
case 'smallest':
if (((type === 'l' || type === 'c' || type === 'r' || type === 'left' || type === 'center' || type === 'right') &&
(curwidth === Number.MIN_VALUE || curwidth > bboxes[i].width)) ||
((type === 't' || type === 'm' || type === 'b' || type === 'top' || type === 'middle' || type === 'bottom') &&
(curheight === Number.MIN_VALUE || curheight > bboxes[i].height))
) {
minx = bboxes[i].x;
miny = bboxes[i].y;
maxx = bboxes[i].x + bboxes[i].width;
maxy = bboxes[i].y + bboxes[i].height;
curwidth = bboxes[i].width;
curheight = bboxes[i].height;
}
break;
case 'largest':
if (((type === 'l' || type === 'c' || type === 'r' || type === 'left' || type === 'center' || type === 'right') &&
(curwidth === Number.MIN_VALUE || curwidth < bboxes[i].width)) ||
((type === 't' || type === 'm' || type === 'b' || type === 'top' || type === 'middle' || type === 'bottom') &&
(curheight === Number.MIN_VALUE || curheight < bboxes[i].height))
) {
minx = bboxes[i].x;
miny = bboxes[i].y;
maxx = bboxes[i].x + bboxes[i].width;
maxy = bboxes[i].y + bboxes[i].height;
curwidth = bboxes[i].width;
curheight = bboxes[i].height;
}
break;
default: // 'selected'
if (bboxes[i].x < minx) { minx = bboxes[i].x; }
if (bboxes[i].y < miny) { miny = bboxes[i].y; }
if (bboxes[i].x + bboxes[i].width > maxx) { maxx = bboxes[i].x + bboxes[i].width; }
if (bboxes[i].y + bboxes[i].height > maxy) { maxy = bboxes[i].y + bboxes[i].height; }
break;
case 'smallest':
if (((type === 'l' || type === 'c' || type === 'r' || type === 'left' || type === 'center' || type === 'right') &&
(curwidth === Number.MIN_VALUE || curwidth > bboxes[i].width)) ||
((type === 't' || type === 'm' || type === 'b' || type === 'top' || type === 'middle' || type === 'bottom') &&
(curheight === Number.MIN_VALUE || curheight > bboxes[i].height))
) {
minx = bboxes[i].x;
miny = bboxes[i].y;
maxx = bboxes[i].x + bboxes[i].width;
maxy = bboxes[i].y + bboxes[i].height;
curwidth = bboxes[i].width;
curheight = bboxes[i].height;
}
break;
case 'largest':
if (((type === 'l' || type === 'c' || type === 'r' || type === 'left' || type === 'center' || type === 'right') &&
(curwidth === Number.MIN_VALUE || curwidth < bboxes[i].width)) ||
((type === 't' || type === 'm' || type === 'b' || type === 'top' || type === 'middle' || type === 'bottom') &&
(curheight === Number.MIN_VALUE || curheight < bboxes[i].height))
) {
minx = bboxes[i].x;
miny = bboxes[i].y;
maxx = bboxes[i].x + bboxes[i].width;
maxy = bboxes[i].y + bboxes[i].height;
curwidth = bboxes[i].width;
curheight = bboxes[i].height;
}
break;
default: // 'selected'
if (bboxes[i].x < minx) { minx = bboxes[i].x; }
if (bboxes[i].y < miny) { miny = bboxes[i].y; }
if (bboxes[i].x + bboxes[i].width > maxx) { maxx = bboxes[i].x + bboxes[i].width; }
if (bboxes[i].y + bboxes[i].height > maxy) { maxy = bboxes[i].y + bboxes[i].height; }
break;
}
} // loop for each element to find the bbox and adjust min/max
@ -355,30 +355,30 @@ export const alignSelectedElements = function (type, relativeTo) {
dx[i] = 0;
dy[i] = 0;
switch (type) {
case 'l': // left (horizontal)
case 'left': // left (horizontal)
dx[i] = minx - bbox.x;
break;
case 'c': // center (horizontal)
case 'center': // center (horizontal)
dx[i] = (minx + maxx) / 2 - (bbox.x + bbox.width / 2);
break;
case 'r': // right (horizontal)
case 'right': // right (horizontal)
dx[i] = maxx - (bbox.x + bbox.width);
break;
case 't': // top (vertical)
case 'top': // top (vertical)
dy[i] = miny - bbox.y;
break;
case 'm': // middle (vertical)
case 'middle': // middle (vertical)
dy[i] = (miny + maxy) / 2 - (bbox.y + bbox.height / 2);
break;
case 'b': // bottom (vertical)
case 'bottom': // bottom (vertical)
dy[i] = maxy - (bbox.y + bbox.height);
break;
case 'l': // left (horizontal)
case 'left': // left (horizontal)
dx[i] = minx - bbox.x;
break;
case 'c': // center (horizontal)
case 'center': // center (horizontal)
dx[i] = (minx + maxx) / 2 - (bbox.x + bbox.width / 2);
break;
case 'r': // right (horizontal)
case 'right': // right (horizontal)
dx[i] = maxx - (bbox.x + bbox.width);
break;
case 't': // top (vertical)
case 'top': // top (vertical)
dy[i] = miny - bbox.y;
break;
case 'm': // middle (vertical)
case 'middle': // middle (vertical)
dy[i] = (miny + maxy) / 2 - (bbox.y + bbox.height / 2);
break;
case 'b': // bottom (vertical)
case 'bottom': // bottom (vertical)
dy[i] = maxy - (bbox.y + bbox.height);
break;
}
}
moveSelectedElements(dx, dy);
@ -416,7 +416,7 @@ export const deleteSelectedElements = function () {
parent = parent.parentNode;
}
const {nextSibling} = t;
const { nextSibling } = t;
t.remove();
const elem = t;
selectedCopy.push(selected); // for the copy
@ -437,7 +437,7 @@ export const deleteSelectedElements = function () {
export const copySelectedElements = function () {
const selectedElements = elementContext_.getSelectedElements();
const data =
JSON.stringify(selectedElements.map((x) => elementContext_.getJsonFromSvgElement(x)));
JSON.stringify(selectedElements.map((x) => elementContext_.getJsonFromSvgElement(x)));
// Use sessionStorage for the clipboard data.
sessionStorage.setItem(elementContext_.getClipboardID(), data);
elementContext_.flashStorage();
@ -461,15 +461,15 @@ export const groupSelectedElements = function (type, urlArg) {
let url;
switch (type) {
case 'a': {
cmdStr = 'Make hyperlink';
url = urlArg || '';
break;
} default: {
type = 'g';
cmdStr = 'Group Elements';
break;
}
case 'a': {
cmdStr = 'Make hyperlink';
url = urlArg || '';
break;
} default: {
type = 'g';
cmdStr = 'Group Elements';
break;
}
}
const batchCmd = new BatchCommand(cmdStr);
@ -713,6 +713,7 @@ export const convertToGroup = function (elem) {
const $elem = elem;
const batchCmd = new BatchCommand();
let ts;
const dataStorage = elementContext_.getDataStorage();
if (dataStorage.has($elem, 'gsvg')) {
// Use the gsvg as the new group
const svg = elem.firstChild;
@ -799,7 +800,7 @@ export const convertToGroup = function (elem) {
if (parent) {
if (!hasMore) {
// remove symbol/svg element
const {nextSibling} = elem;
const { nextSibling } = elem;
elem.remove();
batchCmd.addSubCommand(new RemoveElementCommand(elem, nextSibling, parent));
}
@ -820,13 +821,13 @@ export const convertToGroup = function (elem) {
try {
recalculateDimensions(n);
} catch (e) {
console.log(e);
console.log(e);
}
});
// Give ID for any visible element missing one
const visElems = g.querySelectorAll(elementContext_.getVisElems());
Array.prototype.forEach.call(visElems, function(el, i){
Array.prototype.forEach.call(visElems, function (el, i) {
if (!el.id) { el.id = elementContext_.getNextId(); }
});
@ -839,7 +840,7 @@ export const convertToGroup = function (elem) {
elementContext_.addCommandToHistory(batchCmd);
} else {
console.log('Unexpected element to ungroup:', elem);
console.log('Unexpected element to ungroup:', elem);
}
};
@ -851,6 +852,7 @@ export const convertToGroup = function (elem) {
*/
export const ungroupSelectedElement = function () {
const selectedElements = elementContext_.getSelectedElements();
const dataStorage = elementContext_.getDataStorage();
let g = selectedElements[0];
if (!g) {
return;
@ -891,7 +893,7 @@ export const ungroupSelectedElement = function () {
// Remove child title elements
if (elem.tagName === 'title') {
const {nextSibling} = elem;
const { nextSibling } = elem;
batchCmd.addSubCommand(new RemoveElementCommand(elem, nextSibling, oldParent));
elem.remove();
continue;
@ -979,9 +981,9 @@ export const updateCanvas = function (w, h) {
/**
* @type {module:svgcanvas.SvgCanvas#event:ext_canvasUpdated}
*/
{new_x: x, new_y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY}
{ new_x: x, new_y: y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY }
);
return {x, y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY};
return { x, y, old_x: oldX, old_y: oldY, d_x: x - oldX, d_y: y - oldY };
};
/**
* Select the next/previous element within the current layer.

View File

@ -6,19 +6,19 @@
* @copyright 2011 Jeff Schiller
*/
import {NS} from '../common/namespaces.js';
import { NS } from '../common/namespaces.js';
import {
isNullish, getBBox as utilsGetBBox, getStrokedBBoxDefaultVisible
} from './utilities.js';
import {transformPoint, transformListToTransform, rectsIntersect} from './math.js';
import { transformPoint, transformListToTransform, rectsIntersect } from './math.js';
import jQueryPluginSVG from './jQuery.attr.js';
import {
getTransformList
} from './svgtransformlist.js';
import * as hstry from './history.js';
import {getClosest} from '../editor/components/jgraduate/Util.js';
import { getClosest } from '../editor/components/jgraduate/Util.js';
const {BatchCommand} = hstry;
const { BatchCommand } = hstry;
const $ = jQueryPluginSVG(jQuery);
let selectionContext_ = null;
@ -140,7 +140,7 @@ export const getMouseTargetMethod = function (evt) {
// for foreign content, go up until we find the foreignObject
// WebKit browsers set the mouse target to the svgcanvas div
if ([NS.MATH, NS.HTML].includes(mouseTarget.namespaceURI) &&
mouseTarget.id !== 'svgcanvas'
mouseTarget.id !== 'svgcanvas'
) {
while (mouseTarget.nodeName !== 'foreignObject') {
mouseTarget = mouseTarget.parentNode;
@ -243,9 +243,9 @@ export const getVisibleElementsAndBBoxes = function (parent) {
}
const contentElems = [];
const elements = parent.children;
Array.prototype.forEach.call(elements, function(elem, i){
Array.prototype.forEach.call(elements, function (elem, i) {
if (elem.getBBox) {
contentElems.push({elem, bbox: getStrokedBBoxDefaultVisible([elem])});
contentElems.push({ elem, bbox: getStrokedBBoxDefaultVisible([elem]) });
}
});
return contentElems.reverse();
@ -324,6 +324,7 @@ export const getIntersectionListMethod = function (rect) {
* @returns {void}
*/
export const groupSvgElem = function (elem) {
const dataStorage = selectionContext_.getDataStorage();
const g = document.createElementNS(NS.SVG, 'g');
elem.replaceWith(g);
g.appendChild(elem);

View File

@ -6,7 +6,7 @@
* @copyright 2011 Jeff Schiller
*/
import {jsPDF} from 'jspdf/dist/jspdf.es.min.js';
import { jsPDF } from 'jspdf/dist/jspdf.es.min.js';
import 'svg2pdf.js/dist/svg2pdf.es.js';
import jQueryPluginSVG from './jQuery.attr.js';
import * as hstry from './history.js';
@ -18,18 +18,18 @@ import {
import {
transformPoint, transformListToTransform
} from './math.js';
import {resetListMap} from './svgtransformlist.js';
import { resetListMap } from './svgtransformlist.js';
import {
convertUnit, shortFloat, convertToNum
} from '../common/units.js';
import {isGecko, isChrome, isWebkit} from '../common/browser.js';
import { isGecko, isChrome, isWebkit } from '../common/browser.js';
import * as pathModule from './path.js';
import {NS} from '../common/namespaces.js';
import { NS } from '../common/namespaces.js';
import * as draw from './draw.js';
import {
recalculateDimensions
} from './recalculate.js';
import {getParents, getClosest} from '../editor/components/jgraduate/Util.js';
import { getParents, getClosest } from '../editor/components/jgraduate/Util.js';
const {
InsertElementCommand, RemoveElementCommand,
@ -59,13 +59,13 @@ export const init = function (svgContext) {
*/
export const svgCanvasToString = function () {
// keep calling it until there are none to remove
while (svgContext_.getCanvas().removeUnusedDefElems() > 0) {} // eslint-disable-line no-empty
while (svgContext_.getCanvas().removeUnusedDefElems() > 0) { } // eslint-disable-line no-empty
svgContext_.getCanvas().pathActions.clear(true);
// Keep SVG-Edit comment on top
const childNodesElems = svgContext_.getSVGContent().childNodes;
childNodesElems.forEach(function(node, i){
childNodesElems.forEach(function (node, i) {
if (i && node.nodeType === 8 && node.data.includes('Created with')) {
svgContext_.getSVGContent().firstChild.before(node);
}
@ -81,7 +81,7 @@ export const svgCanvasToString = function () {
// Unwrap gsvg if it has no special attributes (only id and style)
const gsvgElems = svgContext_.getSVGContent().querySelectorAll('g[data-gsvg]');
Array.prototype.forEach.call(gsvgElems, function(element, i){
Array.prototype.forEach.call(gsvgElems, function (element, i) {
const attrs = element.attributes;
let len = attrs.length;
for (let i = 0; i < len; i++) {
@ -100,7 +100,7 @@ export const svgCanvasToString = function () {
// Rewrap gsvg
if (nakedSvgs.length) {
Array.prototype.forEach.call(nakedSvgs, function(el, i){
Array.prototype.forEach.call(nakedSvgs, function (el, i) {
svgContext_.getCanvas().groupSvgElem(el);
});
}
@ -162,7 +162,7 @@ export const svgToString = function (elem, indent) {
const csElements = elem.querySelectorAll('*');
const cElements = Array.prototype.slice.call(csElements);
cElements.push(elem);
Array.prototype.forEach.call(cElements, function(el, i){
Array.prototype.forEach.call(cElements, function (el, i) {
// const el = this;
// for some elements have no attribute
const uri = el.namespaceURI;
@ -170,7 +170,7 @@ export const svgToString = function (elem, indent) {
nsuris[uri] = true;
out.push(' xmlns:' + nsMap[uri] + '="' + uri + '"');
}
if(el.attributes.length > 0) {
if (el.attributes.length > 0) {
for (const [i, attr] of Object.entries(el.attributes)) {
const u = attr.namespaceURI;
if (u && !nsuris[u] && nsMap[u] !== 'xmlns' && nsMap[u] !== 'xml') {
@ -224,10 +224,10 @@ export const svgToString = function (elem, indent) {
// Embed images when saving
if (svgContext_.getSvgOptionApply() &&
elem.nodeName === 'image' &&
attr.localName === 'href' &&
svgContext_.getSvgOptionImages() &&
svgContext_.getSvgOptionImages() === 'embed'
elem.nodeName === 'image' &&
attr.localName === 'href' &&
svgContext_.getSvgOptionImages() &&
svgContext_.getSvgOptionImages() === 'embed'
) {
const img = svgContext_.getEncodableImages(attrVal);
if (img) { attrVal = img; }
@ -251,31 +251,31 @@ export const svgToString = function (elem, indent) {
for (let i = 0; i < childs.length; i++) {
const child = childs.item(i);
switch (child.nodeType) {
case 1: // element node
out.push('\n');
out.push(this.svgToString(child, indent));
break;
case 3: { // text node
const str = child.nodeValue.replace(/^\s+|\s+$/g, '');
if (str !== '') {
bOneLine = true;
out.push(String(toXml(str)));
}
break;
} case 4: // cdata node
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<![CDATA[');
out.push(child.nodeValue);
out.push(']]>');
break;
case 8: // comment
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<!--');
out.push(child.data);
out.push('-->');
break;
case 1: // element node
out.push('\n');
out.push(this.svgToString(child, indent));
break;
case 3: { // text node
const str = child.nodeValue.replace(/^\s+|\s+$/g, '');
if (str !== '') {
bOneLine = true;
out.push(String(toXml(str)));
}
break;
} case 4: // cdata node
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<![CDATA[');
out.push(child.nodeValue);
out.push(']]>');
break;
case 8: // comment
out.push('\n');
out.push(new Array(indent + 1).join(' '));
out.push('<!--');
out.push(child.data);
out.push('-->');
break;
} // switch on node type
}
indent--;
@ -306,11 +306,12 @@ export const svgToString = function (elem, indent) {
*/
export const setSvgString = function (xmlString, preventUndo) {
const curConfig = svgContext_.getCurConfig();
const dataStorage = svgContext_.getDataStorage();
try {
// convert string into XML document
const newDoc = text2xml(xmlString);
if (newDoc.firstElementChild &&
newDoc.firstElementChild.namespaceURI !== NS.SVG) {
newDoc.firstElementChild.namespaceURI !== NS.SVG) {
return false;
}
@ -319,7 +320,7 @@ export const setSvgString = function (xmlString, preventUndo) {
const batchCmd = new BatchCommand('Change Source');
// remove old svg document
const {nextSibling} = svgContext_.getSVGContent();
const { nextSibling } = svgContext_.getSVGContent();
svgContext_.getSVGContent().remove();
const oldzoom = svgContext_.getSVGContent();
@ -348,7 +349,7 @@ export const setSvgString = function (xmlString, preventUndo) {
// change image href vals if possible
const elements = content.querySelectorAll('image');
Array.prototype.forEach.call(elements, function(image, i){
Array.prototype.forEach.call(elements, function (image, i) {
preventClickDefault(image);
const val = svgContext_.getCanvas().getHref(this);
if (val) {
@ -373,7 +374,7 @@ export const setSvgString = function (xmlString, preventUndo) {
// Wrap child SVGs in group elements
const svgElements = content.querySelectorAll('svg');
Array.prototype.forEach.call(svgElements, function(element, i){
Array.prototype.forEach.call(svgElements, function (element, i) {
// Skip if it's in a <defs>
if (getClosest(element.parentNode, 'defs')) { return; }
@ -393,7 +394,7 @@ export const setSvgString = function (xmlString, preventUndo) {
if (isGecko()) {
const svgDefs = findDefs();
const findElems = content.querySelectorAll('linearGradient, radialGradient, pattern');
Array.prototype.forEach.call(findElems, function(ele, i){
Array.prototype.forEach.call(findElems, function (ele, i) {
svgDefs.appendChild(ele);
});
}
@ -420,7 +421,7 @@ export const setSvgString = function (xmlString, preventUndo) {
attrs.height = vb[3];
// handle content that doesn't have a viewBox
} else {
['width', 'height'].forEach(function(dim, i){
['width', 'height'].forEach(function (dim, i) {
// Set to 100 if not given
const val = content.getAttribute(dim) || '100%';
if (String(val).substr(-1) === '%') {
@ -437,9 +438,9 @@ export const setSvgString = function (xmlString, preventUndo) {
// Give ID for any visible layer children missing one
const chiElems = content.children;
Array.prototype.forEach.call(chiElems, function(chiElem, i){
Array.prototype.forEach.call(chiElems, function (chiElem, i) {
const visElems = chiElem.querySelectorAll(svgContext_.getVisElems());
Array.prototype.forEach.call(visElems, function(elem, i){
Array.prototype.forEach.call(visElems, function (elem, i) {
if (!elem.id) { elem.id = svgContext_.getCanvas().getNextId(); }
});
});
@ -466,7 +467,7 @@ export const setSvgString = function (xmlString, preventUndo) {
// update root to the correct size
const width = content.getAttribute('width');
const height = content.getAttribute('height');
const changes = {width: width, height: height};
const changes = { width: width, height: height };
batchCmd.addSubCommand(new ChangeElementCommand(svgContext_.getSVGRoot(), changes));
// reset zoom
@ -481,7 +482,7 @@ export const setSvgString = function (xmlString, preventUndo) {
if (!preventUndo) svgContext_.addCommandToHistory(batchCmd);
svgContext_.call('changed', [svgContext_.getSVGContent()]);
} catch (e) {
console.log(e);
console.log(e);
return false;
}
@ -503,7 +504,7 @@ export const setSvgString = function (xmlString, preventUndo) {
* was obtained
*/
export const importSvgString = function (xmlString) {
console.log('importSvgString --> ', xmlString);
const dataStorage = svgContext_.getDataStorage();
let j, ts, useEl;
try {
// Get unique ID
@ -513,7 +514,7 @@ export const importSvgString = function (xmlString) {
// Look for symbol and make sure symbol exists in image
if (svgContext_.getImportIds(uid) && svgContext_.getImportIds(uid).symbol) {
const parents = getParents(svgContext_.getImportIds(uid).symbol, '#svgroot');
if(parents.length){
if (parents.length) {
useExisting = true;
}
}
@ -521,7 +522,7 @@ export const importSvgString = function (xmlString) {
const batchCmd = new BatchCommand('Import Image');
let symbol;
if (useExisting) {
({symbol} = svgContext_.getImportIds());
({ symbol } = svgContext_.getImportIds());
ts = svgContext_.getImportIds(uid).xform;
} else {
// convert string into XML document
@ -564,7 +565,7 @@ export const importSvgString = function (xmlString) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=353575
// TODO: Make this properly undo-able.
const elements = svg.querySelectorAll('linearGradient, radialGradient, pattern');
Array.prototype.forEach.call(elements, function(el, i){
Array.prototype.forEach.call(elements, function (el, i) {
defs.appendChild(el);
});
}
@ -610,7 +611,7 @@ export const importSvgString = function (xmlString) {
svgContext_.addCommandToHistory(batchCmd);
svgContext_.call('changed', [svgContext_.getSVGContent()]);
} catch (e) {
console.log(e);
console.log(e);
return null;
}
@ -689,7 +690,7 @@ export const save = function (opts) {
* Codes only is useful for locale-independent detection.
* @returns {module:svgcanvas.IssuesAndCodes}
*/
function getIssues () {
function getIssues() {
const uiStrings = svgContext_.getUIStrings();
// remove the selected outline before serializing
svgContext_.getCanvas().clearSelection();
@ -717,7 +718,7 @@ function getIssues () {
issues.push(descr);
}
}
return {issues, issueCodes};
return { issues, issueCodes };
}
/**
* @typedef {PlainObject} module:svgcanvas.ImageExportedResults
@ -749,7 +750,7 @@ function getIssues () {
export const rasterExport = async function (imgType, quality, exportWindowName, opts = {}) {
const type = imgType === 'ICO' ? 'BMP' : (imgType || 'PNG');
const mimeType = 'image/' + type.toLowerCase();
const {issues, issueCodes} = getIssues();
const { issues, issueCodes } = getIssues();
const svg = this.svgCanvasToString();
if (!$id('export_canvas')) {
@ -777,7 +778,7 @@ export const rasterExport = async function (imgType, quality, exportWindowName,
* Called when `bloburl` is available for export.
* @returns {void}
*/
function done () {
function done() {
const obj = {
datauri, bloburl, svg, issues, issueCodes, type: imgType,
mimeType, quality, exportWindowName
@ -854,17 +855,17 @@ export const exportPDF = async (
keywords: '',
creator: '' */
});
const {issues, issueCodes} = getIssues();
const { issues, issueCodes } = getIssues();
// const svg = this.svgCanvasToString();
// await doc.addSvgAsImage(svg)
await doc.svg(svgContext_.getSVGContent(), {x: 0, y: 0, width: res.w, height: res.h});
await doc.svg(svgContext_.getSVGContent(), { x: 0, y: 0, width: res.w, height: res.h });
// doc.output('save'); // Works to open in a new
// window; todo: configure this and other export
// options to optionally work in this manner as
// opposed to opening a new tab
outputType = outputType || 'dataurlstring';
const obj = {issues, issueCodes, exportWindowName, outputType};
const obj = { issues, issueCodes, exportWindowName, outputType };
obj.output = doc.output(outputType, outputType === 'save' ? (exportWindowName || 'svg.pdf') : undefined);
svgContext_.call('exportedPDF', obj);
return obj;
@ -895,7 +896,7 @@ export const uniquifyElemsMethod = function (g) {
// and we haven't tracked this ID yet
if (!(n.id in ids)) {
// add this id to our map
ids[n.id] = {elem: null, attrs: [], hrefs: []};
ids[n.id] = { elem: null, attrs: [], hrefs: [] };
}
ids[n.id].elem = n;
}
@ -911,7 +912,7 @@ export const uniquifyElemsMethod = function (g) {
if (refid) {
if (!(refid in ids)) {
// add this id to our map
ids[refid] = {elem: null, attrs: [], hrefs: []};
ids[refid] = { elem: null, attrs: [], hrefs: [] };
}
ids[refid].attrs.push(attrnode);
}
@ -926,7 +927,7 @@ export const uniquifyElemsMethod = function (g) {
if (refid) {
if (!(refid in ids)) {
// add this id to our map
ids[refid] = {elem: null, attrs: [], hrefs: []};
ids[refid] = { elem: null, attrs: [], hrefs: [] };
}
ids[refid].hrefs.push(n);
}
@ -937,7 +938,7 @@ export const uniquifyElemsMethod = function (g) {
// in ids, we now have a map of ids, elements and attributes, let's re-identify
for (const oldid in ids) {
if (!oldid) { continue; }
const {elem} = ids[oldid];
const { elem } = ids[oldid];
if (elem) {
const newid = svgContext_.getCanvas().getNextId();
@ -945,7 +946,7 @@ export const uniquifyElemsMethod = function (g) {
elem.id = newid;
// remap all url() attributes
const {attrs} = ids[oldid];
const { attrs } = ids[oldid];
let j = attrs.length;
while (j--) {
const attr = attrs[j];
@ -977,7 +978,8 @@ export const setUseDataMethod = function (parent) {
elems = elems.querySelectorAll('use');
}
Array.prototype.forEach.call(elems, function(el, _){
Array.prototype.forEach.call(elems, function (el, _) {
const dataStorage = svgContext_.getDataStorage();
const id = svgContext_.getCanvas().getHref(el).substr(1);
const refElem = svgContext_.getCanvas().getElem(id);
if (!refElem) { return; }
@ -1026,12 +1028,12 @@ export const removeUnusedDefElemsMethod = function () {
}
}
Array.prototype.forEach.call(defs, function(def, i){
Array.prototype.forEach.call(defs, function (def, i) {
const defelems = def.querySelectorAll('linearGradient, radialGradient, filter, marker, svg, symbol');
i = defelems.length;
while (i--) {
const defelem = defelems[i];
const {id} = defelem;
const { id } = defelem;
if (!defelemUses.includes(id)) {
// Not found, so remove (but remember)
svgContext_.setRemovedElements(id, defelem);
@ -1058,7 +1060,7 @@ export const convertGradientsMethod = function (elem) {
});
}
Array.prototype.forEach.call(elems, function(grad, i){
Array.prototype.forEach.call(elems, function (grad, i) {
if (grad.getAttribute('gradientUnits') === 'userSpaceOnUse') {
const svgcontent = svgContext_.getSVGContent();
// TODO: Support more than one element with this ref by duplicating parent grad

View File

@ -9,7 +9,7 @@
*
*/
import {Canvg as canvg} from 'canvg';
import { Canvg as canvg } from 'canvg';
import 'pathseg';
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
@ -28,7 +28,7 @@ import {
setLayerVisibility, moveSelectedToLayer, mergeLayer, mergeAllLayers,
leaveContext, setContext
} from './draw.js';
import {svgRootElement} from './svgroot.js';
import { svgRootElement } from './svgroot.js';
import {
init as undoInit, getUndoManager, changeSelectedAttributeNoUndoMethod,
changeSelectedAttributeMethod, ffClone
@ -45,7 +45,7 @@ import {
init as eventInit, mouseMoveEvent, mouseUpEvent,
dblClickEvent, mouseDownEvent, DOMMouseScrollEvent
} from './event.js';
import {init as jsonInit, getJsonFromSvgElements, addSVGElementsFromJson} from './json.js';
import { init as jsonInit, getJsonFromSvgElements, addSVGElementsFromJson } from './json.js';
import {
init as elemInit, getResolutionMethod, getTitleMethod, setGroupTitleMethod,
setDocumentTitleMethod, setResolutionMethod, getEditorNSMethod, setBBoxZoomMethod,
@ -65,8 +65,8 @@ import {
import {
init as blurInit, setBlurNoUndo, setBlurOffsets, setBlur
} from './blur-event.js';
import {sanitizeSvg} from './sanitize.js';
import {getReverseNS, NS} from '../common/namespaces.js';
import { sanitizeSvg } from './sanitize.js';
import { getReverseNS, NS } from '../common/namespaces.js';
import {
text2xml, assignAttributes, cleanupElement, getElem, getUrlFromAttr,
findDefs, getHref, setHref, getRefElem, getRotationAngle, getPathBBox,
@ -168,8 +168,8 @@ class SvgCanvas {
* @param {HTMLElement} container - The container HTML element that should hold the SVG root element
* @param {module:SVGeditor.configObj.curConfig} config - An object that contains configuration data
*/
constructor (container, config) {
// Alias Namespace constants
constructor(container, config) {
// Alias Namespace constants
// Default configuration options
const curConfig = {
@ -184,7 +184,7 @@ class SvgCanvas {
}
// Array with width/height of canvas
const {dimensions} = curConfig;
const { dimensions } = curConfig;
const canvas = this;
@ -193,6 +193,33 @@ class SvgCanvas {
this.$qa = $qa;
this.getClosest = getClosest;
this.getParents = getParents;
/** A storage solution aimed at replacing jQuerys data function.
* Implementation Note: Elements are stored in a (WeakMap)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap].
* This makes sure the data is garbage collected when the node is removed.
*/
this.dataStorage = {
_storage: new WeakMap(),
put: function (element, key, obj) {
if (!this._storage.has(element)) {
this._storage.set(element, new Map());
}
this._storage.get(element).set(key, obj);
},
get: function (element, key) {
return this._storage.get(element).get(key);
},
has: function (element, key) {
return this._storage.has(element) && this._storage.get(element).has(key);
},
remove: function (element, key) {
var ret = this._storage.get(element).delete(key);
if (!this._storage.get(element).size === 0) {
this._storage.delete(element);
}
return ret;
}
};
const getDataStorage = this.getDataStorage = function () { return canvas.dataStorage; };
this.isLayer = draw.Layer.isLayer;
@ -229,10 +256,10 @@ class SvgCanvas {
*/
{
getSVGContent,
getDOMDocument () { return svgdoc; },
getDOMContainer () { return container; },
getDOMDocument() { return svgdoc; },
getDOMContainer() { return container; },
getSVGRoot,
getCurConfig () { return curConfig; }
getCurConfig() { return curConfig; }
}
);
/**
@ -318,10 +345,10 @@ class SvgCanvas {
* @implements {module:json.jsonContext}
*/
{
getDOMDocument () { return svgdoc; },
getDrawing () { return getCurrentDrawing(); },
getCurShape () { return curShape; },
getCurrentGroup () { return currentGroup; }
getDOMDocument() { return svgdoc; },
getDrawing() { return getCurrentDrawing(); },
getCurShape() { return curShape; },
getCurrentGroup() { return currentGroup; }
}
);
@ -367,9 +394,9 @@ class SvgCanvas {
{
getBaseUnit,
getElement: getElem,
getHeight () { return svgcontent.getAttribute('height') / currentZoom; },
getWidth () { return svgcontent.getAttribute('width') / currentZoom; },
getRoundDigits () { return saveOptions.round_digits; }
getHeight() { return svgcontent.getAttribute('height') / currentZoom; },
getWidth() { return svgcontent.getAttribute('width') / currentZoom; },
getRoundDigits() { return saveOptions.round_digits; }
}
);
@ -391,7 +418,7 @@ class SvgCanvas {
selectedElements = [];
};
const {pathActions} = pathModule;
const { pathActions } = pathModule;
/**
* This should actually be an intersection as all interfaces should be met.
@ -407,12 +434,13 @@ class SvgCanvas {
getSVGContent,
addSVGElementFromJson,
getSelectedElements,
getDOMDocument () { return svgdoc; },
getDOMContainer () { return container; },
getDOMDocument() { return svgdoc; },
getDOMContainer() { return container; },
getSVGRoot,
// TODO: replace this mostly with a way to get the current drawing.
getBaseUnit,
getSnappingStep () { return curConfig.snappingStep; }
getSnappingStep() { return curConfig.snappingStep; },
getDataStorage
}
);
@ -439,7 +467,8 @@ class SvgCanvas {
* @implements {module:coords.EditorContext}
*/
{
getDrawing () { return getCurrentDrawing(); },
getDrawing() { return getCurrentDrawing(); },
getDataStorage,
getSVGRoot,
getGridSnapping
}
@ -452,8 +481,9 @@ class SvgCanvas {
*/
{
getSVGRoot,
getStartTransform () { return startTransform; },
setStartTransform (transform) { startTransform = transform; }
getStartTransform() { return startTransform; },
setStartTransform(transform) { startTransform = transform; },
getDataStorage
}
);
this.recalculateDimensions = recalculateDimensions;
@ -485,9 +515,10 @@ class SvgCanvas {
* @implements {module:select.SVGFactory}
*/
{
createSVGElement (jsonMap) { return canvas.addSVGElementFromJson(jsonMap); },
svgRoot () { return svgroot; },
svgContent () { return svgcontent; },
createSVGElement(jsonMap) { return canvas.addSVGElementFromJson(jsonMap); },
svgRoot() { return svgroot; },
svgContent() { return svgcontent; },
getDataStorage,
getCurrentZoom
}
);
@ -529,7 +560,7 @@ class SvgCanvas {
const restoreRefElems = function (elem) {
// Look for missing reference elements, restore any found
let attrs = {};
refAttrs.forEach(function(item, _){
refAttrs.forEach(function (item, _) {
attrs[item] = elem.getAttribute(item);
});
Object.values(attrs).forEach((val) => {
@ -560,8 +591,8 @@ class SvgCanvas {
call,
restoreRefElems,
getSVGContent,
getCanvas () { return canvas; },
getCurrentMode () { return currentMode; },
getCanvas() { return canvas; },
getCurrentMode() { return currentMode; },
getCurrentZoom,
getSVGRoot,
getSelectedElements
@ -588,21 +619,22 @@ class SvgCanvas {
* @implements {module:selection.selectionContext}
*/
{
getCanvas () { return canvas; },
getCurrentGroup () { return currentGroup; },
getCanvas() { return canvas; },
getDataStorage,
getCurrentGroup() { return currentGroup; },
getSelectedElements,
getSVGRoot,
getSVGContent,
getDOMContainer () { return container; },
getExtensions () { return extensions; },
setExtensions (key, value) { extensions[key] = value; },
getDOMContainer() { return container; },
getExtensions() { return extensions; },
setExtensions(key, value) { extensions[key] = value; },
getCurrentZoom,
getRubberBox () { return rubberBox; },
setCurBBoxes (value) { curBBoxes = value; },
getCurBBoxes (value) { return curBBoxes; },
getCurrentResizeMode () { return currentResizeMode; },
getRubberBox() { return rubberBox; },
setCurBBoxes(value) { curBBoxes = value; },
getCurBBoxes(value) { return curBBoxes; },
getCurrentResizeMode() { return currentResizeMode; },
addCommandToHistory,
getSelector () { return Selector; }
getSelector() { return Selector; }
}
);
@ -645,7 +677,7 @@ class SvgCanvas {
/**
* @type {module:path.EditorContext#resetD}
*/
function resetD (p) {
function resetD(p) {
if (typeof pathActions.convertPath === 'function') {
p.setAttribute('d', pathActions.convertPath(p));
} else if (typeof pathActions.convertPaths === 'function') {
@ -670,16 +702,16 @@ class SvgCanvas {
getGridSnapping,
getOpacity,
getSelectedElements,
getContainer () {
getContainer() {
return container;
},
setStarted (s) {
setStarted(s) {
started = s;
},
getRubberBox () {
getRubberBox() {
return rubberBox;
},
setRubberBox (rb) {
setRubberBox(rb) {
rubberBox = rb;
return rubberBox;
},
@ -691,11 +723,11 @@ class SvgCanvas {
* @fires module:svgcanvas.SvgCanvas#event:selected
* @returns {void}
*/
addPtsToSelection ({closedSubpath, grips}) {
addPtsToSelection({ closedSubpath, grips }) {
// TODO: Correct this:
pathActions.canDeleteNodes = true;
pathActions.closed_subpath = closedSubpath;
call('pointsAdded', {closedSubpath, grips});
call('pointsAdded', { closedSubpath, grips });
call('selected', grips);
},
/**
@ -705,7 +737,7 @@ class SvgCanvas {
* @fires module:svgcanvas.SvgCanvas#event:changed
* @returns {void}
*/
endChanges ({cmd, elem}) {
endChanges({ cmd, elem }) {
addCommandToHistory(cmd);
call('changed', [elem]);
},
@ -713,17 +745,17 @@ class SvgCanvas {
getId,
getNextId,
getMouseTarget,
getCurrentMode () {
getCurrentMode() {
return currentMode;
},
setCurrentMode (cm) {
setCurrentMode(cm) {
currentMode = cm;
return currentMode;
},
getDrawnPath () {
getDrawnPath() {
return drawnPath;
},
setDrawnPath (dp) {
setDrawnPath(dp) {
drawnPath = dp;
return drawnPath;
},
@ -765,7 +797,7 @@ class SvgCanvas {
/**
* @type {module:svgcanvas.SaveOptions}
*/
saveOptions = {round_digits: 5},
saveOptions = { round_digits: 5 },
// Object with IDs for imported files, to see if one was already added
importIds = {},
@ -839,7 +871,7 @@ class SvgCanvas {
* if extension of supplied name already exists
* @returns {Promise<void>} Resolves to `undefined`
*/
this.addExtension = async function (name, extInitFunc, {$: jq, importLocale}) {
this.addExtension = async function (name, extInitFunc, { $: jq, importLocale }) {
if (typeof extInitFunc !== 'function') {
throw new TypeError('Function argument expected for `svgcanvas.addExtension`');
}
@ -1076,7 +1108,7 @@ class SvgCanvas {
* @returns {void}
*/
const logMatrix = function (m) {
console.log([m.a, m.b, m.c, m.d, m.e, m.f]);
console.log([m.a, m.b, m.c, m.d, m.e, m.f]);
};
// Root Current Transformation Matrix in user units
@ -1140,7 +1172,7 @@ class SvgCanvas {
const currentLayer = getCurrentDrawing().getCurrentLayer();
if (currentLayer) {
currentMode = 'select';
if(currentGroup){
if (currentGroup) {
selectOnly(currentGroup.children);
} else {
selectOnly(currentLayer.children);
@ -1167,12 +1199,12 @@ class SvgCanvas {
let rStartY = null;
let initBbox = {};
let sumDistance = 0;
const controllPoint2 = {x: 0, y: 0};
const controllPoint1 = {x: 0, y: 0};
let start = {x: 0, y: 0};
const end = {x: 0, y: 0};
let bSpline = {x: 0, y: 0};
let nextPos = {x: 0, y: 0};
const controllPoint2 = { x: 0, y: 0 };
const controllPoint1 = { x: 0, y: 0 };
let start = { x: 0, y: 0 };
const end = { x: 0, y: 0 };
let bSpline = { x: 0, y: 0 };
let nextPos = { x: 0, y: 0 };
let parameter;
let nextParameter;
@ -1181,73 +1213,74 @@ class SvgCanvas {
* @returns {void}
*/
eventInit(
/**
* @implements {module:event.eventContext_}
*/
/**
* @implements {module:event.eventContext_}
*/
{
getStarted () { return started; },
getCanvas () { return canvas; },
getCurConfig () { return curConfig; },
getCurrentMode () { return currentMode; },
getrootSctm () { return rootSctm; },
getStartX () { return startX; },
setStartX (value) { startX = value; },
getStartY () { return startY; },
setStartY (value) { startY = value; },
getRStartX () { return rStartX; },
getRStartY () { return rStartY; },
getRubberBox () { return rubberBox; },
getInitBbox () { return initBbox; },
getCurrentResizeMode () { return currentResizeMode; },
getCurrentGroup () { return currentGroup; },
getDrawnPath () { return drawnPath; },
getJustSelected () { return justSelected; },
getOpacAni () { return opacAni; },
getParameter () { return parameter; },
getNextParameter () { return nextParameter; },
getStepCount () { return STEP_COUNT; },
getThreSholdDist () { return THRESHOLD_DIST; },
getSumDistance () { return sumDistance; },
getStart (key) { return start[key]; },
getEnd (key) { return end[key]; },
getbSpline (key) { return bSpline[key]; },
getNextPos (key) { return nextPos[key]; },
getControllPoint1 (key) { return controllPoint1[key]; },
getControllPoint2 (key) { return controllPoint2[key]; },
getFreehand (key) { return freehand[key]; },
getDrawing () { return getCurrentDrawing(); },
getCurShape () { return curShape; },
getDAttr () { return dAttr; },
getLastGoodImgUrl () { return lastGoodImgUrl; },
getCurText (key) { return curText[key]; },
setDAttr (value) { dAttr = value; },
setEnd (key, value) { end[key] = value; },
setControllPoint1 (key, value) { controllPoint1[key] = value; },
setControllPoint2 (key, value) { controllPoint2[key] = value; },
setJustSelected (value) { justSelected = value; },
setParameter (value) { parameter = value; },
setStart (value) { start = value; },
setRStartX (value) { rStartX = value; },
setRStartY (value) { rStartY = value; },
setSumDistance (value) { sumDistance = value; },
setbSpline (value) { bSpline = value; },
setNextPos (value) { nextPos = value; },
setNextParameter (value) { nextParameter = value; },
setCurProperties (key, value) { curProperties[key] = value; },
setCurText (key, value) { curText[key] = value; },
setStarted (s) { started = s; },
setStartTransform (transform) { startTransform = transform; },
setCurrentMode (cm) {
getStarted() { return started; },
getCanvas() { return canvas; },
getDataStorage,
getCurConfig() { return curConfig; },
getCurrentMode() { return currentMode; },
getrootSctm() { return rootSctm; },
getStartX() { return startX; },
setStartX(value) { startX = value; },
getStartY() { return startY; },
setStartY(value) { startY = value; },
getRStartX() { return rStartX; },
getRStartY() { return rStartY; },
getRubberBox() { return rubberBox; },
getInitBbox() { return initBbox; },
getCurrentResizeMode() { return currentResizeMode; },
getCurrentGroup() { return currentGroup; },
getDrawnPath() { return drawnPath; },
getJustSelected() { return justSelected; },
getOpacAni() { return opacAni; },
getParameter() { return parameter; },
getNextParameter() { return nextParameter; },
getStepCount() { return STEP_COUNT; },
getThreSholdDist() { return THRESHOLD_DIST; },
getSumDistance() { return sumDistance; },
getStart(key) { return start[key]; },
getEnd(key) { return end[key]; },
getbSpline(key) { return bSpline[key]; },
getNextPos(key) { return nextPos[key]; },
getControllPoint1(key) { return controllPoint1[key]; },
getControllPoint2(key) { return controllPoint2[key]; },
getFreehand(key) { return freehand[key]; },
getDrawing() { return getCurrentDrawing(); },
getCurShape() { return curShape; },
getDAttr() { return dAttr; },
getLastGoodImgUrl() { return lastGoodImgUrl; },
getCurText(key) { return curText[key]; },
setDAttr(value) { dAttr = value; },
setEnd(key, value) { end[key] = value; },
setControllPoint1(key, value) { controllPoint1[key] = value; },
setControllPoint2(key, value) { controllPoint2[key] = value; },
setJustSelected(value) { justSelected = value; },
setParameter(value) { parameter = value; },
setStart(value) { start = value; },
setRStartX(value) { rStartX = value; },
setRStartY(value) { rStartY = value; },
setSumDistance(value) { sumDistance = value; },
setbSpline(value) { bSpline = value; },
setNextPos(value) { nextPos = value; },
setNextParameter(value) { nextParameter = value; },
setCurProperties(key, value) { curProperties[key] = value; },
setCurText(key, value) { curText[key] = value; },
setStarted(s) { started = s; },
setStartTransform(transform) { startTransform = transform; },
setCurrentMode(cm) {
currentMode = cm;
return currentMode;
},
setFreehand (key, value) { freehand[key] = value; },
setCurBBoxes (value) { curBBoxes = value; },
setRubberBox (value) { rubberBox = value; },
setInitBbox (value) { initBbox = value; },
setRootSctm (value) { rootSctm = value; },
setCurrentResizeMode (value) { currentResizeMode = value; },
setLastClickPoint (value) { lastClickPoint = value; },
setFreehand(key, value) { freehand[key] = value; },
setCurBBoxes(value) { curBBoxes = value; },
setRubberBox(value) { rubberBox = value; },
setInitBbox(value) { initBbox = value; },
setRootSctm(value) { rootSctm = value; },
setCurrentResizeMode(value) { currentResizeMode = value; },
setLastClickPoint(value) { lastClickPoint = value; },
getSelectedElements,
getCurrentZoom,
getId,
@ -1324,14 +1357,14 @@ class SvgCanvas {
* @implements {module:text-actions.textActionsContext}
*/
{
getCanvas () { return canvas; },
getrootSctm () { return rootSctm; },
getCanvas() { return canvas; },
getrootSctm() { return rootSctm; },
getSelectedElements,
getCurrentZoom,
getCurrentMode () {
getCurrentMode() {
return currentMode;
},
setCurrentMode (cm) {
setCurrentMode(cm) {
currentMode = cm;
return currentMode;
},
@ -1347,34 +1380,35 @@ class SvgCanvas {
*/
svgInit(
/**
* @implements {module:elem-get-set.elemInit}
*/
/**
* @implements {module:elem-get-set.elemInit}
*/
{
getCanvas () { return canvas; },
getCanvas() { return canvas; },
getDataStorage,
getSVGContent,
getSVGRoot,
getUIStrings () { return uiStrings; },
getCurrentGroup () { return currentGroup; },
getCurConfig () { return curConfig; },
getNsMap () { return nsMap; },
getSvgOption () { return saveOptions; },
setSvgOption (key, value) { saveOptions[key] = value; },
getSvgOptionApply () { return saveOptions.apply; },
getSvgOptionImages () { return saveOptions.images; },
getEncodableImages (key) { return encodableImages[key]; },
setEncodableImages (key, value) { encodableImages[key] = value; },
getUIStrings() { return uiStrings; },
getCurrentGroup() { return currentGroup; },
getCurConfig() { return curConfig; },
getNsMap() { return nsMap; },
getSvgOption() { return saveOptions; },
setSvgOption(key, value) { saveOptions[key] = value; },
getSvgOptionApply() { return saveOptions.apply; },
getSvgOptionImages() { return saveOptions.images; },
getEncodableImages(key) { return encodableImages[key]; },
setEncodableImages(key, value) { encodableImages[key] = value; },
call,
getDOMDocument () { return svgdoc; },
getVisElems () { return visElems; },
getIdPrefix () { return idprefix; },
setCurrentZoom (value) { currentZoom = value; },
getImportIds (key) { return importIds[key]; },
setImportIds (key, value) { importIds[key] = value; },
setRemovedElements (key, value) { removedElements[key] = value; },
setSVGContent (value) { svgcontent = value; },
getrefAttrs () { return refAttrs; },
getcanvg () { return canvg; },
getDOMDocument() { return svgdoc; },
getVisElems() { return visElems; },
getIdPrefix() { return idprefix; },
setCurrentZoom(value) { currentZoom = value; },
getImportIds(key) { return importIds[key]; },
setImportIds(key, value) { importIds[key] = value; },
setRemovedElements(key, value) { removedElements[key] = value; },
setSVGContent(value) { svgcontent = value; },
getrefAttrs() { return refAttrs; },
getcanvg() { return canvg; },
addCommandToHistory
}
);
@ -1619,10 +1653,11 @@ class SvgCanvas {
*/
{
pathActions,
getCurrentGroup () {
getDataStorage,
getCurrentGroup() {
return currentGroup;
},
setCurrentGroup (cg) {
setCurrentGroup(cg) {
currentGroup = cg;
},
getSelectedElements,
@ -1636,7 +1671,7 @@ class SvgCanvas {
* @fires module:svgcanvas.SvgCanvas#event:changed
* @returns {void}
*/
changeSVGContent () {
changeSVGContent() {
call('changed', [svgcontent]);
}
}
@ -1704,15 +1739,16 @@ class SvgCanvas {
getSelectedElements,
call,
changeSelectedAttributeNoUndoMethod,
getDOMDocument () { return svgdoc; },
getCanvas () { return canvas; },
setCanvas (key, value) { canvas[key] = value; },
setCurrentZoom (value) { currentZoom = value; },
setCurProperties (key, value) { curProperties[key] = value; },
getCurProperties (key) { return curProperties[key]; },
setCurShape (key, value) { curShape[key] = value; },
getCurText (key) { return curText[key]; },
setCurText (key, value) { curText[key] = value; }
getDOMDocument() { return svgdoc; },
getCanvas() { return canvas; },
getDataStorage,
setCanvas(key, value) { canvas[key] = value; },
setCurrentZoom(value) { currentZoom = value; },
setCurProperties(key, value) { curProperties[key] = value; },
getCurProperties(key) { return curProperties[key]; },
setCurShape(key, value) { curShape[key] = value; },
getCurText(key) { return curText[key]; },
setCurText(key, value) { curText[key] = value; }
}
);
@ -1830,7 +1866,7 @@ class SvgCanvas {
* position in the editor's canvas.
*/
this.getOffset = function () {
return {x: svgcontent.getAttribute('x'), y: svgcontent.getAttribute('y')};
return { x: svgcontent.getAttribute('x'), y: svgcontent.getAttribute('y') };
};
/**
@ -2153,13 +2189,13 @@ class SvgCanvas {
* @implements {module:elem-get-set.elemInit}
*/
{
getCanvas () { return canvas; },
getCurCommand () { return curCommand; },
setCurCommand (value) { curCommand = value; },
getFilter () { return filter; },
setFilter (value) { filter = value; },
getFilterHidden () { return filterHidden; },
setFilterHidden (value) { filterHidden = value; },
getCanvas() { return canvas; },
getCurCommand() { return curCommand; },
setCurCommand(value) { curCommand = value; },
getFilter() { return filter; },
setFilter(value) { filter = value; },
getFilterHidden() { return filterHidden; },
setFilterHidden(value) { filterHidden = value; },
changeSelectedAttributeNoUndoMethod,
changeSelectedAttributeMethod,
isWebkit,
@ -2424,17 +2460,17 @@ class SvgCanvas {
flashStorage,
call,
getIntersectionList,
setCurBBoxes (value) { curBBoxes = value; },
setCurBBoxes(value) { curBBoxes = value; },
getSVGRoot,
gettingSelectorManager () { return selectorManager; },
gettingSelectorManager() { return selectorManager; },
getCurrentZoom,
getDrawing () { return getCurrentDrawing(); },
getCurrentGroup () { return currentGroup; },
getDrawing() { return getCurrentDrawing(); },
getCurrentGroup() { return currentGroup; },
addToSelection,
getContentW () { return canvas.contentW; },
getContentH () { return canvas.contentH; },
getClipboardID () { return CLIPBOARD_ID; },
getDOMDocument () { return svgdoc; },
getContentW() { return canvas.contentW; },
getContentH() { return canvas.contentH; },
getClipboardID() { return CLIPBOARD_ID; },
getDOMDocument() { return svgdoc; },
clearSelection,
getNextId,
selectOnly,
@ -2442,8 +2478,9 @@ class SvgCanvas {
setUseData,
convertGradients,
getSVGContent,
getCanvas () { return canvas; },
getVisElems () { return visElems; }
getCanvas() { return canvas; },
getDataStorage,
getVisElems() { return visElems; }
}
);
@ -2473,7 +2510,7 @@ class SvgCanvas {
* Flash the clipboard data momentarily on localStorage so all tabs can see.
* @returns {void}
*/
function flashStorage () {
function flashStorage() {
const data = sessionStorage.getItem(CLIPBOARD_ID);
localStorage.setItem(CLIPBOARD_ID, data);
setTimeout(function () {
@ -2486,7 +2523,7 @@ class SvgCanvas {
* @param {!Event} ev Storage event.
* @returns {void}
*/
function storageChange (ev) {
function storageChange(ev) {
if (!ev.newValue) return; // This is a call from removeItem.
if (ev.key === CLIPBOARD_ID + '_startup') {
// Another tab asked for our sessionStorage.
@ -2515,13 +2552,13 @@ class SvgCanvas {
* paste element functionality
*/
pasteInit(
/**
* @implements {module:event.eventContext_}
*/
/**
* @implements {module:event.eventContext_}
*/
{
getCanvas () { return canvas; },
getClipBoardID () { return CLIPBOARD_ID; },
getLastClickPoint (key) { return lastClickPoint[key]; },
getCanvas() { return canvas; },
getClipBoardID() { return CLIPBOARD_ID; },
getLastClickPoint(key) { return lastClickPoint[key]; },
addCommandToHistory,
restoreRefElems
}

View File

@ -8,9 +8,9 @@
*/
import jQueryPluginSVG from './jQuery.attr.js'; // Needed for SVG attribute setting and array form with `attr`
import {NS} from '../common/namespaces.js';
import {getTransformList} from './svgtransformlist.js';
import {setUnitAttr, getTypeMap} from '../common/units.js';
import { NS } from '../common/namespaces.js';
import { getTransformList } from './svgtransformlist.js';
import { setUnitAttr, getTypeMap } from '../common/units.js';
import {
hasMatrixTransform, transformListToTransform, transformBox
} from './math.js';
@ -18,7 +18,7 @@ import {
isWebkit, supportsHVLineContainerBBox, supportsPathBBox, supportsXpath,
supportsSelectors
} from '../common/browser.js';
import {getClosest} from '../editor/components/jgraduate/Util.js';
import { getClosest } from '../editor/components/jgraduate/Util.js';
// Constants
const $ = jQueryPluginSVG(jQuery);
@ -138,7 +138,7 @@ export const toXml = function (str) {
* @param {string} str - The string to be converted
* @returns {string} The converted string
*/
export function fromXml (str) {
export function fromXml(str) {
const p = document.createElement('p');
// eslint-disable-next-line no-unsanitized/property
p.innerHTML = str;
@ -158,7 +158,7 @@ export function fromXml (str) {
* @param {string} input
* @returns {string} Base64 output
*/
export function encode64 (input) {
export function encode64(input) {
// base64 strings are 4/3 larger than the original string
input = encodeUTF8(input); // convert non-ASCII characters
// input = convertToXMLReferences(input);
@ -204,7 +204,7 @@ export function encode64 (input) {
* @param {string} input Base64-encoded input
* @returns {string} Decoded output
*/
export function decode64 (input) {
export function decode64(input) {
if (window.atob) {
return decodeUTF8(window.atob(input));
}
@ -244,7 +244,7 @@ export function decode64 (input) {
* @param {string} argString
* @returns {string}
*/
export function decodeUTF8 (argString) {
export function decodeUTF8(argString) {
return decodeURIComponent(escape(argString));
}
@ -281,7 +281,7 @@ export const dataURLToObjectURL = function (dataurl) {
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], {type: mime});
const blob = new Blob([u8arr], { type: mime });
return URL.createObjectURL(blob);
};
@ -305,7 +305,7 @@ export const blankPageObjectURL = (function () {
if (typeof Blob === 'undefined') {
return '';
}
const blob = new Blob(['<html><head><title>SVG-edit</title></head><body>&nbsp;</body></html>'], {type: 'text/html'});
const blob = new Blob(['<html><head><title>SVG-edit</title></head><body>&nbsp;</body></html>'], { type: 'text/html' });
return createObjectURL(blob);
})();
@ -368,8 +368,8 @@ export const text2xml = function (sXML) {
* @param {SVGRect} bbox - a SVGRect
* @returns {module:utilities.BBoxObject} An object with properties names x, y, width, height.
*/
export const bboxToObj = function ({x, y, width, height}) {
return {x, y, width, height};
export const bboxToObj = function ({ x, y, width, height }) {
return { x, y, width, height };
};
/**
@ -571,24 +571,24 @@ export const getPathBBox = function (path) {
* @param {Element} selected - Container or `<use>` DOM element
* @returns {DOMRect} Bounding box object
*/
function groupBBFix (selected) {
function groupBBFix(selected) {
if (supportsHVLineContainerBBox()) {
try { return selected.getBBox(); } catch (e) {/* empty */}
try { return selected.getBBox(); } catch (e) {/* empty */ }
}
const ref = dataStorage.get(selected, 'ref');
const ref = editorContext_.getDataStorage().get(selected, 'ref');
let matched = null;
let ret, copy;
if (ref) {
let elements = [];
Array.prototype.forEach.call(ref.children, function(el, i){
const elem = el.cloneNode(true);
elem.setAttribute('visibility', 'hidden');
svgroot_.appendChild(elem);
copy.push(elem);
if(['line', 'path'].indexOf(elem.tagName) !== -1){
elements.push(elem);
}
Array.prototype.forEach.call(ref.children, function (el, i) {
const elem = el.cloneNode(true);
elem.setAttribute('visibility', 'hidden');
svgroot_.appendChild(elem);
copy.push(elem);
if (['line', 'path'].indexOf(elem.tagName) !== -1) {
elements.push(elem);
}
});
matched = (elements.length) ? elements : null;
} else {
@ -597,7 +597,7 @@ function groupBBFix (selected) {
let issue = false;
if (matched.length) {
Array.prototype.forEach.call(matched, function(match, i){
Array.prototype.forEach.call(matched, function (match, i) {
const bb = match.getBBox();
if (!bb.width || !bb.height) {
issue = true;
@ -632,70 +632,70 @@ export const getBBox = function (elem) {
let ret = null;
switch (elname) {
case 'text':
if (selected.textContent === '') {
selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox();
selected.textContent = '';
} else if (selected.getBBox) {
ret = selected.getBBox();
}
break;
case 'path':
if (!supportsPathBBox()) {
ret = getPathBBox(selected);
} else if (selected.getBBox) {
ret = selected.getBBox();
}
break;
case 'g':
case 'a':
ret = groupBBFix(selected);
break;
default:
case 'text':
if (selected.textContent === '') {
selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox();
selected.textContent = '';
} else if (selected.getBBox) {
ret = selected.getBBox();
}
break;
case 'path':
if (!supportsPathBBox()) {
ret = getPathBBox(selected);
} else if (selected.getBBox) {
ret = selected.getBBox();
}
break;
case 'g':
case 'a':
ret = groupBBFix(selected);
break;
default:
if (elname === 'use') {
ret = groupBBFix(selected); // , true);
}
if (elname === 'use' || (elname === 'foreignObject' && isWebkit())) {
if (!ret) { ret = selected.getBBox(); }
// This is resolved in later versions of webkit, perhaps we should
// have a featured detection for correct 'use' behavior?
// ——————————
if (!isWebkit()) {
const {x, y, width, height} = ret;
const bb = {
width,
height,
x: x + Number.parseFloat(selected.getAttribute('x') || 0),
y: y + Number.parseFloat(selected.getAttribute('y') || 0)
};
ret = bb;
if (elname === 'use') {
ret = groupBBFix(selected); // , true);
}
} else if (visElemsArr.includes(elname)) {
if (selected) {
try {
ret = selected.getBBox();
} catch (err) {
// tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268
// Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835
const extent = selected.getExtentOfChar(0); // pos+dimensions of the first glyph
const width = selected.getComputedTextLength(); // width of the tspan
ret = {
x: extent.x,
y: extent.y,
if (elname === 'use' || (elname === 'foreignObject' && isWebkit())) {
if (!ret) { ret = selected.getBBox(); }
// This is resolved in later versions of webkit, perhaps we should
// have a featured detection for correct 'use' behavior?
// ——————————
if (!isWebkit()) {
const { x, y, width, height } = ret;
const bb = {
width,
height: extent.height
height,
x: x + Number.parseFloat(selected.getAttribute('x') || 0),
y: y + Number.parseFloat(selected.getAttribute('y') || 0)
};
ret = bb;
}
} else {
// Check if element is child of a foreignObject
const fo = getClosest(selected.parentNode, 'foreignObject');
if (fo.length && fo[0].getBBox) {
ret = fo[0].getBBox();
} else if (visElemsArr.includes(elname)) {
if (selected) {
try {
ret = selected.getBBox();
} catch (err) {
// tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268
// Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835
const extent = selected.getExtentOfChar(0); // pos+dimensions of the first glyph
const width = selected.getComputedTextLength(); // width of the tspan
ret = {
x: extent.x,
y: extent.y,
width,
height: extent.height
};
}
} else {
// Check if element is child of a foreignObject
const fo = getClosest(selected.parentNode, 'foreignObject');
if (fo.length && fo[0].getBBox) {
ret = fo[0].getBBox();
}
}
}
}
}
if (ret) {
ret = bboxToObj(ret);
@ -743,76 +743,75 @@ export const getPathDFromElement = function (elem) {
let num = 1.81;
let d, a, rx, ry;
switch (elem.tagName) {
case 'ellipse':
case 'circle': {
const rx = elem.getAttribute('rx');
const ry = elem.getAttribute('ry');
const cx = elem.getAttribute('cx');
const cy = elem.getAttribute('cy');
if (elem.tagName === 'circle') {
ry = elem.getAttribute('r');
rx = ry;
}
d = getPathDFromSegments([
['M', [(cx - rx), (cy)]],
['C', [(cx - rx), (cy - ry / num), (cx - rx / num), (cy - ry), (cx), (cy - ry)]],
['C', [(cx + rx / num), (cy - ry), (cx + rx), (cy - ry / num), (cx + rx), (cy)]],
['C', [(cx + rx), (cy + ry / num), (cx + rx / num), (cy + ry), (cx), (cy + ry)]],
['C', [(cx - rx / num), (cy + ry), (cx - rx), (cy + ry / num), (cx - rx), (cy)]],
['Z', []]
]);
break;
} case 'path':
d = elem.getAttribute('d');
break;
case 'line':
const x1 = elem.getAttribute('x1');
const y1 = elem.getAttribute('y1');
const x2 = elem.getAttribute('x2');
const y2 = elem.getAttribute('y2');
d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2;
break;
case 'polyline':
d = 'M' + elem.getAttribute('points');
break;
case 'polygon':
d = 'M' + elem.getAttribute('points') + ' Z';
break;
case 'rect': {
const rx = elem.getAttribute('rx');
const ry = elem.getAttribute('ry');
const b = elem.getBBox();
const {x, y} = b,
w = b.width,
h = b.height;
num = 4 - num; // Why? Because!
d = (!rx && !ry)
// Regular rect
? getPathDFromSegments([
['M', [x, y]],
['L', [x + w, y]],
['L', [x + w, y + h]],
['L', [x, y + h]],
['L', [x, y]],
['Z', []]
])
: getPathDFromSegments([
['M', [x, y + ry]],
['C', [x, y + ry / num, x + rx / num, y, x + rx, y]],
['L', [x + w - rx, y]],
['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]],
['L', [x + w, y + h - ry]],
['C', [x + w, y + h - ry / num, x + w - rx / num, y + h, x + w - rx, y + h]],
['L', [x + rx, y + h]],
['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]],
['L', [x, y + ry]],
case 'ellipse':
case 'circle': {
rx = elem.getAttribute('rx');
ry = elem.getAttribute('ry');
const cx = elem.getAttribute('cx');
const cy = elem.getAttribute('cy');
if (elem.tagName === 'circle' && elem.hasAttribute('r')) {
ry = elem.getAttribute('r');
rx = ry;
}
d = getPathDFromSegments([
['M', [(cx - rx), (cy)]],
['C', [(cx - rx), (cy - ry / num), (cx - rx / num), (cy - ry), (cx), (cy - ry)]],
['C', [(cx + rx / num), (cy - ry), (cx + rx), (cy - ry / num), (cx + rx), (cy)]],
['C', [(cx + rx), (cy + ry / num), (cx + rx / num), (cy + ry), (cx), (cy + ry)]],
['C', [(cx - rx / num), (cy + ry), (cx - rx), (cy + ry / num), (cx - rx), (cy)]],
['Z', []]
]);
break;
} default:
break;
break;
} case 'path':
d = elem.getAttribute('d');
break;
case 'line':
const x1 = elem.getAttribute('x1');
const y1 = elem.getAttribute('y1');
const x2 = elem.getAttribute('x2');
const y2 = elem.getAttribute('y2');
d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2;
break;
case 'polyline':
d = 'M' + elem.getAttribute('points');
break;
case 'polygon':
d = 'M' + elem.getAttribute('points') + ' Z';
break;
case 'rect': {
rx = elem.getAttribute('rx');
ry = elem.getAttribute('ry');
const b = elem.getBBox();
const { x, y } = b,
w = b.width,
h = b.height;
num = 4 - num; // Why? Because!
d = (!rx && !ry)
// Regular rect
? getPathDFromSegments([
['M', [x, y]],
['L', [x + w, y]],
['L', [x + w, y + h]],
['L', [x, y + h]],
['L', [x, y]],
['Z', []]
])
: getPathDFromSegments([
['M', [x, y + ry]],
['C', [x, y + ry / num, x + rx / num, y, x + rx, y]],
['L', [x + w - rx, y]],
['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]],
['L', [x + w, y + h - ry]],
['C', [x + w, y + h - ry / num, x + w - rx / num, y + h, x + w - rx, y + h]],
['L', [x + rx, y + h]],
['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]],
['L', [x, y + ry]],
['Z', []]
]);
break;
} default:
break;
}
return d;
@ -856,7 +855,7 @@ export const getBBoxOfElementAsPath = function (elem, addSVGElementFromJson, pat
path.setAttribute('transform', eltrans);
}
const {parentNode} = elem;
const { parentNode } = elem;
if (elem.nextSibling) {
elem.before(path);
} else {
@ -914,8 +913,8 @@ export const convertToPath = function (
path.setAttribute('transform', eltrans);
}
const {id} = elem;
const {parentNode} = elem;
const { id } = elem;
const { parentNode } = elem;
if (elem.nextSibling) {
elem.before(path);
} else {
@ -936,7 +935,7 @@ export const convertToPath = function (
}
}
const {nextSibling} = elem;
const { nextSibling } = elem;
batchCmd.addSubCommand(new hstry.RemoveElementCommand(elem, nextSibling, parent));
batchCmd.addSubCommand(new hstry.InsertElementCommand(path));
@ -975,7 +974,7 @@ export const convertToPath = function (
* @param {boolean} hasAMatrixTransform - True if there is a matrix transform
* @returns {boolean} True if the bbox can be optimized.
*/
function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) {
function bBoxCanBeOptimizedOverNativeGetBBox(angle, hasAMatrixTransform) {
const angleModulo90 = angle % 90;
const closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99;
const closeTo0 = angleModulo90 > -0.001 && angleModulo90 < 0.001;
@ -1026,7 +1025,7 @@ export const getBBoxWithTransform = function (elem, addSVGElementFromJson, pathA
}
if (!goodBb) {
const {matrix} = transformListToTransform(tlist);
const { matrix } = transformListToTransform(tlist);
bb = transformBox(bb.x, bb.y, bb.width, bb.height, matrix).aabox;
// Old technique that was exceedingly slow with large documents.
@ -1053,7 +1052,7 @@ export const getBBoxWithTransform = function (elem, addSVGElementFromJson, pathA
* @todo This is problematic with large stroke-width and, for example, a single
* horizontal line. The calculated BBox extends way beyond left and right sides.
*/
function getStrokeOffsetForBBox (elem) {
function getStrokeOffsetForBBox(elem) {
const sw = elem.getAttribute('stroke-width');
return (!isNaN(sw) && elem.getAttribute('stroke') !== 'none') ? sw / 2 : 0;
}
@ -1141,7 +1140,7 @@ export const getVisibleElements = function (parentElement) {
const contentElems = [];
const childrens = parentElement.children
Array.prototype.forEach.call(childrens, function(elem, i){
Array.prototype.forEach.call(childrens, function (elem, i) {
if (elem.getBBox) {
contentElems.push(elem);
}
@ -1324,8 +1323,8 @@ export const snapToGrid = function (value) {
*/
export const preventClickDefault = function (img) {
const elements = document.querySelectorAll("img");
Array.from(elements).forEach(function(element) {
element.addEventListener('click', function(e) {
Array.from(elements).forEach(function (element) {
element.addEventListener('click', function (e) {
e.preventDefault();
});
});