summary refs log tree commit diff
path: root/resources/app/node_modules/electron-context-menu/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/app/node_modules/electron-context-menu/index.js')
-rw-r--r--resources/app/node_modules/electron-context-menu/index.js198
1 files changed, 198 insertions, 0 deletions
diff --git a/resources/app/node_modules/electron-context-menu/index.js b/resources/app/node_modules/electron-context-menu/index.js
new file mode 100644
index 0000000..2519347
--- /dev/null
+++ b/resources/app/node_modules/electron-context-menu/index.js
@@ -0,0 +1,198 @@
+'use strict';
+const electron = require('electron');
+const {download} = require('electron-dl');
+const isDev = require('electron-is-dev');
+
+const webContents = win => win.webContents || win.getWebContents();
+
+function create(win, options) {
+	webContents(win).on('context-menu', (event, props) => {
+		if (typeof options.shouldShowMenu === 'function' && options.shouldShowMenu(event, props) === false) {
+			return;
+		}
+
+		const {editFlags} = props;
+		const hasText = props.selectionText.trim().length > 0;
+		const can = type => editFlags[`can${type}`] && hasText;
+
+		let menuTpl = [{
+			type: 'separator'
+		}, {
+			id: 'cut',
+			label: 'Cut',
+			// Needed because of macOS limitation:
+			// https://github.com/electron/electron/issues/5860
+			role: can('Cut') ? 'cut' : '',
+			enabled: can('Cut'),
+			visible: props.isEditable
+		}, {
+			id: 'copy',
+			label: 'Copy',
+			role: can('Copy') ? 'copy' : '',
+			enabled: can('Copy'),
+			visible: props.isEditable || hasText
+		}, {
+			id: 'paste',
+			label: 'Paste',
+			role: editFlags.canPaste ? 'paste' : '',
+			enabled: editFlags.canPaste,
+			visible: props.isEditable
+		}, {
+			type: 'separator'
+		}];
+
+		if (props.mediaType === 'image') {
+			menuTpl = [{
+				type: 'separator'
+			}, {
+				id: 'save',
+				label: 'Save Image',
+				click(item, win) {
+					download(win, props.srcURL);
+				}
+			}];
+
+			if (options.showSaveImageAs) {
+				menuTpl.push({
+					id: 'saveImageAs',
+					label: 'Save Image As…',
+					click(item, win) {
+						download(win, props.srcURL, {saveAs: true});
+					}
+				});
+			}
+
+			menuTpl.push({
+				type: 'separator'
+			});
+		}
+
+		if (props.linkURL && props.mediaType === 'none') {
+			menuTpl = [{
+				type: 'separator'
+			}, {
+				id: 'copyLink',
+				label: 'Copy Link',
+				click() {
+					electron.clipboard.write({
+						bookmark: props.linkText,
+						text: props.linkURL
+					});
+				}
+			}, {
+				type: 'separator'
+			}];
+		}
+
+		if (options.showCopyImageAddress && props.mediaType === 'image') {
+			menuTpl.push({
+				type: 'separator'
+			}, {
+				id: 'copyImageAddress',
+				label: 'Copy Image Address',
+				click() {
+					electron.clipboard.write({
+						bookmark: props.srcURL,
+						text: props.srcURL
+					});
+				}
+			}, {
+				type: 'separator'
+			});
+		}
+
+		if (options.prepend) {
+			const result = options.prepend(props, win);
+
+			if (Array.isArray(result)) {
+				menuTpl.unshift(...result);
+			}
+		}
+
+		if (options.append) {
+			const result = options.append(props, win);
+
+			if (Array.isArray(result)) {
+				menuTpl.push(...result);
+			}
+		}
+
+		if (options.showInspectElement || (options.showInspectElement !== false && isDev)) {
+			menuTpl.push({
+				type: 'separator'
+			}, {
+				id: 'inspect',
+				label: 'Inspect Element',
+				click() {
+					win.inspectElement(props.x, props.y);
+
+					if (webContents(win).isDevToolsOpened()) {
+						webContents(win).devToolsWebContents.focus();
+					}
+				}
+			}, {
+				type: 'separator'
+			});
+		}
+
+		// Apply custom labels for default menu items
+		if (options.labels) {
+			for (const menuItem of menuTpl) {
+				if (options.labels[menuItem.id]) {
+					menuItem.label = options.labels[menuItem.id];
+				}
+			}
+		}
+
+		// Filter out leading/trailing separators
+		// TODO: https://github.com/electron/electron/issues/5869
+		menuTpl = delUnusedElements(menuTpl);
+
+		if (menuTpl.length > 0) {
+			const menu = (electron.remote ? electron.remote.Menu : electron.Menu).buildFromTemplate(menuTpl);
+
+			/*
+			 * When electron.remote is not available this runs in the browser process.
+			 * We can safely use win in this case as it refers to the window the
+			 * context-menu should open in.
+			 * When this is being called from a webView, we can't use win as this
+			 * would refere to the webView which is not allowed to render a popup menu.
+			 */
+			menu.popup(electron.remote ? electron.remote.getCurrentWindow() : win);
+		}
+	});
+}
+
+function delUnusedElements(menuTpl) {
+	let notDeletedPrevEl;
+	return menuTpl.filter(el => el.visible !== false).filter((el, i, array) => {
+		const toDelete = el.type === 'separator' && (!notDeletedPrevEl || i === array.length - 1 || array[i + 1].type === 'separator');
+		notDeletedPrevEl = toDelete ? notDeletedPrevEl : el;
+		return !toDelete;
+	});
+}
+
+module.exports = (options = {}) => {
+	if (options.window) {
+		const win = options.window;
+		const wc = webContents(win);
+
+		// When window is a webview that has not yet finished loading webContents is not available
+		if (wc === undefined) {
+			win.addEventListener('dom-ready', () => {
+				create(win, options);
+			}, {once: true});
+			return;
+		}
+
+		return create(win, options);
+	}
+
+	for (const win of (electron.BrowserWindow || electron.remote.BrowserWindow).getAllWindows()) {
+		create(win, options);
+	}
+
+	(electron.app || electron.remote.app).on('browser-window-created', (event, win) => {
+		create(win, options);
+	});
+};