diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index f1ea428..8ee07c7 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -7,7 +7,6 @@ body { margin: 0; overflow-x: hidden; padding: 0; - white-space: nowrap; width: 100%; } #toolbar { @@ -16,7 +15,7 @@ body { box-sizing: border-box; left: 0; margin: 0; - padding: 0 1em; + padding: 0.5em 1em; position: fixed; top: 0; width: 100%; @@ -28,7 +27,7 @@ body { box-sizing: border-box; cursor: pointer; display: inline-block; - font-size: 20px; + font-size: 150%; margin: 0; padding: 8px; } @@ -39,6 +38,19 @@ body { #toolbar .button:hover { background-color: #eee; } +#toolbar > div { + white-space: nowrap; + } +#toolbar > div:first-of-type { + font-size: 120%; + } +#toolbar > div > * { + vertical-align: middle; + } +#pageSelector { + max-width: 28em; + padding: 0.2em 0; + } body #compactViewToggler.button:before { content: '\f102'; } @@ -55,14 +67,13 @@ body.f #filterButton { background-color: #fee; } #maxEntries { - margin-left: 3em; + margin: 0 2em; } input:focus { background-color: #ffe; } #content { font: 13px sans-serif; - margin-top: 3.5em; width: 100%; } diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index 4563ffa..8d74f04 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -445,7 +445,8 @@ var nodeListsAddedHandler = function(nodeLists) { what: 'contentScriptSummary', locationURL: window.location.href, inlineScript: false, - mustReport: true + mustReport: true, + title: '' }; // https://github.com/gorhill/httpswitchboard/issues/25 // & @@ -453,6 +454,12 @@ var nodeListsAddedHandler = function(nodeLists) { // https://github.com/gorhill/httpswitchboard/issues/131 hasInlineScript(document.querySelectorAll('a[href^="javascript:"],script'), summary); + if ( window === window.top ) { + if ( document.title ) { + summary.title = document.title.trim(); + } + } + //console.debug('contentscript-end.js > firstObservationHandler(): found %d script tags in "%s"', Object.keys(summary.scriptSources).length, window.location.href); localMessager.send(summary); diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 522d53b..690f8f5 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -64,6 +64,15 @@ var dateOptions = { /******************************************************************************/ +// Adjust top padding of content table, to match that of toolbar height. + +document.getElementById('content').style.setProperty( + 'margin-top', + document.getElementById('toolbar').offsetHeight + 'px' +); + +/******************************************************************************/ + var escapeHTML = function(s) { return s.replace(reEscapeLeftBracket, '<') .replace(reEscapeRightBracket, '>'); @@ -74,6 +83,18 @@ var reEscapeRightBracket = />/g; /******************************************************************************/ +var classNameFromTabId = function(tabId) { + if ( tabId === noTabId ) { + return 'tab_bts'; + } + if ( tabId !== '' ) { + return 'tab_' + tabId; + } + return ''; +}; + +/******************************************************************************/ + // Emphasize hostname and cookie name. var emphasizeCookie = function(s) { @@ -261,11 +282,12 @@ var renderLogEntry = function(entry) { if ( entry.tab ) { tr.classList.add('tab'); + var className = classNameFromTabId(entry.tab); + if ( className !== '' ) { + tr.classList.add(className); + } if ( entry.tab === noTabId ) { - tr.classList.add('tab_bts'); tr.cells[1].appendChild(createHiddenTextNode('bts')); - } else if ( entry.tab !== '' ) { - tr.classList.add('tab_' + entry.tab); } } if ( entry.cat !== '' ) { @@ -329,6 +351,81 @@ var renderLogEntries = function(response) { /******************************************************************************/ +var synchronizeTabIds = function(newTabIds) { + var oldTabIds = allTabIds; + + // Neuter rows for which a tab does not exist anymore + // TODO: sort to avoid using indexOf + + var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); + var rowVoided = false; + var trs; + for ( var tabId in oldTabIds ) { + if ( oldTabIds.hasOwnProperty(tabId) === false ) { + continue; + } + if ( newTabIds.hasOwnProperty(tabId) ) { + continue; + } + // Mark or remove voided rows + trs = uDom('.tab_' + tabId); + if ( autoDeleteVoidRows ) { + toJunkyard(trs); + } else { + trs.removeClass('canMtx'); + rowVoided = true; + } + // Remove popup if it is currently bound to a removed tab. + if ( tabId === popupManager.tabId ) { + popupManager.toggleOff(); + } + } + + var select = document.getElementById('pageSelector'); + var selectValue = select.value; + var tabIds = Object.keys(newTabIds).sort(function(a, b) { + var sa = newTabIds[a].title || newTabIds[a].url; + var sb = newTabIds[b].title || newTabIds[b].url; + return sa.localeCompare(sb); + }); + var option, entry; + for ( var i = 0, j = 2; i < tabIds.length; i++ ) { + tabId = tabIds[i]; + if ( tabId === noTabId ) { + continue; + } + option = select.options[j]; + j += 1; + if ( !option ) { + option = document.createElement('option'); + select.appendChild(option); + } + entry = newTabIds[tabId]; + option.textContent = entry.title || entry.url; + option.value = classNameFromTabId(tabId); + if ( option.value === selectValue ) { + option.setAttribute('selected', ''); + } else { + option.removeAttribute('selected'); + } + } + for ( ; j < select.options.length; j++ ) { + select.removeChild(select.options[j]); + } + if ( select.value !== selectValue ) { + select.selectedIndex = 0; + select.value = ''; + select.options[0].setAttribute('selected', ''); + pageSelectorChanged(); + } + + allTabIds = newTabIds; + + return rowVoided; +}; + +/******************************************************************************/ + var truncateLog = function(size) { if ( size === 0 ) { size = 5000; @@ -356,28 +453,7 @@ var onLogBufferRead = function(response) { // Neuter rows for which a tab does not exist anymore // TODO: sort to avoid using indexOf - var autoDeleteVoidRows = vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); - var rowVoided = false, trs; - for ( var tabId in allTabIds ) { - if ( allTabIds.hasOwnProperty(tabId) === false ) { - continue; - } - if ( response.tabIds.hasOwnProperty(tabId) ) { - continue; - } - trs = uDom('.tab_' + tabId); - if ( autoDeleteVoidRows ) { - toJunkyard(trs); - } else { - trs.removeClass('canMtx'); - rowVoided = true; - } - if ( tabId === popupManager.tabId ) { - popupManager.toggleOff(); - } - } - allTabIds = response.tabIds; - + var rowVoided = synchronizeTabIds(response.tabIds); renderLogEntries(response); if ( rowVoided ) { @@ -408,6 +484,41 @@ var readLogBuffer = function() { /******************************************************************************/ +var pageSelectorChanged = function() { + var style = document.getElementById('tabFilterer'); + var tabClass = document.getElementById('pageSelector').value; + var sheet = style.sheet; + while ( sheet.cssRules.length !== 0 ) { + sheet.deleteRule(0); + } + if ( tabClass !== '' ) { + sheet.insertRule( + '#content table tr:not(.' + tabClass + ') { display: none; }', + 0 + ); + } + uDom('#refresh').toggleClass( + 'disabled', + tabClass === '' || tabClass === 'tab_bts' + ); +}; + +/******************************************************************************/ + +var refreshTab = function() { + var tabClass = document.getElementById('pageSelector').value; + var matches = tabClass.match(/^tab_(.+)$/); + if ( matches === null ) { + return; + } + if ( matches[1] === 'bts' ) { + return; + } + messager.send({ what: 'forceReloadTab', tabId: matches[1] }); +}; + +/******************************************************************************/ + var onMaxEntriesChanged = function() { var raw = uDom(this).val(); try { @@ -675,7 +786,7 @@ var popupManager = (function() { popupObserver = new MutationObserver(resizePopup); container.appendChild(popup); - style = document.querySelector('#content > style'); + style = document.getElementById('popupFilterer'); style.textContent = styleTemplate.replace('{{tabId}}', localTabId); document.body.classList.add('popupOn'); @@ -727,6 +838,8 @@ var popupManager = (function() { uDom.onLoad(function() { readLogBuffer(); + uDom('#pageSelector').on('change', pageSelectorChanged); + uDom('#refresh').on('click', refreshTab); uDom('#compactViewToggler').on('click', toggleCompactView); uDom('#clean').on('click', cleanBuffer); uDom('#clear').on('click', clearBuffer); diff --git a/src/js/messaging.js b/src/js/messaging.js index bd5e143..4475ab6 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -390,17 +390,22 @@ var contentScriptSummaryHandler = function(tabId, details) { if ( !details || !details.locationURL ) { return; } - if ( details.inlineScript !== true ) { - return; - } - // scripts // https://github.com/gorhill/httpswitchboard/issues/25 var pageStore = µm.pageStoreFromTabId(tabId); if ( pageStore === null ) { return; } + if ( details.title ) { + pageStore.title = details.title; + } + + // scripts + if ( details.inlineScript !== true ) { + return; + } + var pageHostname = pageStore.pageHostname; var µmuri = µm.URI.set(details.locationURL); var frameURL = µmuri.normalizedURI(); @@ -904,10 +909,16 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'readMany': var tabIds = {}; + var pageStore; for ( var tabId in µm.pageStores ) { - if ( µm.pageStores.hasOwnProperty(tabId) ) { - tabIds[tabId] = true; + pageStore = µm.pageStoreFromTabId(tabId); + if ( pageStore === null ) { + continue; } + tabIds[tabId] = { + title: pageStore.title, + url: pageStore.pageUrl + }; } response = { colorBlind: false, diff --git a/src/js/pagestats.js b/src/js/pagestats.js index 4c8b2bf..4a0458f 100644 --- a/src/js/pagestats.js +++ b/src/js/pagestats.js @@ -336,6 +336,7 @@ PageStore.prototype.init = function(tabContext) { this.pageUrl = tabContext.normalURL; this.pageHostname = tabContext.rootHostname; this.pageDomain = tabContext.rootDomain; + this.title = ''; this.requests = µm.PageRequestStats.factory(); this.domains = {}; this.allHostnamesString = ' '; @@ -357,6 +358,7 @@ PageStore.prototype.dispose = function() { this.pageUrl = ''; this.pageHostname = ''; this.pageDomain = ''; + this.title = ''; this.domains = {}; this.allHostnamesString = ' '; diff --git a/src/js/start.js b/src/js/start.js index f18340b..daa5295 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -164,9 +164,8 @@ var onPSLReady = function() { // rhill 2013-11-24: bind behind-the-scene virtual tab/url manually, since the // normal way forbid binding behind the scene tab. // https://github.com/gorhill/httpswitchboard/issues/67 - µm.pageStores[vAPI.noTabId] = µm.PageStore.factory( - µm.tabContextManager.mustLookup(vAPI.noTabId) - ); + µm.pageStores[vAPI.noTabId] = µm.PageStore.factory(µm.tabContextManager.mustLookup(vAPI.noTabId)); + µm.pageStores[vAPI.noTabId].title = vAPI.i18n('statsPageDetailedBehindTheScenePage'); vAPI.tabs.getAll(onTabsReady); }; diff --git a/src/logger-ui.html b/src/logger-ui.html index a249792..faaaf29 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -9,15 +9,25 @@