let UserInfo = {}; let PageIntervals = []; let config = { articleRefresh: 300000, messageFadeIn: 300, messageDisappear: 200 }; function isLoggedIn() { "use strict"; return UserInfo.Email && 0 < UserInfo.Email.length; } function until(conditionFunction) { const poll = resolve => { if(conditionFunction()) resolve(); else setTimeout(_ => poll(resolve), 400); } return new Promise(poll); } async function handleResponse(data, successMessage, failureMessage) { "use strict"; const statusMessageContainer = document.getElementById("statusMessageContainer"); const statusMessage = document.createElement("div"); statusMessage.classList.add("status-message"); if ('Success' === data.Status) { statusMessage.innerText = successMessage; statusMessage.classList.add("success"); } else { statusMessage.innerText = failureMessage; statusMessage.classList.add("failure"); } statusMessageContainer.appendChild(statusMessage); // Automatically remove the message after 3 seconds setTimeout(() => { statusMessage.style.opacity = "0"; setTimeout(() => { statusMessage.remove(); }, config.messageFadeIn); }, config.messageDisappear); } async function showDashboardGreeting() { "use strict"; document.getElementById("welcomeMsg").innerText = `Ahoj, ${UserInfo.FirstName}.`; } async function doAction(url, requestData, successMessage, failureMessage, silent) { "use strict"; const params = new URLSearchParams(); for (const key in requestData) { params.append(key, requestData[key]); } const response = await fetch(url, { method: 'POST', body: params, }); if (!response.ok) { console.error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); if (!silent) { await handleResponse(data, successMessage, failureMessage); } return data; } async function handlePageResponse(data) { "use strict"; const navbar = document.getElementById("navbar_container"); const pageArea = document.getElementById("page_container"); if (data.Navigation) { navbar.innerHTML = data.Navigation; } if (data.PageTitle) { document.title = data.PageTitle; } if (data.Page) { pageArea.innerHTML = data.Page; if (data.PageLocation) { history.pushState({}, "", data.PageLocation); } } } async function displayList(data, elementId, deleteFunction) { "use strict"; const tableContainer = document.getElementById(elementId); tableContainer.innerHTML = ""; // Clear previous content const table = document.createElement("table"); table.classList.add("list-table"); const headerRow = table.insertRow(0); for (const key in data[0]) { const th = document.createElement("th"); th.appendChild(document.createTextNode(key)); headerRow.appendChild(th); } if ("function" === typeof deleteFunction) { const th = document.createElement("th"); th.appendChild(document.createTextNode("Delete")); headerRow.appendChild(th); } for (const line of data) { const dataRow = table.insertRow(); for (const key in line) { const td = document.createElement("td"); td.appendChild(document.createTextNode(line[key])); dataRow.appendChild(td); } if ("function" === typeof deleteFunction) { const td = document.createElement("td"); const deleteButton = document.createElement('button'); deleteButton.textContent = "Delete"; deleteButton.onclick = () => deleteFunction(line.ID); td.appendChild(deleteButton); dataRow.appendChild(td); } } tableContainer.appendChild(table); } async function doPageAction(requestData) { "use strict"; const response = await fetch('/page', { method: 'POST', body: new URLSearchParams(requestData), }); if (!response.ok) { console.error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); await handlePageResponse(data); return data; } async function initAjaxNavigationEvents() { "use strict"; const allLinks = document.querySelectorAll('.navsite_link, .navpage_link'); const pageLinks = document.querySelectorAll('.navpage_link'); pageLinks.forEach(function (link) { link.addEventListener('click', function () { navLinks.classList.remove("active"); }); }); allLinks.forEach(function (link) { link.addEventListener('click', function (e) { e.preventDefault(); let site = this.dataset.site; let page = this.dataset.page; if (site && page) { navigateTo(site, page); } }); }); const toggleButton = document.getElementById("toggle_button"); const navLinks = document.getElementById("navsite_list"); toggleButton.addEventListener('click', () => { navLinks.classList.toggle("active"); }); } async function initAjax() { "use strict"; await initAjaxNavigationEvents(); await onPageLoad(); } async function togglearticlecreate() { "use strict"; let articleContainerElement = document.getElementById("articlecreatecontainer"); articleContainerElement.classList.toggle("hidden"); } async function renderarticles() { "use strict"; let template = document.querySelector('template[data-template-name="article"]').innerHTML; let articles = await doAction( "/newsarticle", { action: "getNewsArticles" }, "Články načítané", "Nastala chyba pri načítavaní článkov", true ); let articleout = ""; for (const article of articles.Articles) { articleout += template.replace("__TEMPLATE_ARTICLE_TITLE__", article.Title).replace("__TEMPLATE_ARTICLE_AUTHOR__", article.WrittenByName).replace("__TEMPLATE_ARTICLE_DATE__", article.WrittenAt).replace("__TEMPLATE_ARTICLE_BODY__", article.Body); } document.getElementById("articleslist").innerHTML = articleout; } async function submitarticle() { "use strict"; let articleTitleElement = document.getElementById("articletitleinput"); let articleBodyElement = document.getElementById("articlebodyinput"); await doAction( "/newsarticle", { action: "addNewsArticle", title: articleTitleElement.value, body: articleBodyElement.value }, "Článok úspešne pridaný", "Nastala chyba pri pridávaní článku", false ); await togglearticlecreate(); } async function articleInit() { "use strict"; let articleContainerElement = document.getElementById("articlecreatecontainer"); let articleCreateOpenElement = document.getElementById("articlecreateopen"); articleContainerElement.addEventListener("keyup", function (ev) { if ("Escape" === ev.key) { togglearticlecreate(); } }); PageIntervals.push(setInterval(renderarticles, config.articleRefresh)); document.getElementById("articleprivilegeinput").setAttribute("max", UserInfo.Privileges); if (2 > UserInfo.Privileges) { articleContainerElement.style.display = "none"; articleCreateOpenElement.style.display = "none"; } else { articleCreateOpenElement.style.display = "inline-block"; } } async function onPageLoad() { "use strict"; await restoreUserInfo(); let currentSite = localStorage.getItem("currentSite"); let currentPage = localStorage.getItem("currentPage"); for (let interval of PageIntervals) { clearInterval(interval); } if ("home" === currentSite && "settings" === currentPage) { if (document.getElementById("user-settings")) { await populateUserInfoFields(UserInfo); } if (document.getElementById("admin-settings")) { await listActivationCodes(true); await listUsers(true); } } if ("account" === currentSite && "index" === currentPage && isLoggedIn()) { await showDashboardGreeting(); } if ("news" === currentSite && "index" === currentPage) { await articleInit(); } if ("files" === currentSite && "list" === currentPage) { await listFiles(); } if ("memes" === currentSite && "create" === currentPage) { await getMemeImages(); } } async function navigateTo(site, page) { "use strict"; const data = { action: "getPage", site: site, page: page, }; doPageAction(data).then(() => { localStorage.setItem("currentSite", site); localStorage.setItem("currentPage", page); onPageLoad(); }); } async function softReload() { "use strict"; let currentSite = localStorage.getItem("currentSite"); let currentPage = localStorage.getItem("currentPage"); await navigateTo(currentSite, currentPage); umami.track("softReload"); } async function refreshNavbar() { "use strict"; const data = { action: "getNavigation", }; await doPageAction(data); umami.track("refreshNavbar"); await initAjaxNavigationEvents(); } async function logout() { "use strict"; const data = { action: "logout", }; doAction('/account', data, "Logout Successful!", "Logout failed.", false) .then(async () => { await refreshNavbar(); await navigateTo(localStorage.getItem("defaultSite"), localStorage.getItem("defaultPage")); localStorage.clear(); umami.track("logout"); }) .catch((error) => { // Handle errors if needed console.error("An error occurred during logout:", error); }); } async function login() { "use strict"; const email = document.getElementById("login_email").value; const password = document.getElementById("login_password").value; await doLogin(email, password); await getUserInfo(); await refreshNavbar(); await softReload(); } async function doLogin(email, password) { "use strict"; const data = { action: "login", email: email, password: password, }; await doAction('/account', data, "Login Successful!", "Login failed. Please check your credentials.", false); umami.track("login"); } async function register() { "use strict"; const firstName = document.getElementById("register_firstName").value; const lastName = document.getElementById("register_lastName").value; const email = document.getElementById("register_email").value; const password = document.getElementById("register_password").value; const activationToken = document.getElementById("register_activationToken").value; const data = { action: "register", firstname: firstName, lastname: lastName, email: email, password: password, activation_token: activationToken, }; await doRegister(data); } async function doRegister(requestData) { "use strict"; await doAction('/account', requestData, "Registration Successful!", "Registration failed.", false); umami.track("register"); } //User settings start async function changePassword() { "use strict"; const oldPassword = document.getElementById("changeOldPassword").value; const newPassword = document.getElementById("changeNewPassword").value; const data = { action: "change_password", old_password: oldPassword, new_password: newPassword, }; await doChangePassword(data, "Password change Successful!", "Password change failed."); } async function doChangePassword(requestData, successMessage, failureMessage) { "use strict"; await doAction('/account', requestData, successMessage, failureMessage, false); umami.track("passwordChange"); } async function updateUserProfile() { "use strict"; const firstName = document.getElementById("updateFirstName").value; const lastName = document.getElementById("updateLastName").value; const nickname = document.getElementById("updateNickname").value; const minecraftNick = document.getElementById("updateMinecraftNick").value; const data = { action: "update_user_profile", first_name: firstName, last_name: lastName, nickname: nickname, minecraft_nick: minecraftNick, }; await doAction('/account', data, "Profile update Successful!", "Profile update failed.", false); umami.track("updateUserProfile"); } async function updateEmail() { "use strict"; const newEmail = document.getElementById("updateNewEmail").value; const data = { action: "update_user_email", email: newEmail, }; await doAction('/account', data, "Email update Successful!", "Email update failed.", false); umami.track("updateEmail"); } async function populateUserInfoFields(userData) { "use strict"; document.getElementById("updateFirstName").value = userData.FirstName || ""; document.getElementById("updateLastName").value = userData.LastName || ""; document.getElementById("updateNickname").value = userData.Nickname || ""; document.getElementById("updateMinecraftNick").value = userData.MinecraftNick || ""; document.getElementById("updateNewEmail").value = userData.Email || ""; } async function restoreUserInfo() { "use strict"; for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i); if (key.startsWith("UserInfo_")) { let keyClean = key.replace("UserInfo_", ""); UserInfo[keyClean] = localStorage.getItem(key); } } } async function getUserInfo() { "use strict"; const data = { action: "get_user_info", }; const result = await doAction('/account', data, "User info retrieved Successfully!", "User info retrieval failed.", true); if (result && "Success" === result.Status) { Object.keys(result.UserInfo).forEach(index => { let value = result.UserInfo[index]; localStorage.setItem("UserInfo_" + index, value); UserInfo[index] = value; }); } } //User settings end //Admin settings start async function addActivationCodes() { "use strict"; const count = document.getElementById("activationCodeCount").value; const data = { action: "add_activation_codes", count: count, }; doAction('/account', data, "Activation codes added Successfully!", "Activation codes addition failed.", false).then((result) => { displayList(result.ActivationCodes, "codeListTable", deleteActivationCode); umami.track("addActivationCodes"); }); } async function listUsers(silent) { "use strict"; const data = { action: "list_users", }; doAction('/account', data, "User list retrieved Successfully!", "User list retrieval failed.", silent).then((result) => { if (result && "Success" === result.Status) { displayList(result.Users, "userListTable", deleteUser); } }); } async function listActivationCodes(silent) { "use strict"; const data = { action: "list_activation_codes", }; doAction('/account', data, "Activation code list retrieved Successfully!", "Activation code list retrieval failed.", silent).then((result) => { displayList(result.ActivationCodes, "codeListTable", deleteActivationCode); }); } async function deleteUser(userId) { "use strict"; const data = { action: "delete_user", user_id: userId, }; await doAction('/account', data, "User deleted Successfully!", "User deletion failed.", false); await listUsers(false); umami.track("deleteUser"); } async function deleteActivationCode(activationCode) { "use strict"; const data = { action: "delete_activation_code", activation_code: activationCode, }; await doAction('/account', data, "Activation code deleted Successfully!", "Activation code deletion failed.", false); await listActivationCodes(false); umami.track("deleteActivationCode"); } //Admin settings end if ("loading" === document.readyState) { document.addEventListener("DOMContentLoaded", initAjax); } else { setTimeout(initAjax, 0); } function uploadFile() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; let formData = new FormData(); formData.append('userfile', file); formData.append('action', 'uploadFiles'); let xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); xhr.onload = function () { const respData = JSON.parse(xhr.responseText); handleResponse(resp, "Súbor bol úspešne nahraný", "Nastala chyba pri nahrávaní súboru"); fileInput.reset(); }; xhr.send(formData); } function deleteFile(fileID) { let formData = new FormData(); formData.append('action', 'deleteFile'); formData.append('file_id', fileID); let xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); xhr.send(formData); } async function getFileList() { let formData = new FormData(); formData.append('action', 'getAllFiles'); let xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); let tmp; xhr.onload = function () { if (xhr.status === 200) { const resp = JSON.parse(xhr.responseText); if (resp.Status == "Success") { tmp = resp.Files; } else { tmp = false; } } }; xhr.send(formData); until(_ => (tmp !== undefined)); return tmp; } async function listFiles() { const fileList = getFileList(); if(fileList){ displayList(fileList, "filelist", deleteFile); } } function addMeme() { let formData = new FormData(); let memeTitleElement = document.getElementById("meme_title_input"); let memeTextElement = document.getElementById("meme_text_input"); let memeImageElement = document.getElementById("meme_image_input"); formData.append('action', 'addMeme'); formData.append('meme_title', memeTitleElement.value); formData.append('meme_text', memeTextElement.value); formData.append('meme_image_id', memeImageElement.value); let xhr = new XMLHttpRequest(); xhr.open('POST', '/meme', true); xhr.onload = function () { const resp = JSON.parse(xhr.responseText); handleResponse(resp, "Meme bol pridaný", "Nastala chyba pri pridávaní meme-u"); memeTitleElement.reset(); memeTextElement.reset(); memeImageElement.reset(); }; xhr.send(formData); } async function getMemeImages() { let memeImageSelector = document.getElementById("meme_image_input"); let fileList = await getFileList(); fileList.forEach((item) => { let option = document.createElement("option"); option.value = item.ID; option.text = `${item.Path.split("/")[-1]} - (${item.ID}) [${item.UploadedBy}]`; memeImageSelector.appendChild(option); }); }