let UserInfo = {}; let PageIntervals = []; let config = { articleRefresh: 300000, messageFadeIn: 300, messageDisappear: 200 }; function isLoggedIn() { "use strict"; return UserInfo.Email && UserInfo.Email.length > 0; } async function setElementClasses(element, newClasses) { // Ensure the element exists if (!element) return; // Clear all existing classes element.className = ''; // Add the new classes to the element element.classList.add(...newClasses); } async function handleResponse(data, successMessage, failureMessage) { "use strict"; const statusMessageContainer = document.getElementById("statusMessageContainer"); const statusMessage = document.createElement("div"); statusMessage.classList.add("status-message"); if (data.Status === 'Success') { 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 FormData(); 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 (typeof deleteFunction === "function") { const th = document.createElement("th"); let deleteBtn = document.createElement('i'); deleteBtn.classList.add("ri-delete-bin-line"); th.appendChild(deleteBtn); 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 (typeof deleteFunction === "function") { const td = document.createElement("td"); const deleteButton = document.createElement('button'); deleteButton.innerHTML = ""; 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 togglememecreate() { "use strict"; let memeContainerElement = document.getElementById("memecreatecontainer"); memeContainerElement.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 (ev.key === "Escape") { togglearticlecreate(); } }); PageIntervals.push(setInterval(renderarticles, config.articleRefresh)); document.getElementById("articleprivilegeinput").setAttribute("max", UserInfo.Privileges); if (UserInfo.Privileges < 2) { 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 (currentSite === "account" && currentPage === "settings") { if (document.getElementById("user-settings")) { await populateUserInfoFields(UserInfo); } if (document.getElementById("admin-settings")) { await listActivationCodes(true); await listUsers(true); } } if (currentSite === "account" && currentPage === "index" && isLoggedIn()) { await showDashboardGreeting(); } if (currentSite === "news" && currentPage === "index") { await articleInit(); } if (currentSite === "account" && currentPage === "files") { await listFiles(); } if (currentSite === "memes" && currentPage === "index") { 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(); UserInfo = {}; 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 && result.Status === "Success") { 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 && result.Status === "Success") { 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 (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initAjax); } else { setTimeout(initAjax, 0); } async function uploadFile() { const fileInput = document.getElementById('fileInput'); const fileForm = document.getElementById('uploadForm'); let data = { action: "uploadFiles" }; for (let i = 0; i < fileInput.files.length; i++) { data[`userFile${i}`] = fileInput.files[i]; } await doAction("/upload", data, "Súbor bol úspešne nahraný", "Nastala chyba pri nahrávaní súboru", false); fileForm.reset(); await listFiles(); } async function deleteFile(fileID) { await doAction("/upload", { action: "deleteFile", file_id: fileID }, "Súbor bol úspešne zmazaný", "Nastala chyba pri mazaní súboru", true); await listFiles(); } async function getFileList() { const resp = await doAction("/upload", { action: "getAllFiles" }, "Zoznam súborov bol úspešne stiahnutý", "Nastala chyba pri sťahovaní zoznamu súborov", true); if (resp.Status === "Success") { return resp.Files; } else { return false; } } async function listFiles() { const fileList = await getFileList(); if (fileList) { await displayList(fileList, "filelist", deleteFile); } } async function addMeme() { let memeTitleElement = document.getElementById("meme_title_input"); let memeTextElement = document.getElementById("meme_text_input"); let memeImageElement = document.getElementById("meme_image_input"); let memeFormElement = document.getElementById("memeAddForm"); await doAction("/meme", { action: "addMeme", meme_title: memeTitleElement.value, meme_text: memeTextElement.value, meme_image_id: memeImageElement.value }, "Meme bol zmazaný", "Nastala chyba pri mazaní meme-u", false); memeFormElement.reset(); await togglememecreate(); } async function deleteMeme(memeId) { await doAction("/meme", { action: "deleteMeme", meme_id: memeId }, "Meme bol zmazaný", "Nastala chyba pri mazaní meme-u", false); await softReload(); } 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; let splitPath = item.Path.split("/"); option.text = `${splitPath[splitPath.length - 1]} - ID: (${item.ID}) Autor: [${item.UploadedBy} (${item.UploadedByID})]`; memeImageSelector.appendChild(option); }); } async function reloadMemeVotes(memeID) { let memeVoteCounterElement = document.getElementById(`meme_votes_counter_${memeID}`); let memeVoteUpvoteElement = document.getElementById(`meme_votes_upvote_${memeID}`); let memeVoteDownvoteElement = document.getElementById(`meme_votes_downvote_${memeID}`); let memeVoteUpvoteButtonElement = document.getElementById(`meme_votes_upvote_button_${memeID}`); let memeVoteDownvoteButtonElement = document.getElementById(`meme_votes_downvote_button_${memeID}`); let memeVoteResponse = await doAction('/meme', { action: "getMemeVotes", meme_id: memeID }, "Počet hlasov k meme-u bol stiahnutý", "Nastala chyba pri sťahovaní počtu hlasov k meme-u", true); let memeVotes = memeVoteResponse.NetVotes; let userVote = memeVoteResponse.UserVote; memeVoteCounterElement.innerText = memeVotes; memeVoteCounterElement.classList.remove("positive", "negative", "neutral"); if (0 < memeVotes) { memeVoteCounterElement.classList.add("positive"); } else if (0 > memeVotes) { memeVoteCounterElement.classList.add("negative"); } else { memeVoteCounterElement.classList.add("neutral"); } memeVoteUpvoteButtonElement.classList.remove('visual_hover'); memeVoteDownvoteButtonElement.classList.remove('visual_hover'); let memeUpvoteVariant = "line"; let memeDownvoteVariant = "line"; if (0 < userVote) { memeUpvoteVariant = "fill"; memeVoteUpvoteButtonElement.classList.add('visual_hover'); } else if (0 > userVote) { memeDownvoteVariant = "fill"; memeVoteDownvoteButtonElement.classList.add('visual_hover'); } await setElementClasses(memeVoteUpvoteElement, [`ri-arrow-up-circle-${memeUpvoteVariant}`]); await setElementClasses(memeVoteDownvoteElement, [`ri-arrow-down-circle-${memeDownvoteVariant}`]) } async function voteMeme(memeID, isUpvote) { let memeVoteUpvoteElement = document.getElementById(`meme_votes_upvote_${memeID}`); let memeVoteDownvoteElement = document.getElementById(`meme_votes_downvote_${memeID}`); let memeVoteDelete = false; if (isUpvote) { if (memeVoteUpvoteElement.classList.contains("ri-arrow-up-circle-fill")) { await deleteVoteMeme(memeID); memeVoteDelete = true; } } else { if (memeVoteDownvoteElement.classList.contains("ri-arrow-down-circle-fill")) { await deleteVoteMeme(memeID); memeVoteDelete = true; } } if (!memeVoteDelete) { await doAction("/meme", { action: "voteMeme", meme_id: memeID, is_upvote: isUpvote }, "Meme bol votovaný", "Nastala chyba pri votovaný", true); } await reloadMemeVotes(memeID); } async function deleteVoteMeme(memeId) { await doAction("/meme", { action: "deleteVoteMeme", meme_id: memeId }, "Hlas na meme bol zmazaný", "Nastala chyba pri mazaní hlasu na meme", true); await reloadMemeVotes(memeId); } async function surveySubmit() { const satisfaction = document.querySelector('input[name="satisfaction"]:checked'); const functionality = document.querySelector('input[name="functionality"]:checked'); const content = document.querySelector('input[name="content"]:checked'); const comment = document.querySelector('textarea[name="comment"]'); if (satisfaction && functionality && content && comment.value) { await doAction("/survey", { action: "surveySubmit", satisfaction: satisfaction.value, functionality: functionality.value, content: content.value, comment: comment.value }, "Zaznamenané", "Nastala chyba"); satisfaction.checked = false; functionality.checked = false; content.checked = false; comment.value = ""; } }