const getId = (id) => document.getElementById(id);
function getOverlay(id) {
const elms = document.getElementsByName(id);
if (elms.length == 0) return null;
return elms[0];
}
function showOverlay(id) {
const elm = getId(id);
if (!elm) return;
const overlay = document.createElement("div");
const styleString = elm.getAttribute("style");
overlay.classList.add("overlay");
overlay.setAttribute("name", id);
overlay.innerHTML = `
${elm.innerHTML}
`;
document.body.appendChild(overlay);
const cb = elm.getAttribute("onopen");
if (typeof cb == "string") {
try {
eval(
`const overlay = document.getElementsByClassName("${id}")[0]; ` +
cb
);
} catch (err) {
console.error(err);
}
}
return overlay;
}
function _onOverlayDestroy(elm) {
const ori_elm = getId(elm.getAttribute("name"));
if (ori_elm) {
const cb = ori_elm.getAttribute("onclose");
if (typeof cb == "string") {
try {
eval(cb);
} catch (err) {
console.error(err);
}
}
}
elm.remove();
}
function hideOverlay(id) {
const elms = document.getElementsByName(id);
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() {
var header_bar_1 = document.querySelector(".header_bar_1");
if (header_bar_1 == null) header_bar_1 = document.querySelector("#header_bar_1");
if (header_bar_1 != null) {
let isFloating = false;
window.addEventListener("load", function () {
if (window.pageYOffset > 0) {
header_bar_1.style.animationDuration = "0s";
header_bar_1.classList.add("header_bar_1_undocked");
isFloating = true;
}
});
window.addEventListener("scroll", function () {
if (window.pageYOffset > 0 && !isFloating) {
header_bar_1.style.animationDuration = "0.5s";
header_bar_1.classList.remove("header_bar_1_docked");
header_bar_1.classList.add("header_bar_1_undocked");
isFloating = true;
} else if (window.pageYOffset === 0 && isFloating) {
header_bar_1.style.animationDuration = "0.5s";
header_bar_1.classList.remove("header_bar_1_undocked");
header_bar_1.classList.add("header_bar_1_docked");
isFloating = false;
}
});
}
}
window.addEventListener("load", (event) => {
_main_events();
});
/* ----------- couch db ----------- */
//http://127.0.0.1:5984/_utils/#_config/couchdb@127.0.0.1
class CouchDB {
static _default_url = "/couchdb/";
constructor() {
this._does_auth_exists_err();
this.request = new XMLHttpRequest();
this.db_name = couchdb_name;
this.is_online = 2;
}
//private methods
_does_auth_exists() {
return (
typeof couchdb_username == "string" &&
typeof couchdb_password == "string" &&
typeof couchdb_name == "string"
);
}
_does_auth_exists_err() {
if (!this._does_auth_exists())
throw 'Keine Auth vorhanden. Digga, mach mal ';
}
_is_error_missing_database(errReason) {
return (
errReason === "no_db_file" || errReason === "Database does not exist."
);
}
_is_error_missing_data(errReason) {
return errReason === "missing";
}
async _ping() {
try {
const response = await fetch(CouchDB._default_url, {
method: "GET",
});
return response.ok;
} catch (err) {
return false;
}
}
_send_db_request(method, url, body = null, errorHandling = true) {
return new Promise(async (resolve, reject) => {
if (this.is_online == 2) {
await this.checkOnlineStatus();
}
if (this.is_online != 1) {
reject("CouchDB is not online!");
return;
}
const dburl = CouchDB._default_url + this.db_name + "/" + url;
const request = this.request;
this.request.onreadystatechange = () => {
if (request.readyState != 4) return;
try {
if (request.status == 200 || request.status == 201) {
var response = JSON.parse(request.responseText);
resolve(response);
}
//errors
else if (request.status == 404) {
var json = JSON.parse(request.responseText);
if (this._is_error_missing_data(json.reason)) {
resolve(null);
} else if (
errorHandling &&
this._is_error_missing_database(json.reason)
) {
this.createDB()
.then((value) => {
this._send_db_request(method, url, body, false)
.then((value) => {
resolve(value);
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
} else {
reject(
`Database not found: ${request.status} (Reason: ${reason.json})`,
);
}
} else {
reject(
`Unexpected status: ${request.status} (Response: ${
request.responseText
})`,
);
}
} catch (err) {
reject("Unexpected error: " + err);
}
};
this.request.open(method, dburl, true);
this.request.setRequestHeader(
"Authorization",
"Basic " + btoa(couchdb_username + ":" + couchdb_password),
);
this.request.setRequestHeader("Content-type", "application/json");
this.request.send(body);
});
}
async _get_db_data(id) {
return await this._send_db_request("GET", id);
}
//public methods
async checkOnlineStatus() {
const res = await this._ping();
if (res === true) {
this.is_online = 1;
} else {
this.is_online = 0;
}
return res;
}
async createDB() {
await this._send_db_request("PUT", "", null, false);
}
async get(id) {
const res = await this._get_db_data(id);
if (res == null) return null;
delete res["_id"];
delete res["_rev"];
return res;
}
async set(id, content, replace = true) {
try {
if (typeof content != "object")
throw (
"basic.js - CouchDB->set: content is not from type object (type = " +
typeof content +
")"
);
let existingDoc;
try {
existingDoc = await this._get_db_data(id);
} catch (err) {
existingDoc = null;
}
if (existingDoc) {
if (replace) {
content = {
...content,
_id: existingDoc._id,
_rev: existingDoc._rev,
};
} else {
content = {
...existingDoc,
...content,
_rev: existingDoc._rev,
};
}
}
const res = await this._send_db_request(
"PUT",
id,
JSON.stringify(content),
);
return {
type: "success",
message: res,
};
} catch (err) {
console.error(err);
return {
type: "error",
message: err,
};
}
}
async delete(id) {
try {
var existingDoc = null;
try {
existingDoc = await this._get_db_data(id);
} catch (err) {}
if (!existingDoc) return {
type: "error",
message: "Not found!",
};
const res = await this._send_db_request(
"DELETE",
`${id}?rev=${existingDoc._rev}`
);
return {
type: "success",
message: res,
};
} catch (err) {
console.error(err);
return {
type: "error",
message: err,
};
}
}
}
//getuser
function getURLArgsMap() {
let spl = location.href.split("?");
if (spl.length != 2) return {};
let _1 = spl[1].split(",");
const res = {};
for (var i of _1) {
let _2 = i.split("=");
if (_2.length == 2) {
res[_2[0]] = _2[1];
} else {
res[i] = null;
}
}
return res;
}
function form1(string) {
if (!string) return string;
return string.charAt(0).toUpperCase() + string.slice(1);
}
async function getUserData(couchDB) {
let username = sessionStorage.getItem("user");
const res = await couchDB.get("users");
if (!res) return null;
const data = res[username];
return !data ? null : {
...data,
"name": username
};
}
var userData = null;
const userDataListener = [];
async function setUserData(couchDB) {
const user = await getUserData(couchDB);
if (user == null) {
location.href = "../login.html";
return false;
}
userData = user;
updateAllProfiles(user);
_constructProfile(user);
await checkUserCourses(couchDB, user);
while (userDataListener.length > 0) {
const call = userDataListener[userDataListener.length - 1];
userDataListener.pop();
try { call(); } catch(err) { console.error(err); }
}
return true;
}
async function updateUserData(couchDB, user = userData) {
const res = await couchDB.get("users");
if (res == null) return {"type":"error", "message": "CouchDB failed"};
const name = user["name"];
if (!name) return {"type":"error", "message": "Incorrect UserData was presented"};
const u = { ...user };
delete u["name"];
res[name] = u;
return await couchDB.set("users", res);
}
function updateAllProfiles(user) {
const features = user.enabled_abilities ??= [];
const hasProfilAbility = features.includes("profile_pic");
const profiles = document.getElementsByClassName("profile");
for (var i of profiles) {
if (hasProfilAbility && user.avatar) {
i.style.backgroundImage = `url("${user.avatar}")`;
} else {
if (features.includes("pink_mode")) {
i.style.backgroundImage = `url("https://static.turtle-coding-gbr.de/gmci/rosa_profil.png")`;
} else {
i.style.backgroundImage = `url("https://static.turtle-coding-gbr.de/gmci/standart_profil.png")`;
}
}
i.onclick = _onProfileClick;
}
}
async function checkUserCourses(couchDB, userData) {
for (var cName of userData.courses) {
const course = await couchDB.get("course:" + cName);
if (!course || !course.expire_date) continue;
const expire_date = new Date(course.expire_date);
if (expire_date < new Date()) {
course.result_history ??= [];
course.questions ??= [];
course.result ??= [];
if (course.questions.length == 0 || course.result.length == 0) continue;
course.result_history.push({
"date": course.start_date ?? expire_date,
"questions": course.questions,
"result": course.result
});
course.questions = [];
course.result = [];
await couchDB.set("course:" + cName, course);
console.log(`Course ${cName} expired`);
}
}
}
function _onProfileClick(event) {
var buttonRect = this.getBoundingClientRect();
var dropdown = document.createElement("dropdown");
dropdown.addEventListener("click", dropdown.remove);
var bg = document.createElement("div");
dropdown.appendChild(bg);
var div = document.createElement("div");
const _1 = window.innerHeight - buttonRect.y - 200;
if (_1 < 0) {
div.style.transform = "translate(calc(-100% + 58px), calc(-100% - 30px))";
}
div.style.top = (buttonRect.bottom) + 'px';
div.style.left = (buttonRect.left) + 'px';
div.innerHTML = `
`;
dropdown.appendChild(div);
document.body.appendChild(dropdown);
}
function _onProfilClickOnProfil() {
if (typeof(showProfile) == "function") {
showProfile(userData);
} else {
_showProfile(userData);
}
}
function _constructProfile(user) {
const elm = document.createElement("overlay");
elm.setAttribute("id", "profile_overlay");
document.body.appendChild(elm);
elm.innerHTML = `
Profil-Übersicht
Level:
Rolle:
Avatar:
`;
}
function _showProfile(user) {
let elm = getId("profile_overlay");
const features = user.enabled_abilities ??= [];
const hasProfilAbility = features.includes("profile_pic");
const avatarInp = elm.querySelector(".avatar-inp");
const profilePic = elm.querySelector(".profile-pic");
if (hasProfilAbility) {
profilePic.style.setProperty("background-image", `url('${user.avatar}')`);
elm.querySelector("input").setAttribute("value", user.avatar);
avatarInp.style.display = "unset";
} else {
if (features.includes("pink_mode")) {
profilePic.style.setProperty("background-image", `url("https://static.turtle-coding-gbr.de/gmci/rosa_profil.png")`);
} else {
profilePic.style.setProperty("background-image", `url("https://static.turtle-coding-gbr.de/gmci/standart_profil.png")`);
}
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");
}
async function _showProfileSave(elm) {
if (!userData) return;
let couchDB = new CouchDB();
const res = await couchDB.get("users");
const avatar = elm.parentElement.querySelector("input");
userData.avatar = avatar.value;
const user = {...userData};
delete user["name"];
res[userData.name] = user;
await couchDB.set("users", res);
updateAllProfiles(userData);
hideOverlay("profile_overlay");
}
async function playSound(src) {
const elm = document.createElement("audio");
elm.innerHTML = `
`;
try {
await elm.play();
} catch(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) }`;
}
async function levelUpXP(couchDB, user = userData, xp = 1) {
var resLevelUp = false;
userData.xp ??= 0;
userData.level ??= 0;
userData.xp += xp;
while (userData.xp >= userData.level+1) {
userData.xp -= (userData.level + 1);
userData.level++;
resLevelUp = true;
}
if (userData.xp < 0) {
console.error("UserData-XP was below zero!");
userData.xp = 0;
}
await updateUserData(couchDB, user);
return resLevelUp;
}
function setXPBarPercent(percent) {
percent = 100 - percent;
const elms = document.querySelectorAll(".mc-xp-bar");
for (var elm of elms) {
const div = elm.querySelector("div");
div.style.setProperty('clip-path', `inset(0 ${percent}% 0 0)`);
}
}