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

Delta Between Two Patch Sets: lib/filterClasses.js

Issue 29375915: Issue 4878 - Start using ESLint for adblockpluscore (Closed)
Left Patch Set: Created Feb. 20, 2017, 10:02 a.m.
Right Patch Set: Removed unused imports Created March 15, 2017, 3:11 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « lib/events.js ('k') | lib/filterListener.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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"; 18 "use strict";
19 19
20 /** 20 /**
21 * @fileOverview Definition of Filter class and its subclasses. 21 * @fileOverview Definition of Filter class and its subclasses.
22 */ 22 */
23 23
24 let {FilterNotifier} = require("filterNotifier"); 24 const {FilterNotifier} = require("filterNotifier");
25 let {extend} = require("coreUtils"); 25 const {extend} = require("coreUtils");
26 let {filterToRegExp} = require("common"); 26 const {filterToRegExp} = require("common");
27 27
28 /** 28 /**
29 * Abstract base class for filters 29 * Abstract base class for filters
30 * 30 *
31 * @param {String} text string representation of the filter 31 * @param {string} text string representation of the filter
32 * @constructor 32 * @constructor
33 */ 33 */
34 function Filter(text) 34 function Filter(text)
35 { 35 {
36 this.text = text; 36 this.text = text;
37 this.subscriptions = []; 37 this.subscriptions = [];
38 } 38 }
39 exports.Filter = Filter; 39 exports.Filter = Filter;
40 40
41 Filter.prototype = 41 Filter.prototype =
42 { 42 {
43 /** 43 /**
44 * String representation of the filter 44 * String representation of the filter
45 * @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.
46 */ 46 */
47 text: null, 47 text: null,
48 48
49 /** 49 /**
50 * Filter subscriptions the filter belongs to 50 * Filter subscriptions the filter belongs to
51 * @type {Subscription[]} 51 * @type {Subscription[]}
52 */ 52 */
53 subscriptions: null, 53 subscriptions: null,
54 54
55 /** 55 /**
56 * Filter type as a string, e.g. "blocking". 56 * Filter type as a string, e.g. "blocking".
57 * @type {String} 57 * @type {string}
58 */ 58 */
59 get type() 59 get type()
60 { 60 {
61 throw new Error("Please define filter type in the subclass"); 61 throw new Error("Please define filter type in the subclass");
62 }, 62 },
63 63
64 /** 64 /**
65 * 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.
66 * @param {string[]} buffer buffer to push the serialization results into 66 * @param {string[]} buffer buffer to push the serialization results into
67 */ 67 */
68 serialize(buffer) 68 serialize(buffer)
69 { 69 {
70 buffer.push("[Filter]"); 70 buffer.push("[Filter]");
71 buffer.push("text=" + this.text); 71 buffer.push("text=" + this.text);
72 }, 72 },
73 73
74 toString() 74 toString()
75 { 75 {
76 return this.text; 76 return this.text;
77 } 77 }
78 }; 78 };
79 79
80 /** 80 /**
81 * Cache for known filters, maps string representation to filter objects. 81 * Cache for known filters, maps string representation to filter objects.
82 * @type {Object} 82 * @type {Object}
83 */ 83 */
84 Filter.knownFilters = Object.create(null); 84 Filter.knownFilters = Object.create(null);
85 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
88 /** 86 /**
89 * Regular expression that element hiding filters should match 87 * Regular expression that element hiding filters should match
90 * @type {RegExp} 88 * @type {RegExp}
91 */ 89 */
92 Filter.elemhideRegExp = /^([^/*|@"!]*?)#(@)?(?:([\w-]+|\*)((?:\([\w-]+(?:[$^*]?= [^()"]*)?\))*)|#(.+))$/; 90 Filter.elemhideRegExp = /^([^/*|@"!]*?)#(@)?(?:([\w-]+|\*)((?:\([\w-]+(?:[$^*]?= [^()"]*)?\))*)|#(.+))$/;
93 /** 91 /**
94 * Regular expression that RegExp filters specified as RegExps should match 92 * Regular expression that RegExp filters specified as RegExps should match
95 * @type {RegExp} 93 * @type {RegExp}
96 */ 94 */
97 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^, \s]+)?)*)?$/; 95 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^, \s]+)?)*)?$/;
98 /** 96 /**
99 * Regular expression that options on a RegExp filter should match 97 * Regular expression that options on a RegExp filter should match
100 * @type {RegExp} 98 * @type {RegExp}
101 */ 99 */
102 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)$/; 100 Filter.optionsRegExp = /\$(~?[\w-]+(?:=[^,\s]+)?(?:,~?[\w-]+(?:=[^,\s]+)?)*)$/;
103 101
104 /* eslint-enable max-len */ 102 /**
105 103 * Creates a filter of correct type from its text representation - does the
106 /** 104 * basic parsing and calls the right constructor then.
107 * Creates a filter of correct type from its text representation -
108 * does the basic parsing and calls the right constructor then.
109 * 105 *
110 * @param {String} text as in Filter() 106 * @param {string} text as in Filter()
111 * @return {Filter} 107 * @return {Filter}
112 */ 108 */
113 Filter.fromText = function(text) 109 Filter.fromText = function(text)
114 { 110 {
115 if (text in Filter.knownFilters) 111 if (text in Filter.knownFilters)
116 return Filter.knownFilters[text]; 112 return Filter.knownFilters[text];
117 113
118 let ret; 114 let ret;
119 let match = ( 115 let match = (text.includes("#") ? Filter.elemhideRegExp.exec(text) : null);
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 );
122 if (match) 116 if (match)
123 { 117 {
124 ret = ElemHideBase.fromText( 118 ret = ElemHideBase.fromText(
125 text, match[1], !!match[2], match[3], match[4], match[5] 119 text, match[1], !!match[2], match[3], match[4], match[5]
126 ); 120 );
127 } 121 }
128 else if (text[0] == "!") 122 else if (text[0] == "!")
129 ret = new CommentFilter(text); 123 ret = new CommentFilter(text);
130 else 124 else
131 ret = RegExpFilter.fromText(text); 125 ret = RegExpFilter.fromText(text);
(...skipping 19 matching lines...) Expand all
151 ret._hitCount = parseInt(obj.hitCount, 10) || 0; 145 ret._hitCount = parseInt(obj.hitCount, 10) || 0;
152 if ("lastHit" in obj) 146 if ("lastHit" in obj)
153 ret._lastHit = parseInt(obj.lastHit, 10) || 0; 147 ret._lastHit = parseInt(obj.lastHit, 10) || 0;
154 } 148 }
155 return ret; 149 return ret;
156 }; 150 };
157 151
158 /** 152 /**
159 * Removes unnecessary whitespaces from filter text, will only return null if 153 * Removes unnecessary whitespaces from filter text, will only return null if
160 * the input parameter is null. 154 * the input parameter is null.
161 * @param {String} text 155 * @param {string} text
162 * @return {String} 156 * @return {string}
163 */ 157 */
164 Filter.normalize = function(text) 158 Filter.normalize = function(text)
165 { 159 {
166 if (!text) 160 if (!text)
167 return text; 161 return text;
168 162
169 // Remove line breaks and such 163 // Remove line breaks and such
170 text = text.replace(/[^\S ]/g, ""); 164 text = text.replace(/[^\S ]/g, "");
171 165
172 if (/^\s*!/.test(text)) 166 if (/^\s*!/.test(text))
173 { 167 {
174 // Don't remove spaces inside comments 168 // Don't remove spaces inside comments
175 return text.trim(); 169 return text.trim();
176 } 170 }
177 else if (Filter.elemhideRegExp.test(text)) 171 else if (Filter.elemhideRegExp.test(text))
178 { 172 {
179 // Special treatment for element hiding filters, right side is 173 // Special treatment for element hiding filters, right side is allowed to
180 // allowed to contain spaces 174 // contain spaces
181 let [, domain, separator, selector] = /^(.*?)(#@?#?)(.*)$/.exec(text); 175 let [, domain, separator, selector] = /^(.*?)(#@?#?)(.*)$/.exec(text);
182 return domain.replace(/\s/g, "") + separator + selector.trim(); 176 return domain.replace(/\s/g, "") + separator + selector.trim();
183 } 177 }
184 return text.replace(/\s/g, ""); 178 return text.replace(/\s/g, "");
185 }; 179 };
186 180
187 /** 181 /**
188 * @see filterToRegExp 182 * @see filterToRegExp
189 */ 183 */
190 Filter.toRegExp = filterToRegExp; 184 Filter.toRegExp = filterToRegExp;
191 185
192 /** 186 /**
193 * Class for invalid filters 187 * Class for invalid filters
194 * @param {String} text see Filter() 188 * @param {string} text see Filter()
195 * @param {String} reason Reason why this filter is invalid 189 * @param {string} reason Reason why this filter is invalid
196 * @constructor 190 * @constructor
197 * @augments Filter 191 * @augments Filter
198 */ 192 */
199 function InvalidFilter(text, reason) 193 function InvalidFilter(text, reason)
200 { 194 {
201 Filter.call(this, text); 195 Filter.call(this, text);
202 196
203 this.reason = reason; 197 this.reason = reason;
204 } 198 }
205 exports.InvalidFilter = InvalidFilter; 199 exports.InvalidFilter = InvalidFilter;
206 200
207 InvalidFilter.prototype = extend(Filter, { 201 InvalidFilter.prototype = extend(Filter, {
208 type: "invalid", 202 type: "invalid",
209 203
210 /** 204 /**
211 * Reason why this filter is invalid 205 * Reason why this filter is invalid
212 * @type {String} 206 * @type {string}
213 */ 207 */
214 reason: null, 208 reason: null,
215 209
216 /** 210 /**
217 * See Filter.serialize() 211 * See Filter.serialize()
218 * @param {string[]} buffer buffer to push the serialization results into 212 * @inheritdoc
219 */ 213 */
220 serialize(buffer) {} 214 serialize(buffer) {}
221 }); 215 });
222 216
223 /** 217 /**
224 * Class for comments 218 * Class for comments
225 * @param {String} text see Filter() 219 * @param {string} text see Filter()
226 * @constructor 220 * @constructor
227 * @augments Filter 221 * @augments Filter
228 */ 222 */
229 function CommentFilter(text) 223 function CommentFilter(text)
230 { 224 {
231 Filter.call(this, text); 225 Filter.call(this, text);
232 } 226 }
233 exports.CommentFilter = CommentFilter; 227 exports.CommentFilter = CommentFilter;
234 228
235 CommentFilter.prototype = extend(Filter, { 229 CommentFilter.prototype = extend(Filter, {
236 type: "comment", 230 type: "comment",
237 231
238 /** 232 /**
239 * See Filter.serialize() 233 * See Filter.serialize()
240 * @param {string[]} buffer buffer to push the serialization results into 234 * @inheritdoc
241 */ 235 */
242 serialize(buffer) {} 236 serialize(buffer) {}
243 }); 237 });
244 238
245 /** 239 /**
246 * Abstract base class for filters that can get hits 240 * Abstract base class for filters that can get hits
247 * @param {String} text see Filter() 241 * @param {string} text
248 * @param {String} [domains] Domains that the filter is restricted to 242 * see Filter()
249 * separated by domainSeparator e.g. "foo.com|bar.com|~baz.com" 243 * @param {string} [domains]
244 * Domains that the filter is restricted to separated by domainSeparator
245 * e.g. "foo.com|bar.com|~baz.com"
250 * @constructor 246 * @constructor
251 * @augments Filter 247 * @augments Filter
252 */ 248 */
253 function ActiveFilter(text, domains) 249 function ActiveFilter(text, domains)
254 { 250 {
255 Filter.call(this, text); 251 Filter.call(this, text);
256 252
257 this.domainSource = domains; 253 this.domainSource = domains;
258 } 254 }
259 exports.ActiveFilter = ActiveFilter; 255 exports.ActiveFilter = ActiveFilter;
260 256
261 ActiveFilter.prototype = extend(Filter, { 257 ActiveFilter.prototype = extend(Filter, {
262 _disabled: false, 258 _disabled: false,
263 _hitCount: 0, 259 _hitCount: 0,
264 _lastHit: 0, 260 _lastHit: 0,
265 261
266 /** 262 /**
267 * Defines whether the filter is disabled 263 * Defines whether the filter is disabled
268 * @type {Boolean} 264 * @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.
269 */ 265 */
270 get disabled() 266 get disabled()
271 { 267 {
272 return this._disabled; 268 return this._disabled;
273 }, 269 },
274 set disabled(value) 270 set disabled(value)
275 { 271 {
276 if (value != this._disabled) 272 if (value != this._disabled)
277 { 273 {
278 let oldValue = this._disabled; 274 let oldValue = this._disabled;
279 this._disabled = value; 275 this._disabled = value;
280 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue); 276 FilterNotifier.triggerListeners("filter.disabled", this, value, oldValue);
281 } 277 }
282 return this._disabled; 278 return this._disabled;
283 }, 279 },
284 280
285 /** 281 /**
286 * Number of hits on the filter since the last reset 282 * Number of hits on the filter since the last reset
287 * @type {Number} 283 * @type {number}
288 */ 284 */
289 get hitCount() 285 get hitCount()
290 { 286 {
291 return this._hitCount; 287 return this._hitCount;
292 }, 288 },
293 set hitCount(value) 289 set hitCount(value)
294 { 290 {
295 if (value != this._hitCount) 291 if (value != this._hitCount)
296 { 292 {
297 let oldValue = this._hitCount; 293 let oldValue = this._hitCount;
298 this._hitCount = value; 294 this._hitCount = value;
299 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue); 295 FilterNotifier.triggerListeners("filter.hitCount", this, value, oldValue);
300 } 296 }
301 return this._hitCount; 297 return this._hitCount;
302 }, 298 },
303 299
304 /** 300 /**
305 * Last time the filter had a hit (in milliseconds since the 301 * Last time the filter had a hit (in milliseconds since the beginning of the
306 * beginning of the epoch) 302 * epoch)
307 * @type {Number} 303 * @type {number}
308 */ 304 */
309 get lastHit() 305 get lastHit()
310 { 306 {
311 return this._lastHit; 307 return this._lastHit;
312 }, 308 },
313 set lastHit(value) 309 set lastHit(value)
314 { 310 {
315 if (value != this._lastHit) 311 if (value != this._lastHit)
316 { 312 {
317 let oldValue = this._lastHit; 313 let oldValue = this._lastHit;
318 this._lastHit = value; 314 this._lastHit = value;
319 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue); 315 FilterNotifier.triggerListeners("filter.lastHit", this, value, oldValue);
320 } 316 }
321 return this._lastHit; 317 return this._lastHit;
322 }, 318 },
323 319
324 /** 320 /**
325 * String that the domains property should be generated from 321 * String that the domains property should be generated from
326 * @type {String} 322 * @type {string}
327 */ 323 */
328 domainSource: null, 324 domainSource: null,
329 325
330 /** 326 /**
331 * Separator character used in domainSource property, must be 327 * Separator character used in domainSource property, must be
332 * overridden by subclasses 328 * overridden by subclasses
333 * @type {String} 329 * @type {string}
334 */ 330 */
335 domainSeparator: null, 331 domainSeparator: null,
336 332
337 /** 333 /**
338 * Determines whether the trailing dot in domain names isn't important and 334 * Determines whether the trailing dot in domain names isn't important and
339 * should be ignored, must be overridden by subclasses. 335 * should be ignored, must be overridden by subclasses.
340 * @type {Boolean} 336 * @type {boolean}
341 */ 337 */
342 ignoreTrailingDot: true, 338 ignoreTrailingDot: true,
343 339
344 /** 340 /**
345 * Determines whether domainSource is already upper-case, 341 * Determines whether domainSource is already upper-case,
346 * can be overridden by subclasses. 342 * can be overridden by subclasses.
347 * @type {Boolean} 343 * @type {boolean}
348 */ 344 */
349 domainSourceIsUpperCase: false, 345 domainSourceIsUpperCase: false,
350 346
351 /** 347 /**
352 * Map containing domains that this filter should match on/not match 348 * Map containing domains that this filter should match on/not match
353 * on or null if the filter should match on all domains 349 * on or null if the filter should match on all domains
354 * @type {Object} 350 * @type {Object}
355 */ 351 */
356 get domains() 352 get domains()
357 { 353 {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 }, 417 },
422 418
423 /** 419 /**
424 * Array containing public keys of websites that this filter should apply to 420 * Array containing public keys of websites that this filter should apply to
425 * @type {string[]} 421 * @type {string[]}
426 */ 422 */
427 sitekeys: null, 423 sitekeys: null,
428 424
429 /** 425 /**
430 * Checks whether this filter is active on a domain. 426 * Checks whether this filter is active on a domain.
431 * @param {String} docDomain domain name of the document that loads the URL 427 * @param {string} docDomain domain name of the document that loads the URL
432 * @param {String} [sitekey] public key provided by the document 428 * @param {string} [sitekey] public key provided by the document
433 * @return {Boolean} true in case of the filter being active 429 * @return {boolean} true in case of the filter being active
434 */ 430 */
435 isActiveOnDomain(docDomain, sitekey) 431 isActiveOnDomain(docDomain, sitekey)
436 { 432 {
437 // Sitekeys are case-sensitive so we shouldn't convert them to 433 // Sitekeys are case-sensitive so we shouldn't convert them to
438 // upper-case to avoid false positives here. Instead we need to 434 // upper-case to avoid false positives here. Instead we need to
439 // change the way filter options are parsed. 435 // change the way filter options are parsed.
440 if (this.sitekeys && 436 if (this.sitekeys &&
441 (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0)) 437 (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase()) < 0))
438 {
442 return false; 439 return false;
440 }
443 441
444 // If no domains are set the rule matches everywhere 442 // If no domains are set the rule matches everywhere
445 if (!this.domains) 443 if (!this.domains)
446 return true; 444 return true;
447 445
448 // If the document has no host name, match only if the filter 446 // If the document has no host name, match only if the filter
449 // isn't restricted to specific domains 447 // isn't restricted to specific domains
450 if (!docDomain) 448 if (!docDomain)
451 return this.domains[""]; 449 return this.domains[""];
452 450
453 if (this.ignoreTrailingDot) 451 if (this.ignoreTrailingDot)
454 docDomain = docDomain.replace(/\.+$/, ""); 452 docDomain = docDomain.replace(/\.+$/, "");
455 docDomain = docDomain.toUpperCase(); 453 docDomain = docDomain.toUpperCase();
456 454
457 /* eslint-disable no-constant-condition */
458 while (true) 455 while (true)
459 { 456 {
460 if (docDomain in this.domains) 457 if (docDomain in this.domains)
461 return this.domains[docDomain]; 458 return this.domains[docDomain];
462 459
463 let nextDot = docDomain.indexOf("."); 460 let nextDot = docDomain.indexOf(".");
464 if (nextDot < 0) 461 if (nextDot < 0)
465 break; 462 break;
466 docDomain = docDomain.substr(nextDot + 1); 463 docDomain = docDomain.substr(nextDot + 1);
467 } 464 }
468 /* eslint-enable no-constant-condition */
469 return this.domains[""]; 465 return this.domains[""];
470 }, 466 },
471 467
472 /** 468 /**
473 * Checks whether this filter is active only on a domain and its subdomains. 469 * Checks whether this filter is active only on a domain and its subdomains.
474 * @param {String} docDomain 470 * @param {string} docDomain
475 * @return {Boolean} 471 * @return {boolean}
476 */ 472 */
477 isActiveOnlyOnDomain(docDomain) 473 isActiveOnlyOnDomain(docDomain)
478 { 474 {
479 if (!docDomain || !this.domains || this.domains[""]) 475 if (!docDomain || !this.domains || this.domains[""])
480 return false; 476 return false;
481 477
482 if (this.ignoreTrailingDot) 478 if (this.ignoreTrailingDot)
483 docDomain = docDomain.replace(/\.+$/, ""); 479 docDomain = docDomain.replace(/\.+$/, "");
484 docDomain = docDomain.toUpperCase(); 480 docDomain = docDomain.toUpperCase();
485 481
486 for (let domain in this.domains) 482 for (let domain in this.domains)
487 { 483 {
488 if (this.domains[domain] && domain != docDomain && 484 if (this.domains[domain] && domain != docDomain)
489 (domain.length <= docDomain.length || 485 {
490 domain.indexOf("." + docDomain) != 486 if (domain.length <= docDomain.length)
491 domain.length - docDomain.length - 1)) 487 return false;
492 return false; 488
489 if (!domain.endsWith("." + docDomain))
490 return false;
491 }
493 } 492 }
494 493
495 return true; 494 return true;
496 }, 495 },
497 496
498 /** 497 /**
499 * Checks whether this filter is generic or specific 498 * Checks whether this filter is generic or specific
500 * @return {Boolean} 499 * @return {boolean}
501 */ 500 */
502 isGeneric() 501 isGeneric()
503 { 502 {
504 return !(this.sitekeys && this.sitekeys.length) && 503 return !(this.sitekeys && this.sitekeys.length) &&
505 (!this.domains || this.domains[""]); 504 (!this.domains || this.domains[""]);
506 }, 505 },
507 506
508 /** 507 /**
509 * See Filter.serialize() 508 * See Filter.serialize()
510 * @param {string[]} buffer buffer to push the serialization results into 509 * @inheritdoc
511 */ 510 */
512 serialize(buffer) 511 serialize(buffer)
513 { 512 {
514 if (this._disabled || this._hitCount || this._lastHit) 513 if (this._disabled || this._hitCount || this._lastHit)
515 { 514 {
516 Filter.prototype.serialize.call(this, buffer); 515 Filter.prototype.serialize.call(this, buffer);
517 if (this._disabled) 516 if (this._disabled)
518 buffer.push("disabled=true"); 517 buffer.push("disabled=true");
519 if (this._hitCount) 518 if (this._hitCount)
520 buffer.push("hitCount=" + this._hitCount); 519 buffer.push("hitCount=" + this._hitCount);
521 if (this._lastHit) 520 if (this._lastHit)
522 buffer.push("lastHit=" + this._lastHit); 521 buffer.push("lastHit=" + this._lastHit);
523 } 522 }
524 } 523 }
525 }); 524 });
526 525
527 /** 526 /**
528 * Abstract base class for RegExp-based filters 527 * Abstract base class for RegExp-based filters
529 * @param {String} text see Filter() 528 * @param {string} text see Filter()
530 * @param {String} regexpSource filter part that the regular expression should 529 * @param {string} regexpSource
531 * be build from 530 * filter part that the regular expression should be build from
532 * @param {Number} [contentType] Content types the filter applies to, 531 * @param {number} [contentType]
533 * combination of values from RegExpFilter.typeMap 532 * Content types the filter applies to, combination of values from
534 * @param {Boolean} [matchCase] Defines whether the filter should distinguish 533 * RegExpFilter.typeMap
535 * between lower and upper case letters 534 * @param {boolean} [matchCase]
536 * @param {String} [domains] Domains that the filter is restricted to, 535 * Defines whether the filter should distinguish between lower and upper case
537 * e.g. "foo.com|bar.com|~baz.com" 536 * letters
538 * @param {Boolean} [thirdParty] Defines whether the filter should apply to 537 * @param {string} [domains]
539 * third-party or first-party content only 538 * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com"
540 * @param {String} [sitekeys] Public keys of websites that this filter should 539 * @param {boolean} [thirdParty]
541 * apply to 540 * Defines whether the filter should apply to third-party or first-party
541 * content only
542 * @param {string} [sitekeys]
543 * Public keys of websites that this filter should apply to
542 * @constructor 544 * @constructor
543 * @augments ActiveFilter 545 * @augments ActiveFilter
544 */ 546 */
545 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, 547 function RegExpFilter(text, regexpSource, contentType, matchCase, domains,
546 thirdParty, sitekeys) 548 thirdParty, sitekeys)
547 { 549 {
548 ActiveFilter.call(this, text, domains, sitekeys); 550 ActiveFilter.call(this, text, domains, sitekeys);
549 551
550 if (contentType != null) 552 if (contentType != null)
551 this.contentType = contentType; 553 this.contentType = contentType;
(...skipping 24 matching lines...) Expand all
576 578
577 RegExpFilter.prototype = extend(ActiveFilter, { 579 RegExpFilter.prototype = extend(ActiveFilter, {
578 /** 580 /**
579 * @see ActiveFilter.domainSourceIsUpperCase 581 * @see ActiveFilter.domainSourceIsUpperCase
580 */ 582 */
581 domainSourceIsUpperCase: true, 583 domainSourceIsUpperCase: true,
582 584
583 /** 585 /**
584 * Number of filters contained, will always be 1 (required to 586 * Number of filters contained, will always be 1 (required to
585 * optimize Matcher). 587 * optimize Matcher).
586 * @type {Integer} 588 * @type {number}
587 */ 589 */
588 length: 1, 590 length: 1,
589 591
590 /** 592 /**
591 * @see ActiveFilter.domainSeparator 593 * @see ActiveFilter.domainSeparator
592 */ 594 */
593 domainSeparator: "|", 595 domainSeparator: "|",
594 596
595 /** 597 /**
596 * Expression from which a regular expression should be generated - 598 * Expression from which a regular expression should be generated -
597 * for delayed creation of the regexp property 599 * for delayed creation of the regexp property
598 * @type {String} 600 * @type {string}
599 */ 601 */
600 regexpSource: null, 602 regexpSource: null,
601 /** 603 /**
602 * Regular expression to be used when testing against this filter 604 * Regular expression to be used when testing against this filter
603 * @type {RegExp} 605 * @type {RegExp}
604 */ 606 */
605 get regexp() 607 get regexp()
606 { 608 {
607 // Despite this property being cached, the getter is called 609 // Despite this property being cached, the getter is called
608 // several times on Safari, due to WebKit bug 132872 610 // several times on Safari, due to WebKit bug 132872
609 let prop = Object.getOwnPropertyDescriptor(this, "regexp"); 611 let prop = Object.getOwnPropertyDescriptor(this, "regexp");
610 if (prop) 612 if (prop)
611 return prop.value; 613 return prop.value;
612 614
613 let source = Filter.toRegExp(this.regexpSource); 615 let source = Filter.toRegExp(this.regexpSource);
614 let regexp = new RegExp(source, this.matchCase ? "" : "i"); 616 let regexp = new RegExp(source, this.matchCase ? "" : "i");
615 Object.defineProperty(this, "regexp", {value: regexp}); 617 Object.defineProperty(this, "regexp", {value: regexp});
616 return regexp; 618 return regexp;
617 }, 619 },
618 /** 620 /**
619 * Content types the filter applies to, combination of values from 621 * Content types the filter applies to, combination of values from
620 * RegExpFilter.typeMap 622 * RegExpFilter.typeMap
621 * @type {Number} 623 * @type {number}
622 */ 624 */
623 contentType: 0x7FFFFFFF, 625 contentType: 0x7FFFFFFF,
624 /** 626 /**
625 * Defines whether the filter should distinguish between lower and 627 * Defines whether the filter should distinguish between lower and
626 * upper case letters 628 * upper case letters
627 * @type {Boolean} 629 * @type {boolean}
628 */ 630 */
629 matchCase: false, 631 matchCase: false,
630 /** 632 /**
631 * Defines whether the filter should apply to third-party or 633 * Defines whether the filter should apply to third-party or
632 * first-party content only. Can be null (apply to all content). 634 * first-party content only. Can be null (apply to all content).
633 * @type {Boolean} 635 * @type {boolean}
634 */ 636 */
635 thirdParty: null, 637 thirdParty: null,
636 638
637 /** 639 /**
638 * String that the sitekey property should be generated from 640 * String that the sitekey property should be generated from
639 * @type {String} 641 * @type {string}
640 */ 642 */
641 sitekeySource: null, 643 sitekeySource: null,
642 644
643 /** 645 /**
644 * Array containing public keys of websites that this filter should apply to 646 * Array containing public keys of websites that this filter should apply to
645 * @type {string[]} 647 * @type {string[]}
646 */ 648 */
647 get sitekeys() 649 get sitekeys()
648 { 650 {
649 // Despite this property being cached, the getter is called 651 // Despite this property being cached, the getter is called
(...skipping 11 matching lines...) Expand all
661 } 663 }
662 664
663 Object.defineProperty( 665 Object.defineProperty(
664 this, "sitekeys", {value: sitekeys, enumerable: true} 666 this, "sitekeys", {value: sitekeys, enumerable: true}
665 ); 667 );
666 return this.sitekeys; 668 return this.sitekeys;
667 }, 669 },
668 670
669 /** 671 /**
670 * Tests whether the URL matches this filter 672 * Tests whether the URL matches this filter
671 * @param {String} location URL to be tested 673 * @param {string} location URL to be tested
672 * @param {number} typeMask bitmask of content / request types to match 674 * @param {number} typeMask bitmask of content / request types to match
673 * @param {String} docDomain domain name of the document that loads the URL 675 * @param {string} docDomain domain name of the document that loads the URL
674 * @param {Boolean} thirdParty should be true if the URL is a third-party 676 * @param {boolean} thirdParty should be true if the URL is a third-party
675 * request 677 * request
676 * @param {String} sitekey public key provided by the document 678 * @param {string} sitekey public key provided by the document
677 * @return {Boolean} true in case of a match 679 * @return {boolean} true in case of a match
678 */ 680 */
679 matches(location, typeMask, docDomain, thirdParty, sitekey) 681 matches(location, typeMask, docDomain, thirdParty, sitekey)
680 { 682 {
681 if (this.contentType & typeMask && 683 if (this.contentType & typeMask &&
682 (this.thirdParty == null || this.thirdParty == thirdParty) && 684 (this.thirdParty == null || this.thirdParty == thirdParty) &&
683 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location)) 685 this.isActiveOnDomain(docDomain, sitekey) && this.regexp.test(location))
686 {
684 return true; 687 return true;
688 }
685 return false; 689 return false;
686 } 690 }
687 }); 691 });
688 692
689 // Required to optimize Matcher, see also RegExpFilter.prototype.length 693 // Required to optimize Matcher, see also RegExpFilter.prototype.length
690 Object.defineProperty(RegExpFilter.prototype, "0", { 694 Object.defineProperty(RegExpFilter.prototype, "0", {
691 get() { return this; } 695 get() { return this; }
692 }); 696 });
693 697
694 /** 698 /**
695 * Creates a RegExp filter from its text representation 699 * Creates a RegExp filter from its text representation
696 * @param {String} text same as in Filter() 700 * @param {string} text same as in Filter()
697 * @return {RegExpFilter} 701 * @return {Filter}
698 */ 702 */
699 RegExpFilter.fromText = function(text) 703 RegExpFilter.fromText = function(text)
700 { 704 {
701 let blocking = true; 705 let blocking = true;
702 let origText = text; 706 let origText = text;
703 if (text.indexOf("@@") == 0) 707 if (text.indexOf("@@") == 0)
704 { 708 {
705 blocking = false; 709 blocking = false;
706 text = text.substr(2); 710 text = text.substr(2);
707 } 711 }
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
808 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't 812 // DOCUMENT, ELEMHIDE, POPUP, GENERICHIDE and GENERICBLOCK options shouldn't
809 // be there by default 813 // be there by default
810 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT | 814 RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.DOCUMENT |
811 RegExpFilter.typeMap.ELEMHIDE | 815 RegExpFilter.typeMap.ELEMHIDE |
812 RegExpFilter.typeMap.POPUP | 816 RegExpFilter.typeMap.POPUP |
813 RegExpFilter.typeMap.GENERICHIDE | 817 RegExpFilter.typeMap.GENERICHIDE |
814 RegExpFilter.typeMap.GENERICBLOCK); 818 RegExpFilter.typeMap.GENERICBLOCK);
815 819
816 /** 820 /**
817 * Class for blocking filters 821 * Class for blocking filters
818 * @param {String} text see Filter() 822 * @param {string} text see Filter()
819 * @param {String} regexpSource see RegExpFilter() 823 * @param {string} regexpSource see RegExpFilter()
820 * @param {Number} contentType see RegExpFilter() 824 * @param {number} contentType see RegExpFilter()
821 * @param {Boolean} matchCase see RegExpFilter() 825 * @param {boolean} matchCase see RegExpFilter()
822 * @param {String} domains see RegExpFilter() 826 * @param {string} domains see RegExpFilter()
823 * @param {Boolean} thirdParty see RegExpFilter() 827 * @param {boolean} thirdParty see RegExpFilter()
824 * @param {String} sitekeys see RegExpFilter() 828 * @param {string} sitekeys see RegExpFilter()
825 * @param {Boolean} collapse defines whether the filter should collapse blocked 829 * @param {boolean} collapse
826 * content, can be null 830 * defines whether the filter should collapse blocked content, can be null
827 * @constructor 831 * @constructor
828 * @augments RegExpFilter 832 * @augments RegExpFilter
829 */ 833 */
830 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, 834 function BlockingFilter(text, regexpSource, contentType, matchCase, domains,
831 thirdParty, sitekeys, collapse) 835 thirdParty, sitekeys, collapse)
832 { 836 {
833 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, 837 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains,
834 thirdParty, sitekeys); 838 thirdParty, sitekeys);
835 839
836 this.collapse = collapse; 840 this.collapse = collapse;
837 } 841 }
838 exports.BlockingFilter = BlockingFilter; 842 exports.BlockingFilter = BlockingFilter;
839 843
840 BlockingFilter.prototype = extend(RegExpFilter, { 844 BlockingFilter.prototype = extend(RegExpFilter, {
841 type: "blocking", 845 type: "blocking",
842 846
843 /** 847 /**
844 * Defines whether the filter should collapse blocked content. 848 * Defines whether the filter should collapse blocked content.
845 * Can be null (use the global preference). 849 * Can be null (use the global preference).
846 * @type {Boolean} 850 * @type {boolean}
847 */ 851 */
848 collapse: null 852 collapse: null
849 }); 853 });
850 854
851 /** 855 /**
852 * Class for whitelist filters 856 * Class for whitelist filters
853 * @param {String} text see Filter() 857 * @param {string} text see Filter()
854 * @param {String} regexpSource see RegExpFilter() 858 * @param {string} regexpSource see RegExpFilter()
855 * @param {Number} contentType see RegExpFilter() 859 * @param {number} contentType see RegExpFilter()
856 * @param {Boolean} matchCase see RegExpFilter() 860 * @param {boolean} matchCase see RegExpFilter()
857 * @param {String} domains see RegExpFilter() 861 * @param {string} domains see RegExpFilter()
858 * @param {Boolean} thirdParty see RegExpFilter() 862 * @param {boolean} thirdParty see RegExpFilter()
859 * @param {String} sitekeys see RegExpFilter() 863 * @param {string} sitekeys see RegExpFilter()
860 * @constructor 864 * @constructor
861 * @augments RegExpFilter 865 * @augments RegExpFilter
862 */ 866 */
863 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, 867 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains,
864 thirdParty, sitekeys) 868 thirdParty, sitekeys)
865 { 869 {
866 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, 870 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains,
867 thirdParty, sitekeys); 871 thirdParty, sitekeys);
868 } 872 }
869 exports.WhitelistFilter = WhitelistFilter; 873 exports.WhitelistFilter = WhitelistFilter;
870 874
871 WhitelistFilter.prototype = extend(RegExpFilter, { 875 WhitelistFilter.prototype = extend(RegExpFilter, {
872 type: "whitelist" 876 type: "whitelist"
873 }); 877 });
874 878
875 /** 879 /**
876 * Base class for element hiding filters 880 * Base class for element hiding filters
877 * @param {String} text see Filter() 881 * @param {string} text see Filter()
878 * @param {String} [domains] Host names or domains the filter should be 882 * @param {string} [domains] Host names or domains the filter should be
879 * restricted to 883 * restricted to
880 * @param {String} selector CSS selector for the HTML elements that should be 884 * @param {string} selector CSS selector for the HTML elements that should be
881 * hidden 885 * hidden
882 * @constructor 886 * @constructor
883 * @augments ActiveFilter 887 * @augments ActiveFilter
884 */ 888 */
885 function ElemHideBase(text, domains, selector) 889 function ElemHideBase(text, domains, selector)
886 { 890 {
887 ActiveFilter.call(this, text, domains || null); 891 ActiveFilter.call(this, text, domains || null);
888 892
889 if (domains) 893 if (domains)
890 { 894 {
891 this.selectorDomain = domains.replace(/,~[^,]+/g, ""). 895 this.selectorDomain = domains.replace(/,~[^,]+/g, "")
892 replace(/^~[^,]+,?/, "").toLowerCase(); 896 .replace(/^~[^,]+,?/, "").toLowerCase();
893 } 897 }
894 898
895 // Braces are being escaped to prevent CSS rule injection. 899 // Braces are being escaped to prevent CSS rule injection.
896 this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D "); 900 this.selector = selector.replace("{", "\\x7B ").replace("}", "\\x7D ");
897 } 901 }
898 exports.ElemHideBase = ElemHideBase; 902 exports.ElemHideBase = ElemHideBase;
899 903
900 ElemHideBase.prototype = extend(ActiveFilter, { 904 ElemHideBase.prototype = extend(ActiveFilter, {
901 /** 905 /**
902 * @see ActiveFilter.domainSeparator 906 * @see ActiveFilter.domainSeparator
903 */ 907 */
904 domainSeparator: ",", 908 domainSeparator: ",",
905 909
906 /** 910 /**
907 * @see ActiveFilter.ignoreTrailingDot 911 * @see ActiveFilter.ignoreTrailingDot
908 */ 912 */
909 ignoreTrailingDot: false, 913 ignoreTrailingDot: false,
910 914
911 /** 915 /**
912 * Host name or domain the filter should be restricted to (can be null for 916 * Host name or domain the filter should be restricted to (can be null for
913 * no restriction) 917 * no restriction)
914 * @type {String} 918 * @type {string}
915 */ 919 */
916 selectorDomain: null, 920 selectorDomain: null,
917 /** 921 /**
918 * CSS selector for the HTML elements that should be hidden 922 * CSS selector for the HTML elements that should be hidden
919 * @type {String} 923 * @type {string}
920 */ 924 */
921 selector: null 925 selector: null
922 }); 926 });
923 927
924 /** 928 /**
925 * Creates an element hiding filter from a pre-parsed text representation 929 * Creates an element hiding filter from a pre-parsed text representation
926 * 930 *
927 * @param {String} text same as in Filter() 931 * @param {string} text same as in Filter()
928 * @param {String} domain domain part of the text representation 932 * @param {string} domain
929 * (can be empty) 933 * domain part of the text representation (can be empty)
930 * @param {Boolean} isException exception rule indicator 934 * @param {boolean} isException exception rule indicator
931 * @param {String} tagName tag name part (can be empty) 935 * @param {string} tagName tag name part (can be empty)
932 * @param {String} attrRules attribute matching rules (can be empty) 936 * @param {string} attrRules attribute matching rules (can be empty)
933 * @param {String} selector raw CSS selector (can be empty) 937 * @param {string} selector raw CSS selector (can be empty)
934 * @return {ElemHideFilter|ElemHideException| 938 * @return {ElemHideFilter|ElemHideException|
935 * ElemHideEmulationFilter|InvalidFilter} 939 * ElemHideEmulationFilter|InvalidFilter}
936 */ 940 */
937 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, 941 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules,
938 selector) 942 selector)
939 { 943 {
940 if (!selector) 944 if (!selector)
941 { 945 {
942 if (tagName == "*") 946 if (tagName == "*")
943 tagName = ""; 947 tagName = "";
(...skipping 16 matching lines...) Expand all
960 { 964 {
961 if (id) 965 if (id)
962 return new InvalidFilter(text, "filter_elemhide_duplicate_id"); 966 return new InvalidFilter(text, "filter_elemhide_duplicate_id");
963 967
964 id = rule; 968 id = rule;
965 } 969 }
966 } 970 }
967 } 971 }
968 972
969 if (id) 973 if (id)
970 { 974 selector = `${tagName}.${id}${additional},${tagName}#${id}${additional}`;
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 }
974 else if (tagName || additional) 975 else if (tagName || additional)
975 selector = tagName + additional; 976 selector = tagName + additional;
976 else 977 else
977 return new InvalidFilter(text, "filter_elemhide_nocriteria"); 978 return new InvalidFilter(text, "filter_elemhide_nocriteria");
978 } 979 }
979 980
980 // We don't allow ElemHide filters which have any empty domains. 981 // We don't allow ElemHide filters which have any empty domains.
981 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that 982 // Note: The ElemHide.prototype.domainSeparator is duplicated here, if that
982 // changes this must be changed too. 983 // changes this must be changed too.
983 if (domain && /(^|,)~?(,|$)/.test(domain)) 984 if (domain && /(^|,)~?(,|$)/.test(domain))
(...skipping 10 matching lines...) Expand all
994 return new InvalidFilter(text, "filter_elemhideemulation_nodomain"); 995 return new InvalidFilter(text, "filter_elemhideemulation_nodomain");
995 996
996 return new ElemHideEmulationFilter(text, domain, selector); 997 return new ElemHideEmulationFilter(text, domain, selector);
997 } 998 }
998 999
999 return new ElemHideFilter(text, domain, selector); 1000 return new ElemHideFilter(text, domain, selector);
1000 }; 1001 };
1001 1002
1002 /** 1003 /**
1003 * Class for element hiding filters 1004 * Class for element hiding filters
1004 * @param {String} text see Filter() 1005 * @param {string} text see Filter()
1005 * @param {String} domains see ElemHideBase() 1006 * @param {string} domains see ElemHideBase()
1006 * @param {String} selector see ElemHideBase() 1007 * @param {string} selector see ElemHideBase()
1007 * @constructor 1008 * @constructor
1008 * @augments ElemHideBase 1009 * @augments ElemHideBase
1009 */ 1010 */
1010 function ElemHideFilter(text, domains, selector) 1011 function ElemHideFilter(text, domains, selector)
1011 { 1012 {
1012 ElemHideBase.call(this, text, domains, selector); 1013 ElemHideBase.call(this, text, domains, selector);
1013 } 1014 }
1014 exports.ElemHideFilter = ElemHideFilter; 1015 exports.ElemHideFilter = ElemHideFilter;
1015 1016
1016 ElemHideFilter.prototype = extend(ElemHideBase, { 1017 ElemHideFilter.prototype = extend(ElemHideBase, {
1017 type: "elemhide" 1018 type: "elemhide"
1018 }); 1019 });
1019 1020
1020 /** 1021 /**
1021 * Class for element hiding exceptions 1022 * Class for element hiding exceptions
1022 * @param {String} text see Filter() 1023 * @param {string} text see Filter()
1023 * @param {String} domains see ElemHideBase() 1024 * @param {string} domains see ElemHideBase()
1024 * @param {String} selector see ElemHideBase() 1025 * @param {string} selector see ElemHideBase()
1025 * @constructor 1026 * @constructor
1026 * @augments ElemHideBase 1027 * @augments ElemHideBase
1027 */ 1028 */
1028 function ElemHideException(text, domains, selector) 1029 function ElemHideException(text, domains, selector)
1029 { 1030 {
1030 ElemHideBase.call(this, text, domains, selector); 1031 ElemHideBase.call(this, text, domains, selector);
1031 } 1032 }
1032 exports.ElemHideException = ElemHideException; 1033 exports.ElemHideException = ElemHideException;
1033 1034
1034 ElemHideException.prototype = extend(ElemHideBase, { 1035 ElemHideException.prototype = extend(ElemHideBase, {
1035 type: "elemhideexception" 1036 type: "elemhideexception"
1036 }); 1037 });
1037 1038
1038 /** 1039 /**
1039 * Class for element hiding emulation filters 1040 * Class for element hiding emulation filters
1040 * @param {String} text see Filter() 1041 * @param {string} text see Filter()
1041 * @param {String} domains see ElemHideBase() 1042 * @param {string} domains see ElemHideBase()
1042 * @param {String} selector see ElemHideBase() 1043 * @param {string} selector see ElemHideBase()
1043 * @constructor 1044 * @constructor
1044 * @augments ElemHideBase 1045 * @augments ElemHideBase
1045 */ 1046 */
1046 function ElemHideEmulationFilter(text, domains, selector) 1047 function ElemHideEmulationFilter(text, domains, selector)
1047 { 1048 {
1048 ElemHideBase.call(this, text, domains, selector); 1049 ElemHideBase.call(this, text, domains, selector);
1049 } 1050 }
1050 exports.ElemHideEmulationFilter = ElemHideEmulationFilter; 1051 exports.ElemHideEmulationFilter = ElemHideEmulationFilter;
1051 1052
1052 ElemHideEmulationFilter.prototype = extend(ElemHideBase, { 1053 ElemHideEmulationFilter.prototype = extend(ElemHideBase, {
1053 type: "elemhideemulation" 1054 type: "elemhideemulation"
1054 }); 1055 });
LEFTRIGHT

Powered by Google App Engine
This is Rietveld