simplify exports
This commit is contained in:
parent
e74a349a88
commit
f8269c7424
|
@ -18,12 +18,7 @@ define(require => {
|
||||||
const ICON_SIZES = FIREFOX || CHROME >= 55 && !VIVALDI ? [16, 32] : [19, 38];
|
const ICON_SIZES = FIREFOX || CHROME >= 55 && !VIVALDI ? [16, 32] : [19, 38];
|
||||||
const staleBadges = new Set();
|
const staleBadges = new Set();
|
||||||
|
|
||||||
let exports;
|
const iconManager = /** @namespace API */ {
|
||||||
const {
|
|
||||||
|
|
||||||
updateIconBadge,
|
|
||||||
|
|
||||||
} = exports = /** @namespace API */ {
|
|
||||||
/**
|
/**
|
||||||
* @param {(number|string)[]} styleIds
|
* @param {(number|string)[]} styleIds
|
||||||
* @param {boolean} [lazyBadge=false] preventing flicker during page load
|
* @param {boolean} [lazyBadge=false] preventing flicker during page load
|
||||||
|
@ -72,7 +67,7 @@ define(require => {
|
||||||
|
|
||||||
function onPortDisconnected({sender}) {
|
function onPortDisconnected({sender}) {
|
||||||
if (tabManager.get(sender.tab.id, 'styleIds')) {
|
if (tabManager.get(sender.tab.id, 'styleIds')) {
|
||||||
updateIconBadge.call({sender}, [], {lazyBadge: true});
|
iconManager.updateIconBadge.call({sender}, [], {lazyBadge: true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,5 +148,5 @@ define(require => {
|
||||||
staleBadges.clear();
|
staleBadges.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports;
|
return iconManager;
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,13 +28,11 @@ define(require => {
|
||||||
|
|
||||||
//#region Declarations
|
//#region Declarations
|
||||||
|
|
||||||
const ready = init();
|
/** @typedef {{
|
||||||
/**
|
style: StyleObj
|
||||||
* @typedef StyleMapData
|
preview?: StyleObj
|
||||||
* @property {StyleObj} style
|
appliesTo: Set<string>
|
||||||
* @property {?StyleObj} [preview]
|
}} StyleMapData */
|
||||||
* @property {Set<string>} appliesTo - urls
|
|
||||||
*/
|
|
||||||
/** @type {Map<number,StyleMapData>} */
|
/** @type {Map<number,StyleMapData>} */
|
||||||
const dataMap = new Map();
|
const dataMap = new Map();
|
||||||
const uuidIndex = new Map();
|
const uuidIndex = new Map();
|
||||||
|
@ -59,13 +57,33 @@ define(require => {
|
||||||
};
|
};
|
||||||
const DELETE_IF_NULL = ['id', 'customName'];
|
const DELETE_IF_NULL = ['id', 'customName'];
|
||||||
|
|
||||||
|
let ready = false;
|
||||||
|
const init = db.exec('getAll').then(async res => {
|
||||||
|
const styles = res || [];
|
||||||
|
const updated = styles.filter(style =>
|
||||||
|
addMissingProps(style) +
|
||||||
|
addCustomName(style));
|
||||||
|
if (updated.length) {
|
||||||
|
await db.exec('putMany', updated);
|
||||||
|
}
|
||||||
|
for (const style of styles) {
|
||||||
|
fixUsoMd5Issue(style);
|
||||||
|
storeInMap(style);
|
||||||
|
uuidIndex.set(style._id, style.id);
|
||||||
|
}
|
||||||
|
ready = true;
|
||||||
|
});
|
||||||
|
|
||||||
chrome.runtime.onConnect.addListener(handleLivePreview);
|
chrome.runtime.onConnect.addListener(handleLivePreview);
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Exports
|
//#region Exports
|
||||||
|
|
||||||
/** @type {StyleManager} */
|
/**
|
||||||
const styleManager = /** @namespace StyleManager */ {
|
* @type StyleManager
|
||||||
|
* @namespace StyleManager
|
||||||
|
*/
|
||||||
|
const styleManager = {
|
||||||
|
|
||||||
/* props first,
|
/* props first,
|
||||||
then method shorthands if any,
|
then method shorthands if any,
|
||||||
|
@ -79,7 +97,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<number>} style id */
|
/** @returns {Promise<number>} style id */
|
||||||
async delete(id, reason) {
|
async delete(id, reason) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const data = id2data(id);
|
const data = id2data(id);
|
||||||
await db.exec('delete', id);
|
await db.exec('delete', id);
|
||||||
if (reason !== 'sync') {
|
if (reason !== 'sync') {
|
||||||
|
@ -100,7 +118,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<number>} style id */
|
/** @returns {Promise<number>} style id */
|
||||||
async deleteByUUID(_id, rev) {
|
async deleteByUUID(_id, rev) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const id = uuidIndex.get(_id);
|
const id = uuidIndex.get(_id);
|
||||||
const oldDoc = id && id2style(id);
|
const oldDoc = id && id2style(id);
|
||||||
if (oldDoc && styleManager.compareRevision(oldDoc._rev, rev) <= 0) {
|
if (oldDoc && styleManager.compareRevision(oldDoc._rev, rev) <= 0) {
|
||||||
|
@ -111,7 +129,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async editSave(style) {
|
async editSave(style) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
style = mergeWithMapped(style);
|
style = mergeWithMapped(style);
|
||||||
style.updateDate = Date.now();
|
style.updateDate = Date.now();
|
||||||
return handleSave(await saveStyle(style), 'editSave');
|
return handleSave(await saveStyle(style), 'editSave');
|
||||||
|
@ -119,7 +137,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<?StyleObj>} */
|
/** @returns {Promise<?StyleObj>} */
|
||||||
async find(filter) {
|
async find(filter) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const filterEntries = Object.entries(filter);
|
const filterEntries = Object.entries(filter);
|
||||||
for (const {style} of dataMap.values()) {
|
for (const {style} of dataMap.values()) {
|
||||||
if (filterEntries.every(([key, val]) => style[key] === val)) {
|
if (filterEntries.every(([key, val]) => style[key] === val)) {
|
||||||
|
@ -131,19 +149,19 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj[]>} */
|
/** @returns {Promise<StyleObj[]>} */
|
||||||
async getAll() {
|
async getAll() {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
return Array.from(dataMap.values(), data2style);
|
return Array.from(dataMap.values(), data2style);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async getByUUID(uuid) {
|
async getByUUID(uuid) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
return id2style(uuidIndex.get(uuid));
|
return id2style(uuidIndex.get(uuid));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleSectionsToApply>} */
|
/** @returns {Promise<StyleSectionsToApply>} */
|
||||||
async getSectionsByUrl(url, id, isInitialApply) {
|
async getSectionsByUrl(url, id, isInitialApply) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
/* Chrome hides text frament from location.href of the page e.g. #:~:text=foo
|
/* Chrome hides text frament from location.href of the page e.g. #:~:text=foo
|
||||||
so we'll use the real URL reported by webNavigation API */
|
so we'll use the real URL reported by webNavigation API */
|
||||||
const {tab, frameId} = this && this.sender || {};
|
const {tab, frameId} = this && this.sender || {};
|
||||||
|
@ -170,13 +188,13 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async get(id) {
|
async get(id) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
return id2style(id);
|
return id2style(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StylesByUrlResult[]>} */
|
/** @returns {Promise<StylesByUrlResult[]>} */
|
||||||
async getByUrl(url, id = null) {
|
async getByUrl(url, id = null) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
// FIXME: do we want to cache this? Who would like to open popup rapidly
|
// FIXME: do we want to cache this? Who would like to open popup rapidly
|
||||||
// or search the DB with the same URL?
|
// or search the DB with the same URL?
|
||||||
const result = [];
|
const result = [];
|
||||||
|
@ -218,7 +236,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj[]>} */
|
/** @returns {Promise<StyleObj[]>} */
|
||||||
async importMany(items) {
|
async importMany(items) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
items.forEach(beforeSave);
|
items.forEach(beforeSave);
|
||||||
const events = await db.exec('putMany', items);
|
const events = await db.exec('putMany', items);
|
||||||
return Promise.all(items.map((item, i) => {
|
return Promise.all(items.map((item, i) => {
|
||||||
|
@ -229,13 +247,13 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async import(data) {
|
async import(data) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
return handleSave(await saveStyle(data), 'import');
|
return handleSave(await saveStyle(data), 'import');
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async install(style, reason = null) {
|
async install(style, reason = null) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
reason = reason || dataMap.has(style.id) ? 'update' : 'install';
|
reason = reason || dataMap.has(style.id) ? 'update' : 'install';
|
||||||
style = mergeWithMapped(style);
|
style = mergeWithMapped(style);
|
||||||
const url = !style.url && style.updateUrl && (
|
const url = !style.url && style.updateUrl && (
|
||||||
|
@ -250,7 +268,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<?StyleObj>} */
|
/** @returns {Promise<?StyleObj>} */
|
||||||
async putByUUID(doc) {
|
async putByUUID(doc) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const id = uuidIndex.get(doc._id);
|
const id = uuidIndex.get(doc._id);
|
||||||
if (id) {
|
if (id) {
|
||||||
doc.id = id;
|
doc.id = id;
|
||||||
|
@ -275,7 +293,7 @@ define(require => {
|
||||||
|
|
||||||
/** @returns {Promise<number>} style id */
|
/** @returns {Promise<number>} style id */
|
||||||
async toggle(id, enabled) {
|
async toggle(id, enabled) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const style = Object.assign({}, id2style(id), {enabled});
|
const style = Object.assign({}, id2style(id), {enabled});
|
||||||
handleSave(await saveStyle(style), 'toggle', false);
|
handleSave(await saveStyle(style), 'toggle', false);
|
||||||
return id;
|
return id;
|
||||||
|
@ -362,7 +380,7 @@ define(require => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addIncludeExclude(type, id, rule) {
|
async function addIncludeExclude(type, id, rule) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const style = Object.assign({}, id2style(id));
|
const style = Object.assign({}, id2style(id));
|
||||||
const list = style[type] || (style[type] = []);
|
const list = style[type] || (style[type] = []);
|
||||||
if (list.includes(rule)) {
|
if (list.includes(rule)) {
|
||||||
|
@ -373,7 +391,7 @@ define(require => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeIncludeExclude(type, id, rule) {
|
async function removeIncludeExclude(type, id, rule) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
const style = Object.assign({}, id2style(id));
|
const style = Object.assign({}, id2style(id));
|
||||||
const list = style[type];
|
const list = style[type];
|
||||||
if (!list || !list.includes(rule)) {
|
if (!list || !list.includes(rule)) {
|
||||||
|
@ -472,21 +490,6 @@ define(require => {
|
||||||
return code.length && code;
|
return code.length && code;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const styles = await db.exec('getAll') || [];
|
|
||||||
const updated = styles.filter(style =>
|
|
||||||
addMissingProps(style) +
|
|
||||||
addCustomName(style));
|
|
||||||
if (updated.length) {
|
|
||||||
await db.exec('putMany', updated);
|
|
||||||
}
|
|
||||||
for (const style of styles) {
|
|
||||||
fixUsoMd5Issue(style);
|
|
||||||
storeInMap(style);
|
|
||||||
uuidIndex.set(style._id, style.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMissingProps(style) {
|
function addMissingProps(style) {
|
||||||
let res = 0;
|
let res = 0;
|
||||||
for (const key in MISSING_PROPS) {
|
for (const key in MISSING_PROPS) {
|
||||||
|
|
|
@ -7,9 +7,6 @@ define(require => {
|
||||||
const {compareRevision} = require('./style-manager');
|
const {compareRevision} = require('./style-manager');
|
||||||
const tokenManager = require('./token-manager');
|
const tokenManager = require('./token-manager');
|
||||||
|
|
||||||
/** @type Sync */
|
|
||||||
let sync;
|
|
||||||
|
|
||||||
//#region Init
|
//#region Init
|
||||||
|
|
||||||
const SYNC_DELAY = 1; // minutes
|
const SYNC_DELAY = 1; // minutes
|
||||||
|
@ -32,30 +29,23 @@ define(require => {
|
||||||
};
|
};
|
||||||
let currentDrive;
|
let currentDrive;
|
||||||
let ctrl;
|
let ctrl;
|
||||||
|
let ready = false;
|
||||||
const ready = prefs.initializing.then(() => {
|
const init = prefs.initializing.then(onPrefsReady);
|
||||||
prefs.subscribe('sync.enabled',
|
chrome.alarms.onAlarm.addListener(onAlarm);
|
||||||
(_, val) => val === 'none'
|
|
||||||
? sync.stop()
|
|
||||||
: sync.start(val, true),
|
|
||||||
{runNow: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.alarms.onAlarm.addListener(info => {
|
|
||||||
if (info.name === 'syncNow') {
|
|
||||||
sync.syncNow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Exports
|
//#region Exports
|
||||||
|
|
||||||
sync = /** @namespace Sync */ {
|
/**
|
||||||
|
* @type Sync
|
||||||
|
* @namespace Sync
|
||||||
|
*/
|
||||||
|
const sync = {
|
||||||
|
|
||||||
// sorted alphabetically
|
// sorted alphabetically
|
||||||
|
|
||||||
async delete(...args) {
|
async delete(...args) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
if (!currentDrive) return;
|
if (!currentDrive) return;
|
||||||
schedule();
|
schedule();
|
||||||
return ctrl.delete(...args);
|
return ctrl.delete(...args);
|
||||||
|
@ -67,7 +57,7 @@ define(require => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async login(name = prefs.get('sync.enabled')) {
|
async login(name = prefs.get('sync.enabled')) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
try {
|
try {
|
||||||
await tokenManager.getToken(name, true);
|
await tokenManager.getToken(name, true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -82,14 +72,14 @@ define(require => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async put(...args) {
|
async put(...args) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
if (!currentDrive) return;
|
if (!currentDrive) return;
|
||||||
schedule();
|
schedule();
|
||||||
return ctrl.put(...args);
|
return ctrl.put(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
async start(name, fromPref = false) {
|
async start(name, fromPref = false) {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
if (currentDrive) {
|
if (currentDrive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +111,7 @@ define(require => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async stop() {
|
async stop() {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
if (!currentDrive) {
|
if (!currentDrive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +132,7 @@ define(require => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async syncNow() {
|
async syncNow() {
|
||||||
await ready;
|
if (!ready) await init;
|
||||||
if (!currentDrive) {
|
if (!currentDrive) {
|
||||||
return Promise.reject(new Error('cannot sync when disconnected'));
|
return Promise.reject(new Error('cannot sync when disconnected'));
|
||||||
}
|
}
|
||||||
|
@ -197,13 +187,6 @@ define(require => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function schedule(delay = SYNC_DELAY) {
|
|
||||||
chrome.alarms.create('syncNow', {
|
|
||||||
delayInMinutes: delay,
|
|
||||||
periodInMinutes: SYNC_INTERVAL,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handle401Error(err) {
|
async function handle401Error(err) {
|
||||||
let emit;
|
let emit;
|
||||||
if (err.code === 401) {
|
if (err.code === 401) {
|
||||||
|
@ -232,6 +215,28 @@ define(require => {
|
||||||
throw new Error(`unknown cloud name: ${name}`);
|
throw new Error(`unknown cloud name: ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAlarm(info) {
|
||||||
|
if (info.name === 'syncNow') {
|
||||||
|
sync.syncNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPrefsReady() {
|
||||||
|
ready = true;
|
||||||
|
prefs.subscribe('sync.enabled',
|
||||||
|
(_, val) => val === 'none'
|
||||||
|
? sync.stop()
|
||||||
|
: sync.start(val, true),
|
||||||
|
{runNow: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedule(delay = SYNC_DELAY) {
|
||||||
|
chrome.alarms.create('syncNow', {
|
||||||
|
delayInMinutes: delay,
|
||||||
|
periodInMinutes: SYNC_INTERVAL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
return sync;
|
return sync;
|
||||||
|
|
|
@ -7,12 +7,7 @@ define(require => {
|
||||||
const AUTH = createAuth();
|
const AUTH = createAuth();
|
||||||
const NETWORK_LATENCY = 30; // seconds
|
const NETWORK_LATENCY = 30; // seconds
|
||||||
|
|
||||||
let exports;
|
const tokenManager = {
|
||||||
const {
|
|
||||||
|
|
||||||
buildKeys,
|
|
||||||
|
|
||||||
} = exports = {
|
|
||||||
|
|
||||||
buildKeys(name) {
|
buildKeys(name) {
|
||||||
const k = {
|
const k = {
|
||||||
|
@ -29,7 +24,7 @@ define(require => {
|
||||||
},
|
},
|
||||||
|
|
||||||
getToken(name, interactive) {
|
getToken(name, interactive) {
|
||||||
const k = buildKeys(name);
|
const k = tokenManager.buildKeys(name);
|
||||||
return chromeLocal.get(k.LIST)
|
return chromeLocal.get(k.LIST)
|
||||||
.then(obj => {
|
.then(obj => {
|
||||||
if (!obj[k.TOKEN]) {
|
if (!obj[k.TOKEN]) {
|
||||||
|
@ -53,7 +48,7 @@ define(require => {
|
||||||
|
|
||||||
async revokeToken(name) {
|
async revokeToken(name) {
|
||||||
const provider = AUTH[name];
|
const provider = AUTH[name];
|
||||||
const k = buildKeys(name);
|
const k = tokenManager.buildKeys(name);
|
||||||
if (provider.revoke) {
|
if (provider.revoke) {
|
||||||
try {
|
try {
|
||||||
const token = await chromeLocal.getValue(k.TOKEN);
|
const token = await chromeLocal.getValue(k.TOKEN);
|
||||||
|
@ -222,5 +217,5 @@ define(require => {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports;
|
return tokenManager;
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,8 +47,11 @@ define(require => {
|
||||||
chrome.alarms.onAlarm.addListener(onAlarm);
|
chrome.alarms.onAlarm.addListener(onAlarm);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @type {StyleUpdater} */
|
/**
|
||||||
const updater = /** @namespace StyleUpdater */ {
|
* @type StyleUpdater
|
||||||
|
* @namespace StyleUpdater
|
||||||
|
*/
|
||||||
|
const updater = {
|
||||||
|
|
||||||
async checkAllStyles({
|
async checkAllStyles({
|
||||||
save = true,
|
save = true,
|
||||||
|
|
|
@ -16,7 +16,11 @@ define(require => {
|
||||||
'missingChar',
|
'missingChar',
|
||||||
];
|
];
|
||||||
|
|
||||||
const usercss = /** @namespace UsercssHelper */ {
|
/**
|
||||||
|
* @type UsercssHelper
|
||||||
|
* @namespace UsercssHelper
|
||||||
|
*/
|
||||||
|
const usercss = {
|
||||||
|
|
||||||
rxMETA: /\/\*!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i,
|
rxMETA: /\/\*!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i,
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,39 @@
|
||||||
|
|
||||||
define(require => { // define and require use `importScripts` which is synchronous
|
define(require => { // define and require use `importScripts` which is synchronous
|
||||||
|
|
||||||
|
/** @namespace EditorWorker */
|
||||||
|
require('/js/worker-util').createAPI({
|
||||||
|
|
||||||
|
async csslint(code, config) {
|
||||||
|
return require('/js/csslint/csslint')
|
||||||
|
.verify(code, config).messages
|
||||||
|
.map(m => Object.assign(m, {rule: {id: m.rule.id}}));
|
||||||
|
},
|
||||||
|
|
||||||
|
getRules(linter) {
|
||||||
|
return ruleRetriever[linter](); // eslint-disable-line no-use-before-define
|
||||||
|
},
|
||||||
|
|
||||||
|
metalint(code) {
|
||||||
|
const result = require('/js/meta-parser').lint(code);
|
||||||
|
// extract needed info
|
||||||
|
result.errors = result.errors.map(err => ({
|
||||||
|
code: err.code,
|
||||||
|
args: err.args,
|
||||||
|
message: err.message,
|
||||||
|
index: err.index,
|
||||||
|
}));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
async stylelint(code, config) {
|
||||||
|
require('/vendor/stylelint-bundle/stylelint-bundle.min');
|
||||||
|
const {results: [res]} = await self.require('stylelint').lint({code, config});
|
||||||
|
delete res._postcssResult; // huge and unused
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const ruleRetriever = {
|
const ruleRetriever = {
|
||||||
|
|
||||||
csslint() {
|
csslint() {
|
||||||
|
@ -54,37 +87,4 @@ define(require => { // define and require use `importScripts` which is synchrono
|
||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @namespace EditorWorker */
|
|
||||||
require('/js/worker-util').createAPI({
|
|
||||||
|
|
||||||
async csslint(code, config) {
|
|
||||||
return require('/js/csslint/csslint')
|
|
||||||
.verify(code, config).messages
|
|
||||||
.map(m => Object.assign(m, {rule: {id: m.rule.id}}));
|
|
||||||
},
|
|
||||||
|
|
||||||
getRules(linter) {
|
|
||||||
return ruleRetriever[linter]();
|
|
||||||
},
|
|
||||||
|
|
||||||
metalint(code) {
|
|
||||||
const result = require('/js/meta-parser').lint(code);
|
|
||||||
// extract needed info
|
|
||||||
result.errors = result.errors.map(err => ({
|
|
||||||
code: err.code,
|
|
||||||
args: err.args,
|
|
||||||
message: err.message,
|
|
||||||
index: err.index,
|
|
||||||
}));
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
async stylelint(code, config) {
|
|
||||||
require('/vendor/stylelint-bundle/stylelint-bundle.min');
|
|
||||||
const {results: [res]} = await self.require('stylelint').lint({code, config});
|
|
||||||
delete res._postcssResult; // huge and unused
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,9 +11,9 @@ define(require => {
|
||||||
const {clipString} = require('./util');
|
const {clipString} = require('./util');
|
||||||
|
|
||||||
const dirty = DirtyReporter();
|
const dirty = DirtyReporter();
|
||||||
|
const toc = [];
|
||||||
let style;
|
let style;
|
||||||
let wasDirty = false;
|
let wasDirty = false;
|
||||||
const toc = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @mixes SectionsEditor
|
* @mixes SectionsEditor
|
||||||
|
|
115
js/cache.js
115
js/cache.js
|
@ -4,63 +4,62 @@
|
||||||
* Creates a FIFO limit-size map.
|
* Creates a FIFO limit-size map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define(require =>
|
define(require => function createCache({size = 1000, onDeleted} = {}) {
|
||||||
function createCache({size = 1000, onDeleted} = {}) {
|
const map = new Map();
|
||||||
const map = new Map();
|
const buffer = Array(size);
|
||||||
const buffer = Array(size);
|
let index = 0;
|
||||||
let index = 0;
|
let lastIndex = 0;
|
||||||
let lastIndex = 0;
|
return {
|
||||||
return {
|
get(id) {
|
||||||
get(id) {
|
const item = map.get(id);
|
||||||
const item = map.get(id);
|
return item && item.data;
|
||||||
return item && item.data;
|
},
|
||||||
},
|
set(id, data) {
|
||||||
set(id, data) {
|
if (map.size === size) {
|
||||||
if (map.size === size) {
|
// full
|
||||||
// full
|
map.delete(buffer[lastIndex].id);
|
||||||
map.delete(buffer[lastIndex].id);
|
|
||||||
if (onDeleted) {
|
|
||||||
onDeleted(buffer[lastIndex].id, buffer[lastIndex].data);
|
|
||||||
}
|
|
||||||
lastIndex = (lastIndex + 1) % size;
|
|
||||||
}
|
|
||||||
const item = {id, data, index};
|
|
||||||
map.set(id, item);
|
|
||||||
buffer[index] = item;
|
|
||||||
index = (index + 1) % size;
|
|
||||||
},
|
|
||||||
delete(id) {
|
|
||||||
const item = map.get(id);
|
|
||||||
if (!item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
map.delete(item.id);
|
|
||||||
const lastItem = buffer[lastIndex];
|
|
||||||
lastItem.index = item.index;
|
|
||||||
buffer[item.index] = lastItem;
|
|
||||||
lastIndex = (lastIndex + 1) % size;
|
|
||||||
if (onDeleted) {
|
if (onDeleted) {
|
||||||
onDeleted(item.id, item.data);
|
onDeleted(buffer[lastIndex].id, buffer[lastIndex].data);
|
||||||
}
|
}
|
||||||
return true;
|
lastIndex = (lastIndex + 1) % size;
|
||||||
},
|
}
|
||||||
clear() {
|
const item = {id, data, index};
|
||||||
map.clear();
|
map.set(id, item);
|
||||||
index = lastIndex = 0;
|
buffer[index] = item;
|
||||||
},
|
index = (index + 1) % size;
|
||||||
has: id => map.has(id),
|
},
|
||||||
*entries() {
|
delete(id) {
|
||||||
for (const [id, item] of map) {
|
const item = map.get(id);
|
||||||
yield [id, item.data];
|
if (!item) {
|
||||||
}
|
return false;
|
||||||
},
|
}
|
||||||
*values() {
|
map.delete(item.id);
|
||||||
for (const item of map.values()) {
|
const lastItem = buffer[lastIndex];
|
||||||
yield item.data;
|
lastItem.index = item.index;
|
||||||
}
|
buffer[item.index] = lastItem;
|
||||||
},
|
lastIndex = (lastIndex + 1) % size;
|
||||||
get size() {
|
if (onDeleted) {
|
||||||
return map.size;
|
onDeleted(item.id, item.data);
|
||||||
},
|
}
|
||||||
};
|
return true;
|
||||||
});
|
},
|
||||||
|
clear() {
|
||||||
|
map.clear();
|
||||||
|
index = lastIndex = 0;
|
||||||
|
},
|
||||||
|
has: id => map.has(id),
|
||||||
|
*entries() {
|
||||||
|
for (const [id, item] of map) {
|
||||||
|
yield [id, item.data];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*values() {
|
||||||
|
for (const item of map.values()) {
|
||||||
|
yield item.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get size() {
|
||||||
|
return map.size;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
15
js/dom.js
15
js/dom.js
|
@ -9,18 +9,11 @@ define(require => {
|
||||||
|
|
||||||
/** @type {Prefs} */
|
/** @type {Prefs} */
|
||||||
let prefs;
|
let prefs;
|
||||||
|
let $, $$;
|
||||||
|
|
||||||
//#region Exports
|
//#region Exports
|
||||||
|
|
||||||
/** @type {DOM} */
|
const dom = {
|
||||||
let dom;
|
|
||||||
const {
|
|
||||||
|
|
||||||
$,
|
|
||||||
$$,
|
|
||||||
$create,
|
|
||||||
|
|
||||||
} = dom = /** @namespace DOM */ {
|
|
||||||
|
|
||||||
$(selector, base = document) {
|
$(selector, base = document) {
|
||||||
// we have ids with . like #manage.onlyEnabled which looks like #id.class
|
// we have ids with . like #manage.onlyEnabled which looks like #id.class
|
||||||
|
@ -351,6 +344,8 @@ define(require => {
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Init
|
//#region Init
|
||||||
|
|
||||||
|
({$, $$} = dom);
|
||||||
|
|
||||||
const Collapsible = {
|
const Collapsible = {
|
||||||
bindEvents(_, elems) {
|
bindEvents(_, elems) {
|
||||||
const prefKeys = [];
|
const prefKeys = [];
|
||||||
|
@ -413,7 +408,7 @@ define(require => {
|
||||||
function addFaviconFF() {
|
function addFaviconFF() {
|
||||||
const iconset = ['', 'light/'][prefs.get('iconset')] || '';
|
const iconset = ['', 'light/'][prefs.get('iconset')] || '';
|
||||||
for (const size of [38, 32, 19, 16]) {
|
for (const size of [38, 32, 19, 16]) {
|
||||||
document.head.appendChild($create('link', {
|
document.head.appendChild(dom.$create('link', {
|
||||||
rel: 'icon',
|
rel: 'icon',
|
||||||
href: `/images/icon/${iconset}${size}.png`,
|
href: `/images/icon/${iconset}${size}.png`,
|
||||||
sizes: size + 'x' + size,
|
sizes: size + 'x' + size,
|
||||||
|
|
12
js/prefs.js
12
js/prefs.js
|
@ -10,8 +10,11 @@ define(require => {
|
||||||
|
|
||||||
const STORAGE_KEY = 'settings';
|
const STORAGE_KEY = 'settings';
|
||||||
const clone = deepCopy || (val => JSON.parse(JSON.stringify(val)));
|
const clone = deepCopy || (val => JSON.parse(JSON.stringify(val)));
|
||||||
/** @type {PrefsValues} */
|
/**
|
||||||
const defaults = /** @namespace PrefsValues */ {
|
* @type PrefsValues
|
||||||
|
* @namespace PrefsValues
|
||||||
|
*/
|
||||||
|
const defaults = {
|
||||||
'openEditInWindow': false, // new editor opens in a own browser window
|
'openEditInWindow': false, // new editor opens in a own browser window
|
||||||
'openEditInWindow.popup': false, // new editor opens in a simplified browser window without omnibox
|
'openEditInWindow.popup': false, // new editor opens in a simplified browser window without omnibox
|
||||||
'windowPosition': {}, // detached window position
|
'windowPosition': {}, // detached window position
|
||||||
|
@ -138,7 +141,10 @@ define(require => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @namespace Prefs */
|
/**
|
||||||
|
* @type Prefs
|
||||||
|
* @namespace Prefs
|
||||||
|
*/
|
||||||
const prefs = {
|
const prefs = {
|
||||||
|
|
||||||
STORAGE_KEY,
|
STORAGE_KEY,
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
define(require => {
|
define(require => {
|
||||||
/** @type {Toolbox} */
|
|
||||||
let toolbox;
|
|
||||||
|
|
||||||
const ua = navigator.userAgent;
|
const ua = navigator.userAgent;
|
||||||
const chromeApp = Boolean(chrome.app);
|
const chromeApp = Boolean(chrome.app);
|
||||||
const CHROME = chromeApp && parseInt(ua.match(/Chrom\w+\/(\d+)|$/)[1]);
|
const CHROME = chromeApp && parseInt(ua.match(/Chrom\w+\/(\d+)|$/)[1]);
|
||||||
|
@ -14,14 +11,9 @@ define(require => {
|
||||||
// (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
|
// (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
|
||||||
const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
|
const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
|
||||||
const debounceTimers = new Map();
|
const debounceTimers = new Map();
|
||||||
const {
|
let URLS, deepCopy, deepEqual, deepMerge;
|
||||||
|
|
||||||
URLS,
|
const toolbox = {
|
||||||
deepCopy,
|
|
||||||
deepEqual,
|
|
||||||
deepMerge,
|
|
||||||
|
|
||||||
} = toolbox = /** @namespace Toolbox */ {
|
|
||||||
|
|
||||||
CHROME,
|
CHROME,
|
||||||
FIREFOX,
|
FIREFOX,
|
||||||
|
@ -384,6 +376,8 @@ define(require => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
({URLS, deepCopy, deepEqual, deepMerge} = toolbox);
|
||||||
|
|
||||||
// see PR #781
|
// see PR #781
|
||||||
if (!CHROME && !chrome.browserAction.openPopup) {
|
if (!CHROME && !chrome.browserAction.openPopup) {
|
||||||
// in FF pre-57 legacy addons can override useragent so we assume the worst
|
// in FF pre-57 legacy addons can override useragent so we assume the worst
|
||||||
|
|
|
@ -5,6 +5,10 @@ define(require => {
|
||||||
const t = require('/js/localization');
|
const t = require('/js/localization');
|
||||||
const prefs = require('/js/prefs');
|
const prefs = require('/js/prefs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type NewUI
|
||||||
|
* @namespace NewUI
|
||||||
|
*/
|
||||||
const newUI = {
|
const newUI = {
|
||||||
enabled: null, // the global option should come first
|
enabled: null, // the global option should come first
|
||||||
favicons: null,
|
favicons: null,
|
||||||
|
@ -14,7 +18,7 @@ define(require => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ...add utility functions
|
// ...add utility functions
|
||||||
Object.assign(newUI, {
|
Object.assign(newUI, /** @namespace NewUI */ {
|
||||||
|
|
||||||
ids: Object.keys(newUI),
|
ids: Object.keys(newUI),
|
||||||
|
|
||||||
|
|
|
@ -336,8 +336,6 @@ define(require => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const {$entry} = render;
|
|
||||||
|
|
||||||
function createAgeText(el, style) {
|
function createAgeText(el, style) {
|
||||||
let val = style.updateDate || style.installDate;
|
let val = style.updateDate || style.installDate;
|
||||||
if (val) {
|
if (val) {
|
||||||
|
@ -359,7 +357,7 @@ define(require => {
|
||||||
|
|
||||||
function highlightEditedStyle() {
|
function highlightEditedStyle() {
|
||||||
if (!sessionStore.justEditedStyleId) return;
|
if (!sessionStore.justEditedStyleId) return;
|
||||||
const entry = $entry(sessionStore.justEditedStyleId);
|
const entry = render.$entry(sessionStore.justEditedStyleId);
|
||||||
delete sessionStore.justEditedStyleId;
|
delete sessionStore.justEditedStyleId;
|
||||||
if (entry) {
|
if (entry) {
|
||||||
animateElement(entry);
|
animateElement(entry);
|
||||||
|
|
|
@ -15,26 +15,9 @@ define(require => {
|
||||||
|
|
||||||
const MODAL_SHOWN = 'data-display'; // attribute name
|
const MODAL_SHOWN = 'data-display'; // attribute name
|
||||||
|
|
||||||
/** @type {PopupEvents} */
|
const Events = {
|
||||||
let exports;
|
|
||||||
const {
|
|
||||||
|
|
||||||
closeExplanation,
|
tabURL: '',
|
||||||
getClickedStyleElement,
|
|
||||||
getClickedStyleId,
|
|
||||||
getExcludeRule,
|
|
||||||
hideModal,
|
|
||||||
openURLandHide,
|
|
||||||
showModal,
|
|
||||||
thisTab,
|
|
||||||
|
|
||||||
} = exports = /** @namespace PopupEvents */ {
|
|
||||||
|
|
||||||
thisTab: {url: ''},
|
|
||||||
|
|
||||||
closeExplanation() {
|
|
||||||
$('#regexp-explanation').remove();
|
|
||||||
},
|
|
||||||
|
|
||||||
async configure(event) {
|
async configure(event) {
|
||||||
const {styleId, styleIsUsercss} = getClickedStyleElement(event);
|
const {styleId, styleIsUsercss} = getClickedStyleElement(event);
|
||||||
|
@ -46,7 +29,7 @@ define(require => {
|
||||||
await configDialog(style);
|
await configDialog(style);
|
||||||
hotkeys.setState(true);
|
hotkeys.setState(true);
|
||||||
} else {
|
} else {
|
||||||
openURLandHide.call(this, event);
|
Events.openURLandHide.call(this, event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -68,19 +51,11 @@ define(require => {
|
||||||
const box = $('#confirm');
|
const box = $('#confirm');
|
||||||
box.dataset.id = entry.styleId;
|
box.dataset.id = entry.styleId;
|
||||||
$('b', box).textContent = $('.style-name', entry).textContent;
|
$('b', box).textContent = $('.style-name', entry).textContent;
|
||||||
showModal(box, '[data-cmd=cancel]');
|
Events.showModal(box, '[data-cmd=cancel]');
|
||||||
},
|
|
||||||
|
|
||||||
getClickedStyleId(event) {
|
|
||||||
return (getClickedStyleElement(event) || {}).styleId;
|
|
||||||
},
|
|
||||||
|
|
||||||
getClickedStyleElement(event) {
|
|
||||||
return event.target.closest('.entry');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getExcludeRule(type) {
|
getExcludeRule(type) {
|
||||||
const u = new URL(thisTab.url);
|
const u = new URL(Events.tabURL);
|
||||||
return type === 'domain'
|
return type === 'domain'
|
||||||
? u.origin + '/*'
|
? u.origin + '/*'
|
||||||
: escapeGlob(u.origin + u.pathname); // current page
|
: escapeGlob(u.origin + u.pathname); // current page
|
||||||
|
@ -100,7 +75,7 @@ define(require => {
|
||||||
const entry = getClickedStyleElement(event);
|
const entry = getClickedStyleElement(event);
|
||||||
const info = t.template.regexpProblemExplanation.cloneNode(true);
|
const info = t.template.regexpProblemExplanation.cloneNode(true);
|
||||||
$remove('#' + info.id);
|
$remove('#' + info.id);
|
||||||
$$('a', info).forEach(el => (el.onclick = openURLandHide));
|
$$('a', info).forEach(el => (el.onclick = Events.openURLandHide));
|
||||||
$$('button', info).forEach(el => (el.onclick = closeExplanation));
|
$$('button', info).forEach(el => (el.onclick = closeExplanation));
|
||||||
entry.appendChild(info);
|
entry.appendChild(info);
|
||||||
},
|
},
|
||||||
|
@ -109,7 +84,7 @@ define(require => {
|
||||||
if (!exclusions) {
|
if (!exclusions) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const rule = getExcludeRule(type);
|
const rule = Events.getExcludeRule(type);
|
||||||
return exclusions.includes(rule);
|
return exclusions.includes(rule);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -149,8 +124,8 @@ define(require => {
|
||||||
|
|
||||||
async openManager(event) {
|
async openManager(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const isSearch = thisTab.url && (event.shiftKey || event.button === 2);
|
const isSearch = Events.tabURL && (event.shiftKey || event.button === 2);
|
||||||
await API.openManage(isSearch ? {search: thisTab.url, searchMode: 'url'} : {});
|
await API.openManage(isSearch ? {search: Events.tabURL, searchMode: 'url'} : {});
|
||||||
window.close();
|
window.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -187,7 +162,7 @@ define(require => {
|
||||||
};
|
};
|
||||||
window.on('keydown', box._onkeydown);
|
window.on('keydown', box._onkeydown);
|
||||||
moveFocus(box, 0);
|
moveFocus(box, 0);
|
||||||
hideModal(oldBox);
|
Events.hideModal(oldBox);
|
||||||
},
|
},
|
||||||
|
|
||||||
async toggleState(event) {
|
async toggleState(event) {
|
||||||
|
@ -200,9 +175,9 @@ define(require => {
|
||||||
toggleExclude(event, type) {
|
toggleExclude(event, type) {
|
||||||
const entry = getClickedStyleElement(event);
|
const entry = getClickedStyleElement(event);
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
API.styles.addExclusion(entry.styleMeta.id, getExcludeRule(type));
|
API.styles.addExclusion(entry.styleMeta.id, Events.getExcludeRule(type));
|
||||||
} else {
|
} else {
|
||||||
API.styles.removeExclusion(entry.styleMeta.id, getExcludeRule(type));
|
API.styles.removeExclusion(entry.styleMeta.id, Events.getExcludeRule(type));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -210,17 +185,29 @@ define(require => {
|
||||||
const entry = getClickedStyleElement(event);
|
const entry = getClickedStyleElement(event);
|
||||||
const menu = $('.menu', entry);
|
const menu = $('.menu', entry);
|
||||||
if (menu.hasAttribute(MODAL_SHOWN)) {
|
if (menu.hasAttribute(MODAL_SHOWN)) {
|
||||||
hideModal(menu, {animate: true});
|
Events.hideModal(menu, {animate: true});
|
||||||
} else {
|
} else {
|
||||||
$('.menu-title', entry).textContent = $('.style-name', entry).textContent;
|
$('.menu-title', entry).textContent = $('.style-name', entry).textContent;
|
||||||
showModal(menu, '.menu-close');
|
Events.showModal(menu, '.menu-close');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function closeExplanation() {
|
||||||
|
$('#regexp-explanation').remove();
|
||||||
|
}
|
||||||
|
|
||||||
function escapeGlob(text) {
|
function escapeGlob(text) {
|
||||||
return text.replace(/\*/g, '\\*');
|
return text.replace(/\*/g, '\\*');
|
||||||
}
|
}
|
||||||
|
|
||||||
return exports;
|
function getClickedStyleElement(event) {
|
||||||
|
return event.target.closest('.entry');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClickedStyleId(event) {
|
||||||
|
return (getClickedStyleElement(event) || {}).styleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Events;
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,8 +31,7 @@ define(require => {
|
||||||
const ENTRY_ID_PREFIX_RAW = 'style-';
|
const ENTRY_ID_PREFIX_RAW = 'style-';
|
||||||
|
|
||||||
initializing.then(({frames, styles, url}) => {
|
initializing.then(({frames, styles, url}) => {
|
||||||
tabURL = url;
|
tabURL = Events.tabURL = url;
|
||||||
Events.thisTab.url = url;
|
|
||||||
toggleUiSliders();
|
toggleUiSliders();
|
||||||
initPopup(frames);
|
initPopup(frames);
|
||||||
if (styles[0]) {
|
if (styles[0]) {
|
||||||
|
|
|
@ -444,7 +444,7 @@ define(require => {
|
||||||
* @returns {boolean} true if the category has actually changed
|
* @returns {boolean} true if the category has actually changed
|
||||||
*/
|
*/
|
||||||
function calcCategory({retry} = {}) {
|
function calcCategory({retry} = {}) {
|
||||||
const u = tryCatch(() => new URL(Events.thisTab.url));
|
const u = tryCatch(() => new URL(Events.tabURL));
|
||||||
const old = category;
|
const old = category;
|
||||||
if (!u) {
|
if (!u) {
|
||||||
// Invalid URL
|
// Invalid URL
|
||||||
|
|
Loading…
Reference in New Issue
Block a user