mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-26 04:12:50 +01:00
some more work re inspector
This commit is contained in:
parent
58b6552207
commit
39b0d719c0
@ -100,6 +100,10 @@ MessagingListeners.prototype.remove = function(callback) {
|
||||
this.listeners.splice(this.listeners.indexOf(callback), 1);
|
||||
};
|
||||
|
||||
MessagingListeners.prototype.removeAll = function() {
|
||||
this.listeners = [];
|
||||
};
|
||||
|
||||
MessagingListeners.prototype.process = function(msg) {
|
||||
var listeners = this.listeners;
|
||||
var n = listeners.length;
|
||||
@ -209,6 +213,10 @@ MessagingChannel.prototype.removeListener = function(callback) {
|
||||
this.listeners.remove(callback);
|
||||
};
|
||||
|
||||
MessagingChannel.prototype.removeAllListeners = function() {
|
||||
this.listeners.removeAll();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.messaging = {
|
||||
|
@ -94,6 +94,10 @@ MessagingListeners.prototype.remove = function(callback) {
|
||||
this.listeners.splice(this.listeners.indexOf(callback), 1);
|
||||
};
|
||||
|
||||
MessagingListeners.prototype.removeAll = function() {
|
||||
this.listeners = [];
|
||||
};
|
||||
|
||||
MessagingListeners.prototype.process = function(msg) {
|
||||
var listeners = this.listeners;
|
||||
var n = listeners.length;
|
||||
@ -199,6 +203,10 @@ MessagingChannel.prototype.removeListener = function(callback) {
|
||||
this.listeners.remove(callback);
|
||||
};
|
||||
|
||||
MessagingChannel.prototype.removeAllListeners = function() {
|
||||
this.listeners.removeAll();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.messaging = {
|
||||
|
88
src/css/logger-ui-inspector.css
Normal file
88
src/css/logger-ui-inspector.css
Normal file
@ -0,0 +1,88 @@
|
||||
#domInspector {
|
||||
border-top: 1px solid #ccc;
|
||||
display: none;
|
||||
max-height: 40%;
|
||||
min-height: 40%;
|
||||
overflow: auto;
|
||||
}
|
||||
#domInspector.enabled {
|
||||
display: block;
|
||||
}
|
||||
#domInspector .permatoolbar {
|
||||
position: absolute;
|
||||
}
|
||||
#domInspector .permatoolbar .highlightMode.invert {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
#domInspector > ul:first-of-type {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
#domInspector ul {
|
||||
background-color: #fff;
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
#domInspector li {
|
||||
list-style-type: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#domInspector li.isCosmeticHide,
|
||||
#domInspector li.isCosmeticHide ul,
|
||||
#domInspector li.isCosmeticHide li {
|
||||
background-color: #fee;
|
||||
}
|
||||
#domInspector li > * {
|
||||
margin-right: 1em;
|
||||
}
|
||||
#domInspector li > span:first-child {
|
||||
color: #000;
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
margin-right: 0;
|
||||
opacity: 0.5;
|
||||
visibility: hidden;
|
||||
width: 1em;
|
||||
}
|
||||
#domInspector li > span:first-child:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#domInspector li > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
#domInspector li > span:first-child:before {
|
||||
content: '\a0';
|
||||
}
|
||||
#domInspector li.branch > span:first-child:before {
|
||||
content: '\25b8';
|
||||
visibility: visible;
|
||||
}
|
||||
#domInspector li.branch.show > span:first-child:before {
|
||||
content: '\25be';
|
||||
visibility: visible;
|
||||
}
|
||||
#domInspector li.branch.hasCosmeticHide > span:first-child:before {
|
||||
color: red;
|
||||
}
|
||||
#domInspector li > code {
|
||||
cursor: pointer;
|
||||
font: 12px/1.4 monospace;
|
||||
}
|
||||
#domInspector li > code.off {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
#domInspector li > span {
|
||||
color: #aaa;
|
||||
}
|
||||
#domInspector li > code.filter {
|
||||
color: red;
|
||||
}
|
||||
#domInspector li > ul {
|
||||
display: none;
|
||||
}
|
||||
#domInspector li.show > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cosmeticFilteringDialog .dialog textarea {
|
||||
height: 40vh;
|
||||
}
|
@ -16,6 +16,12 @@ body {
|
||||
input:focus {
|
||||
background-color: #ffe;
|
||||
}
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
direction: ltr;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
}
|
||||
.permatoolbar {
|
||||
background-color: white;
|
||||
border: 0;
|
||||
@ -55,85 +61,6 @@ input:focus {
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
#domInspector {
|
||||
border-top: 1px solid #ccc;
|
||||
display: none;
|
||||
max-height: 40%;
|
||||
min-height: 40%;
|
||||
overflow: auto;
|
||||
}
|
||||
#domInspector.enabled {
|
||||
display: block;
|
||||
}
|
||||
#domInspector > ul:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
#domInspector ul {
|
||||
background-color: #fff;
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
#domInspector li {
|
||||
list-style-type: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#domInspector li.isCosmeticHide,
|
||||
#domInspector li.isCosmeticHide ul,
|
||||
#domInspector li.isCosmeticHide li {
|
||||
background-color: #fee;
|
||||
}
|
||||
#domInspector li > * {
|
||||
margin-right: 1em;
|
||||
}
|
||||
#domInspector li > span:first-child {
|
||||
color: #000;
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
margin-right: 0;
|
||||
opacity: 0.5;
|
||||
visibility: hidden;
|
||||
width: 1em;
|
||||
}
|
||||
#domInspector li > span:first-child:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#domInspector li > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
#domInspector li > span:first-child:before {
|
||||
content: '\a0';
|
||||
}
|
||||
#domInspector li.branch > span:first-child:before {
|
||||
content: '\25b8';
|
||||
visibility: visible;
|
||||
}
|
||||
#domInspector li.branch.show > span:first-child:before {
|
||||
content: '\25be';
|
||||
visibility: visible;
|
||||
}
|
||||
#domInspector li.branch.hasCosmeticHide > span:first-child:before {
|
||||
color: red;
|
||||
}
|
||||
#domInspector li > code {
|
||||
cursor: pointer;
|
||||
font: 12px/1.4 monospace;
|
||||
}
|
||||
#domInspector li > code.off {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
#domInspector li > span {
|
||||
color: #aaa;
|
||||
}
|
||||
#domInspector li > code.filter {
|
||||
color: red;
|
||||
}
|
||||
#domInspector li > ul {
|
||||
display: none;
|
||||
}
|
||||
#domInspector li.show > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#events {
|
||||
border-top: 1px solid #ccc;
|
||||
font: 13px sans-serif;
|
||||
@ -623,11 +550,7 @@ body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.e
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
#netFilteringDialog .dialog > div.containers > div.static textarea {
|
||||
box-sizing: border-box;
|
||||
direction: ltr;
|
||||
height: 6em;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
}
|
||||
#netFilteringDialog .dialog > div.containers > div.static > p:nth-of-type(2) {
|
||||
text-align: center;
|
||||
|
671
src/js/logger-ui-inspector.js
Normal file
671
src/js/logger-ui-inspector.js
Normal file
@ -0,0 +1,671 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015 Raymond Hill
|
||||
|
||||
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/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, uDom */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Don't bother if the browser is not modern enough.
|
||||
if ( typeof Map === undefined || typeof WeakMap === undefined ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logger = self.logger;
|
||||
var messager = logger.messager;
|
||||
|
||||
var inspectedTabId = '';
|
||||
var inspectedHostname = '';
|
||||
var pollTimer = null;
|
||||
var fingerprint = null;
|
||||
var showdomButton = uDom.nodeFromId('showdom');
|
||||
var inspector = uDom.nodeFromId('domInspector');
|
||||
var domTree = uDom.nodeFromId('domTree');
|
||||
var tabSelector = uDom.nodeFromId('pageSelector');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var nodeFromDomEntry = function(entry) {
|
||||
var node, value;
|
||||
var li = document.createElement('li');
|
||||
li.setAttribute('id', entry.nid);
|
||||
// expander/collapser
|
||||
node = document.createElement('span');
|
||||
li.appendChild(node);
|
||||
// selector
|
||||
node = document.createElement('code');
|
||||
node.textContent = entry.sel;
|
||||
li.appendChild(node);
|
||||
// descendant count
|
||||
value = entry.cnt || 0;
|
||||
node = document.createElement('span');
|
||||
node.textContent = value !== 0 ? value.toLocaleString() : '';
|
||||
node.setAttribute('data-cnt', value);
|
||||
li.appendChild(node);
|
||||
// cosmetic filter
|
||||
if ( entry.filter !== undefined ) {
|
||||
node = document.createElement('code');
|
||||
node.classList.add('filter');
|
||||
node.textContent = entry.filter;
|
||||
li.appendChild(node);
|
||||
li.classList.add('isCosmeticHide');
|
||||
}
|
||||
return li;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var appendListItem = function(ul, li) {
|
||||
ul.appendChild(li);
|
||||
// Ancestor nodes of a node which is affected by a cosmetic filter will
|
||||
// be marked as "containing cosmetic filters", for user convenience.
|
||||
if ( li.classList.contains('isCosmeticHide') === false ) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
li = li.parentElement.parentElement;
|
||||
if ( li === null ) {
|
||||
break;
|
||||
}
|
||||
li.classList.add('hasCosmeticHide');
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderDOMFull = function(response) {
|
||||
var ul = inspector.removeChild(domTree);
|
||||
logger.removeAllChildren(domTree);
|
||||
|
||||
var lvl = 0;
|
||||
var entries = response.layout;
|
||||
var n = entries.length;
|
||||
var li, entry;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
entry = entries[i];
|
||||
if ( entry.lvl === lvl ) {
|
||||
li = nodeFromDomEntry(entry);
|
||||
appendListItem(ul, li);
|
||||
//expandIfBlockElement(li);
|
||||
continue;
|
||||
}
|
||||
if ( entry.lvl > lvl ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
li = nodeFromDomEntry(entry);
|
||||
appendListItem(ul, li);
|
||||
//expandIfBlockElement(li);
|
||||
lvl = entry.lvl;
|
||||
continue;
|
||||
}
|
||||
// entry.lvl < lvl
|
||||
while ( entry.lvl < lvl ) {
|
||||
ul = li.parentNode;
|
||||
li = ul.parentNode;
|
||||
ul = li.parentNode;
|
||||
lvl -= 1;
|
||||
}
|
||||
li = nodeFromDomEntry(entry);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
while ( ul.parentNode !== null ) {
|
||||
ul = ul.parentNode;
|
||||
}
|
||||
ul.firstElementChild.classList.add('show');
|
||||
|
||||
inspector.appendChild(domTree);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var patchIncremental = function(from, delta) {
|
||||
var span, cnt;
|
||||
var li = from.parentElement.parentElement;
|
||||
var patchCosmeticHide = delta >= 0 &&
|
||||
from.classList.contains('isCosmeticFilter') &&
|
||||
li.classList.contains('hasCosmeticFilter') === false;
|
||||
// Include descendants count when removing a node
|
||||
if ( delta < 0 ) {
|
||||
delta -= countFromNode(from);
|
||||
}
|
||||
for ( ; li.localName === 'li'; li = li.parentElement.parentElement ) {
|
||||
span = li.children[2];
|
||||
if ( delta !== 0 ) {
|
||||
cnt = countFromNode(li) + delta;
|
||||
span.textContent = cnt !== 0 ? cnt.toLocaleString() : '';
|
||||
span.setAttribute('data-cnt', cnt);
|
||||
}
|
||||
if ( patchCosmeticHide ) {
|
||||
li.classList.add('hasCosmeticFilter');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderDOMIncremental = function(response) {
|
||||
// Process each journal entry:
|
||||
// 1 = node added
|
||||
// -1 = node removed
|
||||
var journal = response.journal;
|
||||
var nodes = response.nodes;
|
||||
var entry, previous, li, ul;
|
||||
for ( var i = 0, n = journal.length; i < n; i++ ) {
|
||||
entry = journal[i];
|
||||
// Remove node
|
||||
if ( entry.what === -1 ) {
|
||||
li = document.getElementById(entry.nid);
|
||||
if ( li === null ) {
|
||||
continue;
|
||||
}
|
||||
patchIncremental(li, -1);
|
||||
li.parentNode.removeChild(li);
|
||||
continue;
|
||||
}
|
||||
// Modify node
|
||||
if ( entry.what === 0 ) {
|
||||
// TODO: update selector/filter
|
||||
continue;
|
||||
}
|
||||
// Add node as sibling
|
||||
if ( entry.what === 1 && entry.l ) {
|
||||
previous = document.getElementById(entry.l);
|
||||
// This should not happen
|
||||
if ( previous === null ) {
|
||||
// throw new Error('No left sibling!?');
|
||||
continue;
|
||||
}
|
||||
ul = previous.parentElement;
|
||||
li = nodeFromDomEntry(nodes[entry.nid]);
|
||||
ul.insertBefore(li, previous.nextElementSibling);
|
||||
patchIncremental(li, 1);
|
||||
continue;
|
||||
}
|
||||
// Add node as child
|
||||
if ( entry.what === 1 && entry.u ) {
|
||||
li = document.getElementById(entry.u);
|
||||
// This should not happen
|
||||
if ( li === null ) {
|
||||
// throw new Error('No parent!?');
|
||||
continue;
|
||||
}
|
||||
ul = li.querySelector('ul');
|
||||
if ( ul === null ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
}
|
||||
li = nodeFromDomEntry(nodes[entry.nid]);
|
||||
ul.appendChild(li);
|
||||
patchIncremental(li, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var countFromNode = function(li) {
|
||||
var span = li.children[2];
|
||||
var cnt = parseInt(span.getAttribute('data-cnt'), 10);
|
||||
return isNaN(cnt) ? cnt : 0;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var selectorFromNode = function(node, nth) {
|
||||
var selector = '';
|
||||
var code;
|
||||
if ( nth === undefined ) {
|
||||
nth = 1;
|
||||
}
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
code = node.querySelector('code:nth-of-type(' + nth + ')');
|
||||
if ( code !== null ) {
|
||||
selector = code.textContent + ' > ' + selector;
|
||||
if ( selector.indexOf('#') !== -1 ) {
|
||||
break;
|
||||
}
|
||||
nth = 1;
|
||||
}
|
||||
}
|
||||
node = node.parentElement;
|
||||
}
|
||||
return selector.slice(0, -3);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var nidFromNode = function(node) {
|
||||
var li = node;
|
||||
while ( li !== null ) {
|
||||
if ( li.localName === 'li' ) {
|
||||
return li.id || '';
|
||||
}
|
||||
li = li.parentElement;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var startDialog = (function() {
|
||||
var dialog = uDom.nodeFromId('cosmeticFilteringDialog');
|
||||
var candidateFilters = [];
|
||||
|
||||
var onClick = function(ev) {
|
||||
var target = ev.target;
|
||||
|
||||
// click outside the dialog proper
|
||||
if ( target.classList.contains('modalDialog') ) {
|
||||
return stop();
|
||||
}
|
||||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
var stop = function() {
|
||||
dialog.removeEventListener('click', onClick, true);
|
||||
document.body.removeChild(dialog);
|
||||
};
|
||||
|
||||
var start = function() {
|
||||
// Collect all selectors which are currently toggled
|
||||
var node, filters = [];
|
||||
var nodes = domTree.querySelectorAll('code.off');
|
||||
for ( var i = 0; i < nodes.length; i++ ) {
|
||||
node = nodes[i];
|
||||
if ( node.classList.contains('filter') ) {
|
||||
filters.push({
|
||||
prefix: '#@#',
|
||||
nid: '',
|
||||
selector: node.textContent
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
prefix: '##',
|
||||
nid: nidFromNode(node),
|
||||
selector: node.textContent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Send filters through dom-inspector.js for further processing.
|
||||
|
||||
candidateFilters = filters;
|
||||
var taValue = [], filter;
|
||||
for ( i = 0; i < filters.length; i++ ) {
|
||||
filter = filters[i];
|
||||
taValue.push(inspectedHostname + filter.prefix + filter.selector);
|
||||
}
|
||||
dialog.querySelector('textarea').value = taValue.join('\n');
|
||||
document.body.appendChild(dialog);
|
||||
dialog.addEventListener('click', onClick, true);
|
||||
};
|
||||
|
||||
return start;
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onClick = function(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
if ( inspectedTabId === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = ev.target;
|
||||
var parent = target.parentElement;
|
||||
|
||||
// Expand/collapse branch
|
||||
if (
|
||||
target.localName === 'span' &&
|
||||
parent instanceof HTMLLIElement &&
|
||||
parent.classList.contains('branch') &&
|
||||
target === parent.firstElementChild
|
||||
) {
|
||||
target.parentElement.classList.toggle('show');
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle selector
|
||||
if ( target.localName === 'code' ) {
|
||||
var original = target.classList.contains('filter') === false;
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'toggleNodes',
|
||||
original: original,
|
||||
target: original !== target.classList.toggle('off'),
|
||||
selector: selectorFromNode(target, original ? 1 : 2),
|
||||
nid: original ? nidFromNode(target) : ''
|
||||
}
|
||||
});
|
||||
var cantCreate = inspector.querySelector('#domTree .off') === null;
|
||||
inspector.querySelector('.permatoolbar .revert').classList.toggle('disabled', cantCreate);
|
||||
inspector.querySelector('.permatoolbar .commit').classList.toggle('disabled', cantCreate);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMouseOver = (function() {
|
||||
var mouseoverTarget = null;
|
||||
var mouseoverTimer = null;
|
||||
|
||||
var timerHandler = function() {
|
||||
mouseoverTimer = null;
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'highlightOne',
|
||||
selector: selectorFromNode(mouseoverTarget),
|
||||
nid: nidFromNode(mouseoverTarget),
|
||||
scrollTo: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return function(ev) {
|
||||
if ( inspectedTabId === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find closest `li`
|
||||
var target = ev.target;
|
||||
while ( target !== null ) {
|
||||
if ( target.localName === 'li' ) {
|
||||
break;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
if ( target === mouseoverTarget ) {
|
||||
return;
|
||||
}
|
||||
mouseoverTarget = target;
|
||||
if ( mouseoverTimer === null ) {
|
||||
mouseoverTimer = vAPI.setTimeout(timerHandler, 50);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var currentTabId = function() {
|
||||
if ( showdomButton.classList.contains('active') === false ) {
|
||||
return '';
|
||||
}
|
||||
var tabId = logger.tabIdFromClassName(tabSelector.value) || '';
|
||||
return tabId !== 'bts' ? tabId : '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var cancelPollTimer = function() {
|
||||
if ( pollTimer !== null ) {
|
||||
clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onDOMFetched = function(response) {
|
||||
if ( response === undefined || currentTabId() !== inspectedTabId ) {
|
||||
shutdownInspector(inspectedTabId);
|
||||
injectInspectorAsync(250);
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( response.status ) {
|
||||
case 'full':
|
||||
renderDOMFull(response);
|
||||
fingerprint = response.fingerprint;
|
||||
inspectedHostname = response.hostname;
|
||||
break;
|
||||
|
||||
case 'incremental':
|
||||
renderDOMIncremental(response);
|
||||
break;
|
||||
|
||||
case 'nochange':
|
||||
case 'busy':
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fetchDOMAsync();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var fetchDOM = function() {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'domLayout',
|
||||
fingerprint: fingerprint
|
||||
}
|
||||
});
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
onDOMFetched();
|
||||
}, 1001);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var fetchDOMAsync = function(delay) {
|
||||
if ( pollTimer !== null ) {
|
||||
return;
|
||||
}
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
fetchDOM();
|
||||
}, delay || 1001);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var injectInspector = function() {
|
||||
var tabId = currentTabId();
|
||||
// No valid tab, go back
|
||||
if ( tabId === '' ) {
|
||||
injectInspectorAsync();
|
||||
return;
|
||||
}
|
||||
inspectedTabId = tabId;
|
||||
fingerprint = null;
|
||||
messager.send({
|
||||
what: 'scriptlet',
|
||||
tabId: tabId,
|
||||
scriptlet: 'dom-inspector'
|
||||
});
|
||||
fetchDOMAsync(250);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var injectInspectorAsync = function(delay) {
|
||||
if ( pollTimer !== null ) {
|
||||
return;
|
||||
}
|
||||
if ( showdomButton.classList.contains('active') === false ) {
|
||||
return;
|
||||
}
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
injectInspector();
|
||||
}, delay || 1001);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var shutdownInspector = function(tabId) {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: tabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: { what: 'shutdown', }
|
||||
});
|
||||
logger.removeAllChildren(domTree);
|
||||
cancelPollTimer();
|
||||
inspectedTabId = '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onTabIdChanged = function() {
|
||||
if ( inspectedTabId !== currentTabId() ) {
|
||||
shutdownInspector();
|
||||
injectInspectorAsync(250);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleHighlightMode = function() {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'highlightMode',
|
||||
invert: uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').classList.toggle('invert')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var revert = function() {
|
||||
uDom('#domTree .off').removeClass('off');
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: { what: 'resetToggledNodes' }
|
||||
});
|
||||
inspector.querySelector('.permatoolbar .revert').classList.add('disabled');
|
||||
inspector.querySelector('.permatoolbar .commit').classList.add('disabled');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request) {
|
||||
var msg = request.what === 'postMessageTo' ? request.msg : request;
|
||||
switch ( msg.what ) {
|
||||
case 'domLayout':
|
||||
cancelPollTimer();
|
||||
onDOMFetched(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleOn = function() {
|
||||
window.addEventListener('beforeunload', toggleOff);
|
||||
tabSelector.addEventListener('change', onTabIdChanged);
|
||||
domTree.addEventListener('click', onClick, true);
|
||||
domTree.addEventListener('mouseover', onMouseOver, true);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').addEventListener('click', toggleHighlightMode);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .revert').addEventListener('click', revert);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .commit').addEventListener('click', startDialog);
|
||||
inspector.classList.add('enabled');
|
||||
messager.addListener(onMessage);
|
||||
injectInspector();
|
||||
// Adjust tree view for toolbar height
|
||||
domTree.style.setProperty(
|
||||
'margin-top',
|
||||
inspector.querySelector('.permatoolbar').clientHeight + 'px'
|
||||
);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleOff = function() {
|
||||
messager.removeListener(onMessage);
|
||||
cancelPollTimer();
|
||||
shutdownInspector();
|
||||
window.removeEventListener('beforeunload', toggleOff);
|
||||
tabSelector.removeEventListener('change', onTabIdChanged);
|
||||
domTree.removeEventListener('click', onClick, true);
|
||||
domTree.removeEventListener('mouseover', onMouseOver, true);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').removeEventListener('click', toggleHighlightMode);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .revert').removeEventListener('click', revert);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .commit').removeEventListener('click', startDialog);
|
||||
inspectedTabId = '';
|
||||
inspector.classList.remove('enabled');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggle = function() {
|
||||
if ( showdomButton.classList.toggle('active') ) {
|
||||
toggleOn();
|
||||
} else {
|
||||
toggleOff();
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
showdomButton.addEventListener('click', toggle);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
|
@ -30,6 +30,30 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logger = self.logger = {};
|
||||
var messager = logger.messager = vAPI.messaging.channel('logger-ui.js');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var removeAllChildren = logger.removeAllChildren = function(node) {
|
||||
while ( node.firstChild ) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var tabIdFromClassName = logger.tabIdFromClassName = function(className) {
|
||||
var matches = className.match(/(?:^| )tab_([^ ]+)(?: |$)/);
|
||||
if ( matches === null ) {
|
||||
return '';
|
||||
}
|
||||
return matches[1];
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// Adjust top padding of content table, to match that of toolbar height.
|
||||
|
||||
(function() {
|
||||
@ -47,8 +71,6 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('logger-ui.js');
|
||||
|
||||
var tbody = document.querySelector('#events tbody');
|
||||
var trJunkyard = [];
|
||||
var tdJunkyard = [];
|
||||
@ -61,7 +83,6 @@ var allTabIdsToken;
|
||||
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
|
||||
var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
|
||||
var netFilteringDialog = uDom.nodeFromId('netFilteringDialog');
|
||||
var filterFinderDialog = uDom.nodeFromId('filterFinderDialog');
|
||||
|
||||
var prettyRequestTypes = {
|
||||
'main_frame': 'doc',
|
||||
@ -98,14 +119,6 @@ var dateOptions = {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var removeAllChildren = function(node) {
|
||||
while ( node.firstChild ) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var classNameFromTabId = function(tabId) {
|
||||
if ( tabId === noTabId ) {
|
||||
return 'tab_bts';
|
||||
@ -116,503 +129,6 @@ var classNameFromTabId = function(tabId) {
|
||||
return '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var tabIdFromClassName = function(className) {
|
||||
var matches = className.match(/(?:^| )tab_([^ ]+)(?: |$)/);
|
||||
if ( matches === null ) {
|
||||
return '';
|
||||
}
|
||||
return matches[1];
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// DOM inspector
|
||||
|
||||
(function domInspector() {
|
||||
// Don't bother if the browser is not modern enough.
|
||||
if ( typeof Map === undefined || typeof WeakMap === undefined ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var inspectedTabId = '';
|
||||
var currentSelector = '';
|
||||
var showdomButton = uDom.nodeFromId('showdom');
|
||||
var inspector = uDom.nodeFromId('domInspector');
|
||||
var tabSelector = uDom.nodeFromId('pageSelector');
|
||||
|
||||
var nodeFromDomEntry = function(entry) {
|
||||
var node, value;
|
||||
var li = document.createElement('li');
|
||||
li.setAttribute('id', entry.nid);
|
||||
// expander/collapser
|
||||
node = document.createElement('span');
|
||||
li.appendChild(node);
|
||||
// selector
|
||||
node = document.createElement('code');
|
||||
node.textContent = entry.sel;
|
||||
li.appendChild(node);
|
||||
// descendant count
|
||||
value = entry.cnt || 0;
|
||||
node = document.createElement('span');
|
||||
node.textContent = value !== 0 ? value.toLocaleString() : '';
|
||||
node.setAttribute('data-cnt', value);
|
||||
li.appendChild(node);
|
||||
// cosmetic filter
|
||||
if ( entry.filter !== undefined ) {
|
||||
node = document.createElement('code');
|
||||
node.classList.add('filter');
|
||||
node.textContent = entry.filter;
|
||||
li.appendChild(node);
|
||||
li.classList.add('isCosmeticHide');
|
||||
}
|
||||
return li;
|
||||
};
|
||||
|
||||
var appendListItem = function(ul, li) {
|
||||
ul.appendChild(li);
|
||||
// Ancestor nodes of a node which is affected by a cosmetic filter will
|
||||
// be marked as "containing cosmetic filters", for user convenience.
|
||||
if ( li.classList.contains('isCosmeticHide') === false ) {
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
li = li.parentElement.parentElement;
|
||||
if ( li === null ) {
|
||||
break;
|
||||
}
|
||||
li.classList.add('hasCosmeticHide');
|
||||
}
|
||||
};
|
||||
|
||||
var renderDOMFull = function(response) {
|
||||
var ul = document.createElement('ul');
|
||||
var lvl = 0;
|
||||
var entries = response.layout;
|
||||
var n = entries.length;
|
||||
var li, entry;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
entry = entries[i];
|
||||
if ( entry.lvl === lvl ) {
|
||||
li = nodeFromDomEntry(entry);
|
||||
appendListItem(ul, li);
|
||||
//expandIfBlockElement(li);
|
||||
continue;
|
||||
}
|
||||
if ( entry.lvl > lvl ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
li = nodeFromDomEntry(entry);
|
||||
appendListItem(ul, li);
|
||||
//expandIfBlockElement(li);
|
||||
lvl = entry.lvl;
|
||||
continue;
|
||||
}
|
||||
// entry.lvl < lvl
|
||||
while ( entry.lvl < lvl ) {
|
||||
ul = li.parentNode;
|
||||
li = ul.parentNode;
|
||||
ul = li.parentNode;
|
||||
lvl -= 1;
|
||||
}
|
||||
li = nodeFromDomEntry(entry);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
while ( ul.parentNode !== null ) {
|
||||
ul = ul.parentNode;
|
||||
}
|
||||
ul.firstElementChild.classList.add('show');
|
||||
|
||||
removeAllChildren(inspector);
|
||||
inspector.appendChild(ul);
|
||||
};
|
||||
|
||||
var patchIncremental = function(from, delta) {
|
||||
var span, cnt;
|
||||
var li = from.parentElement.parentElement;
|
||||
var patchCosmeticHide = delta >= 0 &&
|
||||
from.classList.contains('isCosmeticFilter') &&
|
||||
li.classList.contains('hasCosmeticFilter') === false;
|
||||
// Include descendants count when removing a node
|
||||
if ( delta < 0 ) {
|
||||
delta -= countFromNode(from);
|
||||
}
|
||||
for ( ; li.localName === 'li'; li = li.parentElement.parentElement ) {
|
||||
span = li.children[2];
|
||||
if ( delta !== 0 ) {
|
||||
cnt = countFromNode(li) + delta;
|
||||
span.textContent = cnt !== 0 ? cnt.toLocaleString() : '';
|
||||
span.setAttribute('data-cnt', cnt);
|
||||
}
|
||||
if ( patchCosmeticHide ) {
|
||||
li.classList.add('hasCosmeticFilter');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var renderDOMIncremental = function(response) {
|
||||
// Process each journal entry:
|
||||
// 1 = node added
|
||||
// -1 = node removed
|
||||
var journal = response.journal;
|
||||
var nodes = response.nodes;
|
||||
var entry, previous, li, ul;
|
||||
for ( var i = 0, n = journal.length; i < n; i++ ) {
|
||||
entry = journal[i];
|
||||
// Remove node
|
||||
if ( entry.what === -1 ) {
|
||||
li = document.getElementById(entry.nid);
|
||||
if ( li === null ) {
|
||||
continue;
|
||||
}
|
||||
patchIncremental(li, -1);
|
||||
li.parentNode.removeChild(li);
|
||||
continue;
|
||||
}
|
||||
// Modify node
|
||||
if ( entry.what === 0 ) {
|
||||
// TODO: update selector/filter
|
||||
continue;
|
||||
}
|
||||
// Add node as sibling
|
||||
if ( entry.what === 1 && entry.l ) {
|
||||
previous = document.getElementById(entry.l);
|
||||
// This should not happen
|
||||
if ( previous === null ) {
|
||||
// throw new Error('No left sibling!?');
|
||||
continue;
|
||||
}
|
||||
ul = previous.parentElement;
|
||||
li = nodeFromDomEntry(nodes[entry.nid]);
|
||||
ul.insertBefore(li, previous.nextElementSibling);
|
||||
patchIncremental(li, 1);
|
||||
continue;
|
||||
}
|
||||
// Add node as child
|
||||
if ( entry.what === 1 && entry.u ) {
|
||||
li = document.getElementById(entry.u);
|
||||
// This should not happen
|
||||
if ( li === null ) {
|
||||
// throw new Error('No parent!?');
|
||||
continue;
|
||||
}
|
||||
ul = li.querySelector('ul');
|
||||
if ( ul === null ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
}
|
||||
li = nodeFromDomEntry(nodes[entry.nid]);
|
||||
ul.appendChild(li);
|
||||
patchIncremental(li, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var countFromNode = function(li) {
|
||||
var span = li.children[2];
|
||||
var cnt = parseInt(span.getAttribute('data-cnt'), 10);
|
||||
return isNaN(cnt) ? cnt : 0;
|
||||
};
|
||||
|
||||
var selectorFromNode = function(node, nth) {
|
||||
var selector = '';
|
||||
var code;
|
||||
if ( nth === undefined ) {
|
||||
nth = 1;
|
||||
}
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
code = node.querySelector('code:nth-of-type(' + nth + ')');
|
||||
if ( code !== null ) {
|
||||
selector = code.textContent + ' > ' + selector;
|
||||
if ( selector.indexOf('#') !== -1 ) {
|
||||
break;
|
||||
}
|
||||
nth = 1;
|
||||
}
|
||||
}
|
||||
node = node.parentElement;
|
||||
}
|
||||
return selector.slice(0, -3);
|
||||
};
|
||||
|
||||
var onClick = function(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
if ( inspectedTabId === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = ev.target;
|
||||
var parent = target.parentElement;
|
||||
|
||||
// Expand/collapse branch
|
||||
if (
|
||||
target.localName === 'span' &&
|
||||
parent instanceof HTMLLIElement &&
|
||||
parent.classList.contains('branch') &&
|
||||
target === parent.firstElementChild
|
||||
) {
|
||||
target.parentElement.classList.toggle('show');
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle selector
|
||||
if ( target.localName === 'code' ) {
|
||||
var original = target.classList.contains('filter') === false;
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'toggleNodes',
|
||||
original: original,
|
||||
target: original !== target.classList.toggle('off'),
|
||||
selector: selectorFromNode(target, original ? 1 : 2)
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Highlight and scrollto
|
||||
if ( target.localName === 'code' ) {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'highlight',
|
||||
selector: selectorFromNode(target),
|
||||
scrollTo: true
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
var onMouseOver = (function() {
|
||||
var mouseoverTarget = null;
|
||||
var mouseoverTimer = null;
|
||||
|
||||
var timerHandler = function() {
|
||||
mouseoverTimer = null;
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'highlight',
|
||||
selector: selectorFromNode(mouseoverTarget),
|
||||
scrollTo: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return function(ev) {
|
||||
if ( inspectedTabId === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find closest `li`
|
||||
var target = ev.target;
|
||||
while ( target !== null ) {
|
||||
if ( target.localName === 'li' ) {
|
||||
break;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
if ( target === mouseoverTarget ) {
|
||||
return;
|
||||
}
|
||||
mouseoverTarget = target;
|
||||
if ( mouseoverTimer === null ) {
|
||||
mouseoverTimer = vAPI.setTimeout(timerHandler, 50);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var pollTimer = null;
|
||||
var fingerprint = null;
|
||||
|
||||
var currentTabId = function() {
|
||||
if ( showdomButton.classList.contains('active') === false ) {
|
||||
return '';
|
||||
}
|
||||
var tabId = tabIdFromClassName(tabSelector.value) || '';
|
||||
return tabId !== 'bts' ? tabId : '';
|
||||
};
|
||||
|
||||
var cancelPollTimer = function() {
|
||||
if ( pollTimer !== null ) {
|
||||
clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
var onDOMFetched = function(response) {
|
||||
if ( response === undefined || currentTabId() !== inspectedTabId ) {
|
||||
shutdownInspector(inspectedTabId);
|
||||
injectInspectorAsync(250);
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( response.status ) {
|
||||
case 'full':
|
||||
renderDOMFull(response);
|
||||
fingerprint = response.fingerprint;
|
||||
break;
|
||||
|
||||
case 'incremental':
|
||||
renderDOMIncremental(response);
|
||||
break;
|
||||
|
||||
case 'nochange':
|
||||
case 'busy':
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fetchDOMAsync();
|
||||
};
|
||||
|
||||
var fetchDOM = function() {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: inspectedTabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: {
|
||||
what: 'domLayout',
|
||||
fingerprint: fingerprint
|
||||
}
|
||||
});
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
onDOMFetched();
|
||||
}, 1001);
|
||||
};
|
||||
|
||||
var fetchDOMAsync = function(delay) {
|
||||
if ( pollTimer !== null ) {
|
||||
return;
|
||||
}
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
fetchDOM();
|
||||
}, delay || 1001);
|
||||
};
|
||||
|
||||
var injectInspector = function() {
|
||||
var tabId = currentTabId();
|
||||
// No valid tab, go back
|
||||
if ( tabId === '' ) {
|
||||
injectInspectorAsync();
|
||||
return;
|
||||
}
|
||||
inspectedTabId = tabId;
|
||||
fingerprint = null;
|
||||
messager.send({
|
||||
what: 'scriptlet',
|
||||
tabId: tabId,
|
||||
scriptlet: 'dom-inspector'
|
||||
});
|
||||
fetchDOMAsync(250);
|
||||
};
|
||||
|
||||
var injectInspectorAsync = function(delay) {
|
||||
if ( pollTimer !== null ) {
|
||||
return;
|
||||
}
|
||||
if ( showdomButton.classList.contains('active') === false ) {
|
||||
return;
|
||||
}
|
||||
pollTimer = vAPI.setTimeout(function() {
|
||||
pollTimer = null;
|
||||
injectInspector();
|
||||
}, delay || 1001);
|
||||
};
|
||||
|
||||
var shutdownInspector = function(tabId) {
|
||||
messager.send({
|
||||
what: 'postMessageTo',
|
||||
senderTabId: null,
|
||||
senderChannel: 'logger-ui.js',
|
||||
receiverTabId: tabId,
|
||||
receiverChannel: 'dom-inspector.js',
|
||||
msg: { what: 'shutdown', }
|
||||
});
|
||||
removeAllChildren(inspector);
|
||||
cancelPollTimer();
|
||||
inspectedTabId = '';
|
||||
};
|
||||
|
||||
var onTabIdChanged = function() {
|
||||
if ( inspectedTabId !== currentTabId() ) {
|
||||
shutdownInspector();
|
||||
injectInspectorAsync(250);
|
||||
}
|
||||
};
|
||||
|
||||
var onMessage = function(request) {
|
||||
var msg = request.what === 'postMessageTo' ? request.msg : request;
|
||||
switch ( msg.what ) {
|
||||
case 'domLayout':
|
||||
cancelPollTimer();
|
||||
onDOMFetched(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var toggleOn = function() {
|
||||
window.addEventListener('beforeunload', toggleOff);
|
||||
inspector.addEventListener('click', onClick, true);
|
||||
inspector.addEventListener('mouseover', onMouseOver, true);
|
||||
tabSelector.addEventListener('change', onTabIdChanged);
|
||||
inspector.classList.add('enabled');
|
||||
messager.addListener(onMessage);
|
||||
injectInspector();
|
||||
};
|
||||
|
||||
var toggleOff = function() {
|
||||
messager.removeListener(onMessage);
|
||||
cancelPollTimer();
|
||||
shutdownInspector();
|
||||
window.removeEventListener('beforeunload', toggleOff);
|
||||
inspector.removeEventListener('click', onClick, true);
|
||||
inspector.removeEventListener('mouseover', onMouseOver, true);
|
||||
tabSelector.removeEventListener('change', onTabIdChanged);
|
||||
currentSelector = inspectedTabId = '';
|
||||
inspector.classList.remove('enabled');
|
||||
};
|
||||
|
||||
var toggle = function() {
|
||||
if ( showdomButton.classList.toggle('active') ) {
|
||||
toggleOn();
|
||||
} else {
|
||||
toggleOff();
|
||||
}
|
||||
};
|
||||
|
||||
showdomButton.addEventListener('click', toggle);
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
@ -1776,6 +1292,7 @@ var netFilteringManager = (function() {
|
||||
var reverseLookupManager = (function() {
|
||||
var reSentence1 = /\{\{filter\}\}/g;
|
||||
var sentence1Template = vAPI.i18n('loggerStaticFilteringFinderSentence1');
|
||||
var filterFinderDialog = uDom.nodeFromId('filterFinderDialog');
|
||||
|
||||
var removeAllChildren = function(node) {
|
||||
while ( node.firstChild ) {
|
||||
|
@ -144,8 +144,9 @@ var svgOcean = null;
|
||||
var svgIslands = null;
|
||||
var svgRoot = null;
|
||||
var pickerRoot = null;
|
||||
var currentSelector = '';
|
||||
var highlightedElements = [];
|
||||
|
||||
var nodeToIdMap = new WeakMap(); // No need to iterate
|
||||
var toggledNodes = new Map();
|
||||
|
||||
/******************************************************************************/
|
||||
@ -175,7 +176,6 @@ var domLayout = (function() {
|
||||
};
|
||||
|
||||
var idGenerator = 0;
|
||||
var nodeToIdMap = new WeakMap(); // No need to iterate
|
||||
|
||||
// This will be used to uniquely identify nodes across process.
|
||||
|
||||
@ -209,7 +209,7 @@ var domLayout = (function() {
|
||||
}
|
||||
return out;
|
||||
})();
|
||||
|
||||
/*
|
||||
var matchesSelector = (function() {
|
||||
if ( typeof Element.prototype.matches === 'function' ) {
|
||||
return 'matches';
|
||||
@ -222,26 +222,7 @@ var domLayout = (function() {
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
|
||||
var hasManyMatches = function(node, selector) {
|
||||
var fnName = matchesSelector;
|
||||
if ( fnName === '' ) {
|
||||
return true;
|
||||
}
|
||||
var child = node.firstElementChild;
|
||||
var match = false;
|
||||
while ( child !== null ) {
|
||||
if ( child[fnName](selector) ) {
|
||||
if ( match ) {
|
||||
return true;
|
||||
}
|
||||
match = true;
|
||||
}
|
||||
child = child.nextElementSibling;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
*/
|
||||
var selectorFromNode = function(node) {
|
||||
var str, attr, pos, sw, i;
|
||||
var tag = node.localName;
|
||||
@ -276,18 +257,6 @@ var domLayout = (function() {
|
||||
selector += '[' + attr + sw + '="' + cssEscape(str) + '"]';
|
||||
}
|
||||
}
|
||||
// The resulting selector must cause only one element to be selected. If
|
||||
// it's not the case, further narrow using `nth-of-type` pseudo-class.
|
||||
if ( hasManyMatches(node.parentElement, selector) ) {
|
||||
i = 1;
|
||||
while ( node.previousElementSibling ) {
|
||||
node = node.previousElementSibling;
|
||||
if ( node.localName === tag ) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
selector += ':nth-of-type(' + i + ')';
|
||||
}
|
||||
return selector;
|
||||
};
|
||||
|
||||
@ -543,7 +512,8 @@ var domLayout = (function() {
|
||||
|
||||
var response = {
|
||||
what: 'domLayout',
|
||||
fingerprint: domFingerprint()
|
||||
fingerprint: domFingerprint(),
|
||||
hostname: window.location.hostname
|
||||
};
|
||||
|
||||
// No mutation observer means we need to send full layout
|
||||
@ -593,7 +563,8 @@ var domLayout = (function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var highlightElements = function(elems, scrollTo) {
|
||||
var highlightElements = function(scrollTo) {
|
||||
var elems = highlightedElements;
|
||||
var wv = pickerRoot.contentWindow.innerWidth;
|
||||
var hv = pickerRoot.contentWindow.innerHeight;
|
||||
var ocean = ['M0 0h' + wv + 'v' + hv + 'h-' + wv, 'z'];
|
||||
@ -684,15 +655,31 @@ var elementsFromSelector = function(filter) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var highlight = function(scrollTo) {
|
||||
var elements = elementsFromSelector(currentSelector);
|
||||
highlightElements(elements, scrollTo);
|
||||
var selectNodes = function(selector, nid) {
|
||||
var nodes = elementsFromSelector(selector);
|
||||
if ( nid === '' ) {
|
||||
return nodes;
|
||||
}
|
||||
var i = nodes.length;
|
||||
while ( i-- ) {
|
||||
if ( nodeToIdMap.get(nodes[i]) === nid ) {
|
||||
return [nodes[i]];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var hightlightNodes = function(selector, nid, scrollTo) {
|
||||
highlightedElements = selectNodes(selector, nid);
|
||||
highlightElements(scrollTo);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onScrolled = function() {
|
||||
highlight();
|
||||
highlightElements();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -703,8 +690,7 @@ var onScrolled = function() {
|
||||
// hidden, any = remove display property, don't remember original state
|
||||
// hidden, hidden = set display to `none`
|
||||
|
||||
var toggleNodes = function(selector, originalState, targetState) {
|
||||
var nodes = document.querySelectorAll(selector);
|
||||
var toggleNodes = function(nodes, originalState, targetState) {
|
||||
var i = nodes.length;
|
||||
if ( i === 0 ) {
|
||||
return;
|
||||
@ -759,13 +745,13 @@ var resetToggledNodes = function() {
|
||||
var shutdown = function() {
|
||||
resetToggledNodes();
|
||||
domLayout.shutdown();
|
||||
localMessager.removeListener(onMessage);
|
||||
localMessager.removeAllListeners();
|
||||
localMessager.close();
|
||||
localMessager = null;
|
||||
window.removeEventListener('scroll', onScrolled, true);
|
||||
document.documentElement.removeChild(pickerRoot);
|
||||
pickerRoot = svgRoot = svgOcean = svgIslands = null;
|
||||
currentSelector = '';
|
||||
highlightedElements = [];
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -779,15 +765,22 @@ var onMessage = function(request) {
|
||||
response = domLayout.get(msg.fingerprint);
|
||||
break;
|
||||
|
||||
case 'highlight':
|
||||
currentSelector = msg.selector;
|
||||
highlight(msg.scrollTo);
|
||||
case 'highlightMode':
|
||||
svgRoot.classList.toggle('invert', msg.invert);
|
||||
break;
|
||||
|
||||
case 'highlightOne':
|
||||
hightlightNodes(msg.selector, msg.nid, msg.scrollTo);
|
||||
break;
|
||||
|
||||
case 'resetToggledNodes':
|
||||
resetToggledNodes();
|
||||
break;
|
||||
|
||||
case 'toggleNodes':
|
||||
toggleNodes(msg.selector, msg.original, msg.target);
|
||||
currentSelector = msg.selector;
|
||||
highlight(true);
|
||||
highlightedElements = selectNodes(msg.selector, msg.nid);
|
||||
toggleNodes(highlightedElements, msg.original, msg.target);
|
||||
highlightElements(true);
|
||||
break;
|
||||
|
||||
case 'shutdown':
|
||||
@ -863,6 +856,13 @@ pickerRoot.onload = function() {
|
||||
'stroke: #FFF;',
|
||||
'stroke-width: 0.5px;',
|
||||
'}',
|
||||
'svg.invert > path:first-child {',
|
||||
'fill: rgba(0,0,255,0.1);',
|
||||
'}',
|
||||
'svg.invert > path + path {',
|
||||
'fill: rgba(0,0,0,0.75);',
|
||||
'stroke: #000;',
|
||||
'}',
|
||||
''
|
||||
].join('\n');
|
||||
pickerDoc.body.appendChild(style);
|
||||
@ -876,7 +876,7 @@ pickerRoot.onload = function() {
|
||||
|
||||
window.addEventListener('scroll', onScrolled, true);
|
||||
|
||||
highlight();
|
||||
highlightElements();
|
||||
|
||||
localMessager.addListener(onMessage);
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/logger-ui-inspector.css">
|
||||
<title data-i18n="statsPageName"></title>
|
||||
</head>
|
||||
<body>
|
||||
@ -20,6 +21,14 @@
|
||||
</div>
|
||||
|
||||
<div id="domInspector">
|
||||
<div class="permatoolbar">
|
||||
<div>
|
||||
<span class="button fa highlightMode"></span>
|
||||
<span class="button fa revert disabled"></span>
|
||||
<span class="button fa commit disabled"></span>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="domTree"></ul>
|
||||
</div>
|
||||
|
||||
<div id="events" class="compactView f">
|
||||
@ -88,7 +97,9 @@
|
||||
<div class="dialog"></div>
|
||||
</div>
|
||||
<div id="cosmeticFilteringDialog" class="modalDialog">
|
||||
<div class="dialog"></div>
|
||||
<div class="dialog">
|
||||
<p><textarea class="cosmeticFilters" value=""></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
|
||||
</div>
|
||||
@ -98,6 +109,7 @@
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/logger-ui.js"></script>
|
||||
<script src="js/logger-ui-inspector.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user