tweak editor (#1063)

* also apply live-preview if an unsaved style was disabled

* use box-shadow instead of outline for focus everywhere

* allow focus outline on click in text/search input or textarea

* search inputs should use the same style as text inputs

* also use box-shadow focus on delete buttons

* remove URLSearchParams workaround, not needed since Chrome 55

* use `once` in addEventListener, available since Chrome 55

* update USO bug workarounds, remove obsolete ones

* ping/pong to fix openURL with `message` in FF

* use unprefixed CSS filter, available since Chrome 53

* use unprefixed CSS user-select, available since Chrome 54

* focus tweaks

* also use text query in inline search for Stylus category

* use event.key, available since Chrome 51

Co-authored-by: narcolepticinsomniac
This commit is contained in:
tophf 2020-10-13 21:14:54 +03:00 committed by GitHub
parent 60fc6f2456
commit 9e487b03e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 240 additions and 388 deletions

View File

@ -66,11 +66,13 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
/* Same as openURL, the only extra prop in `opts` is `message` - it'll be sent when the tab is ready, /* Same as openURL, the only extra prop in `opts` is `message` - it'll be sent when the tab is ready,
which is needed in the popup, otherwise another extension could force the tab to open in foreground which is needed in the popup, otherwise another extension could force the tab to open in foreground
thus auto-closing the popup (in Chrome at least) and preventing the sendMessage code from running */ thus auto-closing the popup (in Chrome at least) and preventing the sendMessage code from running */
openURL(opts) { async openURL(opts) {
const {message} = opts; const tab = await openURL(opts);
return openURL(opts) // will pass the resolved value untouched when `message` is absent or falsy if (opts.message) {
.then(message && (tab => tab.status === 'complete' ? tab : onTabReady(tab))) await onTabReady(tab);
.then(message && (tab => msg.sendTab(tab.id, opts.message))); await msg.sendTab(tab.id, opts.message);
}
return tab;
function onTabReady(tab) { function onTabReady(tab) {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
setTimeout(function ping(numTries = 10, delay = 100) { setTimeout(function ping(numTries = 10, delay = 100) {
@ -297,13 +299,10 @@ function openEditor(params) {
'url-prefix'?: String 'url-prefix'?: String
} }
*/ */
const searchParams = new URLSearchParams(); const u = new URL(chrome.runtime.getURL('edit.html'));
for (const key in params) { u.search = new URLSearchParams(params);
searchParams.set(key, params[key]);
}
const search = searchParams.toString();
return openURL({ return openURL({
url: 'edit.html' + (search && `?${search}`), url: `${u}`,
newWindow: prefs.get('openEditInWindow'), newWindow: prefs.get('openEditInWindow'),
windowPosition: prefs.get('windowPosition'), windowPosition: prefs.get('windowPosition'),
currentWindow: null currentWindow: null

View File

@ -37,7 +37,7 @@ const tokenManager = (() => {
scopes: ['https://www.googleapis.com/auth/drive.appdata'], scopes: ['https://www.googleapis.com/auth/drive.appdata'],
revoke: token => { revoke: token => {
const params = {token}; const params = {token};
return postQuery(`https://accounts.google.com/o/oauth2/revoke?${stringifyQuery(params)}`); return postQuery(`https://accounts.google.com/o/oauth2/revoke?${new URLSearchParams(params)}`);
} }
}, },
onedrive: { onedrive: {
@ -137,14 +137,6 @@ const tokenManager = (() => {
}); });
} }
function stringifyQuery(obj) {
const search = new URLSearchParams();
for (const key of Object.keys(obj)) {
search.set(key, obj[key]);
}
return search.toString();
}
function authUser(name, k, interactive = false) { function authUser(name, k, interactive = false) {
const provider = AUTH[name]; const provider = AUTH[name];
const state = Math.random().toFixed(8).slice(2); const state = Math.random().toFixed(8).slice(2);
@ -160,7 +152,7 @@ const tokenManager = (() => {
if (provider.authQuery) { if (provider.authQuery) {
Object.assign(query, provider.authQuery); Object.assign(query, provider.authQuery);
} }
const url = `${provider.authURL}?${stringifyQuery(query)}`; const url = `${provider.authURL}?${new URLSearchParams(query)}`;
return webextLaunchWebAuthFlow({ return webextLaunchWebAuthFlow({
url, url,
interactive, interactive,
@ -211,11 +203,9 @@ const tokenManager = (() => {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
} },
body: body ? new URLSearchParams(body) : null,
}; };
if (body) {
options.body = stringifyQuery(body);
}
return fetch(url, options) return fetch(url, options)
.then(r => { .then(r => {
if (r.ok) { if (r.ok) {

View File

@ -1,7 +1,11 @@
/* global cloneInto msg API */ /* global cloneInto msg API */
'use strict'; 'use strict';
(() => { // eslint-disable-next-line no-unused-expressions
/^\/styles\/(\d+)(\/([^/]*))?([?#].*)?$/.test(location.pathname) && (() => {
const styleId = RegExp.$1;
const pageEventId = `${performance.now()}${Math.random()}`;
window.dispatchEvent(new CustomEvent(chrome.runtime.id + '-install')); window.dispatchEvent(new CustomEvent(chrome.runtime.id + '-install'));
window.addEventListener(chrome.runtime.id + '-install', orphanCheck, true); window.addEventListener(chrome.runtime.id + '-install', orphanCheck, true);
@ -17,35 +21,18 @@
}, '*'); }, '*');
}); });
let gotBody = false;
let currentMd5; let currentMd5;
new MutationObserver(observeDOM).observe(document.documentElement, { const md5Url = getMeta('stylish-md5-url') || `https://update.userstyles.org/${styleId}.md5`;
childList: true, Promise.all([
subtree: true, API.findStyle({md5Url}),
}); getResource(md5Url),
observeDOM(); onDOMready(),
]).then(checkUpdatability);
function observeDOM() { document.documentElement.appendChild(
if (!gotBody) { Object.assign(document.createElement('script'), {
if (!document.body) return; textContent: `(${inPageContext})('${pageEventId}')`,
gotBody = true; }));
// TODO: remove the following statement when USO pagination title is fixed
document.title = document.title.replace(/^(\d+)&\w+=/, '#$1: ');
const md5Url = getMeta('stylish-md5-url') || location.href;
Promise.all([
API.findStyle({md5Url}),
getResource(md5Url)
])
.then(checkUpdatability);
}
if (document.getElementById('install_button')) {
onDOMready().then(() => {
requestAnimationFrame(() => {
sendEvent(sendEvent.lastEvent);
});
});
}
}
function onMessage(msg) { function onMessage(msg) {
switch (msg.method) { switch (msg.method) {
@ -72,7 +59,7 @@
function checkUpdatability([installedStyle, md5]) { function checkUpdatability([installedStyle, md5]) {
// TODO: remove the following statement when USO is fixed // TODO: remove the following statement when USO is fixed
document.dispatchEvent(new CustomEvent('stylusFixBuggyUSOsettings', { document.dispatchEvent(new CustomEvent(pageEventId, {
detail: installedStyle && installedStyle.updateUrl, detail: installedStyle && installedStyle.updateUrl,
})); }));
currentMd5 = md5; currentMd5 = md5;
@ -141,7 +128,6 @@
}); });
} }
function onClick(event) { function onClick(event) {
if (onClick.processing || !orphanCheck()) { if (onClick.processing || !orphanCheck()) {
return; return;
@ -227,13 +213,11 @@
} }
} }
function getMeta(name) { function getMeta(name) {
const e = document.querySelector(`link[rel="${name}"]`); const e = document.querySelector(`link[rel="${name}"]`);
return e ? e.getAttribute('href') : null; return e ? e.getAttribute('href') : null;
} }
function getResource(url, options) { function getResource(url, options) {
if (url.startsWith('#')) { if (url.startsWith('#')) {
return Promise.resolve(document.getElementById(url.slice(1)).textContent); return Promise.resolve(document.getElementById(url.slice(1)).textContent);
@ -280,7 +264,6 @@
.catch(() => null); .catch(() => null);
} }
function styleSectionsEqual({sections: a}, {sections: b}) { function styleSectionsEqual({sections: a}, {sections: b}) {
if (!a || !b) { if (!a || !b) {
return undefined; return undefined;
@ -318,20 +301,12 @@
} }
} }
function onDOMready() { function onDOMready() {
if (document.readyState !== 'loading') { return document.readyState !== 'loading'
return Promise.resolve(); ? Promise.resolve()
} : new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve, {once: true}));
return new Promise(resolve => {
document.addEventListener('DOMContentLoaded', function _() {
document.removeEventListener('DOMContentLoaded', _);
resolve();
});
});
} }
function openSettings(countdown = 10e3) { function openSettings(countdown = 10e3) {
const button = document.querySelector('.customize_button'); const button = document.querySelector('.customize_button');
if (button) { if (button) {
@ -349,12 +324,12 @@
} }
} }
function orphanCheck() { function orphanCheck() {
// TODO: switch to install-hook-usercss.js impl, and remove explicit orphanCheck() calls try {
if (chrome.i18n && chrome.i18n.getUILanguage()) { if (chrome.i18n.getUILanguage()) {
return true; return true;
} }
} catch (e) {}
// In Chrome content script is orphaned on an extension update/reload // In Chrome content script is orphaned on an extension update/reload
// so we need to detach event listeners // so we need to detach event listeners
window.removeEventListener(chrome.runtime.id + '-install', orphanCheck, true); window.removeEventListener(chrome.runtime.id + '-install', orphanCheck, true);
@ -366,132 +341,56 @@
} }
})(); })();
// run in page context function inPageContext(eventId) {
document.documentElement.appendChild(document.createElement('script')).text = '(' + ( document.currentScript.remove();
() => { const origMethods = {
document.currentScript.remove(); json: Response.prototype.json,
byId: document.getElementById,
// spoof Stylish extension presence in Chrome };
if (window.chrome && chrome.app) { let vars;
const realImage = window.Image; // USO bug workaround: prevent errors in console after install and busy cursor
window.Image = function Image(...args) { document.getElementById = id =>
return new Proxy(new realImage(...args), { origMethods.byId.call(document, id) ||
get(obj, key) { (/^(stylish-code|stylish-installed-style-installed-\w+|post-install-ad|style-install-unknown)$/.test(id)
return obj[key]; ? Object.assign(document.createElement('p'), {className: 'afterdownload-ad'})
}, : null);
set(obj, key, value) { // USO bug workaround: use the actual image data in customized settings
if (key === 'src' && /^chrome-extension:/i.test(value)) { document.addEventListener(eventId, ({detail}) => {
setTimeout(() => typeof obj.onload === 'function' && obj.onload()); vars = /\?/.test(detail) && new URL(detail).searchParams;
} else { if (!vars) Response.prototype.json = origMethods.json;
obj[key] = value; }, {once: true});
} Response.prototype.json = async function () {
return true; const json = await origMethods.json.apply(this, arguments);
}, if (vars && json && Array.isArray(json.style_settings)) {
}); Response.prototype.json = origMethods.json;
}; const images = new Map();
} for (const ss of json.style_settings) {
const value = vars.get('ik-' + ss.install_key);
// USO bug workaround: use the actual style settings in API response if (value && ss.setting_type === 'image' && ss.style_setting_options) {
let settings; let isListed;
const originalResponseJson = Response.prototype.json; for (const opt of ss.style_setting_options) {
document.addEventListener('stylusFixBuggyUSOsettings', function _({detail}) { isListed |= opt.default = (opt.value === value);
document.removeEventListener('stylusFixBuggyUSOsettings', _); }
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425) images.set(ss.install_key, {url: value, isListed});
settings = /\?/.test(detail) && new URLSearchParams(new URL(detail).search.replace(/^\?/, '')); }
if (!settings) {
Response.prototype.json = originalResponseJson;
} }
}); if (images.size) {
Response.prototype.json = function (...args) { new MutationObserver((_, observer) => {
return originalResponseJson.call(this, ...args).then(json => { if (document.getElementById('style-settings')) {
if (!settings || typeof ((json || {}).style_settings || {}).every !== 'function') {
return json;
}
Response.prototype.json = originalResponseJson;
const images = new Map();
for (const jsonSetting of json.style_settings) {
let value = settings.get('ik-' + jsonSetting.install_key);
if (!value
|| !jsonSetting.style_setting_options
|| !jsonSetting.style_setting_options[0]) {
continue;
}
if (value.startsWith('ik-')) {
value = value.replace(/^ik-/, '');
const defaultItem = jsonSetting.style_setting_options.find(item => item.default);
if (!defaultItem || defaultItem.install_key !== value) {
if (defaultItem) {
defaultItem.default = false;
}
jsonSetting.style_setting_options.some(item => {
if (item.install_key === value) {
item.default = true;
return true;
}
});
}
} else if (jsonSetting.setting_type === 'image') {
jsonSetting.style_setting_options.some(item => {
if (item.default) {
item.default = false;
return true;
}
});
images.set(jsonSetting.install_key, value);
} else {
const item = jsonSetting.style_setting_options[0];
if (item.value !== value && item.install_key === 'placeholder') {
item.value = value;
}
}
}
if (images.size) {
new MutationObserver((_, observer) => {
if (!document.getElementById('style-settings')) {
return;
}
observer.disconnect(); observer.disconnect();
for (const [name, url] of images.entries()) { for (const [name, {url, isListed}] of images) {
const elRadio = document.querySelector(`input[name="ik-${name}"][value="user-url"]`); const elRadio = document.querySelector(`input[name="ik-${name}"][value="user-url"]`);
const elUrl = elRadio && document.getElementById(elRadio.id.replace('url-choice', 'user-url')); const elUrl = elRadio &&
document.getElementById(elRadio.id.replace('url-choice', 'user-url'));
if (elUrl) { if (elUrl) {
elRadio.checked = !isListed;
elUrl.value = url; elUrl.value = url;
} }
} }
}).observe(document, {childList: true, subtree: true}); }
} }).observe(document, {childList: true, subtree: true});
return json;
});
};
}
) + `)('${chrome.runtime.getURL('').slice(0, -1)}')`;
// TODO: remove the following statement when USO pagination is fixed
if (location.search.includes('category=')) {
document.addEventListener('DOMContentLoaded', function _() {
document.removeEventListener('DOMContentLoaded', _);
new MutationObserver((_, observer) => {
if (!document.getElementById('pagination')) {
return;
} }
observer.disconnect();
const category = '&' + location.search.match(/category=[^&]+/)[0];
const links = document.querySelectorAll('#pagination a[href*="page="]:not([href*="category="])');
for (let i = 0; i < links.length; i++) {
links[i].href += category;
}
}).observe(document, {childList: true, subtree: true});
});
}
if (/^https?:\/\/userstyles\.org\/styles\/\d{3,}/.test(location.href)) {
new MutationObserver((_, observer) => {
const cssButton = document.getElementsByClassName('css_button');
if (cssButton.length) {
// Click on the "Show CSS Code" button to workaround the JS error
cssButton[0].click();
cssButton[0].click();
observer.disconnect();
} }
}).observe(document, {childList: true, subtree: true}); return json;
};
} }

View File

@ -7,6 +7,12 @@
} }
.CodeMirror { .CodeMirror {
border: solid #CCC 1px; border: solid #CCC 1px;
transition: box-shadow .1s;
}
#stylus#stylus .CodeMirror {
/* Using a specificity hack to override userstyles */
/* Not using the ring-color hack as it became ugly in new Chrome */
outline: none !important;
} }
.CodeMirror-lint-mark-warning { .CodeMirror-lint-mark-warning {
background: none; background: none;
@ -14,9 +20,6 @@
.CodeMirror-dialog { .CodeMirror-dialog {
-webkit-animation: highlight 3s cubic-bezier(.18, .02, 0, .94); -webkit-animation: highlight 3s cubic-bezier(.18, .02, 0, .94);
} }
.CodeMirror-focused {
outline: #7dadd9 auto 1px; /* not using the ring-color hack as it became ugly in new Chrome */
}
.CodeMirror-bookmark { .CodeMirror-bookmark {
background: linear-gradient(to right, currentColor, transparent); background: linear-gradient(to right, currentColor, transparent);
position: absolute; position: absolute;

View File

@ -611,6 +611,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
right: 4px; right: 4px;
top: .5em; top: .5em;
} }
#help-popup input[type="search"] {
margin: 3px;
}
.keymap-list { .keymap-list {
font-size: 12px; font-size: 12px;
@ -788,6 +791,10 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
justify-items: normal; justify-items: normal;
} }
.usercss .CodeMirror-focused {
box-shadow: none;
}
html:not(.usercss) .usercss-only, html:not(.usercss) .usercss-only,
.usercss #mozilla-format-container, .usercss #mozilla-format-container,
.usercss #sections > h2 { .usercss #sections > h2 {

View File

@ -352,8 +352,7 @@ function isUsercss(style) {
} }
function initStyleData() { function initStyleData() {
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425) const params = new URLSearchParams(location.search);
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
const id = Number(params.get('id')); const id = Number(params.get('id'));
const createEmptyStyle = () => ({ const createEmptyStyle = () => ({
name: params.get('domain') || name: params.get('domain') ||
@ -409,7 +408,7 @@ function showHelp(title = '', body) {
!event || !event ||
event.type === 'click' || event.type === 'click' ||
( (
event.which === 27 && event.key === 'Escape' &&
!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey &&
!$('.CodeMirror-hints, #message-box') && !$('.CodeMirror-hints, #message-box') &&
( (
@ -470,7 +469,7 @@ function showCodeMirrorPopup(title, html, options) {
popup.style.pointerEvents = 'auto'; popup.style.pointerEvents = 'auto';
const onKeyDown = event => { const onKeyDown = event => {
if (event.which === 9 && !event.ctrlKey && !event.altKey && !event.metaKey) { if (event.key === 'Tab' && !event.ctrlKey && !event.altKey && !event.metaKey) {
const search = $('#search-replace-dialog'); const search = $('#search-replace-dialog');
const area = search && search.contains(document.activeElement) ? search : popup; const area = search && search.contains(document.activeElement) ? search : popup;
moveFocus(area, event.shiftKey ? -1 : 1); moveFocus(area, event.shiftKey ? -1 : 1);
@ -479,13 +478,12 @@ function showCodeMirrorPopup(title, html, options) {
}; };
window.addEventListener('keydown', onKeyDown, true); window.addEventListener('keydown', onKeyDown, true);
window.addEventListener('closeHelp', function _() { window.addEventListener('closeHelp', () => {
window.removeEventListener('closeHelp', _);
window.removeEventListener('keydown', onKeyDown, true); window.removeEventListener('keydown', onKeyDown, true);
document.documentElement.style.removeProperty('pointer-events'); document.documentElement.style.removeProperty('pointer-events');
rerouteHotkeys(true); rerouteHotkeys(true);
cm = popup.codebox = null; cm = popup.codebox = null;
}); }, {once: true});
return popup; return popup;
} }

View File

@ -53,11 +53,10 @@
cm.on('changes', updateButtonState); cm.on('changes', updateButtonState);
rerouteHotkeys(false); rerouteHotkeys(false);
window.addEventListener('closeHelp', function _() { window.addEventListener('closeHelp', () => {
window.removeEventListener('closeHelp', _);
rerouteHotkeys(true); rerouteHotkeys(true);
cm = null; cm = null;
}); }, {once: true});
loadScript([ loadScript([
'/vendor/codemirror/mode/javascript/javascript.js', '/vendor/codemirror/mode/javascript/javascript.js',

View File

@ -10,7 +10,7 @@ function createLivePreview(preprocess) {
const errorContainer = $('#preview-errors'); const errorContainer = $('#preview-errors');
prefs.subscribe(['editor.livePreview'], (key, value) => { prefs.subscribe(['editor.livePreview'], (key, value) => {
if (value && data && data.id && data.enabled) { if (value && data && data.id && (data.enabled || editor.dirty.has('enabled'))) {
previewer = createPreviewer(); previewer = createPreviewer();
previewer.update(data); previewer.update(data);
} }

View File

@ -206,36 +206,32 @@ function createSection({
} }
function handleKeydown(cm, event) { function handleKeydown(cm, event) {
const key = event.which; if (event.shiftKey || event.altKey || event.metaKey) {
if (key < 37 || key > 40 || event.shiftKey || event.altKey || event.metaKey) {
return; return;
} }
const {key} = event;
const {line, ch} = cm.getCursor(); const {line, ch} = cm.getCursor();
switch (key) { switch (key) {
case 37: case 'ArrowLeft':
// arrow Left
if (line || ch) { if (line || ch) {
return; return;
} }
// fallthrough to arrow Up // fallthrough
case 38: case 'ArrowUp':
// arrow Up
cm = line === 0 && prevEditor(cm, false); cm = line === 0 && prevEditor(cm, false);
if (!cm) { if (!cm) {
return; return;
} }
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch); cm.setCursor(cm.doc.size - 1, key === 'ArrowLeft' ? 1e20 : ch);
break; break;
case 39: case 'ArrowRight':
// arrow Right
if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) { if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) {
return; return;
} }
// fallthrough to arrow Down // fallthrough
case 40: case 'ArrowDown':
// arrow Down
cm = line === cm.doc.size - 1 && nextEditor(cm, false); cm = line === cm.doc.size - 1 && nextEditor(cm, false);
if (!cm) { if (!cm) {
return; return;
@ -245,13 +241,6 @@ function createSection({
cm.setCursor(0, 0); cm.setCursor(0, 0);
break; break;
} }
// FIXME: what is this?
// const animation = (cm.getSection().firstElementChild.getAnimations() || [])[0];
// if (animation) {
// animation.playbackRate = -1;
// animation.currentTime = 2000;
// animation.play();
// }
} }
function showAppliesToHelp(event) { function showAppliesToHelp(event) {

View File

@ -54,18 +54,20 @@ button:active {
border-color: hsl(0, 0%, 50%); border-color: hsl(0, 0%, 50%);
} }
input { input {
font: inherit; font: inherit;
border: 1px solid hsl(0, 0%, 66%);
transition: border-color .1s, box-shadow .1s;
} }
input:not([type]) { input:not([type]),
input[type=search] {
background: #fff; background: #fff;
color: #000; color: #000;
height: 22px; height: 22px;
min-height: 22px!important; min-height: 22px!important;
line-height: 22px; line-height: 22px;
padding: 0 3px; padding: 0 3px;
font: inherit;
border: 1px solid hsl(0, 0%, 66%); border: 1px solid hsl(0, 0%, 66%);
} }
@ -208,9 +210,19 @@ select[disabled] + .select-arrow {
display: none !important; display: none !important;
} }
:focus,
.CodeMirror-focused,
[data-focused-via-click] input[type="text"]:focus,
[data-focused-via-click] input[type="number"]:focus {
/* Using box-shadow instead of the ugly outline in new Chrome */
outline: none;
box-shadow: 0 0 0 1px hsl(180, 100%, 38%), 0 0 3px hsla(180, 100%, 60%, .5);
}
[data-focused-via-click] :focus, [data-focused-via-click] :focus,
[data-focused-via-click]:focus { [data-focused-via-click]:focus {
outline: none; outline: none;
box-shadow: none;
} }
@supports (-moz-appearance: none) { @supports (-moz-appearance: none) {

View File

@ -288,9 +288,7 @@ li {
#header:not(.meta-init) > *:not(.lds-spinner), #header:not(.meta-init) > *:not(.lds-spinner),
#header.meta-init > .lds-spinner { #header.meta-init > .lds-spinner {
-webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
@ -299,9 +297,7 @@ li {
#header.meta-init > * { #header.meta-init > * {
opacity: 1; opacity: 1;
transition: opacity .5s; transition: opacity .5s;
-webkit-user-select: auto;
-moz-user-select: auto; -moz-user-select: auto;
-ms-user-select: auto;
user-select: auto; user-select: auto;
} }

View File

@ -3,8 +3,7 @@
'use strict'; 'use strict';
(() => { (() => {
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425) const params = new URLSearchParams(location.search);
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
const tabId = params.has('tabId') ? Number(params.get('tabId')) : -1; const tabId = params.has('tabId') ? Number(params.get('tabId')) : -1;
const initialUrl = params.get('updateUrl'); const initialUrl = params.get('updateUrl');

View File

@ -20,6 +20,9 @@ for (const type of [NodeList, NamedNodeMap, HTMLCollection, HTMLAllCollection])
} }
} }
$.isTextLikeInput = el =>
el.localName === 'input' && /^(text|search|number)$/.test(el.type);
$.remove = (selector, base = document) => { $.remove = (selector, base = document) => {
const el = selector && typeof selector === 'string' ? $(selector, base) : selector; const el = selector && typeof selector === 'string' ? $(selector, base) : selector;
if (el) { if (el) {
@ -112,15 +115,9 @@ document.addEventListener('wheel', event => {
}); });
function onDOMready() { function onDOMready() {
if (document.readyState !== 'loading') { return document.readyState !== 'loading'
return Promise.resolve(); ? Promise.resolve()
} : new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve, {once: true}));
return new Promise(resolve => {
document.addEventListener('DOMContentLoaded', function _() {
document.removeEventListener('DOMContentLoaded', _);
resolve();
});
});
} }
@ -144,8 +141,7 @@ function animateElement(
onComplete, onComplete,
} = {}) { } = {}) {
return element && new Promise(resolve => { return element && new Promise(resolve => {
element.addEventListener('animationend', function _() { element.addEventListener('animationend', () => {
element.removeEventListener('animationend', _);
element.classList.remove( element.classList.remove(
className, className,
// In Firefox, `resolve()` might be called one frame later. // In Firefox, `resolve()` might be called one frame later.
@ -157,7 +153,7 @@ function animateElement(
onComplete.call(element); onComplete.call(element);
} }
resolve(); resolve();
}); }, {once: true});
element.classList.add(className); element.classList.add(className);
}); });
} }
@ -355,20 +351,23 @@ function focusAccessibility() {
'a', 'a',
'button', 'button',
'input', 'input',
'textarea',
'label', 'label',
'select', 'select',
'summary', 'summary',
]; ];
// try to find a focusable parent for this many parentElement jumps: // try to find a focusable parent for this many parentElement jumps:
const GIVE_UP_DEPTH = 4; const GIVE_UP_DEPTH = 4;
// allow outline on text/search inputs in addition to textareas
const isOutlineAllowed = el =>
!focusAccessibility.ELEMENTS.includes(el.localName) ||
$.isTextLikeInput(el);
addEventListener('mousedown', suppressOutlineOnClick, {passive: true}); addEventListener('mousedown', suppressOutlineOnClick, {passive: true});
addEventListener('keydown', keepOutlineOnTab, {passive: true}); addEventListener('keydown', keepOutlineOnTab, {passive: true});
function suppressOutlineOnClick({target}) { function suppressOutlineOnClick({target}) {
for (let el = target, i = 0; el && i++ < GIVE_UP_DEPTH; el = el.parentElement) { for (let el = target, i = 0; el && i++ < GIVE_UP_DEPTH; el = el.parentElement) {
if (focusAccessibility.ELEMENTS.includes(el.localName)) { if (!isOutlineAllowed(el)) {
focusAccessibility.lastFocusedViaClick = true; focusAccessibility.lastFocusedViaClick = true;
if (el.dataset.focusedViaClick === undefined) { if (el.dataset.focusedViaClick === undefined) {
el.dataset.focusedViaClick = ''; el.dataset.focusedViaClick = '';
@ -379,7 +378,7 @@ function focusAccessibility() {
} }
function keepOutlineOnTab(event) { function keepOutlineOnTab(event) {
if (event.which === 9) { if (event.key === 'Tab') {
focusAccessibility.lastFocusedViaClick = false; focusAccessibility.lastFocusedViaClick = false;
setTimeout(keepOutlineOnTab, 0, true); setTimeout(keepOutlineOnTab, 0, true);
return; return;
@ -387,7 +386,7 @@ function focusAccessibility() {
return; return;
} }
let el = document.activeElement; let el = document.activeElement;
if (!el || !focusAccessibility.ELEMENTS.includes(el.localName)) { if (!el || isOutlineAllowed(el)) {
return; return;
} }
if (el.dataset.focusedViaClick !== undefined) { if (el.dataset.focusedViaClick !== undefined) {

View File

@ -62,5 +62,20 @@ self.INJECTED !== 1 && (() => {
} }
} }
if (!(new URLSearchParams({foo: 1})).get('foo')) {
// TODO: remove when minimum_chrome_version >= 61
window.URLSearchParams = class extends URLSearchParams {
constructor(init) {
if (init && typeof init === 'object') {
super();
for (const [key, val] of Object.entries(init)) {
this.set(key, val);
}
} else {
super(...arguments);
}
}
};
}
//#endregion //#endregion
})(); })();

View File

@ -31,18 +31,9 @@ const router = (() => {
} }
function updateSearch(key, value) { function updateSearch(key, value) {
const search = new URLSearchParams(location.search.replace(/^\?/, '')); const u = new URL(location);
if (!value) { u.searchParams[value ? 'set' : 'delete'](key, value);
search.delete(key); history.replaceState(history.state, null, `${u}`);
} else {
search.set(key, value);
}
const finalSearch = search.toString();
if (finalSearch) {
history.replaceState(history.state, null, `?${finalSearch}${location.hash}`);
} else {
history.replaceState(history.state, null, `${location.pathname}${location.hash}`);
}
update(true); update(true);
} }
@ -66,7 +57,7 @@ const router = (() => {
} }
function getSearch(key) { function getSearch(key) {
return new URLSearchParams(location.search.replace(/^\?/, '')).get(key); return new URLSearchParams(location.search).get(key);
} }
function update(replace) { function update(replace) {
@ -86,8 +77,7 @@ const router = (() => {
if (options.hash) { if (options.hash) {
state = options.hash === location.hash; state = options.hash === location.hash;
} else if (options.search) { } else if (options.search) {
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425) const search = new URLSearchParams(location.search);
const search = new URLSearchParams(location.search.replace(/^\?/, ''));
state = options.search.map(key => search.get(key)); state = options.search.map(key => search.get(key));
} }
if (!deepEqual(state, options.currentState)) { if (!deepEqual(state, options.currentState)) {

View File

@ -82,7 +82,7 @@ const loadScript = (() => {
for (const {addedNodes} of mutations) { for (const {addedNodes} of mutations) {
for (const n of addedNodes) { for (const n of addedNodes) {
if (n.src && getSubscribersForSrc(n.src)) { if (n.src && getSubscribersForSrc(n.src)) {
n.addEventListener('load', notifySubscribers); n.addEventListener('load', notifySubscribers, {once: true});
} }
} }
} }
@ -97,7 +97,6 @@ const loadScript = (() => {
} }
function notifySubscribers(event) { function notifySubscribers(event) {
this.removeEventListener('load', notifySubscribers);
for (let data; (data = getSubscribersForSrc(this.src));) { for (let data; (data = getSubscribersForSrc(this.src));) {
data.listeners.forEach(fn => fn(event)); data.listeners.forEach(fn => fn(event));
if (emptyAfterCleanup(data.suffix)) { if (emptyAfterCleanup(data.suffix)) {

View File

@ -24,12 +24,12 @@ onDOMready().then(() => {
document.body.appendChild(input); document.body.appendChild(input);
window.addEventListener('keydown', maybeRefocus, true); window.addEventListener('keydown', maybeRefocus, true);
function incrementalSearch({which}, immediately) { function incrementalSearch({key}, immediately) {
if (!immediately) { if (!immediately) {
debounce(incrementalSearch, 100, {}, true); debounce(incrementalSearch, 100, {}, true);
return; return;
} }
const direction = which === 38 ? -1 : which === 40 ? 1 : 0; const direction = key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0;
const text = input.value.toLocaleLowerCase(); const text = input.value.toLocaleLowerCase();
if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) { if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) {
prevText = text; prevText = text;
@ -76,40 +76,31 @@ onDOMready().then(() => {
if (event.altKey || event.metaKey || $('#message-box')) { if (event.altKey || event.metaKey || $('#message-box')) {
return; return;
} }
const inTextInput = event.target.matches('[type=text], [type=search], [type=number]'); const inTextInput = $.isTextLikeInput(event.target);
const {which: k, key} = event; const {key, code, ctrlKey: ctrl} = event;
// focus search field on "/" or Ctrl-F key // `code` is independent of the current keyboard language
if (event.ctrlKey if ((code === 'KeyF' && ctrl && !event.shiftKey) ||
? (event.code === 'KeyF' || !event.code && k === 70) && !event.shiftKey (code === 'Slash' || key === '/') && !ctrl && !inTextInput) {
: (key === '/' || !key && k === 191 && !event.shiftKey) && !inTextInput) { // focus search field on "/" or Ctrl-F key
event.preventDefault(); event.preventDefault();
$('#search').focus(); $('#search').focus();
return; return;
} }
if (event.ctrlKey || inTextInput) { if (ctrl || inTextInput) {
return; return;
} }
const time = performance.now(); const time = performance.now();
if ( if (key.length === 1) {
// 0-9
k >= 48 && k <= 57 ||
// a-z
k >= 65 && k <= 90 ||
// numpad keys
k >= 96 && k <= 111 ||
// marks
k >= 186
) {
input.focus(); input.focus();
if (time - prevTime > 1000) { if (time - prevTime > 1000) {
input.value = ''; input.value = '';
} }
prevTime = time; prevTime = time;
} else } else
if (k === 13 && focusedLink) { if (key === 'Enter' && focusedLink) {
focusedLink.dispatchEvent(new MouseEvent('click', {bubbles: true})); focusedLink.dispatchEvent(new MouseEvent('click', {bubbles: true}));
} else } else
if ((k === 38 || k === 40) && !event.shiftKey && if ((key === 'ArrowUp' || key === 'ArrowDown') && !event.shiftKey &&
time - prevTime < 5000 && incrementalSearch(event, true)) { time - prevTime < 5000 && incrementalSearch(event, true)) {
prevTime = time; prevTime = time;
} else } else

View File

@ -550,8 +550,6 @@ a:hover {
.newUI .update-done .updated svg { .newUI .update-done .updated svg {
top: -4px; top: -4px;
position: relative; position: relative;
/* unprefixed since Chrome 53 */
-webkit-filter: drop-shadow(0 4px 0 currentColor);
filter: drop-shadow(0 5px 0 currentColor); filter: drop-shadow(0 5px 0 currentColor);
} }
@ -663,8 +661,6 @@ a:hover {
margin-left: -20px; margin-left: -20px;
margin-right: 4px; margin-right: 4px;
transition: opacity .5s, filter .5s; transition: opacity .5s, filter .5s;
/* unprefixed since Chrome 53 */
-webkit-filter: grayscale(1);
filter: grayscale(1); filter: grayscale(1);
/* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */ /* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */
backface-visibility: hidden; backface-visibility: hidden;
@ -682,9 +678,7 @@ a:hover {
.newUI .entry:hover .target img { .newUI .entry:hover .target img {
opacity: 1; opacity: 1;
/* unprefixed since Chrome 53 */ filter: none;
-webkit-filter: grayscale(0);
filter: grayscale(0);
} }
/* Default, no update buttons */ /* Default, no update buttons */

View File

@ -631,13 +631,11 @@ function switchUI({styleOnly} = {}) {
} }
` + (newUI.faviconsGray ? ` ` + (newUI.faviconsGray ? `
.newUI .target img { .newUI .target img {
-webkit-filter: grayscale(1);
filter: grayscale(1); filter: grayscale(1);
opacity: .25; opacity: .25;
} }
` : ` ` : `
.newUI .target img { .newUI .target img {
-webkit-filter: none;
filter: none; filter: none;
opacity: 1; opacity: 1;
} }

View File

@ -135,12 +135,7 @@
} }
.danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus { .danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
outline: red auto 1px; box-shadow: 0 0 0 1px red; /* Using box-shadow instead of the ugly outline in new Chrome */
}
/* FF ignores color with 'auto' */
.firefox .danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
outline: red solid 1px;
} }
.non-windows #message-box-buttons { .non-windows #message-box-buttons {

View File

@ -62,28 +62,28 @@ function messageBox({
resolveWith({button: this.buttonIndex}); resolveWith({button: this.buttonIndex});
}, },
key(event) { key(event) {
const {which, shiftKey, ctrlKey, altKey, metaKey, target} = event; const {key, shiftKey, ctrlKey, altKey, metaKey, target} = event;
if (shiftKey && which !== 9 || ctrlKey || altKey || metaKey) { if (shiftKey && key !== 'Tab' || ctrlKey || altKey || metaKey) {
return; return;
} }
switch (which) { switch (key) {
case 13: case 'Enter':
if (target.closest(focusAccessibility.ELEMENTS.join(','))) { if (target.closest(focusAccessibility.ELEMENTS.join(','))) {
return; return;
} }
break; break;
case 27: case 'Escape':
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
break; break;
case 9: case 'Tab':
moveFocus(messageBox.element, shiftKey ? -1 : 1); moveFocus(messageBox.element, shiftKey ? -1 : 1);
event.preventDefault(); event.preventDefault();
return; return;
default: default:
return; return;
} }
resolveWith(which === 13 ? {enter: true} : {esc: true}); resolveWith(key === 'Enter' ? {enter: true} : {esc: true});
}, },
scroll() { scroll() {
scrollTo(blockScroll.x, blockScroll.y); scrollTo(blockScroll.x, blockScroll.y);

View File

@ -3,9 +3,8 @@
.onoffswitch { .onoffswitch {
position: relative; position: relative;
margin: 1ex 0; margin: 1ex 0;
-webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
} }
.onoffswitch input { .onoffswitch input {
@ -17,6 +16,7 @@
bottom: -10px; bottom: -10px;
left: -10px; left: -10px;
width: calc(100% + 12px); width: calc(100% + 12px);
border: 0;
} }
#message-box .onoffswitch input { #message-box .onoffswitch input {

View File

@ -298,7 +298,7 @@ function customizeHotkeys() {
} }
window.onkeydown = event => { window.onkeydown = event => {
if (event.keyCode === 27) { if (event.key === 'Escape') {
top.dispatchEvent(new CustomEvent('closeOptions')); top.dispatchEvent(new CustomEvent('closeOptions'));
} }
}; };

View File

@ -9,14 +9,13 @@ const hotkeys = (() => {
let enabled = false; let enabled = false;
let ready = false; let ready = false;
window.addEventListener('showStyles:done', function _() { window.addEventListener('showStyles:done', () => {
window.removeEventListener('showStyles:done', _);
togglablesShown = true; togglablesShown = true;
togglables = getTogglables(); togglables = getTogglables();
ready = true; ready = true;
setState(true); setState(true);
initHotkeyInfo(); initHotkeyInfo();
}); }, {once: true});
window.addEventListener('resize', adjustInfoPosition); window.addEventListener('resize', adjustInfoPosition);
@ -38,40 +37,27 @@ const hotkeys = (() => {
return; return;
} }
let entry; let entry;
const {which: k, key, code} = event; let {key, code, shiftKey} = event;
if (code.startsWith('Digit') || code.startsWith('Numpad') && code.length === 7) { if (key >= '0' && key <= '9') {
entry = entries[(Number(key) || 10) - 1];
} else if (code >= 'Digit0' && code <= 'Digit9') {
entry = entries[(Number(code.slice(-1)) || 10) - 1]; entry = entries[(Number(code.slice(-1)) || 10) - 1];
} else if (key === '`' || key === '*' || code === 'Backquote' || code === 'NumpadMultiply') {
} else if (
code === 'Backquote' || code === 'NumpadMultiply' ||
key && (key === '`' || key === '*') ||
k === 192 || k === 106) {
invertTogglables(); invertTogglables();
} else if (key === '-' || code === 'NumpadSubtract') {
} else if (
code === 'NumpadSubtract' ||
key && key === '-' ||
k === 109) {
toggleState(entries, 'enabled', false); toggleState(entries, 'enabled', false);
} else if (key === '+' || code === 'NumpadAdd') {
} else if (
code === 'NumpadAdd' ||
key && key === '+' ||
k === 107) {
toggleState(entries, 'disabled', true); toggleState(entries, 'disabled', true);
} else if (key.length === 1) {
} else if ( shiftKey = false; // typing ':' etc. needs Shift so we hide it here to avoid opening editor
// any single character key = key.toLocaleLowerCase();
key && key.length === 1 || entry = [...entries].find(e => e.innerText.toLocaleLowerCase().startsWith(key));
k >= 65 && k <= 90) {
const letter = new RegExp(key ? '^' + key : '^\\x' + k.toString(16), 'i');
entry = [...entries].find(entry => letter.test(entry.textContent));
} }
if (!entry) { if (!entry) {
return; return;
} }
const target = $(event.shiftKey ? '.style-edit-link' : '.checker', entry); const target = $(shiftKey ? '.style-edit-link' : '.checker', entry);
target.dispatchEvent(new MouseEvent('click', {cancelable: true})); target.dispatchEvent(new MouseEvent('click', {cancelable: true}));
} }

View File

@ -342,11 +342,7 @@ a.configure[target="_blank"] .svg-icon.config {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus { #confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
outline: red auto 1px; box-shadow: 0 0 0 1px red; /* Using box-shadow instead of the ugly outline in new Chrome */
}
/* FF ignores color with 'auto' */
.firefox #confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
outline: red solid 1px;
} }
.menu-items-wrapper { .menu-items-wrapper {
width: 80%; width: 80%;

View File

@ -507,16 +507,15 @@ Object.assign(handleEvent, {
window.onkeydown = event => { window.onkeydown = event => {
const close = $('.menu-close', entry); const close = $('.menu-close', entry);
const checkbox = $('.exclude-by-domain-checkbox', entry); const checkbox = $('.exclude-by-domain-checkbox', entry);
const keyCode = event.keyCode || event.which; if (document.activeElement === close && (event.key === 'Tab') && !event.shiftKey) {
if (document.activeElement === close && (keyCode === 9) && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
checkbox.focus(); checkbox.focus();
} }
if (document.activeElement === checkbox && (keyCode === 9) && event.shiftKey) { if (document.activeElement === checkbox && (event.key === 'Tab') && event.shiftKey) {
event.preventDefault(); event.preventDefault();
close.focus(); close.focus();
} }
if (keyCode === 27) { if (event.key === 'Escape') {
event.preventDefault(); event.preventDefault();
close.click(); close.click();
} }
@ -542,20 +541,20 @@ Object.assign(handleEvent, {
const close = $('.menu-close', entry); const close = $('.menu-close', entry);
const checkbox = $('.exclude-by-domain-checkbox', entry); const checkbox = $('.exclude-by-domain-checkbox', entry);
const confirmActive = $('#confirm[data-display="true"]'); const confirmActive = $('#confirm[data-display="true"]');
const keyCode = event.keyCode || event.which; const {key} = event;
if (document.activeElement === cancel && (keyCode === 9)) { if (document.activeElement === cancel && (key === 'Tab')) {
event.preventDefault(); event.preventDefault();
affirm.focus(); affirm.focus();
} }
if (document.activeElement === close && (keyCode === 9) && !event.shiftKey) { if (document.activeElement === close && (key === 'Tab') && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
checkbox.focus(); checkbox.focus();
} }
if (document.activeElement === checkbox && (keyCode === 9) && event.shiftKey) { if (document.activeElement === checkbox && (key === 'Tab') && event.shiftKey) {
event.preventDefault(); event.preventDefault();
close.focus(); close.focus();
} }
if (keyCode === 27) { if (key === 'Escape') {
event.preventDefault(); event.preventDefault();
if (confirmActive) { if (confirmActive) {
box.dataset.display = false; box.dataset.display = false;

View File

@ -271,9 +271,7 @@ body.search-results-shown {
/* spinner: https://github.com/loadingio/css-spinner */ /* spinner: https://github.com/loadingio/css-spinner */
.lds-spinner { .lds-spinner {
-webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;

View File

@ -54,7 +54,7 @@ window.addEventListener('showStyles:done', () => {
href: URLS.usoArchive, href: URLS.usoArchive,
onclick(event) { onclick(event) {
if (!prefs.get('popup.findStylesInline') || dom.container) { if (!prefs.get('popup.findStylesInline') || dom.container) {
this.search = `${new URLSearchParams({category, search: $('#search-query').value})}`; this.search = new URLSearchParams({category, search: $('#search-query').value});
handleEvent.openURLandHide.call(this, event); handleEvent.openURLandHide.call(this, event);
return; return;
} }
@ -83,6 +83,9 @@ window.addEventListener('showStyles:done', () => {
const n = Number(m[2]); const n = Number(m[2]);
query.push(n >= 2000 && n <= thisYear ? n : m[1] || m[2]); query.push(n >= 2000 && n <= thisYear ? n : m[1] || m[2]);
} }
if (category === STYLUS_CATEGORY && !query.includes('stylus')) {
query.push('stylus');
}
ready = ready.then(start); ready = ready.then(start);
}; };
$('#search-order').value = order; $('#search-order').value = order;
@ -464,13 +467,18 @@ window.addEventListener('showStyles:done', () => {
} }
function isResultMatching(res) { function isResultMatching(res) {
// We're trying to call calcHaystack only when needed, not on all 100K items
const {c} = res;
return ( return (
res.c === category || c === category ||
searchGlobals && res.c === 'global' && (query.length || calcHaystack(res)._nLC.includes(category)) category !== STYLUS_CATEGORY && (
searchGlobals &&
c === 'global' &&
(query.length || calcHaystack(res)._nLC.includes(category))
)
) && ( ) && (
category === STYLUS_CATEGORY !query.length || // to skip calling calcHaystack
? /\bStylus\b/.test(res.n) query.every(isInHaystack, calcHaystack(res))
: !query.length || query.every(isInHaystack, calcHaystack(res))
); );
} }

View File

@ -86,10 +86,7 @@
border: 1px solid var(--main-border-color); border: 1px solid var(--main-border-color);
background-color: var(--main-background-color); background-color: var(--main-background-color);
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.12);
-webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none; user-select: none;
} }
@ -295,10 +292,7 @@
font-size: 11px; font-size: 11px;
font-weight: bold; font-weight: bold;
box-sizing: border-box; box-sizing: border-box;
-webkit-user-select: text;
-moz-user-select: text; -moz-user-select: text;
-ms-user-select: text;
-o-user-select: text;
user-select: text; user-select: text;
border: 1px solid var(--input-border-color); border: 1px solid var(--input-border-color);
background-color: var(--input-background-color); background-color: var(--input-background-color);
@ -306,7 +300,6 @@
} }
.colorpicker-theme-dark .colorpicker-input::-webkit-inner-spin-button { .colorpicker-theme-dark .colorpicker-input::-webkit-inner-spin-button {
-webkit-filter: invert(1);
filter: invert(1); filter: invert(1);
} }

View File

@ -355,29 +355,29 @@
} }
function setFromKeyboard(event) { function setFromKeyboard(event) {
const {which, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: meta} = event; const {key, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: meta} = event;
switch (which) { switch (key) {
case 9: // Tab case 'Tab':
case 33: // PgUp case 'PageUp':
case 34: // PgDn case 'PageDown':
if (!ctrl && !alt && !meta) { if (!ctrl && !alt && !meta) {
const el = document.activeElement; const el = document.activeElement;
const inputs = $inputs[currentFormat]; const inputs = $inputs[currentFormat];
const lastInput = inputs[inputs.length - 1]; const lastInput = inputs[inputs.length - 1];
if (which === 9 && shift && el === inputs[0]) { if (key === 'Tab' && shift && el === inputs[0]) {
maybeFocus(lastInput); maybeFocus(lastInput);
} else if (which === 9 && !shift && el === lastInput) { } else if (key === 'Tab' && !shift && el === lastInput) {
maybeFocus(inputs[0]); maybeFocus(inputs[0]);
} else if (which !== 9 && !shift) { } else if (key !== 'Tab' && !shift) {
setFromFormatElement({shift: which === 33 || shift}); setFromFormatElement({shift: key === 'PageUp' || shift});
} else { } else {
return; return;
} }
event.preventDefault(); event.preventDefault();
} }
return; return;
case 38: // Up case 'ArrowUp':
case 40: // Down case 'ArrowDown':
if (!event.metaKey && if (!event.metaKey &&
document.activeElement.localName === 'input' && document.activeElement.localName === 'input' &&
document.activeElement.checkValidity()) { document.activeElement.checkValidity()) {
@ -389,8 +389,8 @@
function setFromKeyboardIncrement(event) { function setFromKeyboardIncrement(event) {
const el = document.activeElement; const el = document.activeElement;
const {which, ctrlKey: ctrl, altKey: alt, shiftKey: shift} = event; const {key, ctrlKey: ctrl, altKey: alt, shiftKey: shift} = event;
const dir = which === 38 ? 1 : -1; const dir = key === 'ArrowUp' ? 1 : -1;
let value, newValue; let value, newValue;
if (currentFormat === 'hex') { if (currentFormat === 'hex') {
value = el.value.trim(); value = el.value.trim();
@ -617,9 +617,9 @@
function onKeyDown(e) { function onKeyDown(e) {
if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
switch (e.which) { switch (e.key) {
case 13: case 'Enter':
case 27: case 'Escape':
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
hide(); hide();