3rd-party filters pane revisited

This commit is contained in:
gorhill 2017-01-22 16:05:16 -05:00
parent 20ebe6d18f
commit 9309df4196
10 changed files with 394 additions and 282 deletions

View File

@ -30,7 +30,8 @@
"contentURL": [ "contentURL": [
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt",
"assets/ublock/filters.txt" "assets/ublock/filters.txt"
] ],
"supportURL": "https://github.com/uBlockOrigin/uAssets/issues"
}, },
"ublock-badware": { "ublock-badware": {
"content": "filters", "content": "filters",
@ -196,7 +197,11 @@
"group": "social", "group": "social",
"off": true, "off": true,
"title": "Anti-ThirdpartySocial (see warning inside list)", "title": "Anti-ThirdpartySocial (see warning inside list)",
"contentURL": "https://www.fanboy.co.nz/fanboy-antifacebook.txt", "contentURL": [
"https://easylist.to/easylist/fanboy-social.txt",
"https://fanboy.co.nz/fanboy-antifacebook.txt",
"https://easylist-downloads.adblockplus.org/fanboy-social.txt"
],
"supportURL": "https://forums.lanik.us/" "supportURL": "https://forums.lanik.us/"
}, },
"fanboy-annoyance": { "fanboy-annoyance": {
@ -206,6 +211,7 @@
"title": "Fanboys Annoyance List", "title": "Fanboys Annoyance List",
"contentURL": [ "contentURL": [
"https://easylist.to/easylist/fanboy-annoyance.txt", "https://easylist.to/easylist/fanboy-annoyance.txt",
"https://fanboy.co.nz/fanboy-annoyance.txt",
"https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt" "https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt"
], ],
"supportURL": "https://forums.lanik.us/" "supportURL": "https://forums.lanik.us/"
@ -217,6 +223,7 @@
"title": "Fanboys Social Blocking List", "title": "Fanboys Social Blocking List",
"contentURL": [ "contentURL": [
"https://easylist.to/easylist/fanboy-social.txt", "https://easylist.to/easylist/fanboy-social.txt",
"https://fanboy.co.nz/fanboy-social.txt",
"https://easylist-downloads.adblockplus.org/fanboy-social.txt" "https://easylist-downloads.adblockplus.org/fanboy-social.txt"
], ],
"supportURL": "https://forums.lanik.us/" "supportURL": "https://forums.lanik.us/"

View File

@ -21,12 +21,10 @@
<button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button> <button id="buttonPurgeAll" class="custom disabled" data-i18n="3pPurgeAll"></button>
<li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label><button class="whatisthis"></button> <li><input type="checkbox" id="parseCosmeticFilters"><label data-i18n="3pParseAllABPHideFiltersPrompt1" for="parseCosmeticFilters"></label><button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div> <div class="whatisthis-expandable para" data-i18n="3pParseAllABPHideFiltersInfo"></div>
<ul style="padding-left: 2em;">
<li><input type="checkbox" id="ignoreGenericCosmeticFilters"><label data-i18n="3pIgnoreGenericCosmeticFilters" for="ignoreGenericCosmeticFilters"></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="3pIgnoreGenericCosmeticFiltersInfo"></div> <div class="whatisthis-expandable para" data-i18n="3pIgnoreGenericCosmeticFiltersInfo"></div>
</ul> </ul>
<li><p id="listsOfBlockedHostsPrompt"></p> <p><span id="listsOfBlockedHostsPrompt"></span></p>
</ul>
<ul id="lists"></ul> <ul id="lists"></ul>
</div> </div>
@ -34,10 +32,7 @@
<p> <p>
<span data-i18n="3pExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></span> <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> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">&#xf05a;</a>
</p>
<p style="margin: 0.25em 0 0 0">
<textarea id="externalLists" dir="ltr" spellcheck="false"></textarea> <textarea id="externalLists" dir="ltr" spellcheck="false"></textarea>
<button id="externalListsApply" class="custom important" disabled="true" data-i18n="3pExternalListsApply"></button></p>
</div> </div>
<div id="templates" style="display: none;"> <div id="templates" style="display: none;">
@ -46,14 +41,16 @@
<ul class="listEntries"></ul> <ul class="listEntries"></ul>
</li> </li>
<li class="listEntry"> <li class="listEntry">
<input type="checkbox"> <input type="checkbox"><!--
<a type="text/plain" target="_blank" href=""></a> --><a class="content" type="text/plain" target="_blank" href=""></a><!--
<a class="fa" style="display: none;" target="_blank">&#xf05a;</a> --><a class="fa support" href="" target="_blank">&#xf015;</a><!--
<a href="" style="display: none;" target="_blank"></a>: <!-- --><a class="fa remove" href="">&#xf00d;</a><!--
--><a class="fa mustread" href="" target="_blank">&#xf05a;</a><!--
--><span class="fa status unsecure" title="http">&#xf13e;</span><!--
--><span class="counts dim"></span><!-- --><span class="counts dim"></span><!--
--><span class="status unsecure">http</span><!-- --><span class="fa status failed">&#xf06a;</span><!--
--><span class="status obsolete" data-i18n="3pExternalListObsolete"></span><!-- --><span class="fa status obsolete" title="3pExternalListObsolete">&#xf071;</span><!--
--><span class="status purge" data-i18n="3pExternalListPurge"></span><!-- --><span class="fa status cache">&#xf017;</span><!--
--><span class="fa status updating">&#xf110;</span> --><span class="fa status updating">&#xf110;</span>
</li> </li>
</ul> </ul>

View File

@ -9,14 +9,24 @@ ul {
#options li { #options li {
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
#listsOfBlockedHostsPrompt {
cursor: pointer;
}
#listsOfBlockedHostsPrompt:before {
color: #aaa;
content: '\2212 ';
}
body.hideUnused #listsOfBlockedHostsPrompt:before {
content: '+ ';
}
#lists { #lists {
margin: 0.5em 0 0 0; margin: 0.5em 0 0 0;
padding-left: 1em; padding-left: 0.5em;
padding-right: 0em; padding-right: 0em;
} }
body[dir=rtl] #lists { body[dir=rtl] #lists {
padding-left: 0em; padding-left: 0em;
padding-right: 1em; padding-right: 0.5em;
} }
#lists > li { #lists > li {
margin: 0.5em 0 0 0; margin: 0.5em 0 0 0;
@ -45,8 +55,9 @@ body[dir=rtl] #lists {
display: none; display: none;
} }
li.listEntry { li.listEntry {
line-height: 150%;
margin: 0 auto 0 auto; margin: 0 auto 0 auto;
margin-left: 3em; margin-left: 2.5em;
margin-right: 0em; margin-right: 0em;
text-indent: -2em; text-indent: -2em;
} }
@ -55,26 +66,51 @@ body[dir=rtl] li.listEntry {
margin-right: 1em; margin-right: 1em;
} }
li.listEntry > * { li.listEntry > * {
margin-right: 0.5em;
text-indent: 0; text-indent: 0;
unicode-bidi: embed; unicode-bidi: embed;
} }
li.listEntry > a:nth-of-type(2) { li.listEntry.toRemove > input[type="checkbox"] {
font-size: 16px; visibility: hidden;
opacity: 0.7;
} }
li.listEntry > a:nth-of-type(2), li.listEntry.toRemove > a.content {
li.listEntry > a:nth-of-type(2):visited { text-decoration: line-through;
color: mediumblue;
} }
li.listEntry > a:nth-of-type(2):hover { li.listEntry > .fa {
color: inherit;
display: none;
font-size: 110%;
opacity: 0.5;
vertical-align: baseline;
}
li.listEntry > a.fa:hover {
opacity: 1; opacity: 1;
} }
li.listEntry > a:nth-of-type(3) { li.listEntry.support > a.support {
display: inline-block;
}
li.listEntry > a.remove,
li.listEntry > a.remove:visited {
color: darkred;
}
li.listEntry.external > a.remove {
display: inline-block;
}
li.listEntry.mustread > a.mustread {
display: inline-block;
}
li.listEntry.mustread > a.mustread:hover {
color: mediumblue;
}
li.listEntry > .counts {
display: none;
font-size: smaller; font-size: smaller;
opacity: 0.5; }
li.listEntry > input[type="checkbox"]:checked ~ .counts {
display: inline;
} }
.dim { .dim {
opacity: 0.5; opacity: 0.6;
} }
#buttonApply { #buttonApply {
display: initial; display: initial;
@ -89,42 +125,42 @@ body[dir=rtl] #buttonApply {
#buttonApply.disabled { #buttonApply.disabled {
display: none; display: none;
} }
span.status { li.listEntry span.status {
border: 1px solid transparent;
color: #444; color: #444;
cursor: default;
display: none; display: none;
font-size: smaller;
line-height: 1;
margin: 0 0 0 0.5em;
opacity: 0.8;
padding: 1px 2px;
} }
span.unsecure { li.listEntry span.status:hover {
background-color: hsl(0, 100%, 88%);
border-color: hsl(0, 100%, 83%);
}
li.listEntry.unsecure span.unsecure {
display: inline;
}
span.obsolete {
background-color: hsl(36, 100%, 80%);
border-color: hsl(36, 100%, 75%);
}
li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.obsolete {
display: inline;
}
span.purge {
border-color: #ddd;
background-color: #eee;
cursor: pointer;
}
span.purge:hover {
opacity: 1; opacity: 1;
} }
li.listEntry.cached span.purge { li.listEntry span.status.fa {
display: inline; /* font-size: 110%; */
} }
span.updating { li.listEntry span.unsecure {
color: darkred;
}
li.listEntry.unsecure > input[type="checkbox"]:checked ~ span.unsecure {
display: inline-block;
}
li.listEntry span.failed {
color: darkred;
}
li.listEntry.failed span.failed {
display: inline-block;
}
li.listEntry span.cache {
cursor: pointer;
}
li.listEntry.cached:not(.updating):not(.obsolete) > input[type="checkbox"]:checked ~ span.cache {
display: inline-block;
}
li.listEntry span.obsolete {
color: hsl(36, 100%, 40%);
}
li.listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked ~ span.obsolete {
display: inline-block;
}
li.listEntry span.updating {
border: none; border: none;
padding: 0; padding: 0;
} }
@ -133,14 +169,15 @@ li.listEntry.updating span.updating {
display: inline-block; display: inline-block;
} }
#externalListsDiv { #externalListsDiv {
margin: 2em auto 0 2em; margin: 1.5em auto 0 1.5em;
} }
body[dir=rtl] #externalListsDiv { body[dir=rtl] #externalListsDiv {
margin: 2em 2em 0; margin: 1.5em 1.5em 0 auto;
} }
#externalLists { #externalLists {
box-sizing: border-box; box-sizing: border-box;
height: 10em; height: 8em;
margin-top: 0.25em;
white-space: pre; white-space: pre;
width: 100%; width: 100%;
word-wrap: normal; word-wrap: normal;

