var prot = window.location.protocol; async function updateProtocolAndUrl(host) { prot = window.location.protocol == "miarven:" ? "http:" : window.location.protocol; if (prot == "http:") { try { JSON.parse(await fetchAsync(`${prot}//${host}/_larpix/serverinfo`)); } catch (error) { try { JSON.parse(await fetchAsync(`https://${host}/_larpix/serverinfo`)); prot = "https:"; } catch (error) {} } } url = `${prot}//${host}/_larpix`; } console.log(prot); var url = `${prot}//${window.location.hostname}/_larpix`; var params = new URLSearchParams(window.location.search); try { var loadingScreen = document.querySelector("loading"); var mainScreen = document.querySelector("main"); var loadingStatus = document.getElementById("loadingstatus"); var addDmBtn = document.getElementById("add-dm-btn"); var addGroupBtn = document.getElementById("add-group-btn"); var sidebarHome = document.getElementById("sidebar-home"); var sidebarHomeButton = sidebarHome.children.item(1); var sidebarHomeIndicator = sidebarHome.children.item(0); var sidebarAdd = document.getElementById("sidebar-add"); var sidebarAddButton = sidebarAdd.children.item(1); var sidebarAddIndicator = sidebarAdd.children.item(0); var roomDetailsBar = document.getElementById("roomdetailsbar"); var roomContent = document.getElementsByTagName("roomcontent")[0]; var roomsBar = document.getElementsByTagName("roomcontent2")[0]; var sideBar = document.getElementsByTagName("sidebar")[0]; var roomContentMain = document.getElementsByTagName("roomcontent2")[1]; var roomDetailsMain = document.getElementsByTagName("roomcontent2")[2]; var roomContentBar = roomContent.children[0]; var roomsTopBar = document.getElementsByTagName("roomtopbar")[0]; var roomTopBar = document.getElementsByTagName("roomtopbar")[1]; var detailsTopBar = document.getElementsByTagName("roomtopbar")[2]; var sidebarProfile = document.getElementById("sidebar-profile"); var sidebarProfileButton = sidebarProfile.children.item(1); var sidebarProfileIndicator = sidebarProfile.children.item(0); var sidebarPfp = sidebarProfileButton.children.item(0); var sidebarInbox = document.getElementById("sidebar-inbox"); var sidebarInboxButton = sidebarInbox.children.item(1); var sidebarInboxIndicator = sidebarInbox.children.item(0); var fixedContextMenu = document.getElementById("fixed-context-menu"); } catch (e) { } function delay(time) { return new Promise(resolve => setTimeout(resolve, time)); } async function packetEncPass(pass, key, username) { return await encryptWithNonce(pass, key, getNonce(username, key)); } async function getNonce(username, key) { let nonce; let fetchRes = await (await fetch(`${url}/nextnonce?u=${username}`)).text(); try { nonce = await decryptString(fetchRes, key); } catch (err) { nonce = await decryptString(fetchRes, ""); } return nonce; } async function encryptWithNonce(value, key, nonce) { return await encryptString(value, nonce + key); } async function calcCommunicationKeyClient(p, g, pubServer) { const secretClientBytes = window.crypto.getRandomValues(new Uint8Array(512)); const secretClient = BigInt('0x' + Array.from(secretClientBytes).map(b => b.toString(16).padStart(2, '0')).join('')); const pubClient = power(BigInt(g), secretClient, BigInt(p)); const sharedSecret = power(BigInt(pubServer), secretClient, BigInt(p)); let sharedSecretStr = sharedSecret.toString(16); if (sharedSecretStr.length % 2 !== 0) { sharedSecretStr = '0' + sharedSecretStr; } const sharedSecretBytes = new Uint8Array(sharedSecretStr.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); const hashBuffer = await window.crypto.subtle.digest('SHA-256', sharedSecretBytes); const aesKey = new Uint8Array(hashBuffer); return [pubClient.toString(), aesKey]; } function keyDataFromServerJson(jsonFromServer) { const data = JSON.parse(jsonFromServer); const p = BigInt(data.p); const g = BigInt(data.g); const pubServer = BigInt(data.pubServer); return [p, g, pubServer, data.idKey] } function base64ToUint8(base64) { return Uint8Array.from(atob(base64), c => c.charCodeAt(0)); } function uint8ToBase64(uint8) { return fixBase64Padding(btoa(String.fromCharCode(...uint8))); } async function encrypt(plainText, keyBytes) { const iv = window.crypto.getRandomValues(new Uint8Array(16)); const encoder = new TextEncoder(); const data = encoder.encode(plainText); const cryptoKey = await window.crypto.subtle.importKey( "raw", keyBytes, "AES-CBC", false, ["encrypt"] ); const encryptedContent = await window.crypto.subtle.encrypt( {name: "AES-CBC", iv: iv}, cryptoKey, data ); const result = new Uint8Array(iv.length + encryptedContent.byteLength); result.set(iv); result.set(new Uint8Array(encryptedContent), iv.length); return uint8ToBase64(result); } async function decrypt(cipherTextBase64, keyBytes) { const fullData = base64ToUint8(cipherTextBase64); const iv = fullData.slice(0, 16); const cipherData = fullData.slice(16); const cryptoKey = await window.crypto.subtle.importKey( "raw", keyBytes, "AES-CBC", false, ["decrypt"] ); const decrypted = await window.crypto.subtle.decrypt( {name: "AES-CBC", iv: iv}, cryptoKey, cipherData ); return new TextDecoder().decode(decrypted); } async function getCryptoKey(passphrase) { const encoder = new TextEncoder(); const data = encoder.encode(passphrase); const hash = await crypto.subtle.digest('SHA-256', data); return await crypto.subtle.importKey('raw', hash, {name: 'AES-CBC'}, false, ['encrypt', 'decrypt']); } async function encryptBytes(plainBytes, passphrase) { const key = await getCryptoKey(passphrase); const iv = crypto.getRandomValues(new Uint8Array(16)); const encryptedContent = await crypto.subtle.encrypt( {name: 'AES-CBC', iv: iv}, key, plainBytes ); const result = new Uint8Array(iv.length + encryptedContent.byteLength); result.set(iv); result.set(new Uint8Array(encryptedContent), iv.length); return result; } async function decryptBytes(combinedBytes, passphrase) { const key = await getCryptoKey(passphrase); const iv = combinedBytes.slice(0, 16); const data = combinedBytes.slice(16); const decryptedContent = await crypto.subtle.decrypt( {name: 'AES-CBC', iv: iv}, key, data ); return new Uint8Array(decryptedContent); } async function encryptString(plainText, passphrase) { const encoder = new TextEncoder(); const data = encoder.encode(plainText); const pwHash = await crypto.subtle.digest('SHA-256', encoder.encode(passphrase)); const key = await crypto.subtle.importKey( 'raw', pwHash, {name: 'AES-CBC'}, false, ['encrypt'] ); const iv = crypto.getRandomValues(new Uint8Array(16)); const encrypted = await crypto.subtle.encrypt( {name: 'AES-CBC', iv: iv}, key, data ); const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return fixBase64Padding(btoa(String.fromCharCode(...combined))); } async function decryptString(base64Text, passphrase) { const encoder = new TextEncoder(); const combined = new Uint8Array(atob(base64Text).split("").map(c => c.charCodeAt(0))); const pwHash = await crypto.subtle.digest('SHA-256', encoder.encode(passphrase)); const key = await crypto.subtle.importKey( 'raw', pwHash, {name: 'AES-CBC'}, false, ['decrypt'] ); const iv = combined.slice(0, 16); const data = combined.slice(16); const decrypted = await crypto.subtle.decrypt( {name: 'AES-CBC', iv: iv}, key, data ); return new TextDecoder().decode(decrypted); } async function fetchPost(url, value) { let response = await fetch(url, { method: "POST", body: value, headers: { "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash)) } }); let data = await response.text(); return data; } async function fetchPostEnc(url, value) { let nonce = await getNonce(username, passwordHash); let response = await fetch(url, { method: "POST", body: await encryptWithNonce(value, passwordHash, nonce), headers: { "secret": await encryptWithNonce(passwordHash, passwordHash, nonce) } }); let data = await response.text(); return data; } async function fetchAsync(url) { let response = await fetch(url, { method: "GET", }); let data = await response.text(); return data; } async function fetchAsyncWAuth(url) { let response = await fetch(url, { method: "GET", headers: { "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash)) } }); let data = await response.text(); return data; } async function getServerInfo(host) { console.log(`${prot}//${host}/_larpix/serverinfo`) return JSON.parse(await fetchAsync(`${prot}//${host}/_larpix/serverinfo`)); } async function Auth(username, password) { let passwordHash = await hashSHA3_512(password); let response = await fetch(`${url}/auth?u=${username}`, { method: "GET", headers: { "secret": await encryptWithNonce(passwordHash, passwordHash, await getNonce(username, passwordHash)) } }); let data = await response.text(); return data; } async function fetchEncrypted(request, body) { let nonce = await getNonce(username, passwordHash); let response = await fetch(`${url}/encryptedrequest?u=${username}`, { method: "POST", body: await encryptWithNonce( JSON.stringify({ string1: request, string2: body }) , passwordHash, nonce), headers: { "secret": await encryptWithNonce(passwordHash, passwordHash, nonce) } }); let data = await response.text(); return decryptString(data, passwordHash); } function power(base, exponent, mod) { let res = 1n; base = base % mod; while (exponent > 0n) { if (exponent % 2n === 1n) res = (res * base) % mod; base = (base * base) % mod; exponent = exponent / 2n; } return res; } function fixBase64Padding(base64String) { let str = base64String.replace(/-/g, '+').replace(/_/g, '/'); const pad = str.length % 4; if (pad) { if (pad === 1) { throw new Error(""); } str += '='.repeat(4 - pad); } return str; } function showBlahNotification(blahmessage, duration = 3500) { try { let split = blahmessage.split(":"); showNotification(processBlah(blahmessage), split[0], duration); } catch (e) { showNotification(blahmessage, "info", duration); } } function showNotification(message, type = 'info', duration = 3500) { let container = document.getElementById('notification-container'); if (!container) { container = document.createElement('div'); container.id = 'notification-container'; document.body.appendChild(container); } const notif = document.createElement('div'); notif.className = `notification ${type}`; notif.textContent = message; container.appendChild(notif); requestAnimationFrame(() => { requestAnimationFrame(() => { notif.classList.add('show'); }); }); setTimeout(() => { notif.classList.remove('show'); notif.addEventListener('transitionend', () => { notif.remove(); }); }, duration); } function showAction(message, actionid) { let container = document.getElementById('notification-container'); if (!container) { container = document.createElement('div'); container.id = 'notification-container'; document.body.appendChild(container); } const notif = document.createElement('div'); notif.className = `notification action`; notif.textContent = message; notif.id = `notification-${actionid}`; container.appendChild(notif); requestAnimationFrame(() => { requestAnimationFrame(() => { notif.classList.add('show'); }); }); } function clearAction(actionid) { const notifications = document.querySelectorAll(`[id="notification-${actionid}"]`); notifications.forEach(notif => { notif.classList.remove('show'); notif.addEventListener('transitionend', () => { notif.remove(); }, { once: true }); }); } async function hashSHA3_512(input) { const encoder = new TextEncoder(); const data = encoder.encode(input); const hashBuffer = await crypto.subtle.digest('SHA-512', data); //-3 kiedys xddddddddddddddddd const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return hashHex; } //placeholder pfp function getInitials(name) { const cleanName = (name || "").trim(); //name empty if (!cleanName) return ""; const parts = cleanName.split(/\s+/); //at least 2 words if (parts.length >= 2) { const firstInitial = parts[0][0]; const secondInitial = parts[parts.length - 1][0]; //from last word return (firstInitial + secondInitial).toUpperCase(); } //only 1 word with 1 letter const word = parts[0]; if (word.length === 1) { return word.toUpperCase(); } //1 word at least 2 letters return word.substring(0, 2).toUpperCase(); } function stringToColor(str) { let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } const h = Math.abs(hash) % 360; const s = 50; const l = 65; return `hsl(${h}, ${s}%, ${l}%)`; } function createAvatarSvg(name, size = 512) { const initials = getInitials(name); const color = stringToColor(name); const svg = ` ${initials} `.trim(); return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`; } async function getAvatarUrl(username) { try { let pfpUrl = `${url}/user/storage/public/getentry?u=${username}&e=larp.profile.pfp`; if ((await fetchAsync(pfpUrl)) == "") { throw Error(); } return pfpUrl; } catch (e) { return createAvatarSvg(username); } } function updateLoadingStatus(message) { loadingStatus.innerHTML = processBlah(message); } async function loadingFadeOut() { mainScreen.style.transform = "scale(0.85)"; mainScreen.style.opacity = "0"; loadingScreen.style.transform = "scale(0.85)"; loadingScreen.style.opacity = "0"; await delay(200); loadingScreen.style.display = "none"; mainScreen.style.display = ""; await delay(200); mainScreen.style.transform = ""; mainScreen.style.opacity = ""; } async function loadingFadeIn() { loadingScreen.style.transform = "scale(0.85)"; loadingScreen.style.opacity = "0"; mainScreen.style.transform = "scale(0.85)"; mainScreen.style.opacity = "0"; await delay(200); mainScreen.style.display = "none"; loadingScreen.style.display = ""; await delay(200); loadingScreen.style.transform = ""; loadingScreen.style.opacity = ""; } var blah; async function initBlahs() { lang = lang.toLowerCase(); let res; let path = window.location.pathname.replace("/login/index.html", "/"); try { //try user lang first res = await fetchAsync(`${path}blah/${lang}.json`); } catch (e) { //fallback to en-us res = await fetchAsync(`${path}blah/en-us.json`); } blah = JSON.parse(res); let blahTags = document.getElementsByTagName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } blahTags = document.getElementsByClassName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } let placeholders = document.querySelectorAll("[placeholder]"); for (let i = 0; i < placeholders.length; i++) { if (placeholders[i].placeholder.startsWith("{blah(")) { let value = placeholders[i].placeholder.split("{blah(")[1].split(")}")[0]; placeholders[i].placeholder = processBlah(value); } } } function processBlah(blahmessage) { try { if (!blahmessage.includes(":")) { blahmessage = `:${blahmessage}`; } let split = blahmessage.split(":"); let values = []; try { values = split[2].split(";"); } catch (e) { } let message = blah[split[1]]; let valueslist = ""; for (let i = 0; i < values.length; i++) { let value = processBlah(values[i]); valueslist+=`${value}, `; message = message.replaceAll(`{${i}}`, value); } valueslist = valueslist.slice(0, -2); if (message.includes('{all}')) { message = message.replaceAll('{all}', valueslist); } return message; } catch (e) { return blahmessage; } } function getLang() { return (navigator.language || navigator.languages[0]); } var password = ""; var username = ""; var passwordHash = ""; var host = ""; var lang = getLang(); async function mainJS() { await initBlahs(); passwordHash = await hashSHA3_512(password); if (localStorage.getItem('lang') != null) { lang = localStorage.getItem('lang'); } await start(); } username = localStorage.getItem('username'); password = localStorage.getItem('password'); host = localStorage.getItem('host'); mainJS(); function showFixedContextMenu(rect, html) { let parser = new DOMParser(); let doc = parser.parseFromString(html, "text/html"); let blahTags = doc.getElementsByTagName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } blahTags = doc.getElementsByClassName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } let placeholders = doc.querySelectorAll("[placeholder]"); for (let i = 0; i < placeholders.length; i++) { if (placeholders[i].placeholder.startsWith("{blah(")) { let value = placeholders[i].placeholder.split("{blah(")[1].split(")}")[0]; placeholders[i].placeholder = processBlah(value); } } fixedContextMenu.innerHTML = doc.body.innerHTML; fixedContextMenu.style.left = `${rect.right + 10}px`; fixedContextMenu.style.top = `${rect.top}px`; fixedContextMenu.classList.add("show"); } async function switchRoomsBar(title, content) { let roomsTopBarTransition = roomsTopBar.children.item(0); roomsBar.style.transform = "scale(0.85)"; roomsBar.style.opacity = "0"; roomsTopBarTransition.style.transform = "scale(0.85)"; roomsTopBarTransition.style.opacity = "0"; await delay(200); roomsTopBarTransition.innerHTML = processBlah(title); let parser = new DOMParser(); let doc = parser.parseFromString(content, "text/html"); let blahTags = doc.getElementsByTagName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } blahTags = doc.getElementsByClassName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } let placeholders = doc.querySelectorAll("[placeholder]"); for (let i = 0; i < placeholders.length; i++) { if (placeholders[i].placeholder.startsWith("{blah(")) { let value = placeholders[i].placeholder.split("{blah(")[1].split(")}")[0]; placeholders[i].placeholder = processBlah(value); } } roomsBar.innerHTML = doc.body.innerHTML; roomsBar.style.transform = ""; roomsBar.style.opacity = ""; roomsTopBarTransition.style.transform = ""; roomsTopBarTransition.style.opacity = ""; } async function switchRoomContent(title, content, showRoomBar, icon = "", skipMobileSlide = false) { let roomsTopBarTransition = roomTopBar.children.item(0); roomContentMain.style.transform = "scale(0.85)"; roomContentMain.style.opacity = "0"; roomsTopBarTransition.style.transform = "scale(0.85)"; roomsTopBarTransition.style.opacity = "0"; const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); if (window.innerWidth <= 52 * rem && !skipMobileSlide) { if (title != "title.splash") //do not show splash on mobile { mainScreen.classList.remove('mobile-details'); mainScreen.classList.add('mobile-content'); } } else { await delay(200); } let detailsBtnHtml = showRoomBar ? detailsBtn : ''; roomsTopBarTransition.innerHTML = `${backBtnHtml}${icon}${processBlah(title)}
${detailsBtnHtml}`; roomDetailsBar.style.display = showRoomBar ? "flex" : "none"; let parser = new DOMParser(); let doc = parser.parseFromString(content, "text/html"); let blahTags = doc.getElementsByTagName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } blahTags = doc.getElementsByClassName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } let placeholders = doc.querySelectorAll("[placeholder]"); for (let i = 0; i < placeholders.length; i++) { if (placeholders[i].placeholder.startsWith("{blah(")) { let value = placeholders[i].placeholder.split("{blah(")[1].split(")}")[0]; placeholders[i].placeholder = processBlah(value); } } roomContentMain.innerHTML = doc.body.innerHTML; roomContentMain.style.transform = ""; roomContentMain.style.opacity = ""; roomsTopBarTransition.style.transform = ""; roomsTopBarTransition.style.opacity = ""; } async function switchDetailsContent(title, content) { let roomsTopBarTransition = detailsTopBar.children.item(0); roomDetailsMain.style.transform = "scale(0.85)"; roomDetailsMain.style.opacity = "0"; roomsTopBarTransition.style.transform = "scale(0.85)"; roomsTopBarTransition.style.opacity = "0"; const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); if (window.innerWidth > 52 * rem) { await delay(200); } roomsTopBarTransition.innerHTML = processBlah(title); let parser = new DOMParser(); let doc = parser.parseFromString(content, "text/html"); let blahTags = doc.getElementsByTagName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } blahTags = doc.getElementsByClassName("blah"); for (let i = 0; i < blahTags.length; i++) { blahTags[i].innerHTML = processBlah(blahTags[i].innerHTML); } let placeholders = doc.querySelectorAll("[placeholder]"); for (let i = 0; i < placeholders.length; i++) { if (placeholders[i].placeholder.startsWith("{blah(")) { let value = placeholders[i].placeholder.split("{blah(")[1].split(")}")[0]; placeholders[i].placeholder = processBlah(value); } } roomDetailsMain.innerHTML = doc.body.innerHTML; roomDetailsMain.style.transform = ""; roomDetailsMain.style.opacity = ""; roomsTopBarTransition.style.transform = ""; roomsTopBarTransition.style.opacity = ""; } function clickCollapseDms() { var collapseDmsBtn = document.getElementById("collapse-dms"); collapseDmsBtn.classList.toggle("collapsed"); } function clickCollapseGroups() { var collapseGroupsBtn = document.getElementById("collapse-groups"); collapseGroupsBtn.classList.toggle("collapsed"); } function clickAddGroup() { switchRoomContent("title.create.group", createGroupScreen, false); } function clickAddDm() { switchRoomContent("title.create.dm", addDmScreen, false); } var indicatorBeforeFixedMenu; sidebarAddButton.addEventListener("click", (e) => { e.stopPropagation(); history.pushState({trap: true}, "", location.href); if (fixedContextMenu.classList.contains("show")) { fixedContextMenu.classList.remove("show"); setActiveSidebarIndicator(indicatorBeforeFixedMenu); } else { setActiveSidebarIndicator(sidebarAddIndicator, true); const rect = sidebarAddButton.getBoundingClientRect(); showFixedContextMenu(rect, addSpaceMenu); } }); document.addEventListener("click", (e) => { history.pushState({trap: true}, "", location.href); if (fixedContextMenu.classList.contains("show")) { if (!fixedContextMenu.contains(e.target)) { fixedContextMenu.classList.remove("show"); if (!e.target.closest('sidebarelement')) { setActiveSidebarIndicator(indicatorBeforeFixedMenu); } } } if (mainScreen && mainScreen.classList.contains('mobile-details')) { const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); if (window.innerWidth <= 52 * rem) { if (!roomDetailsBar.contains(e.target) && !e.target.closest('.mobile-nav-btn.right')) { mobileNavBack(); } } } }); window.addEventListener("popstate", (e) => { popstate(); }); const { App } = window.Capacitor?.Plugins || {}; if (App) { App.addListener('backButton', () => { popstate(); }); } function popstate() { if (!mainScreen.classList.contains('mobile-content') && !mainScreen.classList.contains('mobile-details')) { history.back(); if (App) { App.minimizeApp(); } } mobileNavBack(); } function gotoSideProfilePopup() { } function gotoInbox() { setActiveSidebarIndicator(sidebarInboxIndicator); switchRoomsBar("title.inbox", inboxRoomBar); gotoInvites(); } function gotoCreateSpace() { fixedContextMenu.classList.remove("show"); switchRoomContent("title.create.space", createSpaceScreen, false); } function gotoJoinSpace() { fixedContextMenu.classList.remove("show"); switchRoomContent("title.join.space", joinSpaceScreen, false); } function gotoHome() { setActiveSidebarIndicator(sidebarHomeIndicator); switchRoomContent("title.splash", splashScreen, false); switchRoomsBar("title.home", homeRoomBar); } function setActiveSidebarIndicator(element, skipDisabling = false) { let indicators = document.getElementsByTagName("indicator"); for (let i = 0; i < indicators.length; i++) { if (indicators[i].classList.contains("active")) { if (!skipDisabling) //DOKONCZ JEBANY INDICATOR UZYJE DO TEGO indicatorBeforeFixedMenu ZE ON MA BYC AKTYWNY PO MOBILE COFCE { indicatorBeforeFixedMenu = indicators[i]; indicators[i].classList.remove("active"); } } } element.classList.add("active"); } function mobileNavBack() { if(mainScreen.classList.contains('mobile-details')) { mainScreen.classList.remove('mobile-details'); mainScreen.classList.add('mobile-content'); setActiveSidebarIndicator(indicatorBeforeFixedMenu); } else { mainScreen.classList.remove('mobile-content'); setActiveSidebarIndicator(sidebarHomeIndicator); } } function mobileNavDetails() { mainScreen.classList.add('mobile-details'); } let touchStartX = 0; let touchEndX = 0; let touchStartY = 0; let touchEndY = 0; let touchMoved = false; document.addEventListener('touchstart', e => { touchStartX = e.changedTouches[0].screenX; touchStartY = e.changedTouches[0].screenY; touchMoved = false; activeTouchButton = e.target.closest('button, input'); if (activeTouchButton) { activeTouchButton.classList.add('is-pressed'); } }, { passive: true }); document.addEventListener('touchmove', e => { if (!touchMoved) { const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); let diffX = Math.abs(e.changedTouches[0].screenX - touchStartX); let diffY = Math.abs(e.changedTouches[0].screenY - touchStartY); if (diffX > rem || diffY > rem) { touchMoved = true; if (activeTouchButton) { activeTouchButton.classList.remove('is-pressed'); activeTouchButton = null; } } } }, { passive: true }); document.addEventListener('touchend', e => { touchEndX = e.changedTouches[0].screenX; touchEndY = e.changedTouches[0].screenY; handleMobileSwipe(); if (activeTouchButton) { activeTouchButton.classList.remove('is-pressed'); if (!touchMoved) { if (e.cancelable) { e.preventDefault(); } activeTouchButton.click(); } activeTouchButton = null; } }); document.addEventListener('touchcancel', () => { if (activeTouchButton) { activeTouchButton.classList.remove('is-pressed'); activeTouchButton = null; } }); document.addEventListener('contextmenu', e => { if (e.target.closest('button')) { e.preventDefault(); } }); function handleMobileSwipe() { const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); if (window.innerWidth > 52 * rem) return; let diffX = touchEndX - touchStartX; let diffY = touchEndY - touchStartY; if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 4 * rem) { if (diffX > 0) { mobileNavBack(); } else { if (mainScreen.classList.contains('mobile-content') && roomDetailsBar.style.display !== 'none') { mobileNavDetails(); } } } }