this fixes #12, #37

This commit is contained in:
gorhill 2014-07-02 12:02:29 -04:00
parent be27b7aa55
commit fb62bbc29e
32 changed files with 1453 additions and 511 deletions

View File

@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HTTP Switchboard — Ubiquitous rules</title>
<title>µBlock — Your filters</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<style>
@ -33,7 +33,7 @@ div > p:last-child {
<p><button id="userFiltersApply" disabled="true" data-i18n="1pApplyChanges"></button></p>
</div>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script>

View File

@ -33,7 +33,7 @@ ul > li {
<body>
<p id="3pListsOfBlockedHostsPrompt2"></p>
<p id="listsOfBlockedHostsPrompt"></p>
<ul id="blacklistTemplate" style="display:none">
<li class="blacklistDetails"><input type="checkbox">&thinsp;<a href="" type="text/plain"></a>:
<span class="dim"></span>
@ -46,7 +46,7 @@ ul > li {
<li style="margin-top:0.75em"><button id="blacklistsApply" disabled="true" data-i18n="3pApplyChanges"></button>
</ul>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script>

View File

@ -19,6 +19,10 @@
"message": "Deine Filter",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "Über",
"description": "appears as tab name in dashboard"
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} eindeutig blockierte Hostnamen aus:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Enable the logging of blocked requests",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "No blocked requests logged for this page",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Changelog'>Changelog</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "About",
"description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} network filters from:",
"description": "English: {{ubiquitousBlacklistCount}} network filters from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Enable the logging of blocked requests",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "No blocked requests logged for this page",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Vos règles",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistiques",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "À propos",
"description": "appears as tab name in dashboard"
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} filtres actuellement en action :",
"description": "English: {{ubiquitousBlacklistCount}} network filters from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Activer le journal des requêtes bloquées",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "Il n'y a aucune requête bloquée dans le journal",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Journal des changements (en Anglais)</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "I tuoi filtri",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "Info",
"description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} reti filtrate da:",
"description": "English: {{ubiquitousBlacklistCount}} network filters from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Enable the logging of blocked requests",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "No blocked requests logged for this page",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "О расширении",
"description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} блокируется хостов, помимо:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Enable the logging of blocked requests",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "No blocked requests logged for this page",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Список изменений</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters",
"description": "appears as tab name in dashboard."
},
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": {
"message": "关于",
"description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
},
"3pListsOfBlockedHostsPrompt2" : {
"3pListsOfBlockedHostsPrompt" : {
"message": "共{{ubiquitousBlacklistCount}}个不同的屏蔽站点名,来自:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:"
},
@ -101,6 +105,20 @@
},
"logBlockedRequestsPrompt" : {
"message": "Enable the logging of blocked requests",
"description": "English: Enable the logging of blocked requests"
},
"logBlockedRequestsHelp" : {
"message": "You can inspect the details of blocked requests if you wish by enabling this option. The logging of blocked requests increases the memory footprint of µBlock. Since many users will never use this feature, it is disabled by default.",
"description": "English: see _locales/en/messages.log"
},
"logBlockedRequestsEmpty" : {
"message": "No blocked requests logged for this page",
"description": "English: No blocked requests logged for this page"
},
"aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>变更日志</a>",
"description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -81,7 +81,7 @@ table td:first-child {
</div>
</div>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script>

View File

