diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index da12e7e..cf8a9d8 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -155,6 +155,14 @@
"message": "No web page found",
"description": "Displays in place of matrix when no data is found for the current page"
},
+ "matrixRecipeImportTip" : {
+ "message": "Import rules",
+ "description": "Used as a tooltip for the recipe import button"
+ },
+ "matrixRecipeSaveTip" : {
+ "message": "Save rules",
+ "description": "Used as a tooltip for the recipe padlock button"
+ },
"statsPageTitle" : {
"message": "uMatrix – Statistics",
diff --git a/src/css/popup.css b/src/css/popup.css
index 3ef899a..b977931 100644
--- a/src/css/popup.css
+++ b/src/css/popup.css
@@ -159,7 +159,7 @@ body .toolbar button.fa {
}
.dropdown-menu-capture {
- background-color: transparent;
+ background-color: rgba(0,0,0,0.2);
border: 0;
bottom: 0;
display: none;
@@ -171,6 +171,10 @@ body .toolbar button.fa {
top: 0;
z-index: 300;
}
+.dropdown-menu-capture.dropdown-menu-centered > .dropdown-menu {
+ left: 4em;
+ right: 4em;
+ }
.dropdown-menu-capture.show {
display: block;
}
@@ -211,14 +215,15 @@ body .toolbar button.fa {
}
button > span.badge {
- padding: 1px 1px;
- display: inline-block;
- font-size: 40%;
- position: absolute;
- right: 1px;
+ background-color: rgba(240,240,240,0.75);
bottom: 1px;
color: #000;
- background-color: rgba(240,240,240,0.75)
+ display: inline-block;
+ font-size: 40%;
+ padding: 1px 1px;
+ pointer-events: none;
+ position: absolute;
+ right: 1px;
}
button.disabled > span.badge {
display: none;
@@ -238,7 +243,6 @@ button.disabled > span.badge {
padding: 0;
}
.recipe {
- cursor: pointer;
list-style-type: none;
white-space: nowrap;
}
@@ -266,15 +270,21 @@ button.disabled > span.badge {
}
.recipe .name {
color: #000;
+ cursor: default;
flex-grow: 1;
+ padding: 0.4em 0;
}
+.recipe .importer,
.recipe .committer {
+ cursor: pointer;
display: none;
font-size: 120%;
padding: 0.4em;
- width: 0.8em;
+ text-align: center;
+ width: 1em;
}
-.recipe.mustCommit .committer {
+.recipe.mustImport .importer,
+.recipe.mustCommit:not(.mustImport) .committer {
display: inline;
}
.recipe:hover {
diff --git a/src/hosts-files.html b/src/hosts-files.html
index 61c0af7..9df8e52 100644
--- a/src/hosts-files.html
+++ b/src/hosts-files.html
@@ -21,7 +21,7 @@
-
+
-
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 393a329..8a09862 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -349,7 +349,7 @@ var onMessage = function(request, sender, callback) {
break;
case 'fetchRecipeCommitStatuses':
- response = µm.recipeManager.commitStatuses(request);
+ response = µm.recipeManager.statuses(request);
break;
case 'toggleMatrixSwitch':
diff --git a/src/js/popup.js b/src/js/popup.js
index 2382924..d661d2a 100644
--- a/src/js/popup.js
+++ b/src/js/popup.js
@@ -1310,10 +1310,19 @@ let recipeManager = (function() {
}
function apply(ev) {
- if ( ev.target.classList.contains('expander') ) {
+ if (
+ ev.target.classList.contains('expander') ||
+ ev.target.classList.contains('name')
+ ) {
ev.currentTarget.classList.toggle('expanded');
return;
}
+ if (
+ ev.target.classList.contains('importer') === false &&
+ ev.target.classList.contains('committer') === false
+ ) {
+ return;
+ }
let root = ev.currentTarget;
let ruleset = root.querySelector('.ruleset');
let commit = ev.target.classList.contains('committer');
@@ -1326,6 +1335,7 @@ let recipeManager = (function() {
},
updateMatrixSnapshot
);
+ root.classList.remove('mustImport');
if ( commit ) {
root.classList.remove('mustCommit');
}
@@ -1341,6 +1351,7 @@ let recipeManager = (function() {
recipe.ruleset.replace(reScopeAlias, '$1' + details.scope + '$2'),
ul
);
+ li.classList.toggle('mustImport', recipe.mustImport === true);
li.classList.toggle('mustCommit', recipe.mustCommit === true);
li.addEventListener('click', apply);
}
@@ -1448,8 +1459,10 @@ function dropDownMenuShow(button) {
var menu = menuOverlay.querySelector('.dropdown-menu');
var menuRect = menu.getBoundingClientRect();
var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
- menu.style.left = menuLeft.toFixed(0) + 'px';
menu.style.top = butnRect.bottom + 'px';
+ if ( menuOverlay.classList.contains('dropdown-menu-centered') === false ) {
+ menu.style.left = menuLeft.toFixed(0) + 'px';
+ }
}
function dropDownMenuHide() {
diff --git a/src/js/recipe-manager.js b/src/js/recipe-manager.js
index e7813da..213ec6b 100644
--- a/src/js/recipe-manager.js
+++ b/src/js/recipe-manager.js
@@ -148,99 +148,118 @@
rawRecipes = [];
};
- return {
- apply: function(details) {
- let µm = µMatrix;
- let tMatrix = µm.tMatrix;
- let pMatrix = µm.pMatrix;
- let mustPersist = false;
- for ( let rule of details.ruleset.split('\n') ) {
- let parts = rule.split(/\s+/);
- if ( parts.length < 2 ) { continue; }
- let f0 = parts[0];
- let f1 = parts[1];
- // Switch
- if ( f0.endsWith(':') ) {
- f0 = f0.slice(0, -1);
- if ( tMatrix.evaluateSwitchZ(f0, f1) !== false ) {
- tMatrix.setSwitchZ(f0, f1, false);
- if ( details.commit ) {
- pMatrix.setSwitchZ(f0, f1, false);
- mustPersist = true;
- }
- }
- continue;
- }
- // Rule
- if ( parts.length < 3 ) { continue; }
- let f2 = parts[2];
- let action = tMatrix.evaluateCellZXY(f0, f1, f2);
- if ( (action & 3) === 1 ) {
- tMatrix.whitelistCell(f0, f1, f2);
- }
- if ( details.commit !== true ) { continue; }
- action = pMatrix.evaluateCellZXY(f0, f1, f2);
- if ( (action & 3) === 1 ) {
- pMatrix.whitelistCell(f0, f1, f2);
- mustPersist = true;
- }
- }
- if ( mustPersist ) {
- µm.saveMatrix();
- }
- },
- fetch: function(srcHostname, desHostnames, callback) {
- fromPendingStrings();
- let out = [];
- let fetched = new Set();
- let tokens = getTokens(srcHostname + ' ' + desHostnames.join(' '));
- for ( let token of tokens ) {
- let recipes = recipeBook.get(token);
- if ( recipes === undefined ) { continue; }
- for ( let recipe of recipes ) {
- if ( fetched.has(recipe.id) ) { continue; }
- if (
- conditionMatch(
- recipe.condition,
- srcHostname,
- desHostnames
- )
- ) {
- out.push(recipe);
- fetched.add(recipe.id);
+ // true = blocked, false = not blocked
+ var evaluateRuleParts = function(matrix, scope, parts) {
+ if ( parts[0].endsWith(':') ) {
+ return matrix.evaluateSwitchZ(parts[0].slice(0, -1), scope);
+ }
+ return matrix.evaluateCellZXY(scope, parts[1], parts[2]) === 1;
+ };
+
+ var api = {};
+
+ api.apply = function(details) {
+ let µm = µMatrix;
+ let tMatrix = µm.tMatrix;
+ let pMatrix = µm.pMatrix;
+ let mustPersist = false;
+ for ( let rule of details.ruleset.split('\n') ) {
+ let parts = rule.split(/\s+/);
+ if ( parts.length < 2 ) { continue; }
+ let f0 = parts[0];
+ let f1 = parts[1];
+ // Switch
+ if ( f0.endsWith(':') ) {
+ f0 = f0.slice(0, -1);
+ if ( tMatrix.evaluateSwitchZ(f0, f1) !== false ) {
+ tMatrix.setSwitchZ(f0, f1, false);
+ if ( details.commit ) {
+ pMatrix.setSwitchZ(f0, f1, false);
+ mustPersist = true;
}
}
+ continue;
}
- callback(out);
- },
- commitStatuses: function(details) {
- let matrix = µMatrix.pMatrix;
- for ( let recipe of details.recipes ) {
- let ruleIter = new µMatrix.LineIterator(recipe.ruleset);
- while ( ruleIter.eot() === false ) {
- let parts = ruleIter.next().split(/\s+/);
- if (
- matrix.evaluateCellZXY(
- details.scope,
- parts[1],
- parts[2]
- ) === 1
- ) {
- recipe.mustCommit = true;
- break;
- }
- }
+ // Rule
+ if ( parts.length < 3 ) { continue; }
+ let f2 = parts[2];
+ let action = tMatrix.evaluateCellZXY(f0, f1, f2);
+ if ( (action & 3) === 1 ) {
+ tMatrix.whitelistCell(f0, f1, f2);
}
- return details;
- },
- fromString: function(raw) {
- rawRecipes.push(raw);
- },
- reset: function() {
- rawRecipes.length = 0;
- recipeBook.clear();
+ if ( details.commit !== true ) { continue; }
+ action = pMatrix.evaluateCellZXY(f0, f1, f2);
+ if ( (action & 3) === 1 ) {
+ pMatrix.whitelistCell(f0, f1, f2);
+ mustPersist = true;
+ }
+ }
+ if ( mustPersist ) {
+ µm.saveMatrix();
}
};
+
+ api.fetch = function(srcHostname, desHostnames, callback) {
+ fromPendingStrings();
+ let out = [];
+ let fetched = new Set();
+ let tokens = getTokens(srcHostname + ' ' + desHostnames.join(' '));
+ for ( let token of tokens ) {
+ let recipes = recipeBook.get(token);
+ if ( recipes === undefined ) { continue; }
+ for ( let recipe of recipes ) {
+ if ( fetched.has(recipe.id) ) { continue; }
+ if (
+ conditionMatch(
+ recipe.condition,
+ srcHostname,
+ desHostnames
+ )
+ ) {
+ out.push(recipe);
+ fetched.add(recipe.id);
+ }
+ }
+ }
+ callback(out);
+ };
+
+ api.statuses = function(details) {
+ let pMatrix = µMatrix.pMatrix,
+ tMatrix = µMatrix.tMatrix;
+ for ( let recipe of details.recipes ) {
+ let ruleIter = new µMatrix.LineIterator(recipe.ruleset);
+ while ( ruleIter.eot() === false ) {
+ let parts = ruleIter.next().split(/\s+/);
+ if (
+ recipe.mustCommit !== true &&
+ evaluateRuleParts(pMatrix, details.scope, parts)
+ ) {
+ recipe.mustCommit = true;
+ if ( recipe.mustImport ) { break; }
+ }
+ if (
+ recipe.mustImport !== true &&
+ evaluateRuleParts(tMatrix, details.scope, parts)
+ ) {
+ recipe.mustImport = true;
+ if ( recipe.mustCommit ) { break; }
+ }
+ }
+ }
+ return details;
+ };
+
+ api.fromString = function(raw) {
+ rawRecipes.push(raw);
+ };
+
+ api.reset = function() {
+ rawRecipes.length = 0;
+ recipeBook.clear();
+ };
+
+ return api;
})();
/******************************************************************************/
diff --git a/src/popup.html b/src/popup.html
index ca4a6a0..5e49a47 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -74,7 +74,7 @@
-