aboutsummaryrefslogtreecommitdiffstats
path: root/src/webview/contextMenuBuilder.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/webview/contextMenuBuilder.js')
-rw-r--r--src/webview/contextMenuBuilder.js248
1 files changed, 148 insertions, 100 deletions
diff --git a/src/webview/contextMenuBuilder.js b/src/webview/contextMenuBuilder.js
index 8c39e6d04..eb15aa138 100644
--- a/src/webview/contextMenuBuilder.js
+++ b/src/webview/contextMenuBuilder.js
@@ -6,7 +6,8 @@
6 * 6 *
7 * Source: https://github.com/electron-userland/electron-spellchecker/blob/master/src/context-menu-builder.js 7 * Source: https://github.com/electron-userland/electron-spellchecker/blob/master/src/context-menu-builder.js
8 */ 8 */
9import { clipboard, ipcRenderer, nativeImage } from 'electron'; 9// eslint-disable-next-line no-unused-vars
10import { clipboard, ipcRenderer, nativeImage, WebContents } from 'electron';
10import { Menu, MenuItem } from '@electron/remote'; 11import { Menu, MenuItem } from '@electron/remote';
11import { cmdOrCtrlShortcutKey, isMac } from '../environment'; 12import { cmdOrCtrlShortcutKey, isMac } from '../environment';
12 13
@@ -16,7 +17,8 @@ import { openExternalUrl } from '../helpers/url-helpers';
16const { URL } = require('url'); 17const { URL } = require('url');
17 18
18function matchesWord(string) { 19function matchesWord(string) {
19 const regex = /[\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g; 20 const regex =
21 /[A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g;
20 22
21 return string.match(regex); 23 return string.match(regex);
22} 24}
@@ -56,12 +58,12 @@ module.exports = class ContextMenuBuilder {
56 /** 58 /**
57 * Creates an instance of ContextMenuBuilder 59 * Creates an instance of ContextMenuBuilder
58 * 60 *
59 * @param {webContents} webContents Current webContents 61 * @param {WebContents} webContents Current webContents
60 * @param {Boolean} debugMode If true, display the "Inspect Element" menu item. 62 * @param {Boolean} debugMode If true, display the "Inspect Element" menu item.
61 * @param {function} processMenu If passed, this method will be passed the menu to change 63 * @param {function} processMenu If passed, this method will be passed the menu to change
62 * it prior to display. Signature: (menu, info) => menu 64 * it prior to display. Signature: (menu, info) => menu
63 */ 65 */
64 constructor(webContents, debugMode = false, processMenu = (m) => m) { 66 constructor(webContents, debugMode = false, processMenu = m => m) {
65 this.debugMode = debugMode; 67 this.debugMode = debugMode;
66 this.processMenu = processMenu; 68 this.processMenu = processMenu;
67 this.menu = null; 69 this.menu = null;
@@ -115,7 +117,10 @@ module.exports = class ContextMenuBuilder {
115 return this.buildMenuForImage(info); 117 return this.buildMenuForImage(info);
116 } 118 }
117 119
118 if (info.isEditable || (info.inputFieldType && info.inputFieldType !== 'none')) { 120 if (
121 info.isEditable ||
122 (info.inputFieldType && info.inputFieldType !== 'none')
123 ) {
119 return this.buildMenuForTextInput(info); 124 return this.buildMenuForTextInput(info);
120 } 125 }
121 126
@@ -157,12 +162,17 @@ module.exports = class ContextMenuBuilder {
157 const isEmailAddress = menuInfo.linkURL.startsWith('mailto:'); 162 const isEmailAddress = menuInfo.linkURL.startsWith('mailto:');
158 163
159 const copyLink = new MenuItem({ 164 const copyLink = new MenuItem({
160 label: isEmailAddress ? this.stringTable.copyMail() : this.stringTable.copyLinkUrl(), 165 label: isEmailAddress
166 ? this.stringTable.copyMail()
167 : this.stringTable.copyLinkUrl(),
161 click: () => { 168 click: () => {
162 // Omit the mailto: portion of the link; we just want the address 169 // Omit the mailto: portion of the link; we just want the address
163 const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL; 170 const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL;
164 clipboard.writeText(url); 171 clipboard.writeText(url);
165 this._sendNotificationOnClipboardEvent(menuInfo.clipboardNotifications, () => `Link URL copied: ${url}`); 172 this._sendNotificationOnClipboardEvent(
173 menuInfo.clipboardNotifications,
174 () => `Link URL copied: ${url}`,
175 );
166 }, 176 },
167 }); 177 });
168 178
@@ -250,11 +260,13 @@ module.exports = class ContextMenuBuilder {
250 const webContents = this.getWebContents(); 260 const webContents = this.getWebContents();
251 // Add each spelling suggestion 261 // Add each spelling suggestion
252 for (const suggestion of menuInfo.dictionarySuggestions) { 262 for (const suggestion of menuInfo.dictionarySuggestions) {
253 menu.append(new MenuItem({ 263 menu.append(
254 label: suggestion, 264 new MenuItem({
255 // eslint-disable-next-line no-loop-func 265 label: suggestion,
256 click: () => webContents.replaceMisspelling(suggestion), 266 // eslint-disable-next-line no-loop-func
257 })); 267 click: () => webContents.replaceMisspelling(suggestion),
268 }),
269 );
258 } 270 }
259 271
260 // Allow users to add the misspelled word to the dictionary 272 // Allow users to add the misspelled word to the dictionary
@@ -262,7 +274,10 @@ module.exports = class ContextMenuBuilder {
262 menu.append( 274 menu.append(
263 new MenuItem({ 275 new MenuItem({
264 label: this.stringTable.addToDictionary(), 276 label: this.stringTable.addToDictionary(),
265 click: () => webContents.session.addWordToSpellCheckerDictionary(menuInfo.misspelledWord), 277 click: () =>
278 webContents.session.addWordToSpellCheckerDictionary(
279 menuInfo.misspelledWord,
280 ),
266 }), 281 }),
267 ); 282 );
268 } 283 }
@@ -274,7 +289,7 @@ module.exports = class ContextMenuBuilder {
274 * Adds search-related menu items. 289 * Adds search-related menu items.
275 */ 290 */
276 addSearchItems(menu, menuInfo) { 291 addSearchItems(menu, menuInfo) {
277 if (!menuInfo.selectionText || menuInfo.selectionText.length < 1) { 292 if (!menuInfo.selectionText || menuInfo.selectionText.length === 0) {
278 return menu; 293 return menu;
279 } 294 }
280 295
@@ -287,7 +302,9 @@ module.exports = class ContextMenuBuilder {
287 const webContents = this.getWebContents(); 302 const webContents = this.getWebContents();
288 303
289 const lookUpDefinition = new MenuItem({ 304 const lookUpDefinition = new MenuItem({
290 label: this.stringTable.lookUpDefinition({ word: menuInfo.selectionText.trim() }), 305 label: this.stringTable.lookUpDefinition({
306 word: menuInfo.selectionText.trim(),
307 }),
291 click: () => webContents.showDefinitionForSelection(), 308 click: () => webContents.showDefinitionForSelection(),
292 }); 309 });
293 310
@@ -295,9 +312,13 @@ module.exports = class ContextMenuBuilder {
295 } 312 }
296 313
297 const search = new MenuItem({ 314 const search = new MenuItem({
298 label: this.stringTable.searchWith({ searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine] }), 315 label: this.stringTable.searchWith({
316 searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine],
317 }),
299 click: () => { 318 click: () => {
300 const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({ searchTerm: encodeURIComponent(menuInfo.selectionText) }); 319 const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({
320 searchTerm: encodeURIComponent(menuInfo.selectionText),
321 });
301 openExternalUrl(url, true); 322 openExternalUrl(url, true);
302 }, 323 },
303 }); 324 });
@@ -319,10 +340,14 @@ module.exports = class ContextMenuBuilder {
319 const copyImage = new MenuItem({ 340 const copyImage = new MenuItem({
320 label: this.stringTable.copyImage(), 341 label: this.stringTable.copyImage(),
321 click: () => { 342 click: () => {
322 const result = this.convertImageToBase64(menuInfo.srcURL, 343 const result = this.convertImageToBase64(menuInfo.srcURL, dataURL =>
323 (dataURL) => clipboard.writeImage(nativeImage.createFromDataURL(dataURL))); 344 clipboard.writeImage(nativeImage.createFromDataURL(dataURL)),
324 345 );
325 this._sendNotificationOnClipboardEvent(menuInfo.clipboardNotifications, () => `Image copied from URL: ${menuInfo.srcURL}`); 346
347 this._sendNotificationOnClipboardEvent(
348 menuInfo.clipboardNotifications,
349 () => `Image copied from URL: ${menuInfo.srcURL}`,
350 );
326 return result; 351 return result;
327 }, 352 },
328 }); 353 });
@@ -333,7 +358,10 @@ module.exports = class ContextMenuBuilder {
333 label: this.stringTable.copyImageUrl(), 358 label: this.stringTable.copyImageUrl(),
334 click: () => { 359 click: () => {
335 const result = clipboard.writeText(menuInfo.srcURL); 360 const result = clipboard.writeText(menuInfo.srcURL);
336 this._sendNotificationOnClipboardEvent(menuInfo.clipboardNotifications, () => `Image URL copied: ${menuInfo.srcURL}`); 361 this._sendNotificationOnClipboardEvent(
362 menuInfo.clipboardNotifications,
363 () => `Image URL copied: ${menuInfo.srcURL}`,
364 );
337 return result; 365 return result;
338 }, 366 },
339 }); 367 });
@@ -345,20 +373,22 @@ module.exports = class ContextMenuBuilder {
345 const downloadImage = new MenuItem({ 373 const downloadImage = new MenuItem({
346 label: this.stringTable.downloadImage(), 374 label: this.stringTable.downloadImage(),
347 click: () => { 375 click: () => {
348 const urlWithoutBlob = menuInfo.srcURL.substr(5); 376 const urlWithoutBlob = menuInfo.srcURL.slice(5);
349 this.convertImageToBase64(menuInfo.srcURL, 377 this.convertImageToBase64(menuInfo.srcURL, dataURL => {
350 (dataURL) => { 378 const url = new window.URL(urlWithoutBlob);
351 const url = new window.URL(urlWithoutBlob); 379 const fileName = url.pathname.slice(1);
352 const fileName = url.pathname.substr(1); 380 ipcRenderer.send('download-file', {
353 ipcRenderer.send('download-file', { 381 content: dataURL,
354 content: dataURL, 382 fileOptions: {
355 fileOptions: { 383 name: fileName,
356 name: fileName, 384 mime: 'image/png',
357 mime: 'image/png', 385 },
358 },
359 });
360 }); 386 });
361 this._sendNotificationOnClipboardEvent(menuInfo.clipboardNotifications, () => `Image downloaded: ${urlWithoutBlob}`); 387 });
388 this._sendNotificationOnClipboardEvent(
389 menuInfo.clipboardNotifications,
390 () => `Image downloaded: ${urlWithoutBlob}`,
391 );
362 }, 392 },
363 }); 393 });
364 394
@@ -373,12 +403,14 @@ module.exports = class ContextMenuBuilder {
373 */ 403 */
374 addCut(menu, menuInfo) { 404 addCut(menu, menuInfo) {
375 const webContents = this.getWebContents(); 405 const webContents = this.getWebContents();
376 menu.append(new MenuItem({ 406 menu.append(
377 label: this.stringTable.cut(), 407 new MenuItem({
378 accelerator: `${cmdOrCtrlShortcutKey()}+X`, 408 label: this.stringTable.cut(),
379 enabled: menuInfo.editFlags.canCut, 409 accelerator: `${cmdOrCtrlShortcutKey()}+X`,
380 click: () => webContents.cut(), 410 enabled: menuInfo.editFlags.canCut,
381 })); 411 click: () => webContents.cut(),
412 }),
413 );
382 414
383 return menu; 415 return menu;
384 } 416 }
@@ -388,12 +420,14 @@ module.exports = class ContextMenuBuilder {
388 */ 420 */
389 addCopy(menu, menuInfo) { 421 addCopy(menu, menuInfo) {
390 const webContents = this.getWebContents(); 422 const webContents = this.getWebContents();
391 menu.append(new MenuItem({ 423 menu.append(
392 label: this.stringTable.copy(), 424 new MenuItem({
393 accelerator: `${cmdOrCtrlShortcutKey()}+C`, 425 label: this.stringTable.copy(),
394 enabled: menuInfo.editFlags.canCopy, 426 accelerator: `${cmdOrCtrlShortcutKey()}+C`,
395 click: () => webContents.copy(), 427 enabled: menuInfo.editFlags.canCopy,
396 })); 428 click: () => webContents.copy(),
429 }),
430 );
397 431
398 return menu; 432 return menu;
399 } 433 }
@@ -403,21 +437,23 @@ module.exports = class ContextMenuBuilder {
403 */ 437 */
404 addPaste(menu, menuInfo) { 438 addPaste(menu, menuInfo) {
405 const webContents = this.getWebContents(); 439 const webContents = this.getWebContents();
406 menu.append(new MenuItem({ 440 menu.append(
407 label: this.stringTable.paste(), 441 new MenuItem({
408 accelerator: `${cmdOrCtrlShortcutKey()}+V`, 442 label: this.stringTable.paste(),
409 enabled: menuInfo.editFlags.canPaste, 443 accelerator: `${cmdOrCtrlShortcutKey()}+V`,
410 click: () => webContents.paste(), 444 enabled: menuInfo.editFlags.canPaste,
411 })); 445 click: () => webContents.paste(),
446 }),
447 );
412 448
413 return menu; 449 return menu;
414 } 450 }
415 451
416 addPastePlain(menu, menuInfo) { 452 addPastePlain(menu, menuInfo) {
417 if ( 453 if (
418 menuInfo.editFlags.canPaste 454 menuInfo.editFlags.canPaste &&
419 && !menuInfo.linkText 455 !menuInfo.linkText &&
420 && !menuInfo.hasImageContents 456 !menuInfo.hasImageContents
421 ) { 457 ) {
422 const webContents = this.getWebContents(); 458 const webContents = this.getWebContents();
423 menu.append( 459 menu.append(
@@ -463,21 +499,21 @@ module.exports = class ContextMenuBuilder {
463 * @param {String} outputFormat The image format to use, defaults to 'image/png' 499 * @param {String} outputFormat The image format to use, defaults to 'image/png'
464 */ 500 */
465 convertImageToBase64(url, callback, outputFormat = 'image/png') { 501 convertImageToBase64(url, callback, outputFormat = 'image/png') {
466 let canvas = document.createElement('CANVAS'); 502 let canvas = document.createElement('canvas');
467 const ctx = canvas.getContext('2d'); 503 const ctx = canvas.getContext('2d');
468 // eslint-disable-next-line no-undef 504 // eslint-disable-next-line no-undef
469 const img = new Image(); 505 const img = new Image();
470 img.crossOrigin = 'Anonymous'; 506 img.crossOrigin = 'Anonymous';
471 507
472 img.onload = () => { 508 img.addEventListener('load', () => {
473 canvas.height = img.height; 509 canvas.height = img.height;
474 canvas.width = img.width; 510 canvas.width = img.width;
475 ctx.drawImage(img, 0, 0); 511 ctx?.drawImage(img, 0, 0);
476 512
477 const dataURL = canvas.toDataURL(outputFormat); 513 const dataURL = canvas.toDataURL(outputFormat);
478 canvas = null; 514 canvas = null;
479 callback(dataURL); 515 callback(dataURL);
480 }; 516 });
481 517
482 img.src = url; 518 img.src = url;
483 } 519 }
@@ -487,12 +523,14 @@ module.exports = class ContextMenuBuilder {
487 */ 523 */
488 goBack(menu) { 524 goBack(menu) {
489 const webContents = this.getWebContents(); 525 const webContents = this.getWebContents();
490 menu.append(new MenuItem({ 526 menu.append(
491 label: this.stringTable.goBack(), 527 new MenuItem({
492 accelerator: `${cmdOrCtrlShortcutKey()}+left`, 528 label: this.stringTable.goBack(),
493 enabled: webContents.canGoBack(), 529 accelerator: `${cmdOrCtrlShortcutKey()}+left`,
494 click: () => webContents.goBack(), 530 enabled: webContents.canGoBack(),
495 })); 531 click: () => webContents.goBack(),
532 }),
533 );
496 534
497 return menu; 535 return menu;
498 } 536 }
@@ -502,12 +540,14 @@ module.exports = class ContextMenuBuilder {
502 */ 540 */
503 goForward(menu) { 541 goForward(menu) {
504 const webContents = this.getWebContents(); 542 const webContents = this.getWebContents();
505 menu.append(new MenuItem({ 543 menu.append(
506 label: this.stringTable.goForward(), 544 new MenuItem({
507 accelerator: `${cmdOrCtrlShortcutKey()}+right`, 545 label: this.stringTable.goForward(),
508 enabled: webContents.canGoForward(), 546 accelerator: `${cmdOrCtrlShortcutKey()}+right`,
509 click: () => webContents.goForward(), 547 enabled: webContents.canGoForward(),
510 })); 548 click: () => webContents.goForward(),
549 }),
550 );
511 551
512 return menu; 552 return menu;
513 } 553 }
@@ -516,14 +556,19 @@ module.exports = class ContextMenuBuilder {
516 * Adds the 'copy page url' menu item. 556 * Adds the 'copy page url' menu item.
517 */ 557 */
518 copyPageUrl(menu, menuInfo) { 558 copyPageUrl(menu, menuInfo) {
519 menu.append(new MenuItem({ 559 menu.append(
520 label: this.stringTable.copyPageUrl(), 560 new MenuItem({
521 enabled: true, 561 label: this.stringTable.copyPageUrl(),
522 click: () => { 562 enabled: true,
523 clipboard.writeText(window.location.href); 563 click: () => {
524 this._sendNotificationOnClipboardEvent(menuInfo.clipboardNotifications, () => `Page URL copied: ${window.location.href}`); 564 clipboard.writeText(window.location.href);
525 }, 565 this._sendNotificationOnClipboardEvent(
526 })); 566 menuInfo.clipboardNotifications,
567 () => `Page URL copied: ${window.location.href}`,
568 );
569 },
570 }),
571 );
527 572
528 return menu; 573 return menu;
529 } 574 }
@@ -533,15 +578,17 @@ module.exports = class ContextMenuBuilder {
533 */ 578 */
534 goToHomePage(menu, menuInfo) { 579 goToHomePage(menu, menuInfo) {
535 const baseURL = new URL(menuInfo.pageURL); 580 const baseURL = new URL(menuInfo.pageURL);
536 menu.append(new MenuItem({ 581 menu.append(
537 label: this.stringTable.goToHomePage(), 582 new MenuItem({
538 accelerator: `${cmdOrCtrlShortcutKey()}+Home`, 583 label: this.stringTable.goToHomePage(),
539 enabled: true, 584 accelerator: `${cmdOrCtrlShortcutKey()}+Home`,
540 click: () => { 585 enabled: true,
541 // webContents.loadURL(baseURL.origin); 586 click: () => {
542 window.location.href = baseURL.origin; 587 // webContents.loadURL(baseURL.origin);
543 }, 588 window.location.href = baseURL.origin;
544 })); 589 },
590 }),
591 );
545 592
546 return menu; 593 return menu;
547 } 594 }
@@ -550,13 +597,15 @@ module.exports = class ContextMenuBuilder {
550 * Adds the 'open in browser' menu item. 597 * Adds the 'open in browser' menu item.
551 */ 598 */
552 openInBrowser(menu, menuInfo) { 599 openInBrowser(menu, menuInfo) {
553 menu.append(new MenuItem({ 600 menu.append(
554 label: this.stringTable.openInBrowser(), 601 new MenuItem({
555 enabled: true, 602 label: this.stringTable.openInBrowser(),
556 click: () => { 603 enabled: true,
557 openExternalUrl(menuInfo.pageURL, true); 604 click: () => {
558 }, 605 openExternalUrl(menuInfo.pageURL, true);
559 })); 606 },
607 }),
608 );
560 609
561 return menu; 610 return menu;
562 } 611 }
@@ -566,9 +615,8 @@ module.exports = class ContextMenuBuilder {
566 return; 615 return;
567 } 616 }
568 // eslint-disable-next-line no-new 617 // eslint-disable-next-line no-new
569 new window.Notification('Data copied into Clipboard', 618 new window.Notification('Data copied into Clipboard', {
570 { 619 body: notificationText(),
571 body: notificationText(), 620 });
572 });
573 } 621 }
574}; 622};