refactor with Panel classes

master
JFH 2021-03-14 21:16:35 +01:00
parent cb52db7324
commit 57e7fe316b
17 changed files with 2061 additions and 2231 deletions

View File

@ -34,9 +34,7 @@ describe('Basic Module', function () {
workarea.append(svgcanvas);
const toolsLeft = document.createElement('div');
toolsLeft.id = 'tools_left';
const toolsFlyout = document.createElement('div');
toolsFlyout.id = 'tools_flyout';
svgEditor.append(workarea, toolsLeft, toolsFlyout);
document.body.append(svgEditor);

View File

@ -6,10 +6,7 @@ module.exports = {
"functions": 45,
exclude: [
'editor/jquery.min.js',
'editor/jgraduate/**',
'editor/svgicons/**',
'editor/dragmove/**',
'editor/spinbtn'
'editor/jgraduate/**'
],
"reporter": [
"json-summary",

115
package-lock.json generated
View File

@ -9,7 +9,6 @@
"license": "(MIT AND Apache-2.0 AND ISC AND LGPL-3.0-or-later AND X11)",
"dependencies": {
"@babel/polyfill": "7.12.1",
"@knadh/dragmove": "^0.1.2",
"@web/dev-server-rollup": "0.3.2",
"canvg": "3.0.7",
"core-js": "3.9.1",
@ -17,7 +16,7 @@
"jspdf": "2.3.1",
"pathseg": "1.2.0",
"regenerator-runtime": "0.13.7",
"rollup-plugin-polyfill-node": "^0.6.1",
"rollup-plugin-polyfill-node": "^0.6.2",
"svg2pdf.js": "2.1.0"
},
"devDependencies": {
@ -49,7 +48,7 @@
"cypress-multi-reporters": "1.4.0",
"cypress-plugin-snapshots": "1.4.4",
"deparam": "git+https://github.com/brettz9/deparam.git#updates",
"eslint": "^7.21.0",
"eslint": "^7.22.0",
"eslint-config-standard": "16.0.2",
"eslint-plugin-array-func": "3.1.7",
"eslint-plugin-chai-expect": "2.2.0",
@ -74,7 +73,7 @@
"imageoptim-cli": "3.0.2",
"jamilih": "0.54.0",
"jsdoc": "3.6.6",
"mocha": "8.3.1",
"mocha": "8.3.2",
"mocha-badge-generator": "0.9.0",
"mochawesome": "6.2.2",
"mochawesome-merge": "4.2.0",
@ -90,7 +89,7 @@
"remark-lint-ordered-list-marker-value": "2.0.1",
"requirejs": "2.3.6",
"rimraf": "3.0.2",
"rollup": "2.41.0",
"rollup": "2.41.2",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.1",
"rollup-plugin-node-polyfills": "0.2.1",
@ -3245,11 +3244,6 @@
"regenerator-runtime": "^0.13.3"
}
},
"node_modules/@knadh/dragmove": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@knadh/dragmove/-/dragmove-0.1.2.tgz",
"integrity": "sha512-OxfFFHqrpenz9oVxi8AngzrF7+aoGZCvNOGOVlxu+6UlG0dW68hJJeNt7RvVlg3dNK6uaHsju+bH+/NXaRUMTQ=="
},
"node_modules/@mdn/browser-compat-data": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz",
@ -9861,9 +9855,9 @@
}
},
"node_modules/eslint": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz",
"integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==",
"version": "7.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz",
"integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==",
"dev": true,
"dependencies": {
"@babel/code-frame": "7.12.11",
@ -9883,7 +9877,7 @@
"file-entry-cache": "^6.0.1",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"globals": "^13.6.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@ -9891,7 +9885,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.20",
"lodash": "^4.17.21",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@ -9909,6 +9903,9 @@
},
"engines": {
"node": "^10.12.0 || >=12.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-config-standard": {
@ -10978,15 +10975,18 @@
}
},
"node_modules/eslint/node_modules/globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz",
"integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==",
"dev": true,
"dependencies": {
"type-fest": "^0.8.1"
"type-fest": "^0.20.2"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/has-flag": {
@ -11149,6 +11149,18 @@
"node": ">= 0.8.0"
}
},
"node_modules/eslint/node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -16045,9 +16057,9 @@
}
},
"node_modules/mocha": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.1.tgz",
"integrity": "sha512-5SBMxANWqOv5bw3Hx+HVgaWlcWcFEQDUdaUAr1AUU+qwtx6cowhn7gEDT/DwQP7uYxnvShdUOVLbTYAHOEGfDQ==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz",
"integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==",
"dev": true,
"dependencies": {
"@ungap/promise-all-settled": "1.1.2",
@ -19845,9 +19857,9 @@
}
},
"node_modules/rollup": {
"version": "2.41.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.0.tgz",
"integrity": "sha512-Gk76XHTggulWPH95q8V62bw6uqDH6UGvbD6LOa3QUyhuMF3eOuaeDHR7SLm1T9faitkpNrqzUAVYx47klcMnlA==",
"version": "2.41.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.2.tgz",
"integrity": "sha512-6u8fJJXJx6fmvKrAC9DHYZgONvSkz8S9b/VFBjoQ6dkKdHyPpPbpqiNl2Bao9XBzDHpq672X6sGZ9G1ZBqAHMg==",
"bin": {
"rollup": "dist/bin/rollup"
},
@ -20122,9 +20134,9 @@
}
},
"node_modules/rollup-plugin-polyfill-node": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.6.1.tgz",
"integrity": "sha512-r+A3VUWVMm5tiWoQPpe9BOPycN+G+XgXQGmOyDn6REPaZNvNz16rOQ/3X9U6xE7kQAlXQCnec9L5i0fFytNYCQ==",
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.6.2.tgz",
"integrity": "sha512-gMCVuR0zsKq0jdBn8pSXN1Ejsc458k2QsFFvQdbHoM0Pot5hEnck+pBP/FDwFS6uAi77pD3rDTytsaUStsOMlA==",
"dependencies": {
"@rollup/plugin-inject": "^4.0.0"
}
@ -26632,11 +26644,6 @@
"regenerator-runtime": "^0.13.3"
}
},
"@knadh/dragmove": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@knadh/dragmove/-/dragmove-0.1.2.tgz",
"integrity": "sha512-OxfFFHqrpenz9oVxi8AngzrF7+aoGZCvNOGOVlxu+6UlG0dW68hJJeNt7RvVlg3dNK6uaHsju+bH+/NXaRUMTQ=="
},
"@mdn/browser-compat-data": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz",
@ -32351,9 +32358,9 @@
}
},
"eslint": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz",
"integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==",
"version": "7.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz",
"integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==",
"dev": true,
"requires": {
"@babel/code-frame": "7.12.11",
@ -32373,7 +32380,7 @@
"file-entry-cache": "^6.0.1",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"globals": "^13.6.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
@ -32381,7 +32388,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.20",
"lodash": "^4.17.21",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@ -32549,12 +32556,12 @@
}
},
"globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz",
"integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==",
"dev": true,
"requires": {
"type-fest": "^0.8.1"
"type-fest": "^0.20.2"
}
},
"has-flag": {
@ -32675,6 +32682,12 @@
"prelude-ls": "^1.2.1"
}
},
"type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -37425,9 +37438,9 @@
}
},
"mocha": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.1.tgz",
"integrity": "sha512-5SBMxANWqOv5bw3Hx+HVgaWlcWcFEQDUdaUAr1AUU+qwtx6cowhn7gEDT/DwQP7uYxnvShdUOVLbTYAHOEGfDQ==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz",
"integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==",
"dev": true,
"requires": {
"@ungap/promise-all-settled": "1.1.2",
@ -40497,9 +40510,9 @@
}
},
"rollup": {
"version": "2.41.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.0.tgz",
"integrity": "sha512-Gk76XHTggulWPH95q8V62bw6uqDH6UGvbD6LOa3QUyhuMF3eOuaeDHR7SLm1T9faitkpNrqzUAVYx47klcMnlA==",
"version": "2.41.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.2.tgz",
"integrity": "sha512-6u8fJJXJx6fmvKrAC9DHYZgONvSkz8S9b/VFBjoQ6dkKdHyPpPbpqiNl2Bao9XBzDHpq672X6sGZ9G1ZBqAHMg==",
"requires": {
"fsevents": "~2.3.1"
},
@ -40716,9 +40729,9 @@
}
},
"rollup-plugin-polyfill-node": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.6.1.tgz",
"integrity": "sha512-r+A3VUWVMm5tiWoQPpe9BOPycN+G+XgXQGmOyDn6REPaZNvNz16rOQ/3X9U6xE7kQAlXQCnec9L5i0fFytNYCQ==",
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.6.2.tgz",
"integrity": "sha512-gMCVuR0zsKq0jdBn8pSXN1Ejsc458k2QsFFvQdbHoM0Pot5hEnck+pBP/FDwFS6uAi77pD3rDTytsaUStsOMlA==",
"requires": {
"@rollup/plugin-inject": "^4.0.0"
}

View File

