This commit is contained in:
Turtle committed 2025-12-29 13:52:28 +01:00
1 parent 408e115134
commit 31769d1719
13 files changed
+350 -62

No files matched your search

-6
View File
@@ -6,10 +6,8 @@ async function createTemp() {
"level": 1, "level": 1,
"avatar": null, "avatar": null,
"abilities": [ "abilities": [
"dark_mode"
], ],
"enabled_abilities": [ "enabled_abilities": [
"dark_mode"
], ],
"role": "student", "role": "student",
"courses": [ "courses": [
@@ -22,10 +20,8 @@ async function createTemp() {
"level": 4, "level": 4,
"avatar": "https://static.turtle-coding-gbr.de/profile-pic.png", "avatar": "https://static.turtle-coding-gbr.de/profile-pic.png",
"abilities": [ "abilities": [
"dark_mode"
], ],
"enabled_abilities": [ "enabled_abilities": [
"dark_mode"
], ],
"role": "professor", "role": "professor",
"courses": [ "courses": [
@@ -37,10 +33,8 @@ async function createTemp() {
"level": 4, "level": 4,
"avatar": "https://static.turtle-coding-gbr.de/st", "avatar": "https://static.turtle-coding-gbr.de/st",
"abilities": [ "abilities": [
"dark_mode"
], ],
"enabled_abilities": [ "enabled_abilities": [
"dark_mode"
], ],
"role": "professor", "role": "professor",
"courses": [ "courses": [
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+4 -1
View File
@@ -208,7 +208,9 @@ content {
background-color: lightgray; background-color: lightgray;
} }
.do-not-color-me {
color: black !important;
}
dropdown { dropdown {
position: fixed; position: fixed;
@@ -244,6 +246,7 @@ dropdown > div:last-child label {
padding-bottom: 8px; padding-bottom: 8px;
font-weight: normal; font-weight: normal;
text-align: center; text-align: center;
color: black;
} }
dropdown > div:last-child button { dropdown > div:last-child button {
background: none; background: none;
+74 -12
View File
@@ -65,6 +65,18 @@ function hideOverlay(id) {
for (var i of elms) _onOverlayDestroy(i); for (var i of elms) _onOverlayDestroy(i);
} }
function refreshOverlays() {
const elms = document.getElementsByClassName("overlay");
for (var i of elms) {
const id = i.getAttribute("name");
hideOverlay(id);
showOverlay(id);
}
}
//----------------------------------//
function _main_events() { function _main_events() {
const header_bar_1 = document.querySelector(".header_bar_1"); const header_bar_1 = document.querySelector(".header_bar_1");
if (header_bar_1 != null) { if (header_bar_1 != null) {
@@ -359,6 +371,8 @@ async function getUserData(couchDB) {
} }
var userData = null; var userData = null;
const userDataListener = [];
async function setUserData(couchDB) { async function setUserData(couchDB) {
const res = await getUserData(couchDB); const res = await getUserData(couchDB);
if (res == null) { if (res == null) {
@@ -367,9 +381,17 @@ async function setUserData(couchDB) {
} }
userData = res; userData = res;
updateAllProfiles(res); updateAllProfiles(res);
_constructProfile(res);
while (userDataListener.length > 0) {
const call = userDataListener[userDataListener.length - 1];
userDataListener.pop();
try { call(); } catch(err) { console.error(err); }
}
return true; return true;
} }
async function updateUserData(couchDB, user) { async function updateUserData(couchDB, user = userData) {
const res = await couchDB.get("users"); const res = await couchDB.get("users");
if (res == null) return {"type":"error", "message": "CouchDB failed"}; if (res == null) return {"type":"error", "message": "CouchDB failed"};
@@ -385,9 +407,15 @@ async function updateUserData(couchDB, user) {
function updateAllProfiles(user) { function updateAllProfiles(user) {
const hasProfilAbility = user.enabled_abilities.includes("profile_pic");
const profiles = document.getElementsByClassName("profile"); const profiles = document.getElementsByClassName("profile");
for (var i of profiles) { for (var i of profiles) {
if (hasProfilAbility) {
i.style.backgroundImage = `url("${user.avatar}")`; i.style.backgroundImage = `url("${user.avatar}")`;
} else {
i.style.removeProperty("backgroundImage");
}
i.onclick = _onProfileClick; i.onclick = _onProfileClick;
} }
} }
@@ -412,6 +440,7 @@ function _onProfileClick(event) {
div.innerHTML = ` div.innerHTML = `
<label><b>${form1(userData.name)}</b></label> <label><b>${form1(userData.name)}</b></label>
<button onclick='_onProfilClickOnProfil()'>Profil</button> <button onclick='_onProfilClickOnProfil()'>Profil</button>
<button onclick='showSkillTree()'>Skill-Tree</button>
<button onclick='location.href = "../login.html";'>Abmelden</button> <button onclick='location.href = "../login.html";'>Abmelden</button>
`; `;
dropdown.appendChild(div); dropdown.appendChild(div);
@@ -425,28 +454,45 @@ function _onProfilClickOnProfil() {
_showProfile(userData); _showProfile(userData);
} }
} }
function _showProfile(user) { function _constructProfile(user) {
let elm = getId("profile_overlay"); const elm = document.createElement("overlay");
if (!elm) {
elm = document.createElement("overlay");
elm.setAttribute("id", "profile_overlay"); elm.setAttribute("id", "profile_overlay");
document.body.appendChild(elm); document.body.appendChild(elm);
}
elm.innerHTML = ` elm.innerHTML = `
<div class="overlay_title">Profil-Übersicht</div> <div class="overlay_title">Profil-Übersicht</div>
<span style="display: inline-block; width: 100%; text-align: center;"> <span style="display: inline-block; width: 100%; text-align: center;">
<div style="border-radius: 50%; width: 80px; height: 80px; display: inline-block; margin-bottom: 10px; background-color: var(--accent_color_bg); background-size: 80px 80px; background-image: url('${user.avatar}');"></div> <div style="border-radius: 50%; width: 140px; height: 140px; display: inline-block; margin-bottom: 10px; background-color: var(--accent_color_bg); background-size: 140px 140px; background-image: url('');" id="profile-pic" class="profile-pic"></div>
<br> <br>
<u>${!user.title ? "": user.title+" "}${form1(user.name)}</u> <u style="font-size: 22px;"></u>
<br><br> <br><br>
</span> </span>
<span class="selectable"> <span class="selectable">
Level: ${user.level ?? 0}<br> Level: <label></label><br>
Rolle: ${form1(user.role ?? "Student")} Rolle: <label></label>
</span><br> </span><br>
Avatar: <input style="" value="${user.avatar}"><br><br> <span class="avatar-inp">Avatar: <input style="" value=""><br></span><br>
<button style="font-size: 18px; width: 100%; border-radius: var(--border-radius); font-weight: normal;" onclick="_showProfileSave(this);">Speichern</button> <button style="font-size: 18px; width: 100%; border-radius: var(--border-radius); font-weight: normal; color: black; padding: 5px; background: rgba(255,255,255,0.5);" onclick="_showProfileSave(this);">Speichern</button>
`; `;
}
function _showProfile(user) {
let elm = getId("profile_overlay");
const hasProfilAbility = user.enabled_abilities.includes("profile_pic");
const avatarInp = elm.querySelector(".avatar-inp");
if (hasProfilAbility) {
elm.querySelector(".profile-pic").style.setProperty("background-image", `url('${user.avatar}')`);
elm.querySelector("input").setAttribute("value", user.avatar);
avatarInp.style.display = "unset";
} else {
avatarInp.style.display = "none";
}
elm.querySelector("u").innerText = getUserDisplayName(user);
const labels = elm.getElementsByTagName("label");
labels[0].innerText = user.level ?? 0;
labels[1].innerText = form1(user.role ?? "Student");
showOverlay("profile_overlay"); showOverlay("profile_overlay");
} }
async function _showProfileSave(elm) { async function _showProfileSave(elm) {
@@ -481,3 +527,19 @@ async function playSound(src) {
console.log("Error playing audio:", err); console.log("Error playing audio:", err);
} }
} }
function hexToRGBA(hex) {
let r = 0, g = 0, b = 0;
if (hex.length === 7) {
r = parseInt(hex.slice(1, 3), 16);
g = parseInt(hex.slice(3, 5), 16);
b = parseInt(hex.slice(5, 7), 16);
}
return [r, g, b];
}
function getUserDisplayName(user) {
const hasHonor = (user.enabled_abilities ??= []).includes("title_of_honor");
const title_of_honor = hasHonor ? "(Sir) " : "";
return `${title_of_honor}${ !user.title ? "" : user.title + " " }${ form1(user.name) }`;
}
+207
View File
@@ -0,0 +1,207 @@
const getSkillTree = () => ({
id: "root",
text: "Skill-Tree",
children: [
{
text: "Dark-Mode",
id: "dark_mode",
description: "Ändert das Aussehen zu einem dunklen Design!",
collision: ["pink_mode"],
children: [
{
text: "Pink-Mode",
description: "Ändert das Aussehen zu einem pinken Design!",
id: "pink_mode",
collision: ["dark_mode"],
},
{
text: "Accent-Color",
description: "Verändere die Primärfarbe",
id: "accent_color",
call: `chooseAccentColor();`,
call_unset: `unsetAccentColor()`
}
]
},
{
text: "Profilbild",
id: "profile_pic",
description: "Erlange die Fähigkeit, das Profilbild zu ändern",
children: [
{
text: "Einfacher Rahmen",
id: "simple_border",
description: "Ein einfacher Rahmen, der sich um das Profilbild legt",
collision: ["gold_border", "epic_border"],
children: [
{
text: "Gold-Rahmen",
id: "gold_border",
description: "Ein goldener Rahmen, der sich um das Profilbild legt",
collision: ["simple_border", "epic_border"],
children: []
},
{
text: "Epic-Rahmen",
id: "epic_border",
description: "Ein epischer Rahmen, der sich um das Profilbild legt",
collision: ["simple_border", "gold_border"],
children: [
{
text: "Ehren-Titel",
id: "title_of_honor",
description: "Kriege den Ehrentitel \"Sir\" verliehen"
}
]
}
]
}
],
}
]
});
const _skillCb = {
"dark_mode": function() {
document.body.style = "background: black; color: white;";
},
"dark_mode_unset": function() {
document.body.style = "background: unset; color: unset;";
},
"pink_mode": function() {
document.body.style = `background: rgba(242, 199, 252, 1); color: #5a076d;`;
},
"pink_mode_unset": function() {
_skillCb.dark_mode_unset();
},
"accent_color": function() {
const storage = (userData.skillStorage ??= {})["accent_color"];
if (!storage) return;
const rgb = storage.join(",");
setRootStyle("--accent_color_fg", `rgba(${rgb}, 1)`);
setRootStyle("--accent_color_fg_o", `rgba(${rgb}, 0.5)`);
setRootStyle("--accent_color_bg", `rgba(${rgb}, 1)`);
setRootStyle("--accent_color_bg_o", `rgba(${rgb}, 0.5)`);
setRootStyle("--accent_color_bg_o2", `rgba(${rgb}, 0.25)`);
},
"accent_color_unset": function() {
removeRootStyle("--accent_color_fg");
removeRootStyle("--accent_color_fg_o");
removeRootStyle("--accent_color_bg");
removeRootStyle("--accent_color_bg_o");
removeRootStyle("--accent_color_bg_o2");
},
"simple_border": function() {
setProfileBorder("../imgs/simple_border.png", 8);
},
"unset_simple_border": () => unsetProfileBorder(),
"gold_border": function() {
setProfileBorder("../imgs/gold_border.png", 8);
},
"unset_gold_border": () => unsetProfileBorder(),
"epic_border": function() {
setProfileBorder("../imgs/epic_border.png", 8);
},
"unset_epic_border": () => unsetProfileBorder(),
"title_of_honor": function() {},
"profile_pic": () => updateAllProfiles(userData)
};
function chooseAccentColor() {
var elm = getId("skill_chooseAccentColor");
if (!elm) {
elm = document.createElement("overlay");
elm.setAttribute("id", "skill_chooseAccentColor");
elm.innerHTML = `
<div class="overlay_title">Wähle eine Farbe:</div><br>
<input type="color" style="width: 100%; border-radius: var(--border-radius);"><br><br>
<button onclick="setAccentColor(this)" style="width: 100%; border-radius: var(--border-radius); font-size: 18px; padding: 10px; background: rgba(255, 255, 255, 0.5);">Bestätigen</button>
`;
document.body.appendChild(elm);
}
setTimeout(() => showOverlay("skill_chooseAccentColor"), 1);
}
function unsetAccentColor() {
userData.skillStorage ??= {};
userData.skillStorage["accent_color"] = null;
updateUserData(new CouchDB());
}
function setAccentColor(elm) {
const input = elm.parentElement.querySelector("input");
const rgba = hexToRGBA(input.value);
userData.skillStorage ??= {};
userData.skillStorage["accent_color"] = rgba;
updateUserData(new CouchDB());
hideOverlay("skill_chooseAccentColor");
_skillCb.accent_color();
refreshOverlays();
}
//--------------------------------------------//
function unsetProfileBorder() {
const elms = document.getElementsByClassName("profile");
for (var i of elms) i.innerHTML = '';
}
function setProfileBorder(src, x) {
const elms = document.getElementsByClassName("profile");
const style1 = `
width: 67px;
height: 67px;
border-radius: 50%;
border: none;
background-size: 67px 67px;
position: absolute;
background-image: url('${src}');
transform: translate(-${x}px, calc(-50%));
`;
for (var i of elms) {
i.innerHTML = `<div class="border" style="${style1}"></div>`;
}
const style2 = `
width: 160px;
height: 160px;
border-radius: 50%;
border: none;
background-size: 160px 160px;
position: absolute;
background-image: url('${src}');
transform: translate(-${x*1.2}px, -8px);
`;
const profilPic = getId("profile-pic");
if (profilPic) {
profilPic.innerHTML = `<div class="border" style="${style2}"></div>`;
}
}
function setRootStyle(key, value) {
document.documentElement.style.setProperty(key, value);
}
function removeRootStyle(key) {
document.documentElement.style.removeProperty(key);
}
function exeSkill(id, activate = true) {
const cb = _skillCb[activate ? id: id+"_unset"];
if (typeof(cb) != "function") return;
try { cb(); } catch(err) { console.error(err); }
}
var globalSkills = [];
async function runSkills() {
const skills = userData.enabled_abilities;
globalSkills = skills;
for (var i of skills) exeSkill(i, true);
}
userDataListener.push(runSkills);
+32 -43
View File
@@ -2,46 +2,6 @@
//=> use playLevelSound() -> void; //=> use playLevelSound() -> void;
//=> use playXPSound() -> void; //=> use playXPSound() -> void;
const getSkillTree = () => ({
id: "root",
text: "Skill-Tree",
children: [
{
text: "DarkMode",
id: "dark_mode",
description: "Setzt die Theme auf dunkel!",
children: [
{
text: "Bla",
description: "Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk! Lorem ipsum et idk!",
id: "pink_mode",
children: []
}
]
},
{
text: "1",
id: "1",
description: "one",
children: [
{
text: "2",
id: "2",
description: "two",
children: []
},
{
text: "3",
id: "3",
description: "three",
children: []
}
]
}
]
});
var aSkillPoints = 0; var aSkillPoints = 0;
function showSkillTree() { function showSkillTree() {
@@ -52,6 +12,8 @@ function showSkillTree() {
document.body.appendChild(elm); document.body.appendChild(elm);
} }
elm.innerHTML = ``;
const div = document.createElement("div"); const div = document.createElement("div");
div.style = ` div.style = `
display: inline-block; display: inline-block;
@@ -60,6 +22,7 @@ height: 100%;
overflow: auto; overflow: auto;
margin-top: 20px; margin-top: 20px;
text-align: center; text-align: center;
color: black;
`; `;
var res = generateSkillTree(getSkillTree(), userData.abilities) var res = generateSkillTree(getSkillTree(), userData.abilities)
@@ -77,7 +40,7 @@ text-align: center;
margin-top: 20px; margin-top: 20px;
`; `;
div2.innerHTML = ` div2.innerHTML = `
<div style="padding: 10px 30px; background: rgba(255, 255, 255, 0.6); border-radius: var(--border-radius); display: inline-block;"> <div style="padding: 10px 30px; background: rgba(255, 255, 255, 0.6); border-radius: var(--border-radius); display: inline-block;" class="do-not-color-me">
Skill-Points: <span class="aSkillPoints">${aSkillPoints}</span> Skill-Points: <span class="aSkillPoints">${aSkillPoints}</span>
</div> </div>
`; `;
@@ -99,7 +62,7 @@ function _getRect(parent, data) {
elm.innerHTML = ''; elm.innerHTML = '';
const div = document.createElement("div"); const div = document.createElement("div");
div.style = `display: inline-block; width: 100%; min-width: 300px; text-align: center;`; div.style = `display: inline-block; width: 100%; min-width: 300px; text-align: center; color: black;`;
div.innerHTML = ` div.innerHTML = `
<b style="display: inline-block; margin-top: 5px;">${data.text}</b><br><br> <b style="display: inline-block; margin-top: 5px;">${data.text}</b><br><br>
<span>${data.description}</span><br><br> <span>${data.description}</span><br><br>
@@ -138,15 +101,25 @@ async function activateSkill(elm, data) {
const isEnabled = eAblities.includes(data.id); const isEnabled = eAblities.includes(data.id);
if (isEnabled) { if (isEnabled) {
//disabled
userData.enabled_abilities = eAblities.filter((val) => val != data.id); userData.enabled_abilities = eAblities.filter((val) => val != data.id);
for (var elm of elms) { for (var elm of elms) {
elm.setAttribute("fill", "rgb(187, 243, 183)"); elm.setAttribute("fill", "rgb(187, 243, 183)");
} }
} else { } else {
//enabled
const coll = data.collision ??= [];
userData.enabled_abilities = eAblities.filter((val) => !coll.includes(val));
userData.enabled_abilities.push(data.id); userData.enabled_abilities.push(data.id);
for (var elm of elms) { for (var elm of elms) {
elm.setAttribute("fill", "lightgreen"); elm.setAttribute("fill", "lightgreen");
} }
for (var i of coll) {
exeSkill(i, false);
for (var elm of document.getElementsByClassName("skill_" + i)) {
elm.setAttribute("fill", "rgb(187, 243, 183)");
}
}
} }
elm.innerText = !isEnabled ? "Deaktivieren" : "Aktivieren"; elm.innerText = !isEnabled ? "Deaktivieren" : "Aktivieren";
@@ -154,6 +127,19 @@ async function activateSkill(elm, data) {
getOverlay("skill_tree_details_overlay").innerHTML = ""; getOverlay("skill_tree_details_overlay").innerHTML = "";
await updateUserData(new CouchDB(), userData); await updateUserData(new CouchDB(), userData);
hideOverlay("skill_tree_details_overlay"); hideOverlay("skill_tree_details_overlay");
if (!isEnabled) {
if (typeof(data.call) == "string") {
try { eval(data.call); } catch (err) { console.error(err); }
}
}
exeSkill(data.id, !isEnabled);
if (isEnabled) {
if (typeof(data.call_unset) == "string") {
try { eval(data.call_unset); } catch (err) { console.error(err); }
}
}
refreshOverlays();
} }
async function _buySkill(data) { async function _buySkill(data) {
const elms = document.getElementsByClassName("skill_" + data.id); const elms = document.getElementsByClassName("skill_" + data.id);
@@ -170,6 +156,8 @@ async function _buySkill(data) {
userData.abilities.push(data.id); userData.abilities.push(data.id);
playXPSound();
getOverlay("skill_tree_details_overlay").innerHTML = ""; getOverlay("skill_tree_details_overlay").innerHTML = "";
await updateUserData(new CouchDB(), userData); await updateUserData(new CouchDB(), userData);
@@ -187,6 +175,7 @@ function generateSkillTree(skillTree, abilities) {
/* -------- calc layout -------- */ /* -------- calc layout -------- */
function measure_tree(data) { function measure_tree(data) {
data.children ??= [];
if (data.children.length === 0) { if (data.children.length === 0) {
data._width = nWidth; data._width = nWidth;
} else { } else {
@@ -196,6 +185,7 @@ function generateSkillTree(skillTree, abilities) {
} }
return data._width; return data._width;
} }
measure_tree(skillTree);
function calc_tree(data, x, y) { function calc_tree(data, x, y) {
data._x = x; data._x = x;
@@ -211,7 +201,6 @@ function generateSkillTree(skillTree, abilities) {
}); });
} }
measure_tree(skillTree);
calc_tree(skillTree, 0, 0); calc_tree(skillTree, 0, 0);
/* -------- bounding-stuff -------- */ /* -------- bounding-stuff -------- */
+2
View File
@@ -6,6 +6,8 @@
<link rel="stylesheet" href="../lib/basic.css"> <link rel="stylesheet" href="../lib/basic.css">
<script src="../auth.js"></script> <script src="../auth.js"></script>
<script src="../lib/basic.js"></script> <script src="../lib/basic.js"></script>
<script src="../other/skills.js"></script>
<script src="../other/skilltree.js"></script>
</head> </head>
<body> <body>
<style> <style>
+2
View File
@@ -6,6 +6,8 @@
<link rel="stylesheet" href="../lib/basic.css"> <link rel="stylesheet" href="../lib/basic.css">
<script src="../auth.js"></script> <script src="../auth.js"></script>
<script src="../lib/basic.js"></script> <script src="../lib/basic.js"></script>
<script src="../other/skills.js"></script>
<script src="../other/skilltree.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script>
<script> <script>
const couchDB = new CouchDB(); const couchDB = new CouchDB();
+2
View File
@@ -6,6 +6,8 @@
<link rel="stylesheet" href="../lib/basic.css"> <link rel="stylesheet" href="../lib/basic.css">
<script src="../auth.js"></script> <script src="../auth.js"></script>
<script src="../lib/basic.js"></script> <script src="../lib/basic.js"></script>
<script src="../other/skills.js"></script>
<script src="../other/skilltree.js"></script>
<script> <script>
const couchDB = new CouchDB(); const couchDB = new CouchDB();
setUserData(couchDB); setUserData(couchDB);
+25
View File
@@ -0,0 +1,25 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../lib/basic.css">
<script src="../auth.js"></script>
<script src="../lib/basic.js"></script>
<script src="./uebersicht_overlay.js"></script>
<script src="../other/skills.js"></script>
<script src="../other/skilltree.js"></script>
<title>Übersicht</title>
</head>
<body>
<script>
const couchDB = new CouchDB();
setUserData(couchDB).then((val) => {
});
</script>
</body>
</html>
+2
View File
@@ -3,6 +3,8 @@
<link rel="stylesheet" href="../lib/basic.css"> <link rel="stylesheet" href="../lib/basic.css">
<script src="../auth.js"></script> <script src="../auth.js"></script>
<script src="../lib/basic.js"></script> <script src="../lib/basic.js"></script>
<script src="../other/skills.js"></script>
<script src="../other/skilltree.js"></script>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Beispielseite</title> <title>Beispielseite</title>