Refactor: split out regexp tester
This commit is contained in:
parent
f255631a0b
commit
9ee7e2046c
|
@ -23,6 +23,7 @@
|
||||||
<link rel="stylesheet" href="edit/edit.css">
|
<link rel="stylesheet" href="edit/edit.css">
|
||||||
<script src="edit/lint.js"></script>
|
<script src="edit/lint.js"></script>
|
||||||
<script src="edit/util.js"></script>
|
<script src="edit/util.js"></script>
|
||||||
|
<script src="edit/regexp-tester.js"></script>
|
||||||
<script src="edit/source-editor.js"></script>
|
<script src="edit/source-editor.js"></script>
|
||||||
<script src="edit/edit.js"></script>
|
<script src="edit/edit.js"></script>
|
||||||
|
|
||||||
|
|
167
edit/edit.js
167
edit/edit.js
|
@ -3,7 +3,7 @@
|
||||||
/* global css_beautify */
|
/* global css_beautify */
|
||||||
/* global CSSLint initLint linterConfig updateLintReport renderLintReport updateLinter */
|
/* global CSSLint initLint linterConfig updateLintReport renderLintReport updateLinter */
|
||||||
/* global mozParser createSourceEditor */
|
/* global mozParser createSourceEditor */
|
||||||
/* global loadScript closeCurrentTab */
|
/* global loadScript closeCurrentTab regExpTester */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -568,20 +568,30 @@ function addSection(event, section) {
|
||||||
|
|
||||||
toggleTestRegExpVisibility();
|
toggleTestRegExpVisibility();
|
||||||
appliesTo.addEventListener('change', toggleTestRegExpVisibility);
|
appliesTo.addEventListener('change', toggleTestRegExpVisibility);
|
||||||
$('.test-regexp', div).onclick = showRegExpTester;
|
$('.test-regexp', div).onclick = () => {
|
||||||
function toggleTestRegExpVisibility() {
|
regExpTester.toggle();
|
||||||
const show = [...appliesTo.children].some(item =>
|
regExpTester.update(getRegExps());
|
||||||
|
};
|
||||||
|
|
||||||
|
function getRegExps() {
|
||||||
|
return [...appliesTo.children]
|
||||||
|
.map(item =>
|
||||||
!item.matches('.applies-to-everything') &&
|
!item.matches('.applies-to-everything') &&
|
||||||
$('.applies-type', item).value === 'regexp' &&
|
$('.applies-type', item).value === 'regexp' &&
|
||||||
$('.applies-value', item).value.trim()
|
$('.applies-value', item).value.trim()
|
||||||
);
|
)
|
||||||
|
.filter(item => item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTestRegExpVisibility() {
|
||||||
|
const show = getRegExps().length > 0;
|
||||||
div.classList.toggle('has-regexp', show);
|
div.classList.toggle('has-regexp', show);
|
||||||
appliesTo.oninput = appliesTo.oninput || show && (event => {
|
appliesTo.oninput = appliesTo.oninput || show && (event => {
|
||||||
if (
|
if (
|
||||||
event.target.matches('.applies-value') &&
|
event.target.matches('.applies-value') &&
|
||||||
$('.applies-type', event.target.parentElement).value === 'regexp'
|
$('.applies-type', event.target.parentElement).value === 'regexp'
|
||||||
) {
|
) {
|
||||||
showRegExpTester(null, div);
|
regExpTester.update(getRegExps());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1771,151 +1781,6 @@ function showKeyMapHelp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRegExpTester(event, section = getSectionForChild(this)) {
|
|
||||||
const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain=';
|
|
||||||
const OWN_ICON = chrome.runtime.getManifest().icons['16'];
|
|
||||||
const cachedRegexps = showRegExpTester.cachedRegexps =
|
|
||||||
showRegExpTester.cachedRegexps || new Map();
|
|
||||||
const regexps = [...$('.applies-to-list', section).children]
|
|
||||||
.map(item =>
|
|
||||||
!item.matches('.applies-to-everything') &&
|
|
||||||
$('.applies-type', item).value === 'regexp' &&
|
|
||||||
$('.applies-value', item).value.trim()
|
|
||||||
)
|
|
||||||
.filter(item => item)
|
|
||||||
.map(text => {
|
|
||||||
const rxData = Object.assign({text}, cachedRegexps.get(text));
|
|
||||||
if (!rxData.urls) {
|
|
||||||
cachedRegexps.set(text, Object.assign(rxData, {
|
|
||||||
// imitate buggy Stylish-for-chrome, see detectSloppyRegexps()
|
|
||||||
rx: tryRegExp('^' + text + '$'),
|
|
||||||
urls: new Map(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return rxData;
|
|
||||||
});
|
|
||||||
chrome.tabs.onUpdated.addListener(function _(tabId, info) {
|
|
||||||
if ($('.regexp-report')) {
|
|
||||||
if (info.url) {
|
|
||||||
showRegExpTester(event, section);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
chrome.tabs.onUpdated.removeListener(_);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const getMatchInfo = m => m && {text: m[0], pos: m.index};
|
|
||||||
|
|
||||||
queryTabs().then(tabs => {
|
|
||||||
const supported = tabs.map(tab => tab.url)
|
|
||||||
.filter(url => URLS.supported(url));
|
|
||||||
const unique = [...new Set(supported).values()];
|
|
||||||
for (const rxData of regexps) {
|
|
||||||
const {rx, urls} = rxData;
|
|
||||||
if (rx) {
|
|
||||||
const urlsNow = new Map();
|
|
||||||
for (const url of unique) {
|
|
||||||
const match = urls.get(url) || getMatchInfo(url.match(rx));
|
|
||||||
if (match) {
|
|
||||||
urlsNow.set(url, match);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rxData.urls = urlsNow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const stats = {
|
|
||||||
full: {data: [], label: t('styleRegexpTestFull')},
|
|
||||||
partial: {data: [], label: [
|
|
||||||
t('styleRegexpTestPartial'),
|
|
||||||
template.regexpTestPartial.cloneNode(true),
|
|
||||||
]},
|
|
||||||
none: {data: [], label: t('styleRegexpTestNone')},
|
|
||||||
invalid: {data: [], label: t('styleRegexpTestInvalid')},
|
|
||||||
};
|
|
||||||
// collect stats
|
|
||||||
for (const {text, rx, urls} of regexps) {
|
|
||||||
if (!rx) {
|
|
||||||
stats.invalid.data.push({text});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!urls.size) {
|
|
||||||
stats.none.data.push({text});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const full = [];
|
|
||||||
const partial = [];
|
|
||||||
for (const [url, match] of urls.entries()) {
|
|
||||||
const faviconUrl = url.startsWith(URLS.ownOrigin)
|
|
||||||
? OWN_ICON
|
|
||||||
: GET_FAVICON_URL + new URL(url).hostname;
|
|
||||||
const icon = $element({tag: 'img', src: faviconUrl});
|
|
||||||
if (match.text.length === url.length) {
|
|
||||||
full.push($element({appendChild: [
|
|
||||||
icon,
|
|
||||||
url,
|
|
||||||
]}));
|
|
||||||
} else {
|
|
||||||
partial.push($element({appendChild: [
|
|
||||||
icon,
|
|
||||||
url.substr(0, match.pos),
|
|
||||||
$element({tag: 'mark', textContent: match.text}),
|
|
||||||
url.substr(match.pos + match.text.length),
|
|
||||||
]}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (full.length) {
|
|
||||||
stats.full.data.push({text, urls: full});
|
|
||||||
}
|
|
||||||
if (partial.length) {
|
|
||||||
stats.partial.data.push({text, urls: partial});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// render stats
|
|
||||||
const report = $element({className: 'regexp-report'});
|
|
||||||
const br = $element({tag: 'br'});
|
|
||||||
for (const type in stats) {
|
|
||||||
// top level groups: full, partial, none, invalid
|
|
||||||
const {label, data} = stats[type];
|
|
||||||
if (!data.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const block = report.appendChild($element({
|
|
||||||
tag: 'details',
|
|
||||||
open: true,
|
|
||||||
dataset: {type},
|
|
||||||
appendChild: $element({tag: 'summary', appendChild: label}),
|
|
||||||
}));
|
|
||||||
// 2nd level: regexp text
|
|
||||||
for (const {text, urls} of data) {
|
|
||||||
if (urls) {
|
|
||||||
// type is partial or full
|
|
||||||
block.appendChild($element({
|
|
||||||
tag: 'details',
|
|
||||||
open: true,
|
|
||||||
appendChild: [
|
|
||||||
$element({tag: 'summary', textContent: text}),
|
|
||||||
// 3rd level: tab urls
|
|
||||||
...urls,
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
// type is none or invalid
|
|
||||||
block.appendChild(document.createTextNode(text));
|
|
||||||
block.appendChild(br.cloneNode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showHelp(t('styleRegexpTestTitle'), report);
|
|
||||||
|
|
||||||
$('.regexp-report').onclick = event => {
|
|
||||||
const target = event.target.closest('a, .regexp-report div');
|
|
||||||
if (target) {
|
|
||||||
openURL({url: target.href || target.textContent});
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHelp(title, body) {
|
function showHelp(title, body) {
|
||||||
const div = $('#help-popup');
|
const div = $('#help-popup');
|
||||||
div.classList.remove('big');
|
div.classList.remove('big');
|
||||||
|
|
162
edit/regexp-tester.js
Normal file
162
edit/regexp-tester.js
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/* global showHelp */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var regExpTester = (function () {
|
||||||
|
const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain=';
|
||||||
|
const OWN_ICON = chrome.runtime.getManifest().icons['16'];
|
||||||
|
const cachedRegexps = new Map();
|
||||||
|
let currentRegexps = [];
|
||||||
|
|
||||||
|
chrome.tabs.onUpdated.addListener(function _(tabId, info) {
|
||||||
|
if (info.url) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function isShowed() {
|
||||||
|
return $('.regexp-report');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(state = !isShowed()) {
|
||||||
|
if (state && !isShowed()) {
|
||||||
|
showHelp('', $element({className: 'regexp-report'}));
|
||||||
|
} else if (!state && isShowed()) {
|
||||||
|
// TODO: need a closeHelp function
|
||||||
|
$('#help-popup .dismiss').onclick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(newRegexps) {
|
||||||
|
if (!isShowed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newRegexps) {
|
||||||
|
currentRegexps = newRegexps;
|
||||||
|
}
|
||||||
|
const regexps = currentRegexps.map(text => {
|
||||||
|
const rxData = Object.assign({text}, cachedRegexps.get(text));
|
||||||
|
if (!rxData.urls) {
|
||||||
|
cachedRegexps.set(text, Object.assign(rxData, {
|
||||||
|
// imitate buggy Stylish-for-chrome, see detectSloppyRegexps()
|
||||||
|
rx: tryRegExp('^' + text + '$'),
|
||||||
|
urls: new Map(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return rxData;
|
||||||
|
});
|
||||||
|
const getMatchInfo = m => m && {text: m[0], pos: m.index};
|
||||||
|
queryTabs().then(tabs => {
|
||||||
|
const supported = tabs.map(tab => tab.url)
|
||||||
|
.filter(url => URLS.supported(url));
|
||||||
|
const unique = [...new Set(supported).values()];
|
||||||
|
for (const rxData of regexps) {
|
||||||
|
const {rx, urls} = rxData;
|
||||||
|
if (rx) {
|
||||||
|
const urlsNow = new Map();
|
||||||
|
for (const url of unique) {
|
||||||
|
const match = urls.get(url) || getMatchInfo(url.match(rx));
|
||||||
|
if (match) {
|
||||||
|
urlsNow.set(url, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rxData.urls = urlsNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const stats = {
|
||||||
|
full: {data: [], label: t('styleRegexpTestFull')},
|
||||||
|
partial: {data: [], label: [
|
||||||
|
t('styleRegexpTestPartial'),
|
||||||
|
template.regexpTestPartial.cloneNode(true),
|
||||||
|
]},
|
||||||
|
none: {data: [], label: t('styleRegexpTestNone')},
|
||||||
|
invalid: {data: [], label: t('styleRegexpTestInvalid')},
|
||||||
|
};
|
||||||
|
// collect stats
|
||||||
|
for (const {text, rx, urls} of regexps) {
|
||||||
|
if (!rx) {
|
||||||
|
stats.invalid.data.push({text});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!urls.size) {
|
||||||
|
stats.none.data.push({text});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const full = [];
|
||||||
|
const partial = [];
|
||||||
|
for (const [url, match] of urls.entries()) {
|
||||||
|
const faviconUrl = url.startsWith(URLS.ownOrigin)
|
||||||
|
? OWN_ICON
|
||||||
|
: GET_FAVICON_URL + new URL(url).hostname;
|
||||||
|
const icon = $element({tag: 'img', src: faviconUrl});
|
||||||
|
if (match.text.length === url.length) {
|
||||||
|
full.push($element({appendChild: [
|
||||||
|
icon,
|
||||||
|
url,
|
||||||
|
]}));
|
||||||
|
} else {
|
||||||
|
partial.push($element({appendChild: [
|
||||||
|
icon,
|
||||||
|
url.substr(0, match.pos),
|
||||||
|
$element({tag: 'mark', textContent: match.text}),
|
||||||
|
url.substr(match.pos + match.text.length),
|
||||||
|
]}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (full.length) {
|
||||||
|
stats.full.data.push({text, urls: full});
|
||||||
|
}
|
||||||
|
if (partial.length) {
|
||||||
|
stats.partial.data.push({text, urls: partial});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// render stats
|
||||||
|
const report = $element({className: 'regexp-report'});
|
||||||
|
const br = $element({tag: 'br'});
|
||||||
|
for (const type in stats) {
|
||||||
|
// top level groups: full, partial, none, invalid
|
||||||
|
const {label, data} = stats[type];
|
||||||
|
if (!data.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const block = report.appendChild($element({
|
||||||
|
tag: 'details',
|
||||||
|
open: true,
|
||||||
|
dataset: {type},
|
||||||
|
appendChild: $element({tag: 'summary', appendChild: label}),
|
||||||
|
}));
|
||||||
|
// 2nd level: regexp text
|
||||||
|
for (const {text, urls} of data) {
|
||||||
|
if (urls) {
|
||||||
|
// type is partial or full
|
||||||
|
block.appendChild($element({
|
||||||
|
tag: 'details',
|
||||||
|
open: true,
|
||||||
|
appendChild: [
|
||||||
|
$element({tag: 'summary', textContent: text}),
|
||||||
|
// 3rd level: tab urls
|
||||||
|
...urls,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// type is none or invalid
|
||||||
|
block.appendChild(document.createTextNode(text));
|
||||||
|
block.appendChild(br.cloneNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showHelp(t('styleRegexpTestTitle'), report);
|
||||||
|
|
||||||
|
$('.regexp-report').onclick = event => {
|
||||||
|
const target = event.target.closest('a, .regexp-report div');
|
||||||
|
if (target) {
|
||||||
|
openURL({url: target.href || target.textContent});
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {toggle, update};
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user