diff --git a/src/css/popup.css b/src/css/popup.css index 414bd6fcf..1e056a97d 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -244,42 +244,41 @@ body[dir="rtl"] #tooltip { text-align: right; } #firewallContainer > div { - background-color: #e6e6e6; border: 0; direction: ltr; + display: flex; + justify-content: flex-end; margin: 0; + margin-top: 1px; padding: 0; } -#firewallContainer > div:hover { - background-color: #f0f0f0; +#firewallContainer > div:first-child { + margin-top: 0; } -#firewallContainer > div:first-child ~ div:not([class]) { +#firewallContainer > div:first-child ~ div[data-des="*"] { display: none; } -#firewallContainer.minimized > div.isSubDomain { +#firewallContainer:not(.expanded) > div.isSubDomain:not(.expandException):not(.isRootContext), +#firewallContainer.expanded > div.isSubDomain.expandException:not(.isRootContext) { display: none; } #firewallContainer > div > span { - background-color: transparent; + background-color: #e6e6e6; border: none; - border-bottom: 1px solid white; box-sizing: border-box; -moz-box-sizing: border-box; color: #000; - display: inline-block; - height: 1.9em; - line-height: 1.9; - overflow: hidden; + display: inline-flex; + flex-shrink: 0; + line-height: 2; position: relative; - vertical-align: top; } #firewallContainer > div:first-of-type > span:first-of-type { cursor: pointer; } #firewallContainer > div > span:first-of-type { + justify-content: flex-end; padding-right: 2px; - position: relative; - text-overflow: ellipsis; width: calc(100% - 4em); } #firewallContainer > div > span:first-of-type > sup { @@ -295,45 +294,47 @@ body[dir="rtl"] #tooltip { #firewallContainer > div.isDomain > span.isIDN:first-of-type > sup::before { content: '\0416\2002'; } +#firewallContainer > div > span:first-of-type ~ span { + margin-left: 1px; + width: 4em; + } #firewallContainer > div > span:nth-of-type(2) { display: none; } -#firewallContainer > div > span:first-of-type ~ span { - border-left: 1px solid white; - width: 4em; - } #firewallContainer > div > span:nth-of-type(3), #firewallContainer > div > span:nth-of-type(4) { color: #444; - text-align: center; - } -#firewallContainer > div > span:nth-of-type(4) { display: none; + font-family: monospace; + text-align: center; } #firewallContainer > div.isDomain > span:first-of-type { font-weight: bold; } #firewallContainer > div:first-of-type > span:first-of-type::before { color: #aaa; - content: '\2012'; + content: '+'; padding-right: 0.25em; } -#firewallContainer.minimized > div:first-of-type > span:first-of-type::before { - content: '+'; +#firewallContainer.expanded > div:first-of-type > span:first-of-type::before { + content: '\2012'; } -#firewallContainer.minimized > div.isDomain > span:nth-of-type(3) { - display: none; - } -#firewallContainer.minimized > div.isDomain > span:nth-of-type(4) { - display: inline-block; +#firewallContainer > div[data-des="*"] > span:nth-of-type(3), +#firewallContainer > div.isSubDomain > span:nth-of-type(3), +#firewallContainer > div.isSubDomain.isRootContext > span:nth-of-type(3), +#firewallContainer.expanded > div:not(.expandException) > span:nth-of-type(3), +#firewallContainer:not(.expanded) > div.expandException > span:nth-of-type(3), +#firewallContainer:not(.expanded) > div.isDomain:not(.expandException) > span:nth-of-type(4), +#firewallContainer.expanded > div.isDomain.expandException > span:nth-of-type(4) { + display: inline-flex; + justify-content: space-between; } #firewallContainer > div > span[data-acount]::before, #firewallContainer > div > span[data-bcount]::after { - font-family: monospace; - position: absolute; + content: ' '; } #firewallContainer > div > span[data-acount]::before { - left: 0.1em; + padding-left: 0.1em; } #firewallContainer > div > span[data-acount="1"]::before { content: '+'; @@ -345,7 +346,7 @@ body[dir="rtl"] #tooltip { content: '+++'; } #firewallContainer > div > span[data-bcount]::after { - right: 0.1em; + padding-right: 0.1em; } #firewallContainer > div > span[data-bcount="1"]::after { content: '\2212'; @@ -361,10 +362,10 @@ body.advancedUser #firewallContainer > div > span:first-of-type { width: calc(100% - 8em); } body.advancedUser #firewallContainer > div > span:nth-of-type(2) { - display: inline-block; + display: inline-flex; } -body.advancedUser #firewallContainer > div:first-child ~ div:not([class]) { - display: block; +body.advancedUser #firewallContainer > div:first-child ~ div[data-des="*"] { + display: flex; } body.advancedUser #firewallContainer > div > span:first-of-type ~ span { cursor: pointer; @@ -373,10 +374,13 @@ body.advancedUser #firewallContainer > div > span:first-of-type ~ span { /** Small coloured label at the left of a row */ +#firewallContainer > div.isRootContext > span:first-of-type::before, #firewallContainer > div.allowed > span:first-of-type::before, #firewallContainer > div.blocked > span:first-of-type::before, -#firewallContainer.minimized > div.isDomain.totalAllowed > span:first-of-type::before, -#firewallContainer.minimized > div.isDomain.totalBlocked > span:first-of-type::before { +#firewallContainer:not(.expanded) > div.isDomain.totalAllowed:not(.expandException) > span:first-of-type::before, +#firewallContainer:not(.expanded) > div.isDomain.totalBlocked:not(.expandException) > span:first-of-type::before, +#firewallContainer.expanded > div.isDomain.totalAllowed.expandException > span:first-of-type::before, +#firewallContainer.expanded > div.isDomain.totalBlocked.expandException > span:first-of-type::before { box-sizing: border-box; content: ''; display: inline-block; @@ -386,28 +390,32 @@ body.advancedUser #firewallContainer > div > span:first-of-type ~ span { position: absolute; width: 7px; } +#firewallContainer > div.isRootContext > span:first-of-type::before { + background-color: rgb(127, 127, 127); + width: 14px !important; + } /** Source for color-blind color scheme from https://github.com/WyohKnott: https://github.com/chrisaljoudi/uBlock/issues/467#issuecomment-95177219 */ #firewallContainer > div.allowed > span:first-of-type::before, -#firewallContainer.minimized > div.isDomain.totalAllowed > span:first-of-type::before { +#firewallContainer > div.isDomain.totalAllowed > span:first-of-type::before { background-color: rgb(0, 160, 0); } #firewallContainer.colorBlind > div.allowed > span:first-of-type::before, -#firewallContainer.colorBlind.minimized > div.isDomain.totalAllowed > span:first-of-type::before { +#firewallContainer.colorBlind > div.isDomain.totalAllowed > span:first-of-type::before { background-color: rgb(255, 194, 57); } #firewallContainer > div.blocked > span:first-of-type::before, -#firewallContainer.minimized > div.isDomain.totalBlocked > span:first-of-type::before { +#firewallContainer > div.isDomain.totalBlocked > span:first-of-type::before { background-color: rgb(192, 0, 0); } #firewallContainer.colorBlind > div.blocked > span:first-of-type::before, -#firewallContainer.colorBlind.minimized > div.isDomain.totalBlocked > span:first-of-type::before { +#firewallContainer.colorBlind > div.isDomain.totalBlocked > span:first-of-type::before { background-color: rgb(0, 19, 110); } #firewallContainer > div.allowed.blocked > span:first-of-type::before, -#firewallContainer.minimized > div.isDomain.totalAllowed.totalBlocked > span:first-of-type::before { +#firewallContainer > div.isDomain.totalAllowed.totalBlocked > span:first-of-type::before { background-color: rgb(192, 160, 0); } /* Rule cells */ diff --git a/src/js/messaging.js b/src/js/messaging.js index e6eeefd00..4d7d6f960 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -217,7 +217,7 @@ const getHostnameDict = function(hostnameToCountMap) { blockCount: blockCount, allowCount: allowCount, totalBlockCount: 0, - totalAllowCount: 0 + totalAllowCount: 0, }; } return r; diff --git a/src/js/popup.js b/src/js/popup.js index 2683ccb90..b46d8b36e 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -244,12 +244,12 @@ const updateFirewallCell = function(scope, des, type, rule) { if ( hnDetails.allowCount !== 0 ) { cell.setAttribute('data-acount', Math.min(Math.ceil(Math.log(hnDetails.allowCount + 1) / Math.LN10), 3)); } else { - cell.removeAttribute('data-acount'); + cell.setAttribute('data-acount', '0'); } if ( hnDetails.blockCount !== 0 ) { cell.setAttribute('data-bcount', Math.min(Math.ceil(Math.log(hnDetails.blockCount + 1) / Math.LN10), 3)); } else { - cell.removeAttribute('data-bcount'); + cell.setAttribute('data-bcount', '0'); } if ( hnDetails.domain !== des ) { @@ -260,12 +260,12 @@ const updateFirewallCell = function(scope, des, type, rule) { if ( hnDetails.totalAllowCount !== 0 ) { cell.setAttribute('data-acount', Math.min(Math.ceil(Math.log(hnDetails.totalAllowCount + 1) / Math.LN10), 3)); } else { - cell.removeAttribute('data-acount'); + cell.setAttribute('data-acount', '0'); } if ( hnDetails.totalBlockCount !== 0 ) { cell.setAttribute('data-bcount', Math.min(Math.ceil(Math.log(hnDetails.totalBlockCount + 1) / Math.LN10), 3)); } else { - cell.removeAttribute('data-bcount'); + cell.setAttribute('data-bcount', '0'); } }; @@ -302,9 +302,9 @@ const buildAllFirewallRows = function() { dfHotspots.detach(); // Update incrementally: reuse existing rows if possible. - let rowContainer = document.getElementById('firewallContainer'); - let toAppend = document.createDocumentFragment(); - let rowTemplate = document.querySelector('#templates > div:nth-of-type(1)'); + const rowContainer = document.getElementById('firewallContainer'); + const toAppend = document.createDocumentFragment(); + const rowTemplate = document.querySelector('#templates > div:nth-of-type(1)'); let row = rowContainer.querySelector('div:nth-of-type(7) + div'); for ( const des of allHostnameRows ) { @@ -331,12 +331,14 @@ const buildAllFirewallRows = function() { span.title = isDomain && isPunycoded ? des : ''; const classList = row.classList; + classList.toggle('isRootContext', des === popupData.pageHostname); classList.toggle('isDomain', isDomain); classList.toggle('isSubDomain', !isDomain); classList.toggle('allowed', hnDetails.allowCount !== 0); classList.toggle('blocked', hnDetails.blockCount !== 0); classList.toggle('totalAllowed', hnDetails.totalAllowCount !== 0); classList.toggle('totalBlocked', hnDetails.totalBlockCount !== 0); + classList.toggle('expandException', expandExceptions.has(hnDetails.domain)); row = row.nextElementSibling; } @@ -445,7 +447,7 @@ const renderPopup = function() { popupData.pageURL === '' || popupData.netFilteringSwitch !== true ); - let canElementPicker = popupData.canElementPicker === true && + const canElementPicker = popupData.canElementPicker === true && popupData.netFilteringSwitch === true; uDom.nodeFromId('gotoPick').classList.toggle('enabled', canElementPicker); uDom.nodeFromId('gotoZap').classList.toggle('enabled', canElementPicker); @@ -510,16 +512,13 @@ const renderPopup = function() { dfPaneVisible === true ); - elem = uDom.nodeFromId('firewallContainer'); - elem.classList.toggle( - 'minimized', - popupData.firewallPaneMinimized === true - ); - elem.classList.toggle( + uDom.nodeFromId('firewallContainer').classList.toggle( 'colorBlind', popupData.colorBlindFriendly === true ); + setGlobalExpand(popupData.firewallPaneMinimized === false, true); + // Build dynamic filtering pane only if in use if ( dfPaneVisible ) { buildAllFirewallRows(); @@ -923,7 +922,66 @@ document.addEventListener( /******************************************************************************/ -const toggleMinimize = function(ev) { +const expandExceptions = new Set(); + +(( ) => { + try { + const exceptions = JSON.parse( + vAPI.localStorage.getItem('popupExpandExceptions') + ); + if ( Array.isArray(exceptions) === false ) { return; } + for ( const exception of exceptions ) { + expandExceptions.add(exception); + } + } + catch(ex) { + } + +})(); + +const saveExpandExceptions = function() { + vAPI.localStorage.setItem( + 'popupExpandExceptions', + JSON.stringify(Array.from(expandExceptions)) + ); +}; + +const setGlobalExpand = function(state, internal = false) { + uDom('.expandException').removeClass('expandException'); + if ( state ) { + uDom('#firewallContainer').addClass('expanded'); + } else { + uDom('#firewallContainer').removeClass('expanded'); + } + positionRulesetTools(); + if ( internal ) { return; } + popupData.firewallPaneMinimized = !state; + expandExceptions.clear(); + saveExpandExceptions(); + messaging.send('popupPanel', { + what: 'userSettings', + name: 'firewallPaneMinimized', + value: popupData.firewallPaneMinimized, + }); +}; + +const setSpecificExpand = function(domain, state, internal = false) { + const unodes = uDom(`[data-des="${domain}"],[data-des$=".${domain}"]`); + if ( state ) { + unodes.addClass('expandException'); + } else { + unodes.removeClass('expandException'); + } + if ( internal ) { return; } + if ( state ) { + expandExceptions.add(domain); + } else { + expandExceptions.delete(domain); + } + saveExpandExceptions(); +}; + +uDom('[data-i18n="popupAnyRulePrompt"]').on('click', ev => { // Special display mode: in its own tab/window, with no vertical restraint. // Useful to take snapshots of the whole list of domains -- example: // https://github.com/gorhill/uBlock/issues/736#issuecomment-178879944 @@ -931,25 +989,31 @@ const toggleMinimize = function(ev) { messaging.send('popupPanel', { what: 'gotoURL', details: { - url: 'popup.html?tabId=' + popupData.tabId + '&responsive=1', + url: `popup.html?tabId=${popupData.tabId}&responsive=1`, select: true, - index: -1 + index: -1, }, }); vAPI.closePopup(); return; } - popupData.firewallPaneMinimized = - uDom.nodeFromId('firewallContainer').classList.toggle('minimized'); + setGlobalExpand( + uDom('#firewallContainer').hasClass('expanded') === false + ); +}); - messaging.send('popupPanel', { - what: 'userSettings', - name: 'firewallPaneMinimized', - value: popupData.firewallPaneMinimized, - }); - positionRulesetTools(); -}; +uDom('#firewallContainer').on( + 'click', '.isDomain[data-type="*"] > span:first-of-type', + ev => { + const div = ev.target.closest('[data-des]'); + if ( div === null ) { return; } + setSpecificExpand( + div.getAttribute('data-des'), + div.classList.contains('expandException') === false + ); + } +); /******************************************************************************/ @@ -1137,7 +1201,6 @@ uDom('h2').on('click', toggleFirewallPane); uDom('.hnSwitch').on('click', ev => { toggleHostnameSwitch(ev); }); uDom('#saveRules').on('click', saveFirewallRules); uDom('#revertRules').on('click', ( ) => { revertFirewallRules(); }); -uDom('[data-i18n="popupAnyRulePrompt"]').on('click', toggleMinimize); uDom('body').on('mouseenter', '[data-tip]', onShowTooltip) .on('mouseleave', '[data-tip]', onHideTooltip); diff --git a/src/popup.html b/src/popup.html index 57366f744..778e4a431 100644 --- a/src/popup.html +++ b/src/popup.html @@ -42,7 +42,7 @@