1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-07 03:12:33 +01:00

Add widget to control selector depth to element picker

Further iterating on the work done in following commit:
- 1268f0ae43

This commit adds a new widget to the element picker to
control the depth of a cosmetic filter selector. The
new widget is essentially just another way of selecting
the depth, which is still controllable through picking
one of the cosmetic filters in the list of candidates.
This commit is contained in:
Raymond Hill 2020-09-10 10:32:53 -04:00
parent 6f7801d433
commit 260f762c83
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
4 changed files with 118 additions and 104 deletions

View File

@ -94,7 +94,7 @@ html#ublock0-epicker,
margin: 0;
overflow: hidden;
overflow-y: auto;
padding: 2px;
padding: 2px 2px 1em 2px;
resize: none;
width: 100%;
word-break: break-all;
@ -112,63 +112,35 @@ html#ublock0-epicker,
align-items: flex-end;
display: inline-flex;
flex-grow: 1;
justify-content: center;
justify-content: space-evenly;
}
#resultsetSpecificity {
display: inline-flex;
pointer-events: auto;
position: relative;
}
#resultsetSpecificity.hide {
#resultsetModifiers.hide > * {
display: none;
}
#resultsetSpecificity [data-specificity] {
.resultsetModifier {
border: 1px solid white;
border-bottom: 0;
display: inline-flex;
height: 100%;
pointer-events: auto;
position: relative;
width: 32%;
}
.resultsetModifier span {
background-color: var(--button-surface);
border: 0;
border-left: 1px solid white;
display: inline-block;
height: 1.2em;
width: 1.5em;
height: 100%;
width: 12.5%;
}
#resultsetSpecificity[data-specificity="0"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="1"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="1"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="2"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="2"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="2"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="3"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="3"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="3"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="3"] [data-specificity="3"],
#resultsetSpecificity[data-specificity="4"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="4"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="4"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="4"] [data-specificity="3"],
#resultsetSpecificity[data-specificity="4"] [data-specificity="4"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="3"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="4"],
#resultsetSpecificity[data-specificity="5"] [data-specificity="5"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="3"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="4"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="5"],
#resultsetSpecificity[data-specificity="6"] [data-specificity="6"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="0"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="1"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="2"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="3"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="4"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="5"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="6"],
#resultsetSpecificity[data-specificity="7"] [data-specificity="7"] {
.resultsetModifier span:first-of-type {
border-left: 0;
}
.resultsetModifier span.active {
background-color: var(--button-active-surface);
}
#resultsetSpecificity input {
.resultsetModifier input {
box-sizing: border-box;
height: 100%;
left: 0;
@ -183,7 +155,6 @@ html#ublock0-epicker,
background-color: #aaa;
color: white;
min-width: 2.2em;
padding: 2px 0;
text-align: center;
}
#ublock0-epicker section.invalidFilter #resultsetCount {
@ -228,7 +199,7 @@ html#ublock0-epicker,
white-space: nowrap;
}
#ublock0-epicker #candidateFilters .changeFilter li.active {
border: 1px dotted gray;
border: 1px dotted var(--blue-50);
}
#ublock0-epicker #candidateFilters .changeFilter li:hover {
background-color: white;

View File

