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> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <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/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css"> <link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<style> <style>
@ -33,7 +33,7 @@ div > p:last-child {
<p><button id="userFiltersApply" disabled="true" data-i18n="1pApplyChanges"></button></p> <p><button id="userFiltersApply" disabled="true" data-i18n="1pApplyChanges"></button></p>
</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/i18n.js"></script>
<script src="js/dashboard-common.js"></script> <script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script> <script src="js/messaging-client.js"></script>

View File

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

View File

@ -19,6 +19,10 @@
"message": "Deine Filter", "message": "Deine Filter",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "Über", "message": "Über",
"description": "appears as tab name in dashboard" "description": "appears as tab name in dashboard"
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} eindeutig blockierte Hostnamen aus:", "message": "{{ubiquitousBlacklistCount}} eindeutig blockierte Hostnamen aus:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:" "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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Changelog'>Changelog</a>", "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>" "description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters", "message": "Your filters",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "About", "message": "About",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} network filters from:", "message": "{{ubiquitousBlacklistCount}} network filters from:",
"description": "English: {{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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>", "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>" "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", "message": "Vos règles",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistiques",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "À propos", "message": "À propos",
"description": "appears as tab name in dashboard" "description": "appears as tab name in dashboard"
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} filtres actuellement en action :", "message": "{{ubiquitousBlacklistCount}} filtres actuellement en action :",
"description": "English: {{ubiquitousBlacklistCount}} network filters from:" "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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Journal des changements (en Anglais)</a>", "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>" "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", "message": "I tuoi filtri",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "Info", "message": "Info",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} reti filtrate da:", "message": "{{ubiquitousBlacklistCount}} reti filtrate da:",
"description": "English: {{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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>", "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>" "description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters", "message": "Your filters",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "О расширении", "message": "О расширении",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "{{ubiquitousBlacklistCount}} блокируется хостов, помимо:", "message": "{{ubiquitousBlacklistCount}} блокируется хостов, помимо:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:" "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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>Список изменений</a>", "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>" "description": "English: <a href='https://github.com/gorhill/ublock/wiki/Change-log'>Change log</a>"

View File

