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

Unified Diff: lib/filterStorage.js

Issue 29375915: Issue 4878 - Start using ESLint for adblockpluscore (Closed)
Patch Set: Removed unused imports Created March 15, 2017, 3:11 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/filterNotifier.js ('k') | lib/matcher.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/filterStorage.js
diff --git a/lib/filterStorage.js b/lib/filterStorage.js
index a76992abf8e694278fec96506b71205a02fe60e0..cbce2e01926b0e80d95ae69037e96cbadb31cd56 100644
--- a/lib/filterStorage.js
+++ b/lib/filterStorage.js
@@ -15,36 +15,40 @@
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
*/
+"use strict";
+
/**
- * @fileOverview FilterStorage class responsible for managing user's subscriptions and filters.
+ * @fileOverview FilterStorage class responsible for managing user's
+ * subscriptions and filters.
*/
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-let {IO} = require("io");
-let {Prefs} = require("prefs");
-let {Filter, ActiveFilter} = require("filterClasses");
-let {Subscription, SpecialSubscription, ExternalSubscription} = require("subscriptionClasses");
-let {FilterNotifier} = require("filterNotifier");
-let {Utils} = require("utils");
+const {IO} = require("io");
+const {Prefs} = require("prefs");
+const {Filter, ActiveFilter} = require("filterClasses");
+const {Subscription, SpecialSubscription,
+ ExternalSubscription} = require("subscriptionClasses");
+const {FilterNotifier} = require("filterNotifier");
+const {Utils} = require("utils");
/**
* Version number of the filter storage file format.
- * @type Integer
+ * @type {number}
*/
let formatVersion = 4;
/**
- * This class reads user's filters from disk, manages them in memory and writes them back.
+ * This class reads user's filters from disk, manages them in memory
+ * and writes them back.
* @class
*/
let FilterStorage = exports.FilterStorage =
{
/**
* Version number of the patterns.ini format used.
- * @type Integer
+ * @type {number}
*/
get formatVersion()
{
@@ -53,14 +57,15 @@ let FilterStorage = exports.FilterStorage =
/**
* File that the filter list has been loaded from and should be saved to
- * @type nsIFile
+ * @type {nsIFile}
*/
get sourceFile()
{
let file = null;
if (Prefs.patternsfile)
{
- // Override in place, use it instead of placing the file in the regular data dir
+ // Override in place, use it instead of placing the file in the
+ // regular data dir
file = IO.resolveFilePath(Prefs.patternsfile);
}
if (!file)
@@ -75,23 +80,30 @@ let FilterStorage = exports.FilterStorage =
// Data directory pref misconfigured? Try the default value
try
{
- file = IO.resolveFilePath(Services.prefs.getDefaultBranch("extensions.adblockplus.").getCharPref("data_directory"));
+ let dir = Services.prefs.getDefaultBranch("extensions.adblockplus.")
+ .getCharPref("data_directory");
+ file = IO.resolveFilePath(dir);
if (file)
file.append("patterns.ini");
- } catch(e) {}
+ }
+ catch (e) {}
}
if (!file)
- Cu.reportError("Adblock Plus: Failed to resolve filter file location from extensions.adblockplus.patternsfile preference");
+ {
+ Cu.reportError("Adblock Plus: Failed to resolve filter file location " +
+ "from extensions.adblockplus.patternsfile preference");
+ }
// Property is configurable because of the test suite.
- Object.defineProperty(this, "sourceFile", {value: file, configurable: true});
+ Object.defineProperty(this, "sourceFile",
+ {value: file, configurable: true});
return file;
},
/**
* Will be set to true if no patterns.ini file exists.
- * @type Boolean
+ * @type {boolean}
*/
firstRun: false,
@@ -103,21 +115,23 @@ let FilterStorage = exports.FilterStorage =
/**
* List of filter subscriptions containing all filters
- * @type Subscription[]
+ * @type {Subscription[]}
*/
subscriptions: [],
/**
* Map of subscriptions already on the list, by their URL/identifier
- * @type Object
+ * @type {Object}
*/
knownSubscriptions: Object.create(null),
/**
* Finds the filter group that a filter should be added to by default. Will
* return null if this group doesn't exist yet.
+ * @param {Filter} filter
+ * @return {?SpecialSubscription}
*/
- getGroupForFilter: function(/**Filter*/ filter) /**SpecialSubscription*/
+ getGroupForFilter(filter)
{
let generalSubscription = null;
for (let subscription of FilterStorage.subscriptions)
@@ -129,8 +143,11 @@ let FilterStorage = exports.FilterStorage =
return subscription;
// If this is a general subscription - store it as fallback
- if (!generalSubscription && (!subscription.defaults || !subscription.defaults.length))
+ if (!generalSubscription &&
+ (!subscription.defaults || !subscription.defaults.length))
+ {
generalSubscription = subscription;
+ }
}
}
return generalSubscription;
@@ -139,9 +156,10 @@ let FilterStorage = exports.FilterStorage =
/**
* Adds a filter subscription to the list
* @param {Subscription} subscription filter subscription to be added
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
+ * @param {boolean} silent if true, no listeners will be triggered
+ * (to be used when filter list is reloaded)
*/
- addSubscription: function(subscription, silent)
+ addSubscription(subscription, silent)
{
if (subscription.url in FilterStorage.knownSubscriptions)
return;
@@ -157,9 +175,10 @@ let FilterStorage = exports.FilterStorage =
/**
* Removes a filter subscription from the list
* @param {Subscription} subscription filter subscription to be removed
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
+ * @param {boolean} silent if true, no listeners will be triggered
+ * (to be used when filter list is reloaded)
*/
- removeSubscription: function(subscription, silent)
+ removeSubscription(subscription, silent)
{
for (let i = 0; i < FilterStorage.subscriptions.length; i++)
{
@@ -182,13 +201,16 @@ let FilterStorage = exports.FilterStorage =
* @param {Subscription} [insertBefore] filter subscription to insert before
* (if omitted the subscription will be put at the end of the list)
*/
- moveSubscription: function(subscription, insertBefore)
+ moveSubscription(subscription, insertBefore)
{
let currentPos = FilterStorage.subscriptions.indexOf(subscription);
if (currentPos < 0)
return;
- let newPos = insertBefore ? FilterStorage.subscriptions.indexOf(insertBefore) : -1;
+ let newPos = -1;
+ if (insertBefore)
+ newPos = FilterStorage.subscriptions.indexOf(insertBefore);
+
if (newPos < 0)
newPos = FilterStorage.subscriptions.length;
@@ -207,7 +229,7 @@ let FilterStorage = exports.FilterStorage =
* @param {Subscription} subscription filter subscription to be updated
* @param {Filter[]} filters new filter list
*/
- updateSubscriptionFilters: function(subscription, filters)
+ updateSubscriptionFilters(subscription, filters)
{
removeSubscriptionFilters(subscription);
subscription.oldFilters = subscription.filters;
@@ -220,16 +242,23 @@ let FilterStorage = exports.FilterStorage =
/**
* Adds a user-defined filter to the list
* @param {Filter} filter
- * @param {SpecialSubscription} [subscription] particular group that the filter should be added to
- * @param {Integer} [position] position within the subscription at which the filter should be added
- * @param {Boolean} silent if true, no listeners will be triggered (to be used when filter list is reloaded)
+ * @param {SpecialSubscription} [subscription]
+ * particular group that the filter should be added to
+ * @param {number} [position]
+ * position within the subscription at which the filter should be added
+ * @param {boolean} silent
+ * if true, no listeners will be triggered (to be used when filter list is
+ * reloaded)
*/
- addFilter: function(filter, subscription, position, silent)
+ addFilter(filter, subscription, position, silent)
{
if (!subscription)
{
- if (filter.subscriptions.some(s => s instanceof SpecialSubscription && !s.disabled))
+ if (filter.subscriptions.some(s => s instanceof SpecialSubscription &&
+ !s.disabled))
+ {
return; // No need to add
+ }
subscription = FilterStorage.getGroupForFilter(filter);
}
if (!subscription)
@@ -247,24 +276,30 @@ let FilterStorage = exports.FilterStorage =
filter.subscriptions.push(subscription);
subscription.filters.splice(position, 0, filter);
if (!silent)
- FilterNotifier.triggerListeners("filter.added", filter, subscription, position);
+ {
+ FilterNotifier.triggerListeners("filter.added", filter, subscription,
+ position);
+ }
},
/**
* Removes a user-defined filter from the list
* @param {Filter} filter
* @param {SpecialSubscription} [subscription] a particular filter group that
- * the filter should be removed from (if ommited will be removed from all subscriptions)
- * @param {Integer} [position] position inside the filter group at which the
+ * the filter should be removed from (if ommited will be removed from all
+ * subscriptions)
+ * @param {number} [position] position inside the filter group at which the
* filter should be removed (if ommited all instances will be removed)
*/
- removeFilter: function(filter, subscription, position)
+ removeFilter(filter, subscription, position)
{
- let subscriptions = (subscription ? [subscription] : filter.subscriptions.slice());
+ let subscriptions = (
+ subscription ? [subscription] : filter.subscriptions.slice()
+ );
for (let i = 0; i < subscriptions.length; i++)
{
- let subscription = subscriptions[i];
- if (subscription instanceof SpecialSubscription)
+ let currentSubscription = subscriptions[i];
+ if (currentSubscription instanceof SpecialSubscription)
{
let positions = [];
if (typeof position == "undefined")
@@ -272,7 +307,7 @@ let FilterStorage = exports.FilterStorage =
let index = -1;
do
{
- index = subscription.filters.indexOf(filter, index + 1);
+ index = currentSubscription.filters.indexOf(filter, index + 1);
if (index >= 0)
positions.push(index);
} while (index >= 0);
@@ -282,17 +317,19 @@ let FilterStorage = exports.FilterStorage =
for (let j = positions.length - 1; j >= 0; j--)
{
- let position = positions[j];
- if (subscription.filters[position] == filter)
+ let currentPosition = positions[j];
+ if (currentSubscription.filters[currentPosition] == filter)
{
- subscription.filters.splice(position, 1);
- if (subscription.filters.indexOf(filter) < 0)
+ currentSubscription.filters.splice(currentPosition, 1);
+ if (currentSubscription.filters.indexOf(filter) < 0)
{
- let index = filter.subscriptions.indexOf(subscription);
+ let index = filter.subscriptions.indexOf(currentSubscription);
if (index >= 0)
filter.subscriptions.splice(index, 1);
}
- FilterNotifier.triggerListeners("filter.removed", filter, subscription, position);
+ FilterNotifier.triggerListeners(
+ "filter.removed", filter, currentSubscription, currentPosition
+ );
}
}
}
@@ -302,29 +339,35 @@ let FilterStorage = exports.FilterStorage =
/**
* Moves a user-defined filter to a new position
* @param {Filter} filter
- * @param {SpecialSubscription} subscription filter group where the filter is located
- * @param {Integer} oldPosition current position of the filter
- * @param {Integer} newPosition new position of the filter
+ * @param {SpecialSubscription} subscription filter group where the filter is
+ * located
+ * @param {number} oldPosition current position of the filter
+ * @param {number} newPosition new position of the filter
*/
- moveFilter: function(filter, subscription, oldPosition, newPosition)
+ moveFilter(filter, subscription, oldPosition, newPosition)
{
- if (!(subscription instanceof SpecialSubscription) || subscription.filters[oldPosition] != filter)
+ if (!(subscription instanceof SpecialSubscription) ||
+ subscription.filters[oldPosition] != filter)
+ {
return;
+ }
- newPosition = Math.min(Math.max(newPosition, 0), subscription.filters.length - 1);
+ newPosition = Math.min(Math.max(newPosition, 0),
+ subscription.filters.length - 1);
if (oldPosition == newPosition)
return;
subscription.filters.splice(oldPosition, 1);
subscription.filters.splice(newPosition, 0, filter);
- FilterNotifier.triggerListeners("filter.moved", filter, subscription, oldPosition, newPosition);
+ FilterNotifier.triggerListeners("filter.moved", filter, subscription,
+ oldPosition, newPosition);
},
/**
* Increases the hit count for a filter by one
* @param {Filter} filter
*/
- increaseHitCount: function(filter)
+ increaseHitCount(filter)
{
if (!Prefs.savestats || !(filter instanceof ActiveFilter))
return;
@@ -335,9 +378,10 @@ let FilterStorage = exports.FilterStorage =
/**
* Resets hit count for some filters
- * @param {Filter[]} filters filters to be reset, if null all filters will be reset
+ * @param {Filter[]} filters filters to be reset, if null all filters will
+ * be reset
*/
- resetHitCounts: function(filters)
+ resetHitCounts(filters)
{
if (!filters)
{
@@ -358,42 +402,46 @@ let FilterStorage = exports.FilterStorage =
* Loads all subscriptions from the disk
* @param {nsIFile} [sourceFile] File to read from
*/
- loadFromDisk: function(sourceFile)
+ loadFromDisk(sourceFile)
{
if (this._loading)
return;
this._loading = true;
- let readFile = function(sourceFile, backupIndex)
+ let readFile = (currentSourceFile, backupIndex) =>
{
let parser = new INIParser();
- IO.readFromFile(sourceFile, parser, function(e)
+ IO.readFromFile(currentSourceFile, parser, readFromFileException =>
{
- if (!e && parser.subscriptions.length == 0)
+ if (!readFromFileException && parser.subscriptions.length == 0)
{
// No filter subscriptions in the file, this isn't right.
- e = new Error("No data in the file");
+ readFromFileException = new Error("No data in the file");
}
- if (e)
- Cu.reportError(e);
+ if (readFromFileException)
+ Cu.reportError(readFromFileException);
- if (e && !explicitFile)
+ if (readFromFileException && !explicitFile)
{
// Attempt to load a backup
- sourceFile = this.sourceFile;
- if (sourceFile)
+ currentSourceFile = this.sourcefile;
+ if (currentSourceFile)
{
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(sourceFile.leafName) || [null, sourceFile.leafName, ""];
+ let [, part1, part2] = /^(.*)(\.\w+)$/.exec(
+ currentSourceFile.leafName
+ ) || [null, currentSourceFile.leafName, ""];
- sourceFile = sourceFile.clone();
- sourceFile.leafName = part1 + "-backup" + (++backupIndex) + part2;
+ currentSourceFile = currentSourceFile.clone();
+ currentSourceFile.leafName = (
+ part1 + "-backup" + (++backupIndex) + part2
+ );
- IO.statFile(sourceFile, function(e, statData)
+ IO.statFile(currentSourceFile, (statFileException, statData) =>
{
- if (!e && statData.exists)
- readFile(sourceFile, backupIndex);
+ if (!statFileException && statData.exists)
+ readFile(currentSourceFile, backupIndex);
else
doneReading(parser);
});
@@ -401,19 +449,23 @@ let FilterStorage = exports.FilterStorage =
}
}
doneReading(parser);
- }.bind(this));
- }.bind(this);
+ });
+ };
- var doneReading = function(parser)
+ let doneReading = parser =>
{
- // Old special groups might have been converted, remove them if they are empty
+ // Old special groups might have been converted, remove them if
+ // they are empty
let specialMap = {"~il~": true, "~wl~": true, "~fl~": true, "~eh~": true};
let knownSubscriptions = Object.create(null);
for (let i = 0; i < parser.subscriptions.length; i++)
{
let subscription = parser.subscriptions[i];
- if (subscription instanceof SpecialSubscription && subscription.filters.length == 0 && subscription.url in specialMap)
+ if (subscription instanceof SpecialSubscription &&
+ subscription.filters.length == 0 && subscription.url in specialMap)
+ {
parser.subscriptions.splice(i--, 1);
+ }
else
knownSubscriptions[subscription.url] = subscription;
}
@@ -438,8 +490,7 @@ let FilterStorage = exports.FilterStorage =
if (sourceFile != this.sourceFile)
this.saveToDisk();
-
- }.bind(this);
+ };
let explicitFile;
if (sourceFile)
@@ -450,7 +501,7 @@ let FilterStorage = exports.FilterStorage =
else
{
explicitFile = false;
- sourceFile = FilterStorage.sourceFile;
+ ({sourceFile} = FilterStorage);
let callback = function(e, statData)
{
@@ -471,7 +522,7 @@ let FilterStorage = exports.FilterStorage =
}
},
- _generateFilterData: function*(subscriptions)
+ *_generateFilterData(subscriptions)
{
yield "# Adblock Plus preferences";
yield "version=" + formatVersion;
@@ -507,7 +558,7 @@ let FilterStorage = exports.FilterStorage =
subscription.serialize(buf);
if (subscription.filters.length)
{
- buf.push("", "[Subscription filters]")
+ buf.push("", "[Subscription filters]");
subscription.serializeFilters(buf);
}
for (let k = 0; k < buf.length; k++)
@@ -518,14 +569,14 @@ let FilterStorage = exports.FilterStorage =
/**
* Will be set to true if saveToDisk() is running (reentrance protection).
- * @type Boolean
+ * @type {boolean}
*/
_saving: false,
/**
* Will be set to true if a saveToDisk() call arrives while saveToDisk() is
* already running (delayed execution).
- * @type Boolean
+ * @type {boolean}
*/
_needsSave: false,
@@ -533,7 +584,7 @@ let FilterStorage = exports.FilterStorage =
* Saves all subscriptions back to disk
* @param {nsIFile} [targetFile] File to be written
*/
- saveToDisk: function(targetFile)
+ saveToDisk(targetFile)
{
let explicitFile = true;
if (!targetFile)
@@ -551,13 +602,16 @@ let FilterStorage = exports.FilterStorage =
}
// Make sure the file's parent directory exists
- try {
- targetFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
- } catch (e) {}
+ try
+ {
+ targetFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE,
+ FileUtils.PERMS_DIRECTORY);
+ }
+ catch (e) {}
- let writeFilters = function()
+ let writeFilters = () =>
{
- IO.writeToFile(targetFile, this._generateFilterData(subscriptions), function(e)
+ IO.writeToFile(targetFile, this._generateFilterData(subscriptions), e =>
{
if (!explicitFile)
this._saving = false;
@@ -572,44 +626,54 @@ let FilterStorage = exports.FilterStorage =
}
else
FilterNotifier.triggerListeners("save");
- }.bind(this));
- }.bind(this);
+ });
+ };
- let checkBackupRequired = function(callbackNotRequired, callbackRequired)
+ let checkBackupRequired = (callbackNotRequired, callbackRequired) =>
{
if (explicitFile || Prefs.patternsbackups <= 0)
callbackNotRequired();
else
{
- IO.statFile(targetFile, function(e, statData)
+ IO.statFile(targetFile, (statFileException, statData) =>
{
- if (e || !statData.exists)
+ if (statFileException || !statData.exists)
callbackNotRequired();
else
{
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(targetFile.leafName) || [null, targetFile.leafName, ""];
+ let [, part1, part2] = /^(.*)(\.\w+)$/.exec(targetFile.leafName) ||
+ [null, targetFile.leafName, ""];
let newestBackup = targetFile.clone();
newestBackup.leafName = part1 + "-backup1" + part2;
- IO.statFile(newestBackup, function(e, statData)
- {
- if (!e && (!statData.exists || (Date.now() - statData.lastModified) / 3600000 >= Prefs.patternsbackupinterval))
- callbackRequired(part1, part2)
- else
- callbackNotRequired();
- });
+ IO.statFile(
+ newestBackup,
+ (statBackupFileException, statBackupData) =>
+ {
+ if (!statBackupFileException && (!statBackupData.exists ||
+ (Date.now() - statBackupData.lastModified) /
+ 3600000 >= Prefs.patternsbackupinterval))
+ {
+ callbackRequired(part1, part2);
+ }
+ else
+ callbackNotRequired();
+ }
+ );
}
});
}
- }.bind(this);
+ };
- let removeLastBackup = function(part1, part2)
+ let removeLastBackup = (part1, part2) =>
{
let file = targetFile.clone();
file.leafName = part1 + "-backup" + Prefs.patternsbackups + part2;
- IO.removeFile(file, (e) => renameBackup(part1, part2, Prefs.patternsbackups - 1));
- }.bind(this);
+ IO.removeFile(
+ file, e => renameBackup(part1, part2, Prefs.patternsbackups - 1)
+ );
+ };
- let renameBackup = function(part1, part2, index)
+ let renameBackup = (part1, part2, index) =>
{
if (index > 0)
{
@@ -618,7 +682,8 @@ let FilterStorage = exports.FilterStorage =
let toName = part1 + "-backup" + (index + 1) + part2;
- IO.renameFile(fromFile, toName, (e) => renameBackup(part1, part2, index - 1));
+ IO.renameFile(fromFile, toName, e => renameBackup(part1, part2,
+ index - 1));
}
else
{
@@ -627,10 +692,12 @@ let FilterStorage = exports.FilterStorage =
IO.copyFile(targetFile, toFile, writeFilters);
}
- }.bind(this);
+ };
// Do not persist external subscriptions
- let subscriptions = this.subscriptions.filter((s) => !(s instanceof ExternalSubscription));
+ let subscriptions = this.subscriptions.filter(
+ s => !(s instanceof ExternalSubscription)
+ );
if (!explicitFile)
this._saving = true;
@@ -639,13 +706,15 @@ let FilterStorage = exports.FilterStorage =
/**
* Returns the list of existing backup files.
+ * @return {nsIFile[]}
*/
- getBackupFiles: function() /**nsIFile[]*/
+ getBackupFiles()
{
- // TODO: This method should be asynchronous
let result = [];
- let [, part1, part2] = /^(.*)(\.\w+)$/.exec(FilterStorage.sourceFile.leafName) || [null, FilterStorage.sourceFile.leafName, ""];
+ let [, part1, part2] = /^(.*)(\.\w+)$/.exec(
+ FilterStorage.sourceFile.leafName
+ ) || [null, FilterStorage.sourceFile.leafName, ""];
for (let i = 1; ; i++)
{
let file = FilterStorage.sourceFile.clone();
@@ -661,7 +730,8 @@ let FilterStorage = exports.FilterStorage =
/**
* Joins subscription's filters to the subscription without any notifications.
- * @param {Subscription} subscription filter subscription that should be connected to its filters
+ * @param {Subscription} subscription
+ * filter subscription that should be connected to its filters
*/
function addSubscriptionFilters(subscription)
{
@@ -673,7 +743,8 @@ function addSubscriptionFilters(subscription)
}
/**
- * Removes subscription's filters from the subscription without any notifications.
+ * Removes subscription's filters from the subscription without any
+ * notifications.
* @param {Subscription} subscription filter subscription to be removed
*/
function removeSubscriptionFilters(subscription)
@@ -705,14 +776,14 @@ INIParser.prototype =
linesProcessed: 0,
subscriptions: null,
knownFilters: null,
- knownSubscriptions : null,
+ knownSubscriptions: null,
wantObj: true,
fileProperties: null,
curObj: null,
curSection: null,
userFilters: null,
- process: function(val)
+ process(val)
{
let origKnownFilters = Filter.knownFilters;
Filter.knownFilters = this.knownFilters;
@@ -735,16 +806,19 @@ INIParser.prototype =
if ("text" in this.curObj)
Filter.fromObject(this.curObj);
break;
- case "subscription":
+ case "subscription": {
let subscription = Subscription.fromObject(this.curObj);
if (subscription)
this.subscriptions.push(subscription);
break;
+ }
case "subscription filters":
case "subscription patterns":
if (this.subscriptions.length)
{
- let subscription = this.subscriptions[this.subscriptions.length - 1];
+ let subscription = this.subscriptions[
+ this.subscriptions.length - 1
+ ];
for (let text of this.curObj)
{
let filter = Filter.fromText(text);
« no previous file with comments | « lib/filterNotifier.js ('k') | lib/matcher.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld