UNPKG

svgedit

Version:

Powerful SVG-Editor for your browser

846 lines (748 loc) 34.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: EditorStartup.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: EditorStartup.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/* globals seConfirm seAlert */ import { putLocale } from './locale.js' import { hasCustomHandler, getCustomHandler, injectExtendedContextMenuItemsIntoDom } from './contextmenu.js' import editorTemplate from './templates/editorTemplate.html' import SvgCanvas from '@svgedit/svgcanvas' import Rulers from './Rulers.js' /** * @fires module:svgcanvas.SvgCanvas#event:svgEditorReady * @returns {void} */ const readySignal = () => { // let the opener know SVG Edit is ready (now that config is set up) const w = window.opener || window.parent if (w) { try { /** * Triggered on a containing `document` (of `window.opener` * or `window.parent`) when the editor is loaded. * @event module:SVGEditor#event:svgEditorReadyEvent * @type {Event} * @property {true} bubbles * @property {true} cancelable */ /** * @name module:SVGthis.svgEditorReadyEvent * @type {module:SVGEditor#event:svgEditorReadyEvent} */ const svgEditorReadyEvent = new w.CustomEvent('svgEditorReady', { bubbles: true, cancelable: true }) w.document.documentElement.dispatchEvent(svgEditorReadyEvent) } catch (e) { /* empty fn */ } } } const { $id, $click, convertUnit } = SvgCanvas /** * */ class EditorStartup { /** * */ constructor (div) { this.extensionsAdded = false this.messageQueue = [] this.$container = div ?? $id('svg_editor') } /** * Auto-run after a Promise microtask. * @function module:SVGthis.init * @returns {void} */ async init () { if ('localStorage' in window) { this.storage = window.localStorage } this.configObj.load() const { i18next } = await putLocale(this.configObj.pref('lang'), this.goodLangs) this.i18next = i18next await import('./components/index.js') await import('./dialogs/index.js') try { // add editor components to the DOM const template = document.createElement('template') template.innerHTML = editorTemplate this.$container.append(template.content.cloneNode(true)) this.$svgEditor = this.$container.querySelector('.svg_editor') // allow to prepare the dom without display this.$svgEditor.style.visibility = 'hidden' this.workarea = $id('workarea') // Image props dialog added to DOM const newSeImgPropDialog = document.createElement('se-img-prop-dialog') newSeImgPropDialog.setAttribute('id', 'se-img-prop') this.$container.append(newSeImgPropDialog) newSeImgPropDialog.init(this.i18next) // editor prefences dialoag added to DOM const newSeEditPrefsDialog = document.createElement('se-edit-prefs-dialog') newSeEditPrefsDialog.setAttribute('id', 'se-edit-prefs') this.$container.append(newSeEditPrefsDialog) newSeEditPrefsDialog.init(this.i18next) // canvas menu added to DOM const dialogBox = document.createElement('se-cmenu_canvas-dialog') dialogBox.setAttribute('id', 'se-cmenu_canvas') this.$container.append(dialogBox) dialogBox.init(this.i18next) // alertDialog added to DOM const alertBox = document.createElement('se-alert-dialog') alertBox.setAttribute('id', 'se-alert-dialog') this.$container.append(alertBox) // promptDialog added to DOM const promptBox = document.createElement('se-prompt-dialog') promptBox.setAttribute('id', 'se-prompt-dialog') this.$container.append(promptBox) // Export dialog added to DOM const exportDialog = document.createElement('se-export-dialog') exportDialog.setAttribute('id', 'se-export-dialog') this.$container.append(exportDialog) exportDialog.init(this.i18next) } catch (err) { console.error(err) } /** * @name module:SVGthis.canvas * @type {module:svgcanvas.SvgCanvas} */ this.svgCanvas = new SvgCanvas( $id('svgcanvas'), this.configObj.curConfig ) // once svgCanvas is init - adding listener to the changes of the current mode this.modeEvent = this.svgCanvas.modeEvent document.addEventListener('modeChange', (evt) => this.modeListener(evt)) /** if true - selected tool can be cancelled with Esc key * disables on dragging (mousedown) to avoid changing mode in the middle of drawing */ this.enableToolCancel = true this.leftPanel.init() this.bottomPanel.init() this.topPanel.init() this.layersPanel.init() this.mainMenu.init() const { undoMgr } = this.svgCanvas this.canvMenu = $id('se-cmenu_canvas') this.exportWindow = null this.defaultImageURL = `${this.configObj.curConfig.imgPath}/logo.svg` const zoomInIcon = 'crosshair' const zoomOutIcon = 'crosshair' this.uiContext = 'toolbars' // For external openers readySignal() this.rulers = new Rulers(this) this.layersPanel.populateLayers() this.selectedElement = null this.multiselected = false const aLink = $id('cur_context_panel') $click(aLink, (evt) => { const link = evt.target if (link.hasAttribute('data-root')) { this.svgCanvas.leaveContext() } else { this.svgCanvas.setContext(link.textContent) } this.svgCanvas.clearSelection() return false }) // bind the selected event to our function that handles updates to the UI this.svgCanvas.bind('selected', this.selectedChanged.bind(this)) this.svgCanvas.bind('transition', this.elementTransition.bind(this)) this.svgCanvas.bind('changed', this.elementChanged.bind(this)) this.svgCanvas.bind('exported', this.exportHandler.bind(this)) this.svgCanvas.bind('exportedPDF', function (win, data) { if (!data.output) { // Ignore Chrome return } const { exportWindowName } = data if (exportWindowName) { this.exportWindow = window.open('', this.exportWindowName) // A hack to get the window via JSON-able name without opening a new one } if (!this.exportWindow || this.exportWindow.closed) { seAlert(this.i18next.t('notification.popupWindowBlocked')) return } this.exportWindow.location.href = data.output }.bind(this)) this.svgCanvas.bind('zoomed', this.zoomChanged.bind(this)) this.svgCanvas.bind('zoomDone', this.zoomDone.bind(this)) this.svgCanvas.bind( 'updateCanvas', /** * @param {external:Window} win * @param {PlainObject} centerInfo * @param {false} centerInfo.center * @param {module:math.XYObject} centerInfo.newCtr * @listens module:svgcanvas.SvgCanvas#event:updateCanvas * @returns {void} */ function (win, { center, newCtr }) { this.updateCanvas(center, newCtr) }.bind(this) ) this.svgCanvas.bind('contextset', this.contextChanged.bind(this)) this.svgCanvas.bind('extension_added', this.extAdded.bind(this)) this.svgCanvas.bind('elementRenamed', this.elementRenamed.bind(this)) this.svgCanvas.bind('beforeClear', this.beforeClear.bind(this)) this.svgCanvas.bind('afterClear', this.afterClear.bind(this)) this.svgCanvas.textActions.setInputElem($id('text')) this.setBackground(this.configObj.pref('bkgd_color'), this.configObj.pref('bkgd_url')) // update resolution option with actual resolution const res = this.svgCanvas.getResolution() if (this.configObj.curConfig.baseUnit !== 'px') { res.w = convertUnit(res.w) + this.configObj.curConfig.baseUnit res.h = convertUnit(res.h) + this.configObj.curConfig.baseUnit } $id('se-img-prop').setAttribute('dialog', 'close') $id('se-img-prop').setAttribute('title', this.svgCanvas.getDocumentTitle()) $id('se-img-prop').setAttribute('width', res.w) $id('se-img-prop').setAttribute('height', res.h) $id('se-img-prop').setAttribute('save', this.configObj.pref('img_save')) // Lose focus for select elements when changed (Allows keyboard shortcuts to work better) const selElements = document.querySelectorAll('select') Array.from(selElements).forEach(function (element) { element.addEventListener('change', function (evt) { evt.currentTarget.blur() }) }) // fired when user wants to move elements to another layer let promptMoveLayerOnce = false $id('selLayerNames').addEventListener('change', (evt) => { const destLayer = evt.detail.value const confirmStr = this.i18next.t('notification.QmoveElemsToLayer').replace('%s', destLayer) /** * @param {boolean} ok * @returns {void} */ const moveToLayer = (ok) => { if (!ok) { return } promptMoveLayerOnce = true this.svgCanvas.moveSelectedToLayer(destLayer) this.svgCanvas.clearSelection() this.layersPanel.populateLayers() } if (destLayer) { if (promptMoveLayerOnce) { moveToLayer(true) } else { const ok = seConfirm(confirmStr) if (!ok) { return } moveToLayer(true) } } }) $id('tool_font_family').addEventListener('change', (evt) => { this.svgCanvas.setFontFamily(evt.detail.value) }) $id('seg_type').addEventListener('change', (evt) => { this.svgCanvas.setSegType(evt.detail.value) }) const addListenerMulti = (element, eventNames, listener) => { eventNames.split(' ').forEach((eventName) => element.addEventListener(eventName, listener, false)) } addListenerMulti($id('text'), 'keyup input', (evt) => { this.svgCanvas.setTextContent(evt.currentTarget.value) }) $id('link_url').addEventListener('change', (evt) => { if (evt.currentTarget.value.length) { this.svgCanvas.setLinkURL(evt.currentTarget.value) } else { this.svgCanvas.removeHyperlink() } }) $id('g_title').addEventListener('change', (evt) => { this.svgCanvas.setGroupTitle(evt.currentTarget.value) }) let lastX = null; let lastY = null let panning = false; let keypan = false let previousMode = 'select' $id('svgcanvas').addEventListener('mouseup', (evt) => { if (panning === false) { return true } this.workarea.scrollLeft -= (evt.clientX - lastX) this.workarea.scrollTop -= (evt.clientY - lastY) lastX = evt.clientX lastY = evt.clientY if (evt.type === 'mouseup') { panning = false } return false }) $id('svgcanvas').addEventListener('mousemove', (evt) => { if (panning === false) { return true } this.workarea.scrollLeft -= (evt.clientX - lastX) this.workarea.scrollTop -= (evt.clientY - lastY) lastX = evt.clientX lastY = evt.clientY if (evt.type === 'mouseup') { panning = false } return false }) $id('svgcanvas').addEventListener('mousedown', (evt) => { this.enableToolCancel = false if (evt.button === 1 || keypan === true) { // prDefault to avoid firing of browser's panning on mousewheel evt.preventDefault() panning = true previousMode = this.svgCanvas.getMode() this.svgCanvas.setMode('ext-panning') this.workarea.style.cursor = 'grab' lastX = evt.clientX lastY = evt.clientY return false } return true }) // preventing browser's scaling with Ctrl+wheel this.$container.addEventListener('wheel', (e) => { if (e.ctrlKey) { e.preventDefault() } }) window.addEventListener('mouseup', (evt) => { this.enableToolCancel = true if (evt.button === 1) { this.svgCanvas.setMode(previousMode ?? 'select') } panning = false }) // Allows quick change to the select mode while panning mode is active this.workarea.addEventListener('dblclick', (evt) => { if (this.svgCanvas.getMode() === 'ext-panning') { this.leftPanel.clickSelect() } }) document.addEventListener('keydown', (e) => { if (e.target.nodeName !== 'BODY') return if (e.code.toLowerCase() === 'space') { this.svgCanvas.spaceKey = keypan = true e.preventDefault() } else if ((e.key.toLowerCase() === 'shift') &amp;&amp; (this.svgCanvas.getMode() === 'zoom')) { this.workarea.style.cursor = zoomOutIcon e.preventDefault() } }) document.addEventListener('keyup', (e) => { if (e.target.nodeName !== 'BODY') return if (e.code.toLowerCase() === 'space') { this.svgCanvas.spaceKey = keypan = false this.svgCanvas.setMode(previousMode === 'ext-panning' ? 'select' : previousMode ?? 'select') e.preventDefault() } else if ((e.key.toLowerCase() === 'shift') &amp;&amp; (this.svgCanvas.getMode() === 'zoom')) { this.workarea.style.cursor = zoomInIcon e.preventDefault() } }) /** * @function module:SVGthis.setPanning * @param {boolean} active * @returns {void} */ this.setPanning = (active) => { this.svgCanvas.spaceKey = keypan = active } let inp /** * * @returns {void} */ const unfocus = () => { inp.blur() } const liElems = this.$svgEditor.querySelectorAll('button, select, input:not(#text)') const self = this Array.prototype.forEach.call(liElems, function (el) { el.addEventListener('focus', (e) => { inp = e.currentTarget self.uiContext = 'toolbars' self.workarea.addEventListener('mousedown', unfocus) }) el.addEventListener('blur', () => { self.uiContext = 'canvas' self.workarea.removeEventListener('mousedown', unfocus) // Go back to selecting text if in textedit mode if (self.svgCanvas.getMode() === 'textedit') { $id('text').focus() } }) }) // ref: https://stackoverflow.com/a/1038781 function getWidth () { return Math.max( document.body.scrollWidth, document.documentElement.scrollWidth, document.body.offsetWidth, document.documentElement.offsetWidth, document.documentElement.clientWidth ) } function getHeight () { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.documentElement.clientHeight ) } const winWh = { width: getWidth(), height: getHeight() } window.addEventListener('resize', () => { Object.entries(winWh).forEach(([type, val]) => { const curval = (type === 'width') ? window.innerWidth - 15 : window.innerHeight this.workarea['scroll' + (type === 'width' ? 'Left' : 'Top')] -= (curval - val) / 2 winWh[type] = curval }) }) this.workarea.addEventListener('scroll', () => { this.rulers.manageScroll() }) $id('stroke_width').value = this.configObj.curConfig.initStroke.width $id('opacity').value = this.configObj.curConfig.initOpacity * 100 const elements = document.getElementsByClassName('push_button') Array.from(elements).forEach(function (element) { element.addEventListener('mousedown', function (event) { if (!event.currentTarget.classList.contains('disabled')) { event.currentTarget.classList.add('push_button_pressed') event.currentTarget.classList.remove('push_button') } }) element.addEventListener('mouseout', function (event) { event.currentTarget.classList.add('push_button') event.currentTarget.classList.remove('push_button_pressed') }) element.addEventListener('mouseup', function (event) { event.currentTarget.classList.add('push_button') event.currentTarget.classList.remove('push_button_pressed') }) }) this.layersPanel.populateLayers() const centerCanvas = () => { // this centers the canvas vertically in the this.workarea (horizontal handled in CSS) this.workarea.style.lineHeight = this.workarea.style.height } addListenerMulti(window, 'load resize', centerCanvas) // Prevent browser from erroneously repopulating fields const inputEles = document.querySelectorAll('input') Array.from(inputEles).forEach(function (inputEle) { inputEle.setAttribute('autocomplete', 'off') }) const selectEles = document.querySelectorAll('select') Array.from(selectEles).forEach(function (inputEle) { inputEle.setAttribute('autocomplete', 'off') }) $id('se-svg-editor-dialog').addEventListener('change', function (e) { if (e?.detail?.copy === 'click') { this.cancelOverlays(e) } else if (e?.detail?.dialog === 'dynamic') { this.toggleDynamicOutput(e) } else if (e?.detail?.dialog === 'closed') { this.hideSourceEditor() } else { this.saveSourceEditor(e) } }.bind(this)) $id('se-cmenu_canvas').addEventListener('change', function (e) { const action = e?.detail?.trigger switch (action) { case 'delete': this.svgCanvas.deleteSelectedElements() break case 'cut': this.cutSelected() break case 'copy': this.copySelected() break case 'paste': this.svgCanvas.pasteElements() break case 'paste_in_place': this.svgCanvas.pasteElements('in_place') break case 'group': case 'group_elements': this.svgCanvas.groupSelectedElements() break case 'ungroup': this.svgCanvas.ungroupSelectedElement() break case 'move_front': this.svgCanvas.moveToTopSelectedElement() break case 'move_up': this.moveUpDownSelected('Up') break case 'move_down': this.moveUpDownSelected('Down') break case 'move_back': this.svgCanvas.moveToBottomSelectedElement() break default: if (hasCustomHandler(action)) { getCustomHandler(action).call() } break } }.bind(this)) // Select given tool this.ready(function () { const preTool = $id(`tool_${this.configObj.curConfig.initTool}`) const regTool = $id(this.configObj.curConfig.initTool) const selectTool = $id('tool_select') const $editDialog = $id('se-edit-prefs') if (preTool) { preTool.click() } else if (regTool) { regTool.click() } else { selectTool.click() } if (this.configObj.curConfig.wireframe) { $id('tool_wireframe').click() } if (this.configObj.curConfig.showRulers) { this.rulers.display(true) } else { this.rulers.display(false) } if (this.configObj.curConfig.showRulers) { $editDialog.setAttribute('showrulers', true) } if (this.configObj.curConfig.baseUnit) { $editDialog.setAttribute('baseunit', this.configObj.curConfig.baseUnit) } if (this.configObj.curConfig.gridSnapping) { $editDialog.setAttribute('gridsnappingon', true) } if (this.configObj.curConfig.snappingStep) { $editDialog.setAttribute('gridsnappingstep', this.configObj.curConfig.snappingStep) } if (this.configObj.curConfig.gridColor) { $editDialog.setAttribute('gridcolor', this.configObj.curConfig.gridColor) } if (this.configObj.curConfig.dynamicOutput) { $editDialog.setAttribute('dynamicoutput', true) } }.bind(this)) // zoom $id('zoom').value = (this.svgCanvas.getZoom() * 100).toFixed(1) this.canvMenu.setAttribute('disableallmenu', true) this.canvMenu.setAttribute('enablemenuitems', '#delete,#cut,#copy') this.enableOrDisableClipboard() window.addEventListener('storage', function (e) { if (e.key !== 'svgedit_clipboard') { return } this.enableOrDisableClipboard() }.bind(this)) window.addEventListener('beforeunload', function (e) { // Suppress warning if page is empty if (undoMgr.getUndoStackSize() === 0) { this.showSaveWarning = false } // showSaveWarning is set to 'false' when the page is saved. if (!this.configObj.curConfig.no_save_warning &amp;&amp; this.showSaveWarning) { // Browser already asks question about closing the page e.returnValue = this.i18next.t('notification.unsavedChanges') // Firefox needs this when beforeunload set by addEventListener (even though message is not used) return this.i18next.t('notification.unsavedChanges') } return true }.bind(this)) // Use HTML5 File API: http://www.w3.org/TR/FileAPI/ // if browser has HTML5 File API support, then we will show the open menu item // and provide a file input to click. When that change event fires, it will // get the text contents of the file and send it to the canvas this.workarea.addEventListener('dragenter', this.onDragEnter) this.workarea.addEventListener('dragover', this.onDragOver) this.workarea.addEventListener('dragleave', this.onDragLeave) this.updateCanvas(true) // Load extensions this.extAndLocaleFunc() // Defer injection to wait out initial menu processing. This probably goes // away once all context menu behavior is brought to context menu. this.ready(() => { injectExtendedContextMenuItemsIntoDom() }) // run callbacks stored by this.ready await this.runCallbacks() } /** * @fires module:svgcanvas.SvgCanvas#event:ext_addLangData * @fires module:svgcanvas.SvgCanvas#event:ext_langReady * @fires module:svgcanvas.SvgCanvas#event:ext_langChanged * @fires module:svgcanvas.SvgCanvas#event:extensions_added * @returns {Promise&lt;module:locale.LangAndData>} Resolves to result of {@link module:locale.readLang} */ async extAndLocaleFunc () { this.$svgEditor.style.visibility = 'visible' try { // load standard extensions await Promise.all( this.configObj.curConfig.extensions.map(async (extname) => { /** * @tutorial ExtensionDocs * @typedef {PlainObject} module:SVGthis.ExtensionObject * @property {string} [name] Name of the extension. Used internally; no need for i18n. Defaults to extension name without beginning "ext-" or ending ".js". * @property {module:svgcanvas.ExtensionInitCallback} [init] */ try { /** * @type {module:SVGthis.ExtensionObject} */ const extPath = this.configObj.curConfig.extPath const imported = await import(`${extPath}/${encodeURIComponent(extname)}/${encodeURIComponent(extname)}.js`) const { name = extname, init: initfn } = imported.default return this.addExtension(name, (initfn &amp;&amp; initfn.bind(this)), { langParam: 'en' }) /** @todo change to current lng */ } catch (err) { // Todo: Add config to alert any errors console.error('Extension failed to load: ' + extname + '; ', err) return undefined } }) ) // load user extensions (given as pathNames) await Promise.all( this.configObj.curConfig.userExtensions.map(async ({ pathName, config }) => { /** * @tutorial ExtensionDocs * @typedef {PlainObject} module:SVGthis.ExtensionObject * @property {string} [name] Name of the extension. Used internally; no need for i18n. Defaults to extension name without beginning "ext-" or ending ".js". * @property {module:svgcanvas.ExtensionInitCallback} [init] */ try { /** * @type {module:SVGthis.ExtensionObject} */ const imported = await import(encodeURI(pathName)) const { name, init: initfn } = imported.default return this.addExtension(name, (initfn &amp;&amp; initfn.bind(this, config)), {}) } catch (err) { // Todo: Add config to alert any errors console.error('Extension failed to load: ' + pathName + '; ', err) return undefined } }) ) this.svgCanvas.bind( 'extensions_added', /** * @param {external:Window} _win * @param {module:svgcanvas.SvgCanvas#event:extensions_added} _data * @listens module:SvgCanvas#event:extensions_added * @returns {void} */ (_win, _data) => { this.extensionsAdded = true this.setAll() if (this.storagePromptState === 'ignore') { this.updateCanvas(true) } this.messageQueue.forEach( /** * @param {module:svgcanvas.SvgCanvas#event:message} messageObj * @fires module:svgcanvas.SvgCanvas#event:message * @returns {void} */ (messageObj) => { this.svgCanvas.call('message', messageObj) } ) } ) this.svgCanvas.call('extensions_added') } catch (err) { // Todo: Report errors through the UI console.error(err) } } /** * Listens to the mode change, listener is to be added on document * @param {Event} evt custom modeChange event */ modeListener (evt) { const mode = this.svgCanvas.getMode() this.setCursorStyle(mode) } /** * sets cursor styling for workarea depending on the current mode * @param {string} mode */ setCursorStyle (mode) { let cs = 'auto' switch (mode) { case 'ext-panning': cs = 'grab' break case 'zoom': case 'shapelib': cs = 'crosshair' break case 'circle': case 'ellipse': case 'rect': case 'square': case 'star': case 'polygon': cs = `url("./images/cursors/${mode}_cursor.svg"), crosshair` break case 'text': // #TODO: Cursor should be changed back to default after text element was created cs = 'text' break default: cs = 'auto' } this.workarea.style.cursor = cs } /** * Listens for Esc key to be pressed to cancel active mode, sets mode to Select */ cancelTool () { const mode = this.svgCanvas.getMode() // list of modes that are currently save to cancel const modesToCancel = ['zoom', 'rect', 'square', 'circle', 'ellipse', 'line', 'text', 'star', 'polygon', 'shapelib', 'image'] if (modesToCancel.includes(mode)) { this.leftPanel.clickSelect() } } } export default EditorStartup </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-SVGEditor.html">SVGEditor</a></li><li><a href="module-contextmenu.html">contextmenu</a></li><li><a href="module-jGraduate.html">jGraduate</a></li><li><a href="module-jPicker.html">jPicker</a></li><li><a href="module-locale.html">locale</a></li></ul><h3>Externals</h3><ul><li><a href="external-JamilihArray.html">JamilihArray</a></li><li><a href="external-Math.html">Math</a></li><li><a href="external-Window.html">Window</a></li><li><a href="external-jQuery.html">jQuery</a></li></ul><h3>Namespaces</h3><ul><li><a href="external-jQuery.fn.html">fn</a></li><li><a href="external-jQuery.fn.$.fn.jPicker.defaults.html">defaults</a></li><li><a href="external-jQuery.fn.exports.jPickerMethod.html">exports.jPickerMethod</a></li><li><a href="external-jQuery.fn.jGraduateDefaults.html">jGraduateDefaults</a></li><li><a href="external-jQuery.fn.jGraduateDefaults.images.html">images</a></li><li><a href="external-jQuery.fn.jGraduateDefaults.window.html">window</a></li><li><a href="external-jQuery.jGraduate.html">jGraduate</a></li><li><a href="external-jQuery.jPicker.html">jPicker</a></li><li><a href="external-jQuery.jPicker.ColorMethods.html">ColorMethods</a></li></ul><h3>Classes</h3><ul><li><a href="BottomPanel.html">BottomPanel</a></li><li><a href="Dropdown.html">Dropdown</a></li><li><a href="EditorStartup.html">EditorStartup</a></li><li><a href="ElixMenuButton.html">ElixMenuButton</a></li><li><a href="ElixNumberSpinBox.html">ElixNumberSpinBox</a></li><li><a href="ExplorerButton.html">ExplorerButton</a></li><li><a href="FlyingButton.html">FlyingButton</a></li><li><a href="LayersPanel.html">LayersPanel</a></li><li><a href="LeftPanel.html">LeftPanel</a></li><li><a href="MainMenu.html">MainMenu</a></li><li><a href="NumberSpinBox.html">NumberSpinBox</a></li><li><a href="PaintBox.html">PaintBox</a></li><li><a href="PlainNumberSpinBox.html">PlainNumberSpinBox</a></li><li><a href="Rulers.html">Rulers</a></li><li><a href="SEInput.html">SEInput</a></li><li><a href="SEPalette.html">SEPalette</a></li><li><a href="SESpinInput.html">SESpinInput</a></li><li><a href="SeCMenuDialog.html">SeCMenuDialog</a></li><li><a href="SeCMenuLayerDialog.html">SeCMenuLayerDialog</a></li><li><a href="SeColorPicker.html">SeColorPicker</a></li><li><a href="SeEditPrefsDialog.html">SeEditPrefsDialog</a></li><li><a href="SeExportDialog.html">SeExportDialog</a></li><li><a href="SeImgPropDialog.html">SeImgPropDialog</a></li><li><a href="SeList.html">SeList</a></li><li><a href="SeMenu.html">SeMenu</a></li><li><a href="SeMenuItem.html">SeMenuItem</a></li><li><a href="SePlainAlertDialog.html">SePlainAlertDialog</a></li><li><a href="SePlainBorderButton.html">SePlainBorderButton</a></li><li><a href="SePromptDialog.html">SePromptDialog</a></li><li><a href="SeStorageDialog.html">SeStorageDialog</a></li><li><a href="SeSvgSourceEditorDialog.html">SeSvgSourceEditorDialog</a></li><li><a href="SeText.html">SeText</a></li><li><a href="ToolButton.html">ToolButton</a></li><li><a href="TopPanel.html">TopPanel</a></li><li><a href="configObj.html">configObj</a></li><li><a href="external-jQuery.jGraduate.Paint.html">Paint</a></li><li><a href="external-jQuery.jPicker.Color.html">Color</a></li><li><a href="module.exports.html">exports</a></li><li><a href="module.exports_module.exports.html">exports</a></li><li><a href="module-SVGEditor-Editor.html">Editor</a></li><li><a href="module-jPicker.module.exports.html">module.exports</a></li></ul><h3>Interfaces</h3><ul><li><a href="module-SVGEditor.Config.html">Config</a></li><li><a href="module-SVGEditor.Prefs.html">Prefs</a></li><li><a href="module-SVGthis.CustomHandler.html">CustomHandler</a></li><li><a href="module-locale.LocaleEditorInit.html">LocaleEditorInit</a></li></ul><h3>Events</h3><ul><li><a href="module-SVGEditor.html#event:event:svgEditorReadyEvent">svgEditorReadyEvent</a></li></ul><h3>Tutorials</h3><ul><li><a href="tutorial-CanvasAPI.html">CanvasAPI</a></li><li><a href="tutorial-Editor.html">Editor</a></li><li><a href="tutorial-EditorAPI.html">EditorAPI</a></li><li><a href="tutorial-Events.html">Events</a></li><li><a href="tutorial-FrequentlyAskedQuestions.html">Frequently Asked Questions (FAQ)</a></li></ul><h3>Global</h3><ul><li><a href="global.html#attributeChangedCallback">attributeChangedCallback</a></li><li><a href="global.html#connectedCallback">connectedCallback</a></li><li><a href="global.html#constructor">constructor</a></li><li><a href="global.html#createTemplate">createTemplate</a></li><li><a href="global.html#decrement">decrement</a></li><li><a href="global.html#expireCookie">expireCookie</a></li><li><a href="global.html#formatValueFormatthenumericvalueasastring.Thisisusedafterincrementing/decrementingthevaluetoreformatthevalueasastring.">formatValue Format the numeric value as a string. This is used after incrementing/decrementing the value to reformat the value as a string.</a></li><li><a href="global.html#get">get</a></li><li><a href="global.html#handleClick">handleClick</a></li><li><a href="global.html#handleClose">handleClose</a></li><li><a href="global.html#handleInput">handleInput</a></li><li><a href="global.html#handleKeyDown">handleKeyDown</a></li><li><a href="global.html#handleMouseDown">handleMouseDown</a></li><li><a href="global.html#handleMouseUp">handleMouseUp</a></li><li><a href="global.html#handleOptionsChange">handleOptionsChange</a></li><li><a href="global.html#handleSelect">handleSelect</a></li><li><a href="global.html#handleShow">handleShow</a></li><li><a href="global.html#increment">increment</a></li><li><a href="global.html#init">init</a></li><li><a href="global.html#inputsize">inputsize</a></li><li><a href="global.html#isNullish">isNullish</a></li><li><a href="global.html#loadloadConfig">load load Config</a></li><li><a href="global.html#loadFromURLLoadconfig/datafromURLifgiven">loadFromURL Load config/data from URL if given</a></li><li><a href="global.html#name">name</a></li><li><a href="global.html#observedAttributes">observedAttributes</a></li><li><a href="global.html#parseValue">parseValue</a></li><li><a href="global.html#pref">pref</a></li><li><a href="global.html#readySignal">readySignal</a></li><li><a href="global.html#regexEscape">regexEscape</a></li><li><a href="global.html#removeStoragePrefCookie">removeStoragePrefCookie</a></li><li><a href="global.html#replaceStoragePrompt">replaceStoragePrompt</a></li><li><a href="global.html#set">set</a></li><li><a href="global.html#setupCurConfig">setupCurConfig</a></li><li><a href="global.html#setupCurPrefs">setupCurPrefs</a></li><li><a href="global.html#src">src</a></li><li><a href="global.html#stateEffects">stateEffects</a></li><li><a href="global.html#stepDown">stepDown</a></li><li><a href="global.html#stepUp">stepUp</a></li><li><a href="global.html#triggerInputChanged">triggerInputChanged</a></li><li><a href="global.html#updateLib">updateLib</a></li><li><a href="global.html#value">value</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Mon Jun 09 2025 17:03:26 GMT+0200 (Central European Summer Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>