Skip to content

Commit

Permalink
lots of progress, probably, but needs new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthropohedron committed Apr 21, 2015
1 parent 1abbf60 commit 55aeca3
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 20 deletions.
154 changes: 139 additions & 15 deletions lib/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,141 @@ function isKnownAlias(alias) {
}
exports.isKnownAlias = isKnownAlias;

function validateConfig(config) {
switch (config.name) {
case "negate":
if (!allActionsMap.hasOwnProperty(config.negatedName)) {
throw new Error("Unknown (negated) action: " +
config.negatedName);
}
break;
case "alias":
if (!aliases.hasOwnProperty(config.aliasRef)) {
throw new Error("Unknown alias: " + config.aliasRef);
}
break;
default:
if (!allActionsMap.hasOwnProperty(config.name)) {
throw new Error("Unknown action: " + config.name);
}
}
}

// returns whether we overwrote an existing alias
function registerAlias(alias, configs, noValidation) {
var exists = aliases.hasOwnProperty(alias);
if (!noValidation) {
configs.forEach(function(config) {
var name = config.name;
if (name === "negate") {
name = config.negatedName;
if (!allActionsMap.hasOwnProperty(name)) {
throw new Error("Unknown (negated) action: " + name);
}
} else if (!(allActionsMap.hasOwnProperty(name) ||
aliases.hasOwnProperty(name))) {
throw new Error("Unknown action/alias: " + name);
}
});
}
if (!noValidation) { configs.forEach(validateConfig); }
// easy way to deep copy the configs array
aliases[alias] = JSON.parse(JSON.stringify(configs));
return exists;
}
exports.registerAlias = registerAlias;

function flatten(list, elem) {
if (elem instanceof Array) {
list.push.apply(list, elem);
} else {
list.push(elem);
}
return list;
}

exports.registerAliases =
function registerAliases(aliasesCfg) {
var newAliases = {};
var oldDeferred =[];
var tmp, deferred = [];
var hadDeferred;
var deferredNames = {};
var alias, action, actions, newActions;
var name, i, len;

for (name in aliasesCfg) {
if (aliasesCfg.hasOwnProperty(name)) {
newActions = aliasesCfg[name];
len = newActions.length;
actions = [];
newAliases[name] = actions;
for (i=0; i<len; ++i) {
action = newActions[i];
switch (action.name) {
case "negate":
if (!allActionsMap.hasOwnProperty(action.negatedName)) {
throw new Error("Unknown (negated) action: " +
action.negatedName);
}
actions.push({
name: "negate",
negatedName: action.negatedName,
param: action.param
});
break;
case "alias":
if (newAliases.hasOwnProperty(action.aliasRef) &&
!deferredNames.hasOwnProperty(action.aliasRef)) {
// found alias, so add all its actions
actions.push.apply(actions, newAliases[action.aliasRef]);
} else {
// deferred
deferredNames[name] = (deferredNames[name] || 0) + 1;
actions.push([]);
deferred.push({
deferredName: name,
aliasRef: action.aliasRef,
actions: actions[actions.length-1]
});
}
break;
default:
actions.push({
name: action.name,
param: action.param
});
}
}
}
}

hadDeferred = deferred.length;
while (oldDeferred.length !== deferred.length) {
oldDeferred.length = 0;
tmp = oldDeferred;
oldDeferred = deferred;
deferred = tmp;
len = oldDeferred.length;
for (i=0; i<len; ++i) {
alias = oldDeferred[i];
if (newAliases.hasOwnProperty(alias.aliasRef) &&
!deferredNames.hasOwnProperty(alias.aliasRef)) {
// found alias, so add all its actions
actions = alias.actions;
actions.push.apply(actions, newAliases[alias.aliasRef]);
if ((--deferredNames[alias.deferredName]) <= 0) {
delete deferredNames[alias.deferredName];
}
} else {
// still deferred
deferred.push(alias);
}
}
}
if (deferred.length) {
throw new Error("Circular references detected in aliases: " +
deferred.map(function(defRef) {
return defRef.deferredName;
}).join(", "));
}

if (hadDeferred) {
for (name in newAliases) {
if (newAliases.hasOwnProperty(name)) {
newAliases[name] = newAliases[name].reduce(flatten, []);
}
}
}
aliases = newAliases;
};

