mirror of https://github.com/gorhill/uBlock.git
Code maintenance: replace uDom.js with dom.js
`uDom` is old and crusty and `dom` is meant as replacement. The goal of `dom` is to be simpler and mainly just convenience methods for handling the DOM with vanilla JS -- this is not a framework. Additionally, removed keyboard shortcuts pane which was useful only on very old versions of Firefox.
This commit is contained in:
parent
95f1b2f1bc
commit
feaa338678
|
@ -34,6 +34,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/about.js" type="module"></script>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<!-- -------- -->
|
||||
<iframe id="iframe" src=""></iframe>
|
||||
<!-- -------- -->
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -21,15 +21,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { runtime } from './ext.js';
|
||||
import { qs$ } from './dom.js';
|
||||
import { dom } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(async ( ) => {
|
||||
const manifest = runtime.getManifest();
|
||||
|
||||
qs$('#aboutNameVer').textContent = `${manifest.name} ${manifest.version}`;
|
||||
dom.text('#aboutNameVer', `${manifest.name} ${manifest.version}`);
|
||||
})();
|
||||
|
|
|
@ -21,12 +21,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { dom, qsa$ } from './dom.js';
|
||||
import { dom } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Open links in the proper window
|
||||
dom.attr(qsa$('a'), 'target', '_blank');
|
||||
dom.attr(qsa$('a[href*="dashboard.html"]'), 'target', '_parent');
|
||||
dom.attr('a', 'target', '_blank');
|
||||
dom.attr('a[href*="dashboard.html"]', 'target', '_parent');
|
||||
|
|
|
@ -21,15 +21,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { simpleStorage } from './storage.js';
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const discardUnsavedData = function(synchronous = false) {
|
||||
const paneFrame = document.getElementById('iframe');
|
||||
const paneFrame = qs$('#iframe');
|
||||
const paneWindow = paneFrame.contentWindow;
|
||||
if (
|
||||
typeof paneWindow.hasUnsavedData !== 'function' ||
|
||||
|
@ -44,11 +42,11 @@ const discardUnsavedData = function(synchronous = false) {
|
|||
|
||||
return new Promise(resolve => {
|
||||
const modal = document.querySelector('#unsavedWarning');
|
||||
modal.classList.add('on');
|
||||
dom.cl.add(modal, 'on');
|
||||
modal.focus();
|
||||
|
||||
const onDone = status => {
|
||||
modal.classList.remove('on');
|
||||
dom.cl.remove(modal, 'on');
|
||||
document.removeEventListener('click', onClick, true);
|
||||
resolve(status);
|
||||
};
|
||||
|
@ -73,15 +71,15 @@ const discardUnsavedData = function(synchronous = false) {
|
|||
|
||||
const loadDashboardPanel = function(pane, first) {
|
||||
const tabButton = document.querySelector(`[data-pane="${pane}"]`);
|
||||
if ( tabButton === null || tabButton.classList.contains('selected') ) {
|
||||
if ( tabButton === null || dom.cl.has(tabButton, 'selected') ) {
|
||||
return;
|
||||
}
|
||||
const loadPane = ( ) => {
|
||||
self.location.replace(`#${pane}`);
|
||||
for ( const node of document.querySelectorAll('.tabButton.selected') ) {
|
||||
node.classList.remove('selected');
|
||||
dom.cl.remove(node, 'selected');
|
||||
}
|
||||
tabButton.classList.add('selected');
|
||||
dom.cl.add(tabButton, 'selected');
|
||||
tabButton.scrollIntoView();
|
||||
document.querySelector('#iframe').contentWindow.location.replace(pane);
|
||||
if ( pane !== 'no-dashboard.html' ) {
|
||||
|
@ -103,11 +101,11 @@ const loadDashboardPanel = function(pane, first) {
|
|||
};
|
||||
|
||||
const onTabClickHandler = function(ev) {
|
||||
loadDashboardPanel(ev.target.getAttribute('data-pane'));
|
||||
loadDashboardPanel(dom.attr(ev.target, 'data-pane'));
|
||||
};
|
||||
|
||||
if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
|
||||
document.body.classList.add('noDashboard');
|
||||
dom.cl.add(dom.body, 'noDashboard');
|
||||
}
|
||||
|
||||
(async ( ) => {
|
||||
|
@ -117,12 +115,7 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
|
|||
}
|
||||
loadDashboardPanel(pane !== null ? pane : 'settings.html', true);
|
||||
|
||||
dom.on(
|
||||
qs$('#dashboard-nav'),
|
||||
'click',
|
||||
'.tabButton',
|
||||
onTabClickHandler
|
||||
);
|
||||
dom.on('#dashboard-nav', 'click', '.tabButton', onTabClickHandler);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
||||
window.addEventListener('beforeunload', ( ) => {
|
||||
|
|
|
@ -51,7 +51,7 @@ function setFilteringMode(level, commit = false) {
|
|||
modeSlider.dataset.level = level;
|
||||
if ( qs$('.filteringModeSlider.moving') === null ) {
|
||||
dom.text(
|
||||
qs$('#filteringModeText > span:nth-of-type(1)'),
|
||||
'#filteringModeText > span:nth-of-type(1)',
|
||||
i18n$(`filteringMode${level}Name`)
|
||||
);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ async function commitFilteringMode() {
|
|||
}
|
||||
}
|
||||
dom.text(
|
||||
qs$('#filteringModeText > span:nth-of-type(1)'),
|
||||
'#filteringModeText > span:nth-of-type(1)',
|
||||
i18n$(`filteringMode${afterLevel}Name`)
|
||||
);
|
||||
const actualLevel = await sendMessage({
|
||||
|
@ -112,7 +112,7 @@ async function commitFilteringMode() {
|
|||
const modeSlider = qs$('.filteringModeSlider');
|
||||
if ( `${level}` === modeSlider.dataset.level ) { return; }
|
||||
dom.text(
|
||||
qs$('#filteringModeText > span:nth-of-type(2)'),
|
||||
'#filteringModeText > span:nth-of-type(2)',
|
||||
i18n$(`filteringMode${level}Name`)
|
||||
);
|
||||
setFilteringMode(level);
|
||||
|
@ -131,7 +131,7 @@ async function commitFilteringMode() {
|
|||
dom.cl.remove(modeSlider, 'moving');
|
||||
self.removeEventListener('mousemove', moveAsync, { capture: true });
|
||||
self.removeEventListener('mouseup', stop, { capture: true });
|
||||
dom.text(qs$('#filteringModeText > span:nth-of-type(2)'), '');
|
||||
dom.text('#filteringModeText > span:nth-of-type(2)', '');
|
||||
commitFilteringMode();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
|
@ -160,11 +160,11 @@ async function commitFilteringMode() {
|
|||
ev.preventDefault();
|
||||
};
|
||||
|
||||
dom.on(qs$('.filteringModeButton'), 'mousedown', startSliding);
|
||||
dom.on('.filteringModeButton', 'mousedown', startSliding);
|
||||
}
|
||||
|
||||
dom.on(
|
||||
qs$('.filteringModeSlider'),
|
||||
'.filteringModeSlider',
|
||||
'click',
|
||||
'.filteringModeSlider span[data-level]',
|
||||
ev => {
|
||||
|
@ -177,25 +177,25 @@ dom.on(
|
|||
);
|
||||
|
||||
dom.on(
|
||||
qs$('.filteringModeSlider'),
|
||||
'.filteringModeSlider',
|
||||
'mouseenter',
|
||||
'.filteringModeSlider span[data-level]',
|
||||
ev => {
|
||||
const span = ev.target;
|
||||
const level = parseInt(span.dataset.level, 10);
|
||||
dom.text(
|
||||
qs$('#filteringModeText > span:nth-of-type(2)'),
|
||||
'#filteringModeText > span:nth-of-type(2)',
|
||||
i18n$(`filteringMode${level}Name`)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
dom.on(
|
||||
qs$('.filteringModeSlider'),
|
||||
'.filteringModeSlider',
|
||||
'mouseleave',
|
||||
'.filteringModeSlider span[data-level]',
|
||||
( ) => {
|
||||
dom.text(qs$('#filteringModeText > span:nth-of-type(2)'), '');
|
||||
dom.text('#filteringModeText > span:nth-of-type(2)', '');
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -249,11 +249,11 @@ simpleStorage.getItem('popupPanelSections').then(s => {
|
|||
sectionBitsToAttribute(parseInt(s, 10) || 0);
|
||||
});
|
||||
|
||||
dom.on(qs$('#moreButton'), 'click', ( ) => {
|
||||
dom.on('#moreButton', 'click', ( ) => {
|
||||
toggleSections(true);
|
||||
});
|
||||
|
||||
dom.on(qs$('#lessButton'), 'click', ( ) => {
|
||||
dom.on('#lessButton', 'click', ( ) => {
|
||||
toggleSections(false);
|
||||
});
|
||||
|
||||
|
@ -284,12 +284,12 @@ async function init() {
|
|||
|
||||
setFilteringMode(popupPanelData.level);
|
||||
|
||||
dom.text(qs$('#hostname'), tabHostname);
|
||||
dom.text('#hostname', tabHostname);
|
||||
|
||||
const parent = qs$('#rulesetStats');
|
||||
for ( const details of popupPanelData.rulesetDetails || [] ) {
|
||||
const div = qs$('#templates .rulesetDetails').cloneNode(true);
|
||||
dom.text(qs$('h1', div), details.name);
|
||||
const div = dom.clone('#templates .rulesetDetails');
|
||||
dom.text(qs$(div, 'h1'), details.name);
|
||||
const { rules, filters, css } = details;
|
||||
let ruleCount = rules.plain + rules.regex;
|
||||
if ( popupPanelData.hasOmnipotence ) {
|
||||
|
@ -301,7 +301,7 @@ async function init() {
|
|||
specificCount += css.specific.entityBased;
|
||||
}
|
||||
dom.text(
|
||||
qs$('p', div),
|
||||
qs$(div, 'p'),
|
||||
i18n$('perRulesetStats')
|
||||
.replace('{{ruleCount}}', ruleCount.toLocaleString())
|
||||
.replace('{{filterCount}}', filters.accepted.toLocaleString())
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { browser, sendMessage } from './ext.js';
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
|
@ -66,25 +64,24 @@ function renderFilterLists(soft = false) {
|
|||
|
||||
const liFromListEntry = function(ruleset, li, hideUnused) {
|
||||
if ( !li ) {
|
||||
li = listEntryTemplate.cloneNode(true);
|
||||
li = dom.clone(listEntryTemplate);
|
||||
}
|
||||
const on = enabledRulesets.includes(ruleset.id);
|
||||
li.classList.toggle('checked', on);
|
||||
dom.cl.toggle(li, 'checked', on);
|
||||
if ( dom.attr(li, 'data-listkey') !== ruleset.id ) {
|
||||
dom.attr(li, 'data-listkey', ruleset.id);
|
||||
qs$('input[type="checkbox"]', li).checked = on;
|
||||
qs$('.listname', li).textContent = ruleset.name || ruleset.id;
|
||||
qs$(li, 'input[type="checkbox"]').checked = on;
|
||||
dom.text(qs$(li, '.listname'), ruleset.name || ruleset.id);
|
||||
dom.cl.remove(li, 'toRemove');
|
||||
if ( ruleset.homeURL ) {
|
||||
dom.cl.add(li, 'support');
|
||||
const elem = qs$('a.support', li);
|
||||
dom.attr(elem, 'href', ruleset.homeURL);
|
||||
dom.attr(qs$(li, 'a.support'), 'href', ruleset.homeURL);
|
||||
} else {
|
||||
dom.cl.remove(li, 'support');
|
||||
}
|
||||
if ( ruleset.instructionURL ) {
|
||||
dom.cl.add(li, 'mustread');
|
||||
dom.attr(qs$('a.mustread', li), 'href', ruleset.instructionURL);
|
||||
dom.attr(qs$(li, 'a.mustread'), 'href', ruleset.instructionURL);
|
||||
} else {
|
||||
dom.cl.remove(li, 'mustread');
|
||||
}
|
||||
|
@ -93,14 +90,14 @@ function renderFilterLists(soft = false) {
|
|||
}
|
||||
// https://github.com/gorhill/uBlock/issues/1429
|
||||
if ( soft !== true ) {
|
||||
qs$('input[type="checkbox"]', li).checked = on;
|
||||
qs$(li, 'input[type="checkbox"]').checked = on;
|
||||
}
|
||||
const stats = rulesetStats(ruleset.id);
|
||||
li.title = listStatsTemplate
|
||||
.replace('{{ruleCount}}', renderNumber(stats.ruleCount))
|
||||
.replace('{{filterCount}}', renderNumber(stats.filterCount));
|
||||
dom.attr(
|
||||
qs$('.input.checkbox', li),
|
||||
qs$(li, '.input.checkbox'),
|
||||
'disabled',
|
||||
stats.ruleCount === 0 ? '' : null
|
||||
);
|
||||
|
@ -126,22 +123,25 @@ function renderFilterLists(soft = false) {
|
|||
const liFromListGroup = function(groupKey, groupRulesets) {
|
||||
let liGroup = qs$(`#lists > .groupEntry[data-groupkey="${groupKey}"]`);
|
||||
if ( liGroup === null ) {
|
||||
liGroup = listGroupTemplate.cloneNode(true);
|
||||
liGroup = dom.clone(listGroupTemplate);
|
||||
let groupName = groupNames.get(groupKey);
|
||||
if ( groupName === undefined ) {
|
||||
groupName = i18n$('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1));
|
||||
groupNames.set(groupKey, groupName);
|
||||
}
|
||||
if ( groupName !== '' ) {
|
||||
qs$('.geName', liGroup).textContent = groupName;
|
||||
dom.text(qs$(liGroup, '.geName'), groupName);
|
||||
}
|
||||
}
|
||||
if ( qs$('.geName:empty', liGroup) === null ) {
|
||||
qs$('.geCount', liGroup).textContent = listEntryCountFromGroup(groupRulesets);
|
||||
if ( qs$(liGroup, '.geName:empty') === null ) {
|
||||
dom.text(
|
||||
qs$(liGroup, '.geCount'),
|
||||
listEntryCountFromGroup(groupRulesets)
|
||||
);
|
||||
}
|
||||
const hideUnused = mustHideUnusedLists(groupKey);
|
||||
liGroup.classList.toggle('hideUnused', hideUnused);
|
||||
const ulGroup = qs$('.listEntries', liGroup);
|
||||
dom.cl.toggle(liGroup, 'hideUnused', hideUnused);
|
||||
const ulGroup = qs$(liGroup, '.listEntries');
|
||||
if ( !groupRulesets ) { return liGroup; }
|
||||
groupRulesets.sort(function(a, b) {
|
||||
return (a.name || '').localeCompare(b.name || '');
|
||||
|
@ -161,10 +161,7 @@ function renderFilterLists(soft = false) {
|
|||
|
||||
// Incremental rendering: this will allow us to easily discard unused
|
||||
// DOM list entries.
|
||||
dom.cl.add(
|
||||
qsa$('#lists .listEntries .listEntry[data-listkey]'),
|
||||
'discard'
|
||||
);
|
||||
dom.cl.add('#lists .listEntries .listEntry[data-listkey]', 'discard');
|
||||
|
||||
// Visually split the filter lists in three groups
|
||||
const ulLists = qs$('#lists');
|
||||
|
@ -192,14 +189,14 @@ function renderFilterLists(soft = false) {
|
|||
dom.cl.toggle(dom.body, 'hideUnused', mustHideUnusedLists('*'));
|
||||
|
||||
for ( const [ groupKey, groupRulesets ] of groups ) {
|
||||
let liGroup = liFromListGroup(groupKey, groupRulesets);
|
||||
liGroup.setAttribute('data-groupkey', groupKey);
|
||||
const liGroup = liFromListGroup(groupKey, groupRulesets);
|
||||
dom.attr(liGroup, 'data-groupkey', groupKey);
|
||||
if ( liGroup.parentElement === null ) {
|
||||
ulLists.appendChild(liGroup);
|
||||
}
|
||||
}
|
||||
|
||||
dom.remove(qsa$('#lists .listEntries .listEntry.discard'));
|
||||
dom.remove('#lists .listEntries .listEntry.discard');
|
||||
|
||||
renderWidgets();
|
||||
}
|
||||
|
@ -220,15 +217,16 @@ const renderWidgets = function() {
|
|||
let filterCount = 0;
|
||||
let ruleCount = 0;
|
||||
for ( const liEntry of qsa$('#lists .listEntry[data-listkey]') ) {
|
||||
if ( qs$('input[type="checkbox"]:checked', liEntry) === null ) { continue; }
|
||||
if ( qs$(liEntry, 'input[type="checkbox"]:checked') === null ) { continue; }
|
||||
const stats = rulesetStats(liEntry.dataset.listkey);
|
||||
if ( stats === undefined ) { continue; }
|
||||
ruleCount += stats.ruleCount;
|
||||
filterCount += stats.filterCount;
|
||||
}
|
||||
qs$('#listsOfBlockedHostsPrompt').textContent = i18n$('perRulesetStats')
|
||||
dom.text('#listsOfBlockedHostsPrompt', i18n$('perRulesetStats')
|
||||
.replace('{{ruleCount}}', ruleCount.toLocaleString())
|
||||
.replace('{{filterCount}}', filterCount.toLocaleString());
|
||||
.replace('{{filterCount}}', filterCount.toLocaleString())
|
||||
);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -267,7 +265,7 @@ async function onFilteringModeChange(ev) {
|
|||
}
|
||||
|
||||
dom.on(
|
||||
qs$('#defaultFilteringMode'),
|
||||
'#defaultFilteringMode',
|
||||
'change',
|
||||
'.filteringModeCard input[type="radio"]',
|
||||
ev => { onFilteringModeChange(ev); }
|
||||
|
@ -275,7 +273,7 @@ dom.on(
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
dom.on(qs$('#autoReload input[type="checkbox"'), 'change', ev => {
|
||||
dom.on('#autoReload input[type="checkbox"', 'change', ev => {
|
||||
sendMessage({
|
||||
what: 'setAutoReload',
|
||||
state: ev.target.checked,
|
||||
|
@ -287,7 +285,7 @@ dom.on(qs$('#autoReload input[type="checkbox"'), 'change', ev => {
|
|||
async function applyEnabledRulesets() {
|
||||
const enabledRulesets = [];
|
||||
for ( const liEntry of qsa$('#lists .listEntry[data-listkey]') ) {
|
||||
if ( qs$('input[type="checkbox"]:checked', liEntry) === null ) { continue; }
|
||||
if ( qs$(liEntry, 'input[type="checkbox"]:checked') === null ) { continue; }
|
||||
enabledRulesets.push(liEntry.dataset.listkey);
|
||||
}
|
||||
|
||||
|
@ -299,7 +297,7 @@ async function applyEnabledRulesets() {
|
|||
renderWidgets();
|
||||
}
|
||||
|
||||
dom.on(qs$('#lists'), 'change', '.listEntry input[type="checkbox"]', ( ) => {
|
||||
dom.on('#lists', 'change', '.listEntry input[type="checkbox"]', ( ) => {
|
||||
applyEnabledRulesets();
|
||||
});
|
||||
|
||||
|
@ -324,8 +322,8 @@ function toggleHideUnusedLists(which) {
|
|||
if ( mustHide ) {
|
||||
hideUnusedSet.add(which);
|
||||
}
|
||||
document.body.classList.toggle('hideUnused', mustHide);
|
||||
dom.cl.toggle(qsa$('.groupEntry[data-groupkey]'), 'hideUnused', mustHide);
|
||||
dom.cl.toggle(dom.body, 'hideUnused', mustHide);
|
||||
dom.cl.toggle('.groupEntry[data-groupkey]', 'hideUnused', mustHide);
|
||||
} else {
|
||||
const doesHide = hideUnusedSet.has(which);
|
||||
if ( doesHide ) {
|
||||
|
@ -335,7 +333,7 @@ function toggleHideUnusedLists(which) {
|
|||
}
|
||||
mustHide = doesHide === doesHideAll;
|
||||
groupSelector = `.groupEntry[data-groupkey="${which}"]`;
|
||||
dom.cl.toggle(qsa$(groupSelector), 'hideUnused', mustHide);
|
||||
dom.cl.toggle(groupSelector, 'hideUnused', mustHide);
|
||||
}
|
||||
|
||||
for ( const elem of qsa$(`#lists ${groupSelector} .listEntry[data-listkey] input[type="checkbox"]:not(:checked)`) ) {
|
||||
|
@ -352,16 +350,11 @@ function toggleHideUnusedLists(which) {
|
|||
);
|
||||
}
|
||||
|
||||
dom.on(
|
||||
qs$('#lists'),
|
||||
'click',
|
||||
'.groupEntry[data-groupkey] > .geDetails',
|
||||
ev => {
|
||||
dom.on('#lists', 'click', '.groupEntry[data-groupkey] > .geDetails', ev => {
|
||||
toggleHideUnusedLists(
|
||||
dom.attr(ev.target.closest('[data-groupkey]'), 'data-groupkey')
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Initialize from saved state.
|
||||
simpleStorage.getItem('hideUnusedFilterLists').then(value => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2018-present Raymond Hill
|
||||
Copyright (C) 2014-present 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
|
||||
|
@ -18,38 +19,17 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
.commandEntries {
|
||||
margin: 2em;
|
||||
}
|
||||
/* jshint esversion:11 */
|
||||
|
||||
.commandEntries td {
|
||||
padding: 0.5em 0.25em;
|
||||
}
|
||||
'use strict';
|
||||
|
||||
.commandEntries td.commandDesc {
|
||||
text-align: end;
|
||||
}
|
||||
import { dom } from './dom.js';
|
||||
|
||||
.commandEntries td.commandShortcut {
|
||||
white-space: nowrap;
|
||||
}
|
||||
/******************************************************************************/
|
||||
|
||||
.commandEntries td.commandShortcut input {
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
.commandEntries td.commandShortcut input:focus {
|
||||
outline: 2px solid blue;
|
||||
}
|
||||
|
||||
.commandEntries td.commandShortcut input ~ .commandReset {
|
||||
cursor: pointer;
|
||||
font-size: 150%;
|
||||
padding: 0 0.2em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.commandEntries td.commandShortcut input:placeholder-shown ~ .commandReset,
|
||||
.commandEntries td.commandShortcut input:focus ~ .commandReset {
|
||||
display: none;
|
||||
}
|
||||
const mql = self.matchMedia('(prefers-color-scheme: dark)');
|
||||
const theme = mql instanceof Object && mql.matches === true
|
||||
? 'dark'
|
||||
: 'light';
|
||||
dom.cl.toggle(dom.html, 'dark', theme === 'dark');
|
||||
dom.cl.toggle(dom.html, 'light', theme !== 'dark');
|
|
@ -51,6 +51,7 @@
|
|||
<div class="rulesetDetails"><h1></h1><p data-section="b"></p></div>
|
||||
</div>
|
||||
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/fa-icons.js"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/popup.js" type="module"></script>
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/fa-icons.js"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
|
|
|
@ -60,9 +60,9 @@
|
|||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/vapi-client-extra.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/1p-filters.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -77,9 +77,9 @@
|
|||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/vapi-client-extra.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/3p-filters.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -52,10 +52,10 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/about.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/about.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/advanced-settings.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/advanced-settings.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/asset-viewer.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -76,9 +76,6 @@ iframe {
|
|||
width: 100vw;
|
||||
}
|
||||
|
||||
body:not(.canUpdateShortcuts) .tabButton[data-pane="shortcuts.html"] {
|
||||
display: none;
|
||||
}
|
||||
body .tabButton[data-pane="no-dashboard.html"] {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
--><button class="tabButton" type="button" data-pane="1p-filters.html" data-i18n="1pPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="dyna-rules.html" data-i18n="rulesPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="whitelist.html" data-i18n="whitelistPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="shortcuts.html" data-i18n="shortcutsPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="support.html" data-i18n="supportPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="about.html" data-i18n="aboutPageName" tabindex="0"></button><!--
|
||||
--><button class="tabButton" type="button" data-pane="no-dashboard.html"></button>
|
||||
|
@ -39,9 +38,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard.js"></script>
|
||||
<script src="js/dashboard.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -48,9 +48,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/devtools.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/document-blocked.js" type="module"></script>
|
||||
</body>
|
||||
|
|
|
@ -57,9 +57,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/dyna-rules.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -19,18 +19,17 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global CodeMirror, uDom, uBlockDashboard */
|
||||
/* global CodeMirror, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$ } from './dom.js';
|
||||
import './codemirror/ubo-static-filtering.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const cmEditor = new CodeMirror(document.getElementById('userFilters'), {
|
||||
const cmEditor = new CodeMirror(qs$('#userFilters'), {
|
||||
autoCloseBrackets: true,
|
||||
autofocus: true,
|
||||
extraKeys: {
|
||||
|
@ -103,8 +102,8 @@ const userFiltersChanged = function(changed) {
|
|||
if ( typeof changed !== 'boolean' ) {
|
||||
changed = self.hasUnsavedData();
|
||||
}
|
||||
uDom.nodeFromId('userFiltersApply').disabled = !changed;
|
||||
uDom.nodeFromId('userFiltersRevert').disabled = !changed;
|
||||
qs$('#userFiltersApply').disabled = !changed;
|
||||
qs$('#userFiltersRevert').disabled = !changed;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -222,7 +221,7 @@ const handleImportFilePicker = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const startImportFilePicker = function() {
|
||||
const input = document.getElementById('importFilePicker');
|
||||
const input = qs$('#importFilePicker');
|
||||
// Reset to empty string, this will ensure an change event is properly
|
||||
// triggered if the user pick a file, even if it is the same as the last
|
||||
// one picked.
|
||||
|
@ -290,11 +289,11 @@ self.hasUnsavedData = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
// Handle user interaction
|
||||
uDom('#importUserFiltersFromFile').on('click', startImportFilePicker);
|
||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||
uDom('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
|
||||
uDom('#userFiltersApply').on('click', ( ) => { applyChanges(); });
|
||||
uDom('#userFiltersRevert').on('click', revertChanges);
|
||||
dom.on('#importUserFiltersFromFile', 'click', startImportFilePicker);
|
||||
dom.on('#importFilePicker', 'change', handleImportFilePicker);
|
||||
dom.on('#exportUserFiltersToFile', 'click', exportUserFiltersToFile);
|
||||
dom.on('#userFiltersApply', 'click', ( ) => { applyChanges(); });
|
||||
dom.on('#userFiltersRevert', 'click', revertChanges);
|
||||
|
||||
(async ( ) => {
|
||||
await renderUserFilters();
|
||||
|
|
|
@ -19,13 +19,10 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { i18n, i18n$ } from './i18n.js';
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -47,7 +44,7 @@ vAPI.broadcastListener.add(msg => {
|
|||
updateAssetStatus(msg);
|
||||
break;
|
||||
case 'assetsUpdated':
|
||||
document.body.classList.remove('updating');
|
||||
dom.cl.remove(dom.body, 'updating');
|
||||
renderWidgets();
|
||||
break;
|
||||
case 'staticFilteringDataChanged':
|
||||
|
@ -67,8 +64,8 @@ const renderNumber = function(value) {
|
|||
/******************************************************************************/
|
||||
|
||||
const renderFilterLists = function(soft) {
|
||||
const listGroupTemplate = uDom('#templates .groupEntry');
|
||||
const listEntryTemplate = uDom('#templates .listEntry');
|
||||
const listGroupTemplate = qs$('#templates .groupEntry');
|
||||
const listEntryTemplate = qs$('#templates .listEntry');
|
||||
const listStatsTemplate = i18n$('3pListsOfBlockedHostsPerListStats');
|
||||
const renderElapsedTimeToString = i18n.renderElapsedTimeToString;
|
||||
const groupNames = new Map([ [ 'user', '' ] ]);
|
||||
|
@ -84,65 +81,62 @@ const renderFilterLists = function(soft) {
|
|||
const liFromListEntry = function(listKey, li, hideUnused) {
|
||||
const entry = listDetails.available[listKey];
|
||||
if ( !li ) {
|
||||
li = listEntryTemplate.clone().nodeAt(0);
|
||||
li = dom.clone(listEntryTemplate);
|
||||
}
|
||||
const on = entry.off !== true;
|
||||
li.classList.toggle('checked', on);
|
||||
dom.cl.toggle(li, 'checked', on);
|
||||
let elem;
|
||||
if ( li.getAttribute('data-listkey') !== listKey ) {
|
||||
li.setAttribute('data-listkey', listKey);
|
||||
elem = li.querySelector('input[type="checkbox"]');
|
||||
if ( dom.attr(li, 'data-listkey') !== listKey ) {
|
||||
dom.attr(li, 'data-listkey', listKey);
|
||||
elem = qs$(li, 'input[type="checkbox"]');
|
||||
elem.checked = on;
|
||||
elem = li.querySelector('.listname');
|
||||
elem.textContent = listNameFromListKey(listKey);
|
||||
elem = li.querySelector('a.content');
|
||||
elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURIComponent(listKey));
|
||||
elem.setAttribute('type', 'text/html');
|
||||
li.classList.remove('toRemove');
|
||||
dom.text(qs$(li, '.listname'), listNameFromListKey(listKey));
|
||||
elem = qs$(li, 'a.content');
|
||||
dom.attr(elem, 'href', 'asset-viewer.html?url=' + encodeURIComponent(listKey));
|
||||
dom.attr(elem, 'type', 'text/html');
|
||||
dom.cl.remove(li, 'toRemove');
|
||||
if ( entry.supportName ) {
|
||||
li.classList.add('support');
|
||||
elem = li.querySelector('a.support');
|
||||
elem.setAttribute('href', entry.supportURL);
|
||||
elem.setAttribute('title', entry.supportName);
|
||||
dom.cl.add(li, 'support');
|
||||
elem = qs$(li, 'a.support');
|
||||
dom.attr(elem, 'href', entry.supportURL);
|
||||
dom.attr(elem, 'title', entry.supportName);
|
||||
} else {
|
||||
li.classList.remove('support');
|
||||
dom.cl.remove(li, 'support');
|
||||
}
|
||||
if ( entry.external ) {
|
||||
li.classList.add('external');
|
||||
dom.cl.add(li, 'external');
|
||||
} else {
|
||||
li.classList.remove('external');
|
||||
dom.cl.remove(li, 'external');
|
||||
}
|
||||
if ( entry.instructionURL ) {
|
||||
li.classList.add('mustread');
|
||||
elem = li.querySelector('a.mustread');
|
||||
elem.setAttribute('href', entry.instructionURL);
|
||||
dom.cl.add(li, 'mustread');
|
||||
dom.attr(qs$(li, 'a.mustread'), 'href', entry.instructionURL);
|
||||
} else {
|
||||
li.classList.remove('mustread');
|
||||
dom.cl.remove(li, 'mustread');
|
||||
}
|
||||
li.classList.toggle('isDefault', entry.isDefault === true);
|
||||
li.classList.toggle('unused', hideUnused && !on);
|
||||
dom.cl.toggle(li, 'isDefault', entry.isDefault === true);
|
||||
dom.cl.toggle(li, 'unused', hideUnused && !on);
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/issues/1429
|
||||
if ( !soft ) {
|
||||
li.querySelector('input[type="checkbox"]').checked = on;
|
||||
qs$(li, 'input[type="checkbox"]').checked = on;
|
||||
}
|
||||
elem = li.querySelector('span.counts');
|
||||
elem = qs$(li, 'span.counts');
|
||||
let text = '';
|
||||
if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
|
||||
text = listStatsTemplate
|
||||
.replace('{{used}}', renderNumber(on ? entry.entryUsedCount : 0))
|
||||
.replace('{{total}}', renderNumber(entry.entryCount));
|
||||
}
|
||||
elem.textContent = text;
|
||||
dom.text(elem, text);
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/104
|
||||
const asset = listDetails.cache[listKey] || {};
|
||||
const remoteURL = asset.remoteURL;
|
||||
li.classList.toggle(
|
||||
'unsecure',
|
||||
dom.cl.toggle(li, 'unsecure',
|
||||
typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
|
||||
);
|
||||
li.classList.toggle('failed', asset.error !== undefined);
|
||||
li.classList.toggle('obsolete', asset.obsolete === true);
|
||||
dom.cl.toggle(li, 'failed', asset.error !== undefined);
|
||||
dom.cl.toggle(li, 'obsolete', asset.obsolete === true);
|
||||
const lastUpdateString = lastUpdateTemplateString.replace(
|
||||
'{{ago}}',
|
||||
renderElapsedTimeToString(asset.writeTime || 0)
|
||||
|
@ -152,18 +146,15 @@ const renderFilterLists = function(soft) {
|
|||
if ( asset.cached && asset.writeTime !== 0 ) {
|
||||
title += '\n' + lastUpdateString;
|
||||
}
|
||||
li.querySelector('.status.obsolete').setAttribute('title', title);
|
||||
dom.attr(qs$(li, '.status.obsolete'), 'title', title);
|
||||
}
|
||||
if ( asset.cached === true ) {
|
||||
li.classList.add('cached');
|
||||
li.querySelector('.status.cache').setAttribute(
|
||||
'title',
|
||||
lastUpdateString
|
||||
);
|
||||
dom.cl.add(li, 'cached');
|
||||
dom.attr(qs$(li, '.status.cache'), 'title', lastUpdateString);
|
||||
} else {
|
||||
li.classList.remove('cached');
|
||||
dom.cl.remove(li, 'cached');
|
||||
}
|
||||
li.classList.remove('discard');
|
||||
dom.cl.remove(li, 'discard');
|
||||
return li;
|
||||
};
|
||||
|
||||
|
@ -183,24 +174,24 @@ const renderFilterLists = function(soft) {
|
|||
};
|
||||
|
||||
const liFromListGroup = function(groupKey, listKeys) {
|
||||
let liGroup = document.querySelector(`#lists > .groupEntry[data-groupkey="${groupKey}"]`);
|
||||
let liGroup = qs$(`#lists > .groupEntry[data-groupkey="${groupKey}"]`);
|
||||
if ( liGroup === null ) {
|
||||
liGroup = listGroupTemplate.clone().nodeAt(0);
|
||||
liGroup = dom.clone(listGroupTemplate);
|
||||
let groupName = groupNames.get(groupKey);
|
||||
if ( groupName === undefined ) {
|
||||
groupName = i18n$('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1));
|
||||
groupNames.set(groupKey, groupName);
|
||||
}
|
||||
if ( groupName !== '' ) {
|
||||
liGroup.querySelector('.geName').textContent = groupName;
|
||||
dom.text(qs$(liGroup, '.geName'), groupName);
|
||||
}
|
||||
}
|
||||
if ( liGroup.querySelector('.geName:empty') === null ) {
|
||||
liGroup.querySelector('.geCount').textContent = listEntryCountFromGroup(listKeys);
|
||||
if ( qs$(liGroup, '.geName:empty') === null ) {
|
||||
dom.text(qs$(liGroup, '.geCount'), listEntryCountFromGroup(listKeys));
|
||||
}
|
||||
let hideUnused = mustHideUnusedLists(groupKey);
|
||||
liGroup.classList.toggle('hideUnused', hideUnused);
|
||||
let ulGroup = liGroup.querySelector('.listEntries');
|
||||
dom.cl.toggle(liGroup, 'hideUnused', hideUnused);
|
||||
let ulGroup = qs$(liGroup, '.listEntries');
|
||||
if ( !listKeys ) { return liGroup; }
|
||||
listKeys.sort(function(a, b) {
|
||||
return (listDetails.available[a].title || '').localeCompare(listDetails.available[b].title || '');
|
||||
|
@ -246,13 +237,14 @@ const renderFilterLists = function(soft) {
|
|||
|
||||
// Incremental rendering: this will allow us to easily discard unused
|
||||
// DOM list entries.
|
||||
uDom('#lists .listEntries .listEntry[data-listkey]').addClass('discard');
|
||||
dom.cl.add('#lists .listEntries .listEntry[data-listkey]', 'discard');
|
||||
|
||||
// Remove import widget while we recreate list of lists.
|
||||
const importWidget = uDom('.listEntry.toImport').detach();
|
||||
const importWidget = qs$('.listEntry.toImport');
|
||||
importWidget.remove();
|
||||
|
||||
// Visually split the filter lists in purpose-based groups
|
||||
const ulLists = document.querySelector('#lists');
|
||||
const ulLists = qs$('#lists');
|
||||
const groups = groupsFromLists(details.available);
|
||||
const groupKeys = [
|
||||
'user',
|
||||
|
@ -265,11 +257,11 @@ const renderFilterLists = function(soft) {
|
|||
'regions',
|
||||
'custom'
|
||||
];
|
||||
document.body.classList.toggle('hideUnused', mustHideUnusedLists('*'));
|
||||
dom.cl.toggle(dom.body, 'hideUnused', mustHideUnusedLists('*'));
|
||||
for ( let i = 0; i < groupKeys.length; i++ ) {
|
||||
let groupKey = groupKeys[i];
|
||||
let liGroup = liFromListGroup(groupKey, groups.get(groupKey));
|
||||
liGroup.setAttribute('data-groupkey', groupKey);
|
||||
dom.attr(liGroup, 'data-groupkey', groupKey);
|
||||
if ( liGroup.parentElement === null ) {
|
||||
ulLists.appendChild(liGroup);
|
||||
}
|
||||
|
@ -280,28 +272,23 @@ const renderFilterLists = function(soft) {
|
|||
ulLists.appendChild(liFromListGroup(groupKey, groupKey));
|
||||
}
|
||||
|
||||
uDom('#lists .listEntries .listEntry.discard').remove();
|
||||
dom.remove('#lists .listEntries .listEntry.discard');
|
||||
|
||||
// Re-insert import widget.
|
||||
uDom('[data-groupkey="custom"] .listEntries').append(importWidget);
|
||||
qs$('[data-groupkey="custom"] .listEntries').append(importWidget);
|
||||
|
||||
uDom.nodeFromId('autoUpdate').checked =
|
||||
listDetails.autoUpdate === true;
|
||||
uDom.nodeFromId('listsOfBlockedHostsPrompt').textContent =
|
||||
qs$('#autoUpdate').checked = listDetails.autoUpdate === true;
|
||||
dom.text(
|
||||
'#listsOfBlockedHostsPrompt',
|
||||
i18n$('3pListsOfBlockedHostsPrompt')
|
||||
.replace(
|
||||
'{{netFilterCount}}',
|
||||
renderNumber(details.netFilterCount)
|
||||
)
|
||||
.replace(
|
||||
'{{cosmeticFilterCount}}',
|
||||
renderNumber(details.cosmeticFilterCount)
|
||||
.replace('{{netFilterCount}}', renderNumber(details.netFilterCount))
|
||||
.replace('{{cosmeticFilterCount}}', renderNumber(details.cosmeticFilterCount))
|
||||
);
|
||||
uDom.nodeFromId('parseCosmeticFilters').checked =
|
||||
qs$('#parseCosmeticFilters').checked =
|
||||
listDetails.parseCosmeticFilters === true;
|
||||
uDom.nodeFromId('ignoreGenericCosmeticFilters').checked =
|
||||
qs$('#ignoreGenericCosmeticFilters').checked =
|
||||
listDetails.ignoreGenericCosmeticFilters === true;
|
||||
uDom.nodeFromId('suspendUntilListsAreLoaded').checked =
|
||||
qs$('#suspendUntilListsAreLoaded').checked =
|
||||
listDetails.suspendUntilListsAreLoaded === true;
|
||||
|
||||
// Compute a hash of the settings so that we can keep track of changes
|
||||
|
@ -311,7 +298,7 @@ const renderFilterLists = function(soft) {
|
|||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/2394
|
||||
document.body.classList.toggle('updating', listDetails.isUpdating);
|
||||
dom.cl.toggle(dom.body, 'updating', listDetails.isUpdating);
|
||||
|
||||
renderWidgets();
|
||||
};
|
||||
|
@ -326,39 +313,30 @@ const renderFilterLists = function(soft) {
|
|||
/******************************************************************************/
|
||||
|
||||
const renderWidgets = function() {
|
||||
let cl = uDom.nodeFromId('buttonApply').classList;
|
||||
cl.toggle(
|
||||
'disabled',
|
||||
dom.cl.toggle('#buttonApply', 'disabled',
|
||||
filteringSettingsHash === hashFromCurrentFromSettings()
|
||||
);
|
||||
const updating = document.body.classList.contains('updating');
|
||||
cl = uDom.nodeFromId('buttonUpdate').classList;
|
||||
cl.toggle('active', updating);
|
||||
cl.toggle(
|
||||
'disabled',
|
||||
const updating = dom.cl.has(dom.body, 'updating');
|
||||
dom.cl.toggle('#buttonUpdate', 'active', updating);
|
||||
dom.cl.toggle('#buttonUpdate', 'disabled',
|
||||
updating === false &&
|
||||
document.querySelector('#lists .listEntry.obsolete:not(.toRemove) input[type="checkbox"]:checked') === null
|
||||
qs$('#lists .listEntry.obsolete:not(.toRemove) input[type="checkbox"]:checked') === null
|
||||
);
|
||||
cl = uDom.nodeFromId('buttonPurgeAll').classList;
|
||||
cl.toggle(
|
||||
'disabled',
|
||||
updating || document.querySelector('#lists .listEntry.cached:not(.obsolete)') === null
|
||||
dom.cl.toggle('#buttonPurgeAll', 'disabled',
|
||||
updating || qs$('#lists .listEntry.cached:not(.obsolete)') === null
|
||||
);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const updateAssetStatus = function(details) {
|
||||
const li = document.querySelector(
|
||||
'#lists .listEntry[data-listkey="' + details.key + '"]'
|
||||
);
|
||||
const li = qs$(`#lists .listEntry[data-listkey="${details.key}"]`);
|
||||
if ( li === null ) { return; }
|
||||
li.classList.toggle('failed', !!details.failed);
|
||||
li.classList.toggle('obsolete', !details.cached);
|
||||
li.classList.toggle('cached', !!details.cached);
|
||||
dom.cl.toggle(li, 'failed', !!details.failed);
|
||||
dom.cl.toggle(li, 'obsolete', !details.cached);
|
||||
dom.cl.toggle(li, 'cached', !!details.cached);
|
||||
if ( details.cached ) {
|
||||
li.querySelector('.status.cache').setAttribute(
|
||||
'title',
|
||||
dom.attr(qs$(li, '.status.cache'), 'title',
|
||||
lastUpdateTemplateString.replace(
|
||||
'{{ago}}',
|
||||
i18n.renderElapsedTimeToString(Date.now())
|
||||
|
@ -377,21 +355,21 @@ const updateAssetStatus = function(details) {
|
|||
|
||||
const hashFromCurrentFromSettings = function() {
|
||||
const hash = [
|
||||
uDom.nodeFromId('parseCosmeticFilters').checked,
|
||||
uDom.nodeFromId('ignoreGenericCosmeticFilters').checked
|
||||
qs$('#parseCosmeticFilters').checked,
|
||||
qs$('#ignoreGenericCosmeticFilters').checked
|
||||
];
|
||||
const listHash = [];
|
||||
const listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)');
|
||||
const listEntries = qsa$('#lists .listEntry[data-listkey]:not(.toRemove)');
|
||||
for ( const liEntry of listEntries ) {
|
||||
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
|
||||
listHash.push(liEntry.getAttribute('data-listkey'));
|
||||
if ( qs$(liEntry, 'input[type="checkbox"]:checked') !== null ) {
|
||||
listHash.push(dom.attr(liEntry, 'data-listkey'));
|
||||
}
|
||||
}
|
||||
hash.push(
|
||||
listHash.sort().join(),
|
||||
uDom.nodeFromId('importLists').checked &&
|
||||
reValidExternalList.test(uDom.nodeFromId('externalLists').value),
|
||||
document.querySelector('#lists .listEntry.toRemove') !== null
|
||||
qs$('#importLists').checked &&
|
||||
reValidExternalList.test(qs$('#externalLists').value),
|
||||
qs$('#lists .listEntry.toRemove') !== null
|
||||
);
|
||||
return hash.join();
|
||||
};
|
||||
|
@ -400,8 +378,7 @@ const hashFromCurrentFromSettings = function() {
|
|||
|
||||
const onListsetChanged = function(ev) {
|
||||
const input = ev.target;
|
||||
const li = input.closest('.listEntry');
|
||||
li.classList.toggle('checked', input.checked);
|
||||
dom.cl.toggle(input.closest('.listEntry'), 'checked', input.checked);
|
||||
onFilteringSettingsChanged();
|
||||
};
|
||||
|
||||
|
@ -416,7 +393,7 @@ const onFilteringSettingsChanged = function() {
|
|||
const onRemoveExternalList = function(ev) {
|
||||
const liEntry = ev.target.closest('[data-listkey]');
|
||||
if ( liEntry === null ) { return; }
|
||||
liEntry.classList.toggle('toRemove');
|
||||
dom.cl.toggle(liEntry, 'toRemove');
|
||||
renderWidgets();
|
||||
};
|
||||
|
||||
|
@ -424,7 +401,7 @@ const onRemoveExternalList = function(ev) {
|
|||
|
||||
const onPurgeClicked = function(ev) {
|
||||
const liEntry = ev.target.closest('[data-listkey]');
|
||||
const listKey = liEntry.getAttribute('data-listkey') || '';
|
||||
const listKey = dom.attr(liEntry, 'data-listkey') || '';
|
||||
if ( listKey === '' ) { return; }
|
||||
|
||||
messaging.send('dashboard', {
|
||||
|
@ -437,10 +414,10 @@ const onPurgeClicked = function(ev) {
|
|||
// https://github.com/gorhill/uBlock/issues/1733
|
||||
// An external filter list must not be marked as obsolete, they will
|
||||
// always be fetched anyways if there is no cached copy.
|
||||
liEntry.classList.add('obsolete');
|
||||
liEntry.classList.remove('cached');
|
||||
dom.cl.add(liEntry, 'obsolete');
|
||||
dom.cl.remove(liEntry, 'cached');
|
||||
|
||||
if ( liEntry.querySelector('input[type="checkbox"]').checked ) {
|
||||
if ( qs$(liEntry, 'input[type="checkbox"]').checked ) {
|
||||
renderWidgets();
|
||||
}
|
||||
};
|
||||
|
@ -452,41 +429,35 @@ const selectFilterLists = async function() {
|
|||
messaging.send('dashboard', {
|
||||
what: 'userSettings',
|
||||
name: 'parseAllABPHideFilters',
|
||||
value: uDom.nodeFromId('parseCosmeticFilters').checked,
|
||||
value: qs$('#parseCosmeticFilters').checked,
|
||||
});
|
||||
messaging.send('dashboard', {
|
||||
what: 'userSettings',
|
||||
name: 'ignoreGenericCosmeticFilters',
|
||||
value: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked,
|
||||
value: qs$('#ignoreGenericCosmeticFilters').checked,
|
||||
});
|
||||
|
||||
// Filter lists to select
|
||||
const toSelect = [];
|
||||
for (
|
||||
const liEntry of
|
||||
document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)')
|
||||
) {
|
||||
if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
|
||||
toSelect.push(liEntry.getAttribute('data-listkey'));
|
||||
for ( const liEntry of qsa$('#lists .listEntry[data-listkey]:not(.toRemove)') ) {
|
||||
if ( qs$(liEntry, 'input[type="checkbox"]:checked') !== null ) {
|
||||
toSelect.push(dom.attr(liEntry, 'data-listkey'));
|
||||
}
|
||||
}
|
||||
|
||||
// External filter lists to remove
|
||||
const toRemove = [];
|
||||
for (
|
||||
const liEntry of
|
||||
document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]')
|
||||
) {
|
||||
toRemove.push(liEntry.getAttribute('data-listkey'));
|
||||
for ( const liEntry of qsa$('#lists .listEntry.toRemove[data-listkey]') ) {
|
||||
toRemove.push(dom.attr(liEntry, 'data-listkey'));
|
||||
}
|
||||
|
||||
// External filter lists to import
|
||||
const externalListsElem = document.getElementById('externalLists');
|
||||
const externalListsElem = qs$('#externalLists');
|
||||
const toImport = externalListsElem.value.trim();
|
||||
{
|
||||
const liEntry = externalListsElem.closest('.listEntry');
|
||||
liEntry.classList.remove('checked');
|
||||
liEntry.querySelector('input[type="checkbox"]').checked = false;
|
||||
dom.cl.remove(liEntry, 'checked');
|
||||
qs$(liEntry, 'input[type="checkbox"]').checked = false;
|
||||
externalListsElem.value = '';
|
||||
}
|
||||
|
||||
|
@ -503,7 +474,7 @@ const selectFilterLists = async function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const buttonApplyHandler = async function() {
|
||||
uDom('#buttonApply').removeClass('enabled');
|
||||
dom.cl.remove('#buttonApply', 'enabled');
|
||||
await selectFilterLists();
|
||||
renderWidgets();
|
||||
messaging.send('dashboard', { what: 'reloadAllFilters' });
|
||||
|
@ -513,7 +484,7 @@ const buttonApplyHandler = async function() {
|
|||
|
||||
const buttonUpdateHandler = async function() {
|
||||
await selectFilterLists();
|
||||
document.body.classList.add('updating');
|
||||
dom.cl.add(dom.body, 'updating');
|
||||
renderWidgets();
|
||||
messaging.send('dashboard', { what: 'forceUpdateAssets' });
|
||||
};
|
||||
|
@ -521,7 +492,7 @@ const buttonUpdateHandler = async function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const buttonPurgeAllHandler = async function(hard) {
|
||||
uDom('#buttonPurgeAll').removeClass('enabled');
|
||||
dom.cl.remove('#buttonPurgeAll', 'enabled');
|
||||
await messaging.send('dashboard', {
|
||||
what: 'purgeAllCaches',
|
||||
hard,
|
||||
|
@ -561,8 +532,8 @@ const toggleHideUnusedLists = function(which) {
|
|||
if ( mustHide ) {
|
||||
hideUnusedSet.add(which);
|
||||
}
|
||||
document.body.classList.toggle('hideUnused', mustHide);
|
||||
uDom('.groupEntry[data-groupkey]').toggleClass('hideUnused', mustHide);
|
||||
dom.cl.toggle(dom.body, 'hideUnused', mustHide);
|
||||
dom.cl.toggle('.groupEntry[data-groupkey]', 'hideUnused', mustHide);
|
||||
} else {
|
||||
const doesHide = hideUnusedSet.has(which);
|
||||
if ( doesHide ) {
|
||||
|
@ -571,12 +542,13 @@ const toggleHideUnusedLists = function(which) {
|
|||
hideUnusedSet.add(which);
|
||||
}
|
||||
mustHide = doesHide === doesHideAll;
|
||||
groupSelector = '.groupEntry[data-groupkey="' + which + '"] ';
|
||||
uDom(groupSelector).toggleClass('hideUnused', mustHide);
|
||||
groupSelector = `.groupEntry[data-groupkey="${which}"] `;
|
||||
dom.cl.toggle(groupSelector, 'hideUnused', mustHide);
|
||||
}
|
||||
uDom(groupSelector + '.listEntry input[type="checkbox"]:not(:checked)')
|
||||
.ancestors('.listEntry[data-listkey]')
|
||||
.toggleClass('unused', mustHide);
|
||||
qsa$(`${groupSelector}.listEntry input[type="checkbox"]:not(:checked)`)
|
||||
.forEach(elem => {
|
||||
dom.cl.toggle(elem.closest('.listEntry[data-listkey]'), 'unused', mustHide);
|
||||
});
|
||||
vAPI.localStorage.setItem(
|
||||
'hideUnusedFilterLists',
|
||||
Array.from(hideUnusedSet)
|
||||
|
@ -584,20 +556,19 @@ const toggleHideUnusedLists = function(which) {
|
|||
};
|
||||
|
||||
const revealHiddenUsedLists = function() {
|
||||
uDom('#lists .listEntry.unused input[type="checkbox"]:checked')
|
||||
.ancestors('.listEntry[data-listkey]')
|
||||
.removeClass('unused');
|
||||
qsa$('#lists .listEntry.unused input[type="checkbox"]:checked')
|
||||
.forEach(elem => {
|
||||
dom.cl.remove(elem.closest('.listEntry[data-listkey]'), 'unused');
|
||||
});
|
||||
};
|
||||
|
||||
uDom('#listsOfBlockedHostsPrompt').on('click', function() {
|
||||
dom.on('#listsOfBlockedHostsPrompt', 'click', ( ) => {
|
||||
toggleHideUnusedLists('*');
|
||||
});
|
||||
|
||||
uDom('#lists').on('click', '.groupEntry[data-groupkey] > .geDetails', function(ev) {
|
||||
dom.on('#lists', 'click', '.groupEntry[data-groupkey] > .geDetails', ev => {
|
||||
toggleHideUnusedLists(
|
||||
uDom(ev.target)
|
||||
.ancestors('.groupEntry[data-groupkey]')
|
||||
.attr('data-groupkey')
|
||||
dom.attr(ev.target.closest('.groupEntry[data-groupkey]'), 'data-groupkey')
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -614,15 +585,15 @@ vAPI.localStorage.getItemAsync('hideUnusedFilterLists').then(value => {
|
|||
|
||||
const toCloudData = function() {
|
||||
const bin = {
|
||||
parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked,
|
||||
ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked,
|
||||
parseCosmeticFilters: qs$('#parseCosmeticFilters').checked,
|
||||
ignoreGenericCosmeticFilters: qs$('#ignoreGenericCosmeticFilters').checked,
|
||||
selectedLists: []
|
||||
};
|
||||
|
||||
const liEntries = document.querySelectorAll('#lists .listEntry');
|
||||
const liEntries = qsa$('#lists .listEntry');
|
||||
for ( const liEntry of liEntries ) {
|
||||
if ( liEntry.querySelector('input').checked ) {
|
||||
bin.selectedLists.push(liEntry.getAttribute('data-listkey'));
|
||||
if ( qs$(liEntry, 'input').checked ) {
|
||||
bin.selectedLists.push(dom.attr(liEntry, 'data-listkey'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -634,24 +605,22 @@ const fromCloudData = function(data, append) {
|
|||
|
||||
let elem, checked;
|
||||
|
||||
elem = uDom.nodeFromId('parseCosmeticFilters');
|
||||
elem = qs$('#parseCosmeticFilters');
|
||||
checked = data.parseCosmeticFilters === true || append && elem.checked;
|
||||
elem.checked = listDetails.parseCosmeticFilters = checked;
|
||||
|
||||
elem = uDom.nodeFromId('ignoreGenericCosmeticFilters');
|
||||
elem = qs$('#ignoreGenericCosmeticFilters');
|
||||
checked = data.ignoreGenericCosmeticFilters === true || append && elem.checked;
|
||||
elem.checked = listDetails.ignoreGenericCosmeticFilters = checked;
|
||||
|
||||
const selectedSet = new Set(data.selectedLists);
|
||||
const listEntries = uDom('#lists .listEntry');
|
||||
for ( let i = 0, n = listEntries.length; i < n; i++ ) {
|
||||
const listEntry = listEntries.at(i);
|
||||
const listKey = listEntry.attr('data-listkey');
|
||||
for ( const listEntry of qsa$('#lists .listEntry') ) {
|
||||
const listKey = dom.attr(listEntry, 'data-listkey');
|
||||
const hasListKey = selectedSet.has(listKey);
|
||||
selectedSet.delete(listKey);
|
||||
const input = listEntry.descendants('input').first();
|
||||
if ( append && input.prop('checked') ) { continue; }
|
||||
input.prop('checked', hasListKey);
|
||||
const input = qs$(listEntry, 'input');
|
||||
if ( append && input.checked ) { continue; }
|
||||
input.checked = hasListKey;
|
||||
}
|
||||
|
||||
// If there are URL-like list keys left in the selected set, import them.
|
||||
|
@ -661,14 +630,14 @@ const fromCloudData = function(data, append) {
|
|||
}
|
||||
}
|
||||
if ( selectedSet.size !== 0 ) {
|
||||
elem = uDom.nodeFromId('externalLists');
|
||||
elem = qs$('#externalLists');
|
||||
if ( append ) {
|
||||
if ( elem.value.trim() !== '' ) { elem.value += '\n'; }
|
||||
} else {
|
||||
elem.value = '';
|
||||
}
|
||||
elem.value += Array.from(selectedSet).join('\n');
|
||||
uDom.nodeFromId('importLists').checked = true;
|
||||
qs$('#importLists').checked = true;
|
||||
}
|
||||
|
||||
revealHiddenUsedLists();
|
||||
|
@ -686,21 +655,18 @@ self.hasUnsavedData = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom('#autoUpdate').on('change', userSettingCheckboxChanged);
|
||||
uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged);
|
||||
uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged);
|
||||
uDom('#suspendUntilListsAreLoaded').on('change', userSettingCheckboxChanged);
|
||||
uDom('#buttonApply').on('click', ( ) => { buttonApplyHandler(); });
|
||||
uDom('#buttonUpdate').on('click', ( ) => { buttonUpdateHandler(); });
|
||||
uDom('#buttonPurgeAll').on('click', ev => {
|
||||
buttonPurgeAllHandler(ev.shiftKey);
|
||||
});
|
||||
uDom('#lists').on('change', '.listEntry input', onListsetChanged);
|
||||
uDom('#lists').on('click', '.listEntry .remove', onRemoveExternalList);
|
||||
uDom('#lists').on('click', 'span.cache', onPurgeClicked);
|
||||
uDom('#externalLists').on('input', onFilteringSettingsChanged);
|
||||
|
||||
uDom('#lists').on('click', '.listEntry label *', ev => {
|
||||
dom.on('#autoUpdate', 'change', userSettingCheckboxChanged);
|
||||
dom.on('#parseCosmeticFilters', 'change', onFilteringSettingsChanged);
|
||||
dom.on('#ignoreGenericCosmeticFilters', 'change', onFilteringSettingsChanged);
|
||||
dom.on('#suspendUntilListsAreLoaded', 'change', userSettingCheckboxChanged);
|
||||
dom.on('#buttonApply', 'click', ( ) => { buttonApplyHandler(); });
|
||||
dom.on('#buttonUpdate', 'click', ( ) => { buttonUpdateHandler(); });
|
||||
dom.on('#buttonPurgeAll', 'click', ev => { buttonPurgeAllHandler(ev.shiftKey); });
|
||||
dom.on('#lists', 'change', '.listEntry input', onListsetChanged);
|
||||
dom.on('#lists', 'click', '.listEntry .remove', onRemoveExternalList);
|
||||
dom.on('#lists', 'click', 'span.cache', onPurgeClicked);
|
||||
dom.on('#externalLists', 'input', onFilteringSettingsChanged);
|
||||
dom.on('#lists','click', '.listEntry label *', ev => {
|
||||
if ( ev.target.matches('a,input,.forinput') ) { return; }
|
||||
ev.preventDefault();
|
||||
});
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(async ( ) => {
|
||||
|
@ -30,5 +30,5 @@
|
|||
what: 'getAppData',
|
||||
});
|
||||
|
||||
uDom('#aboutNameVer').text(appData.name + ' ' + appData.version);
|
||||
dom.text('#aboutNameVer', appData.name + ' ' + appData.version);
|
||||
})();
|
||||
|
|
|
@ -19,14 +19,11 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global CodeMirror, uDom, uBlockDashboard */
|
||||
/* global CodeMirror, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
{
|
||||
// >>>> Start of private namespace
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -69,15 +66,12 @@ CodeMirror.defineMode('raw-settings', function() {
|
|||
};
|
||||
});
|
||||
|
||||
const cmEditor = new CodeMirror(
|
||||
document.getElementById('advancedSettings'),
|
||||
{
|
||||
const cmEditor = new CodeMirror(qs$('#advancedSettings'), {
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: false,
|
||||
styleActiveLine: true
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
||||
|
||||
|
@ -123,8 +117,6 @@ const arrayFromString = function(s) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// This is to give a visual hint that the content of user blacklist has changed.
|
||||
|
||||
const advancedSettingsChanged = (( ) => {
|
||||
let timer;
|
||||
|
||||
|
@ -132,7 +124,7 @@ const advancedSettingsChanged = (( ) => {
|
|||
timer = undefined;
|
||||
const changed =
|
||||
hashFromAdvancedSettings(cmEditor.getValue()) !== beforeHash;
|
||||
uDom.nodeFromId('advancedSettingsApply').disabled = !changed;
|
||||
qs$('#advancedSettingsApply').disabled = !changed;
|
||||
CodeMirror.commands.save = changed ? applyChanges : function(){};
|
||||
};
|
||||
|
||||
|
@ -195,17 +187,11 @@ const applyChanges = async function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.nodeFromId('advancedSettings').addEventListener(
|
||||
'input',
|
||||
advancedSettingsChanged
|
||||
);
|
||||
uDom.nodeFromId('advancedSettingsApply').addEventListener('click', ( ) => {
|
||||
dom.on('#advancedSettings', 'input', advancedSettingsChanged);
|
||||
dom.on('#advancedSettingsApply', 'click', ( ) => {
|
||||
applyChanges();
|
||||
});
|
||||
|
||||
renderAdvancedSettings(true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// <<<< End of private namespace
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
import { dom, qs$ } from './dom.js';
|
||||
import './codemirror/ubo-static-filtering.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -36,19 +37,19 @@ import './codemirror/ubo-static-filtering.js';
|
|||
if ( assetKey === null ) { return; }
|
||||
|
||||
const subscribeElem = subscribeParams.get('subscribe') !== null
|
||||
? document.getElementById('subscribe')
|
||||
? qs$('#subscribe')
|
||||
: null;
|
||||
if ( subscribeElem !== null && subscribeURL.hash !== '#subscribed' ) {
|
||||
const title = subscribeParams.get('title');
|
||||
const promptElem = document.getElementById('subscribePrompt');
|
||||
promptElem.children[0].textContent = title;
|
||||
const promptElem = qs$('#subscribePrompt');
|
||||
dom.text(promptElem.children[0], title);
|
||||
const a = promptElem.children[1];
|
||||
a.textContent = assetKey;
|
||||
a.setAttribute('href', assetKey);
|
||||
subscribeElem.classList.remove('hide');
|
||||
dom.text(a, assetKey);
|
||||
dom.attr(a, 'href', assetKey);
|
||||
dom.cl.remove(subscribeElem, 'hide');
|
||||
}
|
||||
|
||||
const cmEditor = new CodeMirror(document.getElementById('content'), {
|
||||
const cmEditor = new CodeMirror(qs$('#content'), {
|
||||
autofocus: true,
|
||||
foldGutter: true,
|
||||
gutters: [ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter' ],
|
||||
|
@ -81,10 +82,8 @@ import './codemirror/ubo-static-filtering.js';
|
|||
cmEditor.setValue(details && details.content || '');
|
||||
|
||||
if ( subscribeElem !== null ) {
|
||||
document.getElementById('subscribeButton').addEventListener(
|
||||
'click',
|
||||
( ) => {
|
||||
subscribeElem.classList.add('hide');
|
||||
dom.on('#subscribeButton', 'click', ( ) => {
|
||||
dom.cl.add(subscribeElem, 'hide');
|
||||
vAPI.messaging.send('scriptlets', {
|
||||
what: 'applyFilterListSelection',
|
||||
toImport: assetKey,
|
||||
|
@ -93,16 +92,14 @@ import './codemirror/ubo-static-filtering.js';
|
|||
what: 'reloadAllFilters'
|
||||
});
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
if ( details.sourceURL ) {
|
||||
const a = document.querySelector('.cm-search-widget .sourceURL');
|
||||
a.setAttribute('href', details.sourceURL);
|
||||
a.setAttribute('title', details.sourceURL);
|
||||
const a = qs$('.cm-search-widget .sourceURL');
|
||||
dom.attr(a, 'href', details.sourceURL);
|
||||
dom.attr(a, 'title', details.sourceURL);
|
||||
}
|
||||
|
||||
document.body.classList.remove('loading');
|
||||
dom.cl.remove(dom.body, 'loading');
|
||||
})();
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom, faIconsInit */
|
||||
/* global faIconsInit */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { i18n, i18n$ } from './i18n.js';
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -41,23 +42,23 @@ self.cloud = {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const widget = uDom.nodeFromId('cloudWidget');
|
||||
const widget = qs$('#cloudWidget');
|
||||
if ( widget === null ) { return; }
|
||||
|
||||
self.cloud.datakey = widget.getAttribute('data-cloud-entry') || '';
|
||||
self.cloud.datakey = dom.attr(widget, 'data-cloud-entry') || '';
|
||||
if ( self.cloud.datakey === '' ) { return; }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const fetchStorageUsed = async function() {
|
||||
let elem = widget.querySelector('#cloudCapacity');
|
||||
if ( elem.classList.contains('hide') ) { return; }
|
||||
let elem = qs$(widget, '#cloudCapacity');
|
||||
if ( dom.cl.has(elem, 'hide') ) { return; }
|
||||
const result = await vAPI.messaging.send('cloudWidget', {
|
||||
what: 'cloudUsed',
|
||||
datakey: self.cloud.datakey,
|
||||
});
|
||||
if ( result instanceof Object === false ) {
|
||||
elem.classList.add('hide');
|
||||
dom.cl.add(elem, 'hide');
|
||||
return;
|
||||
}
|
||||
const units = ' ' + i18n$('genericBytes');
|
||||
|
@ -75,7 +76,7 @@ const fetchStorageUsed = async function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const fetchCloudData = async function() {
|
||||
const info = widget.querySelector('#cloudInfo');
|
||||
const info = qs$(widget, '#cloudInfo');
|
||||
|
||||
const entry = await vAPI.messaging.send('cloudWidget', {
|
||||
what: 'cloudPull',
|
||||
|
@ -84,16 +85,16 @@ const fetchCloudData = async function() {
|
|||
|
||||
const hasData = entry instanceof Object;
|
||||
if ( hasData === false ) {
|
||||
uDom.nodeFromId('cloudPull').setAttribute('disabled', '');
|
||||
uDom.nodeFromId('cloudPullAndMerge').setAttribute('disabled', '');
|
||||
dom.attr('#cloudPull', 'disabled', '');
|
||||
dom.attr('#cloudPullAndMerge', 'disabled', '');
|
||||
info.textContent = '...\n...';
|
||||
return entry;
|
||||
}
|
||||
|
||||
self.cloud.data = entry.data;
|
||||
|
||||
uDom.nodeFromId('cloudPull').removeAttribute('disabled');
|
||||
uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled');
|
||||
dom.attr('#cloudPull', 'disabled', null);
|
||||
dom.attr('#cloudPullAndMerge', 'disabled', null);
|
||||
|
||||
const timeOptions = {
|
||||
weekday: 'short',
|
||||
|
@ -123,11 +124,8 @@ const pushData = async function() {
|
|||
data: self.cloud.onPush(),
|
||||
});
|
||||
const failed = typeof error === 'string';
|
||||
document.getElementById('cloudPush')
|
||||
.classList
|
||||
.toggle('error', failed);
|
||||
document.querySelector('#cloudError')
|
||||
.textContent = failed ? error : '';
|
||||
dom.cl.toggle('#cloudPush', 'error', failed);
|
||||
dom.text('#cloudError', failed ? error : '');
|
||||
if ( failed ) { return; }
|
||||
fetchCloudData();
|
||||
fetchStorageUsed();
|
||||
|
@ -139,8 +137,8 @@ const pullData = function() {
|
|||
if ( typeof self.cloud.onPull === 'function' ) {
|
||||
self.cloud.onPull(self.cloud.data, false);
|
||||
}
|
||||
document.getElementById('cloudPush').classList.remove('error');
|
||||
document.querySelector('#cloudError').textContent = '';
|
||||
dom.cl.remove('#cloudPush', 'error');
|
||||
dom.text('#cloudError', '');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -154,29 +152,29 @@ const pullAndMergeData = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const openOptions = function() {
|
||||
const input = uDom.nodeFromId('cloudDeviceName');
|
||||
const input = qs$('#cloudDeviceName');
|
||||
input.value = self.cloud.options.deviceName;
|
||||
input.setAttribute('placeholder', self.cloud.options.defaultDeviceName);
|
||||
uDom.nodeFromId('cloudOptions').classList.add('show');
|
||||
dom.attr(input, 'placeholder', self.cloud.options.defaultDeviceName);
|
||||
dom.cl.add('#cloudOptions', 'show');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const closeOptions = function(ev) {
|
||||
const root = uDom.nodeFromId('cloudOptions');
|
||||
const root = qs$('#cloudOptions');
|
||||
if ( ev.target !== root ) { return; }
|
||||
root.classList.remove('show');
|
||||
dom.cl.remove(root, 'show');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const submitOptions = async function() {
|
||||
uDom.nodeFromId('cloudOptions').classList.remove('show');
|
||||
dom.cl.remove('#cloudOptions', 'show');
|
||||
|
||||
const options = await vAPI.messaging.send('cloudWidget', {
|
||||
what: 'cloudSetOptions',
|
||||
options: {
|
||||
deviceName: uDom.nodeFromId('cloudDeviceName').value
|
||||
deviceName: qs$('#cloudDeviceName').value
|
||||
},
|
||||
});
|
||||
if ( options instanceof Object ) {
|
||||
|
@ -209,19 +207,19 @@ const onInitialize = function(options) {
|
|||
faIconsInit(widget);
|
||||
|
||||
i18n.render(widget);
|
||||
widget.classList.remove('hide');
|
||||
dom.cl.remove(widget, 'hide');
|
||||
|
||||
uDom('#cloudPush').on('click', ( ) => { pushData(); });
|
||||
uDom('#cloudPull').on('click', pullData);
|
||||
uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
|
||||
uDom('#cloudCog').on('click', openOptions);
|
||||
uDom('#cloudOptions').on('click', closeOptions);
|
||||
uDom('#cloudOptionsSubmit').on('click', ( ) => { submitOptions(); });
|
||||
dom.on('#cloudPush', 'click', ( ) => { pushData(); });
|
||||
dom.on('#cloudPull', 'click', pullData);
|
||||
dom.on('#cloudPullAndMerge', 'click', pullAndMergeData);
|
||||
dom.on('#cloudCog', 'click', openOptions);
|
||||
dom.on('#cloudOptions', 'click', closeOptions);
|
||||
dom.on('#cloudOptionsSubmit', 'click', ( ) => { submitOptions(); });
|
||||
|
||||
fetchCloudData().then(result => {
|
||||
if ( typeof result !== 'string' ) { return; }
|
||||
document.getElementById('cloudPush').classList.add('error');
|
||||
document.querySelector('#cloudError').textContent = result;
|
||||
dom.cl.add('#cloudPush', 'error');
|
||||
dom.text('#cloudError', result);
|
||||
});
|
||||
fetchStorageUsed();
|
||||
};
|
||||
|
|
|
@ -28,44 +28,12 @@ import { hostnameFromURI } from './uri-utils.js';
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
µb.canUseShortcuts = vAPI.commands instanceof Object;
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/386
|
||||
// Firefox 74 and above has complete shortcut assignment user interface.
|
||||
µb.canUpdateShortcuts = false;
|
||||
|
||||
if (
|
||||
µb.canUseShortcuts &&
|
||||
vAPI.webextFlavor.soup.has('firefox') &&
|
||||
typeof vAPI.commands.update === 'function'
|
||||
) {
|
||||
self.addEventListener(
|
||||
'webextFlavor',
|
||||
( ) => {
|
||||
µb.canUpdateShortcuts = vAPI.webextFlavor.major < 74;
|
||||
if ( µb.canUpdateShortcuts === false ) { return; }
|
||||
vAPI.storage.get('commandShortcuts').then(bin => {
|
||||
if ( bin instanceof Object === false ) { return; }
|
||||
const shortcuts = bin.commandShortcuts;
|
||||
if ( Array.isArray(shortcuts) === false ) { return; }
|
||||
µb.commandShortcuts = new Map(shortcuts);
|
||||
for ( const [ name, shortcut ] of shortcuts ) {
|
||||
vAPI.commands.update({ name, shortcut });
|
||||
}
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
|
||||
// *****************************************************************************
|
||||
// start of local namespace
|
||||
|
||||
if ( µb.canUseShortcuts === false ) { return; }
|
||||
if ( vAPI.commands instanceof Object === false ) { return; }
|
||||
|
||||
const relaxBlockingMode = (( ) => {
|
||||
const reloadTimers = new Map();
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
self.uBlockDashboard = self.uBlockDashboard || {};
|
||||
|
@ -196,7 +196,7 @@ self.uBlockDashboard.openOrSelectPage = function(url, options = {}) {
|
|||
let ev;
|
||||
if ( url instanceof MouseEvent ) {
|
||||
ev = url;
|
||||
url = ev.target.getAttribute('href');
|
||||
url = dom.attr(ev.target, 'href');
|
||||
}
|
||||
const details = Object.assign({ url, select: true, index: -1 }, options);
|
||||
vAPI.messaging.send('default', {
|
||||
|
@ -211,5 +211,5 @@ self.uBlockDashboard.openOrSelectPage = function(url, options = {}) {
|
|||
/******************************************************************************/
|
||||
|
||||
// Open links in the proper window
|
||||
uDom('a').attr('target', '_blank');
|
||||
uDom('a[href*="dashboard.html"]').attr('target', '_parent');
|
||||
dom.attr('a', 'target', '_blank');
|
||||
dom.attr('a[href*="dashboard.html"]', 'target', '_parent');
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const discardUnsavedData = function(synchronous = false) {
|
||||
const paneFrame = document.getElementById('iframe');
|
||||
const paneFrame = qs$('#iframe');
|
||||
const paneWindow = paneFrame.contentWindow;
|
||||
if (
|
||||
typeof paneWindow.hasUnsavedData !== 'function' ||
|
||||
|
@ -40,13 +40,13 @@ const discardUnsavedData = function(synchronous = false) {
|
|||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
const modal = uDom.nodeFromId('unsavedWarning');
|
||||
modal.classList.add('on');
|
||||
const modal = qs$('#unsavedWarning');
|
||||
dom.cl.add(modal, 'on');
|
||||
modal.focus();
|
||||
|
||||
const onDone = status => {
|
||||
modal.classList.remove('on');
|
||||
document.removeEventListener('click', onClick, true);
|
||||
dom.cl.remove(modal, 'on');
|
||||
dom.off(document, 'click', onClick, true);
|
||||
resolve(status);
|
||||
};
|
||||
|
||||
|
@ -58,27 +58,25 @@ const discardUnsavedData = function(synchronous = false) {
|
|||
if ( target.matches('[data-i18n="dashboardUnsavedWarningIgnore"]') ) {
|
||||
return onDone(true);
|
||||
}
|
||||
if ( modal.querySelector('[data-i18n="dashboardUnsavedWarning"]').contains(target) ) {
|
||||
if ( qs$(modal, '[data-i18n="dashboardUnsavedWarning"]').contains(target) ) {
|
||||
return;
|
||||
}
|
||||
onDone(false);
|
||||
};
|
||||
|
||||
document.addEventListener('click', onClick, true);
|
||||
dom.on(document, 'click', onClick, true);
|
||||
});
|
||||
};
|
||||
|
||||
const loadDashboardPanel = function(pane, first) {
|
||||
const tabButton = uDom.nodeFromSelector(`[data-pane="${pane}"]`);
|
||||
if ( tabButton === null || tabButton.classList.contains('selected') ) {
|
||||
return;
|
||||
}
|
||||
const tabButton = qs$(`[data-pane="${pane}"]`);
|
||||
if ( tabButton === null || dom.cl.has(tabButton, 'selected') ) { return; }
|
||||
const loadPane = ( ) => {
|
||||
self.location.replace(`#${pane}`);
|
||||
uDom('.tabButton.selected').toggleClass('selected', false);
|
||||
tabButton.classList.add('selected');
|
||||
dom.cl.remove('.tabButton.selected', 'selected');
|
||||
dom.cl.add(tabButton, 'selected');
|
||||
tabButton.scrollIntoView();
|
||||
uDom.nodeFromId('iframe').contentWindow.location.replace(pane);
|
||||
qs$('#iframe').contentWindow.location.replace(pane);
|
||||
if ( pane !== 'no-dashboard.html' ) {
|
||||
vAPI.localStorage.setItem('dashboardLastVisitedPane', pane);
|
||||
}
|
||||
|
@ -88,9 +86,7 @@ const loadDashboardPanel = function(pane, first) {
|
|||
}
|
||||
const r = discardUnsavedData();
|
||||
if ( r === false ) { return; }
|
||||
if ( r === true ) {
|
||||
return loadPane();
|
||||
}
|
||||
if ( r === true ) { return loadPane(); }
|
||||
r.then(status => {
|
||||
if ( status === false ) { return; }
|
||||
loadPane();
|
||||
|
@ -98,11 +94,11 @@ const loadDashboardPanel = function(pane, first) {
|
|||
};
|
||||
|
||||
const onTabClickHandler = function(ev) {
|
||||
loadDashboardPanel(ev.target.getAttribute('data-pane'));
|
||||
loadDashboardPanel(dom.attr(ev.target, 'data-pane'));
|
||||
};
|
||||
|
||||
if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
|
||||
document.body.classList.add('noDashboard');
|
||||
dom.cl.add(dom.body, 'noDashboard');
|
||||
}
|
||||
|
||||
(async ( ) => {
|
||||
|
@ -114,13 +110,9 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
|
|||
|
||||
{
|
||||
const details = results[0] || {};
|
||||
document.body.classList.toggle(
|
||||
'canUpdateShortcuts',
|
||||
details.canUpdateShortcuts === true
|
||||
);
|
||||
if ( details.noDashboard ) {
|
||||
self.location.hash = '#no-dashboard.html';
|
||||
document.body.classList.add('noDashboard');
|
||||
dom.cl.add(dom.body, 'noDashboard');
|
||||
} else if ( self.location.hash === '#no-dashboard.html' ) {
|
||||
self.location.hash = '';
|
||||
}
|
||||
|
@ -133,10 +125,10 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
|
|||
}
|
||||
loadDashboardPanel(pane !== null ? pane : 'settings.html', true);
|
||||
|
||||
uDom('.tabButton').on('click', onTabClickHandler);
|
||||
dom.on('.tabButton', 'click', onTabClickHandler);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
||||
window.addEventListener('beforeunload', ( ) => {
|
||||
dom.on(window, 'beforeunload', ( ) => {
|
||||
if ( discardUnsavedData(true) ) { return; }
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global CodeMirror, uDom, uBlockDashboard */
|
||||
/* global CodeMirror, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const reFoldable = /^ *(?=\+ \S)/;
|
||||
|
@ -60,9 +62,7 @@ CodeMirror.registerGlobalHelper(
|
|||
}
|
||||
);
|
||||
|
||||
const cmEditor = new CodeMirror(
|
||||
document.getElementById('console'),
|
||||
{
|
||||
const cmEditor = new CodeMirror(qs$('#console'), {
|
||||
autofocus: true,
|
||||
foldGutter: true,
|
||||
gutters: [ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter' ],
|
||||
|
@ -71,8 +71,7 @@ const cmEditor = new CodeMirror(
|
|||
mode: 'ubo-dump',
|
||||
styleActiveLine: true,
|
||||
undoDepth: 5,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
||||
|
||||
|
@ -84,11 +83,11 @@ function log(text) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.nodeFromId('console-clear').addEventListener('click', ( ) => {
|
||||
dom.on('#console-clear', 'click', ( ) => {
|
||||
cmEditor.setValue('');
|
||||
});
|
||||
|
||||
uDom.nodeFromId('console-fold').addEventListener('click', ( ) => {
|
||||
dom.on('#console-fold', 'click', ( ) => {
|
||||
const unfolded = [];
|
||||
let maxUnfolded = -1;
|
||||
cmEditor.eachLine(handle => {
|
||||
|
@ -110,7 +109,7 @@ uDom.nodeFromId('console-fold').addEventListener('click', ( ) => {
|
|||
cmEditor.endOperation();
|
||||
});
|
||||
|
||||
uDom.nodeFromId('console-unfold').addEventListener('click', ( ) => {
|
||||
dom.on('#console-unfold', 'click', ( ) => {
|
||||
const folded = [];
|
||||
let minFolded = Number.MAX_SAFE_INTEGER;
|
||||
cmEditor.eachLine(handle => {
|
||||
|
@ -132,25 +131,25 @@ uDom.nodeFromId('console-unfold').addEventListener('click', ( ) => {
|
|||
cmEditor.endOperation();
|
||||
});
|
||||
|
||||
uDom.nodeFromId('snfe-dump').addEventListener('click', ev => {
|
||||
dom.on('#snfe-dump', 'click', ev => {
|
||||
const button = ev.target;
|
||||
button.setAttribute('disabled', '');
|
||||
dom.attr(button, 'disabled', '');
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'snfeDump',
|
||||
}).then(result => {
|
||||
log(result);
|
||||
button.removeAttribute('disabled');
|
||||
dom.attr(button, 'disabled', null);
|
||||
});
|
||||
});
|
||||
|
||||
uDom.nodeFromId('snfe-todnr').addEventListener('click', ev => {
|
||||
dom.on('#snfe-todnr', 'click', ev => {
|
||||
const button = ev.target;
|
||||
button.setAttribute('disabled', '');
|
||||
dom.attr(button, 'disabled', '');
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'snfeToDNR',
|
||||
}).then(result => {
|
||||
log(result);
|
||||
button.removeAttribute('disabled');
|
||||
dom.attr(button, 'disabled', null);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -158,27 +157,27 @@ vAPI.messaging.send('dashboard', {
|
|||
what: 'getAppData',
|
||||
}).then(appData => {
|
||||
if ( appData.canBenchmark !== true ) { return; }
|
||||
uDom.nodeFromId('snfe-benchmark').removeAttribute('disabled');
|
||||
uDom.nodeFromId('snfe-benchmark').addEventListener('click', ev => {
|
||||
dom.attr('#snfe-benchmark', 'disabled', null);
|
||||
dom.on('#snfe-benchmark', 'click', ev => {
|
||||
const button = ev.target;
|
||||
button.setAttribute('disabled', '');
|
||||
dom.attr(button, 'disabled', '');
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'snfeBenchmark',
|
||||
}).then(result => {
|
||||
log(result);
|
||||
button.removeAttribute('disabled');
|
||||
dom.attr(button, 'disabled', null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
uDom.nodeFromId('cfe-dump').addEventListener('click', ev => {
|
||||
dom.on('#cfe-dump', 'click', ev => {
|
||||
const button = ev.target;
|
||||
button.setAttribute('disabled', '');
|
||||
dom.attr(button, 'disabled', '');
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'cfeDump',
|
||||
}).then(result => {
|
||||
log(result);
|
||||
button.removeAttribute('disabled');
|
||||
dom.attr(button, 'disabled', null);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -19,13 +19,10 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -58,27 +55,26 @@ let details = {};
|
|||
|
||||
if ( Array.isArray(lists) === false || lists.length === 0 ) { return; }
|
||||
|
||||
const parent = uDom.nodeFromSelector('#whyex > ul');
|
||||
const parent = qs$('#whyex > ul');
|
||||
for ( const list of lists ) {
|
||||
const listElem = document.querySelector('#templates .filterList')
|
||||
.cloneNode(true);
|
||||
const sourceElem = listElem.querySelector('.filterListSource');
|
||||
const listElem = dom.clone('#templates .filterList');
|
||||
const sourceElem = qs$(listElem, '.filterListSource');
|
||||
sourceElem.href += encodeURIComponent(list.assetKey);
|
||||
sourceElem.textContent = list.title;
|
||||
dom.text(sourceElem, list.title);
|
||||
if ( typeof list.supportURL === 'string' && list.supportURL !== '' ) {
|
||||
const supportElem = listElem.querySelector('.filterListSupport');
|
||||
supportElem.setAttribute('href', list.supportURL);
|
||||
supportElem.classList.remove('hidden');
|
||||
const supportElem = qs$(listElem, '.filterListSupport');
|
||||
dom.attr(supportElem, 'href', list.supportURL);
|
||||
dom.cl.remove(supportElem, 'hidden');
|
||||
}
|
||||
parent.appendChild(listElem);
|
||||
}
|
||||
uDom.nodeFromId('whyex').style.removeProperty('display');
|
||||
qs$('#whyex').style.removeProperty('display');
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.nodeFromSelector('#theURL > p > span:first-of-type').textContent = details.url;
|
||||
uDom.nodeFromId('why').textContent = details.fs;
|
||||
dom.text('#theURL > p > span:first-of-type', details.url);
|
||||
dom.text('#why', details.fs);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -95,20 +91,21 @@ uDom.nodeFromId('why').textContent = details.fs;
|
|||
value = name;
|
||||
name = '';
|
||||
}
|
||||
const li = document.createElement('li');
|
||||
let span = document.createElement('span');
|
||||
span.textContent = name;
|
||||
const li = dom.create('li');
|
||||
let span = dom.create('span');
|
||||
dom.text(span, name);
|
||||
li.appendChild(span);
|
||||
if ( name !== '' && value !== '' ) {
|
||||
li.appendChild(document.createTextNode(' = '));
|
||||
}
|
||||
span = document.createElement('span');
|
||||
span = dom.create('span');
|
||||
if ( reURL.test(value) ) {
|
||||
const a = document.createElement('a');
|
||||
a.href = a.textContent = value;
|
||||
const a = dom.create('a');
|
||||
dom.attr(a, 'href', value);
|
||||
dom.text(a, value);
|
||||
span.appendChild(a);
|
||||
} else {
|
||||
span.textContent = value;
|
||||
dom.text(span, value);
|
||||
}
|
||||
li.appendChild(span);
|
||||
return li;
|
||||
|
@ -135,7 +132,7 @@ uDom.nodeFromId('why').textContent = details.fs;
|
|||
for ( const [ name, value ] of params ) {
|
||||
const li = liFromParam(name, value);
|
||||
if ( depth < 2 && reURL.test(value) ) {
|
||||
const ul = document.createElement('ul');
|
||||
const ul = dom.create('ul');
|
||||
renderParams(ul, value, depth + 1);
|
||||
li.appendChild(ul);
|
||||
}
|
||||
|
@ -145,27 +142,22 @@ uDom.nodeFromId('why').textContent = details.fs;
|
|||
return true;
|
||||
};
|
||||
|
||||
if ( renderParams(uDom.nodeFromId('parsed'), details.url) === false ) {
|
||||
if ( renderParams(qs$('#parsed'), details.url) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toggler = document.querySelector('#toggleParse');
|
||||
toggler.classList.remove('hidden');
|
||||
dom.cl.remove('#toggleParse', 'hidden');
|
||||
|
||||
toggler.addEventListener('click', ( ) => {
|
||||
const cl = uDom.nodeFromId('theURL').classList;
|
||||
cl.toggle('collapsed');
|
||||
dom.on('#toggleParse', 'click', ( ) => {
|
||||
dom.cl.toggle('#theURL', 'collapsed');
|
||||
vAPI.localStorage.setItem(
|
||||
'document-blocked-expand-url',
|
||||
(cl.contains('collapsed') === false).toString()
|
||||
(dom.cl.has('#theURL', 'collapsed') === false).toString()
|
||||
);
|
||||
});
|
||||
|
||||
vAPI.localStorage.getItemAsync('document-blocked-expand-url').then(value => {
|
||||
uDom.nodeFromId('theURL').classList.toggle(
|
||||
'collapsed',
|
||||
value !== 'true' && value !== true
|
||||
);
|
||||
dom.cl.toggle('#theURL', 'collapsed', value !== 'true' && value !== true);
|
||||
});
|
||||
})();
|
||||
|
||||
|
@ -174,23 +166,17 @@ uDom.nodeFromId('why').textContent = details.fs;
|
|||
// https://www.reddit.com/r/uBlockOrigin/comments/breeux/close_this_window_doesnt_work_on_firefox/
|
||||
|
||||
if ( window.history.length > 1 ) {
|
||||
uDom('#back').on(
|
||||
'click',
|
||||
( ) => {
|
||||
dom.on('#back', 'click', ( ) => {
|
||||
window.history.back();
|
||||
}
|
||||
);
|
||||
uDom('#bye').css('display', 'none');
|
||||
});
|
||||
qs$('#bye').style.display = 'none';
|
||||
} else {
|
||||
uDom('#bye').on(
|
||||
'click',
|
||||
( ) => {
|
||||
dom.on('#bye', 'click', ( ) => {
|
||||
messaging.send('documentBlocked', {
|
||||
what: 'closeThisTab',
|
||||
});
|
||||
}
|
||||
);
|
||||
uDom('#back').css('display', 'none');
|
||||
});
|
||||
qs$('#back').style.display = 'none';
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -223,15 +209,14 @@ const proceedPermanent = async function() {
|
|||
proceedToURL();
|
||||
};
|
||||
|
||||
uDom('#disableWarning').on('change', ev => {
|
||||
dom.on('#disableWarning', 'change', ev => {
|
||||
const checked = ev.target.checked;
|
||||
document.querySelector('[data-i18n="docblockedBack"]').classList.toggle('disabled', checked);
|
||||
document.querySelector('[data-i18n="docblockedClose"]').classList.toggle('disabled', checked);
|
||||
dom.cl.toggle('[data-i18n="docblockedBack"]', 'disabled', checked);
|
||||
dom.cl.toggle('[data-i18n="docblockedClose"]', 'disabled', checked);
|
||||
});
|
||||
|
||||
uDom('#proceed').on('click', ( ) => {
|
||||
const input = document.querySelector('#disableWarning');
|
||||
if ( input.checked ) {
|
||||
dom.on('#proceed', 'click', ( ) => {
|
||||
if ( qs$('#disableWarning').checked ) {
|
||||
proceedPermanent();
|
||||
} else {
|
||||
proceedTemporary();
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
/******************************************************************************/
|
||||
|
||||
const normalizeTarget = target => {
|
||||
if ( typeof target === 'string' ) { return Array.from(qsa$(target)); }
|
||||
if ( target instanceof Element ) { return [ target ]; }
|
||||
if ( target === null ) { return []; }
|
||||
if ( Array.isArray(target) ) { return target; }
|
||||
return target instanceof Element
|
||||
? [ target ]
|
||||
: Array.from(target);
|
||||
return Array.from(target);
|
||||
};
|
||||
|
||||
const makeEventHandler = (selector, callback) => {
|
||||
|
@ -70,6 +70,16 @@ class dom {
|
|||
}
|
||||
}
|
||||
|
||||
static clone(target) {
|
||||
return normalizeTarget(target)[0].cloneNode(true);
|
||||
}
|
||||
|
||||
static create(a) {
|
||||
if ( typeof a === 'string' ) {
|
||||
return document.createElement(a);
|
||||
}
|
||||
}
|
||||
|
||||
static text(target, text) {
|
||||
for ( const elem of normalizeTarget(target) ) {
|
||||
elem.textContent = text;
|
||||
|
@ -82,15 +92,43 @@ class dom {
|
|||
}
|
||||
}
|
||||
|
||||
static on(target, type, selector, callback) {
|
||||
if ( typeof selector === 'function' ) {
|
||||
callback = selector;
|
||||
selector = undefined;
|
||||
} else {
|
||||
callback = makeEventHandler(selector, callback);
|
||||
// target, type, callback, [options]
|
||||
// target, type, subtarget, callback, [options]
|
||||
|
||||
static on(target, type, subtarget, callback, options) {
|
||||
if ( typeof subtarget === 'function' ) {
|
||||
options = callback;
|
||||
callback = subtarget;
|
||||
subtarget = undefined;
|
||||
if ( typeof options === 'boolean' ) {
|
||||
options = { capture: true };
|
||||
}
|
||||
for ( const elem of normalizeTarget(target) ) {
|
||||
elem.addEventListener(type, callback, selector !== undefined);
|
||||
} else {
|
||||
callback = makeEventHandler(subtarget, callback);
|
||||
if ( options === undefined || typeof options === 'boolean' ) {
|
||||
options = { capture: true };
|
||||
} else {
|
||||
options.capture = true;
|
||||
}
|
||||
}
|
||||
const targets = target instanceof Window || target instanceof Document
|
||||
? [ target ]
|
||||
: normalizeTarget(target);
|
||||
for ( const elem of targets ) {
|
||||
elem.addEventListener(type, callback, options);
|
||||
}
|
||||
}
|
||||
|
||||
static off(target, type, callback, options) {
|
||||
if ( typeof callback !== 'function' ) { return; }
|
||||
if ( typeof options === 'boolean' ) {
|
||||
options = { capture: true };
|
||||
}
|
||||
const targets = target instanceof Window || target instanceof Document
|
||||
? [ target ]
|
||||
: normalizeTarget(target);
|
||||
for ( const elem of targets ) {
|
||||
elem.removeEventListener(type, callback, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,9 +147,11 @@ dom.cl = class {
|
|||
}
|
||||
|
||||
static toggle(target, name, state) {
|
||||
let r;
|
||||
for ( const elem of normalizeTarget(target) ) {
|
||||
elem.classList.toggle(name, state);
|
||||
r = elem.classList.toggle(name, state);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static has(target, name) {
|
||||
|
@ -124,31 +164,27 @@ dom.cl = class {
|
|||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function qs$(a, b) {
|
||||
if ( typeof a === 'string') {
|
||||
return document.querySelector(a);
|
||||
}
|
||||
return a.querySelector(b);
|
||||
}
|
||||
|
||||
function qsa$(a, b) {
|
||||
if ( typeof a === 'string') {
|
||||
return document.querySelectorAll(a);
|
||||
}
|
||||
return a.querySelectorAll(b);
|
||||
}
|
||||
|
||||
dom.root = qs$(':root');
|
||||
dom.html = document.documentElement;
|
||||
dom.head = document.head;
|
||||
dom.body = document.body;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function qs$(s, elem = undefined) {
|
||||
return (elem || document).querySelector(s);
|
||||
}
|
||||
|
||||
function qsa$(s, elem = undefined) {
|
||||
return (elem || document).querySelectorAll(s);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
{
|
||||
const mql = self.matchMedia('(prefers-color-scheme: dark)');
|
||||
const theme = mql instanceof Object && mql.matches === true
|
||||
? 'dark'
|
||||
: 'light';
|
||||
dom.cl.toggle(dom.html, 'dark', theme === 'dark');
|
||||
dom.cl.toggle(dom.html, 'light', theme !== 'dark');
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
export { dom, qs$, qsa$ };
|
|
@ -19,16 +19,15 @@
|
|||
Home: https://github.com/gorhill/uMatrix
|
||||
*/
|
||||
|
||||
/* global CodeMirror, diff_match_patch, uDom, uBlockDashboard */
|
||||
/* global CodeMirror, diff_match_patch, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
|
||||
|
||||
import { hostnameFromURI } from './uri-utils.js';
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
|
||||
import './codemirror/ubo-dynamic-filtering.js';
|
||||
|
||||
|
@ -37,7 +36,7 @@ import './codemirror/ubo-dynamic-filtering.js';
|
|||
const hostnameToDomainMap = new Map();
|
||||
|
||||
const mergeView = new CodeMirror.MergeView(
|
||||
document.querySelector('.codeMirrorMergeContainer'),
|
||||
qs$('.codeMirrorMergeContainer'),
|
||||
{
|
||||
allowEditingOriginals: true,
|
||||
connect: 'align',
|
||||
|
@ -86,24 +85,23 @@ let isCollapsed = false;
|
|||
const commitArrowSelector = '.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy-reverse:not([title="' + i18nCommitStr + '"])';
|
||||
const revertArrowSelector = '.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy:not([title="' + i18nRevertStr + '"])';
|
||||
|
||||
uDom.nodeFromSelector('.CodeMirror-merge-scrolllock')
|
||||
.setAttribute('title', i18n$('genericMergeViewScrollLock'));
|
||||
dom.attr('.CodeMirror-merge-scrolllock', 'title', i18n$('genericMergeViewScrollLock'));
|
||||
|
||||
const translate = function() {
|
||||
let elems = document.querySelectorAll(commitArrowSelector);
|
||||
let elems = qsa$(commitArrowSelector);
|
||||
for ( const elem of elems ) {
|
||||
elem.setAttribute('title', i18nCommitStr);
|
||||
dom.attr(elem, 'title', i18nCommitStr);
|
||||
}
|
||||
elems = document.querySelectorAll(revertArrowSelector);
|
||||
elems = qsa$(revertArrowSelector);
|
||||
for ( const elem of elems ) {
|
||||
elem.setAttribute('title', i18nRevertStr);
|
||||
dom.attr(elem, 'title', i18nRevertStr);
|
||||
}
|
||||
};
|
||||
|
||||
const mergeGapObserver = new MutationObserver(translate);
|
||||
|
||||
mergeGapObserver.observe(
|
||||
uDom.nodeFromSelector('.CodeMirror-merge-copybuttons-left'),
|
||||
qs$('.CodeMirror-merge-copybuttons-left'),
|
||||
{ attributes: true, attributeFilter: [ 'title' ], subtree: true }
|
||||
);
|
||||
|
||||
|
@ -247,7 +245,7 @@ const rulesToDoc = function(clearHistory) {
|
|||
/******************************************************************************/
|
||||
|
||||
const filterRules = function(key) {
|
||||
const filter = uDom.nodeFromSelector('#ruleFilter input').value;
|
||||
const filter = qs$('#ruleFilter input').value;
|
||||
const rules = thePanes[key].modified;
|
||||
if ( filter === '' ) { return rules; }
|
||||
const out = [];
|
||||
|
@ -283,7 +281,7 @@ mergeView.options.revertChunk = function(
|
|||
to, toStart, toEnd
|
||||
) {
|
||||
// https://github.com/gorhill/uBlock/issues/3611
|
||||
if ( document.body.getAttribute('dir') === 'rtl' ) {
|
||||
if ( dom.attr(dom.body, 'dir') === 'rtl' ) {
|
||||
let tmp = from; from = to; to = tmp;
|
||||
tmp = fromStart; fromStart = toStart; toStart = tmp;
|
||||
tmp = fromEnd; fromEnd = toEnd; toEnd = tmp;
|
||||
|
@ -330,7 +328,7 @@ function handleImportFilePicker() {
|
|||
/******************************************************************************/
|
||||
|
||||
const startImportFilePicker = function() {
|
||||
const input = document.getElementById('importFilePicker');
|
||||
const input = qs$('#importFilePicker');
|
||||
// Reset to empty string, this will ensure an change event is properly
|
||||
// triggered if the user pick a file, even if it is the same as the last
|
||||
// one picked.
|
||||
|
@ -363,7 +361,7 @@ const onFilterChanged = (( ) => {
|
|||
const process = function() {
|
||||
timer = undefined;
|
||||
if ( mergeView.editor().isClean(cleanEditToken) === false ) { return; }
|
||||
const filter = uDom.nodeFromSelector('#ruleFilter input').value;
|
||||
const filter = qs$('#ruleFilter input').value;
|
||||
if ( filter === last ) { return; }
|
||||
last = filter;
|
||||
if ( overlay !== null ) {
|
||||
|
@ -498,7 +496,7 @@ const onPresentationChanged = (( ) => {
|
|||
const editPane = thePanes.edit;
|
||||
origPane.modified = origPane.original.slice();
|
||||
editPane.modified = editPane.original.slice();
|
||||
const select = document.querySelector('#ruleFilter select');
|
||||
const select = qs$('#ruleFilter select');
|
||||
sortType = parseInt(select.value, 10);
|
||||
if ( isNaN(sortType) ) { sortType = 1; }
|
||||
{
|
||||
|
@ -528,7 +526,7 @@ const onTextChanged = (( ) => {
|
|||
|
||||
const process = details => {
|
||||
timer = undefined;
|
||||
const diff = document.getElementById('diff');
|
||||
const diff = qs$('#diff');
|
||||
let isClean = mergeView.editor().isClean(cleanEditToken);
|
||||
if (
|
||||
details === undefined &&
|
||||
|
@ -539,20 +537,17 @@ const onTextChanged = (( ) => {
|
|||
isClean = true;
|
||||
}
|
||||
const isDirty = mergeView.leftChunks().length !== 0;
|
||||
document.body.classList.toggle('editing', isClean === false);
|
||||
diff.classList.toggle('dirty', isDirty);
|
||||
uDom('#editSaveButton')
|
||||
.toggleClass('disabled', isClean);
|
||||
uDom('#exportButton,#importButton')
|
||||
.toggleClass('disabled', isClean === false);
|
||||
uDom('#revertButton,#commitButton')
|
||||
.toggleClass('disabled', isClean === false || isDirty === false);
|
||||
const input = document.querySelector('#ruleFilter input');
|
||||
dom.cl.toggle(dom.body, 'editing', isClean === false);
|
||||
dom.cl.toggle(diff, 'dirty', isDirty);
|
||||
dom.cl.toggle('#editSaveButton', 'disabled', isClean);
|
||||
dom.cl.toggle('#exportButton,#importButton', 'disabled', isClean === false);
|
||||
dom.cl.toggle('#revertButton,#commitButton', 'disabled', isClean === false || isDirty === false);
|
||||
const input = qs$('#ruleFilter input');
|
||||
if ( isClean ) {
|
||||
input.removeAttribute('disabled');
|
||||
dom.attr(input, 'disabled', null);
|
||||
CodeMirror.commands.save = undefined;
|
||||
} else {
|
||||
input.setAttribute('disabled', '');
|
||||
dom.attr(input, 'disabled', '');
|
||||
CodeMirror.commands.save = editSaveHandler;
|
||||
}
|
||||
};
|
||||
|
@ -659,23 +654,25 @@ vAPI.messaging.send('dashboard', {
|
|||
});
|
||||
|
||||
// Handle user interaction
|
||||
uDom('#importButton').on('click', startImportFilePicker);
|
||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||
uDom('#exportButton').on('click', exportUserRulesToFile);
|
||||
uDom('#revertButton').on('click', revertAllHandler);
|
||||
uDom('#commitButton').on('click', commitAllHandler);
|
||||
uDom('#editSaveButton').on('click', editSaveHandler);
|
||||
uDom('#ruleFilter input').on('input', onFilterChanged);
|
||||
uDom('#ruleFilter select').on('input', ( ) => {
|
||||
dom.on('#importButton', 'click', startImportFilePicker);
|
||||
dom.on('#importFilePicker', 'change', handleImportFilePicker);
|
||||
dom.on('#exportButton', 'click', exportUserRulesToFile);
|
||||
dom.on('#revertButton', 'click', revertAllHandler);
|
||||
dom.on('#commitButton', 'click', commitAllHandler);
|
||||
dom.on('#editSaveButton', 'click', editSaveHandler);
|
||||
dom.on('#ruleFilter input', 'input', onFilterChanged);
|
||||
dom.on('#ruleFilter select', 'input', ( ) => {
|
||||
onPresentationChanged(true);
|
||||
});
|
||||
uDom('#ruleFilter #diffCollapse').on('click', ev => {
|
||||
isCollapsed = ev.target.classList.toggle('active');
|
||||
dom.on('#ruleFilter #diffCollapse', 'click', ev => {
|
||||
isCollapsed = dom.cl.toggle(ev.target, 'active');
|
||||
onPresentationChanged(true);
|
||||
});
|
||||
|
||||
// https://groups.google.com/forum/#!topic/codemirror/UQkTrt078Vs
|
||||
mergeView.editor().on('updateDiff', ( ) => { onTextChanged(); });
|
||||
mergeView.editor().on('updateDiff', ( ) => {
|
||||
onTextChanged();
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
import './codemirror/ubo-static-filtering.js';
|
||||
|
||||
import { hostnameFromURI } from './uri-utils.js';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015-2018 Raymond Hill
|
||||
Copyright (C) 2015-present 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
|
||||
|
@ -19,17 +19,17 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const showdomButton = uDom.nodeFromId('showdom');
|
||||
const showdomButton = qs$('#showdom');
|
||||
|
||||
// Don't bother if the browser is not modern enough.
|
||||
if (
|
||||
|
@ -37,7 +37,7 @@ if (
|
|||
Map.polyfill ||
|
||||
typeof WeakMap === 'undefined'
|
||||
) {
|
||||
showdomButton.classList.add('disabled');
|
||||
dom.cl.add(showdomButton, 'disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ var inspectorConnectionId;
|
|||
var inspectedTabId = 0;
|
||||
var inspectedURL = '';
|
||||
var inspectedHostname = '';
|
||||
var inspector = uDom.nodeFromId('domInspector');
|
||||
var domTree = uDom.nodeFromId('domTree');
|
||||
var inspector = qs$('#domInspector');
|
||||
var domTree = qs$('#domTree');
|
||||
var uidGenerator = 1;
|
||||
var filterToIdMap = new Map();
|
||||
|
||||
|
@ -92,8 +92,8 @@ vAPI.MessagingConnection.addListener(function(msg) {
|
|||
|
||||
const nodeFromDomEntry = function(entry) {
|
||||
var node, value;
|
||||
var li = document.createElement('li');
|
||||
li.setAttribute('id', entry.nid);
|
||||
const li = document.createElement('li');
|
||||
dom.attr(li, 'id', entry.nid);
|
||||
// expander/collapser
|
||||
li.appendChild(document.createElement('span'));
|
||||
// selector
|
||||
|
@ -104,24 +104,24 @@ const nodeFromDomEntry = function(entry) {
|
|||
value = entry.cnt || 0;
|
||||
node = document.createElement('span');
|
||||
node.textContent = value !== 0 ? value.toLocaleString() : '';
|
||||
node.setAttribute('data-cnt', value);
|
||||
dom.attr(node, 'data-cnt', value);
|
||||
li.appendChild(node);
|
||||
// cosmetic filter
|
||||
if ( entry.filter === undefined ) {
|
||||
return li;
|
||||
}
|
||||
node = document.createElement('code');
|
||||
node.classList.add('filter');
|
||||
dom.cl.add(node, 'filter');
|
||||
value = filterToIdMap.get(entry.filter);
|
||||
if ( value === undefined ) {
|
||||
value = uidGenerator.toString();
|
||||
filterToIdMap.set(entry.filter, value);
|
||||
uidGenerator += 1;
|
||||
}
|
||||
node.setAttribute('data-filter-id', value);
|
||||
dom.attr(node, 'data-filter-id', value);
|
||||
node.textContent = entry.filter;
|
||||
li.appendChild(node);
|
||||
li.classList.add('isCosmeticHide');
|
||||
dom.cl.add(li, 'isCosmeticHide');
|
||||
return li;
|
||||
};
|
||||
|
||||
|
@ -131,11 +131,11 @@ const 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; }
|
||||
if ( dom.cl.has(li, 'isCosmeticHide') === false ) { return; }
|
||||
for (;;) {
|
||||
li = li.parentElement.parentElement;
|
||||
if ( li === null ) { break; }
|
||||
li.classList.add('hasCosmeticHide');
|
||||
dom.cl.add(li, 'hasCosmeticHide');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -162,7 +162,7 @@ const renderDOMFull = function(response) {
|
|||
if ( entry.lvl > lvl ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
dom.cl.add(li, 'branch');
|
||||
li = nodeFromDomEntry(entry);
|
||||
appendListItem(ul, li);
|
||||
lvl = entry.lvl;
|
||||
|
@ -181,7 +181,7 @@ const renderDOMFull = function(response) {
|
|||
while ( ul.parentNode !== null ) {
|
||||
ul = ul.parentNode;
|
||||
}
|
||||
ul.firstElementChild.classList.add('show');
|
||||
dom.cl.add(ul.firstElementChild, 'show');
|
||||
|
||||
domTreeParent.appendChild(domTree);
|
||||
};
|
||||
|
@ -194,8 +194,8 @@ const patchIncremental = function(from, delta) {
|
|||
var span, cnt;
|
||||
var li = from.parentElement.parentElement;
|
||||
var patchCosmeticHide = delta >= 0 &&
|
||||
from.classList.contains('isCosmeticHide') &&
|
||||
li.classList.contains('hasCosmeticHide') === false;
|
||||
dom.cl.has(from, 'isCosmeticHide') &&
|
||||
dom.cl.has(li, 'hasCosmeticHide') === false;
|
||||
// Include descendants count when removing a node
|
||||
if ( delta < 0 ) {
|
||||
delta -= countFromNode(from);
|
||||
|
@ -205,10 +205,10 @@ const patchIncremental = function(from, delta) {
|
|||
if ( delta !== 0 ) {
|
||||
cnt = countFromNode(li) + delta;
|
||||
span.textContent = cnt !== 0 ? cnt.toLocaleString() : '';
|
||||
span.setAttribute('data-cnt', cnt);
|
||||
dom.attr(span, 'data-cnt', cnt);
|
||||
}
|
||||
if ( patchCosmeticHide ) {
|
||||
li.classList.add('hasCosmeticHide');
|
||||
dom.cl.add(li, 'hasCosmeticHide');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -226,7 +226,7 @@ const renderDOMIncremental = function(response) {
|
|||
entry = journal[i];
|
||||
// Remove node
|
||||
if ( entry.what === -1 ) {
|
||||
li = document.getElementById(entry.nid);
|
||||
li = qs$(`#${entry.nid}`);
|
||||
if ( li === null ) { continue; }
|
||||
patchIncremental(li, -1);
|
||||
li.parentNode.removeChild(li);
|
||||
|
@ -239,7 +239,7 @@ const renderDOMIncremental = function(response) {
|
|||
}
|
||||
// Add node as sibling
|
||||
if ( entry.what === 1 && entry.l ) {
|
||||
previous = document.getElementById(entry.l);
|
||||
previous = qs$(`#{entry.l}`);
|
||||
// This should not happen
|
||||
if ( previous === null ) {
|
||||
// throw new Error('No left sibling!?');
|
||||
|
@ -253,17 +253,17 @@ const renderDOMIncremental = function(response) {
|
|||
}
|
||||
// Add node as child
|
||||
if ( entry.what === 1 && entry.u ) {
|
||||
li = document.getElementById(entry.u);
|
||||
li = qs$(`#${entry.u}`);
|
||||
// This should not happen
|
||||
if ( li === null ) {
|
||||
// throw new Error('No parent!?');
|
||||
continue;
|
||||
}
|
||||
ul = li.querySelector('ul');
|
||||
ul = qs$(li, 'ul');
|
||||
if ( ul === null ) {
|
||||
ul = document.createElement('ul');
|
||||
li.appendChild(ul);
|
||||
li.classList.add('branch');
|
||||
dom.cl.add(li, 'branch');
|
||||
}
|
||||
li = nodeFromDomEntry(nodes.get(entry.nid));
|
||||
ul.appendChild(li);
|
||||
|
@ -273,13 +273,11 @@ const renderDOMIncremental = function(response) {
|
|||
}
|
||||
};
|
||||
|
||||
// https://www.youtube.com/watch?v=6u2KPtJB9h8
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const countFromNode = function(li) {
|
||||
var span = li.children[2];
|
||||
var cnt = parseInt(span.getAttribute('data-cnt'), 10);
|
||||
var cnt = parseInt(dom.attr(span, 'data-cnt'), 10);
|
||||
return isNaN(cnt) ? 0 : cnt;
|
||||
};
|
||||
|
||||
|
@ -290,7 +288,7 @@ const selectorFromNode = function(node) {
|
|||
var code;
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
code = node.querySelector('code');
|
||||
code = qs$(node, 'code');
|
||||
if ( code !== null ) {
|
||||
selector = code.textContent + ' > ' + selector;
|
||||
if ( selector.indexOf('#') !== -1 ) {
|
||||
|
@ -308,7 +306,7 @@ const selectorFromNode = function(node) {
|
|||
const selectorFromFilter = function(node) {
|
||||
while ( node !== null ) {
|
||||
if ( node.localName === 'li' ) {
|
||||
var code = node.querySelector('code:nth-of-type(2)');
|
||||
var code = qs$(node, 'code:nth-of-type(2)');
|
||||
if ( code !== null ) {
|
||||
return code.textContent;
|
||||
}
|
||||
|
@ -409,10 +407,10 @@ const startDialog = (function() {
|
|||
|
||||
const start = function() {
|
||||
dialog = logger.modalDialog.create('#cosmeticFilteringDialog', stop);
|
||||
textarea = dialog.querySelector('textarea');
|
||||
textarea = qs$(dialog, 'textarea');
|
||||
hideSelectors = [];
|
||||
for ( const node of domTree.querySelectorAll('code.off') ) {
|
||||
if ( node.classList.contains('filter') ) { continue; }
|
||||
for ( const node of qsa$(domTree, 'code.off') ) {
|
||||
if ( dom.cl.has(node, 'filter') ) { continue; }
|
||||
hideSelectors.push(selectorFromNode(node));
|
||||
}
|
||||
const taValue = [];
|
||||
|
@ -420,8 +418,8 @@ const startDialog = (function() {
|
|||
taValue.push(inspectedHostname + '##' + selector);
|
||||
}
|
||||
const ids = new Set();
|
||||
for ( const node of domTree.querySelectorAll('code.filter.off') ) {
|
||||
const id = node.getAttribute('data-filter-id');
|
||||
for ( const node of qsa$(domTree, 'code.filter.off') ) {
|
||||
const id = dom.attr(node, 'data-filter-id');
|
||||
if ( ids.has(id) ) { continue; }
|
||||
ids.add(id);
|
||||
unhideSelectors.push(node.textContent);
|
||||
|
@ -465,13 +463,13 @@ const onClicked = function(ev) {
|
|||
if (
|
||||
target.localName === 'span' &&
|
||||
parent instanceof HTMLLIElement &&
|
||||
parent.classList.contains('branch') &&
|
||||
dom.cl.has(parent, 'branch') &&
|
||||
target === parent.firstElementChild
|
||||
) {
|
||||
var state = parent.classList.toggle('show');
|
||||
const state = dom.cl.toggle(parent, 'show');
|
||||
if ( !state ) {
|
||||
for ( var node of parent.querySelectorAll('.branch') ) {
|
||||
node.classList.remove('show');
|
||||
for ( const node of qsa$(parent, '.branch') ) {
|
||||
dom.cl.remove(node, 'show');
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -481,18 +479,19 @@ const onClicked = function(ev) {
|
|||
if ( target.localName !== 'code' ) { return; }
|
||||
|
||||
// Toggle cosmetic filter
|
||||
if ( target.classList.contains('filter') ) {
|
||||
if ( dom.cl.has(target, 'filter') ) {
|
||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
||||
what: 'toggleFilter',
|
||||
original: false,
|
||||
target: target.classList.toggle('off'),
|
||||
target: dom.cl.toggle(target, 'off'),
|
||||
selector: selectorFromNode(target),
|
||||
filter: selectorFromFilter(target),
|
||||
nid: nidFromNode(target)
|
||||
});
|
||||
uDom('[data-filter-id="' + target.getAttribute('data-filter-id') + '"]', inspector).toggleClass(
|
||||
dom.cl.toggle(
|
||||
qsa$(inspector, `[data-filter-id="${dom.attr(target, 'data-filter-id')}"]`),
|
||||
'off',
|
||||
target.classList.contains('off')
|
||||
dom.cl.has(target, 'off')
|
||||
);
|
||||
}
|
||||
// Toggle node
|
||||
|
@ -500,15 +499,15 @@ const onClicked = function(ev) {
|
|||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
||||
what: 'toggleNodes',
|
||||
original: true,
|
||||
target: target.classList.toggle('off') === false,
|
||||
target: dom.cl.toggle(target, 'off') === false,
|
||||
selector: selectorFromNode(target),
|
||||
nid: nidFromNode(target)
|
||||
});
|
||||
}
|
||||
|
||||
var cantCreate = domTree.querySelector('.off') === null;
|
||||
inspector.querySelector('.permatoolbar .revert').classList.toggle('disabled', cantCreate);
|
||||
inspector.querySelector('.permatoolbar .commit').classList.toggle('disabled', cantCreate);
|
||||
const cantCreate = qs$(domTree, '.off') === null;
|
||||
dom.cl.toggle(qs$(inspector, '.permatoolbar .revert'), 'disabled', cantCreate);
|
||||
dom.cl.toggle(qs$(inspector, '.permatoolbar .commit'), 'disabled', cantCreate);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -548,7 +547,7 @@ const onMouseOver = (function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const currentTabId = function() {
|
||||
if ( showdomButton.classList.contains('active') === false ) { return 0; }
|
||||
if ( dom.cl.has(showdomButton, 'active') === false ) { return 0; }
|
||||
return logger.tabIdFromPageSelector();
|
||||
};
|
||||
|
||||
|
@ -573,7 +572,7 @@ const shutdownInspector = function() {
|
|||
inspectorConnectionId = undefined;
|
||||
}
|
||||
logger.removeAllChildren(domTree);
|
||||
inspector.classList.remove('vExpanded');
|
||||
dom.cl.remove(inspector, 'vExpanded');
|
||||
inspectedTabId = 0;
|
||||
};
|
||||
|
||||
|
@ -593,76 +592,65 @@ const onTabIdChanged = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const toggleVCompactView = function() {
|
||||
var state = inspector.classList.toggle('vExpanded');
|
||||
var branches = document.querySelectorAll('#domInspector li.branch');
|
||||
for ( var branch of branches ) {
|
||||
branch.classList.toggle('show', state);
|
||||
const state = dom.cl.toggle(inspector, 'vExpanded');
|
||||
const branches = qsa$('#domInspector li.branch');
|
||||
for ( const branch of branches ) {
|
||||
dom.cl.toggle(branch, 'show', state);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleHCompactView = function() {
|
||||
inspector.classList.toggle('hCompact');
|
||||
dom.cl.toggle(inspector, 'hCompact');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
var toggleHighlightMode = function() {
|
||||
vAPI.MessagingConnection.sendTo(inspectorConnectionId, {
|
||||
what: 'highlightMode',
|
||||
invert: uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').classList.toggle('invert')
|
||||
});
|
||||
};
|
||||
*/
|
||||
/******************************************************************************/
|
||||
|
||||
const revert = function() {
|
||||
uDom('#domTree .off').removeClass('off');
|
||||
dom.cl.remove('#domTree .off', 'off');
|
||||
vAPI.MessagingConnection.sendTo(
|
||||
inspectorConnectionId,
|
||||
{ what: 'resetToggledNodes' }
|
||||
);
|
||||
inspector.querySelector('.permatoolbar .revert').classList.add('disabled');
|
||||
inspector.querySelector('.permatoolbar .commit').classList.add('disabled');
|
||||
dom.cl.add(qs$(inspector, '.permatoolbar .revert'), 'disabled');
|
||||
dom.cl.add(qs$(inspector, '.permatoolbar .commit'), 'disabled');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const toggleOn = function() {
|
||||
uDom.nodeFromId('inspectors').classList.add('dom');
|
||||
dom.cl.add('#inspectors', 'dom');
|
||||
window.addEventListener('beforeunload', toggleOff);
|
||||
document.addEventListener('tabIdChanged', onTabIdChanged);
|
||||
domTree.addEventListener('click', onClicked, true);
|
||||
domTree.addEventListener('mouseover', onMouseOver, true);
|
||||
uDom.nodeFromSelector('#domInspector .vCompactToggler').addEventListener('click', toggleVCompactView);
|
||||
uDom.nodeFromSelector('#domInspector .hCompactToggler').addEventListener('click', toggleHCompactView);
|
||||
//uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').addEventListener('click', toggleHighlightMode);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .revert').addEventListener('click', revert);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .commit').addEventListener('click', startDialog);
|
||||
dom.on('#domInspector .vCompactToggler', 'click', toggleVCompactView);
|
||||
dom.on('#domInspector .hCompactToggler', 'click', toggleHCompactView);
|
||||
dom.on('#domInspector .permatoolbar .revert', 'click', revert);
|
||||
dom.on('#domInspector .permatoolbar .commit', 'click', startDialog);
|
||||
injectInspector();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const toggleOff = function() {
|
||||
showdomButton.classList.remove('active');
|
||||
uDom.nodeFromId('inspectors').classList.remove('dom');
|
||||
dom.cl.remove(showdomButton, 'active');
|
||||
dom.cl.remove('#inspectors', 'dom');
|
||||
shutdownInspector();
|
||||
window.removeEventListener('beforeunload', toggleOff);
|
||||
document.removeEventListener('tabIdChanged', onTabIdChanged);
|
||||
domTree.removeEventListener('click', onClicked, true);
|
||||
domTree.removeEventListener('mouseover', onMouseOver, true);
|
||||
uDom.nodeFromSelector('#domInspector .vCompactToggler').removeEventListener('click', toggleVCompactView);
|
||||
uDom.nodeFromSelector('#domInspector .hCompactToggler').removeEventListener('click', toggleHCompactView);
|
||||
//uDom.nodeFromSelector('#domInspector .permatoolbar .highlightMode').removeEventListener('click', toggleHighlightMode);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .revert').removeEventListener('click', revert);
|
||||
uDom.nodeFromSelector('#domInspector .permatoolbar .commit').removeEventListener('click', startDialog);
|
||||
dom.off('#domInspector .vCompactToggler', 'click', toggleVCompactView);
|
||||
dom.off('#domInspector .hCompactToggler', 'click', toggleHCompactView);
|
||||
dom.off('#domInspector .permatoolbar .revert', 'click', revert);
|
||||
dom.off('#domInspector .permatoolbar .commit', 'click', startDialog);
|
||||
inspectedTabId = 0;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const toggle = function() {
|
||||
if ( showdomButton.classList.toggle('active') ) {
|
||||
if ( dom.cl.toggle(showdomButton, 'active') ) {
|
||||
toggleOn();
|
||||
} else {
|
||||
toggleOff();
|
||||
|
@ -670,9 +658,7 @@ const toggle = function() {
|
|||
logger.resize();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
showdomButton.addEventListener('click', toggle);
|
||||
dom.on(showdomButton, 'click', toggle);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1311,40 +1311,6 @@ const modifyRuleset = function(details) {
|
|||
}
|
||||
};
|
||||
|
||||
// Shortcuts
|
||||
const getShortcuts = function(callback) {
|
||||
if ( µb.canUseShortcuts === false ) {
|
||||
return callback([]);
|
||||
}
|
||||
|
||||
vAPI.commands.getAll(commands => {
|
||||
let response = [];
|
||||
for ( let command of commands ) {
|
||||
let desc = command.description;
|
||||
let match = /^__MSG_(.+?)__$/.exec(desc);
|
||||
if ( match !== null ) {
|
||||
desc = i18n$(match[1]);
|
||||
}
|
||||
if ( desc === '' ) { continue; }
|
||||
command.description = desc;
|
||||
response.push(command);
|
||||
}
|
||||
callback(response);
|
||||
});
|
||||
};
|
||||
|
||||
const setShortcut = function(details) {
|
||||
if ( µb.canUpdateShortcuts === false ) { return; }
|
||||
if ( details.shortcut === undefined ) {
|
||||
vAPI.commands.reset(details.name);
|
||||
µb.commandShortcuts.delete(details.name);
|
||||
} else {
|
||||
vAPI.commands.update({ name: details.name, shortcut: details.shortcut });
|
||||
µb.commandShortcuts.set(details.name, details.shortcut);
|
||||
}
|
||||
vAPI.storage.set({ commandShortcuts: Array.from(µb.commandShortcuts) });
|
||||
};
|
||||
|
||||
// Support
|
||||
const getSupportData = async function() {
|
||||
const diffArrays = function(modified, original) {
|
||||
|
@ -1506,9 +1472,6 @@ const onMessage = function(request, sender, callback) {
|
|||
callback(localData);
|
||||
});
|
||||
|
||||
case 'getShortcuts':
|
||||
return getShortcuts(callback);
|
||||
|
||||
case 'getSupportData': {
|
||||
getSupportData().then(response => {
|
||||
callback(response);
|
||||
|
@ -1536,7 +1499,6 @@ const onMessage = function(request, sender, callback) {
|
|||
switch ( request.what ) {
|
||||
case 'dashboardConfig':
|
||||
response = {
|
||||
canUpdateShortcuts: µb.canUpdateShortcuts,
|
||||
noDashboard: µb.noDashboard,
|
||||
};
|
||||
break;
|
||||
|
@ -1597,10 +1559,6 @@ const onMessage = function(request, sender, callback) {
|
|||
resetUserData();
|
||||
break;
|
||||
|
||||
case 'setShortcut':
|
||||
setShortcut(request);
|
||||
break;
|
||||
|
||||
case 'writeHiddenSettings':
|
||||
µb.changeHiddenSettings(µb.hiddenSettingsFromString(request.content));
|
||||
break;
|
||||
|
|
|
@ -19,12 +19,11 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import punycode from '../lib/punycode.js';
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -110,7 +109,7 @@ const cachePopupData = function(data) {
|
|||
const hashFromPopupData = function(reset = false) {
|
||||
// It makes no sense to offer to refresh the behind-the-scene scope
|
||||
if ( popupData.pageHostname === 'behind-the-scene' ) {
|
||||
document.body.classList.remove('needReload');
|
||||
dom.cl.remove(dom.body, 'needReload');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -122,17 +121,19 @@ const hashFromPopupData = function(reset = false) {
|
|||
hasher.push(rule);
|
||||
}
|
||||
hasher.sort();
|
||||
hasher.push(uDom('body').hasClass('off'));
|
||||
hasher.push(uDom.nodeFromId('no-large-media').classList.contains('on'));
|
||||
hasher.push(uDom.nodeFromId('no-cosmetic-filtering').classList.contains('on'));
|
||||
hasher.push(uDom.nodeFromId('no-remote-fonts').classList.contains('on'));
|
||||
hasher.push(uDom.nodeFromId('no-scripting').classList.contains('on'));
|
||||
hasher.push(
|
||||
dom.cl.has('body', 'off'),
|
||||
dom.cl.has('#no-large-media', 'on'),
|
||||
dom.cl.has('#no-cosmetic-filtering', 'on'),
|
||||
dom.cl.has('#no-remote-fonts', 'on'),
|
||||
dom.cl.has('#no-scripting', 'on')
|
||||
);
|
||||
|
||||
const hash = hasher.join('');
|
||||
if ( reset ) {
|
||||
cachedPopupHash = hash;
|
||||
}
|
||||
document.body.classList.toggle('needReload', hash !== cachedPopupHash);
|
||||
dom.cl.toggle(dom.body, 'needReload', hash !== cachedPopupHash);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -199,20 +200,18 @@ const safePunycodeToUnicode = function(hn) {
|
|||
const updateFirewallCellCount = function(cells, allowed, blocked) {
|
||||
for ( const cell of cells ) {
|
||||
if ( gtz(allowed) ) {
|
||||
cell.setAttribute(
|
||||
'data-acount',
|
||||
dom.attr(cell, 'data-acount',
|
||||
Math.min(Math.ceil(Math.log(allowed + 1) / Math.LN10), 3)
|
||||
);
|
||||
} else {
|
||||
cell.setAttribute('data-acount', '0');
|
||||
dom.attr(cell, 'data-acount', '0');
|
||||
}
|
||||
if ( gtz(blocked) ) {
|
||||
cell.setAttribute(
|
||||
'data-bcount',
|
||||
dom.attr(cell, 'data-bcount',
|
||||
Math.min(Math.ceil(Math.log(blocked + 1) / Math.LN10), 3)
|
||||
);
|
||||
} else {
|
||||
cell.setAttribute('data-bcount', '0');
|
||||
dom.attr(cell, 'data-bcount', '0');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -224,12 +223,12 @@ const updateFirewallCellRule = function(cells, scope, des, type, rule) {
|
|||
|
||||
for ( const cell of cells ) {
|
||||
if ( ruleParts === undefined ) {
|
||||
cell.removeAttribute('class');
|
||||
dom.attr(cell, 'class', null);
|
||||
continue;
|
||||
}
|
||||
|
||||
const action = updateFirewallCellRule.actionNames[ruleParts[3]];
|
||||
cell.setAttribute('class', `${action}Rule`);
|
||||
dom.attr(cell, 'class', `${action}Rule`);
|
||||
|
||||
// Use dark shade visual cue if the rule is specific to the cell.
|
||||
if (
|
||||
|
@ -238,7 +237,7 @@ const updateFirewallCellRule = function(cells, scope, des, type, rule) {
|
|||
(ruleParts[0] === scopeToSrcHostnameMap[scope])
|
||||
|
||||
) {
|
||||
cell.classList.add('ownRule');
|
||||
dom.cl.add(cell, 'ownRule');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -249,26 +248,26 @@ updateFirewallCellRule.actionNames = { '1': 'block', '2': 'allow', '3': 'noop' }
|
|||
|
||||
const updateAllFirewallCells = function(doRules = true, doCounts = true) {
|
||||
const { pageDomain } = popupData;
|
||||
const rowContainer = document.getElementById('firewall');
|
||||
const rows = rowContainer.querySelectorAll('#firewall > [data-des][data-type]');
|
||||
const rowContainer = qs$('#firewall');
|
||||
const rows = qsa$(rowContainer, '#firewall > [data-des][data-type]');
|
||||
|
||||
let a1pScript = 0, b1pScript = 0;
|
||||
let a3pScript = 0, b3pScript = 0;
|
||||
let a3pFrame = 0, b3pFrame = 0;
|
||||
|
||||
for ( const row of rows ) {
|
||||
const des = row.getAttribute('data-des');
|
||||
const type = row.getAttribute('data-type');
|
||||
const des = dom.attr(row, 'data-des');
|
||||
const type = dom.attr(row, 'data-type');
|
||||
if ( doRules ) {
|
||||
updateFirewallCellRule(
|
||||
row.querySelectorAll(`:scope > span[data-src="/"]`),
|
||||
qsa$(row, ':scope > span[data-src="/"]'),
|
||||
'/',
|
||||
des,
|
||||
type,
|
||||
popupData.firewallRules[`/ ${des} ${type}`]
|
||||
);
|
||||
}
|
||||
const cells = row.querySelectorAll(`:scope > span[data-src="."]`);
|
||||
const cells = qsa$(row, ':scope > span[data-src="."]');
|
||||
if ( doRules ) {
|
||||
updateFirewallCellRule(
|
||||
cells,
|
||||
|
@ -301,17 +300,15 @@ const updateAllFirewallCells = function(doRules = true, doCounts = true) {
|
|||
|
||||
if ( doCounts ) {
|
||||
const fromType = type =>
|
||||
document.querySelectorAll(
|
||||
`#firewall > [data-des="*"][data-type="${type}"] > [data-src="."]`
|
||||
);
|
||||
qsa$(`#firewall > [data-des="*"][data-type="${type}"] > [data-src="."]`);
|
||||
updateFirewallCellCount(fromType('1p-script'), a1pScript, b1pScript);
|
||||
updateFirewallCellCount(fromType('3p-script'), a3pScript, b3pScript);
|
||||
rowContainer.classList.toggle('has3pScript', a3pScript !== 0 || b3pScript !== 0);
|
||||
dom.cl.toggle(rowContainer, 'has3pScript', a3pScript !== 0 || b3pScript !== 0);
|
||||
updateFirewallCellCount(fromType('3p-frame'), a3pFrame, b3pFrame);
|
||||
rowContainer.classList.toggle('has3pFrame', a3pFrame !== 0 || b3pFrame !== 0);
|
||||
dom.cl.toggle(rowContainer, 'has3pFrame', a3pFrame !== 0 || b3pFrame !== 0);
|
||||
}
|
||||
|
||||
document.body.classList.toggle('needSave', popupData.matrixIsDirty === true);
|
||||
dom.cl.toggle(dom.body, 'needSave', popupData.matrixIsDirty === true);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -346,8 +343,8 @@ const expandHostnameStats = ( ) => {
|
|||
const buildAllFirewallRows = function() {
|
||||
// Do this before removing the rows
|
||||
if ( dfHotspots === null ) {
|
||||
dfHotspots = uDom.nodeFromId('actionSelector');
|
||||
dfHotspots.addEventListener('click', setFirewallRuleHandler);
|
||||
dfHotspots = qs$('#actionSelector');
|
||||
dom.on(dfHotspots, 'click', setFirewallRuleHandler);
|
||||
}
|
||||
dfHotspots.remove();
|
||||
|
||||
|
@ -355,23 +352,19 @@ const buildAllFirewallRows = function() {
|
|||
expandHostnameStats();
|
||||
|
||||
// Update incrementally: reuse existing rows if possible.
|
||||
const rowContainer = document.getElementById('firewall');
|
||||
const rowContainer = qs$('#firewall');
|
||||
const toAppend = document.createDocumentFragment();
|
||||
const rowTemplate = document.querySelector(
|
||||
'#templates > div[data-des=""][data-type="*"]'
|
||||
);
|
||||
const rowTemplate = qs$('#templates > div[data-des=""][data-type="*"]');
|
||||
const { cnameMap, hostnameDict, pageDomain, pageHostname } = popupData;
|
||||
|
||||
let row = rowContainer.querySelector(
|
||||
'div[data-des="*"][data-type="3p-frame"] + div'
|
||||
);
|
||||
let row = qs$(rowContainer, 'div[data-des="*"][data-type="3p-frame"] + div');
|
||||
|
||||
for ( const des of allHostnameRows ) {
|
||||
if ( row === null ) {
|
||||
row = rowTemplate.cloneNode(true);
|
||||
row = dom.clone(rowTemplate);
|
||||
toAppend.appendChild(row);
|
||||
}
|
||||
row.setAttribute('data-des', des);
|
||||
dom.attr(row, 'data-des', des);
|
||||
|
||||
const hnDetails = hostnameDict[des] || {};
|
||||
const isDomain = des === hnDetails.domain;
|
||||
|
@ -381,13 +374,13 @@ const buildAllFirewallRows = function() {
|
|||
const isPunycoded = prettyDomainName !== des;
|
||||
|
||||
if ( isDomain && row.childElementCount < 4 ) {
|
||||
row.append(row.children[2].cloneNode(true));
|
||||
row.append(dom.clone(row.children[2]));
|
||||
} else if ( isDomain === false && row.childElementCount === 4 ) {
|
||||
row.children[3].remove();
|
||||
}
|
||||
|
||||
const span = row.querySelector('span:first-of-type');
|
||||
span.querySelector(':scope > span > span').textContent = prettyDomainName;
|
||||
const span = qs$(row, 'span:first-of-type');
|
||||
dom.text(qs$(span, ':scope > span > span'), prettyDomainName);
|
||||
|
||||
const classList = row.classList;
|
||||
|
||||
|
@ -401,7 +394,7 @@ const buildAllFirewallRows = function() {
|
|||
) {
|
||||
desExtra = des;
|
||||
}
|
||||
span.querySelector('sub').textContent = desExtra;
|
||||
dom.text(qs$(span, 'sub'), desExtra);
|
||||
|
||||
classList.toggle('isRootContext', des === pageHostname);
|
||||
classList.toggle('is3p', hnDetails.domain !== pageDomain);
|
||||
|
@ -435,10 +428,9 @@ const buildAllFirewallRows = function() {
|
|||
}
|
||||
|
||||
if ( dfPaneBuilt !== true && popupData.advancedUserEnabled ) {
|
||||
uDom('#firewall')
|
||||
.on('click', 'span[data-src]', unsetFirewallRuleHandler)
|
||||
.on('mouseenter', '[data-src]', mouseenterCellHandler)
|
||||
.on('mouseleave', '[data-src]', mouseleaveCellHandler);
|
||||
dom.on('#firewall', 'click', 'span[data-src]', unsetFirewallRuleHandler);
|
||||
dom.on('#firewall', 'mouseenter', 'span[data-src]', mouseenterCellHandler);
|
||||
dom.on('#firewall', 'mouseleave', 'span[data-src]', mouseleaveCellHandler);
|
||||
dfPaneBuilt = true;
|
||||
}
|
||||
|
||||
|
@ -507,32 +499,17 @@ const renderPrivacyExposure = function() {
|
|||
const summary = domainsHitStr
|
||||
.replace('{{count}}', touchedDomainCount.toLocaleString())
|
||||
.replace('{{total}}', allDomainCount.toLocaleString());
|
||||
uDom.nodeFromSelector(
|
||||
'[data-i18n^="popupDomainsConnected"] + span'
|
||||
).textContent = summary;
|
||||
dom.text('[data-i18n^="popupDomainsConnected"] + span', summary);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const updateHnSwitches = function() {
|
||||
uDom.nodeFromId('no-popups').classList.toggle(
|
||||
'on', popupData.noPopups === true
|
||||
);
|
||||
uDom.nodeFromId('no-large-media').classList.toggle(
|
||||
'on', popupData.noLargeMedia === true
|
||||
);
|
||||
uDom.nodeFromId('no-cosmetic-filtering').classList.toggle(
|
||||
'on',
|
||||
popupData.noCosmeticFiltering === true
|
||||
);
|
||||
uDom.nodeFromId('no-remote-fonts').classList.toggle(
|
||||
'on',
|
||||
popupData.noRemoteFonts === true
|
||||
);
|
||||
uDom.nodeFromId('no-scripting').classList.toggle(
|
||||
'on',
|
||||
popupData.noScripting === true
|
||||
);
|
||||
dom.cl.toggle('#no-popups', 'on', popupData.noPopups === true);
|
||||
dom.cl.toggle('#no-large-media', 'on', popupData.noLargeMedia === true);
|
||||
dom.cl.toggle('#no-cosmetic-filtering', 'on',popupData.noCosmeticFiltering === true);
|
||||
dom.cl.toggle('#no-remote-fonts', 'on', popupData.noRemoteFonts === true);
|
||||
dom.cl.toggle('#no-scripting', 'on', popupData.noScripting === true);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -546,26 +523,28 @@ const renderPopup = function() {
|
|||
|
||||
const isFiltering = popupData.netFilteringSwitch;
|
||||
|
||||
const body = document.body;
|
||||
body.classList.toggle('advancedUser', popupData.advancedUserEnabled === true);
|
||||
body.classList.toggle('off', popupData.pageURL === '' || isFiltering !== true);
|
||||
body.classList.toggle('needSave', popupData.matrixIsDirty === true);
|
||||
dom.cl.toggle(dom.body, 'advancedUser', popupData.advancedUserEnabled === true);
|
||||
dom.cl.toggle(dom.body, 'off', popupData.pageURL === '' || isFiltering !== true);
|
||||
dom.cl.toggle(dom.body, 'needSave', popupData.matrixIsDirty === true);
|
||||
|
||||
// The hostname information below the power switch
|
||||
{
|
||||
const [ elemHn, elemDn ] = uDom.nodeFromId('hostname').children;
|
||||
const [ elemHn, elemDn ] = qs$('#hostname').children;
|
||||
const { pageDomain, pageHostname } = popupData;
|
||||
if ( pageDomain !== '' ) {
|
||||
elemDn.textContent = safePunycodeToUnicode(pageDomain);
|
||||
elemHn.textContent = pageHostname !== pageDomain
|
||||
dom.text(elemDn, safePunycodeToUnicode(pageDomain));
|
||||
dom.text(elemHn, pageHostname !== pageDomain
|
||||
? safePunycodeToUnicode(pageHostname.slice(0, -pageDomain.length - 1)) + '.'
|
||||
: '';
|
||||
: ''
|
||||
);
|
||||
} else {
|
||||
elemHn.textContent = elemDn.textContent = '';
|
||||
dom.text(elemDn, '');
|
||||
dom.text(elemHn, '');
|
||||
}
|
||||
}
|
||||
|
||||
uDom.nodeFromId('basicTools').classList.toggle(
|
||||
dom.cl.toggle(
|
||||
'#basicTools',
|
||||
'canPick',
|
||||
popupData.canElementPicker === true && isFiltering
|
||||
);
|
||||
|
@ -586,7 +565,7 @@ const renderPopup = function() {
|
|||
text = statsStr.replace('{{count}}', formatNumber(blocked))
|
||||
.replace('{{percent}}', formatNumber(Math.floor(blocked * 100 / total)));
|
||||
}
|
||||
uDom.nodeFromSelector('[data-i18n^="popupBlockedOnThisPage"] + span').textContent = text;
|
||||
dom.text('[data-i18n^="popupBlockedOnThisPage"] + span', text);
|
||||
|
||||
blocked = popupData.globalBlockedRequestCount;
|
||||
total = popupData.globalAllowedRequestCount + blocked;
|
||||
|
@ -596,7 +575,7 @@ const renderPopup = function() {
|
|||
text = statsStr.replace('{{count}}', formatNumber(blocked))
|
||||
.replace('{{percent}}', formatNumber(Math.floor(blocked * 100 / total)));
|
||||
}
|
||||
uDom.nodeFromSelector('[data-i18n^="popupBlockedSinceInstall"] + span').textContent = text;
|
||||
dom.text('[data-i18n^="popupBlockedSinceInstall"] + span', text);
|
||||
|
||||
// This will collate all domains, touched or not
|
||||
renderPrivacyExposure();
|
||||
|
@ -606,24 +585,27 @@ const renderPopup = function() {
|
|||
|
||||
// Report popup count on badge
|
||||
total = popupData.popupBlockedCount;
|
||||
uDom.nodeFromSelector('#no-popups .fa-icon-badge')
|
||||
.textContent = total ? Math.min(total, 99).toLocaleString() : '';
|
||||
dom.text(
|
||||
'#no-popups .fa-icon-badge',
|
||||
total ? Math.min(total, 99).toLocaleString() : ''
|
||||
);
|
||||
|
||||
// Report large media count on badge
|
||||
total = popupData.largeMediaCount;
|
||||
uDom.nodeFromSelector('#no-large-media .fa-icon-badge')
|
||||
.textContent = total ? Math.min(total, 99).toLocaleString() : '';
|
||||
dom.text(
|
||||
'#no-large-media .fa-icon-badge',
|
||||
total ? Math.min(total, 99).toLocaleString() : ''
|
||||
);
|
||||
|
||||
// Report remote font count on badge
|
||||
total = popupData.remoteFontCount;
|
||||
uDom.nodeFromSelector('#no-remote-fonts .fa-icon-badge')
|
||||
.textContent = total ? Math.min(total, 99).toLocaleString() : '';
|
||||
|
||||
document.documentElement.classList.toggle(
|
||||
'colorBlind',
|
||||
popupData.colorBlindFriendly === true
|
||||
dom.text(
|
||||
'#no-remote-fonts .fa-icon-badge',
|
||||
total ? Math.min(total, 99).toLocaleString() : ''
|
||||
);
|
||||
|
||||
dom.cl.toggle(dom.html, 'colorBlind', popupData.colorBlindFriendly === true);
|
||||
|
||||
setGlobalExpand(popupData.firewallPaneMinimized === false, true);
|
||||
|
||||
// Build dynamic filtering pane only if in use
|
||||
|
@ -642,14 +624,14 @@ const renderPopup = function() {
|
|||
const renderTooltips = function(selector) {
|
||||
for ( const [ key, details ] of tooltipTargetSelectors ) {
|
||||
if ( selector !== undefined && key !== selector ) { continue; }
|
||||
const elem = uDom.nodeFromSelector(key);
|
||||
const elem = qs$(key);
|
||||
if ( elem.hasAttribute('title') === false ) { continue; }
|
||||
const text = i18n$(
|
||||
details.i18n +
|
||||
(uDom.nodeFromSelector(details.state) === null ? '1' : '2')
|
||||
(qs$(details.state) === null ? '1' : '2')
|
||||
);
|
||||
elem.setAttribute('aria-label', text);
|
||||
elem.setAttribute('title', text);
|
||||
dom.attr(elem, 'aria-label', text);
|
||||
dom.attr(elem, 'title', text);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -705,47 +687,45 @@ const tooltipTargetSelectors = new Map([
|
|||
let renderOnce = function() {
|
||||
renderOnce = function(){};
|
||||
|
||||
const body = document.body;
|
||||
|
||||
if ( popupData.fontSize !== popupFontSize ) {
|
||||
popupFontSize = popupData.fontSize;
|
||||
if ( popupFontSize !== 'unset' ) {
|
||||
body.style.setProperty('--font-size', popupFontSize);
|
||||
dom.body.style.setProperty('--font-size', popupFontSize);
|
||||
vAPI.localStorage.setItem('popupFontSize', popupFontSize);
|
||||
} else {
|
||||
body.style.removeProperty('--font-size');
|
||||
dom.body.style.removeProperty('--font-size');
|
||||
vAPI.localStorage.removeItem('popupFontSize');
|
||||
}
|
||||
}
|
||||
|
||||
uDom.nodeFromId('version').textContent = popupData.appVersion;
|
||||
dom.text('#version', popupData.appVersion);
|
||||
|
||||
sectionBitsToAttribute(computedSections());
|
||||
|
||||
if ( popupData.uiPopupConfig !== undefined ) {
|
||||
document.body.setAttribute('data-ui', popupData.uiPopupConfig);
|
||||
dom.attr(dom.body, 'data-ui', popupData.uiPopupConfig);
|
||||
}
|
||||
|
||||
body.classList.toggle('no-tooltips', popupData.tooltipsDisabled === true);
|
||||
dom.cl.toggle(dom.body, 'no-tooltips', popupData.tooltipsDisabled === true);
|
||||
if ( popupData.tooltipsDisabled === true ) {
|
||||
uDom('[title]').removeAttr('title');
|
||||
dom.attr('[title]', 'title', null);
|
||||
}
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/22
|
||||
if ( popupData.advancedUserEnabled !== true ) {
|
||||
uDom('#firewall [title][data-src]').removeAttr('title');
|
||||
dom.attr('#firewall [title][data-src]', 'title', null);
|
||||
}
|
||||
|
||||
// This must be done the firewall is populated
|
||||
if ( popupData.popupPanelHeightMode === 1 ) {
|
||||
body.classList.add('vMin');
|
||||
dom.cl.add(dom.body, 'vMin');
|
||||
}
|
||||
|
||||
// Prevent non-advanced user opting into advanced user mode from harming
|
||||
// themselves by disabling by default features generally suitable to
|
||||
// filter list maintainers and actual advanced users.
|
||||
if ( popupData.godMode ) {
|
||||
body.classList.add('godMode');
|
||||
dom.cl.add(dom.body, 'godMode');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -758,15 +738,15 @@ const renderPopupLazy = (( ) => {
|
|||
// Launch potentially expensive hidden elements-counting scriptlet on
|
||||
// demand only.
|
||||
{
|
||||
const sw = uDom.nodeFromId('no-cosmetic-filtering');
|
||||
const badge = sw.querySelector(':scope .fa-icon-badge');
|
||||
badge.textContent = '\u22EF';
|
||||
const sw = qs$('#no-cosmetic-filtering');
|
||||
const badge = qs$(sw, ':scope .fa-icon-badge');
|
||||
dom.text(badge, '\u22EF');
|
||||
|
||||
const render = ( ) => {
|
||||
if ( mustRenderCosmeticFilteringBadge === false ) { return; }
|
||||
mustRenderCosmeticFilteringBadge = false;
|
||||
if ( sw.classList.contains('hnSwitchBusy') ) { return; }
|
||||
sw.classList.add('hnSwitchBusy');
|
||||
if ( dom.cl.has(sw, 'hnSwitchBusy') ) { return; }
|
||||
dom.cl.add(sw, 'hnSwitchBusy');
|
||||
messaging.send('popupPanel', {
|
||||
what: 'getHiddenElementCount',
|
||||
tabId: popupData.tabId,
|
||||
|
@ -779,12 +759,12 @@ const renderPopupLazy = (( ) => {
|
|||
} else {
|
||||
text = Math.min(count, 99).toLocaleString();
|
||||
}
|
||||
badge.textContent = text;
|
||||
sw.classList.remove('hnSwitchBusy');
|
||||
dom.text(badge, text);
|
||||
dom.cl.remove(sw, 'hnSwitchBusy');
|
||||
});
|
||||
};
|
||||
|
||||
sw.addEventListener('mouseenter', render, { passive: true });
|
||||
dom.on(sw, 'mouseenter', render, { passive: true });
|
||||
}
|
||||
|
||||
return async function() {
|
||||
|
@ -792,10 +772,10 @@ const renderPopupLazy = (( ) => {
|
|||
what: 'getScriptCount',
|
||||
tabId: popupData.tabId,
|
||||
});
|
||||
uDom.nodeFromSelector('#no-scripting .fa-icon-badge')
|
||||
.textContent = (count || 0) !== 0
|
||||
? Math.min(count, 99).toLocaleString()
|
||||
: '';
|
||||
dom.text(
|
||||
'#no-scripting .fa-icon-badge',
|
||||
(count || 0) !== 0 ? Math.min(count, 99).toLocaleString() : ''
|
||||
);
|
||||
mustRenderCosmeticFilteringBadge = true;
|
||||
};
|
||||
})();
|
||||
|
@ -808,7 +788,7 @@ const toggleNetFilteringSwitch = function(ev) {
|
|||
what: 'toggleNetFiltering',
|
||||
url: popupData.pageURL,
|
||||
scope: ev.ctrlKey || ev.metaKey ? 'page' : '',
|
||||
state: !uDom('body').toggleClass('off').hasClass('off'),
|
||||
state: dom.cl.toggle(dom.body, 'off') === false,
|
||||
tabId: popupData.tabId,
|
||||
});
|
||||
renderTooltips('#switch');
|
||||
|
@ -892,7 +872,7 @@ const gotoURL = function(ev) {
|
|||
|
||||
ev.preventDefault();
|
||||
|
||||
let url = this.getAttribute('href');
|
||||
let url = dom.attr(ev.target, 'href');
|
||||
if (
|
||||
url === 'logger-ui.html#_' &&
|
||||
typeof popupData.tabId === 'number'
|
||||
|
@ -984,14 +964,14 @@ const toggleSections = function(more) {
|
|||
}
|
||||
};
|
||||
|
||||
uDom('#moreButton').on('click', ( ) => { toggleSections(true); });
|
||||
uDom('#lessButton').on('click', ( ) => { toggleSections(false); });
|
||||
dom.on('#moreButton', 'click', ( ) => { toggleSections(true); });
|
||||
dom.on('#lessButton', 'click', ( ) => { toggleSections(false); });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const mouseenterCellHandler = function(ev) {
|
||||
const target = ev.target;
|
||||
if ( target.classList.contains('ownRule') ) { return; }
|
||||
if ( dom.cl.has(target, 'ownRule') ) { return; }
|
||||
target.appendChild(dfHotspots);
|
||||
};
|
||||
|
||||
|
@ -1038,9 +1018,9 @@ const unsetFirewallRuleHandler = function(ev) {
|
|||
const cell = ev.target;
|
||||
const row = cell.closest('[data-des]');
|
||||
setFirewallRule(
|
||||
cell.getAttribute('data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
row.getAttribute('data-des'),
|
||||
row.getAttribute('data-type'),
|
||||
dom.attr(cell, 'data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
dom.attr(row, 'data-des'),
|
||||
dom.attr(row, 'data-type'),
|
||||
0,
|
||||
ev.ctrlKey || ev.metaKey
|
||||
);
|
||||
|
@ -1063,9 +1043,9 @@ const setFirewallRuleHandler = function(ev) {
|
|||
action = 1;
|
||||
}
|
||||
setFirewallRule(
|
||||
cell.getAttribute('data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
row.getAttribute('data-des'),
|
||||
row.getAttribute('data-type'),
|
||||
dom.attr(cell, 'data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
dom.attr(row, 'data-des'),
|
||||
dom.attr(row, 'data-type'),
|
||||
action,
|
||||
ev.ctrlKey || ev.metaKey
|
||||
);
|
||||
|
@ -1093,19 +1073,15 @@ const reloadTab = function(ev) {
|
|||
hashFromPopupData(true);
|
||||
};
|
||||
|
||||
uDom('#refresh').on('click', reloadTab);
|
||||
dom.on('#refresh', 'click', reloadTab);
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/672
|
||||
document.addEventListener(
|
||||
'keydown',
|
||||
ev => {
|
||||
dom.on(document, 'keydown', ev => {
|
||||
if ( ev.code !== 'F5' ) { return; }
|
||||
reloadTab(ev);
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
}, { capture: true });
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -1130,11 +1106,11 @@ const saveExpandExceptions = function() {
|
|||
};
|
||||
|
||||
const setGlobalExpand = function(state, internal = false) {
|
||||
uDom('.expandException').removeClass('expandException');
|
||||
dom.cl.remove('.expandException', 'expandException');
|
||||
if ( state ) {
|
||||
uDom('#firewall').addClass('expanded');
|
||||
dom.cl.add('#firewall', 'expanded');
|
||||
} else {
|
||||
uDom('#firewall').removeClass('expanded');
|
||||
dom.cl.remove('#firewall', 'expanded');
|
||||
}
|
||||
if ( internal ) { return; }
|
||||
popupData.firewallPaneMinimized = !state;
|
||||
|
@ -1148,11 +1124,11 @@ const setGlobalExpand = function(state, internal = false) {
|
|||
};
|
||||
|
||||
const setSpecificExpand = function(domain, state, internal = false) {
|
||||
const unodes = uDom(`[data-des="${domain}"],[data-des$=".${domain}"]`);
|
||||
const elems = qsa$(`[data-des="${domain}"],[data-des$=".${domain}"]`);
|
||||
if ( state ) {
|
||||
unodes.addClass('expandException');
|
||||
dom.cl.add(elems, 'expandException');
|
||||
} else {
|
||||
unodes.removeClass('expandException');
|
||||
dom.cl.remove(elems, 'expandException');
|
||||
}
|
||||
if ( internal ) { return; }
|
||||
if ( state ) {
|
||||
|
@ -1163,7 +1139,7 @@ const setSpecificExpand = function(domain, state, internal = false) {
|
|||
saveExpandExceptions();
|
||||
};
|
||||
|
||||
uDom('[data-i18n="popupAnyRulePrompt"]').on('click', ev => {
|
||||
dom.on('[data-i18n="popupAnyRulePrompt"]', 'click', ev => {
|
||||
// Special display mode: in its own tab/window, with no vertical restraint.
|
||||
// Useful to take snapshots of the whole list of domains -- example:
|
||||
// https://github.com/gorhill/uBlock/issues/736#issuecomment-178879944
|
||||
|
@ -1180,22 +1156,17 @@ uDom('[data-i18n="popupAnyRulePrompt"]').on('click', ev => {
|
|||
return;
|
||||
}
|
||||
|
||||
setGlobalExpand(
|
||||
uDom('#firewall').hasClass('expanded') === false
|
||||
);
|
||||
setGlobalExpand(dom.cl.has('#firewall', 'expanded') === false);
|
||||
});
|
||||
|
||||
uDom('#firewall').on(
|
||||
'click', '.isDomain[data-type="*"] > span:first-of-type',
|
||||
ev => {
|
||||
dom.on('#firewall', 'click', '.isDomain[data-type="*"] > span:first-of-type', ev => {
|
||||
const div = ev.target.closest('[data-des]');
|
||||
if ( div === null ) { return; }
|
||||
setSpecificExpand(
|
||||
div.getAttribute('data-des'),
|
||||
div.classList.contains('expandException') === false
|
||||
dom.attr(div, 'data-des'),
|
||||
dom.cl.has(div, 'expandException') === false
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -1205,13 +1176,13 @@ const saveFirewallRules = function() {
|
|||
srcHostname: popupData.pageHostname,
|
||||
desHostnames: popupData.hostnameDict,
|
||||
});
|
||||
document.body.classList.remove('needSave');
|
||||
dom.cl.remove(dom.body, 'needSave');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const revertFirewallRules = async function() {
|
||||
document.body.classList.remove('needSave');
|
||||
dom.cl.remove(dom.body, 'needSave');
|
||||
const response = await messaging.send('popupPanel', {
|
||||
what: 'revertFirewallRules',
|
||||
srcHostname: popupData.pageHostname,
|
||||
|
@ -1228,23 +1199,23 @@ const revertFirewallRules = async function() {
|
|||
|
||||
const toggleHostnameSwitch = async function(ev) {
|
||||
const target = ev.currentTarget;
|
||||
const switchName = target.getAttribute('id');
|
||||
const switchName = dom.attr(target, 'id');
|
||||
if ( !switchName ) { return; }
|
||||
// For touch displays, process click only if the switch is not "busy".
|
||||
if (
|
||||
vAPI.webextFlavor.soup.has('mobile') &&
|
||||
target.classList.contains('hnSwitchBusy')
|
||||
dom.cl.has(target, 'hnSwitchBusy')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
target.classList.toggle('on');
|
||||
dom.cl.toggle(target, 'on');
|
||||
renderTooltips(`#${switchName}`);
|
||||
|
||||
const response = await messaging.send('popupPanel', {
|
||||
what: 'toggleHostnameSwitch',
|
||||
name: switchName,
|
||||
hostname: popupData.pageHostname,
|
||||
state: target.classList.contains('on'),
|
||||
state: dom.cl.has(target, 'on'),
|
||||
tabId: popupData.tabId,
|
||||
persist: ev.ctrlKey || ev.metaKey,
|
||||
});
|
||||
|
@ -1252,7 +1223,7 @@ const toggleHostnameSwitch = async function(ev) {
|
|||
cachePopupData(response);
|
||||
hashFromPopupData();
|
||||
|
||||
document.body.classList.toggle('needSave', popupData.matrixIsDirty === true);
|
||||
dom.cl.toggle(dom.body, 'needSave', popupData.matrixIsDirty === true);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -1268,7 +1239,7 @@ const toggleHostnameSwitch = async function(ev) {
|
|||
let eventCount = 0;
|
||||
let eventTime = 0;
|
||||
|
||||
document.addEventListener('keydown', ev => {
|
||||
dom.on(document, 'keydown', ev => {
|
||||
if ( ev.key !== 'Control' ) {
|
||||
eventCount = 0;
|
||||
return;
|
||||
|
@ -1282,7 +1253,7 @@ const toggleHostnameSwitch = async function(ev) {
|
|||
eventTime = now;
|
||||
if ( eventCount < 2 ) { return; }
|
||||
eventCount = 0;
|
||||
document.body.classList.toggle('godMode');
|
||||
dom.cl.toggle(dom.body, 'godMode');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1383,42 +1354,41 @@ const getPopupData = async function(tabId, first = false) {
|
|||
// Use a tolerance proportional to the sum of the width of the panes
|
||||
// when testing against viewport width.
|
||||
const checkViewport = async function() {
|
||||
const root = document.querySelector(':root');
|
||||
if (
|
||||
root.classList.contains('mobile') ||
|
||||
dom.cl.has(dom.root, 'mobile') ||
|
||||
selfURL.searchParams.get('portrait')
|
||||
) {
|
||||
root.classList.add('portrait');
|
||||
} else if ( root.classList.contains('desktop') ) {
|
||||
dom.cl.add(dom.root, 'portrait');
|
||||
} else if ( dom.cl.has(dom.root, 'desktop') ) {
|
||||
await nextFrames(4);
|
||||
const main = document.getElementById('main');
|
||||
const firewall = document.getElementById('firewall');
|
||||
const main = qs$('#main');
|
||||
const firewall = qs$('#firewall');
|
||||
const minWidth = (main.offsetWidth + firewall.offsetWidth) / 1.1;
|
||||
if (
|
||||
selfURL.searchParams.get('portrait') ||
|
||||
window.innerWidth < minWidth
|
||||
) {
|
||||
root.classList.add('portrait');
|
||||
dom.cl.add(dom.root, 'portrait');
|
||||
}
|
||||
}
|
||||
if ( root.classList.contains('portrait') ) {
|
||||
const panes = document.getElementById('panes');
|
||||
const sticky = document.getElementById('sticky');
|
||||
if ( dom.cl.has(dom.root, 'portrait') ) {
|
||||
const panes = qs$('#panes');
|
||||
const sticky = qs$('#sticky');
|
||||
const stickyParent = sticky.parentElement;
|
||||
if ( stickyParent !== panes ) {
|
||||
panes.prepend(sticky);
|
||||
}
|
||||
}
|
||||
if ( selfURL.searchParams.get('intab') !== null ) {
|
||||
root.classList.add('intab');
|
||||
dom.cl.add(dom.root, 'intab');
|
||||
}
|
||||
await nextFrames(1);
|
||||
document.body.classList.remove('loading');
|
||||
dom.cl.remove(dom.body, 'loading');
|
||||
};
|
||||
|
||||
getPopupData(tabId, true).then(( ) => {
|
||||
if ( document.readyState !== 'complete' ) {
|
||||
self.addEventListener('load', ( ) => { checkViewport(); }, { once: true });
|
||||
dom.on(self, 'load', ( ) => { checkViewport(); }, { once: true });
|
||||
} else {
|
||||
checkViewport();
|
||||
}
|
||||
|
@ -1427,27 +1397,25 @@ const getPopupData = async function(tabId, first = false) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom('#switch').on('click', toggleNetFilteringSwitch);
|
||||
uDom('#gotoZap').on('click', gotoZap);
|
||||
uDom('#gotoPick').on('click', gotoPick);
|
||||
uDom('#gotoReport').on('click', gotoReport);
|
||||
uDom('.hnSwitch').on('click', ev => { toggleHostnameSwitch(ev); });
|
||||
uDom('#saveRules').on('click', saveFirewallRules);
|
||||
uDom('#revertRules').on('click', ( ) => { revertFirewallRules(); });
|
||||
uDom('a[href]').on('click', gotoURL);
|
||||
dom.on('#switch', 'click', toggleNetFilteringSwitch);
|
||||
dom.on('#gotoZap', 'click', gotoZap);
|
||||
dom.on('#gotoPick', 'click', gotoPick);
|
||||
dom.on('#gotoReport', 'click', gotoReport);
|
||||
dom.on('.hnSwitch', 'click', ev => { toggleHostnameSwitch(ev); });
|
||||
dom.on('#saveRules', 'click', saveFirewallRules);
|
||||
dom.on('#revertRules', 'click', ( ) => { revertFirewallRules(); });
|
||||
dom.on('a[href]', 'click', gotoURL);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Toggle emphasis of rows with[out] 3rd-party scripts/frames
|
||||
document.querySelector('#firewall > [data-type="3p-script"] .filter')
|
||||
.addEventListener('click', ( ) => {
|
||||
document.getElementById('firewall').classList.toggle('show3pScript');
|
||||
});
|
||||
dom.on('#firewall > [data-type="3p-script"] .filter', 'click', ( ) => {
|
||||
dom.cl.toggle('#firewall', 'show3pScript');
|
||||
});
|
||||
|
||||
// Toggle visibility of rows with[out] 3rd-party frames
|
||||
document.querySelector('#firewall > [data-type="3p-frame"] .filter')
|
||||
.addEventListener('click', ( ) => {
|
||||
document.getElementById('firewall').classList.toggle('show3pFrame');
|
||||
});
|
||||
dom.on('#firewall > [data-type="3p-frame"] .filter', 'click', ( ) => {
|
||||
dom.cl.toggle('#firewall', 'show3pFrame');
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -113,6 +113,16 @@ const getElementBoundingClientRect = function(elem) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const elementsFromPoint = function(parent, x, y) {
|
||||
const elems = parent.elementsFromPoint(x, y);
|
||||
if ( elems.length !== 0 && elems[0].shadowRoot !== null ) {
|
||||
return elementsFromPoint(elems[0].shadowRoot, x, y);
|
||||
}
|
||||
return elems;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const highlightElements = function(elems, force) {
|
||||
// To make mouse move handler more efficient
|
||||
if (
|
||||
|
@ -554,11 +564,11 @@ const filtersFrom = function(x, y) {
|
|||
// Network filter candidates from all other elements found at [x,y].
|
||||
// https://www.reddit.com/r/uBlockOrigin/comments/qmjk36/
|
||||
// Extract network candidates first.
|
||||
if ( typeof x === 'number' ) {
|
||||
const magicAttr = `${vAPI.sessionId}-clickblind`;
|
||||
pickerRoot.setAttribute(magicAttr, '');
|
||||
const elems = document.elementsFromPoint(x, y);
|
||||
const elems = elementsFromPoint(document, x, y);
|
||||
pickerRoot.removeAttribute(magicAttr);
|
||||
if ( typeof x === 'number' ) {
|
||||
for ( const elem of elems ) {
|
||||
netFilterFromElement(elem);
|
||||
}
|
||||
|
@ -570,11 +580,14 @@ const filtersFrom = function(x, y) {
|
|||
// https://github.com/gorhill/uBlock/issues/2519
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/17
|
||||
// Prepend `body` if full selector is ambiguous.
|
||||
let elem = first;
|
||||
while ( elem && elem !== document.body ) {
|
||||
for ( const elem of elems ) {
|
||||
cosmeticFilterFromElement(elem);
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
//let elem = first;
|
||||
//while ( elem && elem !== document.body ) {
|
||||
// cosmeticFilterFromElement(elem);
|
||||
// elem = elem.parentNode;
|
||||
//}
|
||||
// The body tag is needed as anchor only when the immediate child
|
||||
// uses `nth-of-type`.
|
||||
let i = cosmeticFilterCandidates.length;
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$, qsa$ } from './dom.js';
|
||||
import { setAccentColor, setTheme } from './theme.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -84,7 +84,7 @@ const handleImportFilePicker = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const startImportFilePicker = function() {
|
||||
const input = document.getElementById('restoreFilePicker');
|
||||
const input = qs$('#restoreFilePicker');
|
||||
// Reset to empty string, this will ensure an change event is properly
|
||||
// triggered if the user pick a file, even if it is the same as the last
|
||||
// one picked.
|
||||
|
@ -134,10 +134,12 @@ const onLocalDataReceived = function(details) {
|
|||
v = '?';
|
||||
unit = '';
|
||||
}
|
||||
uDom.nodeFromId('storageUsed').textContent =
|
||||
dom.text(
|
||||
'#storageUsed',
|
||||
i18n$('storageUsed')
|
||||
.replace('{{value}}', v.toLocaleString(undefined, { maximumSignificantDigits: 3 }))
|
||||
.replace('{{unit}}', unit && i18n$(unit) || '');
|
||||
.replace('{{unit}}', unit && i18n$(unit) || '')
|
||||
);
|
||||
|
||||
const timeOptions = {
|
||||
weekday: 'long',
|
||||
|
@ -153,7 +155,7 @@ const onLocalDataReceived = function(details) {
|
|||
if ( lastBackupFile !== '' ) {
|
||||
const dt = new Date(details.lastBackupTime);
|
||||
const text = i18n$('settingsLastBackupPrompt');
|
||||
const node = uDom.nodeFromId('settingsLastBackupPrompt');
|
||||
const node = qs$('#settingsLastBackupPrompt');
|
||||
node.textContent = text + '\xA0' + dt.toLocaleString('fullwide', timeOptions);
|
||||
node.style.display = '';
|
||||
}
|
||||
|
@ -162,19 +164,19 @@ const onLocalDataReceived = function(details) {
|
|||
if ( lastRestoreFile !== '' ) {
|
||||
const dt = new Date(details.lastRestoreTime);
|
||||
const text = i18n$('settingsLastRestorePrompt');
|
||||
const node = uDom.nodeFromId('settingsLastRestorePrompt');
|
||||
const node = qs$('#settingsLastRestorePrompt');
|
||||
node.textContent = text + '\xA0' + dt.toLocaleString('fullwide', timeOptions);
|
||||
node.style.display = '';
|
||||
}
|
||||
|
||||
if ( details.cloudStorageSupported === false ) {
|
||||
uDom('[data-setting-name="cloudStorageEnabled"]').attr('disabled', '');
|
||||
dom.attr('[data-setting-name="cloudStorageEnabled"]', 'disabled', '');
|
||||
}
|
||||
|
||||
if ( details.privacySettingsSupported === false ) {
|
||||
uDom('[data-setting-name="prefetchingDisabled"]').attr('disabled', '');
|
||||
uDom('[data-setting-name="hyperlinkAuditingDisabled"]').attr('disabled', '');
|
||||
uDom('[data-setting-name="webrtcIPAddressHidden"]').attr('disabled', '');
|
||||
dom.attr('[data-setting-name="prefetchingDisabled"]', 'disabled', '');
|
||||
dom.attr('[data-setting-name="hyperlinkAuditingDisabled"]', 'disabled', '');
|
||||
dom.attr('[data-setting-name="webrtcIPAddressHidden"]', 'disabled', '');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -192,9 +194,10 @@ const resetUserData = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const synchronizeDOM = function() {
|
||||
document.body.classList.toggle(
|
||||
dom.cl.toggle(
|
||||
dom.body,
|
||||
'advancedUser',
|
||||
uDom.nodeFromSelector('[data-setting-name="advancedUserEnabled"]').checked === true
|
||||
qs$('[data-setting-name="advancedUserEnabled"]').checked === true
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -210,13 +213,13 @@ const changeUserSettings = function(name, value) {
|
|||
// Maybe reflect some changes immediately
|
||||
switch ( name ) {
|
||||
case 'uiTheme':
|
||||
uDom.setTheme(value, true);
|
||||
setTheme(value, true);
|
||||
break;
|
||||
case 'uiAccentCustom':
|
||||
case 'uiAccentCustom0':
|
||||
uDom.setAccentColor(
|
||||
uDom.nodeFromSelector('[data-setting-name="uiAccentCustom"]').checked,
|
||||
uDom.nodeFromSelector('[data-setting-name="uiAccentCustom0"]').value,
|
||||
setAccentColor(
|
||||
qs$('[data-setting-name="uiAccentCustom"]').checked,
|
||||
qs$('[data-setting-name="uiAccentCustom0"]').value,
|
||||
true
|
||||
);
|
||||
break;
|
||||
|
@ -229,7 +232,7 @@ const changeUserSettings = function(name, value) {
|
|||
|
||||
const onValueChanged = function(ev) {
|
||||
const input = ev.target;
|
||||
const name = this.getAttribute('data-setting-name');
|
||||
const name = dom.attr(input, 'data-setting-name');
|
||||
let value = input.value;
|
||||
// Maybe sanitize value
|
||||
switch ( name ) {
|
||||
|
@ -251,36 +254,36 @@ const onValueChanged = function(ev) {
|
|||
// TODO: use data-* to declare simple settings
|
||||
|
||||
const onUserSettingsReceived = function(details) {
|
||||
const checkboxes = document.querySelectorAll('[data-setting-type="bool"]');
|
||||
const checkboxes = qsa$('[data-setting-type="bool"]');
|
||||
for ( const checkbox of checkboxes ) {
|
||||
const name = checkbox.getAttribute('data-setting-name') || '';
|
||||
const name = dom.attr(checkbox, 'data-setting-name') || '';
|
||||
if ( details[name] === undefined ) {
|
||||
checkbox.closest('.checkbox').setAttribute('disabled', '');
|
||||
checkbox.setAttribute('disabled', '');
|
||||
dom.attr(checkbox.closest('.checkbox'), 'disabled', '');
|
||||
dom.attr(checkbox, 'disabled', '');
|
||||
continue;
|
||||
}
|
||||
checkbox.checked = details[name] === true;
|
||||
checkbox.addEventListener('change', ( ) => {
|
||||
dom.on(checkbox, 'change', ( ) => {
|
||||
changeUserSettings(name, checkbox.checked);
|
||||
synchronizeDOM();
|
||||
});
|
||||
}
|
||||
|
||||
if ( details.canLeakLocalIPAddresses === true ) {
|
||||
uDom('[data-setting-name="webrtcIPAddressHidden"]')
|
||||
.ancestors('div.li')
|
||||
.css('display', '');
|
||||
qs$('[data-setting-name="webrtcIPAddressHidden"]')
|
||||
.closest('div.li')
|
||||
.style.display = '';
|
||||
}
|
||||
|
||||
uDom('[data-setting-type="value"]').forEach(function(uNode) {
|
||||
uNode.val(details[uNode.attr('data-setting-name')])
|
||||
.on('change', onValueChanged);
|
||||
qsa$('[data-setting-type="value"]').forEach(function(elem) {
|
||||
elem.value = details[dom.attr(elem, 'data-setting-name')];
|
||||
dom.on(elem, 'change', onValueChanged);
|
||||
});
|
||||
|
||||
uDom('#export').on('click', ( ) => { exportToFile(); });
|
||||
uDom('#import').on('click', startImportFilePicker);
|
||||
uDom('#reset').on('click', resetUserData);
|
||||
uDom('#restoreFilePicker').on('change', handleImportFilePicker);
|
||||
dom.on('#export', 'click', ( ) => { exportToFile(); });
|
||||
dom.on('#import', 'click', startImportFilePicker);
|
||||
dom.on('#reset', 'click', resetUserData);
|
||||
dom.on('#restoreFilePicker', 'change', handleImportFilePicker);
|
||||
|
||||
synchronizeDOM();
|
||||
};
|
||||
|
@ -296,9 +299,8 @@ vAPI.messaging.send('dashboard', { what: 'getLocalData' }).then(result => {
|
|||
});
|
||||
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/591
|
||||
document.querySelector(
|
||||
'[data-i18n-title="settingsAdvancedUserSettings"]'
|
||||
).addEventListener(
|
||||
dom.on(
|
||||
'[data-i18n-title="settingsAdvancedUserSettings"]',
|
||||
'click',
|
||||
self.uBlockDashboard.openOrSelectPage
|
||||
);
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2018-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(( ) => {
|
||||
|
||||
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/commands#Shortcut_values
|
||||
const validStatus0Keys = new Map([
|
||||
[ 'alt', 'Alt' ],
|
||||
[ 'control', 'Ctrl' ],
|
||||
]);
|
||||
const validStatus1Keys = new Map([
|
||||
[ 'a', 'A' ],
|
||||
[ 'b', 'B' ],
|
||||
[ 'c', 'C' ],
|
||||
[ 'd', 'D' ],
|
||||
[ 'e', 'E' ],
|
||||
[ 'f', 'F' ],
|
||||
[ 'g', 'G' ],
|
||||
[ 'h', 'H' ],
|
||||
[ 'i', 'I' ],
|
||||
[ 'j', 'J' ],
|
||||
[ 'k', 'K' ],
|
||||
[ 'l', 'L' ],
|
||||
[ 'm', 'M' ],
|
||||
[ 'n', 'N' ],
|
||||
[ 'o', 'O' ],
|
||||
[ 'p', 'P' ],
|
||||
[ 'q', 'Q' ],
|
||||
[ 'r', 'R' ],
|
||||
[ 's', 'S' ],
|
||||
[ 't', 'T' ],
|
||||
[ 'u', 'U' ],
|
||||
[ 'v', 'V' ],
|
||||
[ 'w', 'W' ],
|
||||
[ 'x', 'X' ],
|
||||
[ 'y', 'Y' ],
|
||||
[ 'z', 'Z' ],
|
||||
[ '0', '0' ],
|
||||
[ '1', '1' ],
|
||||
[ '2', '2' ],
|
||||
[ '3', '3' ],
|
||||
[ '4', '4' ],
|
||||
[ '5', '5' ],
|
||||
[ '6', '6' ],
|
||||
[ '7', '7' ],
|
||||
[ '8', '8' ],
|
||||
[ '9', '9' ],
|
||||
[ 'f1', 'F1' ],
|
||||
[ 'f2', 'F2' ],
|
||||
[ 'f3', 'F3' ],
|
||||
[ 'f4', 'F4' ],
|
||||
[ 'f5', 'F5' ],
|
||||
[ 'f6', 'F6' ],
|
||||
[ 'f7', 'F7' ],
|
||||
[ 'f8', 'F8' ],
|
||||
[ 'f9', 'F9' ],
|
||||
[ 'f10', 'F10' ],
|
||||
[ 'f11', 'F11' ],
|
||||
[ 'f12', 'F12' ],
|
||||
[ ' ', 'Space' ],
|
||||
[ ',', 'Comma' ],
|
||||
[ '.', 'Period' ],
|
||||
[ 'home', 'Home' ],
|
||||
[ 'end', 'End' ],
|
||||
[ 'pageup', 'PageUp' ],
|
||||
[ 'pagedown', 'PageDown' ],
|
||||
[ 'insert', 'Insert' ],
|
||||
[ 'delete', 'Delete' ],
|
||||
[ 'arrowup', 'Up' ],
|
||||
[ 'arrowdown', 'Down' ],
|
||||
[ 'arrowleft', 'Left' ],
|
||||
[ 'arrowright', 'Right' ],
|
||||
[ 'shift', 'Shift' ],
|
||||
]);
|
||||
|
||||
const commandNameFromElement = function(elem) {
|
||||
while ( elem !== null ) {
|
||||
const name = elem.getAttribute('data-name');
|
||||
if ( typeof name === 'string' && name !== '' ) { return name; }
|
||||
elem = elem.parentElement;
|
||||
}
|
||||
};
|
||||
|
||||
const captureShortcut = function(ev) {
|
||||
const input = ev.target;
|
||||
const name = commandNameFromElement(input);
|
||||
if ( name === undefined ) { return; }
|
||||
|
||||
const before = input.value;
|
||||
const after = new Set();
|
||||
let status = 0;
|
||||
|
||||
const updateCapturedShortcut = function() {
|
||||
return (input.value = Array.from(after).join('+'));
|
||||
};
|
||||
|
||||
const blurHandler = function() {
|
||||
input.removeEventListener('blur', blurHandler, true);
|
||||
input.removeEventListener('keydown', keydownHandler, true);
|
||||
input.removeEventListener('keyup', keyupHandler, true);
|
||||
if ( status === 2 ) {
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'setShortcut',
|
||||
name,
|
||||
shortcut: updateCapturedShortcut(),
|
||||
});
|
||||
} else {
|
||||
input.value = before;
|
||||
}
|
||||
};
|
||||
|
||||
const keydownHandler = function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
if ( ev.code === 'Escape' ) {
|
||||
input.blur();
|
||||
return;
|
||||
}
|
||||
if ( status === 0 ) {
|
||||
const keyName = validStatus0Keys.get(ev.key.toLowerCase());
|
||||
if ( keyName !== undefined ) {
|
||||
after.add(keyName);
|
||||
updateCapturedShortcut();
|
||||
status = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ( status === 1 ) {
|
||||
if ( ev.key === 'Shift' ) {
|
||||
after.add('Shift');
|
||||
updateCapturedShortcut();
|
||||
return;
|
||||
}
|
||||
let keyName = validStatus1Keys.get(ev.key.toLowerCase());
|
||||
if ( keyName !== undefined ) {
|
||||
after.add(keyName);
|
||||
updateCapturedShortcut();
|
||||
status = 2;
|
||||
input.blur();
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const keyupHandler = function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
if ( status !== 1 ) { return; }
|
||||
const keyName = validStatus0Keys.get(ev.key.toLowerCase());
|
||||
if ( keyName !== undefined && after.has(keyName) ) {
|
||||
after.clear();
|
||||
updateCapturedShortcut();
|
||||
status = 0;
|
||||
return;
|
||||
}
|
||||
if ( ev.key === 'Shift' ) {
|
||||
after.delete('Shift');
|
||||
updateCapturedShortcut();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
input.value = '';
|
||||
input.addEventListener('blur', blurHandler, true);
|
||||
input.addEventListener('keydown', keydownHandler, true);
|
||||
input.addEventListener('keyup', keyupHandler, true);
|
||||
};
|
||||
|
||||
const resetShortcut = function(ev) {
|
||||
const name = commandNameFromElement(ev.target);
|
||||
if ( name === undefined ) { return; }
|
||||
|
||||
const input = document.querySelector('[data-name="' + name + '"] input');
|
||||
if ( input === null ) { return; }
|
||||
input.value = '';
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'setShortcut',
|
||||
name,
|
||||
});
|
||||
};
|
||||
|
||||
const onShortcutsReady = function(commands) {
|
||||
if ( Array.isArray(commands) === false ) { return; }
|
||||
const template = document.querySelector('#templates .commandEntry');
|
||||
const tbody = document.querySelector('.commandEntries tbody');
|
||||
for ( const command of commands ) {
|
||||
if (
|
||||
typeof command.description !== 'string' ||
|
||||
command.description === '' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const tr = template.cloneNode(true);
|
||||
tr.setAttribute('data-name', command.name);
|
||||
tr.querySelector('.commandDesc').textContent = command.description;
|
||||
const input = tr.querySelector('.commandShortcut input');
|
||||
input.setAttribute('data-name', command.name);
|
||||
input.value = command.shortcut;
|
||||
input.addEventListener('focus', captureShortcut);
|
||||
tr.querySelector('.commandReset').addEventListener('click', resetShortcut);
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
};
|
||||
|
||||
vAPI.messaging.send('dashboard', {
|
||||
what: 'getShortcuts',
|
||||
}).then(commands => {
|
||||
onShortcutsReady(commands);
|
||||
});
|
||||
})();
|
|
@ -19,10 +19,12 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global CodeMirror, uBlockDashboard, uDom */
|
||||
/* global CodeMirror, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
let supportData;
|
||||
|
@ -130,10 +132,10 @@ function configToMarkdown(collapse = false) {
|
|||
}
|
||||
|
||||
function addDetailsToReportURL(id, collapse = false) {
|
||||
const elem = uDom.nodeFromId(id);
|
||||
const url = new URL(elem.getAttribute('data-url'));
|
||||
const elem = qs$(`#${id}`);
|
||||
const url = new URL(dom.attr(elem, 'data-url'));
|
||||
url.searchParams.set('configuration', configToMarkdown(collapse));
|
||||
elem.setAttribute('data-url', url);
|
||||
dom.attr(elem, 'data-url', url);
|
||||
}
|
||||
|
||||
function showData() {
|
||||
|
@ -180,21 +182,21 @@ const reportedPage = (( ) => {
|
|||
parsedURL.username = '';
|
||||
parsedURL.password = '';
|
||||
parsedURL.hash = '';
|
||||
const select = document.querySelector('select[name="url"]');
|
||||
select.options[0].textContent = parsedURL.href;
|
||||
const select = qs$('select[name="url"]');
|
||||
dom.text(select.options[0], parsedURL.href);
|
||||
if ( parsedURL.search !== '' ) {
|
||||
const option = document.createElement('option');
|
||||
const option = dom.create('option');
|
||||
parsedURL.search = '';
|
||||
option.textContent = parsedURL.href;
|
||||
dom.text(option, parsedURL.href);
|
||||
select.append(option);
|
||||
}
|
||||
if ( parsedURL.pathname !== '/' ) {
|
||||
const option = document.createElement('option');
|
||||
const option = dom.create('option');
|
||||
parsedURL.pathname = '';
|
||||
option.textContent = parsedURL.href;
|
||||
dom.text(option, parsedURL.href);
|
||||
select.append(option);
|
||||
}
|
||||
document.body.classList.add('filterIssue');
|
||||
dom.cl.add(dom.body, 'filterIssue');
|
||||
return {
|
||||
hostname: parsedURL.hostname.replace(/^(m|mobile|www)\./, ''),
|
||||
popupPanel: JSON.parse(url.searchParams.get('popupPanel')),
|
||||
|
@ -205,21 +207,20 @@ const reportedPage = (( ) => {
|
|||
})();
|
||||
|
||||
function reportSpecificFilterType() {
|
||||
return document.querySelector('select[name="type"]').value;
|
||||
return qs$('select[name="type"]').value;
|
||||
}
|
||||
|
||||
function reportSpecificFilterIssue(ev) {
|
||||
const githubURL = new URL('https://github.com/uBlockOrigin/uAssets/issues/new?template=specific_report_from_ubo.yml');
|
||||
const issueType = reportSpecificFilterType();
|
||||
let title = `${reportedPage.hostname}: ${issueType}`;
|
||||
if ( document.getElementById('isNSFW').checked ) {
|
||||
if ( qs$('#isNSFW').checked ) {
|
||||
title = `[nsfw] ${title}`;
|
||||
}
|
||||
githubURL.searchParams.set('title', title);
|
||||
githubURL.searchParams.set(
|
||||
'url_address_of_the_web_page', '`' +
|
||||
document.querySelector('select[name="url"]').value +
|
||||
'`'
|
||||
'url_address_of_the_web_page',
|
||||
'`' + qs$('select[name="url"]').value + '`'
|
||||
);
|
||||
githubURL.searchParams.set('category', issueType);
|
||||
githubURL.searchParams.set('configuration', configToMarkdown(true));
|
||||
|
@ -232,7 +233,7 @@ function reportSpecificFilterIssue(ev) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const cmEditor = new CodeMirror(document.getElementById('supportData'), {
|
||||
const cmEditor = new CodeMirror(qs$('#supportData'), {
|
||||
autofocus: true,
|
||||
readOnly: true,
|
||||
styleActiveLine: true,
|
||||
|
@ -249,9 +250,9 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
|||
|
||||
showData();
|
||||
|
||||
uDom('[data-url]').on('click', ev => {
|
||||
dom.on('[data-url]', 'click', ev => {
|
||||
const elem = ev.target.closest('[data-url]');
|
||||
const url = elem.getAttribute('data-url');
|
||||
const url = dom.attr(elem, 'data-url');
|
||||
if ( typeof url !== 'string' || url === '' ) { return; }
|
||||
vAPI.messaging.send('default', {
|
||||
what: 'gotoURL',
|
||||
|
@ -261,11 +262,11 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
|||
});
|
||||
|
||||
if ( reportedPage !== null ) {
|
||||
uDom('[data-i18n="supportReportSpecificButton"]').on('click', ev => {
|
||||
dom.on('[data-i18n="supportReportSpecificButton"]', 'click', ev => {
|
||||
reportSpecificFilterIssue(ev);
|
||||
});
|
||||
|
||||
uDom('[data-i18n="supportFindSpecificButton"]').on('click', ev => {
|
||||
dom.on('[data-i18n="supportFindSpecificButton"]', 'click', ev => {
|
||||
const url = new URL('https://github.com/uBlockOrigin/uAssets/issues');
|
||||
url.searchParams.set('q', `is:issue sort:updated-desc "${reportedPage.hostname}" in:title`);
|
||||
vAPI.messaging.send('default', {
|
||||
|
@ -275,15 +276,15 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
|||
ev.preventDefault();
|
||||
});
|
||||
|
||||
uDom('#showSupportInfo').on('click', ev => {
|
||||
dom.on('#showSupportInfo', 'click', ev => {
|
||||
const button = ev.target;
|
||||
button.classList.add('hidden');
|
||||
uDom.nodeFromSelector('.a.b.c.d').classList.add('e');
|
||||
dom.cl.add(button, 'hidden');
|
||||
dom.cl.add('.a.b.c.d', 'e');
|
||||
cmEditor.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
uDom('#selectAllButton').on('click', ( ) => {
|
||||
dom.on('#selectAllButton', 'click', ( ) => {
|
||||
cmEditor.focus();
|
||||
cmEditor.execCommand('selectAll');
|
||||
});
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-present 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function setTheme(theme, propagate = false) {
|
||||
if ( theme === 'auto' ) {
|
||||
if ( typeof self.matchMedia === 'function' ) {
|
||||
const mql = self.matchMedia('(prefers-color-scheme: dark)');
|
||||
theme = mql instanceof Object && mql.matches === true
|
||||
? 'dark'
|
||||
: 'light';
|
||||
} else {
|
||||
theme = 'light';
|
||||
}
|
||||
}
|
||||
let w = self;
|
||||
for (;;) {
|
||||
const rootcl = w.document.documentElement.classList;
|
||||
if ( theme === 'dark' ) {
|
||||
rootcl.add('dark');
|
||||
rootcl.remove('light');
|
||||
} else /* if ( theme === 'light' ) */ {
|
||||
rootcl.add('light');
|
||||
rootcl.remove('dark');
|
||||
}
|
||||
if ( propagate === false ) { break; }
|
||||
if ( w === w.parent ) { break; }
|
||||
w = w.parent;
|
||||
try { void w.document; } catch(ex) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
function setAccentColor(
|
||||
accentEnabled,
|
||||
accentColor,
|
||||
propagate,
|
||||
stylesheet = ''
|
||||
) {
|
||||
if ( accentEnabled && stylesheet === '' && self.hsluv !== undefined ) {
|
||||
const toRGB = hsl => self.hsluv.hsluvToRgb(hsl).map(a => Math.round(a * 255)).join(' ');
|
||||
// Normalize first
|
||||
const hsl = self.hsluv.hexToHsluv(accentColor);
|
||||
hsl[0] = Math.round(hsl[0] * 10) / 10;
|
||||
hsl[1] = Math.round(Math.min(100, Math.max(0, hsl[1])));
|
||||
// Use normalized result to derive all shades
|
||||
const shades = [ 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95 ];
|
||||
const text = [];
|
||||
text.push(':root.accented {');
|
||||
for ( const shade of shades ) {
|
||||
hsl[2] = shade;
|
||||
text.push(` --primary-${shade}: ${toRGB(hsl)};`);
|
||||
}
|
||||
text.push('}');
|
||||
hsl[1] = Math.min(25, hsl[1]);
|
||||
hsl[2] = 80;
|
||||
text.push(
|
||||
':root.light.accented {',
|
||||
` --button-surface-rgb: ${toRGB(hsl)};`,
|
||||
'}',
|
||||
);
|
||||
hsl[2] = 30;
|
||||
text.push(
|
||||
':root.dark.accented {',
|
||||
` --button-surface-rgb: ${toRGB(hsl)};`,
|
||||
'}',
|
||||
);
|
||||
text.push('');
|
||||
stylesheet = text.join('\n');
|
||||
vAPI.messaging.send('dom', { what: 'uiAccentStylesheet', stylesheet });
|
||||
}
|
||||
let w = self;
|
||||
for (;;) {
|
||||
const wdoc = w.document;
|
||||
let style = wdoc.querySelector('style#accentColors');
|
||||
if ( style !== null ) { style.remove(); }
|
||||
if ( accentEnabled ) {
|
||||
style = wdoc.createElement('style');
|
||||
style.id = 'accentColors';
|
||||
style.textContent = stylesheet;
|
||||
wdoc.head.append(style);
|
||||
wdoc.documentElement.classList.add('accented');
|
||||
} else {
|
||||
wdoc.documentElement.classList.remove('accented');
|
||||
}
|
||||
if ( propagate === false ) { break; }
|
||||
if ( w === w.parent ) { break; }
|
||||
w = w.parent;
|
||||
try { void w.document; } catch(ex) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/1044
|
||||
// Offer the possibility to bypass uBO's default styling
|
||||
vAPI.messaging.send('dom', { what: 'uiStyles' }).then(response => {
|
||||
if ( typeof response !== 'object' || response === null ) { return; }
|
||||
setTheme(response.uiTheme);
|
||||
if ( response.uiAccentCustom ) {
|
||||
setAccentColor(
|
||||
true,
|
||||
response.uiAccentCustom0,
|
||||
false,
|
||||
response.uiAccentStylesheet
|
||||
);
|
||||
}
|
||||
if ( response.uiStyles !== 'unset' ) {
|
||||
document.body.style.cssText = response.uiStyles;
|
||||
}
|
||||
});
|
||||
|
||||
const rootcl = document.documentElement.classList;
|
||||
if ( vAPI.webextFlavor.soup.has('mobile') ) {
|
||||
rootcl.add('mobile');
|
||||
} else {
|
||||
rootcl.add('desktop');
|
||||
}
|
||||
if ( window.matchMedia('(min-resolution: 150dpi)').matches ) {
|
||||
rootcl.add('hidpi');
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
setTheme,
|
||||
setAccentColor,
|
||||
};
|
778
src/js/udom.js
778
src/js/udom.js
|
@ -1,778 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-present 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 DOMTokenList */
|
||||
/* exported uDom */
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// It's just a silly, minimalist DOM framework: this allows me to not rely
|
||||
// on jQuery. jQuery contains way too much stuff than I need, and as per
|
||||
// Opera rules, I am not allowed to use a cut-down version of jQuery. So
|
||||
// the code here does *only* what I need, and nothing more, and with a lot
|
||||
// of assumption on passed parameters, etc. I grow it on a per-need-basis only.
|
||||
|
||||
const uDom = (( ) => {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const DOMList = class {
|
||||
constructor() {
|
||||
this.nodes = [];
|
||||
}
|
||||
get length() {
|
||||
return this.nodes.length;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const DOMListFactory = function(selector, context) {
|
||||
const r = new DOMList();
|
||||
if ( typeof selector === 'string' ) {
|
||||
selector = selector.trim();
|
||||
if ( selector !== '' ) {
|
||||
return addSelectorToList(r, selector, context);
|
||||
}
|
||||
}
|
||||
if ( selector instanceof Node ) {
|
||||
return addNodeToList(r, selector);
|
||||
}
|
||||
if ( selector instanceof NodeList ) {
|
||||
return addNodeListToList(r, selector);
|
||||
}
|
||||
if ( selector instanceof DOMList ) {
|
||||
return addListToList(r, selector);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
DOMListFactory.root = document.querySelector(':root');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMListFactory.setTheme = function(theme, propagate = false) {
|
||||
if ( theme === 'auto' ) {
|
||||
if ( typeof self.matchMedia === 'function' ) {
|
||||
const mql = self.matchMedia('(prefers-color-scheme: dark)');
|
||||
theme = mql instanceof Object && mql.matches === true
|
||||
? 'dark'
|
||||
: 'light';
|
||||
} else {
|
||||
theme = 'light';
|
||||
}
|
||||
}
|
||||
let w = self;
|
||||
for (;;) {
|
||||
const rootcl = w.document.documentElement.classList;
|
||||
if ( theme === 'dark' ) {
|
||||
rootcl.add('dark');
|
||||
rootcl.remove('light');
|
||||
} else /* if ( theme === 'light' ) */ {
|
||||
rootcl.add('light');
|
||||
rootcl.remove('dark');
|
||||
}
|
||||
if ( propagate === false ) { break; }
|
||||
if ( w === w.parent ) { break; }
|
||||
w = w.parent;
|
||||
try { void w.document; } catch(ex) { return; }
|
||||
}
|
||||
};
|
||||
|
||||
DOMListFactory.setAccentColor = function(
|
||||
accentEnabled,
|
||||
accentColor,
|
||||
propagate,
|
||||
stylesheet = ''
|
||||
) {
|
||||
if ( accentEnabled && stylesheet === '' && self.hsluv !== undefined ) {
|
||||
const toRGB = hsl => self.hsluv.hsluvToRgb(hsl).map(a => Math.round(a * 255)).join(' ');
|
||||
// Normalize first
|
||||
const hsl = self.hsluv.hexToHsluv(accentColor);
|
||||
hsl[0] = Math.round(hsl[0] * 10) / 10;
|
||||
hsl[1] = Math.round(Math.min(100, Math.max(0, hsl[1])));
|
||||
// Use normalized result to derive all shades
|
||||
const shades = [ 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95 ];
|
||||
const text = [];
|
||||
text.push(':root.accented {');
|
||||
for ( const shade of shades ) {
|
||||
hsl[2] = shade;
|
||||
text.push(` --primary-${shade}: ${toRGB(hsl)};`);
|
||||
}
|
||||
text.push('}');
|
||||
hsl[1] = Math.min(25, hsl[1]);
|
||||
hsl[2] = 80;
|
||||
text.push(
|
||||
':root.light.accented {',
|
||||
` --button-surface-rgb: ${toRGB(hsl)};`,
|
||||
'}',
|
||||
);
|
||||
hsl[2] = 30;
|
||||
text.push(
|
||||
':root.dark.accented {',
|
||||
` --button-surface-rgb: ${toRGB(hsl)};`,
|
||||
'}',
|
||||
);
|
||||
text.push('');
|
||||
stylesheet = text.join('\n');
|
||||
vAPI.messaging.send('uDom', { what: 'uiAccentStylesheet', stylesheet });
|
||||
}
|
||||
let w = self;
|
||||
for (;;) {
|
||||
const wdoc = w.document;
|
||||
let style = wdoc.querySelector('style#accentColors');
|
||||
if ( style !== null ) { style.remove(); }
|
||||
if ( accentEnabled ) {
|
||||
style = wdoc.createElement('style');
|
||||
style.id = 'accentColors';
|
||||
style.textContent = stylesheet;
|
||||
wdoc.head.append(style);
|
||||
wdoc.documentElement.classList.add('accented');
|
||||
} else {
|
||||
wdoc.documentElement.classList.remove('accented');
|
||||
}
|
||||
if ( propagate === false ) { break; }
|
||||
if ( w === w.parent ) { break; }
|
||||
w = w.parent;
|
||||
try { void w.document; } catch(ex) { break; }
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/1044
|
||||
// Offer the possibility to bypass uBO's default styling
|
||||
vAPI.messaging.send('uDom', { what: 'uiStyles' }).then(response => {
|
||||
if ( typeof response !== 'object' || response === null ) { return; }
|
||||
uDom.setTheme(response.uiTheme);
|
||||
if ( response.uiAccentCustom ) {
|
||||
uDom.setAccentColor(
|
||||
true,
|
||||
response.uiAccentCustom0,
|
||||
false,
|
||||
response.uiAccentStylesheet
|
||||
);
|
||||
}
|
||||
if ( response.uiStyles !== 'unset' ) {
|
||||
document.body.style.cssText = response.uiStyles;
|
||||
}
|
||||
});
|
||||
|
||||
const rootcl = DOMListFactory.root.classList;
|
||||
if ( vAPI.webextFlavor.soup.has('mobile') ) {
|
||||
rootcl.add('mobile');
|
||||
} else {
|
||||
rootcl.add('desktop');
|
||||
}
|
||||
if ( window.matchMedia('(min-resolution: 150dpi)').matches ) {
|
||||
rootcl.add('hidpi');
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMListFactory.onLoad = function(callback) {
|
||||
window.addEventListener('load', callback);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMListFactory.nodeFromId = function(id) {
|
||||
return document.getElementById(id);
|
||||
};
|
||||
|
||||
DOMListFactory.nodeFromSelector = function(selector) {
|
||||
return document.querySelector(selector);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const addNodeToList = function(list, node) {
|
||||
if ( node ) {
|
||||
list.nodes.push(node);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const addNodeListToList = function(list, nodelist) {
|
||||
if ( nodelist ) {
|
||||
var n = nodelist.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
list.nodes.push(nodelist[i]);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const addListToList = function(list, other) {
|
||||
list.nodes = list.nodes.concat(other.nodes);
|
||||
return list;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const addSelectorToList = function(list, selector, context) {
|
||||
var p = context || document;
|
||||
var r = p.querySelectorAll(selector);
|
||||
var n = r.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
list.nodes.push(r[i]);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.nodeAt = function(i) {
|
||||
return this.nodes[i] || null;
|
||||
};
|
||||
|
||||
DOMList.prototype.at = function(i) {
|
||||
return addNodeToList(new DOMList(), this.nodes[i]);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.toArray = function() {
|
||||
return this.nodes.slice();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.pop = function() {
|
||||
return addNodeToList(new DOMList(), this.nodes.pop());
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.forEach = function(fn) {
|
||||
var n = this.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
fn(this.at(i), i);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.subset = function(i, l) {
|
||||
var r = new DOMList();
|
||||
var n = l !== undefined ? l : this.nodes.length;
|
||||
var j = Math.min(i + n, this.nodes.length);
|
||||
if ( i < j ) {
|
||||
r.nodes = this.nodes.slice(i, j);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.first = function() {
|
||||
return this.subset(0, 1);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.next = function(selector) {
|
||||
var r = new DOMList();
|
||||
var n = this.nodes.length;
|
||||
var node;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
node = this.nodes[i];
|
||||
while ( node.nextSibling !== null ) {
|
||||
node = node.nextSibling;
|
||||
if ( node.nodeType !== 1 ) { continue; }
|
||||
if ( node.matches(selector) === false ) { continue; }
|
||||
addNodeToList(r, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.parent = function() {
|
||||
var r = new DOMList();
|
||||
if ( this.nodes.length ) {
|
||||
addNodeToList(r, this.nodes[0].parentNode);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.filter = function(filter) {
|
||||
var r = new DOMList();
|
||||
var filterFunc;
|
||||
if ( typeof filter === 'string' ) {
|
||||
filterFunc = function() {
|
||||
return this.matches(filter);
|
||||
};
|
||||
} else if ( typeof filter === 'function' ) {
|
||||
filterFunc = filter;
|
||||
} else {
|
||||
filterFunc = function(){
|
||||
return true;
|
||||
};
|
||||
}
|
||||
var n = this.nodes.length;
|
||||
var node;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
node = this.nodes[i];
|
||||
if ( filterFunc.apply(node) ) {
|
||||
addNodeToList(r, node);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// TODO: Avoid possible duplicates
|
||||
|
||||
DOMList.prototype.ancestors = function(selector) {
|
||||
var r = new DOMList();
|
||||
for ( var i = 0, n = this.nodes.length; i < n; i++ ) {
|
||||
var node = this.nodes[i].parentNode;
|
||||
while ( node ) {
|
||||
if (
|
||||
node instanceof Element &&
|
||||
node.matches(selector)
|
||||
) {
|
||||
addNodeToList(r, node);
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.descendants = function(selector) {
|
||||
var r = new DOMList();
|
||||
var n = this.nodes.length;
|
||||
var nl;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
nl = this.nodes[i].querySelectorAll(selector);
|
||||
addNodeListToList(r, nl);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.contents = function() {
|
||||
var r = new DOMList();
|
||||
var cnodes, cn, ci;
|
||||
var n = this.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
cnodes = this.nodes[i].childNodes;
|
||||
cn = cnodes.length;
|
||||
for ( ci = 0; ci < cn; ci++ ) {
|
||||
addNodeToList(r, cnodes.item(ci));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.remove = function() {
|
||||
var cn, p;
|
||||
var i = this.nodes.length;
|
||||
while ( i-- ) {
|
||||
cn = this.nodes[i];
|
||||
if ( (p = cn.parentNode) ) {
|
||||
p.removeChild(cn);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
DOMList.prototype.detach = DOMList.prototype.remove;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.empty = function() {
|
||||
var node;
|
||||
var i = this.nodes.length;
|
||||
while ( i-- ) {
|
||||
node = this.nodes[i];
|
||||
while ( node.firstChild ) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.append = function(selector, context) {
|
||||
var p = this.nodes[0];
|
||||
if ( p ) {
|
||||
var c = DOMListFactory(selector, context);
|
||||
var n = c.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
p.appendChild(c.nodes[i]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.prepend = function(selector, context) {
|
||||
var p = this.nodes[0];
|
||||
if ( p ) {
|
||||
var c = DOMListFactory(selector, context);
|
||||
var i = c.nodes.length;
|
||||
while ( i-- ) {
|
||||
p.insertBefore(c.nodes[i], p.firstChild);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.appendTo = function(selector, context) {
|
||||
var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context);
|
||||
var n = p.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
p.nodes[0].appendChild(this.nodes[i]);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.insertAfter = function(selector, context) {
|
||||
if ( this.nodes.length === 0 ) {
|
||||
return this;
|
||||
}
|
||||
var p = this.nodes[0].parentNode;
|
||||
if ( !p ) {
|
||||
return this;
|
||||
}
|
||||
var c = DOMListFactory(selector, context);
|
||||
var n = c.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
p.appendChild(c.nodes[i]);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.insertBefore = function(selector, context) {
|
||||
if ( this.nodes.length === 0 ) {
|
||||
return this;
|
||||
}
|
||||
var referenceNodes = DOMListFactory(selector, context);
|
||||
if ( referenceNodes.nodes.length === 0 ) {
|
||||
return this;
|
||||
}
|
||||
var referenceNode = referenceNodes.nodes[0];
|
||||
var parentNode = referenceNode.parentNode;
|
||||
if ( !parentNode ) {
|
||||
return this;
|
||||
}
|
||||
var n = this.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
parentNode.insertBefore(this.nodes[i], referenceNode);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.clone = function(notDeep) {
|
||||
var r = new DOMList();
|
||||
var n = this.nodes.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
addNodeToList(r, this.nodes[i].cloneNode(!notDeep));
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.nthOfType = function() {
|
||||
if ( this.nodes.length === 0 ) {
|
||||
return 0;
|
||||
}
|
||||
var node = this.nodes[0];
|
||||
var tagName = node.tagName;
|
||||
var i = 1;
|
||||
while ( node.previousElementSibling !== null ) {
|
||||
node = node.previousElementSibling;
|
||||
if ( typeof node.tagName !== 'string' ) {
|
||||
continue;
|
||||
}
|
||||
if ( node.tagName !== tagName ) {
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.attr = function(attr, value) {
|
||||
var i = this.nodes.length;
|
||||
if ( value === undefined && typeof attr !== 'object' ) {
|
||||
return i ? this.nodes[0].getAttribute(attr) : undefined;
|
||||
}
|
||||
if ( typeof attr === 'object' ) {
|
||||
var attrNames = Object.keys(attr);
|
||||
var node, j, attrName;
|
||||
while ( i-- ) {
|
||||
node = this.nodes[i];
|
||||
j = attrNames.length;
|
||||
while ( j-- ) {
|
||||
attrName = attrNames[j];
|
||||
node.setAttribute(attrName, attr[attrName]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ( i-- ) {
|
||||
this.nodes[i].setAttribute(attr, value);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.removeAttr = function(attr) {
|
||||
var i = this.nodes.length;
|
||||
while ( i-- ) {
|
||||
this.nodes[i].removeAttribute(attr);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.prop = function(prop, value) {
|
||||
var i = this.nodes.length;
|
||||
if ( value === undefined ) {
|
||||
return i !== 0 ? this.nodes[0][prop] : undefined;
|
||||
}
|
||||
while ( i-- ) {
|
||||
this.nodes[i][prop] = value;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.css = function(prop, value) {
|
||||
var i = this.nodes.length;
|
||||
if ( value === undefined ) {
|
||||
return i ? this.nodes[0].style[prop] : undefined;
|
||||
}
|
||||
if ( value !== '' ) {
|
||||
while ( i-- ) {
|
||||
this.nodes[i].style.setProperty(prop, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
while ( i-- ) {
|
||||
this.nodes[i].style.removeProperty(prop);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.val = function(value) {
|
||||
return this.prop('value', value);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.text = function(text) {
|
||||
var i = this.nodes.length;
|
||||
if ( text === undefined ) {
|
||||
return i ? this.nodes[0].textContent : '';
|
||||
}
|
||||
while ( i-- ) {
|
||||
this.nodes[i].textContent = text;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const toggleClass = function(node, className, targetState) {
|
||||
const tokenList = node.classList;
|
||||
if ( tokenList instanceof DOMTokenList === false ) { return; }
|
||||
const currentState = tokenList.contains(className);
|
||||
const newState = targetState !== undefined ? targetState : !currentState;
|
||||
if ( newState === currentState ) { return; }
|
||||
tokenList.toggle(className, newState);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.hasClass = function(className) {
|
||||
if ( !this.nodes.length ) {
|
||||
return false;
|
||||
}
|
||||
const tokenList = this.nodes[0].classList;
|
||||
return tokenList instanceof DOMTokenList &&
|
||||
tokenList.contains(className);
|
||||
};
|
||||
DOMList.prototype.hasClassName = DOMList.prototype.hasClass;
|
||||
|
||||
DOMList.prototype.addClass = function(className) {
|
||||
return this.toggleClass(className, true);
|
||||
};
|
||||
|
||||
DOMList.prototype.removeClass = function(className) {
|
||||
if ( className !== undefined ) {
|
||||
return this.toggleClass(className, false);
|
||||
}
|
||||
for ( const node of this.nodes ) {
|
||||
node.className = '';
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.toggleClass = function(className, targetState) {
|
||||
if ( className.indexOf(' ') !== -1 ) {
|
||||
return this.toggleClasses(className, targetState);
|
||||
}
|
||||
for ( const node of this.nodes ) {
|
||||
toggleClass(node, className, targetState);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.toggleClasses = function(classNames, targetState) {
|
||||
const tokens = classNames.split(/\s+/);
|
||||
for ( const node of this.nodes ) {
|
||||
for ( const token of tokens ) {
|
||||
toggleClass(node, token, targetState);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const listenerEntries = [];
|
||||
|
||||
const ListenerEntry = function(target, type, capture, callback) {
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
this.capture = capture;
|
||||
this.callback = callback;
|
||||
target.addEventListener(type, callback, capture);
|
||||
};
|
||||
|
||||
ListenerEntry.prototype.dispose = function() {
|
||||
this.target.removeEventListener(this.type, this.callback, this.capture);
|
||||
this.target = null;
|
||||
this.callback = null;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const makeEventHandler = function(selector, callback) {
|
||||
return function(event) {
|
||||
const dispatcher = event.currentTarget;
|
||||
if (
|
||||
dispatcher instanceof HTMLElement === false ||
|
||||
typeof dispatcher.querySelectorAll !== 'function'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const receiver = event.target;
|
||||
const ancestor = receiver.closest(selector);
|
||||
if (
|
||||
ancestor === receiver &&
|
||||
ancestor !== dispatcher &&
|
||||
dispatcher.contains(ancestor)
|
||||
) {
|
||||
callback.call(receiver, event);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
DOMList.prototype.on = function(etype, selector, callback) {
|
||||
if ( typeof selector === 'function' ) {
|
||||
callback = selector;
|
||||
selector = undefined;
|
||||
} else {
|
||||
callback = makeEventHandler(selector, callback);
|
||||
}
|
||||
|
||||
for ( const node of this.nodes ) {
|
||||
listenerEntries.push(
|
||||
new ListenerEntry(node, etype, selector !== undefined, callback)
|
||||
);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// TODO: Won't work for delegated handlers. Need to figure
|
||||
// what needs to be done.
|
||||
|
||||
DOMList.prototype.off = function(evtype, callback) {
|
||||
var i = this.nodes.length;
|
||||
while ( i-- ) {
|
||||
this.nodes[i].removeEventListener(evtype, callback);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
DOMList.prototype.trigger = function(etype) {
|
||||
var ev = new CustomEvent(etype);
|
||||
var i = this.nodes.length;
|
||||
while ( i-- ) {
|
||||
this.nodes[i].dispatchEvent(ev);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return DOMListFactory;
|
||||
|
||||
})();
|
|
@ -19,11 +19,12 @@
|
|||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global CodeMirror, uDom, uBlockDashboard */
|
||||
/* global CodeMirror, uBlockDashboard */
|
||||
|
||||
'use strict';
|
||||
|
||||
import { i18n$ } from './i18n.js';
|
||||
import { dom, qs$ } from './dom.js';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -90,15 +91,12 @@ const noopFunc = function(){};
|
|||
|
||||
let cachedWhitelist = '';
|
||||
|
||||
const cmEditor = new CodeMirror(
|
||||
document.getElementById('whitelist'),
|
||||
{
|
||||
const cmEditor = new CodeMirror(qs$('#whitelist'), {
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
||||
|
||||
|
@ -116,12 +114,12 @@ const setEditorText = function(text) {
|
|||
/******************************************************************************/
|
||||
|
||||
const whitelistChanged = function() {
|
||||
const whitelistElem = uDom.nodeFromId('whitelist');
|
||||
const bad = whitelistElem.querySelector('.cm-error') !== null;
|
||||
const whitelistElem = qs$('#whitelist');
|
||||
const bad = qs$(whitelistElem, '.cm-error') !== null;
|
||||
const changedWhitelist = getEditorText().trim();
|
||||
const changed = changedWhitelist !== cachedWhitelist;
|
||||
uDom.nodeFromId('whitelistApply').disabled = !changed || bad;
|
||||
uDom.nodeFromId('whitelistRevert').disabled = !changed;
|
||||
qs$('#whitelistApply').disabled = !changed || bad;
|
||||
qs$('#whitelistRevert').disabled = !changed;
|
||||
CodeMirror.commands.save = changed && !bad ? applyChanges : noopFunc;
|
||||
};
|
||||
|
||||
|
@ -188,7 +186,7 @@ const handleImportFilePicker = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
const startImportFilePicker = function() {
|
||||
const input = document.getElementById('importFilePicker');
|
||||
const input = qs$('#importFilePicker');
|
||||
// Reset to empty string, this will ensure an change event is properly
|
||||
// triggered if the user pick a file, even if it is the same as the last
|
||||
// one picked.
|
||||
|
@ -251,11 +249,11 @@ self.hasUnsavedData = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom('#importWhitelistFromFile').on('click', startImportFilePicker);
|
||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||
uDom('#exportWhitelistToFile').on('click', exportWhitelistToFile);
|
||||
uDom('#whitelistApply').on('click', ( ) => { applyChanges(); });
|
||||
uDom('#whitelistRevert').on('click', revertChanges);
|
||||
dom.on('#importWhitelistFromFile', 'click', startImportFilePicker);
|
||||
dom.on('#importFilePicker', 'change', handleImportFilePicker);
|
||||
dom.on('#exportWhitelistToFile', 'click', exportWhitelistToFile);
|
||||
dom.on('#whitelistApply', 'click', ( ) => { applyChanges(); });
|
||||
dom.on('#whitelistRevert', 'click', revertChanges);
|
||||
|
||||
renderWhitelist();
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/vapi-client-extra.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/logger-ui.js" type="module"></script>
|
||||
<script src="js/logger-ui-inspector.js" type="module"></script>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/popup-fenix.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -89,9 +89,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/settings.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>uBlock Origin — Keyboard shortcuts</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/themes/default.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/shortcuts.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
<table class="commandEntries"><tbody></tbody></table>
|
||||
</div>
|
||||
|
||||
<div id="templates" style="display: none;">
|
||||
<table>
|
||||
<tr class="commandEntry">
|
||||
<td class="commandDesc">
|
||||
<td class="commandShortcut">
|
||||
<input type="text" placeholder="shortcutCapturePlaceholder">
|
||||
<button class="commandReset vflex" type="button">×</button>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="lib/hsluv/hsluv-0.1.0.min.js"></script>
|
||||
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/shortcuts.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -112,10 +112,10 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/support.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/support.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
<script src="../js/vapi.js"></script>
|
||||
<script src="../js/vapi-common.js"></script>
|
||||
<script src="../js/vapi-client.js"></script>
|
||||
<script src="../js/i18n.js"></script>
|
||||
<script src="../js/udom.js"></script>
|
||||
<script src="../js/click2load.js"></script>
|
||||
<script src="../js/theme.js" type="module"></script>
|
||||
<script src="../js/i18n.js" type="module"></script>
|
||||
<script src="../js/click2load.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
<script src="../js/vapi-common.js"></script>
|
||||
<script src="../js/vapi-client.js"></script>
|
||||
<script src="../js/vapi-client-extra.js"></script>
|
||||
<script src="../js/udom.js"></script>
|
||||
<script src="../js/theme.js" type="module"></script>
|
||||
<script src="../js/i18n.js" type="module"></script>
|
||||
<script src="../js/epicker-ui.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -52,9 +52,9 @@
|
|||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/whitelist.js" type="module"></script>
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ cp src/css/common.css $DES/css/
|
|||
cp src/css/dashboard-common.css $DES/css/
|
||||
cp src/css/fa-icons.css $DES/css/
|
||||
|
||||
cp src/js/dom.js $DES/js/
|
||||
cp src/js/fa-icons.js $DES/js/
|
||||
cp src/js/i18n.js $DES/js/
|
||||
|
||||
|
|
Loading…
Reference in New Issue