mirror of
https://github.com/khairul169/honey.git
synced 2025-04-28 14:59:32 +07:00
Full rebase
This commit is contained in:
parent
63f51d9cac
commit
8c162678a7
17
css/dark.css
17
css/dark.css
@ -1,17 +0,0 @@
|
||||
html {
|
||||
--color: #EEE;
|
||||
--color2: #EEE9;
|
||||
--background: #1118;
|
||||
--bg2: #0008;
|
||||
--hover: #FFF1;
|
||||
}
|
||||
.box .icon {
|
||||
background: hsl(var(--color), 100%, 80%);
|
||||
color: hsl(var(--color), 100%, 28%);
|
||||
}
|
||||
.hostedby {
|
||||
color: #FFF7;
|
||||
}
|
||||
.hostedby b {
|
||||
color: #79F;
|
||||
}
|
205
css/main.css
205
css/main.css
@ -7,7 +7,7 @@
|
||||
src: url(../font/MaterialSymbolsRounded.woff2) format('woff2');
|
||||
}
|
||||
|
||||
html {
|
||||
body {
|
||||
--color: #222;
|
||||
--color2: #2229;
|
||||
--background: #EEE8;
|
||||
@ -17,6 +17,30 @@ html {
|
||||
--blur: blur(16px);
|
||||
}
|
||||
|
||||
body.dark {
|
||||
--color: #EEE;
|
||||
--color2: #EEE9;
|
||||
--background: #1118;
|
||||
--bg2: #0008;
|
||||
--hover: #FFF1;
|
||||
}
|
||||
|
||||
#theme-switcher .icon::after {
|
||||
content: "light_mode"
|
||||
}
|
||||
|
||||
body.dark #theme-switcher .icon::after {
|
||||
content: "dark_mode"
|
||||
}
|
||||
|
||||
body.noblur {
|
||||
--blur: 0;
|
||||
}
|
||||
|
||||
body.noanim * {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #000;
|
||||
color: var(--color);
|
||||
@ -27,25 +51,11 @@ body {
|
||||
font-size: 14px;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.noblur {
|
||||
--blur: blur(0);
|
||||
}
|
||||
.noanim * {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
a {
|
||||
color: #68F;
|
||||
}
|
||||
.init * {
|
||||
transition: none !important;
|
||||
}
|
||||
.init .page {
|
||||
transition: opacity .3s, top .3s !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-family: 'Material Symbols Rounded';
|
||||
font-weight: normal;
|
||||
@ -61,6 +71,7 @@ a {
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#background img {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -71,26 +82,28 @@ a {
|
||||
object-fit: cover;
|
||||
transition: transform .4s, opacity .4s !important;
|
||||
}
|
||||
|
||||
#background.scaled img {
|
||||
transform: scale(var(--scale-factor));
|
||||
}
|
||||
#background.dark > img:first-child {
|
||||
opacity: 0;
|
||||
transform: none;
|
||||
|
||||
body #background img {
|
||||
opacity: 1;
|
||||
}
|
||||
#background:not(.dark) > img:last-child {
|
||||
|
||||
body:not(.dark) #background img:last-child,
|
||||
body.dark #background img:first-child {
|
||||
opacity: 0;
|
||||
transform: none;
|
||||
}
|
||||
#background.dark:not(.scaled) > img:first-child {
|
||||
|
||||
body.dark #background:not(.scaled) img:first-child,
|
||||
body:not(.dark) #background:not(.scaled) img:last-child {
|
||||
transform: scale(var(--scale-factor));
|
||||
}
|
||||
#background:not(.dark):not(.scaled) > img:last-child {
|
||||
transform: scale(var(--scale-factor));
|
||||
}
|
||||
.unloaded {
|
||||
opacity: 0;
|
||||
transform: scale(1) !important;
|
||||
|
||||
body.dark #background.scaled img:first-child,
|
||||
body:not(.dark) #background.scaled img:last-child {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
@ -100,11 +113,13 @@ a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.appicon {
|
||||
width: 192px;
|
||||
height: 192px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.page {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -116,6 +131,7 @@ a {
|
||||
overflow-y: scroll;
|
||||
transition: top .4s, opacity .4s, visibility .4s, color .3s;
|
||||
}
|
||||
|
||||
.home.page {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@ -124,14 +140,17 @@ a {
|
||||
overflow: hidden;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.page.hidden {
|
||||
|
||||
.page:not(.current) {
|
||||
top: 240px;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
.home.page.hidden {
|
||||
|
||||
.home.page:not(.current) {
|
||||
top: calc(50% - 64px);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
box-shadow: 2px 2px 8px #0003;
|
||||
background: var(--background);
|
||||
@ -144,6 +163,7 @@ a {
|
||||
min-width: 340px;
|
||||
transition: background .2s;
|
||||
}
|
||||
|
||||
.home .wrapper {
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
@ -153,10 +173,12 @@ a {
|
||||
.appname {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.appdesc {
|
||||
opacity: .6;
|
||||
margin: 2px 12px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
box-shadow: 2px 2px 8px #0002;
|
||||
display: flex;
|
||||
@ -169,6 +191,7 @@ a {
|
||||
justify-content: space-between;
|
||||
transition: background .3s;
|
||||
}
|
||||
|
||||
.buttons > div {
|
||||
padding: 16px;
|
||||
margin: 2px;
|
||||
@ -177,9 +200,11 @@ a {
|
||||
width: 100%;
|
||||
transition: background .2s;
|
||||
}
|
||||
|
||||
.buttons > div:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
|
||||
.buttons .text {
|
||||
margin-top: -2px;
|
||||
}
|
||||
@ -204,9 +229,11 @@ a {
|
||||
border-radius: 20px;
|
||||
transition: background .2s;
|
||||
}
|
||||
|
||||
.back .icon:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
|
||||
.back .icon:after {
|
||||
content: "chevron_left";
|
||||
position: absolute;
|
||||
@ -220,12 +247,7 @@ a {
|
||||
flex: 1 1 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.static {
|
||||
margin: 16px 6px;
|
||||
}
|
||||
.static .box:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.box {
|
||||
min-width: 292px;
|
||||
flex: 1;
|
||||
@ -239,9 +261,11 @@ a {
|
||||
color: inherit;
|
||||
transition: background .2s;
|
||||
}
|
||||
|
||||
.box:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
|
||||
.box .icon {
|
||||
font-size: 24px;
|
||||
padding: 20px;
|
||||
@ -250,116 +274,47 @@ a {
|
||||
border-radius: 100px;
|
||||
margin: 2px 12px 2px 2px;
|
||||
}
|
||||
|
||||
a.box {
|
||||
cursor: pointer;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.box img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
object-fit: cover;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.box .name {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.box .desc {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 20px 16px;
|
||||
}
|
||||
|
||||
.header .icon {
|
||||
margin-top: 1px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.header .text {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.selector {
|
||||
position: relative;
|
||||
background: var(--bg2);
|
||||
margin: 18px auto;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
width: calc(100% - 32px);
|
||||
max-width: 640px;
|
||||
transition: background .2s;
|
||||
}
|
||||
.selector .bg {
|
||||
position: absolute;
|
||||
width: calc(100% / var(--screens) - 8px);
|
||||
height: calc(100% - 8px);
|
||||
background: var(--hover);
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-left: 4px;
|
||||
border-radius: 12px;
|
||||
transform: translateY(-50%);
|
||||
transition: background .4s, left .3s;
|
||||
}
|
||||
.selector .entry {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14px;
|
||||
margin: 2px;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.selector .entry > div {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.split {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: right;
|
||||
padding: 96px 24px 68px;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.split .fancy {
|
||||
font-size: 80px;
|
||||
}
|
||||
.split .pp > b {
|
||||
font-size: 48px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.split .text {
|
||||
max-width: 240px;
|
||||
margin-left: auto;
|
||||
opacity: .6;
|
||||
}
|
||||
.split .right {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.screens {
|
||||
display: flex;
|
||||
width: calc(100% * var(--screens));
|
||||
align-items: flex-start;
|
||||
transition: transform .4s, height .4s;
|
||||
}
|
||||
.screen {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.screen.hidden {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin: 12px auto 32px;
|
||||
margin: 32px auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.setting {
|
||||
background: var(--bg2);
|
||||
margin: 8px;
|
||||
@ -371,17 +326,21 @@ a.box {
|
||||
text-align: left;
|
||||
transition: background .4s;
|
||||
}
|
||||
|
||||
.setting .icon {
|
||||
margin-right: 14px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.setting .name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.setting .desc {
|
||||
opacity: .6;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.setting .switch {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
@ -392,6 +351,7 @@ a.box {
|
||||
margin: 0 4px 0 auto;
|
||||
transition: border .4s, background .4s;
|
||||
}
|
||||
|
||||
.setting .switch:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@ -404,26 +364,21 @@ a.box {
|
||||
transform: translateY(-50%);
|
||||
transition: left .2s, background .4s;
|
||||
}
|
||||
|
||||
.setting.checked .switch {
|
||||
background-color: #68F;
|
||||
border-color: #68F;
|
||||
}
|
||||
|
||||
.setting.checked .switch:after {
|
||||
left: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.hostedby {
|
||||
font-size: 13px;
|
||||
color: #0007;
|
||||
margin: 18px 0 20px;
|
||||
}
|
||||
.hostedby b {
|
||||
color: #56F;
|
||||
}
|
||||
.warn {
|
||||
margin: 8px 0;
|
||||
margin: 24px 0 -8px;
|
||||
color: #F60;
|
||||
}
|
||||
.none {
|
||||
|
||||
.warn.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
103
index.html
103
index.html
@ -5,17 +5,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" id="favicon">
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dark.css" id="css_dark" disabled>
|
||||
<script type="text/javascript" src="js/dom.js"></script>
|
||||
<script type="text/javascript" src="js/main.js"></script>
|
||||
<script src="js/main.js" type="module"></script>
|
||||
</head>
|
||||
<body onload="onload()" class="init">
|
||||
<div id="background" class="scaled">
|
||||
<img class="unloaded" onload="load_img(this)">
|
||||
<img class="unloaded" onload="load_img(this)">
|
||||
</div>
|
||||
<body class="init">
|
||||
<div id="background"></div>
|
||||
<div class="main">
|
||||
<div class="page home hidden" id="page-home">
|
||||
<div class="page home" p="home">
|
||||
<div class="wrapper">
|
||||
<div class="home">
|
||||
<img class="appicon" id="app-icon">
|
||||
@ -23,108 +18,36 @@
|
||||
<div class="appdesc" id="app-desc"></div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div onclick="switch_theme()">
|
||||
<div class="icon" id="theme-indicator">light_mode</div>
|
||||
<div id="theme-switcher">
|
||||
<div class="icon"></div>
|
||||
<div class="text">Theme</div>
|
||||
</div>
|
||||
<div onclick="show('page-services')">
|
||||
<div t="services">
|
||||
<div class="icon">apps</div>
|
||||
<div class="text">Services</div>
|
||||
</div>
|
||||
<div onclick="show('page-more')">
|
||||
<div t="more">
|
||||
<div class="icon">more</div>
|
||||
<div class="text">More</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page hidden" id="page-services">
|
||||
<div class="page" p="services">
|
||||
<div class="back"><div class="icon"></div></div>
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<div class="icon">apps</div>
|
||||
<div class="text">Services</div>
|
||||
</div>
|
||||
<div class="boxes" id="applist"></div>
|
||||
<div class="boxes" id="app-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page hidden" id="page-more" style="--screens: 2">
|
||||
<div class="page" p="more">
|
||||
<div class="back"><div class="icon"></div></div>
|
||||
<div class="wrapper">
|
||||
<div class="selector">
|
||||
<div class="bg"></div>
|
||||
<div class="entry" onclick="open_screen(this)">
|
||||
<div>Overview</div>
|
||||
</div>
|
||||
<div class="entry" onclick="open_screen(this)">
|
||||
<div>Settings</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="screens">
|
||||
<div class="screen">
|
||||
<div class="split">
|
||||
<div class="icon fancy">shield</div>
|
||||
<div class="right">
|
||||
<div class="pp"><b id="security-pp">85</b>%</div>
|
||||
<div class="text"><b id="services-secure"></b> out of <b id="services-total"></b> listed services use secure connections</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxes static">
|
||||
<div class="box">
|
||||
<div class="icon" style="--color: 144deg">lock</div>
|
||||
<div>
|
||||
<div class="name">Encryption</div>
|
||||
<div class="desc">HTTPS ensures data is transferred securely, so nobody can monitor your traffic.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="icon" style="--color: 272deg">dns</div>
|
||||
<div>
|
||||
<div class="name">Self-hosted</div>
|
||||
<div class="desc">All services are independent. No more relying on centralized data-harvesters.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hostedby">All services hosted by <b id="app-hostedby"></b></div>
|
||||
</div>
|
||||
<div class="screen hidden">
|
||||
<div class="warn none" id="nocook">WARNING: due to blocked cookies, all settings will be lost after page reload</div>
|
||||
<div class="settings">
|
||||
<div class="setting" onclick="switch_theme()" id="setting-theme">
|
||||
<div class="icon">dark_mode</div>
|
||||
<div class="text">
|
||||
<div class="name">Dark mode</div>
|
||||
<div class="desc">Make the colors more appropriate for low-light environments</div>
|
||||
</div>
|
||||
<div class="switch"></div>
|
||||
</div>
|
||||
<div class="setting" onclick="new_tab_toggle()" id="setting-newtab">
|
||||
<div class="icon">open_in_new</div>
|
||||
<div class="text">
|
||||
<div class="name">Open in new tab</div>
|
||||
<div class="desc">Clicking an application will open it in a new browser tab</div>
|
||||
</div>
|
||||
<div class="switch"></div>
|
||||
</div>
|
||||
<div class="setting" onclick="blur_toggle()" id="setting-blur">
|
||||
<div class="icon">blur_on</div>
|
||||
<div class="text">
|
||||
<div class="name">Enable blur</div>
|
||||
<div class="desc">Improves UI sweetness but has a huge impact on performance</div>
|
||||
</div>
|
||||
<div class="switch"></div>
|
||||
</div>
|
||||
<div class="setting" onclick="animations()" id="setting-animations">
|
||||
<div class="icon">animation</div>
|
||||
<div class="text">
|
||||
<div class="name">Animations</div>
|
||||
<div class="desc">Show nice and fancy page transitions, improves experience</div>
|
||||
</div>
|
||||
<div class="switch"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warn none" id="no-cookies">WARNING: due to blocked cookies, all settings will be lost after page reload</div>
|
||||
<div class="settings" id="settings"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
28
js/App.js
Normal file
28
js/App.js
Normal file
@ -0,0 +1,28 @@
|
||||
import Drawer from "./UI/Drawer/Drawer"
|
||||
import Home from "./UI/Home/Home"
|
||||
import Main from "./UI/Main/Main"
|
||||
import Settings from "./UI/Settings/Settings"
|
||||
import Config from "./Utils/Config"
|
||||
import { showPage } from "./Utils/DOMUtils"
|
||||
|
||||
|
||||
export default class App {
|
||||
static instance
|
||||
|
||||
constructor(config) {
|
||||
if (App.instance) return App.instance
|
||||
App.instance = this
|
||||
document.body.classList.remove("init")
|
||||
this.config = new Config(config)
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.main = new Main()
|
||||
this.home = new Home()
|
||||
this.drawer = new Drawer()
|
||||
this.settings = new Settings()
|
||||
|
||||
showPage("home")
|
||||
}
|
||||
}
|
27
js/UI/Drawer/Drawer.js
Normal file
27
js/UI/Drawer/Drawer.js
Normal file
@ -0,0 +1,27 @@
|
||||
import App from "../../App";
|
||||
|
||||
export default class Drawer {
|
||||
constructor(name) {
|
||||
this.app = new App()
|
||||
this.config = this.app.config
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.importApps()
|
||||
}
|
||||
|
||||
importApps() {
|
||||
let apps = this.config.getServices()
|
||||
let applist = document.getElementById("app-list");
|
||||
for (let app of apps) {
|
||||
applist.innerHTML += `<a class="box" href="${app.href}">
|
||||
<img src="${app.icon}">
|
||||
<div>
|
||||
<div class="name">${app.name}</div>
|
||||
<div class="desc">${app.desc}</div>
|
||||
</div>
|
||||
</a>`
|
||||
}
|
||||
}
|
||||
}
|
45
js/UI/Home/Home.js
Normal file
45
js/UI/Home/Home.js
Normal file
@ -0,0 +1,45 @@
|
||||
import App from "../../App"
|
||||
import { showPage } from "../../Utils/DOMUtils"
|
||||
|
||||
|
||||
export default class Home {
|
||||
constructor() {
|
||||
this.app = new App()
|
||||
this.config = this.app.config
|
||||
this.init()
|
||||
this.initHomeUI()
|
||||
this.initBackButtons()
|
||||
}
|
||||
|
||||
init() {
|
||||
let buttons = document.getElementsByClassName("buttons")[0].children
|
||||
for (let button of buttons) {
|
||||
let target = button.getAttribute("t")
|
||||
if (target) {
|
||||
button.addEventListener("click", () => {
|
||||
showPage(target)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initBackButtons() {
|
||||
let backButtons = document.getElementsByClassName("back")
|
||||
for (let button of backButtons) {
|
||||
button.addEventListener("click", () => {
|
||||
showPage("home")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
initHomeUI() {
|
||||
let logo = document.getElementById("app-icon")
|
||||
logo.src = this.config.get("icon")
|
||||
|
||||
let name = document.getElementById("app-name")
|
||||
name.innerText = this.config.get("name")
|
||||
|
||||
let desc = document.getElementById("app-desc")
|
||||
desc.innerText = this.config.get("desc")
|
||||
}
|
||||
}
|
26
js/UI/Main/Main.js
Normal file
26
js/UI/Main/Main.js
Normal file
@ -0,0 +1,26 @@
|
||||
import App from "../../App"
|
||||
|
||||
export default class Main {
|
||||
constructor() {
|
||||
this.app = new App()
|
||||
this.config = this.app.config
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
document.title = this.config.get("name")
|
||||
this.initBackgrounds()
|
||||
}
|
||||
|
||||
initBackgrounds() {
|
||||
this.backgrounds = document.getElementById("background")
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let img = document.createElement("img")
|
||||
this.backgrounds.appendChild(img)
|
||||
}
|
||||
|
||||
this.backgrounds = this.backgrounds.children
|
||||
this.backgrounds[0].src = this.config.get("wallpaper")
|
||||
this.backgrounds[1].src = this.config.get("wallpaper_dark")
|
||||
}
|
||||
}
|
78
js/UI/Settings/Settings.js
Normal file
78
js/UI/Settings/Settings.js
Normal file
@ -0,0 +1,78 @@
|
||||
import App from "../../App";
|
||||
import * as EVENTS from "./events";
|
||||
|
||||
export default class Settings {
|
||||
constructor() {
|
||||
this.app = new App()
|
||||
this.config = this.app.config
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.checkLocalStorage()
|
||||
|
||||
let darkMode = this.addOnOffTile(
|
||||
"dark_mode", "Dark mode",
|
||||
"Make the colors more appropriate for low-light environments",
|
||||
"dark_mode", EVENTS.onThemeChange
|
||||
)
|
||||
document.getElementById("theme-switcher").addEventListener("click", () => {
|
||||
darkMode.click()
|
||||
})
|
||||
|
||||
this.addOnOffTile(
|
||||
"open_in_new", "Open in new tab",
|
||||
"Clicking on application will open it in a new browser tab",
|
||||
"open_new_tab", EVENTS.onNewTabChange
|
||||
)
|
||||
|
||||
this.addOnOffTile(
|
||||
"blur_on", "Enable blur",
|
||||
"Improves UI sweetness but may have a huge impact on performance",
|
||||
"blur", EVENTS.onBlurChange
|
||||
)
|
||||
|
||||
this.addOnOffTile(
|
||||
"animation", "Animations",
|
||||
"Show nice and fancy page transitions for improved experience",
|
||||
"animations", EVENTS.onAnimationChange
|
||||
)
|
||||
}
|
||||
|
||||
addOnOffTile(icon, name, desc, key, func) {
|
||||
let item = document.createElement("div")
|
||||
item.classList.add("setting")
|
||||
item.innerHTML = `
|
||||
<div class="icon">${icon}</div>
|
||||
<div class="text">
|
||||
<div class="name">${name}</div>
|
||||
<div class="desc">${desc}</div>
|
||||
</div>
|
||||
<div class="switch"></div>`
|
||||
|
||||
let handleState = () => {
|
||||
let c = item.classList
|
||||
if (this.config.get(key)) c.add("checked")
|
||||
else c.remove("checked")
|
||||
}
|
||||
let write = () => {
|
||||
let target_value = !this.config.get(key)
|
||||
this.config.set(key, target_value)
|
||||
}
|
||||
let f = () => {func(this.config)}
|
||||
|
||||
item.addEventListener("click", write)
|
||||
item.addEventListener("click", handleState)
|
||||
if (func) item.addEventListener("click", f)
|
||||
|
||||
handleState()
|
||||
if (func && this.config.changed(key)) f()
|
||||
document.getElementById("settings").appendChild(item)
|
||||
return item
|
||||
}
|
||||
|
||||
checkLocalStorage() {
|
||||
let warn = document.getElementById("no-cookies").classList
|
||||
if (this.config.storageAvailable) warn.add("hidden")
|
||||
}
|
||||
}
|
26
js/UI/Settings/events.js
Normal file
26
js/UI/Settings/events.js
Normal file
@ -0,0 +1,26 @@
|
||||
const CL = document.body.classList
|
||||
|
||||
export function onThemeChange(config) {
|
||||
let isDark = config.get("dark_mode")
|
||||
isDark ? CL.add("dark") : CL.remove("dark")
|
||||
}
|
||||
|
||||
export function onNewTabChange(config) {
|
||||
let openNewTab = config.get("open_new_tab")
|
||||
let appList = document.getElementById("app-list").children
|
||||
|
||||
for (let app of appList) {
|
||||
if (openNewTab) app.setAttribute("target", "_blank")
|
||||
else app.removeAttribute("target")
|
||||
}
|
||||
}
|
||||
|
||||
export function onBlurChange(config) {
|
||||
let blur = config.get("blur")
|
||||
blur ? CL.remove("noblur") : CL.add("noblur")
|
||||
}
|
||||
|
||||
export function onAnimationChange(config) {
|
||||
let animations = config.get("animations")
|
||||
animations ? CL.remove("noanim") : CL.add("noanim")
|
||||
}
|
47
js/Utils/Config.js
Normal file
47
js/Utils/Config.js
Normal file
@ -0,0 +1,47 @@
|
||||
export default class Config {
|
||||
constructor(config) {
|
||||
this.config = config
|
||||
|
||||
// localStorage availability check
|
||||
try {
|
||||
window.localStorage
|
||||
this.storageAvailable = true
|
||||
}
|
||||
catch (e) {
|
||||
this.storageAvailable = false
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
let value = this.config["ui"][key]
|
||||
|
||||
if (this.storageAvailable) {
|
||||
let type = typeof(value)
|
||||
let stored_value = window.localStorage.getItem(key)
|
||||
|
||||
if (stored_value != null) {
|
||||
value = stored_value
|
||||
if (type == "number") value = Number(value)
|
||||
else if (type == "boolean") value = value == "true"
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.config["ui"][key] = value
|
||||
|
||||
if (this.storageAvailable) {
|
||||
window.localStorage.setItem(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
changed(key) {
|
||||
return this.get(key) != this.config["ui"][key]
|
||||
}
|
||||
|
||||
getServices() {
|
||||
return this.config["services"]
|
||||
}
|
||||
}
|
12
js/Utils/DOMUtils.js
Normal file
12
js/Utils/DOMUtils.js
Normal file
@ -0,0 +1,12 @@
|
||||
export function showPage(target) {
|
||||
let bg = document.getElementById("background").classList
|
||||
if (target == "home") bg.add("scaled")
|
||||
else bg.remove("scaled")
|
||||
|
||||
let pages = document.getElementsByClassName("page")
|
||||
for (let page of pages) {
|
||||
let p = page.getAttribute("p")
|
||||
if (p == target) page.classList.add("current")
|
||||
else page.classList.remove("current")
|
||||
}
|
||||
}
|
11
js/main.js
Normal file
11
js/main.js
Normal file
@ -0,0 +1,11 @@
|
||||
import App from "./App"
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("GET", "config/config.json")
|
||||
xhr.onload = function() {
|
||||
let config = JSON.parse(this.responseText)
|
||||
window.app = new App(config)
|
||||
}
|
||||
xhr.send()
|
||||
})
|
101
public/js/dom.js
101
public/js/dom.js
@ -1,101 +0,0 @@
|
||||
function get(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function set(id, text) {
|
||||
get(id).innerText = text;
|
||||
}
|
||||
|
||||
function get_class(class_name, parent) {
|
||||
if (!parent) parent = document;
|
||||
return parent.getElementsByClassName(class_name);
|
||||
}
|
||||
|
||||
function for_all(class_name, func, parent) {
|
||||
let a = get_class(class_name, parent);
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
func(a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function load_img(img) {
|
||||
img.classList.remove("unloaded");
|
||||
}
|
||||
|
||||
function get_background() {
|
||||
let bg = get("background");
|
||||
return bg.children[1-1+bg.classList.contains("dark")];
|
||||
}
|
||||
|
||||
function safe_text(text) {
|
||||
text = text.replaceAll("<", "<");
|
||||
text = text.replaceAll(">", ">");
|
||||
text = text.replaceAll("&", "&");
|
||||
return text;
|
||||
}
|
||||
|
||||
function mk_entry(app) {
|
||||
let new_tab = get_bool("open_new_tab") ? ` target="_blank"` : "";
|
||||
return `
|
||||
<a class="box" href="${safe_text(app["href"])}"${new_tab}>
|
||||
<img src="${safe_text(app["icon"])}">
|
||||
<div>
|
||||
<div class="name">${safe_text(app["name"])}</div>
|
||||
<div class="desc">${safe_text(app["desc"])}</div>
|
||||
</div>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
function check_cookies() {
|
||||
try {
|
||||
localStorage;
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function config(key, value) {
|
||||
let write = value !== undefined;
|
||||
if (check_cookies()) {
|
||||
let val = localStorage.getItem(key);
|
||||
if (CONFIG["ui"][key] == value && !val) return;
|
||||
if (write) localStorage.setItem(key, value);
|
||||
else if (!val) return CONFIG["ui"][key].toString();
|
||||
return val;
|
||||
}
|
||||
if (write) CONFIG["ui"][key] = value.toString();
|
||||
return CONFIG["ui"][key].toString();
|
||||
}
|
||||
|
||||
function get_bool(key) {
|
||||
return config(key) == "true";
|
||||
}
|
||||
|
||||
function load_config(conf) {
|
||||
CONFIG_DEFAULT = conf;
|
||||
if (conf) CONFIG = JSON.parse(conf);
|
||||
let ui = CONFIG.ui;
|
||||
set("app-name", ui.name);
|
||||
document.title = ui.name;
|
||||
set("app-desc", ui.desc);
|
||||
get("app-icon").src = ui.icon;
|
||||
get("favicon").href = ui.icon;
|
||||
if (!check_cookies()) get("nocook").classList.remove("none");
|
||||
if (ui.hosted_by) set("app-hostedby", ui.hosted_by);
|
||||
else get("app-hostedby").parentNode.style.display = "none";
|
||||
load_apps();
|
||||
switch_theme(get_bool("dark_mode"));
|
||||
new_tab_toggle(get_bool("open_new_tab"));
|
||||
blur_toggle(get_bool("blur"));
|
||||
animations(get_bool("animations"));
|
||||
}
|
||||
|
||||
function is_secure(uri) {
|
||||
let secure = uri.indexOf("tps://") != -1;
|
||||
let insecure = uri.indexOf("tp://") != -1;
|
||||
if (secure) return true;
|
||||
if (insecure) return false;
|
||||
return is_secure(location.href);
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
document.title = "Loading...";
|
||||
function onload() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "config/config.json");
|
||||
xhr.onload = function() {
|
||||
load_config(this.responseText);
|
||||
for_all("back", (btn) => {
|
||||
btn.onclick = show;
|
||||
});
|
||||
setTimeout(() => {
|
||||
show();
|
||||
document.body.classList.remove("init");
|
||||
}, 50);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function show(id) {
|
||||
if (typeof(id) != "string") id = "page-home";
|
||||
CURRENT_VIEW = id;
|
||||
for_all("page", (page) => {
|
||||
page.classList.add("hidden");
|
||||
});
|
||||
get(id).classList.remove("hidden");
|
||||
get(id).scrollTop = 0;
|
||||
let bg = get("background").classList;
|
||||
if (CURRENT_VIEW == "page-home") bg.add("scaled");
|
||||
else bg.remove("scaled");
|
||||
}
|
||||
|
||||
function switch_theme(value) {
|
||||
let is_dark = get_bool("dark_mode");
|
||||
if (value === undefined) is_dark = !is_dark;
|
||||
config("dark_mode", is_dark);
|
||||
get("css_dark").disabled = !is_dark;
|
||||
let bg = get("background").classList;
|
||||
let setting = get("setting-theme");
|
||||
let icon = get("theme-indicator");
|
||||
if (is_dark) {
|
||||
setting.classList.add("checked");
|
||||
icon.innerText = "dark_mode";
|
||||
bg.add("dark");
|
||||
get_background().src = CONFIG.ui.wallpaper_dark;
|
||||
}
|
||||
else {
|
||||
setting.classList.remove("checked");
|
||||
icon.innerText = "light_mode";
|
||||
bg.remove("dark");
|
||||
get_background().src = CONFIG.ui.wallpaper;
|
||||
}
|
||||
}
|
||||
|
||||
function load_apps() {
|
||||
let final = "";
|
||||
let secures = 0;
|
||||
let i = 0;
|
||||
while (i < CONFIG["services"].length) {
|
||||
let item = CONFIG["services"][i];
|
||||
let app = mk_entry(item);
|
||||
final += app;
|
||||
i++;
|
||||
if (is_secure(item["href"])) secures++;
|
||||
}
|
||||
get("applist").innerHTML = final;
|
||||
set("services-total", i);
|
||||
set("services-secure", secures);
|
||||
set("security-pp", Math.round(100 * secures / i));
|
||||
}
|
||||
|
||||
function new_tab_toggle(value) {
|
||||
let v = get_bool("open_new_tab");
|
||||
if (value === undefined) v = !v;
|
||||
config("open_new_tab", v);
|
||||
setting = get("setting-newtab").classList;
|
||||
v ? setting.add("checked") : setting.remove("checked");
|
||||
load_apps();
|
||||
}
|
||||
|
||||
function blur_toggle(value) {
|
||||
let v = get_bool("blur");
|
||||
if (value === undefined) v = !v;
|
||||
config("blur", v);
|
||||
setting = get("setting-blur").classList;
|
||||
let body = document.body.classList;
|
||||
if (v) {
|
||||
setting.add("checked");
|
||||
body.remove("noblur");
|
||||
}
|
||||
else {
|
||||
setting.remove("checked");
|
||||
body.add("noblur");
|
||||
}
|
||||
}
|
||||
|
||||
function animations(value) {
|
||||
let v = get_bool("animations");
|
||||
if (value === undefined) v = !v;
|
||||
config("animations", v);
|
||||
setting = get("setting-animations").classList;
|
||||
let body = document.body.classList;
|
||||
if (v) {
|
||||
setting.add("checked");
|
||||
body.remove("noanim");
|
||||
}
|
||||
else {
|
||||
setting.remove("checked");
|
||||
body.add("noanim");
|
||||
}
|
||||
}
|
||||
|
||||
let S_TAP_LOCK;
|
||||
function open_screen(button) {
|
||||
if (S_TAP_LOCK) return;
|
||||
S_TAP_LOCK = true;
|
||||
let parent = button.parentNode;
|
||||
let cursor = parent.getElementsByClassName("bg")[0];
|
||||
let items = parent.getElementsByClassName("entry");
|
||||
let clicked_id = 0;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i] === button) {
|
||||
clicked_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cursor.style.left = `${100 * clicked_id / items.length}%`;
|
||||
let wrapper = parent.parentNode.parentNode;
|
||||
let screens = get_class("screens", wrapper)[0];
|
||||
let from_height = screens.clientHeight;
|
||||
for_all("screen", (screen) => {
|
||||
screen.classList.add("hidden");
|
||||
}, wrapper);
|
||||
screens.children[clicked_id].classList.remove("hidden");
|
||||
let to_height = screens.children[clicked_id].clientHeight;
|
||||
screens.style.transform = `translateX(calc(-${clicked_id}% * (100 / var(--screens))))`;
|
||||
screens.style.height = `${from_height}px`;
|
||||
setTimeout(() => {
|
||||
screens.style.height = `${to_height}px`;
|
||||
}, 10);
|
||||
setTimeout(() => {
|
||||
screens.style.height = null;
|
||||
S_TAP_LOCK = false;
|
||||
}, 420);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user