UNPKG

svgedit

Version:

Powerful SVG-Editor for your browser

181 lines (170 loc) 6.65 kB
/* globals seConfirm */ /** * @file ext-opensave.js * * @license MIT * * @copyright 2020 OptimistikSAS * */ /** * @type {module:svgcanvas.EventHandler} * @param {external:Window} wind * @param {module:svgcanvas.SvgCanvas#event:saved} svg The SVG source * @listens module:svgcanvas.SvgCanvas#event:saved * @returns {void} */ import { fileOpen, fileSave } from 'browser-fs-access'; const name = "opensave"; let handle = null; const loadExtensionTranslation = async function (svgEditor) { let translationModule; const lang = svgEditor.configObj.pref('lang'); try { // eslint-disable-next-line no-unsanitized/method translationModule = await import(`./locale/${lang}.js`); } catch (_error) { // eslint-disable-next-line no-console console.warn(`Missing translation (${lang}) for ${name} - using 'en'`); // eslint-disable-next-line no-unsanitized/method translationModule = await import(`./locale/en.js`); } svgEditor.i18next.addResourceBundle(lang, 'translation', translationModule.default, true, true); }; export default { name, async init(_S) { const svgEditor = this; const { svgCanvas } = svgEditor; const { $id } = svgCanvas; await loadExtensionTranslation(svgEditor); /** * @fires module:svgcanvas.SvgCanvas#event:ext_onNewDocument * @returns {void} */ const clickClear = async function () { const [ x, y ] = svgEditor.configObj.curConfig.dimensions; const ok = await seConfirm(svgEditor.i18next.t('notification.QwantToClear')); if (ok === "Cancel") { return; } svgEditor.leftPanel.clickSelect(); svgEditor.svgCanvas.clear(); svgEditor.svgCanvas.setResolution(x, y); svgEditor.updateCanvas(true); svgEditor.zoomImage(); svgEditor.layersPanel.populateLayers(); svgEditor.topPanel.updateContextPanel(); svgEditor.svgCanvas.runExtensions("onNewDocument"); }; /** * By default, this.editor.svgCanvas.open() is a no-op. It is up to an extension * mechanism (opera widget, etc.) to call `setCustomHandlers()` which * will make it do something. * @returns {void} */ const clickOpen = async function () { // ask user before clearing an unsaved SVG const response = await svgEditor.openPrep(); if (response === 'Cancel') { return; } svgCanvas.clear(); try { const blob = await fileOpen({ mimeTypes: [ 'image/*' ] }); const svgContent = await blob.text(); await svgEditor.loadSvgString(svgContent); svgEditor.updateCanvas(); } catch (err) { if (err.name !== 'AbortError') { return console.error(err); } } }; const b64toBlob = (b64Data, contentType='', sliceSize=512) => { const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } const blob = new Blob(byteArrays, { type: contentType }); return blob; }; /** * * @returns {void} */ const clickSave = async function (type, _) { const $editorDialog = $id("se-svg-editor-dialog"); const editingsource = $editorDialog.getAttribute("dialog") === "open"; if (editingsource) { svgEditor.saveSourceEditor(); } else { // In the future, more options can be provided here const saveOpts = { images: svgEditor.configObj.pref("img_save"), round_digits: 6 }; // remove the selected outline before serializing svgCanvas.clearSelection(); // Update save options if provided if (saveOpts) { const saveOptions = svgCanvas.mergeDeep(svgCanvas.getSvgOption(), saveOpts); for (const [ key, value ] of Object.entries(saveOptions)) { svgCanvas.setSvgOption(key, value); } } svgCanvas.setSvgOption('apply', true); // no need for doctype, see https://jwatt.org/svg/authoring/#doctype-declaration const svg = '<?xml version="1.0"?>\n' + svgCanvas.svgCanvasToString(); const b64Data = svgCanvas.encode64(svg); const blob = b64toBlob(b64Data, 'image/svg+xml'); try { if(type === "save" && handle !== null) { const throwIfExistingHandleNotGood = false; handle = await fileSave(blob, { fileName: 'icon.svg', extensions: [ '.svg' ] }, handle, throwIfExistingHandleNotGood); } else { handle = await fileSave(blob, { fileName: 'icon.svg', extensions: [ '.svg' ] }); } } catch (err) { if (err.name !== 'AbortError') { return console.error(err); } } } }; return { name: svgEditor.i18next.t(`${name}:name`), // The callback should be used to load the DOM with the appropriate UI items callback() { // eslint-disable-next-line no-unsanitized/property const buttonTemplate = ` <se-menu-item id="tool_clear" label="opensave.new_doc" shortcut="N" src="new.svg"></se-menu-item>`; svgCanvas.insertChildAtIndex($id('main_button'), buttonTemplate, 0); const openButtonTemplate = `<se-menu-item id="tool_open" label="opensave.open_image_doc" src="open.svg"></se-menu-item>`; svgCanvas.insertChildAtIndex($id('main_button'), openButtonTemplate, 1); const saveButtonTemplate = `<se-menu-item id="tool_save" label="opensave.save_doc" shortcut="S" src="saveImg.svg"></se-menu-item>`; svgCanvas.insertChildAtIndex($id('main_button'), saveButtonTemplate, 2); const saveAsButtonTemplate = `<se-menu-item id="tool_save_as" label="opensave.save_as_doc" src="saveImg.svg"></se-menu-item>`; svgCanvas.insertChildAtIndex($id('main_button'), saveAsButtonTemplate, 3); // handler $id("tool_clear").addEventListener("click", clickClear.bind(this)); $id("tool_open").addEventListener("click", clickOpen.bind(this)); $id("tool_save").addEventListener("click", clickSave.bind(this, "save")); $id("tool_save_as").addEventListener("click", clickSave.bind(this, "saveas")); } }; } };