From 43e773aab2e3c6b4725eac8c586d9b874a23e420 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 31 Dec 2014 10:47:19 -0500 Subject: [PATCH] finally a fully working draft: now onto myriad finishing touches --- src/css/popup.css | 100 ++++++++++++++------- src/js/dynamic-net-filtering.js | 53 +++++------- src/js/pagestore.js | 3 + src/js/popup.js | 148 ++++++++++++++++++++++---------- src/js/ublock.js | 4 +- src/popup.html | 21 ++--- 6 files changed, 213 insertions(+), 116 deletions(-) diff --git a/src/css/popup.css b/src/css/popup.css index 6cf491d7e..b41bd6edd 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -8,13 +8,13 @@ body { white-space: nowrap; } h1,h2,h3,h4 { - margin: 0; - padding: 4px; + background-color: #444; border: 0; color: white; - background-color: #444; - text-align: center; cursor: pointer; + margin: 0; + padding: 4px; + text-align: center; } a { color: inherit; @@ -24,9 +24,9 @@ a { outline: 0; } #version { - margin-left: 1em; - font-weight: normal; font-size: 11px; + font-weight: normal; + margin-left: 1em; } body > div { background-color: transparent; @@ -47,14 +47,14 @@ body > div:nth-of-type(2) { } p { margin: 16px 0; - white-space: nowrap; text-align: center; + white-space: nowrap; } #switch .fa { - margin: 0; - font-size: 96px; color: green; cursor: pointer; + font-size: 96px; + margin: 0; } #switch .fa:hover { opacity: 0.9; @@ -63,22 +63,22 @@ p { color: #ccc; } #switch-hint { - font-size: 11px; color: #888; + font-size: 11px; } [data-i18n="popupBlockedRequestPrompt"] { font-size: 16px; } #page-blocked { - margin-top: 4px; font-size: 20px; + margin-top: 4px; } #total-blocked { - margin-top: 4px; - margin-bottom: 8px; font-size: 14px; + margin-bottom: 8px; + margin-top: 4px; } -#stats { +.stats { margin-bottom: 4px; text-align: center; } @@ -142,11 +142,11 @@ body.dynamicFilteringEnabled #dynamicFilteringToggler::before { margin: 0; padding: 0; text-align: right; - width: 7px; + width: 5px; } body.dynamicFilteringEnabled #dynamicFilteringContainer { display: block; - width: 280px; + width: auto; } #dynamicFilteringContainer > div { background-color: transparent; @@ -154,7 +154,7 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer { direction: ltr; margin: 0; padding: 0; - width: 280px; + width: 320px; } body.dynamicFilteringEnabled #dynamicFilteringContainer > div { background-color: #e6e6e6; @@ -176,8 +176,9 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div:hover { color: transparent; display: inline-block; height: 24px; - line-height: 28px; + line-height: 24px; pointer-events: none; + position: relative; vertical-align: top; } body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span { @@ -188,33 +189,74 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span { #dynamicFilteringContainer > div > span:nth-of-type(1) { border-right: 1px solid white; padding-right: 4px; - width: 75%; + width: 70%; } #dynamicFilteringContainer > div > span:nth-of-type(2) { cursor: pointer; - width: 9%; + width: 15%; } #dynamicFilteringContainer > div > span:nth-of-type(3) { border-left: 1px solid white; cursor: pointer; text-align: center; - width: 16%; + width: 15%; } #dynamicFilteringContainer > div.isDomain > span:nth-of-type(1) { font-weight: bold; } body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span:nth-of-type(3) { - color: #444; + color: #666; pointer-events: auto; } -#dynamicFilteringContainer span.blocked[data-src] { - background-color: #fbb; +#dynamicFilteringContainer span.aRule { + background-color: rgba(0, 160, 0, 0.3); } -#dynamicFilteringContainer span.ownFilter[data-src] { - background-color: #bbb; +#dynamicFilteringContainer span.bRule { + background-color: rgba(192, 0, 0, 0.3); + } +#dynamicFilteringContainer span.nRule { + background-color: rgba(96, 96, 96, 0.3); + } +#dynamicFilteringContainer span.aRule.ownRule { + background-color: rgba(0, 160, 0, 1); color: white; } -#dynamicFilteringContainer span.blocked.ownFilter[data-src] { - background-color: #f66; +#dynamicFilteringContainer span.bRule.ownRule { + background-color: rgba(192, 0, 0, 1); color: white; - } \ No newline at end of file + } +#dynamicFilteringContainer span.nRule.ownRule { + background-color: rgba(108, 108, 108, 1); + color: white; + } + +#actionSelector { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } +#actionSelector > span { + display: inline-block; + height: 24px; + opacity: 0.2; + width: 33.33%; + } +#actionSelector > span:hover { + opacity: 0.75; + } +#actionSelector > span:nth-of-type(1) { + background-color: rgb(0, 160, 0); + } +#actionSelector > span:nth-of-type(2) { + background-color: rgb(108, 108, 108); + } +#actionSelector > span:nth-of-type(3) { + background-color: rgb(192, 0, 0); + } +#dynamicFilteringContainer span.aRule #actionSelector > span:nth-of-type(1), +#dynamicFilteringContainer span.nRule #actionSelector > span:nth-of-type(2), +#dynamicFilteringContainer span.bRule #actionSelector > span:nth-of-type(3) { + visibility: hidden; + } diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js index d1d8bfe1a..5b104a750 100644 --- a/src/js/dynamic-net-filtering.js +++ b/src/js/dynamic-net-filtering.js @@ -130,52 +130,43 @@ Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) { /******************************************************************************/ -Matrix.prototype.blockCell = function(srcHostname, desHostname, type) { +Matrix.prototype.unsetCell = function(srcHostname, desHostname, type) { this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 1 ) { + if ( this.r === 0 ) { + return false; + } + this.setCell(srcHostname, desHostname, type, 0); + return true; +}; + +/******************************************************************************/ + +Matrix.prototype.setCellZ = function(srcHostname, desHostname, type, action) { + this.evaluateCellZY(srcHostname, desHostname, type); + if ( this.r === action ) { return false; } this.setCell(srcHostname, desHostname, type, 0); this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 1 ) { + if ( this.r === action ) { return true; } - this.setCell(srcHostname, desHostname, type, 1); + this.setCell(srcHostname, desHostname, type, action); return true; }; +/******************************************************************************/ + +Matrix.prototype.blockCell = function(srcHostname, desHostname, type) { + return this.setCellZ(srcHostname, desHostname, type, 1); +}; + // https://www.youtube.com/watch?v=Csewb_eIStY /******************************************************************************/ Matrix.prototype.allowCell = function(srcHostname, desHostname, type) { - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 2 ) { - return false; - } - this.setCell(srcHostname, desHostname, type, 0); - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 2 ) { - return true; - } - this.setCell(srcHostname, desHostname, type, 2); - return true; -}; - -/******************************************************************************/ - -Matrix.prototype.unsetCell = function(srcHostname, desHostname, type) { - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 0 ) { - return false; - } - this.setCell(srcHostname, desHostname, type, 0); - this.evaluateCellZY(srcHostname, desHostname, type); - if ( this.r === 0 || this.r === 3 ) { - return true; - } - this.setCell(srcHostname, desHostname, type, 3); - return true; + return this.setCellZ(srcHostname, desHostname, type, 2); }; /******************************************************************************/ diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 33715d015..ad70f4281 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -440,6 +440,9 @@ PageStore.prototype.filterRequest = function(context) { //console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL); this.recordResult(context.requestType, requestURL, result); + // TODO: send this to a dev-panel tool + //console.debug('[%s, %s] = "%s"', context.requestHostname, context.requestType, result); + var requestHostname = context.requestHostname; if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) { this.hostnameToCountMap[requestHostname] = 0; diff --git a/src/js/popup.js b/src/js/popup.js index cc21baeb3..56dc07cdf 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -46,8 +46,9 @@ var scopeToSrcHostnameMap = { '.': '' }; var threePlus = '+++'; -var threeMinus = '\u2012\u2012\u2012'; +var threeMinus = '−−−'; var sixSpace = '\u2007\u2007\u2007\u2007\u2007\u2007'; +var dynaHotspots = null; /******************************************************************************/ @@ -102,6 +103,7 @@ var addDynamicFilterRow = function(des) { row.toggleClass('isDomain', isDomain); if ( hnDetails.allowCount !== 0 ) { touchedDomains[hnDetails.domain] = true; + row.addClass('wasTouched'); } row.appendTo('#dynamicFilteringContainer'); @@ -120,7 +122,7 @@ var addDynamicFilterRow = function(des) { /******************************************************************************/ -var syncDynamicFilter = function(scope, des, type, result) { +var syncDynamicFilterCell = function(scope, des, type, result) { var selector = '#dynamicFilteringContainer span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]'; var cell = uDom(selector); @@ -129,17 +131,20 @@ var syncDynamicFilter = function(scope, des, type, result) { cell = addDynamicFilterRow(des).descendants(selector); } - var blocked = result.charAt(1) === 'b'; - cell.toggleClass('blocked', blocked); + cell.removeClass(); + var action = result.charAt(1); + if ( action !== '' ) { + cell.toggleClass(action + 'Rule', true); + } // Use dark shade visual cue if the filter is specific to the cell. - var ownFilter = false; + var ownRule = false; var matches = reSrcHostnameFromResult.exec(result); if ( matches !== null ) { - ownFilter = matches[2] === des && + ownRule = matches[2] === des && matches[1] === scopeToSrcHostnameMap[scope]; } - cell.toggleClass('ownFilter', ownFilter); + cell.toggleClass('ownRule', ownRule); if ( scope !== '.' || type !== '*' ) { return; @@ -150,29 +155,30 @@ var syncDynamicFilter = function(scope, des, type, result) { var hnDetails = stats.hostnameDict[des]; var aCount = Math.min(Math.ceil(Math.log10(hnDetails.allowCount + 1)), 3); var bCount = Math.min(Math.ceil(Math.log10(hnDetails.blockCount + 1)), 3); - cell.text( - threePlus.slice(0, aCount) + - sixSpace.slice(aCount + bCount) + - threeMinus.slice(0, bCount) - ); + // IMPORTANT: It is completely assumed the first node is a TEXT_NODE, so + // ensure this in the HTML file counterpart when you make + // changes + cell.nodeAt(0).firstChild.nodeValue = threePlus.slice(0, aCount) + + sixSpace.slice(aCount + bCount) + + threeMinus.slice(0, bCount); }; /******************************************************************************/ var syncAllDynamicFilters = function() { - var hasBlock = false; + var hasRule = false; var rules = stats.dynamicFilterRules; var type, result; var types = dynaTypes; var i = types.length; while ( i-- ) { type = types[i]; - syncDynamicFilter('/', '*', type, rules['/ * ' + type] || ''); + syncDynamicFilterCell('/', '*', type, rules['/ * ' + type] || ''); result = rules['. * ' + type] || ''; - if ( result.charAt(1) === 'b' ) { - hasBlock = true; + if ( result.charAt(1) !== '' ) { + hasRule = true; } - syncDynamicFilter('.', '*', type, result); + syncDynamicFilterCell('.', '*', type, result); } // Sort hostnames. First-party hostnames must always appear at the top @@ -185,10 +191,9 @@ var syncAllDynamicFilters = function() { if ( key.slice(-1) !== '*' ) { continue; } - syncDynamicFilter(key.charAt(0), key.slice(2, key.indexOf(' ', 2)), '*', rules[key]); + syncDynamicFilterCell(key.charAt(0), key.slice(2, key.indexOf(' ', 2)), '*', rules[key]); } - uDom('body').toggleClass('hasDynamicBlock', hasBlock); uDom('#privacyInfo > b').text(Object.keys(touchedDomains).length); }; @@ -333,30 +338,6 @@ var gotoLink = function(ev) { /******************************************************************************/ -var onDynamicFilterClicked = function(ev) { - // This can happen on pages where uBlock does not work - if ( typeof stats.pageHostname !== 'string' || stats.pageHostname === '' ) { - return; - } - var cell = uDom(ev.target); - var scope = cell.attr('data-src') === '/' ? '*' : stats.pageHostname; - var onDynamicFilterChanged = function(details) { - cachePopupData(details); - syncAllDynamicFilters(); - }; - messager.send({ - what: 'toggleDynamicFilter', - tabId: stats.tabId, - pageHostname: stats.pageHostname, - srcHostname: scope, - desHostname: cell.attr('data-des'), - requestType: cell.attr('data-type'), - block: cell.hasClass('blocked') === false - }, onDynamicFilterChanged); -}; - -/******************************************************************************/ - var toggleDynamicFiltering = function(ev) { var el = uDom('body'); el.toggleClass('dynamicFilteringEnabled'); @@ -369,6 +350,79 @@ var toggleDynamicFiltering = function(ev) { /******************************************************************************/ +var mouseenterCellHandler = function() { + if ( uDom(this).hasClass('ownRule') === false ) { + dynaHotspots.appendTo(this); + } +}; + +var mouseleaveCellHandler = function() { + dynaHotspots.detach(); +}; + +/******************************************************************************/ + +var setDynamicFilter = function(src, des, type, action) { + // This can happen on pages where uBlock does not work + if ( typeof stats.pageHostname !== 'string' || stats.pageHostname === '' ) { + return; + } + var onDynamicFilterChanged = function(details) { + cachePopupData(details); + syncAllDynamicFilters(); + }; + messager.send({ + what: 'toggleDynamicFilter', + tabId: stats.tabId, + pageHostname: stats.pageHostname, + srcHostname: src, + desHostname: des, + requestType: type, + action: action + }, onDynamicFilterChanged); +}; + +/******************************************************************************/ + +var unsetDynamicFilterHandler = function() { + var cell = uDom(this); + setDynamicFilter( + cell.attr('data-src') === '/' ? '*' : stats.pageHostname, + cell.attr('data-des'), + cell.attr('data-type'), + 0 + ); + dynaHotspots.appendTo(cell); +}; + +/******************************************************************************/ + +var setDynamicFilterHandler = function() { + var hotspot = uDom(this); + var cell = hotspot.ancestors('[data-src]'); + if ( cell.length === 0 ) { + return; + } + var action = 0; + var hotspotId = hotspot.attr('id'); + if ( hotspotId === 'dynaAllow' ) { + action = 2; + } else if ( hotspotId === 'dynaNoop' ) { + action = 3 + } else { + action = 1; + } + setDynamicFilter( + cell.attr('data-src') === '/' ? '*' : stats.pageHostname, + cell.attr('data-des'), + cell.attr('data-type'), + action + ); + dynaHotspots.detach(); +}; + +/******************************************************************************/ + var installEventHandlers = function() { uDom('h1,h2,h3,h4').on('click', gotoDashboard); uDom('#switch .fa').on('click', toggleNetFilteringSwitch); @@ -376,7 +430,13 @@ var installEventHandlers = function() { uDom('#gotoPick').on('click', gotoPick); uDom('a[href^=http]').on('click', gotoLink); uDom('#dynamicFilteringToggler').on('click', toggleDynamicFiltering); - uDom('#dynamicFilteringContainer').on('click', 'span[data-type]', onDynamicFilterClicked); + + uDom('#dynamicFilteringContainer').on('click', 'span[data-src]', unsetDynamicFilterHandler); + uDom('#dynamicFilteringContainer') + .on('mouseenter', '[data-src]', mouseenterCellHandler) + .on('mouseleave', '[data-src]', mouseleaveCellHandler); + dynaHotspots = uDom('#actionSelector'); + dynaHotspots.on('click', 'span', setDynamicFilterHandler).detach(); }; /******************************************************************************/ diff --git a/src/js/ublock.js b/src/js/ublock.js index e34e6c621..c7e402555 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -308,8 +308,8 @@ var matchWhitelistDirective = function(url, hostname, directive) { µBlock.toggleDynamicFilter = function(details) { var changed = false; - if ( details.block ) { - changed = this.dynamicNetFilteringEngine.blockCell(details.srcHostname, details.desHostname, details.requestType); + if ( details.action !== 0 ) { + changed = this.dynamicNetFilteringEngine.setCellZ(details.srcHostname, details.desHostname, details.requestType, details.action); } else { changed = this.dynamicNetFilteringEngine.unsetCell(details.srcHostname, details.desHostname, details.requestType); } diff --git a/src/popup.html b/src/popup.html index a902c607b..257e548de 100644 --- a/src/popup.html +++ b/src/popup.html @@ -12,30 +12,31 @@

v

-
images
-
inline scripts
-
1st-party scripts
-
3rd-party scripts
-
3rd-party frames
+
images
+
inline scripts
+
1st-party scripts
+
3rd-party scripts
+
3rd-party frames
? distinct domains touched
-
+

-

+

?

-

+

?