+
@@ -23,108 +18,36 @@
-
-
- light_mode
+
+
- Theme
+
-
apps
Services
+
more
More
+
-
+
-
+
-
apps
Services
+
diff --git a/js/App.js b/js/App.js
new file mode 100644
index 0000000..b3b7ba2
--- /dev/null
+++ b/js/App.js
@@ -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")
+ }
+}
diff --git a/js/UI/Drawer/Drawer.js b/js/UI/Drawer/Drawer.js
new file mode 100644
index 0000000..322630e
--- /dev/null
+++ b/js/UI/Drawer/Drawer.js
@@ -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 += `
+
-
-
-
-
-
- Overview
-
-
- Settings
-
-
+
-
-
-
- shield
-
-
- 85%
- out of listed services use secure connections
-
-
-
-
- lock
-
-
- Encryption
- HTTPS ensures data is transferred securely, so nobody can monitor your traffic.
-
-
- dns
-
-
- Self-hosted
- All services are independent. No more relying on centralized data-harvesters.
- All services hosted by
-
-
- WARNING: due to blocked cookies, all settings will be lost after page reload
-
-
-
-
- dark_mode
-
-
-
- Dark mode
- Make the colors more appropriate for low-light environments
-
-
- open_in_new
-
-
-
- Open in new tab
- Clicking an application will open it in a new browser tab
-
-
- blur_on
-
-
-
- Enable blur
- Improves UI sweetness but has a huge impact on performance
-
-
- animation
-
-
-
- Animations
- Show nice and fancy page transitions, improves experience
- WARNING: due to blocked cookies, all settings will be lost after page reload
+
+
+ `
+ }
+ }
+}
diff --git a/js/UI/Home/Home.js b/js/UI/Home/Home.js
new file mode 100644
index 0000000..da385c4
--- /dev/null
+++ b/js/UI/Home/Home.js
@@ -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")
+ }
+}
\ No newline at end of file
diff --git a/js/UI/Main/Main.js b/js/UI/Main/Main.js
new file mode 100644
index 0000000..784a701
--- /dev/null
+++ b/js/UI/Main/Main.js
@@ -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")
+ }
+}
diff --git a/js/UI/Settings/Settings.js b/js/UI/Settings/Settings.js
new file mode 100644
index 0000000..40dfcd9
--- /dev/null
+++ b/js/UI/Settings/Settings.js
@@ -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 = `
+ ${app.name}
+ ${app.desc}
+ ${icon}
+
+
+ `
+
+ 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")
+ }
+}
\ No newline at end of file
diff --git a/js/UI/Settings/events.js b/js/UI/Settings/events.js
new file mode 100644
index 0000000..8b9f6f8
--- /dev/null
+++ b/js/UI/Settings/events.js
@@ -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")
+}
diff --git a/js/Utils/Config.js b/js/Utils/Config.js
new file mode 100644
index 0000000..95f303f
--- /dev/null
+++ b/js/Utils/Config.js
@@ -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"]
+ }
+}
diff --git a/js/Utils/DOMUtils.js b/js/Utils/DOMUtils.js
new file mode 100644
index 0000000..d2c9fbd
--- /dev/null
+++ b/js/Utils/DOMUtils.js
@@ -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")
+ }
+}
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 0000000..577e317
--- /dev/null
+++ b/js/main.js
@@ -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()
+})
diff --git a/public/js/dom.js b/public/js/dom.js
deleted file mode 100644
index 3a1bf89..0000000
--- a/public/js/dom.js
+++ /dev/null
@@ -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 `
-
- ${name}
+ ${desc}
+
-
- `;
-}
-
-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);
-}
\ No newline at end of file
diff --git a/public/js/main.js b/public/js/main.js
deleted file mode 100644
index 4438be1..0000000
--- a/public/js/main.js
+++ /dev/null
@@ -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);
-}
${safe_text(app["name"])}
- ${safe_text(app["desc"])}
-