Add document title in topbar and allow params with user extensions (#720)

* use document title to display the current file
* add document title in top bar
master
JFH 2022-01-31 23:36:57 +01:00 committed by GitHub
parent 5d1ba0205b
commit db8522fcb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 654 additions and 507 deletions

View File

@ -1,4 +1,7 @@
# SVG-Edit CHANGES
## 7.1.2
- add the current document title in the toolbar
- allow user extensions to define optional parameters
## 7.1.1
- Fix an issue when moving a text with an existing transformation (issue #689)
## 7.1.0

147
package-lock.json generated
View File

@ -11,12 +11,12 @@
"dependencies": {
"@babel/polyfill": "7.12.1",
"browser-fs-access": "0.24.0",
"canvg": "3.0.9",
"canvg": "3.0.10",
"core-js": "3.20.3",
"elix": "15.0.1",
"html2canvas": "1.4.1",
"i18next": "21.6.7",
"jspdf": "2.5.0",
"i18next": "21.6.10",
"jspdf": "2.5.1",
"pathseg": "1.2.1",
"regenerator-runtime": "0.13.9",
"rollup-plugin-polyfill-node": "0.8.0",
@ -36,16 +36,16 @@
"@rollup/plugin-replace": "3.0.1",
"@rollup/plugin-url": "6.1.0",
"@web/dev-server": "0.1.29",
"@web/dev-server-rollup": "0.3.14",
"@web/dev-server-rollup": "0.3.15",
"babel-plugin-transform-object-rest-spread": "7.0.0-beta.3",
"copyfiles": "2.4.1",
"core-js-bundle": "3.20.3",
"cp-cli": "2.0.0",
"cypress": "9.3.1",
"cypress": "9.4.1",
"cypress-multi-reporters": "1.5.0",
"cypress-plugin-snapshots": "1.4.4",
"jamilih": "0.54.0",
"jsdoc": "3.6.9",
"jsdoc": "3.6.10",
"node-static": "0.7.11",
"npm-run-all": "4.1.5",
"nyc": "15.1.0",
@ -56,10 +56,10 @@
"remark-cli": "10.0.1",
"remark-lint-ordered-list-marker-value": "3.1.1",
"rimraf": "3.0.2",
"rollup": "2.66.0",
"rollup": "2.66.1",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.2",
"rollup-plugin-html": "^0.2.1",
"rollup-plugin-html": "0.2.1",
"rollup-plugin-node-polyfills": "0.2.1",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-re": "1.0.7",
@ -4442,16 +4442,16 @@
}
},
"node_modules/@web/dev-server-rollup": {
"version": "0.3.14",
"resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.3.14.tgz",
"integrity": "sha512-nx55QXh0e3ZtEFQcQ17n2nWIK1H7uGG0OC1S1PnT1akg0eCYMwHgeJe35hKnXecw8YVY93KsvgS/aKAV+on5Iw==",
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.3.15.tgz",
"integrity": "sha512-hhxvBmNIY19vXeocYB1IBOuhpVpy1L7jbwBarmvC0QJKZsgkxssNTzXJ8iga70c2+H0c/rBz1xUaKuAcov0uOA==",
"dev": true,
"dependencies": {
"@rollup/plugin-node-resolve": "^11.0.1",
"@web/dev-server-core": "^0.3.16",
"nanocolors": "^0.2.1",
"parse5": "^6.0.1",
"rollup": "^2.58.0",
"rollup": "^2.66.1",
"whatwg-url": "^11.0.0"
},
"engines": {
@ -6072,9 +6072,9 @@
}
},
"node_modules/canvg": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.9.tgz",
"integrity": "sha512-rDXcnRPuz4QHoCilMeoTxql+fvGqNAxp+qV/KHD8rOiJSAfVjFclbdUNHD2Uqfthr+VMg17bD2bVuk6F07oLGw==",
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
@ -7211,9 +7211,9 @@
"dev": true
},
"node_modules/cypress": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz",
"integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==",
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-9.4.1.tgz",
"integrity": "sha512-+JgMG9uT+QFx97JU9kOHE3jO3+0UdkQ9H1oCBiC7A74qme7Jkdy2sYDBCPjjGczutnWnGUTMRlwiNMP/Uq6LrQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@ -7254,10 +7254,10 @@
"pretty-bytes": "^5.6.0",
"proxy-from-env": "1.0.0",
"request-progress": "^3.0.0",
"semver": "^7.3.2",
"supports-color": "^8.1.1",
"tmp": "~0.2.1",
"untildify": "^4.0.0",
"url": "^0.11.0",
"yauzl": "^2.10.0"
},
"bin": {
@ -7506,6 +7506,21 @@
"node": ">=6"
}
},
"node_modules/cypress/node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/cypress/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@ -10548,9 +10563,23 @@
}
},
"node_modules/i18next": {
"version": "21.6.7",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.7.tgz",
"integrity": "sha512-26dTDa2gBz+vMk6WPf1pxTx3S5HIAptbyODmni/JsN6R1W2WNkGVFXBusUK7T6y1wLeJi5CIrqmQ2gl18vdh3A==",
"version": "21.6.10",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.10.tgz",
"integrity": "sha512-Xw+tEGQ61BF6SXtBlFffhM/YhJKHZf2cyDrcNK/l2dE6yVbkPkSasC3VhkAsHXX30vUJ0yG04WIUtf7UvwjOxg==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"dependencies": {
"@babel/runtime": "^7.12.0"
}
@ -11654,11 +11683,10 @@
"dev": true
},
"node_modules/jsdoc": {
"version": "3.6.9",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.9.tgz",
"integrity": "sha512-bVrM2DT2iLmv6jd2IdTRk67tC4iaSDUicD+47y+cNCYlE8dccd4xZnlANG4M+OmGyV389bABSTKKfoPCOofbKw==",
"version": "3.6.10",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz",
"integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@babel/parser": "^7.9.4",
"@types/markdown-it": "^12.2.3",
@ -11861,9 +11889,9 @@
}
},
"node_modules/jspdf": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.0.tgz",
"integrity": "sha512-XT0E2m8A9P1xl7ItA2OUbmhokzbDQEyZEdWQZD2olADiTiBEZGDRiK1J1zWxBRUG2KezQJOZq//GYZTkvEZuJg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
"integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
"dependencies": {
"@babel/runtime": "^7.14.0",
"atob": "^2.1.2",
@ -18456,9 +18484,9 @@
}
},
"node_modules/rollup": {
"version": "2.66.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.66.0.tgz",
"integrity": "sha512-L6mKOkdyP8HK5kKJXaiWG7KZDumPJjuo1P+cfyHOJPNNTK3Moe7zCH5+fy7v8pVmHXtlxorzaBjvkBMB23s98g==",
"version": "2.66.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.66.1.tgz",
"integrity": "sha512-crSgLhSkLMnKr4s9iZ/1qJCplgAgrRY+igWv8KhG/AjKOJ0YX/WpmANyn8oxrw+zenF3BXWDLa7Xl/QZISH+7w==",
"bin": {
"rollup": "dist/bin/rollup"
},
@ -26746,16 +26774,16 @@
}
},
"@web/dev-server-rollup": {
"version": "0.3.14",
"resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.3.14.tgz",
"integrity": "sha512-nx55QXh0e3ZtEFQcQ17n2nWIK1H7uGG0OC1S1PnT1akg0eCYMwHgeJe35hKnXecw8YVY93KsvgS/aKAV+on5Iw==",
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.3.15.tgz",
"integrity": "sha512-hhxvBmNIY19vXeocYB1IBOuhpVpy1L7jbwBarmvC0QJKZsgkxssNTzXJ8iga70c2+H0c/rBz1xUaKuAcov0uOA==",
"dev": true,
"requires": {
"@rollup/plugin-node-resolve": "^11.0.1",
"@web/dev-server-core": "^0.3.16",
"nanocolors": "^0.2.1",
"parse5": "^6.0.1",
"rollup": "^2.58.0",
"rollup": "^2.66.1",
"whatwg-url": "^11.0.0"
},
"dependencies": {
@ -28049,9 +28077,9 @@
"dev": true
},
"canvg": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.9.tgz",
"integrity": "sha512-rDXcnRPuz4QHoCilMeoTxql+fvGqNAxp+qV/KHD8rOiJSAfVjFclbdUNHD2Uqfthr+VMg17bD2bVuk6F07oLGw==",
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
"requires": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
@ -28977,9 +29005,9 @@
}
},
"cypress": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz",
"integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==",
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-9.4.1.tgz",
"integrity": "sha512-+JgMG9uT+QFx97JU9kOHE3jO3+0UdkQ9H1oCBiC7A74qme7Jkdy2sYDBCPjjGczutnWnGUTMRlwiNMP/Uq6LrQ==",
"dev": true,
"requires": {
"@cypress/request": "^2.88.10",
@ -29019,10 +29047,10 @@
"pretty-bytes": "^5.6.0",
"proxy-from-env": "1.0.0",
"request-progress": "^3.0.0",
"semver": "^7.3.2",
"supports-color": "^8.1.1",
"tmp": "~0.2.1",
"untildify": "^4.0.0",
"url": "^0.11.0",
"yauzl": "^2.10.0"
},
"dependencies": {
@ -29156,6 +29184,15 @@
"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
"dev": true
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@ -31654,9 +31691,9 @@
}
},
"i18next": {
"version": "21.6.7",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.7.tgz",
"integrity": "sha512-26dTDa2gBz+vMk6WPf1pxTx3S5HIAptbyODmni/JsN6R1W2WNkGVFXBusUK7T6y1wLeJi5CIrqmQ2gl18vdh3A==",
"version": "21.6.10",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.10.tgz",
"integrity": "sha512-Xw+tEGQ61BF6SXtBlFffhM/YhJKHZf2cyDrcNK/l2dE6yVbkPkSasC3VhkAsHXX30vUJ0yG04WIUtf7UvwjOxg==",
"requires": {
"@babel/runtime": "^7.12.0"
}
@ -32503,9 +32540,9 @@
"dev": true
},
"jsdoc": {
"version": "3.6.9",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.9.tgz",
"integrity": "sha512-bVrM2DT2iLmv6jd2IdTRk67tC4iaSDUicD+47y+cNCYlE8dccd4xZnlANG4M+OmGyV389bABSTKKfoPCOofbKw==",
"version": "3.6.10",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz",
"integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==",
"dev": true,
"requires": {
"@babel/parser": "^7.9.4",
@ -32672,9 +32709,9 @@
}
},
"jspdf": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.0.tgz",
"integrity": "sha512-XT0E2m8A9P1xl7ItA2OUbmhokzbDQEyZEdWQZD2olADiTiBEZGDRiK1J1zWxBRUG2KezQJOZq//GYZTkvEZuJg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
"integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
"requires": {
"@babel/runtime": "^7.14.0",
"atob": "^2.1.2",
@ -37608,9 +37645,9 @@
}
},
"rollup": {
"version": "2.66.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.66.0.tgz",
"integrity": "sha512-L6mKOkdyP8HK5kKJXaiWG7KZDumPJjuo1P+cfyHOJPNNTK3Moe7zCH5+fy7v8pVmHXtlxorzaBjvkBMB23s98g==",
"version": "2.66.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.66.1.tgz",
"integrity": "sha512-crSgLhSkLMnKr4s9iZ/1qJCplgAgrRY+igWv8KhG/AjKOJ0YX/WpmANyn8oxrw+zenF3BXWDLa7Xl/QZISH+7w==",
"requires": {
"fsevents": "~2.3.2"
},

View File

@ -83,12 +83,12 @@
"dependencies": {
"@babel/polyfill": "7.12.1",
"browser-fs-access": "0.24.0",
"canvg": "3.0.9",
"canvg": "3.0.10",
"core-js": "3.20.3",
"elix": "15.0.1",
"html2canvas": "1.4.1",
"i18next": "21.6.7",
"jspdf": "2.5.0",
"i18next": "21.6.10",
"jspdf": "2.5.1",
"pathseg": "1.2.1",
"regenerator-runtime": "0.13.9",
"rollup-plugin-polyfill-node": "0.8.0",
@ -108,16 +108,16 @@
"@rollup/plugin-replace": "3.0.1",
"@rollup/plugin-url": "6.1.0",
"@web/dev-server": "0.1.29",
"@web/dev-server-rollup": "0.3.14",
"@web/dev-server-rollup": "0.3.15",
"babel-plugin-transform-object-rest-spread": "7.0.0-beta.3",
"copyfiles": "2.4.1",
"core-js-bundle": "3.20.3",
"cp-cli": "2.0.0",
"cypress": "9.3.1",
"cypress": "9.4.1",
"cypress-multi-reporters": "1.5.0",
"cypress-plugin-snapshots": "1.4.4",
"jamilih": "0.54.0",
"jsdoc": "3.6.9",
"jsdoc": "3.6.10",
"node-static": "0.7.11",
"npm-run-all": "4.1.5",
"nyc": "15.1.0",
@ -128,10 +128,10 @@
"remark-cli": "10.0.1",
"remark-lint-ordered-list-marker-value": "3.1.1",
"rimraf": "3.0.2",
"rollup": "2.66.0",
"rollup": "2.66.1",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-filesize": "9.1.2",
"rollup-plugin-html": "^0.2.1",
"rollup-plugin-html": "0.2.1",
"rollup-plugin-node-polyfills": "0.2.1",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-re": "1.0.7",

View File

@ -351,6 +351,7 @@ export default class ConfigObj {
const cached = this.editor.storage.getItem(name)
if (cached) {
this.editor.loadFromString(cached)
this.editor.topPanel.updateTitle(this.editor.storage.getItem(`title-${name}`) ?? 'untitled.svg')
}
}

View File

@ -28,7 +28,7 @@ import LayersPanel from './panels/LayersPanel.js'
import MainMenu from './MainMenu.js'
import { getParentsUntil } from './components/jgraduate/Util.js'
const { $id, $qa, $click, decode64, blankPageObjectURL } = SvgCanvas
const { $id, $click, decode64, blankPageObjectURL } = SvgCanvas
/**
*
@ -52,6 +52,10 @@ class Editor extends EditorStartup {
* @type {"ignore"|"waiting"|"closed"}
*/
this.storagePromptState = 'ignore'
/**
* document title
*/
this.title = 'untitled.svg'
this.svgCanvas = null
this.$click = $click
@ -197,7 +201,7 @@ class Editor extends EditorStartup {
setAll () {
const keyHandler = {} // will contain the action for each pressed key
this.shortcuts.forEach((shortcut) => {
this.shortcuts.forEach(shortcut => {
// Bind function to shortcut key
if (shortcut.key) {
// Set shortcut based on options
@ -205,20 +209,26 @@ class Editor extends EditorStartup {
let pd = false
if (Array.isArray(shortcut.key)) {
keyval = shortcut.key[0]
if (shortcut.key.length > 1) { pd = shortcut.key[1] }
if (shortcut.key.length > 1) {
pd = shortcut.key[1]
}
}
keyval = String(keyval)
const { fn } = shortcut
keyval.split('/').forEach((key) => { keyHandler[key] = { fn, pd } })
keyval.split('/').forEach(key => {
keyHandler[key] = { fn, pd }
})
}
return true
})
// register the keydown event
document.addEventListener('keydown', (e) => {
document.addEventListener('keydown', e => {
// only track keyboard shortcuts for the body containing the SVG-Editor
if (e.target.nodeName !== 'BODY') return
// normalize key
const key = `${(e.altKey) ? 'alt+' : ''}${(e.shiftKey) ? 'shift+' : ''}${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toLowerCase()}`
const key = `${e.altKey ? 'alt+' : ''}${e.shiftKey ? 'shift+' : ''}${
e.metaKey ? 'meta+' : ''
}${e.ctrlKey ? 'ctrl+' : ''}${e.key.toLowerCase()}`
// return if no shortcut defined for this key
if (!keyHandler[key]) return
// launch associated handler and preventDefault if necessary
@ -264,47 +274,12 @@ class Editor extends EditorStartup {
return parents
}
/**
* @returns {void}
*/
setTitles () {
// Tooltips not directly associated with a single function
const keyAssocs = {
'4/Shift+4': 'tools_rect',
'5/Shift+5': 'tools_ellipse'
}
Object.entries(keyAssocs).forEach(([keyval, sel]) => {
const parentsElements = this.getParents($id(sel), $id('main_menu'))
const menu = (parentsElements.length)
$qa(sel).forEach((element) => {
const t = (menu) ? element.textContent.split(' [')[0] : element.title.split(' [')[0]
let keyStr = ''
// Shift+Up
keyval.split('/').forEach((key, i) => {
const modBits = key.split('+')
let mod = ''
if (modBits.length > 1) {
mod = modBits[0] + '+'
key = modBits[1]
}
keyStr += (i ? '/' : '') + mod + (this.i18next.t('key_' + key) || key)
})
if (menu) {
this.lastChild.textContent = t + ' [' + keyStr + ']'
} else {
this.title = t + ' [' + keyStr + ']'
}
})
})
}
/**
* @param {string} sel Selector to match
* @returns {module:SVGthis.ToolButton}
*/
getButtonData (sel) {
return Object.values(this.shortcuts).find((btn) => {
return Object.values(this.shortcuts).find(btn => {
return btn.sel === sel
})
}
@ -328,12 +303,18 @@ class Editor extends EditorStartup {
this.exportWindow.location.href = data.bloburl || data.datauri
const done = this.configObj.pref('export_notice_done')
if (done !== 'all') {
let note = this.i18next.t('notification.saveFromBrowser', { type: data.type })
let note = this.i18next.t('notification.saveFromBrowser', {
type: data.type
})
// Check if there are issues
if (issues.length) {
const pre = '\n \u2022 '
note += ('\n\n' + this.i18next.t('notification..noteTheseIssues') + pre + issues.join(pre))
note +=
'\n\n' +
this.i18next.t('notification..noteTheseIssues') +
pre +
issues.join(pre)
}
// Note that this will also prevent the notice even though new issues may appear later.
@ -370,8 +351,11 @@ class Editor extends EditorStartup {
const cnvs = $id('svgcanvas')
let w = parseFloat(getComputedStyle(workarea, null).width.replace('px', ''))
let h = parseFloat(getComputedStyle(workarea, null).height.replace('px', ''))
const wOrig = w; const hOrig = h
let h = parseFloat(
getComputedStyle(workarea, null).height.replace('px', '')
)
const wOrig = w
const hOrig = h
const oldCtr = {
x: workarea.scrollLeft + wOrig / 2,
y: workarea.scrollTop + hOrig / 2
@ -386,8 +370,10 @@ class Editor extends EditorStartup {
workarea.style.overflow = 'scroll'
}
const oldCanY = parseFloat(getComputedStyle(cnvs, null).height.replace('px', '')) / 2
const oldCanX = parseFloat(getComputedStyle(cnvs, null).width.replace('px', '')) / 2
const oldCanY =
parseFloat(getComputedStyle(cnvs, null).height.replace('px', '')) / 2
const oldCanX =
parseFloat(getComputedStyle(cnvs, null).width.replace('px', '')) / 2
cnvs.style.width = w + 'px'
cnvs.style.height = h + 'px'
@ -418,7 +404,10 @@ class Editor extends EditorStartup {
if (center) {
// Go to top-left for larger documents
if (this.svgCanvas.contentW > parseFloat(getComputedStyle(workarea, null).width.replace('px', ''))) {
if (
this.svgCanvas.contentW >
parseFloat(getComputedStyle(workarea, null).width.replace('px', ''))
) {
// Top-left
workarea.scrollLeft = offset.x - 10
workarea.scrollTop = offset.y - 10
@ -436,7 +425,10 @@ class Editor extends EditorStartup {
workarea.scroll()
}
if (this.configObj.urldata.storagePrompt !== true && this.storagePromptState === 'ignore') {
if (
this.configObj.urldata.storagePrompt !== true &&
this.storagePromptState === 'ignore'
) {
if ($id('dialog_box') != null) $id('dialog_box').style.display = 'none'
}
}
@ -452,25 +444,12 @@ class Editor extends EditorStartup {
}
`
if (document.querySelectorAll('#wireframe_rules').length > 0) {
document.querySelector('#wireframe_rules').textContent = (this.workarea.classList.contains('wireframe') ? rule : '')
document.querySelector(
'#wireframe_rules'
).textContent = this.workarea.classList.contains('wireframe') ? rule : ''
}
}
/**
* @param {string} [title=svgCanvas.getDocumentTitle()]
* @returns {void}
*/
updateTitle (title) {
title = title || this.svgCanvas.getDocumentTitle()
const newTitle = document.querySelector('title').text + (title ? ': ' + title : '')
// Remove title update with current context info, isn't really necessary
// if (this.curContext) {
// new_title = new_title + this.curContext;
// }
document.querySelector('title').textContent = newTitle
}
// called when we've selected a different element
/**
*
@ -487,8 +466,8 @@ class Editor extends EditorStartup {
}
const isNode = mode === 'pathedit'
// if this.elems[1] is present, then we have more than one element
this.selectedElement = (elems.length === 1 || !elems[1] ? elems[0] : null)
this.multiselected = (elems.length >= 2 && elems[1])
this.selectedElement = elems.length === 1 || !elems[1] ? elems[0] : null
this.multiselected = elems.length >= 2 && elems[1]
if (this.selectedElement && !isNode) {
this.topPanel.update()
} // if (elem)
@ -496,11 +475,14 @@ class Editor extends EditorStartup {
// Deal with pathedit mode
this.topPanel.togglePathEditMode(isNode, elems)
this.topPanel.updateContextPanel()
this.svgCanvas.runExtensions('selectedChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_selectedChanged} */ {
this.svgCanvas.runExtensions(
'selectedChanged',
/** @type {module:svgcanvas.SvgCanvas#event:ext_selectedChanged} */ {
elems,
selectedElement: this.selectedElement,
multiselected: this.multiselected
})
}
)
}
// Call when part of element is in process of changing, generally
@ -520,21 +502,26 @@ class Editor extends EditorStartup {
return
}
this.multiselected = (elems.length >= 2 && elems[1])
this.multiselected = elems.length >= 2 && elems[1]
// Only updating fields for single elements for now
if (!this.multiselected) {
switch (mode) {
case 'rotate': {
const ang = this.svgCanvas.getRotationAngle(elem)
$id('angle').value = ang;
(ang === 0) ? $id('tool_reorient').classList.add('disabled') : $id('tool_reorient').classList.remove('disabled')
$id('angle').value = ang
ang === 0
? $id('tool_reorient').classList.add('disabled')
: $id('tool_reorient').classList.remove('disabled')
break
}
}
}
this.svgCanvas.runExtensions('elementTransition', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementTransition} */ {
this.svgCanvas.runExtensions(
'elementTransition',
/** @type {module:svgcanvas.SvgCanvas#event:ext_elementTransition} */ {
elems
})
}
)
}
// called when any element has changed
@ -551,8 +538,8 @@ class Editor extends EditorStartup {
this.leftPanel.clickSelect()
}
elems.forEach((elem) => {
const isSvgElem = (elem?.tagName === 'svg')
elems.forEach(elem => {
const isSvgElem = elem?.tagName === 'svg'
if (isSvgElem || this.svgCanvas.isLayer(elem)) {
this.layersPanel.populateLayers()
// if the element changed was the svg, then it could be a resolution change
@ -582,9 +569,12 @@ class Editor extends EditorStartup {
this.bottomPanel.updateColorpickers()
}
this.svgCanvas.runExtensions('elementChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_elementChanged} */ {
this.svgCanvas.runExtensions(
'elementChanged',
/** @type {module:svgcanvas.SvgCanvas#event:ext_elementChanged} */ {
elems
})
}
)
}
/**
@ -615,8 +605,18 @@ class Editor extends EditorStartup {
*/
zoomChanged (win, bbox, autoCenter) {
const scrbar = 15
const zInfo = this.svgCanvas.setBBoxZoom(bbox, parseFloat(getComputedStyle(this.workarea, null).width.replace('px', '')) - scrbar, parseFloat(getComputedStyle(this.workarea, null).height.replace('px', '')) - scrbar)
if (!zInfo) { return }
const zInfo = this.svgCanvas.setBBoxZoom(
bbox,
parseFloat(
getComputedStyle(this.workarea, null).width.replace('px', '')
) - scrbar,
parseFloat(
getComputedStyle(this.workarea, null).height.replace('px', '')
) - scrbar
)
if (!zInfo) {
return
}
const zoomlevel = zInfo.zoom
const bb = zInfo.bbox
@ -630,10 +630,10 @@ class Editor extends EditorStartup {
if (autoCenter) {
this.updateCanvas()
} else {
this.updateCanvas(
false,
{ x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2, y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2 }
)
this.updateCanvas(false, {
x: bb.x * zoomlevel + (bb.width * zoomlevel) / 2,
y: bb.y * zoomlevel + (bb.height * zoomlevel) / 2
})
}
if (this.svgCanvas.getMode() === 'zoom' && bb.width) {
@ -654,12 +654,18 @@ class Editor extends EditorStartup {
let linkStr = ''
if (context) {
let str = ''
linkStr = '<a href="#" data-root="y">' + this.svgCanvas.getCurrentDrawing().getCurrentLayerName() + '</a>'
linkStr =
'<a href="#" data-root="y">' +
this.svgCanvas.getCurrentDrawing().getCurrentLayerName() +
'</a>'
const parentsUntil = getParentsUntil(context, '#svgcontent')
parentsUntil.forEach(function (parent) {
if (parent.id) {
str += ' > ' + parent.id
linkStr += (parent !== context) ? ` > <a href="#">${parent.id}</a>` : ` > ${parent.id}`
linkStr +=
parent !== context
? ` > <a href="#">${parent.id}</a>`
: ` > ${parent.id}`
}
})
@ -669,8 +675,6 @@ class Editor extends EditorStartup {
}
$id('cur_context_panel').style.display = context ? 'block' : 'none'
$id('cur_context_panel').innerHTML = linkStr
this.updateTitle()
}
/**
@ -682,14 +686,16 @@ class Editor extends EditorStartup {
setIcon (elem, iconId) {
const img = document.createElement('img')
img.src = this.configObj.curConfig.imgPath + iconId
const icon = (typeof iconId === 'string') ? img : iconId.cloneNode(true)
const icon = typeof iconId === 'string' ? img : iconId.cloneNode(true)
if (!icon) {
// Todo: Investigate why this still occurs in some cases
console.warn('NOTE: Icon image missing: ' + iconId)
return
}
// empty()
while ($id(elem).firstChild) { $id(elem).removeChild($id(elem).firstChild) }
while ($id(elem).firstChild) {
$id(elem).removeChild($id(elem).firstChild)
}
$id(elem).appendChild(icon)
}
@ -763,8 +769,18 @@ class Editor extends EditorStartup {
pasteInCenter () {
const { workarea } = this
const zoom = this.svgCanvas.getZoom()
const x = (workarea.scrollLeft + parseFloat(getComputedStyle(workarea, null).width.replace('px', '')) / 2) / zoom - this.svgCanvas.contentW
const y = (workarea.scrollTop + parseFloat(getComputedStyle(workarea, null).height.replace('px', '')) / 2) / zoom - this.svgCanvas.contentH
const x =
(workarea.scrollLeft +
parseFloat(getComputedStyle(workarea, null).width.replace('px', '')) /
2) /
zoom -
this.svgCanvas.contentW
const y =
(workarea.scrollTop +
parseFloat(getComputedStyle(workarea, null).height.replace('px', '')) /
2) /
zoom -
this.svgCanvas.contentH
this.svgCanvas.pasteElements('point', x, y)
}
@ -787,7 +803,8 @@ class Editor extends EditorStartup {
if (this.selectedElement || this.multiselected) {
if (this.configObj.curConfig.gridSnapping) {
// Use grid snap value regardless of zoom level
const multi = this.svgCanvas.getZoom() * this.configObj.curConfig.snappingStep
const multi =
this.svgCanvas.getZoom() * this.configObj.curConfig.snappingStep
dx *= multi
dy *= multi
}
@ -817,8 +834,12 @@ class Editor extends EditorStartup {
* @returns {void}
*/
rotateSelected (cw, step) {
if (!this.selectedElement || this.multiselected) { return }
if (!cw) { step *= -1 }
if (!this.selectedElement || this.multiselected) {
return
}
if (!cw) {
step *= -1
}
const angle = Number.parseFloat($id('angle').value) + step
this.svgCanvas.setRotationAngle(angle)
this.topPanel.updateContextPanel()
@ -845,11 +866,12 @@ class Editor extends EditorStartup {
this.hideSourceEditor()
this.zoomImage()
this.layersPanel.populateLayers()
this.updateTitle()
}
if (!this.svgCanvas.setSvgString(e.detail.value)) {
const ok = await seConfirm(this.i18next.t('notification.QerrorsRevertToSource'))
const ok = await seConfirm(
this.i18next.t('notification.QerrorsRevertToSource')
)
if (ok === false || ok === 'Cancel') {
return
}
@ -878,7 +900,9 @@ class Editor extends EditorStartup {
if (editingsource) {
const origSource = this.svgCanvas.getSvgString()
if (origSource !== e.detail.value) {
const ok = seConfirm(this.i18next.t('notification.QignoreSourceChanges'))
const ok = seConfirm(
this.i18next.t('notification.QignoreSourceChanges')
)
if (ok) {
this.hideSourceEditor()
}
@ -895,8 +919,13 @@ class Editor extends EditorStartup {
let svgeditClipboard
try {
svgeditClipboard = this.localStorage.getItem('svgedit_clipboard')
} catch (err) { /* empty fn */ }
this.canvMenu.setAttribute((svgeditClipboard ? 'en' : 'dis') + 'ablemenuitems', '#paste,#paste_in_place')
} catch (err) {
/* empty fn */
}
this.canvMenu.setAttribute(
(svgeditClipboard ? 'en' : 'dis') + 'ablemenuitems',
'#paste,#paste_in_place'
)
}
/**
@ -956,17 +985,25 @@ class Editor extends EditorStartup {
this.configObj.pref('lang', lang)
const $editDialog = $id('se-edit-prefs')
$editDialog.setAttribute('lang', lang)
const oldLayerName = ($id('#layerlist')) ? $id('#layerlist').querySelector('tr.layersel td.layername').textContent : ''
const renameLayer = (oldLayerName === this.i18next.t('notification.common.layer') + ' 1')
const oldLayerName = $id('#layerlist')
? $id('#layerlist').querySelector('tr.layersel td.layername').textContent
: ''
const renameLayer =
oldLayerName === this.i18next.t('notification.common.layer') + ' 1'
this.setTitles()
if (renameLayer) {
this.svgCanvas.renameCurrentLayer(this.i18next.t('notification.common.layer') + ' 1')
this.svgCanvas.renameCurrentLayer(
this.i18next.t('notification.common.layer') + ' 1'
)
this.layersPanel.populateLayers()
}
this.svgCanvas.runExtensions('langChanged', /** @type {module:svgcanvas.SvgCanvas#event:ext_langChanged} */ lang)
this.svgCanvas.runExtensions(
'langChanged',
/** @type {module:svgcanvas.SvgCanvas#event:ext_langChanged} */ lang
)
}
/**
@ -998,9 +1035,11 @@ class Editor extends EditorStartup {
*/
async runCallbacks () {
try {
await Promise.all(this.callbacks.map(([cb]) => {
await Promise.all(
this.callbacks.map(([cb]) => {
return cb()
}))
})
)
} catch (err) {
this.callbacks.forEach(([, , reject]) => {
reject()
@ -1051,7 +1090,7 @@ class Editor extends EditorStartup {
return this.ready(() => {
return new Promise((resolve, reject) => {
fetch(url, { cache: cache ? 'force-cache' : 'no-cache' })
.then((response) => {
.then(response => {
if (!response.ok) {
if (noAlert) {
reject(new Error('URLLoadFail'))
@ -1062,11 +1101,11 @@ class Editor extends EditorStartup {
}
return response.text()
})
.then((str) => {
.then(str => {
this.loadSvgString(str, { noAlert })
return str
})
.catch((error) => {
.catch(error => {
if (noAlert) {
reject(new Error('URLLoadFail'))
return
@ -1098,7 +1137,10 @@ class Editor extends EditorStartup {
pre = pre[0]
}
const src = str.slice(pre.length)
return this.loadSvgString(base64 ? decode64(src) : decodeURIComponent(src), { noAlert })
return this.loadSvgString(
base64 ? decode64(src) : decodeURIComponent(src),
{ noAlert }
)
})
}

View File

@ -630,7 +630,7 @@ class EditorStartup {
)
// load user extensions (given as pathNames)
await Promise.all(
this.configObj.curConfig.userExtensions.map(async (extPathName) => {
this.configObj.curConfig.userExtensions.map(async ({ pathName, config }) => {
/**
* @tutorial ExtensionDocs
* @typedef {PlainObject} module:SVGthis.ExtensionObject
@ -641,12 +641,12 @@ class EditorStartup {
/**
* @type {module:SVGthis.ExtensionObject}
*/
const imported = await import(encodeURI(extPathName))
const imported = await import(encodeURI(pathName))
const { name, init: initfn } = imported.default
return this.addExtension(name, (initfn && initfn.bind(this)), {})
return this.addExtension(name, (initfn && initfn.bind(this, config)), {})
} catch (err) {
// Todo: Add config to alert any errors
console.error('Extension failed to load: ' + extPathName + '; ', err)
console.error('Extension failed to load: ' + pathName + '; ', err)
return undefined
}
})

View File

@ -138,6 +138,7 @@ export default {
svgEditor.zoomImage()
svgEditor.layersPanel.populateLayers()
svgEditor.topPanel.updateContextPanel()
svgEditor.topPanel.updateTitle('untitled.svg')
svgEditor.svgCanvas.runExtensions('onNewDocument')
}
@ -160,6 +161,7 @@ export default {
await svgEditor.loadSvgString(svgContent)
svgEditor.updateCanvas()
handle = blob.handle
svgEditor.topPanel.updateTitle(blob.name)
svgEditor.svgCanvas.runExtensions('onOpenedDocument', {
name: blob.name,
lastModified: blob.lastModified,
@ -192,7 +194,7 @@ export default {
*
* @returns {void}
*/
const clickSave = async function (type, _) {
const clickSave = async function (type) {
const $editorDialog = $id('se-svg-editor-dialog')
const editingsource = $editorDialog.getAttribute('dialog') === 'open'
if (editingsource) {
@ -222,15 +224,16 @@ export default {
if (type === 'save' && handle !== null) {
const throwIfExistingHandleNotGood = false
handle = await fileSave(blob, {
fileName: 'icon.svg',
fileName: 'untitled.svg',
extensions: ['.svg']
}, handle, throwIfExistingHandleNotGood)
} else {
handle = await fileSave(blob, {
fileName: 'icon.svg',
fileName: svgEditor.title,
extensions: ['.svg']
})
}
svgEditor.topPanel.updateTitle(handle.name)
svgCanvas.runExtensions('onSavedDocument', {
name: handle.name,
kind: handle.kind

View File

@ -32,8 +32,9 @@ const removeStoragePrefCookie = () => {
* @param {string} cookie
* @returns {void}
*/
const expireCookie = (cookie) => {
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'
const expireCookie = cookie => {
document.cookie =
encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'
}
/**
@ -42,12 +43,16 @@ const expireCookie = (cookie) => {
* @returns {void}
* @todo Replace the string manipulation with `searchParams.set`
*/
const replaceStoragePrompt = (val) => {
const replaceStoragePrompt = val => {
val = val ? 'storagePrompt=' + val : ''
const loc = top.location // Allow this to work with the embedded editor as well
if (loc.href.includes('storagePrompt=')) {
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''))
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (
n0,
n1,
amp
) {
return (val ? n1 : '') + val + (!val && amp ? n1 : amp || '')
})
} else {
loc.href += (loc.href.includes('?') ? '&' : '?') + val
@ -87,21 +92,29 @@ export default {
// manage the change in the storageDialog
storageBox.addEventListener('change', (e) => {
storageBox.addEventListener('change', e => {
storageBox.setAttribute('dialog', 'close')
if (e?.detail?.trigger === 'ok') {
if (e?.detail?.select !== 'noPrefsOrContent') {
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt')
document.cookie = 'svgeditstore=' + encodeURIComponent(e.detail.select) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'
const storagePrompt = new URL(top.location).searchParams.get(
'storagePrompt'
)
document.cookie =
'svgeditstore=' +
encodeURIComponent(e.detail.select) +
'; expires=Fri, 31 Dec 9999 23:59:59 GMT'
if (storagePrompt === 'true' && e?.detail?.checkbox) {
replaceStoragePrompt()
return
}
} else {
removeStoragePrefCookie()
if (svgEditor.configObj.curConfig.emptyStorageOnDecline && e?.detail?.checkbox) {
this.setSvgContentStorage('')
Object.keys(svgEditor.curPrefs).forEach((name) => {
if (
svgEditor.configObj.curConfig.emptyStorageOnDecline &&
e?.detail?.checkbox
) {
setSvgContentStorage('')
Object.keys(svgEditor.curPrefs).forEach(name => {
name = 'svg-edit-' + name
if (svgEditor.storage) {
svgEditor.storage.removeItem(name)
@ -125,17 +138,17 @@ export default {
/**
* Sets SVG content as a string with "svgedit-" and the current
* canvas name as namespace.
* @param {string} val
* @param {string} svgString
* @returns {void}
*/
function setSvgContentStorage (val) {
if (storage) {
const name = 'svgedit-' + svgEditor.configObj.curConfig.canvasName
if (!val) {
const setSvgContentStorage = (svgString) => {
const name = `svgedit-${svgEditor.configObj.curConfig.canvasName}`
if (!svgString) {
storage.removeItem(name)
storage.removeItem(`${name}-title`)
} else {
storage.setItem(name, val)
}
storage.setItem(name, svgString)
storage.setItem(`title-${name}`, svgEditor.title)
}
}
@ -151,20 +164,23 @@ export default {
function setupBeforeUnloadListener () {
window.addEventListener('beforeunload', function () {
// Don't save anything unless the user opted in to storage
if (!(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/).test(document.cookie)) {
if (
!/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/.test(
document.cookie
)
) {
return
}
if ((/(?:^|;\s*)svgeditstore=prefsAndContent/).test(document.cookie)) {
if (/(?:^|;\s*)svgeditstore=prefsAndContent/.test(document.cookie)) {
setSvgContentStorage(svgCanvas.getSvgString())
}
svgEditor.setConfig({ no_save_warning: true }) // No need for explicit saving at all once storage is on
// svgEditor.showSaveWarning = false;
const { curPrefs } = svgEditor.configObj
Object.entries(curPrefs).forEach(([key, val]) => {
const store = (val !== undefined)
const store = val !== undefined
key = 'svg-edit-' + key
if (!store) {
return
@ -175,7 +191,11 @@ export default {
window.widget.setPreferenceForKey(val, key)
} else {
val = encodeURIComponent(val)
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'
document.cookie =
encodeURIComponent(key) +
'=' +
val +
'; expires=Fri, 31 Dec 9999 23:59:59 GMT'
}
})
})
@ -185,7 +205,9 @@ export default {
return {
name: 'storage',
callback () {
const storagePrompt = new URL(top.location).searchParams.get('storagePrompt')
const storagePrompt = new URL(top.location).searchParams.get(
'storagePrompt'
)
// No need to run this one-time dialog again just because the user
// changes the language
if (loaded) {
@ -197,22 +219,23 @@ export default {
// set to false; to avoid any chance of storage, avoid this
// extension! (and to avoid using any prior storage, set the
// config option "noStorageOnLoad" to true).
if (!forceStorage && (
if (
!forceStorage &&
// If the URL has been explicitly set to always prompt the
// user (e.g., so one can be pointed to a URL where one
// can alter one's settings, say to prevent future storage)...
storagePrompt === 'true' ||
(
(storagePrompt === 'true' ||
// ...or...if the URL at least doesn't explicitly prevent a
// storage prompt (as we use for users who
// don't want to set cookies at all but who don't want
// continual prompts about it)...
storagePrompt !== 'false' &&
(storagePrompt !== 'false' &&
// ...and this user hasn't previously indicated a desire for storage
!(/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/).test(document.cookie)
)
!/(?:^|;\s*)svgeditstore=(?:prefsAndContent|prefsOnly)/.test(
document.cookie
)))
// ...then show the storage prompt.
)) {
) {
const options = Boolean(storage)
// Open select-with-checkbox dialog
// From svg-editor.js

View File

@ -142,23 +142,6 @@ export class SeStorageDialog extends HTMLElement {
svgEditor.$click(this.$okBtn, (evt) => onSubmitHandler(evt, 'ok'))
svgEditor.$click(this.$cancelBtn, (evt) => onSubmitHandler(evt, 'cancel'))
}
/**
* Sets SVG content as a string with "svgedit-" and the current
* canvas name as namespace.
* @param {string} val
* @returns {void}
*/
setSvgContentStorage (val) {
if (this.storage) {
const name = 'svgedit-' + this.configObj.curConfig.canvasName
if (!val) {
this.storage.removeItem(name)
} else {
this.storage.setItem(name, val)
}
}
}
}
// Register

View File

@ -1,4 +1,7 @@
<div id="tools_top">
<div id="title_panel">
<p>untitled.svg</p>
</div> <!-- title panel -->
<div id="editor_panel">
<div class="tool_sep"></div>
<se-button id="tool_source" title="tools.tool_source" shortcut="U" src="source.svg"></se-button>

View File

@ -26,14 +26,16 @@ class TopPanel {
*/
displayTool (className) {
// default display is 'none' so removing the property will make the panel visible
$qa(`.${className}`).map((el) => el.style.removeProperty('display'))
$qa(`.${className}`).map(el => el.style.removeProperty('display'))
}
/**
* @type {module}
*/
hideTool (className) {
$qa(`.${className}`).forEach((el) => { el.style.display = 'none' })
$qa(`.${className}`).forEach(el => {
el.style.display = 'none'
})
}
/**
@ -72,9 +74,12 @@ class TopPanel {
this.svgCanvas.setStrokeAttr('stroke-' + pre, val)
}
opt.classList.add('current')
const elements = Array.prototype.filter.call(opt.parentNode.children, function (child) {
const elements = Array.prototype.filter.call(
opt.parentNode.children,
function (child) {
return child !== opt
})
}
)
Array.from(elements).forEach(function (element) {
element.classList.remove('current')
})
@ -87,7 +92,10 @@ class TopPanel {
* @returns {void}
*/
update () {
let i; let len
let i
let len
// set title
$qa('#title_panel > p')[0].textContent = this.editor.title
if (this.selectedElement) {
switch (this.selectedElement.tagName) {
case 'use':
@ -109,15 +117,17 @@ class TopPanel {
}
}
$id('stroke_width').value = (gWidth === null ? '' : gWidth)
$id('stroke_width').value = gWidth === null ? '' : gWidth
this.editor.bottomPanel.updateColorpickers(true)
break
}
default: {
this.editor.bottomPanel.updateColorpickers(true)
$id('stroke_width').value = this.selectedElement.getAttribute('stroke-width') || 1
$id('stroke_style').value = this.selectedElement.getAttribute('stroke-dasharray') || 'none'
$id('stroke_width').value =
this.selectedElement.getAttribute('stroke-width') || 1
$id('stroke_style').value =
this.selectedElement.getAttribute('stroke-dasharray') || 'none'
$id('stroke_style').setAttribute('value', $id('stroke_style').value)
let attr =
@ -226,13 +236,14 @@ class TopPanel {
if (['line', 'circle', 'ellipse'].includes(elname)) {
this.hideTool('xy_panel')
} else {
let x; let y
let x
let y
// Get BBox vals for g, polyline and path
if (['g', 'polyline', 'path'].includes(elname)) {
const bb = this.editor.svgCanvas.getStrokedBBox([elem])
if (bb) {
({ x, y } = bb)
;({ x, y } = bb)
}
} else {
x = elem.getAttribute('x')
@ -244,19 +255,13 @@ class TopPanel {
y = convertUnit(y)
}
$id('selected_x').value = (x || 0)
$id('selected_y').value = (y || 0)
$id('selected_x').value = x || 0
$id('selected_y').value = y || 0
this.displayTool('xy_panel')
}
// Elements in this array cannot be converted to a path
if ([
'image',
'text',
'path',
'g',
'use'
].includes(elname)) {
if (['image', 'text', 'path', 'g', 'use'].includes(elname)) {
this.hideTool('tool_topath')
} else {
this.displayTool('tool_topath')
@ -266,11 +271,13 @@ class TopPanel {
} else {
this.hideTool('tool_reorient')
}
$id('tool_reorient').disabled = (angle === 0)
$id('tool_reorient').disabled = angle === 0
} else {
const point = this.path.getNodePoint()
$id('tool_add_subpath').pressed = false;
(!this.path.canDeleteNodes) ? $id('tool_node_delete').classList.add('disabled') : $id('tool_node_delete').classList.remove('disabled')
$id('tool_add_subpath').pressed = false
!this.path.canDeleteNodes
? $id('tool_node_delete').classList.add('disabled')
: $id('tool_node_delete').classList.remove('disabled')
// Show open/close button based on selected point
// setIcon('#tool_openclose_path', path.closed_subpath ? 'open_path' : 'close_path');
@ -281,10 +288,10 @@ class TopPanel {
point.x = convertUnit(point.x)
point.y = convertUnit(point.y)
}
$id('path_node_x').value = (point.x)
$id('path_node_y').value = (point.y)
$id('path_node_x').value = point.x
$id('path_node_y').value = point.y
if (point.type) {
segType.value = (point.type)
segType.value = point.type
segType.removeAttribute('disabled')
} else {
segType.value = 4
@ -316,9 +323,12 @@ class TopPanel {
}
// siblings
if (elem.parentNode) {
const selements = Array.prototype.filter.call(elem.parentNode.children, function (child) {
const selements = Array.prototype.filter.call(
elem.parentNode.children,
function (child) {
return child !== elem
})
}
)
if (elem.parentNode.tagName === 'a' && !selements.length) {
this.displayTool('a_panel')
linkHref = this.editor.svgCanvas.getHref(elem.parentNode)
@ -339,7 +349,7 @@ class TopPanel {
const curPanel = panels[tagName]
this.displayTool(tagName + '_panel')
curPanel.forEach((item) => {
curPanel.forEach(item => {
let attrVal = elem.getAttribute(item)
if (this.editor.configObj.curConfig.baseUnit !== 'px' && elem[item]) {
const bv = elem[item].baseVal.value
@ -352,16 +362,31 @@ class TopPanel {
this.displayTool('text_panel')
$id('tool_italic').pressed = this.editor.svgCanvas.getItalic()
$id('tool_bold').pressed = this.editor.svgCanvas.getBold()
$id('tool_text_decoration_underline').pressed = this.editor.svgCanvas.hasTextDecoration('underline')
$id('tool_text_decoration_linethrough').pressed = this.editor.svgCanvas.hasTextDecoration('line-through')
$id('tool_text_decoration_overline').pressed = this.editor.svgCanvas.hasTextDecoration('overline')
$id('tool_font_family').setAttribute('value', elem.getAttribute('font-family'))
$id('tool_text_anchor').setAttribute('value', elem.getAttribute('text-anchor'))
$id(
'tool_text_decoration_underline'
).pressed = this.editor.svgCanvas.hasTextDecoration('underline')
$id(
'tool_text_decoration_linethrough'
).pressed = this.editor.svgCanvas.hasTextDecoration('line-through')
$id(
'tool_text_decoration_overline'
).pressed = this.editor.svgCanvas.hasTextDecoration('overline')
$id('tool_font_family').setAttribute(
'value',
elem.getAttribute('font-family')
)
$id('tool_text_anchor').setAttribute(
'value',
elem.getAttribute('text-anchor')
)
$id('font_size').value = elem.getAttribute('font-size')
$id('tool_letter_spacing').value = elem.getAttribute('letter-spacing') ?? 0
$id('tool_word_spacing').value = elem.getAttribute('word-spacing') ?? 0
$id('tool_letter_spacing').value =
elem.getAttribute('letter-spacing') ?? 0
$id('tool_word_spacing').value =
elem.getAttribute('word-spacing') ?? 0
$id('tool_text_length').value = elem.getAttribute('textLength') ?? 0
$id('tool_length_adjust').value = elem.getAttribute('lengthAdjust') ?? 0
$id('tool_length_adjust').value =
elem.getAttribute('lengthAdjust') ?? 0
$id('text').value = elem.textContent
if (this.editor.svgCanvas.addedNew) {
// Timeout needed for IE9
@ -375,16 +400,14 @@ class TopPanel {
tagName === 'image' &&
this.editor.svgCanvas.getMode() === 'image'
) {
this.editor.svgCanvas.setImageURL(
this.editor.svgCanvas.getHref(elem)
)
this.editor.svgCanvas.setImageURL(this.editor.svgCanvas.getHref(elem))
// image
} else if (tagName === 'g' || tagName === 'use') {
this.displayTool('container_panel')
const title = this.editor.svgCanvas.getTitle()
const label = $id('g_title')
label.value = title
$id('g_title').disabled = (tagName === 'use')
$id('g_title').disabled = tagName === 'use'
}
}
menuItems.setAttribute(
@ -463,7 +486,9 @@ class TopPanel {
fcRules.setAttribute('id', 'wireframe_rules')
document.getElementsByTagName('head')[0].appendChild(fcRules)
} else {
while (wfRules.firstChild) { wfRules.removeChild(wfRules.firstChild) }
while (wfRules.firstChild) {
wfRules.removeChild(wfRules.firstChild)
}
}
this.editor.updateWireFrame()
}
@ -513,8 +538,10 @@ class TopPanel {
* @type {module}
*/
changeRotationAngle (e) {
this.editor.svgCanvas.setRotationAngle(e.target.value);
(Number.parseInt(e.target.value) === 0) ? $id('tool_reorient').classList.add('disabled') : $id('tool_reorient').classList.remove('disabled')
this.editor.svgCanvas.setRotationAngle(e.target.value)
;(Number.parseInt(e.target.value) === 0)
? $id('tool_reorient').classList.add('disabled')
: $id('tool_reorient').classList.remove('disabled')
}
/**
@ -655,8 +682,8 @@ class TopPanel {
* @returns {void}
*/
linkControlPoints () {
$id('tool_node_link').pressed = !($id('tool_node_link').pressed)
const linked = !!($id('tool_node_link').pressed)
$id('tool_node_link').pressed = !$id('tool_node_link').pressed
const linked = !!$id('tool_node_link').pressed
this.path.linkControlPoints(linked)
}
@ -828,19 +855,34 @@ class TopPanel {
// eslint-disable-next-line promise/catch-or-return
promised
// eslint-disable-next-line promise/always-return
.then(() => {
.then(
() => {
// switch into "select" mode if we've clicked on an element
editor.svgCanvas.setMode('select')
editor.svgCanvas.selectOnly(editor.svgCanvas.getSelectedElements(), true)
}, (error) => {
editor.svgCanvas.selectOnly(
editor.svgCanvas.getSelectedElements(),
true
)
},
error => {
console.error('error =', error)
seAlert(editor.i18next.t('tools.no_embed'))
editor.svgCanvas.deleteSelectedElements()
})
}
)
this.displayTool('image_url')
}
}
/**
*
*/
updateTitle (title) {
if (title) this.editor.title = title
const titleElement = $qa('#title_panel > p')[0]
if (titleElement) titleElement.textContent = this.editor.title
}
/**
* @param {boolean} editmode
* @param {module:svgcanvas.SvgCanvas#event:selected} elems
@ -883,6 +925,7 @@ class TopPanel {
)
newSeEditorDialog.setAttribute('id', 'se-svg-editor-dialog')
this.editor.$container.append(newSeEditorDialog)
this.updateTitle()
newSeEditorDialog.init(i18next)
$id('tool_link_url').setAttribute('title', i18next.t('tools.set_link_url'))
// register action to top panel buttons
@ -954,7 +997,7 @@ class TopPanel {
'image_height',
'path_node_x',
'path_node_y'
].forEach((attrId) =>
].forEach(attrId =>
$id(attrId).addEventListener('change', this.attrChanger.bind(this))
)
}

View File

@ -33,6 +33,13 @@
height: 100%;
}
#title_panel > p {
color: white;
padding-left: 5px;
padding-right: 3px;
font-weight: bold;
}
/* on smaller screen, allow 2 lines for the toolbar */
@media screen and (max-width:1250px) {
.svg_editor {
@ -42,7 +49,7 @@
/* class to open the right panel */
.svg_editor.open {
grid-template-columns: 34px 15px 50px 1fr 150px;
grid-template-columns: 34px 15px 50px 1fr 220px;
}
#svgroot {
@ -179,6 +186,8 @@ hr {
#selLayerNames {
display: block;
top: -8px;
position: relative;
}
/* Main button