shuffle and tidy up options (#1406)
* move updates/sync to the top, theme to the bottom * remove font override * replace 'Back to manage' with 'Close' * add a note for the built-in shortcuts UI in FF - update button + confirm reset * one button to connect/disconnect * shorten ids * simplify/extract sync js * reuse :invalid style
This commit is contained in:
parent
c9b8593830
commit
8d3e01e05a
|
@ -1163,7 +1163,7 @@
|
|||
"message": "Reset options"
|
||||
},
|
||||
"optionsStylusThemes": {
|
||||
"message": "Find a Stylus UI theme"
|
||||
"message": "Click Stylus icon in the browser toolbar on any Stylus page including this one, then click 'Find styles'"
|
||||
},
|
||||
"optionsSubheading": {
|
||||
"message": "More Options",
|
||||
|
@ -1500,6 +1500,9 @@
|
|||
"shortcutsNote": {
|
||||
"message": "Define keyboard shortcuts"
|
||||
},
|
||||
"shortcutsNoteFF": {
|
||||
"message": "In Firefox 66+ you can open the built-in shortcuts UI manually:\n1) right-click Stylus icon in the toolbar and choose 'Manage'\n(alternatively, open about:addons via the main menu or Ctrl-Shift-A),\n2) in the page that opens click the cog wheel icon in the top right corner,\n3) choose 'Manage extension shortcuts'.\n\nYou can also customize the shortcuts here."
|
||||
},
|
||||
"sortDateNewestFirst": {
|
||||
"message": "newest first",
|
||||
"description": "Text added to indicate that sorting a date would add the newest entries at the top"
|
||||
|
|
|
@ -215,10 +215,6 @@ label {
|
|||
#options span .svg-icon {
|
||||
margin-top: -3px; /* inline info and config icons */
|
||||
}
|
||||
input:invalid {
|
||||
background-color: rgba(255, 0, 0, 0.1);
|
||||
color: darkred;
|
||||
}
|
||||
#enabled {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,11 @@ input[type=search] {
|
|||
border: 1px solid var(--c65);
|
||||
}
|
||||
|
||||
input:invalid {
|
||||
background-color: rgba(255, 0, 0, 0.1);
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -43,6 +43,7 @@ Object.assign(t, {
|
|||
continue;
|
||||
}
|
||||
if (node.localName === 'template') {
|
||||
node.remove();
|
||||
t.createTemplate(node);
|
||||
continue;
|
||||
}
|
||||
|
@ -87,17 +88,20 @@ Object.assign(t, {
|
|||
text.replace(t.RX_WORD_BREAK, '$&\u00AD');
|
||||
},
|
||||
|
||||
createTemplate(node) {
|
||||
const el = node.content.firstElementChild.cloneNode(true);
|
||||
t.NodeList(el);
|
||||
t.template[node.dataset.id] = el;
|
||||
// compress inter-tag whitespace to reduce number of DOM nodes by 25%
|
||||
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
||||
createTemplate(el) {
|
||||
const {content} = el;
|
||||
const toRemove = [];
|
||||
// Compress inter-tag whitespace to reduce DOM tree and avoid space between elements without flex
|
||||
const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);
|
||||
for (let n; (n = walker.nextNode());) {
|
||||
if (!/[\xA0\S]/.test(n.textContent)) { // allow \xA0 to keep
|
||||
n.remove();
|
||||
if (!/[\xA0\S]/.test(n.textContent) || // allowing \xA0 so as to preserve
|
||||
n.nodeType === Node.COMMENT_NODE) {
|
||||
toRemove.push(n);
|
||||
}
|
||||
}
|
||||
toRemove.forEach(n => n.remove());
|
||||
t.NodeList(content.querySelectorAll('*'));
|
||||
t.template[el.dataset.id] = content.childNodes.length > 1 ? content : content.childNodes[0];
|
||||
},
|
||||
|
||||
createText(str) {
|
||||
|
|
151
options.html
151
options.html
|
@ -5,7 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title i18n-text-append="optionsHeading">Stylus </title>
|
||||
<link rel="stylesheet" href="global.css">
|
||||
<link href="global-dark.css" rel="stylesheet">
|
||||
<link href="global-dark.css?bugfix" rel="stylesheet"> <!-- https://crbug.com/1298600 -->
|
||||
|
||||
<script src="js/polyfill.js"></script>
|
||||
<script src="js/toolbox.js"></script>
|
||||
|
@ -19,6 +19,30 @@
|
|||
|
||||
<link rel="stylesheet" href="options/onoffswitch.css">
|
||||
<link rel="stylesheet" href="options/options.css">
|
||||
|
||||
<template data-id="shortcutsFF">
|
||||
<p style="line-height: 1.5" i18n-text="shortcutsNoteFF"></p>
|
||||
<table style="margin: 0 auto">
|
||||
<tr>
|
||||
<td i18n-text="optionsCustomizePopup"></td>
|
||||
<td><input id="hotkey._execute_browser_action" type="search"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n-text="openManage"></td>
|
||||
<td><input id="hotkey.openManage" type="search"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n-text="disableAllStyles"></td>
|
||||
<td><input id="hotkey.styleDisableAll" type="search"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="text-align: center">
|
||||
<a href="https://developer.mozilla.org/Add-ons/WebExtensions/manifest.json/commands#Key_combinations"
|
||||
target="_blank" i18n-text="helpAlt"></a>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script src="options/options-sync.js"></script>
|
||||
<script src="js/dark-themer.js"></script> <!-- must be last in HEAD to avoid FOUC -->
|
||||
</head>
|
||||
|
||||
|
@ -38,12 +62,55 @@
|
|||
|
||||
<div class="options-wrapper">
|
||||
|
||||
<div class="block">
|
||||
<h1 i18n-text="cm_theme"></h1>
|
||||
<div class="block" id="updates">
|
||||
<h1 i18n-text="optionsCustomizeUpdate"></h1>
|
||||
<div class="items">
|
||||
<div class="label">
|
||||
<a i18n-text="optionsStylusThemes" target="_blank"
|
||||
href="https://33kk.github.io/uso-archive/?category=chrome-extension&search=Stylus"></a>
|
||||
<label>
|
||||
<span i18n-text="optionsUpdateInterval">
|
||||
<a i18n-title="optionsUpdateImportNote"
|
||||
data-cmd="note" class="svg-inline-wrapper" tabindex="0">
|
||||
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
|
||||
</a>
|
||||
</span>
|
||||
<input type="number" min="0" id="updateInterval">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block sync-options">
|
||||
<h1 i18n-text="optionsCustomizeSync"></h1>
|
||||
<div class="items">
|
||||
<label>
|
||||
<span class="sync-status"></span>
|
||||
<div class="select-resizer">
|
||||
<select class="cloud-name">
|
||||
<option value="none" i18n-text="optionsSyncNone"></option>
|
||||
<option value="dropbox">Dropbox</option>
|
||||
<option value="google">Google Drive</option>
|
||||
<option value="onedrive">OneDrive</option>
|
||||
<option value="webdav">WebDAV</option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
</label>
|
||||
<table class="drive-options" data-drive="webdav">
|
||||
<tr>
|
||||
<td i18n-text="optionsSyncUrl"></td>
|
||||
<td><input type="url" data-option="url"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n-text="optionsSyncUsername"></td>
|
||||
<td><input type="text" data-option="username"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n-text="optionsSyncPassword"></td>
|
||||
<td><input type="password" data-option="password"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="actions">
|
||||
<button class="connect"></button>
|
||||
<button class="sync-now" i18n-text="optionsSyncSyncNow"></button>
|
||||
<button class="sync-login" i18n-text="optionsSyncLogin"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -207,62 +274,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block" id="updates">
|
||||
<h1 i18n-text="optionsCustomizeUpdate"></h1>
|
||||
<div class="items">
|
||||
<label>
|
||||
<span i18n-text="optionsUpdateInterval">
|
||||
<a i18n-title="optionsUpdateImportNote"
|
||||
data-cmd="note" class="svg-inline-wrapper" tabindex="0">
|
||||
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
|
||||
</a>
|
||||
</span>
|
||||
<input type="number" min="0" id="updateInterval">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block sync-options">
|
||||
<h1 i18n-text="optionsCustomizeSync"></h1>
|
||||
<div class="items">
|
||||
<div class="label">
|
||||
<span class="sync-status"></span>
|
||||
<div class="select-resizer">
|
||||
<select class="cloud-name">
|
||||
<option value="none" i18n-text="optionsSyncNone"></option>
|
||||
<option value="dropbox">Dropbox</option>
|
||||
<option value="google">Google Drive</option>
|
||||
<option value="onedrive">OneDrive</option>
|
||||
<option value="webdav">WebDAV</option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="drive-options">
|
||||
<div class="webdav-options" data-drive="webdav">
|
||||
<label class="url">
|
||||
<span i18n-text="optionsSyncUrl"></span>
|
||||
<input type="text" data-option="url">
|
||||
</label>
|
||||
<label class="username">
|
||||
<span i18n-text="optionsSyncUsername"></span>
|
||||
<input type="text" data-option="username">
|
||||
</label>
|
||||
<label class="password">
|
||||
<span i18n-text="optionsSyncPassword"></span>
|
||||
<input type="password" data-option="password">
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="actions">
|
||||
<button type="button" class="connect" i18n-text="optionsSyncConnect"></button>
|
||||
<button type="button" class="disconnect" i18n-text="optionsSyncDisconnect"></button>
|
||||
<button type="button" class="sync-now" i18n-text="optionsSyncSyncNow"></button>
|
||||
<button type="button" class="sync-login" i18n-text="optionsSyncLogin"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block" id="advanced">
|
||||
<h1 i18n-text="optionsAdvanced"></h1>
|
||||
<div class="items">
|
||||
|
@ -325,18 +336,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<h1 i18n-text="cm_theme"></h1>
|
||||
<div class="items" i18n-text="optionsStylusThemes" style="width:0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block" id="actions">
|
||||
<button data-cmd="reset" i18n-text="optionsResetButton" i18n-title="optionsReset"></button>
|
||||
<button data-cmd="open-manage" i18n-text="styleCancelEditLabel"></button>
|
||||
<div data-cmd="check-updates">
|
||||
<button i18n-text="optionsCheck" i18n-title="optionsCheckUpdate">
|
||||
<span id="update-progress"></span>
|
||||
</button>
|
||||
<div id="updates-installed" i18n-text="updatesCurrentlyInstalled"></div>
|
||||
</div>
|
||||
<button data-cmd="open-keyboard" class="chromium-only" i18n-text="shortcuts" i18n-title="shortcutsNote"></button>
|
||||
<button id="reset" i18n-text="optionsResetButton" i18n-title="optionsReset"></button>
|
||||
<button id="shortcuts" i18n-text="shortcuts" i18n-title="shortcutsNote"></button>
|
||||
<button id="manage" i18n-text="confirmClose"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
96
options/options-sync.js
Normal file
96
options/options-sync.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* global API msg */// msg.js
|
||||
/* global t */// localization.js
|
||||
/* global $ $$ toggleDataset waitForSelector */// dom.js
|
||||
/* global capitalize */// toolbox.js
|
||||
'use strict';
|
||||
|
||||
Promise.all([
|
||||
API.sync.getStatus(),
|
||||
waitForSelector('.sync-options'),
|
||||
]).then(([status, elSync]) => {
|
||||
const elCloud = $('.cloud-name', elSync);
|
||||
const elToggle = $('.connect', elSync);
|
||||
const elSyncNow = $('.sync-now', elSync);
|
||||
const elStatus = $('.sync-status', elSync);
|
||||
const elLogin = $('.sync-login', elSync);
|
||||
const elDriveOptions = $$('.drive-options', elSync);
|
||||
updateButtons();
|
||||
msg.onExtension(e => {
|
||||
if (e.method === 'syncStatusUpdate') {
|
||||
setStatus(e.status);
|
||||
}
|
||||
});
|
||||
elCloud.on('change', updateButtons);
|
||||
elToggle.onclick = async () => {
|
||||
if (elToggle.dataset.cmd === 'start') {
|
||||
await API.sync.setDriveOptions(elCloud.value, getDriveOptions());
|
||||
await API.sync.start(elCloud.value);
|
||||
} else {
|
||||
await API.sync.stop();
|
||||
}
|
||||
};
|
||||
elSyncNow.onclick = API.sync.syncNow;
|
||||
elLogin.onclick = async () => {
|
||||
await API.sync.login();
|
||||
await API.sync.syncNow();
|
||||
};
|
||||
|
||||
function getDriveOptions() {
|
||||
const result = {};
|
||||
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
|
||||
result[el.dataset.option] = el.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setDriveOptions(options) {
|
||||
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
|
||||
el.value = options[el.dataset.option] || '';
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(newStatus) {
|
||||
status = newStatus;
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
async function updateButtons() {
|
||||
const {state, STATES} = status;
|
||||
const isConnected = state === STATES.connected;
|
||||
const off = state === STATES.disconnected;
|
||||
if (status.currentDriveName) {
|
||||
elCloud.value = status.currentDriveName;
|
||||
}
|
||||
elCloud.disabled = !off;
|
||||
elToggle.disabled = status.syncing;
|
||||
elToggle.textContent = t(`optionsSync${off ? 'Connect' : 'Disconnect'}`);
|
||||
elToggle.dataset.cmd = off ? 'start' : 'stop';
|
||||
elSyncNow.disabled = !isConnected || status.syncing || !status.login;
|
||||
elStatus.textContent = getStatusText();
|
||||
elLogin.hidden = !isConnected || status.login;
|
||||
for (const el of elDriveOptions) {
|
||||
el.hidden = el.dataset.drive !== elCloud.value;
|
||||
el.disabled = !off;
|
||||
}
|
||||
toggleDataset(elSync, 'enabled', elCloud.value !== 'none');
|
||||
setDriveOptions(await API.sync.getDriveOptions(elCloud.value));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
|
@ -8,8 +8,6 @@ html {
|
|||
|
||||
body {
|
||||
background: none;
|
||||
font-family: "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
|
@ -184,12 +182,6 @@ input[type=number] {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
input[type=number]:invalid,
|
||||
input[type=text]:invalid {
|
||||
background-color: rgba(255, 0, 0, 0.1);
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
box-sizing: border-box;
|
||||
height: 2em;
|
||||
|
@ -201,25 +193,16 @@ input[type=time] {
|
|||
}
|
||||
|
||||
#actions {
|
||||
justify-content: space-around;
|
||||
align-items: stretch;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
padding: .5em 1em 1em;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(0, 0, 0, .05);
|
||||
margin: 0;
|
||||
border-top: 1px solid var(--c60);
|
||||
border-bottom: none;
|
||||
min-height: min-content; /* workaround for old Chrome ~70 bug when the window height is small */
|
||||
}
|
||||
|
||||
#actions button {
|
||||
width: auto;
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
#actions button:not(:last-child) {
|
||||
margin-right: 4px;
|
||||
margin: .5em 1em 0 0;
|
||||
}
|
||||
|
||||
[data-cmd="check-updates"] button {
|
||||
|
@ -229,41 +212,6 @@ input[type=time] {
|
|||
padding: .5em 0 .5em 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.update-in-progress [data-cmd="check-updates"] {
|
||||
opacity: .5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.update-in-progress #update-progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: currentColor;
|
||||
content: "";
|
||||
opacity: .35;
|
||||
}
|
||||
|
||||
#updates-installed {
|
||||
position: absolute;
|
||||
font-size: 85%;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
#updates-installed::after {
|
||||
content: attr(data-value);
|
||||
margin-left: .5ex;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#updates-installed:not([data-value]),
|
||||
#updates-installed[data-value=""] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html:not(.firefox):not(.opera) #updates {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.svg-inline-wrapper .svg-icon {
|
||||
width: 16px;
|
||||
|
@ -298,24 +246,27 @@ html:not(.firefox):not(.opera) #updates {
|
|||
.sync-status::first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.sync-options .drive-options {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
[data-drive="webdav"] {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.drive-options > :not([hidden]) {
|
||||
display: table;
|
||||
[data-drive="webdav"] td:nth-child(1) {
|
||||
padding: 1px .5em 1px 0;
|
||||
max-width: 10em;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
[data-drive="webdav"] td:nth-child(2) {
|
||||
padding: 1px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.drive-options > * > label {
|
||||
display: table-row;
|
||||
}
|
||||
.drive-options > * > label > * {
|
||||
display: table-cell;
|
||||
}
|
||||
.drive-options > * input {
|
||||
[data-drive="webdav"] input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.sync-options:not([data-enabled]) .actions {
|
||||
display: none;
|
||||
}
|
||||
.sync-options .actions button {
|
||||
margin-top: .5em;
|
||||
|
|
|
@ -1,20 +1,11 @@
|
|||
/* global API msg */// msg.js
|
||||
/* global API */// msg.js
|
||||
/* global prefs */
|
||||
/* global t */// localization.js
|
||||
/* global
|
||||
$
|
||||
$$
|
||||
$create
|
||||
$createLink
|
||||
getEventKeyName
|
||||
messageBoxProxy
|
||||
setupLivePrefs
|
||||
*/// dom.js
|
||||
/* global $ $$ getEventKeyName messageBoxProxy setupLivePrefs */// dom.js
|
||||
/* global
|
||||
CHROME_POPUP_BORDER_BUG
|
||||
FIREFOX
|
||||
URLS
|
||||
capitalize
|
||||
clamp
|
||||
ignoreChromeError
|
||||
openURL
|
||||
|
@ -23,242 +14,61 @@
|
|||
|
||||
setupLivePrefs();
|
||||
$$('input[min], input[max]').forEach(enforceInputRange);
|
||||
|
||||
if (CHROME_POPUP_BORDER_BUG) {
|
||||
$('.chrome-no-popup-border').classList.remove('chrome-no-popup-border');
|
||||
}
|
||||
|
||||
if (FIREFOX && 'update' in (chrome.commands || {})) {
|
||||
$('[data-cmd="open-keyboard"]').classList.remove('chromium-only');
|
||||
$('#shortcuts').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;
|
||||
$('#manage').onclick = () => {
|
||||
API.openManage();
|
||||
};
|
||||
$('#shortcuts').onclick = () => {
|
||||
if (FIREFOX) {
|
||||
customizeHotkeys();
|
||||
} else {
|
||||
openURL({url: URLS.configureCommands});
|
||||
}
|
||||
// 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});
|
||||
};
|
||||
$('#reset').onclick = async () => {
|
||||
if (await messageBoxProxy.confirm(t('confirmDiscardChanges'))) {
|
||||
for (const el of $$('input')) {
|
||||
const id = el.id || el.name;
|
||||
if (prefs.knownKeys.includes(id)) {
|
||||
prefs.reset(id);
|
||||
}
|
||||
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');
|
||||
const elDriveOptions = $('.sync-options .drive-options');
|
||||
/** @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, async () => {
|
||||
await API.sync.setDriveOptions(elCloud.value, getDriveOptions());
|
||||
await 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 getDriveOptions() {
|
||||
const result = {};
|
||||
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
|
||||
result[el.dataset.option] = el.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setDriveOptions(options) {
|
||||
for (const el of $$(`[data-drive=${elCloud.value}] [data-option]`)) {
|
||||
el.value = options[el.dataset.option] || '';
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(newStatus) {
|
||||
status = newStatus;
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
async 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],
|
||||
[elDriveOptions, 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;
|
||||
for (const el of elDriveOptions.children) {
|
||||
el.hidden = el.dataset.drive !== elCloud.value;
|
||||
}
|
||||
setDriveOptions(await API.sync.getDriveOptions(elCloud.value));
|
||||
}
|
||||
|
||||
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',
|
||||
contents: t.template.shortcutsFF.cloneNode(true),
|
||||
className: 'center-dialog pre-line',
|
||||
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')));
|
||||
box.oninput = onInput;
|
||||
setupLivePrefs($$('input', box).map(el => el.id));
|
||||
},
|
||||
});
|
||||
|
||||
function onInput() {
|
||||
const name = this.id.split('.')[1];
|
||||
const shortcut = this.value.trim();
|
||||
async function onInput({target: el}) {
|
||||
const name = el.id.split('.')[1];
|
||||
const shortcut = el.value.trim();
|
||||
if (!shortcut) {
|
||||
browser.commands.reset(name).catch(ignoreChromeError);
|
||||
this.setCustomValidity('');
|
||||
el.setCustomValidity('');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
browser.commands.update({name, shortcut}).then(
|
||||
() => this.setCustomValidity(''),
|
||||
err => this.setCustomValidity(err)
|
||||
);
|
||||
await browser.commands.update({name, shortcut});
|
||||
el.setCustomValidity('');
|
||||
} catch (err) {
|
||||
this.setCustomValidity(err);
|
||||
el.setCustomValidity(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user