Add regexp tester to the editor
This commit is contained in:
parent
486d4258d3
commit
c356fb9be5
|
@ -362,6 +362,30 @@
|
|||
"message": "Regexp is invalid.",
|
||||
"description": "Validation message for a bad regexp in a style"
|
||||
},
|
||||
"styleRegexpTestButton": {
|
||||
"message": "RegExp test",
|
||||
"description": "RegExp test button label in the editor shown when applies-to list has a regexp value"
|
||||
},
|
||||
"styleRegexpTestTitle": {
|
||||
"message": "List of matching opened tabs (click on URL to focus its tab)",
|
||||
"description": "RegExp test report: title of the report"
|
||||
},
|
||||
"styleRegexpTestFull": {
|
||||
"message": "Matching tabs",
|
||||
"description": "RegExp test report: label for the fully matching expressions"
|
||||
},
|
||||
"styleRegexpTestPartial": {
|
||||
"message": "Not matching fully, hence skipped",
|
||||
"description": "RegExp test report: label for the partially matching expressions"
|
||||
},
|
||||
"styleRegexpTestNone": {
|
||||
"message": "No matching tabs",
|
||||
"description": "RegExp test report: label for expressions that didn't match any tabs"
|
||||
},
|
||||
"styleRegexpTestInvalid": {
|
||||
"message": "Invalid regexps skipped",
|
||||
"description": "RegExp test report: label for the invalid expressions"
|
||||
},
|
||||
"styleBeautify": {
|
||||
"message": "Beautify",
|
||||
"description": "Label for the CSS-beautifier button on the edit style page"
|
||||
|
|
66
edit.html
66
edit.html
|
@ -207,6 +207,14 @@
|
|||
background-color: none;
|
||||
}
|
||||
}
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.resize-grip {
|
||||
position: absolute;
|
||||
display: block;
|
||||
|
@ -261,6 +269,61 @@
|
|||
.applies-to img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.test-regexp {
|
||||
display: none;
|
||||
}
|
||||
.has-regexp .test-regexp {
|
||||
display: inline-block;
|
||||
}
|
||||
.regexp-report summary, .regexp-report div {
|
||||
cursor: pointer;
|
||||
}
|
||||
.regexp-report mark {
|
||||
background-color: rgba(255, 255, 0, .5);
|
||||
}
|
||||
.regexp-report details {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.regexp-report details:not(:last-child) {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.regexp-report summary {
|
||||
font-weight: bold;
|
||||
margin-left: -1rem;
|
||||
margin-bottom: .5rem;
|
||||
outline: none;
|
||||
cursor: default;
|
||||
}
|
||||
.regexp-report details[data-type="full"] {
|
||||
color: darkgreen;
|
||||
}
|
||||
.regexp-report details[data-type="partial"] {
|
||||
color: darkgray;
|
||||
}
|
||||
.regexp-report details[data-type="invalid"] {
|
||||
color: maroon;
|
||||
}
|
||||
.regexp-report details details {
|
||||
margin-left: 2rem;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
.regexp-report .svg-icon {
|
||||
position: absolute;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.regexp-report details div:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-skip: ink;
|
||||
}
|
||||
.regexp-report details div img {
|
||||
width: 16px;
|
||||
max-height: 16px;
|
||||
position: absolute;
|
||||
margin-left: -20px;
|
||||
margin-top: -1px;
|
||||
animation: fadein 1s cubic-bezier(.03, .67, .08, .94);
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
/************ help popup ************/
|
||||
#help-popup {
|
||||
top: 3rem;
|
||||
|
@ -285,7 +348,7 @@
|
|||
font-weight: bold;
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
margin: -0.5rem -0.5rem 0.5rem;
|
||||
padding: 0.5rem;
|
||||
padding: .5rem 32px .5rem .5rem;
|
||||
}
|
||||
#help-popup .contents {
|
||||
max-height: calc(100vh - 8rem);
|
||||
|
@ -537,6 +600,7 @@
|
|||
<button class="remove-section" i18n-text="sectionRemove"></button>
|
||||
<button class="add-section" i18n-text="sectionAdd"></button>
|
||||
<button class="beautify-section" i18n-text="styleBeautify"></button>
|
||||
<button class="test-regexp" i18n-text="styleRegexpTestButton"></button>
|
||||
</div>
|
||||
</template>
|
||||
<template data-id="find">
|
||||
|
|
118
edit.js
118
edit.js
|
@ -500,6 +500,23 @@ function addSection(event, section) {
|
|||
appliesTo.addEventListener("change", onChange);
|
||||
appliesTo.addEventListener("input", onChange);
|
||||
|
||||
toggleTestRegExpVisibility();
|
||||
appliesTo.addEventListener('change', toggleTestRegExpVisibility);
|
||||
div.querySelector('.test-regexp').onclick = showRegExpTester;
|
||||
function toggleTestRegExpVisibility() {
|
||||
const show = [...appliesTo.children].some(item =>
|
||||
!item.matches('.applies-to-everything') &&
|
||||
item.querySelector('.applies-type').value == 'regexp' &&
|
||||
item.querySelector('.applies-value').value.trim());
|
||||
div.classList.toggle('has-regexp', show);
|
||||
appliesTo.oninput = appliesTo.oninput || show && (event => {
|
||||
if (event.target.matches('.applies-value')
|
||||
&& event.target.parentElement.querySelector('.applies-type').value == 'regexp') {
|
||||
showRegExpTester(null, div);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var sections = document.getElementById("sections");
|
||||
if (event) {
|
||||
var clickedSection = getSectionForChild(event.target);
|
||||
|
@ -1605,6 +1622,107 @@ function showLintHelp() {
|
|||
);
|
||||
}
|
||||
|
||||
function showRegExpTester(event, section = getSectionForChild(this)) {
|
||||
const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain=';
|
||||
const OWN_ICON = chrome.app.getDetails().icons['16'];
|
||||
const RX_SUPPORTED_URLS = new RegExp(`^(file|https?|ftps?):|^${OWN_ORIGIN}`);
|
||||
const cachedRegexps = showRegExpTester.cachedRegexps =
|
||||
showRegExpTester.cachedRegexps || new Map();
|
||||
const regexps = [...section.querySelector('.applies-to-list').children]
|
||||
.map(item =>
|
||||
!item.matches('.applies-to-everything') &&
|
||||
item.querySelector('.applies-type').value == 'regexp' &&
|
||||
item.querySelector('.applies-value').value.trim())
|
||||
.filter(item => item)
|
||||
.map(text => {
|
||||
const rxData = Object.assign({text}, cachedRegexps.get(text));
|
||||
if (!rxData.urls) {
|
||||
cachedRegexps.set(text, Object.assign(rxData, {
|
||||
rx: tryRegExp(text),
|
||||
urls: new Map(),
|
||||
}));
|
||||
}
|
||||
return rxData;
|
||||
});
|
||||
chrome.tabs.query({}, tabs => {
|
||||
const supported = tabs.map(tab => tab.url)
|
||||
.filter(url => RX_SUPPORTED_URLS.test(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) || (url.match(rx) || [])[0];
|
||||
if (match) {
|
||||
urlsNow.set(url, match);
|
||||
}
|
||||
}
|
||||
rxData.urls = urlsNow;
|
||||
}
|
||||
}
|
||||
const moreInfoLink = '<a target="_blank" ' +
|
||||
'href="https://github.com/stylish-userstyles/' +
|
||||
'stylish/wiki/Applying-styles-to-specific-sites' +
|
||||
'#advanced-matching-with-regular-expressions">' +
|
||||
'<img src="/images/help.png"></a>';
|
||||
const stats = {
|
||||
full: {data: [], label: t('styleRegexpTestFull')},
|
||||
partial: {data: [], label: t('styleRegexpTestPartial') + moreInfoLink},
|
||||
none: {data: [], label: t('styleRegexpTestNone')},
|
||||
invalid: {data: [], label: t('styleRegexpTestInvalid')},
|
||||
};
|
||||
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(OWN_ORIGIN)
|
||||
? OWN_ICON
|
||||
: GET_FAVICON_URL + new URL(url).hostname;
|
||||
const icon = `<img src="${faviconUrl}">`;
|
||||
if (match.length == url.length) {
|
||||
full.push(`<div>${icon + url}</div>`);
|
||||
} else {
|
||||
partial.push(`<div>${icon}<mark>${match}</mark>` +
|
||||
url.substr(match.length) + '</div>');
|
||||
}
|
||||
}
|
||||
if (full.length) {
|
||||
stats.full.data.push({text, urls: full});
|
||||
}
|
||||
if (partial.length) {
|
||||
stats.partial.data.push({text, urls: partial});
|
||||
}
|
||||
}
|
||||
showHelp(t('styleRegexpTestTitle'),
|
||||
'<div class="regexp-report">' +
|
||||
Object.keys(stats).map(type => (!stats[type].data.length ? '' :
|
||||
`<details open data-type="${type}">
|
||||
<summary>${stats[type].label}</summary>` +
|
||||
stats[type].data.map(({text, urls}) => (!urls ? text :
|
||||
`<details open><summary>${text}</summary>${urls.join('')}</details>`
|
||||
)).join('<br>') +
|
||||
'</details>'
|
||||
)).join('') +
|
||||
'</div>');
|
||||
document.querySelector('.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, text) {
|
||||
var div = document.getElementById("help-popup");
|
||||
div.classList.remove("big");
|
||||
|
|
Loading…
Reference in New Issue
Block a user