diff --git a/background/background.js b/background/background.js index 8f7249bb..a699fdfa 100644 --- a/background/background.js +++ b/background/background.js @@ -37,6 +37,12 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, { }); return KEEP_CHANNEL_OPEN; }, + + optionsCustomizeHotkeys() { + return browser.runtime.openOptionsPage() + .then(() => new Promise(resolve => setTimeout(resolve, 100))) + .then(() => sendMessage({method: 'optionsCustomizeHotkeys'})); + }, }); // eslint-disable-next-line no-var diff --git a/js/prefs.js b/js/prefs.js index b7369654..6bf8cd10 100644 --- a/js/prefs.js +++ b/js/prefs.js @@ -79,6 +79,11 @@ var prefs = new function Prefs() { // last color 'editor.colorpicker.color': '', + // Firefox-only chrome.commands.update + 'hotkey._execute_browser_action': '', + 'hotkey.openManage': '', + 'hotkey.styleDisableAll': '', + 'iconset': 0, // 0 = dark-themed icon // 1 = light-themed icon diff --git a/manage/manage.js b/manage/manage.js index 156b6979..4edbd075 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -58,7 +58,10 @@ function initGlobalEvents() { installed = $('#installed'); installed.onclick = handleEvent.entryClicked; $('#manage-options-button').onclick = () => chrome.runtime.openOptionsPage(); - $('#manage-shortcuts-button').onclick = () => openURL({url: URLS.configureCommands}); + { + const btn = $('#manage-shortcuts-button'); + btn.onclick = btn.onclick || (() => openURL({url: URLS.configureCommands})); + } $$('#header a[href^="http"]').forEach(a => (a.onclick = handleEvent.external)); // show date installed & last update on hover installed.addEventListener('mouseover', handleEvent.lazyAddEntryTitle); @@ -690,4 +693,10 @@ function usePrefsDuringPageLoad() { } } $$('#header select').forEach(el => el.adjustWidth()); + + if (FIREFOX && 'update' in (chrome.commands || {})) { + const btn = $('#manage-shortcuts-button'); + btn.classList.remove('chromium-only'); + btn.onclick = API.optionsCustomizeHotkeys; + } } diff --git a/manifest.json b/manifest.json index 0308f046..0decb823 100644 --- a/manifest.json +++ b/manifest.json @@ -40,6 +40,7 @@ ] }, "commands": { + "_execute_browser_action": {}, "openManage": { "description": "__MSG_openManage__" }, diff --git a/options/options.js b/options/options.js index ca50688b..0afae768 100644 --- a/options/options.js +++ b/options/options.js @@ -18,6 +18,15 @@ if (!FIREFOX && !OPERA) { block.classList.add('collapsible', 'collapsed'); } +if (FIREFOX && 'update' in (chrome.commands || {})) { + $('[data-cmd="open-keyboard"]').classList.remove('chromium-only'); + chrome.runtime.onMessage.addListener(msg => { + if (msg.method === 'optionsCustomizeHotkeys') { + customizeHotkeys(); + } + }); +} + // actions document.onclick = e => { const target = e.target.closest('[data-cmd]'); @@ -37,7 +46,11 @@ document.onclick = e => { break; case 'open-keyboard': - openURL({url: URLS.configureCommands}); + if (FIREFOX) { + customizeHotkeys(); + } else { + openURL({url: URLS.configureCommands}); + } e.preventDefault(); break; @@ -127,3 +140,62 @@ function splitLongTooltips() { } } } + +function customizeHotkeys() { + // command name -> i18n id + const hotkeys = new Map([ + ['_execute_browser_action', 'optionsCustomizePopup'], + ['openManage', 'openManage'], + ['styleDisableAll', 'disableAllStyles'], + ]); + + messageBox({ + title: t('shortcutsNote'), + contents: [ + $create('table', + [...hotkeys.entries()].map(([cmd, i18n]) => + $create('tr', [ + $create('td', t(i18n)), + $create('td', + $create('input', { + id: 'hotkey.' + cmd, + //placeholder: t('helpKeyMapHotkey'), + })), + ]))), + ], + className: 'center', + buttons: [t('confirmClose')], + onshow(box) { + const ids = []; + for (const cmd of hotkeys.keys()) { + const id = 'hotkey.' + cmd; + ids.push(id); + $('#' + id).oninput = onInput; + } + setupLivePrefs(ids); + $('button', box).insertAdjacentElement('beforebegin', + $createLink( + 'https://developer.mozilla.org/Add-ons/WebExtensions/manifest.json/commands#Key_combinations', + t('helpAlt'))); + }, + }); + + function onInput() { + const hotkey = this.value.trim(); + if (!hotkey) { + this.setCustomValidity(''); + return; + } + try { + browser.commands.update({ + name: this.id.split('.')[1], + shortcut: hotkey, + }).then( + () => this.setCustomValidity(''), + err => this.setCustomValidity(err) + ); + } catch (err) { + this.setCustomValidity(err); + } + } +} diff --git a/popup/popup.css b/popup/popup.css index fda61a80..70234749 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -456,7 +456,6 @@ body.blocked .actions > .main-controls { min-width: 4em; } -.firefox #popup-options button:nth-last-child(2), #popup-options button:last-child { margin-right: 0; } diff --git a/popup/popup.js b/popup/popup.js index df59a533..b16e8e42 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -100,6 +100,13 @@ function initPopup() { shortcutsButton.dataset.href = URLS.configureCommands; shortcutsButton.onclick = handleEvent.openURLandHide; + if (FIREFOX && 'update' in (chrome.commands || {})) { + shortcutsButton.classList.remove('chromium-only'); + shortcutsButton.onclick = () => API.optionsCustomizeHotkeys().then(window.close); + } else if (FIREFOX) { + shortcutsButton.remove(); + } + if (!prefs.get('popup.stylesFirst')) { document.body.insertBefore( $('body > .actions'),