Full rebase

This commit is contained in:
Daniel 2023-10-14 01:43:36 +02:00
parent 63f51d9cac
commit 8c162678a7
14 changed files with 393 additions and 476 deletions

View File

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

View File

@ -7,7 +7,7 @@
src: url(../font/MaterialSymbolsRounded.woff2) format('woff2'); src: url(../font/MaterialSymbolsRounded.woff2) format('woff2');
} }
html { body {
--color: #222; --color: #222;
--color2: #2229; --color2: #2229;
--background: #EEE8; --background: #EEE8;
@ -17,6 +17,30 @@ html {
--blur: blur(16px); --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 { body {
background: #000; background: #000;
color: var(--color); color: var(--color);
@ -27,25 +51,11 @@ body {
font-size: 14px; font-size: 14px;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
.noblur {
--blur: blur(0);
}
.noanim * {
transition: none !important;
}
::-webkit-scrollbar { ::-webkit-scrollbar {
display: none; display: none;
} }
a {
color: #68F;
}
.init * {
transition: none !important;
}
.init .page {
transition: opacity .3s, top .3s !important;
}
.icon { .icon {
font-family: 'Material Symbols Rounded'; font-family: 'Material Symbols Rounded';
font-weight: normal; font-weight: normal;
@ -61,6 +71,7 @@ a {
-webkit-font-feature-settings: 'liga'; -webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
#background img { #background img {
position: fixed; position: fixed;
top: 0; top: 0;
@ -71,26 +82,28 @@ a {
object-fit: cover; object-fit: cover;
transition: transform .4s, opacity .4s !important; transition: transform .4s, opacity .4s !important;
} }
#background.scaled img { #background.scaled img {
transform: scale(var(--scale-factor)); transform: scale(var(--scale-factor));
} }
#background.dark > img:first-child {
opacity: 0; body #background img {
transform: none; opacity: 1;
} }
#background:not(.dark) > img:last-child {
body:not(.dark) #background img:last-child,
body.dark #background img:first-child {
opacity: 0; 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)); transform: scale(var(--scale-factor));
} }
#background:not(.dark):not(.scaled) > img:last-child {
transform: scale(var(--scale-factor)); body.dark #background.scaled img:first-child,
} body:not(.dark) #background.scaled img:last-child {
.unloaded { transform: none;
opacity: 0;
transform: scale(1) !important;
} }
.main { .main {
@ -100,11 +113,13 @@ a {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.appicon { .appicon {
width: 192px; width: 192px;
height: 192px; height: 192px;
object-fit: cover; object-fit: cover;
} }
.page { .page {
position: fixed; position: fixed;
top: 0; top: 0;
@ -116,6 +131,7 @@ a {
overflow-y: scroll; overflow-y: scroll;
transition: top .4s, opacity .4s, visibility .4s, color .3s; transition: top .4s, opacity .4s, visibility .4s, color .3s;
} }
.home.page { .home.page {
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -124,14 +140,17 @@ a {
overflow: hidden; overflow: hidden;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.page.hidden {
.page:not(.current) {
top: 240px; top: 240px;
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
} }
.home.page.hidden {
.home.page:not(.current) {
top: calc(50% - 64px); top: calc(50% - 64px);
} }
.wrapper { .wrapper {
box-shadow: 2px 2px 8px #0003; box-shadow: 2px 2px 8px #0003;
background: var(--background); background: var(--background);
@ -144,6 +163,7 @@ a {
min-width: 340px; min-width: 340px;
transition: background .2s; transition: background .2s;
} }
.home .wrapper { .home .wrapper {
box-shadow: none; box-shadow: none;
background: transparent; background: transparent;
@ -153,10 +173,12 @@ a {
.appname { .appname {
font-size: 48px; font-size: 48px;
} }
.appdesc { .appdesc {
opacity: .6; opacity: .6;
margin: 2px 12px; margin: 2px 12px;
} }
.buttons { .buttons {
box-shadow: 2px 2px 8px #0002; box-shadow: 2px 2px 8px #0002;
display: flex; display: flex;
@ -169,6 +191,7 @@ a {
justify-content: space-between; justify-content: space-between;
transition: background .3s; transition: background .3s;
} }
.buttons > div { .buttons > div {
padding: 16px; padding: 16px;
margin: 2px; margin: 2px;
@ -177,9 +200,11 @@ a {
width: 100%; width: 100%;
transition: background .2s; transition: background .2s;
} }
.buttons > div:hover { .buttons > div:hover {
background: var(--hover); background: var(--hover);
} }
.buttons .text { .buttons .text {
margin-top: -2px; margin-top: -2px;
} }
@ -204,9 +229,11 @@ a {
border-radius: 20px; border-radius: 20px;
transition: background .2s; transition: background .2s;
} }
.back .icon:hover { .back .icon:hover {
background: var(--hover); background: var(--hover);
} }
.back .icon:after { .back .icon:after {
content: "chevron_left"; content: "chevron_left";
position: absolute; position: absolute;
@ -220,12 +247,7 @@ a {
flex: 1 1 0; flex: 1 1 0;
flex-wrap: wrap; flex-wrap: wrap;
} }
.static {
margin: 16px 6px;
}
.static .box:hover {
background: transparent;
}
.box { .box {
min-width: 292px; min-width: 292px;
flex: 1; flex: 1;
@ -239,9 +261,11 @@ a {
color: inherit; color: inherit;
transition: background .2s; transition: background .2s;
} }
.box:hover { .box:hover {
background: var(--hover); background: var(--hover);
} }
.box .icon { .box .icon {
font-size: 24px; font-size: 24px;
padding: 20px; padding: 20px;
@ -250,116 +274,47 @@ a {
border-radius: 100px; border-radius: 100px;
margin: 2px 12px 2px 2px; margin: 2px 12px 2px 2px;
} }
a.box { a.box {
cursor: pointer; cursor: pointer;
padding: 14px; padding: 14px;
} }
.box img { .box img {
width: 64px; width: 64px;
height: 64px; height: 64px;
object-fit: cover; object-fit: cover;
margin-right: 12px; margin-right: 12px;
} }
.box .name { .box .name {
font-size: 18px; font-size: 18px;
} }
.box .desc { .box .desc {
opacity: .6; opacity: .6;
} }
.header { .header {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 20px 20px 16px; margin: 20px 20px 16px;
} }
.header .icon { .header .icon {
margin-top: 1px; margin-top: 1px;
margin-right: 10px; margin-right: 10px;
} }
.header .text { .header .text {
font-size: 26px; 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 { .settings {
margin: 12px auto 32px; margin: 32px auto;
max-width: 800px; max-width: 800px;
} }
.setting { .setting {
background: var(--bg2); background: var(--bg2);
margin: 8px; margin: 8px;
@ -371,17 +326,21 @@ a.box {
text-align: left; text-align: left;
transition: background .4s; transition: background .4s;
} }
.setting .icon { .setting .icon {
margin-right: 14px; margin-right: 14px;
font-size: 28px; font-size: 28px;
} }
.setting .name { .setting .name {
font-size: 16px; font-size: 16px;
} }
.setting .desc { .setting .desc {
opacity: .6; opacity: .6;
margin-right: 16px; margin-right: 16px;
} }
.setting .switch { .setting .switch {
position: relative; position: relative;
width: 44px; width: 44px;
@ -392,6 +351,7 @@ a.box {
margin: 0 4px 0 auto; margin: 0 4px 0 auto;
transition: border .4s, background .4s; transition: border .4s, background .4s;
} }
.setting .switch:after { .setting .switch:after {
content: ""; content: "";
position: absolute; position: absolute;
@ -404,26 +364,21 @@ a.box {
transform: translateY(-50%); transform: translateY(-50%);
transition: left .2s, background .4s; transition: left .2s, background .4s;
} }
.setting.checked .switch { .setting.checked .switch {
background-color: #68F; background-color: #68F;
border-color: #68F; border-color: #68F;
} }
.setting.checked .switch:after { .setting.checked .switch:after {
left: calc(100% - 20px); left: calc(100% - 20px);
} }
.hostedby {
font-size: 13px;
color: #0007;
margin: 18px 0 20px;
}
.hostedby b {
color: #56F;
}
.warn { .warn {
margin: 8px 0; margin: 24px 0 -8px;
color: #F60; color: #F60;
} }
.none {
.warn.hidden {
display: none; display: none;
} }

View File

@ -5,17 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" id="favicon"> <link rel="icon" id="favicon">
<link rel="stylesheet" type="text/css" href="css/main.css"> <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 src="js/main.js" type="module"></script>
<script type="text/javascript" src="js/dom.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</head> </head>
<body onload="onload()" class="init"> <body class="init">
<div id="background" class="scaled"> <div id="background"></div>
<img class="unloaded" onload="load_img(this)">
<img class="unloaded" onload="load_img(this)">
</div>
<div class="main"> <div class="main">
<div class="page home hidden" id="page-home"> <div class="page home" p="home">
<div class="wrapper"> <div class="wrapper">
<div class="home"> <div class="home">
<img class="appicon" id="app-icon"> <img class="appicon" id="app-icon">
@ -23,108 +18,36 @@
<div class="appdesc" id="app-desc"></div> <div class="appdesc" id="app-desc"></div>
</div> </div>
<div class="buttons"> <div class="buttons">
<div onclick="switch_theme()"> <div id="theme-switcher">
<div class="icon" id="theme-indicator">light_mode</div> <div class="icon"></div>
<div class="text">Theme</div> <div class="text">Theme</div>
</div> </div>
<div onclick="show('page-services')"> <div t="services">
<div class="icon">apps</div> <div class="icon">apps</div>
<div class="text">Services</div> <div class="text">Services</div>
</div> </div>
<div onclick="show('page-more')"> <div t="more">
<div class="icon">more</div> <div class="icon">more</div>
<div class="text">More</div> <div class="text">More</div>
</div> </div>
</div> </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="back"><div class="icon"></div></div>
<div class="wrapper"> <div class="wrapper">
<div class="header"> <div class="header">
<div class="icon">apps</div> <div class="icon">apps</div>
<div class="text">Services</div> <div class="text">Services</div>
</div> </div>
<div class="boxes" id="applist"></div> <div class="boxes" id="app-list"></div>
</div> </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="back"><div class="icon"></div></div>
<div class="wrapper"> <div class="wrapper">
<div class="selector"> <div class="warn none" id="no-cookies">WARNING: due to blocked cookies, all settings will be lost after page reload</div>
<div class="bg"></div> <div class="settings" id="settings"></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> </div>
</div> </div>
</div> </div>

28
js/App.js Normal file
View 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
View 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
View 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
View 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")
}
}

View 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
View 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
View 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
View 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
View 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()
})

View File

@ -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("<", "&lt;");
text = text.replaceAll(">", "&gt;");
text = text.replaceAll("&", "&amp;");
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);
}

View File

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