various improvements to the "Filter lists" pane

This commit is contained in:
Raymond Hill 2018-04-09 09:01:39 -04:00
parent fb2b402940
commit b60c06f3c4
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
4 changed files with 230 additions and 119 deletions

View file

@ -13,32 +13,30 @@
<body> <body>
<div class="body"> <div class="body">
<div id="cloudWidget" class="hide" data-cloud-entry="tpFiltersPane"></div>
<div> <div id="cloudWidget" class="hide" data-cloud-entry="tpFiltersPane"></div>
<ul id="options"> <div>
<li><button id="buttonUpdate" class="custom important disabled" data-i18n="3pUpdateNow"></button> <ul id="options" class="root">
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button> <li><button id="buttonUpdate" class="custom important disabled" data-i18n="3pUpdateNow"></button>
<button id="buttonApply" class="custom important disabled" data-i18n="3pApplyChanges"></button> <button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
<li><input type="checkbox" id="autoUpdate"><label data-i18n="3pAutoUpdatePrompt1" for="autoUpdate"></label>&ensp; <button id="buttonApply" class="custom important disabled" data-i18n="3pApplyChanges"></button>
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label><button class="whatisthis"></button> <li><input type="checkbox" id="autoUpdate"><label data-i18n="3pAutoUpdatePrompt1" for="autoUpdate"></label>&ensp;
<div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div> <li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label><button class="whatisthis"></button>
<li><input type="checkbox" id="ignoreGenericCosmeticFilters"><label data-i18n="3pIgnoreGenericCosmeticFilters" for="ignoreGenericCosmeticFilters"></label><button class="whatisthis"></button> <div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div>
<div class="whatisthis-expandable para" data-i18n="3pIgnoreGenericCosmeticFiltersInfo"></div> <li><input type="checkbox" id="ignoreGenericCosmeticFilters"><label data-i18n="3pIgnoreGenericCosmeticFilters" for="ignoreGenericCosmeticFilters"></label><button class="whatisthis"></button>
</ul> <div class="whatisthis-expandable para" data-i18n="3pIgnoreGenericCosmeticFiltersInfo"></div>
<p><span id="listsOfBlockedHostsPrompt"></span></p> </ul>
<ul class="root">
<li><span id="listsOfBlockedHostsPrompt"></span>
<ul id="lists"></ul> <ul id="lists"></ul>
</div> </ul>
<div id="externalListsDiv"> </div>
<p>
<span data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></span>
<a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">&#xf05a;</a>
<textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
</div>
</div> </div>
<div id="templates" style="display: none;"> <div id="templates" style="display: none;">
<ul> <ul>
<li class="groupEntry"><span class="geName"></span> <span class="geCount dim"></span> <li class="groupEntry"><span class="geDetails"><span class="geName"></span> <span class="geCount dim"></span></span>
<ul class="listEntries"></ul> <ul class="listEntries"></ul>
</li> </li>
<li class="listEntry"> <li class="listEntry">
@ -54,6 +52,9 @@
--><span class="fa status updating" title="3pUpdating">&#xf110;</span>&#8203;<!-- --><span class="fa status updating" title="3pUpdating">&#xf110;</span>&#8203;<!--
--><span class="fa status failed" title="3pNetworkError">&#xf127;</span> --><span class="fa status failed" title="3pNetworkError">&#xf127;</span>
</li> </li>
<li class="listEntry toImport"><input type="checkbox" id="importLists"><label for="importLists" data-i18n="3pImport"></label><!--
--><a class="fa info towiki" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">&#xf05a;</a><!--
--><textarea id="externalLists" dir="ltr" spellcheck="false" placeholder="3pExternalListsHint"></textarea>
</ul> </ul>
</div> </div>

View file

