mirror of https://github.com/gorhill/uBlock.git
functional DOM inspector
This commit is contained in:
parent
8305b8990a
commit
9becb466d4
|
@ -75,7 +75,7 @@ li.listEntry > a:nth-of-type(3) {
|
||||||
}
|
}
|
||||||
/* I designed the button with: http://charliepark.org/bootstrap_buttons/ */
|
/* I designed the button with: http://charliepark.org/bootstrap_buttons/ */
|
||||||
button.custom {
|
button.custom {
|
||||||
padding: 5px;
|
padding: 0.6em 1em;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-color: #80b3ff #80b3ff hsl(216, 100%, 75%);
|
border-color: #80b3ff #80b3ff hsl(216, 100%, 75%);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
@ -98,7 +98,6 @@ button.custom:hover {
|
||||||
#buttonApply {
|
#buttonApply {
|
||||||
display: initial;
|
display: initial;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
padding: 1em;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 1em;
|
right: 1em;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -18,7 +18,7 @@ body {
|
||||||
font: 14px/1.3 sans-serif;
|
font: 14px/1.3 sans-serif;
|
||||||
}
|
}
|
||||||
button.important {
|
button.important {
|
||||||
padding: 5px;
|
padding: 0.6em 1em;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
|
border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#domInspector {
|
#domInspector {
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
display: none;
|
display: none;
|
||||||
max-height: 40%;
|
max-height: 70%;
|
||||||
min-height: 40%;
|
min-height: 70%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
#domInspector.enabled {
|
#domInspector.enabled {
|
||||||
|
@ -83,6 +83,11 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cosmeticFilteringDialog .dialog {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
#cosmeticFilteringDialog .dialog textarea {
|
#cosmeticFilteringDialog .dialog textarea {
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
}
|
white-space: pre;
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ var logger = self.logger;
|
||||||
var messager = logger.messager;
|
var messager = logger.messager;
|
||||||
|
|
||||||
var inspectedTabId = '';
|
var inspectedTabId = '';
|
||||||
|
var inspectedURL = '';
|
||||||
var inspectedHostname = '';
|
var inspectedHostname = '';
|
||||||
var pollTimer = null;
|
var pollTimer = null;
|
||||||
var fingerprint = null;
|
var fingerprint = null;
|
||||||
|
@ -282,7 +283,48 @@ var nidFromNode = function(node) {
|
||||||
|
|
||||||
var startDialog = (function() {
|
var startDialog = (function() {
|
||||||
var dialog = uDom.nodeFromId('cosmeticFilteringDialog');
|
var dialog = uDom.nodeFromId('cosmeticFilteringDialog');
|
||||||
var candidateFilters = [];
|
var textarea = dialog.querySelector('textarea');
|
||||||
|
var hideSelectors = [];
|
||||||
|
var unhideSelectors = [];
|
||||||
|
var inputTimer = null;
|
||||||
|
|
||||||
|
var onInputChanged = (function() {
|
||||||
|
var parse = function() {
|
||||||
|
inputTimer = null;
|
||||||
|
hideSelectors = [];
|
||||||
|
unhideSelectors = [];
|
||||||
|
|
||||||
|
var line, matches;
|
||||||
|
var re = /^([^#]*)(#@?#)(.+)$/;
|
||||||
|
var lines = textarea.value.split(/\s*\n\s*/);
|
||||||
|
for ( var i = 0; i < lines.length; i++ ) {
|
||||||
|
line = lines[i].trim();
|
||||||
|
if ( line === '' || line.charAt(0) === '!' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
matches = re.exec(line);
|
||||||
|
if ( matches === null || matches.length !== 4 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( inspectedHostname.lastIndexOf(matches[1]) === -1 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( matches[2] === '##' ) {
|
||||||
|
hideSelectors.push(matches[3]);
|
||||||
|
} else {
|
||||||
|
unhideSelectors.push(matches[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showCommitted();
|
||||||
|
};
|
||||||
|
|
||||||
|
return function parseAsync() {
|
||||||
|
if ( inputTimer === null ) {
|
||||||
|
inputTimer = vAPI.setTimeout(parse, 743);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
var onClick = function(ev) {
|
var onClick = function(ev) {
|
||||||
var target = ev.target;
|
var target = ev.target;
|
||||||
|
@ -292,45 +334,94 @@ var startDialog = (function() {
|
||||||
return stop();
|
return stop();
|
||||||
}
|
}
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
if ( target.id === 'createCosmeticFilters' ) {
|
||||||
|
messager.send({ what: 'createUserFilter', filters: textarea.value });
|
||||||
|
// Force a reload for the new cosmetic filter(s) to take effect
|
||||||
|
messager.send({ what: 'reloadTab', tabId: inspectedTabId });
|
||||||
|
return stop();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var stop = function() {
|
var onCooked = function(entries) {
|
||||||
dialog.removeEventListener('click', onClick, true);
|
if ( Array.isArray(entries) === false ) {
|
||||||
document.body.removeChild(dialog);
|
return;
|
||||||
|
}
|
||||||
|
hideSelectors = entries;
|
||||||
|
var taValue = [], i, node;
|
||||||
|
var d = new Date();
|
||||||
|
taValue.push('! ' + d.toLocaleString() + ' ' + inspectedURL);
|
||||||
|
for ( i = 0; i < entries.length; i++ ) {
|
||||||
|
taValue.push(inspectedHostname + '##' + entries[i]);
|
||||||
|
}
|
||||||
|
var nodes = domTree.querySelectorAll('code.filter.off');
|
||||||
|
for ( i = 0; i < nodes.length; i++ ) {
|
||||||
|
node = nodes[i];
|
||||||
|
unhideSelectors.push(node.textContent);
|
||||||
|
taValue.push(inspectedHostname + '#@#' + node.textContent);
|
||||||
|
}
|
||||||
|
textarea.value = taValue.join('\n');
|
||||||
|
document.body.appendChild(dialog);
|
||||||
|
dialog.addEventListener('click', onClick, true);
|
||||||
|
showCommitted();
|
||||||
|
};
|
||||||
|
|
||||||
|
var showCommitted = function() {
|
||||||
|
messager.sendTo(
|
||||||
|
{
|
||||||
|
what: 'showCommitted',
|
||||||
|
hide: hideSelectors.join(',\n'),
|
||||||
|
unhide: unhideSelectors.join(',\n')
|
||||||
|
},
|
||||||
|
inspectedTabId,
|
||||||
|
'dom-inspector.js'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var showInteractive = function() {
|
||||||
|
messager.sendTo(
|
||||||
|
{
|
||||||
|
what: 'showInteractive',
|
||||||
|
hide: hideSelectors.join(',\n'),
|
||||||
|
unhide: unhideSelectors.join(',\n')
|
||||||
|
},
|
||||||
|
inspectedTabId,
|
||||||
|
'dom-inspector.js'
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var start = function() {
|
var start = function() {
|
||||||
// Collect all selectors which are currently toggled
|
textarea.addEventListener('input', onInputChanged);
|
||||||
var node, filters = [];
|
var node, entries = [];
|
||||||
var nodes = domTree.querySelectorAll('code.off');
|
var nodes = domTree.querySelectorAll('code.off');
|
||||||
for ( var i = 0; i < nodes.length; i++ ) {
|
for ( var i = 0; i < nodes.length; i++ ) {
|
||||||
node = nodes[i];
|
node = nodes[i];
|
||||||
if ( node.classList.contains('filter') ) {
|
if ( node.classList.contains('filter') === false ) {
|
||||||
filters.push({
|
entries.push({
|
||||||
prefix: '#@#',
|
|
||||||
nid: '',
|
|
||||||
selector: node.textContent
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
filters.push({
|
|
||||||
prefix: '##',
|
|
||||||
nid: nidFromNode(node),
|
nid: nidFromNode(node),
|
||||||
selector: node.textContent
|
selector: selectorFromNode(node)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
messager.sendTo(
|
||||||
|
{ what: 'cookFilters', entries: entries },
|
||||||
|
inspectedTabId,
|
||||||
|
'dom-inspector.js',
|
||||||
|
onCooked
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Send filters through dom-inspector.js for further processing.
|
var stop = function() {
|
||||||
|
if ( inputTimer !== null ) {
|
||||||
candidateFilters = filters;
|
clearTimeout(inputTimer);
|
||||||
var taValue = [], filter;
|
inputTimer = null;
|
||||||
for ( i = 0; i < filters.length; i++ ) {
|
|
||||||
filter = filters[i];
|
|
||||||
taValue.push(inspectedHostname + filter.prefix + filter.selector);
|
|
||||||
}
|
}
|
||||||
dialog.querySelector('textarea').value = taValue.join('\n');
|
showInteractive();
|
||||||
document.body.appendChild(dialog);
|
hideSelectors = [];
|
||||||
dialog.addEventListener('click', onClick, true);
|
unhideSelectors = [];
|
||||||
|
textarea.removeEventListener('input', onInputChanged);
|
||||||
|
dialog.removeEventListener('click', onClick, true);
|
||||||
|
document.body.removeChild(dialog);
|
||||||
};
|
};
|
||||||
|
|
||||||
return start;
|
return start;
|
||||||
|
@ -404,7 +495,10 @@ var onMouseOver = (function() {
|
||||||
if ( inspectedTabId === '' ) {
|
if ( inspectedTabId === '' ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Convenience: skip real-time highlighting if shift key is pressed.
|
||||||
|
if ( ev.shiftKey ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Find closest `li`
|
// Find closest `li`
|
||||||
var target = ev.target;
|
var target = ev.target;
|
||||||
while ( target !== null ) {
|
while ( target !== null ) {
|
||||||
|
@ -447,6 +541,7 @@ var fetchDOMAsync = (function() {
|
||||||
case 'full':
|
case 'full':
|
||||||
renderDOMFull(response);
|
renderDOMFull(response);
|
||||||
fingerprint = response.fingerprint;
|
fingerprint = response.fingerprint;
|
||||||
|
inspectedURL = response.url;
|
||||||
inspectedHostname = response.hostname;
|
inspectedHostname = response.hostname;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -140,11 +140,9 @@ var cssEscape = (function(root) {
|
||||||
var localMessager = vAPI.messaging.channel('dom-inspector.js');
|
var localMessager = vAPI.messaging.channel('dom-inspector.js');
|
||||||
|
|
||||||
// Highlighter-related
|
// Highlighter-related
|
||||||
var svgOcean = null;
|
|
||||||
var svgIslands = null;
|
|
||||||
var svgRoot = null;
|
var svgRoot = null;
|
||||||
var pickerRoot = null;
|
var pickerRoot = null;
|
||||||
var highlightedElements = [];
|
var highlightedElementLists = [ [], [], [] ];
|
||||||
|
|
||||||
var nodeToIdMap = new WeakMap(); // No need to iterate
|
var nodeToIdMap = new WeakMap(); // No need to iterate
|
||||||
var toggledNodes = new Map();
|
var toggledNodes = new Map();
|
||||||
|
@ -209,20 +207,7 @@ var domLayout = (function() {
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
})();
|
})();
|
||||||
/*
|
|
||||||
var matchesSelector = (function() {
|
|
||||||
if ( typeof Element.prototype.matches === 'function' ) {
|
|
||||||
return 'matches';
|
|
||||||
}
|
|
||||||
if ( typeof Element.prototype.mozMatchesSelector === 'function' ) {
|
|
||||||
return 'mozMatchesSelector';
|
|
||||||
}
|
|
||||||
if ( typeof Element.prototype.webkitMatchesSelector === 'function' ) {
|
|
||||||
return 'webkitMatchesSelector';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
})();
|
|
||||||
*/
|
|
||||||
var selectorFromNode = function(node) {
|
var selectorFromNode = function(node) {
|
||||||
var str, attr, pos, sw, i;
|
var str, attr, pos, sw, i;
|
||||||
var tag = node.localName;
|
var tag = node.localName;
|
||||||
|
@ -362,13 +347,12 @@ var domLayout = (function() {
|
||||||
|
|
||||||
// Track and report mutations to the DOM
|
// Track and report mutations to the DOM
|
||||||
|
|
||||||
var journalEntries = [];
|
|
||||||
var journalNodes = Object.create(null);
|
|
||||||
|
|
||||||
var mutationObserver = null;
|
var mutationObserver = null;
|
||||||
var mutationTimer = null;
|
var mutationTimer = null;
|
||||||
var addedNodelists = [];
|
var addedNodelists = [];
|
||||||
var removedNodelist = [];
|
var removedNodelist = [];
|
||||||
|
var journalEntries = [];
|
||||||
|
var journalNodes = Object.create(null);
|
||||||
|
|
||||||
var previousElementSiblingId = function(node) {
|
var previousElementSiblingId = function(node) {
|
||||||
var sibling = node;
|
var sibling = node;
|
||||||
|
@ -499,20 +483,14 @@ var domLayout = (function() {
|
||||||
// API
|
// API
|
||||||
|
|
||||||
var getLayout = function(fingerprint) {
|
var getLayout = function(fingerprint) {
|
||||||
if ( fingerprint !== domFingerprint() && mutationObserver !== null ) {
|
if ( fingerprint !== domFingerprint() ) {
|
||||||
if ( mutationTimer !== null ) {
|
reset();
|
||||||
clearTimeout(mutationTimer);
|
|
||||||
mutationTimer = null;
|
|
||||||
}
|
|
||||||
mutationObserver.disconnect();
|
|
||||||
mutationObserver = null;
|
|
||||||
journalEntries = [];
|
|
||||||
journalNodes = Object.create(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = {
|
var response = {
|
||||||
what: 'domLayout',
|
what: 'domLayout',
|
||||||
fingerprint: domFingerprint(),
|
fingerprint: domFingerprint(),
|
||||||
|
url: window.location.href,
|
||||||
hostname: window.location.hostname
|
hostname: window.location.hostname
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -542,6 +520,10 @@ var domLayout = (function() {
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var reset = function() {
|
||||||
|
shutdown();
|
||||||
|
};
|
||||||
|
|
||||||
var shutdown = function() {
|
var shutdown = function() {
|
||||||
if ( mutationTimer !== null ) {
|
if ( mutationTimer !== null ) {
|
||||||
clearTimeout(mutationTimer);
|
clearTimeout(mutationTimer);
|
||||||
|
@ -551,67 +533,232 @@ var domLayout = (function() {
|
||||||
mutationObserver.disconnect();
|
mutationObserver.disconnect();
|
||||||
mutationObserver = null;
|
mutationObserver = null;
|
||||||
}
|
}
|
||||||
|
addedNodelists = [];
|
||||||
|
removedNodelist = [];
|
||||||
journalEntries = [];
|
journalEntries = [];
|
||||||
journalNodes = Object.create(null);
|
journalNodes = Object.create(null);
|
||||||
|
nodeToIdMap = new WeakMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get: getLayout,
|
get: getLayout,
|
||||||
|
reset: reset,
|
||||||
shutdown: shutdown
|
shutdown: shutdown
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// https://www.youtube.com/watch?v=qo8zKhd4Cf0
|
// https://www.youtube.com/watch?v=qo8zKhd4Cf0
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// For browsers not supporting `:scope`, it's not the end of the world: the
|
||||||
|
// suggested CSS selectors may just end up being more verbose.
|
||||||
|
|
||||||
|
var cssScope = ':scope > ';
|
||||||
|
try {
|
||||||
|
document.querySelector(':scope *');
|
||||||
|
} catch (e) {
|
||||||
|
cssScope = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cosmeticFilterFromEntries = function(entries) {
|
||||||
|
var out = [];
|
||||||
|
var entry, i = entries.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
entry = entries[i];
|
||||||
|
out.push(cosmeticFilterFromTarget(entry.nid, entry.selector));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Extract the best possible cosmetic filter, i.e. as specific as possible.
|
||||||
|
|
||||||
|
var cosmeticFilterFromNode = function(elem) {
|
||||||
|
var tagName = elem.localName;
|
||||||
|
var prefix = '';
|
||||||
|
var suffix = [];
|
||||||
|
var v, i;
|
||||||
|
|
||||||
|
// Id
|
||||||
|
v = typeof elem.id === 'string' && cssEscape(elem.id);
|
||||||
|
if ( v ) {
|
||||||
|
suffix.push('#', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class(es)
|
||||||
|
if ( suffix.length === 0 ) {
|
||||||
|
v = elem.classList;
|
||||||
|
if ( v ) {
|
||||||
|
i = v.length || 0;
|
||||||
|
while ( i-- ) {
|
||||||
|
suffix.push('.' + cssEscape(v.item(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag name
|
||||||
|
if ( suffix.length === 0 ) {
|
||||||
|
prefix = tagName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes (depends on tag name)
|
||||||
|
var attributes = [], attr;
|
||||||
|
switch ( tagName ) {
|
||||||
|
case 'a':
|
||||||
|
v = elem.getAttribute('href');
|
||||||
|
if ( v ) {
|
||||||
|
v = v.replace(/\?.*$/, '');
|
||||||
|
if ( v.length ) {
|
||||||
|
attributes.push({ k: 'href', v: v });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'img':
|
||||||
|
v = elem.getAttribute('alt');
|
||||||
|
if ( v && v.length !== 0 ) {
|
||||||
|
attributes.push({ k: 'alt', v: v });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while ( attr = attributes.pop() ) {
|
||||||
|
if ( attr.v.length === 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
suffix.push('[', attr.k, '="', attr.v, '"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
var selector = prefix + suffix.join('');
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/637
|
||||||
|
// If the selector is still ambiguous at this point, further narrow using
|
||||||
|
// `nth-of-type`. It is preferable to use `nth-of-type` as opposed to
|
||||||
|
// `nth-child`, as `nth-of-type` is less volatile.
|
||||||
|
var parent = elem.parentElement;
|
||||||
|
if ( elementsFromSelector(cssScope + selector, parent).length > 1 ) {
|
||||||
|
i = 1;
|
||||||
|
while ( elem.previousElementSibling !== null ) {
|
||||||
|
elem = elem.previousElementSibling;
|
||||||
|
if ( elem.localName !== tagName ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
selector += ':nth-of-type(' + i + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cosmeticFilterFromTarget = function(nid, coarseSelector) {
|
||||||
|
var elems = elementsFromSelector(coarseSelector);
|
||||||
|
var target = null;
|
||||||
|
var i = elems.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
if ( nodeToIdMap.get(elems[i]) === nid ) {
|
||||||
|
target = elems[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( target === null ) {
|
||||||
|
return coarseSelector;
|
||||||
|
}
|
||||||
|
// Find the most concise selector from the target node
|
||||||
|
var segments = [], segment;
|
||||||
|
var node = target;
|
||||||
|
while ( node !== document.body ) {
|
||||||
|
segment = cosmeticFilterFromNode(node);
|
||||||
|
segments.unshift(segment);
|
||||||
|
if ( segment.charAt(0) === '#' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node.parentElement;
|
||||||
|
}
|
||||||
|
var fineSelector = segments.join(' > ');
|
||||||
|
if ( fineSelector.charAt(0) === '#' ) {
|
||||||
|
return fineSelector;
|
||||||
|
}
|
||||||
|
if ( fineSelector.charAt(0) === '.' && elementsFromSelector(fineSelector).length === 1 ) {
|
||||||
|
return fineSelector;
|
||||||
|
}
|
||||||
|
return 'body > ' + fineSelector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var elementsFromSelector = function(selector, context) {
|
||||||
|
if ( !context ) {
|
||||||
|
context = document;
|
||||||
|
}
|
||||||
|
var out = [];
|
||||||
|
try {
|
||||||
|
out = context.querySelectorAll(selector);
|
||||||
|
} catch (ex) {
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var highlightElements = function(scrollTo) {
|
var highlightElements = function(scrollTo) {
|
||||||
var elems = highlightedElements;
|
|
||||||
var wv = pickerRoot.contentWindow.innerWidth;
|
var wv = pickerRoot.contentWindow.innerWidth;
|
||||||
var hv = pickerRoot.contentWindow.innerHeight;
|
var hv = pickerRoot.contentWindow.innerHeight;
|
||||||
var ocean = ['M0 0h' + wv + 'v' + hv + 'h-' + wv, 'z'];
|
var ocean = ['M0 0h' + wv + 'v' + hv + 'h-' + wv, 'z'], islands;
|
||||||
var islands = [];
|
var elems, elem, rect, poly;
|
||||||
var elem, rect, poly;
|
|
||||||
var xl, xr, yt, yb, w, h, ws;
|
var xl, xr, yt, yb, w, h, ws;
|
||||||
var xlu = Number.MAX_VALUE, xru = 0, ytu = Number.MAX_VALUE, ybu = 0;
|
var xlu = Number.MAX_VALUE, xru = 0, ytu = Number.MAX_VALUE, ybu = 0;
|
||||||
|
var lists = highlightedElementLists;
|
||||||
|
|
||||||
for ( var i = 0; i < elems.length; i++ ) {
|
for ( var i = 0; i < lists.length; i++ ) {
|
||||||
elem = elems[i];
|
elems = lists[i];
|
||||||
if ( elem === pickerRoot ) {
|
islands = [];
|
||||||
continue;
|
for ( var j = 0; j < elems.length; j++ ) {
|
||||||
|
elem = elems[j];
|
||||||
|
if ( elem === pickerRoot ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = elem.getBoundingClientRect();
|
||||||
|
xl = rect.left;
|
||||||
|
xr = rect.right;
|
||||||
|
w = rect.width;
|
||||||
|
yt = rect.top;
|
||||||
|
yb = rect.bottom;
|
||||||
|
h = rect.height;
|
||||||
|
|
||||||
|
ws = w.toFixed(1);
|
||||||
|
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
ocean.push(poly);
|
||||||
|
islands.push(poly);
|
||||||
|
|
||||||
|
if ( !scrollTo ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( xl < xlu ) { xlu = xl; }
|
||||||
|
if ( xr > xru ) { xru = xr; }
|
||||||
|
if ( yt < ytu ) { ytu = yt; }
|
||||||
|
if ( yb > ybu ) { ybu = yb; }
|
||||||
}
|
}
|
||||||
if ( typeof elem.getBoundingClientRect !== 'function' ) {
|
svgRoot.children[i+1].setAttribute('d', islands.join('') || 'M0 0');
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rect = elem.getBoundingClientRect();
|
|
||||||
xl = rect.left;
|
|
||||||
xr = rect.right;
|
|
||||||
w = rect.width;
|
|
||||||
yt = rect.top;
|
|
||||||
yb = rect.bottom;
|
|
||||||
h = rect.height;
|
|
||||||
|
|
||||||
ws = w.toFixed(1);
|
|
||||||
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
|
||||||
'h' + ws +
|
|
||||||
'v' + h.toFixed(1) +
|
|
||||||
'h-' + ws +
|
|
||||||
'z';
|
|
||||||
ocean.push(poly);
|
|
||||||
islands.push(poly);
|
|
||||||
|
|
||||||
if ( !scrollTo ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( xl < xlu ) { xlu = xl; }
|
|
||||||
if ( xr > xru ) { xru = xr; }
|
|
||||||
if ( yt < ytu ) { ytu = yt; }
|
|
||||||
if ( yb > ybu ) { ybu = yb; }
|
|
||||||
}
|
}
|
||||||
svgOcean.setAttribute('d', ocean.join(''));
|
|
||||||
svgIslands.setAttribute('d', islands.join('') || 'M0 0');
|
svgRoot.children[0].setAttribute('d', ocean.join(''));
|
||||||
|
|
||||||
if ( !scrollTo ) {
|
if ( !scrollTo ) {
|
||||||
return;
|
return;
|
||||||
|
@ -646,13 +793,30 @@ var highlightElements = function(scrollTo) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var elementsFromSelector = function(filter) {
|
var onScrolled = function() {
|
||||||
var out = [];
|
highlightElements();
|
||||||
try {
|
};
|
||||||
out = document.querySelectorAll(filter);
|
|
||||||
} catch (ex) {
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var resetToggledNodes = function() {
|
||||||
|
var value;
|
||||||
|
// Chromium does not support destructuring as of v43.
|
||||||
|
for ( var node of toggledNodes.keys() ) {
|
||||||
|
value = toggledNodes.get(node);
|
||||||
|
if ( value !== null ) {
|
||||||
|
node.style.removeProperty('display');
|
||||||
|
} else {
|
||||||
|
node.style.setProperty('display', value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
toggledNodes.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var forgetToggledNodes = function() {
|
||||||
|
toggledNodes.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -673,15 +837,16 @@ var selectNodes = function(selector, nid) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var hightlightNodes = function(selector, nid, scrollTo) {
|
var shutdown = function() {
|
||||||
highlightedElements = selectNodes(selector, nid);
|
resetToggledNodes();
|
||||||
highlightElements(scrollTo);
|
domLayout.shutdown();
|
||||||
};
|
localMessager.removeAllListeners();
|
||||||
|
localMessager.close();
|
||||||
/******************************************************************************/
|
localMessager = null;
|
||||||
|
window.removeEventListener('scroll', onScrolled, true);
|
||||||
var onScrolled = function() {
|
document.documentElement.removeChild(pickerRoot);
|
||||||
highlightElements();
|
pickerRoot = svgRoot = null;
|
||||||
|
highlightedElementLists = [ [], [], [] ];
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -718,8 +883,10 @@ var toggleNodes = function(nodes, originalState, targetState) {
|
||||||
}
|
}
|
||||||
} else { // hidden, ?
|
} else { // hidden, ?
|
||||||
if ( targetState ) { // hidden, any
|
if ( targetState ) { // hidden, any
|
||||||
|
toggledNodes.set(node, 'none');
|
||||||
node.style.setProperty('display', 'initial', 'important');
|
node.style.setProperty('display', 'initial', 'important');
|
||||||
} else { // hidden, hidden
|
} else { // hidden, hidden
|
||||||
|
toggledNodes.delete(node);
|
||||||
node.style.setProperty('display', 'none', 'important');
|
node.style.setProperty('display', 'none', 'important');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -729,41 +896,25 @@ var toggleNodes = function(nodes, originalState, targetState) {
|
||||||
// https://www.youtube.com/watch?v=L5jRewnxSBY
|
// https://www.youtube.com/watch?v=L5jRewnxSBY
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var resetToggledNodes = function() {
|
|
||||||
var value;
|
|
||||||
// Chromium does not support destructuring as of v43.
|
|
||||||
for ( var node of toggledNodes.keys() ) {
|
|
||||||
value = toggledNodes.get(node);
|
|
||||||
if ( value !== null ) {
|
|
||||||
node.style.removeProperty('display');
|
|
||||||
} else {
|
|
||||||
node.style.setProperty('display', value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toggledNodes.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var shutdown = function() {
|
|
||||||
resetToggledNodes();
|
|
||||||
domLayout.shutdown();
|
|
||||||
localMessager.removeAllListeners();
|
|
||||||
localMessager.close();
|
|
||||||
localMessager = null;
|
|
||||||
window.removeEventListener('scroll', onScrolled, true);
|
|
||||||
document.documentElement.removeChild(pickerRoot);
|
|
||||||
pickerRoot = svgRoot = svgOcean = svgIslands = null;
|
|
||||||
highlightedElements = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onMessage = function(request) {
|
var onMessage = function(request) {
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
|
case 'commitFilters':
|
||||||
|
resetToggledNodes();
|
||||||
|
toggleNodes(selectNodes(request.hide, ''), true, false);
|
||||||
|
toggleNodes(selectNodes(request.unhide, ''), false, true);
|
||||||
|
forgetToggledNodes();
|
||||||
|
highlightedElementLists = [ [], [], [] ];
|
||||||
|
highlightElements();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cookFilters':
|
||||||
|
response = cosmeticFilterFromEntries(request.entries);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'domLayout':
|
case 'domLayout':
|
||||||
response = domLayout.get(request.fingerprint);
|
response = domLayout.get(request.fingerprint);
|
||||||
break;
|
break;
|
||||||
|
@ -773,16 +924,34 @@ var onMessage = function(request) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'highlightOne':
|
case 'highlightOne':
|
||||||
hightlightNodes(request.selector, request.nid, request.scrollTo);
|
highlightedElementLists[0] = selectNodes(request.selector, request.nid);
|
||||||
|
highlightElements(request.scrollTo);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'resetToggledNodes':
|
case 'resetToggledNodes':
|
||||||
resetToggledNodes();
|
resetToggledNodes();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'showCommitted':
|
||||||
|
resetToggledNodes();
|
||||||
|
highlightedElementLists[0] = [];
|
||||||
|
highlightedElementLists[1] = selectNodes(request.hide, '');
|
||||||
|
highlightedElementLists[2] = selectNodes(request.unhide, '');
|
||||||
|
toggleNodes(highlightedElementLists[2], false, true);
|
||||||
|
highlightElements(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'showInteractive':
|
||||||
|
resetToggledNodes();
|
||||||
|
toggleNodes(selectNodes(request.hide, ''), true, false);
|
||||||
|
toggleNodes(selectNodes(request.unhide, ''), false, true);
|
||||||
|
highlightedElementLists = [ [], [], [] ];
|
||||||
|
highlightElements();
|
||||||
|
break;
|
||||||
|
|
||||||
case 'toggleNodes':
|
case 'toggleNodes':
|
||||||
highlightedElements = selectNodes(request.selector, request.nid);
|
highlightedElementLists[0] = selectNodes(request.selector, request.nid);
|
||||||
toggleNodes(highlightedElements, request.original, request.target);
|
toggleNodes(highlightedElementLists[0], request.original, request.target);
|
||||||
highlightElements(true);
|
highlightElements(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -845,27 +1014,28 @@ pickerRoot.onload = function() {
|
||||||
'fill: rgba(0,0,0,0.75);',
|
'fill: rgba(0,0,0,0.75);',
|
||||||
'fill-rule: evenodd;',
|
'fill-rule: evenodd;',
|
||||||
'}',
|
'}',
|
||||||
'svg > path + path {',
|
'svg > path:nth-of-type(2) {',
|
||||||
'fill: rgba(0,0,255,0.1);',
|
'fill: rgba(0,0,255,0.1);',
|
||||||
'stroke: #FFF;',
|
'stroke: #FFF;',
|
||||||
'stroke-width: 0.5px;',
|
'stroke-width: 0.5px;',
|
||||||
'}',
|
'}',
|
||||||
'svg.invert > path:first-child {',
|
'svg > path:nth-of-type(3) {',
|
||||||
'fill: rgba(0,0,255,0.1);',
|
'fill: rgba(255,0,0,0.2);',
|
||||||
|
'stroke: #F00;',
|
||||||
'}',
|
'}',
|
||||||
'svg.invert > path + path {',
|
'svg > path:nth-of-type(4) {',
|
||||||
'fill: rgba(0,0,0,0.75);',
|
'fill: rgba(0,255,0,0.2);',
|
||||||
'stroke: #000;',
|
'stroke: #0F0;',
|
||||||
'}',
|
'}',
|
||||||
''
|
''
|
||||||
].join('\n');
|
].join('\n');
|
||||||
pickerDoc.body.appendChild(style);
|
pickerDoc.body.appendChild(style);
|
||||||
|
|
||||||
svgRoot = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
svgRoot = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
svgOcean = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
||||||
svgRoot.appendChild(svgOcean);
|
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
||||||
svgIslands = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
||||||
svgRoot.appendChild(svgIslands);
|
svgRoot.appendChild(pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path'));
|
||||||
pickerDoc.body.appendChild(svgRoot);
|
pickerDoc.body.appendChild(svgRoot);
|
||||||
|
|
||||||
window.addEventListener('scroll', onScrolled, true);
|
window.addEventListener('scroll', onScrolled, true);
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<div id="domInspector">
|
<div id="domInspector">
|
||||||
<div class="permatoolbar">
|
<div class="permatoolbar">
|
||||||
<div>
|
<div>
|
||||||
<span class="button fa highlightMode"></span>
|
<span class="button fa highlightMode" style="display: none"></span>
|
||||||
<span class="button fa revert disabled"></span>
|
<span class="button fa revert disabled"></span>
|
||||||
<span class="button fa commit disabled"></span>
|
<span class="button fa commit disabled"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
<p></p>
|
<p></p>
|
||||||
<p><textarea class="staticFilter" value=""></textarea>
|
<p><textarea class="staticFilter" value=""></textarea>
|
||||||
<button id="createStaticFilter" class="important" type="button" data-i18n="pickerCreate"></button>
|
<button id="createStaticFilter" class="important" type="button" data-i18n="pickerCreate"></button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,7 +98,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="cosmeticFilteringDialog" class="modalDialog">
|
<div id="cosmeticFilteringDialog" class="modalDialog">
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
<p><textarea class="cosmeticFilters" value=""></textarea>
|
<textarea class="cosmeticFilters" value=""></textarea>
|
||||||
|
<button id="createCosmeticFilters" class="important" type="button" data-i18n="pickerCreate"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
|
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
|
||||||
|
|
Loading…
Reference in New Issue