add contributor mode and tools to contribute ruleset recipes (need more)

pull/2/head
Raymond Hill 7 years ago
parent a88a301d81
commit 55b9f1c645
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

@ -551,6 +551,14 @@
"message": "Import...", "message": "Import...",
"description": "" "description": ""
}, },
"assetsInlineHostsLabel": {
"message": "My hosts",
"description": ""
},
"assetsInlineRecipesLabel": {
"message": "My recipes",
"description": ""
},
"rawSettingsWarning" : { "rawSettingsWarning" : {
"message": "Warning! Change these raw configuration settings at your own risk.", "message": "Warning! Change these raw configuration settings at your own risk.",

@ -139,20 +139,29 @@ body.updating li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.upda
.dim { .dim {
opacity: 0.5; opacity: 0.5;
} }
li.listEntry.importURL > input[type="checkbox"] ~ textarea { li.listEntry.notAnAsset > input[type="checkbox"] ~ textarea {
display: none; display: none;
margin-left: 1.6em; margin-left: 1.6em;
} }
li.listEntry.importURL > input[type="checkbox"]:checked ~ textarea { li.listEntry.notAnAsset > input[type="checkbox"]:checked ~ textarea {
display: block; display: block;
} }
li.listEntry.importURL > textarea { li.listEntry.notAnAsset > textarea {
border: 1px solid #ddd; border: 1px solid #ddd;
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
font-size: smaller; font-size: smaller;
width: calc(100% - 4em); width: calc(100% - 4em);
height: 6em; height: 6em;
resize: none; resize: vertical;
white-space: pre; white-space: pre;
} }
#recipes li.listEntry.toInline {
display: none;
}
body.contributor #recipes li.listEntry.toInline {
display: block;
}
#recipes li.listEntry.toInline > textarea {
height: 18em;
}

@ -24,7 +24,7 @@
<p data-i18n="hostsFilesPrompt"></p> <p data-i18n="hostsFilesPrompt"></p>
<p id="listsOfBlockedHostsPrompt"></p> <p id="listsOfBlockedHostsPrompt"></p>
<ul id="hosts"> <ul id="hosts">
<li class="listEntry importURL"><input type="checkbox" id="importHosts"><label for="importHosts"data-i18n="assetsImportLabel"></label><!-- <li class="listEntry notAnAsset toImport"><input type="checkbox" id="importHosts"><label for="importHosts" data-i18n="assetsImportLabel"></label><!--
--><textarea dir="ltr" spellcheck="false" placeholder="hostsFilesExternalListsHint"></textarea> --><textarea dir="ltr" spellcheck="false" placeholder="hostsFilesExternalListsHint"></textarea>
</ul> </ul>
</div> </div>
@ -35,8 +35,10 @@
<div> <div>
<p data-i18n="assetsRecipesSummary"></p> <p data-i18n="assetsRecipesSummary"></p>
<ul id="recipes"> <ul id="recipes">
<li class="listEntry importURL"><input type="checkbox" id="importRecipes"><label for="importRecipes"data-i18n="assetsImportLabel"></label><!-- <li class="listEntry notAnAsset toImport"><input type="checkbox" id="importRecipes"><label for="importRecipes" data-i18n="assetsImportLabel"></label><!--
--><textarea dir="ltr" spellcheck="false" placeholder="hostsFilesExternalListsHint"></textarea> --><textarea dir="ltr" spellcheck="false" placeholder="hostsFilesExternalListsHint"></textarea>
<li class="listEntry notAnAsset toInline"><input type="checkbox" id="inlineRecipes"><label for="inlineRecipes" data-i18n="assetsInlineRecipesLabel"></label><!--
--><textarea dir="ltr" spellcheck="false" placeholder="assetsInlineRecipesHint"></textarea>
</ul> </ul>
</div> </div>
</div> </div>

