diff --git a/src/css/common.css b/src/css/common.css index ce51448d..ac9b0be9 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -30,6 +30,24 @@ vertical-align: baseline; display: inline-block; } +.fa-icon { + align-self: center; + background-color: transparent; + border: border: 1px solid transparent; + display: inline-block; + height: 1.2em; + vertical-align: middle; + width: 1.2em; + } +.fa-icon:hover { + background-color: #eee; + } +.fa-icon.disabled { + fill: #888; + opacity: 0.4; + stroke: #fff; + pointer-events: none; + } body { font-size: 14px; diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index a4800826..5a38ce5f 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -9,6 +9,12 @@ body { padding: 0; width: 100%; } +.fa-icon { + cursor: pointer; + height: 1.5em; + padding: 0.5em 1em; + width: 1.5em; + } #toolbar { background-color: white; border: 0; @@ -23,42 +29,73 @@ body { width: 100%; z-index: 10; } -#toolbar .button { - background-color: white; - border: none; - box-sizing: border-box; - -moz-box-sizing: border-box; - cursor: pointer; - display: inline-block; - font-size: 150%; - margin: 0; - padding: 8px; - } -#toolbar .button.disabled { - opacity: 0.2; - pointer-events: none; - } -#toolbar .button:hover { - background-color: #eee; - } #toolbar > div { + display: flex; + padding: 0.5em; white-space: nowrap; } -#toolbar > div:first-of-type { - font-size: 120%; - } -#toolbar > div > * { - vertical-align: middle; - } #pageSelector { width: 28em; + margin-right: 0.5em; padding: 0.2em 0; } -body #compactViewToggler.button:before { - content: '\f102'; + +@keyframes popupPanelShow { + from { opacity: 0; } + to { opacity: 1; } +} +#popupPanelContainer { + background: white; + border: 1px solid gray; + display: none; + overflow: hidden; + position: fixed; + right: 0; + /*top: 0;*/ + z-index: 2000; + } +body.popupPanelOn #popupPanelContainer { + animation-duration: 0.25s; + animation-name: popupPanelShow; + display: block; + } +#popupPanelContainer.hide { + width: 6em !important; +} +#popupPanelContainer > div { + background: #888; + border: 0; + display: none; + text-align: right; + } +#popupPanelContainer > div > span { + color: #ccc; + cursor: pointer; + display: inline-block; + font: 14px FontAwesome; + padding: 3px; + } +#popupPanelContainer > div > span:hover { + color: white; + } +#popupPanelContainer > iframe { + border: 0; + padding: 0; + margin: 0; + width: 100%; + } +#popupPanelContainer.hide > iframe { + display: none; + } + +#popupPanelButton > use { + transform: scaleY(0.4); + } +body.popupPanelOn #popupPanelButton > use { + transform: scaleY(1); } -body.compactView #compactViewToggler.button:before { - content: '\f103'; +body.compactView #compactViewToggler { + transform: rotateZ(180deg); } #filterButton { opacity: 0.25; @@ -189,7 +226,7 @@ body.compactView #content tr:not(.vExpanded) td { position: fixed; top: 0; width: 100vw; - z-index: 1000; + z-index: 5000; } .modalDialog > .dialog { background-color: white; @@ -218,46 +255,20 @@ body.compactView #content tr:not(.vExpanded) td { justify-content: space-around; margin-left: 0.5em; } -.ruleEditorToolbar button { - background-color: white; - border: 0; - color: black; - cursor: pointer; - margin: 0; - padding: 0.2em; - position: relative; - } -.ruleEditorToolbar button:hover { - background-color: #eee; - } -.ruleEditorToolbar button.disabled { - color: #ccc; - } -.ruleEditorToolbar button.fa { - font: 1.7em FontAwesome; - min-width: 1.5em; - } -.ruleEditorToolbar button > span.badge { - background-color: rgba(240,240,240,0.75); - bottom: 1px; - color: #000; - display: inline-block; +.ruleEditorToolbar .fa-icon > .badge { font-family: sans-serif; - font-size: 40%; - padding: 1px 1px; + font-size: 80%; pointer-events: none; - position: absolute; - right: 1px; } -.ruleEditorToolbar button.disabled > span.badge { +.ruleEditorToolbar .fa-icon.disabled > .badge { display: none; } -button.scopeRel { - color: #24c; +.fa-icon.scopeRel { + fill: #24c; } -body[data-scope="*"] button.scopeRel { - color: #000; +body[data-scope="*"] .fa-icon.scopeRel { + fill: #000; } .ruleWidgets { diff --git a/src/img/fontawesome/LICENSE.txt b/src/img/fontawesome/LICENSE.txt new file mode 100644 index 00000000..0abfaa6e --- /dev/null +++ b/src/img/fontawesome/LICENSE.txt @@ -0,0 +1,34 @@ +Font Awesome Free License +------------------------- + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) +In the Font Awesome Free download, the CC BY 4.0 license applies to all icons +packaged as SVG and JS file types. + +# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) +In the Font Awesome Free download, the SIL OLF license applies to all icons +packaged as web and desktop font files. + +# Code: MIT License (https://opensource.org/licenses/MIT) +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +# Attribution +Attribution is required by MIT, SIL OLF, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +# Brand Icons +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/src/img/fontawesome/fontawesome-defs.svg b/src/img/fontawesome/fontawesome-defs.svg new file mode 100644 index 00000000..c418d628 --- /dev/null +++ b/src/img/fontawesome/fontawesome-defs.svg @@ -0,0 +1,16 @@ + + diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 9eeeb44c..3ee8613f 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -537,32 +537,39 @@ var readLogBufferAsync = function() { /******************************************************************************/ +var tabIdFromPageSelector = function() { + let tabId = parseInt(document.getElementById('pageSelector').value, 10); + return isNaN(tabId) === false ? tabId : 0; +}; + var pageSelectorChanged = function() { let style = document.getElementById('tabFilterer'); - let tabId = document.getElementById('pageSelector').value; + let tabId = tabIdFromPageSelector(); let sheet = style.sheet; while ( sheet.cssRules.length !== 0 ) { sheet.deleteRule(0); } - if ( tabId.length !== 0 ) { + if ( tabId !== 0 ) { sheet.insertRule( '#content table tr:not([data-tabid="' + tabId + '"]) { display: none; }', 0 ); } - uDom('#refresh').toggleClass('disabled', /^\d+$/.test(tabId) === false); + document.getElementById('reloadTab').classList.toggle('disabled', tabId <= 0); + document.getElementById('popupPanelButton').classList.toggle('disabled', tabId === 0); + popupPanel.update(); }; /******************************************************************************/ var reloadTab = function(ev) { - let tabId = document.getElementById('pageSelector').value; - if ( /^\d+$/.test(tabId) === false ) { return; } + let tabId = tabIdFromPageSelector(); + if ( tabId <= 0 ) { return; } vAPI.messaging.send( 'default', { what: 'forceReloadTab', - tabId: parseInt(tabId, 10), + tabId: tabId, bypassCache: ev && (ev.ctrlKey || ev.metaKey || ev.shiftKey) } ); @@ -592,6 +599,135 @@ var onMaxEntriesChanged = function() { /******************************************************************************/ +var popupPanel = (function() { + let tabId = 0; + let popupObserver = null; + let timer; + + let resizePopup = function() { + if ( timer !== undefined ) { + cancelAnimationFrame(timer); + timer = undefined; + } + let container = document.getElementById('popupPanelContainer'); + let popup = container.querySelector('iframe'); + if ( popup === null ) { return; } + let popupBody = popup.contentWindow.document.body; + if ( + popupBody.clientWidth !== 0 && + container.clientWidth !== popupBody.clientWidth + ) { + container.style.setProperty('width', popupBody.clientWidth + 'px'); + } + popup.style.removeProperty('height'); + if ( + popupBody.clientHeight !== 0 && + popup.clientHeight !== popupBody.clientHeight + ) { + popup.style.setProperty('height', popupBody.clientHeight + 'px'); + } + let ph = document.documentElement.clientHeight; + let crect = container.getBoundingClientRect(); + if ( crect.height > ph ) { + popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)'); + } + // Adjust width for presence/absence of vertical scroll bar which may + // have appeared as a result of last operation. + let cw = container.clientWidth; + let dw = popup.contentWindow.document.documentElement.clientWidth; + if ( cw !== dw ) { + container.style.setProperty('width', 2 * cw - dw + 'px'); + } + }; + + let onResizeRequested = function() { + let popup = document.querySelector('#popupPanelContainer iframe'); + if ( popup === null ) { return; } + let popupBody = popup.contentWindow.document.body; + if ( popupBody.hasAttribute('data-resize-popup') === false ) { return; } + popupBody.removeAttribute('data-resize-popup'); + if ( timer === undefined ) { + timer = requestAnimationFrame(( ) => { + timer = undefined; + resizePopup(); + }); + } + }; + + let onLoad = function() { + resizePopup(); + let popup = document.querySelector('#popupPanelContainer iframe'); + if ( popup === null ) { return; } + let popupBody = popup.contentDocument.body; + popupBody.removeAttribute('data-resize-popup'); + if ( popupObserver === null ) { + popupObserver = new MutationObserver(onResizeRequested); + } + popupObserver.observe(popupBody, { + attributes: true, + attributesFilter: [ 'data-resize-popup' ] + }); + document.body.classList.add('popupPanelOn'); + }; + + let start = function() { + let newTabId = tabIdFromPageSelector(); + if ( newTabId === 0 ) { + return stop(); + } + if ( newTabId === tabId ) { return; } + tabId = newTabId; + let container = document.getElementById('popupPanelContainer'); + let popup = container.querySelector('iframe'); + if ( popup === null ) { + popup = document.createElement('iframe'); + popup.addEventListener('load', onLoad); + } + if ( popupObserver !== null ) { + popupObserver.disconnect(); + } + popup.setAttribute('src', 'popup.html?tabId=' + tabId); + if ( popup.parentNode === null ) { + container.appendChild(popup); + } + }; + + let stop = function() { + document.body.classList.remove('popupPanelOn'); + popupObserver.disconnect(); + popupObserver = null; + let popup = document.querySelector('#popupPanelContainer iframe'); + if ( popup !== null ) { + popup.removeEventListener('load', onLoad); + removeSelf(popup); + } + tabId = 0; + }; + + return { + get tabId() { + return tabId; + }, + toggle: function(state) { + if ( state === undefined ) { + state = tabId === 0; + } + if ( state === false ) { + stop(); + } else { + start(); + } + }, + update: function() { + if ( tabId !== 0 ) { + start(); + } + } + }; +})(); + +/******************************************************************************/ + var rowFilterer = (function() { var filters = []; @@ -728,7 +864,6 @@ var rowFilterer = (function() { }; })(); -/******************************************************************************/ /******************************************************************************/ var ruleEditor = (function() { @@ -817,9 +952,9 @@ var ruleEditor = (function() { addListener(ruleWidgets, 'mouseenter', attachRulePicker, 0b11); addListener(ruleWidgets, 'mouseleave', removeRulePicker, 0b11); addListener(ruleActionPicker, 'click', rulePickerHandler, 0b11); - addListener(ruleEditorNode.querySelector('.buttonReload'), 'click', reload); - addListener(ruleEditorNode.querySelector('.buttonRevertScope'), 'click', revert); - addListener(ruleEditorNode.querySelector('.buttonPersist'), 'click', persist); + addListener(ruleEditorNode.querySelector('#matrixReloadButton'), 'click', reload); + addListener(ruleEditorNode.querySelector('#matrixRevertButton'), 'click', revert); + addListener(ruleEditorNode.querySelector('#matrixPersistButton'), 'click', persist); document.body.appendChild(ruleEditorNode); }; @@ -854,14 +989,14 @@ var ruleEditor = (function() { } let dirty = diffCount !== 0; ruleEditorNode - .querySelector('.buttonPersist .badge') + .querySelector('#matrixPersistButton .badge') .textContent = dirty ? diffCount : ''; ruleEditorNode - .querySelector('.buttonRevertScope') + .querySelector('#matrixRevertButton') .classList .toggle('disabled', !dirty); ruleEditorNode - .querySelector('.buttonPersist') + .querySelector('#matrixPersistButton') .classList .toggle('disabled', !dirty); } @@ -1000,7 +1135,9 @@ var ruleEditor = (function() { node.removeEventListener(type, handler, options); } listeners = []; - ruleEditorNode.querySelector('.buttonReload').removeEventListener('click', reload); + ruleEditorNode + .querySelector('#matrixReloadButton') + .removeEventListener('click', reload); removeSelf(ruleEditorNode); }; @@ -1080,7 +1217,8 @@ window.addEventListener('beforeunload', releaseView); readLogBuffer(); uDom('#pageSelector').on('change', pageSelectorChanged); -uDom('#refresh').on('click', reloadTab); +uDom('#reloadTab').on('click', reloadTab); +uDom('#popupPanelButton').on('click', ( ) => popupPanel.toggle()); uDom('#compactViewToggler').on('click', toggleCompactView); uDom('#clean').on('click', cleanBuffer); uDom('#clear').on('click', clearBuffer); diff --git a/src/logger-ui.html b/src/logger-ui.html index e4d0e77b..cd6653f8 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -15,28 +15,30 @@