drawio/war/js/diagramly/ElectronApp.js
David Benson 3f96045ead Updated electron paths
Former-commit-id: 022cc17f50
2017-06-07 17:10:08 +01:00

735 lines
19 KiB
JavaScript

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() {};
})();