1
0
Fork 0
mirror of https://github.com/gorhill/uMatrix.git synced 2024-06-02 02:14:52 +12:00
uMatrix/src/js/popup.js

1527 lines
49 KiB
JavaScript
Raw Normal View History

2014-10-18 08:01:09 +13:00
/*******************************************************************************
2016-08-12 11:21:51 +12:00
uMatrix - a Chromium browser extension to black/white list requests.
2017-03-16 09:30:48 +13:00
Copyright (C) 2014-2017 Raymond Hill
2014-10-18 08:01:09 +13:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uMatrix
*/
/* global punycode, uDom */
2014-10-20 17:53:13 +13:00
/* jshint esnext: true, bitwise: false */
2014-10-18 08:01:09 +13:00
2017-03-16 09:30:48 +13:00
'use strict';
2014-10-18 08:01:09 +13:00
/******************************************************************************/
/******************************************************************************/
(function() {
/******************************************************************************/
/******************************************************************************/
// Stuff which is good to do very early so as to avoid visual glitches.
(function() {
var paneContentPaddingTop,
touchDevice;
try {
paneContentPaddingTop = localStorage.getItem('paneContentPaddingTop');
touchDevice = localStorage.getItem('touchDevice');
} catch(ex) {
}
if ( typeof paneContentPaddingTop === 'string' ) {
document.querySelector('.paneContent').style.setProperty(
'padding-top',
paneContentPaddingTop
);
}
if ( touchDevice === 'true' ) {
document.body.setAttribute('data-touch', 'true');
} else {
document.addEventListener('touchstart', function onTouched(ev) {
document.removeEventListener(ev.type, onTouched);
document.body.setAttribute('data-touch', 'true');
try {
localStorage.setItem('touchDevice', 'true');
} catch(ex) {
}
resizePopup();
});
}
})();
var resizePopup = (function() {
var timer;
var fix = function() {
timer = undefined;
var doc = document;
// Manually adjust the position of the main matrix according to the
// height of the toolbar/matrix header.
var paddingTop = (doc.querySelector('.paneHead').clientHeight + 2) + 'px',
paneContent = doc.querySelector('.paneContent');
if ( paddingTop !== paneContent.style.paddingTop ) {
paneContent.style.setProperty('padding-top', paddingTop);
try {
localStorage.setItem('paneContentPaddingTop', paddingTop);
} catch(ex) {
}
}
document.body.classList.toggle(
'hConstrained',
window.innerWidth < document.body.clientWidth
);
};
return function() {
if ( timer !== undefined ) {
clearTimeout(timer);
}
timer = vAPI.setTimeout(fix, 97);
};
})();
/******************************************************************************/
/******************************************************************************/
2014-10-20 17:53:13 +13:00
// Must be consistent with definitions in matrix.js
2015-05-13 11:50:48 +12:00
var Pale = 0x00;
var Dark = 0x80;
var Transparent = 0;
var Red = 1;
var Green = 2;
var Gray = 3;
var DarkRed = Dark | Red;
var PaleRed = Pale | Red;
var DarkGreen = Dark | Green;
var PaleGreen = Pale | Green;
var DarkGray = Dark | Gray;
var PaleGray = Pale | Gray;
2014-10-18 08:01:09 +13:00
var matrixSnapshot = {};
var groupsSnapshot = [];
var allHostnamesSnapshot = 'do not leave this initial string empty';
var matrixCellHotspots = null;
var matrixHeaderPrettyNames = {
'all': '',
'cookie': '',
'css': '',
'image': '',
2017-04-28 00:10:54 +12:00
'media': '',
'script': '',
'xhr': '',
'frame': '',
'other': ''
};
2014-11-04 08:27:59 +13:00
var firstPartyLabel = '';
var blacklistedHostnamesLabel = '';
2017-03-21 10:01:19 +13:00
var expandosIdGenerator = 1;
2017-03-16 09:30:48 +13:00
var nodeToExpandosMap = (function() {
2017-03-21 10:01:19 +13:00
if ( typeof window.Map === 'function' ) {
return new window.Map();
2017-03-16 09:30:48 +13:00
}
})();
var expandosFromNode = function(node) {
if (
node instanceof HTMLElement === false &&
typeof node.nodeAt === 'function'
) {
node = node.nodeAt(0);
}
if ( nodeToExpandosMap ) {
2017-03-21 10:01:19 +13:00
var expandosId = node.getAttribute('data-expandos');
if ( !expandosId ) {
expandosId = '' + (expandosIdGenerator++);
node.setAttribute('data-expandos', expandosId);
}
var expandos = nodeToExpandosMap.get(expandosId);
2017-03-16 09:30:48 +13:00
if ( expandos === undefined ) {
2017-03-21 10:01:19 +13:00
nodeToExpandosMap.set(expandosId, (expandos = Object.create(null)));
2017-03-16 09:30:48 +13:00
}
return expandos;
}
return node;
};
var messager = vAPI.messaging.channel('popup.js');
2014-10-18 08:01:09 +13:00
/******************************************************************************/
/******************************************************************************/
function getUserSetting(setting) {
2017-11-25 11:22:42 +13:00
return matrixSnapshot.userSettings[setting];
}
2014-10-18 08:01:09 +13:00
function setUserSetting(setting, value) {
matrixSnapshot.userSettings[setting] = value;
2015-04-12 09:15:57 +12:00
messager.send({
2014-10-18 08:01:09 +13:00
what: 'userSettings',
name: setting,
value: value
});
}
/******************************************************************************/
function getUISetting(setting) {
var r = vAPI.localStorage.getItem(setting);
if ( typeof r !== 'string' ) {
return undefined;
}
return JSON.parse(r);
}
function setUISetting(setting, value) {
vAPI.localStorage.setItem(
setting,
JSON.stringify(value)
);
}
/******************************************************************************/
function updateMatrixSnapshot() {
2015-05-07 10:59:07 +12:00
matrixSnapshotPoller.pollNow();
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// For display purpose, create four distinct groups of rows:
// 0th: literal "1st-party" row
2014-10-18 08:01:09 +13:00
// 1st: page domain's related
// 2nd: whitelisted
// 3rd: graylisted
// 4th: blacklisted
function getGroupStats() {
// Try to not reshuffle groups around while popup is opened if
// no new hostname added.
2014-10-20 17:53:13 +13:00
var latestDomainListSnapshot = Object.keys(matrixSnapshot.rows).sort().join();
if ( latestDomainListSnapshot === allHostnamesSnapshot ) {
return groupsSnapshot;
2014-10-18 08:01:09 +13:00
}
allHostnamesSnapshot = latestDomainListSnapshot;
2014-10-18 08:01:09 +13:00
// First, group according to whether at least one node in the domain
// hierarchy is white or blacklisted
var pageDomain = matrixSnapshot.domain;
2014-10-20 17:53:13 +13:00
var rows = matrixSnapshot.rows;
2017-11-29 06:33:22 +13:00
var anyTypeOffset = matrixSnapshot.headerIndices.get('*');
2014-10-20 17:53:13 +13:00
var hostname, domain;
2014-10-26 13:03:22 +13:00
var row, color, count, groupIndex;
2014-10-20 17:53:13 +13:00
var domainToGroupMap = {};
2014-10-26 13:03:22 +13:00
// These have hard-coded position which cannot be overriden
domainToGroupMap['1st-party'] = 0;
domainToGroupMap[pageDomain] = 1;
// 1st pass: domain wins if it has an explicit rule or a count
2014-10-20 17:53:13 +13:00
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
continue;
}
2014-10-26 13:03:22 +13:00
if ( hostname === '*' || hostname === '1st-party' ) {
2014-10-18 08:01:09 +13:00
continue;
}
2014-10-26 13:03:22 +13:00
domain = rows[hostname].domain;
if ( domain === pageDomain || hostname !== domain ) {
2014-10-18 08:01:09 +13:00
continue;
}
2014-10-26 13:03:22 +13:00
row = rows[domain];
color = row.temporary[anyTypeOffset];
if ( color === DarkGreen ) {
domainToGroupMap[domain] = 2;
continue;
}
if ( color === DarkRed ) {
domainToGroupMap[domain] = 4;
continue;
}
count = row.counts[anyTypeOffset];
if ( count !== 0 ) {
domainToGroupMap[domain] = 3;
continue;
}
}
// 2nd pass: green wins
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
continue;
}
row = rows[hostname];
domain = row.domain;
if ( domainToGroupMap.hasOwnProperty(domain) ) {
continue;
}
2014-10-20 17:53:13 +13:00
color = row.temporary[anyTypeOffset];
if ( color === DarkGreen ) {
domainToGroupMap[domain] = 2;
2014-10-26 13:03:22 +13:00
}
}
// 3rd pass: gray with count wins
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
2014-10-20 17:53:13 +13:00
continue;
}
2014-10-26 13:03:22 +13:00
row = rows[hostname];
domain = row.domain;
if ( domainToGroupMap.hasOwnProperty(domain) ) {
continue;
}
color = row.temporary[anyTypeOffset];
count = row.counts[anyTypeOffset];
if ( color !== DarkRed && count !== 0 ) {
domainToGroupMap[domain] = 3;
}
}
// 4th pass: red wins whatever is left
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
continue;
}
row = rows[hostname];
domain = row.domain;
if ( domainToGroupMap.hasOwnProperty(domain) ) {
continue;
}
color = row.temporary[anyTypeOffset];
2014-10-20 17:53:13 +13:00
if ( color === DarkRed ) {
2014-10-26 13:03:22 +13:00
domainToGroupMap[domain] = 4;
}
}
// 5th pass: gray wins whatever is left
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
2014-10-20 17:53:13 +13:00
continue;
2014-10-18 08:01:09 +13:00
}
2014-10-26 13:03:22 +13:00
domain = rows[hostname].domain;
if ( domainToGroupMap.hasOwnProperty(domain) ) {
2014-10-20 17:53:13 +13:00
continue;
2014-10-18 08:01:09 +13:00
}
domainToGroupMap[domain] = 3;
2014-10-20 17:53:13 +13:00
}
2014-10-26 13:03:22 +13:00
// Last pass: put each domain in a group
var groups = [ {}, {}, {}, {}, {} ];
2014-10-20 17:53:13 +13:00
var group;
for ( hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
continue;
2014-10-18 08:01:09 +13:00
}
2014-10-20 17:53:13 +13:00
if ( hostname === '*' ) {
continue;
2014-10-18 08:01:09 +13:00
}
2014-10-26 13:03:22 +13:00
domain = rows[hostname].domain;
2014-10-20 17:53:13 +13:00
groupIndex = domainToGroupMap[domain];
group = groups[groupIndex];
if ( group.hasOwnProperty(domain) === false ) {
group[domain] = {};
2014-10-18 08:01:09 +13:00
}
2014-10-20 17:53:13 +13:00
group[domain][hostname] = true;
2014-10-18 08:01:09 +13:00
}
groupsSnapshot = groups;
2014-10-18 08:01:09 +13:00
return groups;
}
/******************************************************************************/
// helpers
function getTemporaryColor(hostname, type) {
2017-11-29 06:33:22 +13:00
return matrixSnapshot.rows[hostname].temporary[matrixSnapshot.headerIndices.get(type)];
2014-10-18 08:01:09 +13:00
}
function getPermanentColor(hostname, type) {
2017-11-29 06:33:22 +13:00
return matrixSnapshot.rows[hostname].permanent[matrixSnapshot.headerIndices.get(type)];
2014-10-18 08:01:09 +13:00
}
2017-03-16 09:30:48 +13:00
function addCellClass(cell, hostname, type) {
var cl = cell.classList;
cell.classList.add('matCell');
cell.classList.add('t' + getTemporaryColor(hostname, type).toString(16));
cell.classList.add('p' + getPermanentColor(hostname, type).toString(16));
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// This is required for when we update the matrix while it is open:
// the user might have collapsed/expanded one or more domains, and we don't
// want to lose all his hardwork.
function getCollapseState(domain) {
var states = getUISetting('popupCollapseSpecificDomains');
if ( typeof states === 'object' && states[domain] !== undefined ) {
2014-10-18 08:01:09 +13:00
return states[domain];
}
2015-05-14 11:11:44 +12:00
return getUISetting('popupCollapseDomains') === true;
2014-10-18 08:01:09 +13:00
}
2014-10-19 09:49:06 +13:00
function toggleCollapseState(elem) {
if ( elem.ancestors('#matHead.collapsible').length > 0 ) {
toggleMainCollapseState(elem);
2014-10-18 08:01:09 +13:00
} else {
2014-10-19 09:49:06 +13:00
toggleSpecificCollapseState(elem);
2014-10-18 08:01:09 +13:00
}
}
2014-10-19 09:49:06 +13:00
function toggleMainCollapseState(uelem) {
var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
2014-11-08 03:05:05 +13:00
var collapsed = matHead.hasClass('collapsed');
2014-10-18 18:09:09 +13:00
uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed);
setUISetting('popupCollapseDomains', collapsed);
2014-10-18 08:01:09 +13:00
var specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
2014-10-18 08:01:09 +13:00
var domains = Object.keys(specificCollapseStates);
var i = domains.length;
var domain;
while ( i-- ) {
domain = domains[i];
if ( specificCollapseStates[domain] === collapsed ) {
delete specificCollapseStates[domain];
}
}
setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
2014-10-18 08:01:09 +13:00
}
2014-10-19 09:49:06 +13:00
function toggleSpecificCollapseState(uelem) {
2014-10-18 08:01:09 +13:00
// Remember collapse state forever, but only if it is different
// from main collapse switch.
2017-03-16 09:30:48 +13:00
var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'),
domain = expandosFromNode(section).domain,
collapsed = section.hasClass('collapsed'),
mainCollapseState = getUISetting('popupCollapseDomains') === true,
specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
2014-10-18 08:01:09 +13:00
if ( collapsed !== mainCollapseState ) {
specificCollapseStates[domain] = collapsed;
setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
2014-10-18 08:01:09 +13:00
} else if ( specificCollapseStates[domain] !== undefined ) {
delete specificCollapseStates[domain];
setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
2014-10-18 08:01:09 +13:00
}
}
/******************************************************************************/
// Update count value of matrix cells(s)
function updateMatrixCounts() {
2017-03-16 09:30:48 +13:00
var matCells = uDom('.matrix .matRow.rw > .matCell'),
i = matCells.length,
matRow, matCell, count, counts,
2017-11-29 06:33:22 +13:00
headerIndices = matrixSnapshot.headerIndices,
2017-03-16 09:30:48 +13:00
rows = matrixSnapshot.rows,
expandos;
while ( i-- ) {
matCell = matCells.nodeAt(i);
2017-03-16 09:30:48 +13:00
expandos = expandosFromNode(matCell);
if ( expandos.hostname === '*' || expandos.reqType === '*' ) {
continue;
}
matRow = matCell.parentNode;
counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
2017-11-29 06:33:22 +13:00
count = rows[expandos.hostname][counts][headerIndices.get(expandos.reqType)];
2017-03-16 09:30:48 +13:00
if ( count === expandos.count ) { continue; }
expandos.count = count;
matCell.textContent = count ? count : '\u00A0';
}
}
/******************************************************************************/
2014-10-18 08:01:09 +13:00
// Update color of matrix cells(s)
// Color changes when rules change
function updateMatrixColors() {
2017-03-16 09:30:48 +13:00
var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(),
i = cells.length,
cell, expandos;
2014-10-18 08:01:09 +13:00
while ( i-- ) {
2014-10-19 10:11:10 +13:00
cell = cells.nodeAt(i);
2017-03-16 09:30:48 +13:00
expandos = expandosFromNode(cell);
addCellClass(cell, expandos.hostname, expandos.reqType);
2014-10-18 08:01:09 +13:00
}
}
/******************************************************************************/
// Update behavior of matrix:
// - Whether a section is collapsible or not. It is collapsible if:
// - It has at least one subdomain AND
// - There is no explicit rule anywhere in the subdomain cells AND
// - It is not part of group 3 (blacklisted hostnames)
function updateMatrixBehavior() {
2014-10-19 09:49:06 +13:00
matrixList = matrixList || uDom('#matList');
var sections = matrixList.descendants('.matSection');
2014-10-18 08:01:09 +13:00
var i = sections.length;
var section, subdomainRows, j, subdomainRow;
while ( i-- ) {
2014-10-19 10:11:10 +13:00
section = sections.at(i);
subdomainRows = section.descendants('.l2:not(.g4)');
2014-10-18 08:01:09 +13:00
j = subdomainRows.length;
while ( j-- ) {
2014-10-19 10:11:10 +13:00
subdomainRow = subdomainRows.at(j);
2014-10-26 09:41:08 +13:00
subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.t81,.t82').length === 0);
2014-10-18 08:01:09 +13:00
}
section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0);
}
}
/******************************************************************************/
// handle user interaction with filters
function getCellAction(hostname, type, leaning) {
var temporaryColor = getTemporaryColor(hostname, type);
var hue = temporaryColor & 0x03;
// Special case: root toggle only between two states
if ( type === '*' && hostname === '*' ) {
return hue === Green ? 'blacklistMatrixCell' : 'whitelistMatrixCell';
}
// When explicitly blocked/allowed, can only graylist
var saturation = temporaryColor & 0x80;
if ( saturation === Dark ) {
return 'graylistMatrixCell';
}
return leaning === 'whitelisting' ? 'whitelistMatrixCell' : 'blacklistMatrixCell';
}
2014-10-18 08:01:09 +13:00
function handleFilter(button, leaning) {
// our parent cell knows who we are
2017-03-16 09:30:48 +13:00
var cell = button.ancestors('div.matCell'),
expandos = expandosFromNode(cell),
type = expandos.reqType,
desHostname = expandos.hostname;
2014-10-30 02:27:11 +13:00
// https://github.com/gorhill/uMatrix/issues/24
// No hostname can happen -- like with blacklist meta row
if ( desHostname === '' ) {
return;
}
var request = {
what: getCellAction(desHostname, type, leaning),
srcHostname: matrixSnapshot.scope,
desHostname: desHostname,
type: type
};
2015-04-12 09:15:57 +12:00
messager.send(request, updateMatrixSnapshot);
2014-10-18 08:01:09 +13:00
}
function handleWhitelistFilter(button) {
handleFilter(button, 'whitelisting');
}
function handleBlacklistFilter(button) {
handleFilter(button, 'blacklisting');
}
/******************************************************************************/
var matrixRowPool = [];
var matrixSectionPool = [];
var matrixGroupPool = [];
var matrixRowTemplate = null;
var matrixList = null;
var startMatrixUpdate = function() {
2014-10-19 09:49:06 +13:00
matrixList = matrixList || uDom('#matList');
2014-10-18 08:01:09 +13:00
matrixList.detach();
2014-10-19 09:49:06 +13:00
var rows = matrixList.descendants('.matRow');
2014-10-18 08:01:09 +13:00
rows.detach();
matrixRowPool = matrixRowPool.concat(rows.toArray());
2014-10-19 09:49:06 +13:00
var sections = matrixList.descendants('.matSection');
2014-10-18 08:01:09 +13:00
sections.detach();
matrixSectionPool = matrixSectionPool.concat(sections.toArray());
2014-10-19 09:49:06 +13:00
var groups = matrixList.descendants('.matGroup');
2014-10-18 08:01:09 +13:00
groups.detach();
matrixGroupPool = matrixGroupPool.concat(groups.toArray());
};
var endMatrixUpdate = function() {
// https://github.com/gorhill/httpswitchboard/issues/246
// If the matrix has no rows, we need to insert a dummy one, invisible,
// to ensure the extension pop-up is properly sized. This is needed because
// the header pane's `position` property is `fixed`, which means it doesn't
// affect layout size, hence the matrix header row will be truncated.
2014-10-20 17:53:13 +13:00
if ( matrixSnapshot.rowCount <= 1 ) {
2014-10-18 08:01:09 +13:00
matrixList.append(createMatrixRow().css('visibility', 'hidden'));
}
updateMatrixBehavior();
matrixList.css('display', '');
2014-10-19 09:49:06 +13:00
matrixList.appendTo('.paneContent');
2014-10-18 08:01:09 +13:00
};
var createMatrixGroup = function() {
var group = matrixGroupPool.pop();
if ( group ) {
2014-10-19 09:49:06 +13:00
return uDom(group).removeClass().addClass('matGroup');
2014-10-18 08:01:09 +13:00
}
2015-07-22 00:29:15 +12:00
return uDom(document.createElement('div')).addClass('matGroup');
2014-10-18 08:01:09 +13:00
};
var createMatrixSection = function() {
var section = matrixSectionPool.pop();
if ( section ) {
2014-10-19 09:49:06 +13:00
return uDom(section).removeClass().addClass('matSection');
2014-10-18 08:01:09 +13:00
}
2015-07-22 00:29:15 +12:00
return uDom(document.createElement('div')).addClass('matSection');
2014-10-18 08:01:09 +13:00
};
var createMatrixRow = function() {
var row = matrixRowPool.pop();
if ( row ) {
row.style.visibility = '';
2014-10-19 09:49:06 +13:00
row = uDom(row);
row.descendants('.matCell').removeClass().addClass('matCell');
2014-10-18 08:01:09 +13:00
row.removeClass().addClass('matRow');
return row;
}
if ( matrixRowTemplate === null ) {
2014-10-19 09:49:06 +13:00
matrixRowTemplate = uDom('#templates .matRow');
2014-10-18 08:01:09 +13:00
}
return matrixRowTemplate.clone();
};
/******************************************************************************/
function renderMatrixHeaderRow() {
2014-10-18 18:09:09 +13:00
var matHead = uDom('#matHead.collapsible');
2015-05-14 11:14:00 +12:00
matHead.toggleClass('collapsed', getUISetting('popupCollapseDomains') === true);
2017-03-16 09:30:48 +13:00
var cells = matHead.descendants('.matCell'), cell, expandos;
cell = cells.nodeAt(0);
expandos = expandosFromNode(cell);
expandos.reqType = '*';
expandos.hostname = '*';
addCellClass(cell, '*', '*');
cell = cells.nodeAt(1);
expandos = expandosFromNode(cell);
expandos.reqType = 'cookie';
expandos.hostname = '*';
addCellClass(cell, '*', 'cookie');
cell = cells.nodeAt(2);
expandos = expandosFromNode(cell);
expandos.reqType = 'css';
expandos.hostname = '*';
addCellClass(cell, '*', 'css');
cell = cells.nodeAt(3);
expandos = expandosFromNode(cell);
expandos.reqType = 'image';
expandos.hostname = '*';
addCellClass(cell, '*', 'image');
cell = cells.nodeAt(4);
expandos = expandosFromNode(cell);
2017-04-28 00:10:54 +12:00
expandos.reqType = 'media';
2017-03-16 09:30:48 +13:00
expandos.hostname = '*';
2017-04-28 00:10:54 +12:00
addCellClass(cell, '*', 'media');
2017-03-16 09:30:48 +13:00
cell = cells.nodeAt(5);
expandos = expandosFromNode(cell);
expandos.reqType = 'script';
expandos.hostname = '*';
addCellClass(cell, '*', 'script');
cell = cells.nodeAt(6);
expandos = expandosFromNode(cell);
expandos.reqType = 'xhr';
expandos.hostname = '*';
addCellClass(cell, '*', 'xhr');
cell = cells.nodeAt(7);
expandos = expandosFromNode(cell);
expandos.reqType = 'frame';
expandos.hostname = '*';
addCellClass(cell, '*', 'frame');
cell = cells.nodeAt(8);
expandos = expandosFromNode(cell);
expandos.reqType = 'other';
expandos.hostname = '*';
addCellClass(cell, '*', 'other');
2014-10-18 18:09:09 +13:00
uDom('#matHead .matRow').css('display', '');
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function renderMatrixCellDomain(cell, domain) {
2017-03-16 09:30:48 +13:00
var expandos = expandosFromNode(cell);
expandos.hostname = domain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), domain, '*');
var contents = cell.contents();
contents.nodeAt(0).textContent = domain === '1st-party' ?
firstPartyLabel :
punycode.toUnicode(domain);
2014-10-19 10:11:10 +13:00
contents.nodeAt(1).textContent = ' ';
2014-10-18 08:01:09 +13:00
}
function renderMatrixCellSubdomain(cell, domain, subomain) {
2017-03-16 09:30:48 +13:00
var expandos = expandosFromNode(cell);
expandos.hostname = subomain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), subomain, '*');
var contents = cell.contents();
2014-11-04 08:27:59 +13:00
contents.nodeAt(0).textContent = punycode.toUnicode(subomain.slice(0, subomain.lastIndexOf(domain)-1)) + '.';
2014-10-19 10:11:10 +13:00
contents.nodeAt(1).textContent = punycode.toUnicode(domain);
2014-10-18 08:01:09 +13:00
}
function renderMatrixMetaCellDomain(cell, domain) {
2017-03-16 09:30:48 +13:00
var expandos = expandosFromNode(cell);
expandos.hostname = domain;
expandos.reqType = '*';
addCellClass(cell.nodeAt(0), domain, '*');
var contents = cell.contents();
2014-11-04 08:27:59 +13:00
contents.nodeAt(0).textContent = '\u2217.' + punycode.toUnicode(domain);
2014-10-19 10:11:10 +13:00
contents.nodeAt(1).textContent = ' ';
2014-10-18 08:01:09 +13:00
}
2014-10-20 17:53:13 +13:00
function renderMatrixCellType(cell, hostname, type, count) {
2017-03-16 09:30:48 +13:00
var expandos = expandosFromNode(cell);
expandos.hostname = hostname;
expandos.reqType = type;
expandos.count = count;
addCellClass(cell.nodeAt(0), hostname, type);
2014-10-20 17:53:13 +13:00
if ( count ) {
cell.text(count);
2014-10-18 08:01:09 +13:00
} else {
2014-10-19 10:11:10 +13:00
cell.text('\u00A0');
2014-10-18 08:01:09 +13:00
}
}
2014-10-20 17:53:13 +13:00
function renderMatrixCellTypes(cells, hostname, countName) {
var counts = matrixSnapshot.rows[hostname][countName];
2017-11-29 06:33:22 +13:00
var headerIndices = matrixSnapshot.headerIndices;
renderMatrixCellType(cells.at(1), hostname, 'cookie', counts[headerIndices.get('cookie')]);
renderMatrixCellType(cells.at(2), hostname, 'css', counts[headerIndices.get('css')]);
renderMatrixCellType(cells.at(3), hostname, 'image', counts[headerIndices.get('image')]);
renderMatrixCellType(cells.at(4), hostname, 'media', counts[headerIndices.get('media')]);
renderMatrixCellType(cells.at(5), hostname, 'script', counts[headerIndices.get('script')]);
renderMatrixCellType(cells.at(6), hostname, 'xhr', counts[headerIndices.get('xhr')]);
renderMatrixCellType(cells.at(7), hostname, 'frame', counts[headerIndices.get('frame')]);
renderMatrixCellType(cells.at(8), hostname, 'other', counts[headerIndices.get('other')]);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function makeMatrixRowDomain(domain) {
var matrixRow = createMatrixRow().addClass('rw');
2014-10-19 09:49:06 +13:00
var cells = matrixRow.descendants('.matCell');
2014-10-19 10:11:10 +13:00
renderMatrixCellDomain(cells.at(0), domain);
2014-10-20 17:53:13 +13:00
renderMatrixCellTypes(cells, domain, 'counts');
2014-10-18 08:01:09 +13:00
return matrixRow;
}
function makeMatrixRowSubdomain(domain, subdomain) {
var matrixRow = createMatrixRow().addClass('rw');
2014-10-19 09:49:06 +13:00
var cells = matrixRow.descendants('.matCell');
2014-10-19 10:11:10 +13:00
renderMatrixCellSubdomain(cells.at(0), domain, subdomain);
2014-10-20 17:53:13 +13:00
renderMatrixCellTypes(cells, subdomain, 'counts');
2014-10-18 08:01:09 +13:00
return matrixRow;
}
2014-10-20 17:53:13 +13:00
function makeMatrixMetaRowDomain(domain) {
2014-10-18 08:01:09 +13:00
var matrixRow = createMatrixRow().addClass('rw');
2014-10-19 09:49:06 +13:00
var cells = matrixRow.descendants('.matCell');
2014-10-19 10:11:10 +13:00
renderMatrixMetaCellDomain(cells.at(0), domain);
2014-10-20 17:53:13 +13:00
renderMatrixCellTypes(cells, domain, 'totals');
2014-10-18 08:01:09 +13:00
return matrixRow;
}
/******************************************************************************/
function renderMatrixMetaCellType(cell, count) {
2014-10-30 02:27:11 +13:00
// https://github.com/gorhill/uMatrix/issues/24
// Don't forget to reset cell properties
2017-03-16 09:30:48 +13:00
var expandos = expandosFromNode(cell);
expandos.hostname = '';
expandos.reqType = '';
expandos.count = count;
cell.addClass('t1');
2014-10-18 08:01:09 +13:00
if ( count ) {
2014-10-19 10:11:10 +13:00
cell.text(count);
2014-10-30 03:10:07 +13:00
} else {
cell.text('\u00A0');
2014-10-18 08:01:09 +13:00
}
}
2014-10-20 17:53:13 +13:00
function makeMatrixMetaRow(totals) {
2017-11-29 06:33:22 +13:00
var headerIndices = matrixSnapshot.headerIndices,
matrixRow = createMatrixRow().at(0).addClass('ro'),
cells = matrixRow.descendants('.matCell'),
contents = cells.at(0).addClass('t81').contents(),
expandos = expandosFromNode(cells.nodeAt(0));
2017-03-16 09:30:48 +13:00
expandos.hostname = '';
expandos.reqType = '*';
2014-10-19 10:11:10 +13:00
contents.nodeAt(0).textContent = ' ';
2015-05-18 06:56:53 +12:00
contents.nodeAt(1).textContent = blacklistedHostnamesLabel.replace(
'{{count}}',
2017-11-29 06:33:22 +13:00
totals[headerIndices.get('*')].toLocaleString()
2015-05-18 06:56:53 +12:00
);
2017-11-29 06:33:22 +13:00
renderMatrixMetaCellType(cells.at(1), totals[headerIndices.get('cookie')]);
renderMatrixMetaCellType(cells.at(2), totals[headerIndices.get('css')]);
renderMatrixMetaCellType(cells.at(3), totals[headerIndices.get('image')]);
renderMatrixMetaCellType(cells.at(4), totals[headerIndices.get('media')]);
renderMatrixMetaCellType(cells.at(5), totals[headerIndices.get('script')]);
renderMatrixMetaCellType(cells.at(6), totals[headerIndices.get('xhr')]);
renderMatrixMetaCellType(cells.at(7), totals[headerIndices.get('frame')]);
renderMatrixMetaCellType(cells.at(8), totals[headerIndices.get('other')]);
2014-10-19 09:49:06 +13:00
return matrixRow;
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function computeMatrixGroupMetaStats(group) {
2017-11-29 06:33:22 +13:00
var headerIndices = matrixSnapshot.headerIndices,
anyTypeIndex = headerIndices.get('*'),
n = headerIndices.size,
totals = new Array(n),
i = n;
2014-10-18 08:01:09 +13:00
while ( i-- ) {
2014-10-20 17:53:13 +13:00
totals[i] = 0;
}
var rows = matrixSnapshot.rows, row;
for ( var hostname in rows ) {
if ( rows.hasOwnProperty(hostname) === false ) {
continue;
2014-10-18 08:01:09 +13:00
}
row = rows[hostname];
if ( group.hasOwnProperty(row.domain) === false ) {
continue;
}
2017-11-29 06:33:22 +13:00
if ( row.counts[anyTypeIndex] === 0 ) {
continue;
}
totals[0] += 1;
for ( i = 1; i < n; i++ ) {
totals[i] += row.counts[i];
}
2014-10-18 08:01:09 +13:00
}
2014-10-20 17:53:13 +13:00
return totals;
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// Compare hostname helper, to order hostname in a logical manner:
// top-most < bottom-most, take into account whether IP address or
// named hostname
function hostnameCompare(a,b) {
// Normalize: most significant parts first
if ( !a.match(/^\d+(\.\d+){1,3}$/) ) {
var aa = a.split('.');
a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.');
}
if ( !b.match(/^\d+(\.\d+){1,3}$/) ) {
var bb = b.split('.');
b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.');
}
return a.localeCompare(b);
}
/******************************************************************************/
function makeMatrixGroup0SectionDomain() {
return makeMatrixRowDomain('1st-party').addClass('g0 l1');
2014-10-18 08:01:09 +13:00
}
2014-10-26 13:03:22 +13:00
function makeMatrixGroup0Section() {
2017-03-16 09:30:48 +13:00
var domainDiv = createMatrixSection();
expandosFromNode(domainDiv).domain = '1st-party';
makeMatrixGroup0SectionDomain().appendTo(domainDiv);
2014-10-18 08:01:09 +13:00
return domainDiv;
}
2014-10-26 13:03:22 +13:00
function makeMatrixGroup0() {
// Show literal "1st-party" row only if there is
// at least one 1st-party hostname
if ( Object.keys(groupsSnapshot[1]).length === 0 ) {
return;
}
var groupDiv = createMatrixGroup().addClass('g0');
makeMatrixGroup0Section().appendTo(groupDiv);
groupDiv.appendTo(matrixList);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function makeMatrixGroup1SectionDomain(domain) {
return makeMatrixRowDomain(domain)
.addClass('g1 l1');
}
function makeMatrixGroup1SectionSubomain(domain, subdomain) {
return makeMatrixRowSubdomain(domain, subdomain)
.addClass('g1 l2');
}
2014-10-20 17:53:13 +13:00
function makeMatrixGroup1SectionMetaDomain(domain) {
return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta');
2014-10-18 08:01:09 +13:00
}
function makeMatrixGroup1Section(hostnames) {
var domain = hostnames[0];
var domainDiv = createMatrixSection()
2017-03-16 09:30:48 +13:00
.toggleClass('collapsed', getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
2014-10-18 08:01:09 +13:00
if ( hostnames.length > 1 ) {
makeMatrixGroup1SectionMetaDomain(domain)
.appendTo(domainDiv);
2014-10-18 08:01:09 +13:00
}
makeMatrixGroup1SectionDomain(domain)
.appendTo(domainDiv);
for ( var i = 1; i < hostnames.length; i++ ) {
makeMatrixGroup1SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
return domainDiv;
}
function makeMatrixGroup1(group) {
var domains = Object.keys(group).sort(hostnameCompare);
if ( domains.length ) {
var groupDiv = createMatrixGroup().addClass('g1');
2014-10-20 17:53:13 +13:00
makeMatrixGroup1Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
for ( var i = 1; i < domains.length; i++ ) {
2014-10-20 17:53:13 +13:00
makeMatrixGroup1Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
}
/******************************************************************************/
function makeMatrixGroup2SectionDomain(domain) {
return makeMatrixRowDomain(domain)
.addClass('g2 l1');
}
function makeMatrixGroup2SectionSubomain(domain, subdomain) {
return makeMatrixRowSubdomain(domain, subdomain)
.addClass('g2 l2');
}
2014-10-20 17:53:13 +13:00
function makeMatrixGroup2SectionMetaDomain(domain) {
return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta');
2014-10-18 08:01:09 +13:00
}
function makeMatrixGroup2Section(hostnames) {
var domain = hostnames[0];
var domainDiv = createMatrixSection()
2017-03-16 09:30:48 +13:00
.toggleClass('collapsed', getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
2014-10-18 08:01:09 +13:00
if ( hostnames.length > 1 ) {
2014-10-20 17:53:13 +13:00
makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
2014-10-18 08:01:09 +13:00
}
makeMatrixGroup2SectionDomain(domain)
.appendTo(domainDiv);
for ( var i = 1; i < hostnames.length; i++ ) {
makeMatrixGroup2SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
return domainDiv;
}
function makeMatrixGroup2(group) {
var domains = Object.keys(group).sort(hostnameCompare);
if ( domains.length) {
var groupDiv = createMatrixGroup()
.addClass('g2');
2014-10-20 17:53:13 +13:00
makeMatrixGroup2Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
for ( var i = 1; i < domains.length; i++ ) {
2014-10-20 17:53:13 +13:00
makeMatrixGroup2Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
}
/******************************************************************************/
function makeMatrixGroup3SectionDomain(domain) {
return makeMatrixRowDomain(domain)
.addClass('g3 l1');
}
function makeMatrixGroup3SectionSubomain(domain, subdomain) {
return makeMatrixRowSubdomain(domain, subdomain)
.addClass('g3 l2');
}
function makeMatrixGroup3SectionMetaDomain(domain) {
return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta');
}
2014-10-18 08:01:09 +13:00
function makeMatrixGroup3Section(hostnames) {
var domain = hostnames[0];
var domainDiv = createMatrixSection()
2017-03-16 09:30:48 +13:00
.toggleClass('collapsed', getCollapseState(domain));
expandosFromNode(domainDiv).domain = domain;
if ( hostnames.length > 1 ) {
makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
}
2014-10-18 08:01:09 +13:00
makeMatrixGroup3SectionDomain(domain)
.appendTo(domainDiv);
for ( var i = 1; i < hostnames.length; i++ ) {
makeMatrixGroup3SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
return domainDiv;
}
function makeMatrixGroup3(group) {
var domains = Object.keys(group).sort(hostnameCompare);
if ( domains.length) {
var groupDiv = createMatrixGroup()
.addClass('g3');
makeMatrixGroup3Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
.appendTo(groupDiv);
for ( var i = 1; i < domains.length; i++ ) {
makeMatrixGroup3Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
}
/******************************************************************************/
function makeMatrixGroup4SectionDomain(domain) {
return makeMatrixRowDomain(domain)
.addClass('g4 l1');
}
function makeMatrixGroup4SectionSubomain(domain, subdomain) {
return makeMatrixRowSubdomain(domain, subdomain)
.addClass('g4 l2');
}
function makeMatrixGroup4Section(hostnames) {
var domain = hostnames[0];
2017-03-16 09:30:48 +13:00
var domainDiv = createMatrixSection();
expandosFromNode(domainDiv).domain = domain;
makeMatrixGroup4SectionDomain(domain)
.appendTo(domainDiv);
for ( var i = 1; i < hostnames.length; i++ ) {
makeMatrixGroup4SectionSubomain(domain, hostnames[i])
.appendTo(domainDiv);
}
return domainDiv;
}
function makeMatrixGroup4(group) {
2014-10-18 08:01:09 +13:00
var domains = Object.keys(group).sort(hostnameCompare);
if ( domains.length === 0 ) {
return;
}
var groupDiv = createMatrixGroup().addClass('g4');
2014-10-18 08:01:09 +13:00
createMatrixSection()
.addClass('g4Meta')
.toggleClass('g4Collapsed', !!getUISetting('popupHideBlacklisted'))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4')
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
makeMatrixGroup4Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
for ( var i = 1; i < domains.length; i++ ) {
makeMatrixGroup4Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
2014-10-18 08:01:09 +13:00
.appendTo(groupDiv);
}
groupDiv.appendTo(matrixList);
}
/******************************************************************************/
2014-10-20 17:53:13 +13:00
var makeMenu = function() {
2014-10-18 08:01:09 +13:00
var groupStats = getGroupStats();
if ( Object.keys(groupStats).length === 0 ) { return; }
2014-10-18 08:01:09 +13:00
// https://github.com/gorhill/httpswitchboard/issues/31
if ( matrixCellHotspots ) {
matrixCellHotspots.detach();
}
renderMatrixHeaderRow();
startMatrixUpdate();
makeMatrixGroup0(groupStats[0]);
makeMatrixGroup1(groupStats[1]);
makeMatrixGroup2(groupStats[2]);
makeMatrixGroup3(groupStats[3]);
makeMatrixGroup4(groupStats[4]);
2014-10-18 08:01:09 +13:00
endMatrixUpdate();
initScopeCell();
updateMatrixButtons();
2015-07-29 07:00:31 +12:00
resizePopup();
2014-10-20 17:53:13 +13:00
};
2014-10-18 08:01:09 +13:00
/******************************************************************************/
// Do all the stuff that needs to be done before building menu et al.
function initMenuEnvironment() {
2017-11-25 11:22:42 +13:00
document.body.style.setProperty(
'font-size',
getUserSetting('displayTextSize')
);
document.body.classList.toggle(
2017-11-25 13:55:48 +13:00
'colorblind',
2017-11-25 11:22:42 +13:00
getUserSetting('colorBlindFriendly')
);
uDom.nodeFromId('version').textContent = matrixSnapshot.appVersion || '';
var prettyNames = matrixHeaderPrettyNames;
2014-10-18 08:01:09 +13:00
var keys = Object.keys(prettyNames);
var i = keys.length;
var cell, key, text;
while ( i-- ) {
key = keys[i];
2014-11-02 05:52:02 +13:00
cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
2015-04-12 09:15:57 +12:00
text = vAPI.i18n(key + 'PrettyName');
2014-10-18 08:01:09 +13:00
cell.text(text);
prettyNames[key] = text;
}
2014-11-04 08:27:59 +13:00
firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text();
blacklistedHostnamesLabel = uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// Create page scopes for the web page
function selectGlobalScope() {
2017-11-22 04:36:08 +13:00
if ( matrixSnapshot.scope === '*' ) { return; }
matrixSnapshot.scope = '*';
document.body.classList.add('globalScope');
matrixSnapshot.tMatrixModifiedTime = undefined;
2014-10-20 17:53:13 +13:00
updateMatrixSnapshot();
2014-10-18 08:01:09 +13:00
dropDownMenuHide();
}
2017-11-22 04:36:08 +13:00
function selectSpecificScope(ev) {
var newScope = ev.target.getAttribute('data-scope');
2017-11-22 05:57:41 +13:00
if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
2017-11-22 04:36:08 +13:00
document.body.classList.remove('globalScope');
matrixSnapshot.scope = newScope;
matrixSnapshot.tMatrixModifiedTime = undefined;
2014-10-20 17:53:13 +13:00
updateMatrixSnapshot();
2014-10-18 08:01:09 +13:00
dropDownMenuHide();
}
function initScopeCell() {
// It's possible there is no page URL at this point: some pages cannot
2017-11-22 04:36:08 +13:00
// be filtered by uMatrix.
if ( matrixSnapshot.url === '' ) { return; }
var specificScope = uDom.nodeFromId('specificScope');
while ( specificScope.firstChild !== null ) {
specificScope.removeChild(specificScope.firstChild);
2014-10-18 08:01:09 +13:00
}
2017-11-22 04:36:08 +13:00
2014-10-18 08:01:09 +13:00
// Fill in the scope menu entries
2017-11-22 04:36:08 +13:00
var pos = matrixSnapshot.domain.indexOf('.');
var tld, labels;
if ( pos === -1 ) {
tld = '';
labels = matrixSnapshot.hostname;
2014-10-19 14:42:06 +13:00
} else {
2017-11-22 04:36:08 +13:00
tld = matrixSnapshot.domain.slice(pos + 1);
labels = matrixSnapshot.hostname.slice(0, -tld.length);
}
var beg = 0, span;
while ( beg < labels.length ) {
pos = labels.indexOf('.', beg);
if ( pos === -1 ) {
pos = labels.length;
} else {
pos += 1;
}
span = document.createElement('span');
span.setAttribute('data-scope', labels.slice(beg) + tld);
span.appendChild(
document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
);
specificScope.appendChild(span);
beg = pos;
}
if ( tld !== '' ) {
span = document.createElement('span');
span.setAttribute('data-scope', tld);
span.appendChild(document.createTextNode(punycode.toUnicode(tld)));
specificScope.appendChild(span);
2014-10-19 14:42:06 +13:00
}
2014-10-18 08:01:09 +13:00
updateScopeCell();
}
function updateScopeCell() {
2017-11-22 04:36:08 +13:00
var specificScope = uDom.nodeFromId('specificScope'),
isGlobal = matrixSnapshot.scope === '*';
document.body.classList.toggle('globalScope', isGlobal);
2017-11-22 04:36:08 +13:00
specificScope.classList.toggle('on', !isGlobal);
uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
for ( var node of specificScope.children ) {
2017-11-22 04:36:08 +13:00
node.classList.toggle(
'on',
!isGlobal &&
matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
);
}
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
2014-11-19 15:18:57 +13:00
function updateMatrixSwitches() {
2016-08-12 01:18:15 +12:00
var count = 0,
enabled,
switches = matrixSnapshot.tSwitches;
2014-11-19 15:18:57 +13:00
for ( var switchName in switches ) {
if ( switches.hasOwnProperty(switchName) === false ) {
continue;
}
2016-08-12 11:21:51 +12:00
enabled = switches[switchName];
if ( enabled && switchName !== 'matrix-off' ) {
count += 1;
}
2016-08-12 01:18:15 +12:00
uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
2014-11-19 15:18:57 +13:00
}
2016-08-12 01:18:15 +12:00
uDom('#buttonMtxSwitches').descendants('span.badge').text(count.toLocaleString());
count = matrixSnapshot.blockedCount;
2014-11-19 15:18:57 +13:00
var button = uDom('#mtxSwitch_matrix-off');
button.descendants('span.badge').text(count.toLocaleString());
2014-10-19 09:49:06 +13:00
button.attr('data-tip', button.attr('data-tip').replace('{{count}}', count));
2014-11-19 15:18:57 +13:00
uDom('body').toggleClass('powerOff', switches['matrix-off']);
2014-10-18 08:01:09 +13:00
}
function toggleMatrixSwitch(ev) {
var elem = ev.currentTarget;
var pos = elem.id.indexOf('_');
2014-11-19 15:18:57 +13:00
if ( pos === -1 ) {
return;
}
var switchName = elem.id.slice(pos + 1);
var request = {
what: 'toggleMatrixSwitch',
2014-11-19 15:18:57 +13:00
switchName: switchName,
srcHostname: matrixSnapshot.scope
};
2015-04-12 09:15:57 +12:00
messager.send(request, updateMatrixSnapshot);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function updatePersistButton() {
var diffCount = matrixSnapshot.diff.length;
2014-10-19 09:49:06 +13:00
var button = uDom('#buttonPersist');
2014-10-18 08:01:09 +13:00
button.contents()
.filter(function(){return this.nodeType===3;})
2014-10-19 09:49:06 +13:00
.first()
.text(diffCount > 0 ? '\uf13e' : '\uf023');
button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
var disabled = diffCount === 0;
2014-10-18 08:01:09 +13:00
button.toggleClass('disabled', disabled);
2014-10-18 18:09:09 +13:00
uDom('#buttonRevertScope').toggleClass('disabled', disabled);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function persistMatrix() {
var request = {
what: 'applyDiffToPermanentMatrix',
diff: matrixSnapshot.diff
};
2015-04-12 09:15:57 +12:00
messager.send(request, updateMatrixSnapshot);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// rhill 2014-03-12: revert completely ALL changes related to the
// current page, including scopes.
function revertMatrix() {
var request = {
what: 'applyDiffToTemporaryMatrix',
diff: matrixSnapshot.diff
};
2015-04-12 09:15:57 +12:00
messager.send(request, updateMatrixSnapshot);
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
// Buttons which are affected by any changes in the matrix
function updateMatrixButtons() {
updateScopeCell();
2014-11-19 15:18:57 +13:00
updateMatrixSwitches();
2014-10-18 08:01:09 +13:00
updatePersistButton();
}
/******************************************************************************/
function revertAll() {
var request = {
what: 'revertTemporaryMatrix'
};
2015-04-12 09:15:57 +12:00
messager.send(request, updateMatrixSnapshot);
2015-05-05 12:26:45 +12:00
dropDownMenuHide();
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
2017-11-20 12:18:02 +13:00
function buttonReloadHandler(ev) {
2015-04-12 09:15:57 +12:00
messager.send({
2014-10-18 08:01:09 +13:00
what: 'forceReloadTab',
2017-11-20 12:18:02 +13:00
tabId: matrixSnapshot.tabId,
bypassCache: ev.shiftKey
2014-10-18 08:01:09 +13:00
});
}
/******************************************************************************/
function mouseenterMatrixCellHandler(ev) {
matrixCellHotspots.appendTo(ev.target);
2014-10-18 08:01:09 +13:00
}
function mouseleaveMatrixCellHandler() {
matrixCellHotspots.detach();
}
/******************************************************************************/
function gotoExtensionURL(ev) {
var url = uDom(ev.currentTarget).attr('data-extension-url');
2014-10-18 08:01:09 +13:00
if ( url ) {
2015-04-12 09:15:57 +12:00
messager.send({ what: 'gotoExtensionURL', url: url });
2014-10-18 08:01:09 +13:00
}
dropDownMenuHide();
2015-05-03 05:43:50 +12:00
vAPI.closePopup();
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
function dropDownMenuShow(ev) {
var button = ev.target;
var menu = button.nextElementSibling;
var butnRect = button.getBoundingClientRect();
var viewRect = document.body.getBoundingClientRect();
var butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
menu.classList.add('show');
var menuRect = menu.getBoundingClientRect();
var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
menu.style.left = menuLeft.toFixed(0) + 'px';
2014-10-18 08:01:09 +13:00
}
function dropDownMenuHide() {
2014-10-18 18:09:09 +13:00
uDom('.dropdown-menu').removeClass('show');
2014-10-18 08:01:09 +13:00
}
/******************************************************************************/
2014-10-20 17:53:13 +13:00
var onMatrixSnapshotReady = function(response) {
2017-11-25 11:22:42 +13:00
if ( response === 'ENOTFOUND' ) {
uDom.nodeFromId('noTabFound').textContent =
vAPI.i18n('matrixNoTabFound');
document.body.classList.add('noTabFound');
return;
}
2014-10-18 08:01:09 +13:00
// Now that tabId and pageURL are set, we can build our menu
initMenuEnvironment();
makeMenu();
// After popup menu is built, check whether there is a non-empty matrix
if ( matrixSnapshot.url === '' ) {
2014-10-19 09:49:06 +13:00
uDom('#matHead').remove();
2017-11-22 04:36:08 +13:00
uDom('#toolbarContainer').remove();
2014-10-18 08:01:09 +13:00
// https://github.com/gorhill/httpswitchboard/issues/191
2015-04-12 09:15:57 +12:00
uDom('#noNetTrafficPrompt').text(vAPI.i18n('matrixNoNetTrafficPrompt'));
2014-10-18 18:09:09 +13:00
uDom('#noNetTrafficPrompt').css('display', '');
2014-10-18 08:01:09 +13:00
}
// Create a hash to find out whether the reload button needs to be
// highlighted.
// TODO:
2014-10-18 08:01:09 +13:00
};
2015-07-25 00:22:54 +12:00
/******************************************************************************/
var matrixSnapshotPoller = (function() {
var timer = null;
2017-11-29 06:33:22 +13:00
var preprocessMatrixSnapshot = function(snapshot) {
if ( Array.isArray(snapshot.headerIndices) ) {
snapshot.headerIndices = new Map(snapshot.headerIndices);
}
return snapshot;
};
2015-05-07 10:59:07 +12:00
var processPollResult = function(response) {
if ( typeof response !== 'object' ) {
return;
}
if (
response.mtxContentModified === false &&
response.mtxCountModified === false &&
response.pMatrixModified === false &&
response.tMatrixModified === false
2015-05-07 10:59:07 +12:00
) {
return;
}
2017-11-29 06:33:22 +13:00
matrixSnapshot = preprocessMatrixSnapshot(response);
2015-05-07 10:59:07 +12:00
if ( response.mtxContentModified ) {
makeMenu();
return;
}
if ( response.mtxCountModified ) {
updateMatrixCounts();
}
if (
response.pMatrixModified ||
response.tMatrixModified ||
response.scopeModified
) {
2015-05-07 10:59:07 +12:00
updateMatrixColors();
updateMatrixBehavior();
updateMatrixButtons();
}
};
2015-05-07 10:59:07 +12:00
var onPolled = function(response) {
processPollResult(response);
pollAsync();
};
var pollNow = function() {
unpollAsync();
messager.send({
what: 'matrixSnapshot',
tabId: matrixSnapshot.tabId,
2017-11-22 04:36:08 +13:00
scope: matrixSnapshot.scope,
mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime,
mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime,
mtxDiffCount: matrixSnapshot.diff.length,
pMatrixModifiedTime: matrixSnapshot.pMatrixModifiedTime,
tMatrixModifiedTime: matrixSnapshot.tMatrixModifiedTime,
}, onPolled);
};
var poll = function() {
timer = null;
2015-05-07 10:59:07 +12:00
pollNow();
};
var pollAsync = function() {
if ( timer !== null ) {
return;
}
2015-07-29 07:00:31 +12:00
if ( document.defaultView === null ) {
return;
}
timer = vAPI.setTimeout(poll, 1414);
2014-10-28 17:38:02 +13:00
};
var unpollAsync = function() {
if ( timer !== null ) {
clearTimeout(timer);
timer = null;
}
};
(function() {
var tabId = matrixSnapshot.tabId;
// If no tab id yet, see if there is one specified in our URL
if ( tabId === undefined ) {
var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/);
if ( matches !== null ) {
tabId = matches[1];
// No need for logger button when embedded in logger
uDom('[data-extension-url="logger-ui.html"]').remove();
}
}
var snapshotFetched = function(response) {
if ( typeof response === 'object' ) {
2017-11-29 06:33:22 +13:00
matrixSnapshot = preprocessMatrixSnapshot(response);
}
2017-11-25 11:22:42 +13:00
onMatrixSnapshotReady(response);
pollAsync();
};
messager.send({
what: 'matrixSnapshot',
tabId: tabId
}, snapshotFetched);
})();
2015-05-07 10:59:07 +12:00
return {
pollNow: pollNow
};
})();
2014-10-20 17:53:13 +13:00
2014-10-18 08:01:09 +13:00
/******************************************************************************/
// 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;
2014-10-18 08:01:09 +13:00
});
matrixCellHotspots = uDom('#cellHotspots').detach();
uDom('body')
.on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
.on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
2017-11-22 04:36:08 +13:00
uDom('#specificScope').on('click', selectSpecificScope);
uDom('#globalScope').on('click', selectGlobalScope);
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');
setUISetting('popupHideBlacklisted', collapsed);
2014-10-18 08:01:09 +13:00
});
/******************************************************************************/
})();