mirror of
https://github.com/gorhill/uMatrix.git
synced 2024-06-03 02:44:57 +12:00
add contributor mode and tools to contribute ruleset recipes (need more)
This commit is contained in:
parent
a88a301d81
commit
55b9f1c645
|
@ -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…
Reference in a new issue