diff --git a/src/js/matrix.js b/src/js/matrix.js index 5ae6bfa..8b5e0d0 100644 --- a/src/js/matrix.js +++ b/src/js/matrix.js @@ -207,6 +207,7 @@ Matrix.prototype.reset = function() { this.switches = {}; this.rules = {}; this.rootValue = Matrix.GreenIndirect; + this.modifiedTime = 0; }; /******************************************************************************/ @@ -248,6 +249,7 @@ Matrix.prototype.assign = function(other) { } this.switches[k] = other.switches[k]; } + this.modifiedTime = other.modifiedTime; return this; }; @@ -273,6 +275,7 @@ Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) { } else { this.switches[srcHostname] = bits; } + this.modifiedTime = Date.now(); return true; }; @@ -294,6 +297,7 @@ Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) { } else { this.rules[k] = newBitmap; } + this.modifiedTime = Date.now(); return true; }; @@ -535,6 +539,7 @@ Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) { } else { this.switches[srcHostname] = bits; } + this.modifiedTime = Date.now(); state = this.evaluateSwitchZ(switchName, srcHostname); if ( state === newState ) { return true; @@ -819,6 +824,8 @@ Matrix.prototype.fromString = function(text, append) { if ( !append ) { this.assign(matrix); } + + this.modifiedTime = Date.now(); }; /******************************************************************************/ @@ -836,6 +843,7 @@ Matrix.prototype.toSelfie = function() { Matrix.prototype.fromSelfie = function(selfie) { this.switches = selfie.switches; this.rules = selfie.rules; + this.modifiedTime = Date.now(); }; /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index f310c9a..7e49501 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -146,6 +146,10 @@ var matrixSnapshot = function(pageStore, details) { domain: pageStore.pageDomain, headers: µm.Matrix.getColumnHeaders(), hostname: pageStore.pageHostname, + mtxColorModified: µm.tMatrix.modifiedTime !== details.mtxColorModifiedTime, + mtxContentModified: pageStore.mtxContentModifiedTime !== details.mtxContentModifiedTime, + mtxCountModified: pageStore.mtxCountModifiedTime !== details.mtxCountModifiedTime, + mtxColorModifiedTime: µm.tMatrix.modifiedTime, mtxContentModifiedTime: pageStore.mtxContentModifiedTime, mtxCountModifiedTime: pageStore.mtxCountModifiedTime, pSwitches: {}, @@ -260,6 +264,7 @@ var matrixSnapshotFromTabId = function(details, callback) { // First verify whether we must return data or not. if ( + µm.tMatrix.modifiedTime === details.mtxColorModifiedTime && pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime && pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime ) { diff --git a/src/js/popup.js b/src/js/popup.js index e03816c..257d0a9 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -87,13 +87,28 @@ function setUserSetting(setting, value) { /******************************************************************************/ -function updateMatrixSnapshot() { - var snapshotReady = function() { +var matrixSnapshotChanged = function() { + if ( typeof matrixSnapshot !== 'object' ) { + return; + } + if ( matrixSnapshot.mtxContentModified ) { + makeMenu(); + return; + } + if ( matrixSnapshot.mtxCountModified ) { + updateMatrixCounts(); + } + if ( matrixSnapshot.mtxColorModified ) { updateMatrixColors(); updateMatrixBehavior(); updateMatrixButtons(); - }; - matrixSnapshotPoller.mustFetch(snapshotReady); + } +}; + +/******************************************************************************/ + +function updateMatrixSnapshot() { + matrixSnapshotPoller.pollNow(matrixSnapshotChanged); } /******************************************************************************/ @@ -316,6 +331,35 @@ function toggleSpecificCollapseState(uelem) { /******************************************************************************/ +// Update count value of matrix cells(s) + +function updateMatrixCounts() { + var matCells = uDom('.matrix .matRow.rw > .matCell'); + var i = matCells.length; + var matRow, matCell, count, counts; + var headers = matrixSnapshot.headers; + var rows = matrixSnapshot.rows; + while ( i-- ) { + matCell = matCells.nodeAt(i); + if ( matCell.hostname === '*' ) { + continue; + } + if ( matCell.reqType === '*' ) { + continue; + } + matRow = matCell.parentNode; + counts = matRow.classList.contains('meta') ? 'totals' : 'counts'; + count = rows[matCell.hostname][counts][headers[matCell.reqType]]; + if ( count === matCell.count) { + continue; + } + matCell.count = count; + matCell.textContent = count ? count : '\u00A0'; + } +} + +/******************************************************************************/ + // Update color of matrix cells(s) // Color changes when rules change @@ -1169,38 +1213,45 @@ var matrixSnapshotPoller = (function() { timer = null; if ( typeof response === 'object' ) { matrixSnapshot = response; - makeMenu(); + matrixSnapshotChanged(); } - pollSnapshotAsync(); }; - var pollSnapshot = function() { - timer = null; + var pollNow = function(callback) { + unpollAsync(); + var onPolled = function(response) { + callback(response); + pollAsync(); + }; messager.send({ what: 'matrixSnapshot', tabId: matrixSnapshot.tabId, + mtxColorModifiedTime: matrixSnapshot.mtxColorModifiedTime, mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime, mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime - }, snapshotPolled); + }, onPolled); }; - var pollSnapshotAsync = function() { + var poll = function() { + timer = null; + pollNow(snapshotPolled); + }; + + var pollAsync = function() { if ( timer !== null ) { return; } - timer = setTimeout(pollSnapshot, 1414); + timer = setTimeout(poll, 1414); }; - var cancelSnapshotAsync = function() { + var unpollAsync = function() { if ( timer !== null ) { clearTimeout(timer); timer = null; } }; - var mustFetch = function(callback) { - cancelSnapshotAsync(); - + (function() { var tabId = matrixSnapshot.tabId; // If no tab id yet, see if there is one specified in our URL @@ -1215,68 +1266,60 @@ var matrixSnapshotPoller = (function() { if ( typeof response === 'object' ) { matrixSnapshot = response; } - callback(); - pollSnapshotAsync(); + onMatrixSnapshotReady(); + pollAsync(); }; messager.send({ what: 'matrixSnapshot', tabId: tabId }, snapshotFetched); - }; + })(); - return { - mustFetch: mustFetch - }; + return pollNow; })(); /******************************************************************************/ -// Make menu only when popup html is fully loaded - -uDom.onLoad(function() { - matrixSnapshotPoller.mustFetch(onMatrixSnapshotReady); - - // Below is UI stuff which is not key to make the menu, so this can - // be done without having to wait for a tab to be bound to the menu. - - // We reuse for all cells the one and only cell hotspots. - uDom('#whitelist').on('click', function() { - handleWhitelistFilter(uDom(this)); - return false; - }); - uDom('#blacklist').on('click', function() { - handleBlacklistFilter(uDom(this)); - return false; - }); - uDom('#domainOnly').on('click', function() { - toggleCollapseState(uDom(this)); - return false; - }); - matrixCellHotspots = uDom('#cellHotspots').detach(); - uDom('body') - .on('mouseenter', '.matCell', mouseenterMatrixCellHandler) - .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler); - uDom('#scopeKeyGlobal').on('click', selectGlobalScope); - uDom('#scopeKeyDomain').on('click', selectDomainScope); - uDom('#scopeKeySite').on('click', selectSiteScope); - uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch); - uDom('#buttonPersist').on('click', persistMatrix); - uDom('#buttonRevertScope').on('click', revertMatrix); - - uDom('#buttonRevertAll').on('click', revertAll); - uDom('#buttonReload').on('click', buttonReloadHandler); - uDom('.extensionURL').on('click', gotoExtensionURL); - - uDom('body').on('click', '.dropdown-menu-button', dropDownMenuShow); - uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide); - - uDom('#matList').on('click', '.g4Meta', function() { - var collapsed = uDom(this) - .toggleClass('g4Collapsed') - .hasClass('g4Collapsed'); - setUserSetting('popupHideBlacklisted', collapsed); +// Below is UI stuff which is not key to make the menu, so this can +// be done without having to wait for a tab to be bound to the menu. + +// We reuse for all cells the one and only cell hotspots. +uDom('#whitelist').on('click', function() { + handleWhitelistFilter(uDom(this)); + return false; + }); +uDom('#blacklist').on('click', function() { + handleBlacklistFilter(uDom(this)); + return false; + }); +uDom('#domainOnly').on('click', function() { + toggleCollapseState(uDom(this)); + return false; }); +matrixCellHotspots = uDom('#cellHotspots').detach(); +uDom('body') + .on('mouseenter', '.matCell', mouseenterMatrixCellHandler) + .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler); +uDom('#scopeKeyGlobal').on('click', selectGlobalScope); +uDom('#scopeKeyDomain').on('click', selectDomainScope); +uDom('#scopeKeySite').on('click', selectSiteScope); +uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch); +uDom('#buttonPersist').on('click', persistMatrix); +uDom('#buttonRevertScope').on('click', revertMatrix); + +uDom('#buttonRevertAll').on('click', revertAll); +uDom('#buttonReload').on('click', buttonReloadHandler); +uDom('.extensionURL').on('click', gotoExtensionURL); + +uDom('body').on('click', '.dropdown-menu-button', dropDownMenuShow); +uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide); + +uDom('#matList').on('click', '.g4Meta', function() { + var collapsed = uDom(this) + .toggleClass('g4Collapsed') + .hasClass('g4Collapsed'); + setUserSetting('popupHideBlacklisted', collapsed); }); /******************************************************************************/