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:
parent
60fc6f2456
commit
9e487b03e5
|
@ -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,
|
||||
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 */
|
||||
openURL(opts) {
|
||||
const {message} = opts;
|
||||
return openURL(opts) // will pass the resolved value untouched when `message` is absent or falsy
|
||||
.then(message && (tab => tab.status === 'complete' ? tab : onTabReady(tab)))
|
||||
.then(message && (tab => msg.sendTab(tab.id, opts.message)));
|
||||
async openURL(opts) {
|
||||
const tab = await openURL(opts);
|
||||
if (opts.message) {
|
||||
await onTabReady(tab);
|
||||
await msg.sendTab(tab.id, opts.message);
|
||||
}
|
||||
return tab;
|
||||
function onTabReady(tab) {
|
||||
return new Promise((resolve, reject) =>
|
||||
setTimeout(function ping(numTries = 10, delay = 100) {
|
||||
|
@ -297,13 +299,10 @@ function openEditor(params) {
|
|||
'url-prefix'?: String
|
||||
}
|
||||
*/
|
||||
const searchParams = new URLSearchParams();
|
||||
for (const key in params) {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
const search = searchParams.toString();
|
||||
const u = new URL(chrome.runtime.getURL('edit.html'));
|
||||
u.search = new URLSearchParams(params);
|
||||
return openURL({
|
||||
url: 'edit.html' + (search && `?${search}`),
|
||||
url: `${u}`,
|
||||
newWindow: prefs.get('openEditInWindow'),
|
||||
windowPosition: prefs.get('windowPosition'),
|
||||
currentWindow: null
|
||||
|
|
|
@ -37,7 +37,7 @@ const tokenManager = (() => {
|
|||
scopes: ['https://www.googleapis.com/auth/drive.appdata'],
|
||||
revoke: 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: {
|
||||
|
@ -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) {
|
||||
const provider = AUTH[name];
|
||||
const state = Math.random().toFixed(8).slice(2);
|
||||
|
@ -160,7 +152,7 @@ const tokenManager = (() => {
|
|||
if (provider.authQuery) {
|
||||
Object.assign(query, provider.authQuery);
|
||||
}
|
||||
const url = `${provider.authURL}?${stringifyQuery(query)}`;
|
||||
const url = `${provider.authURL}?${new URLSearchParams(query)}`;
|
||||
return webextLaunchWebAuthFlow({
|
||||
url,
|
||||
interactive,
|
||||
|
@ -211,11 +203,9 @@ const tokenManager = (() => {
|
|||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
},
|
||||
body: body ? new URLSearchParams(body) : null,
|
||||
};
|
||||
if (body) {
|
||||
options.body = stringifyQuery(body);
|
||||
}
|
||||
return fetch(url, options)
|
||||
.then(r => {
|
||||
if (r.ok) {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
/* global cloneInto msg API */
|
||||
'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.addEventListener(chrome.runtime.id + '-install', orphanCheck, true);
|
||||
|
||||
|
@ -17,35 +21,18 @@
|
|||
}, '*');
|
||||
});
|
||||
|
||||
let gotBody = false;
|
||||
let currentMd5;
|
||||
new MutationObserver(observeDOM).observe(document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
observeDOM();
|
||||
const md5Url = getMeta('stylish-md5-url') || `https://update.userstyles.org/${styleId}.md5`;
|
||||
Promise.all([
|
||||
API.findStyle({md5Url}),
|
||||
getResource(md5Url),
|
||||
onDOMready(),
|
||||
]).then(checkUpdatability);
|
||||
|
||||
function observeDOM() {
|
||||
if (!gotBody) {
|
||||
if (!document.body) return;
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
document.documentElement.appendChild(
|
||||
Object.assign(document.createElement('script'), {
|
||||
textContent: `(${inPageContext})('${pageEventId}')`,
|
||||
}));
|
||||
|
||||
function onMessage(msg) {
|
||||
switch (msg.method) {
|
||||
|
@ -72,7 +59,7 @@
|
|||
|
||||
function checkUpdatability([installedStyle, md5]) {
|
||||
// TODO: remove the following statement when USO is fixed
|
||||
document.dispatchEvent(new CustomEvent('stylusFixBuggyUSOsettings', {
|
||||
document.dispatchEvent(new CustomEvent(pageEventId, {
|
||||
detail: installedStyle && installedStyle.updateUrl,
|
||||
}));
|
||||
currentMd5 = md5;
|
||||
|
@ -141,7 +128,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function onClick(event) {
|
||||
if (onClick.processing || !orphanCheck()) {
|
||||
return;
|
||||
|
@ -227,13 +213,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function getMeta(name) {
|
||||
const e = document.querySelector(`link[rel="${name}"]`);
|
||||
return e ? e.getAttribute('href') : null;
|
||||
}
|
||||
|
||||
|
||||
function getResource(url, options) {
|
||||
if (url.startsWith('#')) {
|
||||
return Promise.resolve(document.getElementById(url.slice(1)).textContent);
|
||||
|
@ -280,7 +264,6 @@
|
|||
.catch(() => null);
|
||||
}
|
||||
|
||||
|
||||
function styleSectionsEqual({sections: a}, {sections: b}) {
|
||||
if (!a || !b) {
|
||||
return undefined;
|
||||
|
@ -318,20 +301,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function onDOMready() {
|
||||
if (document.readyState !== 'loading') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
document.addEventListener('DOMContentLoaded', function _() {
|
||||
document.removeEventListener('DOMContentLoaded', _);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return document.readyState !== 'loading'
|
||||
? Promise.resolve()
|
||||
: new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve, {once: true}));
|
||||
}
|
||||
|
||||
|
||||
function openSettings(countdown = 10e3) {
|
||||
const button = document.querySelector('.customize_button');
|
||||
if (button) {
|
||||
|
@ -349,12 +324,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function orphanCheck() {
|
||||
// TODO: switch to install-hook-usercss.js impl, and remove explicit orphanCheck() calls
|
||||
if (chrome.i18n && chrome.i18n.getUILanguage()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (chrome.i18n.getUILanguage()) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {}
|
||||
// In Chrome content script is orphaned on an extension update/reload
|
||||
// so we need to detach event listeners
|
||||
window.removeEventListener(chrome.runtime.id + '-install', orphanCheck, true);
|
||||
|
@ -366,132 +341,56 @@
|
|||
}
|
||||
})();
|
||||
|
||||
// run in page context
|
||||
document.documentElement.appendChild(document.createElement('script')).text = '(' + (
|
||||
() => {
|
||||
document.currentScript.remove();
|
||||
|
||||
// spoof Stylish extension presence in Chrome
|
||||
if (window.chrome && chrome.app) {
|
||||
const realImage = window.Image;
|
||||
window.Image = function Image(...args) {
|
||||
return new Proxy(new realImage(...args), {
|
||||
get(obj, key) {
|
||||
return obj[key];
|
||||
},
|
||||
set(obj, key, value) {
|
||||
if (key === 'src' && /^chrome-extension:/i.test(value)) {
|
||||
setTimeout(() => typeof obj.onload === 'function' && obj.onload());
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// USO bug workaround: use the actual style settings in API response
|
||||
let settings;
|
||||
const originalResponseJson = Response.prototype.json;
|
||||
document.addEventListener('stylusFixBuggyUSOsettings', function _({detail}) {
|
||||
document.removeEventListener('stylusFixBuggyUSOsettings', _);
|
||||
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
||||
settings = /\?/.test(detail) && new URLSearchParams(new URL(detail).search.replace(/^\?/, ''));
|
||||
if (!settings) {
|
||||
Response.prototype.json = originalResponseJson;
|
||||
function inPageContext(eventId) {
|
||||
document.currentScript.remove();
|
||||
const origMethods = {
|
||||
json: Response.prototype.json,
|
||||
byId: document.getElementById,
|
||||
};
|
||||
let vars;
|
||||
// USO bug workaround: prevent errors in console after install and busy cursor
|
||||
document.getElementById = id =>
|
||||
origMethods.byId.call(document, id) ||
|
||||
(/^(stylish-code|stylish-installed-style-installed-\w+|post-install-ad|style-install-unknown)$/.test(id)
|
||||
? Object.assign(document.createElement('p'), {className: 'afterdownload-ad'})
|
||||
: null);
|
||||
// USO bug workaround: use the actual image data in customized settings
|
||||
document.addEventListener(eventId, ({detail}) => {
|
||||
vars = /\?/.test(detail) && new URL(detail).searchParams;
|
||||
if (!vars) Response.prototype.json = origMethods.json;
|
||||
}, {once: true});
|
||||
Response.prototype.json = async function () {
|
||||
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);
|
||||
if (value && ss.setting_type === 'image' && ss.style_setting_options) {
|
||||
let isListed;
|
||||
for (const opt of ss.style_setting_options) {
|
||||
isListed |= opt.default = (opt.value === value);
|
||||
}
|
||||
images.set(ss.install_key, {url: value, isListed});
|
||||
}
|
||||
}
|
||||
});
|
||||
Response.prototype.json = function (...args) {
|
||||
return originalResponseJson.call(this, ...args).then(json => {
|
||||
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;
|
||||
}
|
||||
if (images.size) {
|
||||
new MutationObserver((_, observer) => {
|
||||
if (document.getElementById('style-settings')) {
|
||||
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 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) {
|
||||
elRadio.checked = !isListed;
|
||||
elUrl.value = url;
|
||||
}
|
||||
}
|
||||
}).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;
|
||||
}
|
||||
}).observe(document, {childList: true, subtree: true});
|
||||
}
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
}
|
||||
.CodeMirror {
|
||||
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 {
|
||||
background: none;
|
||||
|
@ -14,9 +20,6 @@
|
|||
.CodeMirror-dialog {
|
||||
-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 {
|
||||
background: linear-gradient(to right, currentColor, transparent);
|
||||
position: absolute;
|
||||
|
|
|
@ -611,6 +611,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
|||
right: 4px;
|
||||
top: .5em;
|
||||
}
|
||||
#help-popup input[type="search"] {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.keymap-list {
|
||||
font-size: 12px;
|
||||
|
@ -788,6 +791,10 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
|||
justify-items: normal;
|
||||
}
|
||||
|
||||
.usercss .CodeMirror-focused {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
html:not(.usercss) .usercss-only,
|
||||
.usercss #mozilla-format-container,
|
||||
.usercss #sections > h2 {
|
||||
|
|
12
edit/edit.js
12
edit/edit.js
|
@ -352,8 +352,7 @@ function isUsercss(style) {
|
|||
}
|
||||
|
||||
function initStyleData() {
|
||||
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
||||
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
|
||||
const params = new URLSearchParams(location.search);
|
||||
const id = Number(params.get('id'));
|
||||
const createEmptyStyle = () => ({
|
||||
name: params.get('domain') ||
|
||||
|
@ -409,7 +408,7 @@ function showHelp(title = '', body) {
|
|||
!event ||
|
||||
event.type === 'click' ||
|
||||
(
|
||||
event.which === 27 &&
|
||||
event.key === 'Escape' &&
|
||||
!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey &&
|
||||
!$('.CodeMirror-hints, #message-box') &&
|
||||
(
|
||||
|
@ -470,7 +469,7 @@ function showCodeMirrorPopup(title, html, options) {
|
|||
popup.style.pointerEvents = 'auto';
|
||||
|
||||
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 area = search && search.contains(document.activeElement) ? search : popup;
|
||||
moveFocus(area, event.shiftKey ? -1 : 1);
|
||||
|
@ -479,13 +478,12 @@ function showCodeMirrorPopup(title, html, options) {
|
|||
};
|
||||
window.addEventListener('keydown', onKeyDown, true);
|
||||
|
||||
window.addEventListener('closeHelp', function _() {
|
||||
window.removeEventListener('closeHelp', _);
|
||||
window.addEventListener('closeHelp', () => {
|
||||
window.removeEventListener('keydown', onKeyDown, true);
|
||||
document.documentElement.style.removeProperty('pointer-events');
|
||||
rerouteHotkeys(true);
|
||||
cm = popup.codebox = null;
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
|
|
@ -53,11 +53,10 @@
|
|||
cm.on('changes', updateButtonState);
|
||||
|
||||
rerouteHotkeys(false);
|
||||
window.addEventListener('closeHelp', function _() {
|
||||
window.removeEventListener('closeHelp', _);
|
||||
window.addEventListener('closeHelp', () => {
|
||||
rerouteHotkeys(true);
|
||||
cm = null;
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
loadScript([
|
||||
'/vendor/codemirror/mode/javascript/javascript.js',
|
||||
|
|
|
@ -10,7 +10,7 @@ function createLivePreview(preprocess) {
|
|||
const errorContainer = $('#preview-errors');
|
||||
|
||||
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.update(data);
|
||||
}
|
||||
|
|
|
@ -206,36 +206,32 @@ function createSection({
|
|||
}
|
||||
|
||||
function handleKeydown(cm, event) {
|
||||
const key = event.which;
|
||||
if (key < 37 || key > 40 || event.shiftKey || event.altKey || event.metaKey) {
|
||||
if (event.shiftKey || event.altKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
const {key} = event;
|
||||
const {line, ch} = cm.getCursor();
|
||||
switch (key) {
|
||||
case 37:
|
||||
// arrow Left
|
||||
case 'ArrowLeft':
|
||||
if (line || ch) {
|
||||
return;
|
||||
}
|
||||
// fallthrough to arrow Up
|
||||
case 38:
|
||||
// arrow Up
|
||||
// fallthrough
|
||||
case 'ArrowUp':
|
||||
cm = line === 0 && prevEditor(cm, false);
|
||||
if (!cm) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch);
|
||||
cm.setCursor(cm.doc.size - 1, key === 'ArrowLeft' ? 1e20 : ch);
|
||||
break;
|
||||
case 39:
|
||||
// arrow Right
|
||||
case 'ArrowRight':
|
||||
if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) {
|
||||
return;
|
||||
}
|
||||
// fallthrough to arrow Down
|
||||
case 40:
|
||||
// arrow Down
|
||||
// fallthrough
|
||||
case 'ArrowDown':
|
||||
cm = line === cm.doc.size - 1 && nextEditor(cm, false);
|
||||
if (!cm) {
|
||||
return;
|
||||
|
@ -245,13 +241,6 @@ function createSection({
|
|||
cm.setCursor(0, 0);
|
||||
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) {
|
||||
|
|
18
global.css
18
global.css
|
@ -54,18 +54,20 @@ button:active {
|
|||
border-color: hsl(0, 0%, 50%);
|
||||
}
|
||||
|
||||
input {
|
||||
input {
|
||||
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;
|
||||
color: #000;
|
||||
height: 22px;
|
||||
min-height: 22px!important;
|
||||
line-height: 22px;
|
||||
padding: 0 3px;
|
||||
font: inherit;
|
||||
border: 1px solid hsl(0, 0%, 66%);
|
||||
}
|
||||
|
||||
|
@ -208,9 +210,19 @@ select[disabled] + .select-arrow {
|
|||
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 {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@supports (-moz-appearance: none) {
|
||||
|
|
|
@ -288,9 +288,7 @@ li {
|
|||
|
||||
#header:not(.meta-init) > *:not(.lds-spinner),
|
||||
#header.meta-init > .lds-spinner {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
|
@ -299,9 +297,7 @@ li {
|
|||
#header.meta-init > * {
|
||||
opacity: 1;
|
||||
transition: opacity .5s;
|
||||
-webkit-user-select: auto;
|
||||
-moz-user-select: auto;
|
||||
-ms-user-select: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
'use strict';
|
||||
|
||||
(() => {
|
||||
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
||||
const params = new URLSearchParams(location.search.replace(/^\?/, ''));
|
||||
const params = new URLSearchParams(location.search);
|
||||
const tabId = params.has('tabId') ? Number(params.get('tabId')) : -1;
|
||||
const initialUrl = params.get('updateUrl');
|
||||
|
||||
|
|
31
js/dom.js
31
js/dom.js
|
@ -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) => {
|
||||
const el = selector && typeof selector === 'string' ? $(selector, base) : selector;
|
||||
if (el) {
|
||||
|
@ -112,15 +115,9 @@ document.addEventListener('wheel', event => {
|
|||
});
|
||||
|
||||
function onDOMready() {
|
||||
if (document.readyState !== 'loading') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
document.addEventListener('DOMContentLoaded', function _() {
|
||||
document.removeEventListener('DOMContentLoaded', _);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return document.readyState !== 'loading'
|
||||
? Promise.resolve()
|
||||
: new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve, {once: true}));
|
||||
}
|
||||
|
||||
|
||||
|
@ -144,8 +141,7 @@ function animateElement(
|
|||
onComplete,
|
||||
} = {}) {
|
||||
return element && new Promise(resolve => {
|
||||
element.addEventListener('animationend', function _() {
|
||||
element.removeEventListener('animationend', _);
|
||||
element.addEventListener('animationend', () => {
|
||||
element.classList.remove(
|
||||
className,
|
||||
// In Firefox, `resolve()` might be called one frame later.
|
||||
|
@ -157,7 +153,7 @@ function animateElement(
|
|||
onComplete.call(element);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}, {once: true});
|
||||
element.classList.add(className);
|
||||
});
|
||||
}
|
||||
|
@ -355,20 +351,23 @@ function focusAccessibility() {
|
|||
'a',
|
||||
'button',
|
||||
'input',
|
||||
'textarea',
|
||||
'label',
|
||||
'select',
|
||||
'summary',
|
||||
];
|
||||
// try to find a focusable parent for this many parentElement jumps:
|
||||
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('keydown', keepOutlineOnTab, {passive: true});
|
||||
|
||||
function suppressOutlineOnClick({target}) {
|
||||
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;
|
||||
if (el.dataset.focusedViaClick === undefined) {
|
||||
el.dataset.focusedViaClick = '';
|
||||
|
@ -379,7 +378,7 @@ function focusAccessibility() {
|
|||
}
|
||||
|
||||
function keepOutlineOnTab(event) {
|
||||
if (event.which === 9) {
|
||||
if (event.key === 'Tab') {
|
||||
focusAccessibility.lastFocusedViaClick = false;
|
||||
setTimeout(keepOutlineOnTab, 0, true);
|
||||
return;
|
||||
|
@ -387,7 +386,7 @@ function focusAccessibility() {
|
|||
return;
|
||||
}
|
||||
let el = document.activeElement;
|
||||
if (!el || !focusAccessibility.ELEMENTS.includes(el.localName)) {
|
||||
if (!el || isOutlineAllowed(el)) {
|
||||
return;
|
||||
}
|
||||
if (el.dataset.focusedViaClick !== undefined) {
|
||||
|
|
|
@ -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
|
||||
})();
|
||||
|
|
20
js/router.js
20
js/router.js
|
@ -31,18 +31,9 @@ const router = (() => {
|
|||
}
|
||||
|
||||
function updateSearch(key, value) {
|
||||
const search = new URLSearchParams(location.search.replace(/^\?/, ''));
|
||||
if (!value) {
|
||||
search.delete(key);
|
||||
} 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}`);
|
||||
}
|
||||
const u = new URL(location);
|
||||
u.searchParams[value ? 'set' : 'delete'](key, value);
|
||||
history.replaceState(history.state, null, `${u}`);
|
||||
update(true);
|
||||
}
|
||||
|
||||
|
@ -66,7 +57,7 @@ const router = (() => {
|
|||
}
|
||||
|
||||
function getSearch(key) {
|
||||
return new URLSearchParams(location.search.replace(/^\?/, '')).get(key);
|
||||
return new URLSearchParams(location.search).get(key);
|
||||
}
|
||||
|
||||
function update(replace) {
|
||||
|
@ -86,8 +77,7 @@ const router = (() => {
|
|||
if (options.hash) {
|
||||
state = options.hash === location.hash;
|
||||
} else if (options.search) {
|
||||
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
|
||||
const search = new URLSearchParams(location.search.replace(/^\?/, ''));
|
||||
const search = new URLSearchParams(location.search);
|
||||
state = options.search.map(key => search.get(key));
|
||||
}
|
||||
if (!deepEqual(state, options.currentState)) {
|
||||
|
|
|
@ -82,7 +82,7 @@ const loadScript = (() => {
|
|||
for (const {addedNodes} of mutations) {
|
||||
for (const n of addedNodes) {
|
||||
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) {
|
||||
this.removeEventListener('load', notifySubscribers);
|
||||
for (let data; (data = getSubscribersForSrc(this.src));) {
|
||||
data.listeners.forEach(fn => fn(event));
|
||||
if (emptyAfterCleanup(data.suffix)) {
|
||||
|
|
|
@ -24,12 +24,12 @@ onDOMready().then(() => {
|
|||
document.body.appendChild(input);
|
||||
window.addEventListener('keydown', maybeRefocus, true);
|
||||
|
||||
function incrementalSearch({which}, immediately) {
|
||||
function incrementalSearch({key}, immediately) {
|
||||
if (!immediately) {
|
||||
debounce(incrementalSearch, 100, {}, true);
|
||||
return;
|
||||
}
|
||||
const direction = which === 38 ? -1 : which === 40 ? 1 : 0;
|
||||
const direction = key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0;
|
||||
const text = input.value.toLocaleLowerCase();
|
||||
if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) {
|
||||
prevText = text;
|
||||
|
@ -76,40 +76,31 @@ onDOMready().then(() => {
|
|||
if (event.altKey || event.metaKey || $('#message-box')) {
|
||||
return;
|
||||
}
|
||||
const inTextInput = event.target.matches('[type=text], [type=search], [type=number]');
|
||||
const {which: k, key} = event;
|
||||
// focus search field on "/" or Ctrl-F key
|
||||
if (event.ctrlKey
|
||||
? (event.code === 'KeyF' || !event.code && k === 70) && !event.shiftKey
|
||||
: (key === '/' || !key && k === 191 && !event.shiftKey) && !inTextInput) {
|
||||
const inTextInput = $.isTextLikeInput(event.target);
|
||||
const {key, code, ctrlKey: ctrl} = event;
|
||||
// `code` is independent of the current keyboard language
|
||||
if ((code === 'KeyF' && ctrl && !event.shiftKey) ||
|
||||
(code === 'Slash' || key === '/') && !ctrl && !inTextInput) {
|
||||
// focus search field on "/" or Ctrl-F key
|
||||
event.preventDefault();
|
||||
$('#search').focus();
|
||||
return;
|
||||
}
|
||||
if (event.ctrlKey || inTextInput) {
|
||||
if (ctrl || inTextInput) {
|
||||
return;
|
||||
}
|
||||
const time = performance.now();
|
||||
if (
|
||||
// 0-9
|
||||
k >= 48 && k <= 57 ||
|
||||
// a-z
|
||||
k >= 65 && k <= 90 ||
|
||||
// numpad keys
|
||||
k >= 96 && k <= 111 ||
|
||||
// marks
|
||||
k >= 186
|
||||
) {
|
||||
if (key.length === 1) {
|
||||
input.focus();
|
||||
if (time - prevTime > 1000) {
|
||||
input.value = '';
|
||||
}
|
||||
prevTime = time;
|
||||
} else
|
||||
if (k === 13 && focusedLink) {
|
||||
if (key === 'Enter' && focusedLink) {
|
||||
focusedLink.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
||||
} else
|
||||
if ((k === 38 || k === 40) && !event.shiftKey &&
|
||||
if ((key === 'ArrowUp' || key === 'ArrowDown') && !event.shiftKey &&
|
||||
time - prevTime < 5000 && incrementalSearch(event, true)) {
|
||||
prevTime = time;
|
||||
} else
|
||||
|
|
|
@ -550,8 +550,6 @@ a:hover {
|
|||
.newUI .update-done .updated svg {
|
||||
top: -4px;
|
||||
position: relative;
|
||||
/* unprefixed since Chrome 53 */
|
||||
-webkit-filter: drop-shadow(0 4px 0 currentColor);
|
||||
filter: drop-shadow(0 5px 0 currentColor);
|
||||
}
|
||||
|
||||
|
@ -663,8 +661,6 @@ a:hover {
|
|||
margin-left: -20px;
|
||||
margin-right: 4px;
|
||||
transition: opacity .5s, filter .5s;
|
||||
/* unprefixed since Chrome 53 */
|
||||
-webkit-filter: grayscale(1);
|
||||
filter: grayscale(1);
|
||||
/* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */
|
||||
backface-visibility: hidden;
|
||||
|
@ -682,9 +678,7 @@ a:hover {
|
|||
|
||||
.newUI .entry:hover .target img {
|
||||
opacity: 1;
|
||||
/* unprefixed since Chrome 53 */
|
||||
-webkit-filter: grayscale(0);
|
||||
filter: grayscale(0);
|
||||
filter: none;
|
||||
}
|
||||
|
||||
/* Default, no update buttons */
|
||||
|
|
|
@ -631,13 +631,11 @@ function switchUI({styleOnly} = {}) {
|
|||
}
|
||||
` + (newUI.faviconsGray ? `
|
||||
.newUI .target img {
|
||||
-webkit-filter: grayscale(1);
|
||||
filter: grayscale(1);
|
||||
opacity: .25;
|
||||
}
|
||||
` : `
|
||||
.newUI .target img {
|
||||
-webkit-filter: none;
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
@ -135,12 +135,7 @@
|
|||
}
|
||||
|
||||
.danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
|
||||
outline: red auto 1px;
|
||||
}
|
||||
|
||||
/* FF ignores color with 'auto' */
|
||||
.firefox .danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
|
||||
outline: red solid 1px;
|
||||
box-shadow: 0 0 0 1px red; /* Using box-shadow instead of the ugly outline in new Chrome */
|
||||
}
|
||||
|
||||
.non-windows #message-box-buttons {
|
||||
|
|
|
@ -62,28 +62,28 @@ function messageBox({
|
|||
resolveWith({button: this.buttonIndex});
|
||||
},
|
||||
key(event) {
|
||||
const {which, shiftKey, ctrlKey, altKey, metaKey, target} = event;
|
||||
if (shiftKey && which !== 9 || ctrlKey || altKey || metaKey) {
|
||||
const {key, shiftKey, ctrlKey, altKey, metaKey, target} = event;
|
||||
if (shiftKey && key !== 'Tab' || ctrlKey || altKey || metaKey) {
|
||||
return;
|
||||
}
|
||||
switch (which) {
|
||||
case 13:
|
||||
switch (key) {
|
||||
case 'Enter':
|
||||
if (target.closest(focusAccessibility.ELEMENTS.join(','))) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 27:
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
case 9:
|
||||
case 'Tab':
|
||||
moveFocus(messageBox.element, shiftKey ? -1 : 1);
|
||||
event.preventDefault();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
resolveWith(which === 13 ? {enter: true} : {esc: true});
|
||||
resolveWith(key === 'Enter' ? {enter: true} : {esc: true});
|
||||
},
|
||||
scroll() {
|
||||
scrollTo(blockScroll.x, blockScroll.y);
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
.onoffswitch {
|
||||
position: relative;
|
||||
margin: 1ex 0;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.onoffswitch input {
|
||||
|
@ -17,6 +16,7 @@
|
|||
bottom: -10px;
|
||||
left: -10px;
|
||||
width: calc(100% + 12px);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#message-box .onoffswitch input {
|
||||
|
|
|
@ -298,7 +298,7 @@ function customizeHotkeys() {
|
|||
}
|
||||
|
||||
window.onkeydown = event => {
|
||||
if (event.keyCode === 27) {
|
||||
if (event.key === 'Escape') {
|
||||
top.dispatchEvent(new CustomEvent('closeOptions'));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,14 +9,13 @@ const hotkeys = (() => {
|
|||
let enabled = false;
|
||||
let ready = false;
|
||||
|
||||
window.addEventListener('showStyles:done', function _() {
|
||||
window.removeEventListener('showStyles:done', _);
|
||||
window.addEventListener('showStyles:done', () => {
|
||||
togglablesShown = true;
|
||||
togglables = getTogglables();
|
||||
ready = true;
|
||||
setState(true);
|
||||
initHotkeyInfo();
|
||||
});
|
||||
}, {once: true});
|
||||
|
||||
window.addEventListener('resize', adjustInfoPosition);
|
||||
|
||||
|
@ -38,40 +37,27 @@ const hotkeys = (() => {
|
|||
return;
|
||||
}
|
||||
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];
|
||||
|
||||
} else if (
|
||||
code === 'Backquote' || code === 'NumpadMultiply' ||
|
||||
key && (key === '`' || key === '*') ||
|
||||
k === 192 || k === 106) {
|
||||
} else if (key === '`' || key === '*' || code === 'Backquote' || code === 'NumpadMultiply') {
|
||||
invertTogglables();
|
||||
|
||||
} else if (
|
||||
code === 'NumpadSubtract' ||
|
||||
key && key === '-' ||
|
||||
k === 109) {
|
||||
} else if (key === '-' || code === 'NumpadSubtract') {
|
||||
toggleState(entries, 'enabled', false);
|
||||
|
||||
} else if (
|
||||
code === 'NumpadAdd' ||
|
||||
key && key === '+' ||
|
||||
k === 107) {
|
||||
} else if (key === '+' || code === 'NumpadAdd') {
|
||||
toggleState(entries, 'disabled', true);
|
||||
|
||||
} else if (
|
||||
// any single character
|
||||
key && key.length === 1 ||
|
||||
k >= 65 && k <= 90) {
|
||||
const letter = new RegExp(key ? '^' + key : '^\\x' + k.toString(16), 'i');
|
||||
entry = [...entries].find(entry => letter.test(entry.textContent));
|
||||
} else if (key.length === 1) {
|
||||
shiftKey = false; // typing ':' etc. needs Shift so we hide it here to avoid opening editor
|
||||
key = key.toLocaleLowerCase();
|
||||
entry = [...entries].find(e => e.innerText.toLocaleLowerCase().startsWith(key));
|
||||
}
|
||||
if (!entry) {
|
||||
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}));
|
||||
}
|
||||
|
||||
|
|
|
@ -342,11 +342,7 @@ a.configure[target="_blank"] .svg-icon.config {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
#confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
|
||||
outline: red auto 1px;
|
||||
}
|
||||
/* FF ignores color with 'auto' */
|
||||
.firefox #confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
|
||||
outline: red solid 1px;
|
||||
box-shadow: 0 0 0 1px red; /* Using box-shadow instead of the ugly outline in new Chrome */
|
||||
}
|
||||
.menu-items-wrapper {
|
||||
width: 80%;
|
||||
|
|
|
@ -507,16 +507,15 @@ Object.assign(handleEvent, {
|
|||
window.onkeydown = event => {
|
||||
const close = $('.menu-close', entry);
|
||||
const checkbox = $('.exclude-by-domain-checkbox', entry);
|
||||
const keyCode = event.keyCode || event.which;
|
||||
if (document.activeElement === close && (keyCode === 9) && !event.shiftKey) {
|
||||
if (document.activeElement === close && (event.key === 'Tab') && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
checkbox.focus();
|
||||
}
|
||||
if (document.activeElement === checkbox && (keyCode === 9) && event.shiftKey) {
|
||||
if (document.activeElement === checkbox && (event.key === 'Tab') && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
close.focus();
|
||||
}
|
||||
if (keyCode === 27) {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
close.click();
|
||||
}
|
||||
|
@ -542,20 +541,20 @@ Object.assign(handleEvent, {
|
|||
const close = $('.menu-close', entry);
|
||||
const checkbox = $('.exclude-by-domain-checkbox', entry);
|
||||
const confirmActive = $('#confirm[data-display="true"]');
|
||||
const keyCode = event.keyCode || event.which;
|
||||
if (document.activeElement === cancel && (keyCode === 9)) {
|
||||
const {key} = event;
|
||||
if (document.activeElement === cancel && (key === 'Tab')) {
|
||||
event.preventDefault();
|
||||
affirm.focus();
|
||||
}
|
||||
if (document.activeElement === close && (keyCode === 9) && !event.shiftKey) {
|
||||
if (document.activeElement === close && (key === 'Tab') && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
checkbox.focus();
|
||||
}
|
||||
if (document.activeElement === checkbox && (keyCode === 9) && event.shiftKey) {
|
||||
if (document.activeElement === checkbox && (key === 'Tab') && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
close.focus();
|
||||
}
|
||||
if (keyCode === 27) {
|
||||
if (key === 'Escape') {
|
||||
event.preventDefault();
|
||||
if (confirmActive) {
|
||||
box.dataset.display = false;
|
||||
|
|
|
@ -271,9 +271,7 @@ body.search-results-shown {
|
|||
|
||||
/* spinner: https://github.com/loadingio/css-spinner */
|
||||
.lds-spinner {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
|
|
|
@ -54,7 +54,7 @@ window.addEventListener('showStyles:done', () => {
|
|||
href: URLS.usoArchive,
|
||||
onclick(event) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -83,6 +83,9 @@ window.addEventListener('showStyles:done', () => {
|
|||
const n = Number(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);
|
||||
};
|
||||
$('#search-order').value = order;
|
||||
|
@ -464,13 +467,18 @@ window.addEventListener('showStyles:done', () => {
|
|||
}
|
||||
|
||||
function isResultMatching(res) {
|
||||
// We're trying to call calcHaystack only when needed, not on all 100K items
|
||||
const {c} = res;
|
||||
return (
|
||||
res.c === category ||
|
||||
searchGlobals && res.c === 'global' && (query.length || calcHaystack(res)._nLC.includes(category))
|
||||
c === category ||
|
||||
category !== STYLUS_CATEGORY && (
|
||||
searchGlobals &&
|
||||
c === 'global' &&
|
||||
(query.length || calcHaystack(res)._nLC.includes(category))
|
||||
)
|
||||
) && (
|
||||
category === STYLUS_CATEGORY
|
||||
? /\bStylus\b/.test(res.n)
|
||||
: !query.length || query.every(isInHaystack, calcHaystack(res))
|
||||
!query.length || // to skip calling calcHaystack
|
||||
query.every(isInHaystack, calcHaystack(res))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,7 @@
|
|||
border: 1px solid var(--main-border-color);
|
||||
background-color: var(--main-background-color);
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.12);
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
@ -295,10 +292,7 @@
|
|||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
-o-user-select: text;
|
||||
user-select: text;
|
||||
border: 1px solid var(--input-border-color);
|
||||
background-color: var(--input-background-color);
|
||||
|
@ -306,7 +300,6 @@
|
|||
}
|
||||
|
||||
.colorpicker-theme-dark .colorpicker-input::-webkit-inner-spin-button {
|
||||
-webkit-filter: invert(1);
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -355,29 +355,29 @@
|
|||
}
|
||||
|
||||
function setFromKeyboard(event) {
|
||||
const {which, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: meta} = event;
|
||||
switch (which) {
|
||||
case 9: // Tab
|
||||
case 33: // PgUp
|
||||
case 34: // PgDn
|
||||
const {key, ctrlKey: ctrl, altKey: alt, shiftKey: shift, metaKey: meta} = event;
|
||||
switch (key) {
|
||||
case 'Tab':
|
||||
case 'PageUp':
|
||||
case 'PageDown':
|
||||
if (!ctrl && !alt && !meta) {
|
||||
const el = document.activeElement;
|
||||
const inputs = $inputs[currentFormat];
|
||||
const lastInput = inputs[inputs.length - 1];
|
||||
if (which === 9 && shift && el === inputs[0]) {
|
||||
if (key === 'Tab' && shift && el === inputs[0]) {
|
||||
maybeFocus(lastInput);
|
||||
} else if (which === 9 && !shift && el === lastInput) {
|
||||
} else if (key === 'Tab' && !shift && el === lastInput) {
|
||||
maybeFocus(inputs[0]);
|
||||
} else if (which !== 9 && !shift) {
|
||||
setFromFormatElement({shift: which === 33 || shift});
|
||||
} else if (key !== 'Tab' && !shift) {
|
||||
setFromFormatElement({shift: key === 'PageUp' || shift});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
case 38: // Up
|
||||
case 40: // Down
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown':
|
||||
if (!event.metaKey &&
|
||||
document.activeElement.localName === 'input' &&
|
||||
document.activeElement.checkValidity()) {
|
||||
|
@ -389,8 +389,8 @@
|
|||
|
||||
function setFromKeyboardIncrement(event) {
|
||||
const el = document.activeElement;
|
||||
const {which, ctrlKey: ctrl, altKey: alt, shiftKey: shift} = event;
|
||||
const dir = which === 38 ? 1 : -1;
|
||||
const {key, ctrlKey: ctrl, altKey: alt, shiftKey: shift} = event;
|
||||
const dir = key === 'ArrowUp' ? 1 : -1;
|
||||
let value, newValue;
|
||||
if (currentFormat === 'hex') {
|
||||
value = el.value.trim();
|
||||
|
@ -617,9 +617,9 @@
|
|||
|
||||
function onKeyDown(e) {
|
||||
if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
switch (e.which) {
|
||||
case 13:
|
||||
case 27:
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
hide();
|
||||
|
|
Loading…
Reference in New Issue
Block a user