@ -101,6 +101,7 @@ var requestStatsFactory = function() {
*/ */
var rawSettingsDefault = { var rawSettingsDefault = {
contributorMode: false,
disableCSPReportInjection: false, disableCSPReportInjection: false,
placeholderBackground: placeholderBackground:
[ [
@ -188,7 +189,15 @@ return {
processHyperlinkAuditing: true, processHyperlinkAuditing: true,
processReferer: false, processReferer: false,
selectedHostsFiles: [ '' ], selectedHostsFiles: [ '' ],
selectedRecipeFiles: [ '' ] selectedRecipeFiles: [ '' ],
userHosts: {
enabled: false,
content: ''
},
userRecipes: {
enabled: false,
content: ''
}
}, },
rawSettingsDefault: rawSettingsDefault, rawSettingsDefault: rawSettingsDefault,

@ -145,22 +145,23 @@ var renderHostsFiles = function(soft) {
}); });
let ulList = document.querySelector(listSelector), let ulList = document.querySelector(listSelector),
liImport = ulList.querySelector('.importURL'); liLast = ulList.querySelector('.notAnAsset');
if ( liImport.parentNode !== null ) {
liImport.parentNode.removeChild(liImport);
}
for ( let i = 0; i < assetKeys.length; i++ ) { for ( let i = 0; i < assetKeys.length; i++ ) {
let liEntry = liFromListEntry( let liReuse = i < ulList.childElementCount ?
collection, ulList.children[i] :
assetKeys[i], null;
ulList.children[i] if (
); liReuse !== null &&
liReuse.classList.contains('notAnAsset')
) {
liReuse = null;
}
let liEntry = liFromListEntry(collection, assetKeys[i], liReuse);
if ( liEntry.parentElement === null ) { if ( liEntry.parentElement === null ) {
ulList.appendChild(liEntry); ulList.insertBefore(liEntry, liLast);
} }
} }
ulList.appendChild(liImport);
}; };
var onAssetDataReceived = function(details) { var onAssetDataReceived = function(details) {
@ -171,9 +172,14 @@ var renderHostsFiles = function(soft) {
// Before all, set context vars // Before all, set context vars
listDetails = details; listDetails = details;
document.body.classList.toggle(
'contributor',
listDetails.contributor === true
);
// Incremental rendering: this will allow us to easily discard unused // Incremental rendering: this will allow us to easily discard unused
// DOM list entries. // DOM list entries.
uDom('#hosts .listEntry:not(.importURL)').addClass('discard'); uDom('#hosts .listEntry:not(.notAnAsset)').addClass('discard');
onRenderAssetFiles(details.hosts, '#hosts'); onRenderAssetFiles(details.hosts, '#hosts');
onRenderAssetFiles(details.recipes, '#recipes'); onRenderAssetFiles(details.recipes, '#recipes');
@ -188,6 +194,12 @@ var renderHostsFiles = function(soft) {
); );
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom.nodeFromSelector('#recipes .toInline > input[type="checkbox"]').checked =
listDetails.userRecipes.enabled === true;
uDom.nodeFromSelector('#recipes .toInline > textarea').value =
listDetails.userRecipes.content;
if ( !soft ) { if ( !soft ) {
hostsFilesSettingsHash = hashFromCurrentFromSettings(); hostsFilesSettingsHash = hashFromCurrentFromSettings();
} }
@ -231,31 +243,50 @@ var updateAssetStatus = function(details) {
/******************************************************************************* /*******************************************************************************
Compute a hash from all the settings affecting how filter lists are loaded Compute a hash from all the settings affecting how assets are loaded
in memory. in memory.
**/ **/
var hashFromCurrentFromSettings = function() { var hashFromCurrentFromSettings = function() {
var hash = [], let listHash = [],
listHash = [], listEntries = document.querySelectorAll(
listEntries = document.querySelectorAll('.assets .listEntry[data-listkey]:not(.toRemove)'), '.assets .listEntry[data-listkey]:not(.toRemove)'
i = listEntries.length; );
while ( i-- ) { for ( let liEntry of listEntries ) {
let liEntry = listEntries[i];
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) { if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
listHash.push(liEntry.getAttribute('data-listkey')); listHash.push(liEntry.getAttribute('data-listkey'));
} }
} }
let textarea1 = document.querySelector('.assets .importURL > input[type="checkbox"]:checked ~ textarea'); return [
let textarea2 = document.querySelector('#recipes .importURL > input[type="checkbox"]:checked ~ textarea'); listHash.join(),
hash.push( document.querySelector('.listEntry.toRemove') !== null,
listHash.sort().join(), reValidExternalList.test(
textarea1 !== null && reValidExternalList.test(textarea1.value), textFromTextarea(
textarea2 !== null && reValidExternalList.test(textarea2.value), '#hosts .toImport > input[type="checkbox"]:checked ~ textarea'
document.querySelector('.listEntry.toRemove') !== null )
); ),
return hash.join(); textFromTextarea(
'#hosts .toInline > input[type="checkbox"]:checked ~ textarea'
),
reValidExternalList.test(
textFromTextarea(
'#recipes .toImport > input[type="checkbox"]:checked ~ textarea'
)
),
textFromTextarea(
'#recipes .toInline > input[type="checkbox"]:checked ~ textarea'
),
].join('\n');
};
/******************************************************************************/
var textFromTextarea = function(textarea) {
if ( typeof textarea === 'string' ) {
textarea = document.querySelector(textarea);
}
return textarea !== null ? textarea.value.trim() : '';
}; };
/******************************************************************************/ /******************************************************************************/
@ -300,36 +331,44 @@ var selectAssets = function(callback) {
var out = { var out = {
toSelect: [], toSelect: [],
toImport: '', toImport: '',
toRemove: [] toRemove: [],
toInline: {
enabled: false,
content: ''
}
}; };
let root = document.querySelector(listSelector); let root = document.querySelector(listSelector);
let liEntries = root.querySelectorAll('.listEntry[data-listkey]:not(.toRemove)'), // Lists to select or remove
i = liEntries.length; let liEntries = root.querySelectorAll(
while ( i-- ) { '.listEntry[data-listkey]:not(.notAnAsset)'
let liEntry = liEntries[i]; );
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) { for ( let liEntry of liEntries ) {
if ( liEntry.classList.contains('toRemove') ) {
out.toRemove.push(liEntry.getAttribute('data-listkey'));
} else if ( liEntry.querySelector('input[type="checkbox"]:checked') ) {
out.toSelect.push(liEntry.getAttribute('data-listkey')); out.toSelect.push(liEntry.getAttribute('data-listkey'));
} }
} }
// External hosts files to remove
liEntries = root.querySelectorAll('.listEntry.toRemove[data-listkey]');
i = liEntries.length;
while ( i-- ) {
out.toRemove.push(liEntries[i].getAttribute('data-listkey'));
}
// External hosts files to import // External hosts files to import
let input = root.querySelector('.importURL > input[type="checkbox"]:checked'); let input = root.querySelector(
'.toImport > input[type="checkbox"]:checked'
);
if ( input !== null ) { if ( input !== null ) {
let textarea = root.querySelector('.importURL textarea'); let textarea = root.querySelector('.toImport textarea');
out.toImport = textarea.value.trim(); out.toImport = textarea.value.trim();
textarea.value = ''; textarea.value = '';
input.checked = false; input.checked = false;
} }
// Inline data
out.toInline.enabled = root.querySelector(
'.toInline > input[type="checkbox"]:checked'
) !== null;
out.toInline.content = textFromTextarea('.toInline > textarea');
return out; return out;
}; };
@ -406,7 +445,7 @@ uDom('#buttonApply').on('click', buttonApplyHandler);
uDom('#buttonUpdate').on('click', buttonUpdateHandler); uDom('#buttonUpdate').on('click', buttonUpdateHandler);
uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler); uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
uDom('.assets').on('change', '.listEntry > input', onHostsFilesSettingsChanged); uDom('.assets').on('change', '.listEntry > input', onHostsFilesSettingsChanged);
uDom('.assets').on('input', '.listEntry.importURL textarea', onHostsFilesSettingsChanged); uDom('.assets').on('input', '.listEntry > textarea', onHostsFilesSettingsChanged);
uDom('.assets').on('click', '.listEntry > a.remove', onRemoveExternalAsset); uDom('.assets').on('click', '.listEntry > a.remove', onRemoveExternalAsset);
uDom('.assets').on('click', 'span.cache', onPurgeClicked); uDom('.assets').on('click', 'span.cache', onPurgeClicked);