View File

@ -31,7 +31,7 @@
var listDetails = {}, var listDetails = {},
filteringSettingsHash = '', filteringSettingsHash = '',
externalLists = ''; lastUpdateTemplateString = vAPI.i18n('3pLastUpdate');
/******************************************************************************/ /******************************************************************************/
@ -41,10 +41,6 @@ var onMessage = function(msg) {
updateAssetStatus(msg); updateAssetStatus(msg);
break; break;
case 'staticFilteringDataChanged': case 'staticFilteringDataChanged':
filteringSettingsHash = [
msg.parseCosmeticFilters,
msg.ignoreGenericCosmeticFilters
].concat(msg.listKeys.sort()).join();
renderFilterLists(); renderFilterLists();
break; break;
default: default:
@ -63,12 +59,12 @@ var renderNumber = function(value) {
/******************************************************************************/ /******************************************************************************/
var renderFilterLists = function(first) { var renderFilterLists = function(soft) {
var listGroupTemplate = uDom('#templates .groupEntry'), var listGroupTemplate = uDom('#templates .groupEntry'),
listEntryTemplate = uDom('#templates .listEntry'), listEntryTemplate = uDom('#templates .listEntry'),
listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'), listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'),
renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString, renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
lastUpdateString = vAPI.i18n('3pLastUpdate'); hideUnusedLists = document.body.classList.contains('hideUnused');
// Assemble a pretty list name if possible // Assemble a pretty list name if possible
var listNameFromListKey = function(listKey) { var listNameFromListKey = function(listKey) {
@ -92,46 +88,55 @@ var renderFilterLists = function(first) {
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');
elem.textContent = listNameFromListKey(listKey) + '\u200E'; elem.textContent = listNameFromListKey(listKey) + '\u200E';
elem = li.querySelector('a:nth-of-type(2)');
if ( entry.instructionURL ) {
elem.setAttribute('href', entry.instructionURL);
elem.style.setProperty('display', '');
} else {
elem.style.setProperty('display', 'none');
}
elem = li.querySelector('a:nth-of-type(3)');
if ( entry.supportName ) { if ( entry.supportName ) {
li.classList.add('support');
elem = li.querySelector('a.support');
elem.setAttribute('href', entry.supportURL); elem.setAttribute('href', entry.supportURL);
elem.textContent = '(' + entry.supportName + ')'; elem.setAttribute('title', entry.supportName);
elem.style.setProperty('display', '');
} else { } else {
elem.style.setProperty('display', 'none'); li.classList.remove('support');
}
if ( entry.external ) {
li.classList.add('external');
} else {
li.classList.remove('external');
}
if ( entry.instructionURL ) {
li.classList.add('mustread');
elem = li.querySelector('a.mustread');
elem.setAttribute('href', entry.instructionURL);
} else {
li.classList.remove('mustread');
} }
} }
// https://github.com/gorhill/uBlock/issues/1429
if ( !soft ) {
elem = li.querySelector('input[type="checkbox"]');
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 = listStatsTemplate var text = '';
.replace('{{used}}', renderNumber(!entry.off && !isNaN(+entry.entryUsedCount) ? entry.entryUsedCount : 0)) if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
.replace('{{total}}', !isNaN(+entry.entryCount) ? renderNumber(entry.entryCount) : '?'); text = listStatsTemplate
.replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
.replace('{{total}}', renderNumber(entry.entryCount));
}
elem.textContent = text; elem.textContent = text;
// https://github.com/chrisaljoudi/uBlock/issues/104 // https://github.com/chrisaljoudi/uBlock/issues/104
var asset = listDetails.cache[listKey] || {}; var asset = listDetails.cache[listKey] || {};
// https://github.com/gorhill/uBlock/issues/78
// Badge for non-secure connection
var remoteURL = asset.remoteURL; var remoteURL = asset.remoteURL;
li.classList.toggle( li.classList.toggle(
'unsecure', 'unsecure',
typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0 typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
); );
// Badge for update status li.classList.toggle('failed', asset.error !== undefined);
li.classList.toggle('obsolete', entry.off !== true && asset.obsolete === true); li.classList.toggle('obsolete', asset.obsolete === true);
// Badge for cache status
li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0); li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
if ( asset.cached ) { if ( asset.cached ) {
li.querySelector('.status.purge').setAttribute( li.querySelector('.status.cache').setAttribute(
'title', 'title',
lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime)) lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
); );
} }
li.classList.remove('updating'); li.classList.remove('updating');
@ -237,7 +242,6 @@ var renderFilterLists = function(first) {
} }
uDom('#lists .listEntries .listEntry.discard').remove(); uDom('#lists .listEntries .listEntry.discard').remove();
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null);
uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
uDom('#listsOfBlockedHostsPrompt').text( uDom('#listsOfBlockedHostsPrompt').text(
vAPI.i18n('3pListsOfBlockedHostsPrompt') vAPI.i18n('3pListsOfBlockedHostsPrompt')
@ -247,9 +251,9 @@ var renderFilterLists = function(first) {
// 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.
if ( first ) {
uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true);
uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true);
if ( !soft ) {
filteringSettingsHash = hashFromCurrentFromSettings(); filteringSettingsHash = hashFromCurrentFromSettings();
} }
renderWidgets(); renderWidgets();
@ -265,16 +269,27 @@ var renderFilterLists = function(first) {
var renderWidgets = function() { var renderWidgets = function() {
uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings()); uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings());
uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null); uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete > input[type="checkbox"]:checked') === null); uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked') === null);
}; };
/******************************************************************************/ /******************************************************************************/
var updateAssetStatus = function(details) { var updateAssetStatus = function(details) {
var li = uDom('#lists .listEntry[data-listkey="' + details.key + '"]'); var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
li.toggleClass('obsolete', !details.cached); if ( li === null ) { return; }
li.toggleClass('cached', details.cached); li.classList.toggle('failed', !!details.failed);
li.removeClass('updating'); li.classList.toggle('obsolete', !details.cached);
li.classList.toggle('cached', !!details.cached);
li.classList.remove('updating');
if ( details.cached ) {
li.querySelector('.status.cache').setAttribute(
'title',
lastUpdateTemplateString.replace(
'{{ago}}',
vAPI.i18n.renderElapsedTimeToString(Date.now())
)
);
}
renderWidgets(); renderWidgets();
}; };
@ -291,7 +306,7 @@ var hashFromCurrentFromSettings = function() {
document.getElementById('ignoreGenericCosmeticFilters').checked document.getElementById('ignoreGenericCosmeticFilters').checked
]; ];
var listHash = [], var listHash = [],
listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'), listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
liEntry, liEntry,
i = listEntries.length; i = listEntries.length;
while ( i-- ) { while ( i-- ) {
@ -300,7 +315,9 @@ var hashFromCurrentFromSettings = function() {
listHash.push(liEntry.getAttribute('data-listkey')); listHash.push(liEntry.getAttribute('data-listkey'));
} }
} }
return hash.concat(listHash.sort()).join(); hash.push(listHash.sort().join());
hash.push(document.getElementById('externalLists').value.trim());
return hash.join();
}; };
/******************************************************************************/ /******************************************************************************/
@ -311,6 +328,18 @@ var onFilteringSettingsChanged = function() {
/******************************************************************************/ /******************************************************************************/
var onRemoveExternalList = function(ev) {
var liEntry = uDom(this).ancestors('[data-listkey]'),
listKey = liEntry.attr('data-listkey');
if ( listKey ) {
liEntry.toggleClass('toRemove');
renderWidgets();
}
ev.preventDefault();
};
/******************************************************************************/
var onPurgeClicked = function() { var onPurgeClicked = function() {
var button = uDom(this), var button = uDom(this),
liEntry = button.ancestors('[data-listkey]'), liEntry = button.ancestors('[data-listkey]'),
@ -322,13 +351,9 @@ var onPurgeClicked = function() {
// If the cached version is purged, the installed version must be assumed // If the cached version is purged, the installed version must be assumed
// to be obsolete. // to be obsolete.
// https://github.com/gorhill/uBlock/issues/1733 // https://github.com/gorhill/uBlock/issues/1733
// An external filter list must not be marked as obsolete, they will always // An external filter list must not be marked as obsolete, they will
// be fetched anyways if there is no cached copy. // always be fetched anyways if there is no cached copy.
var entry = listDetails.current && listDetails.current[listKey];
if ( entry && entry.off !== true ) {
liEntry.addClass('obsolete'); liEntry.addClass('obsolete');
uDom('#buttonUpdate').removeClass('disabled');
}
liEntry.removeClass('cached'); liEntry.removeClass('cached');
if ( liEntry.descendants('input').first().prop('checked') ) { if ( liEntry.descendants('input').first().prop('checked') ) {
@ -351,24 +376,41 @@ var selectFilterLists = function(callback) {
value: document.getElementById('ignoreGenericCosmeticFilters').checked value: document.getElementById('ignoreGenericCosmeticFilters').checked
}); });
// Filter lists // Filter lists to select
var listKeys = [], var toSelect = [],
liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'), liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
i = liEntries.length, i = liEntries.length,
liEntry; liEntry;
while ( i-- ) { while ( i-- ) {
liEntry = liEntries[i]; liEntry = liEntries[i];
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) { if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
listKeys.push(liEntry.getAttribute('data-listkey')); toSelect.push(liEntry.getAttribute('data-listkey'));
} }
} }
// External filter lists to remove
var toRemove = [];
liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
i = liEntries.length;
while ( i-- ) {
toRemove.push(liEntries[i].getAttribute('data-listkey'));
}
// External filter lists to import
var externalListsElem = document.getElementById('externalLists'),
toImport = externalListsElem.value.trim();
externalListsElem.value = '';
messaging.send( messaging.send(
'dashboard', 'dashboard',
{ what: 'selectFilterLists', keys: listKeys }, {
what: 'applyFilterListSelection',
toSelect: toSelect,
toImport: toImport,
toRemove: toRemove
},
callback callback
); );
filteringSettingsHash = hashFromCurrentFromSettings(); filteringSettingsHash = hashFromCurrentFromSettings();
}; };
@ -387,8 +429,11 @@ var buttonApplyHandler = function() {
var buttonUpdateHandler = function() { var buttonUpdateHandler = function() {
var onSelectionDone = function() { var onSelectionDone = function() {
uDom('#lists .listEntry.obsolete').addClass('updating'); uDom('#lists .listEntry.obsolete > input[type="checkbox"]:checked')
.ancestors('.listEntry[data-listkey]')
.addClass('updating');
messaging.send('dashboard', { what: 'forceUpdateAssets' }); messaging.send('dashboard', { what: 'forceUpdateAssets' });
renderWidgets();
}; };
selectFilterLists(onSelectionDone); selectFilterLists(onSelectionDone);
renderWidgets(); renderWidgets();
@ -404,7 +449,7 @@ var buttonPurgeAllHandler = function(ev) {
what: 'purgeAllCaches', what: 'purgeAllCaches',
hard: ev.ctrlKey && ev.shiftKey hard: ev.ctrlKey && ev.shiftKey
}, },
renderFilterLists function() { renderFilterLists(true); }
); );
}; };
@ -423,39 +468,13 @@ var autoUpdateCheckboxChanged = function() {
/******************************************************************************/ /******************************************************************************/
var renderExternalLists = function() { var toggleUnusedLists = function() {
var onReceived = function(details) { document.body.classList.toggle('hideUnused');
uDom('#externalLists').val(details); var hide = document.body.classList.contains('hideUnused');
externalLists = details; uDom('#lists li.listEntry > input[type="checkbox"]:not(:checked)')
}; .ancestors('li.listEntry[data-listkey]')
messaging.send( .css('display', hide ? 'none' : '');
'dashboard', vAPI.localStorage.setItem('hideUnusedFilterLists', hide ? '1' : '0');
{ what: 'userSettings', name: 'externalLists' },
onReceived
);
};
/******************************************************************************/
var externalListsChangeHandler = function() {
uDom.nodeFromId('externalListsApply').disabled =
uDom.nodeFromId('externalLists').value.trim() === externalLists.trim();
};
/******************************************************************************/
var externalListsApplyHandler = function() {
externalLists = uDom.nodeFromId('externalLists').value;
messaging.send(
'dashboard',
{
what: 'userSettings',
name: 'externalLists',
value: externalLists
}
);
renderFilterLists();
uDom('#externalListsApply').prop('disabled', true);
}; };
/******************************************************************************/ /******************************************************************************/
@ -478,7 +497,7 @@ var toCloudData = function() {
parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked, parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked,
ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked, ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked,
selectedLists: [], selectedLists: [],
externalLists: externalLists externalLists: listDetails.externalLists
}; };
var liEntries = uDom('#lists .listEntry'), liEntry; var liEntries = uDom('#lists .listEntry'), liEntry;
@ -529,7 +548,6 @@ var fromCloudData = function(data, append) {
elem.value += data.externalLists || ''; elem.value += data.externalLists || '';
renderWidgets(); renderWidgets();
externalListsChangeHandler();
}; };
self.cloud.onPush = toCloudData; self.cloud.onPush = toCloudData;
@ -537,20 +555,25 @@ 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('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged); uDom('#listsOfBlockedHostsPrompt').on('click', toggleUnusedLists);
uDom('#lists').on('click', 'span.purge', onPurgeClicked);
uDom('#externalLists').on('input', externalListsChangeHandler);
uDom('#externalListsApply').on('click', externalListsApplyHandler);
uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler); uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler);
uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged);
uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalList);
uDom('#lists').on('click', 'span.cache', onPurgeClicked);
uDom('#externalLists').on('input', onFilteringSettingsChanged);
renderFilterLists(true); renderFilterLists();
renderExternalLists();
/******************************************************************************/ /******************************************************************************/