@ -19,6 +19,10 @@
"message": "Your filters", "message": "Your filters",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
}, },
"statsPageName" : {
"message": "Statistics",
"description": "appears as tab name in dashboard."
},
"aboutPageName": { "aboutPageName": {
"message": "关于", "message": "关于",
"description": "appears as tab name in dashboard." "description": "appears as tab name in dashboard."
@ -53,7 +57,7 @@
}, },
"3pListsOfBlockedHostsPrompt2" : { "3pListsOfBlockedHostsPrompt" : {
"message": "共{{ubiquitousBlacklistCount}}个不同的屏蔽站点名,来自:", "message": "共{{ubiquitousBlacklistCount}}个不同的屏蔽站点名,来自:",
"description": "English: {{ubiquitousBlacklistCount}} distinct blocked hostnames from:" "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" : { "aboutChangelog" : {
"message": "<a href='https://github.com/gorhill/ublock/wiki/Change-log'>变更日志</a>", "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>" "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>
</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/i18n.js"></script>
<script src="js/dashboard-common.js"></script> <script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.js"></script> <script src="js/messaging-client.js"></script>

View File

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

View File

@ -27,7 +27,7 @@ body {
#dashboard-nav-widgets { #dashboard-nav-widgets {
margin: 0; margin: 0;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding: 4px 0 3px 0; padding: 4px 0 4px 0;
white-space: nowrap; white-space: nowrap;
background-color: white; background-color: white;
} }
@ -76,16 +76,17 @@ iframe {
<div id="dashboard-nav"> <div id="dashboard-nav">
<div id="dashboard-nav-widgets"> <div id="dashboard-nav-widgets">
<span>µBlock</span> <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" href="#" 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" href="#" 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" href="#" 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="stats.html" data-i18n="statsPageName"></a>
<a class="tabButton" href="#" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
</div> </div>
</div> </div>
<iframe src=""></iframe> <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/i18n.js"></script>
<script src="js/dashboard.js"></script> <script src="js/dashboard.js"></script>
</body> </body>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,10 @@
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
(function() {
/******************************************************************************/
// https://github.com/gorhill/httpswitchboard/issues/345 // https://github.com/gorhill/httpswitchboard/issues/345
var messaging = (function(name){ var messaging = (function(name){
@ -121,301 +125,300 @@ var messaging = (function(name){
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
(function() {
/******************************************************************************/
// ABP cosmetic filters // ABP cosmetic filters
var CosmeticFiltering = function() { var cosmeticFiltering = (function() {
this.queriedSelectors = {};
this.injectedSelectors = {};
this.classSelectors = null;
this.idSelectors = null;
};
CosmeticFiltering.prototype.onDOMContentLoaded = function() { var queriedSelectors = {};
// https://github.com/gorhill/uBlock/issues/14 var injectedSelectors = {};
// Treat any existing domain-specific exception selectors as if they had var classSelectors = null;
// been injected already. var idSelectors = null;
var style = document.getElementById('uBlock1ae7a5f130fc79b4fdb8a4272d9426b5');
var exceptions = style && style.getAttribute('uBlock1ae7a5f130fc79b4fdb8a4272d9426b5'); var domLoaded = function() {
if ( exceptions ) { // https://github.com/gorhill/uBlock/issues/14
exceptions = decodeURIComponent(exceptions).split('\n'); // Treat any existing domain-specific exception selectors as if they had
var i = exceptions.length; // been injected already.
var style = document.getElementById('uBlock1ae7a5f130fc79b4fdb8a4272d9426b5');
var exceptions = style && style.getAttribute('uBlock1ae7a5f130fc79b4fdb8a4272d9426b5');
if ( exceptions ) {
exceptions = decodeURIComponent(exceptions).split('\n');
var i = exceptions.length;
while ( i-- ) {
injectedSelectors[exceptions[i]] = true;
}
}
// TODO: evaluate merging into a single loop
selectorsFromNodeList(document.querySelectorAll('*[class],*[id]'));
retrieveGenericSelectors();
};
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);
messaging.ask({
what: 'retrieveGenericCosmeticSelectors',
pageURL: window.location.href,
selectors: selectors
},
retrieveHandler
);
}
idSelectors = null;
classSelectors = null;
};
var retrieveHandler = function(selectors) {
if ( !selectors ) {
return;
}
var styleText = [];
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);
applyCSS(selectors.hide, 'display', 'none');
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.hide.length, hideStyleText);
}
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);
applyCSS(selectors.donthide, 'display', 'initial');
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.donthide.length, dontHideStyleText);
}
if ( styleText.length > 0 ) {
var style = document.createElement('style');
style.appendChild(document.createTextNode(styleText.join('\n')));
var parent = document.body || document.documentElement;
if ( parent ) {
parent.appendChild(style);
}
}
};
var applyCSS = function(selectors, prop, value) {
if ( document.body === null ) {
return;
}
var elems = document.querySelectorAll(selectors);
var i = elems.length;
while ( i-- ) { while ( i-- ) {
this.injectedSelectors[exceptions[i]] = true; elems[i].style[prop] = value;
} }
} };
// TODO: evaluate merging into a single loop var filterUnfiltered = function(inSelectors, outSelectors) {
this.classesFromNodeList(document.querySelectorAll('*[class]')); var i = inSelectors.length;
this.idsFromNodeList(document.querySelectorAll('*[id]')); var selector;
this.retrieveGenericSelectors(); while ( i-- ) {
}; selector = inSelectors[i];
if ( injectedSelectors[selector] ) {
CosmeticFiltering.prototype.retrieveGenericSelectors = function() {
var selectors = this.classSelectors !== null ? Object.keys(this.classSelectors) : [];
if ( this.idSelectors !== null ) {
selectors = selectors.concat(this.idSelectors);
}
if ( selectors.length > 0 ) {
//console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', selectors.length);
messaging.ask({
what: 'retrieveGenericCosmeticSelectors',
pageURL: window.location.href,
selectors: selectors
},
this.retrieveHandler.bind(this)
);
}
this.idSelectors = null;
this.classSelectors = null;
};
CosmeticFiltering.prototype.retrieveHandler = function(selectors) {
if ( !selectors ) {
return;
}
var styleText = [];
this.filterUnfiltered(selectors.hideUnfiltered, selectors.hide);
this.reduce(selectors.hide, this.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');
//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);
if ( selectors.donthide.length ) {
var dontHideStyleText = '{{donthideSelectors}} {display:initial !important;}'
.replace('{{donthideSelectors}}', selectors.donthide.join(','));
styleText.push(dontHideStyleText);
this.applyCSS(selectors.donthide, 'display', 'initial');
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.donthide.length, dontHideStyleText);
}
if ( styleText.length > 0 ) {
var style = document.createElement('style');
style.appendChild(document.createTextNode(styleText.join('\n')));
var parent = document.body || document.documentElement;
if ( parent ) {
parent.appendChild(style);
}
}
};
CosmeticFiltering.prototype.applyCSS = function(selectors, prop, value) {
if ( document.body === null ) {
return;
}
var elems = document.querySelectorAll(selectors);
var i = elems.length;
while ( i-- ) {
elems[i].style[prop] = value;
}
};
CosmeticFiltering.prototype.filterUnfiltered = function(inSelectors, outSelectors) {
var i = inSelectors.length;
var selector;
while ( i-- ) {
selector = inSelectors[i];
if ( this.injectedSelectors[selector] ) {
continue;
}
if ( document.querySelector(selector) !== null ) {
outSelectors.push(selector);
}
}
};
CosmeticFiltering.prototype.reduce = function(selectors, dict) {
var i = selectors.length, selector, end;
while ( i-- ) {
selector = selectors[i];
if ( !dict[selector] ) {
if ( end !== undefined ) {
selectors.splice(i+1, end-i);
end = undefined;
}
dict[selector] = true;
} else if ( end === undefined ) {
end = i;
}
}
if ( end !== undefined ) {
selectors.splice(0, end+1);
}
};
CosmeticFiltering.prototype.classesFromNodeList = function(nodes) {
if ( !nodes ) {
return;
}
if ( this.classSelectors === null ) {
this.classSelectors = {};
}
var classNames, className, 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; continue;
} }
this.classSelectors[className] = true; if ( document.querySelector(selector) !== null ) {
this.queriedSelectors[className] = true; outSelectors.push(selector);
continue; }
} }
classNames = className.trim().split(/\s+/); };
j = classNames.length;
while ( j-- ) { var reduce = function(selectors, dict) {
className = classNames[j]; var i = selectors.length, selector, end;
if ( className === '' ) { while ( i-- ) {
selector = selectors[i];
if ( !dict[selector] ) {
if ( end !== undefined ) {
selectors.splice(i+1, end-i);
end = undefined;
}
dict[selector] = true;
} else if ( end === undefined ) {
end = i;
}
}
if ( end !== undefined ) {
selectors.splice(0, end+1);
}
};
var selectorsFromNodeList = function(nodes) {
if ( !nodes || !nodes.length ) {
return;
}
if ( idSelectors === null ) {
idSelectors = [];
}
if ( classSelectors === null ) {
classSelectors = {};
}
var qq = queriedSelectors;
var cc = classSelectors;
var ii = idSelectors;
var node, v, classNames, j;
var i = nodes.length;
while ( i-- ) {
node = nodes[i];
if ( node.nodeType !== 1 ) {
continue; continue;
} }
className = '.' + className; // id
if ( this.queriedSelectors[className] ) { 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; continue;
} }
this.classSelectors[className] = true; // many classes
this.queriedSelectors[className] = true; 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;
}
} }
} };
};
CosmeticFiltering.prototype.idsFromNodeList = function(nodes) { var processNodeLists = function(nodeLists) {
if ( !nodes ) { var i = nodeLists.length;
return; var nodeList, j, node;
} while ( i-- ) {
if ( this.idSelectors === null ) { nodeList = nodeLists[i];
this.idSelectors = []; selectorsFromNodeList(nodeList);
} j = nodeList.length;
var id; while ( j-- ) {
var i = nodes.length; node = nodeList[j];
while ( i-- ) { if ( node.querySelectorAll ) {
id = nodes[i].id; selectorsFromNodeList(node.querySelectorAll('*[id],*[class]'));
if ( !id ) { }
continue; }
} }
id = '#' + id; retrieveGenericSelectors();
if ( this.queriedSelectors[id] ) { };
continue;
}
this.idSelectors.push(id);
this.queriedSelectors[id] = true;
}
};
CosmeticFiltering.prototype.allFromNodeList = function(nodes) { domLoaded();
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]'));
}
}
};
var cosmeticFiltering = new CosmeticFiltering(); return {
processNodeLists: processNodeLists
};
})();
/******************************************************************************/ /******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7 // https://github.com/gorhill/uBlock/issues/7
var observeElement = function(elem) { var blockedElementHider = (function() {
var onLoad = function() { var hideOne = function(elem, collapse) {
var elem = this; // If `!important` is not there, going back using history will likely
var onAnswerReceived = function(details) { // cause the hidden element to re-appear.
if ( details.blocked ) { elem.style.visibility = 'hidden !important';
hideBlockedElement(elem, details.collapse); if ( collapse && elem.parentNode ) {
elem.parentNode.removeChild(elem);
}
};
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;
var elem, src;
while ( i-- ) {
elem = elems[i];
src = elem.src;
if ( typeof src !== 'string' ) {
continue;
}
if ( src === '' ) {
observeOne(elem);
} else if ( blockedRequests[src] ) {
hideOne(elem, collapse);
}
}
};
var processElements = function(elems) {
var blockedRequestsReceived = function(details) {
hideMany(elems, details);
var i = elems.length;
while ( i-- ) {
hideMany(elems[i].querySelectorAll('img,iframe'), details);
} }
}; };
messaging.ask({ what: 'blockedRequest', url: this.src }, onAnswerReceived); messaging.ask({ what: 'blockedRequests' }, blockedRequestsReceived);
this.removeEventListener('load', onLoad);
}; };
elem.addEventListener('load', onLoad);
};
var hideBlockedElement = function(elem, collapse) { // rhill 2014-07-01: Avoid useless work: only nodes which are element are
// If `!important` is not there, going back using history will likely // of interest at this point -- because it is common that a lot of plain
// cause the hidden element to re-appear. // text nodes get added.
elem.style.visibility = 'hidden !important'; var addNodeLists = function(nodeLists) {
if ( collapse && elem.parentNode ) { var elems = [];
elem.parentNode.removeChild(elem); var i = nodeLists.length;
} var nodeList, j, node;
};
var hideBlockedElements = function(elems, details) {
var blockedRequests = details.blockedRequests;
var collapse = details.collapse;
var i = elems.length;
var elem, src;
while ( i-- ) {
elem = elems[i];
src = elem.src;
if ( typeof src !== 'string' ) {
continue;
}
if ( src === '' ) {
observeElement(elem);
} else if ( blockedRequests[src] ) {
hideBlockedElement(elem, collapse);
}
}
};
var hideBlockedElementInAddedNodes = function(nodes) {
var onBlockedRequestsReceived = function(details) {
hideBlockedElements(nodes, details);
var i = nodes.length;
var elem;
while ( i-- ) { while ( i-- ) {
elem = nodes[i]; nodeList = nodeLists[i];
if ( elem.querySelectorAll ) { j = nodeList.length;
hideBlockedElements(elem.querySelectorAll('img,iframe'), details); while ( j-- ) {
node = nodeList[j];
if ( node.querySelectorAll ) {
elems.push(node);
}
} }
} }
}; if ( elems.length ) {
messaging.ask({ what: 'blockedRequests' }, onBlockedRequestsReceived); processElements(elems);
};
(function() {
var onBlockedRequestsReceived = function(details) {
hideBlockedElements(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(); var onBlockedRequestsReceived = function(details) {
}; hideMany(document.querySelectorAll('img,iframe'), details);
};
messaging.ask({ what: 'blockedRequests' }, onBlockedRequestsReceived);
return {
addNodeLists: addNodeLists
};
})();
/******************************************************************************/ /******************************************************************************/
@ -435,12 +438,23 @@ if ( /^https?:\/\/./.test(window.location.href) === false ) {
/******************************************************************************/ /******************************************************************************/
cosmeticFiltering.onDOMContentLoaded();
/******************************************************************************/
// Observe changes in the DOM // 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/ // This fixes http://acid3.acidtests.org/
if ( document.body ) { if ( document.body ) {
// https://github.com/gorhill/httpswitchboard/issues/176 // 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
/******************************************************************************/ uDom('a').attr('target', '_blank');
uDom('a[href*="dashboard.html"]').attr('target', '_parent');
// Open links in the proper window uDom('.whatisthis').on('click', function() {
$('a').attr('target', '_blank'); uDom(this)
$('a[href*="dashboard.html"]').attr('target', '_parent'); .parent()
.find('.whatisthis-expandable')
$('.whatisthis').on('click', function() { .toggleClass('whatisthis-expanded');
$(this).parent() });
.find('.whatisthis-expandable')
.toggleClass('whatisthis-expanded');
});
/******************************************************************************/
}); });

