From c0b56cffabfc08bf7f4ab172be8b8f8b1a878044 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Apr 2024 10:28:11 -0400 Subject: [PATCH] Improve suggested candidate filters in element picker Additionally, fix eslint warnings. --- .eslintrc.yml | 1 + src/js/epicker-ui.js | 68 +++++++++---------- src/js/scriptlets/epicker.js | 125 ++++++++++++++++++----------------- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 81a7f015a..967dadf03 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -16,6 +16,7 @@ rules: - Program > ExpressionStatement > CallExpression > ArrowFunctionExpression > BlockStatement - CallExpression > MemberExpression - ArrayExpression > * + - ObjectExpression > * no-control-regex: off no-empty: off sort-imports: warn diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 0c7ea1f5a..981602b66 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -21,14 +21,13 @@ /* global CodeMirror */ -'use strict'; - import './codemirror/ubo-static-filtering.js'; +import * as sfp from './static-filtering-parser.js'; + +import { dom } from './dom.js'; import { hostnameFromURI } from './uri-utils.js'; import punycode from '../lib/punycode.js'; -import * as sfp from './static-filtering-parser.js'; -import { dom } from './dom.js'; /******************************************************************************/ /******************************************************************************/ @@ -63,13 +62,10 @@ const reCosmeticAnchor = /^#(\$|\?|\$\?)?#/; const docURL = new URL(vAPI.getURL('')); -let resultsetOpt; - -let netFilterCandidates = []; -let cosmeticFilterCandidates = []; -let computedCandidateSlot = 0; -let computedCandidate = ''; const computedSpecificityCandidates = new Map(); +let resultsetOpt; +let cosmeticFilterCandidates = []; +let computedCandidate = ''; let needBody = false; /******************************************************************************/ @@ -183,7 +179,6 @@ const candidateFromFilterChoice = function(filterChoice) { elem.classList.remove('active'); } - computedCandidateSlot = slot; computedCandidate = ''; if ( filter === undefined ) { return ''; } @@ -726,7 +721,6 @@ const svgListening = (( ) => { // current mode is narrow or broad. const populateCandidates = function(candidates, selector) { - const root = dialog.querySelector(selector); const ul = root.querySelector('ul'); while ( ul.firstChild !== null ) { @@ -751,8 +745,6 @@ const showDialog = function(details) { const { netFilters, cosmeticFilters, filter } = details; - netFilterCandidates = netFilters; - needBody = cosmeticFilters.length !== 0 && cosmeticFilters[cosmeticFilters.length - 1] === '##body'; @@ -869,31 +861,31 @@ const quitPicker = function() { const onPickerMessage = function(msg) { switch ( msg.what ) { - case 'candidatesOptimized': - onCandidatesOptimized(msg); - break; - case 'showDialog': - showDialog(msg); - break; - case 'resultsetDetails': { - resultsetOpt = msg.opt; - $id('resultsetCount').textContent = msg.count; - if ( msg.count !== 0 ) { - $id('create').removeAttribute('disabled'); - } else { - $id('create').setAttribute('disabled', ''); - } - break; + case 'candidatesOptimized': + onCandidatesOptimized(msg); + break; + case 'showDialog': + showDialog(msg); + break; + case 'resultsetDetails': { + resultsetOpt = msg.opt; + $id('resultsetCount').textContent = msg.count; + if ( msg.count !== 0 ) { + $id('create').removeAttribute('disabled'); + } else { + $id('create').setAttribute('disabled', ''); } - case 'svgPaths': { - let { ocean, islands } = msg; - ocean += islands; - svgOcean.setAttribute('d', ocean); - svgIslands.setAttribute('d', islands || NoPaths); - break; - } - default: - break; + break; + } + case 'svgPaths': { + let { ocean, islands } = msg; + ocean += islands; + svgOcean.setAttribute('d', ocean); + svgIslands.setAttribute('d', islands || NoPaths); + break; + } + default: + break; } }; diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index 41b0b76bd..e127540d1 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -19,19 +19,12 @@ Home: https://github.com/gorhill/uBlock */ -/* global CSS */ - -'use strict'; - -/******************************************************************************/ -/******************************************************************************/ - (async ( ) => { /******************************************************************************/ if ( typeof vAPI !== 'object' ) { return; } -if ( typeof vAPI === null ) { return; } +if ( vAPI === null ) { return; } if ( vAPI.pickerFrame ) { return; } vAPI.pickerFrame = true; @@ -902,6 +895,20 @@ const onOptimizeCandidates = function(details) { if ( r !== 0 ) { return r; } return a.selector.length - b.selector.length; }); + + // If two candidates have the same count of matching elements, replace + // the less specific cosmetic filters with the more specific one if the + // less specific one has an id and is simpler + for ( let i = 0, n = results.length-1; i < n; i++ ) { + const a = results[i+0]; + const b = results[i+1]; + if ( b.count !== a.count ) { continue; } + if ( b.selector.length <= a.selector.length ) { continue; } + if ( a.selector.startsWith('#') === false ) { continue; } + if ( b.selector.length < a.selector.length ) { continue; } + b.selector = a.selector; + } + pickerFramePort.postMessage({ what: 'candidatesOptimized', candidates: results.map(a => a.selector), @@ -1163,59 +1170,59 @@ vAPI.shutdown.add(quitPicker); const onDialogMessage = function(msg) { switch ( msg.what ) { - case 'start': - startPicker(); - if ( pickerFramePort === null ) { break; } - if ( targetElements.length === 0 ) { - highlightElements([], true); - } - break; - case 'optimizeCandidates': - onOptimizeCandidates(msg); - break; - case 'dialogCreate': - filterToDOMInterface.queryAll(msg); - filterToDOMInterface.preview(true, true); - quitPicker(); - break; - case 'dialogSetFilter': { - const resultset = filterToDOMInterface.queryAll(msg) || []; - highlightElements(resultset.map(a => a.elem), true); - if ( msg.filter === '!' ) { break; } - pickerFramePort.postMessage({ - what: 'resultsetDetails', - count: resultset.length, - opt: resultset.length !== 0 ? resultset[0].opt : undefined, - }); - break; + case 'start': + startPicker(); + if ( pickerFramePort === null ) { break; } + if ( targetElements.length === 0 ) { + highlightElements([], true); } - case 'quitPicker': - filterToDOMInterface.preview(false); + break; + case 'optimizeCandidates': + onOptimizeCandidates(msg); + break; + case 'dialogCreate': + filterToDOMInterface.queryAll(msg); + filterToDOMInterface.preview(true, true); + quitPicker(); + break; + case 'dialogSetFilter': { + const resultset = filterToDOMInterface.queryAll(msg) || []; + highlightElements(resultset.map(a => a.elem), true); + if ( msg.filter === '!' ) { break; } + pickerFramePort.postMessage({ + what: 'resultsetDetails', + count: resultset.length, + opt: resultset.length !== 0 ? resultset[0].opt : undefined, + }); + break; + } + case 'quitPicker': + filterToDOMInterface.preview(false); + quitPicker(); + break; + case 'highlightElementAtPoint': + highlightElementAtPoint(msg.mx, msg.my); + break; + case 'unhighlight': + highlightElements([]); + break; + case 'filterElementAtPoint': + filterElementAtPoint(msg.mx, msg.my, msg.broad); + break; + case 'zapElementAtPoint': + zapElementAtPoint(msg.mx, msg.my, msg.options); + if ( msg.options.highlight !== true && msg.options.stay !== true ) { quitPicker(); - break; - case 'highlightElementAtPoint': - highlightElementAtPoint(msg.mx, msg.my); - break; - case 'unhighlight': - highlightElements([]); - break; - case 'filterElementAtPoint': - filterElementAtPoint(msg.mx, msg.my, msg.broad); - break; - case 'zapElementAtPoint': - zapElementAtPoint(msg.mx, msg.my, msg.options); - if ( msg.options.highlight !== true && msg.options.stay !== true ) { - quitPicker(); - } - break; - case 'togglePreview': - filterToDOMInterface.preview(msg.state); - if ( msg.state === false ) { - highlightElements(targetElements, true); - } - break; - default: - break; + } + break; + case 'togglePreview': + filterToDOMInterface.preview(msg.state); + if ( msg.state === false ) { + highlightElements(targetElements, true); + } + break; + default: + break; } };