View File

@ -96,7 +96,7 @@ var getTextFileFromURL = function(url, onLoad, onError) {
var onErrorReceived = function() { var onErrorReceived = function() {
this.onload = this.onerror = this.ontimeout = null; this.onload = this.onerror = this.ontimeout = null;
µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', '')); µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', url));
onError.call(this); onError.call(this);
}; };
@ -805,7 +805,11 @@ var getRemote = function(assetKey, callback) {
}; };
var onRemoteContentError = function() { var onRemoteContentError = function() {
registerAssetSource(assetKey, { error: { time: Date.now(), error: this.statusText } }); var text = this.statusText;
if ( this.status === 0 ) {
text = 'network error';
}
registerAssetSource(assetKey, { error: { time: Date.now(), error: text } });
tryLoading(); tryLoading();
}; };
@ -948,6 +952,8 @@ var updateNext = function() {
if ( details.assetKey === 'assets.json' ) { if ( details.assetKey === 'assets.json' ) {
updateAssetSourceRegistry(details.content); updateAssetSourceRegistry(details.content);
} }
} else {
fireNotification('asset-update-failed', { assetKey: details.assetKey });
} }
if ( findOne() !== undefined ) { if ( findOne() !== undefined ) {
vAPI.setTimeout(updateNext, updaterAssetDelay); vAPI.setTimeout(updateNext, updaterAssetDelay);

View File

@ -127,6 +127,7 @@ return {
userFiltersPath: 'user-filters', userFiltersPath: 'user-filters',
pslAssetKey: 'public_suffix_list.dat', pslAssetKey: 'public_suffix_list.dat',
selectedFilterLists: [],
availableFilterLists: {}, availableFilterLists: {},
selfieAfter: 23 * oneMinute, selfieAfter: 23 * oneMinute,

View File

@ -94,10 +94,8 @@ var onMessage = function(request, sender, callback) {
var response; var response;
switch ( request.what ) { switch ( request.what ) {
case 'mouseClick': case 'applyFilterListSelection':
µb.mouseX = request.x; response = µb.applyFilterListSelection(request);
µb.mouseY = request.y;
µb.mouseURL = request.url;
break; break;
case 'compileCosmeticFilterSelector': case 'compileCosmeticFilterSelector':
@ -147,6 +145,12 @@ var onMessage = function(request, sender, callback) {
µb.openNewTab(request.details); µb.openNewTab(request.details);
break; break;
case 'mouseClick':
µb.mouseX = request.x;
µb.mouseY = request.y;
µb.mouseURL = request.url;
break;
case 'reloadTab': case 'reloadTab':
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) { if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
vAPI.tabs.reload(request.tabId); vAPI.tabs.reload(request.tabId);
@ -160,10 +164,6 @@ var onMessage = function(request, sender, callback) {
µb.scriptlets.report(tabId, request.scriptlet, request.response); µb.scriptlets.report(tabId, request.scriptlet, request.response);
break; break;
case 'selectFilterLists':
µb.saveSelectedFilterLists(request.keys, request.append);
break;
case 'setWhitelist': case 'setWhitelist':
µb.netWhitelist = µb.whitelistFromString(request.whitelist); µb.netWhitelist = µb.whitelistFromString(request.whitelist);
µb.saveWhitelist(); µb.saveWhitelist();
@ -873,11 +873,12 @@ var getLists = function(callback) {
autoUpdate: µb.userSettings.autoUpdate, autoUpdate: µb.userSettings.autoUpdate,
available: null, available: null,
cache: null, cache: null,
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(), cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(),
current: µb.availableFilterLists, current: µb.availableFilterLists,
externalLists: µb.userSettings.externalLists,
ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters, ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters,
netFilterCount: µb.staticNetFilteringEngine.getFilterCount(), netFilterCount: µb.staticNetFilteringEngine.getFilterCount(),
parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters,
userFiltersPath: µb.userFiltersPath, userFiltersPath: µb.userFiltersPath,
aliases: µb.assets.listKeyAliases aliases: µb.assets.listKeyAliases
}; };
@ -1287,8 +1288,7 @@ var onMessage = function(request, sender, callback) {
case 'subscriberData': case 'subscriberData':
response = { response = {
confirmStr: vAPI.i18n('subscriberConfirm'), confirmStr: vAPI.i18n('subscriberConfirm')
externalLists: µBlock.userSettings.externalLists
}; };
break; break;

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global vAPI, HTMLDocument */ /* global HTMLDocument */
'use strict'; 'use strict';
@ -79,9 +79,7 @@ var onAbpLinkClicked = function(ev) {
var matches = /^abp:\/*subscribe\/*\?location=([^&]+).*title=([^&]+)/.exec(href); var matches = /^abp:\/*subscribe\/*\?location=([^&]+).*title=([^&]+)/.exec(href);
if ( matches === null ) { if ( matches === null ) {
matches = /^https?:\/\/.*?[&?]location=([^&]+).*?&title=([^&]+)/.exec(href); matches = /^https?:\/\/.*?[&?]location=([^&]+).*?&title=([^&]+)/.exec(href);
if ( matches === null ) { if ( matches === null ) { return; }
return;
}
} }
var location = decodeURIComponent(matches[1]); var location = decodeURIComponent(matches[1]);
@ -95,44 +93,18 @@ var onAbpLinkClicked = function(ev) {
messaging.send('scriptlets', { what: 'reloadAllFilters' }); messaging.send('scriptlets', { what: 'reloadAllFilters' });
}; };
var onExternalListsSaved = function() {
messaging.send(
'scriptlets',
{
what: 'selectFilterLists',
keys: [ location ],
append: true
},
onListsSelectionDone
);
};
var onSubscriberDataReady = function(details) { var onSubscriberDataReady = function(details) {
var confirmStr = details.confirmStr var confirmStr = details.confirmStr
.replace('{{url}}', location) .replace('{{url}}', location)
.replace('{{title}}', title); .replace('{{title}}', title);
if ( !window.confirm(confirmStr) ) { if ( !window.confirm(confirmStr) ) { return; }
return;
}
// List already subscribed to?
// https://github.com/chrisaljoudi/uBlock/issues/1033
// Split on line separators, not whitespaces.
var text = details.externalLists.trim();
var lines = text !== '' ? text.split(/\s*[\n\r]+\s*/) : [];
if ( lines.indexOf(location) !== -1 ) {
return;
}
lines.push(location, '');
messaging.send( messaging.send(
'scriptlets', 'scriptlets',
{ {
what: 'userSettings', what: 'applyFilterListSelection',
name: 'externalLists', toImport: location
value: lines.join('\n')
}, },
onExternalListsSaved onListsSelectionDone
); );
}; };

View File

@ -186,28 +186,33 @@
// Uncomment when all have moved to v1.11 and beyond. // Uncomment when all have moved to v1.11 and beyond.
//vAPI.storage.remove('remoteBlacklists'); //vAPI.storage.remove('remoteBlacklists');
} }
µb.selectedFilterLists = listKeys.slice();
callback(listKeys); callback(listKeys);
}); });
}; };
µBlock.saveSelectedFilterLists = function(listKeys, append) { µBlock.saveSelectedFilterLists = function(newKeys, append) {
var µb = this; var µb = this;
var save = function(keys) { this.loadSelectedFilterLists(function(oldKeys) {
var uniqueKeys = µb.setToArray(new Set(keys)); oldKeys = oldKeys || [];
var bin = {
selectedFilterLists: uniqueKeys,
remoteBlacklists: µb.oldDataFromNewListKeys(uniqueKeys)
};
vAPI.storage.set(bin);
};
if ( append ) { if ( append ) {
this.loadSelectedFilterLists(function(keys) { newKeys = newKeys.concat(oldKeys);
listKeys = listKeys.concat(keys || []);
save(listKeys);
});
} else {
save(listKeys);
} }
var newSet = new Set(newKeys);
// Purge unused filter lists from cache.
for ( var i = 0, n = oldKeys.length; i < n; i++ ) {
if ( newSet.has(oldKeys[i]) === false ) {
µb.removeFilterList(oldKeys[i]);
}
}
newKeys = µb.setToArray(newSet);
var bin = {
selectedFilterLists: newKeys,
remoteBlacklists: µb.oldDataFromNewListKeys(newKeys)
};
µb.selectedFilterLists = newKeys;
vAPI.storage.set(bin);
});
}; };
// TODO(seamless migration): // TODO(seamless migration):
@ -262,6 +267,113 @@
/******************************************************************************/ /******************************************************************************/
µBlock.applyFilterListSelection = function(details, callback) {
var µb = this,
selectedListKeySet = new Set(this.selectedFilterLists),
externalLists = this.userSettings.externalLists,
i, n, assetKey;
// Filter lists to select
if ( Array.isArray(details.toSelect) ) {
if ( details.merge ) {
for ( i = 0, n = details.toSelect.length; i < n; i++ ) {
selectedListKeySet.add(details.toSelect[i]);
}
} else {
selectedListKeySet = new Set(details.toSelect);
}
}
// Imported filter lists to remove
if ( Array.isArray(details.toRemove) ) {
var removeURLFromHaystack = function(haystack, needle) {
return haystack.replace(
new RegExp(
'(^|\\n)' +
needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
'(\\n|$)', 'g'),
'\n'
).trim();
};
for ( i = 0, n = details.toRemove.length; i < n; i++ ) {
assetKey = details.toRemove[i];
selectedListKeySet.delete(assetKey);
externalLists = removeURLFromHaystack(externalLists, assetKey);
this.removeFilterList(assetKey);
}
}
// Filter lists to import
if ( typeof details.toImport === 'string' ) {
// https://github.com/gorhill/uBlock/issues/1181
// Try mapping the URL of an imported filter list to the assetKey of an
// existing stock list.
var assetKeyFromURL = function(url) {
var needle = url.replace(/^https?:/, '');
var assets = µb.availableFilterLists, asset;
for ( var assetKey in assets ) {
asset = assets[assetKey];
if ( asset.content !== 'filters' ) { continue; }
if ( typeof asset.contentURL === 'string' ) {
if ( asset.contentURL.endsWith(needle) ) { return assetKey; }
continue;
}
if ( Array.isArray(asset.contentURL) === false ) { continue; }
for ( i = 0, n = asset.contentURL.length; i < n; i++ ) {
if ( asset.contentURL[i].endsWith(needle) ) {
return assetKey;
}
}
}
return url;
};
var importedSet = new Set(this.listKeysFromCustomFilterLists(externalLists)),
toImportSet = new Set(this.listKeysFromCustomFilterLists(details.toImport)),
iter = toImportSet.values();
for (;;) {
var entry = iter.next();
if ( entry.done ) { break; }
if ( importedSet.has(entry.value) ) { continue; }
assetKey = assetKeyFromURL(entry.value);
if ( assetKey === entry.value ) {
importedSet.add(entry.value);
}
selectedListKeySet.add(assetKey);
}
externalLists = this.setToArray(importedSet).sort().join('\n');
}
var result = this.setToArray(selectedListKeySet);
if ( externalLists !== this.userSettings.externalLists ) {
this.userSettings.externalLists = externalLists;
vAPI.storage.set({ externalLists: externalLists });
}
this.saveSelectedFilterLists(result);
if ( typeof callback === 'function' ) {
callback(result);
}
};
/******************************************************************************/
µBlock.listKeysFromCustomFilterLists = function(raw) {
var out = new Set(),
reIgnore = /^[!#]/,
reValid = /^[a-z-]+:\/\/\S+/,
lineIter = new this.LineIterator(raw),
location;
while ( lineIter.eot() === false ) {
location = lineIter.next().trim();
if ( reIgnore.test(location) || !reValid.test(location) ) {
continue;
}
out.add(location);
}
return this.setToArray(out);
};
/******************************************************************************/
µBlock.saveUserFilters = function(content, callback) { µBlock.saveUserFilters = function(content, callback) {
// https://github.com/gorhill/uBlock/issues/1022 // https://github.com/gorhill/uBlock/issues/1022
// Be sure to end with an empty line. // Be sure to end with an empty line.
@ -315,21 +427,6 @@
/******************************************************************************/ /******************************************************************************/
µBlock.listKeysFromCustomFilterLists = function(raw) {
var out = {};
var reIgnore = /^[!#]|[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/,
lineIter = new this.LineIterator(raw),
location;
while ( lineIter.eot() === false ) {
location = lineIter.next().trim();
if ( location === '' || reIgnore.test(location) ) { continue; }
out[location] = true;
}
return Object.keys(out);
};
/******************************************************************************/
µBlock.autoSelectRegionalFilterLists = function(lists) { µBlock.autoSelectRegionalFilterLists = function(lists) {
var lang = self.navigator.language.slice(0, 2), var lang = self.navigator.language.slice(0, 2),
selectedListKeys = [], selectedListKeys = [],
@ -351,41 +448,6 @@
/******************************************************************************/ /******************************************************************************/
µBlock.changeExternalFilterLists = function(before, after) {
var µb = µBlock;
var onLoaded = function(keys) {
var fullDict = new Set(keys || []),
mustSave = false,
oldKeys = µb.listKeysFromCustomFilterLists(before),
oldDict = new Set(oldKeys),
newKeys = µb.listKeysFromCustomFilterLists(after),
newDict = new Set(newKeys),
i, key;
i = oldKeys.length;
while ( i-- ) {
key = oldKeys[i];
if ( fullDict.has(key) && !newDict.has(key) ) {
fullDict.delete(key);
mustSave = true;
}
}
i = newKeys.length;
while ( i-- ) {
key = newKeys[i];
if ( !fullDict.has(key) && !oldDict.has(key) ) {
fullDict.add(key);
mustSave = true;
}
}
if ( mustSave ) {
µb.saveSelectedFilterLists(µb.setToArray(fullDict));
}
};
this.loadSelectedFilterLists(onLoaded);
};
/******************************************************************************/
µBlock.getAvailableLists = function(callback) { µBlock.getAvailableLists = function(callback) {
var µb = this, var µb = this,
oldAvailableLists = {}, oldAvailableLists = {},
@ -1006,7 +1068,7 @@
if ( topic === 'before-asset-updated' ) { if ( topic === 'before-asset-updated' ) {
if ( if (
this.availableFilterLists.hasOwnProperty(details.assetKey) && this.availableFilterLists.hasOwnProperty(details.assetKey) &&
this.availableFilterLists[details.assetKey].off === true this.selectedFilterLists.indexOf(details.assetKey) === -1
) { ) {
return false; return false;
} }
@ -1018,7 +1080,7 @@
var cached = typeof details.content === 'string' && details.content !== ''; var cached = typeof details.content === 'string' && details.content !== '';
if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) { if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) {
if ( cached ) { if ( cached ) {
if ( this.availableFilterLists[details.assetKey].off !== true ) { if ( this.selectedFilterLists.indexOf(details.assetKey) !== -1 ) {
this.extractFilterListMetadata( this.extractFilterListMetadata(
details.assetKey, details.assetKey,
details.content details.content
@ -1049,6 +1111,16 @@
return; return;
} }
// Update failed.
if ( topic === 'asset-update-failed' ) {
vAPI.messaging.broadcast({
what: 'assetUpdated',
key: details.assetKey,
failed: true
});
return;
}
// Reload all filter lists if needed. // Reload all filter lists if needed.
if ( topic === 'after-assets-updated' ) { if ( topic === 'after-assets-updated' ) {
if ( details.assetKeys.length !== 0 ) { if ( details.assetKeys.length !== 0 ) {

View File

@ -317,9 +317,6 @@ var reInvalidHostname = /[^a-z0-9.\-\[\]:]/,
// Pre-change // Pre-change
switch ( name ) { switch ( name ) {
case 'externalLists':
this.changeExternalFilterLists(us.externalLists, value);
break;
case 'largeMediaSize': case 'largeMediaSize':
if ( typeof value !== 'number' ) { if ( typeof value !== 'number' ) {
value = parseInt(value, 10) || 0; value = parseInt(value, 10) || 0;