diff --git a/src/editor/MainMenu.js b/src/editor/MainMenu.js index e72bc45c..28eab152 100644 --- a/src/editor/MainMenu.js +++ b/src/editor/MainMenu.js @@ -308,7 +308,6 @@ class MainMenu { - diff --git a/src/editor/extensions/ext-eyedropper/ext-eyedropper.js b/src/editor/extensions/ext-eyedropper/ext-eyedropper.js index 4a7069da..db992a1d 100644 --- a/src/editor/extensions/ext-eyedropper/ext-eyedropper.js +++ b/src/editor/extensions/ext-eyedropper/ext-eyedropper.js @@ -21,12 +21,12 @@ const loadExtensionTranslation = async function (lang) { export default { name: 'eyedropper', - async init (S) { + async init(S) { const svgEditor = this; const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang')); - const {$, ChangeElementCommand} = S, // , svgcontent, + const { $, ChangeElementCommand } = S, // , svgcontent, // svgdoc = S.svgroot.parentNode.ownerDocument, - {svgCanvas} = svgEditor, + { svgCanvas } = svgEditor, addToHistory = function (cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); }, currentStyle = { fillPaint: 'red', fillOpacity: 1.0, @@ -36,14 +36,14 @@ export default { strokeLinecap: 'butt', strokeLinejoin: 'miter' }; - const {$id} = svgCanvas; + const { $id } = svgCanvas; /** * * @param {module:svgcanvas.SvgCanvas#event:ext_selectedChanged|module:svgcanvas.SvgCanvas#event:ext_elementChanged} opts * @returns {void} */ - function getStyle (opts) { + const getStyle = (opts) => { // if we are in eyedropper mode, we don't want to disable the eye-dropper tool const mode = svgCanvas.getMode(); if (mode === 'eyedropper') { return; } @@ -66,30 +66,33 @@ export default { currentStyle.strokeLinecap = elem.getAttribute('stroke-linecap'); currentStyle.strokeLinejoin = elem.getAttribute('stroke-linejoin'); currentStyle.opacity = elem.getAttribute('opacity') || 1.0; - // disable eye-dropper tool + // disable eye-dropper tool } else { tool.classList.add('disabled'); } } - const events = { - id: 'tool_eyedropper', - click () { - svgCanvas.setMode('eyedropper'); - } - }; - return { name: strings.name, - events, + callback() { + // Add the button and its handler(s) + const buttonTemplate = document.createElement("template"); + buttonTemplate.innerHTML = ` + + ` + $id('tools_left').append(buttonTemplate.content.cloneNode(true)); + $id('tool_eyedropper').addEventListener("click", () => { + svgCanvas.setMode('eyedropper'); + }); + }, // if we have selected an element, grab its paint and enable the eye dropper button selectedChanged: getStyle, elementChanged: getStyle, - mouseDown (opts) { + mouseDown(opts) { const mode = svgCanvas.getMode(); if (mode === 'eyedropper') { const e = opts.event; - const {target} = e; + const { target } = e; if (!['svg', 'g', 'use'].includes(target.nodeName)) { const changes = {}; diff --git a/src/editor/extensions/ext-imagelib/ext-imagelib.js b/src/editor/extensions/ext-imagelib/ext-imagelib.js index 15e3ee25..2eac0703 100644 --- a/src/editor/extensions/ext-imagelib/ext-imagelib.js +++ b/src/editor/extensions/ext-imagelib/ext-imagelib.js @@ -23,14 +23,14 @@ const loadExtensionTranslation = async function (lang) { export default { name: 'imagelib', - async init ({$, decode64, dropXMLInternalSubset}) { + async init({ $, decode64, dropXMLInternalSubset }) { const svgEditor = this; - const {$id} = svgEditor.svgCanvas; + const { $id } = svgEditor.svgCanvas; const imagelibStrings = await loadExtensionTranslation(svgEditor.configObj.pref('lang')); - const {uiStrings, canvas: svgCanvas} = svgEditor; + const { uiStrings, canvas: svgCanvas } = svgEditor; - const allowedImageLibOrigins = imagelibStrings.imgLibs.map(({url}) => { + const allowedImageLibOrigins = imagelibStrings.imgLibs.map(({ url }) => { try { return new URL(url).origin; } catch (err) { @@ -42,7 +42,7 @@ export default { * * @returns {void} */ - function closeBrowser () { + const closeBrowser = () => { $id("imgbrowse_holder").style.display = 'none'; document.activeElement.blur(); // make sure focus is the body to correct issue #417 } @@ -51,7 +51,7 @@ export default { * @param {string} url * @returns {void} */ - function importImage (url) { + const importImage = (url) => { const newImage = svgCanvas.addSVGElementFromJson({ element: 'image', attr: { @@ -111,7 +111,7 @@ export default { * @param {ImageLibMetaMessage|ImageLibMessage|string} cfg.data String is deprecated when parsed to JSON `ImageLibMessage` * @returns {void} */ - async function onMessage ({origin, data: response}) { + async function onMessage({ origin, data: response }) { if (!response || !['string', 'object'].includes(typeof response)) { // Do nothing return; @@ -128,7 +128,7 @@ export default { } if (!allowedImageLibOrigins.includes('*') && !allowedImageLibOrigins.includes(origin)) { // Todo: Surface this error to user? - console.log(`Origin ${origin} not whitelisted for posting to ${window.origin}`); + console.log(`Origin ${origin} not whitelisted for posting to ${window.origin}`); return; } const hasName = 'name' in response; @@ -173,165 +173,165 @@ export default { let entry, curMeta, svgStr, imgStr; switch (type) { - case 'meta': { - // Metadata - transferStopped = false; - curMeta = response; + case 'meta': { + // Metadata + transferStopped = false; + curMeta = response; - // Should be safe to add dynamic property as passed metadata - pending[curMeta.id] = curMeta; // lgtm [js/remote-property-injection] + // Should be safe to add dynamic property as passed metadata + pending[curMeta.id] = curMeta; // lgtm [js/remote-property-injection] - const name = (curMeta.name || 'file'); + const name = (curMeta.name || 'file'); - const message = uiStrings.notification.retrieving.replace('%s', name); + const message = uiStrings.notification.retrieving.replace('%s', name); - if (mode !== 'm') { - await seConfirm(message); - transferStopped = true; - } else { - entry = document.createElement('div'); - entry.textContent = message; - entry.dataset.id = curMeta.id; - preview.appendChild(entry); - curMeta.entry = entry; + if (mode !== 'm') { + await seConfirm(message); + transferStopped = true; + } else { + entry = document.createElement('div'); + entry.textContent = message; + entry.dataset.id = curMeta.id; + preview.appendChild(entry); + curMeta.entry = entry; + } + + return; } - - return; - } - case '<': - svgStr = true; - break; - case 'd': { - if (response.startsWith('data:image/svg+xml')) { - const pre = 'data:image/svg+xml;base64,'; - const src = response.substring(pre.length); - response = decode64(src); + case '<': svgStr = true; break; - } else if (response.startsWith('data:image/')) { - imgStr = true; - break; + case 'd': { + if (response.startsWith('data:image/svg+xml')) { + const pre = 'data:image/svg+xml;base64,'; + const src = response.substring(pre.length); + response = decode64(src); + svgStr = true; + break; + } else if (response.startsWith('data:image/')) { + imgStr = true; + break; + } } - } - // Else fall through - default: - // TODO: See if there's a way to base64 encode the binary data stream - // const str = 'data:;base64,' + svgedit.utilities.encode64(response, true); + // Else fall through + default: + // TODO: See if there's a way to base64 encode the binary data stream + // const str = 'data:;base64,' + svgedit.utilities.encode64(response, true); - // Assume it's raw image data - // importImage(str); + // Assume it's raw image data + // importImage(str); - // Don't give warning as postMessage may have been used by something else - if (mode !== 'm') { - closeBrowser(); - } else { - pending[id].entry.remove(); - } - // await alert('Unexpected data was returned: ' + response, function() { - // if (mode !== 'm') { - // closeBrowser(); - // } else { - // pending[id].entry.remove(); - // } - // }); - return; + // Don't give warning as postMessage may have been used by something else + if (mode !== 'm') { + closeBrowser(); + } else { + pending[id].entry.remove(); + } + // await alert('Unexpected data was returned: ' + response, function() { + // if (mode !== 'm') { + // closeBrowser(); + // } else { + // pending[id].entry.remove(); + // } + // }); + return; } switch (mode) { - case 's': - // Import one - if (svgStr) { - svgEditor.svgCanvas.importSvgString(response); - } else if (imgStr) { - importImage(response); - } - closeBrowser(); - break; - case 'm': { - // Import multiple - multiArr.push([(svgStr ? 'svg' : 'img'), response]); - curMeta = pending[id]; - let title; - if (svgStr) { - if (curMeta && curMeta.name) { - title = curMeta.name; - } else { - // Try to find a title - // `dropXMLInternalSubset` is to help prevent the billion laughs attack - const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement; // lgtm [js/xml-bomb] - title = xml.querySelector('title').textContent || '(SVG #' + response.length + ')'; + case 's': + // Import one + if (svgStr) { + svgEditor.svgCanvas.importSvgString(response); + } else if (imgStr) { + importImage(response); } - if (curMeta) { - Array.from(preview.children).forEach(function(element) { - if (element.dataset.id === id) { - if (curMeta.preview_url) { - const img = document.createElement("img"); - img.src = curMeta.preview_url; - const span = document.createElement("span"); - span.appendChild(img); - element.append(span); - } else { - element.textContent = title; + closeBrowser(); + break; + case 'm': { + // Import multiple + multiArr.push([(svgStr ? 'svg' : 'img'), response]); + curMeta = pending[id]; + let title; + if (svgStr) { + if (curMeta && curMeta.name) { + title = curMeta.name; + } else { + // Try to find a title + // `dropXMLInternalSubset` is to help prevent the billion laughs attack + const xml = new DOMParser().parseFromString(dropXMLInternalSubset(response), 'text/xml').documentElement; // lgtm [js/xml-bomb] + title = xml.querySelector('title').textContent || '(SVG #' + response.length + ')'; + } + if (curMeta) { + Array.from(preview.children).forEach(function (element) { + if (element.dataset.id === id) { + if (curMeta.preview_url) { + const img = document.createElement("img"); + img.src = curMeta.preview_url; + const span = document.createElement("span"); + span.appendChild(img); + element.append(span); + } else { + element.textContent = title; + } + submit.removeAttribute('disabled'); } - submit.removeAttribute('disabled'); - } - }); + }); + } else { + const div = document.createElement("div"); + div.textContent = title; + preview.appendChild(div); + submit.removeAttribute('disabled'); + } } else { - const div = document.createElement("div"); - div.textContent = title; - preview.appendChild(div); - submit.removeAttribute('disabled'); - } - } else { - if (curMeta && curMeta.preview_url) { - title = curMeta.name || ''; - entry = document.createElement('span'); - const img = document.createElement("img"); - img.src = curMeta.preview_url; - entry.appendChild(img); - entry.appendChild(document.createTextNode(title)) - } else { - entry = document.createElement("img"); - entry.src = response; - } + if (curMeta && curMeta.preview_url) { + title = curMeta.name || ''; + entry = document.createElement('span'); + const img = document.createElement("img"); + img.src = curMeta.preview_url; + entry.appendChild(img); + entry.appendChild(document.createTextNode(title)) + } else { + entry = document.createElement("img"); + entry.src = response; + } - if (curMeta) { - Array.from(preview.children).forEach(function(element) { - if (element.dataset.id === id) { - element.appendChild(entry); - submit.removeAttribute('disabled'); - } - }); - } else { - const div = document.createElement("div"); - div.appendChild(entry); - preview.appendChild(div); - submit.removeAttribute('disabled'); + if (curMeta) { + Array.from(preview.children).forEach(function (element) { + if (element.dataset.id === id) { + element.appendChild(entry); + submit.removeAttribute('disabled'); + } + }); + } else { + const div = document.createElement("div"); + div.appendChild(entry); + preview.appendChild(div); + submit.removeAttribute('disabled'); + } } + break; + } case 'o': { + // Open + if (!svgStr) { break; } + closeBrowser(); + const ok = await svgEditor.openPrep(); + if (!ok) { return; } + svgCanvas.clear(); + svgCanvas.setSvgString(response); + // updateCanvas(); + break; } - break; - } case 'o': { - // Open - if (!svgStr) { break; } - closeBrowser(); - const ok = await svgEditor.openPrep(); - if (!ok) { return; } - svgCanvas.clear(); - svgCanvas.setSvgString(response); - // updateCanvas(); - break; - } } } // Receive `postMessage` data window.addEventListener('message', onMessage, true); - function insertAfter(referenceNode, newNode) { + const insertAfter = (referenceNode, newNode) => { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } - function toggleMultiLoop() { + const toggleMultiLoop = () => { $.each(multiArr, function (i) { const type = this[0]; const data = this[1]; @@ -342,8 +342,8 @@ export default { } svgCanvas.moveSelectedElements(i * 20, i * 20, false); }); - while(preview.firstChild) - preview.removeChild(preview.firstChild); + while (preview.firstChild) + preview.removeChild(preview.firstChild); multiArr = []; $id("imgbrowse_holder").style.display = 'none'; } @@ -352,7 +352,7 @@ export default { * @param {boolean} show * @returns {void} */ - function toggleMulti (show) { + const toggleMulti = (show) => { $id('lib_framewrap').style.right = (show ? 200 : 10); $id('imglib_opts').style.right = (show ? 200 : 10); if (!preview) { @@ -361,26 +361,26 @@ export default { // eslint-disable-next-line max-len preview.setAttribute('style', `position: absolute;top: 45px;right: 10px;width: 180px;bottom: 45px;background: #fff;overflow: auto;`); insertAfter($id('lib_framewrap'), preview); - + submit = document.createElement('button'); submit.setAttribute('content', 'Import selected'); - submit.setAttribute('disabled', true); + submit.setAttribute('disabled', true); submit.textContent = 'Import selected'; - submit.setAttribute('style', `position: absolute;bottom: 10px;right: -10px;`); + submit.setAttribute('style', `position: absolute;bottom: 10px;right: -10px;`); $id('imgbrowse').appendChild(submit); submit.addEventListener('click', toggleMultiLoop); submit.addEventListener('touchend', toggleMultiLoop); } - submit.style.display = (show) ? 'block' : 'none'; + submit.style.display = (show) ? 'block' : 'none'; preview.style.display = (show) ? 'block' : 'none'; - + } /** * * @returns {void} */ - function showBrowser () { + const showBrowser = () => { let browser = $id('imgbrowse'); if (!browser) { const div = document.createElement('div'); @@ -410,7 +410,7 @@ export default { const button = document.createElement('button'); // eslint-disable-next-line max-len - button.innerHTML = '' + uiStrings.common.cancel ; + button.innerHTML = '' + uiStrings.common.cancel; browser.appendChild(button); button.addEventListener('click', function () { $id("imgbrowse_holder").style.display = 'none'; @@ -419,11 +419,11 @@ export default { $id("imgbrowse_holder").style.display = 'none'; }); button.setAttribute('style', `position: absolute;top: 5;right: -10;`); - + const leftBlock = document.createElement('span'); leftBlock.setAttribute('style', `position: absolute;top: 5;left: 10;`); browser.appendChild(leftBlock); - + const back = document.createElement('button'); back.style.visibility = "hidden"; // eslint-disable-next-line max-len @@ -448,27 +448,27 @@ export default { const select = document.createElement('select'); select.innerHTML = '' + - imagelibStrings.import_single + '' + - imagelibStrings.import_multi + '' + - imagelibStrings.open + ''; + imagelibStrings.import_single + '' + + imagelibStrings.import_multi + '' + + imagelibStrings.open + ''; leftBlock.appendChild(select); select.addEventListener('change', function () { mode = this.value; switch (mode) { - case 's': - case 'o': - toggleMulti(false); - break; + case 's': + case 'o': + toggleMulti(false); + break; - case 'm': - // Import multiple - toggleMulti(true); - break; + case 'm': + // Import multiple + toggleMulti(true); + break; } }); select.setAttribute('style', `margin-top: 10px;`); - imagelibStrings.imgLibs.forEach(function ({name, url, description}) { + imagelibStrings.imgLibs.forEach(function ({ name, url, description }) { const li = document.createElement('li'); libOpts.appendChild(li); li.textContent = name; @@ -495,91 +495,93 @@ export default { } } - const events = { - id: 'tool_imagelib', - click () { - showBrowser(); - } - }; - return { svgicons: 'ext-imagelib.xml', - events, - callback () { + callback() { + // Add the button and its handler(s) + const buttonTemplate = document.createElement("template"); + buttonTemplate.innerHTML = ` + + `; + insertAfter($id('tool_export'), buttonTemplate.content.cloneNode(true)); + $id('tool_imagelib').addEventListener("click", () => { + showBrowser(); + }); + const style = document.createElement('style'); style.textContent = '#imgbrowse_holder {' + - 'position: absolute;' + - 'top: 0;' + - 'left: 0;' + - 'width: 100%;' + - 'height: 100%;' + - 'background-color: rgba(0, 0, 0, .5);' + - 'z-index: 5;' + - '}' + - '#imgbrowse {' + - 'position: absolute;' + - 'top: 25px;' + - 'left: 25px;' + - 'right: 25px;' + - 'bottom: 25px;' + - 'min-width: 300px;' + - 'min-height: 200px;' + - 'background: #B0B0B0;' + - 'border: 1px outset #777;' + - '}' + - '#imgbrowse h1 {' + - 'font-size: 20px;' + - 'margin: .4em;' + - 'text-align: center;' + - '}' + - '#lib_framewrap,' + - '#imgbrowse > ul {' + - 'position: absolute;' + - 'top: 45px;' + - 'left: 10px;' + - 'right: 10px;' + - 'bottom: 10px;' + - 'background: white;' + - 'margin: 0;' + - 'padding: 0;' + - '}' + - '#imgbrowse > ul {' + - 'overflow: auto;' + - '}' + - '#imgbrowse > div {' + - 'border: 1px solid #666;' + - '}' + - '#imglib_preview > div {' + - 'padding: 5px;' + - 'font-size: 12px;' + - '}' + - '#imglib_preview img {' + - 'display: block;' + - 'margin: 0 auto;' + - 'max-height: 100px;' + - '}' + - '#imgbrowse li {' + - 'list-style: none;' + - 'padding: .5em;' + - 'background: #E8E8E8;' + - 'border-bottom: 1px solid #B0B0B0;' + - 'line-height: 1.2em;' + - 'font-style: sans-serif;' + - '}' + - '#imgbrowse li > span {' + - 'color: #666;' + - 'font-size: 15px;' + - 'display: block;' + - '}' + - '#imgbrowse li:hover {' + - 'background: #FFC;' + - 'cursor: pointer;' + - '}' + - '#imgbrowse iframe {' + - 'width: 100%;' + - 'height: 100%;' + - 'border: 0;' + - '}'; + 'position: absolute;' + + 'top: 0;' + + 'left: 0;' + + 'width: 100%;' + + 'height: 100%;' + + 'background-color: rgba(0, 0, 0, .5);' + + 'z-index: 5;' + + '}' + + '#imgbrowse {' + + 'position: absolute;' + + 'top: 25px;' + + 'left: 25px;' + + 'right: 25px;' + + 'bottom: 25px;' + + 'min-width: 300px;' + + 'min-height: 200px;' + + 'background: #B0B0B0;' + + 'border: 1px outset #777;' + + '}' + + '#imgbrowse h1 {' + + 'font-size: 20px;' + + 'margin: .4em;' + + 'text-align: center;' + + '}' + + '#lib_framewrap,' + + '#imgbrowse > ul {' + + 'position: absolute;' + + 'top: 45px;' + + 'left: 10px;' + + 'right: 10px;' + + 'bottom: 10px;' + + 'background: white;' + + 'margin: 0;' + + 'padding: 0;' + + '}' + + '#imgbrowse > ul {' + + 'overflow: auto;' + + '}' + + '#imgbrowse > div {' + + 'border: 1px solid #666;' + + '}' + + '#imglib_preview > div {' + + 'padding: 5px;' + + 'font-size: 12px;' + + '}' + + '#imglib_preview img {' + + 'display: block;' + + 'margin: 0 auto;' + + 'max-height: 100px;' + + '}' + + '#imgbrowse li {' + + 'list-style: none;' + + 'padding: .5em;' + + 'background: #E8E8E8;' + + 'border-bottom: 1px solid #B0B0B0;' + + 'line-height: 1.2em;' + + 'font-style: sans-serif;' + + '}' + + '#imgbrowse li > span {' + + 'color: #666;' + + 'font-size: 15px;' + + 'display: block;' + + '}' + + '#imgbrowse li:hover {' + + 'background: #FFC;' + + 'cursor: pointer;' + + '}' + + '#imgbrowse iframe {' + + 'width: 100%;' + + 'height: 100%;' + + 'border: 0;' + + '}'; document.head.appendChild(style); } }; diff --git a/src/editor/extensions/ext-panning/ext-panning.js b/src/editor/extensions/ext-panning/ext-panning.js index 10f6ecfc..1be741b2 100644 --- a/src/editor/extensions/ext-panning/ext-panning.js +++ b/src/editor/extensions/ext-panning/ext-panning.js @@ -24,28 +24,44 @@ const loadExtensionTranslation = async function (lang) { export default { name: 'panning', - async init ({importLocale}) { + async init({ + importLocale + }) { const svgEditor = this; const strings = await loadExtensionTranslation(svgEditor.configObj.pref('lang')); - const {svgCanvas} = svgEditor; - const events = { - id: 'ext-panning', - click () { - svgCanvas.setMode('ext-panning'); - } - }; + const { + svgCanvas + } = svgEditor; + const { + $id + } = svgCanvas; + const insertAfter = (referenceNode, newNode) => { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); + } return { newUI: true, name: strings.name, - events, - mouseDown () { + callback() { + // Add the button and its handler(s) + const buttonTemplate = document.createElement("template"); + buttonTemplate.innerHTML = ` + + `; + insertAfter($id('tool_zoom'), buttonTemplate.content.cloneNode(true)); + $id('ext-panning').addEventListener("click", () => { + svgCanvas.setMode('ext-panning'); + }); + }, + mouseDown() { if (svgCanvas.getMode() === 'ext-panning') { svgEditor.setPanning(true); - return {started: true}; + return { + started: true + }; } return undefined; }, - mouseUp () { + mouseUp() { if (svgCanvas.getMode() === 'ext-panning') { svgEditor.setPanning(false); return { diff --git a/src/editor/panels/LeftPanel.js b/src/editor/panels/LeftPanel.js index c58684bd..600c9157 100644 --- a/src/editor/panels/LeftPanel.js +++ b/src/editor/panels/LeftPanel.js @@ -200,7 +200,6 @@ class LeftPanel { - @@ -216,7 +215,6 @@ class LeftPanel { - `; this.editor.$svgEditor.append(template.content.cloneNode(true));