@ -62,6 +62,7 @@ let netFilterCandidates = [];
let cosmeticFilterCandidates = [];
let computedCandidateSlot = 0;
let computedCandidate = '';
let needBody = false;
/******************************************************************************/
@ -77,6 +78,26 @@ const filterFromTextarea = function() {
/******************************************************************************/
const renderRange = function(id, value, invert = false) {
const cells = $storAll(`#${id} span`);
const input = $stor(`#${id} input`);
const max = parseInt(input.max, 10);
if ( typeof value !== 'number' ) {
value = parseInt(input.value, 10);
}
if ( invert ) {
value = max - value;
}
input.value = value;
for ( let i = 0, n = cells.length; i < n; i++ ) {
cells[i].classList.toggle(
'active', Math.round(i * max / (n - 1)) <= value
);
}
};
/******************************************************************************/
const userFilterFromCandidate = function(filter) {
if ( filter === '' || filter === '!' ) { return; }
@ -131,24 +152,8 @@ const candidateFromFilterChoice = function(filterChoice) {
$stor(`#cosmeticFilters li:nth-of-type(${slot+1})`)
.classList.add('active');
// Modifier means "target broadly". Hence:
// - Do not compute exact path.
// - Discard narrowing directives.
// - Remove the id if one or more classes exist
// TODO: should remove tag name too? ¯\_(ツ)_/¯
if ( filterChoice.broad ) {
filter = filter.replace(/:nth-of-type\(\d+\)/, '');
// https://github.com/uBlockOrigin/uBlock-issues/issues/162
// Mind escaped periods: they do not denote a class identifier.
if ( filter.charAt(2) === '#' ) {
const pos = filter.search(/[^\\]\./);
if ( pos !== -1 ) {
filter = '##' + filter.slice(pos + 1);
}
}
return filter;
}
renderRange('resultsetDepth', slot, true);
renderRange('resultsetSpecificity');
const specificity = [
0b0000, // remove hierarchy; remove id, nth-of-type, attribute values
@ -159,12 +164,7 @@ const candidateFromFilterChoice = function(filterChoice) {
0b1100, // remove id, nth-of-type, attribute values
0b1110, // remove id, nth-of-type
0b1111, // keep all = most specific
][
parseInt(
$id('resultsetSpecificity').getAttribute('data-specificity'),
10
)
];
][ parseInt($stor('#resultsetSpecificity input').value, 10) ];
// Return path: the target element, then all siblings prepended
const paths = [];
@ -222,6 +222,15 @@ const candidateFromFilterChoice = function(filterChoice) {
}
}
if (
needBody &&
paths.length !== 0 &&
paths[0].startsWith('#') === false &&
(specificity & 0b1100) !== 0
) {
paths.unshift('body > ');
}
computedCandidate = `##${paths.join('')}`;
return computedCandidate;
@ -359,7 +368,7 @@ const onCandidateChanged = function() {
$id('resultsetCount').textContent = 'E';
$id('create').setAttribute('disabled', '');
}
$id('resultsetSpecificity').classList.toggle(
$id('resultsetModifiers').classList.toggle(
'hide',
taCandidate.value === '' || taCandidate.value !== computedCandidate
);
@ -420,16 +429,26 @@ const onQuitClicked = function() {
/******************************************************************************/
const onSpecificityChanged = function(ev) {
const { target } = ev;
$id('resultsetSpecificity').setAttribute('data-specificity', target.value);
if ( taCandidate.value === computedCandidate ) {
const onDepthChanged = function() {
const input = $stor('#resultsetDepth input');
const max = parseInt(input.max, 10);
const value = parseInt(input.value, 10);
taCandidate.value = candidateFromFilterChoice({
filters: cosmeticFilterCandidates,
slot: max - value,
});
onCandidateChanged();
};
/******************************************************************************/
const onSpecificityChanged = function() {
if ( taCandidate.value !== computedCandidate ) { return; }
taCandidate.value = candidateFromFilterChoice({
filters: cosmeticFilterCandidates,
slot: computedCandidateSlot,
});
onCandidateChanged();
}
};
/******************************************************************************/
@ -473,8 +492,8 @@ const onStartMoving = (( ) => {
const move = ( ) => {
timer = undefined;
let r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax);
let b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax);
const r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax);
const b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax);
dialog.style.setProperty('right', `${r1}px`, 'important');
dialog.style.setProperty('bottom', `${b1}px`, 'important');
};
@ -612,6 +631,13 @@ const showDialog = function(details) {
const { netFilters, cosmeticFilters, filter } = details;
netFilterCandidates = netFilters;
needBody =
cosmeticFilters.length !== 0 &&
cosmeticFilters[cosmeticFilters.length - 1] === '##body';
if ( needBody ) {
cosmeticFilters.pop();
}
cosmeticFilterCandidates = cosmeticFilters;
// https://github.com/gorhill/uBlock/issues/738
@ -625,6 +651,11 @@ const showDialog = function(details) {
populateCandidates(netFilters, '#netFilters');
populateCandidates(cosmeticFilters, '#cosmeticFilters');
const depthInput = $stor('#resultsetDepth input');
depthInput.max = cosmeticFilters.length - 1;
depthInput.value = depthInput.max;
onDepthChanged();
dialog.querySelector('ul').style.display =
netFilters.length || cosmeticFilters.length ? '' : 'none';
dialog.querySelector('#create').disabled = true;
@ -682,6 +713,8 @@ const startPicker = function() {
if ( pickerRoot.classList.contains('zap') ) { return; }
onSpecificityChanged();
taCandidate.addEventListener('input', onCandidateChanged);
$id('preview').addEventListener('click', onPreviewClicked);
$id('create').addEventListener('click', onCreateClicked);
@ -690,6 +723,7 @@ const startPicker = function() {
$id('toolbar').addEventListener('mousedown', onStartMoving);
$id('toolbar').addEventListener('touchstart', onStartMoving);
$id('candidateFilters').addEventListener('click', onCandidateClicked);
$stor('#resultsetDepth input').addEventListener('input', onDepthChanged);
$stor('#resultsetSpecificity input').addEventListener('input', onSpecificityChanged);
staticFilteringParser = new vAPI.StaticFilteringParser({ interactive: true });
};

View File

@ -529,12 +529,8 @@ const filtersFrom = function(x, y) {
// uses `nth-of-type`.
let i = cosmeticFilterCandidates.length;
if ( i !== 0 ) {
const selector = cosmeticFilterCandidates[i-1];
if (
selector.indexOf(':nth-of-type(') !== -1 &&
safeQuerySelectorAll(document.body, selector).length > 1 ||
safeQuerySelectorAll(document, cosmeticFilterCandidates.join(' > ')).length > 1
) {
const selector = cosmeticFilterCandidates[i-1].slice(2);
if ( safeQuerySelectorAll(document.body, selector).length > 1 ) {
cosmeticFilterCandidates.push('##body');
}
}

View File

@ -15,18 +15,31 @@
<textarea lang="en" dir="ltr" spellcheck="false"></textarea>
<div>
<span id="resultsetModifiers">
<span id="resultsetSpecificity" data-specificity="6">
<span data-specificity="0"></span>
<span data-specificity="1"></span>
<span data-specificity="2"></span>
<span data-specificity="3"></span>
<span data-specificity="4"></span>
<span data-specificity="5"></span>
<span data-specificity="6"></span>
<span data-specificity="7"></span>
<input type="range" min="0" max="7" value="6"></span>
<span id="resultsetDepth" class="resultsetModifier">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<input type="range" min="0" max="7" value="7">
</span>
<span id="resultsetCount"></span></div>
<span id="resultsetSpecificity" class="resultsetModifier">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<input type="range" min="0" max="7" value="6">
</span>
</span>
<span id="resultsetCount"></span>
</div>
</div>
<div id="toolbar">
<div>