123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- '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, opts) {
- webContents(win).on('context-menu', (e, props) => {
- if (typeof opts.shouldShowMenu === 'function' && opts.shouldShowMenu(e, props) === false) {
- return;
- }
- const editFlags = props.editFlags;
- 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() {
- download(win, props.srcURL);
- }
- }, {
- type: 'separator'
- }];
- }
- if (props.linkURL && props.mediaType === 'none') {
- menuTpl = [{
- type: 'separator'
- }, {
- id: 'copyLink',
- label: 'Copy Link',
- click() {
- if (process.platform === 'darwin') {
- electron.clipboard.writeBookmark(props.linkText, props.linkURL);
- } else {
- electron.clipboard.writeText(props.linkURL);
- }
- }
- }, {
- type: 'separator'
- }];
- }
- if (opts.prepend) {
- const result = opts.prepend(props, win);
- if (Array.isArray(result)) {
- menuTpl.unshift(...result);
- }
- }
- if (opts.append) {
- const result = opts.append(props, win);
- if (Array.isArray(result)) {
- menuTpl.push(...result);
- }
- }
- if (opts.showInspectElement || (opts.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 (opts.labels) {
- for (const menuItem of menuTpl) {
- if (opts.labels[menuItem.id]) {
- menuItem.label = opts.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, arr) => {
- const toDelete = el.type === 'separator' && (!notDeletedPrevEl || i === arr.length - 1 || arr[i + 1].type === 'separator');
- notDeletedPrevEl = toDelete ? notDeletedPrevEl : el;
- return !toDelete;
- });
- }
- module.exports = (opts = {}) => {
- if (opts.window) {
- const win = opts.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, opts);
- }, {once: true});
- return;
- }
- return create(win, opts);
- }
- (electron.BrowserWindow || electron.remote.BrowserWindow).getAllWindows().forEach(win => {
- create(win, opts);
- });
- (electron.app || electron.remote.app).on('browser-window-created', (e, win) => {
- create(win, opts);
- });
- };
|