| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /* global API msg */// msg.js
- /* global prefs */
- /* global t */// localization.js
- /* global
- $
- $$
- $create
- $createLink
- getEventKeyName
- messageBoxProxy
- setupLivePrefs
- */// dom.js
- /* global
- CHROME
- CHROME_POPUP_BORDER_BUG
- FIREFOX
- OPERA
- URLS
- capitalize
- ignoreChromeError
- openURL
- */// toolbox.js
- 'use strict';
- setupLivePrefs();
- $$('input[min], input[max]').forEach(enforceInputRange);
- if (CHROME_POPUP_BORDER_BUG) {
- const borderOption = $('.chrome-no-popup-border');
- if (borderOption) {
- borderOption.classList.remove('chrome-no-popup-border');
- }
- }
- // collapse #advanced block in Chrome pre-66 (classic chrome://extensions UI)
- if (!FIREFOX && !OPERA && CHROME < 66) {
- const block = $('#advanced');
- $('h1', block).onclick = event => {
- event.preventDefault();
- block.classList.toggle('collapsed');
- const isCollapsed = block.classList.contains('collapsed');
- const visibleToggle = $(isCollapsed ? '.is-collapsed' : '.is-expanded', block);
- visibleToggle.focus();
- };
- block.classList.add('collapsible', 'collapsed');
- }
- if (FIREFOX && 'update' in (chrome.commands || {})) {
- $('[data-cmd="open-keyboard"]').classList.remove('chromium-only');
- }
- // actions
- $('#options-close-icon').onclick = () => {
- top.dispatchEvent(new CustomEvent('closeOptions'));
- };
- document.onclick = e => {
- const target = e.target.closest('[data-cmd]');
- if (!target) {
- return;
- }
- // prevent double-triggering in case a sub-element was clicked
- e.stopPropagation();
- switch (target.dataset.cmd) {
- case 'open-manage':
- API.openManage();
- break;
- case 'check-updates':
- checkUpdates();
- break;
- case 'open-keyboard':
- if (FIREFOX) {
- customizeHotkeys();
- } else {
- openURL({url: URLS.configureCommands});
- }
- e.preventDefault();
- break;
- case 'reset':
- $$('input')
- .filter(input => prefs.knownKeys.includes(input.id))
- .forEach(input => prefs.reset(input.id));
- break;
- }
- };
- // sync to cloud
- (() => {
- const elCloud = $('.sync-options .cloud-name');
- const elStart = $('.sync-options .connect');
- const elStop = $('.sync-options .disconnect');
- const elSyncNow = $('.sync-options .sync-now');
- const elStatus = $('.sync-options .sync-status');
- const elLogin = $('.sync-options .sync-login');
- /** @type {Sync.Status} */
- let status = {};
- msg.onExtension(e => {
- if (e.method === 'syncStatusUpdate') {
- setStatus(e.status);
- }
- });
- API.sync.getStatus()
- .then(setStatus);
- elCloud.on('change', updateButtons);
- for (const [btn, fn] of [
- [elStart, () => API.sync.start(elCloud.value)],
- [elStop, API.sync.stop],
- [elSyncNow, API.sync.syncNow],
- [elLogin, async () => {
- await API.sync.login();
- await API.sync.syncNow();
- }],
- ]) {
- btn.on('click', e => {
- if (getEventKeyName(e) === 'MouseL') {
- fn();
- }
- });
- }
- function setStatus(newStatus) {
- status = newStatus;
- updateButtons();
- }
- function updateButtons() {
- const {state, STATES} = status;
- const isConnected = state === STATES.connected;
- const isDisconnected = state === STATES.disconnected;
- if (status.currentDriveName) {
- elCloud.value = status.currentDriveName;
- }
- for (const [el, enable] of [
- [elCloud, isDisconnected],
- [elStart, isDisconnected && elCloud.value !== 'none'],
- [elStop, isConnected && !status.syncing],
- [elSyncNow, isConnected && !status.syncing && status.login],
- ]) {
- el.disabled = !enable;
- }
- elStatus.textContent = getStatusText();
- elLogin.hidden = !isConnected || status.login;
- }
- function getStatusText() {
- if (status.syncing) {
- const {phase, loaded, total} = status.progress || {};
- return phase
- ? t(`optionsSyncStatus${capitalize(phase)}`, [loaded + 1, total], false) ||
- `${phase} ${loaded} / ${total}`
- : t('optionsSyncStatusSyncing');
- }
- const {state, errorMessage, STATES} = status;
- if (errorMessage && (state === STATES.connected || state === STATES.disconnected)) {
- return errorMessage;
- }
- if (state === STATES.connected && !status.login) {
- return t('optionsSyncStatusRelogin');
- }
- return t(`optionsSyncStatus${capitalize(state)}`, null, false) || state;
- }
- })();
- function checkUpdates() {
- let total = 0;
- let checked = 0;
- let updated = 0;
- const maxWidth = $('#update-progress').parentElement.clientWidth;
- chrome.runtime.onConnect.addListener(function onConnect(port) {
- if (port.name !== 'updater') return;
- port.onMessage.addListener(observer);
- chrome.runtime.onConnect.removeListener(onConnect);
- });
- API.updater.checkAllStyles({observe: true});
- function observer(info) {
- if ('count' in info) {
- total = info.count;
- document.body.classList.add('update-in-progress');
- } else if (info.updated) {
- updated++;
- checked++;
- } else if (info.error) {
- checked++;
- } else if (info.done) {
- document.body.classList.remove('update-in-progress');
- }
- $('#update-progress').style.width = Math.round(checked / total * maxWidth) + 'px';
- $('#updates-installed').dataset.value = updated || '';
- }
- }
- function customizeHotkeys() {
- // command name -> i18n id
- const hotkeys = new Map([
- ['_execute_browser_action', 'optionsCustomizePopup'],
- ['openManage', 'openManage'],
- ['styleDisableAll', 'disableAllStyles'],
- ]);
- messageBoxProxy.show({
- title: t('shortcutsNote'),
- contents: [
- $create('table',
- [...hotkeys.entries()].map(([cmd, i18n]) =>
- $create('tr', [
- $create('td', t(i18n)),
- $create('td',
- $create('input', {
- id: 'hotkey.' + cmd,
- type: 'search',
- //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 name = this.id.split('.')[1];
- const shortcut = this.value.trim();
- if (!shortcut) {
- browser.commands.reset(name).catch(ignoreChromeError);
- this.setCustomValidity('');
- return;
- }
- try {
- browser.commands.update({name, shortcut}).then(
- () => this.setCustomValidity(''),
- err => this.setCustomValidity(err)
- );
- } catch (err) {
- this.setCustomValidity(err);
- }
- }
- }
- function enforceInputRange(element) {
- const min = Number(element.min);
- const max = Number(element.max);
- const doNotify = () => element.dispatchEvent(new Event('change', {bubbles: true}));
- const onChange = ({type}) => {
- if (type === 'input' && element.checkValidity()) {
- doNotify();
- } else if (type === 'change' && !element.checkValidity()) {
- element.value = Math.max(min, Math.min(max, Number(element.value)));
- doNotify();
- }
- };
- element.on('change', onChange);
- element.on('input', onChange);
- }
- window.onkeydown = event => {
- if (getEventKeyName(event) === 'Escape') {
- top.dispatchEvent(new CustomEvent('closeOptions'));
- }
- };
|