/
githubmirror
/
drawio
Обзор
Документация
Войти
/
githubmirror
/
drawio
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
src/main/webapp/js/grapheditor/Editor.js
4 362 строки
167 KB
David Benson
31.1.8 release
07 авг 2026, 02:43
07 авг 2026, 02:43
a1f615b
Код
Авторство
О чём код?
/** * Copyright (c) 2006-2012, JGraph Holdings Ltd */ /** * Editor constructor executed on page load. */ Editor = function(chromeless, themes, model, graph, editable) { mxEventSource.call(this); this.chromeless = (chromeless != null) ? chromeless : this.chromeless; this.initStencilRegistry(); this.graph = graph || this.createGraph(themes, model); this.editable = (editable != null) ? editable : !chromeless; this.undoManager = this.createUndoManager(); this.status = ''; this.getOrCreateFilename = function() { return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml'; }; this.getFilename = function() { return this.filename; }; // Sets the status and fires a statusChanged event this.setStatus = function(value, fn) { this.status = value; this.statusFunction = fn; this.fireEvent(new mxEventObject('statusChanged')); }; // Returns the current status this.getStatus = function() { return this.status; }; // Updates modified state if graph changes this.graphChangeListener = function(sender, eventObject) { var edit = (eventObject != null) ? eventObject.getProperty('edit') : null; if (edit == null || !edit.ignoreEdit) { this.setModified(true); } }; this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() { this.graphChangeListener.apply(this, arguments); })); // Sets persistent graph state defaults this.graph.resetViewOnRootChange = false; this.init(); }; /** * Measurements Units */ mxConstants.POINTS = 1; mxConstants.MILLIMETERS = 2; mxConstants.INCHES = 3; mxConstants.METERS = 4; /** * This ratio is with page scale 1 */ mxConstants.PIXELS_PER_MM = 3.937; mxConstants.PIXELS_PER_INCH = 100; /** * Counts open editor tabs (must be global for cross-window access) */ Editor.pageCounter = 0; // Cross-domain window access is not allowed in FF, so if we // were opened from another domain then this will fail. (function() { try { var op = window; while (op.opener != null && typeof op.opener.Editor !== 'undefined' && !isNaN(op.opener.Editor.pageCounter) && // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795 op.opener != op) { op = op.opener; } // Increments the counter in the first opener in the chain if (op != null) { op.Editor.pageCounter++; Editor.pageCounter = op.Editor.pageCounter; } } catch (e) { // ignore } })(); /** * */ Editor.defaultHtmlFont = '-apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", system-ui, ui-sans-serif, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"'; /** * Specifies if local storage should be used (eg. on the iPad which has no filesystem) */ Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS; /** * Window width for simple mode to collapse panels. */ Editor.smallScreenWidth = 1024; /** * */ Editor.helpImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDc4LTI0MHEyMSAwIDM1LjUtMTQuNVQ1MjgtMjkwcTAtMjEtMTQuNS0zNS41VDQ3OC0zNDBxLTIxIDAtMzUuNSAxNC41VDQyOC0yOTBxMCAyMSAxNC41IDM1LjVUNDc4LTI0MFptLTM2LTE1NGg3NHEwLTMzIDcuNS01MnQ0Mi41LTUycTI2LTI2IDQxLTQ5LjV0MTUtNTYuNXEwLTU2LTQxLTg2dC05Ny0zMHEtNTcgMC05Mi41IDMwVDM0Mi02MThsNjYgMjZxNS0xOCAyMi41LTM5dDUzLjUtMjFxMzIgMCA0OCAxNy41dDE2IDM4LjVxMCAyMC0xMiAzNy41VDUwNi01MjZxLTQ0IDM5LTU0IDU5dC0xMCA3M1ptMzggMzE0cS04MyAwLTE1Ni0zMS41VDE5Ny0xOTdxLTU0LTU0LTg1LjUtMTI3VDgwLTQ4MHEwLTgzIDMxLjUtMTU2VDE5Ny03NjNxNTQtNTQgMTI3LTg1LjVUNDgwLTg4MHE4MyAwIDE1NiAzMS41VDc2My03NjNxNTQgNTQgODUuNSAxMjdUODgwLTQ4MHEwIDgzLTMxLjUgMTU2VDc2My0xOTdxLTU0IDU0LTEyNyA4NS41VDQ4MC04MFptMC04MHExMzQgMCAyMjctOTN0OTMtMjI3cTAtMTM0LTkzLTIyN3QtMjI3LTkzcS0xMzQgMC0yMjcgOTN0LTkzIDIyN3EwIDEzNCA5MyAyMjd0MjI3IDkzWm0wLTMyMFoiLz48L3N2Zz4='; Editor.menuImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTI0MHYtODBoNzIwdjgwSDEyMFptMC0yMDB2LTgwaDcyMHY4MEgxMjBabTAtMjAwdi04MGg3MjB2ODBIMTIwWiIvPjwvc3ZnPg=='; Editor.moveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI4cHgiIGhlaWdodD0iMjhweCI+PGc+PC9nPjxnPjxnPjxnPjxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIuNCwyLjQpc2NhbGUoMC44KXJvdGF0ZSg0NSwxMiwxMikiIHN0cm9rZT0iIzI5YjZmMiIgZmlsbD0iIzI5YjZmMiIgZD0iTTE1LDNsMi4zLDIuM2wtMi44OSwyLjg3bDEuNDIsMS40MkwxOC43LDYuN0wyMSw5VjNIMTV6IE0zLDlsMi4zLTIuM2wyLjg3LDIuODlsMS40Mi0xLjQyTDYuNyw1LjNMOSwzSDNWOXogTTksMjEgbC0yLjMtMi4zbDIuODktMi44N2wtMS40Mi0xLjQyTDUuMywxNy4zTDMsMTV2Nkg5eiBNMjEsMTVsLTIuMywyLjNsLTIuODctMi44OWwtMS40MiwxLjQybDIuODksMi44N0wxNSwyMWg2VjE1eiIvPjwvZz48L2c+PC9nPjwvc3ZnPgo='; Editor.zoomInImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzg0LTEyMCA1MzItMzcycS0zMCAyNC02OSAzOHQtODMgMTRxLTEwOSAwLTE4NC41LTc1LjVUMTIwLTU4MHEwLTEwOSA3NS41LTE4NC41VDM4MC04NDBxMTA5IDAgMTg0LjUgNzUuNVQ2NDAtNTgwcTAgNDQtMTQgODN0LTM4IDY5bDI1MiAyNTItNTYgNTZaTTM4MC00MDBxNzUgMCAxMjcuNS01Mi41VDU2MC01ODBxMC03NS01Mi41LTEyNy41VDM4MC03NjBxLTc1IDAtMTI3LjUgNTIuNVQyMDAtNTgwcTAgNzUgNTIuNSAxMjcuNVQzODAtNDAwWm0tNDAtNjB2LTgwaC04MHYtODBoODB2LTgwaDgwdjgwaDgwdjgwaC04MHY4MGgtODBaIi8+PC9zdmc+'; Editor.zoomOutImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzg0LTEyMCA1MzItMzcycS0zMCAyNC02OSAzOHQtODMgMTRxLTEwOSAwLTE4NC41LTc1LjVUMTIwLTU4MHEwLTEwOSA3NS41LTE4NC41VDM4MC04NDBxMTA5IDAgMTg0LjUgNzUuNVQ2NDAtNTgwcTAgNDQtMTQgODN0LTM4IDY5bDI1MiAyNTItNTYgNTZaTTM4MC00MDBxNzUgMCAxMjcuNS01Mi41VDU2MC01ODBxMC03NS01Mi41LTEyNy41VDM4MC03NjBxLTc1IDAtMTI3LjUgNTIuNVQyMDAtNTgwcTAgNzUgNTIuNSAxMjcuNVQzODAtNDAwWk0yODAtNTQwdi04MGgyMDB2ODBIMjgwWiIvPjwvc3ZnPg=='; Editor.fullscreenImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtMjAwaDgwdjEyMGgxMjB2ODBIMTIwWm01MjAgMHYtODBoMTIwdi0xMjBoODB2MjAwSDY0MFpNMTIwLTY0MHYtMjAwaDIwMHY4MEgyMDB2MTIwaC04MFptNjQwIDB2LTEyMEg2NDB2LTgwaDIwMHYyMDBoLTgwWiIvPjwvc3ZnPg=='; Editor.fullscreenExitImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjQwLTEyMHYtMTIwSDEyMHYtODBoMjAwdjIwMGgtODBabTQwMCAwdi0yMDBoMjAwdjgwSDcyMHYxMjBoLTgwWk0xMjAtNjQwdi04MGgxMjB2LTEyMGg4MHYyMDBIMTIwWm01MjAgMHYtMjAwaDgwdjEyMGgxMjB2ODBINjQwWiIvPjwvc3ZnPg=='; Editor.zoomFitImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTMyMHEtNjYgMC0xMTMtNDd0LTQ3LTExM3EwLTY2IDQ3LTExM3QxMTMtNDdxNjYgMCAxMTMgNDd0NDcgMTEzcTAgNjYtNDcgMTEzdC0xMTMgNDdabTAtODBxMzMgMCA1Ni41LTIzLjVUNTYwLTQ4MHEwLTMzLTIzLjUtNTYuNVQ0ODAtNTYwcS0zMyAwLTU2LjUgMjMuNVQ0MDAtNDgwcTAgMzMgMjMuNSA1Ni41VDQ4MC00MDBabTAtODBaTTIwMC0xMjBxLTMzIDAtNTYuNS0yMy41VDEyMC0yMDB2LTE2MGg4MHYxNjBoMTYwdjgwSDIwMFptNDAwIDB2LTgwaDE2MHYtMTYwaDgwdjE2MHEwIDMzLTIzLjUgNTYuNVQ3NjAtMTIwSDYwMFpNMTIwLTYwMHYtMTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoMTYwdjgwSDIwMHYxNjBoLTgwWm02NDAgMHYtMTYwSDYwMHYtODBoMTYwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2MTYwaC04MFoiLz48L3N2Zz4='; Editor.layersImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTExOCAxMjAtMzk4bDY2LTUwIDI5NCAyMjggMjk0LTIyOCA2NiA1MC0zNjAgMjgwWm0wLTIwMkwxMjAtNjAwbDM2MC0yODAgMzYwIDI4MC0zNjAgMjgwWm0wLTI4MFptMCAxNzggMjMwLTE3OC0yMzAtMTc4LTIzMCAxNzggMjMwIDE3OFoiLz48L3N2Zz4='; Editor.editImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTIwMGg1N2wzOTEtMzkxLTU3LTU3LTM5MSAzOTF2NTdabS04MCA4MHYtMTcwbDUyOC01MjdxMTItMTEgMjYuNS0xN3QzMC41LTZxMTYgMCAzMSA2dDI2IDE4bDU1IDU2cTEyIDExIDE3LjUgMjZ0NS41IDMwcTAgMTYtNS41IDMwLjVUODE3LTY0N0wyOTAtMTIwSDEyMFptNjQwLTU4NC01Ni01NiA1NiA1NlptLTE0MSA4NS0yOC0yOSA1NyA1Ny0yOS0yOFoiLz48L3N2Zz4='; Editor.duplicateImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzYwLTI0MHEtMzMgMC01Ni41LTIzLjVUMjgwLTMyMHYtNDgwcTAtMzMgMjMuNS01Ni41VDM2MC04ODBoMzYwcTMzIDAgNTYuNSAyMy41VDgwMC04MDB2NDgwcTAgMzMtMjMuNSA1Ni41VDcyMC0yNDBIMzYwWm0wLTgwaDM2MHYtNDgwSDM2MHY0ODBaTTIwMC04MHEtMzMgMC01Ni41LTIzLjVUMTIwLTE2MHYtNTYwaDgwdjU2MGg0NDB2ODBIMjAwWm0xNjAtMjQwdi00ODAgNDgwWiIvPjwvc3ZnPg=='; Editor.crossImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDYuNDFMMTcuNTkgNSAxMiAxMC41OSA2LjQxIDUgNSA2LjQxIDEwLjU5IDEyIDUgMTcuNTkgNi40MSAxOSAxMiAxMy40MSAxNy41OSAxOSAxOSAxNy41OSAxMy40MSAxMiAxOSA2LjQxeiIvPjwvc3ZnPg=='; Editor.trashImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjgwLTEyMHEtMzMgMC01Ni41LTIzLjVUMjAwLTIwMHYtNTIwaC00MHYtODBoMjAwdi00MGgyNDB2NDBoMjAwdjgwaC00MHY1MjBxMCAzMy0yMy41IDU2LjVUNjgwLTEyMEgyODBabTQwMC02MDBIMjgwdjUyMGg0MDB2LTUyMFpNMzYwLTI4MGg4MHYtMzYwaC04MHYzNjBabTE2MCAwaDgwdi0zNjBoLTgwdjM2MFpNMjgwLTcyMHY1MjAtNTIwWiIvPjwvc3ZnPg=='; Editor.hiddenImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDZjMy43OSAwIDcuMTcgMi4xMyA4LjgyIDUuNS0uNTkgMS4yMi0xLjQyIDIuMjctMi40MSAzLjEybDEuNDEgMS40MWMxLjM5LTEuMjMgMi40OS0yLjc3IDMuMTgtNC41M0MyMS4yNyA3LjExIDE3IDQgMTIgNGMtMS4yNyAwLTIuNDkuMi0zLjY0LjU3bDEuNjUgMS42NUMxMC42NiA2LjA5IDExLjMyIDYgMTIgNnptLTEuMDcgMS4xNEwxMyA5LjIxYy41Ny4yNSAxLjAzLjcxIDEuMjggMS4yOGwyLjA3IDIuMDdjLjA4LS4zNC4xNC0uNy4xNC0xLjA3QzE2LjUgOS4wMSAxNC40OCA3IDEyIDdjLS4zNyAwLS43Mi4wNS0xLjA3LjE0ek0yLjAxIDMuODdsMi42OCAyLjY4QzMuMDYgNy44MyAxLjc3IDkuNTMgMSAxMS41IDIuNzMgMTUuODkgNyAxOSAxMiAxOWMxLjUyIDAgMi45OC0uMjkgNC4zMi0uODJsMy40MiAzLjQyIDEuNDEtMS40MUwzLjQyIDIuNDUgMi4wMSAzLjg3em03LjUgNy41bDIuNjEgMi42MWMtLjA0LjAxLS4wOC4wMi0uMTIuMDItMS4zOCAwLTIuNS0xLjEyLTIuNS0yLjUgMC0uMDUuMDEtLjA4LjAxLS4xM3ptLTMuNC0zLjRsMS43NSAxLjc1Yy0uMjMuNTUtLjM2IDEuMTUtLjM2IDEuNzggMCAyLjQ4IDIuMDIgNC41IDQuNSA0LjUuNjMgMCAxLjIzLS4xMyAxLjc3LS4zNmwuOTguOThjLS44OC4yNC0xLjguMzgtMi43NS4zOC0zLjc5IDAtNy4xNy0yLjEzLTguODItNS41LjctMS40MyAxLjcyLTIuNjEgMi45My0zLjUzeiIvPjwvc3ZnPg=='; Editor.visibleImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDZjMy43OSAwIDcuMTcgMi4xMyA4LjgyIDUuNUMxOS4xNyAxNC44NyAxNS43OSAxNyAxMiAxN3MtNy4xNy0yLjEzLTguODItNS41QzQuODMgOC4xMyA4LjIxIDYgMTIgNm0wLTJDNyA0IDIuNzMgNy4xMSAxIDExLjUgMi43MyAxNS44OSA3IDE5IDEyIDE5czkuMjctMy4xMSAxMS03LjVDMjEuMjcgNy4xMSAxNyA0IDEyIDR6bTAgNWMxLjM4IDAgMi41IDEuMTIgMi41IDIuNVMxMy4zOCAxNCAxMiAxNHMtMi41LTEuMTItMi41LTIuNVMxMC42MiA5IDEyIDltMC0yYy0yLjQ4IDAtNC41IDIuMDItNC41IDQuNVM5LjUyIDE2IDEyIDE2czQuNS0yLjAyIDQuNS00LjVTMTQuNDggNyAxMiA3eiIvPjwvc3ZnPg=='; Editor.lockedImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PGcgZmlsbD0ibm9uZSI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6Ii8+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBvcGFjaXR5PSIuODciLz48L2c+PHBhdGggZD0iTTE4IDhoLTFWNmMwLTIuNzYtMi4yNC01LTUtNVM3IDMuMjQgNyA2djJINmMtMS4xIDAtMiAuOS0yIDJ2MTBjMCAxLjEuOSAyIDIgMmgxMmMxLjEgMCAyLS45IDItMlYxMGMwLTEuMS0uOS0yLTItMnpNOSA2YzAtMS42NiAxLjM0LTMgMy0zczMgMS4zNCAzIDN2Mkg5VjZ6bTkgMTRINlYxMGgxMnYxMHptLTYtM2MxLjEgMCAyLS45IDItMnMtLjktMi0yLTItMiAuOS0yIDIgLjkgMiAyIDJ6Ii8+PC9zdmc+'; Editor.unlockedImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE4IDhoLTFWNmMwLTIuNzYtMi4yNC01LTUtNVM3IDMuMjQgNyA2aDJjMC0xLjY2IDEuMzQtMyAzLTNzMyAxLjM0IDMgM3YySDZjLTEuMSAwLTIgLjktMiAydjEwYzAgMS4xLjkgMiAyIDJoMTJjMS4xIDAgMi0uOSAyLTJWMTBjMC0xLjEtLjktMi0yLTJ6bTAgMTJINlYxMGgxMnYxMHptLTYtM2MxLjEgMCAyLS45IDItMnMtLjktMi0yLTItMiAuOS0yIDIgLjkgMiAyIDJ6Ii8+PC9zdmc+'; Editor.printImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNjQwLTY0MHYtMTIwSDMyMHYxMjBoLTgwdi0yMDBoNDgwdjIwMGgtODBabS00ODAgODBoNjQwLTY0MFptNTYwIDEwMHExNyAwIDI4LjUtMTEuNVQ3NjAtNTAwcTAtMTctMTEuNS0yOC41VDcyMC01NDBxLTE3IDAtMjguNSAxMS41VDY4MC01MDBxMCAxNyAxMS41IDI4LjVUNzIwLTQ2MFptLTgwIDI2MHYtMTYwSDMyMHYxNjBoMzIwWm04MCA4MEgyNDB2LTE2MEg4MHYtMjQwcTAtNTEgMzUtODUuNXQ4NS0zNC41aDU2MHE1MSAwIDg1LjUgMzQuNVQ4ODAtNTIwdjI0MEg3MjB2MTYwWm04MC0yNDB2LTE2MHEwLTE3LTExLjUtMjguNVQ3NjAtNTYwSDIwMHEtMTcgMC0yOC41IDExLjVUMTYwLTUyMHYxNjBoODB2LTgwaDQ4MHY4MGg4MFoiLz48L3N2Zz4='; Editor.refreshImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE3LjY1IDYuMzVDMTYuMiA0LjkgMTQuMjEgNCAxMiA0Yy00LjQyIDAtNy45OSAzLjU4LTcuOTkgOHMzLjU3IDggNy45OSA4YzMuNzMgMCA2Ljg0LTIuNTUgNy43My02aC0yLjA4Yy0uODIgMi4zMy0zLjA0IDQtNS42NSA0LTMuMzEgMC02LTIuNjktNi02czIuNjktNiA2LTZjMS42NiAwIDMuMTQuNjkgNC4yMiAxLjc4TDEzIDExaDdWNGwtMi4zNSAyLjM1eiIvPjwvc3ZnPg=='; Editor.backImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuODciLz48cGF0aCBkPSJNMTcuNTEgMy44N0wxNS43MyAyLjEgNS44NCAxMmw5LjkgOS45IDEuNzctMS43N0w5LjM4IDEybDguMTMtOC4xM3oiLz48L3N2Zz4='; Editor.closeImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuODciLz48cGF0aCBkPSJNMTIgMkM2LjQ3IDIgMiA2LjQ3IDIgMTJzNC40NyAxMCAxMCAxMCAxMC00LjQ3IDEwLTEwUzE3LjUzIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDh6bTMuNTktMTNMMTIgMTAuNTkgOC40MSA3IDcgOC40MSAxMC41OSAxMiA3IDE1LjU5IDguNDEgMTcgMTIgMTMuNDEgMTUuNTkgMTcgMTcgMTUuNTkgMTMuNDEgMTIgMTcgOC40MXoiLz48L3N2Zz4=' Editor.closeBlackImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjZweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjZweCI+PGVsbGlwc2UgY3g9IjEyIiBjeT0iMTIiIHJ4PSI5IiByeT0iOSIgc3Ryb2tlPSJub25lIiBmaWxsPSIjMDAwIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE0LjU5IDhMMTIgMTAuNTkgOS40MSA4IDggOS40MSAxMC41OSAxMiA4IDE0LjU5IDkuNDEgMTYgMTIgMTMuNDEgMTQuNTkgMTYgMTYgMTQuNTkgMTMuNDEgMTIgMTYgOS40MSAxNC41OSA4ek0xMiAyQzYuNDcgMiAyIDYuNDcgMiAxMnM0LjQ3IDEwIDEwIDEwIDEwLTQuNDcgMTAtMTBTMTcuNTMgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHoiLz48L3N2Zz4='; Editor.minusImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0iTTEwIDI1LjV2LTNoMjh2M1oiLz48L3N2Zz4='; Editor.plusImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgMTNoLTZ2NmgtMnYtNkg1di0yaDZWNWgydjZoNnYyeiIvPjwvc3ZnPg=='; Editor.addBoxImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDQwLTI4MGg4MHYtMTYwaDE2MHYtODBINTIwdi0xNjBoLTgwdjE2MEgyODB2ODBoMTYwdjE2MFpNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoNTYwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2NTYwcTAgMzMtMjMuNSA1Ni41VDc2MC0xMjBIMjAwWm0wLTgwaDU2MHYtNTYwSDIwMHY1NjBabTAtNTYwdjU2MC01NjBaIi8+PC9zdmc+'; Editor.shapesImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNjAwLTM2MFpNMzIwLTI0MnExMCAxIDE5LjUgMS41dDIwLjUuNXExMSAwIDIwLjUtLjVUNDAwLTI0MnY4Mmg0MDB2LTQwMGgtODJxMS0xMCAxLjUtMTkuNXQuNS0yMC41cTAtMTEtLjUtMjAuNVQ3MTgtNjQwaDgycTMzIDAgNTYuNSAyMy41VDg4MC01NjB2NDAwcTAgMzMtMjMuNSA1Ni41VDgwMC04MEg0MDBxLTMzIDAtNTYuNS0yMy41VDMyMC0xNjB2LTgyWm00MC03OHEtMTE3IDAtMTk4LjUtODEuNVQ4MC02MDBxMC0xMTcgODEuNS0xOTguNVQzNjAtODgwcTExNyAwIDE5OC41IDgxLjVUNjQwLTYwMHEwIDExNy04MS41IDE5OC41VDM2MC0zMjBabTAtODBxODMgMCAxNDEuNS01OC41VDU2MC02MDBxMC04My01OC41LTE0MS41VDM2MC04MDBxLTgzIDAtMTQxLjUgNTguNVQxNjAtNjAwcTAgODMgNTguNSAxNDEuNVQzNjAtNDAwWm0wLTIwMFoiLz48L3N2Zz4='; Editor.formatImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJtMzUyLTUyMiA4Ni04Ny01Ni01Ny00NCA0NC01Ni01NiA0My00NC00NS00NS04NyA4NyAxNTkgMTU4Wm0zMjggMzI5IDg3LTg3LTQ1LTQ1LTQ0IDQzLTU2LTU2IDQzLTQ0LTU3LTU2LTg2IDg2IDE1OCAxNTlabTI0LTU2NyA1NyA1Ny01Ny01N1pNMjkwLTEyMEgxMjB2LTE3MGwxNzUtMTc1TDgwLTY4MGwyMDAtMjAwIDIxNiAyMTYgMTUxLTE1MnExMi0xMiAyNy0xOHQzMS02cTE2IDAgMzEgNnQyNyAxOGw1MyA1NHExMiAxMiAxOCAyN3Q2IDMxcTAgMTYtNiAzMC41VDgxNi02NDdMNjY1LTQ5NWwyMTUgMjE1TDY4MC04MCA0NjUtMjk1IDI5MC0xMjBabS05MC04MGg1NmwzOTItMzkxLTU3LTU3LTM5MSAzOTJ2NTZabTQyMC00MTktMjktMjkgNTcgNTctMjgtMjhaIi8+PC9zdmc+'; Editor.freehandImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHJlY3QgZmlsbD0ibm9uZSIgaGVpZ2h0PSIyNCIgd2lkdGg9IjI0Ii8+PHBhdGggZD0iTTQuNSw4YzEuMDQsMCwyLjM0LTEuNSw0LjI1LTEuNWMxLjUyLDAsMi43NSwxLjIzLDIuNzUsMi43NWMwLDIuMDQtMS45OSwzLjE1LTMuOTEsNC4yMkM1LjQyLDE0LjY3LDQsMTUuNTcsNCwxNyBjMCwxLjEsMC45LDIsMiwydjJjLTIuMjEsMC00LTEuNzktNC00YzAtMi43MSwyLjU2LTQuMTQsNC42Mi01LjI4YzEuNDItMC43OSwyLjg4LTEuNiwyLjg4LTIuNDdjMC0wLjQxLTAuMzQtMC43NS0wLjc1LTAuNzUgQzcuNSw4LjUsNi4yNSwxMCw0LjUsMTBDMy4xMiwxMCwyLDguODgsMiw3LjVDMiw1LjQ1LDQuMTcsMi44Myw1LDJsMS40MSwxLjQxQzUuNDEsNC40Miw0LDYuNDMsNCw3LjVDNCw3Ljc4LDQuMjIsOCw0LjUsOHogTTgsMjEgbDMuNzUsMGw4LjA2LTguMDZsLTMuNzUtMy43NUw4LDE3LjI1TDgsMjF6IE0xMCwxOC4wOGw2LjA2LTYuMDZsMC45MiwwLjkyTDEwLjkyLDE5TDEwLDE5TDEwLDE4LjA4eiBNMjAuMzcsNi4yOSBjLTAuMzktMC4zOS0xLjAyLTAuMzktMS40MSwwbC0xLjgzLDEuODNsMy43NSwzLjc1bDEuODMtMS44M2MwLjM5LTAuMzksMC4zOS0xLjAyLDAtMS40MUwyMC4zNyw2LjI5eiIvPjwvc3ZnPg=='; Editor.undoImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjgwLTIwMHYtODBoMjg0cTYzIDAgMTA5LjUtNDBUNzIwLTQyMHEwLTYwLTQ2LjUtMTAwVDU2NC01NjBIMzEybDEwNCAxMDQtNTYgNTYtMjAwLTIwMCAyMDAtMjAwIDU2IDU2LTEwNCAxMDRoMjUycTk3IDAgMTY2LjUgNjNUODAwLTQyMHEwIDk0LTY5LjUgMTU3VDU2NC0yMDBIMjgwWiIvPjwvc3ZnPg=='; Editor.redoImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzk2LTIwMHEtOTcgMC0xNjYuNS02M1QxNjAtNDIwcTAtOTQgNjkuNS0xNTdUMzk2LTY0MGgyNTJMNTQ0LTc0NGw1Ni01NiAyMDAgMjAwLTIwMCAyMDAtNTYtNTYgMTA0LTEwNEgzOTZxLTYzIDAtMTA5LjUgNDBUMjQwLTQyMHEwIDYwIDQ2LjUgMTAwVDM5Ni0yODBoMjg0djgwSDM5NloiLz48L3N2Zz4='; Editor.outlineImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTE1IDIxLTYtMi4xLTQuNjUgMS44cS0uNS4yLS45MjUtLjExM1EzIDIwLjI3NSAzIDE5Ljc1di0xNHEwLS4zMjUuMTg4LS41NzUuMTg3LS4yNS41MTItLjM3NUw5IDNsNiAyLjEgNC42NS0xLjhxLjUtLjIuOTI1LjExMi40MjUuMzEzLjQyNS44Mzh2MTRxMCAuMzI1LS4xODguNTc1LS4xODcuMjUtLjUxMi4zNzVabS0xLTIuNDVWNi44NWwtNC0xLjR2MTEuN1ptMiAwIDMtMVY1LjdsLTMgMS4xNVpNNSAxOC4zbDMtMS4xNVY1LjQ1bC0zIDFaTTE2IDYuODV2MTEuN1ptLTgtMS40djExLjdaIi8+PC9zdmc+'; Editor.saveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDEydjdINXYtN0gzdjdjMCAxLjEuOSAyIDIgMmgxNGMxLjEgMCAyLS45IDItMnYtN2gtMnptLTYgLjY3bDIuNTktMi41OEwxNyAxMS41bC01IDUtNS01IDEuNDEtMS40MUwxMSAxMi42N1YzaDJ2OS42N3oiLz48L3N2Zz4='; Editor.compareImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0ibTE1Ljg1IDQwLTIuMS0yLjEgNi4wNS02LjA1SDR2LTNoMTUuOGwtNi4wNS02LjA1IDIuMS0yLjEgOS42NSA5LjY1Wm0xNi4zLTEyLjctOS42NS05LjY1TDMyLjE1IDhsMi4xIDIuMS02LjA1IDYuMDVINDR2M0gyOC4ybDYuMDUgNi4wNVoiLz48L3N2Zz4='; Editor.expandMoreImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTEyIDE1LjM3NS02LTYgMS40LTEuNCA0LjYgNC42IDQuNi00LjYgMS40IDEuNFoiLz48L3N2Zz4='; Editor.expandLessImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTcuNCAxNS4zNzUtMS40LTEuNCA2LTYgNiA2LTEuNCAxLjQtNC42LTQuNloiLz48L3N2Zz4='; Editor.gearImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTkuMjUgMjItLjQtMy4ycS0uMzI1LS4xMjUtLjYxMi0uMy0uMjg4LS4xNzUtLjU2My0uMzc1TDQuNyAxOS4zNzVsLTIuNzUtNC43NSAyLjU3NS0xLjk1UTQuNSAxMi41IDQuNSAxMi4zMzd2LS42NzVxMC0uMTYyLjAyNS0uMzM3TDEuOTUgOS4zNzVsMi43NS00Ljc1IDIuOTc1IDEuMjVxLjI3NS0uMi41NzUtLjM3NS4zLS4xNzUuNi0uM2wuNC0zLjJoNS41bC40IDMuMnEuMzI1LjEyNS42MTMuMy4yODcuMTc1LjU2Mi4zNzVsMi45NzUtMS4yNSAyLjc1IDQuNzUtMi41NzUgMS45NXEuMDI1LjE3NS4wMjUuMzM3di42NzVxMCAuMTYzLS4wNS4zMzhsMi41NzUgMS45NS0yLjc1IDQuNzUtMi45NS0xLjI1cS0uMjc1LjItLjU3NS4zNzUtLjMuMTc1LS42LjNsLS40IDMuMlptMi44LTYuNXExLjQ1IDAgMi40NzUtMS4wMjVRMTUuNTUgMTMuNDUgMTUuNTUgMTJxMC0xLjQ1LTEuMDI1LTIuNDc1UTEzLjUgOC41IDEyLjA1IDguNXEtMS40NzUgMC0yLjQ4OCAxLjAyNVE4LjU1IDEwLjU1IDguNTUgMTJxMCAxLjQ1IDEuMDEyIDIuNDc1UTEwLjU3NSAxNS41IDEyLjA1IDE1LjVabTAtMnEtLjYyNSAwLTEuMDYyLS40MzgtLjQzOC0uNDM3LS40MzgtMS4wNjJ0LjQzOC0xLjA2MnEuNDM3LS40MzggMS4wNjItLjQzOHQxLjA2My40MzhxLjQzNy40MzcuNDM3IDEuMDYydC0uNDM3IDEuMDYycS0uNDM4LjQzOC0xLjA2My40MzhaTTEyIDEyWm0tMSA4aDEuOTc1bC4zNS0yLjY1cS43NzUtLjIgMS40MzgtLjU4OC42NjItLjM4NyAxLjIxMi0uOTM3bDIuNDc1IDEuMDI1Ljk3NS0xLjctMi4xNS0xLjYyNXEuMTI1LS4zNS4xNzUtLjczOC4wNS0uMzg3LjA1LS43ODd0LS4wNS0uNzg4cS0uMDUtLjM4Ny0uMTc1LS43MzdsMi4xNS0xLjYyNS0uOTc1LTEuNy0yLjQ3NSAxLjA1cS0uNTUtLjU3NS0xLjIxMi0uOTYzLS42NjMtLjM4Ny0xLjQzOC0uNTg3TDEzIDRoLTEuOTc1bC0uMzUgMi42NXEtLjc3NS4yLTEuNDM3LjU4Ny0uNjYzLjM4OC0xLjIxMy45MzhMNS41NSA3LjE1bC0uOTc1IDEuNyAyLjE1IDEuNnEtLjEyNS4zNzUtLjE3NS43NS0uMDUuMzc1LS4wNS44IDAgLjQuMDUuNzc1dC4xNzUuNzVsLTIuMTUgMS42MjUuOTc1IDEuNyAyLjQ3NS0xLjA1cS41NS41NzUgMS4yMTMuOTYyLjY2Mi4zODggMS40MzcuNTg4WiIvPjwvc3ZnPg=='; Editor.extensionImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTguOCAyMUg1cS0uODI1IDAtMS40MTMtLjU4N1EzIDE5LjgyNSAzIDE5di0zLjhxMS4yIDAgMi4xLS43NjIuOS0uNzYzLjktMS45MzggMC0xLjE3NS0uOS0xLjkzOFE0LjIgOS44IDMgOS44VjZxMC0uODI1LjU4Ny0xLjQxMlE0LjE3NSA0IDUgNGg0cTAtMS4wNS43MjUtMS43NzVRMTAuNDUgMS41IDExLjUgMS41cTEuMDUgMCAxLjc3NS43MjVRMTQgMi45NSAxNCA0aDRxLjgyNSAwIDEuNDEzLjU4OFEyMCA1LjE3NSAyMCA2djRxMS4wNSAwIDEuNzc1LjcyNS43MjUuNzI1LjcyNSAxLjc3NSAwIDEuMDUtLjcyNSAxLjc3NVEyMS4wNSAxNSAyMCAxNXY0cTAgLjgyNS0uNTg3IDEuNDEzUTE4LjgyNSAyMSAxOCAyMWgtMy44cTAtMS4yNS0uNzg3LTIuMTI1UTEyLjYyNSAxOCAxMS41IDE4dC0xLjkxMi44NzVROC44IDE5Ljc1IDguOCAyMVpNNSAxOWgyLjEyNXEuNi0xLjY1IDEuOTI1LTIuMzI1UTEwLjM3NSAxNiAxMS41IDE2cTEuMTI1IDAgMi40NS42NzUgMS4zMjUuNjc1IDEuOTI1IDIuMzI1SDE4di02aDJxLjIgMCAuMzUtLjE1LjE1LS4xNS4xNS0uMzUgMC0uMi0uMTUtLjM1UTIwLjIgMTIgMjAgMTJoLTJWNmgtNlY0cTAtLjItLjE1LS4zNS0uMTUtLjE1LS4zNS0uMTUtLjIgMC0uMzUuMTVRMTEgMy44IDExIDR2Mkg1djIuMnExLjM1LjUgMi4xNzUgMS42NzVROCAxMS4wNSA4IDEyLjVxMCAxLjQyNS0uODI1IDIuNlQ1IDE2LjhabTcuNzUtNy43NVoiLz48L3N2Zz4='; Editor.colorDropperImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0iTTYgNDJ2LTguNGwxOC44NS0xOC44NS0zLjYtMy42TDIzLjMgOS4xbDQuNiA0LjZMMzUgNi42cS41NS0uNTUgMS4xNzUtLjU1dDEuMTc1LjU1bDQuMDUgNC4wNXEuNTUuNTUuNTUgMS4xNzVUNDEuNCAxM2wtNy4xIDcuMSA0LjYgNC42LTIuMDUgMi4wNS0zLjYtMy42TDE0LjQgNDJabTMtM2g0LjM1TDMxLjEgMjEuMjVsLTQuMzUtNC4zNUw5IDM0LjY1Wm0yMy4xNS0yMSA2LjItNi4yLTIuMTUtMi4xNS02LjIgNi4yWm0wIDBMMzAgMTUuODUgMzIuMTUgMThaIi8+PC9zdmc+'; Editor.magnifyImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik03ODQtMTIwIDUzMi0zNzJxLTMwIDI0LTY5IDM4dC04MyAxNHEtMTA5IDAtMTg0LjUtNzUuNVQxMjAtNTgwcTAtMTA5IDc1LjUtMTg0LjVUMzgwLTg0MHExMDkgMCAxODQuNSA3NS41VDY0MC01ODBxMCA0NC0xNCA4M3QtMzggNjlsMjUyIDI1Mi01NiA1NlpNMzgwLTQwMHE3NSAwIDEyNy41LTUyLjVUNTYwLTU4MHEwLTc1LTUyLjUtMTI3LjVUMzgwLTc2MHEtNzUgMC0xMjcuNSA1Mi41VDIwMC01ODBxMCA3NSA1Mi41IDEyNy41VDM4MC00MDBaIi8+PC9zdmc+'; Editor.sendImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik0xMjAtMTYwdi02NDBsNzYwIDMyMC03NjAgMzIwWm04MC0xMjAgNDc0LTIwMC00NzQtMjAwdjE0MGwyNDAgNjAtMjQwIDYwdjE0MFptMCAwdi00MDAgNDAwWiIvPjwvc3ZnPg=='; Editor.micImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTQwMHEtNTAgMC04NS0zNXQtMzUtODV2LTI0MHEwLTUwIDM1LTg1dDg1LTM1cTUwIDAgODUgMzV0MzUgODV2MjQwcTAgNTAtMzUgODV0LTg1IDM1Wm0tNDAgMzIwdi0xMjNxLTEwNC0xNC0xNzItOTN0LTY4LTE4NGg4MHEwIDgzIDU4LjUgMTQxLjVUNDgwLTI4MHE4MyAwIDE0MS41LTU4LjVUNjgwLTQ4MGg4MHEwIDEwNS02OCAxODR0LTE3MiA5M3YxMjNoLTgwWiIvPjwvc3ZnPg=='; Editor.sparklesImage = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjkyLDI2Mi45MSwyMTYuNCwxNDkuNzdhOSw5LDAsMCwwLTE2LjgsMEwxNTYuMDgsMjYyLjkxYTksOSwwLDAsMS01LjE3LDUuMTdMMzcuNzcsMzExLjZhOSw5LDAsMCwwLDAsMTYuOGwxMTMuMTQsNDMuNTJhOSw5LDAsMCwxLDUuMTcsNS4xN0wxOTkuNiw0OTAuMjNhOSw5LDAsMCwwLDE2LjgsMGw0My41Mi0xMTMuMTRhOSw5LDAsMCwxLDUuMTctNS4xN0wzNzguMjMsMzI4LjRhOSw5LDAsMCwwLDAtMTYuOEwyNjUuMDksMjY4LjA4QTksOSwwLDAsMSwyNTkuOTIsMjYyLjkxWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iNDAiIGRhdGEtZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlPSIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTogIzE4MWExYjsiLz4KPHBvbHlnb24gcG9pbnRzPSIxMDggNjggODggMTYgNjggNjggMTYgODggNjggMTA4IDg4IDE2MCAxMDggMTA4IDE2MCA4OCAxMDggNjgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMwIi8+Cjxwb2x5Z29uIHBvaW50cz0iNDI2LjY3IDExNy4zMyA0MDAgNDggMzczLjMzIDExNy4zMyAzMDQgMTQ0IDM3My4zMyAxNzAuNjcgNDAwIDI0MCA0MjYuNjcgMTcwLjY3IDQ5NiAxNDQgNDI2LjY3IDExNy4zMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMzAiLz4KPC9zdmc+'; Editor.thinSparklesImage = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIgogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0yNTkuOTIsMjYyLjkxLDIxNi40LDE0OS43N2E5LDksMCwwLDAtMTYuOCwwTDE1Ni4wOCwyNjIuOTFhOSw5LDAsMCwxLTUuMTcsNS4xN0wzNy43NywzMTEuNmE5LDksMCwwLDAsMCwxNi44bDExMy4xNCw0My41MmE5LDksMCwwLDEsNS4xNyw1LjE3TDE5OS42LDQ5MC4yM2E5LDksMCwwLDAsMTYuOCwwbDQzLjUyLTExMy4xNGE5LDksMCwwLDEsNS4xNy01LjE3TDM3OC4yMywzMjguNGE5LDksMCwwLDAsMC0xNi44TDI2NS4wOSwyNjguMDhBOSw5LDAsMCwxLDI1OS45MiwyNjIuOTFaIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyNCIgZGF0YS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U9IiIgc3R5bGU9Ii0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOiAjMTgxYTFiOyIvPgogICAgPHBvbHlnb24gcG9pbnRzPSIxMDggNjggODggMTYgNjggNjggMTYgODggNjggMTA4IDg4IDE2MCAxMDggMTA4IDE2MCA4OCAxMDggNjgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjI0Ii8+CiAgICA8cG9seWdvbiBwb2ludHM9IjQyNi42NyAxMTcuMzMgNDAwIDQ4IDM3My4zMyAxMTcuMzMgMzA0IDE0NCAzNzMuMzMgMTcwLjY3IDQwMCAyNDAgNDI2LjY3IDE3MC42NyA0OTYgMTQ0IDQyNi42NyAxMTcuMzMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjI0Ii8+Cjwvc3ZnPg=='; Editor.layoutImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMWYxZjFmIj48cGF0aCBkPSJNNjAwLTE2MHYtODBINDQwdi0yMDBoLTgwdjgwSDgwdi0yNDBoMjgwdjgwaDgwdi0yMDBoMTYwdi04MGgyODB2MjQwSDYwMHYtODBoLTgwdjMyMGg4MHYtODBoMjgwdjI0MEg2MDBabTgwLTgwaDEyMHYtODBINjgwdjgwWk0xNjAtNDQwaDEyMHYtODBIMTYwdjgwWm01MjAtMjAwaDEyMHYtODBINjgwdjgwWm0wIDQwMHYtODAgODBaTTI4MC00NDB2LTgwIDgwWm00MDAtMjAwdi04MCA4MFoiLz48L3N2Zz4='; Editor.arrowRightImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDAwLTI4MHYtNDAwbDIwMCAyMDAtMjAwIDIwMFoiLz48L3N2Zz4='; Editor.arrowDownImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTM2MCAyODAtNTYwaDQwMEw0ODAtMzYwWiIvPjwvc3ZnPg=='; Editor.arrowUpImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJtMjgwLTQwMCAyMDAtMjAwIDIwMCAyMDBIMjgwWiIvPjwvc3ZnPg=='; Editor.refreshImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTE2MHEtMTM0IDAtMjI3LTkzdC05My0yMjdxMC0xMzQgOTMtMjI3dDIyNy05M3E2OSAwIDEzMiAyOC41VDcyMC02OTB2LTExMGg4MHYyODBINTIwdi04MGgxNjhxLTMyLTU2LTg3LjUtODhUNDgwLTcyMHEtMTAwIDAtMTcwIDcwdC03MCAxNzBxMCAxMDAgNzAgMTcwdDE3MCA3MHE3NyAwIDEzOS00NHQ4Ny0xMTZoODRxLTI4IDEwNi0xMTQgMTczdC0xOTYgNjdaIi8+PC9zdmc+'; Editor.checkImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzgyLTI0MCAxNTQtNDY4bDU3LTU3IDE3MSAxNzEgMzY3LTM2NyA1NyA1Ny00MjQgNDI0WiIvPjwvc3ZnPg=='; Editor.dockRightImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoNTYwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2NTYwcTAgMzMtMjMuNSA1Ni41VDc2MC0xMjBIMjAwWm0xMjAtODB2LTU2MEgyMDB2NTYwaDEyMFptODAgMGgzNjB2LTU2MEg0MDB2NTYwWm0tODAgMEgyMDBoMTIwWiIvPjwvc3ZnPg=='; Editor.toFrontImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzYwLTI4MHEtMzMgMC01Ni41LTIzLjVUMjgwLTM2MHYtNDAwcTAtMzMgMjMuNS01Ni41VDM2MC04NDBoNDAwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2NDAwcTAgMzMtMjMuNSA1Ni41VDc2MC0yODBIMzYwWm0wLTgwaDQwMHYtNDAwSDM2MHY0MDBaTTIwMC0yMDB2ODBxLTMzIDAtNTYuNS0yMy41VDEyMC0yMDBoODBabS04MC04MHYtODBoODB2ODBoLTgwWm0wLTE2MHYtODBoODB2ODBoLTgwWm0wLTE2MHYtODBoODB2ODBoLTgwWm0xNjAgNDgwdi04MGg4MHY4MGgtODBabTE2MCAwdi04MGg4MHY4MGgtODBabTE2MCAwdi04MGg4MHY4MGgtODBaIi8+PC9zdmc+'; Editor.toBackImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNDgwaDgwdjQ4MGg0ODB2ODBIMjAwWm0xNjAtMjQwdjgwcS0zMyAwLTU2LjUtMjMuNVQyODAtMzYwaDgwWm0tODAtODB2LTgwaDgwdjgwaC04MFptMC0xNjB2LTgwaDgwdjgwaC04MFptODAtMTYwaC04MHEwLTMzIDIzLjUtNTYuNVQzNjAtODQwdjgwWm04MCA0ODB2LTgwaDgwdjgwaC04MFptMC00ODB2LTgwaDgwdjgwaC04MFptMTYwIDB2LTgwaDgwdjgwaC04MFptMCA0ODB2LTgwaDgwdjgwaC04MFptMTYwLTQ4MHYtODBxMzMgMCA1Ni41IDIzLjVUODQwLTc2MGgtODBabTAgNDAwaDgwcTAgMzMtMjMuNSA1Ni41VDc2MC0yODB2LTgwWm0wLTgwdi04MGg4MHY4MGgtODBabTAtMTYwdi04MGg4MHY4MGgtODBaIi8+PC9zdmc+'; Editor.fillColorImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJtMjQ3LTkwNCA1Ny01NiAzNDMgMzQzcTIzIDIzIDIzIDU3dC0yMyA1N0w0NTctMzEzcS0yMyAyMy01NyAyM3QtNTctMjNMMTUzLTUwM3EtMjMtMjMtMjMtNTd0MjMtNTdsMTkwLTE5MS05Ni05NlptMTUzIDE1M0wyMDktNTYwaDM4Mkw0MDAtNzUxWm0zNjAgNDcxcS0zMyAwLTU2LjUtMjMuNVQ2ODAtMzYwcTAtMjEgMTIuNS00NXQyNy41LTQ1cTktMTIgMTktMjV0MjEtMjVxMTEgMTIgMjEgMjV0MTkgMjVxMTUgMjEgMjcuNSA0NXQxMi41IDQ1cTAgMzMtMjMuNSA1Ni41VDc2MC0yODBaTTgwIDB2LTE2MGg4MDBWMEg4MFoiLz48L3N2Zz4='; Editor.strokeColorImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAgMHYtMTYwaDgwMFYwSDgwWm0xNjAtMzIwaDU2bDMxMi0zMTEtMjktMjktMjgtMjgtMzExIDMxMnY1NlptLTgwIDgwdi0xNzBsNDQ4LTQ0N3ExMS0xMSAyNS41LTE3dDMwLjUtNnExNiAwIDMxIDZ0MjcgMThsNTUgNTZxMTIgMTEgMTcuNSAyNnQ1LjUgMzFxMCAxNS01LjUgMjkuNVQ3NzctNjg3TDMzMC0yNDBIMTYwWm01NjAtNTA0LTU2LTU2IDU2IDU2Wk02MDgtNjMxbC0yOS0yOS0yOC0yOCA1NyA1N1oiLz48L3N2Zz4='; Editor.fontColorImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAgMHYtMTYwaDgwMFYwSDgwWm0xNDAtMjgwIDIxMC01NjBoMTAwbDIxMCA1NjBoLTk2bC01MC0xNDRIMzY4bC01MiAxNDRoLTk2Wm0xNzYtMjI0aDE2OGwtODItMjMyaC00bC04MiAyMzJaIi8+PC9zdmc+'; Editor.backgroundColorImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAgMHYtMTYwaDgwMFYwSDgwWm01MDQtNDgwTDQ4MC01ODQgMzIwLTQyNGwxMDMgMTA0IDE2MS0xNjBabS00Ny0xNjAgMTAzIDEwMyAxNjAtMTU5LTEwNC0xMDQtMTU5IDE2MFptLTg0LTI5IDIxNiAyMTYtMTg5IDE5MHEtMjQgMjQtNTYuNSAyNFQzNjctMjYzbC0yNyAyM0gxNDBsMTI2LTEyNXEtMjQtMjQtMjUtNTcuNXQyMy01Ny41bDE4OS0xODlabTAgMCAxODctMTg3cTI0LTI0IDU2LjUtMjR0NTYuNSAyNGwxMDQgMTAzcTI0IDI0IDI0IDU2LjVUODU3LTY0MEw2NjktNDUzIDQ1My02NjlaIi8+PC9zdmc+'; Editor.shadowImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTYwLTgwcS0zMyAwLTU2LjUtMjMuNVQ4MC0xNjB2LTQ4MHEwLTMzIDIzLjUtNTYuNVQxNjAtNzIwaDgwdi04MHEwLTMzIDIzLjUtNTYuNVQzMjAtODgwaDQ4MHEzMyAwIDU2LjUgMjMuNVQ4ODAtODAwdjQ4MHEwIDMzLTIzLjUgNTYuNVQ4MDAtMjQwaC04MHY4MHEwIDMzLTIzLjUgNTYuNVQ2NDAtODBIMTYwWm0xNjAtMjQwaDQ4MHYtNDgwSDMyMHY0ODBaIi8+PC9zdmc+'; Editor.boldImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjcyLTIwMHYtNTYwaDIyMXE2NSAwIDEyMCA0MHQ1NSAxMTFxMCA1MS0yMyA3OC41VDYwMi00OTFxMjUgMTEgNTUuNSA0MXQzMC41IDkwcTAgODktNjUgMTI0LjVUNTAxLTIwMEgyNzJabTEyMS0xMTJoMTA0cTQ4IDAgNTguNS0yNC41VDU2Ni0zNzJxMC0xMS0xMC41LTM1LjVUNDk0LTQzMkgzOTN2MTIwWm0wLTIyOGg5M3EzMyAwIDQ4LTE3dDE1LTM4cTAtMjQtMTctMzl0LTQ0LTE1aC05NXYxMDlaIi8+PC9zdmc+'; Editor.italicImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTIwMHYtMTAwaDE2MGwxMjAtMzYwSDMyMHYtMTAwaDQwMHYxMDBINTgwTDQ2MC0zMDBoMTQwdjEwMEgyMDBaIi8+PC9zdmc+'; Editor.underlineImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHYtODBoNTYwdjgwSDIwMFptMjgwLTE2MHEtMTAxIDAtMTU3LTYzdC01Ni0xNjd2LTMzMGgxMDN2MzM2cTAgNTYgMjggOTF0ODIgMzVxNTQgMCA4Mi0zNXQyOC05MXYtMzM2aDEwM3YzMzBxMCAxMDQtNTYgMTY3dC0xNTcgNjNaIi8+PC9zdmc+'; Editor.strikethroughImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDg2LTE2MHEtNzYgMC0xMzUtNDV0LTg1LTEyM2w4OC0zOHExNCA0OCA0OC41IDc5dDg1LjUgMzFxNDIgMCA3Ni0yMHQzNC02NHEwLTE4LTctMzN0LTE5LTI3aDExMnE1IDE0IDcuNSAyOC41VDY5NC0zNDBxMCA4Ni02MS41IDEzM1Q0ODYtMTYwWk04MC00ODB2LTgwaDgwMHY4MEg4MFptNDAyLTMyNnE2NiAwIDExNS41IDMyLjVUNjc0LTY3NGwtODggMzlxLTktMjktMzMuNS01MlQ0ODQtNzEwcS00MSAwLTY4IDE4LjVUMzg2LTY0MGgtOTZxMi02OSA1NC41LTExNy41VDQ4Mi04MDZaIi8+PC9zdmc+'; Editor.verticalTextImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzYwLTE2MGgtODB2LTUyOGwtNDIgNDItNTYtNTYgMTM4LTEzOCAxNDAgMTM4LTU4IDU2LTQyLTQydjUyOFpNNTYwLTI3NiAxMjAtNDQwdi04MGw0NDAtMTY0djc2bC0xMTIgMzh2MTc4bDExMiA0MHY3NlpNMzg0LTQxNHYtMTMybC0xODIgNjR2NGwxODIgNjRaIi8+PC9zdmc+'; Editor.alignLeftImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFptMC0xNjB2LTgwaDQ4MHY4MEgxMjBabTAtMTYwdi04MGg3MjB2ODBIMTIwWm0wLTE2MHYtODBoNDgwdjgwSDEyMFptMC0xNjB2LTgwaDcyMHY4MEgxMjBaIi8+PC9zdmc+'; Editor.alignCenterImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFptMTYwLTE2MHYtODBoNDAwdjgwSDI4MFpNMTIwLTQ0MHYtODBoNzIwdjgwSDEyMFptMTYwLTE2MHYtODBoNDAwdjgwSDI4MFpNMTIwLTc2MHYtODBoNzIwdjgwSDEyMFoiLz48L3N2Zz4='; Editor.alignRightImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTc2MHYtODBoNzIwdjgwSDEyMFptMjQwIDE2MHYtODBoNDgwdjgwSDM2MFpNMTIwLTQ0MHYtODBoNzIwdjgwSDEyMFptMjQwIDE2MHYtODBoNDgwdjgwSDM2MFpNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFoiLz48L3N2Zz4='; Editor.alignJustifyImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFptMC0xNjB2LTgwaDcyMHY4MEgxMjBabTAtMTYwdi04MGg3MjB2ODBIMTIwWm0wLTE2MHYtODBoNzIwdjgwSDEyMFptMC0xNjB2LTgwaDcyMHY4MEgxMjBaIi8+PC9zdmc+'; Editor.alignTopImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTYwLTc2MHYtODBoNjQwdjgwSDE2MFptMjgwIDY0MHYtNDA4TDMzNi00MjRsLTU2LTU2IDIwMC0yMDAgMjAwIDIwMC01NiA1Ni0xMDQtMTA0djQwOGgtODBaIi8+PC9zdmc+'; Editor.alignMiddleImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDQwLTgwdi0xNjhsLTY0IDY0LTU2LTU2IDE2MC0xNjAgMTYwIDE2MC01NiA1Ni02NC02NHYxNjhoLTgwWk0xNjAtNDQwdi04MGg2NDB2ODBIMTYwWm0zMjAtMTIwTDMyMC03MjBsNTYtNTYgNjQgNjR2LTE2OGg4MHYxNjhsNjQtNjQgNTYgNTYtMTYwIDE2MFoiLz48L3N2Zz4='; Editor.alignBottomImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTYwLTEyMHYtODBoNjQwdjgwSDE2MFptMzIwLTE2MEwyODAtNDgwbDU2LTU2IDEwNCAxMDR2LTQwOGg4MHY0MDhsMTA0LTEwNCA1NiA1Ni0yMDAgMjAwWiIvPjwvc3ZnPg=='; Editor.alignHorizontalLeftImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAtODB2LTgwMGg4MHY4MDBIODBabTE2MC0yMDB2LTEyMGg0MDB2MTIwSDI0MFptMC0yODB2LTEyMGg2NDB2MTIwSDI0MFoiLz48L3N2Zz4='; Editor.alignHorizontalCenterImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDQwLTgwdi0yMDBIMjQwdi0xMjBoMjAwdi0xNjBIMTIwdi0xMjBoMzIwdi0yMDBoODB2MjAwaDMyMHYxMjBINTIwdjE2MGgyMDB2MTIwSDUyMHYyMDBoLTgwWiIvPjwvc3ZnPg=='; Editor.alignHorizontalRightImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAwLTgwdi04MDBoODB2ODAwaC04MFpNMzIwLTI4MHYtMTIwaDQwMHYxMjBIMzIwWk04MC01NjB2LTEyMGg2NDB2MTIwSDgwWiIvPjwvc3ZnPg=='; Editor.alignVerticalTopImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjgwLTgwdi02NDBoMTIwdjY0MEgyODBabTI4MC0yNDB2LTQwMGgxMjB2NDAwSDU2MFpNODAtODAwdi04MGg4MDB2ODBIODBaIi8+PC9zdmc+'; Editor.alignVerticalMiddleImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjgwLTEyMHYtMzIwSDgwdi04MGgyMDB2LTMyMGgxMjB2MzIwaDE2MHYtMjAwaDEyMHYyMDBoMjAwdjgwSDY4MHYyMDBINTYwdi0yMDBINDAwdjMyMEgyODBaIi8+PC9zdmc+'; Editor.alignVerticalBottomImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAtODB2LTgwaDgwMHY4MEg4MFptMjAwLTE2MHYtNjQwaDEyMHY2NDBIMjgwWm0yODAgMHYtNDAwaDEyMHY0MDBINTYwWiIvPjwvc3ZnPg=='; Editor.subscriptImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzYwLTE2MHYtODBxMC0xNyAxMS41LTI4LjVUODAwLTI4MGg4MHYtNDBINzYwdi00MGgxMjBxMTcgMCAyOC41IDExLjVUOTIwLTMyMHY0MHEwIDE3LTExLjUgMjguNVQ4ODAtMjQwaC04MHY0MGgxMjB2NDBINzYwWm0tNTI1LTgwIDE4NS0yOTEtMTcyLTI2OWgxMDZsMTI0IDIwMGg0bDEyMy0yMDBoMTA3TDUzOS01MzFsMTg2IDI5MUg2MThMNDgyLTQ1N2gtNEwzNDItMjQwSDIzNVoiLz48L3N2Zz4='; Editor.superscriptImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzYwLTYwMHYtODBxMC0xNyAxMS41LTI4LjVUODAwLTcyMGg4MHYtNDBINzYwdi00MGgxMjBxMTcgMCAyOC41IDExLjVUOTIwLTc2MHY0MHEwIDE3LTExLjUgMjguNVQ4ODAtNjgwaC04MHY0MGgxMjB2NDBINzYwWk0yMzUtMTYwbDE4NS0yOTEtMTcyLTI2OWgxMDZsMTI0IDIwMGg0bDEyMy0yMDBoMTA3TDUzOS00NTFsMTg2IDI5MUg2MThMNDgyLTM3N2gtNEwzNDItMTYwSDIzNVoiLz48L3N2Zz4='; Editor.orderedListImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTgwdi02MGgxMDB2LTMwaC02MHYtNjBoNjB2LTMwSDEyMHYtNjBoMTIwcTE3IDAgMjguNSAxMS41VDI4MC0yODB2NDBxMCAxNy0xMS41IDI4LjVUMjQwLTIwMHExNyAwIDI4LjUgMTEuNVQyODAtMTYwdjQwcTAgMTctMTEuNSAyOC41VDI0MC04MEgxMjBabTAtMjgwdi0xMTBxMC0xNyAxMS41LTI4LjVUMTYwLTUxMGg2MHYtMzBIMTIwdi02MGgxMjBxMTcgMCAyOC41IDExLjVUMjgwLTU2MHY3MHEwIDE3LTExLjUgMjguNVQyNDAtNDUwaC02MHYzMGgxMDB2NjBIMTIwWm02MC0yODB2LTE4MGgtNjB2LTYwaDEyMHYyNDBoLTYwWm0xODAgNDQwdi04MGg0ODB2ODBIMzYwWm0wLTI0MHYtODBoNDgwdjgwSDM2MFptMC0yNDB2LTgwaDQ4MHY4MEgzNjBaIi8+PC9zdmc+'; Editor.unorderedListImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzYwLTIwMHYtODBoNDgwdjgwSDM2MFptMC0yNDB2LTgwaDQ4MHY4MEgzNjBabTAtMjQwdi04MGg0ODB2ODBIMzYwWk0yMDAtMTYwcS0zMyAwLTU2LjUtMjMuNVQxMjAtMjQwcTAtMzMgMjMuNS01Ni41VDIwMC0zMjBxMzMgMCA1Ni41IDIzLjVUMjgwLTI0MHEwIDMzLTIzLjUgNTYuNVQyMDAtMTYwWm0wLTI0MHEtMzMgMC01Ni41LTIzLjVUMTIwLTQ4MHEwLTMzIDIzLjUtNTYuNVQyMDAtNTYwcTMzIDAgNTYuNSAyMy41VDI4MC00ODBxMCAzMy0yMy41IDU2LjVUMjAwLTQwMFptMC0yNDBxLTMzIDAtNTYuNS0yMy41VDEyMC03MjBxMC0zMyAyMy41LTU2LjVUMjAwLTgwMHEzMyAwIDU2LjUgMjMuNVQyODAtNzIwcTAgMzMtMjMuNSA1Ni41VDIwMC02NDBaIi8+PC9zdmc+'; Editor.indentImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFptMzIwLTE2MHYtODBoNDAwdjgwSDQ0MFptMC0xNjB2LTgwaDQwMHY4MEg0NDBabTAtMTYwdi04MGg0MDB2ODBINDQwWk0xMjAtNzYwdi04MGg3MjB2ODBIMTIwWm0wIDQ0MHYtMzIwbDE2MCAxNjAtMTYwIDE2MFoiLz48L3N2Zz4='; Editor.outdentImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTIwLTEyMHYtODBoNzIwdjgwSDEyMFptMzIwLTE2MHYtODBoNDAwdjgwSDQ0MFptMC0xNjB2LTgwaDQwMHY4MEg0NDBabTAtMTYwdi04MGg0MDB2ODBINDQwWk0xMjAtNzYwdi04MGg3MjB2ODBIMTIwWm0xNjAgNDQwTDEyMC00ODBsMTYwLTE2MHYzMjBaIi8+PC9zdmc+'; Editor.removeFormatImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJtNTI4LTU0Ni05My05My0xMjEtMTIxaDQ4NnYxMjBINTY4bC00MCA5NFpNNzkyLTU2IDQ2MC0zODhsLTgwIDE4OEgyNDlsMTE5LTI4MEw1Ni03OTJsNTYtNTYgNzM2IDczNi01NiA1NloiLz48L3N2Zz4='; Editor.textFormatImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTIwMHYtODBoNTYwdjgwSDIwMFptNzYtMTYwIDE2NC00NDBoODBsMTY0IDQ0MGgtNzZsLTM4LTExMkgzOTJsLTQwIDExMmgtNzZabTEzOC0xNzZoMTMybC02NC0xODJoLTRsLTY0IDE4MloiLz48L3N2Zz4='; Editor.codeImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzIwLTI0MCA4MC00ODBsMjQwLTI0MCA1NyA1Ny0xODQgMTg0IDE4MyAxODMtNTYgNTZabTMyMCAwLTU3LTU3IDE4NC0xODQtMTgzLTE4MyA1Ni01NiAyNDAgMjQwLTI0MCAyNDBaIi8+PC9zdmc+'; Editor.linkImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDQwLTI4MEgyODBxLTgzIDAtMTQxLjUtNTguNVQ4MC00ODBxMC04MyA1OC41LTE0MS41VDI4MC02ODBoMTYwdjgwSDI4MHEtNTAgMC04NSAzNXQtMzUgODVxMCA1MCAzNSA4NXQ4NSAzNWgxNjB2ODBaTTMyMC00NDB2LTgwaDMyMHY4MEgzMjBabTIwMCAxNjB2LTgwaDE2MHE1MCAwIDg1LTM1dDM1LTg1cTAtNTAtMzUtODV0LTg1LTM1SDUyMHYtODBoMTYwcTgzIDAgMTQxLjUgNTguNVQ4ODAtNDgwcTAgODMtNTguNSAxNDEuNVQ2ODAtMjgwSDUyMFoiLz48L3N2Zz4='; Editor.lightDarkLinkImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBzdHlsZT0iY29sb3Itc2NoZW1lOmxpZ2h0IGRhcms7IiBmaWxsPSJsaWdodC1kYXJrKCMwMDAwMDAsI2ZmZmZmZikiPjxwYXRoIGQ9Ik00NDAtMjgwSDI4MHEtODMgMC0xNDEuNS01OC41VDgwLTQ4MHEwLTgzIDU4LjUtMTQxLjVUMjgwLTY4MGgxNjB2ODBIMjgwcS01MCAwLTg1IDM1dC0zNSA4NXEwIDUwIDM1IDg1dDg1IDM1aDE2MHY4MFpNMzIwLTQ0MHYtODBoMzIwdjgwSDMyMFptMjAwIDE2MHYtODBoMTYwcTUwIDAgODUtMzV0MzUtODVxMC01MC0zNS04NXQtODUtMzVINTIwdi04MGgxNjBxODMgMCAxNDEuNSA1OC41VDg4MC00ODBxMCA4My01OC41IDE0MS41VDY4MC0yODBINTIwWiIvPjwvc3ZnPg=='; Editor.tooltipImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMWYxZjFmIj48cGF0aCBkPSJNNDQwLTI4MGg4MHYtMjQwaC04MHYyNDBabTY4LjUtMzMxLjVRNTIwLTYyMyA1MjAtNjQwdC0xMS41LTI4LjVRNDk3LTY4MCA0ODAtNjgwdC0yOC41IDExLjVRNDQwLTY1NyA0NDAtNjQwdDExLjUgMjguNVE0NjMtNjAwIDQ4MC02MDB0MjguNS0xMS41Wk00ODAtODBxLTgzIDAtMTU2LTMxLjVUMTk3LTE5N3EtNTQtNTQtODUuNS0xMjdUODAtNDgwcTAtODMgMzEuNS0xNTZUMTk3LTc2M3E1NC01NCAxMjctODUuNVQ0ODAtODgwcTgzIDAgMTU2IDMxLjVUNzYzLTc2M3E1NCA1NCA4NS41IDEyN1Q4ODAtNDgwcTAgODMtMzEuNSAxNTZUNzYzLTE5N3EtNTQgNTQtMTI3IDg1LjVUNDgwLTgwWm0wLTgwcTEzNCAwIDIyNy05M3Q5My0yMjdxMC0xMzQtOTMtMjI3dC0yMjctOTNxLTEzNCAwLTIyNyA5M3QtOTMgMjI3cTAgMTM0IDkzIDIyN3QyMjcgOTNabTAtMzIwWiIvPjwvc3ZnPg=='; Editor.lightDarkTooltipImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBzdHlsZT0iY29sb3Itc2NoZW1lOmxpZ2h0IGRhcms7IiBmaWxsPSJsaWdodC1kYXJrKCMwMDAwMDAsI2ZmZmZmZikiPjxwYXRoIGQ9Ik00NDAtMjgwaDgwdi0yNDBoLTgwdjI0MFptNjguNS0zMzEuNVE1MjAtNjIzIDUyMC02NDB0LTExLjUtMjguNVE0OTctNjgwIDQ4MC02ODB0LTI4LjUgMTEuNVE0NDAtNjU3IDQ0MC02NDB0MTEuNSAyOC41UTQ2My02MDAgNDgwLTYwMHQyOC41LTExLjVaTTQ4MC04MHEtODMgMC0xNTYtMzEuNVQxOTctMTk3cS01NC01NC04NS41LTEyN1Q4MC00ODBxMC04MyAzMS41LTE1NlQxOTctNzYzcTU0LTU0IDEyNy04NS41VDQ4MC04ODBxODMgMCAxNTYgMzEuNVQ3NjMtNzYzcTU0IDU0IDg1LjUgMTI3VDg4MC00ODBxMCA4My0zMS41IDE1NlQ3NjMtMTk3cS01NCA1NC0xMjcgODUuNVQ0ODAtODBabTAtODBxMTM0IDAgMjI3LTkzdDkzLTIyN3EwLTEzNC05My0yMjd0LTIyNy05M3EtMTM0IDAtMjI3IDkzdC05MyAyMjdxMCAxMzQgOTMgMjI3dDIyNyA5M1ptMC0zMjBaIi8+PC9zdmc+'; Editor.lightDarkNoteImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBzdHlsZT0iY29sb3Itc2NoZW1lOmxpZ2h0IGRhcms7IiBmaWxsPSJsaWdodC1kYXJrKCMwMDAwMDAsI2ZmZmZmZikiPjxwYXRoIGQ9Ik0yNDAtMTIwcS0zMyAwLTU2LjUtMjMuNVQxNjAtMjAwdi01NjBxMC0zMyAyMy41LTU2LjVUMjQwLTg0MGg0ODBxMzMgMCA1Ni41IDIzLjVUODAwLTc2MHYzNjBMNTIwLTEyMEgyNDBabTAtODBoMjA3di0yMzNoMjUzdi0zMjdIMjQwdjU2MFptODAtMzYwdi04MGgzMjB2ODBIMzIwWm0wIDE2MHYtODBoMTIwdjgwSDMyMFoiLz48L3N2Zz4='; Editor.imageImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoNTYwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2NTYwcTAgMzMtMjMuNSA1Ni41VDc2MC0xMjBIMjAwWm0wLTgwaDU2MHYtNTYwSDIwMHY1NjBabTQwLTgwaDQ4MEw1NzAtNDgwIDQ1MC0zMjBsLTkwLTEyMC0xMjAgMTYwWm0tNDAgODB2LTU2MCA1NjBaIi8+PC9zdmc+'; Editor.horizontalRuleImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTYwLTQ0MHYtODBoNjQwdjgwSDE2MFoiLz48L3N2Zz4='; Editor.tableImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoNTYwcTMzIDAgNTYuNSAyMy41VDg0MC03NjB2NTYwcTAgMzMtMjMuNSA1Ni41VDc2MC0xMjBIMjAwWm0yNDAtMjQwSDIwMHYxNjBoMjQwdi0xNjBabTgwIDB2MTYwaDI0MHYtMTYwSDUyMFptLTgwLTgwdi0xNjBIMjAwdjE2MGgyNDBabTgwIDBoMjQwdi0xNjBINTIwdjE2MFpNMjAwLTY4MGg1NjB2LTgwSDIwMHY4MFoiLz48L3N2Zz4='; Editor.spacingImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjQwLTgwIDgwLTI0MGwxNjAtMTYwIDU3IDU2LTY0IDY0aDQ5NGwtNjMtNjQgNTYtNTYgMTYwIDE2MEw3MjAtODBsLTU3LTU2IDY0LTY0SDIzM2w2MyA2NC01NiA1NlptMzYtMzYwIDE2NC00NDBoODBsMTY0IDQ0MGgtNzZsLTM4LTExMkgzOTJsLTQwIDExMmgtNzZabTEzOC0xNzZoMTMybC02NC0xODJoLTRsLTY0IDE4MloiLz48L3N2Zz4='; Editor.addColumnLeftImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNODAwLTIwMHYtNTYwSDU2MHY1NjBoMjQwWm0tNjQwIDgwdi0xNjBoODB2ODBoMjQwdi01NjBIMjQwdjgwaC04MHYtMTYwaDcyMHY3MjBIMTYwWm0zMjAtMzYwWm04MCAwaC04MCA4MFptMCAwWk0xNjAtMzYwdi04MEg4MHYtODBoODB2LTgwaDgwdjgwaDgwdjgwaC04MHY4MGgtODBaIi8+PC9zdmc+'; Editor.addColumnRightImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMTYwLTc2MHY1NjBoMjQwdi01NjBIMTYwWk04MC0xMjB2LTcyMGg3MjB2MTYwaC04MHYtODBINDgwdjU2MGgyNDB2LTgwaDgwdjE2MEg4MFptNDAwLTM2MFptLTgwIDBoODAtODBabTAgMFptMzIwIDEyMHYtODBoLTgwdi04MGg4MHYtODBoODB2ODBoODB2ODBoLTgwdjgwaC04MFoiLz48L3N2Zz4='; Editor.removeColumnImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCkgc2NhbGUoMSwtMSkiPjxwYXRoIGQ9Ik01NjAtMjgwSDEyMHYtNDAwaDcyMHYxMjBoLTgwdi00MEgyMDB2MjQwaDM2MHY4MFptLTM2MC04MHYtMjQwIDI0MFptNDQwIDEwNCA4NC04NC04NC04NCA1Ni01NiA4NCA4NCA4NC04NCA1NiA1Ni04MyA4NCA4MyA4NC01NiA1Ni04NC04My04NCA4My01Ni01NloiLz48L3N2Zz4='; Editor.addRowAboveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTE2MGg1NjB2LTI0MEgyMDB2MjQwWm02NDAgODBIMTIwdi03MjBoMTYwdjgwaC04MHYyNDBoNTYwdi0yNDBoLTgwdi04MGgxNjB2NzIwWk00ODAtNDgwWm0wIDgwdi04MCA4MFptMCAwWm0tNDAtMjQwdi04MGgtODB2LTgwaDgwdi04MGg4MHY4MGg4MHY4MGgtODB2ODBoLTgwWiIvPjwvc3ZnPg=='; Editor.addRowBelowImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTU2MGg1NjB2LTI0MEgyMDB2MjQwWm0tODAgNDAwdi03MjBoNzIwdjcyMEg2ODB2LTgwaDgwdi0yNDBIMjAwdjI0MGg4MHY4MEgxMjBabTM2MC0zMjBabTAtODB2ODAtODBabTAgMFpNNDQwLTgwdi04MGgtODB2LTgwaDgwdi04MGg4MHY4MGg4MHY4MGgtODB2ODBoLTgwWiIvPjwvc3ZnPg=='; Editor.removeRowImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNTYwLTI4MEgxMjB2LTQwMGg3MjB2MTIwaC04MHYtNDBIMjAwdjI0MGgzNjB2ODBabS0zNjAtODB2LTI0MCAyNDBabTQ0MCAxMDQgODQtODQtODQtODQgNTYtNTYgODQgODQgODQtODQgNTYgNTYtODMgODQgODMgODQtNTYgNTYtODQtODMtODQgODMtNTYtNTZaIi8+PC9zdmc+'; Editor.autosizeImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDAtNDB2LTI0MGg4MHYtNDAwSDQwdi0yNDBoMjQwdjgwaDQwMHYtODBoMjQwdjI0MGgtODB2NDAwaDgwdjI0MEg2ODB2LTgwSDI4MHY4MEg0MFptMjQwLTE2MGg0MDB2LTgwaDgwdi00MDBoLTgwdi04MEgyODB2ODBoLTgwdjQwMGg4MHY4MFptMzItMTIwIDEzNi0zNjBoNjRsMTM2IDM2MGgtNjJsLTMyLTkySDQwOGwtMzIgOTJoLTY0Wm0xMTQtMTQ0aDEwOGwtNTItMTUwaC00bC01MiAxNTBaTTEyMC03NjBoODB2LTgwaC04MHY4MFptNjQwIDBoODB2LTgwaC04MHY4MFptMCA2NDBoODB2LTgwaC04MHY4MFptLTY0MCAwaDgwdi04MGgtODB2ODBabTgwLTY0MFptNTYwIDBabTAgNTYwWm0tNTYwIDBaIi8+PC9zdmc+'; Editor.checkmarkImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjJweCIgaGVpZ2h0PSIxOHB4IiB2ZXJzaW9uPSIxLjEiPjxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQgMCkiIGQ9Ik03LjE4MSwxNS4wMDdhMSwxLDAsMCwxLS43OTMtMC4zOTFMMy4yMjIsMTAuNUExLDEsMCwxLDEsNC44MDgsOS4yNzRMNy4xMzIsMTIuM2w2LjA0NC04Ljg2QTEsMSwwLDEsMSwxNC44Myw0LjU2OWwtNi44MjMsMTBhMSwxLDAsMCwxLS44LjQzN0g3LjE4MVoiIGZpbGw9IiMwMDAwMDAiLz48L3N2Zz4='; Editor.swapImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMzIwLTQ0MHYtMjg3TDIxNy02MjRsLTU3LTU2IDIwMC0yMDAgMjAwIDIwMC01NyA1Ni0xMDMtMTAzdjI4N2gtODBaTTYwMC04MCA0MDAtMjgwbDU3LTU2IDEwMyAxMDN2LTI4N2g4MHYyODdsMTAzLTEwMyA1NyA1Nkw2MDAtODBaIi8+PC9zdmc+'; Editor.copyImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNzYwLTIwMEgzMjBxLTMzIDAtNTYuNS0yMy41VDI0MC0yODB2LTU2MHEwLTMzIDIzLjUtNTYuNVQzMjAtOTIwaDI4MGwyNDAgMjQwdjQwMHEwIDMzLTIzLjUgNTYuNVQ3NjAtMjAwWk01NjAtNjQwdi0yMDBIMzIwdjU2MGg0NDB2LTM2MEg1NjBaTTE2MC00MHEtMzMgMC01Ni41LTIzLjVUODAtMTIwdi01NjBoODB2NTYwaDQ0MHY4MEgxNjBabTE2MC04MDB2MjAwLTIwMCA1NjAtNTYwWiIvPjwvc3ZnPg=='; Editor.pasteImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNMjAwLTEyMHEtMzMgMC01Ni41LTIzLjVUMTIwLTIwMHYtNTYwcTAtMzMgMjMuNS01Ni41VDIwMC04NDBoMTY4cTEzLTM2IDQzLjUtNTh0NjguNS0yMnEzOCAwIDY4LjUgMjJ0NDMuNSA1OGgxNjhxMzMgMCA1Ni41IDIzLjVUODQwLTc2MHY1NjBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBabTAtODBoNTYwdi01NjBoLTgwdjEyMEgyODB2LTEyMGgtODB2NTYwWm0yODAtNTYwcTE3IDAgMjguNS0xMS41VDUyMC04MDBxMC0xNy0xMS41LTI4LjVUNDgwLTg0MHEtMTcgMC0yOC41IDExLjVUNDQwLTgwMHEwIDE3IDExLjUgMjguNVQ0ODAtNzYwWiIvPjwvc3ZnPg=='; Editor.doubleArrowUpImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJtMjk2LTIyNC01Ni01NiAyNDAtMjQwIDI0MCAyNDAtNTYgNTYtMTg0LTE4My0xODQgMTgzWm0wLTI0MC01Ni01NiAyNDAtMjQwIDI0MCAyNDAtNTYgNTYtMTg0LTE4My0xODQgMTgzWiIvPjwvc3ZnPg=='; Editor.doubleArrowDownImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMDAwMDAwIj48cGF0aCBkPSJNNDgwLTIwMCAyNDAtNDQwbDU2LTU2IDE4NCAxODMgMTg0LTE4MyA1NiA1Ni0yNDAgMjQwWm0wLTI0MEwyNDAtNjgwbDU2LTU2IDE4NCAxODMgMTg0LTE4MyA1NiA1Ni0yNDAgMjQwWiIvPjwvc3ZnPg=='; Editor.opacityImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMWYxZjFmIj48cGF0aCBkPSJNNDgwLTEyMHEtMTMzIDAtMjI2LjUtOTJUMTYwLTQzNnEwLTY1IDI1LTEyMS41VDI1NC02NThsMjI2LTIyMiAyMjYgMjIycTQ0IDQ0IDY5IDEwMC41VDgwMC00MzZxMCAxMzItOTMuNSAyMjRUNDgwLTEyMFpNMjQyLTQwMGg0NzRxMTItNzItMTMuNS0xMjNUNjUwLTYwMEw0ODAtNzY4IDMxMC02MDBxLTI3IDI2LTUzIDc3dC0xNSAxMjNaIi8+PC9zdmc+'; Editor.shareImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjMWYxZjFmIj48cGF0aCBkPSJNMjQwLTgwcS0zMyAwLTU2LjUtMjMuNVQxNjAtMTYwdi00MDBxMC0zMyAyMy41LTU2LjVUMjQwLTY0MGgxMjB2ODBIMjQwdjQwMGg0ODB2LTQwMEg2MDB2LTgwaDEyMHEzMyAwIDU2LjUgMjMuNVQ4MDAtNTYwdjQwMHEwIDMzLTIzLjUgNTYuNVQ3MjAtODBIMjQwWm0yMDAtMjQwdi00NDdsLTY0IDY0LTU2LTU3IDE2MC0xNjAgMTYwIDE2MC01NiA1Ny02NC02NHY0NDdoLTgwWiIvPjwvc3ZnPg=='; /** * All fill styles supported by rough.js. */ Editor.roughFillStyles = [{val: 'auto', dispName: 'Auto', res: 'automatic'}, {val: 'hachure', dispName: 'Hachure', res: 'hachure'}, {val: 'solid', dispName: 'Solid', res: 'solid'}, {val: 'zigzag', dispName: 'ZigZag', res: 'zigzag'}, {val: 'cross-hatch', dispName: 'Cross Hatch', res: 'crossHatch'}, {val: 'dashed', dispName: 'Dashed', res: 'dashed'}, {val: 'zigzag-line', dispName: 'ZigZag Line', res: 'zigzagLine'}]; /** * Fill styles for normal mode. */ Editor.fillStyles = [{val: 'auto', dispName: 'Auto', res: 'automatic'}, {val: 'hatch', dispName: 'Hatch', res: 'hatch'}, {val: 'solid', dispName: 'Solid', res: 'solid'}, {val: 'dots', dispName: 'Dots', res: 'dots'}, {val: 'cross-hatch', dispName: 'Cross Hatch', res: 'crossHatch'}, {val: 'dashed', dispName: 'Dashed', res: 'dashed'}, {val: 'zigzag-line', dispName: 'ZigZag Line', res: 'zigzagLine'}]; /** * List of supported custom themes. */ Editor.themes = []; /** * Specifies the image URL to be used for the transparent background. */ Editor.ctrlKey = (mxClient.IS_MAC) ? '⌘' : 'Ctrl'; /** * Specifies the image URL to be used for the transparent background. */ Editor.altKey = (mxClient.IS_MAC) ? '⌥' : 'Alt'; /** * Specifies the image URL to be used for the transparent background. */ Editor.shiftKey = (mxClient.IS_MAC) ? '⇧' : 'Shift'; /** * Specifies the image URL to be used for the transparent background. */ Editor.hintOffset = 20; /** * Delay in ms to show shape picker on hover over blue arrows. */ Editor.shapePickerHoverDelay = 300; /** * Specifies the image URL to be used for the transparent background. */ Editor.fitWindowBorders = null; /** * Specifies if the diagram should be saved automatically if possible. Default * is true. */ /** * Specifies if the host blocks opening new windows/tabs (window.open). When true * (via the suppressNewWindows config option or ?suppressNewWindows=1), link * clicks are forwarded to the embedding host instead of opening locally, and the * "open in new window/tab" options in the Save/Export/HTML dialogs are hidden * (popupsAllowed is forced off). Default false. */ Editor.suppressNewWindows = window.urlParams != null && urlParams['suppressNewWindows'] == '1'; Editor.popupsAllowed = (window.urlParams != null? urlParams['noDevice'] != '1' : true) && !Editor.suppressNewWindows; /** * Specifies if the html and whiteSpace styles should be removed on inserted cells. */ Editor.simpleLabels = false; /** * Specifies if unnecessary spans should be removed from HTML labels when * editing is stopped. Adjacent spans with compatible styles are merged * and redundant wrapper spans are unwrapped. Default is false. */ Editor.optimizeHtmlLabels = false; /** * Specifies if the native clipboard is enabled. Blocked in iframes for possible sandbox attribute. * LATER: Check if actually blocked. */ Editor.enableNativeClipboard = window == window.top && navigator.clipboard != null && navigator.clipboard.read != null; /** * Dynamic change of dark mode for minimal and sketch theme. */ Editor.sketchMode = false; /** * Dynamic change of dark mode for minimal and sketch theme. */ Editor.darkMode = false; /** * Dynamic change of dark mode for minimal and sketch theme. */ Editor.currentTheme = uiTheme; /** * Dynamic change of dark mode for minimal and sketch theme. */ Editor.darkColor = '#121212'; /** * Name of the variable to use for the dark color var in exported SVG. * Default is '--ge-dark-color'. */ Editor.darkColorVar = '--ge-dark-color'; /** * Default page background color for light mode. Default is '#ffffff'. */ Editor.pageBackgroundColor = '#ffffff'; /** * Default page background color for dark mode. Editor.darkColor is used * if this is null. */ Editor.darkPageBackgroundColor = null; /** * Returns the default page background color as a light-dark expression. */ Editor.getDefaultPageBackgroundColor = function() { return 'light-dark(' + Editor.pageBackgroundColor + ', ' + ((Editor.darkPageBackgroundColor != null) ? Editor.darkPageBackgroundColor : Editor.darkColor) + ')'; }; /** * Label for the font size unit. Default is 'px'. */ Editor.fontSizeUnit = 'px'; /** * Delay for transitions in secons. Default in 0.1. */ Editor.transitionDelay = 0.1; /** * Default unit for page sizes. Default is inches. */ Editor.pageSizeUnit = mxConstants.INCHES; /** * Whether to remove EXIF metadata from pasted images. Default is true. */ Editor.removeImageMetadata = true; /** * Whether to show the connect handle on selected vertices. Default is false. */ Editor.showConnectHandle = false; /** * Whether to enable the inline toolbar. Default is true. */ Editor.enableInlineToolbar = true; /** * Whether new groups are created with transparentBounds=1 so that their * bounds are derived from their children (the Automatic option in the * Arrange panel). Default is false. See jgraph/drawio#5688. */ Editor.defaultTransparentGroups = false; /** * Whether to show the "Automatic" (tangent) label rotation option in the * Arrange > Rotation panel. The engine support (labelAutoRotate style) is * always active so saved diagrams render correctly; this only gates the UI. * Experimental, see jgraph/drawio#5365. */ Editor.enableAutoRotateLabels = true; /** * Returns the current state of the dark mode. */ Editor.isDarkMode = function() { return Editor.darkMode; }; /** * Returns true if the given URL is a PNG data URL. */ Editor.isPngDataUrl = function(url) { return url != null && url.substring(0, 15) == 'data:image/png;' }; /** * Returns true if the given URL is a JPEG data URL. */ Editor.isJpgDataUrl = function(url) { return url != null && (url.substring(0, 15) == 'data:image/jpg;' || url.substring(0, 16) == 'data:image/jpeg;'); }; /** * Returns true if the given binary data is a PNG file. */ Editor.isPngData = function(data) { return data.length > 8 && data.charCodeAt(0) == 137 && data.charCodeAt(1) == 80 && data.charCodeAt(2) == 78 && data.charCodeAt(3) == 71 && data.charCodeAt(4) == 13 && data.charCodeAt(5) == 10 && data.charCodeAt(6) == 26 && data.charCodeAt(7) == 10; }; /** * Returns true if the given binary data is a JPEG file. */ Editor.isJpgData = function(data) { return data.length > 2 && data.charCodeAt(0) == 255 && data.charCodeAt(1) == 216; }; /** * Removes EXIF metadata from the given JPEG data URI and returns the cleaned data URI. */ Editor.stripImageMetadata = function(dataUri, enabled) { if (dataUri != null && enabled && (Editor.isJpgDataUrl(dataUri) || Editor.isPngDataUrl(dataUri))) { var comma = dataUri.indexOf(',') + 1; var base64 = dataUri.substring(comma); // Workaround for invalid character error in Safari var binary = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true); var buffer = new ArrayBuffer(binary.length); // Ready bytes into buffer var bytes = new Uint8Array(buffer); for (var i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } // Removes metadata for supported formats var result = null; try { if (Editor.isJpgData(binary)) { result = Editor.stripJpgMetadata(buffer); } else if (Editor.isPngData(binary)) { result = Editor.stripPngMetadata(buffer); } } catch (e) { // ignore } // Converts back to Base64 if (result != null) { var newBase64 = ''; for (var i = 0; i < result.length; i++) { newBase64 += String.fromCharCode(result[i]); } dataUri = dataUri.substring(0, comma) + btoa(newBase64); } } return dataUri; }; /** * Removes EXIF metadata from the given JPEG data. */ Editor.stripJpgMetadata = function(buffer) { var view = new DataView(buffer); var chunks = []; chunks.push(new Uint8Array(buffer, 0, 2)); // SOI var offset = 2; while (offset < buffer.byteLength - 1) { var marker = view.getUint16(offset); // SOS - Copies rest if (marker === 0xFFDA) { chunks.push(new Uint8Array(buffer, offset)); break; } // Standalone markers if (marker >= 0xFFD0 && marker <= 0xFFD9) { chunks.push(new Uint8Array(buffer, offset, 2)); offset += 2; continue; } var segmentLength = view.getUint16(offset + 2); // Skips APP1/APP2 var isApp1 = marker === 0xFFE1; var isApp2 = marker === 0xFFE2; if (!isApp1 && !isApp2) { chunks.push(new Uint8Array(buffer, offset, 2 + segmentLength)); } offset += 2 + segmentLength; } // Merges chunks var totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); var result = new Uint8Array(totalLength); var pos = 0; for (var chunk of chunks) { result.set(chunk, pos); pos += chunk.length; } return result; }; /** * Removes EXIF metadata from the given PNG data. */ Editor.stripPngMetadata = function(buffer) { var view = new DataView(buffer); // Chunks to be removed var metadataChunks = new Set([ 'eXIf', // EXIF 'tEXt', // Text 'iTXt', // International text / XMP 'zTXt', // Compressed text 'iCCP', // ICC Profile 'tIME', // Modification time // Optional chunks: // 'pHYs', // DPI info // 'hIST', // Histogram // 'sPLT', // Suggested palette ]); var chunks = []; chunks.push(new Uint8Array(buffer, 0, 8)); // PNG signature var offset = 8; while (offset < buffer.byteLength) { var length = view.getUint32(offset); var type = String.fromCharCode( view.getUint8(offset + 4), view.getUint8(offset + 5), view.getUint8(offset + 6), view.getUint8(offset + 7)) var chunkSize = 4 + 4 + length + 4; // length + type + data + CRC if (!metadataChunks.has(type)) { chunks.push(new Uint8Array(buffer, offset, chunkSize)); } offset += chunkSize; if (type === 'IEND') { break; } } // Merges chunks var totalLength = chunks.reduce((sum, c) => sum + c.length, 0); var result = new Uint8Array(totalLength); var pos = 0; for (var chunk of chunks) { result.set(chunk, pos); pos += chunk.length; } return result; }; /** * Converts HTML to plain text. */ Editor.convertHtmlToText = function(label) { if (label != null) { var temp = document.createElement('div'); temp.innerHTML = Graph.sanitizeHtml(label); return mxUtils.extractTextWithWhitespace(temp.childNodes) } else { return null; } }; /** * */ Editor.extractGraphModelFromText = function(text) { var start = text.indexOf('<mxGraphModel '); var end = text.indexOf('</mxGraphModel>'); if (start < 0) { start = text.indexOf('<mxGraphModel>'); } function trimStart(text) { var index = text.indexOf('```'); if (index >= 0) { text = text.substring(text, 0, index + 6); } return text; }; function trimEnd(text) { var index = text.lastIndexOf('```'); if (index >= 0) { text = text.substring(index + 3); } return text; }; if (start >= 0 && end > start) { return [mxUtils.trim(trimStart(text.substring(0, start))), text.substring(start, end + 15), mxUtils.trim(trimEnd(text.substring(end + 15)))]; } else { return [text, '', '']; } }; /** * Returns the given XML with an incomplete trailing construct removed * and all open elements closed, or null if the text is not truncated. * Used to render the complete prefix of a diagram from a response that * was cut off mid-stream (eg. by an output token limit). */ Editor.repairTruncatedXml = function(text) { // Anchors the scan at the diagram so a stray < in prose // around the XML does not derail the tag scanner var offset = text.indexOf('<mxfile'); if (offset < 0) { offset = text.indexOf('<mxGraphModel'); } if (offset < 0) { offset = 0; } var stack = []; var lastGood = offset; var truncated = false; var pos = offset; while (pos < text.length) { var lt = text.indexOf('<', pos); if (lt < 0) { // Trailing character data is dropped if any element is // still open as it may be cut off mid-entity truncated = stack.length > 0; break; } if (text.substring(lt, lt + 4) == '<!--') { var close = text.indexOf('-->', lt + 4); if (close < 0) { truncated = true; break; } pos = close + 3; } else if (text.substring(lt, lt + 9) == '<![CDATA[') { var close = text.indexOf(']]>', lt + 9); if (close < 0) { truncated = true; break; } pos = close + 3; } else { // Finds the end of the tag ignoring > in attribute values var quote = null; var gt = -1; for (var i = lt + 1; i < text.length; i++) { var c = text.charAt(i); if (quote != null) { if (c == quote) { quote = null; } } else if (c == '"' || c == '\'') { quote = c; } else if (c == '>') { gt = i; break; } } if (gt < 0) { truncated = true; break; } var first = text.charAt(lt + 1); if (first == '/') { var match = /^<\/\s*([^\s>]+)/.exec(text.substring(lt, gt)); if (match != null) { // Tolerates unbalanced content by closing all // elements that were opened after the match for (var j = stack.length - 1; j >= 0; j--) { if (stack[j] == match[1]) { stack.splice(j); break; } } } } else if (first != '?' && first != '!' && text.charAt(gt - 1) != '/') { var match = /^<\s*([^\s\/>]+)/.exec(text.substring(lt, gt)); if (match != null) { stack.push(match[1]); } } pos = gt + 1; } lastGood = pos; } if ((!truncated && stack.length == 0) || lastGood <= offset) { return null; } var result = text.substring(0, lastGood); for (var k = stack.length - 1; k >= 0; k--) { result += '</' + stack[k] + '>'; } return result; }; /** * Extracts the XML from the compressed or non-compressed text chunk. */ Editor.extractGraphModelFromPng = function(data) { var result = null; try { var base64 = data.substring(data.indexOf(',') + 1); // Workaround for invalid character error in Safari var binary = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true); EditorUi.parsePng(binary, mxUtils.bind(this, function(pos, type, length) { var value = binary.substring(pos + 8, pos + 8 + length); if (type == 'zTXt') { var idx = value.indexOf(String.fromCharCode(0)); var keyword = value.substring(0, idx); if (keyword == 'mxGraphModel' || keyword == 'mxfile') { // Compressed payload starts after the keyword null and the // 1-byte compression method (idx + 2). var buffer = Graph.stringToArrayBuffer(value.substring(idx + 2)); var xmlData = null; try { // Spec-compliant zTXt is a zlib (RFC 1950) datastream xmlData = pako.inflate(buffer, {to: 'string'}); } catch (e) { // Fallback for PNGs written by the pre-fix CLI, which // stored a raw DEFLATE stream [jgraph/drawio-desktop#2425] xmlData = pako.inflateRaw(buffer, {to: 'string'}); } // Workaround for Java URL Encoder using + for spaces, which isn't compatible with JS xmlData = (xmlData != null) ? xmlData.replace(/\+/g,' ') : null; if (xmlData != null && xmlData.length > 0) { result = xmlData; } } } // Uncompressed section is normally not used else if (type == 'tEXt') { var vals = value.split(String.fromCharCode(0)); if (vals.length > 1 && (vals[0] == 'mxGraphModel' || vals[0] == 'mxfile')) { result = vals[1]; } } if (result != null || type == 'IDAT') { // Stops processing the file as our text chunks // are always placed before the data section return true; } })); } catch (e) { // ignores decoding errors } if (result != null && result.charAt(0) == '%') { result = decodeURIComponent(result); } // Workaround for double encoded content if (result != null && result.charAt(0) == '%') { result = decodeURIComponent(result); } return result; }; /** * Parses a camel case name with an optional trailing number and returns a * user-friendly label. */ Editor.getLabelForStylename = function(name) { var m = name.match(/^([A-Za-z]*)(\d*)$/); if (m != null && m.length > 1) { var words = mxUtils.trim(m[1] .replace(/([a-z])([A-Z])/g, '$1 $2') .replace(/\b\w/g, function(token) { return token.toUpperCase(); }) .replace(/Color[s]?/g, '')); var num = (m[2].length > 0 ? ' (' + m[2] + ')' : ''); name = mxUtils.trim(words + num); } return name; }; /** * Soundex algorithm for strings. * See https://www.codedrome.com/the-soundex-algorithm-in-javascript/ */ Editor.soundex = function(name) { if (name == null || name.length == 0) { return ''; } else { var s = []; var si = 1; var c; // Changed: s maps to 0 not 2 to ignore plurals // ABCDEFGHIJKLMNOPQRSTUVWXYZ var mappings = '01230120022455012603010202'; s[0] = name[0].toUpperCase(); for(var i = 1, l = name.length; i < l; i++) { c = (name[i].toUpperCase()).charCodeAt(0) - 65; if(c >= 0 && c <= 25) { if(mappings[c] != '0') { if(mappings[c] != s[si-1]) { s[si] = mappings[c]; si++; } if(si > 3) { break; } } } } if(si <= 3) { while(si <= 3) { s[si] = '0'; si++; } } return s.join(''); } }; /** * Selects the given part of the input element. */ Editor.selectFilename = function(input) { var end = input.value.lastIndexOf('.'); if (end > 0) { var ext = input.value.substring(end + 1); if (ext != 'drawio') { if (mxUtils.indexOf(['png', 'svg', 'html', 'xml', 'pdf'], ext) >= 0) { var temp = input.value.lastIndexOf('.drawio.', end); if (temp > 0) { end = temp; } } } } end = (end > 0) ? end : input.value.length; Editor.selectSubstring(input, 0, end); }; /** * Selects the given part of the input element. */ Editor.selectSubstring = function(input, startPos, endPos) { input.focus(); if (typeof input.selectionStart != 'undefined') { input.selectionStart = startPos; input.selectionEnd = endPos; } else if (document.selection && document.selection.createRange) { // IE branch input.select(); var range = document.selection.createRange(); range.collapse(true); range.moveEnd('character', endPos); range.moveStart('character', startPos); range.select(); } }; /** * */ Editor.toUnit = function(pixels, unit) { if (unit == mxConstants.INCHES) { return Math.round(pixels * 1000 / mxConstants.PIXELS_PER_INCH) / 1000; } else if (unit == mxConstants.MILLIMETERS) { return Math.round(pixels * 1000 / mxConstants.PIXELS_PER_MM) / 1000; } else if (unit == mxConstants.METERS) { return Math.round(pixels * 100000 / (mxConstants.PIXELS_PER_MM * 1000)) / 100000; } else { return Math.round(pixels * 10) / 10; } }; /** * */ Editor.fromUnit = function(value, unit) { if (unit == mxConstants.INCHES) { value = value * mxConstants.PIXELS_PER_INCH; } else if (unit == mxConstants.MILLIMETERS) { value = value * mxConstants.PIXELS_PER_MM; } else if (unit == mxConstants.METERS) { value = value * mxConstants.PIXELS_PER_MM * 1000; } // Rounds to 0.1px so unit round-trips are stable and values // entered in one unit convert back exactly (eg. 1/8in = 12.5px) return Math.round(value * 10) / 10; }; /** * Returns the cursor-key nudge step in model pixels for the given unit. This makes * arrow-key moves correspond to a sensible increment of the document unit instead of * a fixed pixel. Points (the default) return 1 so point-based diagrams are unchanged. */ Editor.getCursorMoveStep = function(unit) { if (unit == mxConstants.MILLIMETERS) { return 0.1 * mxConstants.PIXELS_PER_MM; } else if (unit == mxConstants.INCHES) { return 0.01 * mxConstants.PIXELS_PER_INCH; } else if (unit == mxConstants.METERS) { return 0.0001 * mxConstants.PIXELS_PER_MM * 1000; } else { return 1; } }; /** * Editor inherits from mxEventSource */ mxUtils.extend(Editor, mxEventSource); /** * Stores initial state of mxClient.NO_FO. */ Editor.prototype.originalNoForeignObject = mxClient.NO_FO; /** * Specifies the image URL to be used for the transparent background. */ Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' : IMAGE_PATH + '/transparent.gif'; /** * Specifies if the canvas should be extended in all directions. Default is true. */ Editor.prototype.extendCanvas = true; /** * Specifies if the app should run in chromeless mode. Default is false. * This default is only used if the contructor argument is null. */ Editor.prototype.chromeless = false; /** * Specifies the order of OK/Cancel buttons in dialogs. Default is true. * Cancel first is used on Macs, Windows/Confluence uses cancel last. */ Editor.prototype.cancelFirst = true; /** * Specifies if the editor is enabled. Default is true. */ Editor.prototype.enabled = true; /** * Contains the name which was used for the last save. Default value is null. */ Editor.prototype.filename = null; /** * Contains the current modified state of the diagram. This is false for * new diagrams and after the diagram was saved. */ Editor.prototype.modified = false; /** * Specifies if the diagram should be saved automatically if possible. Default * is true. */ Editor.prototype.autosave = true; /** * Specifies the app name. Default is document.title. */ Editor.prototype.appName = document.title; /** * */ Editor.prototype.editBlankUrl = window.location.origin + window.location.pathname; /** * Default value for the graph container overflow style. */ Editor.prototype.defaultGraphOverflow = 'hidden'; /** * Initializes the environment. */ Editor.prototype.init = function() { }; /** * Sets the XML node for the current diagram. */ Editor.prototype.isChromelessView = function() { return this.chromeless; }; /** * Sets the XML node for the current diagram. */ Editor.prototype.setAutosave = function(value) { this.autosave = value; this.fireEvent(new mxEventObject('autosaveChanged')); }; /** * */ Editor.prototype.getEditBlankUrl = function(params) { return this.editBlankUrl + params; } /** * */ Editor.prototype.editAsNew = function(xml, title, lightbox, pageId) { var p = (title != null) ? '?title=' + encodeURIComponent(title) : ''; if (lightbox) { p += ((p.length > 0) ? '&' : '?') + 'lightbox=1'; } else if (urlParams['ui'] != null) { p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui']; } if (pageId != null) { p += ((p.length > 0) ? '&' : '?') + 'page-id=' + pageId; } if (!navigator.standalone && typeof window.postMessage !== 'undefined') { var wnd = null; var l = mxUtils.bind(this, function(evt) { if (evt.data == 'ready' && evt.source == wnd) { mxEvent.removeListener(window, 'message', l); wnd.postMessage(xml, '*'); } }); // Uses transient hash for URL parameters var tokens = p.substring(1).split('&'); var json = {'client': 1}; for (var i = 0; i < tokens.length; i++) { var keyValue = tokens[i].split('='); json[keyValue[0]] = keyValue[1] || ''; } mxEvent.addListener(window, 'message', l); wnd = this.graph.openLink(this.getEditBlankUrl('') + '#P' + encodeURIComponent(JSON.stringify(json)), null, true); } else { this.graph.openLink(this.getEditBlankUrl(p) + '#R' + encodeURIComponent(xml)); } }; /** * Sets the XML node for the current diagram. */ Editor.prototype.createGraph = function(themes, model) { var graph = new Graph(null, model, null, null, themes); graph.transparentBackground = false; // Disables CSS transforms in Safari in chromeless mode var graphIsCssTransformsSupported = graph.isCssTransformsSupported; var self = this; graph.isCssTransformsSupported = function() { return graphIsCssTransformsSupported.apply(this, arguments) && (!self.chromeless || !mxClient.IS_SF); }; // Opens all links in a new window while editing if (!this.chromeless) { graph.isBlankLink = function(href) { return !this.isExternalProtocol(href); }; } return graph; }; /** * Sets the XML node for the current diagram. */ Editor.prototype.resetGraph = function() { this.graph.gridEnabled = this.graph.defaultGridEnabled && (!this.isChromelessView() || urlParams['grid'] == '1'); this.graph.graphHandler.guidesEnabled = true; this.graph.setTooltips(window.urlParams == null || urlParams['tooltips'] != '0'); this.graph.setConnectable(this.graph.defaultConnectable); this.graph.connectionArrowsEnabled = this.graph.defaultConnectionArrowsEnabled; this.graph.foldingEnabled = this.graph.defaultFoldingEnabled; this.graph.scrollbars = this.graph.defaultScrollbars; this.graph.pageVisible = this.graph.defaultPageVisible; this.graph.pageBreaksVisible = this.graph.pageVisible; this.graph.preferPageSize = this.graph.pageBreaksVisible; this.graph.background = null; this.graph.pageScale = mxGraph.prototype.pageScale; this.graph.pageFormat = mxGraph.prototype.pageFormat; this.graph.currentScale = 1; this.graph.currentTranslate.x = 0; this.graph.currentTranslate.y = 0; this.updateGraphComponents(); this.graph.view.setScale(1); }; /** * Sets the XML node for the current diagram. */ Editor.prototype.readGraphState = function(node) { // Preserve original XML attribute values for settings overridden by // URL parameters or config defaults so getGraphXml can write them back // instead of persisting the forced runtime state. this.savedGraphState = {}; if (Editor.preserveViewState) { var dx = node.getAttribute('dx'); var dy = node.getAttribute('dy'); if (dx != null) this.savedGraphState.dx = dx; if (dy != null) this.savedGraphState.dy = dy; } var grid = node.getAttribute('grid'); if (grid == null || grid == '') { grid = this.graph.defaultGridEnabled ? '1' : '0'; } if (urlParams['grid'] != null) { this.savedGraphState.grid = grid; } this.graph.gridEnabled = (urlParams['grid'] != null ? urlParams['grid'] != '0' : grid != '0') && (!this.isChromelessView() || urlParams['grid'] == '1'); this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize; this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0'; this.graph.setTooltips(node.getAttribute('tooltips') != '0' && (window.urlParams == null || urlParams['tooltips'] != '0')); var connect = node.getAttribute('connect'); if (connect != null && Editor.config != null && Editor.config.defaultConnectable != null) { this.savedGraphState.connect = connect; } this.graph.setConnectable(connect != null && (Editor.config == null || Editor.config.defaultConnectable == null) ? connect != '0' : this.graph.defaultConnectable); var arrows = node.getAttribute('arrows'); if (arrows != null && Editor.config != null && Editor.config.defaultConnectionArrowsEnabled != null) { this.savedGraphState.arrows = arrows; } this.graph.connectionArrowsEnabled = arrows != null && (Editor.config == null || Editor.config.defaultConnectionArrowsEnabled == null) ? arrows != '0' : this.graph.defaultConnectionArrowsEnabled; var fold = node.getAttribute('fold'); if (fold != null && Editor.config != null && Editor.config.defaultFoldingEnabled != null) { this.savedGraphState.fold = fold; } this.graph.foldingEnabled = fold != null && (Editor.config == null || Editor.config.defaultFoldingEnabled == null) ? fold != '0' : this.graph.defaultFoldingEnabled; if (this.isChromelessView() && this.graph.foldingEnabled) { this.graph.foldingEnabled = urlParams['nav'] == '1'; this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled; } var ps = parseFloat(node.getAttribute('pageScale')); if (!isNaN(ps) && ps > 0) { this.graph.pageScale = ps; } else { this.graph.pageScale = mxGraph.prototype.pageScale; } if (!this.graph.isLightboxView() && !this.graph.isViewer()) { if (urlParams['pv'] != null) { var pv = node.getAttribute('page'); this.savedGraphState.page = pv; this.graph.pageVisible = urlParams['pv'] != '0'; } else { var pv = node.getAttribute('page'); if (pv != null) { this.graph.pageVisible = (pv != '0'); } else { this.graph.pageVisible = this.graph.defaultPageVisible; } } } else { this.graph.pageVisible = false; } this.graph.pageBreaksVisible = this.graph.pageVisible; this.graph.preferPageSize = this.graph.pageBreaksVisible; var pw = parseFloat(node.getAttribute('pageWidth')); var ph = parseFloat(node.getAttribute('pageHeight')); if (!isNaN(pw) && !isNaN(ph)) { this.graph.pageFormat = new mxRectangle(0, 0, pw, ph); } // Loads the persistent state settings var bg = node.getAttribute('background'); if (bg != null && bg.length > 0) { this.graph.background = bg; } else { this.graph.background = null; } }; /** * Sets the XML node for the current diagram. */ Editor.prototype.setGraphXml = function(node) { if (node != null) { var dec = new mxCodec(node.ownerDocument); if (node.nodeName == 'mxGraphModel') { this.graph.model.beginUpdate(); try { this.graph.model.clear(); this.graph.view.scale = 1; this.readGraphState(node); this.updateGraphComponents(); dec.decode(node, this.graph.getModel()); } finally { this.graph.model.endUpdate(); } } else if (node.nodeName == 'root') { this.resetGraph(); // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml var wrapper = dec.document.createElement('mxGraphModel'); wrapper.appendChild(node); dec.decode(wrapper, this.graph.getModel()); this.updateGraphComponents(); } else { throw { message: mxResources.get('cannotOpenFile'), node: node, toString: function() { return this.message; } }; } } else { this.resetGraph(); this.graph.model.clear(); } this.fireEvent(new mxEventObject('resetGraphView')); }; /** * Returns the XML node that represents the current diagram. */ Editor.prototype.getGraphXml = function(ignoreSelection) { ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; var node = null; if (ignoreSelection) { var enc = new mxCodec(mxUtils.createXmlDocument()); node = enc.encode(this.graph.getModel()); enc.document.appendChild(node); } else { node = this.graph.encodeCells(mxUtils.sortCells( this.graph.model.getTopmostCells( this.graph.getSelectionCells()))); } var saved = this.savedGraphState || {}; if (saved.dx != null || saved.dy != null) { if (saved.dx != null) node.setAttribute('dx', saved.dx); if (saved.dy != null) node.setAttribute('dy', saved.dy); } else if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0) { node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100); node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100); } node.setAttribute('grid', saved.grid != null ? saved.grid : (this.graph.isGridEnabled()) ? '1' : '0'); node.setAttribute('gridSize', this.graph.gridSize); node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0'); node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0'); node.setAttribute('connect', saved.connect != null ? saved.connect : (this.graph.connectionHandler.isEnabled()) ? '1' : '0'); node.setAttribute('arrows', saved.arrows != null ? saved.arrows : (this.graph.connectionArrowsEnabled) ? '1' : '0'); node.setAttribute('fold', saved.fold != null ? saved.fold : (this.graph.foldingEnabled) ? '1' : '0'); node.setAttribute('page', saved.page != null ? saved.page : (this.graph.pageVisible) ? '1' : '0'); node.setAttribute('pageScale', this.graph.pageScale); node.setAttribute('pageWidth', this.graph.pageFormat.width); node.setAttribute('pageHeight', this.graph.pageFormat.height); if (this.graph.background != null) { node.setAttribute('background', this.graph.background); } return node; }; /** * Keeps the graph container in sync with the persistent graph state */ Editor.prototype.updateGraphComponents = function() { var graph = this.graph; if (graph.container != null) { graph.view.validateBackground(); graph.container.style.overflow = (graph.scrollbars) ? 'auto' : this.defaultGraphOverflow; this.fireEvent(new mxEventObject('updateGraphComponents')); } }; /** * Sets the modified flag. */ Editor.prototype.setModified = function(value) { this.modified = value; }; /** * Sets the filename. */ Editor.prototype.setFilename = function(value) { this.filename = value; }; /** * Creates and returns a new undo manager. */ Editor.prototype.createUndoManager = function() { var graph = this.graph; var undoMgr = new mxUndoManager(); this.undoListener = function(sender, evt) { undoMgr.undoableEditHappened(evt.getProperty('edit')); }; // Installs the command history var listener = mxUtils.bind(this, function(sender, evt) { this.undoListener.apply(this, arguments); }); graph.getModel().addListener(mxEvent.UNDO, listener); graph.getView().addListener(mxEvent.UNDO, listener); // Keeps the selection in sync with the history var undoHandler = function(sender, evt) { var changes = evt.getProperty('edit').changes; var cand = graph.getSelectionCellsForChanges(changes); var scrolled = false; if (cand.length > 0) { var cells = []; for (var i = 0; i < cand.length; i++) { if (graph.view.getState(cand[i]) != null) { cells.push(cand[i]); if (!scrolled) { graph.scrollCellToVisible(cand[i]); scrolled = true; } } } graph.setSelectionCells(cells); } }; undoMgr.addListener(mxEvent.UNDO, undoHandler); undoMgr.addListener(mxEvent.REDO, undoHandler); return undoMgr; }; /** * Adds basic stencil set (no namespace). */ Editor.prototype.initStencilRegistry = function() { }; /** * Creates and returns a new undo manager. */ Editor.prototype.destroy = function() { if (this.graph != null) { this.graph.destroy(); this.graph = null; } }; /** * Class for asynchronously opening a new window and loading a file at the same * time. This acts as a bridge between the open dialog and the new editor. */ OpenFile = function(done) { this.producer = null; this.consumer = null; this.done = done; this.args = null; }; /** * Registers the editor from the new window. */ OpenFile.prototype.setConsumer = function(value) { this.consumer = value; this.execute(); }; /** * Sets the data from the loaded file. */ OpenFile.prototype.setData = function() { this.args = arguments; this.execute(); }; /** * Displays an error message. */ OpenFile.prototype.error = function(msg) { this.cancel(true); mxUtils.alert(msg); }; /** * Consumes the data. */ OpenFile.prototype.execute = function() { if (this.consumer != null && this.args != null) { this.cancel(false); this.consumer.apply(this, this.args); } }; /** * Cancels the operation. */ OpenFile.prototype.cancel = function(cancel) { if (this.done != null) { this.done((cancel != null) ? cancel : true); } }; /** * Basic dialogs that are available in the viewer (print dialog). */ function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent, minSize, ignoreBgClick) { this.editorUi = editorUi; if (this.bg == null) { this.bg = editorUi.createDiv('geBackground'); } // document.body can be null in edge cases (e.g. during page teardown); // skip the appendChild calls so the dialog construction does not crash if (modal && document.body != null) { document.body.appendChild(this.bg); } var div = editorUi.createDiv(transparent? 'geTransDialog' : 'geDialog'); div.style.width = (w + 48) + 'px'; if (h != null) { div.style.height = (h + 48) + 'px'; } if (urlParams['embedInline'] == '1' && !Editor.inlineFullscreen && editorUi.embedViewport != null) { div.style.left = (editorUi.embedViewport.x + editorUi.embedViewport.width / 2) + 'px'; div.style.top = (editorUi.embedViewport.y + editorUi.embedViewport.height / 2) + 'px'; } div.appendChild(elt); if (document.body != null) { document.body.appendChild(div); } if (h == null) { div.style.height = (elt.scrollHeight + 48) + 'px'; } if (closable) { var closeImg = document.createElement('div'); closeImg.className = 'geButton'; closeImg.setAttribute('title', mxResources.get('close')); closeImg.style.backgroundImage = 'url(' + Editor.crossImage + ')'; closeImg.style.position = 'absolute'; closeImg.style.top = '0px'; closeImg.style.right = '0px'; mxEvent.addListener(closeImg, 'click', mxUtils.bind(this, function() { editorUi.hideDialog(true); })); div.appendChild(closeImg); if (!ignoreBgClick) { var mouseDownSeen = false; mxEvent.addGestureListeners(this.bg, mxUtils.bind(this, function(evt) { mouseDownSeen = true; }), null, mxUtils.bind(this, function(evt) { if (mouseDownSeen) { editorUi.hideDialog(true); mouseDownSeen = false; } })); } } this.onDialogClose = onClose; this.container = div; if (minSize != null) { this.addResizeHandler(minSize); } editorUi.editor.fireEvent(new mxEventObject('showDialog')); }; /** * */ Dialog.prototype.noColorImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC'; /** * */ Dialog.prototype.closeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg=='; /** * */ Dialog.prototype.clearImage = 'data:image/gif;base64,R0lGODlhDQAKAIABAMDAwP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUIzOEM1NzI4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUIzOEM1NzM4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QjM4QzU3MDg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QjM4QzU3MTg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAEALAAAAAANAAoAAAIXTGCJebD9jEOTqRlttXdrB32PJ2ncyRQAOw=='; /** * Removes the dialog from the DOM. */ Dialog.prototype.getDocumentSize = function() { return mxUtils.getDocumentSize(); }; /** * Removes the dialog from the DOM. */ Dialog.prototype.getPosition = function(left, top) { return new mxPoint(left, top); }; /** * Adds a resize handler to the dialog. * * In addition to the bottom-right `resize.gif` corner, installs * transparent edge / corner handles on every other side via * `installDialogEdgeResizeHandles` so users can resize the dialog * from any side — same affordance native OS windows provide. The * Dialog is center-positioned, so each handle grows / shrinks the * container symmetrically (`2 * dx` / `2 * dy`); the centering CSS * takes care of repositioning. */ Dialog.prototype.addResizeHandler = function(minSize) { var resize = document.createElement('img'); resize.setAttribute('src', mxWindow.prototype.resizeImage); resize.style.cursor = 'nwse-resize'; resize.style.position = 'absolute'; resize.style.bottom = '0px'; resize.style.right = '0px'; resize.style.zIndex = '2'; var self = this; var commitResize = function() { if (typeof self.onResize === 'function') { self.onResize(parseInt(self.container.style.width), parseInt(self.container.style.height)); } }; var addHandle = function(el, edges) { var startX = null, startY = null, startW = null, startH = null; var start = function(evt) { startX = mxEvent.getClientX(evt); startY = mxEvent.getClientY(evt); startW = parseInt(self.container.style.width); startH = parseInt(self.container.style.height); mxEvent.addGestureListeners(document, null, drag, drop); mxEvent.consume(evt); }; var drag = function(evt) { if (startX == null) return; var dx = mxEvent.getClientX(evt) - startX; var dy = mxEvent.getClientY(evt) - startY; var w = startW, h = startH; // 2 * delta so the dialog grows / shrinks symmetrically // around its CSS-centered position. The signs differ per // edge: dragging the bottom or right outward (+dx/+dy) // grows; dragging the top or left outward (-dx/-dy on // the inside) shrinks the negative direction, hence the // `- 2 *` for `t` / `l`. if (edges.indexOf('r') >= 0) w += 2 * dx; if (edges.indexOf('l') >= 0) w -= 2 * dx; if (edges.indexOf('b') >= 0) h += 2 * dy; if (edges.indexOf('t') >= 0) h -= 2 * dy; self.container.style.width = Math.max(minSize.width, w) + 'px'; self.container.style.height = Math.max(minSize.height, h) + 'px'; mxEvent.consume(evt); }; var drop = function(evt) { if (startX == null) return; startX = null; startY = null; mxEvent.removeGestureListeners(document, null, drag, drop); mxEvent.consume(evt); commitResize(); }; mxEvent.addGestureListeners(el, start, drag, drop); }; addHandle(resize, 'br'); this.container.appendChild(resize); installDialogEdgeResizeHandles(this.container, addHandle); }; /** * Appends 7 transparent edge / corner resize handles (skipping the * bottom-right corner, which the caller already owns) to the given * container. Each handle delegates to the supplied `addHandle(el, * edges)` callback so the host (Dialog or mxWindow) can apply its own * resize math. */ function installDialogEdgeResizeHandles(container, addHandle) { // Edge codes: t/b/l/r — which sides of the window this handle moves. // The bottom-right (`br`) is skipped because the caller already // installed a handle there (Dialog ships an <img>, mxWindow's // setResizable does the same). var configs = [ {styles: 'top:0;left:0;width:8px;height:8px;', cursor: 'nw-resize', edges: 'tl'}, {styles: 'top:0;left:8px;right:8px;height:4px;', cursor: 'n-resize', edges: 't'}, {styles: 'top:0;right:0;width:8px;height:8px;', cursor: 'ne-resize', edges: 'tr'}, // Right and bottom edges stop short of the bottom-right corner // handle (~14px) so dragging the edge doesn't accidentally // trigger the corner. {styles: 'top:8px;right:0;bottom:14px;width:4px;', cursor: 'e-resize', edges: 'r'}, {styles: 'bottom:0;left:0;width:8px;height:8px;', cursor: 'sw-resize', edges: 'bl'}, {styles: 'bottom:0;left:8px;right:14px;height:4px;', cursor: 's-resize', edges: 'b'}, {styles: 'top:8px;left:0;bottom:8px;width:4px;', cursor: 'w-resize', edges: 'l'} ]; var handles = []; configs.forEach(function(cfg) { var h = document.createElement('div'); h.style.cssText = 'position:absolute;z-index:2;' + cfg.styles + 'cursor:' + cfg.cursor; addHandle(h, cfg.edges); container.appendChild(h); handles.push(h); }); return handles; } /** * Default mxWindow.setResizable only installs a single bottom-right * <img> handle. Wrap it so every resizable mxWindow also gets * transparent handles on the other three corners and the four edges — * matching the resize affordance native windows on Windows / macOS * provide. The added handles are reused across toggles (cached on the * window via `_edgeResizeHandles`) and hidden / shown together with * the built-in handle. */ (function() { var mxWindowSetResizable = mxWindow.prototype.setResizable; mxWindow.prototype.setResizable = function(resizable) { mxWindowSetResizable.apply(this, arguments); if (resizable) { if (this._edgeResizeHandles == null) { this._edgeResizeHandles = installMxWindowEdgeResizeHandles(this); } else { this._edgeResizeHandles.forEach(function(h) { h.style.display = ''; }); } } else if (this._edgeResizeHandles != null) { this._edgeResizeHandles.forEach(function(h) { h.style.display = 'none'; }); } }; })(); /** * Builds the 7 edge / corner resize handles for an mxWindow. The * mxWindow positions itself via setLocation / setSize (no CSS centering), * so each handle moves the opposite edge to keep the anchor edge fixed. * Fires RESIZE_START / RESIZE / RESIZE_END so the persistence listeners * pick up the new size. If the window is docked, fires MOVE_START / * MOVE_END first so the docking wrapper undocks before resizing (the * dock wrapper otherwise blocks setLocation when dockState is set). */ function installMxWindowEdgeResizeHandles(wnd) { var minSize = wnd.minimumSize || new mxRectangle(0, 0, 50, 40); var addHandle = function(el, edges) { var startX = null, startY = null; var startWinX, startWinY, startW, startH; var down = function(evt) { wnd.activate(); if (wnd.dockState != null) { wnd.fireEvent(new mxEventObject(mxEvent.MOVE_START, 'event', evt)); wnd.fireEvent(new mxEventObject(mxEvent.MOVE_END, 'event', evt)); } startX = mxEvent.getClientX(evt); startY = mxEvent.getClientY(evt); startWinX = wnd.getX(); startWinY = wnd.getY(); // Use style.width/height (CSS content size — what setSize // writes) rather than offsetWidth/Height (which includes // borders). Using offsetWidth here makes the anchor edge // drift by exactly the border width when dragging the // opposite edge: setSize would re-apply the dragged size to // style.width, but the new offsetWidth would then be // border-larger than the original, shifting the anchor. startW = parseInt(wnd.div.style.width) || wnd.div.offsetWidth; startH = parseInt(wnd.div.style.height) || wnd.div.offsetHeight; mxEvent.addGestureListeners(document, null, move, up); wnd.fireEvent(new mxEventObject(mxEvent.RESIZE_START, 'event', evt)); mxEvent.consume(evt); }; var move = function(evt) { if (startX == null) return; var dx = mxEvent.getClientX(evt) - startX; var dy = mxEvent.getClientY(evt) - startY; var newX = startWinX, newY = startWinY; var newW = startW, newH = startH; if (edges.indexOf('t') >= 0) { newY = startWinY + dy; newH = startH - dy; } if (edges.indexOf('b') >= 0) { newH = startH + dy; } if (edges.indexOf('l') >= 0) { newX = startWinX + dx; newW = startW - dx; } if (edges.indexOf('r') >= 0) { newW = startW + dx; } if (newW < minSize.width) { if (edges.indexOf('l') >= 0) { newX = startWinX + startW - minSize.width; } newW = minSize.width; } if (newH < minSize.height) { if (edges.indexOf('t') >= 0) { newY = startWinY + startH - minSize.height; } newH = minSize.height; } if (newX !== startWinX || newY !== startWinY) { wnd.setLocation(newX, newY); } wnd.setSize(newW, newH); wnd.fireEvent(new mxEventObject(mxEvent.RESIZE, 'event', evt)); mxEvent.consume(evt); }; var up = function(evt) { if (startX == null) return; startX = null; startY = null; mxEvent.removeGestureListeners(document, null, move, up); wnd.fireEvent(new mxEventObject(mxEvent.RESIZE_END, 'event', evt)); mxEvent.consume(evt); }; mxEvent.addGestureListeners(el, down, move, up); }; return installDialogEdgeResizeHandles(wnd.div, addHandle); } /** * Removes the dialog from the DOM. */ Dialog.prototype.close = function(cancel, isEsc) { if (this.onDialogClose != null) { if (this.onDialogClose(cancel, isEsc) == false) { return false; } this.onDialogClose = null; } if (this.dialogImg != null && this.dialogImg.parentNode != null) { this.dialogImg.parentNode.removeChild(this.dialogImg); this.dialogImg = null; } if (this.bg != null && this.bg.parentNode != null) { this.bg.parentNode.removeChild(this.bg); } if (this.editorUi.embedViewport != null) { this.editorUi.removeListener(this.resizeListener); } else { mxEvent.removeListener(window, 'resize', this.resizeListener); } if (this.container.parentNode != null) { this.container.parentNode.removeChild(this.container); } }; /** * */ var ErrorDialog = function(editorUi, title, message, buttonText, fn, retry, buttonText2, fn2, hide, buttonText3, fn3) { hide = (hide != null) ? hide : true; var div = document.createElement('div'); div.style.textAlign = 'center'; div.style.flexDirection = 'column'; div.style.maxHeight = '100%'; div.style.display = 'flex'; if (title != null) { var hd = document.createElement('div'); hd.style.padding = '0px'; hd.style.margin = '0px'; hd.style.fontSize = '14px'; hd.style.fontWeight = '600'; hd.style.paddingBottom = '12px'; hd.style.marginBottom = '10px'; hd.style.borderBottom = '1px solid light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1))'; hd.style.whiteSpace = 'nowrap'; hd.style.textOverflow = 'ellipsis'; hd.style.overflow = 'hidden'; hd.style.flexShrink = '0'; mxUtils.write(hd, title); hd.setAttribute('title', title); div.appendChild(hd); } var p2 = document.createElement('div'); p2.style.lineHeight = '1.2em'; p2.style.padding = '6px'; p2.style.flexGrow = '1'; p2.style.overflowY = 'auto'; if (typeof message === 'string') { message = message.replace(/\n/g, '<br/>'); } p2.innerHTML = Graph.sanitizeHtml(message); div.appendChild(p2); var btns = document.createElement('div'); btns.style.marginTop = '12px'; btns.style.textAlign = 'center'; if (retry != null) { var retryBtn = mxUtils.button(mxResources.get('tryAgain'), function() { editorUi.hideDialog(); retry(); }); retryBtn.className = 'geBtn'; btns.appendChild(retryBtn); btns.style.textAlign = 'center'; } if (buttonText3 != null) { var btn3 = mxUtils.button(buttonText3, function() { if (fn3 != null) { fn3(); } }); btn3.className = 'geBtn'; btns.appendChild(btn3); } var btn = mxUtils.button(buttonText, function() { if (hide) { editorUi.hideDialog(); } if (fn != null) { fn(); } }); btn.className = 'geBtn'; btns.appendChild(btn); if (buttonText2 != null) { var mainBtn = mxUtils.button(buttonText2, function() { if (hide) { editorUi.hideDialog(); } if (fn2 != null) { fn2(); } }); mainBtn.className = 'geBtn gePrimaryBtn'; btns.appendChild(mainBtn); } this.init = function() { btn.focus(); }; div.appendChild(btns); this.container = div; }; /** * Constructs a new print dialog. */ var PrintDialog = function(editorUi, title, fn, btnTitle) { this.create(editorUi, title, fn, btnTitle); }; /** * Constructs a new print dialog. */ PrintDialog.prototype.create = function(editorUi) { var graph = editorUi.editor.graph; var row, td; var table = document.createElement('table'); table.style.width = '100%'; table.style.height = '100%'; var tbody = document.createElement('tbody'); row = document.createElement('tr'); var onePageCheckBox = document.createElement('input'); onePageCheckBox.setAttribute('type', 'checkbox'); td = document.createElement('td'); td.setAttribute('colspan', '2'); td.style.fontSize = '10pt'; td.appendChild(onePageCheckBox); var span = document.createElement('span'); mxUtils.write(span, ' ' + mxResources.get('fitPage')); td.appendChild(span); mxEvent.addListener(span, 'click', function(evt) { onePageCheckBox.checked = !onePageCheckBox.checked; pageCountCheckBox.checked = !onePageCheckBox.checked; mxEvent.consume(evt); }); mxEvent.addListener(onePageCheckBox, 'change', function() { pageCountCheckBox.checked = !onePageCheckBox.checked; }); row.appendChild(td); tbody.appendChild(row); row = row.cloneNode(false); var pageCountCheckBox = document.createElement('input'); pageCountCheckBox.setAttribute('type', 'checkbox'); td = document.createElement('td'); td.style.fontSize = '10pt'; td.appendChild(pageCountCheckBox); var span = document.createElement('span'); mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':'); td.appendChild(span); mxEvent.addListener(span, 'click', function(evt) { pageCountCheckBox.checked = !pageCountCheckBox.checked; onePageCheckBox.checked = !pageCountCheckBox.checked; mxEvent.consume(evt); }); row.appendChild(td); var pageCountInput = document.createElement('input'); pageCountInput.setAttribute('value', '1'); pageCountInput.setAttribute('type', 'number'); pageCountInput.setAttribute('min', '1'); pageCountInput.setAttribute('size', '4'); pageCountInput.setAttribute('disabled', 'disabled'); pageCountInput.style.width = '50px'; td = document.createElement('td'); td.style.fontSize = '10pt'; td.appendChild(pageCountInput); mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)'); row.appendChild(td); tbody.appendChild(row); mxEvent.addListener(pageCountCheckBox, 'change', function() { if (pageCountCheckBox.checked) { pageCountInput.removeAttribute('disabled'); } else { pageCountInput.setAttribute('disabled', 'disabled'); } onePageCheckBox.checked = !pageCountCheckBox.checked; }); row = row.cloneNode(false); td = document.createElement('td'); mxUtils.write(td, mxResources.get('pageScale') + ':'); row.appendChild(td); td = document.createElement('td'); var pageScaleInput = document.createElement('input'); pageScaleInput.setAttribute('value', '100 %'); pageScaleInput.setAttribute('size', '5'); pageScaleInput.style.width = '50px'; td.appendChild(pageScaleInput); row.appendChild(td); tbody.appendChild(row); row = document.createElement('tr'); td = document.createElement('td'); td.colSpan = 2; td.style.paddingTop = '30px'; td.setAttribute('align', 'right'); // Overall scale for print-out to account for print borders in dialogs etc function preview(print) { var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked; var printScale = parseInt(pageScaleInput.value) / 100; if (isNaN(printScale)) { printScale = 1; pageScaleInput.value = '100%'; } // Workaround to match available paper size in actual print output if (mxClient.IS_SF) { printScale *= 0.75; } var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT; var scale = 1 / graph.pageScale; if (autoOrigin) { var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value); if (!isNaN(pageCount)) { scale = mxUtils.getScaleForPageCount(pageCount, graph, pf); } } // Negative coordinates are cropped or shifted if page visible var border = 0; var x0 = 0; var y0 = 0; // Applies print scale without rounding so the printed page grid // stays aligned with the page breaks on the canvas pf = mxRectangle.fromRectangle(pf); pf.width = pf.width * printScale; pf.height = pf.height * printScale; scale *= printScale; // Starts at first visible page if (!autoOrigin && graph.pageVisible) { var layout = graph.getPageLayout(); x0 -= layout.x * pf.width; y0 -= layout.y * pf.height; } else { autoOrigin = true; } var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin); preview.open(); if (print) { PrintDialog.printPreview(preview); } }; var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() { editorUi.hideDialog(); }); cancelBtn.className = 'geBtn'; if (editorUi.editor.cancelFirst) { td.appendChild(cancelBtn); } if (PrintDialog.previewEnabled) { var previewBtn = mxUtils.button(mxResources.get('preview'), function() { editorUi.hideDialog(); preview(false); }); previewBtn.className = 'geBtn'; td.appendChild(previewBtn); } var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function() { editorUi.hideDialog(); preview(true); }); printBtn.className = 'geBtn gePrimaryBtn'; td.appendChild(printBtn); if (!editorUi.editor.cancelFirst) { td.appendChild(cancelBtn); } row.appendChild(td); tbody.appendChild(row); table.appendChild(tbody); var div = document.createElement('div'); div.style.paddingBottom = '20px'; div.appendChild(table); this.container = div; }; /** * Constructs a new print dialog. */ PrintDialog.printPreview = function(preview) { try { if (preview.wnd != null) { var printFn = function() { preview.wnd.focus(); preview.wnd.print(); preview.wnd.close(); }; // Workaround for rendering SVG output and // make window available for printing window.setTimeout(printFn, 500); } } catch (e) { // ignores possible Access Denied } }; /** * Constructs a new print dialog. */ PrintDialog.createPrintPreview = function(graph, scale, pf, border, x0, y0, autoOrigin) { var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0); preview.title = mxResources.get('preview'); preview.addPageCss = !mxClient.IS_SF; preview.printBackgroundImage = true; preview.autoOrigin = autoOrigin; var bg = graph.background; if (bg == null || bg == '' || bg == mxConstants.NONE) { bg = '#ffffff'; } preview.backgroundColor = bg; // Excludes hints from being printed as text labels var isTextLabel = preview.isTextLabel; preview.isTextLabel = function(node) { return !node.className == 'geHint' && isTextLabel.apply(this, arguments); }; // Makes relative links absolute var getLinkForCellState = preview.getLinkForCellState; preview.getLinkForCellState = function(state) { return graph.getAbsoluteUrl(getLinkForCellState.apply(this, arguments)); }; return preview; }; /** * Specifies if the preview button should be enabled. Default is true. */ PrintDialog.previewEnabled = true; /** * Constructs a new page setup dialog. */ var PageSetupDialog = function(editorUi) { var graph = editorUi.editor.graph; var div = document.createElement('div'); // Consistent label width and content spacing across rows so inputs // align even with longer translations (e.g. German "Gitternetzgröße"). var labelMinWidth = '140px'; var contentMargin = '8px'; var styleLabel = function(lbl) { lbl.style.minWidth = labelMinWidth; return lbl; }; var styleContent = function(elt) { elt.style.marginLeft = contentMargin; return elt; }; var hd = document.createElement('h3'); mxUtils.write(hd, mxResources.get('pageSetup')); div.appendChild(hd); // Paper size section var paperSection = document.createElement('div'); paperSection.className = 'geDialogSection'; var paperRow = document.createElement('div'); paperRow.className = 'geDialogFormRow geDialogFormRowTop'; var paperLabel = document.createElement('span'); paperLabel.className = 'geDialogFormLabel'; mxUtils.write(paperLabel, mxResources.get('paperSize') + ':'); paperRow.appendChild(styleLabel(paperLabel)); var paperContent = document.createElement('div'); paperContent.style.flex = '1'; paperContent.style.minWidth = '0'; var accessor = PageSetupDialog.addPageFormatPanel(paperContent, 'pagesetupdialog', graph.pageFormat); paperRow.appendChild(styleContent(paperContent)); paperSection.appendChild(paperRow); div.appendChild(paperSection); // Grid size section var gridSection = document.createElement('div'); gridSection.className = 'geDialogSection'; var gridRow = document.createElement('div'); gridRow.className = 'geDialogFormRow'; var gridLabel = document.createElement('span'); gridLabel.className = 'geDialogFormLabel'; mxUtils.write(gridLabel, mxResources.get('gridSize') + ':'); gridRow.appendChild(styleLabel(gridLabel)); var gridSizeInput = document.createElement('input'); gridSizeInput.setAttribute('type', 'number'); gridSizeInput.setAttribute('min', '0'); gridSizeInput.setAttribute('step', 'any'); gridSizeInput.style.width = '60px'; gridSizeInput.style.flex = '0 0 auto'; gridSizeInput.value = graph.getGridSize(); gridRow.appendChild(styleContent(gridSizeInput)); mxEvent.addListener(gridSizeInput, 'change', function() { var value = parseFloat(gridSizeInput.value); gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value); }); gridSection.appendChild(gridRow); div.appendChild(gridSection); // Background section var bgSection = document.createElement('div'); bgSection.className = 'geDialogSection'; var bgRow = document.createElement('div'); bgRow.className = 'geDialogFormRow'; var bgLabel = document.createElement('span'); bgLabel.className = 'geDialogFormLabel'; mxUtils.write(bgLabel, mxResources.get('background') + ':'); bgRow.appendChild(styleLabel(bgLabel)); var newBackgroundImage = graph.backgroundImage; var newBackgroundColor = graph.background; var changeImageLink = document.createElement('button'); changeImageLink.className = 'geBtn'; changeImageLink.style.margin = '0px'; mxUtils.write(changeImageLink, mxResources.get('change') + '...'); mxEvent.addListener(changeImageLink, 'click', function(evt) { editorUi.showBackgroundImageDialog(function(image, failed, color) { if (!failed) { if (image != null && image.src != null && Graph.isPageLink(image.src)) { image = {originalSrc: image.src}; } newBackgroundImage = image; } newBackgroundColor = color; }, newBackgroundImage, newBackgroundColor, true); mxEvent.consume(evt); }); bgRow.appendChild(styleContent(changeImageLink)); bgSection.appendChild(bgRow); div.appendChild(bgSection); // Less common options live in a collapsed Advanced section. Each option // keeps its own geDialogSection card: the helper's content div is the // first card, the other cards are appended to the collapsible wrapper. var advanced = editorUi.addAdvancedSection(div); // Adaptive colors section var adaptiveRow = document.createElement('div'); adaptiveRow.className = 'geDialogFormRow'; var adaptiveLabel = document.createElement('span'); adaptiveLabel.className = 'geDialogFormLabel'; mxUtils.write(adaptiveLabel, mxResources.get('adaptiveColors') + ':'); adaptiveRow.appendChild(styleLabel(adaptiveLabel)); var adaptiveDropdown = document.createElement('select'); styleContent(adaptiveDropdown); var addAdaptiveOption = function(value, label) { var opt = document.createElement('option'); opt.setAttribute('value', value); mxUtils.write(opt, label); adaptiveDropdown.appendChild(opt); }; addAdaptiveOption('default', mxResources.get('default') + ' (' + mxResources.get(Graph.getDefaultAdaptiveColorsKey()) + ')'); addAdaptiveOption('auto', mxResources.get('automatic')); addAdaptiveOption('simple', mxResources.get('simple')); addAdaptiveOption('none', mxResources.get('none')); adaptiveDropdown.value = (graph.adaptiveColors == null) ? 'default' : graph.adaptiveColors; adaptiveRow.appendChild(adaptiveDropdown); if (editorUi.menus != null && editorUi.menus.createHelpLink != null && (!editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)) { var helpLink = editorUi.menus.createHelpLink( 'https://www.drawio.com/docs/manual/editor/appearance/adaptive-colours/'); helpLink.style.marginLeft = '8px'; adaptiveRow.appendChild(helpLink); } advanced.content.appendChild(adaptiveRow); // Initial view section — lets the author pin the page's opening viewport // (pan + zoom). Stored on the diagram node via ChangePageView and restored // by EditorUi.fitInitialView on the fitDiagramOnLoad / fitDiagramOnPage // paths. Pages live in the diagramly layer, so the section (and its apply // below) is skipped when there is no current page (e.g. bare grapheditor). var pendingViewBox, viewBoxChanged; if (editorUi.currentPage != null) { pendingViewBox = editorUi.currentPage.getViewBox(); viewBoxChanged = false; var viewSection = document.createElement('div'); viewSection.className = 'geDialogSection'; var viewRow = document.createElement('div'); viewRow.className = 'geDialogFormRow'; var viewLabel = document.createElement('span'); viewLabel.className = 'geDialogFormLabel'; mxUtils.write(viewLabel, mxResources.get('initialView') + ':'); viewRow.appendChild(styleLabel(viewLabel)); var viewContent = document.createElement('div'); viewContent.style.flex = '1'; viewContent.style.minWidth = '0'; viewContent.style.display = 'flex'; viewContent.style.alignItems = 'center'; var viewStatus = document.createElement('span'); viewStatus.style.flex = '1 1 auto'; viewStatus.style.minWidth = '0'; viewStatus.style.overflow = 'hidden'; viewStatus.style.textOverflow = 'ellipsis'; viewStatus.style.whiteSpace = 'nowrap'; // The dialog's 1em line-height would clip descenders under // overflow:hidden - give the single-line box full glyph height viewStatus.style.lineHeight = 'normal'; var updateViewStatus = function() { var label; if (pendingViewBox != null) { label = pendingViewBox.x + ', ' + pendingViewBox.y; if (pendingViewBox.scale != null) { label += ' · ' + Math.round(pendingViewBox.scale * 100) + '%'; } } else { label = mxResources.get('default'); } viewStatus.textContent = label; }; updateViewStatus(); viewContent.appendChild(viewStatus); viewRow.appendChild(styleContent(viewContent)); viewSection.appendChild(viewRow); // Action buttons sit on their own full-width row. geBtn is display:flex, // so its text-overflow:ellipsis never renders; squeezed into the // label-indented content column the long translations (German "Aktuelle // übernehmen" / "Zurücksetzen") would be hard-clipped on both ends. A // dedicated row gives them the full section width, and flexShrink:0 keeps // each label intact (wrapping only as a last resort for longer locales). var viewButtonRow = document.createElement('div'); viewButtonRow.className = 'geDialogFormRow'; viewButtonRow.style.justifyContent = 'flex-end'; viewButtonRow.style.flexWrap = 'wrap'; viewButtonRow.style.gap = '8px'; var useCurrentViewBtn = mxUtils.button(mxResources.get('useCurrent'), function() { pendingViewBox = graph.getCurrentViewBox(); viewBoxChanged = true; updateViewStatus(); }); useCurrentViewBtn.className = 'geBtn'; useCurrentViewBtn.style.margin = '0px'; useCurrentViewBtn.style.flexShrink = '0'; viewButtonRow.appendChild(useCurrentViewBtn); var resetViewBtn = mxUtils.button(mxResources.get('reset'), function() { pendingViewBox = null; viewBoxChanged = true; updateViewStatus(); }); resetViewBtn.className = 'geBtn'; resetViewBtn.style.margin = '0px'; resetViewBtn.style.flexShrink = '0'; viewButtonRow.appendChild(resetViewBtn); viewSection.appendChild(viewButtonRow); advanced.wrapper.appendChild(viewSection); } // Animation section — entry point for the page-level animation editor. // The animation auto-plays when the page is viewed in chromeless / // lightbox mode (the hint text explains this). AnimationDialog isn't // part of the grapheditor layer, so we guard the section with a `typeof` // check; in builds without the diagramly layer (rare — only the // bare-bones grapheditor demo) the section is skipped. if (typeof AnimationDialog !== 'undefined') { var animationSection = document.createElement('div'); animationSection.className = 'geDialogSection'; var animationRow = document.createElement('div'); animationRow.className = 'geDialogFormRow geDialogFormRowTop'; // The label keeps its natural width (no styleLabel) so the hint // text next to the Edit button gets the remaining row width var animationLabel = document.createElement('span'); animationLabel.className = 'geDialogFormLabel'; animationLabel.style.minWidth = '0'; mxUtils.write(animationLabel, mxResources.get('animation') + ':'); animationRow.appendChild(animationLabel); var animationContent = document.createElement('div'); animationContent.style.flex = '1'; animationContent.style.minWidth = '0'; animationContent.style.display = 'flex'; animationContent.style.alignItems = 'center'; animationContent.style.gap = '8px'; var animationHint = document.createElement('span'); animationHint.className = 'geDialogHint'; animationHint.style.flex = '1 1 auto'; animationHint.style.minWidth = '0'; mxUtils.write(animationHint, mxResources.get('lightboxAnimationHint', null, 'Plays automatically when the page is shown in lightbox mode.')); animationContent.appendChild(animationHint); var editAnimBtn = mxUtils.button( mxResources.get('edit'), function() { // Close Page Setup first so the non-modal AnimationDialog // isn't obscured by the modal backdrop. The user can // reopen Page Setup afterwards. editorUi.hideDialog(); // Reuse the singleton animation window (also opened via the // 'animation' action) instead of stacking a new dialog. The // action's handler creates it on first use and wires up // window-state persistence. var menus = editorUi.menus; if (menus != null && menus.animationWindow != null) { menus.animationWindow.window.setVisible(true); menus.animationWindow.window.activate(); } else if (editorUi.actions.get('animation') != null) { editorUi.actions.get('animation').funct(); } }); editAnimBtn.className = 'geBtn'; editAnimBtn.style.minWidth = '90px'; animationContent.appendChild(editAnimBtn); animationRow.appendChild(styleContent(animationContent)); animationSection.appendChild(animationRow); advanced.wrapper.appendChild(animationSection); } // Apply function var applyFn = function() { var gridSize = parseFloat(gridSizeInput.value); if (!isNaN(gridSize) && graph.gridSize !== gridSize) { graph.setGridSize(gridSize); } var change = new ChangePageSetup(editorUi, newBackgroundColor, newBackgroundImage, accessor.get()); change.ignoreColor = graph.background == newBackgroundColor; var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null; var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null; change.ignoreImage = oldSrc === newSrc; var newAdaptive = adaptiveDropdown.value; var currentAdaptive = (graph.adaptiveColors == null) ? 'default' : graph.adaptiveColors; var adaptiveChanged = newAdaptive != currentAdaptive; if (adaptiveChanged) { change.adaptiveColors = newAdaptive; } if (graph.pageFormat.width != change.previousFormat.width || graph.pageFormat.height != change.previousFormat.height || !change.ignoreColor || !change.ignoreImage || adaptiveChanged) { graph.model.execute(change); } // Applies an initial-view change (Use Current / Reset). Guarded on the // current page so this stays a no-op outside the diagramly layer, where // ChangePageView is not defined. if (viewBoxChanged && editorUi.currentPage != null) { graph.model.execute(new ChangePageView(editorUi, editorUi.currentPage, pendingViewBox)); } }; // Exposes each button's own label as its tooltip so the full text stays // available on hover even when a long translation is clipped to fit the // dialog width (geBtn is display:flex, so text-overflow never renders). var dialogButtons = div.querySelectorAll('.geBtn'); for (var i = 0; i < dialogButtons.length; i++) { var btnLabel = mxUtils.trim(dialogButtons[i].textContent); if (dialogButtons[i].getAttribute('title') == null && btnLabel != '') { dialogButtons[i].setAttribute('title', btnLabel); } } var dlg = new CustomDialog(editorUi, div, applyFn, null, mxResources.get('apply')); this.container = dlg.container; }; /** * */ PageSetupDialog.addPageFormatPanel = function(div, namePostfix, pageFormat, pageFormatListener) { var formatName = 'format-' + namePostfix; var portraitCheckBox = document.createElement('input'); portraitCheckBox.setAttribute('name', formatName); portraitCheckBox.setAttribute('type', 'radio'); portraitCheckBox.setAttribute('value', 'portrait'); var landscapeCheckBox = document.createElement('input'); landscapeCheckBox.setAttribute('name', formatName); landscapeCheckBox.setAttribute('type', 'radio'); landscapeCheckBox.setAttribute('value', 'landscape'); var paperSizeSelect = document.createElement('select'); paperSizeSelect.style.position = 'relative'; paperSizeSelect.style.marginBottom = '4px'; paperSizeSelect.style.borderRadius = '4px'; paperSizeSelect.style.borderWidth = '1px'; paperSizeSelect.style.borderStyle = 'solid'; paperSizeSelect.style.boxSizing = 'border-box'; paperSizeSelect.style.padding = '2px'; paperSizeSelect.style.width = '210px'; var formatDiv = document.createElement('div'); formatDiv.style.alignItems = 'center'; formatDiv.style.whiteSpace = 'nowrap'; formatDiv.style.marginLeft = '4px'; formatDiv.style.width = '210px'; formatDiv.style.height = '24px'; portraitCheckBox.style.margin = '0 6px 0 0'; formatDiv.appendChild(portraitCheckBox); var portraitSpan = document.createElement('span'); portraitSpan.style.maxWidth = '100px'; mxUtils.write(portraitSpan, mxResources.get('portrait')); formatDiv.appendChild(portraitSpan); landscapeCheckBox.style.margin = '0 6px 0 10px'; formatDiv.appendChild(landscapeCheckBox); var landscapeSpan = document.createElement('span'); landscapeSpan.style.width = '100px'; mxUtils.write(landscapeSpan, mxResources.get('landscape')); formatDiv.appendChild(landscapeSpan) var customDiv = document.createElement('div'); customDiv.style.whiteSpace = 'nowrap'; customDiv.style.fontSize = '12px'; customDiv.style.width = '210px'; customDiv.style.height = '24px'; var widthInput = document.createElement('input'); widthInput.setAttribute('size', '6'); widthInput.setAttribute('title', mxResources.get('width')); widthInput.style.textAlign = 'right'; customDiv.appendChild(widthInput); mxUtils.write(customDiv, ' x '); var heightInput = document.createElement('input'); heightInput.setAttribute('size', '6'); heightInput.setAttribute('title', mxResources.get('height')); heightInput.style.textAlign = 'right'; customDiv.appendChild(heightInput); var unitSelect = document.createElement('select'); unitSelect.style.marginLeft = '4px'; unitSelect.style.boxSizing = 'border-box'; var units = [{label: mxResources.get('points'), unit: mxConstants.POINTS}, {label: mxResources.get('inches'), unit: mxConstants.INCHES}, {label: mxResources.get('millimeters'), unit: mxConstants.MILLIMETERS}, {label: mxResources.get('meters'), unit: mxConstants.METERS}]; for (var i = 0; i < units.length; i++) { var unitOption = document.createElement('option'); unitOption.setAttribute('value', units[i].unit); mxUtils.write(unitOption, units[i].label); unitSelect.appendChild(unitOption); } unitSelect.value = Editor.pageSizeUnit; customDiv.appendChild(unitSelect); formatDiv.style.display = 'none'; customDiv.style.display = 'none'; var pf = new Object(); var formats = PageSetupDialog.getFormats(); for (var i = 0; i < formats.length; i++) { var f = formats[i]; pf[f.key] = f; var paperSizeOption = document.createElement('option'); paperSizeOption.setAttribute('value', f.key); mxUtils.write(paperSizeOption, f.title); paperSizeSelect.appendChild(paperSizeOption); } var listener = function() { paperSizeSelect.value = 'custom'; for (var i = 0; i < formats.length; i++) { var f = formats[i]; if (f.format != null) { // Fixes wrong values for previous A4 and A5 page sizes if (f.key == 'a4') { if (pageFormat.width == 826) { pageFormat = mxRectangle.fromRectangle(pageFormat); pageFormat.width = 827; } else if (pageFormat.height == 826) { pageFormat = mxRectangle.fromRectangle(pageFormat); pageFormat.height = 827; } } else if (f.key == 'a5') { if (pageFormat.width == 584) { pageFormat = mxRectangle.fromRectangle(pageFormat); pageFormat.width = 583; } else if (pageFormat.height == 584) { pageFormat = mxRectangle.fromRectangle(pageFormat); pageFormat.height = 583; } } if (pageFormat.width == f.format.width && pageFormat.height == f.format.height) { paperSizeSelect.value = f.key; portraitCheckBox.setAttribute('checked', 'checked'); portraitCheckBox.defaultChecked = true; portraitCheckBox.checked = true; landscapeCheckBox.removeAttribute('checked'); landscapeCheckBox.defaultChecked = false; landscapeCheckBox.checked = false; detected = true; } else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width) { paperSizeSelect.value = f.key; portraitCheckBox.removeAttribute('checked'); portraitCheckBox.defaultChecked = false; portraitCheckBox.checked = false; landscapeCheckBox.setAttribute('checked', 'checked'); landscapeCheckBox.defaultChecked = true; landscapeCheckBox.checked = true; detected = true; } } } // Selects custom format which is last in list if (paperSizeSelect.value == 'custom') { widthInput.value = Editor.toUnit(pageFormat.width, unitSelect.value); heightInput.value = Editor.toUnit(pageFormat.height, unitSelect.value); portraitCheckBox.setAttribute('checked', 'checked'); paperSizeSelect.value = 'custom'; formatDiv.style.display = 'none'; customDiv.style.display = ''; } else { formatDiv.style.display = 'flex'; customDiv.style.display = 'none'; } }; listener(); div.appendChild(paperSizeSelect); mxUtils.br(div); div.appendChild(formatDiv); div.appendChild(customDiv); var update = function(evt, quiet) { var f = pf[paperSizeSelect.value]; if (f.format != null) { widthInput.value = Editor.toUnit((!landscapeCheckBox.checked) ? f.format.width : f.format.height, unitSelect.value); heightInput.value = Editor.toUnit((!landscapeCheckBox.checked) ? f.format.height : f.format.width, unitSelect.value); customDiv.style.display = 'none'; formatDiv.style.display = 'flex'; } else { formatDiv.style.display = 'none'; customDiv.style.display = ''; } var wi = parseFloat(widthInput.value); if (isNaN(wi) || wi <= 0) { widthInput.value = Editor.toUnit(pageFormat.width, unitSelect.value); } var hi = parseFloat(heightInput.value); if (isNaN(hi) || hi <= 0) { heightInput.value = Editor.toUnit(pageFormat.height, unitSelect.value); } // Uses the exact preset size so that selecting a standard format never // depends on the display precision of the current unit round-tripping var newPageFormat = (f.format != null) ? new mxRectangle(0, 0, (!landscapeCheckBox.checked) ? f.format.width : f.format.height, (!landscapeCheckBox.checked) ? f.format.height : f.format.width) : new mxRectangle(0, 0, Editor.fromUnit(parseFloat(widthInput.value), unitSelect.value), Editor.fromUnit(parseFloat(heightInput.value), unitSelect.value)); // Initial select of custom should not update page format to avoid update of combo if (!quiet && (newPageFormat.width != pageFormat.width || newPageFormat.height != pageFormat.height)) { pageFormat = newPageFormat; // Updates page format and reloads format panel if (pageFormatListener != null) { pageFormatListener(pageFormat); } } }; mxEvent.addListener(portraitSpan, 'click', function(evt) { portraitCheckBox.checked = true; update(evt); mxEvent.consume(evt); }); mxEvent.addListener(landscapeSpan, 'click', function(evt) { landscapeCheckBox.checked = true; update(evt); mxEvent.consume(evt); }); mxEvent.addListener(widthInput, 'blur', update); mxEvent.addListener(widthInput, 'click', update); mxEvent.addListener(heightInput, 'blur', update); mxEvent.addListener(heightInput, 'click', update); mxEvent.addListener(landscapeCheckBox, 'change', update); mxEvent.addListener(portraitCheckBox, 'change', update); mxEvent.addListener(paperSizeSelect, 'change', function(evt) { update(evt, paperSizeSelect.value == 'custom'); mxEvent.consume(evt); }); mxEvent.addListener(unitSelect, 'change', function(evt) { widthInput.value = Editor.toUnit(Editor.fromUnit(widthInput.value, Editor.pageSizeUnit), unitSelect.value); heightInput.value = Editor.toUnit(Editor.fromUnit(heightInput.value, Editor.pageSizeUnit), unitSelect.value); Editor.pageSizeUnit = unitSelect.value; update(evt, true); mxEvent.consume(evt); }); update(null, true); return {set: function(value) { pageFormat = value; listener(null, null, true); },get: function() { return pageFormat; }, widthInput: widthInput, heightInput: heightInput}; }; /** * */ PageSetupDialog.getFormats = function() { return [{key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT}, {key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400)}, {key: 'tabloid', title: 'US-Tabloid (11" x 17")', format: new mxRectangle(0, 0, 1100, 1700)}, {key: 'executive', title: 'US-Executive (7" x 10")', format: new mxRectangle(0, 0, 700, 1000)}, {key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681)}, {key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300)}, {key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336)}, {key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654)}, {key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT}, {key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827)}, {key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583)}, {key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413)}, {key: 'b4', title: 'B4 (250 mm x 353 mm)', format: new mxRectangle(0, 0, 980, 1390)}, {key: 'b5', title: 'B5 (176 mm x 250 mm)', format: new mxRectangle(0, 0, 690, 980)}, {key: '16-9', title: '16:9 (1600 x 900)', format: new mxRectangle(0, 0, 900, 1600)}, {key: '16-10', title: '16:10 (1920 x 1200)', format: new mxRectangle(0, 0, 1200, 1920)}, {key: '4-3', title: '4:3 (1600 x 1200)', format: new mxRectangle(0, 0, 1200, 1600)}, {key: 'custom', title: mxResources.get('custom'), format: null}]; }; /** * Constructs a new filename dialog. */ var FilenameDialog = function(editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn, hints) { closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true; var container = document.createElement('div'); var table = document.createElement('div'); table.style.width = '100%'; table.style.display = 'grid'; table.style.gap = '5px 8px'; table.style.gridAutoColumns = 'auto 1fr'; // Workaround for clipped focus rectangle in FF table.style.boxSizing = 'border-box'; table.style.padding = '3px'; var td = document.createElement('div'); td.style.display = 'inline-flex'; td.style.alignItems = 'center'; td.style.justifyContent = 'flex-end'; td.style.minWidth = '0'; // Wrapper for text ellipsis var wrapper = document.createElement('div'); wrapper.style.display = 'inline-block'; wrapper.style.textOverflow = 'ellipsis'; wrapper.style.whiteSpace = 'nowrap'; wrapper.style.overflow = 'hidden'; wrapper.style.fontSize = '10pt'; // Workaround for vertical clipping wrapper.style.padding = '2px 0'; wrapper.setAttribute('title', label || mxResources.get('filename')); mxUtils.write(wrapper, (label || mxResources.get('filename')) + ':'); td.appendChild(wrapper); table.appendChild(td); var nameInput = document.createElement('input'); nameInput.setAttribute('value', filename || ''); nameInput.style.flexGrow = '1'; var genericBtn = mxUtils.button(buttonText, function() { if (validateFn == null || validateFn(nameInput.value)) { if (closeOnBtn) { editorUi.hideDialog(); } fn(nameInput.value); } }); genericBtn.className = 'geBtn gePrimaryBtn'; this.init = function() { if (label == null && content != null) { return; } if (hints != null) { Editor.selectFilename(nameInput); } else { nameInput.focus(); if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5) { nameInput.select(); } else { document.execCommand('selectAll', false, null); } } // Installs drag and drop handler for links if (Graph.fileSupport) { // Setup the dnd listeners var dlg = table.parentNode; if (dlg != null) { var dropElt = null; mxEvent.addListener(dlg, 'dragleave', function(evt) { if (dropElt != null) { dropElt.style.backgroundColor = ''; dropElt = null; } evt.stopPropagation(); evt.preventDefault(); }); mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function(evt) { if (dropElt == null) { dropElt = nameInput; dropElt.style.backgroundColor = '#ebf2f9'; } evt.stopPropagation(); evt.preventDefault(); })); mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function(evt) { if (dropElt != null) { dropElt.style.backgroundColor = ''; dropElt = null; } if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) { nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list')); genericBtn.click(); } evt.stopPropagation(); evt.preventDefault(); })); } } }; td = document.createElement('div'); td.style.display = 'inline-flex'; td.style.alignItems = 'center'; td.style.gap = '4px'; td.style.whiteSpace = 'nowrap'; td.appendChild(nameInput); table.appendChild(td); if (label != null || content == null) { if (hints != null && editorUi.editor.diagramFileTypes != null) { td = document.createElement('div'); td.style.display = 'inline-flex'; td.style.alignItems = 'center'; td.style.textOverflow = 'ellipsis'; td.style.justifyContent = 'flex-end'; td.style.fontSize = '10pt'; mxUtils.write(td, mxResources.get('type') + ':'); table.appendChild(td); td = document.createElement('div'); td.style.display = 'inline-flex'; td.style.alignItems = 'center'; td.style.whiteSpace = 'nowrap'; table.appendChild(td); var typeSelect = FilenameDialog.createFileTypes(editorUi, nameInput, editorUi.editor.diagramFileTypes); typeSelect.style.width = '100%'; td.appendChild(typeSelect); } } if (content != null) { table.appendChild(content); } row = document.createElement('div'); row.style.gridColumn = '1 / span 2'; row.style.paddingTop = (hints != null) ? '6px' : '14px'; row.style.whiteSpace = 'nowrap'; row.style.display = 'inline-flex'; row.style.alignItems = 'center'; row.style.justifyContent = 'flex-end'; row.setAttribute('align', 'right'); var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() { editorUi.hideDialog(); if (cancelFn != null) { cancelFn(); } }); cancelBtn.className = 'geBtn'; if (helpLink != null && !editorUi.isOffline()) { row.appendChild(editorUi.createHelpIcon(helpLink)); } if (editorUi.editor.cancelFirst) { row.appendChild(cancelBtn); } mxEvent.addListener(nameInput, 'keypress', function(e) { if (e.keyCode == 13) { genericBtn.click(); } }); row.appendChild(genericBtn); if (!editorUi.editor.cancelFirst) { row.appendChild(cancelBtn); } table.appendChild(row); container.appendChild(table); this.container = container; }; /** * */ FilenameDialog.filenameHelpLink = null; /** * */ FilenameDialog.createFileTypes = function(editorUi, nameInput, types) { var typeSelect = document.createElement('select'); for (var i = 0; i < types.length; i++) { var typeOption = document.createElement('option'); typeOption.setAttribute('value', i); mxUtils.write(typeOption, mxResources.get(types[i].description) + ' (.' + types[i].extension + ')'); typeSelect.appendChild(typeOption); } mxEvent.addListener(typeSelect, 'change', function(evt) { var ext = types[typeSelect.value].extension; var idx2 = nameInput.value.lastIndexOf('.drawio.'); var idx = (idx2 > 0) ? idx2 : nameInput.value.lastIndexOf('.'); if (ext != 'drawio') { ext = 'drawio.' + ext; } if (idx > 0) { nameInput.value = nameInput.value.substring(0, idx + 1) + ext; } else { nameInput.value = nameInput.value + '.' + ext; } if ('createEvent' in document) { var changeEvent = document.createEvent('HTMLEvents'); changeEvent.initEvent('change', false, true); nameInput.dispatchEvent(changeEvent); } else { nameInput.fireEvent('onchange'); } }); var nameInputChanged = function(evt) { var name = nameInput.value.toLowerCase(); var active = 0; // Finds current extension for (var i = 0; i < types.length; i++) { var ext = types[i].extension; var subExt = null; if (ext != 'drawio') { subExt = ext; ext = '.drawio.' + ext; } if (name.substring(name.length - ext.length - 1) == '.' + ext || (subExt != null && name.substring(name.length - subExt.length - 1) == '.' + subExt)) { active = i; break; } } typeSelect.value = active; }; mxEvent.addListener(nameInput, 'change', nameInputChanged); mxEvent.addListener(nameInput, 'keyup', nameInputChanged); nameInputChanged(); // Applies default file type from configuration if (Editor.defaultFileType != null && typeSelect.value == '0') { for (var i = 0; i < types.length; i++) { if (types[i].extension == Editor.defaultFileType) { typeSelect.value = i; var ext = types[i].extension; var idx2 = nameInput.value.lastIndexOf('.drawio.'); var idx = (idx2 > 0) ? idx2 : nameInput.value.lastIndexOf('.'); if (ext != 'drawio') { ext = 'drawio.' + ext; } if (idx > 0) { nameInput.value = nameInput.value.substring(0, idx + 1) + ext; } else { nameInput.value = nameInput.value + '.' + ext; } break; } } } return typeSelect; }; /** * */ var WrapperWindow = function(editorUi, title, x, y, w, h, fn, div) { div = (div != null) ? div : document.createElement('div'); this.window = new mxWindow(title, div, x, y, w, h, true, true); this.window.destroyOnClose = false; this.window.setMaximizable(false); this.window.setResizable(true); this.window.setClosable(true); this.window.setVisible(true); editorUi.installResizeHandler(this, true); fn(div); }; /** * Static overrides */ (function() { // Uses HTML for background pages (to support grid background image) mxGraphView.prototype.validateBackgroundPage = function() { var graph = this.graph; if (graph.container != null && !graph.transparentBackground) { if (graph.pageVisible) { var bounds = this.getBackgroundPageBounds(); if (this.backgroundPageShape == null) { // Finds first element in graph container var firstChild = graph.container.firstChild; while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT) { firstChild = firstChild.nextSibling; } if (firstChild != null) { this.backgroundPageShape = this.createBackgroundPageShape(bounds); this.backgroundPageShape.scale = 1; // IE8 standards has known rendering issues inside mxWindow but not using shadow is worse. this.backgroundPageShape.isShadow = true; this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML; this.backgroundPageShape.init(graph.container); // Required for the browser to render the background page in correct order firstChild.style.position = 'absolute'; graph.container.insertBefore(this.backgroundPageShape.node, firstChild); this.backgroundPageShape.redraw(); this.backgroundPageShape.node.className = 'geBackgroundPage'; // Adds listener for double click handling on background mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', mxUtils.bind(this, function(evt) { graph.dblClick(evt); }) ); // Adds basic listeners for graph event dispatching outside of the // container and finishing the handling of a single gesture mxEvent.addGestureListeners(this.backgroundPageShape.node, mxUtils.bind(this, function(evt) { graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt)); }), mxUtils.bind(this, function(evt) { // Hides the tooltip if mouse is outside container if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) { graph.tooltipHandler.hide(); } if (graph.isMouseDown && !mxEvent.isConsumed(evt)) { graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt)); } }), mxUtils.bind(this, function(evt) { graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt)); }) ); } } else { this.backgroundPageShape.scale = 1; this.backgroundPageShape.bounds = bounds; this.backgroundPageShape.redraw(); } } else if (this.backgroundPageShape != null) { this.backgroundPageShape.destroy(); this.backgroundPageShape = null; } this.validateBackgroundStyles(); } }; // Updates the CSS of the background to draw the grid and background color mxGraphView.prototype.validateBackgroundStyles = function(factor, cx, cy) { var graph = this.graph; factor = (factor != null) ? factor : 1; var color = mxUtils.getLightDarkColor((graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background).cssText; var pageBorder = mxUtils.getLightDarkColor( graph.defaultPageBorderColor).cssText; var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff'; var image = 'none'; var position = ''; // Uses light custom background with simple adaptive colors if (graph.getAdaptiveColors() == 'simple' && graph.background != mxConstants.NONE && graph.background != null) { color = graph.background; } if (graph.isGridEnabled() || graph.gridVisible) { var phase = 10; if (mxClient.IS_SVG) { // Paints the grid image = unescape(encodeURIComponent(this.createSvgGrid(gridColor, factor))); image = (window.btoa) ? btoa(image) : Base64.encode(image, true); image = 'url(' + 'data:image/svg+xml;base64,' + image + ')' phase = graph.gridSize * this.scale * this.gridSteps * factor; } else { // Fallback to grid wallpaper with fixed size image = 'url(' + this.gridImage + ')'; } var x0 = 0; var y0 = 0; var dx = (cx != null) ? cx - this.translate.x * this.scale : 0; var dy = (cy != null) ? cy - this.translate.y * this.scale : 0; var p = graph.gridSize * this.scale * this.gridSteps; var ddx = dx % p; var ddy = dy % p; if (graph.view.backgroundPageShape != null) { var bds = this.getBackgroundPageBounds(); x0 = 1 + bds.x; y0 = 1 + bds.y; } // Computes the offset to maintain origin for grid position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0 + dx, phase) + ddx * factor) + 'px ' + -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0 + dy, phase) + ddy * factor) + 'px'; } var canvas = graph.view.canvas; if (canvas.ownerSVGElement != null) { canvas = canvas.ownerSVGElement; } var useDiagramBackground = !Editor.isDarkMode() && graph.enableDiagramBackground; if (graph.view.backgroundPageShape != null) { graph.view.backgroundPageShape.node.style.backgroundPosition = position; graph.view.backgroundPageShape.node.style.backgroundImage = image; graph.view.backgroundPageShape.node.style.backgroundColor = color; graph.view.backgroundPageShape.node.style.borderColor = pageBorder; graph.view.backgroundPageShape.node.style.colorScheme = (graph.getAdaptiveColors() == 'none') ? 'light' : ''; canvas.style.backgroundImage = 'none'; canvas.style.backgroundColor = ''; if (useDiagramBackground) { graph.container.style.backgroundColor = graph.diagramBackgroundColor; } else { graph.container.style.backgroundColor = ''; } } else { canvas.style.backgroundPosition = position; canvas.style.backgroundImage = image; if (urlParams['transparent'] == '1') { graph.container.style.background = 'transparent'; } else if (useDiagramBackground && (graph.background == null || graph.background == mxConstants.NONE)) { canvas.style.backgroundColor = graph.diagramBackgroundColor; graph.container.style.backgroundColor = ''; } else { canvas.style.backgroundColor = color; } } }; // Returns the SVG required for painting the background grid. mxGraphView.prototype.createSvgGrid = function(color, factor) { factor = (factor != null) ? factor : 1; var tmp = this.graph.gridSize * this.scale * factor; while (tmp < this.minGridSize) { tmp *= 2; } var tmp2 = this.gridSteps * tmp; // Small grid lines var d = []; for (var i = 1; i < this.gridSteps; i++) { var tmp3 = i * tmp; d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2); } // Major grid lines on all four tile edges: the pattern clips the outer half of a // stroke on the tile boundary, so adjacent tiles must supply the two halves var major = 'M ' + tmp2 + ' 0 L 0 0 0 ' + tmp2 + ' M ' + tmp2 + ' 0 L ' + tmp2 + ' ' + tmp2 + ' L 0 ' + tmp2; // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams. var size = tmp2; var cssColor = mxUtils.getLightDarkColor(color); var scheme = 'color-scheme: light dark;'; // Workaround for ignored parent scheme in Safari if (mxClient.IS_SF) { scheme = 'color-scheme: ' + (this.graph.getAdaptiveColors() != 'none' && Editor.isDarkMode() ? 'dark' : 'light'); } var svg = '<svg style="' + scheme + '" width="' + size + '" height="' + size + '" xmlns="' + mxConstants.NS_SVG + '">' + '<defs><pattern id="grid" width="' + tmp2 + '" height="' + tmp2 + '" patternUnits="userSpaceOnUse">' + '<path d="' + d.join(' ') + '" fill="none" style="stroke:' + mxUtils.htmlEntities(cssColor.cssText) + ';" stroke="' + cssColor.light + '" opacity="0.2" stroke-width="1"/>' + '<path d="' + major + '" fill="none" style="stroke:' + mxUtils.htmlEntities(cssColor.cssText) + ';" stroke="' + cssColor.light + '" stroke-width="1"/>' + '</pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg>'; return svg; }; // Adds panning for the grid with no page view and disabled scrollbars var mxGraphPanGraph = mxGraph.prototype.panGraph; mxGraph.prototype.panGraph = function(dx, dy) { mxGraphPanGraph.apply(this, arguments); if (this.shiftPreview1 != null) { var canvas = this.view.canvas; if (canvas.ownerSVGElement != null) { canvas = canvas.ownerSVGElement; } var phase = this.gridSize * this.view.scale * this.view.gridSteps; var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' + -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px'; canvas.style.backgroundPosition = position; } }; // Draws page breaks only within the visible area mxGraph.prototype.updatePageBreaks = function(visible, width, height) { var scale = this.view.scale; var tr = this.view.translate; var fmt = this.pageFormat; var ps = scale * this.pageScale; var bounds2 = this.view.getBackgroundPageBounds(); width = bounds2.width; height = bounds2.height; var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps); // Maps from canvas to screen coordinates using the DOM as the view // state is normalized in this call if CSS transforms are used var ctm = (this.view.canvas != null && this.view.canvas.getCTM != null) ? this.view.canvas.getCTM() : null; var cs = (ctm != null) ? ctm.a : 1; var cx = (ctm != null) ? ctm.e : 0; var cy = (ctm != null) ? ctm.f : 0; // Does not show page breaks if the scale is too small visible = visible && Math.min(bounds.width, bounds.height) * cs > this.minPageBreakDist; var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0; var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0; var right = bounds2.x + width; var bottom = bounds2.y + height; // Restricts the page breaks to the visible area plus one viewport of // margin in each direction so that extreme cell coordinates cannot // block the UI with an excessive number of nodes, and keeps track of // the covered area for updating the clipped page breaks after scrolling var hMin = 0; var hMax = horizontalCount - 1; var vMin = 0; var vMax = verticalCount - 1; this.pageBreakCoverage = null; if (visible && this.container != null) { var cw = this.container.clientWidth; var ch = this.container.clientHeight; var x0 = (this.container.scrollLeft - cw - cx) / cs - bounds2.x; var y0 = (this.container.scrollTop - ch - cy) / cs - bounds2.y; vMin = Math.max(0, Math.floor(x0 / bounds.width) - 1); vMax = Math.min(verticalCount - 1, Math.ceil((x0 + 3 * cw / cs) / bounds.width)); hMin = Math.max(0, Math.floor(y0 / bounds.height) - 1); hMax = Math.min(horizontalCount - 1, Math.ceil((y0 + 3 * ch / cs) / bounds.height)); if (hMin > 0 || hMax < horizontalCount - 1 || vMin > 0 || vMax < verticalCount - 1) { this.pageBreakCoverage = new mxRectangle(this.container.scrollLeft - cw / 2, this.container.scrollTop - ch / 2, 2 * cw, 2 * ch); } } if (this.horizontalPageBreaks == null && horizontalCount > 0) { this.horizontalPageBreaks = []; } if (this.verticalPageBreaks == null && verticalCount > 0) { this.verticalPageBreaks = []; } var drawPageBreaks = mxUtils.bind(this, function(breaks) { if (breaks != null) { var horizontal = breaks == this.horizontalPageBreaks; var first = (horizontal) ? hMin : vMin; var last = (horizontal) ? hMax : vMax; // Bounds the drawing loop as a fallback where the visible // area is unknown (eg. no container) var count = (visible && isFinite(first) && isFinite(last)) ? Math.max(0, Math.min(this.maxPageBreaks, last - first + 1)) : 0; for (var i = 0; i < count; i++) { var pos = first + i + 1; var pts = (horizontal) ? [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + pos * bounds.height)), new mxPoint(Math.round(right), Math.round(bounds2.y + pos * bounds.height))] : [new mxPoint(Math.round(bounds2.x + pos * bounds.width), Math.round(bounds2.y)), new mxPoint(Math.round(bounds2.x + pos * bounds.width), Math.round(bottom))]; if (breaks[i] != null && breaks[i].node != null) { breaks[i].points = pts; breaks[i].redraw(); } else { if (breaks[i] != null) { breaks[i].destroy(); } var pageBreak = new mxPolyline(pts, this.pageBreakColor); pageBreak.dialect = this.dialect; pageBreak.isDashed = this.pageBreakDashed; pageBreak.pointerEvents = false; pageBreak.init(this.view.backgroundPane); pageBreak.redraw(); breaks[i] = pageBreak; } } for (var i = count; i < breaks.length; i++) { if (breaks[i] != null) { breaks[i].destroy(); } } breaks.splice(count, breaks.length - count); } }); drawPageBreaks(this.horizontalPageBreaks); drawPageBreaks(this.verticalPageBreaks); }; // Disables removing relative children and table rows and cells from parents var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent; mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt) { for (var i = 0; i < cells.length; i++) { if (this.graph.isTableCell(cells[i]) || this.graph.isTableRow(cells[i])) { return false; } else if (this.graph.getModel().isVertex(cells[i])) { var geo = this.graph.getCellGeometry(cells[i]); if (geo != null && geo.relative) { return false; } } } return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments); }; // Overrides to ignore hotspot only for target terminal var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; mxConnectionHandler.prototype.createMarker = function() { var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); marker.intersects = mxUtils.bind(this, function(state, evt) { if (this.isConnecting()) { return true; } return mxCellMarker.prototype.intersects.apply(marker, arguments); }); return marker; }; // Creates background page shape mxGraphView.prototype.createBackgroundPageShape = function(bounds) { return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor); }; // Fits the number of background pages to the graph mxGraphView.prototype.getBackgroundPageBounds = function() { var gb = this.getGraphBounds(); // Computes unscaled, untranslated graph bounds var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0; var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0; var w = gb.width / this.scale; var h = gb.height / this.scale; var fmt = this.graph.pageFormat; var ps = this.graph.pageScale; var pw = fmt.width * ps; var ph = fmt.height * ps; var x0 = Math.floor(Math.min(0, x) / pw); var y0 = Math.floor(Math.min(0, y) / ph); var xe = Math.ceil(Math.max(1, x + w) / pw); var ye = Math.ceil(Math.max(1, y + h) / ph); var rows = xe - x0; var cols = ye - y0; var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale * (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph); return bounds; }; // Add panning for background page in VML var graphPanGraph = mxGraph.prototype.panGraph; mxGraph.prototype.panGraph = function(dx, dy) { graphPanGraph.apply(this, arguments); if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) && (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container))) { this.view.backgroundPageShape.node.style.marginLeft = dx + 'px'; this.view.backgroundPageShape.node.style.marginTop = dy + 'px'; } }; /** * Consumes click events for disabled menu items. */ var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem; mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled) { var result = mxPopupMenuAddItem.apply(this, arguments); if (enabled != null && !enabled) { mxEvent.addListener(result, 'mousedown', function(evt) { mxEvent.consume(evt); }); } return result; }; /** * Selects tables before cells and rows. */ var mxGraphHandlerIsPropagateSelectionCell = mxGraphHandler.prototype.isPropagateSelectionCell; mxGraphHandler.prototype.isPropagateSelectionCell = function(cell, immediate, me) { var result = false; var parent = this.graph.model.getParent(cell) if (immediate) { var geo = (this.graph.model.isEdge(cell)) ? null : this.graph.getCellGeometry(cell); result = !this.graph.model.isEdge(parent) && !this.graph.isSiblingSelected(cell) && ((geo != null && geo.relative) || !this.graph.isContainer(parent) || this.graph.isPart(cell)); } else { result = mxGraphHandlerIsPropagateSelectionCell.apply(this, arguments); if (this.graph.isTableCell(cell) || this.graph.isTableRow(cell)) { var table = parent; if (!this.graph.isTable(table)) { table = this.graph.model.getParent(table); } result = !this.graph.selectionCellsHandler.isHandled(table) || (this.graph.isCellSelected(table) && this.graph.isToggleEvent(me.getEvent())) || (this.graph.isCellSelected(cell) && !this.graph.isToggleEvent(me.getEvent())) || (this.graph.isTableCell(cell) && this.graph.isCellSelected(parent)); } } return result; }; /** * Returns last selected ancestor */ mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me) { var cell = me.getCell(); var model = this.graph.getModel(); var parent = model.getParent(cell); var state = this.graph.view.getState(parent); var selected = this.graph.isCellSelected(cell); while (state != null && (model.isVertex(parent) || model.isEdge(parent))) { var temp = this.graph.isCellSelected(parent); selected = selected || temp; if (temp || (!selected && (this.graph.isTableCell(cell) || this.graph.isTableRow(cell)))) { cell = parent; } parent = model.getParent(parent); } return cell; }; })();