Add support for `nth-ancestor` operator in HTML filtering

Also opportunitisically converted some code to
ES6's `class`.
This commit is contained in:
Raymond Hill 2019-05-11 13:21:23 -04:00
parent d42d86dd12
commit 8a7e704080
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 134 additions and 109 deletions

View File

@ -42,14 +42,15 @@
} }
}; };
const PSelectorHasTextTask = function(task) { const PSelectorHasTextTask = class {
constructor(task) {
let arg0 = task[1], arg1; let arg0 = task[1], arg1;
if ( Array.isArray(task[1]) ) { if ( Array.isArray(task[1]) ) {
arg1 = arg0[1]; arg0 = arg0[0]; arg1 = arg0[1]; arg0 = arg0[0];
} }
this.needle = new RegExp(arg0, arg1); this.needle = new RegExp(arg0, arg1);
}; }
PSelectorHasTextTask.prototype.exec = function(input) { exec(input) {
const output = []; const output = [];
for ( const node of input ) { for ( const node of input ) {
if ( this.needle.test(node.textContent) ) { if ( this.needle.test(node.textContent) ) {
@ -57,18 +58,14 @@
} }
} }
return output; return output;
}
}; };
const PSelectorIfTask = function(task) { const PSelectorIfTask = class {
constructor(task) {
this.pselector = new PSelector(task[1]); this.pselector = new PSelector(task[1]);
};
PSelectorIfTask.prototype.target = true;
Object.defineProperty(PSelectorIfTask.prototype, 'invalid', {
get: function() {
return this.pselector.invalid;
} }
}); exec(input) {
PSelectorIfTask.prototype.exec = function(input) {
const output = []; const output = [];
for ( const node of input ) { for ( const node of input ) {
if ( this.pselector.test(node) === this.target ) { if ( this.pselector.test(node) === this.target ) {
@ -76,19 +73,46 @@
} }
} }
return output; return output;
}
get invalid() {
return this.pselector.invalid;
}
}; };
PSelectorIfTask.prototype.target = true;
const PSelectorIfNotTask = function(task) { const PSelectorIfNotTask = class extends PSelectorIfTask {
PSelectorIfTask.call(this, task); constructor(task) {
super.call(task);
this.target = false; this.target = false;
}
}; };
PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype);
PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask;
const PSelectorXpathTask = function(task) { const PSelectorNthAncestorTask = class {
this.xpe = task[1]; constructor(task) {
this.nth = task[1];
}
exec(input) {
const output = [];
for ( let node of input ) {
let nth = this.nth;
for (;;) {
node = node.parentElement;
if ( node === null ) { break; }
nth -= 1;
if ( nth !== 0 ) { continue; }
output.push(node);
break;
}
}
return output;
}
}; };
PSelectorXpathTask.prototype.exec = function(input) {
const PSelectorXpathTask = class {
constructor(task) {
this.xpe = task[1];
}
exec(input) {
const output = []; const output = [];
const xpe = docRegister.createExpression(this.xpe, null); const xpe = docRegister.createExpression(this.xpe, null);
let xpr = null; let xpr = null;
@ -107,19 +131,11 @@
} }
} }
return output; return output;
}
}; };
const PSelector = function(o) { const PSelector = class {
if ( PSelector.prototype.operatorToTaskMap === undefined ) { constructor(o) {
PSelector.prototype.operatorToTaskMap = new Map([
[ ':has', PSelectorIfTask ],
[ ':has-text', PSelectorHasTextTask ],
[ ':if', PSelectorIfTask ],
[ ':if-not', PSelectorIfNotTask ],
[ ':not', PSelectorIfNotTask ],
[ ':xpath', PSelectorXpathTask ]
]);
}
this.raw = o.raw; this.raw = o.raw;
this.selector = o.selector; this.selector = o.selector;
this.tasks = []; this.tasks = [];
@ -137,17 +153,15 @@
} }
this.tasks.push(pselector); this.tasks.push(pselector);
} }
}; }
PSelector.prototype.operatorToTaskMap = undefined; prime(input) {
PSelector.prototype.invalid = false;
PSelector.prototype.prime = function(input) {
const root = input || docRegister; const root = input || docRegister;
if ( this.selector !== '' ) { if ( this.selector !== '' ) {
return root.querySelectorAll(this.selector); return root.querySelectorAll(this.selector);
} }
return [ root ]; return [ root ];
}; }
PSelector.prototype.exec = function(input) { exec(input) {
if ( this.invalid ) { return []; } if ( this.invalid ) { return []; }
let nodes = this.prime(input); let nodes = this.prime(input);
for ( const task of this.tasks ) { for ( const task of this.tasks ) {
@ -155,8 +169,8 @@
nodes = task.exec(nodes); nodes = task.exec(nodes);
} }
return nodes; return nodes;
}; }
PSelector.prototype.test = function(input) { test(input) {
if ( this.invalid ) { return false; } if ( this.invalid ) { return false; }
const nodes = this.prime(input); const nodes = this.prime(input);
const AA = [ null ]; const AA = [ null ];
@ -170,7 +184,18 @@
if ( aa.length !== 0 ) { return true; } if ( aa.length !== 0 ) { return true; }
} }
return false; return false;
}
}; };
PSelector.prototype.operatorToTaskMap = new Map([
[ ':has', PSelectorIfTask ],
[ ':has-text', PSelectorHasTextTask ],
[ ':if', PSelectorIfTask ],
[ ':if-not', PSelectorIfNotTask ],
[ ':not', PSelectorIfNotTask ],
[ ':nth-ancestor', PSelectorNthAncestorTask ],
[ ':xpath', PSelectorXpathTask ]
]);
PSelector.prototype.invalid = false;
const logOne = function(details, exception, selector) { const logOne = function(details, exception, selector) {
µBlock.filteringContext µBlock.filteringContext