@ -16,7 +16,7 @@
"description":"appears as tab name in dashboard" "description":"appears as tab name in dashboard"
}, },
"3pPageName":{ "3pPageName":{
"message":"3rd-party filters", "message":"Filter lists",
"description":"appears as tab name in dashboard" "description":"appears as tab name in dashboard"
}, },
"1pPageName":{ "1pPageName":{
@ -351,6 +351,10 @@
"message":"Apply changes", "message":"Apply changes",
"description":"English: Apply changes" "description":"English: Apply changes"
}, },
"3pGroupDefault":{
"message":"Local",
"description":"Header for the uBlock filters section in 'Filter lists pane'"
},
"3pGroupAds":{ "3pGroupAds":{
"message":"Ads", "message":"Ads",
"description":"English: Ads" "description":"English: Ads"
@ -379,8 +383,12 @@
"message":"Custom", "message":"Custom",
"description":"English: Custom" "description":"English: Custom"
}, },
"3pImport":{
"message":"Import...",
"description":"The label for the checkbox used to import external filter lists"
},
"3pExternalListsHint":{ "3pExternalListsHint":{
"message":"One URL per line. Lines prefixed with <code>!</code> will be ignored. Invalid URLs will be silently ignored.", "message":"One URL per line. Invalid URLs will be silently ignored.",
"description":"Short information about how to use the textarea to import external filter lists by URL" "description":"Short information about how to use the textarea to import external filter lists by URL"
}, },
"3pExternalListObsolete":{ "3pExternalListObsolete":{

View file

@ -3,8 +3,16 @@
100% { transform: rotate(360deg); -webkit-transform: rotate(360deg); } 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg); }
} }
ul { ul {
padding: 0;
list-style-type: none; list-style-type: none;
padding-left: 1em;
padding-right: 0;
}
body[dir="rtl"] ul {
padding-left: 0;
padding-right: 1em;
}
ul.root {
padding: 0;
} }
#options li { #options li {
margin-bottom: 0.5em; margin-bottom: 0.5em;
@ -13,7 +21,7 @@ ul {
cursor: pointer; cursor: pointer;
} }
#listsOfBlockedHostsPrompt:before { #listsOfBlockedHostsPrompt:before {
color: #aaa; color: #888;
content: '\2212 '; content: '\2212 ';
} }
body.hideUnused #listsOfBlockedHostsPrompt:before { body.hideUnused #listsOfBlockedHostsPrompt:before {
@ -21,53 +29,46 @@ body.hideUnused #listsOfBlockedHostsPrompt:before {
} }
#lists { #lists {
margin: 0.5em 0 0 0; margin: 0.5em 0 0 0;
padding-left: 0.5em; padding: 0;
padding-right: 0em;
}
body[dir="rtl"] #lists {
padding-left: 0em;
padding-right: 0.5em;
} }
#lists > li { #lists > li {
margin: 0.5em 0 0 0; margin: 0.5em 0 0 0;
padding: 0; padding: 0;
list-style-type: none; list-style-type: none;
} }
#lists > .groupEntry > .geName { #lists > .groupEntry .geDetails {
cursor: pointer; cursor: pointer;
font-size: 110%;
} }
#lists > .groupEntry > .geCount { #lists > .groupEntry .geDetails:before {
font-size: 90%; color: #888;
}
#lists > .groupEntry:not(:first-child) > .geName:before {
color: #aaa;
content: '\2212 '; content: '\2212 ';
} }
#lists > .groupEntry.collapsed > .geName:before { #lists > .groupEntry.hideUnused .geDetails:before {
color: #aaa;
content: '+ '; content: '+ ';
} }
#lists > .groupEntry .geName {
pointer-events: none;
}
#lists > .groupEntry .geCount {
font-size: 90%;
pointer-events: none;
}
#lists > .groupEntry > ul { #lists > .groupEntry > ul {
margin: 0.25em 0 0 0; margin: 0.25em 0 0 0;
} padding-left: 1em;
#lists > .groupEntry.collapsed > ul {
display: none;
} }
li.listEntry { li.listEntry {
line-height: 150%;
margin: 0 auto 0 auto; margin: 0 auto 0 auto;
margin-left: 2.5em; padding: 0.2em 0;
margin-right: 0; white-space: nowrap;
text-indent: -2em;
} }
body[dir="rtl"] li.listEntry { body[dir="rtl"] li.listEntry {
margin-left: 0em; }
margin-right: 2.5em; li.listEntry.unused {
display: none;
} }
li.listEntry > * { li.listEntry > * {
margin-right: 0.5em; margin-right: 0.5em;
text-indent: 0;
unicode-bidi: embed; unicode-bidi: embed;
} }
li.listEntry.toRemove > input[type="checkbox"] { li.listEntry.toRemove > input[type="checkbox"] {
@ -83,6 +84,9 @@ li.listEntry > .fa {
opacity: 0.5; opacity: 0.5;
vertical-align: baseline; vertical-align: baseline;
} }
li.listEntry > a.towiki {
display: inline-block;
}
li.listEntry > a.fa:hover { li.listEntry > a.fa:hover {
opacity: 1; opacity: 1;
} }
@ -164,17 +168,18 @@ body.updating li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.upda
animation: spin 2s linear infinite; animation: spin 2s linear infinite;
display: inline-block; display: inline-block;
} }
#externalListsDiv { li.listEntry.toImport > input[type="checkbox"] ~ textarea {
margin: 1.5em auto 0 1.5em; border: 1px solid #ccc;
}
body[dir=rtl] #externalListsDiv {
margin: 1.5em 1.5em 0 auto;
}
#externalLists {
box-sizing: border-box; box-sizing: border-box;
height: 8em; display: block;
margin-top: 0.25em; font-size: smaller;
height: 6em;
margin-left: 2em;
resize: vertical;
visibility: hidden;
white-space: pre; white-space: pre;
width: 100%; width: calc(100% - 4em);
word-wrap: normal; }
li.listEntry.toImport > input[type="checkbox"]:checked ~ textarea {
visibility: visible;
} }