View File

@ -23,31 +23,41 @@
(function() { (function() {
var loadDashboardPanel = function(hash) { /******************************************************************************/
var button = $(hash);
var url = button.data('dashboardPanelUrl'); var loadDashboardPanel = function(tab) {
$('iframe')[0].src = url; var tabButton = uDom('[data-dashboard-panel-url="' + tab + '"]');
$('.tabButton').each(function(){ if ( !tabButton ) {
var button = $(this); return;
button.toggleClass('selected', button.data('dashboardPanelUrl') === url); }
}); uDom('iframe').attr('src', tab);
} uDom('.tabButton').toggleClass('selected', false);
tabButton.toggleClass('selected', true);
};
/******************************************************************************/ /******************************************************************************/
var onTabClickHandler = function() { var onTabClickHandler = function() {
loadDashboardPanel(window.location.hash); loadDashboardPanel(uDom(this).attr('data-dashboard-panel-url'));
} };
/******************************************************************************/ /******************************************************************************/
$(function() { uDom.onLoad(function() {
$(window).on('hashchange', onTabClickHandler); var matches = window.location.search.slice(1).match(/\??(tab=([^&]+))?(.*)$/);
var hash = window.location.hash; var tab = '', q = '';
if ( hash.length < 2 ) { if ( matches && matches.length === 4 ) {
hash = '#thirdparty-filters'; 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. // Helper to deal with the i18n'ing of HTML files.
// jQuery must be present at this point.
window.addEventListener('load', function() { uDom.onLoad(function() {
var i; var fillin = function() {
var fillin = function(elem) { this.innerHTML = chrome.i18n.getMessage(this.getAttribute('data-i18n'));
var key = elem.getAttribute("data-i18n");
elem.innerHTML = chrome.i18n.getMessage(key);
}
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 µb = µBlock;
var r = { var r = {
globalBlockedRequestCount: µb.localSettings.blockedRequestCount, globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
@ -39,7 +39,8 @@ var getStats = function(request, callback) {
pageBlockedRequestCount: 0, pageBlockedRequestCount: 0,
pageAllowedRequestCount: 0, pageAllowedRequestCount: 0,
netFilteringSwitch: false, netFilteringSwitch: false,
cosmeticFilteringSwitch: false cosmeticFilteringSwitch: false,
logBlockedRequests: µb.userSettings.logBlockedRequests
}; };
var pageStore = µb.pageStoreFromTabId(request.tabId); var pageStore = µb.pageStoreFromTabId(request.tabId);
if ( pageStore ) { if ( pageStore ) {
@ -157,7 +158,7 @@ var onMessage = function(request, sender, callback) {
response = { response = {
collapse: µBlock.userSettings.collapseBlocked, collapse: µBlock.userSettings.collapseBlocked,
blockedRequests: pageStore ? pageStore.blockedRequests : {} blockedRequests: pageStore ? pageStore.blockedRequests : {}
} };
break; break;
// Check a single request // Check a single request
@ -165,7 +166,7 @@ var onMessage = function(request, sender, callback) {
response = { response = {
collapse: µBlock.userSettings.collapseBlocked, collapse: µBlock.userSettings.collapseBlocked,
blocked: pageStore && pageStore.blockedRequests[request.url] blocked: pageStore && pageStore.blockedRequests[request.url]
} };
break; break;
default: 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 // about.js
(function() { (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 // 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 // already logged, since the request stats are cached for a while after
// the page is no longer visible in a browser tab. // the page is no longer visible in a browser tab.
µb.updateBadge(this.tabId); µb.updateBadge(this.tabId);
if ( block === false ) { if ( reason === false ) {
this.perLoadAllowedRequestCount++; this.perLoadAllowedRequestCount++;
µb.localSettings.allowedRequestCount++; µb.localSettings.allowedRequestCount++;
return; 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/7
// https://github.com/gorhill/uBlock/issues/12 // 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 // Update badge, incrementally
// rhill 2013-11-09: well this sucks, I can't update icon/badge // 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. // notifying me, and this causes internal cached state to be out of sync.
PageStore.prototype.updateBadge = function() { PageStore.prototype.updateBadge = function() {

View File

@ -24,12 +24,10 @@
(function() { (function() {
/******************************************************************************/
/******************************************************************************/ /******************************************************************************/
var stats; var stats;
/******************************************************************************/
/******************************************************************************/ /******************************************************************************/
// https://github.com/gorhill/httpswitchboard/issues/345 // 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() { var renderStats = function() {
if ( !stats || !document.getElementById('switch') ) { if ( !stats ) {
return; return;
} }
uDom('#gotoLog').toggleClass('enabled', stats.logBlockedRequests);
var blocked = stats.pageBlockedRequestCount; var blocked = stats.pageBlockedRequestCount;
var total = stats.pageAllowedRequestCount + blocked; var total = stats.pageAllowedRequestCount + blocked;
var elem = document.getElementById('page-blocked'); var html = [];
if ( total === 0 ) { if ( total === 0 ) {
elem.innerHTML = '0'; html.push('0');
} else { } else {
elem.innerHTML = [ html.push(
formatNumber(blocked), formatNumber(blocked),
'<span class="dim">&nbsp;or&nbsp;', '<span class="dim">&nbsp;or&nbsp;',
(blocked * 100 / total).toFixed(0), (blocked * 100 / total).toFixed(0),
'%</span>' '%</span>'
].join(''); );
} }
uDom('#page-blocked').html(html.join(''));
blocked = stats.globalBlockedRequestCount; blocked = stats.globalBlockedRequestCount;
total = stats.globalAllowedRequestCount + blocked; total = stats.globalAllowedRequestCount + blocked;
elem = document.getElementById('total-blocked'); html = [];
if ( total === 0 ) { if ( total === 0 ) {
elem.innerHTML = '0'; html.push('0');
} else { } else {
elem.innerHTML = [ html.push(
formatNumber(blocked), formatNumber(blocked),
'<span class="dim">&nbsp;or&nbsp;', '<span class="dim">&nbsp;or&nbsp;',
(blocked * 100 / total).toFixed(0), (blocked * 100 / total).toFixed(0),
'%</span>' '%</span>'
].join(''); );
} }
uDom('#total-blocked').html(html.join(''));
toggleClassName( uDom('#switch .fa').toggleClass(
document.querySelector('#switch .fa'),
'off', 'off',
stats.pageURL === '' || !stats.netFilteringSwitch stats.pageURL === '' || !stats.netFilteringSwitch
); );
@ -154,34 +134,53 @@ var handleNetFilteringSwitch = function() {
if ( !stats || !stats.pageURL ) { if ( !stats || !stats.pageURL ) {
return; return;
} }
toggleClassName(this, 'off'); var off = uDom(this).toggleClass('off').hasClassName('off');
messaging.tell({ messaging.tell({
what: 'toggleNetFiltering', what: 'toggleNetFiltering',
hostname: stats.pageHostname, hostname: stats.pageHostname,
state: !hasClassName(this, 'off'), state: !off,
tabId: stats.tabId tabId: stats.tabId
}); });
}; };
/******************************************************************************/ /******************************************************************************/
var renderHeader = function() { var gotoDashboard = function() {
var hdr = document.getElementById('version'); messaging.tell({
hdr.innerHTML = hdr.innerHTML + 'v' + chrome.runtime.getManifest().version; 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() { 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 // Make menu only when popup html is fully loaded
window.addEventListener('load', function() { uDom.onLoad(function() {
renderHeader(); renderHeader();
renderStats(); renderStats();
installEventHandlers(); installEventHandlers();

View File

@ -19,11 +19,11 @@
Home: https://github.com/gorhill/uBlock 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 // TODO: use data-* to declare simple settings
var onUserSettingsReceived = function(details) { var onUserSettingsReceived = function(details) {
$('#collapse-blocked') uDom('#collapse-blocked')
.attr('checked', details.collapseBlocked === true) .attr('checked', details.collapseBlocked === true)
.on('change', function(){ .on('change', function(){
changeUserSettings('collapseBlocked', $(this).is(':checked')); changeUserSettings('collapseBlocked', this.checked);
}); });
$('#icon-badge') uDom('#icon-badge')
.attr('checked', details.showIconBadge === true) .attr('checked', details.showIconBadge === true)
.on('change', function(){ .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; 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 requestHostname = µburi.hostname;
var requestPath = µburi.path; 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 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 removeFragment = function(url) {
var pos = url.indexOf('#'); var pos = url.indexOf('#');
if ( pos < 0 ) { if ( pos < 0 ) {
@ -54,26 +62,19 @@ var gotoExtensionURL = function(url) {
var tabIndex = 9999; var tabIndex = 9999;
var targetUrl = chrome.extension.getURL(url); var targetUrl = chrome.extension.getURL(url);
var urlToFind = removeFragment(targetUrl);
var currentWindow = function(tabs) { var currentWindow = function(tabs) {
var updateProperties = { active: true }; var updateProperties = { active: true };
var i = tabs.length; var i = tabs.length;
while ( i-- ) { while ( i-- ) {
if ( removeFragment(tabs[i].url) !== urlToFind ) { if ( removeQuery(tabs[i].url) !== removeQuery(targetUrl) ) {
continue; continue;
} }
// If current tab in dashboard is different, force the new one, if // If current tab in dashboard is different, force the new one, if
// there is one, to be activated. // there is one, to be activated.
if ( tabs[i].url !== targetUrl ) { if ( tabs[i].url !== targetUrl ) {
if ( hasFragment(targetUrl) ) { updateProperties.url = 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); chrome.tabs.update(tabs[i].id, updateProperties);
return; 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; color: white;
background-color: #444; background-color: #444;
text-align: center; text-align: center;
cursor: pointer;
} }
a { a {
color: inherit; color: inherit;
@ -40,6 +41,17 @@ p {
white-space: nowrap; white-space: nowrap;
text-align: center; 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 { #switch-hint {
font-size: 11px; font-size: 11px;
color: #888; color: #888;
@ -56,16 +68,17 @@ p {
margin-bottom: 4px; margin-bottom: 4px;
text-align: center; text-align: center;
} }
#switch .fa { #gotoLog {
font-size: 64px; display: none;
color: green; font-size: 14px;
color: gray;
cursor: pointer; cursor: pointer;
} }
#switch .fa:hover { #gotoLog.hover {
opacity: 0.9; color: #444;
} }
#switch .fa.off { #gotoLog.enabled {
color: #ccc; display: inline;
} }
#options { #options {
margin: 16px 0 0 0; margin: 16px 0 0 0;
@ -80,17 +93,18 @@ p {
</head> </head>
<body> <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> <div>
<p id="switch"><span class="fa">&#xf011;</span></p> <p id="switch"><span class="fa">&#xf011;</span></p>
<p id="switch-hint" data-i18n="popupPowerSwitchInfo"></p> <p id="switch-hint" data-i18n="popupPowerSwitchInfo"></p>
<p style="font-size: 16px;" data-i18n="popupBlockedRequestPrompt"></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="page-blocked">?</p>
<p id="stats" data-i18n="popupBlockedSinceInstallPrompt"></p> <p id="stats" data-i18n="popupBlockedSinceInstallPrompt"></p>
<p id="total-blocked">?</p> <p id="total-blocked">?</p>
</div> </div>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script> <script src="js/i18n.js"></script>
<script src="js/messaging-client.js"></script> <script src="js/messaging-client.js"></script>
<script src="js/popup.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> <li><input id="icon-badge" type="checkbox"> <span data-i18n="settingsIconBadgePrompt"></span>
</ul> </ul>
<script src="lib/jquery-2.min.js"></script> <script src="js/udom.js"></script>
<script src="js/i18n.js"></script> <script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script> <script src="js/dashboard-common.js"></script>
<script src="js/messaging-client.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>