functional DOM inspector

This commit is contained in:
gorhill 2015-06-30 18:02:29 -04:00
parent 8305b8990a
commit 9becb466d4
6 changed files with 431 additions and 161 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -23,7 +23,7 @@
<div id="domInspector"> <div id="domInspector">
<div class="permatoolbar"> <div class="permatoolbar">
<div> <div>
<span class="button fa highlightMode">&#xf042;</span> <span class="button fa highlightMode" style="display: none">&#xf042;</span>
<span class="button fa revert disabled">&#xf12d;</span> <span class="button fa revert disabled">&#xf12d;</span>
<span class="button fa commit disabled">&#xf0c7;</span> <span class="button fa commit disabled">&#xf0c7;</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>