diff --git a/war/ElectronApp.js b/war/ElectronApp.js deleted file mode 100644 index bd902c98..00000000 --- a/war/ElectronApp.js +++ /dev/null @@ -1,735 +0,0 @@ -window.OPEN_URL = 'https://www.draw.io/open'; -window.TEMPLATE_PATH = 'templates'; -FeedbackDialog.feedbackUrl = 'https://log.draw.io/email'; - -(function() -{ - // Overrides default mode - App.mode = App.MODE_DEVICE; - - // Disables new window option in edit diagram dialog - EditDiagramDialog.showNewWindowOption = false; - - // Redirects printing to iframe to avoid document.write - var printDialogCreatePrintPreview = PrintDialog.createPrintPreview; - - PrintDialog.createPrintPreview = function() - { - var iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - - var result = printDialogCreatePrintPreview.apply(this, arguments); - result.wnd = iframe.contentWindow; - result.iframe = iframe; - - // Workaround for lost gradients in print output - result.previousGetBaseUrl = mxSvgCanvas2D.prototype.getBaseUrl; - - mxSvgCanvas2D.prototype.getBaseUrl = function() - { - return ''; - }; - - return result; - }; - - var oldWindowOpen = window.open; - window.open = function(url) - { - if (url != null && url.startsWith('http')) - { - const {shell} = require('electron'); - shell.openExternal(url); - } - else - { - return oldWindowOpen(url); - } - } - - mxPrintPreview.prototype.addPageBreak = function(doc) - { - // Do nothing - }; - - mxPrintPreview.prototype.closeDocument = function() - { - var doc = this.wnd.document; - - // Removes all event handlers in the print output - mxEvent.release(doc.body); - }; - - PrintDialog.printPreview = function(preview) - { - if (preview.iframe != null) - { - preview.iframe.contentWindow.print(); - preview.iframe.parentNode.removeChild(preview.iframe); - - mxSvgCanvas2D.prototype.getBaseUrl = preview.previousGetBaseUrl; - preview.iframe = null; - } - }; - - PrintDialog.previewEnabled = false; - - // Enables PDF export via print - EditorUi.prototype.printPdfExport = true; - - var menusInit = Menus.prototype.init; - Menus.prototype.init = function() - { - menusInit.apply(this, arguments); - - var editorUi = this.editorUi; - - // Replaces file menu to replace openFrom menu with open and rename downloadAs to export - this.put('file', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import'], parent); - this.addSubmenu('exportAs', menu, parent); - this.addSubmenu('embed', menu, parent); - this.addMenuItems(menu, ['-', 'newLibrary', 'openLibrary', '-', 'documentProperties', 'print'], parent); - }))); - - this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'mathematicalTypesetting', 'autosave', '-', - 'createShape', 'editDiagram', '-', 'tags', '-', 'online'], parent); - }))); - }; - - // Initializes the user interface - var editorUiInit = EditorUi.prototype.init; - EditorUi.prototype.init = function() - { - editorUiInit.apply(this, arguments); - - var editorUi = this; - var graph = this.editor.graph; - this.editor.autosave = false; - - global.__emt_isModified = e => { - if (this.getCurrentFile()) - return this.getCurrentFile().isModified() - return false - } - // global.__emt_getCurrentFile = e => { - // return this.getCurrentFile() - // } - - // Adds support for libraries - this.actions.addAction('newLibrary...', mxUtils.bind(this, function() - { - editorUi.showLibraryDialog(null, null, null, null, App.MODE_DEVICE); - })); - - this.actions.addAction('openLibrary...', mxUtils.bind(this, function() - { - editorUi.pickLibrary(App.MODE_DEVICE); - })); - - // Replaces import action - this.actions.addAction('import...', mxUtils.bind(this, function() - { - if (editorUi.getCurrentFile() != null) - { - const electron = require('electron'); - var remote = electron.remote; - var dialog = remote.dialog; - - var paths = dialog.showOpenDialog({properties: ['openFile']}); - - if (paths !== undefined && paths[0] != null) - { - var fs = require('fs'); - var path = paths[0]; - var index = path.lastIndexOf('.png'); - var isPng = index > -1 && index == path.length - 4; - var encoding = (isPng || /\.gif$/i.test(path) || /\.jpe?g$/i.test(path) || - /\.vsdx$/i.test(path)) ? 'base64' : 'utf-8' - - if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) - { - fs.readFile(path, encoding, mxUtils.bind(this, function (e, data) - { - if (e) - { - editorUi.spinner.stop(); - editorUi.handleError(e); - } - else - { - try - { - if (isPng) - { - var tmp = editorUi.extractGraphModelFromPng(data); - - if (tmp != null) - { - data = tmp; - } - } - - if (!editorUi.isOffline() && new XMLHttpRequest().upload && editorUi.isRemoteFileFormat(data, path)) - { - // Asynchronous parsing via server - editorUi.parseFile(editorUi.base64ToBlob(data, 'application/octet-stream'), mxUtils.bind(this, function(xhr) - { - if (xhr.readyState == 4) - { - editorUi.spinner.stop(); - - if (xhr.status >= 200 && xhr.status <= 299) - { - - editorUi.editor.graph.setSelectionCells(editorUi.insertTextAt(xhr.responseText, 0, 0, true)); - } - } - }), path); - } - else if (isPng || /\.gif$/i.test(path) || /\.jpe?g$/i.test(path)) - { - var img = new Image(); - img.onload = function() - { - editorUi.resizeImage(img, img.src, function(data2, w, h) - { - editorUi.spinner.stop(); - var pt = graph.getInsertPoint(); - graph.setSelectionCell(graph.insertVertex(null, null, '', pt.x, pt.y, w, h, - 'shape=image;aspect=fixed;image=' + editorUi.convertDataUri(data2) + ';')); - }, true); - }; - - img.src = 'data:image/png;base64,' + data; - } - else if (data != null) - { - editorUi.spinner.stop(); - graph.setSelectionCells(editorUi.importXml(data)); - } - } - catch(e) - { - editorUi.spinner.stop(); - editorUi.handleError(e); - } - } - })); - } - } - } - })); - - // Replaces new action - var oldNew = this.actions.get('new').funct; - - this.actions.addAction('new...', mxUtils.bind(this, function() - { - mxLog.debug(this.getCurrentFile()); - - if (this.getCurrentFile() == null) - { - oldNew(); - } - else { - const ipc = require('electron').ipcRenderer - ipc.sendSync('winman', {action: 'newfile', opt: {width: 1600}}) - - } - }), null, null, 'Ctrl+N'); - - this.actions.get('open').shortcut = 'Ctrl+O'; - - // Adds shortcut keys for file operations - editorUi.keyHandler.bindAction(78, true, 'new'); // Ctrl+N - editorUi.keyHandler.bindAction(79, true, 'open'); // Ctrl+O - - editorUi.actions.addAction('keyboardShortcuts...', function() - { - const electron = require('electron'); - const remote = electron.remote; - const BrowserWindow = remote.BrowserWindow; - keyboardWindow = new BrowserWindow({width: 1200, height: 1000}); - - // and load the index.html of the app. - keyboardWindow.loadURL(`file://${__dirname}/shortcuts.svg`); - - // Emitted when the window is closed. - keyboardWindow.on('closed', function() - { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - keyboardWindow = null; - }); - }); - } - - // Uses local picker - App.prototype.pickFile = function() - { - var doPickFile = mxUtils.bind(this, function() - { - this.chooseFileEntry(mxUtils.bind(this, function(fileEntry, data) - { - var file = new LocalFile(this, data, ''); - file.fileObject = fileEntry; - this.fileLoaded(file); - })); - }); - - var file = this.getCurrentFile(); - - if (file != null && file.isModified()) - { - this.confirm(mxResources.get('allChangesLost'), null, doPickFile, - mxResources.get('cancel'), mxResources.get('discardChanges')); - } - else - { - doPickFile(); - } - }; - - /** - * Selects a library to load from a picker - * - * @param mode the device mode, ignored in this case - */ - App.prototype.pickLibrary = function(mode) - { - this.chooseFileEntry(mxUtils.bind(this, function(fileEntry, data) - { - try - { - var library = new LocalLibrary(this, data, fileEntry.name); - library.fileObject = fileEntry; - this.loadLibrary(library); - } - catch (e) - { - this.handleError(e, mxResources.get('errorLoadingFile')); - } - })); - }; - - // Uses local picker - App.prototype.chooseFileEntry = function(fn) - { - const electron = require('electron'); - var remote = electron.remote; - var dialog = remote.dialog; - - var paths = dialog.showOpenDialog({properties: ['openFile']}); - - if (paths !== undefined && paths[0] != null) - { - var fs = require('fs'); - var path = paths[0]; - var index = path.lastIndexOf('.png'); - var isPng = index > -1 && index == path.length - 4; - var encoding = isPng ? 'base64' : 'utf-8' - - fs.readFile(path, encoding, mxUtils.bind(this, function (e, data) - { - if (e) - { - this.handleError(e); - } - else - { - if (isPng) - { - // Detecting png by extension. Would need https://github.com/mscdex/mmmagic - // to do it by inspection - data = this.extractGraphModelFromPng(data, true); - } - - var fileEntry = new Object(); - fileEntry.path = path; - fileEntry.name = path.replace(/^.*[\\\/]/, ''); - fileEntry.type = encoding; - fn(fileEntry, data); - } - })); - } - }; - - // Disables temp files in Electron - var LocalFileCtor = LocalFile; - - LocalFile = function(ui, data, title, temp) - { - LocalFileCtor.call(this, ui, data, title, false); - }; - - mxUtils.extend(LocalFile, LocalFileCtor); - - LocalFile.prototype.isAutosave = function() - { - return this.ui.editor.autosave && this.fileObject != null; - }; - - LocalFile.prototype.isAutosaveOptional = function() - { - return true; - }; - - LocalLibrary.prototype.isAutosave = function() - { - return this.fileObject != null; - }; - - LocalFile.prototype.getTitle = function() - { - return (this.fileObject != null) ? this.fileObject.name : this.title; - }; - - LocalFile.prototype.isRenamable = function() - { - return false; - }; - - // Restores default implementation of open with autosave - LocalFile.prototype.open = DrawioFile.prototype.open; - - LocalFile.prototype.save = function(revision, success, error) - { - DrawioFile.prototype.save.apply(this, arguments); - - this.saveFile(revision, success, error); - }; - - LocalFile.prototype.saveFile = function(revision, success, error) - { - var fn = mxUtils.bind(this, function() - { - var doSave = mxUtils.bind(this, function(data, enc) - { - if (!this.savingFile) - { - this.savingFile = true; - - // Makes sure no changes get lost while the file is saved - var prevModified = this.isModified; - var modified = this.isModified(); - this.setModified(false); - var fs = require('fs'); - - fs.writeFile(this.fileObject.path, data, enc || this.fileObject.encoding, mxUtils.bind(this, function (e) - { - if (e) - { - this.savingFile = false; - this.isModified = prevModified; - this.setModified(modified || this.isModified()); - - if (error != null) - { - error(); - } - } - else - { - this.savingFile = false; - this.isModified = prevModified; - this.contentChanged(); - this.lastData = data; - - if (success != null) - { - success(); - } - } - })); - } - else - { - // TODO, already saving. Need a better error - if (error != null) - { - error(); - } - } - }); - - if (!/(\.png)$/i.test(this.fileObject.name)) - { - doSave(this.getData()); - } - else - { - var graph = this.ui.editor.graph; - - // Exports PNG for first page while other page is visible by creating a graph - // LATER: Add caching for the graph or SVG while not on first page - if (this.ui.pages != null && this.ui.currentPage != this.ui.pages[0]) - { - graph = this.ui.createTemporaryGraph(graph.getStylesheet()); - var graphGetGlobalVariable = graph.getGlobalVariable; - var page = this.ui.pages[0]; - - graph.getGlobalVariable = function(name) - { - if (name == 'page') - { - return page.getName(); - } - else if (name == 'pagenumber') - { - return 1; - } - - return graphGetGlobalVariable.apply(this, arguments); - }; - - document.body.appendChild(graph.container); - graph.model.setRoot(page.root); - } - - this.ui.exportToCanvas(mxUtils.bind(this, function(canvas) - { - try - { - var data = canvas.toDataURL('image/png'); - data = this.ui.writeGraphModelToPng(data, 'zTXt', 'mxGraphModel', - atob(this.ui.editor.graph.compress(this.ui.getFileData(true)))); - doSave(atob(data.substring(data.lastIndexOf(',') + 1)), 'binary'); - - // Removes temporary graph from DOM - if (graph != this.ui.editor.graph) - { - graph.container.parentNode.removeChild(graph.container); - } - } - catch (e) - { - if (error != null) - { - error(e); - } - } - }), null, null, null, mxUtils.bind(this, function(e) - { - if (error != null) - { - error(e); - } - }), null, null, null, null, null, null, graph); - } - }); - - if (this.fileObject == null) - { - const electron = require('electron'); - var remote = electron.remote; - var dialog = remote.dialog; - - var path = dialog.showSaveDialog({defaultPath: this.title}); - - if (path != null) - { - this.fileObject = new Object(); - this.fileObject.path = path; - this.fileObject.name = path.replace(/^.*[\\\/]/, ''); - this.fileObject.type = 'utf-8'; - fn(); - } - else if (error != null) - { - error(); - } - } - else - { - fn(); - } - }; - - LocalLibrary.prototype.save = function(revision, success, error) - { - LocalFile.prototype.saveFile.apply(this, arguments); - }; - - LocalFile.prototype.saveAs = function(title, success, error) - { - const electron = require('electron'); - var remote = electron.remote; - var dialog = remote.dialog; - var filename = this.title; - - // Adds default extension - if (filename.length > 0 && (!/(\.xml)$/i.test(filename) && !/(\.html)$/i.test(filename) && - !/(\.svg)$/i.test(filename) && !/(\.png)$/i.test(filename))) - { - filename += '.xml'; - } - - var path = dialog.showSaveDialog({defaultPath: filename}); - - if (path != null) - { - this.fileObject = new Object(); - this.fileObject.path = path; - this.fileObject.name = path.replace(/^.*[\\\/]/, ''); - this.fileObject.type = 'utf-8'; - this.save(false, success, error); - } - else if (error != null) - { - error(); - } - }; - - App.prototype.saveFile = function(forceDialog) - { - var file = this.getCurrentFile(); - - if (file != null) - { - if (!forceDialog && file.getTitle() != null) - { - file.save(true, mxUtils.bind(this, function(resp) - { - this.spinner.stop(); - this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('allChangesSaved'))); - }), mxUtils.bind(this, function(resp) - { - this.editor.setStatus(''); - this.handleError(resp, (resp != null) ? mxResources.get('errorSavingFile') : null); - })); - } - else - { - file.saveAs(null, mxUtils.bind(this, function(resp) - { - this.spinner.stop(); - this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('allChangesSaved'))); - }), mxUtils.bind(this, function(resp) - { - this.editor.setStatus(''); - this.handleError(resp, (resp != null) ? mxResources.get('errorSavingFile') : null); - })); - } - } - }; - - /** - * Translates this point by the given vector. - * - * @param {number} dx X-coordinate of the translation. - * @param {number} dy Y-coordinate of the translation. - */ - App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload, fn) - { - mode = (mode != null) ? mode : this.mode; - noSpin = (noSpin != null) ? noSpin : false; - noReload = (noReload != null) ? noReload : false; - var xml = this.createLibraryDataFromImages(images); - - var error = mxUtils.bind(this, function(resp) - { - this.spinner.stop(); - - if (fn != null) - { - fn(); - } - - // Null means cancel by user and is ignored - if (resp != null) - { - this.handleError(resp, mxResources.get('errorSavingFile')); - } - }); - - // Handles special case for local libraries - if (file == null) - { - file = new LocalLibrary(this, xml, name); - } - - if (noSpin || this.spinner.spin(document.body, mxResources.get('saving'))) - { - file.setData(xml); - - var doSave = mxUtils.bind(this, function() - { - file.save(true, mxUtils.bind(this, function(resp) - { - this.spinner.stop(); - this.hideDialog(true); - - if (!noReload) - { - this.libraryLoaded(file, images) - } - - if (fn != null) - { - fn(); - } - }), error); - }); - - if (name != file.getTitle()) - { - var oldHash = file.getHash(); - - file.rename(name, mxUtils.bind(this, function(resp) - { - // Change hash in stored settings - if (file.constructor != LocalLibrary && oldHash != file.getHash()) - { - mxSettings.removeCustomLibrary(oldHash); - mxSettings.addCustomLibrary(file.getHash()); - } - - // Workaround for library files changing hash so - // the old library cannot be removed from the - // sidebar using the updated file in libraryLoaded - this.removeLibrarySidebar(oldHash); - - doSave(); - }), error) - } - else - { - doSave(); - } - } - }; - - EditorUi.prototype.saveData = function(filename, format, data, mimeType, base64Encoded) - { - const electron = require('electron'); - var remote = electron.remote; - var dialog = remote.dialog; - - var path = dialog.showSaveDialog({defaultPath: filename}); - - if (path != null) - { - this.fileObject = new Object(); - this.fileObject.path = path; - this.fileObject.name = path.replace(/^.*[\\\/]/, ''); - var isImage = mimeType != null && mimeType.startsWith('image'); - this.fileObject.type = base64Encoded ? 'base64' : 'utf-8'; - var fs = require('fs'); - - fs.writeFile(this.fileObject.path, data, this.fileObject.type, mxUtils.bind(this, function (e) - { - if (e) - { - this.handleError({message: mxResources.get('errorSavingFile')}); - } - })); - } - }; - - EditorUi.prototype.addBeforeUnloadListener = function() {}; -})(); diff --git a/war/package.json b/war/package.json index 42f131c3..130b9f73 100644 --- a/war/package.json +++ b/war/package.json @@ -24,6 +24,10 @@ "url": "https://github.com/jgraph/drawio-desktop/issues" }, "homepage": "https://github.com/jgraph/draw.io", + "dependencies": { + "electron-log": "^2.2.6", + "electron-updater": "^1.16.0" + }, "devDependencies": { "electron": "^1.7.2", "electron-builder": "^18.1.0"