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 = "http://127.0.0.1:5984/"; 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 = getURLArgsMap()["user"]; if (!username) { if (debug && typeof (debug_username) == "string") { username = debug_username; } else { return null; } } 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)`); } }