[mv3] Rework dashboard to avoid usage of `iframe`

Related issue:
https://github.com/uBlockOrigin/uBOL-home/issues/67
This commit is contained in:
Raymond Hill 2023-09-16 11:46:39 -04:00
parent e6aae07310
commit ee6de37b6e
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
9 changed files with 145 additions and 389 deletions

View File

@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>uBlock Origin Lite — About</title>
<link rel="stylesheet" type="text/css" href="css/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/about.css">
</head>
<body>
<div class="body">
<div id="aboutNameVer" class="li"></div>
<div class="liul">
<div class="li">Copyright (c) Raymond Hill 2014-present</div>
</div>
<div class="li"><a href="https://github.com/gorhill/uBlock/wiki/Privacy-policy" data-i18n="aboutPrivacyPolicy"></a></div>
<div class="li"><a href="https://github.com/uBlockOrigin/uBOL-home/releases" data-i18n="aboutChangelog"></a></div>
<div class="li"><a href="https://github.com/gorhill/uBlock" data-i18n="aboutCode"></a></div>
<div class="li"><span data-i18n="aboutContributors"></span></div>
<div class="liul">
<div class="li"><a href="https://github.com/gorhill/uBlock/graphs/contributors" data-i18n="aboutSourceCode"></a></div>
<div class="li"><a href="https://crowdin.com/project/ublock" data-i18n="aboutTranslations"></a></div>
<div class="li"><a href="https://github.com/uBlockOrigin/uAssets/graphs/contributors" data-i18n="aboutFilterLists"></a></div>
</div>
<div class="li"><span data-i18n="aboutDependencies"></span></div>
<div class="liul">
<div class="li"><span><a href="https://github.com/chrismsimpson/Metropolis" target="_blank">Metropolis font family</a> by <a href="https://github.com/chrismsimpson">Chris Simpson</a></span></div>
<div class="li"><span><a href="https://github.com/rsms/inter" target="_blank">Inter font family</a> by <a href="https://github.com/rsms">Rasmus Andersson</a></span></div>
<div class="li"><span><a href="https://fontawesome.com/" target="_blank">FontAwesome font family</a> by <a href="https://github.com/davegandy">Dave Gandy</a></span></div>
<div class="li"><span><a href="https://github.com/mathiasbynens/punycode.js" target="_blank">Punycode.js</a> by <a href="https://github.com/mathiasbynens">Mathias Bynens</a></span></div>
<div class="li"><span><a href="https://flagpedia.net/" target="_blank">Flags of the World</a> by <a href="https://www.davidkrmela.com/">David Krmela</a></span></div>
</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>
</body>
</html>

View File

@ -1,5 +1,3 @@
body {
}
h2, h3 {
margin: 1em 0;
}

View File

@ -1,19 +1,14 @@
html, body {
body {
align-items: center;
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100vh;
justify-content: stretch;
padding: 0 1em;
overflow: hidden;
position: relative;
width: 100vw;
}
body > * {
width: min(641px, 100vw);
width: min(640px, 100%);
}
#dashboard-nav {
background-color: var(--surface-1);
border: 0;
border-bottom: 1px solid var(--border-1);
display: flex;
@ -23,7 +18,7 @@ body > * {
padding: 0;
position: sticky;
top: 0;
z-index: 10;
z-index: 100;
}
.tabButton {
background-color: transparent;
@ -41,53 +36,25 @@ body > * {
.tabButton:focus {
outline: 0;
}
/*
* TODO: support keyboard-driven navigation
*
.tabButton:not(:active):focus {
background-color: var(--dashboard-tab-focus-surface);
.tabButton:hover {
background-color: var(--dashboard-tab-hover-surface);
border-bottom-color: var(--dashboard-tab-hover-border);
}
*/
.tabButton.selected {
body[data-pane="settings"] #dashboard-nav .tabButton[data-pane="settings"],
body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] {
background-color: var(--dashboard-tab-active-surface);
border-bottom: 3px solid var(--dashboard-tab-active-ink);
color: var(--dashboard-tab-active-ink);
fill: var(--dashboard-tab-active-ink);
}
iframe {
background-color: transparent;
border: 0;
flex-grow: 1;
margin: 0;
min-height: 600px;
padding: 0;
}
#unsavedWarning {
display: none;
left: 0;
position: absolute;
width: 100%;
z-index: 20;
}
#unsavedWarning.on {
display: initial;
}
#unsavedWarning > div:first-of-type {
padding: 0.5em;
}
#unsavedWarning > div:last-of-type {
height: 100vh;
position: absolute;
}
body:not(.canUpdateShortcuts) .tabButton[data-pane="shortcuts.html"] {
body > section {
display: none;
}
body .tabButton[data-pane="no-dashboard.html"] {
display: none;
}
body.noDashboard #dashboard-nav {
display: none;
body[data-pane="settings"] > section[data-pane="settings"],
body[data-pane="about"] > section[data-pane="about"] {
display: block;
}
/* high dpi devices */
@ -97,18 +64,6 @@ body.noDashboard #dashboard-nav {
letter-spacing: 0.5px;
}
/* hover-able devices */
:root.desktop .tabButton {
cursor: default;
}
:root.desktop .tabButton:not(.selected) {
cursor: pointer;
}
:root.desktop .tabButton:not(.selected):hover {
background-color: var(--dashboard-tab-hover-surface);
border-bottom-color: var(--dashboard-tab-hover-border);
}
/* touch-screen devices */
:root.mobile #dashboard-nav {
flex-wrap: nowrap;