@ -736,7 +736,9 @@ var getAssets = function(callback) {
blockedHostnameCount: µm.ubiquitousBlacklist.count, blockedHostnameCount: µm.ubiquitousBlacklist.count,
hosts: null, hosts: null,
recipes: null, recipes: null,
cache: null userRecipes: µm.userSettings.userRecipes,
cache: null,
contributor: µm.rawSettings.contributorMode
}; };
var onMetadataReady = function(entries) { var onMetadataReady = function(entries) {
r.cache = entries; r.cache = entries;

@ -292,6 +292,14 @@
for ( let assetKey of µm.userSettings.selectedRecipeFiles ) { for ( let assetKey of µm.userSettings.selectedRecipeFiles ) {
this.assets.get(assetKey, onLoaded); this.assets.get(assetKey, onLoaded);
} }
let userRecipes = µm.userSettings.userRecipes;
if ( userRecipes.enabled ) {
µm.recipeManager.fromString(
'! uMatrix: Ruleset recipes 1.0\n' + userRecipes.content
);
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -572,7 +580,8 @@
metadata, metadata,
details, details,
propSelectedAssetKeys, propSelectedAssetKeys,
propImportedAssetKeys propImportedAssetKeys,
propInlineAsset
) { ) {
let µmus = µm.userSettings; let µmus = µm.userSettings;
let selectedAssetKeys = new Set(); let selectedAssetKeys = new Set();
@ -629,14 +638,35 @@
} }
let bin = {}, let bin = {},
changed = false; needReload = false;
if ( details.toInline instanceof Object ) {
let newInline = details.toInline;
let oldInline = µmus[propInlineAsset];
newInline.content = newInline.content.trim();
if ( newInline.content.length !== 0 ) {
newInline.content += '\n';
}
let newContent = newInline.enabled ? newInline.content : '';
let oldContent = oldInline.enabled ? oldInline.content : '';
if ( newContent !== oldContent ) {
needReload = true;
}
if (
newInline.enabled !== oldInline.enabled ||
newInline.content !== oldInline.content
) {
µmus[propInlineAsset] = newInline;
bin[propInlineAsset] = newInline;
}
}
selectedAssetKeys = Array.from(selectedAssetKeys).sort(); selectedAssetKeys = Array.from(selectedAssetKeys).sort();
µmus[propSelectedAssetKeys].sort(); µmus[propSelectedAssetKeys].sort();
if ( selectedAssetKeys.join() !== µmus[propSelectedAssetKeys].join() ) { if ( selectedAssetKeys.join() !== µmus[propSelectedAssetKeys].join() ) {
µmus[propSelectedAssetKeys] = selectedAssetKeys; µmus[propSelectedAssetKeys] = selectedAssetKeys;
bin[propSelectedAssetKeys] = selectedAssetKeys; bin[propSelectedAssetKeys] = selectedAssetKeys;
changed = true; needReload = true;
} }
importedAssetKeys = Array.from(importedAssetKeys).sort(); importedAssetKeys = Array.from(importedAssetKeys).sort();
@ -644,14 +674,14 @@
if ( importedAssetKeys.join() !== µmus[propImportedAssetKeys].join() ) { if ( importedAssetKeys.join() !== µmus[propImportedAssetKeys].join() ) {
µmus[propImportedAssetKeys] = importedAssetKeys; µmus[propImportedAssetKeys] = importedAssetKeys;
bin[propImportedAssetKeys] = importedAssetKeys; bin[propImportedAssetKeys] = importedAssetKeys;
changed = true; needReload = true;
} }
if ( changed ) { if ( Object.keys(bin).length !== 0 ) {
vAPI.storage.set(bin); vAPI.storage.set(bin);
} }
return changed; return needReload;
}; };
var onMetadataReady = function(response) { var onMetadataReady = function(response) {
@ -660,7 +690,8 @@
metadata, metadata,
details.hosts, details.hosts,
'selectedHostsFiles', 'selectedHostsFiles',
'externalHostsFiles' 'externalHostsFiles',
'userHosts'
); );
if ( hostsChanged ) { if ( hostsChanged ) {
µm.hostsFilesSelfie.destroy(); µm.hostsFilesSelfie.destroy();
@ -669,7 +700,8 @@
metadata, metadata,
details.recipes, details.recipes,
'selectedRecipeFiles', 'selectedRecipeFiles',
'externalRecipeFiles' 'externalRecipeFiles',
'userRecipes'
); );
if ( recipesChanged ) { if ( recipesChanged ) {
µm.recipeManager.reset(); µm.recipeManager.reset();

Loading…
Cancel
Save