@ -1,7 +1,7 @@
body {
margin: 0;
padding: 0 0.5em 5em 0.5em;
font: 15px sans-serif;
font: 14px sans-serif;
}
h2, h3 {
margin: 1em 0;

View File

@ -27,7 +27,7 @@ body {
#dashboard-nav-widgets {
margin: 0;
border-bottom: 1px solid #ccc;
padding: 4px 0 3px 0;
padding: 4px 0 4px 0;
white-space: nowrap;
background-color: white;
}
@ -76,16 +76,17 @@ iframe {
<div id="dashboard-nav">
<div id="dashboard-nav-widgets">
<span>µBlock</span>
<a class="tabButton" id="thirdparty-filters" href="#thirdparty-filters" data-dashboard-panel-url="3p-filters.html" data-i18n="3pPageName"></a>
<a class="tabButton" id="firstparty-filters" href="#firstparty-filters" data-dashboard-panel-url="1p-filters.html" data-i18n="1pPageName"></a>
<a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
<a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="3p-filters.html" data-i18n="3pPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="1p-filters.html" data-i18n="1pPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="stats.html" data-i18n="statsPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
</div>
</div>
<iframe src=""></iframe>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard.js"></script>
</body>

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, $ */
/* global chrome, messaging, uDom */
/******************************************************************************/
@ -37,11 +37,10 @@ messaging.start('1p-filters.js');
// This is to give a visual hint that the content of user blacklist has changed.
function userFiltersChanged() {
$('#userFiltersApply')
.attr(
function userFiltersChanged(ev) {
uDom('#userFiltersApply').prop(
'disabled',
$('#userFilters').val().trim() === cachedUserFilters
uDom('#userFilters').val().trim() === cachedUserFilters
);
}
@ -53,7 +52,7 @@ function renderUserFilters() {
return;
}
cachedUserFilters = details.content.trim();
$('#userFilters').val(details.content);
uDom('#userFilters').val(details.content);
};
messaging.ask({ what: 'readUserFilters' }, onRead);
}
@ -62,23 +61,22 @@ function renderUserFilters() {
function allFiltersApplyHandler() {
messaging.tell({ what: 'reloadAllFilters' });
$('#userFiltersApply').attr('disabled', true );
uDom('#userFiltersApply').prop('disabled', true );
}
/******************************************************************************/
function appendToUserFiltersFromFile() {
var input = $('<input />').attr({
var input = uDom('<input />').attr({
type: 'file',
accept: 'text/plain'
});
var fileReaderOnLoadHandler = function() {
var textarea = $('#userFilters');
textarea.val(textarea.val() + '\n' + this.result);
var textarea = uDom('#userFilters');
textarea.val([textarea.val(), this.result].join('\n').trim());
userFiltersChanged();
};
var filePickerOnChangeHandler = function() {
$(this).off('change', filePickerOnChangeHandler);
var file = this.files[0];
if ( !file ) {
return;
@ -99,7 +97,7 @@ function appendToUserFiltersFromFile() {
function exportUserFiltersToFile() {
chrome.downloads.download({
'url': 'data:text/plain,' + encodeURIComponent($('#userFilters').val()),
'url': 'data:text/plain,' + encodeURIComponent(uDom('#userFilters').val()),
'filename': 'my-ublock-filters.txt',
'saveAs': true
});
@ -118,19 +116,19 @@ function userFiltersApplyHandler() {
};
var request = {
what: 'writeUserFilters',
content: $('#userFilters').val()
content: uDom('#userFilters').val()
};
messaging.ask(request, onWritten);
}
/******************************************************************************/
$(function() {
uDom.onLoad(function() {
// Handle user interaction
$('#importUserFiltersFromFile').on('click', appendToUserFiltersFromFile);
$('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
$('#userFilters').on('input propertychange', userFiltersChanged);
$('#userFiltersApply').on('click', userFiltersApplyHandler);
uDom('#importUserFiltersFromFile').on('click', appendToUserFiltersFromFile);
uDom('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
uDom('#userFilters').on('input', userFiltersChanged);
uDom('#userFiltersApply').on('click', userFiltersApplyHandler);
renderUserFilters();
});

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, $ */
/* global chrome, messaging, uDom */
/******************************************************************************/
@ -28,8 +28,6 @@
/******************************************************************************/
var userListName = chrome.i18n.getMessage('1pPageName');
var cachedUserUbiquitousBlacklistedHosts = '';
var cachedUserUbiquitousWhitelistedHosts = '';
var selectedBlacklistsHash = '';
/******************************************************************************/
@ -77,12 +75,12 @@ function renderNumber(value) {
function renderBlacklists() {
// empty list first
$('#blacklists .blacklistDetails').remove();
uDom('#blacklists .blacklistDetails').remove();
var µb = getµb();
$('#3pListsOfBlockedHostsPrompt2').text(
chrome.i18n.getMessage('3pListsOfBlockedHostsPrompt2')
uDom('#listsOfBlockedHostsPrompt').text(
chrome.i18n.getMessage('3pListsOfBlockedHostsPrompt')
.replace('{{ubiquitousBlacklistCount}}', renderNumber(µb.abpFilters.getFilterCount()))
);
@ -119,31 +117,29 @@ function renderBlacklists() {
var listStatsTemplate = chrome.i18n.getMessage('3pListsOfBlockedHostsPerListStats');
var blacklists = µb.remoteBlacklists;
var ul = $('#blacklists');
var ul = uDom('#blacklists');
var keys = Object.keys(blacklists);
var i = keys.length;
var blacklist, blacklistHref;
var liTemplate = $('#blacklistTemplate .blacklistDetails').first();
var li, child, text;
var liTemplate = uDom('#blacklistTemplate .blacklistDetails').first();
var li, text;
while ( i-- ) {
blacklistHref = keys[i];
blacklist = blacklists[blacklistHref];
li = liTemplate.clone();
child = $('input', li);
child.prop('checked', !blacklist.off);
child = $('a', li);
child.attr('href', encodeURI(blacklistHref));
child.html(prettifyListName(blacklist.title, blacklistHref));
child = $('span', li);
li.find('input').prop('checked', !blacklist.off);
li.find('a')
.attr('href', encodeURI(blacklistHref))
.html(prettifyListName(blacklist.title, blacklistHref));
text = listStatsTemplate
.replace('{{used}}', !blacklist.off && !isNaN(+blacklist.entryUsedCount) ? renderNumber(blacklist.entryUsedCount) : '0')
.replace('{{total}}', !isNaN(+blacklist.entryCount) ? renderNumber(blacklist.entryCount) : '?')
;
child.text(text);
li.find('span').text(text);
ul.prepend(li);
}
$('#parseAllABPHideFilters').attr('checked', µb.userSettings.parseAllABPHideFilters === true);
$('#ubiquitousParseAllABPHideFiltersPrompt2').text(
uDom('#parseAllABPHideFilters').attr('checked', µb.userSettings.parseAllABPHideFilters === true);
uDom('#ubiquitousParseAllABPHideFiltersPrompt2').text(
chrome.i18n.getMessage("listsParseAllABPHideFiltersPrompt2")
.replace('{{abpHideFilterCount}}', renderNumber(µb.abpHideFilters.getFilterCount()))
);
@ -158,15 +154,13 @@ function renderBlacklists() {
function getSelectedBlacklistsHash() {
var hash = '';
var inputs = $('#blacklists .blacklistDetails > input');
var i = inputs.length;
var entryHash;
var inputs = uDom('#blacklists .blacklistDetails > input');
var i = inputs.length();
while ( i-- ) {
entryHash = $(inputs[i]).prop('checked').toString();
hash += entryHash;
hash += inputs.subset(i).prop('checked').toString();
}
// Factor in whether ABP filters are to be processed
hash += $('#parseAllABPHideFilters').prop('checked').toString();
// Factor in whether cosmetic filters are to be processed
hash += uDom('#parseAllABPHideFilters').prop('checked').toString();
return hash;
}
@ -176,7 +170,7 @@ function getSelectedBlacklistsHash() {
// This is to give a visual hint that the selection of blacklists has changed.
function selectedBlacklistsChanged() {
$('#blacklistsApply').attr(
uDom('#blacklistsApply').prop(
'disabled',
getSelectedBlacklistsHash() === selectedBlacklistsHash
);
@ -191,21 +185,21 @@ function blacklistsApplyHandler() {
}
// Reload blacklists
var switches = [];
var lis = $('#blacklists .blacklistDetails');
var i = lis.length;
var lis = uDom('#blacklists .blacklistDetails');
var i = lis.length();
var path;
while ( i-- ) {
path = $(lis[i]).children('a').attr('href');
path = lis.subset(i).find('a').attr('href');
switches.push({
location: path,
off: $(lis[i]).children('input').prop('checked') === false
off: lis.subset(i).find('input').prop('checked') === false
});
}
messaging.tell({
what: 'reloadAllFilters',
switches: switches
});
$('#blacklistsApply').attr('disabled', true );
uDom('#blacklistsApply').attr('disabled', true );
}
/******************************************************************************/
@ -214,18 +208,18 @@ function abpHideFiltersCheckboxChanged() {
messaging.tell({
what: 'userSettings',
name: 'parseAllABPHideFilters',
value: $(this).is(':checked')
value: this.checked
});
selectedBlacklistsChanged();
}
/******************************************************************************/
window.addEventListener('load', function() {
uDom.onLoad(function() {
// Handle user interaction
$('#blacklists').on('change', '.blacklistDetails', selectedBlacklistsChanged);
$('#blacklistsApply').on('click', blacklistsApplyHandler);
$('#parseAllABPHideFilters').on('change', abpHideFiltersCheckboxChanged);
uDom('#blacklists').on('change', '.blacklistDetails', selectedBlacklistsChanged);
uDom('#blacklistsApply').on('click', blacklistsApplyHandler);
uDom('#parseAllABPHideFilters').on('change', abpHideFiltersCheckboxChanged);
renderBlacklists();
});

View File

@ -19,11 +19,11 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, $ */
/* global chrome, messaging, uDom */
/******************************************************************************/
window.addEventListener('load', function() {
uDom.onLoad(function() {
/******************************************************************************/
@ -35,7 +35,7 @@ var commitHistoryURLPrefix = 'https://github.com/gorhill/ublock/commits/master/'
var setAssetListClassBit = function(bit, state) {
assetListSwitches[assetListSwitches.length-1-bit] = !state ? 'o' : 'x';
$('#assetList')
uDom('#assetList')
.removeClass()
.addClass(assetListSwitches.join(''));
};
@ -46,24 +46,24 @@ var renderAssetList = function(details) {
var dirty = false;
var paths = Object.keys(details.list).sort();
if ( paths.length > 0 ) {
$('#assetList .assetEntry').remove();
var assetTable = $('#assetList table');
uDom('#assetList .assetEntry').remove();
var i = 0;
var path, status, html;
var path, status, html = [];
while ( path = paths[i++] ) {
status = details.list[path].status;
dirty = dirty || status !== 'Unchanged';
html = [];
html.push('<tr class="assetEntry ' + status.toLowerCase().replace(/ +/g, '-') + '">');
html.push('<td>');
html.push('<a href="' + commitHistoryURLPrefix + path + '">');
html.push(path.replace(/^(assets\/[^/]+\/)(.+)$/, '$1<b>$2</b>'));
html.push('</a>');
html.push('<td>');
html.push(chrome.i18n.getMessage('aboutAssetsUpdateStatus' + status));
assetTable.append(html.join(''));
html.push(
'<tr class="assetEntry ' + status.toLowerCase().replace(/ +/g, '-') + '">',
'<td>',
'<a href="' + commitHistoryURLPrefix + path + '">',
path.replace(/^(assets\/[^/]+\/)(.+)$/, '$1<b>$2</b>'),
'</a>',
'<td>',
chrome.i18n.getMessage('aboutAssetsUpdateStatus' + status)
);
}
$('#assetList a').attr('target', '_blank');
uDom('#assetList table tBody').append(html.join(''));
uDom('#assetList a').attr('target', '_blank');
updateList = details.list;
}
setAssetListClassBit(0, paths.length !== 0);
@ -110,8 +110,8 @@ messaging.listen(onAnnounce);
/******************************************************************************/
$('#aboutVersion').html(chrome.runtime.getManifest().version);
$('#aboutAssetsUpdateButton').on('click', updateAssets);
uDom('#aboutVersion').html(chrome.runtime.getManifest().version);
uDom('#aboutAssetsUpdateButton').on('click', updateAssets);
/******************************************************************************/

View File

@ -600,16 +600,11 @@ FilterContainer.prototype.retrieveGenericSelectors = function(tabHostname, reque
//quickProfiler.stop();
/*
console.log(
'µBlock> abp-hide-filters.js: "%s"\n\t%d selectors in => %d/%d filters/buckets tested => %d selectors out',
url,
inSelectors.length,
//filterTestCount,
//bucketTestCount,
hideSelectors.length + donthideSelectors.length
);
*/
//console.log(
// 'µBlock> abp-hide-filters.js: %d selectors in => %d selectors out',
// request.selectors.length,
// r.hide.length + r.donthide.length
//);
return r;
};
@ -656,16 +651,11 @@ FilterContainer.prototype.retrieveDomainSelectors = function(tabHostname, reques
//quickProfiler.stop();
/*
console.log(
'µBlock> abp-hide-filters.js: "%s"\n\t%d selectors in => %d/%d filters/buckets tested => %d selectors out',
url,
inSelectors.length,
//filterTestCount,
//bucketTestCount,
hideSelectors.length + donthideSelectors.length
);
*/
//console.log(
// 'µBlock> abp-hide-filters.js: "%s" => %d selectors out',
// request.locationURL,
// r.hide.length + r.donthide.length
//);
return r;
};

View File

@ -32,6 +32,7 @@ return {
userSettings: {
collapseBlocked: true,
logBlockedRequests: false,
parseAllABPHideFilters: true,
netExceptionList: {},
showIconBadge: true
@ -41,7 +42,7 @@ return {
allowedRequestCount: 0
},
updateAssetsEvery: 5 * 24 * 60 * 60 * 1000,
updateAssetsEvery: 4 * 24 * 60 * 60 * 1000,
projectServerRoot: 'https://raw2.github.com/gorhill/ublock/master/',
userFiltersPath: 'assets/user/filters.txt',

View File

@ -27,6 +27,10 @@
/******************************************************************************/
/******************************************************************************/
(function() {
/******************************************************************************/
// https://github.com/gorhill/httpswitchboard/issues/345
var messaging = (function(name){
@ -121,20 +125,16 @@ var messaging = (function(name){
/******************************************************************************/
/******************************************************************************/
(function() {
/******************************************************************************/
// ABP cosmetic filters
var CosmeticFiltering = function() {
this.queriedSelectors = {};
this.injectedSelectors = {};
this.classSelectors = null;
this.idSelectors = null;
};
var cosmeticFiltering = (function() {
CosmeticFiltering.prototype.onDOMContentLoaded = function() {
var queriedSelectors = {};
var injectedSelectors = {};
var classSelectors = null;
var idSelectors = null;
var domLoaded = function() {
// https://github.com/gorhill/uBlock/issues/14
// Treat any existing domain-specific exception selectors as if they had
// been injected already.
@ -144,20 +144,19 @@ CosmeticFiltering.prototype.onDOMContentLoaded = function() {
exceptions = decodeURIComponent(exceptions).split('\n');
var i = exceptions.length;
while ( i-- ) {
this.injectedSelectors[exceptions[i]] = true;
injectedSelectors[exceptions[i]] = true;
}
}
// TODO: evaluate merging into a single loop
this.classesFromNodeList(document.querySelectorAll('*[class]'));
this.idsFromNodeList(document.querySelectorAll('*[id]'));
this.retrieveGenericSelectors();
selectorsFromNodeList(document.querySelectorAll('*[class],*[id]'));
retrieveGenericSelectors();
};
CosmeticFiltering.prototype.retrieveGenericSelectors = function() {
var selectors = this.classSelectors !== null ? Object.keys(this.classSelectors) : [];
if ( this.idSelectors !== null ) {
selectors = selectors.concat(this.idSelectors);
var retrieveGenericSelectors = function() {
var selectors = classSelectors !== null ? Object.keys(classSelectors) : [];
if ( idSelectors !== null ) {
selectors = selectors.concat(idSelectors);
}
if ( selectors.length > 0 ) {
//console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', selectors.length);
@ -166,34 +165,34 @@ CosmeticFiltering.prototype.retrieveGenericSelectors = function() {
pageURL: window.location.href,
selectors: selectors
},
this.retrieveHandler.bind(this)
retrieveHandler
);
}
this.idSelectors = null;
this.classSelectors = null;
idSelectors = null;
classSelectors = null;
};
CosmeticFiltering.prototype.retrieveHandler = function(selectors) {
var retrieveHandler = function(selectors) {
if ( !selectors ) {
return;
}
var styleText = [];
this.filterUnfiltered(selectors.hideUnfiltered, selectors.hide);
this.reduce(selectors.hide, this.injectedSelectors);
filterUnfiltered(selectors.hideUnfiltered, selectors.hide);
reduce(selectors.hide, injectedSelectors);
if ( selectors.hide.length ) {
var hideStyleText = '{{hideSelectors}} {display:none !important;}'
.replace('{{hideSelectors}}', selectors.hide.join(','));
styleText.push(hideStyleText);
this.applyCSS(selectors.hide, 'display', 'none');
applyCSS(selectors.hide, 'display', 'none');
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.hide.length, hideStyleText);
}
this.filterUnfiltered(selectors.donthideUnfiltered, selectors.donthide);
this.reduce(selectors.donthide, this.injectedSelectors);
filterUnfiltered(selectors.donthideUnfiltered, selectors.donthide);
reduce(selectors.donthide, injectedSelectors);
if ( selectors.donthide.length ) {
var dontHideStyleText = '{{donthideSelectors}} {display:initial !important;}'
.replace('{{donthideSelectors}}', selectors.donthide.join(','));
styleText.push(dontHideStyleText);
this.applyCSS(selectors.donthide, 'display', 'initial');
applyCSS(selectors.donthide, 'display', 'initial');
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.donthide.length, dontHideStyleText);
}
if ( styleText.length > 0 ) {
@ -206,7 +205,7 @@ CosmeticFiltering.prototype.retrieveHandler = function(selectors) {
}
};
CosmeticFiltering.prototype.applyCSS = function(selectors, prop, value) {
var applyCSS = function(selectors, prop, value) {
if ( document.body === null ) {
return;
}
@ -217,12 +216,12 @@ CosmeticFiltering.prototype.applyCSS = function(selectors, prop, value) {
}
};
CosmeticFiltering.prototype.filterUnfiltered = function(inSelectors, outSelectors) {
var filterUnfiltered = function(inSelectors, outSelectors) {
var i = inSelectors.length;
var selector;
while ( i-- ) {
selector = inSelectors[i];
if ( this.injectedSelectors[selector] ) {
if ( injectedSelectors[selector] ) {
continue;
}
if ( document.querySelector(selector) !== null ) {
@ -231,7 +230,7 @@ CosmeticFiltering.prototype.filterUnfiltered = function(inSelectors, outSelector
}
};
CosmeticFiltering.prototype.reduce = function(selectors, dict) {
var reduce = function(selectors, dict) {
var i = selectors.length, selector, end;
while ( i-- ) {
selector = selectors[i];
@ -250,108 +249,93 @@ CosmeticFiltering.prototype.reduce = function(selectors, dict) {
}
};
CosmeticFiltering.prototype.classesFromNodeList = function(nodes) {
if ( !nodes ) {
var selectorsFromNodeList = function(nodes) {
if ( !nodes || !nodes.length ) {
return;
}
if ( this.classSelectors === null ) {
this.classSelectors = {};
if ( idSelectors === null ) {
idSelectors = [];
}
var classNames, className, j;
if ( classSelectors === null ) {
classSelectors = {};
}
var qq = queriedSelectors;
var cc = classSelectors;
var ii = idSelectors;
var node, v, classNames, j;
var i = nodes.length;
while ( i-- ) {
className = nodes[i].className;
if ( typeof className !== 'string' ) {
continue;
}
className = className.trim();
if ( className === '' ) {
continue;
}
if ( className.indexOf(' ') < 0 ) {
className = '.' + className;
if ( this.queriedSelectors[className] ) {
continue;
}
this.classSelectors[className] = true;
this.queriedSelectors[className] = true;
continue;
}
classNames = className.trim().split(/\s+/);
j = classNames.length;
while ( j-- ) {
className = classNames[j];
if ( className === '' ) {
continue;
}
className = '.' + className;
if ( this.queriedSelectors[className] ) {
continue;
}
this.classSelectors[className] = true;
this.queriedSelectors[className] = true;
}
}
};
CosmeticFiltering.prototype.idsFromNodeList = function(nodes) {
if ( !nodes ) {
return;
}
if ( this.idSelectors === null ) {
this.idSelectors = [];
}
var id;
var i = nodes.length;
while ( i-- ) {
id = nodes[i].id;
if ( !id ) {
continue;
}
id = '#' + id;
if ( this.queriedSelectors[id] ) {
continue;
}
this.idSelectors.push(id);
this.queriedSelectors[id] = true;
}
};
CosmeticFiltering.prototype.allFromNodeList = function(nodes) {
this.classesFromNodeList(nodes);
this.idsFromNodeList(nodes);
var i = nodes.length;
var node;
while ( i-- ) {
node = nodes[i];
if ( node.querySelectorAll ) {
this.classesFromNodeList(node.querySelectorAll('*[class]'));
this.idsFromNodeList(node.querySelectorAll('*[id]'));
if ( node.nodeType !== 1 ) {
continue;
}
// id
v = nodes[i].id.trim();
if ( v !== '' ) {
v = '#' + v;
if ( !qq[v] ) {
ii.push(v);
qq[v] = true;
}
}
// class
v = nodes[i].className;
// it could be an SVGAnimatedString...
if ( typeof v !== 'string' ) { continue; }
v = v.trim();
if ( v === '' ) { continue; }
// one class
if ( v.indexOf(' ') < 0 ) {
v = '.' + v;
if ( qq[v] ) { continue; }
cc[v] = true;
qq[v] = true;
continue;
}
// many classes
classNames = v.trim().split(/\s+/);
j = classNames.length;
while ( j-- ) {
v = classNames[j];
if ( v === '' ) { continue; }
v = '.' + v;
if ( qq[v] ) { continue; }
cc[v] = true;
qq[v] = true;
}
}
};
var cosmeticFiltering = new CosmeticFiltering();
var processNodeLists = function(nodeLists) {
var i = nodeLists.length;
var nodeList, j, node;
while ( i-- ) {
nodeList = nodeLists[i];
selectorsFromNodeList(nodeList);
j = nodeList.length;
while ( j-- ) {
node = nodeList[j];
if ( node.querySelectorAll ) {
selectorsFromNodeList(node.querySelectorAll('*[id],*[class]'));
}
}
}
retrieveGenericSelectors();
};
domLoaded();
return {
processNodeLists: processNodeLists
};
})();
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7
var observeElement = function(elem) {
var onLoad = function() {
var elem = this;
var onAnswerReceived = function(details) {
if ( details.blocked ) {
hideBlockedElement(elem, details.collapse);
}
};
messaging.ask({ what: 'blockedRequest', url: this.src }, onAnswerReceived);
this.removeEventListener('load', onLoad);
};
elem.addEventListener('load', onLoad);
};
var hideBlockedElement = function(elem, collapse) {
var blockedElementHider = (function() {
var hideOne = function(elem, collapse) {
// If `!important` is not there, going back using history will likely
// cause the hidden element to re-appear.
elem.style.visibility = 'hidden !important';
@ -360,7 +344,21 @@ var hideBlockedElement = function(elem, collapse) {
}
};
var hideBlockedElements = function(elems, details) {
var observeOne = function(elem) {
var onComplete = function() {
var elem = this;
var onAnswerReceived = function(details) {
if ( details.blocked ) {
hideOne(elem, details.collapse);
}
};
messaging.ask({ what: 'blockedRequest', url: this.src }, onAnswerReceived);
this.removeEventListener('load', onComplete);
};
elem.addEventListener('load', onComplete);
};
var hideMany = function(elems, details) {
var blockedRequests = details.blockedRequests;
var collapse = details.collapse;
var i = elems.length;
@ -372,50 +370,55 @@ var hideBlockedElements = function(elems, details) {
continue;
}
if ( src === '' ) {
observeElement(elem);
observeOne(elem);
} else if ( blockedRequests[src] ) {
hideBlockedElement(elem, collapse);
hideOne(elem, collapse);
}
}
};
var hideBlockedElementInAddedNodes = function(nodes) {
var onBlockedRequestsReceived = function(details) {
hideBlockedElements(nodes, details);
var i = nodes.length;
var elem;
var processElements = function(elems) {
var blockedRequestsReceived = function(details) {
hideMany(elems, details);
var i = elems.length;
while ( i-- ) {
elem = nodes[i];
if ( elem.querySelectorAll ) {
hideBlockedElements(elem.querySelectorAll('img,iframe'), details);
}
hideMany(elems[i].querySelectorAll('img,iframe'), details);
}
};
messaging.ask({ what: 'blockedRequests' }, onBlockedRequestsReceived);
messaging.ask({ what: 'blockedRequests' }, blockedRequestsReceived);
};
// rhill 2014-07-01: Avoid useless work: only nodes which are element are
// of interest at this point -- because it is common that a lot of plain
// text nodes get added.
var addNodeLists = function(nodeLists) {
var elems = [];
var i = nodeLists.length;
var nodeList, j, node;
while ( i-- ) {
nodeList = nodeLists[i];
j = nodeList.length;
while ( j-- ) {
node = nodeList[j];
if ( node.querySelectorAll ) {
elems.push(node);
}
}
}
if ( elems.length ) {
processElements(elems);
}
};
(function() {
var onBlockedRequestsReceived = function(details) {
hideBlockedElements(document.querySelectorAll('img,iframe'), details);
hideMany(document.querySelectorAll('img,iframe'), details);
};
messaging.ask({ what: 'blockedRequests' }, onBlockedRequestsReceived);
})();
/******************************************************************************/
var mutationObservedHandler = function(mutations) {
var iMutation = mutations.length;
var mutation;
while ( iMutation-- ) {
mutation = mutations[iMutation];
if ( mutation.addedNodes ) {
cosmeticFiltering.allFromNodeList(mutation.addedNodes);
hideBlockedElementInAddedNodes(mutation.addedNodes);
}
}
cosmeticFiltering.retrieveGenericSelectors();
return {
addNodeLists: addNodeLists
};
})();
/******************************************************************************/
@ -435,12 +438,23 @@ if ( /^https?:\/\/./.test(window.location.href) === false ) {
/******************************************************************************/
cosmeticFiltering.onDOMContentLoaded();
/******************************************************************************/
// Observe changes in the DOM
var mutationObservedHandler = function(mutations) {
var iMutation = mutations.length;
var nodeLists = [], nodeList;
while ( iMutation-- ) {
nodeList = mutations[iMutation].addedNodes;
if ( nodeList && nodeList.length ) {
nodeLists.push(nodeList);
}
}
if ( nodeLists.length ) {
cosmeticFiltering.processNodeLists(nodeLists);
blockedElementHider.addNodeLists(nodeLists);
}
};
// This fixes http://acid3.acidtests.org/
if ( document.body ) {
// https://github.com/gorhill/httpswitchboard/issues/176

View File

@ -21,20 +21,14 @@
/******************************************************************************/
window.addEventListener('load', function() {
/******************************************************************************/
uDom.onLoad(function() {
// Open links in the proper window
$('a').attr('target', '_blank');
$('a[href*="dashboard.html"]').attr('target', '_parent');
$('.whatisthis').on('click', function() {
$(this).parent()
uDom('a').attr('target', '_blank');
uDom('a[href*="dashboard.html"]').attr('target', '_parent');
uDom('.whatisthis').on('click', function() {
uDom(this)
.parent()
.find('.whatisthis-expandable')
.toggleClass('whatisthis-expanded');
});
/******************************************************************************/
});

View File

@ -23,31 +23,41 @@
(function() {
var loadDashboardPanel = function(hash) {
var button = $(hash);
var url = button.data('dashboardPanelUrl');
$('iframe')[0].src = url;
$('.tabButton').each(function(){
var button = $(this);
button.toggleClass('selected', button.data('dashboardPanelUrl') === url);
});
/******************************************************************************/
var loadDashboardPanel = function(tab) {
var tabButton = uDom('[data-dashboard-panel-url="' + tab + '"]');
if ( !tabButton ) {
return;
}
uDom('iframe').attr('src', tab);
uDom('.tabButton').toggleClass('selected', false);
tabButton.toggleClass('selected', true);
};
/******************************************************************************/
var onTabClickHandler = function() {
loadDashboardPanel(window.location.hash);
}
loadDashboardPanel(uDom(this).attr('data-dashboard-panel-url'));
};
/******************************************************************************/
$(function() {
$(window).on('hashchange', onTabClickHandler);
var hash = window.location.hash;
if ( hash.length < 2 ) {
hash = '#thirdparty-filters';
uDom.onLoad(function() {
var matches = window.location.search.slice(1).match(/\??(tab=([^&]+))?(.*)$/);
var tab = '', q = '';
if ( matches && matches.length === 4 ) {
tab = matches[2];
q = matches[3];
if ( q !== '' && q.charAt(0) === '&' ) {
q = '?' + q.slice(1);
}
loadDashboardPanel(hash);
}
if ( !tab ) {
tab = '3p-filters';
}
loadDashboardPanel(tab + '.html' + q);
uDom('.tabButton').on('click', onTabClickHandler);
});
/******************************************************************************/

View File

@ -20,18 +20,11 @@
*/
// Helper to deal with the i18n'ing of HTML files.
// jQuery must be present at this point.
window.addEventListener('load', function() {
var i;
var fillin = function(elem) {
var key = elem.getAttribute("data-i18n");
elem.innerHTML = chrome.i18n.getMessage(key);
uDom.onLoad(function() {
var fillin = function() {
this.innerHTML = chrome.i18n.getMessage(this.getAttribute('data-i18n'));
}
var elems = document.querySelectorAll('[data-i18n]');
i = elems.length;
while ( i-- ) {
fillin(elems[i]);
}
uDom('[data-i18n]').forEach(fillin);
});

View File

@ -29,7 +29,7 @@
/******************************************************************************/
var getStats = function(request, callback) {
var getStats = function(request) {
var µb = µBlock;
var r = {
globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
@ -39,7 +39,8 @@ var getStats = function(request, callback) {
pageBlockedRequestCount: 0,
pageAllowedRequestCount: 0,
netFilteringSwitch: false,
cosmeticFilteringSwitch: false
cosmeticFilteringSwitch: false,
logBlockedRequests: µb.userSettings.logBlockedRequests
};
var pageStore = µb.pageStoreFromTabId(request.tabId);
if ( pageStore ) {
@ -157,7 +158,7 @@ var onMessage = function(request, sender, callback) {
response = {
collapse: µBlock.userSettings.collapseBlocked,
blockedRequests: pageStore ? pageStore.blockedRequests : {}
}
};
break;
// Check a single request
@ -165,7 +166,7 @@ var onMessage = function(request, sender, callback) {
response = {
collapse: µBlock.userSettings.collapseBlocked,
blocked: pageStore && pageStore.blockedRequests[request.url]
}
};
break;
default:
@ -253,6 +254,78 @@ var onMessage = function(request, sender, callback) {
/******************************************************************************/
// stats.js
(function() {
var getPageDetails = function(µb, tabId) {
var r = {
requests: [],
hash: ''
};
var pageStore = µb.pageStores[tabId];
if ( !pageStore ) {
return r;
}
var blockedRequests = pageStore.blockedRequests;
var details, pos;
var hasher = new YaMD5();
for ( var requestURL in blockedRequests ) {
if ( blockedRequests.hasOwnProperty(requestURL) === false ) {
continue;
}
details = blockedRequests[requestURL];
if ( typeof details !== 'string' ) {
continue;
}
hasher.appendStr(requestURL);
hasher.appendStr(details);
pos = details.indexOf('\t');
r.requests.push({
type: details.slice(0, pos),
domain: µb.URI.domainFromURI(requestURL),
url: requestURL,
reason: details.slice(pos + 1)
});
}
r.hash = hasher.end();
return r;
};
var onMessage = function(request, sender, callback) {
var µb = µBlock;
// Async
switch ( request.what ) {
default:
break;
}
// Sync
var response;
switch ( request.what ) {
case 'getPageSelectors':
response = Object.keys(µb.pageStores);
break;
case 'getPageDetails':
response = getPageDetails(µb, request.tabId);
break;
default:
return µb.messaging.defaultHandler(request, sender, callback);
}
callback(response);
};
µBlock.messaging.listen('stats.js', onMessage);
})();
/******************************************************************************/
// about.js
(function() {

View File

@ -95,13 +95,13 @@ PageStore.prototype.dispose = function() {
/******************************************************************************/
PageStore.prototype.recordRequest = function(type, url, block) {
PageStore.prototype.recordRequest = function(type, url, reason) {
// rhill 2013-10-26: This needs to be called even if the request is
// already logged, since the request stats are cached for a while after
// the page is no longer visible in a browser tab.
µb.updateBadge(this.tabId);
if ( block === false ) {
if ( reason === false ) {
this.perLoadAllowedRequestCount++;
µb.localSettings.allowedRequestCount++;
return;
@ -112,7 +112,9 @@ PageStore.prototype.recordRequest = function(type, url, block) {
// https://github.com/gorhill/uBlock/issues/7
// https://github.com/gorhill/uBlock/issues/12
this.blockedRequests[url] = true;
this.blockedRequests[url] = µb.userSettings.logBlockedRequests ?
type + '\t' + reason :
true;
};
/******************************************************************************/
@ -120,7 +122,7 @@ PageStore.prototype.recordRequest = function(type, url, block) {
// Update badge, incrementally
// rhill 2013-11-09: well this sucks, I can't update icon/badge
// incrementally, as chromium overwrite the icon at some point without
// incrementally, as chromium overwrites the icon at some point without
// notifying me, and this causes internal cached state to be out of sync.
PageStore.prototype.updateBadge = function() {

View File

@ -24,12 +24,10 @@
(function() {
/******************************************************************************/
/******************************************************************************/
var stats;
/******************************************************************************/
/******************************************************************************/
// https://github.com/gorhill/httpswitchboard/issues/345
@ -65,62 +63,44 @@ formatNumber = function(count) {
/******************************************************************************/
var hasClassName = function(elem, className) {
var re = new RegExp('(^| )' + className + '( |$)', 'g');
return re.test(elem.className);
};
var toggleClassName = function(elem, className, newState) {
var re = new RegExp('(^| )' + className + '( |$)', 'g');
var currentState = re.test(elem.className);
if ( newState === undefined ) {
newState = !currentState;
}
if ( newState !== currentState ) {
if ( newState ) {
elem.className += ' ' + className;
} else {
elem.className = elem.className.replace(re, '').trim();
}
}
};
/******************************************************************************/
var renderStats = function() {
if ( !stats || !document.getElementById('switch') ) {
if ( !stats ) {
return;
}
uDom('#gotoLog').toggleClass('enabled', stats.logBlockedRequests);
var blocked = stats.pageBlockedRequestCount;
var total = stats.pageAllowedRequestCount + blocked;
var elem = document.getElementById('page-blocked');
var html = [];
if ( total === 0 ) {
elem.innerHTML = '0';
html.push('0');
} else {
elem.innerHTML = [
html.push(
formatNumber(blocked),
'<span class="dim">&nbsp;or&nbsp;',
(blocked * 100 / total).toFixed(0),
'%</span>'
].join('');
);
}
uDom('#page-blocked').html(html.join(''));
blocked = stats.globalBlockedRequestCount;
total = stats.globalAllowedRequestCount + blocked;
elem = document.getElementById('total-blocked');
html = [];
if ( total === 0 ) {
elem.innerHTML = '0';
html.push('0');
} else {
elem.innerHTML = [
html.push(
formatNumber(blocked),
'<span class="dim">&nbsp;or&nbsp;',
(blocked * 100 / total).toFixed(0),
'%</span>'
].join('');
);
}
uDom('#total-blocked').html(html.join(''));
toggleClassName(
document.querySelector('#switch .fa'),
uDom('#switch .fa').toggleClass(
'off',
stats.pageURL === '' || !stats.netFilteringSwitch
);
@ -154,34 +134,53 @@ var handleNetFilteringSwitch = function() {
if ( !stats || !stats.pageURL ) {
return;
}
toggleClassName(this, 'off');
var off = uDom(this).toggleClass('off').hasClassName('off');
messaging.tell({
what: 'toggleNetFiltering',
hostname: stats.pageHostname,
state: !hasClassName(this, 'off'),
state: !off,
tabId: stats.tabId
});
};
/******************************************************************************/
var renderHeader = function() {
var hdr = document.getElementById('version');
hdr.innerHTML = hdr.innerHTML + 'v' + chrome.runtime.getManifest().version;
var gotoDashboard = function() {
messaging.tell({
what: 'gotoExtensionURL',
url: 'dashboard.html'
});
};
/******************************************************************************/
var gotoStats = function() {
messaging.tell({
what: 'gotoExtensionURL',
url: 'dashboard.html?tab=stats&which=' + stats.tabId
});
};
/******************************************************************************/
var renderHeader = function() {
var hdr = uDom('#version');
hdr.html(hdr.html() + 'v' + chrome.runtime.getManifest().version);
};
/******************************************************************************/
var installEventHandlers = function() {
document.querySelector('#switch .fa').addEventListener('click', handleNetFilteringSwitch);
uDom('h1,h2,h3,h4').on('click', gotoDashboard);
uDom('#switch .fa').on('click', handleNetFilteringSwitch);
uDom('#gotoLog').on('click', gotoStats);
};
/******************************************************************************/
// Make menu only when popup html is fully loaded
window.addEventListener('load', function() {
uDom.onLoad(function() {
renderHeader();
renderStats();
installEventHandlers();

View File

@ -19,11 +19,11 @@
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, $ */
/* global messaging, uDom */
/******************************************************************************/
$(function() {
uDom.onLoad(function() {
/******************************************************************************/
@ -44,16 +44,16 @@ var changeUserSettings = function(name, value) {
// TODO: use data-* to declare simple settings
var onUserSettingsReceived = function(details) {
$('#collapse-blocked')
uDom('#collapse-blocked')
.attr('checked', details.collapseBlocked === true)
.on('change', function(){
changeUserSettings('collapseBlocked', $(this).is(':checked'));
changeUserSettings('collapseBlocked', this.checked);
});
$('#icon-badge')
uDom('#icon-badge')
.attr('checked', details.showIconBadge === true)
.on('change', function(){
changeUserSettings('showIconBadge', $(this).is(':checked'));
changeUserSettings('showIconBadge', this.checked);
});
};

180
js/stats.js Normal file
View File

@ -0,0 +1,180 @@
/*******************************************************************************
µBlock - a Chromium browser extension to block requests.
Copyright (C) 2014 Raymond Hill
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* global chrome, uDom, messaging */
/******************************************************************************/
(function() {
/******************************************************************************/
messaging.start('stats.js');
/******************************************************************************/
var logSettingChanged = function() {
messaging.tell({
what: 'userSettings',
name: 'logBlockedRequests',
value: this.checked
});
uDom('#blockedRequests').toggleClass('logEnabled', this.checked);
renderPageSelector();
};
/******************************************************************************/
var cachedPageSelectors = {};
var cachedPageHash = '';
var toPrettyTypeNames = {
'sub_frame': 'frame',
'object': 'plugin',
'xmlhttprequest': 'XHR'
};
/******************************************************************************/
var renderPageDetails = function(tabId) {
if ( !cachedPageSelectors[tabId] ) {
return;
}
var onDataReceived = function(details) {
if ( details.hash === cachedPageHash ) {
return;
}
blockedRequests = details.requests || [];
blockedRequests.sort(function(a, b) {
var r = a.domain.localeCompare(b.domain);
if ( r === 0 ) {
r = a.reason.localeCompare(b.reason);
if ( r === 0 ) {
r = a.type.localeCompare(b.type);
}
}
return r;
});
uDom('#tableHeader ~ tr').remove();
var blockedRequest, requestURL, renderedURL;
var html = [];
for ( var i = 0; i < blockedRequests.length; i++ ) {
blockedRequest = blockedRequests[i];
requestURL = blockedRequest.url;
renderedURL = [];
while ( requestURL.length ) {
renderedURL.push(requestURL.slice(0, 60));
requestURL = requestURL.slice(60);
}
html.push(
'<tr>',
'<td>', toPrettyTypeNames[blockedRequest.type] || blockedRequest.type,
'<td>', blockedRequest.domain,
'<td>', renderedURL.join('\n'),
'<td>', blockedRequest.reason
);
}
if ( !html.length ) {
html.push(
'<tr><td colspan="4">',
chrome.i18n.getMessage('logBlockedRequestsEmpty')
);
}
uDom('#tableHeader').insertAfter(html.join(''));
cachedPageHash = details.hash;
};
messaging.ask({ what: 'getPageDetails', tabId: tabId }, onDataReceived);
};
/******************************************************************************/
var pageSelectorChanged = function() {
renderPageDetails(this.value);
};
/******************************************************************************/
var renderPageSelector = function(targetTabId) {
if ( uDom('#logBlockedRequests').prop('checked') !== true ) {
return;
}
var selectedTabId = targetTabId || parseInt(uDom('#pageSelector').val(), 10);
var onTabReceived = function(tab) {
var html = [
'<option value="',
tab.id,
'">',
tab.title
];
uDom('#pageSelector').append(html.join(''));
if ( tab.id === selectedTabId ) {
uDom('#pageSelector').val(tab.id);
}
};
var onDataReceived = function(pageSelectors) {
uDom('#pageSelector option').remove();
cachedPageSelectors = {};
pageSelectors.sort().map(function(tabId) {
cachedPageSelectors[tabId] = true;
});
if ( !cachedPageSelectors[selectedTabId] ) {
selectedTabId = pageSelectors[0];
}
for ( var i = 0; i < pageSelectors.length; i++ ) {
chrome.tabs.get(parseInt(pageSelectors[i], 10), onTabReceived);
}
if ( selectedTabId ) {
renderPageDetails(selectedTabId);
}
};
messaging.ask({ what: 'getPageSelectors' }, onDataReceived);
};
/******************************************************************************/
var onUserSettingsReceived = function(details) {
uDom('#logBlockedRequests').prop('checked', details.logBlockedRequests);
uDom('#blockedRequests').toggleClass('logEnabled', details.logBlockedRequests);
var matches = window.location.search.slice(1).match(/(?:^|&)which=(\d+)/);
var tabId = matches && matches.length === 2 ? parseInt(matches[1], 10) : 0;
renderPageSelector(tabId);
uDom('#logBlockedRequests').on('change', logSettingChanged);
uDom('#refresh').on('click', function() { renderPageSelector(); });
uDom('#pageSelector').on('change', pageSelectorChanged);
};
/******************************************************************************/
uDom.onLoad(function() {
messaging.ask({ what: 'userSettings' }, onUserSettingsReceived);
});
/******************************************************************************/
})();

View File

@ -58,11 +58,6 @@ var onBeforeRequestHandler = function(details) {
return;
}
// Do not block myself from updating assets
if ( requestType === 'xmlhttprequest' && requestURL.slice(0, µb.projectServerRoot.length) === µb.projectServerRoot ) {
return;
}
var requestHostname = µburi.hostname;
var requestPath = µburi.path;

503
js/udom.js Normal file
View File

@ -0,0 +1,503 @@
/*******************************************************************************
µBlock - a Chromium browser extension to block requests.
Copyright (C) 2014 Raymond Hill
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/******************************************************************************/
/******************************************************************************/
// It's just a silly, minimalist DOM framework: this allows me to not rely
// on jQuery. jQuery contains way too much stuff than I need, and as per
// Opera rules, I am not allowed to use a cut-down version of jQuery. So
// the code here does *only* what I need, and nothing more, and with a lot
// of assumption on passed parameters, etc. I grow it on a per-need-basis only.
var uDom = (function() {
/******************************************************************************/
var DOMList = function() {
this.nodes = [];
};
/******************************************************************************/
var DOMListFactory = function(selector, context) {
var r = new DOMList();
if ( typeof selector === 'string' ) {
selector = selector.trim();
if ( selector.charAt(0) === '<' ) {
return addHTMLToList(r, selector);
}
if ( selector !== '' ) {
return addSelectorToList(r, selector, context);
}
}
if ( selector instanceof Node ) {
return addNodeToList(r, selector);
}
if ( selector instanceof NodeList ) {
return addNodeListToList(r, selector);
}
if ( selector instanceof DOMList ) {
return addListToList(r, selector);
}
return r;
};
/******************************************************************************/
DOMListFactory.onLoad = function(callback) {
window.addEventListener('load', callback);
};
/******************************************************************************/
var addNodeToList = function(list, node) {
if ( node ) {
list.nodes.push(node);
}
return list;
};
/******************************************************************************/
var addNodeListToList = function(list, nodelist) {
if ( nodelist ) {
var n = nodelist.length;
for ( var i = 0; i < n; i++ ) {
list.nodes.push(nodelist[i]);
}
}
return list;
};
/******************************************************************************/
var addListToList = function(list, other) {
list.nodes = list.nodes.concat(other.nodes);
return list;
};
/******************************************************************************/
var addSelectorToList = function(list, selector, context) {
var p = context || document;
var r = p.querySelectorAll(selector);
var i = r.length;
while ( i-- ) {
list.nodes.push(r[i]);
}
return list;
};
/******************************************************************************/
var pTagOfChildTag = {
'tr': 'table',
'option': 'select'
};
var addHTMLToList = function(list, html) {
var matches = html.match(/^<([a-z]+)/);
if ( !matches || matches.length !== 2 ) {
return this;
}
var cTag = matches[1];
var pTag = pTagOfChildTag[cTag] || 'div';
var p = document.createElement(pTag);
p.innerHTML = html;
// Find real parent
var c = p.querySelector(cTag);
p = c.parentNode;
while ( p.firstChild ) {
list.nodes.push(p.removeChild(p.firstChild));
}
return list;
};
/******************************************************************************/
DOMList.prototype.length = function() {
return this.nodes.length;
};
/******************************************************************************/
DOMList.prototype.subset = function(i, l) {
var r = new DOMList();
var n = l !== undefined ? l : 1;
var j = Math.min(i + n, this.nodes.length);
if ( i < j ) {
r.nodes = this.nodes.slice(i, j)
}
return r;
};
/******************************************************************************/
DOMList.prototype.first = function() {
return this.subset(0);
};
/******************************************************************************/
DOMList.prototype.node = function(i) {
return this.nodes[i];
};
/******************************************************************************/
DOMList.prototype.parent = function() {
var r = new DOMList();
if ( this.nodes.length ) {
addNodeToList(r, this.nodes[0].parentNode)
}
return r;
};
/******************************************************************************/
DOMList.prototype.find = function(selector) {
var r = new DOMList();
var n = this.nodes.length;
var nl;
for ( var i = 0; i < n; i++ ) {
nl = this.nodes[i].querySelectorAll(selector);
addNodeListToList(r, nl)
}
return r;
};
/******************************************************************************/
DOMList.prototype.forEach = function(callback) {
var n = this.nodes.length;
for ( var i = 0; i < n; i++ ) {
callback.bind(this.nodes[i]).call();
}
return this;
};
/******************************************************************************/
DOMList.prototype.remove = function() {
var n = this.nodes.length;
var c, p;
for ( var i = 0; i < n; i++ ) {
c = this.nodes[i];
if ( p = c.parentNode ) {
p.removeChild(c);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.empty = function() {
var node;
var i = this.nodes.length;
while ( i-- ) {
node = this.nodes[i];
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.append = function(selector, context) {
var p = this.nodes[0];
if ( p ) {
var c = DOMListFactory(selector, context);
var n = c.nodes.length;
for ( var i = 0; i < n; i++ ) {
p.appendChild(c.nodes[i]);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.prepend = function(selector, context) {
var p = this.nodes[0];
if ( p ) {
var c = DOMListFactory(selector, context);
var i = c.nodes.length;
while ( i-- ) {
p.insertBefore(c.nodes[i], p.firstChild);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.appendTo = function(selector, context) {
var p = DOMListFactory(selector, context);
if ( p.length ) {
var n = this.nodes.length;
for ( var i = 0; i < n; i++ ) {
p.nodes[0].appendChild(this.nodes[i]);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.insertAfter = function(selector, context) {
if ( this.nodes.length === 0 ) {
return this;
}
var p = this.nodes[0].parentNode;
if ( !p ) {
return this;
}
var c = DOMListFactory(selector, context);
var n = c.nodes.length;
for ( var i = 0; i < n; i++ ) {
p.appendChild(c.nodes[i]);
}
return this;
};
/******************************************************************************/
DOMList.prototype.clone = function(notDeep) {
var r = new DOMList();
var n = this.nodes.length;
for ( var i = 0; i < n; i++ ) {
addNodeToList(r, this.nodes[i].cloneNode(!notDeep));
}
return r;
};
/******************************************************************************/
DOMList.prototype.attr = function(attr, value) {
var i = this.nodes.length;
if ( value === undefined && typeof attr !== 'object' ) {
return i ? this.nodes[0].getAttribute(attr) : undefined;
}
if ( typeof attr === 'object' ) {
var attrNames = Object.keys(attr);
var node, j, attrName;
while ( i-- ) {
node = this.nodes[i];
j = attrNames.length;
while ( j-- ) {
attrName = attrNames[j];
node.setAttribute(attrName, attr[attrName]);
}
}
} else {
while ( i-- ) {
this.nodes[i].setAttribute(attr, value);
}
}
return this;
};
/******************************************************************************/
DOMList.prototype.prop = function(prop, value) {
var i = this.nodes.length;
if ( value === undefined ) {
return i ? this.nodes[0][prop] : undefined;
}
while ( i-- ) {
this.nodes[i][prop] = value;
}
return this;
};
/******************************************************************************/
DOMList.prototype.css = function(prop, value) {
var i = this.nodes.length;
if ( value === undefined ) {
return i ? this.nodes[0].style[prop] : undefined;
}
while ( i-- ) {
this.nodes[i].style[prop] = value;
}
return this;
};
/******************************************************************************/
DOMList.prototype.val = function(value) {
return this.prop('value', value);
};
/******************************************************************************/
DOMList.prototype.html = function(html) {
var i = this.nodes.length;
if ( html === undefined ) {
return i ? this.nodes[0].innerHTML : '';
}
while ( i-- ) {
this.nodes[i].innerHTML = html;
}
return this;
};
/******************************************************************************/
DOMList.prototype.text = function(text) {
var i = this.nodes.length;
if ( text === undefined ) {
return i ? this.nodes[0].textContent : '';
}
while ( i-- ) {
this.nodes[i].textContent = text;
}
return this;
};
/******************************************************************************/
DOMList.prototype.hasClassName = function(className) {
if ( !this.nodes.length ) {
return false;
}
var re = new RegExp('(^| )' + className + '( |$)');
return re.test(this.nodes[0].className);
};
DOMList.prototype.addClass = function(className) {
var re = new RegExp('(^| )' + className + '( |$)');
var i = this.nodes.length;
var before, after;
while ( i-- ) {
before = this.nodes[i].className;
if ( !re.test(before) ) {
after = before + ' ' + className;
this.nodes[i].className = after.trim();
}
}
return this;
};
DOMList.prototype.removeClass = function() {
var i = this.nodes.length;
while ( i-- ) {
this.nodes[i].className = '';
}
return this;
};
DOMList.prototype.toggleClass = function(className, targetState) {
var re = new RegExp('(^| )' + className + '( |$)');
var n = this.nodes.length;
var node, currentState, newState, newClassName;
for ( var i = 0; i < n; i++ ) {
node = this.nodes[i];
currentState = re.test(node.className);
newState = targetState;
if ( newState === undefined ) {
newState = !currentState;
}
if ( newState === currentState ) {
continue;
}
newClassName = node.className;
if ( newState ) {
newClassName += ' ' + className;
} else {
newClassName = newClassName.replace(re, '');
}
node.className = newClassName.trim();
}
return this;
};
/******************************************************************************/
var makeEventHandler = function(context, selector, callback) {
return function(event) {
var candidates = context.querySelectorAll(selector);
if ( !candidates.length ) {
return;
}
var node = event.target;
var i;
while ( node && node !== context ) {
i = candidates.length;
while ( i-- ) {
if ( candidates[i] === node ) {
return callback.bind(node).call(event);
}
}
node = node.parentNode;
}
};
};
DOMList.prototype.on = function(etype, selector, callback) {
if ( typeof selector === 'function' ) {
callback = selector;
selector = undefined;
}
var i = this.nodes.length;
while ( i-- ) {
if ( selector !== undefined ) {
this.nodes[i].addEventListener(etype, makeEventHandler(this.nodes[i], selector, callback), true);
} else {
this.nodes[i].addEventListener(etype, callback);
}
}
return this;
};
/******************************************************************************/
// TODO: Won't work for delegated handlers. Need to figure
// what needs to be done.
DOMList.prototype.off = function(evtype, callback) {
var i = this.nodes.length;
while ( i-- ) {
this.nodes[i].removeEventListener(evtype, callback);
}
return this;
};
/******************************************************************************/
DOMList.prototype.trigger = function(etype) {
var ev = new CustomEvent(etype);
var i = this.nodes.length;
while ( i-- ) {
this.nodes[i].dispatchEvent(ev);
}
return this;
};
/******************************************************************************/
return DOMListFactory;
})();

View File

@ -40,10 +40,18 @@ var gotoURL = function(details) {
/******************************************************************************/
var gotoExtensionURL = function(url) {
var hasFragment = function(url) {
return url.indexOf('#') >= 0;
var hasQuery = function(url) {
return url.indexOf('?') >= 0;
};
var removeQuery = function(url) {
var pos = url.indexOf('?');
if ( pos < 0 ) {
return url;
}
return url.slice(0, pos);
};
var removeFragment = function(url) {
var pos = url.indexOf('#');
if ( pos < 0 ) {
@ -54,26 +62,19 @@ var gotoExtensionURL = function(url) {
var tabIndex = 9999;
var targetUrl = chrome.extension.getURL(url);
var urlToFind = removeFragment(targetUrl);
var currentWindow = function(tabs) {
var updateProperties = { active: true };
var i = tabs.length;
while ( i-- ) {
if ( removeFragment(tabs[i].url) !== urlToFind ) {
if ( removeQuery(tabs[i].url) !== removeQuery(targetUrl) ) {
continue;
}
// If current tab in dashboard is different, force the new one, if
// there is one, to be activated.
if ( tabs[i].url !== targetUrl ) {
if ( hasFragment(targetUrl) ) {
updateProperties.url = targetUrl;
}
}
// Activate found matching tab
// Commented out as per:
// https://github.com/gorhill/httpswitchboard/issues/150#issuecomment-32683726
// chrome.tabs.move(tabs[0].id, { index: index + 1 });
chrome.tabs.update(tabs[i].id, updateProperties);
return;
}

4
lib/jquery-2.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,7 @@ h1,h2,h3,h4 {
color: white;
background-color: #444;
text-align: center;
cursor: pointer;
}
a {
color: inherit;
@ -40,6 +41,17 @@ p {
white-space: nowrap;
text-align: center;
}
#switch .fa {
font-size: 64px;
color: green;
cursor: pointer;
}
#switch .fa:hover {
opacity: 0.9;
}
#switch .fa.off {
color: #ccc;
}
#switch-hint {
font-size: 11px;
color: #888;
@ -56,16 +68,17 @@ p {
margin-bottom: 4px;
text-align: center;
}
#switch .fa {
font-size: 64px;
color: green;
#gotoLog {
display: none;
font-size: 14px;
color: gray;
cursor: pointer;
}
#switch .fa:hover {
opacity: 0.9;
#gotoLog.hover {
color: #444;
}
#switch .fa.off {
color: #ccc;
#gotoLog.enabled {
display: inline;
}
#options {
margin: 16px 0 0 0;
@ -80,17 +93,18 @@ p {
</head>
<body>
<h4><a href="dashboard.html" target="_blank" title="Click to open the dashboard">µBlock<span id="version"></span></a></h4>
<h4 title="Click to open the dashboard">µBlock<span id="version"></span></h4>
<div>
<p id="switch"><span class="fa">&#xf011;</span></p>
<p id="switch-hint" data-i18n="popupPowerSwitchInfo"></p>
<p style="font-size: 16px;" data-i18n="popupBlockedRequestPrompt"></p>
<p id="stats" data-i18n="popupBlockedOnThisPagePrompt"></p>
<p id="stats"><span data-i18n="popupBlockedOnThisPagePrompt"></span>&ensp;<span id="gotoLog" class="fa">&#xf06e;</span></p>
<p id="page-blocked">?</p>
<p id="stats" data-i18n="popupBlockedSinceInstallPrompt"></p>
<p id="total-blocked">?</p>
</div>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/messaging-client.js"></script>
<script src="js/popup.js"></script>

View File

@ -19,7 +19,7 @@ ul {
<li><input id="icon-badge" type="checkbox"> <span data-i18n="settingsIconBadgePrompt"></span>
</ul>
<script src="lib/jquery-2.min.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script>

76
stats.html Normal file
View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>µBlock — Statistics</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<style>
div {
margin: 1em 0;
}
ul {
list-style-type: none;
}
#refresh {
margin: 0 0.5em 0 4px;
display: inline-block;
vertical-align: middle;
font-size: 2em;
cursor: pointer;
}
select {
padding: 2px 0;
font-size: 14px;
}
table {
margin: 1em 0;
border: 1px solid gray;
font: 12px mono;
border-collapse: collapse;
min-width: 600px;
}
td, th {
border: 1px solid #aaa;
padding: 6px 6px;
white-space: pre;
}
td:nth-of-type(2) {
text-align: right;
}
#blockedRequests {
margin: 2em 0 0 0;
display: none;
}
#blockedRequests.logEnabled {
display: block;
}
</style>
</head>
<body>
<ul>
<li><input id="logBlockedRequests" type="checkbox" data-range="bool" /><span data-i18n="logBlockedRequestsPrompt"></span>
<button class="whatisthis"></button>
<div class="whatisthis-expandable para" data-i18n="logBlockedRequestsHelp"></div>
</ul>
<div id="blockedRequests">
<span id="refresh" class="fa">&#xf021;</span><select id="pageSelector"></select>
<table id="pageDetails">
<tr id="tableHeader"><th>Type<th>Domain<th>Blocked URL<th>Filter
</table>
</div>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script>
<script src="js/stats.js"></script>
</body>
</html>