| Index: lib/rules.js | 
| =================================================================== | 
| --- a/lib/rules.js | 
| +++ b/lib/rules.js | 
| @@ -9,6 +9,8 @@ | 
| let RULES_VERSION = 2; | 
| +let CUSTOM_RULE_PRIORITY = 0x7FFFFFFF; | 
| + | 
| let rules = {expressions: []}; | 
| loadRules(); | 
| @@ -46,6 +48,10 @@ | 
| { | 
| rules = data; | 
| callback(true); | 
| + | 
| + // Add user-defined rules after calling the callback - if the callback | 
| + // saves the rules then the custom rules won't be included. | 
| + addCustomRules(); | 
| } | 
| else | 
| callback(false); | 
| @@ -81,6 +87,101 @@ | 
| return getRuleFile(); | 
| } | 
| +function addCustomRules() | 
| +{ | 
| + for (let domain in Prefs.whitelist) | 
| + onWhitelistEntryAdded(domain); | 
| +} | 
| + | 
| +function onWhitelistEntryAdded(domain) | 
| +{ | 
| + let reverse = domain.split("").reverse().join(""); | 
| + addSuffix(rules.domain, reverse, CUSTOM_RULE_PRIORITY); | 
| +} | 
| +exports.onWhitelistEntryAdded = onWhitelistEntryAdded; | 
| + | 
| +function onWhitelistEntryRemoved(domain) | 
| +{ | 
| + let reverse = domain.split("").reverse().join(""); | 
| + removeSuffix(rules.domain, reverse, CUSTOM_RULE_PRIORITY); | 
| +} | 
| +exports.onWhitelistEntryRemoved = onWhitelistEntryRemoved; | 
| + | 
| +function addSuffix(tree, suffix, priority) | 
| +{ | 
| + if (suffix.length == 0) | 
| + { | 
| + // We are at the last character, just put our priority here | 
| + tree[""] = " " + priority; | 
| + return; | 
| + } | 
| + | 
| + let c = suffix[0]; | 
| + if (c in tree) | 
| + { | 
| + let existing = tree[c]; | 
| + if (typeof existing == "string") | 
| + { | 
| + // Single choice for this suffix, maybe the same entry? | 
| + if (existing.substr(0, suffix.length - 1) == suffix.substr(1) && existing[suffix.length - 1] == " ") | 
| + { | 
| + // Same entry, simply replace it by new priority | 
| + tree[c] = suffix.substr(1) + " " + priority; | 
| + } | 
| + else | 
| + { | 
| + // Different entry, need to add a new branching point and go deeper | 
| + if (existing[0] == " ") | 
| + tree[c] = {"": existing}; | 
| + else | 
| + { | 
| + tree[c] = {}; | 
| + tree[c][existing[0]] = existing.substr(1); | 
| + } | 
| + addSuffix(tree[c], suffix.substr(1), priority); | 
| + } | 
| + } | 
| + else | 
| + { | 
| + // Multiple choices for this suffix - go deeper | 
| + addSuffix(existing, suffix.substr(1), priority); | 
| + } | 
| + } | 
| + else | 
| + { | 
| + // No existing entry yet, just add ours | 
| + tree[c] = suffix.substr(1) + " " + priority; | 
| + } | 
| +} | 
| + | 
| +function removeSuffix(tree, suffix, priority) | 
| +{ | 
| + if (suffix.length == 0) | 
| + { | 
| + // We are at the last character, check whether there is an entry with | 
| + // matching priority | 
| + if ("" in tree && tree[""] == " " + priority) | 
| + delete tree[""]; | 
| + return; | 
| + } | 
| + | 
| + let c = suffix[0]; | 
| + if (!(c in tree)) | 
| + return; | 
| + | 
| + if (typeof tree[c] == "string") | 
| + { | 
| + // Single entry - check whether it is the right one | 
| + if (tree[c] == suffix.substr(1) + " " + priority) | 
| + delete tree[c]; | 
| + } | 
| + else | 
| + { | 
| + // Multiple entries, need to go deeper | 
| + removeSuffix(tree[c], suffix.substr(1), priority); | 
| + } | 
| +} | 
| + | 
| function onTimer() | 
| { | 
| // Next check in 1 hour | 
| @@ -97,12 +198,6 @@ | 
| { | 
| rules.timestamp = Date.now(); | 
| - var dynRules = require("updateRules").updateRules(); | 
| - for(var i in dynRules) | 
| - { | 
| - rules[i] = dynRules[i]; | 
| - } | 
| - | 
| try | 
| { | 
| // Save the rules to file. |