View File

@ -2,9 +2,6 @@
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
body {
margin-bottom: 6rem;
}
legend {
color: var(--ink-3);
font-size: var(--font-size-smaller);
@ -18,11 +15,8 @@ body.firstRun .firstRun {
display: block;
padding: 8px;
}
body > div {
margin: 1em 0;
}
h3 {
margin: 0;
margin: 1em 0;
}
p {
white-space: pre-line;

View File

@ -8,31 +8,142 @@
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/fa-icons.css">
<link rel="stylesheet" href="css/dashboard.css">
<link rel="stylesheet" href="css/dashboard-common.css">
<link rel="stylesheet" href="css/filtering-mode.css">
<link rel="stylesheet" href="css/settings.css">
<link rel="stylesheet" href="css/about.css">
<link rel="shortcut icon" type="image/png" href="img/icon_64.png"/>
</head>
<body>
<body data-pane="settings">
<!-- -------- -->
<div id="dashboard-nav">
<span class="logo"><img data-i18n-title="extName" src="img/ublock.svg"></span><!--
--><button class="tabButton" type="button" data-pane="settings.html" data-i18n="settingsPageName" 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="settings" data-i18n="settingsPageName" tabindex="0"></button><!--
--><button class="tabButton" type="button" data-pane="about" data-i18n="aboutPageName" tabindex="0"></button>
</div>
<!-- -------- -->
<section id="unsavedWarning" class="notice">
<div>
<span data-i18n="dashboardUnsavedWarning"></span>&emsp;
<button type="button" data-i18n="dashboardUnsavedWarningStay">_<span class="hover"></span></button>&ensp;
<button type="button" data-i18n="dashboardUnsavedWarningIgnore">_<span class="hover"></span></button>
<section data-pane="settings">
<div class="firstRun">
<h3 data-i18n="firstRunSectionLabel"></h3>
<p data-i18n="firstRunDescription"></p>
</div>
<div>
<h3 data-i18n="defaultFilteringModeSectionLabel"></h3>
<p data-i18n="defaultFilteringModeDescription"></p>
<div id="defaultFilteringMode">
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="1"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode1Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="1">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="basicFilteringModeDescription"></div>
</label>
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="2"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode2Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="2">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="optimalFilteringModeDescription"></div>
</label>
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="3"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode3Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="3">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="completeFilteringModeDescription"></div>
</label>
</div>
</div>
<div>
<h3 data-i18n="behaviorSectionLabel"></h3>
<p><label id="autoReload" data-i18n="autoReloadLabel"><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span>_</label>
</p>
</div>
<div>
<h3>Filter lists</h3>
<div>
<p id="listsOfBlockedHostsPrompt"></p>
</div>
<div>
<div id="lists"></div>
</div>
</div>
<div id="templates">
<div class="groupEntry">
<div class="geDetails"><span class="geName"></span> <span class="geCount"></span></div>
<div class="listEntries"></div>
</div>
<div class="li listEntry">
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span class="listname forinput"></span> <span class="iconbar"><!--
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
--></span></span></label>
</div>
</div>
<div></div>
</section>
<!-- -------- -->
<iframe id="iframe" src=""></iframe>
<section data-pane="about">
<div class="body">
<div id="aboutNameVer" class="li"></div>
<div class="liul">
<div class="li">Copyright (c) Raymond Hill 2014-present</div>
</div>
<div class="li"><a href="https://github.com/gorhill/uBlock/wiki/Privacy-policy" data-i18n="aboutPrivacyPolicy"></a></div>
<div class="li"><a href="https://github.com/uBlockOrigin/uBOL-home/releases" data-i18n="aboutChangelog"></a></div>
<div class="li"><a href="https://github.com/gorhill/uBlock" data-i18n="aboutCode"></a></div>
<div class="li"><span data-i18n="aboutContributors"></span></div>
<div class="liul">
<div class="li"><a href="https://github.com/gorhill/uBlock/graphs/contributors" data-i18n="aboutSourceCode"></a></div>
<div class="li"><a href="https://crowdin.com/project/ublock" data-i18n="aboutTranslations"></a></div>
<div class="li"><a href="https://github.com/uBlockOrigin/uAssets/graphs/contributors" data-i18n="aboutFilterLists"></a></div>
</div>
<div class="li"><span data-i18n="aboutDependencies"></span></div>
<div class="liul">
<div class="li"><span><a href="https://github.com/chrismsimpson/Metropolis" target="_blank">Metropolis font family</a> by <a href="https://github.com/chrismsimpson">Chris Simpson</a></span></div>
<div class="li"><span><a href="https://github.com/rsms/inter" target="_blank">Inter font family</a> by <a href="https://github.com/rsms">Rasmus Andersson</a></span></div>
<div class="li"><span><a href="https://fontawesome.com/" target="_blank">FontAwesome font family</a> by <a href="https://github.com/davegandy">Dave Gandy</a></span></div>
<div class="li"><span><a href="https://github.com/mathiasbynens/punycode.js" target="_blank">Punycode.js</a> by <a href="https://github.com/mathiasbynens">Mathias Bynens</a></span></div>
<div class="li"><span><a href="https://flagpedia.net/" target="_blank">Flags of the World</a> by <a href="https://www.davidkrmela.com/">David Krmela</a></span></div>
</div>
</div>
</section>
<!-- -------- -->
<script src="js/theme.js" type="module"></script>
<script src="js/fa-icons.js" type="module"></script>
<script src="js/i18n.js" type="module"></script>
<script src="js/dashboard.js" type="module"></script>
<script src="js/settings.js" type="module"></script>
</body>
</html>

View File

@ -1,33 +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
*/
'use strict';
import { runtime } from './ext.js';
import { dom } from './dom.js';
/******************************************************************************/
(async ( ) => {
const manifest = runtime.getManifest();
dom.text('#aboutNameVer', `${manifest.name} ${manifest.version}`);
})();

View File

@ -1,30 +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
*/
'use strict';
import { dom } from './dom.js';
/******************************************************************************/
// Open links in the proper window
dom.attr('a', 'target', '_blank');
dom.attr('a[href*="dashboard.html"]', 'target', '_parent');

View File

@ -21,104 +21,20 @@
'use strict';
import { dom, qs$ } from './dom.js';
import { runtime } from './ext.js';
import { dom } from './dom.js';
/******************************************************************************/
const discardUnsavedData = function(synchronous = false) {
const paneFrame = qs$('#iframe');
const paneWindow = paneFrame.contentWindow;
if (
typeof paneWindow.hasUnsavedData !== 'function' ||
paneWindow.hasUnsavedData() === false
) {
return true;
{
const manifest = runtime.getManifest();
dom.text('#aboutNameVer', `${manifest.name} ${manifest.version}`);
}
if ( synchronous ) {
return false;
}
dom.attr('a', 'target', '_blank');
return new Promise(resolve => {
const modal = document.querySelector('#unsavedWarning');
dom.cl.add(modal, 'on');
modal.focus();
const onDone = status => {
dom.cl.remove(modal, 'on');
document.removeEventListener('click', onClick, true);
resolve(status);
};
const onClick = ev => {
const target = ev.target;
if ( target.matches('[data-i18n="dashboardUnsavedWarningStay"]') ) {
return onDone(false);
}
if ( target.matches('[data-i18n="dashboardUnsavedWarningIgnore"]') ) {
return onDone(true);
}
if ( modal.querySelector('[data-i18n="dashboardUnsavedWarning"]').contains(target) ) {
return;
}
onDone(false);
};
document.addEventListener('click', onClick, true);
dom.on('#dashboard-nav', 'click', '.tabButton', ev => {
dom.body.dataset.pane = ev.target.dataset.pane;
});
};
const loadDashboardPanel = function(pane, first) {
const tabButton = document.querySelector(`[data-pane="${pane}"]`);
if ( tabButton === null || dom.cl.has(tabButton, 'selected') ) {
return;
}
const loadPane = ( ) => {
self.location.replace(`#${pane}`);
for ( const node of document.querySelectorAll('.tabButton.selected') ) {
dom.cl.remove(node, 'selected');
}
dom.cl.add(tabButton, 'selected');
tabButton.scrollIntoView();
document.querySelector('#iframe').contentWindow.location.replace(pane);
};
if ( first ) {
return loadPane();
}
const r = discardUnsavedData();
if ( r === false ) { return; }
if ( r === true ) {
return loadPane();
}
r.then(status => {
if ( status === false ) { return; }
loadPane();
});
};
const onTabClickHandler = function(ev) {
loadDashboardPanel(dom.attr(ev.target, 'data-pane'));
};
if ( self.location.hash.slice(1) === 'no-dashboard.html' ) {
dom.cl.add(dom.body, 'noDashboard');
}
(async ( ) => {
let pane = null;
if ( self.location.hash !== '' ) {
pane = self.location.hash.slice(1) || null;
}
loadDashboardPanel(pane !== null ? pane : 'settings.html', true);
dom.on('#dashboard-nav', 'click', '.tabButton', onTabClickHandler);
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
window.addEventListener('beforeunload', ( ) => {
if ( discardUnsavedData(true) ) { return; }
event.preventDefault();
event.returnValue = '';
});
})();
/******************************************************************************/

View File

@ -1,110 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<title>uBlock Origin Lite — Filter lists</title>
<link rel="stylesheet" type="text/css" href="css/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/fa-icons.css">
<link rel="stylesheet" type="text/css" href="css/filtering-mode.css">
<link rel="stylesheet" type="text/css" href="css/settings.css">
</head>
<body>
<div class="firstRun">
<h3 data-i18n="firstRunSectionLabel"></h3>
<p data-i18n="firstRunDescription"></p>
</div>
<div>
<h3 data-i18n="defaultFilteringModeSectionLabel"></h3>
<p data-i18n="defaultFilteringModeDescription"></p>
<div id="defaultFilteringMode">
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="1"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode1Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="1">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="basicFilteringModeDescription"></div>
</label>
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="2"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode2Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="2">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="optimalFilteringModeDescription"></div>
</label>
<label class="filteringModeCard">
<div>
<span><span class="input radio"><input type="radio" name="filteringMode" value="3"><svg viewBox="0 0 24 24"><path d="M 12 0 A 12 12 0 0 0 0 12 A 12 12 0 0 0 12 24 A 12 12 0 0 0 24 12 A 12 12 0 0 0 12 0 z M 12 2.5 A 9.5 9.5 0 0 1 21.5 12 A 9.5 9.5 0 0 1 12 21.5 A 9.5 9.5 0 0 1 2.5 12 A 9.5 9.5 0 0 1 12 2.5 z"/><circle cx="12" cy="12" r="7"/></svg></span><span data-i18n="filteringMode3Name">_</span></span>
</div>
<div>
<div class="filteringModeSlider" data-level="3">
<div class="filteringModeButton"><div></div></div>
<span data-level="0"></span>
<span data-level="1"></span>
<span data-level="2"></span>
<span data-level="3"></span>
</div>
</div>
<div data-i18n="completeFilteringModeDescription"></div>
</label>
</div>
</div>
<div>
<h3 data-i18n="behaviorSectionLabel"></h3>
<p><label id="autoReload" data-i18n="autoReloadLabel"><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span>_</label>
</p>
</div>
<div>
<h3>Filter lists</h3>
<div>
<p id="listsOfBlockedHostsPrompt"></p>
</div>
<div>
<div id="lists"></div>
</div>
</div>
<div id="templates">
<div class="groupEntry">
<div class="geDetails"><span class="geName"></span> <span class="geCount"></span></div>
<div class="listEntries"></div>
</div>
<div class="li listEntry">
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span class="listname forinput"></span> <span class="iconbar"><!--
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
--></span></span></label>
</div>
</div>
<script src="js/theme.js" type="module"></script>
<script src="js/fa-icons.js" type="module"></script>
<script src="js/i18n.js" type="module"></script>
<script src="js/dashboard-common.js" type="module"></script>
<script src="js/settings.js" type="module"></script>
</body>
</html>