var actionRE = new RegExp("(?:^|\\s)(?:([\\-+])(" +
allActions.join("|") +
")(?:{([^}]+)})?|([\\-+]?[a-z\\-]+))(?=\\s|$)", "gi");
Expand Down Expand Up @@ -80,6 +192,7 @@ function toActionConfig(line, negation, name, param, alias) {
return "";
}

exports.parseActions =
function parseActions(actionsClause) {
var actions = [];
var remainder = actionsClause.replace(actionRE,
Expand All @@ -88,7 +201,18 @@ function parseActions(actionsClause) {
throw new Error("Malformed actions clause:\n\n" + actionsClause);
}
return actions;
};

function configToAction(config) {
var name = config && config.name;
if (!allActionsMap.hasOwnProperty(name)) {
throw new Error("Unknown action: '" + name + "'");
}
return new allActionsMap[name](config.param);
}

exports.parseActions = parseActions;
exports.createActions =
function createActions(configs) {
return configs.map(configToAction);
};

62 changes: 57 additions & 5 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/*jslint vars: true, white: true, plusplus: true, todo: true */
/*global WeakMap */
"use strict";
var addonPrefs = require("sdk/simple-prefs").prefs;
var actions = require("./actions");
var Matcher = require("./matcher").Matcher;
var Filter = require("./filter").Filter;

// set plugin prefs syncing by default
(function() {
Expand Down Expand Up @@ -34,6 +38,9 @@ var defaultConfig = {
actions: [{
name: "block",
param: "garbage"
}, {
name: "alias",
aliasRef: "+crunch-all-cookies"
}],
patterns: [
"ad.",
Expand All @@ -47,35 +54,75 @@ var defaultConfig = {
subs: [{
find: "cyber",
repl: "computery",
flag: "gI"
flag: "gi"
}, {
find: "cloud",
repl: "butt",
flag: "gI"
flag: "gi"
}]
}]
};

var curConfig = null;
var cookedConfig = null;
var rawToCooked = new WeakMap();

if (require("sdk/system").staticArgs.supportMockXPCOM) {
exports.getDefaultConfig = function() { return defaultConfig; };
exports.getConfig = function() { return curConfig; };
exports.setConfig = function(config) { curConfig = config; };
}

// This does a lot of validation, but exits on the first failure; this
// deals with the internal and nominally opaque config format, though, so
// not giving useful feedback to the user (and silently resetting to the
// default) is probably okay.
function cookConfig(config) {
var cooked = {};

// register aliases
actions.registerAliases(config.actionAliases); //throws

// instantiate filters
cooked.filters = config.filters.reduce(function(filters, filter) {
var cookedFilter = new Filter(filter);
rawToCooked[filter] = cookedFilter;
filters[filter.name] = cookedFilter;
return filters;
}, {});

// build action groups
cooked.actionGroups = config.actionGroups.map(function(group) {
var newGroup = {
matcher: new Matcher(group.patterns)
};
//TODO: instantiate matchers
//TODO: resolve aliases
//TODO: validate filter references
return newGroup;
});

return cooked;
}

exports.loadConfig =
function loadConfig() {
var config = null;
var success = true;
try {
config = JSON.parse(addonPrefs.configJSON);
cookedConfig = cookConfig(config);
} catch (ignore) { }
if (config && !cookedConfig) {
addonPrefs.invalidConfigJSON = JSON.stringify(config);
config = null;
}
if (!config) {
success = false;
config = JSON.stringify(defaultConfig);
addonPrefs.configJSON = config;
config = JSON.parse(config);
cookedConfig = cookConfig(config);
}
curConfig = config;
return success;
Expand All @@ -89,8 +136,13 @@ function saveConfig() {

exports.actionGroupsForMatches =
function actionGroupsForMatches(callback) {
var actionGroups = [];
//TODO
return actionGroups;
var foundActions = [];
var groups = cookedConfig && cookedConfig.actionGroups;
groups.forEach(function(group) {
if (callback(group.matcher)) {
foundActions.push.apply(foundActions, group.actions);
}
});
return foundActions;
};

0 comments on commit 55aeca3

Please sign in to comment.