').addClass('matSection');
};
var createMatrixRow = function() {
var row = matrixRowPool.pop();
if ( row ) {
row.style.visibility = '';
- row = $(row);
- row.children('.matCell').removeClass().addClass('matCell');
+ row = uDom(row);
+ row.descendants('.matCell').removeClass().addClass('matCell');
row.removeClass().addClass('matRow');
return row;
}
if ( matrixRowTemplate === null ) {
- matrixRowTemplate = $('#templates .matRow');
+ matrixRowTemplate = uDom('#templates .matRow');
}
return matrixRowTemplate.clone();
};
@@ -759,7 +757,7 @@ var createMatrixRow = function() {
function renderMatrixHeaderRow() {
var matHead = uDom('#matHead.collapsible');
matHead.toggleClass('collapsed', getUserSetting('popupCollapseDomains'));
- var cells = matHead.find('.matCell');
+ var cells = matHead.descendants('.matCell');
uDom(cells.node(0))
.prop('reqType', '*')
.prop('hostname', '*')
@@ -845,38 +843,38 @@ function renderMatrixCellType(cell, hostname, type, stats) {
}
function renderMatrixCellTypes(cells, hostname, stats) {
- renderMatrixCellType(cells[1], hostname, 'cookie', stats.cookie);
- renderMatrixCellType(cells[2], hostname, 'css', stats.css);
- renderMatrixCellType(cells[3], hostname, 'image', stats.image);
- renderMatrixCellType(cells[4], hostname, 'plugin', stats.plugin);
- renderMatrixCellType(cells[5], hostname, 'script', stats.script);
- renderMatrixCellType(cells[6], hostname, 'xhr', stats.xhr);
- renderMatrixCellType(cells[7], hostname, 'frame', stats.frame);
- renderMatrixCellType(cells[8], hostname, 'other', stats.other);
+ renderMatrixCellType(cells.unode(1), hostname, 'cookie', stats.cookie);
+ renderMatrixCellType(cells.unode(2), hostname, 'css', stats.css);
+ renderMatrixCellType(cells.unode(3), hostname, 'image', stats.image);
+ renderMatrixCellType(cells.unode(4), hostname, 'plugin', stats.plugin);
+ renderMatrixCellType(cells.unode(5), hostname, 'script', stats.script);
+ renderMatrixCellType(cells.unode(6), hostname, 'xhr', stats.xhr);
+ renderMatrixCellType(cells.unode(7), hostname, 'frame', stats.frame);
+ renderMatrixCellType(cells.unode(8), hostname, 'other', stats.other);
}
/******************************************************************************/
function makeMatrixRowDomain(domain) {
var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.children('.matCell');
- renderMatrixCellDomain(cells[0], domain);
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellDomain(cells.node(0), domain);
renderMatrixCellTypes(cells, domain, HTTPSBPopup.matrixStats[domain].types);
return matrixRow;
}
function makeMatrixRowSubdomain(domain, subdomain) {
var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.children('.matCell');
- renderMatrixCellSubdomain(cells[0], domain, subdomain);
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellSubdomain(cells.node(0), domain, subdomain);
renderMatrixCellTypes(cells, subdomain, HTTPSBPopup.matrixStats[subdomain].types);
return matrixRow;
}
function makeMatrixMetaRowDomain(domain, stats) {
var matrixRow = createMatrixRow().addClass('rw');
- var cells = matrixRow.children('.matCell');
- renderMatrixMetaCellDomain(cells[0], domain);
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixMetaCellDomain(cells.node(0), domain);
renderMatrixCellTypes(cells, domain, stats);
return matrixRow;
}
@@ -893,11 +891,9 @@ function renderMatrixMetaCellType(cell, count) {
function makeMatrixMetaRow(stats) {
var typeStats = stats.types;
- var matrixRow = uDom(createMatrixRow()[0]).addClass('ro');
- var cells = matrixRow.find('.matCell');
- var contents = uDom(cells.node(0))
- .addClass('matCell rd')
- .contents();
+ var matrixRow = createMatrixRow().unode(0).addClass('ro');
+ var cells = matrixRow.descendants('.matCell');
+ var contents = cells.unode(0).addClass('rd').contents();
contents.node(0).textContent = ' ';
contents.node(1).textContent = '\u202A' + typeStats['*'].count + ' blacklisted hostname(s)';
renderMatrixMetaCellType(cells.node(1), typeStats.cookie.count);
@@ -908,7 +904,7 @@ function makeMatrixMetaRow(stats) {
renderMatrixMetaCellType(cells.node(6), typeStats.xhr.count);
renderMatrixMetaCellType(cells.node(7), typeStats.frame.count);
renderMatrixMetaCellType(cells.node(8), typeStats.other.count);
- return $(matrixRow.node(0));
+ return matrixRow;
}
/******************************************************************************/
@@ -1204,7 +1200,7 @@ function initMenuEnvironment() {
var cell, key, text;
while ( i-- ) {
key = keys[i];
- cell = $('#matHead .matCell[data-filter-type="'+ key +'"]');
+ cell = uDom('#matHead .matCell[data-filter-type="'+ key +'"]');
text = chrome.i18n.getMessage(key + 'PrettyName');
cell.text(text);
prettyNames[key] = text;
@@ -1242,19 +1238,14 @@ function createSiteScope() {
dropDownMenuHide();
}
-function getClassSuffixFromScopeKey(scopeKey) {
- if ( scopeKey === '*' ) {
- return 'ScopeGlobal';
+function getClassFromTargetScope() {
+ if ( targetScope === '*' ) {
+ return 'tScopeGlobal';
}
- return 'ScopeLocal';
-}
-
-function getClassFromTemporaryScopeKey(scopeKey) {
- return 't' + getClassSuffixFromScopeKey(scopeKey);
-}
-
-function getClassFromPermanentScopeKey(scopeKey) {
- return 'p' + getClassSuffixFromScopeKey(scopeKey);
+ if ( targetScope === targetPageDomain ) {
+ return 'tScopeNarrow';
+ }
+ return 'tScopeNarrow';
}
function initScopeCell() {
@@ -1264,21 +1255,19 @@ function initScopeCell() {
return;
}
// Fill in the scope menu entries
- var µm = µMatrix;
if ( targetPageDomain === '' || targetPageDomain === targetPageHostname ) {
- $('#scopeKeyDomain').css('display', 'none');
+ uDom('#scopeKeyDomain').css('display', 'none');
} else {
- $('#scopeKeyDomain').text(targetPageDomain);
+ uDom('#scopeKeyDomain').text(targetPageDomain);
}
- $('#scopeKeySite').text(targetPageHostname);
+ uDom('#scopeKeySite').text(targetPageHostname);
updateScopeCell();
}
function updateScopeCell() {
- var µm = µMatrix;
uDom('body')
- .removeClass('tScopeGlobal tScopeLocal tScopeSite')
- .addClass(getClassFromTemporaryScopeKey(targetScope))
+ .removeClass('tScopeGlobal tScopeNarrow')
+ .addClass(getClassFromTargetScope());
uDom('#scopeCell').text(targetScope.replace('*', '\u2217'));
}
@@ -1289,11 +1278,11 @@ function updateMtxbutton() {
var masterSwitch = µm.getTemporaryMtxFiltering(targetScope);
var pageStats = getPageStats();
var count = pageStats ? pageStats.requestStats.blocked.all : '';
- var button = $('#buttonMtxFiltering');
+ var button = uDom('#buttonMtxFiltering');
button.toggleClass('disabled', !masterSwitch);
- button.children('span.badge').text(µm.formatCount(count));
- button.attr('data-tip', button.data('tip').replace('{{count}}', count));
- $('body').toggleClass('powerOff', !masterSwitch);
+ button.descendants('span.badge').text(µm.formatCount(count));
+ button.attr('data-tip', button.attr('data-tip').replace('{{count}}', count));
+ uDom('body').toggleClass('powerOff', !masterSwitch);
}
function toggleMtxFiltering() {
@@ -1309,12 +1298,12 @@ function toggleMtxFiltering() {
function updatePersistButton() {
var ruleset = getTemporaryRuleset();
- var button = $('#buttonPersist');
+ var button = uDom('#buttonPersist');
button.contents()
.filter(function(){return this.nodeType===3;})
- .first()[0]
- .textContent = ruleset.count > 0 ? '\uf13e' : '\uf023';
- button.children('span.badge').text(ruleset.count > 0 ? ruleset.count : '');
+ .first()
+ .text(ruleset.count > 0 ? '\uf13e' : '\uf023');
+ button.descendants('span.badge').text(ruleset.count > 0 ? ruleset.count : '');
var disabled = ruleset.count === 0;
button.toggleClass('disabled', disabled);
uDom('#buttonRevertScope').toggleClass('disabled', disabled);
@@ -1405,7 +1394,7 @@ function gotoExternalURL() {
/******************************************************************************/
function dropDownMenuShow() {
- $(this).next('.dropdown-menu').addClass('show');
+ uDom(this).next('.dropdown-menu').addClass('show');
}
function dropDownMenuHide() {
@@ -1443,8 +1432,8 @@ var bindToTab = function(tabs) {
// After popup menu is built, check whether there is a non-empty matrix
if ( !targetPageURL ) {
- $('#matHead').remove();
- $('#toolbarLeft').remove();
+ uDom('#matHead').remove();
+ uDom('#toolbarLeft').remove();
// https://github.com/gorhill/httpswitchboard/issues/191
uDom('#noNetTrafficPrompt').text(chrome.i18n.getMessage('matrixNoNetTrafficPrompt'));
@@ -1456,55 +1445,50 @@ var bindToTab = function(tabs) {
// Make menu only when popup html is fully loaded
-$(function() {
+uDom.onLoad(function() {
chrome.tabs.query({ currentWindow: true, active: true }, bindToTab);
// 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.
- var popup = HTTPSBPopup;
-
// Matrix appearance
- $('body').css('font-size', getUserSetting('displayTextSize'));
- $('body').toggleClass('colorblind', getUserSetting('colorBlindFriendly') === true);
+ uDom('body').css('font-size', getUserSetting('displayTextSize'));
+ uDom('body').toggleClass('colorblind', getUserSetting('colorBlindFriendly') === true);
// We reuse for all cells the one and only cell hotspots.
- matrixCellHotspots = $('#cellHotspots').detach();
- $('#whitelist', matrixCellHotspots)
- .on('click', function() {
- handleWhitelistFilter($(this));
+ uDom('#whitelist').on('click', function() {
+ handleWhitelistFilter(uDom(this));
return false;
});
- $('#blacklist', matrixCellHotspots)
- .on('click', function() {
- handleBlacklistFilter($(this));
+ uDom('#blacklist').on('click', function() {
+ handleBlacklistFilter(uDom(this));
return false;
});
- $('#domainOnly', matrixCellHotspots)
- .on('click', function() {
- toggleCollapseState(this);
+ uDom('#domainOnly').on('click', function() {
+ toggleCollapseState(uDom(this));
return false;
});
- $('body')
+ matrixCellHotspots = uDom('#cellHotspots').detach();
+ uDom('body')
.on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
.on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
- $('#scopeKeyGlobal').on('click', createGlobalScope);
- $('#scopeKeyDomain').on('click', createDomainScope);
- $('#scopeKeySite').on('click', createSiteScope);
- $('#buttonMtxFiltering').on('click', toggleMtxFiltering);
- $('#buttonPersist').on('click', persistScope);
- $('#buttonRevertScope').on('click', revertScope);
-
- $('#buttonRevertAll').on('click', revertAll);
- $('#buttonReload').on('click', buttonReloadHandler);
- $('.extensionURL').on('click', gotoExtensionURL);
- $('.externalURL').on('click', gotoExternalURL);
-
- $('body').on('click', '.dropdown-menu-button', dropDownMenuShow);
- $('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
-
- $('#matList').on('click', '.g3Meta', function() {
+ uDom('#scopeKeyGlobal').on('click', createGlobalScope);
+ uDom('#scopeKeyDomain').on('click', createDomainScope);
+ uDom('#scopeKeySite').on('click', createSiteScope);
+ uDom('#buttonMtxFiltering').on('click', toggleMtxFiltering);
+ uDom('#buttonPersist').on('click', persistScope);
+ uDom('#buttonRevertScope').on('click', revertScope);
+
+ uDom('#buttonRevertAll').on('click', revertAll);
+ uDom('#buttonReload').on('click', buttonReloadHandler);
+ uDom('.extensionURL').on('click', gotoExtensionURL);
+ uDom('.externalURL').on('click', gotoExternalURL);
+
+ uDom('body').on('click', '.dropdown-menu-button', dropDownMenuShow);
+ uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
+
+ uDom('#matList').on('click', '.g3Meta', function() {
var collapsed = uDom(this)
.toggleClass('g3Collapsed')
.hasClass('g3Collapsed');
diff --git a/src/js/udom.js b/src/js/udom.js
index 5782e16..5bd8855 100644
--- a/src/js/udom.js
+++ b/src/js/udom.js
@@ -38,12 +38,15 @@ var DOMList = function() {
/******************************************************************************/
-var addNodeToList = function(list, node) {
- if ( node ) {
- list.nodes.push(node);
+Object.defineProperty(
+ DOMList.prototype,
+ 'length',
+ {
+ get: function() {
+ return this.nodes.length;
+ }
}
- return list;
-};
+);
/******************************************************************************/
@@ -78,6 +81,15 @@ DOMListFactory.onLoad = function(callback) {
/******************************************************************************/
+var addNodeToList = function(list, node) {
+ if ( node ) {
+ list.nodes.push(node);
+ }
+ return list;
+};
+
+/******************************************************************************/
+
var addNodeListToList = function(list, nodelist) {
if ( nodelist ) {
var n = nodelist.length;
@@ -114,6 +126,8 @@ var pTagOfChildTag = {
'option': 'select'
};
+// TODO: documentFragment
+
var addHTMLToList = function(list, html) {
var matches = html.match(/^<([a-z]+)/);
if ( !matches || matches.length !== 2 ) {
@@ -134,6 +148,12 @@ var addHTMLToList = function(list, html) {
/******************************************************************************/
+var isChildOf = function(child, parent) {
+ return child !== null && parent !== null && child.parentNode === parent;
+};
+
+/******************************************************************************/
+
var isDescendantOf = function(descendant, ancestor) {
while ( descendant.parentNode !== null ) {
if ( descendant.parentNode === ancestor ) {
@@ -146,6 +166,34 @@ var isDescendantOf = function(descendant, ancestor) {
/******************************************************************************/
+var doesMatchSelector = function(node, selector) {
+ if ( !node ) {
+ return false;
+ }
+ if ( node.nodeType !== 1 ) {
+ return false;
+ }
+ if ( selector === undefined ) {
+ return true;
+ }
+ var parentNode = node.parentNode;
+ if ( !parentNode || !parentNode.setAttribute ) {
+ return false;
+ }
+ var doesMatch = false;
+ parentNode.setAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO', '');
+ var grandpaNode = parentNode.parentNode || document;
+ var nl = grandpaNode.querySelectorAll('[uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO] > ' + selector);
+ var i = nl.length;
+ while ( doesMatch === false && i-- ) {
+ doesMatch = nl[i] === node;
+ }
+ parentNode.removeAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO');
+ return doesMatch;
+};
+
+/******************************************************************************/
+
DOMList.prototype.length = function() {
return this.nodes.length;
};
@@ -156,6 +204,16 @@ DOMList.prototype.node = function(i) {
return this.nodes[i];
};
+DOMList.prototype.unode = function(i) {
+ return addNodeToList(new DOMList(), this.nodes[i]);
+};
+
+/******************************************************************************/
+
+DOMList.prototype.toArray = function() {
+ return this.nodes.slice();
+};
+
/******************************************************************************/
DOMList.prototype.subset = function(i, l) {
@@ -176,6 +234,29 @@ DOMList.prototype.first = function() {
/******************************************************************************/
+DOMList.prototype.next = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ while ( node.nextSibling !== null ) {
+ node = node.nextSibling;
+ if ( node.nodeType !== 1 ) {
+ continue;
+ }
+ if ( doesMatchSelector(node, selector) === false ) {
+ continue;
+ }
+ addNodeToList(r, node);
+ break;
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
DOMList.prototype.parent = function() {
var r = new DOMList();
if ( this.nodes.length ) {
@@ -186,21 +267,46 @@ DOMList.prototype.parent = function() {
/******************************************************************************/
-DOMList.prototype.ancestors = function(selector) {
+DOMList.prototype.filter = function(filter) {
var r = new DOMList();
- if ( this.nodes.length === 0 ) {
- return r;
+ var filterFunc;
+ if ( typeof filter === 'string' ) {
+ filterFunc = function() {
+ return doesMatchSelector(this, filter);
+ };
+ } else if ( typeof filter === 'function' ) {
+ filterFunc = filter;
+ } else {
+ filterFunc = function(){
+ return true;
+ };
}
- var candidates = document.querySelectorAll(selector);
- var i = candidates.length;
- var j, candidate;
- while ( i-- ) {
- candidate = candidates[i];
- j = this.nodes.length;
- while ( j-- ) {
- if ( isDescendantOf(this.nodes[j], candidate) ) {
- addNodeToList(r, candidate);
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ if ( filterFunc.apply(node) ) {
+ addNodeToList(r, node);
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+// TODO: Avoid possible duplicates
+
+DOMList.prototype.ancestors = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i].parentNode;
+ while ( node ) {
+ if ( doesMatchSelector(node, selector) ) {
+ addNodeToList(r, node);
}
+ node = node.parentNode;
}
}
return r;
@@ -208,7 +314,7 @@ DOMList.prototype.ancestors = function(selector) {
/******************************************************************************/
-DOMList.prototype.find = function(selector) {
+DOMList.prototype.descendants = function(selector) {
var r = new DOMList();
var n = this.nodes.length;
var nl;
@@ -248,17 +354,19 @@ DOMList.prototype.forEach = function(callback) {
/******************************************************************************/
DOMList.prototype.remove = function() {
- var n = this.nodes.length;
- var c, p;
- for ( var i = 0; i < n; i++ ) {
- c = this.nodes[i];
- if ( p = c.parentNode ) {
- p.removeChild(c);
+ var cn, p;
+ var i = this.nodes.length;
+ while ( i-- ) {
+ cn = this.nodes[i];
+ if ( p = cn.parentNode ) {
+ p.removeChild(cn);
}
}
return this;
};
+DOMList.prototype.detach = DOMList.prototype.remove;
+
/******************************************************************************/
DOMList.prototype.empty = function() {
@@ -304,12 +412,10 @@ DOMList.prototype.prepend = function(selector, context) {
/******************************************************************************/
DOMList.prototype.appendTo = function(selector, context) {
- var p = DOMListFactory(selector, context);
- if ( p.length ) {
- var n = this.nodes.length;
- for ( var i = 0; i < n; i++ ) {
- p.nodes[0].appendChild(this.nodes[i]);
- }
+ var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context);
+ var n = p.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.nodes[0].appendChild(this.nodes[i]);
}
return this;
};
@@ -491,22 +597,10 @@ DOMList.prototype.toggleClass = function(className, targetState) {
/******************************************************************************/
-var makeEventHandler = function(context, selector, callback) {
+var makeEventHandler = function(selector, callback) {
return function(event) {
- var candidates = context.querySelectorAll(selector);
- if ( !candidates.length ) {
- return;
- }
- var node = event.target;
- var i;
- while ( node && node !== context ) {
- i = candidates.length;
- while ( i-- ) {
- if ( candidates[i] === node ) {
- return callback.call(node, event);
- }
- }
- node = node.parentNode;
+ if ( doesMatchSelector(event.target, selector) ) {
+ callback.call(event.target, event);
}
};
};
@@ -519,7 +613,7 @@ DOMList.prototype.on = function(etype, selector, callback) {
var i = this.nodes.length;
while ( i-- ) {
if ( selector !== undefined ) {
- this.nodes[i].addEventListener(etype, makeEventHandler(this.nodes[i], selector, callback), true);
+ this.nodes[i].addEventListener(etype, makeEventHandler(selector, callback), true);
} else {
this.nodes[i].addEventListener(etype, callback);
}
diff --git a/src/popup.html b/src/popup.html
index a180bfa..163d46e 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -72,7 +72,6 @@