Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/filterClasses.js

Issue 29375915: Issue 4878 - Start using ESLint for adblockpluscore (Closed)
Patch Set: Created Feb. 20, 2017, 10:02 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict";
19
18 /** 20 /**
19 * @fileOverview Definition of Filter class and its subclasses. 21 * @fileOverview Definition of Filter class and its subclasses.
20 */ 22 */
21 23
22 let {FilterNotifier} = require("filterNotifier"); 24 let {FilterNotifier} = require("filterNotifier");
23 let {extend} = require("coreUtils"); 25 let {extend} = require("coreUtils");
24 let {filterToRegExp} = require("common"); 26 let {filterToRegExp} = require("common");
25 27
26 /** 28 /**
27 * Abstract base class for filters 29 * Abstract base class for filters
28 * 30 *
29 * @param {String} text string representation of the filter 31 * @param {String} text string representation of the filter
30 * @constructor 32 * @constructor
31 */ 33 */
32 function Filter(text) 34 function Filter(text)
33 { 35 {
34 this.text = text; 36 this.text = text;
35 this.subscriptions = []; 37 this.subscriptions = [];
36 } 38 }
37 exports.Filter = Filter; 39 exports.Filter = Filter;
38 40
39 Filter.prototype = 41 Filter.prototype =
40 { 42 {
41 /** 43 /**
42 * String representation of the filter 44 * String representation of the filter
43 * @type String 45 * @type {String}
Sebastian Noack 2017/02/20 13:14:51 This should be "string", not "String". The former
kzar 2017/02/21 06:13:59 Done.
44 */ 46 */
45 text: null, 47 text: null,
46 48
47 /** 49 /**
48 * Filter subscriptions the filter belongs to 50 * Filter subscriptions the filter belongs to
49 * @type Subscription[] 51 * @type {Subscription[]}
50 */ 52 */
51 subscriptions: null, 53 subscriptions: null,
52 54
53 /** 55 /**
54 * Filter type as a string, e.g. "blocking". 56 * Filter type as a string, e.g. "blocking".
55 * @type String 57 * @type {String}
56 */ 58 */
57 get type() 59 get type()
58 { 60 {
59 throw new Error("Please define filter type in the subclass"); 61 throw new Error("Please define filter type in the subclass");
60 }, 62 },
61 63
62 /** 64 /**
63 * Serializes the filter to an array of strings for writing out on the disk. 65 * Serializes the filter to an array of strings for writing out on the disk.
64 * @param {string[]} buffer buffer to push the serialization results into 66 * @param {string[]} buffer buffer to push the serialization results into
65 */ 67 */
66 serialize: function(buffer) 68 serialize(buffer)
67 { 69 {
68 buffer.push("[Filter]"); 70 buffer.push("[Filter]");
69 buffer.push("text=" + this.text); 71 buffer.push("text=" + this.text);
70 }, 72 },
71 73
72 toString: function() 74 toString()
73 { 75 {
74 return this.text; 76 return this.text;
75 } 77 }
76 }; 78 };
77 79
78 /** 80 /**
79 * Cache for known filters, maps string representation to filter objects. 81 * Cache for known filters, maps string representation to filter objects.
80 * @type Object 82 * @type {Object}
81 */ 83 */
82 Filter.knownFilters = Object.create(null); 84 Filter.knownFilters = Object.create(null);
83 85
86 /* eslint-disable max-len */
Sebastian Noack 2017/02/20 13:14:51 The "max-len" rule has an "ignoreRegExpLiterals" o
kzar 2017/02/21 06:13:59 Done. https://codereview.adblockplus.org/29376671/
87
84 /** 88 /**
85 * Regular expression that element hiding filters should match 89 * Regular expression that element hiding filters should match
86 * @type RegExp 90 * @type {RegExp}
87 */ 91 */
88 Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?: [$^*]?=[^\(\)"]*)?\))*)|#(.+))$/; 92 Filter.elemhideRegExp = /^([^/*|@"!]*?)#(@)?(?:([\w-]+|\*)((?:\([\w-]+(?:[$^*]?= [^()"]*)?\))*)|#(.+))$/;
89 /** 93 /**
90 * Regular expression that RegExp filters specified as RegExps should match 94 * Regular expression that RegExp filters specified as RegExps should match
91 * @type RegExp 95 * @type {RegExp}
92 */ 96 */
93 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/; 97 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^, \s]+)?)*)?$/;
94 /** 98 /**
95 * Regular expression that options on a RegExp filter should match 99 * Regular expression that options on a RegExp filter should match
96 * @type RegExp 100 * @type {RegExp}
97 */ 101 */
98 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ; 102 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)$/;
103
104 /* eslint-enable max-len */
99 105
100 /** 106 /**
101 * Creates a filter of correct type from its text representation - does the basi c parsing and 107 * Creates a filter of correct type from its text representation -
102 * calls the right constructor then. 108 * does the basic parsing and calls the right constructor then.
103 * 109 *
104 * @param {String} text as in Filter() 110 * @param {String} text as in Filter()
105 * @return {Filter} 111 * @return {Filter}
106 */ 112 */
107 Filter.fromText = function(text) 113 Filter.fromText = function(text)
108 { 114 {
109 if (text in Filter.knownFilters) 115 if (text in Filter.knownFilters)
110 return Filter.knownFilters[text]; 116 return Filter.knownFilters[text];
111 117
112 let ret; 118 let ret;
113 let match = (text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null) ; 119 let match = (
Sebastian Noack 2017/02/20 13:14:51 Better than wrapping, just use the more concise in
kzar 2017/02/21 06:13:59 Done.
120 text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null
121 );
114 if (match) 122 if (match)
115 ret = ElemHideBase.fromText(text, match[1], !!match[2], match[3], match[4], match[5]); 123 {
124 ret = ElemHideBase.fromText(
125 text, match[1], !!match[2], match[3], match[4], match[5]
126 );
127 }
116 else if (text[0] == "!") 128 else if (text[0] == "!")
117 ret = new CommentFilter(text); 129 ret = new CommentFilter(text);
118 else 130 else
119 ret = RegExpFilter.fromText(text); 131 ret = RegExpFilter.fromText(text);
120 132
121 Filter.knownFilters[ret.text] = ret; 133 Filter.knownFilters[ret.text] = ret;
122 return ret; 134 return ret;
123 }; 135 };
124 136
125 /** 137 /**
126 * Deserializes a filter 138 * Deserializes a filter
127 * 139 *
128 * @param {Object} obj map of serialized properties and their values 140 * @param {Object} obj map of serialized properties and their values
129 * @return {Filter} filter or null if the filter couldn't be created 141 * @return {Filter} filter or null if the filter couldn't be created
130 */ 142 */
131 Filter.fromObject = function(obj) 143 Filter.fromObject = function(obj)
132 { 144 {
133 let ret = Filter.fromText(obj.text); 145 let ret = Filter.fromText(obj.text);
134 if (ret instanceof ActiveFilter) 146 if (ret instanceof ActiveFilter)
135 { 147 {
136 if ("disabled" in obj) 148 if ("disabled" in obj)
137 ret._disabled = (obj.disabled == "true"); 149 ret._disabled = (obj.disabled == "true");
138 if ("hitCount" in obj) 150 if ("hitCount" in obj)
139 ret._hitCount = parseInt(obj.hitCount) || 0; 151 ret._hitCount = parseInt(obj.hitCount, 10) || 0;
140 if ("lastHit" in obj) 152 if ("lastHit" in obj)
141 ret._lastHit = parseInt(obj.lastHit) || 0; 153 ret._lastHit = parseInt(obj.lastHit, 10) || 0;
142 } 154 }
143 return ret; 155 return ret;
144 }; 156 };
145 157
146 /** 158 /**
147 * Removes unnecessary whitespaces from filter text, will only return null if 159 * Removes unnecessary whitespaces from filter text, will only return null if
148 * the input parameter is null. 160 * the input parameter is null.
161 * @param {String} text
162 * @return {String}
149 */ 163 */
150 Filter.normalize = function(/**String*/ text) /**String*/ 164 Filter.normalize = function(text)
151 { 165 {
152 if (!text) 166 if (!text)
153 return text; 167 return text;
154 168
155 // Remove line breaks and such 169 // Remove line breaks and such
156 text = text.replace(/[^\S ]/g, ""); 170 text = text.replace(/[^\S ]/g, "");
157 171
158 if (/^\s*!/.test(text)) 172 if (/^\s*!/.test(text))
159 { 173 {
160 // Don't remove spaces inside comments 174 // Don't remove spaces inside comments
161 return text.trim(); 175 return text.trim();
162 } 176 }
163 else if (Filter.elemhideRegExp.test(text)) 177 else if (Filter.elemhideRegExp.test(text))
164 { 178 {
165 // Special treatment for element hiding filters, right side is allowed to co ntain spaces 179 // Special treatment for element hiding filters, right side is
166 let [, domain, separator, selector] = /^(.*?)(#\@?#?)(.*)$/.exec(text); 180 // allowed to contain spaces
181 let [, domain, separator, selector] = /^(.*?)(#@?#?)(.*)$/.exec(text);
167 return domain.replace(/\s/g, "") + separator + selector.trim(); 182 return domain.replace(/\s/g, "") + separator + selector.trim();
168 } 183 }
169 else 184 return text.replace(/\s/g, "");
170 return text.replace(/\s/g, "");
171 }; 185 };
172 186
173 /** 187 /**
174 * @see filterToRegExp 188 * @see filterToRegExp
175 */ 189 */
176 Filter.toRegExp = filterToRegExp; 190 Filter.toRegExp = filterToRegExp;
177 191
178 /** 192 /**
179 * Class for invalid filters 193 * Class for invalid filters
180 * @param {String} text see Filter() 194 * @param {String} text see Filter()
181 * @param {String} reason Reason why this filter is invalid 195 * @param {String} reason Reason why this filter is invalid
182 * @constructor 196 * @constructor
183 * @augments Filter 197 * @augments Filter
184 */ 198 */
185 function InvalidFilter(text, reason) 199 function InvalidFilter(text, reason)
186 { 200 {
187 Filter.call(this, text); 201 Filter.call(this, text);
188 202
189 this.reason = reason; 203 this.reason = reason;
190 } 204 }
191 exports.InvalidFilter = InvalidFilter; 205 exports.InvalidFilter = InvalidFilter;
192 206
193 InvalidFilter.prototype = extend(Filter, { 207 InvalidFilter.prototype = extend(Filter, {
194 type: "invalid", 208 type: "invalid",
195 209
196 /** 210 /**
197 * Reason why this filter is invalid 211 * Reason why this filter is invalid
198 * @type String 212 * @type {String}
199 */ 213 */
200 reason: null, 214 reason: null,
201 215
202 /** 216 /**
203 * See Filter.serialize() 217 * See Filter.serialize()
218 * @param {string[]} buffer buffer to push the serialization results into
204 */ 219 */
205 serialize: function(buffer) {} 220 serialize(buffer) {}
206 }); 221 });
207 222
208 /** 223 /**
209 * Class for comments 224 * Class for comments
210 * @param {String} text see Filter() 225 * @param {String} text see Filter()
211 * @constructor 226 * @constructor
212 * @augments Filter 227 * @augments Filter
213 */ 228 */
214 function CommentFilter(text) 229 function CommentFilter(text)
215 { 230 {
216 Filter.call(this, text); 231 Filter.call(this, text);
217 } 232 }
218 exports.CommentFilter = CommentFilter; 233 exports.CommentFilter = CommentFilter;
219 234
220 CommentFilter.prototype = extend(Filter, { 235 CommentFilter.prototype = extend(Filter, {
221 type: "comment", 236 type: "comment",
222 237
223 /** 238 /**
224 * See Filter.serialize() 239 * See Filter.serialize()
240 * @param {string[]} buffer buffer to push the serialization results into
225 */ 241 */
226 serialize: function(buffer) {} 242 serialize(buffer) {}
227 }); 243 });
228 244
229 /** 245 /**
230 * Abstract base class for filters that can get hits 246 * Abstract base class for filters that can get hits
231 * @param {String} text see Filter() 247 * @param {String} text see Filter()
232 * @param {String} [domains] Domains that the filter is restricted to separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" 248 * @param {String} [domains] Domains that the filter is restricted to
249 * separated by domainSeparator e.g. "foo.com|bar.com|~baz.com"
233 * @constructor 250 * @constructor
234 * @augments Filter 251 * @augments Filter
235 */ 252 */
236 function ActiveFilter(text, domains) 253 function ActiveFilter(text, domains)
237 { 254 {
238 Filter.call(this, text); 255 Filter.call(this, text);
239 256
240 this.domainSource = domains; 257 this.domainSource = domains;
241 } 258 }
242 exports.ActiveFilter = ActiveFilter; 259 exports.ActiveFilter = ActiveFilter;
243 260
244 ActiveFilter.prototype = extend(Filter, { 261 ActiveFilter.prototype = extend(Filter, {
245 _disabled: false, 262 _disabled: false,
246 _hitCount: 0, 263 _hitCount: 0,
247 _lastHit: 0, 264 _lastHit: 0,
248 265
249 /** 266 /**
250 * Defines whether the filter is disabled 267 * Defines whether the filter is disabled
251 * @type Boolean 268 * @type {Boolean}
Sebastian Noack 2017/02/20 13:14:51 This should be "boolean", not "Boolean". The forme
kzar 2017/02/21 06:13:58 Done.
252 */ 269 */
253 get disabled() 270 get disabled()
254 { 271 {
255 return this._disabled; 272 return this._disabled;
256 }, 273 },
257 set disabled(value) 274 set disabled(value)
258 { 275 {
259 if (value != this._disabled) 276 if (value != this._disabled)
260 { 277 {
261 let oldValue = this._disabled; 278 let oldValue = this._disabled;
262 this._disabled = value; 279 this._disabled = value;
263 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); 280 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue);
264 } 281 }
265 return this._disabled; 282 return this._disabled;
266 }, 283 },
267 284
268 /** 285 /**
269 * Number of hits on the filter since the last reset 286 * Number of hits on the filter since the last reset
270 * @type Number 287 * @type {Number}
271 */ 288 */
272 get hitCount() 289 get hitCount()
273 { 290 {
274 return this._hitCount; 291 return this._hitCount;
275 }, 292 },
276 set hitCount(value) 293 set hitCount(value)
277 { 294 {
278 if (value != this._hitCount) 295 if (value != this._hitCount)
279 { 296 {
280 let oldValue = this._hitCount; 297 let oldValue = this._hitCount;
281 this._hitCount = value; 298 this._hitCount = value;
282 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); 299 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue);
283 } 300 }
284 return this._hitCount; 301 return this._hitCount;
285 }, 302 },
286 303
287 /** 304 /**
288 * Last time the filter had a hit (in milliseconds since the beginning of the epoch) 305 * Last time the filter had a hit (in milliseconds since the
289 * @type Number 306 * beginning of the epoch)
307 * @type {Number}
290 */ 308 */
291 get lastHit() 309 get lastHit()
292 { 310 {
293 return this._lastHit; 311 return this._lastHit;
294 }, 312 },
295 set lastHit(value) 313 set lastHit(value)
296 { 314 {
297 if (value != this._lastHit) 315 if (value != this._lastHit)
298 { 316 {
299 let oldValue = this._lastHit; 317 let oldValue = this._lastHit;
300 this._lastHit = value; 318 this._lastHit = value;
301 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); 319 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue);
302 } 320 }
303 return this._lastHit; 321 return this._lastHit;
304 }, 322 },
305 323
306 /** 324 /**
307 * String that the domains property should be generated from 325 * String that the domains property should be generated from
308 * @type String 326 * @type {String}
309 */ 327 */
310 domainSource: null, 328 domainSource: null,
311 329
312 /** 330 /**
313 * Separator character used in domainSource property, must be overridden by su bclasses 331 * Separator character used in domainSource property, must be
314 * @type String 332 * overridden by subclasses
333 * @type {String}
315 */ 334 */
316 domainSeparator: null, 335 domainSeparator: null,
317 336
318 /** 337 /**
319 * Determines whether the trailing dot in domain names isn't important and 338 * Determines whether the trailing dot in domain names isn't important and
320 * should be ignored, must be overridden by subclasses. 339 * should be ignored, must be overridden by subclasses.
321 * @type Boolean 340 * @type {Boolean}
322 */ 341 */
323 ignoreTrailingDot: true, 342 ignoreTrailingDot: true,
324 343
325 /** 344 /**
326 * Determines whether domainSource is already upper-case, 345 * Determines whether domainSource is already upper-case,
327 * can be overridden by subclasses. 346 * can be overridden by subclasses.
328 * @type Boolean 347 * @type {Boolean}
329 */ 348 */
330 domainSourceIsUpperCase: false, 349 domainSourceIsUpperCase: false,
331 350
332 /** 351 /**
333 * Map containing domains that this filter should match on/not match on or nul l if the filter should match on all domains 352 * Map containing domains that this filter should match on/not match
334 * @type Object 353 * on or null if the filter should match on all domains
354 * @type {Object}
335 */ 355 */
336 get domains() 356 get domains()
337 { 357 {
338 // Despite this property being cached, the getter is called 358 // Despite this property being cached, the getter is called
339 // several times on Safari, due to WebKit bug 132872 359 // several times on Safari, due to WebKit bug 132872
340 let prop = Object.getOwnPropertyDescriptor(this, "domains"); 360 let prop = Object.getOwnPropertyDescriptor(this, "domains");
341 if (prop) 361 if (prop)
342 return prop.value; 362 return prop.value;
343 363
344 let domains = null; 364 let domains = null;
345 365
346 if (this.domainSource) 366 if (this.domainSource)
347 { 367 {
348 let source = this.domainSource; 368 let source = this.domainSource;
349 if (!this.domainSourceIsUpperCase) { 369 if (!this.domainSourceIsUpperCase)
370 {
350 // RegExpFilter already have uppercase domains 371 // RegExpFilter already have uppercase domains
351 source = source.toUpperCase(); 372 source = source.toUpperCase();
352 } 373 }
353 let list = source.split(this.domainSeparator); 374 let list = source.split(this.domainSeparator);
354 if (list.length == 1 && list[0][0] != "~") 375 if (list.length == 1 && list[0][0] != "~")
355 { 376 {
356 // Fast track for the common one-domain scenario 377 // Fast track for the common one-domain scenario
357 domains = Object.create(null); 378 domains = Object.create(null);
358 domains[""] = false; 379 domains[""] = false;
359 if (this.ignoreTrailingDot) 380 if (this.ignoreTrailingDot)
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 415
395 this.domainSource = null; 416 this.domainSource = null;
396 } 417 }
397 418
398 Object.defineProperty(this, "domains", {value: domains, enumerable: true}); 419 Object.defineProperty(this, "domains", {value: domains, enumerable: true});
399 return this.domains; 420 return this.domains;
400 }, 421 },
401 422
402 /** 423 /**
403 * Array containing public keys of websites that this filter should apply to 424 * Array containing public keys of websites that this filter should apply to
404 * @type string[] 425 * @type {string[]}
405 */ 426 */
406 sitekeys: null, 427 sitekeys: null,
407 428
408 /** 429 /**
409 * Checks whether this filter is active on a domain. 430 * Checks whether this filter is active on a domain.
410 * @param {String} docDomain domain name of the document that loads the URL 431 * @param {String} docDomain domain name of the document that loads the URL
411 * @param {String} [sitekey] public key provided by the document 432 * @param {String} [sitekey] public key provided by the document
412 * @return {Boolean} true in case of the filter being active 433 * @return {Boolean} true in case of the filter being active
413 */ 434 */
414 isActiveOnDomain: function(docDomain, sitekey) 435 isActiveOnDomain(docDomain, sitekey)
415 { 436 {
416 // Sitekeys are case-sensitive so we shouldn't convert them to upper-case to avoid false 437 // Sitekeys are case-sensitive so we shouldn't convert them to
417 // positives here. Instead we need to change the way filter options are pars ed. 438 // upper-case to avoid false positives here. Instead we need to
418 if (this.sitekeys && (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase( )) < 0)) 439 // change the way filter options are parsed.
440 if (this.sitekeys &&
441 (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0))
419 return false; 442 return false;
420 443
421 // If no domains are set the rule matches everywhere 444 // If no domains are set the rule matches everywhere
422 if (!this.domains) 445 if (!this.domains)
423 return true; 446 return true;
424 447
425 // If the document has no host name, match only if the filter isn't restrict ed to specific domains 448 // If the document has no host name, match only if the filter
449 // isn't restricted to specific domains
426 if (!docDomain) 450 if (!docDomain)
427 return this.domains[""]; 451 return this.domains[""];
428 452
429 if (this.ignoreTrailingDot) 453 if (this.ignoreTrailingDot)
430 docDomain = docDomain.replace(/\.+$/, ""); 454 docDomain = docDomain.replace(/\.+$/, "");
431 docDomain = docDomain.toUpperCase(); 455 docDomain = docDomain.toUpperCase();
432 456
457 /* eslint-disable no-constant-condition */
433 while (true) 458 while (true)
434 { 459 {
435 if (docDomain in this.domains) 460 if (docDomain in this.domains)
436 return this.domains[docDomain]; 461 return this.domains[docDomain];
437 462
438 let nextDot = docDomain.indexOf("."); 463 let nextDot = docDomain.indexOf(".");
439 if (nextDot < 0) 464 if (nextDot < 0)
440 break; 465 break;
441 docDomain = docDomain.substr(nextDot + 1); 466 docDomain = docDomain.substr(nextDot + 1);
442 } 467 }
468 /* eslint-enable no-constant-condition */
443 return this.domains[""]; 469 return this.domains[""];
444 }, 470 },
445 471
446 /** 472 /**
447 * Checks whether this filter is active only on a domain and its subdomains. 473 * Checks whether this filter is active only on a domain and its subdomains.
474 * @param {String} docDomain
475 * @return {Boolean}
448 */ 476 */
449 isActiveOnlyOnDomain: function(/**String*/ docDomain) /**Boolean*/ 477 isActiveOnlyOnDomain(docDomain)
450 { 478 {
451 if (!docDomain || !this.domains || this.domains[""]) 479 if (!docDomain || !this.domains || this.domains[""])
452 return false; 480 return false;
453 481
454 if (this.ignoreTrailingDot) 482 if (this.ignoreTrailingDot)
455 docDomain = docDomain.replace(/\.+$/, ""); 483 docDomain = docDomain.replace(/\.+$/, "");
456 docDomain = docDomain.toUpperCase(); 484 docDomain = docDomain.toUpperCase();
457 485
458 for (let domain in this.domains) 486 for (let domain in this.domains)
459 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1)) 487 {
488 if (this.domains[domain] && domain != docDomain &&
489 (domain.length <= docDomain.length ||
490 domain.indexOf("." + docDomain) !=
491 domain.length - docDomain.length - 1))
460 return false; 492 return false;
493 }
461 494
462 return true; 495 return true;
463 }, 496 },
464 497
465 /** 498 /**
466 * Checks whether this filter is generic or specific 499 * Checks whether this filter is generic or specific
500 * @return {Boolean}
467 */ 501 */
468 isGeneric: function() /**Boolean*/ 502 isGeneric()
469 { 503 {
470 return !(this.sitekeys && this.sitekeys.length) && 504 return !(this.sitekeys && this.sitekeys.length) &&
471 (!this.domains || this.domains[""]); 505 (!this.domains || this.domains[""]);
472 }, 506 },
473 507
474 /** 508 /**
475 * See Filter.serialize() 509 * See Filter.serialize()
510 * @param {string[]} buffer buffer to push the serialization results into
476 */ 511 */
477 serialize: function(buffer) 512 serialize(buffer)
478 { 513 {
479 if (this._disabled || this._hitCount || this._lastHit) 514 if (this._disabled || this._hitCount || this._lastHit)
480 { 515 {
481 Filter.prototype.serialize.call(this, buffer); 516 Filter.prototype.serialize.call(this, buffer);
482 if (this._disabled) 517 if (this._disabled)
483 buffer.push("disabled=true"); 518 buffer.push("disabled=true");
484 if (this._hitCount) 519 if (this._hitCount)
485 buffer.push("hitCount=" + this._hitCount); 520 buffer.push("hitCount=" + this._hitCount);
486 if (this._lastHit) 521 if (this._lastHit)
487 buffer.push("lastHit=" + this._lastHit); 522 buffer.push("lastHit=" + this._lastHit);
488 } 523 }
489 } 524 }
490 }); 525 });
491 526
492 /** 527 /**
493 * Abstract base class for RegExp-based filters 528 * Abstract base class for RegExp-based filters
494 * @param {String} text see Filter() 529 * @param {String} text see Filter()
495 * @param {String} regexpSource filter part that the regular expression should b e build from 530 * @param {String} regexpSource filter part that the regular expression should
496 * @param {Number} [contentType] Content types the filter applies to, combinatio n of values from RegExpFilter.typeMap 531 * be build from
497 * @param {Boolean} [matchCase] Defines whether the filter should distinguish be tween lower and upper case letters 532 * @param {Number} [contentType] Content types the filter applies to,
498 * @param {String} [domains] Domains that the filter is restricted to, e.g. "foo .com|bar.com|~baz.com" 533 * combination of values from RegExpFilter.typeMap
499 * @param {Boolean} [thirdParty] Defines whether the filter should apply to thir d-party or first-party content only 534 * @param {Boolean} [matchCase] Defines whether the filter should distinguish
500 * @param {String} [sitekeys] Public keys of websites that this filter should ap ply to 535 * between lower and upper case letters
536 * @param {String} [domains] Domains that the filter is restricted to,
537 * e.g. "foo.com|bar.com|~baz.com"
538 * @param {Boolean} [thirdParty] Defines whether the filter should apply to
539 * third-party or first-party content only
540 * @param {String} [sitekeys] Public keys of websites that this filter should
541 * apply to
501 * @constructor 542 * @constructor
502 * @augments ActiveFilter 543 * @augments ActiveFilter
503 */ 544 */
504 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party, sitekeys) 545 function RegExpFilter(text, regexpSource, contentType, matchCase, domains,
546 thirdParty, sitekeys)
505 { 547 {
506 ActiveFilter.call(this, text, domains, sitekeys); 548 ActiveFilter.call(this, text, domains, sitekeys);
507 549
508 if (contentType != null) 550 if (contentType != null)
509 this.contentType = contentType; 551 this.contentType = contentType;
510 if (matchCase) 552 if (matchCase)
511 this.matchCase = matchCase; 553 this.matchCase = matchCase;
512 if (thirdParty != null) 554 if (thirdParty != null)
513 this.thirdParty = thirdParty; 555 this.thirdParty = thirdParty;
514 if (sitekeys != null) 556 if (sitekeys != null)
515 this.sitekeySource = sitekeys; 557 this.sitekeySource = sitekeys;
516 558
517 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/") 559 if (regexpSource.length >= 2 &&
560 regexpSource[0] == "/" &&
561 regexpSource[regexpSource.length - 1] == "/")
518 { 562 {
519 // The filter is a regular expression - convert it immediately to catch synt ax errors 563 // The filter is a regular expression - convert it immediately to
520 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), thi s.matchCase ? "" : "i"); 564 // catch syntax errors
565 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2),
566 this.matchCase ? "" : "i");
521 Object.defineProperty(this, "regexp", {value: regexp}); 567 Object.defineProperty(this, "regexp", {value: regexp});
522 } 568 }
523 else 569 else
524 { 570 {
525 // No need to convert this filter to regular expression yet, do it on demand 571 // No need to convert this filter to regular expression yet, do it on demand
526 this.regexpSource = regexpSource; 572 this.regexpSource = regexpSource;
527 } 573 }
528 } 574 }
529 exports.RegExpFilter = RegExpFilter; 575 exports.RegExpFilter = RegExpFilter;
530 576
531 RegExpFilter.prototype = extend(ActiveFilter, { 577 RegExpFilter.prototype = extend(ActiveFilter, {
532 /** 578 /**
533 * @see ActiveFilter.domainSourceIsUpperCase 579 * @see ActiveFilter.domainSourceIsUpperCase
534 */ 580 */
535 domainSourceIsUpperCase: true, 581 domainSourceIsUpperCase: true,
536 582
537 /** 583 /**
538 * Number of filters contained, will always be 1 (required to optimize Matcher ). 584 * Number of filters contained, will always be 1 (required to
539 * @type Integer 585 * optimize Matcher).
586 * @type {Integer}
540 */ 587 */
541 length: 1, 588 length: 1,
542 589
543 /** 590 /**
544 * @see ActiveFilter.domainSeparator 591 * @see ActiveFilter.domainSeparator
545 */ 592 */
546 domainSeparator: "|", 593 domainSeparator: "|",
547 594
548 /** 595 /**
549 * Expression from which a regular expression should be generated - for delaye d creation of the regexp property 596 * Expression from which a regular expression should be generated -
550 * @type String 597 * for delayed creation of the regexp property
598 * @type {String}
551 */ 599 */
552 regexpSource: null, 600 regexpSource: null,
553 /** 601 /**
554 * Regular expression to be used when testing against this filter 602 * Regular expression to be used when testing against this filter
555 * @type RegExp 603 * @type {RegExp}
556 */ 604 */
557 get regexp() 605 get regexp()
558 { 606 {
559 // Despite this property being cached, the getter is called 607 // Despite this property being cached, the getter is called
560 // several times on Safari, due to WebKit bug 132872 608 // several times on Safari, due to WebKit bug 132872
561 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); 609 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
562 if (prop) 610 if (prop)
563 return prop.value; 611 return prop.value;
564 612
565 let source = Filter.toRegExp(this.regexpSource); 613 let source = Filter.toRegExp(this.regexpSource);
566 let regexp = new RegExp(source, this.matchCase ? "" : "i"); 614 let regexp = new RegExp(source, this.matchCase ? "" : "i");
567 Object.defineProperty(this, "regexp", {value: regexp}); 615 Object.defineProperty(this, "regexp", {value: regexp});
568 return regexp; 616 return regexp;
569 }, 617 },
570 /** 618 /**
571 * Content types the filter applies to, combination of values from RegExpFilte r.typeMap 619 * Content types the filter applies to, combination of values from
572 * @type Number 620 * RegExpFilter.typeMap
621 * @type {Number}
573 */ 622 */
574 contentType: 0x7FFFFFFF, 623 contentType: 0x7FFFFFFF,
575 /** 624 /**
576 * Defines whether the filter should distinguish between lower and upper case letters 625 * Defines whether the filter should distinguish between lower and
577 * @type Boolean 626 * upper case letters
627 * @type {Boolean}
578 */ 628 */
579 matchCase: false, 629 matchCase: false,
580 /** 630 /**
581 * Defines whether the filter should apply to third-party or first-party conte nt only. Can be null (apply to all content). 631 * Defines whether the filter should apply to third-party or
582 * @type Boolean 632 * first-party content only. Can be null (apply to all content).
633 * @type {Boolean}
583 */ 634 */
584 thirdParty: null, 635 thirdParty: null,
585 636
586 /** 637 /**
587 * String that the sitekey property should be generated from 638 * String that the sitekey property should be generated from
588 * @type String 639 * @type {String}
589 */ 640 */
590 sitekeySource: null, 641 sitekeySource: null,
591 642
592 /** 643 /**
593 * Array containing public keys of websites that this filter should apply to 644 * Array containing public keys of websites that this filter should apply to
594 * @type string[] 645 * @type {string[]}
595 */ 646 */
596 get sitekeys() 647 get sitekeys()
597 { 648 {
598 // Despite this property being cached, the getter is called 649 // Despite this property being cached, the getter is called
599 // several times on Safari, due to WebKit bug 132872 650 // several times on Safari, due to WebKit bug 132872
600 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys"); 651 let prop = Object.getOwnPropertyDescriptor(this, "sitekeys");
601 if (prop) 652 if (prop)
602 return prop.value; 653 return prop.value;
603 654
604 let sitekeys = null; 655 let sitekeys = null;
605 656
606 if (this.sitekeySource) 657 if (this.sitekeySource)
607 { 658 {
608 sitekeys = this.sitekeySource.split("|"); 659 sitekeys = this.sitekeySource.split("|");
609 this.sitekeySource = null; 660 this.sitekeySource = null;
610 } 661 }
611 662
612 Object.defineProperty(this, "sitekeys", {value: sitekeys, enumerable: true}) ; 663 Object.defineProperty(
664 this, "sitekeys", {value: sitekeys, enumerable: true}
665 );
613 return this.sitekeys; 666 return this.sitekeys;
614 }, 667 },
615 668
616 /** 669 /**
617 * Tests whether the URL matches this filter 670 * Tests whether the URL matches this filter
618 * @param {String} location URL to be tested 671 * @param {String} location URL to be tested
619 * @param {number} typeMask bitmask of content / request types to match 672 * @param {number} typeMask bitmask of content / request types to match
620 * @param {String} docDomain domain name of the document that loads the URL 673 * @param {String} docDomain domain name of the document that loads the URL
621 * @param {Boolean} thirdParty should be true if the URL is a third-party requ est 674 * @param {Boolean} thirdParty should be true if the URL is a third-party
675 * request
622 * @param {String} sitekey public key provided by the document 676 * @param {String} sitekey public key provided by the document
623 * @return {Boolean} true in case of a match 677 * @return {Boolean} true in case of a match
624 */ 678 */
625 matches: function(location, typeMask, docDomain, thirdParty, sitekey) 679 matches(location, typeMask, docDomain, thirdParty, sitekey)
626 { 680 {
627 if (this.contentType & typeMask && 681 if (this.contentType & typeMask &&
628 (this.thirdParty == null || this.thirdParty == thirdParty) && 682 (this.thirdParty == null || this.thirdParty == thirdParty) &&
629 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) 683 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location))
630 {
631 return true; 684 return true;
632 }
633
634 return false; 685 return false;
635 } 686 }
636 }); 687 });
637 688
638 // Required to optimize Matcher, see also RegExpFilter.prototype.length 689 // Required to optimize Matcher, see also RegExpFilter.prototype.length
639 Object.defineProperty(RegExpFilter.prototype, "0", 690 Object.defineProperty(RegExpFilter.prototype, "0", {
640 { 691 get() { return this; }
641 get: function() { return this; }
642 }); 692 });
643 693
644 /** 694 /**
645 * Creates a RegExp filter from its text representation 695 * Creates a RegExp filter from its text representation
646 * @param {String} text same as in Filter() 696 * @param {String} text same as in Filter()
697 * @return {RegExpFilter}
647 */ 698 */
648 RegExpFilter.fromText = function(text) 699 RegExpFilter.fromText = function(text)
649 { 700 {
650 let blocking = true; 701 let blocking = true;
651 let origText = text; 702 let origText = text;
652 if (text.indexOf("@@") == 0) 703 if (text.indexOf("@@") == 0)
653 { 704 {
654 blocking = false; 705 blocking = false;
655 text = text.substr(2); 706 text = text.substr(2);
656 } 707 }
(...skipping 22 matching lines...) Expand all
679 option = option.replace(/-/, "_"); 730 option = option.replace(/-/, "_");
680 if (option in RegExpFilter.typeMap) 731 if (option in RegExpFilter.typeMap)
681 { 732 {
682 if (contentType == null) 733 if (contentType == null)
683 contentType = 0; 734 contentType = 0;
684 contentType |= RegExpFilter.typeMap[option]; 735 contentType |= RegExpFilter.typeMap[option];
685 } 736 }
686 else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) 737 else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap)
687 { 738 {
688 if (contentType == null) 739 if (contentType == null)
689 contentType = RegExpFilter.prototype.contentType; 740 ({contentType} = RegExpFilter.prototype);
690 contentType &= ~RegExpFilter.typeMap[option.substr(1)]; 741 contentType &= ~RegExpFilter.typeMap[option.substr(1)];
691 } 742 }
692 else if (option == "MATCH_CASE") 743 else if (option == "MATCH_CASE")
693 matchCase = true; 744 matchCase = true;
694 else if (option == "~MATCH_CASE") 745 else if (option == "~MATCH_CASE")
695 matchCase = false; 746 matchCase = false;
696 else if (option == "DOMAIN" && typeof value != "undefined") 747 else if (option == "DOMAIN" && typeof value != "undefined")
697 domains = value; 748 domains = value;
698 else if (option == "THIRD_PARTY") 749 else if (option == "THIRD_PARTY")
699 thirdParty = true; 750 thirdParty = true;
700 else if (option == "~THIRD_PARTY") 751 else if (option == "~THIRD_PARTY")
701 thirdParty = false; 752 thirdParty = false;
702 else if (option == "COLLAPSE") 753 else if (option == "COLLAPSE")
703 collapse = true; 754 collapse = true;
704 else if (option == "~COLLAPSE") 755 else if (option == "~COLLAPSE")
705 collapse = false; 756 collapse = false;
706 else if (option == "SITEKEY" && typeof value != "undefined") 757 else if (option == "SITEKEY" && typeof value != "undefined")
707 sitekeys = value; 758 sitekeys = value;
708 else 759 else
709 return new InvalidFilter(origText, "filter_unknown_option"); 760 return new InvalidFilter(origText, "filter_unknown_option");
710 } 761 }
711 } 762 }
712 763
713 try 764 try
714 { 765 {
715 if (blocking) 766 if (blocking)
716 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 767 {
717 else 768 return new BlockingFilter(origText, text, contentType, matchCase, domains,
718 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, sitekeys); 769 thirdParty, sitekeys, collapse);
770 }
771 return new WhitelistFilter(origText, text, contentType, matchCase, domains,
772 thirdParty, sitekeys);
719 } 773 }
720 catch (e) 774 catch (e)
721 { 775 {
722 return new InvalidFilter(origText, "filter_invalid_regexp"); 776 return new InvalidFilter(origText, "filter_invalid_regexp");
723 } 777 }
724 }; 778 };
725 779
726 /** 780 /**
727 * Maps type strings like "SCRIPT" or "OBJECT" to bit masks 781 * Maps type strings like "SCRIPT" or "OBJECT" to bit masks
728 */ 782 */
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 815
762 /** 816 /**
763 * Class for blocking filters 817 * Class for blocking filters
764 * @param {String} text see Filter() 818 * @param {String} text see Filter()
765 * @param {String} regexpSource see RegExpFilter() 819 * @param {String} regexpSource see RegExpFilter()
766 * @param {Number} contentType see RegExpFilter() 820 * @param {Number} contentType see RegExpFilter()
767 * @param {Boolean} matchCase see RegExpFilter() 821 * @param {Boolean} matchCase see RegExpFilter()
768 * @param {String} domains see RegExpFilter() 822 * @param {String} domains see RegExpFilter()
769 * @param {Boolean} thirdParty see RegExpFilter() 823 * @param {Boolean} thirdParty see RegExpFilter()
770 * @param {String} sitekeys see RegExpFilter() 824 * @param {String} sitekeys see RegExpFilter()
771 * @param {Boolean} collapse defines whether the filter should collapse blocked content, can be null 825 * @param {Boolean} collapse defines whether the filter should collapse blocked
826 * content, can be null
772 * @constructor 827 * @constructor
773 * @augments RegExpFilter 828 * @augments RegExpFilter
774 */ 829 */
775 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, sitekeys, collapse) 830 function BlockingFilter(text, regexpSource, contentType, matchCase, domains,
831 thirdParty, sitekeys, collapse)
776 { 832 {
777 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys); 833 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains,
834 thirdParty, sitekeys);
778 835
779 this.collapse = collapse; 836 this.collapse = collapse;
780 } 837 }
781 exports.BlockingFilter = BlockingFilter; 838 exports.BlockingFilter = BlockingFilter;
782 839
783 BlockingFilter.prototype = extend(RegExpFilter, { 840 BlockingFilter.prototype = extend(RegExpFilter, {
784 type: "blocking", 841 type: "blocking",
785 842
786 /** 843 /**
787 * Defines whether the filter should collapse blocked content. Can be null (us e the global preference). 844 * Defines whether the filter should collapse blocked content.
788 * @type Boolean 845 * Can be null (use the global preference).
846 * @type {Boolean}
789 */ 847 */
790 collapse: null 848 collapse: null
791 }); 849 });
792 850
793 /** 851 /**
794 * Class for whitelist filters 852 * Class for whitelist filters
795 * @param {String} text see Filter() 853 * @param {String} text see Filter()
796 * @param {String} regexpSource see RegExpFilter() 854 * @param {String} regexpSource see RegExpFilter()
797 * @param {Number} contentType see RegExpFilter() 855 * @param {Number} contentType see RegExpFilter()
798 * @param {Boolean} matchCase see RegExpFilter() 856 * @param {Boolean} matchCase see RegExpFilter()
799 * @param {String} domains see RegExpFilter() 857 * @param {String} domains see RegExpFilter()
800 * @param {Boolean} thirdParty see RegExpFilter() 858 * @param {Boolean} thirdParty see RegExpFilter()
801 * @param {String} sitekeys see RegExpFilter() 859 * @param {String} sitekeys see RegExpFilter()
802 * @constructor 860 * @constructor
803 * @augments RegExpFilter 861 * @augments RegExpFilter
804 */ 862 */
805 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, sitekeys) 863 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains,
864 thirdParty, sitekeys)
806 { 865 {
807 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys); 866 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains,
867 thirdParty, sitekeys);
808 } 868 }
809 exports.WhitelistFilter = WhitelistFilter; 869 exports.WhitelistFilter = WhitelistFilter;
810 870
811 WhitelistFilter.prototype = extend(RegExpFilter, { 871 WhitelistFilter.prototype = extend(RegExpFilter, {
812 type: "whitelist" 872 type: "whitelist"
813 }); 873 });
814 874
815 /** 875 /**
816 * Base class for element hiding filters 876 * Base class for element hiding filters
817 * @param {String} text see Filter() 877 * @param {String} text see Filter()
818 * @param {String} [domains] Host names or domains the filter should be restrict ed to 878 * @param {String} [domains] Host names or domains the filter should be
819 * @param {String} selector CSS selector for the HTML elements that should be hidden 879 * restricted to
880 * @param {String} selector CSS selector for the HTML elements that should be
881 * hidden
820 * @constructor 882 * @constructor
821 * @augments ActiveFilter 883 * @augments ActiveFilter
822 */ 884 */
823 function ElemHideBase(text, domains, selector) 885 function ElemHideBase(text, domains, selector)
824 { 886 {
825 ActiveFilter.call(this, text, domains || null); 887 ActiveFilter.call(this, text, domains || null);
826 888
827 if (domains) 889 if (domains)
828 this.selectorDomain = domains.replace(/,~[^,]+/g, "").replace(/^~[^,]+,?/, " ").toLowerCase(); 890 {
891 this.selectorDomain = domains.replace(/,~[^,]+/g, "").
892 replace(/^~[^,]+,?/, "").toLowerCase();
893 }
829 894
830 // Braces are being escaped to prevent CSS rule injection. 895 // Braces are being escaped to prevent CSS rule injection.
831 this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D "); 896 this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D ");
832 } 897 }
833 exports.ElemHideBase = ElemHideBase; 898 exports.ElemHideBase = ElemHideBase;
834 899
835 ElemHideBase.prototype = extend(ActiveFilter, { 900 ElemHideBase.prototype = extend(ActiveFilter, {
836 /** 901 /**
837 * @see ActiveFilter.domainSeparator 902 * @see ActiveFilter.domainSeparator
838 */ 903 */
839 domainSeparator: ",", 904 domainSeparator: ",",
840 905
841 /** 906 /**
842 * @see ActiveFilter.ignoreTrailingDot 907 * @see ActiveFilter.ignoreTrailingDot
843 */ 908 */
844 ignoreTrailingDot: false, 909 ignoreTrailingDot: false,
845 910
846 /** 911 /**
847 * Host name or domain the filter should be restricted to (can be null for no restriction) 912 * Host name or domain the filter should be restricted to (can be null for
848 * @type String 913 * no restriction)
914 * @type {String}
849 */ 915 */
850 selectorDomain: null, 916 selectorDomain: null,
851 /** 917 /**
852 * CSS selector for the HTML elements that should be hidden 918 * CSS selector for the HTML elements that should be hidden
853 * @type String 919 * @type {String}
854 */ 920 */
855 selector: null 921 selector: null
856 }); 922 });
857 923
858 /** 924 /**
859 * Creates an element hiding filter from a pre-parsed text representation 925 * Creates an element hiding filter from a pre-parsed text representation
860 * 926 *
861 * @param {String} text same as in Filter() 927 * @param {String} text same as in Filter()
862 * @param {String} domain domain part of the text representation (can be e mpty) 928 * @param {String} domain domain part of the text representation
929 * (can be empty)
863 * @param {Boolean} isException exception rule indicator 930 * @param {Boolean} isException exception rule indicator
864 * @param {String} tagName tag name part (can be empty) 931 * @param {String} tagName tag name part (can be empty)
865 * @param {String} attrRules attribute matching rules (can be empty) 932 * @param {String} attrRules attribute matching rules (can be empty)
866 * @param {String} selector raw CSS selector (can be empty) 933 * @param {String} selector raw CSS selector (can be empty)
867 * @return {ElemHideFilter|ElemHideException|ElemHideEmulationFilter|InvalidFilt er} 934 * @return {ElemHideFilter|ElemHideException|
935 * ElemHideEmulationFilter|InvalidFilter}
868 */ 936 */
869 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector) 937 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules,
938 selector)
870 { 939 {
871 if (!selector) 940 if (!selector)
872 { 941 {
873 if (tagName == "*") 942 if (tagName == "*")
874 tagName = ""; 943 tagName = "";
875 944
876 let id = null; 945 let id = null;
877 let additional = ""; 946 let additional = "";
878 if (attrRules) 947 if (attrRules)
879 { 948 {
880 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g); 949 attrRules = attrRules.match(/\([\w-]+(?:[$^*]?=[^()"]*)?\)/g);
881 for (let rule of attrRules) 950 for (let rule of attrRules)
882 { 951 {
883 rule = rule.substr(1, rule.length - 2); 952 rule = rule.substr(1, rule.length - 2);
884 let separatorPos = rule.indexOf("="); 953 let separatorPos = rule.indexOf("=");
885 if (separatorPos > 0) 954 if (separatorPos > 0)
886 { 955 {
887 rule = rule.replace(/=/, '="') + '"'; 956 rule = rule.replace(/=/, '="') + '"';
888 additional += "[" + rule + "]"; 957 additional += "[" + rule + "]";
889 } 958 }
890 else 959 else
891 { 960 {
892 if (id) 961 if (id)
893 return new InvalidFilter(text, "filter_elemhide_duplicate_id"); 962 return new InvalidFilter(text, "filter_elemhide_duplicate_id");
894 963
895 id = rule; 964 id = rule;
896 } 965 }
897 } 966 }
898 } 967 }
899 968
900 if (id) 969 if (id)
901 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional; 970 {
971 selector = tagName + "." + id + additional + "," + tagName +
Sebastian Noack 2017/02/20 13:14:51 This can be written in one line (with <80 characte
kzar 2017/02/21 06:13:59 Done.
972 "#" + id + additional;
973 }
902 else if (tagName || additional) 974 else if (tagName || additional)
903 selector = tagName + additional; 975 selector = tagName + additional;
904 else 976 else
905 return new InvalidFilter(text, "filter_elemhide_nocriteria"); 977 return new InvalidFilter(text, "filter_elemhide_nocriteria");
906 } 978 }
907 979
908 // We don't allow ElemHide filters which have any empty domains. 980 // We don't allow ElemHide filters which have any empty domains.
909 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that 981 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that
910 // changes this must be changed too. 982 // changes this must be changed too.
911 if (domain && /(^|,)~?(,|$)/.test(domain)) 983 if (domain && /(^|,)~?(,|$)/.test(domain))
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
973 */ 1045 */
974 function ElemHideEmulationFilter(text, domains, selector) 1046 function ElemHideEmulationFilter(text, domains, selector)
975 { 1047 {
976 ElemHideBase.call(this, text, domains, selector); 1048 ElemHideBase.call(this, text, domains, selector);
977 } 1049 }
978 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; 1050 exports.ElemHideEmulationFilter = ElemHideEmulationFilter;
979 1051
980 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { 1052 ElemHideEmulationFilter.prototype = extend(ElemHideBase, {
981 type: "elemhideemulation" 1053 type: "elemhideemulation"
982 }); 1054 });
OLDNEW

Powered by Google App Engine
This is Rietveld