@ -107,7 +107,6 @@
],
"dependencies": {
"@babel/polyfill": "7.12.1",
"@knadh/dragmove": "^0.1.2",
"@web/dev-server-rollup": "0.3.2",
"canvg": "3.0.7",
"core-js": "3.9.1",
@ -115,7 +114,7 @@
"jspdf": "2.3.1",
"pathseg": "1.2.0",
"regenerator-runtime": "0.13.7",
"rollup-plugin-polyfill-node": "^0.6.1",
"rollup-plugin-polyfill-node": "^0.6.2",
"svg2pdf.js": "2.1.0"
},
"devDependencies": {
@ -147,7 +146,7 @@
"cypress-multi-reporters": "1.4.0",
"cypress-plugin-snapshots": "1.4.4",
"deparam": "git+https://github.com/brettz9/deparam.git#updates",
"eslint": "^7.21.0",
"eslint": "^7.22.0",
"eslint-config-standard": "16.0.2",
"eslint-plugin-array-func": "3.1.7",
"eslint-plugin-chai-expect": "2.2.0",
@ -172,7 +171,7 @@
"imageoptim-cli": "3.0.2",
"jamilih": "0.54.0",
"jsdoc": "3.6.6",
"mocha": "8.3.1",
"mocha": "8.3.2",
"mocha-badge-generator": "0.9.0",
"mochawesome": "6.2.2",
"mochawesome-merge": "4.2.0",
@ -188,7 +187,7 @@
"remark-lint-ordered-list-marker-value": "2.0.1",
"requirejs": "2.3.6",
"rimraf": "3.0.2",
"rollup": "2.41.0",
"rollup": "2.41.2",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.1",
"rollup-plugin-node-polyfills": "0.2.1",

View File

@ -30,6 +30,10 @@ import {
} from './locale.js';
import EditorStartup from './EditorStartup.js';
import LeftPanel from './panels/LeftPanel.js';
import TopPanel from './panels/TopPanel.js';
import BottomPanel from './panels/BottomPanel.js';
import LayersPanel from './panels/LayersPanel.js';
const {$id, $qa, isNullish, encode64, decode64, blankPageObjectURL} = SvgCanvas;
@ -131,6 +135,10 @@ class Editor extends EditorStartup {
{key: modKey + 'c', fn () { curObj.copySelected(); }},
{key: modKey + 'v', fn () { curObj.pasteInCenter(); }}
];
this.leftPanel = new LeftPanel(this);
this.bottomPanel = new BottomPanel(this);
this.topPanel = new TopPanel(this);
this.layersPanel = new LayersPanel(this);
}
/**
*
@ -391,23 +399,6 @@ class Editor extends EditorStartup {
}
}
/**
*
* @param {Element} opt
* @param {boolean} changeElem
* @returns {void}
*/
setStrokeOpt (opt, changeElem) {
const {id} = opt;
const bits = id.split('_');
const [pre, val] = bits;
if (changeElem) {
this.svgCanvas.setStrokeAttr('stroke-' + pre, val);
}
$(opt).addClass('current').siblings().removeClass('current');
}
/**
* Set a selected image's URL.
* @function module:SVGthis.setImageURL
@ -532,71 +523,6 @@ class Editor extends EditorStartup {
}
}
/**
* Updates the toolbar (colors, opacity, etc) based on the selected element.
* This function also updates the opacity and id elements that are in the
* context panel.
* @returns {void}
*/
updateToolbar () {
let i, len;
if (!isNullish(this.selectedElement)) {
switch (this.selectedElement.tagName) {
case 'use':
case 'image':
case 'foreignObject':
break;
case 'g':
case 'a': {
// Look for common styles
const childs = this.selectedElement.getElementsByTagName('*');
let gWidth = null;
for (i = 0, len = childs.length; i < len; i++) {
const swidth = childs[i].getAttribute('stroke-width');
if (i === 0) {
gWidth = swidth;
} else if (gWidth !== swidth) {
gWidth = null;
}
}
$('#stroke_width').val(gWidth === null ? '' : gWidth);
this.bottomPanelHandlers.updateColorpickers(true);
break;
} default: {
this.bottomPanelHandlers.updateColorpickers(true);
$('#stroke_width').val(this.selectedElement.getAttribute('stroke-width') || 1);
$('#stroke_style').val(this.selectedElement.getAttribute('stroke-dasharray') || 'none');
let attr = this.selectedElement.getAttribute('stroke-linejoin') || 'miter';
if ($('#linejoin_' + attr).length) {
this.setStrokeOpt($('#linejoin_' + attr)[0]);
}
attr = this.selectedElement.getAttribute('stroke-linecap') || 'butt';
if ($('#linecap_' + attr).length) {
this.setStrokeOpt($('#linecap_' + attr)[0]);
}
}
}
}
// All elements including image and group have opacity
if (!isNullish(this.selectedElement)) {
const opacPerc = (this.selectedElement.getAttribute('opacity') || 1.0) * 100;
$id('opacity').value = opacPerc;
$id('elem_id').value = this.selectedElement.id;
$id('elem_class').value =
(this.selectedElement.getAttribute('class') !== null) ? this.selectedElement.getAttribute('class') : '';
}
this.bottomPanelHandlers.updateToolButtonState();
}
/**
*
* @returns {void}
@ -639,19 +565,19 @@ class Editor extends EditorStartup {
selectedChanged (win, elems) {
const mode = this.svgCanvas.getMode();
if (mode === 'select') {
this.leftPanelHandlers.clickSelect();
this.leftPanel.clickSelect();
}
const isNode = mode === 'pathedit';
// if this.elems[1] is present, then we have more than one element
this.selectedElement = (elems.length === 1 || isNullish(elems[1]) ? elems[0] : null);
this.multiselected = (elems.length >= 2 && !isNullish(elems[1]));
if (!isNullish(this.selectedElement) && !isNode) {
this.updateToolbar();
this.topPanel.update();
} // if (!isNullish(elem))
// Deal with pathedit mode
this.togglePathEditMode(isNode, elems);
this.topPanelHandlers.updateContextPanel();
this.topPanel.updateContextPanel();
this.svgCanvas.runExtensions('selectedChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_selectedChanged} */ {
elems,
selectedElement: this.selectedElement,
@ -704,7 +630,7 @@ class Editor extends EditorStartup {
elementChanged (win, elems) {
const mode = this.svgCanvas.getMode();
if (mode === 'select') {
this.leftPanelHandlers.clickSelect();
this.leftPanel.clickSelect();
}
elems.forEach((elem) => {
@ -732,11 +658,11 @@ class Editor extends EditorStartup {
// we tell it to skip focusing the text control if the
// text element was previously in focus
this.topPanelHandlers.updateContextPanel();
this.topPanel.updateContextPanel();
// In the event a gradient was flipped:
if (this.selectedElement && mode === 'select') {
this.bottomPanelHandlers.updateColorpickers();
this.bottomPanel.updateColorpickers();
}
this.svgCanvas.runExtensions('elementChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementChanged} */ {
@ -798,7 +724,7 @@ class Editor extends EditorStartup {
if (this.svgCanvas.getMode() === 'zoom' && bb.width) {
// Go to select if a zoom box was drawn
this.leftPanelHandlers.clickSelect();
this.leftPanel.clickSelect();
}
this.zoomDone();
@ -832,183 +758,6 @@ class Editor extends EditorStartup {
this.updateTitle();
}
/**
* Makes sure the current selected paint is available to work with.
* @returns {void}
*/
/* prepPaints () {
paintBox.fill.prep();
paintBox.stroke.prep();
} */
/**
*
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
setFlyoutPositions () {
$('.tools_flyout').each(function () {
const shower = $('#' + this.id + '_show');
const {left, top} = shower.offset();
const w = shower.outerWidth();
this.css({left: (left + w) * editor.tool_scale, top});
});
}
/**
*
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
setFlyoutTitles () {
$('.tools_flyout').each(function () {
const shower = $('#' + this.id + '_show');
if (shower.data('isLibrary')) {
return;
}
const tooltips = this.children().map(function () {
return this.title;
}).get();
shower[0].title = tooltips.join(' / ');
});
}
/**
* @param {PlainObject<string, module:SVGEditor.ToolButton>} holders Key is a selector
* @returns {void}
*/
setupFlyouts (holders) {
const allHolders = {};
const currentObj = this;
$.each(holders, function (holdSel, btnOpts) {
if (!allHolders[holdSel]) {
allHolders[holdSel] = [];
}
allHolders[holdSel].push(...btnOpts);
const buttons = $(holdSel).children().not('.tool_button_evt_handled');
const showSel = holdSel + '_show';
const shower = $(showSel);
let def = false;
buttons.addClass('tool_button tool_button_evt_handled')
.unbind('click mousedown mouseup') // may not be necessary
.each(function () {
// Get this button's options
const idSel = '#' + this.getAttribute('id');
const [i, opts] = Object.entries(btnOpts).find(([_, {sel}]) => {
return sel === idSel;
});
// Remember the function that goes with this ID
currentObj.flyoutFuncs[opts.sel] = opts.fn;
if (opts.isDefault) { def = i; }
const flyoutAction = function (ev) {
let options = opts;
// Find the currently selected tool if comes from keystroke
if (ev.type === 'keydown') {
const flyoutIsSelected = $(options.parent + '_show').hasClass('tool_button_current');
const currentOperation = $(options.parent + '_show').attr('data-curopt');
Object.entries(holders[opts.parent]).some(([j, tool]) => {
if (tool.sel !== currentOperation) {
return false;
}
if (!ev.shiftKey || !flyoutIsSelected) {
options = tool;
} else {
// If flyout is selected, allow shift key to iterate through subitems
j = Number.parseInt(j);
// Use `allHolders` to include both extension `includeWith` and toolbarButtons
options = allHolders[opts.parent][j + 1] ||
holders[opts.parent][0];
}
return true;
});
}
if ($(this).hasClass('disabled')) { return false; }
/* if (toolButtonClick(showSel)) {
options.fn();
} */
const icon = (options.icon) ? $.getSvgIcon(options.icon, true) : $(options.sel).children().eq(0).clone();
icon[0].setAttribute('width', shower.width());
icon[0].setAttribute('height', shower.height());
shower.children(':not(.flyout_arrow_horiz)').remove();
shower.append(icon).attr('data-curopt', options.sel); // This sets the current mode
return true;
};
$(this).mouseup(flyoutAction);
// TODO: currently not trigger here
/* if (opts.key) {
$(document).bind('keydown', opts.key[0] + ' shift+' + opts.key[0], flyoutAction);
} */
return true;
});
if (def) {
shower.attr('data-curopt', btnOpts[def].sel);
} else if (!shower.attr('data-curopt')) {
// Set first as default
shower.attr('data-curopt', btnOpts[0].sel);
}
let timer;
// Clicking the "show" icon should set the current mode
shower.mousedown(function (evt) {
if (shower.hasClass('disabled')) {
return false;
}
const holder = $(holdSel);
const pos = $(showSel).position();
const l = pos.left + 34;
const w = holder.width() * -1;
const time = holder.data('shown_popop') ? 200 : 0;
timer = setTimeout(function () {
// Show corresponding menu
if (!shower.data('isLibrary')) {
holder.css('left', w).show().animate({
left: l
}, 150);
} else {
holder.css('left', l).show();
}
holder.data('shown_popop', true);
}, time);
evt.preventDefault();
return true;
}).mouseup(function (evt) {
clearTimeout(timer);
/* const opt = $(this).attr('data-curopt');
// Is library and popped up, so do nothing
if (shower.data('isLibrary') && $(showSel.replace('_show', '')).is(':visible')) {
toolButtonClick(showSel, true);
return;
}
if (toolButtonClick(showSel) && this.flyoutFuncs[opt]) {
this.flyoutFuncs[opt]();
} */
});
// $('#tools_rect').mouseleave(function () { $('#tools_rect').fadeOut(); });
});
this.setFlyoutTitles();
// this.setFlyoutPositions();
}
/**
* @param {string} id
* @param {external:jQuery} child
* @returns {external:jQuery}
*/
// eslint-disable-next-line class-methods-use-this
makeFlyoutHolder (id, child) {
return $('<div>', {
class: 'tools_flyout',
id
}).appendTo('#svg_editor').append(child);
}
/**
* @function module:SVGEditor.setIcon
* @param {string|Element|external:jQuery} elem
@ -1123,378 +872,12 @@ class Editor extends EditorStartup {
const runCallback = () => {
if (ext.callback && !cbCalled) {
cbCalled = true;
ext.callback.call(editor);
ext.callback.call(this);
}
};
/**
* @typedef {PlainObject} module:SVGthis.ContextTool
* @property {string} panel The ID of the existing panel to which the tool is being added. Required.
* @property {string} id The ID of the actual tool element. Required.
* @property {PlainObject<string, external:jQuery.Function>|PlainObject<"change", external:jQuery.Function>} events DOM event names keyed to associated functions. Example: `{change () { seAlert('Option was changed') } }`. "change" event is one specifically handled for the "button-select" type. Required.
* @property {string} title The tooltip text that will appear when the user hovers over the tool. Required.
* @property {"tool_button"|"select"|"button-select"|"input"|string} type The type of tool being added. Expected.
* @property {PlainObject<string, string>} [options] List of options and their labels for select tools. Example: `{1: 'One', 2: 'Two', all: 'All' }`. Required by "select" tools.
* @property {string} [container_id] The ID to be given to the tool's container element.
* @property {string} [defval] Default value
* @property {string|Integer} [colnum] Added as part of the option list class.
* @property {string} [label] Label associated with the tool, visible in the UI
* @property {Integer} [size] Value of the "size" attribute of the tool input
*/
if (ext.context_tools) {
$.each(ext.context_tools, function (i, tool) {
// Add select tool
const contId = tool.container_id ? (' id="' + tool.container_id + '"') : '';
let panel = $('#' + tool.panel);
// create the panel if it doesn't exist
if (!panel.length) {
panel = $('<div>', {id: tool.panel}).appendTo('#tools_top');
}
let html;
// TODO: Allow support for other types, or adding to existing tool
switch (tool.type) {
case 'tool_button': {
html = '<div class="tool_button">' + tool.id + '</div>';
const div = $(html).appendTo(panel);
if (tool.events) {
$.each(tool.events, function (evt, func) {
$(div).bind(evt, func);
});
}
break;
} case 'select': {
html = '<label' + contId + '>' +
'<select id="' + tool.id + '">';
$.each(tool.options, function (val, text) {
const sel = (val === tool.defval) ? ' selected' : '';
html += '<option value="' + val + '"' + sel + '>' + text + '</option>';
});
html += '</select></label>';
// Creates the tool, hides & adds it, returns the select element
const sel = $(html).appendTo(panel).find('select');
$.each(tool.events, function (evt, func) {
$(sel).bind(evt, func);
});
break;
} case 'button-select': {
html = '<div id="' + tool.id + '" class="dropdown toolset" title="' + tool.title + '">' +
'<div id="cur_' + tool.id + '" class="icon_label"></div>' +
'<button><img class="svg_icon" src="./images/arrow_down.svg" alt="icon" width="7" height="7"></button>' +
'</div>';
const list = $('<ul id="' + tool.id + '_opts"></ul>').appendTo('#option_lists');
if (tool.colnum) {
list.addClass('optcols' + tool.colnum);
}
// Creates the tool, hides & adds it, returns the select element
/* const dropdown = */ $(html).appendTo(panel).children();
btnSelects.push({
elem: ('#' + tool.id),
list: ('#' + tool.id + '_opts'),
title: tool.title,
callback: tool.events.change,
cur: ('#cur_' + tool.id)
});
break;
} case 'input': {
html = '<label' + contId + '>' +
'<span id="' + tool.id + '_label">' +
tool.label + ':</span>' +
'<input id="' + tool.id + '" title="' + tool.title +
'" size="' + (tool.size || '4') +
'" value="' + (tool.defval || '') + '" type="text"/></label>';
// Creates the tool, hides & adds it, returns the select element
// Add to given tool.panel
const inp = $(html).appendTo(panel).find('input');
if (tool.events) {
$.each(tool.events, function (evt, func) {
inp.bind(evt, func);
});
}
break;
} default:
break;
}
});
}
const {svgicons} = ext;
if (ext.buttons) {
const fallbackObj = {},
altsObj = {},
placementObj = {},
holders = {};
/**
* @typedef {GenericArray} module:SVGEditor.KeyArray
* @property {string} 0 The key to bind (on `keydown`)
* @property {boolean} 1 Whether to `preventDefault` on the `keydown` event
* @property {boolean} 2 Not apparently in use (NoDisableInInput)
*/
/**
* @typedef {string|module:SVGEditor.KeyArray} module:SVGEditor.Key
*/
/**
* @typedef {PlainObject} module:SVGEditor.Button
* @property {string} id A unique identifier for this button. If SVG icons are used, this must match the ID used in the icon file. Required.
* @property {"mode_flyout"|"mode"|"context"|"app_menu"} type Type of button. Required.
* @property {string} title The tooltip text that will appear when the user hovers over the icon. Required.
* @property {PlainObject<string, external:jQuery.Function>|PlainObject<"click", external:jQuery.Function>} events DOM event names with associated functions. Example: `{click () { alert('Button was clicked') } }`. Click is used with `includeWith` and `type` of "mode_flyout" (and "mode"); any events may be added if `list` is not present. Expected.
* @property {string} panel The ID of the context panel to be included, if type is "context". Required only if type is "context".
* @property {string} icon The file path to the raster version of the icon image source. Required only if no `svgicons` is supplied from [ExtensionInitResponse]{@link module:svgcanvas.ExtensionInitResponse}.
* @property {string} [svgicon] If absent, will utilize the button "id"; used to set "placement" on the `svgIcons` call
* @property {string} [list] Points to the "id" of a `context_tools` item of type "button-select" into which the button will be added as a panel list item
* @property {Integer} [position] The numeric index for placement; defaults to last position (as of the time of extension addition) if not present. For use with {@link http://api.jquery.com/eq/}.
* @property {boolean} [isDefault] Whether or not the button is the default. Used with `list`.
* @property {PlainObject} [includeWith] Object with flyout menu data
* @property {boolean} [includeWith.isDefault] Indicates whether button is default in flyout list or not.
* @property {string} includeWith.button jQuery selector of the existing button to be joined. Example: '#tool_line'. Required if `includeWith` is used.
* @property {"last"|Integer} [includeWith.position] Position of icon in flyout list; will be added to end if not indicated. Integer is for use with {@link http://api.jquery.com/eq/}.
* @property {module:SVGEditor.Key} [key] The key to bind to the button
*/
// Add buttons given by extension
$.each(ext.buttons, function (i, /** @type {module:SVGEditor.Button} */ btn) {
let {id} = btn;
let num = i;
// Give button a unique ID
while ($('#' + id).length) {
id = btn.id + '_' + (++num);
}
let icon;
if (!svgicons) {
icon = $(
'<img src="' + self.configObj.curConfig.imgPath + btn.icon +
(btn.title ? '" alt="' + btn.title : '') +
'">'
);
} else {
fallbackObj[id] = btn.icon;
altsObj[id] = btn.title;
const svgicon = btn.svgicon || btn.id;
if (btn.type === 'app_menu') {
placementObj['#' + id + ' > div'] = svgicon;
} else {
placementObj['#' + id] = svgicon;
}
}
let cls, parent;
// Set button up according to its type
switch (btn.type) {
case 'mode_flyout':
case 'mode':
cls = 'tool_button';
parent = '#tools_left';
break;
case 'context':
cls = 'tool_button';
parent = '#' + btn.panel;
// create the panel if it doesn't exist
if (!$(parent).length) {
$('<div>', {id: btn.panel}).appendTo('#tools_top');
}
break;
case 'app_menu':
cls = '';
parent = '#main_menu ul';
break;
}
// refData
let flyoutHolder, showBtn, refBtn;
const button = $((btn.list || btn.type === 'app_menu') ? '<li/>' : '<div/>')
.attr('id', id)
.attr('title', btn.title)
.addClass(cls);
if (!btn.includeWith && !btn.list) {
if ('position' in btn) {
if ($(parent).children().eq(btn.position).length) {
$(parent).children().eq(btn.position).before(button);
} else {
$(parent).children().last().after(button);
}
} else {
button.appendTo(parent);
}
if (btn.type === 'mode_flyout') {
// Add to flyout menu / make flyout menu
// const opts = btn.includeWith;
// // opts.button, default, position
refBtn = $(button);
flyoutHolder = refBtn.parent();
// Create a flyout menu if there isn't one already
let tlsId;
if (!refBtn.parent().hasClass('tools_flyout')) {
// Create flyout placeholder
tlsId = refBtn[0].id.replace('tool_', 'tools_');
showBtn = refBtn.clone()
.attr('id', tlsId + '_show')
.append($('<div>', {class: 'flyout_arrow_horiz'}));
refBtn.before(showBtn);
// Create a flyout div
flyoutHolder = self.makeFlyoutHolder(tlsId, refBtn);
flyoutHolder.data('isLibrary', true);
showBtn.data('isLibrary', true);
}
// refData = Actions.getButtonData(opts.button);
placementObj['#' + tlsId + '_show'] = btn.id;
// TODO: Find way to set the current icon using the iconloader if this is not default
// Include data for extension button as well as ref button
/* curH = */ holders['#' + flyoutHolder[0].id] = [{
sel: '#' + id,
fn: btn.events.click,
icon: btn.id,
// key: btn.key,
isDefault: true
}]; // , refData
//
// // {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'}
//
// const pos = ('position' in opts)?opts.position:'last';
// const len = flyoutHolder.children().length;
//
// // Add at given position or end
// if (!isNaN(pos) && pos >= 0 && pos < len) {
// flyoutHolder.children().eq(pos).before(button);
// } else {
// flyoutHolder.append(button);
// curH.reverse();
// }
} else if (btn.type === 'app_menu') {
button.append('<div>').append(btn.title);
}
} else if (btn.list) {
// Add button to list
button.addClass('push_button');
$('#' + btn.list + '_opts').append(button);
if (btn.isDefault) {
$('#cur_' + btn.list).append(button.children().clone());
const svgicon = btn.svgicon || btn.id;
placementObj['#cur_' + btn.list] = svgicon;
}
} else if (btn.includeWith) {
// Add to flyout menu / make flyout menu
const opts = btn.includeWith;
// opts.button, default, position
refBtn = $(opts.button);
flyoutHolder = refBtn.parent();
// Create a flyout menu if there isn't one already
let tlsId;
if (!refBtn.parent().hasClass('tools_flyout')) {
// Create flyout placeholder
tlsId = refBtn[0].id.replace('tool_', 'tools_');
showBtn = refBtn.clone()
.attr('id', tlsId + '_show')
.append($('<div>', {class: 'flyout_arrow_horiz'}));
refBtn.before(showBtn);
// Create a flyout div
flyoutHolder = self.makeFlyoutHolder(tlsId, refBtn);
}
// refData = Actions.getButtonData(opts.button);
if (opts.isDefault) {
placementObj['#' + tlsId + '_show'] = btn.id;
}
// TODO: Find way to set the current icon using the iconloader if this is not default
// Include data for extension button as well as ref button
/* const curH = */ holders['#' + flyoutHolder[0].id] = [{
sel: '#' + id,
fn: btn.events.click,
icon: btn.id,
key: btn.key,
isDefault: Boolean(btn.includeWith && btn.includeWith.isDefault)
}]; // , refData
// {sel:'#tool_rect', fn: clickRect, evt: 'mouseup', key: 4, parent: '#tools_rect', icon: 'rect'}
const pos = ('position' in opts) ? opts.position : 'last';
const len = flyoutHolder.children().length;
// Add at given position or end
if (!isNaN(pos) && pos >= 0 && pos < len) {
flyoutHolder.children().eq(pos).before(button);
} else {
flyoutHolder.append(button);
// curH.reverse();
}
}
if (!svgicons) {
button.append(icon);
}
if (!btn.list) {
// Add given events to button
$.each(btn.events, function (name, func) {
if (name === 'click' && btn.type === 'mode') {
// `touch.js` changes `touchstart` to `mousedown`,
// so we must map extension click events as well
if (isTouch() && name === 'click') {
name = 'mousedown';
}
if (btn.includeWith) {
button.bind(name, func);
} else {
button.bind(name, function () {
/* if (toolButtonClick(button)) {
func();
} */
});
}
if (btn.key) {
// TODO: currently not trigger here
// $(document).bind('keydown', btn.key, func);
if (btn.title) {
button.attr('title', btn.title + ' [' + btn.key + ']');
}
}
} else {
button.bind(name, func);
}
});
}
self.setupFlyouts(holders);
});
$.each(btnSelects, function () {
self.addAltDropDown(this.elem, this.list, this.callback, {seticon: true});
});
/* if (svgicons) {
return new Promise((resolve, reject) => {
$.svgIcons(`${this.configObj.curConfig.imgPath}${svgicons}`, {
w: 24, h: 24,
id_match: false,
no_img: (!isWebkit()),
fallback: fallbackObj,
placement: placementObj,
callback (icons) {
// Non-ideal hack to make the icon match the current size
// if (curPrefs.iconsize && curPrefs.iconsize !== 'm') {
if (editor.pref('iconsize') !== 'm') {
// prepResize();
}
runCallback();
resolve();
}
});
});
} */
}
if (ext.events) {
this.leftPanelHandlers.add(ext.events.id, ext.events.click);
this.leftPanel.add(ext.events.id, ext.events.click);
}
return runCallback();
}
@ -1602,7 +985,7 @@ class Editor extends EditorStartup {
if (!cw) { step *= -1; }
const angle = Number.parseFloat($('#angle').val()) + step;
this.svgCanvas.setRotationAngle(angle);
this.topPanelHandlers.updateContextPanel();
this.topPanel.updateContextPanel();
}
/**
@ -1615,13 +998,13 @@ class Editor extends EditorStartup {
if (ok === 'Cancel') {
return;
}
this.leftPanelHandlers.clickSelect();
this.leftPanel.clickSelect();
this.svgCanvas.clear();
this.svgCanvas.setResolution(x, y);
this.updateCanvas(true);
this.zoomImage();
this.layersPanel.populateLayers();
this.topPanelHandlers.updateContextPanel();
this.topPanel.updateContextPanel();
this.svgCanvas.runExtensions('onNewDocument');
}
@ -1803,7 +1186,7 @@ class Editor extends EditorStartup {
return;
}
saveChanges();
this.leftPanelHandlers.clickSelect();
this.leftPanel.clickSelect();
}
/**
@ -2184,7 +1567,4 @@ class Editor extends EditorStartup {
}
}
const editor = new Editor();
editor.init();
export default editor;
export default Editor;

View File

@ -6,10 +6,6 @@ import {
} from './contextmenu.js';
import editorTemplate from './templates/editorTemplate.js';
import SvgCanvas from '../svgcanvas/svgcanvas.js';
import LayersPanel from './panels/LayersPanel.js';
import LeftPanelHandlers from './panels/LeftPanelHandlers.js';
import BottomPanelHandlers from './panels/BottomPanelHandlers.js';
import TopPanelHandlers from './panels/TopPanelHandlers.js';
import Rulers from './Rulers.js';
/**
@ -54,6 +50,7 @@ class EditorStartup {
constructor () {
this.extensionsAdded = false;
this.messageQueue = [];
this.$svgEditor = $id('svg_editor')
}
/**
* Auto-run after a Promise microtask.
@ -62,7 +59,7 @@ class EditorStartup {
*/
async init () {
// allow to prepare the dom without display
$id('svg_editor').style.visibility = 'hidden';
this.$svgEditor.style.visibility = 'hidden';
try {
// add editor components to the DOM
document.body.append(editorTemplate.content.cloneNode(true));
@ -109,10 +106,10 @@ class EditorStartup {
this.configObj.curConfig
);
this.leftPanelHandlers = new LeftPanelHandlers(this);
this.bottomPanelHandlers = new BottomPanelHandlers(this);
this.topPanelHandlers = new TopPanelHandlers(this);
this.layersPanel = new LayersPanel(this);
this.leftPanel.init();
this.bottomPanel.init();
this.topPanel.init();
this.layersPanel.init();
const {undoMgr} = this.svgCanvas;
this.workarea = document.getElementById('workarea');
@ -461,10 +458,6 @@ class EditorStartup {
/**
* Associate all button actions as well as non-button keyboard shortcuts.
*/
this.leftPanelHandlers.init();
this.bottomPanelHandlers.init();
this.topPanelHandlers.init();
this.layersPanel.init();
$id('tool_clear').addEventListener('click', this.clickClear.bind(this));
$id('tool_open').addEventListener('click', (e) => {
@ -748,7 +741,7 @@ class EditorStartup {
const {langParam, langData} = await this.putLocale(this.configObj.pref('lang'), this.goodLangs);
await this.setLang(langParam, langData);
$id('svg_editor').style.visibility = 'visible';
this.$svgEditor.style.visibility = 'visible';
try {
// load standard extensions

View File

@ -0,0 +1,97 @@
// https://github.com/knadh/dragmove.js
// Kailash Nadh (c) 2020.
// MIT License.
// can't use npm version as the dragmove is different.
let _loaded = false;
let _callbacks = [];
const _isTouch = window.ontouchstart !== undefined;
export const dragmove = function(target, handler, parent, onStart, onEnd, onDrag) {
// Register a global event to capture mouse moves (once).
if (!_loaded) {
document.addEventListener(_isTouch ? "touchmove" : "mousemove", function(e) {
let c = e;
if (e.touches) {
c = e.touches[0];
}
// On mouse move, dispatch the coords to all registered callbacks.
for (var i = 0; i < _callbacks.length; i++) {
_callbacks[i](c.clientX, c.clientY);
}
});
}
_loaded = true;
let isMoving = false, hasStarted = false;
let startX = 0, startY = 0, lastX = 0, lastY = 0;
// On the first click and hold, record the offset of the pointer in relation
// to the point of click inside the element.
handler.addEventListener(_isTouch ? "touchstart" : "mousedown", function(e) {
e.stopPropagation();
e.preventDefault();
if (target.dataset.dragEnabled === "false") {
return;
}
let c = e;
if (e.touches) {
c = e.touches[0];
}
isMoving = true;
startX = target.offsetLeft - c.clientX;
startY = target.offsetTop - c.clientY;
});
// On leaving click, stop moving.
document.addEventListener(_isTouch ? "touchend" : "mouseup", function(e) {
if (onEnd && hasStarted) {
onEnd(target, parent, parseInt(target.style.left), parseInt(target.style.top));
}
isMoving = false;
hasStarted = false;
});
// On leaving click, stop moving.
document.addEventListener(_isTouch ? "touchmove" : "mousemove", function(e) {
if (onDrag && hasStarted) {
onDrag(target, parseInt(target.style.left), parseInt(target.style.top));
}
});
// Register mouse-move callback to move the element.
_callbacks.push(function move(x, y) {
if (!isMoving) {
return;
}
if (!hasStarted) {
hasStarted = true;
if (onStart) {
onStart(target, lastX, lastY);
}
}
lastX = x + startX;
lastY = y + startY;
// If boundary checking is on, don't let the element cross the viewport.
if (target.dataset.dragBoundary === "true") {
if (lastX < 1 || lastX >= window.innerWidth - target.offsetWidth) {
return;
}
if (lastY < 1 || lastY >= window.innerHeight - target.offsetHeight) {
return;
}
}
target.style.left = lastX + "px";
target.style.top = lastY + "px";
});
}
export { dragmove as default };

View File

@ -6,7 +6,7 @@
* @copyright 2013 James Sacksteder
*
*/
import { dragmove } from '@knadh/dragmove';
import { dragmove } from '../../../editor/dragmove/dragmove.js';
export default {
name: 'overview_window',
init ({$, isChrome}) {
@ -105,11 +105,11 @@ export default {
document.getElementById('workarea').scrollLeft = portX;
document.getElementById('workarea').scrollTop = portY;
};
const onStart = function () {
const onStart = () => {
overviewWindowGlobals.viewBoxDragging = true;
updateViewPortFromViewBox();
};
const onEnd = function (el, parent, x, y) {
const onEnd = (el, parent, x, y) => {
if((el.offsetLeft + el.offsetWidth) > $(parent).attr('width')){
el.style.left = ($(parent).attr('width') - el.offsetWidth) + 'px';
} else if(el.offsetLeft < 0){

View File

@ -29,32 +29,6 @@
<body>
<div id="svg_editor" role="main">
<div id="sidepanels">
<div id="layerpanel">
<h3 id="layersLabel">Layers</h3>
<fieldset id="layerbuttons">
<se-button id="layer_new" title="New Layer" size="small" src="./images/new.svg"></se-button>
<se-button id="layer_delete" title="Delete Layer" size="small" src="./images/delete.svg"></se-button>
<se-button id="layer_rename" title="Rename Layer" size="small" src="./images/text.svg"></se-button>
<se-button id="layer_up" title="Move Layer Up" size="small" src="./images/go_up.svg"></se-button>
<se-button id="layer_down" title="Move Layer Down" size="small" src="./images/go_down.svg"></se-button>
<se-button id="layer_moreopts" title="More Options" size="small" src="./images/context_menu.svg">
</se-button>
</fieldset>
<table id="layerlist">
<tr class="layer">
<td class="layervis"></td>
<td class="layername">Layer 1</td>
</tr>
</table>
<span id="selLayerLabel">Move elements to:</span>
<select id="selLayerNames" title="Move selected elements to a different layer" disabled="disabled">
<option selected="selected" value="layer1">Layer 1</option>
</select>
</div>
<div id="sidepanel_handle" title="Drag left/right to resize side panel [X]">L a y e r s
</div>
</div>
<se-menu id="main_button" label="SVG-Edit" src="./images/logo.svg" alt="logo">
<!-- File-like buttons: New, Save, Source -->
<se-menu-item id="tool_clear" label="New Image" shortcut="N" src="./images/new.svg"></se-menu-item>
@ -69,301 +43,8 @@
<se-menu-item id="tool_editor_homepage" label="SVG-Edit Home Page" src="./images/logo.svg">
</se-menu-item>
</se-menu>
<div id="tools_top">
<div id="editor_panel">
<div class="tool_sep"></div>
<se-button id="tool_source" title="Edit Source" shortcut="U" src="./images/source.svg"></se-button>
<se-button id="tool_wireframe" title="Wireframe Mode" shortcut="F" src="./images/wireframe.svg"></se-button>
<se-button id="view_grid" title="Show grid" src="./images/grid.svg"></se-button>
</div> <!-- editor_panel -->
<div id="history_panel">
<div class="tool_sep"></div>
<se-button id="tool_undo" title="Undo" shortcut="Z" src="./images/undo.svg" disabled></se-button>
<se-button id="tool_redo" title="Redo" shortcut="Y" src="./images/redo.svg" disabled></se-button>
</div> <!-- history_panel -->
<!-- Buttons when a single element is selected -->
<div id="selected_panel">
<div class="toolset">
<div class="tool_sep"></div>
<se-button id="tool_clone" title="Duplicate Element" shortcut="D" src="./images/clone.svg"></se-button>
<se-button id="tool_delete" title="Delete Element" shortcut="Backspace" src="./images/delete.svg">
</se-button>
</div>
<div class="toolset">
<div class="tool_sep"></div>
<se-button id="tool_move_top" title="Bring to Front" shortcut="Ctrl+Shift+]" src="./images/move_top.svg">
</se-button>
<se-button id="tool_move_bottom" title="Send to Back" shortcut="Ctrl+Shift+[" src="./images/move_bottom.svg">
</se-button>
</div>
<div class="toolset">
<se-button id="tool_topath" title="Convert to Path" src="./images/to_path.svg"></se-button>
<se-button id="tool_reorient" title="Reorient path" src="./images/reorient.svg"></se-button>
<se-button id="tool_make_link" title="Make (hyper)link" src="./images/globe_link.svg"></se-button>
</div>
<div class="toolset">
<div class="tool_sep"></div>
<se-input id="elem_id" data-attr="id" size="10" label="id" title="Identify the element"></se-input>
</div>
<div class="toolset">
<se-input id="elem_class" data-attr="class" size="10" label="class" title="Element class"></se-input>
</div>
<se-spin-input size="3" id="angle" min=-180 max=180 step=5 src="./images/angle.svg"
title="Change rotation angle"></se-spin-input>
<se-spin-input size="2" id="blur" min=0 max=100 step=5 src="./images/blur.svg"
title="Change gaussian blur value"></se-spin-input>
<se-list id="tool_position" title="Align Element to Page" label="" width="22px" height="24px">
<se-list-item id="tool_posleft" value="l">
<img title="align left" src="./images/align_left.svg" height="22px">
</se-list-item>
<se-list-item id="tool_poscenter" value="c">
<img title="align center" src="./images/align_center.svg" height="22px">
</se-list-item>
<se-list-item id="tool_posright" value="r">
<img title="align right" src="./images/align_right.svg" height="22px">
</se-list-item>
<se-list-item id="tool_postop" value="t">
<img title="align top" src="./images/align_top.svg" height="22px">
</se-list-item>
<se-list-item id="tool_posmiddle" value="m">
<img title="align middle" src="./images/align_middle.svg" height="22px">
</se-list-item>
<se-list-item id="tool_posbottom" value="b">
<img title="align bottom" src="./images/align_bottom.svg" height="22px">
</se-list-item>
</se-list>
<div id="xy_panel" class="toolset">
<se-spin-input id="selected_x" data-attr="x" size="4" type="text" label="x" title="Change X coordinate">
</se-spin-input>
<se-spin-input id="selected_y" data-attr="y" size="4" type="text" label="y" title="Change Y coordinate">
</se-spin-input>
</div>
</div> <!-- selected_panel -->
<!-- Buttons when multiple elements are selected -->
<div id="multiselected_panel">
<div class="tool_sep"></div>
<se-button id="tool_clone_multi" title="Clone Elements" shortcut="C" src="./images/clone.svg"></se-button>
<se-button id="tool_delete_multi" title="Delete Selected Elements" shortcut="Delete/Backspace"
src="./images/delete.svg"></se-button>
<div class="tool_sep"></div>
<se-button id="tool_group_elements" title="Group Elements" shortcut="G" src="./images/group_elements.svg">
</se-button>
<se-button id="tool_make_link_multi" title="Make (hyper)link" src="./images/globe_link.svg"></se-button>
<se-button id="tool_align_left" title="Align Left" src="./images/align_left.svg"></se-button>
<se-button id="tool_align_center" title="Align Center" src="./images/align_center.svg"></se-button>
<se-button id="tool_align_right" title="Align Right" src="./images/align_right.svg"></se-button>
<se-button id="tool_align_top" title="Align Top" src="./images/align_top.svg"></se-button>
<se-button id="tool_align_middle" title="Align Middle" src="./images/align_middle.svg"></se-button>
<se-button id="tool_align_bottom" title="Align Bottom" src="./images/align_bottom.svg"></se-button>
<se-list id="tool_align_relative" label="relative to:">
<se-list-item id="selected_objects" value="selected">selected objects</se-list-item>
<se-list-item id="largest_object" value="largest">largest object</se-list-item>
<se-list-item id="smallest_object" value="smallest">smallest object</se-list-item>
<se-list-item id="page" value="page">page</se-list-item>
</se-list>
<div class="tool_sep"></div>
</div> <!-- multiselected_panel -->
<div id="rect_panel">
<div class="toolset">
<se-spin-input id="rect_width" data-attr="width" size="4" label="w"
title="Change rectangle width"></se-spin-input>
<se-spin-input id="rect_height" data-attr="height" size="4" label="h"
title="Change rectangle height"></se-spin-input>
</div>
<se-spin-input id="rect_rx" min=0 max=1000 step=1 size="3" title="Change Rectangle Corner Radius"
data-attr="Corner Radius" src="./images/c_radius.svg"></se-spin-input>
</div> <!-- rect_panel -->
<div id="image_panel">
<div class="toolset">
<se-spin-input id="image_width" data-attr="width" size="4" type="text" label="w"
title="Change image width"></se-spin-input>
<se-spin-input id="image_height" data-attr="height" size="4" type="text" label="h"
title="Change image height"></se-spin-input>
</div>
<div class="toolset">
<label id="tool_image_url">url:
<input id="image_url" type="text" title="Change URL" size="35" />
</label>
<label id="tool_change_image">
<button id="change_image_url" style="display: none;">Change Image</button>
<span id="url_notice"
title="NOTE: This image cannot be embedded. It will depend on this path to be displayed"></span>
</label>
</div>
</div> <!-- image_panel -->
<div id="circle_panel">
<div class="toolset">
<se-spin-input id="circle_cx" data-attr="cx" size="4" label="cx"></se-spin-input>
<se-spin-input id="circle_cy" data-attr="cy" size="4" label="cy"></se-spin-input>
</div>
<div class="toolset">
<se-spin-input id="circle_r" data-attr="r" size="4" label="r"></se-spin-input>
</div>
</div> <!-- circle_panel -->
<div id="ellipse_panel">
<div class="toolset">
<se-spin-input id="ellipse_cx" data-attr="cx" size="4" title="Change ellipse's cx coordinate" label="cx">
</se-spin-input>
<se-spin-input id="ellipse_cy" data-attr="cy" size="4" title="Change ellipse's cy coordinate" label="cy">
</se-spin-input>
</div>
<div class="toolset">
<se-spin-input id="ellipse_rx" data-attr="rx" size="4" title="Change ellipse's x radius" label="rx">
</se-spin-input>
<se-spin-input id="ellipse_ry" data-attr="ry" size="4" title="Change ellipse's y radius" label="ry">
</se-spin-input>
</div>
</div> <!-- ellipse_panel -->
<div id="line_panel">
<div class="toolset">
<se-spin-input id="line_x1" data-attr="x1" size="4" title="Change line's starting x coordinate" label="x1">
</se-spin-input>
<se-spin-input id="line_y1" data-attr="y1" size="4" title="Change line's starting y coordinate" label="y1">
</se-spin-input>
<se-spin-input id="line_x2" data-attr="x2" size="4" title="Change line's ending x coordinate" label="x2">
</se-spin-input>
<se-spin-input id="line_y2" data-attr="y2" size="4" title="Change line's ending y coordinate" label="y2">
</se-spin-input>
</div>
</div> <!-- line_panel -->
<div id="text_panel">
<div class="toolset">
<se-button id="tool_bold" title="Bold Text [B]" src="./images/bold.svg" shortcut="B"></se-button>
<se-button id="tool_italic" title="Italic Text [I]" src="./images/italic.svg" shortcut="I"></se-button>
<se-button id="tool_text_anchor_start" title="Align the text from start" src="./images/anchor_start.svg">
</se-button>
<se-button id="tool_text_anchor_middle" title="Align the text from middle" src="./images/anchor_middle.svg">
</se-button>
<se-button id="tool_text_anchor_end" title="Align the text from end" src="./images/anchor_end.svg">
</se-button>
</div>
<se-list id="tool_font_family" label="Font:">
<se-list-item value="Serif" style="font-family:serif;"> Serif</se-list-item>
<se-list-item value="Sans-serif" style="font-family:sans-serif;"> Sans-serif</se-list-item>
<se-list-item value="Cursive" style="font-family:cursive;"> Cursive</se-list-item>
<se-list-item value="Fantasy" style="font-family:fantasy;"> Fantasy</se-list-item>
<se-list-item value="Monospace" style="font-family:monospace;"> Monospace</se-list-item>
<se-list-item value="Courier" style="font-family:courier;"> Courier</se-list-item>
<se-list-item value="Helvetica" style="font-family:helvetica;">Helvetica</se-list-item>
<se-list-item value="Times" style="font-family:times;">Times</se-list-item>
</se-list>
<se-spin-input size="2" id="font_size" min=1 max=1000 step=1 title="Change Font Size"
src="./images/fontsize.svg"></se-spin-input>
<!-- Not visible, but still used -->
<input id="text" type="text" size="35" />
</div> <!-- text_panel -->
<!-- formerly gsvg_panel -->
<div id="container_panel">
<div class="tool_sep"></div>
<!-- Add viewBox field here? -->
<label id="group_title" title="Group identification label">
<span>label</span>
<input id="g_title" data-attr="title" size="10" type="text" />
</label>
</div> <!-- container_panel -->
<div id="use_panel">
<se-button id="tool_unlink_use" title="Break link to reference element (make unique)" src="./images/unlink_use.svg">
</se-button>
</div> <!-- use_panel -->
<div id="g_panel">
<se-button id="tool_ungroup" title="Ungroup Elements [G]" src="./images/ungroup.svg">
</se-button>
</div> <!-- g_panel -->
<!-- For anchor elements -->
<div id="a_panel">
<label id="tool_link_url" title="Set link URL (leave empty to remove)">
<span id="linkLabel" class="icon_label"></span>
<input id="link_url" type="text" size="35" />
</label>
</div> <!-- a_panel -->
<div id="path_node_panel">
<div class="tool_sep"></div>
<se-button id="tool_node_link" title="Link Control Points" src="./images/tool_node_link.svg" pressed>
</se-button>
<div class="tool_sep"></div>
<se-spin-input id="path_node_x" data-attr="x" size="4" title="Change node's x coordinate" label="x:">
</se-spin-input>
<se-spin-input id="path_node_y" data-attr="y" size="4" title="Change node's y coordinate" label="y:">
</se-spin-input>
<select id="seg_type" title="Change Segment type">
<option id="straight_segments" selected="selected" value="4">Straight</option>
<option id="curve_segments" value="6">Curve</option>
</select>
<se-button id="tool_node_clone" title="Clone Node" src="./images/tool_node_clone.svg"></se-button>
<se-button id="tool_node_delete" title="Delete Node" src="./images/tool_node_delete.svg"></se-button>
<se-button id="tool_openclose_path" title="Open/close sub-path" src="./images/tool_openclose_path.svg">
</se-button>
<se-button id="tool_add_subpath" title="Add sub-path" src="./images/tool_add_subpath.svg"></se-button>
</div> <!-- path_node_panel -->
<div id="cur_context_panel"></div>
</div> <!-- tools_top -->
<div id="tools_left">
<se-button id="tool_select" title="Select Tool" src="./images/select.svg"></se-button>
<se-button id="tool_zoom" title="Zoom Tool" src="./images/zoom.svg" shortcut="Z"></se-button>
<se-button id="ext-panning" title="Panning" src="./images/panning.svg"></se-button>
<se-button id="tool_fhpath" title="Pencil Tool" src="./images/pencil.svg" shortcut="Q"></se-button>
<se-button id="tool_line" title="Line Tool" src="./images/pen.svg" shortcut="L"></se-button>
<se-button id="tool_path" title="Path Tool" src="./images/path.svg" shortcut="P"></se-button>
<se-flyingbutton id="tools_rect" title="Square/Rect Tool">
<se-button id="tool_rect" title="Rectangle" src="./images/rect.svg" shortcut="R"></se-button>
<se-button id="tool_square" title="Square" src="./images/square.svg"></se-button>
<se-button id="tool_fhrect" title="Free-Hand Rectangle" src="./images/fh_rect.svg"></se-button>
</se-flyingbutton>
<se-flyingbutton id="tools_ellipse" title="Ellipse/Circle Tool">
<se-button id="tool_ellipse" title="Rectangle" src="./images/ellipse.svg" shortcut="E"></se-button>
<se-button id="tool_circle" title="Square" src="./images/circle.svg"></se-button>
<se-button id="tool_fhellipse" title="Free-Hand Rectangle" src="./images/fh_ellipse.svg"></se-button>
</se-flyingbutton>
<se-flyingbutton id="tools_polygon" title="Polygone/Star Tool">
<se-button id="tool_polygon" title="Polygon Tool" src="./images/polygon.svg"></se-button>
<se-button id="tool_star" title="Star Tool" src="./images/star.svg"></se-button>
</se-flyingbutton>
<se-button id="mode_connect" title="Connect two objects" src="./images/conn.svg"></se-button>
<se-button id="tool_text" title="Text Tool" src="./images/text.svg" shortcut="T"></se-button>
<se-button id="tool_image" title="Image Tool" src="./images/image.svg"></se-button>
<se-button id="tool_eyedropper" title="Eye Dropper Tool" src="./images/eye_dropper.svg" shortcut="I"></se-button>
<se-explorerbutton id="tool_shapelib" title="Shape library" lib="./extensions/ext-shapes/shapelib/"
src="./images/shapelib.svg"></se-explorerbutton>
</div> <!-- tools_left -->
<div id="tools_bottom">
<!-- Zoom buttons -->
<se-zoom id="zoom" src="./images/zoom.svg" title="Change zoom level" inputsize="40px">
<div value="1000">1000</div>
<div value="400">400</div>
<div value="200">200</div>
<div value="100">100</div>
<div value="50">50</div>
<div value="25">25</div>
<div value="canvas">Fit to canvas</div>
<div value="selection">Fit to selection</div>
<div value="layer">Fit to layer content</div>
<div value="content">Fit to all content</div>
</se-zoom>
<se-colorpicker id="fill_color" src="./images/fill.svg" title="Change fill color" type="fill"></se-colorpicker>
<se-colorpicker id="stroke_color" src="./images/stroke.svg" title="Change stroke color" type="stroke"></se-colorpicker>
<se-spin-input id="stroke_width" min=0 max=99 step=1 title="Change stroke width" label=""></se-spin-input>
<se-list id="stroke_style" title="Change stroke dash style" label="" width="22px" height="24px">
<se-list-item value="none">&#8212;</se-list-item>
<se-list-item value="2,2">...</se-list-item>
<se-list-item value="5,5">- -</se-list-item>
<se-list-item value="5,2,2,2">- .</se-list-item>
<se-list-item value="5,2,2,2,2,2">- ..</se-list-item>
</se-list>
<se-list id="stroke_linejoin" title="Linejoin: Miter" label="" width="22px" height="24px">
<se-list-item id="linejoin_miter" value="miter"><img title="Linejoin: Miter" src="./images/linejoin_miter.svg" height="22px"></img></se-list-item>
<se-list-item id="linejoin_round" value="round"><img title="Linejoin: Round" src="./images/linejoin_round.svg" height="22px"></img></se-list-item>
<se-list-item id="linejoin_bevel" value="bevel"><img title="Linejoin: Bevel" src="./images/linejoin_bevel.svg" height="22px"></img></se-list-item>
</se-list>
<se-list id="stroke_linecap" title="Linecap: Butt" label="" width="22px" height="24px">
<se-list-item id="linecap_butt" value="butt"><img title="Linecap: Butt" src="./images/linecap_butt.svg" height="22px"></img></se-list-item>
<se-list-item id="linecap_square" value="square"><img title="Linecap: Square" src="./images/linecap_square.svg" height="22px"></img></se-list-item>
<se-list-item id="linecap_round" value="round"><img title="Linecap: Round" src="./images/linecap_round.svg" height="22px"></img></se-list-item>
</se-list>
<se-spin-input size="3" id="opacity" min=0 max=100 step=5 title="Change selected item opacity" src="./images/opacity.svg"></se-spin-input>
<se-palette id="palette"></se-palette>
</div> <!-- tools_bottom -->
<div id="option_lists" class="dropdown"></div>
<div id="option_lists" class="dropdown"></div>
</div>
</body>
</html>
</html>

View File

@ -7,7 +7,10 @@ For default config and extensions (and available options) available to
import './jquery.min.js';
import './components/index.js';
import './dialogs/index.js';
import svgEditor from './svgedit.js';
import Editor from './Editor.js';
const svgEditor = new Editor();
svgEditor.init();
// URL OVERRIDE CONFIG
svgEditor.setConfig({

View File

@ -0,0 +1,244 @@
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
import {jGraduate} from '../components/jgraduate/jQuery.jGraduate.js';
const {$id, $qa} = SvgCanvas;
/*
* register actions for left panel
*/
/**
* @type {module}
*/
class BottomPanel {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
}
/**
* @type {module}
*/
get selectedElement () {
return this.editor.selectedElement;
}
/**
* @type {module}
*/
get multiselected () {
return this.editor.multiselected;
}
/**
* @type {module}
*/
changeStrokeWidth (e) {
let val = e.target.value;
if (val === 0 && this.editor.selectedElement && ['line', 'polyline'].includes(this.editor.selectedElement.nodeName)) {
val = 1;
}
this.editor.svgCanvas.setStrokeWidth(val);
}
/**
* @type {module}
*/
changeZoom (value) {
switch (value) {
case 'canvas':
case 'selection':
case 'layer':
case 'content':
this.editor.zoomChanged(window, value);
break;
default:
{
const zoomlevel = Number(value) / 100;
if (zoomlevel < 0.001) {
value = 0.1;
return;
}
const zoom = this.editor.svgCanvas.getZoom();
const wArea = this.editor.workarea;
this.editor.zoomChanged(window, {
width: 0,
height: 0,
// center pt of scroll position
x: (wArea.scrollLeft + parseFloat(getComputedStyle(wArea, null).width.replace("px", "")) / 2) / zoom,
y: (wArea.scrollTop + parseFloat(getComputedStyle(wArea, null).height.replace("px", "")) / 2) / zoom,
zoom: zoomlevel
}, true);
}
}
}
/**
* @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate
* @returns {void}
*/
updateToolButtonState () {
const bNoFill = (this.editor.svgCanvas.getColor('fill') === 'none');
const bNoStroke = (this.editor.svgCanvas.getColor('stroke') === 'none');
const buttonsNeedingStroke = ['tool_fhpath', 'tool_line'];
const buttonsNeedingFillAndStroke = [
'tools_rect', 'tools_ellipse',
'tool_text', 'tool_path'
];
if (bNoStroke) {
buttonsNeedingStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
if (bNoStroke && bNoFill) {
buttonsNeedingFillAndStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingFillAndStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
this.editor.svgCanvas.runExtensions(
'toolButtonStateUpdate',
/** @type {module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate} */ {
nofill: bNoFill,
nostroke: bNoStroke
}
);
}
/**
* @type {module}
*/
handleColorPicker (type, evt) {
const {paint} = evt.detail;
this.editor.svgCanvas.setPaint(type, paint);
this.updateToolButtonState();
}
/**
* @type {module}
*/
handleStrokeAttr (type, evt) {
this.editor.svgCanvas.setStrokeAttr(type, evt.detail.value);
}
/**
* @type {module}
*/
handleOpacity (evt) {
// if ($(this).find('div').length) { return; }
const val = Number.parseInt(evt.currentTarget.value.split('%')[0]);
this.editor.svgCanvas.setOpacity(val / 100);
}
/**
* @type {module}
*/
handlePalette (e) {
e.preventDefault();
// shift key or right click for stroke
const {picker, color} = e.detail;
// Webkit-based browsers returned 'initial' here for no stroke
const paint = color === 'none' ? new jGraduate.Paint() : new jGraduate.Paint({alpha: 100, solidColor: color.substr(1)});
if (picker === 'fill') {
$id('fill_color').setPaint(paint);
} else {
$id('stroke_color').setPaint(paint);
}
this.editor.svgCanvas.setColor(picker, color);
if (color !== 'none' && this.editor.svgCanvas.getPaintOpacity(picker) !== 1) {
this.editor.svgCanvas.setPaintOpacity(picker, 1.0);
}
this.updateToolButtonState();
}
/**
* @type {module}
*/
init () {
// register actions for Bottom panel
const template = document.createElement('template');
template.innerHTML = `
<div id="tools_bottom">
<!-- Zoom buttons -->
<se-zoom id="zoom" src="./images/zoom.svg" title="Change zoom level" inputsize="40px">
<div value="1000">1000</div>
<div value="400">400</div>
<div value="200">200</div>
<div value="100">100</div>
<div value="50">50</div>
<div value="25">25</div>
<div value="canvas">Fit to canvas</div>
<div value="selection">Fit to selection</div>
<div value="layer">Fit to layer content</div>
<div value="content">Fit to all content</div>
</se-zoom>
<se-colorpicker id="fill_color" src="./images/fill.svg" title="Change fill color" type="fill"></se-colorpicker>
<se-colorpicker id="stroke_color" src="./images/stroke.svg" title="Change stroke color" type="stroke">
</se-colorpicker>
<se-spin-input id="stroke_width" min=0 max=99 step=1 title="Change stroke width" label=""></se-spin-input>
<se-list id="stroke_style" title="Change stroke dash style" label="" width="22px" height="24px">
<se-list-item value="none">&#8212;</se-list-item>
<se-list-item value="2,2">...</se-list-item>
<se-list-item value="5,5">- -</se-list-item>
<se-list-item value="5,2,2,2">- .</se-list-item>
<se-list-item value="5,2,2,2,2,2">- ..</se-list-item>
</se-list>
<se-list id="stroke_linejoin" title="Linejoin: Miter" label="" width="22px" height="24px">
<se-list-item id="linejoin_miter" value="miter"><img title="Linejoin: Miter" src="./images/linejoin_miter.svg"
height="22px"></img></se-list-item>
<se-list-item id="linejoin_round" value="round"><img title="Linejoin: Round" src="./images/linejoin_round.svg"
height="22px"></img></se-list-item>
<se-list-item id="linejoin_bevel" value="bevel"><img title="Linejoin: Bevel" src="./images/linejoin_bevel.svg"
height="22px"></img></se-list-item>
</se-list>
<se-list id="stroke_linecap" title="Linecap: Butt" label="" width="22px" height="24px">
<se-list-item id="linecap_butt" value="butt"><img title="Linecap: Butt" src="./images/linecap_butt.svg"
height="22px"></img></se-list-item>
<se-list-item id="linecap_square" value="square"><img title="Linecap: Square" src="./images/linecap_square.svg"
height="22px"></img></se-list-item>
<se-list-item id="linecap_round" value="round"><img title="Linecap: Round" src="./images/linecap_round.svg"
height="22px"></img></se-list-item>
</se-list>
<se-spin-input size="3" id="opacity" min=0 max=100 step=5 title="Change selected item opacity"
src="./images/opacity.svg"></se-spin-input>
<se-palette id="palette"></se-palette>
</div> <!-- tools_bottom -->
`
this.editor.$svgEditor.append(template.content.cloneNode(true));
$id('palette').addEventListener('change', this.handlePalette.bind(this));
const {curConfig} = this.editor.configObj;
$id('fill_color').setPaint(new jGraduate.Paint({alpha: 100, solidColor: curConfig.initFill.color}));
$id('stroke_color').setPaint(new jGraduate.Paint({alpha: 100, solidColor: curConfig.initStroke.color}));
$id('zoom').addEventListener('change', (e) => this.changeZoom.bind(this)(e.detail.value));
$id('stroke_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('stroke', evt));
$id('fill_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('fill', evt));
$id('stroke_width').addEventListener('change', this.changeStrokeWidth.bind(this));
$id('stroke_style').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-dasharray', evt));
$id('stroke_linejoin').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linejoin', evt));
$id('stroke_linecap').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linecap', evt));
$id('opacity').addEventListener('change', this.handleOpacity.bind(this));
}
/**
* @type {module}
*/
updateColorpickers (apply) {
$id('fill_color').update(this.editor.svgCanvas, this.editor.selectedElement, apply);
$id('stroke_color').update(this.editor.svgCanvas, this.editor.selectedElement, apply);
}
}
export default BottomPanel;

View File

@ -1,197 +0,0 @@
/* globals $ */
import {jGraduate} from '../components/jgraduate/jQuery.jGraduate.js';
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
const {$id} = SvgCanvas;
/*
* register actions for left panel
*/
/**
*
*/
class BottomPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
}
/**
* @type {module}
*/
get selectedElement () {
return this.editor.selectedElement;
}
/**
* @type {module}
*/
get multiselected () {
return this.editor.multiselected;
}
/**
* @type {module}
*/
changeStrokeWidth (e) {
let val = e.target.value;
if (val === 0 && this.selectedElement && ['line', 'polyline'].includes(this.selectedElement.nodeName)) {
val = 1;
}
this.svgCanvas.setStrokeWidth(val);
}
/**
* @type {module}
*/
changeZoom (value) {
switch (value) {
case 'canvas':
case 'selection':
case 'layer':
case 'content':
this.editor.zoomChanged(window, value);
break;
default:
{
const zoomlevel = Number(value) / 100;
if (zoomlevel < 0.001) {
value = 0.1;
return;
}
const zoom = this.svgCanvas.getZoom();
const wArea = this.editor.workarea;
this.editor.zoomChanged(window, {
width: 0,
height: 0,
// center pt of scroll position
x: (wArea.scrollLeft + parseFloat(getComputedStyle(wArea, null).width.replace("px", "")) / 2) / zoom,
y: (wArea.scrollTop + parseFloat(getComputedStyle(wArea, null).height.replace("px", "")) / 2) / zoom,
zoom: zoomlevel
}, true);
}
}
}
/**
* @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate
* @returns {void}
*/
updateToolButtonState () {
const bNoFill = (this.svgCanvas.getColor('fill') === 'none');
const bNoStroke = (this.svgCanvas.getColor('stroke') === 'none');
const buttonsNeedingStroke = ['tool_fhpath', 'tool_line'];
const buttonsNeedingFillAndStroke = [
'tools_rect', 'tools_ellipse',
'tool_text', 'tool_path'
];
if (bNoStroke) {
buttonsNeedingStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
if (bNoStroke && bNoFill) {
buttonsNeedingFillAndStroke.forEach((btn) => {
// if btn is pressed, change to select button
if ($id(btn).pressed) {
this.editor.leftPanelHandlers.clickSelect();
}
$(btn).disabled = true;
});
} else {
buttonsNeedingFillAndStroke.forEach((btn) => {
$id(btn).disabled = false;
});
}
this.svgCanvas.runExtensions(
'toolButtonStateUpdate',
/** @type {module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate} */ {
nofill: bNoFill,
nostroke: bNoStroke
}
);
}
/**
* @type {module}
*/
updateColorpickers (apply) {
$id('fill_color').update(this.svgCanvas, this.selectedElement, apply);
$id('stroke_color').update(this.svgCanvas, this.selectedElement, apply);
}
/**
* @type {module}
*/
handleColorPicker (type, evt) {
const {paint} = evt.detail;
this.svgCanvas.setPaint(type, paint);
this.updateToolButtonState();
}
/**
* @type {module}
*/
handleStrokeAttr (type, evt) {
this.svgCanvas.setStrokeAttr(type, evt.detail.value);
}
/**
* @type {module}
*/
handleOpacity (evt) {
// if ($(this).find('div').length) { return; }
const val = Number.parseInt(evt.currentTarget.value.split('%')[0]);
this.svgCanvas.setOpacity(val / 100);
}
/**
* @type {module}
*/
handlePalette (e) {
e.preventDefault();
// shift key or right click for stroke
const {picker, color} = e.detail;
// Webkit-based browsers returned 'initial' here for no stroke
const paint = color === 'none' ? new jGraduate.Paint() : new jGraduate.Paint({alpha: 100, solidColor: color.substr(1)});
if (picker === 'fill') {
$id('fill_color').setPaint(paint);
} else {
$id('stroke_color').setPaint(paint);
}
this.svgCanvas.setColor(picker, color);
if (color !== 'none' && this.svgCanvas.getPaintOpacity(picker) !== 1) {
this.svgCanvas.setPaintOpacity(picker, 1.0);
}
this.updateToolButtonState();
}
/**
* @type {module}
*/
init () {
// register actions for bottom panel
$id('zoom').addEventListener('change', (e) => this.changeZoom.bind(this)(e.detail.value));
$id('stroke_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('stroke', evt));
$id('fill_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('fill', evt));
$id('stroke_width').addEventListener('change', this.changeStrokeWidth.bind(this));
$id('stroke_style').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-dasharray', evt));
$id('stroke_linejoin').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linejoin', evt));
$id('stroke_linecap').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linecap', evt));
$id('opacity').addEventListener('change', this.handleOpacity.bind(this));
$id('palette').addEventListener('change', this.handlePalette.bind(this));
const {curConfig} = this.editor.configObj;
$id('fill_color').setPaint(new jGraduate.Paint({alpha: 100, solidColor: curConfig.initFill.color}));
$id('stroke_color').setPaint(new jGraduate.Paint({alpha: 100, solidColor: curConfig.initStroke.color}));
}
}
export default BottomPanelHandlers;

View File

@ -1,11 +1,11 @@
/* eslint-disable max-len */
/* eslint-disable no-alert */
/* globals $ */
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
import SvgCanvas from "../../svgcanvas/svgcanvas.js";
const SIDEPANEL_MAXWIDTH = 300;
const SIDEPANEL_OPENWIDTH = 150;
const {$id} = SvgCanvas;
const { $id } = SvgCanvas;
/**
*
@ -14,10 +14,9 @@ class LayersPanel {
/**
* @param {PlainObject} editor
*/
constructor (editor) {
this.svgCanvas = editor.svgCanvas;
constructor(editor) {
this.uiStrings = editor.uiStrings;
this.updateContextPanel = editor.topPanelHandlers.updateContextPanel;
this.updateContextPanel = editor.topPanel.updateContextPanel;
this.sidedrag = -1;
this.sidedragging = false;
this.allowmove = false;
@ -29,25 +28,37 @@ class LayersPanel {
* @fires module:svgcanvas.SvgCanvas#event:ext_workareaResized
* @returns {void}
*/
changeSidePanelWidth (delta) {
const rulerX = document.querySelector('#ruler_x');
$('#sidepanels').width('+=' + delta);
$('#layerpanel').width('+=' + delta);
rulerX.style.right = (parseFloat(getComputedStyle(rulerX, null).right.replace("px", "")) + delta) + "px";
this.editor.workarea.style.right = (parseFloat(getComputedStyle(this.editor.workarea, null).right.replace("px", "")) + delta) + "px";
this.svgCanvas.runExtensions('workareaResized');
changeSidePanelWidth(delta) {
const rulerX = document.querySelector("#ruler_x");
$("#sidepanels").width("+=" + delta);
$("#layerpanel").width("+=" + delta);
rulerX.style.right =
parseFloat(getComputedStyle(rulerX, null).right.replace("px", "")) +
delta +
"px";
this.editor.workarea.style.right =
parseFloat(
getComputedStyle(this.editor.workarea, null).right.replace("px", "")
) +
delta +
"px";
this.editor.svgCanvas.runExtensions("workareaResized");
}
/**
* @param {Event} evt
* @returns {void}
*/
resizeSidePanel (evt) {
if (!this.allowmove) { return; }
if (this.sidedrag === -1) { return; }
* @param {Event} evt
* @returns {void}
*/
resizeSidePanel(evt) {
if (!this.allowmove) {
return;
}
if (this.sidedrag === -1) {
return;
}
this.sidedragging = true;
let deltaX = this.sidedrag - evt.pageX;
const sideWidth = $('#sidepanels').width();
const sideWidth = $("#sidepanels").width();
if (sideWidth + deltaX > SIDEPANEL_MAXWIDTH) {
deltaX = SIDEPANEL_MAXWIDTH - sideWidth;
// sideWidth = SIDEPANEL_MAXWIDTH;
@ -55,7 +66,9 @@ class LayersPanel {
deltaX = 2 - sideWidth;
// sideWidth = 2;
}
if (deltaX === 0) { return; }
if (deltaX === 0) {
return;
}
this.sidedrag -= deltaX;
this.changeSidePanelWidth(deltaX);
}
@ -65,11 +78,12 @@ class LayersPanel {
* @param {boolean} close Forces the side panel closed
* @returns {void}
*/
toggleSidePanel (close) {
toggleSidePanel(close) {
const dpr = window.devicePixelRatio || 1;
const w = $('#sidepanels').width();
const w = $("#sidepanels").width();
const isOpened = (dpr < 1 ? w : w / dpr) > 2;
const zoomAdjustedSidepanelWidth = (dpr < 1 ? 1 : dpr) * SIDEPANEL_OPENWIDTH;
const zoomAdjustedSidepanelWidth =
(dpr < 1 ? 1 : dpr) * SIDEPANEL_OPENWIDTH;
const deltaX = (isOpened || close ? 0 : zoomAdjustedSidepanelWidth) - w;
this.changeSidePanelWidth(deltaX);
}
@ -77,90 +91,147 @@ class LayersPanel {
* @param {PlainObject} e event
* @returns {void}
*/
lmenuFunc (e) {
lmenuFunc(e) {
const action = e?.detail?.trigger;
switch (action) {
case 'dupe':
this.cloneLayer();
break;
case 'delete':
this.deleteLayer();
break;
case 'merge_down':
this.mergeLayer();
break;
case 'merge_all':
this.svgCanvas.mergeAllLayers();
this.updateContextPanel();
this.populateLayers();
break;
case "dupe":
this.cloneLayer();
break;
case "delete":
this.deleteLayer();
break;
case "merge_down":
this.mergeLayer();
break;
case "merge_all":
this.editor.svgCanvas.mergeAllLayers();
this.updateContextPanel();
this.populateLayers();
break;
}
}
/**
* @returns {void}
*/
init () {
init() {
const template = document.createElement("template");
template.innerHTML = `
<div id="sidepanels">
<div id="layerpanel">
<h3 id="layersLabel">Layers</h3>
<fieldset id="layerbuttons">
<se-button id="layer_new" title="New Layer" size="small" src="./images/new.svg"></se-button>
<se-button id="layer_delete" title="Delete Layer" size="small" src="./images/delete.svg"></se-button>
<se-button id="layer_rename" title="Rename Layer" size="small" src="./images/text.svg"></se-button>
<se-button id="layer_up" title="Move Layer Up" size="small" src="./images/go_up.svg"></se-button>
<se-button id="layer_down" title="Move Layer Down" size="small" src="./images/go_down.svg"></se-button>
<se-button id="layer_moreopts" title="More Options" size="small" src="./images/context_menu.svg">
</se-button>
</fieldset>
<table id="layerlist">
<tr class="layer">
<td class="layervis"></td>
<td class="layername">Layer 1</td>
</tr>
</table>
<span id="selLayerLabel">Move elements to:</span>
<select id="selLayerNames" title="Move selected elements to a different layer" disabled="disabled">
<option selected="selected" value="layer1">Layer 1</option>
</select>
</div>
<div id="sidepanel_handle" title="Drag left/right to resize side panel [X]">L a y e r s
</div>
</div>
`;
this.editor.$svgEditor.append(template.content.cloneNode(true));
this.editor.svgCanvas = this.editor.svgCanvas;
// layer menu added to DOM
const menuMore = document.createElement('se-cmenu-layers');
menuMore.setAttribute('id', 'se-cmenu-layers-more');
menuMore.value = 'layer_moreopts';
menuMore.setAttribute('leftclick', true);
const menuMore = document.createElement("se-cmenu-layers");
menuMore.setAttribute("id", "se-cmenu-layers-more");
menuMore.value = "layer_moreopts";
menuMore.setAttribute("leftclick", true);
document.body.append(menuMore);
const menuLayerBox = document.createElement('se-cmenu-layers');
menuLayerBox.setAttribute('id', 'se-cmenu-layers-list');
menuLayerBox.value = 'layerlist';
menuLayerBox.setAttribute('leftclick', false);
const menuLayerBox = document.createElement("se-cmenu-layers");
menuLayerBox.setAttribute("id", "se-cmenu-layers-list");
menuLayerBox.value = "layerlist";
menuLayerBox.setAttribute("leftclick", false);
document.body.append(menuLayerBox);
document.getElementById('layer_new').addEventListener('click', this.newLayer.bind(this));
document.getElementById('layer_delete').addEventListener('click', this.deleteLayer.bind(this));
document.getElementById('layer_up').addEventListener('click', () => this.moveLayer.bind(this)(-1));
document.getElementById('layer_down').addEventListener('click', () => this.moveLayer.bind(this)(1));
document.getElementById('layer_rename').addEventListener('click', this.layerRename.bind(this));
$id('se-cmenu-layers-more').addEventListener('change', this.lmenuFunc.bind(this));
$id('se-cmenu-layers-list').addEventListener('change', (e) => {
document
.getElementById("layer_new")
.addEventListener("click", this.newLayer.bind(this));
document
.getElementById("layer_delete")
.addEventListener("click", this.deleteLayer.bind(this));
document
.getElementById("layer_up")
.addEventListener("click", () => this.moveLayer.bind(this)(-1));
document
.getElementById("layer_down")
.addEventListener("click", () => this.moveLayer.bind(this)(1));
document
.getElementById("layer_rename")
.addEventListener("click", this.layerRename.bind(this));
$id("se-cmenu-layers-more").addEventListener(
"change",
this.lmenuFunc.bind(this)
);
$id("se-cmenu-layers-list").addEventListener("change", e => {
this.lmenuFunc.bind(this)(e?.detail?.trigger, e?.detail?.source);
});
$id('sidepanel_handle').addEventListener('click', this.toggleSidePanel.bind(this));
$id("sidepanel_handle").addEventListener(
"click",
this.toggleSidePanel.bind(this)
);
if (this.editor.configObj.curConfig.showlayers) {
this.toggleSidePanel();
}
$id('sidepanel_handle').addEventListener('mousedown', (evt) => {
$id("sidepanel_handle").addEventListener("mousedown", evt => {
this.sidedrag = evt.pageX;
window.addEventListener('mousemove', this.resizeSidePanel.bind(this));
window.addEventListener("mousemove", this.resizeSidePanel.bind(this));
this.allowmove = false;
// Silly hack for Chrome, which always runs mousemove right after mousedown
setTimeout(() => {
this.allowmove = true;
}, 20);
});
$id('sidepanel_handle').addEventListener('mouseup', (evt) => {
if (!this.sidedragging) { this.toggleSidePanel(); }
$id("sidepanel_handle").addEventListener("mouseup", evt => {
if (!this.sidedragging) {
this.toggleSidePanel();
}
this.sidedrag = -1;
this.sidedragging = false;
});
window.addEventListener('mouseup', (evt) => {
window.addEventListener("mouseup", evt => {
this.sidedrag = -1;
this.sidedragging = false;
$id('svg_editor').removeEventListener('mousemove', this.resizeSidePanel.bind(this));
$id("svg_editor").removeEventListener(
"mousemove",
this.resizeSidePanel.bind(this)
);
});
}
/**
* @returns {void}
*/
newLayer () {
newLayer() {
let uniqName;
let i = this.svgCanvas.getCurrentDrawing().getNumLayers();
let i = this.editor.svgCanvas.getCurrentDrawing().getNumLayers();
do {
uniqName = this.uiStrings.layers.layer + ' ' + (++i);
} while (this.svgCanvas.getCurrentDrawing().hasLayer(uniqName));
uniqName = this.uiStrings.layers.layer + " " + ++i;
} while (this.editor.svgCanvas.getCurrentDrawing().hasLayer(uniqName));
const newName = prompt(this.uiStrings.notification.enterUniqueLayerName, uniqName);
if (!newName) { return; }
if (this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
const newName = prompt(
this.uiStrings.notification.enterUniqueLayerName,
uniqName
);
if (!newName) {
return;
}
if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
alert(this.uiStrings.notification.dupeLayerName);
return;
}
this.svgCanvas.createLayer(newName);
this.editor.svgCanvas.createLayer(newName);
this.updateContextPanel();
this.populateLayers();
}
@ -169,15 +240,15 @@ class LayersPanel {
*
* @returns {void}
*/
deleteLayer () {
if (this.svgCanvas.deleteCurrentLayer()) {
deleteLayer() {
if (this.editor.svgCanvas.deleteCurrentLayer()) {
this.updateContextPanel();
this.populateLayers();
// This matches what this.svgCanvas does
// This matches what this.editor.svgCanvas does
// TODO: make this behavior less brittle (svg-editor should get which
// layer is selected from the canvas and then select that one in the UI)
$('#layerlist tr.layer').removeClass('layersel');
$('#layerlist tr.layer:first').addClass('layersel');
$("#layerlist tr.layer").removeClass("layersel");
$("#layerlist tr.layer:first").addClass("layersel");
}
}
@ -185,16 +256,22 @@ class LayersPanel {
*
* @returns {void}
*/
cloneLayer () {
const name = this.svgCanvas.getCurrentDrawing().getCurrentLayerName() + ' copy';
cloneLayer() {
const name =
this.editor.svgCanvas.getCurrentDrawing().getCurrentLayerName() + " copy";
const newName = prompt(this.uiStrings.notification.enterUniqueLayerName, name);
if (!newName) { return; }
if (this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
const newName = prompt(
this.uiStrings.notification.enterUniqueLayerName,
name
);
if (!newName) {
return;
}
if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
alert(this.uiStrings.notification.dupeLayerName);
return;
}
this.svgCanvas.cloneLayer(newName);
this.editor.svgCanvas.cloneLayer(newName);
this.updateContextPanel();
this.populateLayers();
}
@ -203,11 +280,14 @@ class LayersPanel {
*
* @returns {void}
*/
mergeLayer () {
if ($('#layerlist tr.layersel').index() === this.svgCanvas.getCurrentDrawing().getNumLayers() - 1) {
mergeLayer() {
if (
$("#layerlist tr.layersel").index() ===
this.editor.svgCanvas.getCurrentDrawing().getNumLayers() - 1
) {
return;
}
this.svgCanvas.mergeLayer();
this.editor.svgCanvas.mergeLayer();
this.updateContextPanel();
this.populateLayers();
}
@ -216,13 +296,13 @@ class LayersPanel {
* @param {Integer} pos
* @returns {void}
*/
moveLayer (pos) {
const total = this.svgCanvas.getCurrentDrawing().getNumLayers();
moveLayer(pos) {
const total = this.editor.svgCanvas.getCurrentDrawing().getNumLayers();
let curIndex = $('#layerlist tr.layersel').index();
let curIndex = $("#layerlist tr.layersel").index();
if (curIndex > 0 || curIndex < total - 1) {
curIndex += pos;
this.svgCanvas.setCurrentLayerPosition(total - curIndex - 1);
this.editor.svgCanvas.setCurrentLayerPosition(total - curIndex - 1);
this.populateLayers();
}
}
@ -230,16 +310,21 @@ class LayersPanel {
/**
* @returns {void}
*/
layerRename () {
// const curIndex = $('#layerlist tr.layersel').prevAll().length; // Currently unused
const oldName = $('#layerlist tr.layersel td.layername').text();
const newName = prompt(this.uiStrings.notification.enterNewLayerName, '');
if (!newName) { return; }
if (oldName === newName || this.svgCanvas.getCurrentDrawing().hasLayer(newName)) {
layerRename() {
// const curIndex = $('#layerlist tr.layersel').prevAll().length; // Currently unused
const oldName = $("#layerlist tr.layersel td.layername").text();
const newName = prompt(this.uiStrings.notification.enterNewLayerName, "");
if (!newName) {
return;
}
if (
oldName === newName ||
this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)
) {
alert(this.uiStrings.notification.layerHasThatName);
return;
}
this.svgCanvas.renameCurrentLayer(newName);
this.editor.svgCanvas.renameCurrentLayer(newName);
this.populateLayers();
}
@ -248,72 +333,86 @@ class LayersPanel {
* If no layer is passed in, this function restores the other layers.
* @param {string} [layerNameToHighlight]
* @returns {void}
*/
toggleHighlightLayer (layerNameToHighlight) {
*/
toggleHighlightLayer(layerNameToHighlight) {
let i;
const curNames = [], numLayers = this.svgCanvas.getCurrentDrawing().getNumLayers();
const curNames = [],
numLayers = this.editor.svgCanvas.getCurrentDrawing().getNumLayers();
for (i = 0; i < numLayers; i++) {
curNames[i] = this.svgCanvas.getCurrentDrawing().getLayerName(i);
curNames[i] = this.editor.svgCanvas.getCurrentDrawing().getLayerName(i);
}
if (layerNameToHighlight) {
curNames.forEach((curName) => {
curNames.forEach(curName => {
if (curName !== layerNameToHighlight) {
this.svgCanvas.getCurrentDrawing().setLayerOpacity(curName, 0.5);
this.editor.svgCanvas
.getCurrentDrawing()
.setLayerOpacity(curName, 0.5);
}
});
} else {
curNames.forEach((curName) => {
this.svgCanvas.getCurrentDrawing().setLayerOpacity(curName, 1.0);
curNames.forEach(curName => {
this.editor.svgCanvas.getCurrentDrawing().setLayerOpacity(curName, 1.0);
});
}
}
/**
* @returns {void}
*/
populateLayers () {
this.svgCanvas.clearSelection();
const layerlist = $('#layerlist tbody').empty();
const selLayerNames = $('#selLayerNames').empty();
const drawing = this.svgCanvas.getCurrentDrawing();
* @returns {void}
*/
populateLayers() {
this.editor.svgCanvas.clearSelection();
const layerlist = $("#layerlist tbody").empty();
const selLayerNames = $("#selLayerNames").empty();
const drawing = this.editor.svgCanvas.getCurrentDrawing();
const currentLayerName = drawing.getCurrentLayerName();
let layer = this.svgCanvas.getCurrentDrawing().getNumLayers();
let layer = this.editor.svgCanvas.getCurrentDrawing().getNumLayers();
// we get the layers in the reverse z-order (the layer rendered on top is listed first)
while (layer--) {
const name = drawing.getLayerName(layer);
const layerTr = $('<tr class="layer">').toggleClass('layersel', name === currentLayerName);
const layerVis = $('<td class="layervis">').toggleClass('layerinvis', !drawing.getLayerVisibility(name));
const layerName = $('<td class="layername">' + name + '</td>');
const layerTr = $('<tr class="layer">').toggleClass(
"layersel",
name === currentLayerName
);
const layerVis = $('<td class="layervis">').toggleClass(
"layerinvis",
!drawing.getLayerVisibility(name)
);
const layerName = $('<td class="layername">' + name + "</td>");
layerlist.append(layerTr.append(layerVis, layerName));
selLayerNames.append('<option value="' + name + '">' + name + '</option>');
selLayerNames.append(
'<option value="' + name + '">' + name + "</option>"
);
}
// handle selection of layer
$('#layerlist td.layername')
.mouseup((evt) => {
$('#layerlist tr.layer').removeClass('layersel');
$(evt.currentTarget.parentNode).addClass('layersel');
this.svgCanvas.setCurrentLayer(evt.currentTarget.textContent);
$("#layerlist td.layername")
.mouseup(evt => {
$("#layerlist tr.layer").removeClass("layersel");
$(evt.currentTarget.parentNode).addClass("layersel");
this.editor.svgCanvas.setCurrentLayer(evt.currentTarget.textContent);
evt.preventDefault();
})
.mouseover((evt) => {
this.toggleHighlightLayer(this.svgCanvas, evt.currentTarget.textContent);
.mouseover(evt => {
this.toggleHighlightLayer(
this.editor.svgCanvas,
evt.currentTarget.textContent
);
})
.mouseout(() => {
this.toggleHighlightLayer(this.svgCanvas);
this.toggleHighlightLayer(this.editor.svgCanvas);
});
$('#layerlist td.layervis').click((evt) => {
$("#layerlist td.layervis").click(evt => {
const row = $(evt.currentTarget.parentNode).prevAll().length;
const name = $('#layerlist tr.layer:eq(' + row + ') td.layername').text();
const vis = $(evt.currentTarget).hasClass('layerinvis');
this.svgCanvas.setLayerVisibility(name, vis);
$(evt.currentTarget).toggleClass('layerinvis');
const name = $("#layerlist tr.layer:eq(" + row + ") td.layername").text();
const vis = $(evt.currentTarget).hasClass("layerinvis");
this.editor.svgCanvas.setLayerVisibility(name, vis);
$(evt.currentTarget).toggleClass("layerinvis");
});
// if there were too few rows, let's add a few to make it not so lonely
let num = 5 - $('#layerlist tr.layer').size();
let num = 5 - $("#layerlist tr.layer").size();
while (num-- > 0) {
// TODO: there must a better way to do this
// TODO: there must a better way to do this
layerlist.append('<tr><td style="color:white">_</td><td/></tr>');
}
}

View File

@ -0,0 +1,253 @@
import SvgCanvas from "../../svgcanvas/svgcanvas.js";
const { $id, $qa } = SvgCanvas;
/*
* register actions for left panel
*/
/**
* @type {module}
*/
class LeftPanel {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor(editor) {
this.editor = editor;
}
/**
* This is a common function used when a tool has been clicked (chosen).
* It does several common things:
* - Removes the pressed button from whatever tool currently has it.
* - Adds the the pressed button to the button passed in.
* @function this.updateLeftPanel
* @param {string|Element} button The DOM element or string selector representing the toolbar button
* @returns {boolean} Whether the button was disabled or not
*/
// eslint-disable-next-line class-methods-use-this
updateLeftPanel(button) {
if (button.disabled) return false;
// remove the pressed state on other(s) button(s)
$qa("#tools_left *[pressed]").forEach(b => {
b.pressed = false;
});
// pressed state for the clicked button
$id(button).pressed = true;
return true;
}
/**
* Unless the select toolbar button is disabled, sets the button
* and sets the select mode and cursor styles.
* @function module:SVGEditor.clickSelect
* @returns {void}
*/
clickSelect() {
if (this.updateLeftPanel("tool_select")) {
this.editor.workarea.style.cursor = "auto";
this.editor.svgCanvas.setMode("select");
}
}
/**
*
* @returns {void}
*/
clickFHPath() {
if (this.updateLeftPanel("tool_fhpath")) {
this.editor.svgCanvas.setMode("fhpath");
}
}
/**
*
* @returns {void}
*/
clickLine() {
if (this.updateLeftPanel("tool_line")) {
this.editor.svgCanvas.setMode("line");
}
}
/**
*
* @returns {void}
*/
clickSquare() {
if (this.updateLeftPanel("tool_square")) {
this.editor.svgCanvas.setMode("square");
}
}
/**
*
* @returns {void}
*/
clickRect() {
if (this.updateLeftPanel("tool_rect")) {
this.editor.svgCanvas.setMode("rect");
}
}
/**
*
* @returns {void}
*/
clickFHRect() {
if (this.updateLeftPanel("tool_fhrect")) {
this.editor.svgCanvas.setMode("fhrect");
}
}
/**
*
* @returns {void}
*/
clickCircle() {
if (this.updateLeftPanel("tool_circle")) {
this.editor.svgCanvas.setMode("circle");
}
}
/**
*
* @returns {void}
*/
clickEllipse() {
if (this.updateLeftPanel("tool_ellipse")) {
this.editor.svgCanvas.setMode("ellipse");
}
}
/**
*
* @returns {void}
*/
clickFHEllipse() {
if (this.updateLeftPanel("tool_fhellipse")) {
this.editor.svgCanvas.setMode("fhellipse");
}
}
/**
*
* @returns {void}
*/
clickImage() {
if (this.updateLeftPanel("tool_image")) {
this.editor.svgCanvas.setMode("image");
}
}
/**
*
* @returns {void}
*/
clickZoom() {
if (this.updateLeftPanel("tool_zoom")) {
this.editor.svgCanvas.setMode("zoom");
this.editor.workarea.style.cursor = this.editor.zoomInIcon;
}
}
/**
*
* @returns {void}
*/
dblclickZoom() {
if (this.updateLeftPanel("tool_zoom")) {
this.editor.zoomImage();
this.clickSelect();
}
}
/**
*
* @returns {void}
*/
clickText() {
if (this.updateLeftPanel("tool_text")) {
this.editor.svgCanvas.setMode("text");
}
}
/**
*
* @returns {void}
*/
clickPath() {
if (this.updateLeftPanel("tool_path")) {
this.editor.svgCanvas.setMode("path");
}
}
/**
* @type {module}
*/
add(id, handler) {
$id(id).addEventListener("click", () => {
if (this.updateLeftPanel(id)) {
handler();
}
});
}
/**
* @type {module}
*/
init() {
// add Left panel
const template = document.createElement("template");
template.innerHTML = `
<div id="tools_left">
<se-button id="tool_select" title="Select Tool" src="./images/select.svg"></se-button>
<se-button id="tool_zoom" title="Zoom Tool" src="./images/zoom.svg" shortcut="Z"></se-button>
<se-button id="ext-panning" title="Panning" src="./images/panning.svg"></se-button>
<se-button id="tool_fhpath" title="Pencil Tool" src="./images/pencil.svg" shortcut="Q"></se-button>
<se-button id="tool_line" title="Line Tool" src="./images/pen.svg" shortcut="L"></se-button>
<se-button id="tool_path" title="Path Tool" src="./images/path.svg" shortcut="P"></se-button>
<se-flyingbutton id="tools_rect" title="Square/Rect Tool">
<se-button id="tool_rect" title="Rectangle" src="./images/rect.svg" shortcut="R"></se-button>
<se-button id="tool_square" title="Square" src="./images/square.svg"></se-button>
<se-button id="tool_fhrect" title="Free-Hand Rectangle" src="./images/fh_rect.svg"></se-button>
</se-flyingbutton>
<se-flyingbutton id="tools_ellipse" title="Ellipse/Circle Tool">
<se-button id="tool_ellipse" title="Rectangle" src="./images/ellipse.svg" shortcut="E"></se-button>
<se-button id="tool_circle" title="Square" src="./images/circle.svg"></se-button>
<se-button id="tool_fhellipse" title="Free-Hand Rectangle" src="./images/fh_ellipse.svg"></se-button>
</se-flyingbutton>
<se-flyingbutton id="tools_polygon" title="Polygone/Star Tool">
<se-button id="tool_polygon" title="Polygon Tool" src="./images/polygon.svg"></se-button>
<se-button id="tool_star" title="Star Tool" src="./images/star.svg"></se-button>
</se-flyingbutton>
<se-button id="mode_connect" title="Connect two objects" src="./images/conn.svg"></se-button>
<se-button id="tool_text" title="Text Tool" src="./images/text.svg" shortcut="T"></se-button>
<se-button id="tool_image" title="Image Tool" src="./images/image.svg"></se-button>
<se-button id="tool_eyedropper" title="Eye Dropper Tool" src="./images/eye_dropper.svg" shortcut="I"></se-button>
<se-explorerbutton id="tool_shapelib" title="Shape library" lib="./extensions/ext-shapes/shapelib/"
src="./images/shapelib.svg"></se-explorerbutton>
</div> <!-- tools_left -->
`;
this.editor.$svgEditor.append(template.content.cloneNode(true));
// register actions for left panel
$id("tool_select").addEventListener("click", this.clickSelect.bind(this));
$id("tool_fhpath").addEventListener("click", this.clickFHPath.bind(this));
$id("tool_text").addEventListener("click", this.clickText.bind(this));
$id("tool_image").addEventListener("click", this.clickImage.bind(this));
$id("tool_zoom").addEventListener("click", this.clickZoom.bind(this));
$id("tool_zoom").addEventListener("dblclick", this.dblclickZoom.bind(this));
$id("tool_path").addEventListener("click", this.clickPath.bind(this));
$id("tool_line").addEventListener("click", this.clickLine.bind(this));
// flyout
$id("tool_rect").addEventListener("click", this.clickRect.bind(this));
$id("tool_square").addEventListener("click", this.clickSquare.bind(this));
$id("tool_fhrect").addEventListener("click", this.clickFHRect.bind(this));
$id("tool_ellipse").addEventListener("click", this.clickEllipse.bind(this));
$id("tool_circle").addEventListener("click", this.clickCircle.bind(this));
$id("tool_fhellipse").addEventListener(
"click",
this.clickFHEllipse.bind(this)
);
}
}
export default LeftPanel;

View File

@ -1,216 +0,0 @@
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
const {$id, $qa} = SvgCanvas;
/*
* register actions for left panel
*/
/**
* @type {module}
*/
class LeftPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
}
/**
* This is a common function used when a tool has been clicked (chosen).
* It does several common things:
* - Removes the pressed button from whatever tool currently has it.
* - Adds the the pressed button to the button passed in.
* @function this.updateLeftPanel
* @param {string|Element} button The DOM element or string selector representing the toolbar button
* @returns {boolean} Whether the button was disabled or not
*/
// eslint-disable-next-line class-methods-use-this
updateLeftPanel (button) {
if (button.disabled) return false;
// remove the pressed state on other(s) button(s)
$qa('#tools_left *[pressed]').forEach((b) => { b.pressed = false; });
// pressed state for the clicked button
$id(button).pressed = true;
return true;
}
/**
* Unless the select toolbar button is disabled, sets the button
* and sets the select mode and cursor styles.
* @function module:SVGEditor.clickSelect
* @returns {void}
*/
clickSelect () {
if (this.updateLeftPanel('tool_select')) {
this.editor.workarea.style.cursor = "auto";
this.svgCanvas.setMode('select');
}
}
/**
*
* @returns {void}
*/
clickFHPath () {
if (this.updateLeftPanel('tool_fhpath')) {
this.svgCanvas.setMode('fhpath');
}
}
/**
*
* @returns {void}
*/
clickLine () {
if (this.updateLeftPanel('tool_line')) {
this.svgCanvas.setMode('line');
}
}
/**
*
* @returns {void}
*/
clickSquare () {
if (this.updateLeftPanel('tool_square')) {
this.svgCanvas.setMode('square');
}
}
/**
*
* @returns {void}
*/
clickRect () {
if (this.updateLeftPanel('tool_rect')) {
this.svgCanvas.setMode('rect');
}
}
/**
*
* @returns {void}
*/
clickFHRect () {
if (this.updateLeftPanel('tool_fhrect')) {
this.svgCanvas.setMode('fhrect');
}
}
/**
*
* @returns {void}
*/
clickCircle () {
if (this.updateLeftPanel('tool_circle')) {
this.svgCanvas.setMode('circle');
}
}
/**
*
* @returns {void}
*/
clickEllipse () {
if (this.updateLeftPanel('tool_ellipse')) {
this.svgCanvas.setMode('ellipse');
}
}
/**
*
* @returns {void}
*/
clickFHEllipse () {
if (this.updateLeftPanel('tool_fhellipse')) {
this.svgCanvas.setMode('fhellipse');
}
}
/**
*
* @returns {void}
*/
clickImage () {
if (this.updateLeftPanel('tool_image')) {
this.svgCanvas.setMode('image');
}
}
/**
*
* @returns {void}
*/
clickZoom () {
if (this.updateLeftPanel('tool_zoom')) {
this.svgCanvas.setMode('zoom');
this.editor.workarea.style.cursor = this.editor.zoomInIcon;
}
}
/**
*
* @returns {void}
*/
dblclickZoom () {
if (this.updateLeftPanel('tool_zoom')) {
this.editor.zoomImage();
this.clickSelect();
}
}
/**
*
* @returns {void}
*/
clickText () {
if (this.updateLeftPanel('tool_text')) {
this.svgCanvas.setMode('text');
}
}
/**
*
* @returns {void}
*/
clickPath () {
if (this.updateLeftPanel('tool_path')) {
this.svgCanvas.setMode('path');
}
}
/**
* @type {module}
*/
add (id, handler) {
$id(id).addEventListener('click', () => {
if (this.updateLeftPanel(id)) {
handler();
}
});
}
/**
* @type {module}
*/
init () {
// register actions for left panel
$id('tool_select').addEventListener('click', this.clickSelect.bind(this));
$id('tool_fhpath').addEventListener('click', this.clickFHPath.bind(this));
$id('tool_text').addEventListener('click', this.clickText.bind(this));
$id('tool_image').addEventListener('click', this.clickImage.bind(this));
$id('tool_zoom').addEventListener('click', this.clickZoom.bind(this));
$id('tool_zoom').addEventListener('dblclick', this.dblclickZoom.bind(this));
$id('tool_path').addEventListener('click', this.clickPath.bind(this));
$id('tool_line').addEventListener('click', this.clickLine.bind(this));
// flyout
$id('tool_rect').addEventListener('click', this.clickRect.bind(this));
$id('tool_square').addEventListener('click', this.clickSquare.bind(this));
$id('tool_fhrect').addEventListener('click', this.clickFHRect.bind(this));
$id('tool_ellipse').addEventListener('click', this.clickEllipse.bind(this));
$id('tool_circle').addEventListener('click', this.clickCircle.bind(this));
$id('tool_fhellipse').addEventListener('click', this.clickFHEllipse.bind(this));
}
}
export default LeftPanelHandlers;

File diff suppressed because it is too large Load Diff

View File

@ -1,645 +0,0 @@
/* globals $ */
import SvgCanvas from '../../svgcanvas/svgcanvas.js';
import {isValidUnit, getTypeMap, convertUnit} from '../../common/units.js';
const {$id, isNullish} = SvgCanvas;
/*
* register actions for left panel
*/
/**
*
*/
class TopPanelHandlers {
/**
* @param {PlainObject} editor svgedit handler
*/
constructor (editor) {
this.editor = editor;
this.svgCanvas = editor.svgCanvas;
this.uiStrings = editor.uiStrings;
}
/**
* @type {module}
*/
get selectedElement () {
return this.editor.selectedElement;
}
/**
* @type {module}
*/
get multiselected () {
return this.editor.multiselected;
}
/**
* @type {module}
*/
get path () {
return this.svgCanvas.pathActions;
}
/**
* @param {PlainObject} [opts={}]
* @param {boolean} [opts.cancelDeletes=false]
* @returns {void} Resolves to `undefined`
*/
promptImgURL ({cancelDeletes = false} = {}) {
let curhref = this.svgCanvas.getHref(this.selectedElement);
curhref = curhref.startsWith('data:') ? '' : curhref;
// eslint-disable-next-line no-alert
const url = prompt(this.editor.uiStrings.notification.enterNewImgURL, curhref);
if (url) {
this.editor.setImageURL(url);
} else if (cancelDeletes) {
this.svgCanvas.deleteSelectedElements();
}
}
/**
* Updates the context panel tools based on the selected element.
* @returns {void}
*/
updateContextPanel () {
const setInputWidth = (elem) => {
const w = Math.min(Math.max(12 + elem.value.length * 6, 50), 300);
$(elem).width(w);
};
let elem = this.selectedElement;
// If element has just been deleted, consider it null
if (!isNullish(elem) && !elem.parentNode) { elem = null; }
const currentLayerName = this.svgCanvas.getCurrentDrawing().getCurrentLayerName();
const currentMode = this.svgCanvas.getMode();
const unit = this.editor.configObj.curConfig.baseUnit !== 'px' ? this.editor.configObj.curConfig.baseUnit : null;
const isNode = currentMode === 'pathedit'; // elem ? (elem.id && elem.id.startsWith('pathpointgrip')) : false;
const menuItems = document.getElementById('se-cmenu_canvas');
$('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' +
'#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' +
' #use_panel, #a_panel').hide();
if (!isNullish(elem)) {
const elname = elem.nodeName;
// If this is a link with no transform and one child, pretend
// its child is selected
// if (elname === 'a') { // && !$(elem).attr('transform')) {
// elem = elem.firstChild;
// }
const angle = this.svgCanvas.getRotationAngle(elem);
$('#angle').val(angle);
const blurval = this.svgCanvas.getBlur(elem) * 10;
$id('blur').value = blurval;
if (this.svgCanvas.addedNew &&
elname === 'image' &&
this.svgCanvas.getMode() === 'image' &&
!this.svgCanvas.getHref(elem).startsWith('data:')) {
/* await */ this.promptImgURL({cancelDeletes: true});
}
if (!isNode && currentMode !== 'pathedit') {
$('#selected_panel').show();
// Elements in this array already have coord fields
if (['line', 'circle', 'ellipse'].includes(elname)) {
$('#xy_panel').hide();
} else {
let x, y;
// Get BBox vals for g, polyline and path
if (['g', 'polyline', 'path'].includes(elname)) {
const bb = this.svgCanvas.getStrokedBBox([elem]);
if (bb) {
({x, y} = bb);
}
} else {
x = elem.getAttribute('x');
y = elem.getAttribute('y');
}
if (unit) {
x = convertUnit(x);
y = convertUnit(y);
}
$('#selected_x').val(x || 0);
$('#selected_y').val(y || 0);
$('#xy_panel').show();
}
// Elements in this array cannot be converted to a path
$id('tool_topath').style.display = ['image', 'text', 'path', 'g', 'use'].includes(elname) ? 'none' : 'block';
$id('tool_reorient').style.display = (elname === 'path') ? 'block' : 'none';
$id('tool_reorient').disabled = (angle === 0);
} else {
const point = this.path.getNodePoint();
$('#tool_add_subpath').pressed = false;
$('#tool_node_delete').toggleClass('disabled', !this.path.canDeleteNodes);
// Show open/close button based on selected point
// setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path');
if (point) {
const segType = $('#seg_type');
if (unit) {
point.x = convertUnit(point.x);
point.y = convertUnit(point.y);
}
$('#path_node_x').val(point.x);
$('#path_node_y').val(point.y);
if (point.type) {
segType.val(point.type).removeAttr('disabled');
} else {
segType.val(4).attr('disabled', 'disabled');
}
}
return;
}
// update contextual tools here
const panels = {
g: [],
a: [],
rect: ['rx', 'width', 'height'],
image: ['width', 'height'],
circle: ['cx', 'cy', 'r'],
ellipse: ['cx', 'cy', 'rx', 'ry'],
line: ['x1', 'y1', 'x2', 'y2'],
text: [],
use: []
};
const {tagName} = elem;
// if ($(elem).data('gsvg')) {
// $('#g_panel').show();
// }
let linkHref = null;
if (tagName === 'a') {
linkHref = this.svgCanvas.getHref(elem);
$('#g_panel').show();
}
if (elem.parentNode.tagName === 'a' && !$(elem).siblings().length) {
$('#a_panel').show();
linkHref = this.svgCanvas.getHref(elem.parentNode);
}
// Hide/show the make_link buttons
$('#tool_make_link, #tool_make_link_multi').toggle(!linkHref);
if (linkHref) {
$('#link_url').val(linkHref);
}
if (panels[tagName]) {
const curPanel = panels[tagName];
$('#' + tagName + '_panel').show();
curPanel.forEach((item) => {
let attrVal = elem.getAttribute(item);
if (this.editor.configObj.curConfig.baseUnit !== 'px' && elem[item]) {
const bv = elem[item].baseVal.value;
attrVal = convertUnit(bv);
}
$id(`${tagName}_${item}`).value = attrVal || 0;
});
if (tagName === 'text') {
$('#text_panel').css('display', 'inline-block');
$('#tool_font_size').css('display', 'inline');
$id('tool_italic').pressed = this.svgCanvas.getItalic();
$id('tool_bold').pressed = this.svgCanvas.getBold();
$('#tool_font_family').val(elem.getAttribute('font-family'));
$('#font_size').val(elem.getAttribute('font-size'));
$('#text').val(elem.textContent);
const textAnchorStart = $id('tool_text_anchor_start');
const textAnchorMiddle = $id('tool_text_anchor_middle');
const textAnchorEnd = $id('tool_text_anchor_end');
switch (elem.getAttribute('text-anchor')) {
case 'start':
textAnchorStart.pressed = true;
textAnchorMiddle.pressed = false;
textAnchorEnd.pressed = false;
break;
case 'middle':
textAnchorStart.pressed = false;
textAnchorMiddle.pressed = true;
textAnchorEnd.pressed = false;
break;
case 'end':
textAnchorStart.pressed = false;
textAnchorMiddle.pressed = false;
textAnchorEnd.pressed = true;
break;
}
if (this.svgCanvas.addedNew) {
// Timeout needed for IE9
setTimeout(() => {
$('#text').focus().select();
}, 100);
}
// text
} else if (tagName === 'image' && this.svgCanvas.getMode() === 'image') {
this.svgCanvas.setImageURL(this.svgCanvas.getHref(elem));
// image
} else if (tagName === 'g' || tagName === 'use') {
$('#container_panel').show();
const title = this.svgCanvas.getTitle();
const label = $('#g_title')[0];
label.value = title;
setInputWidth(label);
$('#g_title').prop('disabled', tagName === 'use');
}
}
menuItems.setAttribute((tagName === 'g' ? 'en' : 'dis') + 'ablemenuitems', '#ungroup');
menuItems.setAttribute(((tagName === 'g' || !this.multiselected) ? 'dis' : 'en') + 'ablemenuitems', '#group');
// if (!isNullish(elem))
} else if (this.multiselected) {
$('#multiselected_panel').show();
menuItems.setAttribute('enablemenuitems', '#group');
menuItems.setAttribute('disablemenuitems', '#ungroup');
} else {
menuItems.setAttribute('disablemenuitems', '#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back');
}
// update history buttons
$id('tool_undo').disabled = (this.svgCanvas.undoMgr.getUndoStackSize() === 0);
$id('tool_redo').disabled = (this.svgCanvas.undoMgr.getRedoStackSize() === 0);
this.svgCanvas.addedNew = false;
if ((elem && !isNode) || this.multiselected) {
// update the selected elements' layer
$('#selLayerNames').removeAttr('disabled').val(currentLayerName);
// Enable regular menu options
const canCMenu = document.getElementById('se-cmenu_canvas');
canCMenu.setAttribute('enablemenuitems', '#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back');
} else {
$('#selLayerNames').attr('disabled', 'disabled');
}
}
/**
* @param {Event} [e] Not used.
* @param {boolean} forSaving
* @returns {void}
*/
showSourceEditor (e, forSaving) {
const $editorDialog = document.getElementById('se-svg-editor-dialog');
if ($editorDialog.getAttribute('dialog') === 'open') return;
const origSource = this.svgCanvas.getSvgString();
$editorDialog.setAttribute('dialog', 'open');
$editorDialog.setAttribute('value', origSource);
$editorDialog.setAttribute('copysec', Boolean(forSaving));
$editorDialog.setAttribute('applysec', !forSaving);
}
/**
*
* @returns {void}
*/
clickWireframe () {
$id('tool_wireframe').pressed = !$id('tool_wireframe').pressed;
this.editor.workarea.classList.toggle('wireframe');
const wfRules = $('#wireframe_rules');
if (!wfRules.length) {
/* wfRules = */ $('<style id="wireframe_rules"></style>').appendTo('head');
} else {
wfRules.empty();
}
this.editor.updateWireFrame();
}
/**
*
* @returns {void}
*/
clickUndo () {
const {undoMgr} = this.editor.svgCanvas;
if (undoMgr.getUndoStackSize() > 0) {
undoMgr.undo();
this.editor.layersPanel.populateLayers();
}
}
/**
*
* @returns {void}
*/
clickRedo () {
const {undoMgr} = this.editor.svgCanvas;
if (undoMgr.getRedoStackSize() > 0) {
undoMgr.redo();
this.editor.layersPanel.populateLayers();
}
}
/**
* @type {module}
*/
changeRectRadius (e) {
this.svgCanvas.setRectRadius(e.target.value);
}
/**
* @type {module}
*/
changeFontSize (e) {
this.svgCanvas.setFontSize(e.target.value);
}
/**
* @type {module}
*/
changeRotationAngle (e) {
this.svgCanvas.setRotationAngle(e.target.value);
$('#tool_reorient').toggleClass('disabled', Number.parseInt(e.target.value) === 0);
}
/**
* @param {PlainObject} e
* @returns {void}
*/
changeBlur (e) {
this.svgCanvas.setBlur(e.target.value / 10, true);
}
/**
*
* @returns {void}
*/
clickGroup () {
// group
if (this.editor.multiselected) {
this.svgCanvas.groupSelectedElements();
// ungroup
} else if (this.editor.selectedElement) {
this.svgCanvas.ungroupSelectedElement();
}
}
/**
*
* @returns {void}
*/
clickClone () {
this.svgCanvas.cloneSelectedElements(20, 20);
}
/**
* @param {PlainObject} evt
* @returns {void}
*/
clickAlignEle (evt) {
this.svgCanvas.alignSelectedElements(evt.detail.value, 'page');
}
/**
* @param {string} pos indicate the alignment relative to top, bottom, middle etc..
* @returns {void}
*/
clickAlign (pos) {
let value = $('#tool_align_relative').val();
if(value === ''){
value = 'selected';
}
this.svgCanvas.alignSelectedElements(pos, value);
}
/**
*
* @type {module}
*/
attrChanger (e) {
const attr = e.target.getAttribute('data-attr');
let val = e.target.value;
const valid = isValidUnit(attr, val, this.selectedElement);
if (!valid) {
e.target.value = this.selectedElement().getAttribute(attr);
// eslint-disable-next-line no-alert
alert(this.uiStrings.notification.invalidAttrValGiven);
return false;
}
if (attr !== 'id' && attr !== 'class') {
if (isNaN(val)) {
val = this.svgCanvas.convertToNum(attr, val);
} else if (this.editor.configObj.curConfig.baseUnit !== 'px') {
// Convert unitless value to one with given unit
const unitData = getTypeMap();
if (this.selectedElement[attr] || this.svgCanvas.getMode() === 'pathedit' || attr === 'x' || attr === 'y') {
val *= unitData[this.editor.configObj.curConfig.baseUnit];
}
}
}
// if the user is changing the id, then de-select the element first
// change the ID, then re-select it with the new ID
if (attr === 'id') {
const elem = this.selectedElement;
this.svgCanvas.clearSelection();
elem.id = val;
this.svgCanvas.addToSelection([elem], true);
} else {
this.svgCanvas.changeSelectedAttribute(attr, val);
}
return true;
}
/**
*
* @returns {void}
*/
convertToPath () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.convertToPath();
}
}
/**
*
* @returns {void}
*/
reorientPath () {
if (!isNullish(this.selectedElement)) {
this.path.reorient();
}
}
/**
*
* @returns {void} Resolves to `undefined`
*/
makeHyperlink () {
if (!isNullish(this.selectedElement) || this.multiselected) {
// eslint-disable-next-line no-alert
const url = prompt(this.uiStrings.notification.enterNewLinkURL, 'http://');
if (url) {
this.svgCanvas.makeHyperlink(url);
}
}
}
/**
*
* @returns {void}
*/
linkControlPoints () {
const linked = $id('tool_node_link').pressed;
$id('tool_node_link').pressed = !linked;
this.path.linkControlPoints(linked);
}
/**
*
* @returns {void}
*/
clonePathNode () {
if (this.path.getNodePoint()) {
this.path.clonePathNode();
}
}
/**
*
* @returns {void}
*/
deletePathNode () {
if (this.path.getNodePoint()) {
this.path.deletePathNode();
}
}
/**
*
* @returns {void}
*/
addSubPath () {
const button = $('#tool_add_subpath');
const sp = !button.hasClass('pressed');
button.pressed = sp;
// button.toggleClass('push_button_pressed tool_button');
this.path.addSubPath(sp);
}
/**
*
* @returns {void}
*/
opencloseSubPath () {
this.path.opencloseSubPath();
}
/**
* Delete is a contextual tool that only appears in the ribbon if
* an element has been selected.
* @returns {void}
*/
deleteSelected () {
if (!isNullish(this.selectedElement) || this.multiselected) {
this.svgCanvas.deleteSelectedElements();
}
}
/**
*
* @returns {void}
*/
moveToTopSelected () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.moveToTopSelectedElement();
}
}
/**
*
* @returns {void}
*/
moveToBottomSelected () {
if (!isNullish(this.selectedElement)) {
this.svgCanvas.moveToBottomSelectedElement();
}
}
/**
*
* @returns {false}
*/
clickBold () {
this.svgCanvas.setBold(!this.svgCanvas.getBold());
this.updateContextPanel();
return false;
}
/**
*
* @returns {false}
*/
clickItalic () {
this.svgCanvas.setItalic(!this.svgCanvas.getItalic());
this.updateContextPanel();
return false;
}
/**
*
* @param {string} value "start","end" or "middle"
* @returns {false}
*/
clickTextAnchor (value) {
this.svgCanvas.setTextAnchor(value);
this.updateContextPanel();
return false;
}
/**
* @type {module}
*/
init () {
// svg editor source dialoag added to DOM
const newSeEditorDialog = document.createElement('se-svg-source-editor-dialog');
newSeEditorDialog.setAttribute('id', 'se-svg-editor-dialog');
document.body.append(newSeEditorDialog);
// register action to top panel buttons
$id('tool_source').addEventListener('click', this.showSourceEditor.bind(this));
$id('tool_wireframe').addEventListener('click', this.clickWireframe.bind(this));
$id('tool_undo').addEventListener('click', this.clickUndo.bind(this));
$id('tool_redo').addEventListener('click', this.clickRedo.bind(this));
$id('tool_clone').addEventListener('click', this.clickClone.bind(this));
$id('tool_clone_multi').addEventListener('click', this.clickClone.bind(this));
$id('tool_delete').addEventListener('click', this.deleteSelected.bind(this));
$id('tool_delete_multi').addEventListener('click', this.deleteSelected.bind(this));
$id('tool_move_top').addEventListener('click', this.moveToTopSelected.bind(this));
$id('tool_move_bottom').addEventListener('click', this.moveToBottomSelected.bind(this));
$id('tool_topath').addEventListener('click', this.convertToPath.bind(this));
$id('tool_make_link').addEventListener('click', this.makeHyperlink.bind(this));
$id('tool_make_link_multi').addEventListener('click', this.makeHyperlink.bind(this));
$id('tool_reorient').addEventListener('click', this.reorientPath.bind(this));
$id('tool_group_elements').addEventListener('click', this.clickGroup.bind(this));
$id('tool_position').addEventListener('change', (evt) => this.clickAlignEle.bind(this)(evt));
$id('tool_align_left').addEventListener('click', () => this.clickAlign.bind(this)('left'));
$id('tool_align_right').addEventListener('click', () => this.clickAlign.bind(this)('right'));
$id('tool_align_center').addEventListener('click', () => this.clickAlign.bind(this)('center'));
$id('tool_align_top').addEventListener('click', () => this.clickAlign.bind(this)('top'));
$id('tool_align_bottom').addEventListener('click', () => this.clickAlign.bind(this)('bottom'));
$id('tool_align_middle').addEventListener('click', () => this.clickAlign.bind(this)('middle'));
$id('tool_node_clone').addEventListener('click', this.clonePathNode.bind(this));
$id('tool_node_delete').addEventListener('click', this.deletePathNode.bind(this));
$id('tool_openclose_path').addEventListener('click', this.opencloseSubPath.bind(this));
$id('tool_add_subpath').addEventListener('click', this.addSubPath.bind(this));
$id('tool_node_link').addEventListener('click', this.linkControlPoints.bind(this));
$id('angle').addEventListener('change', this.changeRotationAngle.bind(this));
$id('blur').addEventListener('change', this.changeBlur.bind(this));
$id('rect_rx').addEventListener('change', this.changeRectRadius.bind(this));
$id('font_size').addEventListener('change', this.changeFontSize.bind(this));
$id('tool_ungroup').addEventListener('click', this.clickGroup.bind(this));
$id('tool_bold').addEventListener('click', this.clickBold.bind(this));
$id('tool_italic').addEventListener('click', this.clickItalic.bind(this));
$id('tool_text_anchor_start').addEventListener('click', () => this.clickTextAnchor.bind(this)('start'));
$id('tool_text_anchor_middle').addEventListener('click', () => this.clickTextAnchor.bind(this)('middle'));
$id('tool_text_anchor_end').addEventListener('click', () => this.clickTextAnchor.bind(this)('end'));
$id('tool_unlink_use').addEventListener('click', this.clickGroup.bind(this));
$id('change_image_url').addEventListener('click', this.promptImgURL.bind(this));
// all top panel attributes
['elem_id', 'elem_class', 'circle_cx', 'circle_cy', 'circle_r', 'ellipse_cx',
'ellipse_cy', 'ellipse_rx', 'ellipse_ry', 'selected_x', 'selected_y', 'rect_width',
'rect_height', 'line_x1', 'line_x2', 'line_y2', 'image_width', 'image_height', 'path_node_x',
'path_node_y'].forEach((attrId) => $id(attrId).addEventListener('change', this.attrChanger.bind(this)));
}
}
export default TopPanelHandlers;