View file

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2017 Raymond Hill Copyright (C) 2014-2018 Raymond Hill
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -32,7 +32,8 @@
var listDetails = {}, var listDetails = {},
filteringSettingsHash = '', filteringSettingsHash = '',
lastUpdateTemplateString = vAPI.i18n('3pLastUpdate'), lastUpdateTemplateString = vAPI.i18n('3pLastUpdate'),
reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/; reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/,
hideUnusedSet = new Set();
/******************************************************************************/ /******************************************************************************/
@ -69,7 +70,6 @@ var renderFilterLists = function(soft) {
listEntryTemplate = uDom('#templates .listEntry'), listEntryTemplate = uDom('#templates .listEntry'),
listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'), listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'),
renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString, renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
hideUnusedLists = document.body.classList.contains('hideUnused'),
groupNames = new Map(); groupNames = new Map();
// Assemble a pretty list name if possible // Assemble a pretty list name if possible
@ -80,16 +80,17 @@ var renderFilterLists = function(soft) {
return listTitle; return listTitle;
}; };
var liFromListEntry = function(listKey, li) { var liFromListEntry = function(listKey, li, hideUnused) {
var entry = listDetails.available[listKey], var entry = listDetails.available[listKey],
elem; elem;
if ( !li ) { if ( !li ) {
li = listEntryTemplate.clone().nodeAt(0); li = listEntryTemplate.clone().nodeAt(0);
} }
var on = entry.off !== true;
if ( li.getAttribute('data-listkey') !== listKey ) { if ( li.getAttribute('data-listkey') !== listKey ) {
li.setAttribute('data-listkey', listKey); li.setAttribute('data-listkey', listKey);
elem = li.querySelector('input[type="checkbox"]'); elem = li.querySelector('input[type="checkbox"]');
elem.checked = entry.off !== true; elem.checked = on;
elem = li.querySelector('a:nth-of-type(1)'); elem = li.querySelector('a:nth-of-type(1)');
elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
elem.setAttribute('type', 'text/html'); elem.setAttribute('type', 'text/html');
@ -115,18 +116,17 @@ var renderFilterLists = function(soft) {
} else { } else {
li.classList.remove('mustread'); li.classList.remove('mustread');
} }
li.classList.toggle('unused', hideUnused && !on);
} }
// https://github.com/gorhill/uBlock/issues/1429 // https://github.com/gorhill/uBlock/issues/1429
if ( !soft ) { if ( !soft ) {
elem = li.querySelector('input[type="checkbox"]'); li.querySelector('input[type="checkbox"]').checked = on;
elem.checked = entry.off !== true;
} }
li.style.setProperty('display', hideUnusedLists && entry.off === true ? 'none' : '');
elem = li.querySelector('span.counts'); elem = li.querySelector('span.counts');
var text = ''; var text = '';
if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) { if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
text = listStatsTemplate text = listStatsTemplate
.replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount)) .replace('{{used}}', renderNumber(on ? entry.entryUsedCount : 0))
.replace('{{total}}', renderNumber(entry.entryCount)); .replace('{{total}}', renderNumber(entry.entryCount));
} }
elem.textContent = text; elem.textContent = text;
@ -157,14 +157,18 @@ var renderFilterLists = function(soft) {
var listEntryCountFromGroup = function(listKeys) { var listEntryCountFromGroup = function(listKeys) {
if ( Array.isArray(listKeys) === false ) { return ''; } if ( Array.isArray(listKeys) === false ) { return ''; }
var count = 0; var count = 0,
total = 0;
var i = listKeys.length; var i = listKeys.length;
while ( i-- ) { while ( i-- ) {
if ( listDetails.available[listKeys[i]].off !== true ) { if ( listDetails.available[listKeys[i]].off !== true ) {
count += 1; count += 1;
} }
total += 1;
} }
return count === 0 ? '' : '(' + count.toLocaleString() + ')'; return total !== 0 ?
'(' + count.toLocaleString() + '/' + total.toLocaleString() + ')' :
'';
}; };
var liFromListGroup = function(groupKey, listKeys) { var liFromListGroup = function(groupKey, listKeys) {
@ -189,13 +193,19 @@ var renderFilterLists = function(soft) {
if ( liGroup.querySelector('.geName:empty') === null ) { if ( liGroup.querySelector('.geName:empty') === null ) {
liGroup.querySelector('.geCount').textContent = listEntryCountFromGroup(listKeys); liGroup.querySelector('.geCount').textContent = listEntryCountFromGroup(listKeys);
} }
var hideUnused = mustHideUnusedLists(groupKey);
liGroup.classList.toggle('hideUnused', hideUnused);
var ulGroup = liGroup.querySelector('.listEntries'); var ulGroup = liGroup.querySelector('.listEntries');
if ( !listKeys ) { return liGroup; } if ( !listKeys ) { return liGroup; }
listKeys.sort(function(a, b) { listKeys.sort(function(a, b) {
return (listDetails.available[a].title || '').localeCompare(listDetails.available[b].title || ''); return (listDetails.available[a].title || '').localeCompare(listDetails.available[b].title || '');
}); });
for ( var i = 0; i < listKeys.length; i++ ) { for ( var i = 0; i < listKeys.length; i++ ) {
var liEntry = liFromListEntry(listKeys[i], ulGroup.children[i]); var liEntry = liFromListEntry(
listKeys[i],
ulGroup.children[i],
hideUnused
);
if ( liEntry.parentElement === null ) { if ( liEntry.parentElement === null ) {
ulGroup.appendChild(liEntry); ulGroup.appendChild(liEntry);
} }
@ -226,7 +236,10 @@ var renderFilterLists = function(soft) {
// 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('#lists .listEntries .listEntry').addClass('discard'); uDom('#lists .listEntries .listEntry[data-listkey]').addClass('discard');
// Remove import widget while we recreate list of lists.
var importWidget = uDom('.listEntry.toImport').detach();
// Visually split the filter lists in purpose-based groups // Visually split the filter lists in purpose-based groups
var ulLists = document.querySelector('#lists'), var ulLists = document.querySelector('#lists'),
@ -242,6 +255,7 @@ var renderFilterLists = function(soft) {
'regions', 'regions',
'custom' 'custom'
]; ];
document.body.classList.toggle('hideUnused', mustHideUnusedLists('*'));
for ( i = 0; i < groupKeys.length; i++ ) { for ( i = 0; i < groupKeys.length; i++ ) {
groupKey = groupKeys[i]; groupKey = groupKeys[i];
liGroup = liFromListGroup(groupKey, groups[groupKey]); liGroup = liFromListGroup(groupKey, groups[groupKey]);
@ -263,17 +277,28 @@ var renderFilterLists = function(soft) {
} }
uDom('#lists .listEntries .listEntry.discard').remove(); uDom('#lists .listEntries .listEntry.discard').remove();
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom('#listsOfBlockedHostsPrompt').text( // Re-insert import widget.
uDom('[data-groupkey="custom"] .listEntries').append(importWidget);
uDom.nodeFromId('autoUpdate').checked = listDetails.autoUpdate === true;
uDom.nodeFromId('listsOfBlockedHostsPrompt').textContent =
vAPI.i18n('3pListsOfBlockedHostsPrompt') vAPI.i18n('3pListsOfBlockedHostsPrompt')
.replace('{{netFilterCount}}', renderNumber(details.netFilterCount)) .replace(
.replace('{{cosmeticFilterCount}}', renderNumber(details.cosmeticFilterCount)) '{{netFilterCount}}',
); renderNumber(details.netFilterCount)
)
.replace(
'{{cosmeticFilterCount}}',
renderNumber(details.cosmeticFilterCount)
);
uDom.nodeFromId('parseCosmeticFilters').checked =
listDetails.parseCosmeticFilters === true;
uDom.nodeFromId('ignoreGenericCosmeticFilters').checked =
listDetails.ignoreGenericCosmeticFilters === true;
// Compute a hash of the settings so that we can keep track of changes // Compute a hash of the settings so that we can keep track of changes
// affecting the loading of filter lists. // affecting the loading of filter lists.
uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true);
uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true);
if ( !soft ) { if ( !soft ) {
filteringSettingsHash = hashFromCurrentFromSettings(); filteringSettingsHash = hashFromCurrentFromSettings();
} }
@ -286,12 +311,18 @@ var renderFilterLists = function(soft) {
/******************************************************************************/ /******************************************************************************/
var renderWidgets = function() { var renderWidgets = function() {
uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings()); uDom('#buttonApply').toggleClass(
'disabled',
filteringSettingsHash === hashFromCurrentFromSettings()
);
uDom('#buttonPurgeAll').toggleClass( uDom('#buttonPurgeAll').toggleClass(
'disabled', 'disabled',
document.querySelector('#lists .listEntry.cached:not(.obsolete)') === null document.querySelector('#lists .listEntry.cached:not(.obsolete)') === null
); );
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null); uDom('#buttonUpdate').toggleClass(
'disabled',
document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null
);
}; };
/******************************************************************************/ /******************************************************************************/
@ -323,8 +354,8 @@ var updateAssetStatus = function(details) {
var hashFromCurrentFromSettings = function() { var hashFromCurrentFromSettings = function() {
var hash = [ var hash = [
document.getElementById('parseCosmeticFilters').checked, uDom.nodeFromId('parseCosmeticFilters').checked,
document.getElementById('ignoreGenericCosmeticFilters').checked uDom.nodeFromId('ignoreGenericCosmeticFilters').checked
]; ];
var listHash = [], var listHash = [],
listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'), listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
@ -338,7 +369,8 @@ var hashFromCurrentFromSettings = function() {
} }
hash.push( hash.push(
listHash.sort().join(), listHash.sort().join(),
reValidExternalList.test(document.getElementById('externalLists').value), uDom.nodeFromId('importLists').checked &&
reValidExternalList.test(uDom.nodeFromId('externalLists').value),
document.querySelector('#lists .listEntry.toRemove') !== null document.querySelector('#lists .listEntry.toRemove') !== null
); );
return hash.join(); return hash.join();
@ -424,6 +456,7 @@ var selectFilterLists = function(callback) {
var externalListsElem = document.getElementById('externalLists'), var externalListsElem = document.getElementById('externalLists'),
toImport = externalListsElem.value.trim(); toImport = externalListsElem.value.trim();
externalListsElem.value = ''; externalListsElem.value = '';
uDom.nodeFromId('importLists').checked = false;
messaging.send( messaging.send(
'dashboard', 'dashboard',
@ -490,36 +523,89 @@ var autoUpdateCheckboxChanged = function() {
/******************************************************************************/ /******************************************************************************/
var toggleUnusedLists = function() { // Collapsing of unused lists.
document.body.classList.toggle('hideUnused');
var hide = document.body.classList.contains('hideUnused'); var mustHideUnusedLists = function(which) {
uDom('#lists li.listEntry > input[type="checkbox"]:not(:checked)') var hideAll = hideUnusedSet.has('*');
.ancestors('li.listEntry[data-listkey]') if ( which === '*' ) { return hideAll; }
.css('display', hide ? 'none' : ''); return hideUnusedSet.has(which) !== hideAll;
vAPI.localStorage.setItem('hideUnusedFilterLists', hide ? '1' : '0');
}; };
/******************************************************************************/ var toggleHideUnusedLists = function(which) {
var groupSelector,
var groupEntryClickHandler = function() { doesHideAll = hideUnusedSet.has('*'),
var li = uDom(this).ancestors('.groupEntry'); mustHide;
li.toggleClass('collapsed'); if ( which === '*' ) {
var key = 'collapseGroup' + li.nthOfType(); mustHide = doesHideAll === false;
if ( li.hasClass('collapsed') ) { groupSelector = '';
vAPI.localStorage.setItem(key, 'y'); hideUnusedSet.clear();
if ( mustHide ) {
hideUnusedSet.add(which);
}
document.body.classList.toggle('hideUnused', mustHide);
uDom('.groupEntry[data-groupkey]').toggleClass('hideUnused', mustHide);
} else { } else {
vAPI.localStorage.removeItem(key); var doesHide = hideUnusedSet.has(which);
if ( doesHide ) {
hideUnusedSet.delete(which);
} else {
hideUnusedSet.add(which);
}
mustHide = doesHide === doesHideAll;
groupSelector = '.groupEntry[data-groupkey="' + which + '"] ';
uDom(groupSelector).toggleClass('hideUnused', mustHide);
} }
uDom(groupSelector + '.listEntry > input[type="checkbox"]:not(:checked)')
.ancestors('.listEntry[data-listkey]')
.toggleClass('unused', mustHide);
vAPI.localStorage.setItem(
'hideUnusedFilterLists',
JSON.stringify(Array.from(hideUnusedSet))
);
}; };
var revealHiddenUsedLists = function() {
uDom('#lists .listEntry.unused > input[type="checkbox"]:checked')
.ancestors('.listEntry[data-listkey]')
.removeClass('unused');
};
uDom('#listsOfBlockedHostsPrompt').on('click', function() {
toggleHideUnusedLists('*');
});
uDom('#lists').on('click', '.groupEntry[data-groupkey] > .geDetails', function(ev) {
toggleHideUnusedLists(
uDom(ev.target)
.ancestors('.groupEntry[data-groupkey]')
.attr('data-groupkey')
);
});
(function() {
var aa;
try {
var json = vAPI.localStorage.getItem('hideUnusedFilterLists');
if ( json !== null ) {
aa = JSON.parse(json);
}
} catch (ex) {
}
if ( Array.isArray(aa) === false ) {
aa = [ '*' ];
}
hideUnusedSet = new Set(aa);
})();
/******************************************************************************/ /******************************************************************************/
// Cloud-related.
var toCloudData = function() { var toCloudData = function() {
var bin = { var bin = {
parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked, parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked,
ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked, ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked,
selectedLists: [], selectedLists: []
externalLists: listDetails.externalLists
}; };
var liEntries = uDom('#lists .listEntry'), liEntry; var liEntries = uDom('#lists .listEntry'), liEntry;
@ -537,7 +623,7 @@ var toCloudData = function() {
var fromCloudData = function(data, append) { var fromCloudData = function(data, append) {
if ( typeof data !== 'object' || data === null ) { return; } if ( typeof data !== 'object' || data === null ) { return; }
var elem, checked, i, n; var elem, checked;
elem = uDom.nodeFromId('parseCosmeticFilters'); elem = uDom.nodeFromId('parseCosmeticFilters');
checked = data.parseCosmeticFilters === true || append && elem.checked; checked = data.parseCosmeticFilters === true || append && elem.checked;
@ -549,19 +635,35 @@ var fromCloudData = function(data, append) {
var selectedSet = new Set(data.selectedLists), var selectedSet = new Set(data.selectedLists),
listEntries = uDom('#lists .listEntry'), listEntries = uDom('#lists .listEntry'),
listEntry, listKey, input; listEntry, listKey;
for ( i = 0, n = listEntries.length; i < n; i++ ) { for ( var i = 0, n = listEntries.length; i < n; i++ ) {
listEntry = listEntries.at(i); listEntry = listEntries.at(i);
listKey = listEntry.attr('data-listkey'); listKey = listEntry.attr('data-listkey');
input = listEntry.descendants('input').first(); var hasListKey = selectedSet.has(listKey);
selectedSet.delete(listKey);
var input = listEntry.descendants('input').first();
if ( append && input.prop('checked') ) { continue; } if ( append && input.prop('checked') ) { continue; }
input.prop('checked', selectedSet.has(listKey) ); input.prop('checked', hasListKey);
} }
elem = uDom.nodeFromId('externalLists'); // If there are URL-like list keys left in the selected set, import them.
if ( !append ) { elem.value = ''; } for ( listKey of selectedSet ) {
elem.value += data.externalLists || ''; if ( reValidExternalList.test(listKey) === false ) {
selectedSet.delete(listKey);
}
}
if ( selectedSet.size !== 0 ) {
elem = uDom.nodeFromId('externalLists');
if ( append ) {
if ( elem.value.trim() !== '' ) { elem.value += '\n'; }
} else {
elem.value = '';
}
elem.value += Array.from(selectedSet).join('\n');
uDom.nodeFromId('importLists').checked = true;
}
revealHiddenUsedLists();
renderWidgets(); renderWidgets();
}; };
@ -570,24 +672,19 @@ self.cloud.onPull = fromCloudData;
/******************************************************************************/ /******************************************************************************/
document.body.classList.toggle(
'hideUnused',
vAPI.localStorage.getItem('hideUnusedFilterLists') === '1'
);
uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged); uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged); uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged); uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged);
uDom('#buttonApply').on('click', buttonApplyHandler); 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('#listsOfBlockedHostsPrompt').on('click', toggleUnusedLists);
uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler);
uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged); uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged);
uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalList); uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalList);
uDom('#lists').on('click', 'span.cache', onPurgeClicked); uDom('#lists').on('click', 'span.cache', onPurgeClicked);
uDom('#externalLists').on('input', onFilteringSettingsChanged); uDom('#externalLists').on('input', onFilteringSettingsChanged);
/******************************************************************************/
renderFilterLists(); renderFilterLists();
/******************************************************************************/ /******************************************************************************/