diff --git a/index.mjs b/index.mjs index 64b578b5..0349c739 100644 --- a/index.mjs +++ b/index.mjs @@ -140,7 +140,7 @@ if (cluster.isPrimary) { app.get("/", sendHtml("$.html")); app.get("/g", sendHtml("!.html")); - app.get("/a", sendHtml("!!.html")); + app.get("/s", sendHtml("!!.html")); app.get("/resent", (_req, res) => res.sendFile(path.join(publicPath, "resent", "index.html"))); app.get("/api/info", (_req, res) => { diff --git a/public/!!.html b/public/!!.html index b8371dd7..361cdf3a 100644 --- a/public/!!.html +++ b/public/!!.html @@ -1,122 +1,155 @@ - - - - - - - - - Waves. - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
+ + + + + + + + + Waves. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-
- - Waves. - Home - Games - Apps - Movies - AI - - - +
+ +
+ favicon + Waves. + Home + Games + Shortcuts + Movies + AI + + + +
+ +
+ + + +
+

Shortcuts

+ -
- + + +
+ + + + + + + + + \ No newline at end of file diff --git a/public/!.html b/public/!.html index 2b91a2dd..5c133a9f 100644 --- a/public/!.html +++ b/public/!.html @@ -1,122 +1,155 @@ - - - - - - - - - Waves. - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
+ + + + + + + + + Waves. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-
- - Waves. - Home - Games - Apps - Movies - AI - - - +
+ +
+ favicon + Waves. + Home + Games + Shortcuts + Movies + AI + + + +
+ +
+ + + +
+

Games

+ -
- + + +
+ + + + + + + + + \ No newline at end of file diff --git a/public/$.html b/public/$.html index c8489bb9..ec479fb7 100644 --- a/public/$.html +++ b/public/$.html @@ -1,137 +1,172 @@ - - - - - - - - - Waves. - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
+ + + + + + + + + Waves. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
-
- - Waves - Home - Games - Apps - Movies - AI - - - +
+ +
+ + Waves + Home + Games + Shortcuts + Movies + AI + +
+ +
+ + + +
+
Waves.
+ -
-
- + + +
+ + + +
Ping: Connecting...
+
+ +
Loading latest commit
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/css/$.css b/public/assets/css/$.css index cf4ad1e0..85340279 100644 --- a/public/assets/css/$.css +++ b/public/assets/css/$.css @@ -63,7 +63,7 @@ body { .home-navbar { transform: translateX(-50%); - width: 670px; + width: 690px; top: 1%; margin-bottom: -10px; margin-left: 50%; @@ -86,7 +86,7 @@ body { } .home-navbar .favicon { - width: 24px; + width: 28px; height: 24px; margin-right: -10px; vertical-align: middle; @@ -105,7 +105,7 @@ body { .home-navbar a:hover { color: #bbbbbb; - font-size: 16px; + font-size: 18px; } .home-navbar a:active { @@ -223,6 +223,26 @@ body { color: #818181; } +.shortcut-indicator { + position: absolute; + top: 50%; + left: 50%; + transform: translate(calc(-50% + 235px), -50%); + font-size: 12px; + font-weight: 500; + color: #000000; + background-color: #c4c4c4; + padding: 2px 6px; + border-radius: 7px; + cursor: pointer; + transition: all 0.3s ease; + animation: fadeIn 0.3s ease; +} + +.shortcut-indicator:hover { + background-color: #ffffff; +} + .shortcut-indicator-2 { position: absolute; top: 50%; @@ -236,12 +256,20 @@ body { border-radius: 7px; cursor: pointer; transition: all 0.3s ease; + animation: fadeUp 0.3s ease; } .shortcut-indicator-2:hover { background-color: #ffffff; } +.arrow-mode { + display: inline-block; + margin-left: 17px; + transition: all 0.3s ease; + animation: fadeIn 0.3s ease; +} + #lockIcon { position: absolute; left: 25px; @@ -320,25 +348,6 @@ body { color: #ffffff69; } -.shortcut-indicator { - position: absolute; - top: 50%; - left: 50%; - transform: translate(calc(-50% + 235px), -50%); - font-size: 12px; - font-weight: 500; - color: #000000; - background-color: #c4c4c4; - padding: 2px 6px; - border-radius: 7px; - cursor: pointer; - transition: all 0.3s ease; -} - -.shortcut-indicator:hover { - background-color: #ffffff; -} - #erudaLoadingScreen { position: fixed; top: 50%; @@ -451,7 +460,7 @@ body { display: none; opacity: 0; transition: opacity 0.3s ease-in-out; - animation: fadeInOverlay 0.3s ease-in-out forwards; + animation: fadeIn 0.3s ease-in-out forwards; } #namePrompt { @@ -578,7 +587,7 @@ body { } #namePrompt.fade-out { - animation: fadeOutPrompt 0.3s ease-in-out forwards; + animation: fadeOut 0.3s ease-in-out forwards; } #lastest-commit { @@ -650,6 +659,14 @@ body { font-size: 14px; } +.fadeIn { + animation: fadeIn 0.3s ease-in-out forwards; +} + +.fadeOut { + animation: fadeOut 0.3s ease-in-out forwards; +} + @keyframes fadeSlideIn { to { opacity: 1; @@ -657,26 +674,6 @@ body { } } -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes fadeInOverlay { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} - @keyframes fadeInPrompt { 0% { opacity: 0; @@ -689,7 +686,17 @@ body { } } -@keyframes fadeOutPrompt { +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes fadeOut { 0% { opacity: 1; } @@ -709,47 +716,6 @@ body { } } -@keyframes swing { - - 0%, - 100% { - transform: rotate(3deg); - } - - 50% { - transform: rotate(-3deg); - } -} - -@keyframes steamLarge { - 0% { - stroke-dashoffset: 13; - opacity: 0.6; - } - - 100% { - stroke-dashoffset: 39; - opacity: 0; - } -} - -@keyframes steamSmall { - 10% { - stroke-dashoffset: 9; - opacity: 0.6; - } - - 80% { - stroke-dashoffset: 27; - opacity: 0; - } - - 100% { - stroke-dashoffset: 27; - opacity: 0; - } -} - @keyframes spin { 0% { transform: rotate(0deg); diff --git a/public/assets/css/a.css b/public/assets/css/s.css similarity index 83% rename from public/assets/css/a.css rename to public/assets/css/s.css index b1653f14..2df8a4c9 100644 --- a/public/assets/css/a.css +++ b/public/assets/css/s.css @@ -1,16 +1,16 @@ -.apps-page { +.shortcuts-page { padding: 100px 20px 40px; text-align: center; position: relative; } -.apps-page h1 { +.shortcuts-page h1 { font-size: 2.5rem; margin-bottom: 20px; color: #fff; } -.apps-grid { +.shortcuts-grid { display: flex; flex-wrap: wrap; justify-content: center; @@ -21,14 +21,14 @@ margin: 0 auto; } -.content.apps-page { +.content.shortcuts-page { display: flex; flex-direction: column; align-items: center; text-align: center; } -.app-card { +.shortcut-card { background-color: #08080894; border-radius: 25px; padding: 15px 20px; @@ -48,7 +48,7 @@ animation: fadeIn 2s ease; } -.app-card img { +.shortcut-card img { width: 100%; height: 200px; object-fit: cover; @@ -57,26 +57,26 @@ transition: all 0.3s ease; } -.app-card:hover img { +.shortcut-card:hover img { filter: brightness(1); } -.app-card:hover { +.shortcut-card:hover { background-color: #333333; } -.app-card h2 { +.shortcut-card h2 { font-size: 1rem; margin: 10px 0; color: #fff; } -.app-card p { +.shortcut-card p { font-size: 1rem; color: #ccc; } -.apps-search-bar { +.shortcuts-search-bar { text-align: center; margin-top: 20px; margin-bottom: 20px; @@ -84,12 +84,12 @@ position: relative; } -#appSearchInput { +#shortcutSearchInput { padding: 14px 30px; border: 1px solid #ffffff1a; background-image: url('/assets/images/icons/search.png'); background-size: 35px 35px; - background-position: 10px center; + background-position: 5px center; background-repeat: no-repeat; padding-left: 45px; border-radius: 20px; @@ -102,12 +102,12 @@ transition: all 0.3s ease; } -#appSearchInput:focus, -#appSearchInput:hover { +#shortcutSearchInput:focus, +#shortcutSearchInput:hover { border: 1px solid #ffffff69; } -#appSearchInput::placeholder { +#shortcutSearchInput::placeholder { color: #a8a8a8; } diff --git a/public/assets/data/a.json b/public/assets/data/s.json similarity index 98% rename from public/assets/data/a.json rename to public/assets/data/s.json index fab13846..f28a2db2 100644 --- a/public/assets/data/a.json +++ b/public/assets/data/s.json @@ -1,5 +1,5 @@ { - "apps": [ + "shortcuts": [ { "icon": "/assets/images/a/crazygames.jpg", "title": "Crazy Games", @@ -51,4 +51,4 @@ "link": "https://store.steampowered.com" } ] -} +} \ No newline at end of file diff --git a/public/assets/js/$.js b/public/assets/js/$.js index 2b7e0f30..f6703c3a 100644 --- a/public/assets/js/$.js +++ b/public/assets/js/$.js @@ -145,8 +145,8 @@ document.addEventListener('DOMContentLoaded', () => { const subjects = ['math', 'science', 'history', 'art', 'programming', 'philosophy']; const randomSubject = subjects[Math.floor(Math.random() * subjects.length)]; try { - history.replaceState({}, '', `/learning?subject=${randomSubject}`); - } catch(e) {} + history.replaceState({}, '', `/learning?subject=${randomSubject}`); + } catch (e) {} } function setupIframeNavigationListeners() { @@ -203,26 +203,25 @@ document.addEventListener('DOMContentLoaded', () => { try { const newUrl = iframe.contentWindow ? iframe.contentWindow.location.href : iframe.src; if (newUrl && newUrl !== 'about:blank') { - if (currentIndex === -1 || normalizeUrl(historyStack[currentIndex]) !== normalizeUrl(newUrl) || historyStack[currentIndex] !== newUrl ) { - addToHistory(newUrl); + if (currentIndex === -1 || normalizeUrl(historyStack[currentIndex]) !== normalizeUrl(newUrl) || historyStack[currentIndex] !== newUrl) { + addToHistory(newUrl); } else if (historyStack[currentIndex] !== newUrl) { addToHistory(newUrl, true); } } else if (newUrl === 'about:blank' && historyStack.length > 0 && historyStack[currentIndex] !== 'about:blank') { if (currentIndex > 0) { - const previousUrl = historyStack[currentIndex -1]; + const previousUrl = historyStack[currentIndex - 1]; currentIndex--; iframe.src = previousUrl; - return; + return; } } setupIframeNavigationListeners(); generateSubject(); - if (navbarToggle && navbarToggle.checked && navBar) { + if (navbarToggle && navbarToggle.checked && navBar) { navBar.style.display = 'block'; } - } catch (error) { - } finally { + } catch (error) {} finally { updateNavButtons(); updateDecodedSearchInput(); } @@ -241,7 +240,7 @@ document.addEventListener('DOMContentLoaded', () => { setTimeout(() => button.classList.remove(animationClass), 200); } } - + backIcon.addEventListener('click', () => { toggleButtonAnimation(backIcon, 'button-animate-back'); if (currentIndex > 0) { @@ -263,14 +262,14 @@ document.addEventListener('DOMContentLoaded', () => { updateDecodedSearchInput(); } }); - + refreshIcon.addEventListener('click', () => { if (refreshIcon) refreshIcon.classList.add('spin'); if (iframe.contentWindow) { showLoadingScreen(false); const currentIframeUrl = iframe.contentWindow.location.href; if (normalizeUrl(currentIframeUrl) !== normalizeUrl(historyStack[currentIndex] || '')) { - addToHistory(currentIframeUrl); + addToHistory(currentIframeUrl); } iframe.contentWindow.location.reload(true); } @@ -294,30 +293,30 @@ document.addEventListener('DOMContentLoaded', () => { return query; } catch (e) {} } - + if (/^(localhost|(\d{1,3}\.){3}\d{1,3})(:\d+)?(\/.*)?$/.test(query) || query.toLowerCase() === "localhost") { - if (!query.toLowerCase().startsWith("http:") && !query.toLowerCase().startsWith("https:")) { + if (!query.toLowerCase().startsWith("http:") && !query.toLowerCase().startsWith("https:")) { return `http://${query}`; - } - return query; + } + return query; } - + try { const urlWithHttps = new URL(`https://${query}`); if (urlWithHttps.hostname && urlWithHttps.hostname.includes('.') && !urlWithHttps.hostname.endsWith('.') && urlWithHttps.hostname !== '.' && - urlWithHttps.hostname.split('.').pop().length >= 2 && - !/^\d+$/.test(urlWithHttps.hostname.split('.').pop()) - ) { + urlWithHttps.hostname.split('.').pop().length >= 2 && + !/^\d+$/.test(urlWithHttps.hostname.split('.').pop()) + ) { return urlWithHttps.toString(); } } catch (e) {} return `https://duckduckgo.com/?q=${encodeURIComponent(query)}&ia=web`; } - + async function getUrl(url) { if (typeof __uv$config !== 'undefined' && __uv$config.encodeUrl) { return Promise.resolve(__uv$config.prefix + __uv$config.encodeUrl(url)); @@ -336,7 +335,7 @@ document.addEventListener('DOMContentLoaded', () => { } }); } - + async function handleSearch(query) { if (!query || !query.trim()) { showToast('Please enter something in the Search Bar.', 'error', 'warning'); @@ -357,17 +356,20 @@ document.addEventListener('DOMContentLoaded', () => { if (searchURL.startsWith(window.location.origin + '/assets/g/')) { finalUrlToLoad = searchURL; } else if (searchURL.startsWith('/assets/g/')) { - finalUrlToLoad = new URL(searchURL, window.location.origin).href; - } - else { + finalUrlToLoad = new URL(searchURL, window.location.origin).href; + } else { try { const tempUrl = new URL(searchURL); if (tempUrl.origin === window.location.origin && tempUrl.pathname.startsWith('/assets/g/')) { finalUrlToLoad = tempUrl.href; - } else { finalUrlToLoad = await getUrl(searchURL); } - } catch (e) { finalUrlToLoad = await getUrl(searchURL); } + } else { + finalUrlToLoad = await getUrl(searchURL); + } + } catch (e) { + finalUrlToLoad = await getUrl(searchURL); + } } - + if (searchInput2) searchInput2.value = decodeUrl(finalUrlToLoad); iframe.src = finalUrlToLoad; } @@ -392,27 +394,47 @@ document.addEventListener('DOMContentLoaded', () => { } }); - if (movies) movies.addEventListener('click', e => { e.preventDefault(); APP.handleSearch('https://movies.usewaves.site/'); }); - if (ai) ai.addEventListener('click', e => { e.preventDefault(); APP.handleSearch('https://ai.usewaves.site/'); }); - + if (movies) movies.addEventListener('click', e => { + e.preventDefault(); + APP.handleSearch('https://movies.usewaves.site/'); + }); + if (ai) ai.addEventListener('click', e => { + e.preventDefault(); + APP.handleSearch('https://ai.usewaves.site/'); + }); + function showToast(message, type = 'success', iconType = 'check') { const toast = document.createElement('div'); toast.className = `toast show ${type}`; - const icons = { 'check': 'fa-regular fa-check-circle', 'times-circle': 'fa-regular fa-times-circle', 'info': 'fa-regular fa-info-circle', 'warning': 'fa-regular fa-exclamation-triangle', 'heart': 'fa-solid fa-heart' }; + const icons = { + 'check': 'fa-regular fa-check-circle', + 'times-circle': 'fa-regular fa-times-circle', + 'info': 'fa-regular fa-info-circle', + 'warning': 'fa-regular fa-exclamation-triangle', + 'heart': 'fa-solid fa-heart' + }; toast.innerHTML = `${message}`; - const progressBar = document.createElement('div'); progressBar.className = 'progress-bar'; toast.appendChild(progressBar); - const closeBtn = document.createElement('button'); closeBtn.className = 'toast-close'; + const progressBar = document.createElement('div'); + progressBar.className = 'progress-bar'; + toast.appendChild(progressBar); + const closeBtn = document.createElement('button'); + closeBtn.className = 'toast-close'; closeBtn.innerHTML = ''; - closeBtn.onclick = () => { toast.classList.add('hide'); setTimeout(() => toast.remove(), 500); }; + closeBtn.onclick = () => { + toast.classList.add('hide'); + setTimeout(() => toast.remove(), 500); + }; toast.appendChild(closeBtn); document.body.appendChild(toast); - setTimeout(() => { toast.classList.add('hide'); setTimeout(() => toast.remove(), 500); }, 3000); + setTimeout(() => { + toast.classList.add('hide'); + setTimeout(() => toast.remove(), 500); + }, 3000); } - + if (navbarToggle && navBar && iframe) { const savedNavbarState = localStorage.getItem('navbarToggled'); - const defaultChecked = iframe.style.display !== 'block' ? true : (savedNavbarState === null ? false : savedNavbarState === 'true'); - navbarToggle.checked = defaultChecked; + navbarToggle.checked = savedNavbarState !== 'false'; const updateNavbarDisplayBasedOnToggle = () => { if (iframe.style.display === 'block') { @@ -422,15 +444,15 @@ document.addEventListener('DOMContentLoaded', () => { } }; updateNavbarDisplayBasedOnToggle(); - + navbarToggle.addEventListener('change', () => { localStorage.setItem('navbarToggled', navbarToggle.checked.toString()); updateNavbarDisplayBasedOnToggle(); }); - + window.APP.updateNavbarDisplay = updateNavbarDisplayBasedOnToggle; } - + window.addEventListener('load', () => { let iframeInitiallyHidden = true; if (iframe.src && iframe.src !== 'about:blank') { @@ -445,8 +467,8 @@ document.addEventListener('DOMContentLoaded', () => { if (topBar) topBar.style.display = 'flex'; iframe.style.display = 'block'; } - - if(window.APP.updateNavbarDisplay) window.APP.updateNavbarDisplay(); + + if (window.APP.updateNavbarDisplay) window.APP.updateNavbarDisplay(); updateNavButtons(); updateDecodedSearchInput(); }); @@ -456,44 +478,17 @@ document.addEventListener('DOMContentLoaded', () => { }); (async () => { - const commitElement = document.getElementById('lastest-commit'); - - try { - const res = await fetch('/api/latest-commit'); - - console.log('Fetching latest commit from API:', res); - - if (!res.ok) { - const remaining = res.headers.get('X-RateLimit-Remaining'); - const reset = res.headers.get('X-RateLimit-Reset'); - - console.warn(`Status: ${res.status}`); - if (remaining !== null) { - console.warn(`Rate limit remaining: ${remaining}`); - if (Number(remaining) === 0) { - const resetDate = reset ? new Date(Number(reset) * 1000) : 'unknown'; - throw new Error(`Rate limit exceeded. Try again at ${resetDate}`); - } - } - - if (res.status === 403) { - throw new Error('403 Forbidden — possibly rate limited or blocked.'); - } - - throw new Error(`HTTP error! status: ${res.status}`); + try { + const res = await fetch('/api/latest-commit'); + const { + updates + } = await res.json(); + const u = updates[0]; + const commitUrl = `https://github.com/xojw/waves/commit/${u.sha}`; + document.getElementById('lastest-commit').innerHTML = + ` ${u.sha}`; + } catch { + document.getElementById('lastest-commit').textContent = + 'Failed to load lastest commit'; } - - const { updates } = await res.json(); - const u = updates[0]; - const commitUrl = `https://github.com/xojw/waves/commit/${u.sha}`; - - commitElement.innerHTML = ` - - ${u.sha} - `; - } catch (err) { - console.error('Failed to fetch latest commit:', err.message || err); - - commitElement.textContent = 'Failed to load latest commit'; - } })(); \ No newline at end of file diff --git a/public/assets/js/a.js b/public/assets/js/a.js deleted file mode 100644 index 6989c00d..00000000 --- a/public/assets/js/a.js +++ /dev/null @@ -1,45 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - const searchInput = document.getElementById('appSearchInput'); - const grid = document.querySelector('.apps-grid'); - let appsData = []; - - fetch('/assets/data/a.json') - .then(response => response.json()) - .then(data => { - appsData = data.apps; - - searchInput.placeholder = `Search through ${appsData.length} Apps…`; - - displayApps(appsData); - - searchInput.addEventListener('input', function() { - const query = searchInput.value.toLowerCase(); - const filteredApps = appsData.filter(app => { - const title = app.title ? app.title.toLowerCase() : ''; - const description = app.description ? app.description.toLowerCase() : ''; - return title.includes(query) || description.includes(query); - }); - displayApps(filteredApps); - }); - }) - .catch(err => console.error('Error loading apps data:', err)); - - function displayApps(apps) { - grid.innerHTML = ''; - if (apps.length === 0) { - grid.innerHTML = '

Zero apps were found matching your search :(

'; - } - apps.forEach(app => { - const card = document.createElement('div'); - card.classList.add('app-card'); - card.innerHTML = ` - ${app.title} Icon -

${app.title}

- `; - card.addEventListener('click', function() { - window.handleSearch(app.link); - }); - grid.appendChild(card); - }); - } -}); \ No newline at end of file diff --git a/public/assets/js/g.js b/public/assets/js/g.js index 194fa274..9de0c6eb 100644 --- a/public/assets/js/g.js +++ b/public/assets/js/g.js @@ -1,98 +1,69 @@ -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('gameSearchInput'); - const grid = document.querySelector('.games-grid'); - let gamesData = []; - let filteredData = []; - const BATCH_SIZE = 50; - let renderedCount = 0; - + const grid = document.querySelector('.games-grid'); const sentinel = document.createElement('div'); - sentinel.className = 'sentinel'; - grid.after(sentinel); + let allGames = []; + let filteredGames = []; + let renderedCount = 0; + const BATCH_SIZE = 20; const observer = new IntersectionObserver(entries => { - entries.forEach(entry => { - if (entry.isIntersecting) { - renderNextBatch(); - } - }); - }, { rootMargin: '200px', threshold: 0.1 }); - - function debounce(fn, wait = 200) { - let t; - return (...args) => { - clearTimeout(t); - t = setTimeout(() => fn.apply(this, args), wait); - }; - } + if (entries[0].isIntersecting) loadNextBatch(); + }, { rootMargin: '500px' }); fetch('/assets/data/g.json') - .then(r => r.json()) + .then(res => res.json()) .then(data => { - gamesData = data; - filteredData = data; - searchInput.placeholder = `Search through ${data.length} Games…`; - + allGames = data; + filteredGames = data; + searchInput.placeholder = `Search through ${allGames.length} Games…`; + grid.parentNode.appendChild(sentinel); observer.observe(sentinel); - renderNextBatch(); - - searchInput.addEventListener('input', debounce(() => { - const q = searchInput.value.trim().toLowerCase(); - filteredData = gamesData.filter(g => - (g.name || '').toLowerCase().includes(q) - ); - resetRendering(); - }, 250)); + resetAndRender(); }) - .catch(console.error); + .catch(err => console.error(err)); - function resetRendering() { + searchInput.addEventListener('input', function() { + const q = this.value.toLowerCase(); + filteredGames = allGames.filter(game => + (game.name || '').toLowerCase().includes(q) + ); + resetAndRender(); + }); + + function resetAndRender() { grid.innerHTML = ''; - renderedCount = 0; - observer.observe(sentinel); - renderNextBatch(); - } - - function renderNextBatch() { - const batch = filteredData.slice(renderedCount, renderedCount + BATCH_SIZE); - if (!batch.length) { + renderedCount = 0; + if (filteredGames.length === 0) { + grid.innerHTML = '

Zero games were found matching your search :(

'; observer.unobserve(sentinel); return; } + observer.observe(sentinel); + loadNextBatch(); + } - const frag = document.createDocumentFragment(); - batch.forEach(game => { + function loadNextBatch() { + const nextCount = Math.min(renderedCount + BATCH_SIZE, filteredGames.length); + for (let i = renderedCount; i < nextCount; i++) { + const game = filteredGames[i]; const card = document.createElement('div'); - card.className = 'game-card'; + card.classList.add('game-card'); card.innerHTML = ` - ${game.name} Icon + ${game.name} Icon

${game.name}

`; card.addEventListener('click', () => { - window.handleSearch(`/assets/g/${game.directory}`); + const gameUrl = window.location.origin + `/assets/g/${game.directory}`; + APP.handleSearch(gameUrl); }); - frag.appendChild(card); - }); - - grid.appendChild(frag); - renderedCount += batch.length; - - preloadNextImages(5); - - if (renderedCount >= filteredData.length) { + grid.appendChild(card); + } + renderedCount = nextCount; + if (renderedCount >= filteredGames.length) { observer.unobserve(sentinel); + } else { + grid.parentNode.appendChild(sentinel); } } - - function preloadNextImages(limit = 3) { - const next = filteredData.slice(renderedCount, renderedCount + limit); - next.forEach(({ directory, image }) => { - const img = new Image(); - img.src = `/assets/g/${directory}/${image}`; - }); - } }); \ No newline at end of file diff --git a/public/assets/js/greetings.js b/public/assets/js/greetings.js index c2354c7a..14b344e1 100644 --- a/public/assets/js/greetings.js +++ b/public/assets/js/greetings.js @@ -38,8 +38,8 @@ function getWelcomeMessage(name) { const path = window.location.pathname; if (path === '/g') { return `Have fun playing games, ${name}!`; - } else if (path === '/a') { - return `Enjoy our collection of apps, ${name}!`; + } else if (path === '/s') { + return `Enjoy our collection of, ${name}!`; } else { return `Welcome back, ${name}!`; } @@ -47,7 +47,7 @@ function getWelcomeMessage(name) { function getIconType(path) { if (path === '/g') return 'game'; - if (path === '/a') return 'apps'; + if (path === '/s') return 'shortcuts'; return 'wave'; } @@ -65,7 +65,7 @@ timeGreetings.push( { text: 'Good morning, sunshine', icon: '', suffix: ' :D' }, { text: 'Here’s to a bright morning', icon: '', suffix: '.' }, { text: 'Enjoy your morning', icon: '', suffix: '!' }, - { text: 'Your day starts here', icon: '', suffix: ' !' } + { text: 'Your day starts here', icon: '', suffix: '!' } ); timeGreetings.push( diff --git a/public/assets/js/register.js b/public/assets/js/register.js index b833de2a..158e58d5 100644 --- a/public/assets/js/register.js +++ b/public/assets/js/register.js @@ -1,89 +1,80 @@ -document.addEventListener('DOMContentLoaded', function() { +;(function(){ + const origLog = console.log.bind(console); + document.addEventListener("DOMContentLoaded", () => { + origLog( + "%c\n" + + " ᶻ 𝗓 𐰁 .ᐟ\n" + + " |\\ _,,,---,,_\n" + + " /, `.-'`' -. ;-;;,_\n" + + " |,4- ) )-,_..;\\ ( `'-'\n" + + " '---''(_/--' `-`\\_)\n discord.gg/dJvdkPRheV", + "color: hotpink; font-size: 16px; display: block; white-space: pre; text-align: center; padding-left: 28%;" + ); + }); - const defaultWispUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}/w/`; - let currentWispUrl = localStorage.getItem('customWispUrl') || defaultWispUrl; - const wispUrl = currentWispUrl; - const connection = new BareMux.BareMuxConnection("/baremux/worker.js"); + const labelStyle = "background: white; color: black; font-weight: bold; padding: 2px 4px; border-radius: 2px"; + const messageStyle = "background: black; color: white; padding: 2px 4px; border-radius: 2px"; + ["log","info","warn","error","debug"].forEach(method => { + const orig = console[method].bind(console); + console[method] = (...args) => { + const text = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' '); + orig(`%cWaves:%c ${text}`, labelStyle, messageStyle); + }; + }); - async function registerSW() { - try { - if (!navigator.serviceWorker) { - console.log("%c[!]%c Service Workers are not supported by this browser.", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - return; - } - await ensureWebSocketConnection(wispUrl); - console.log("%c[+]%c Registering Service Worker...", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - await navigator.serviceWorker.register("/wah/sw.js", { scope: '/wah/a/' }); - console.log("%c[*]%c Service Worker registered successfully.", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - const savedTransport = localStorage.getItem('transport') || "epoxy"; - switchTransport(savedTransport); - updateTransportUI(savedTransport); - console.log(`%c[#]%c Using ${capitalizeTransport(savedTransport)} transport.`, "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - } catch (error) { - logError(error, 'An error occurred during Service Worker registration or WebSocket connection'); - } - } + document.addEventListener("DOMContentLoaded", function(){ + const defaultWispUrl = `${window.location.protocol==="https:"?"wss":"ws"}://${window.location.host}/w/`; + let currentWispUrl = localStorage.getItem("customWispUrl") || defaultWispUrl; + const wispUrl = currentWispUrl; + const connection = new BareMux.BareMuxConnection("/baremux/worker.js"); - async function ensureWebSocketConnection(url) { - return new Promise((resolve, reject) => { - console.log("%c[+]%c Establishing WebSocket connection...", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - const ws = new WebSocket(url); - ws.onopen = () => { - console.log("%c[*]%c WebSocket connection established.", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - resolve(ws); - }; - ws.onerror = (error) => { - logError(error, 'Failed to establish WebSocket connection'); - reject(error); - }; - ws.onclose = (event) => { - if (event.code !== 1000) { - console.warn(`%c[-]%c WebSocket connection closed. Reason: ${event.reason || "No reason provided"}`, "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - } else { - console.warn("%c[-]%c WebSocket connection closed normally.", "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - } - }; - }); - } + registerSW(); - function logError(error, message) { - console.error(`%c[!]%c ${message}: ${error.message || error}`, "background-color: black; color: white; font-weight: bold;", "background-color: black; color: white;"); - } + async function registerSW(){ + try { + if (!navigator.serviceWorker) return console.error("Service Workers are not supported by this browser."); + await ensureWebSocketConnection(wispUrl); + console.log("Registering Service Worker..."); + await navigator.serviceWorker.register("/wah/sw.js", { scope: "/wah/a/" }); + console.log("Service Worker registered successfully."); + const savedTransport = localStorage.getItem("transport") || "epoxy"; + switchTransport(savedTransport); + updateTransportUI(savedTransport); + console.log(`Using ${capitalizeTransport(savedTransport)} transport.`); + } catch (e) { + console.error(`An error occurred during Service Worker registration or WebSocket connection: ${e.message||e}`); + } + } - function switchTransport(transport) { - const transportMap = { - "epoxy": "/epoxy/index.mjs", - "libcurl": "/libcurl/index.mjs" - }; - const transportFile = transportMap[transport]; - if (transportFile) { - connection.setTransport(transportFile, [{ wisp: wispUrl }]); - } - } + async function ensureWebSocketConnection(url){ + return new Promise((resolve, reject) => { + console.log("Establishing WebSocket connection..."); + const ws = new WebSocket(url); + ws.onopen = () => { console.log("WebSocket connection established."); resolve(ws); }; + ws.onerror = e => { console.error(`Failed to establish WebSocket connection: ${e.message||e}`); reject(e); }; + ws.onclose = ev => { + if (ev.code !== 1000) console.warn(`WebSocket connection closed. Reason: ${ev.reason||"No reason provided"}`); + else console.warn("WebSocket connection closed normally."); + }; + }); + } - async function changeTransport(newTransport) { - try { - localStorage.setItem('transport', newTransport); - switchTransport(newTransport); - updateTransportUI(newTransport); - } catch (error) { - logError(error, 'An error occurred while storing transport preference'); - } - } + function switchTransport(t){ + const m = { epoxy: "/epoxy/index.mjs", libcurl: "/libcurl/index.mjs" }[t]; + if (m) connection.setTransport(m, [{ wisp: wispUrl }]); + } - function updateTransportUI(transport) { - const transportSelected = document.querySelector(".transport-selected"); - transportSelected.textContent = capitalizeTransport(transport); - } + function updateTransportUI(t){ + document.querySelector(".transport-selected").textContent = capitalizeTransport(t); + } - function capitalizeTransport(transport) { - return transport.charAt(0).toUpperCase() + transport.slice(1).toLowerCase(); - } + function capitalizeTransport(t){ + return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase(); + } - document.addEventListener('wispUrlChanged', function(e) { - currentWispUrl = e.detail; - switchTransport(localStorage.getItem('transport') || "epoxy"); - }); - - registerSW(); -}); \ No newline at end of file + document.addEventListener("wispUrlChanged", function(e){ + currentWispUrl = e.detail; + switchTransport(localStorage.getItem("transport") || "epoxy"); + }); + }); +})(); \ No newline at end of file diff --git a/public/assets/js/s.js b/public/assets/js/s.js new file mode 100644 index 00000000..ab01a157 --- /dev/null +++ b/public/assets/js/s.js @@ -0,0 +1,45 @@ +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.getElementById('shortcutSearchInput'); + const grid = document.querySelector('.shortcuts-grid'); + let shortcutsData = []; + + fetch('/assets/data/s.json') + .then(response => response.json()) + .then(data => { + shortcutsData = data.shortcuts; + + searchInput.placeholder = `Search through ${shortcutsData.length} Shortcuts...`; + + displayShortcuts(shortcutsData); + + searchInput.addEventListener('input', function() { + const query = searchInput.value.toLowerCase(); + const filteredShortcuts = shortcutsData.filter(shortcut => { + const title = shortcut.title ? shortcut.title.toLowerCase() : ''; + const description = shortcut.description ? shortcut.description.toLowerCase() : ''; + return title.includes(query) || description.includes(query); + }); + displayShortcuts(filteredShortcuts); + }); + }) + .catch(err => console.error('Error loading shortcuts data:', err)); + + function displayShortcuts(shortcuts) { + grid.innerHTML = ''; + if (shortcuts.length === 0) { + grid.innerHTML = '

Zero shortcuts were found matching your search :(

'; + } + shortcuts.forEach(shortcut => { + const card = document.createElement('div'); + card.classList.add('shortcut-card'); + card.innerHTML = ` + ${shortcut.title} Icon +

${shortcut.title}

+ `; + card.addEventListener('click', function() { + APP.handleSearch(shortcut.link); + }); + grid.appendChild(card); + }); + } +}); \ No newline at end of file diff --git a/public/assets/js/settings.js b/public/assets/js/settings.js index 7d0154b3..4890d269 100644 --- a/public/assets/js/settings.js +++ b/public/assets/js/settings.js @@ -1,6 +1,6 @@ -document.addEventListener('DOMContentLoaded', function () { - const settingsMenu = document.getElementById('settings-menu'); - settingsMenu.innerHTML = ` +document.addEventListener('DOMContentLoaded', function() { + const settingsMenu = document.getElementById('settings-menu'); + settingsMenu.innerHTML = `

Settings

`; - const settingsIcon = document.getElementById('settings-icon'); - const closeSettingsBtn = document.getElementById('close-settings'); - const saveWispBtn = document.getElementById('save-wisp-url'); - const transportSelector = document.querySelector('.transport-selector'); - const transportSelected = transportSelector.querySelector('.transport-selected'); - const transportOptions = transportSelector.querySelector('.transport-options'); - const navbarToggle = document.getElementById('navbar-toggle'); - const defaultWispUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}/w/`; - let currentWispUrl = localStorage.getItem('customWispUrl') || defaultWispUrl; - const wispInput = document.querySelector("#wisp-server"); - wispInput.value = currentWispUrl; - let isToggling = false; + const settingsIcon = document.getElementById('settings-icon'); + const closeSettingsBtn = document.getElementById('close-settings'); + const saveWispBtn = document.getElementById('save-wisp-url'); + const transportSelector = document.querySelector('.transport-selector'); + const transportSelected = transportSelector.querySelector('.transport-selected'); + const transportOptions = transportSelector.querySelector('.transport-options'); + const navbarToggle = document.getElementById('navbar-toggle'); + const defaultWispUrl = `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}/w/`; + let currentWispUrl = localStorage.getItem('customWispUrl') || defaultWispUrl; + const wispInput = document.querySelector("#wisp-server"); + wispInput.value = currentWispUrl; + let isToggling = false; - function isValidUrl(url) { - try { - const p = new URL(url); - return (p.protocol === "wss:" || p.protocol === "ws:") && url.endsWith('/'); - } catch (_) { - return false; + navbarToggle.checked = localStorage.getItem('navbarToggled') !== 'false'; + + function isValidUrl(url) { + try { + const p = new URL(url); + return (p.protocol === "wss:" || p.protocol === "ws:") && url.endsWith('/'); + } catch (_) { + return false; + } } - } function runScriptIfChecked() { - let inFrame; - try { - inFrame = window !== top; - } catch (e) { - inFrame = true; - } - const aboutBlankChecked = JSON.parse(localStorage.getItem("aboutBlankChecked")) || false; - if (!aboutBlankChecked || inFrame) { - return; - } - const defaultTitle = "Google."; - const defaultIcon = "https://www.google.com/favicon.ico"; - const title = localStorage.getItem("siteTitle") || defaultTitle; - const icon = localStorage.getItem("faviconURL") || defaultIcon; - const iframeSrc = "/"; - const popup = window.open("", "_blank"); - if (!popup || popup.closed) { - alert("Failed to load automask. Please allow popups and try again."); - return; - } - popup.document.head.innerHTML = ` + let inFrame; + try { + inFrame = window !== top; + } catch (e) { + inFrame = true; + } + const aboutBlankChecked = JSON.parse(localStorage.getItem("aboutBlankChecked")) || false; + if (!aboutBlankChecked || inFrame) { + return; + } + const defaultTitle = "Google."; + const defaultIcon = "https://www.google.com/favicon.ico"; + const title = localStorage.getItem("siteTitle") || defaultTitle; + const icon = localStorage.getItem("faviconURL") || defaultIcon; + const iframeSrc = "/"; + const popup = window.open("", "_blank"); + if (!popup || popup.closed) { + alert("Failed to load automask. Please allow popups and try again."); + return; + } + popup.document.head.innerHTML = ` ${title} `; - popup.document.body.innerHTML = ` + popup.document.body.innerHTML = ` `; - window.location.replace("https://bisd.schoology.com/home"); - } - document.getElementById("aboutblank-toggle").addEventListener("change", function() { - localStorage.setItem("aboutBlankChecked", JSON.stringify(this.checked)); - if (this.checked) { - showToast('success', 'Auto About:Blank is now enabled.'); - } else { - showToast('error', 'Auto About:Blank is now disabled.'); - } - runScriptIfChecked(); - }); - window.addEventListener("load", function() { - const aboutBlankChecked = JSON.parse(localStorage.getItem("aboutBlankChecked")) || false; - document.getElementById("aboutblank-toggle").checked = aboutBlankChecked; - runScriptIfChecked(); - }); - - function updateWispServerUrl(url) { - if (isValidUrl(url)) { - currentWispUrl = url; - localStorage.setItem('customWispUrl', url); - document.dispatchEvent(new CustomEvent('wispUrlChanged', { detail: currentWispUrl })); - showToast('success', `Wisp Server URL changed to: ${currentWispUrl}`); - } else { - currentWispUrl = defaultWispUrl; - localStorage.setItem('customWispUrl', defaultWispUrl); - document.dispatchEvent(new CustomEvent('wispUrlChanged', { detail: currentWispUrl })); - showToast('error', "Invalid URL. Reverting to default..."); + window.location.replace("https://bisd.schoology.com/home"); } - } - function updateServerInfo() { - const speedSpan = document.getElementById('server-speed'); - const uptimeSpan = document.getElementById('server-uptime'); - const specsSpan = document.getElementById('server-specs'); - let uptimeInterval; - - fetch('/api/info') - .then(response => { - if (!response.ok) throw new Error(); - return response.json(); - }) - .then(data => { - if (data?.speed) { - const updateUptimeDisplay = () => { - const uptime = Date.now() - data.startTime; - const days = Math.floor(uptime / 86400000); - const hours = Math.floor((uptime % 86400000) / 3600000); - const minutes = Math.floor((uptime % 3600000) / 60000); - const seconds = Math.floor((uptime % 60000) / 1000); - uptimeSpan.textContent = `${days}d ${hours}h ${minutes}m ${seconds}s`; - }; - - updateUptimeDisplay(); - clearInterval(uptimeInterval); - uptimeInterval = setInterval(updateUptimeDisplay, 1000); - - speedSpan.textContent = `${data.speed} (${data.averageLatency}ms)`; - specsSpan.textContent = data.specs; + document.getElementById("aboutblank-toggle").addEventListener("change", function() { + localStorage.setItem("aboutBlankChecked", JSON.stringify(this.checked)); + if (this.checked) { + showToast('success', 'Auto About:Blank is now enabled.'); + } else { + showToast('error', 'Auto About:Blank is now disabled.'); } - setTimeout(updateServerInfo, 10000); - }) - .catch(() => { - speedSpan.textContent = 'Connection error'; - setTimeout(updateServerInfo, 30000); - }); - } + runScriptIfChecked(); + }); - function initializeInfo() { - const infoTab = document.getElementById('info-tab'); - const infoContent = document.getElementById('info-content'); + window.addEventListener("load", function() { + const aboutBlankChecked = JSON.parse(localStorage.getItem("aboutBlankChecked")) || false; + document.getElementById("aboutblank-toggle").checked = aboutBlankChecked; + runScriptIfChecked(); + }); - const startMonitoring = () => { - updateServerInfo(); - infoTab.removeEventListener('click', startMonitoring); - }; - - if (infoContent.classList.contains('active')) { - updateServerInfo(); - } else { - infoTab.addEventListener('click', startMonitoring, { once: true }); + function updateWispServerUrl(url) { + if (isValidUrl(url)) { + currentWispUrl = url; + localStorage.setItem('customWispUrl', url); + document.dispatchEvent(new CustomEvent('wispUrlChanged', { + detail: currentWispUrl + })); + showToast('success', `Wisp Server URL changed to: ${currentWispUrl}`); + } else { + currentWispUrl = defaultWispUrl; + localStorage.setItem('customWispUrl', defaultWispUrl); + document.dispatchEvent(new CustomEvent('wispUrlChanged', { + detail: currentWispUrl + })); + showToast('error', "Invalid URL. Reverting to default..."); + } } - } - function showToast(type, message) { - const toast = document.createElement('div'); - toast.className = `toast ${type} show`; - const icons = { - success: '', - error: '', - info: '', - warning: '' - }; - toast.innerHTML = `${icons[type]||''}${message}`; - const progressBar = document.createElement('div'); - progressBar.className = 'progress-bar'; - toast.appendChild(progressBar); - const closeBtn = document.createElement('button'); - closeBtn.className = 'toast-close'; - closeBtn.innerHTML = ''; - closeBtn.addEventListener('click', () => { - toast.classList.replace('show', 'hide'); - setTimeout(() => toast.remove(), 500); - }); - toast.appendChild(closeBtn); - document.body.appendChild(toast); - setTimeout(() => { - toast.classList.replace('show', 'hide'); - setTimeout(() => toast.remove(), 500); - }, 3000); - } - saveWispBtn.addEventListener('click', () => updateWispServerUrl(wispInput.value.trim())); - settingsIcon.addEventListener('click', e => { - e.preventDefault(); - toggleSettingsMenu(); - }); - closeSettingsBtn.addEventListener('click', toggleSettingsMenu); - function toggleSettingsMenu() { - if (isToggling) return; - isToggling = true; - const icon = document.querySelector('#settings-icon i.settings-icon'); - if (settingsMenu.classList.contains('open')) { - settingsMenu.classList.add('close'); - icon.classList.replace('fa-solid', 'fa-regular'); - setTimeout(() => { - settingsMenu.classList.remove('open', 'close'); - isToggling = false; - }, 300); - } else { - settingsMenu.classList.add('open'); - icon.classList.replace('fa-regular', 'fa-solid'); - setTimeout(() => { - settingsMenu.classList.remove('close'); - isToggling = false; - }, 300); + function updateServerInfo() { + const speedSpan = document.getElementById('server-speed'); + const uptimeSpan = document.getElementById('server-uptime'); + const specsSpan = document.getElementById('server-specs'); + let uptimeInterval; + + fetch('/api/info') + .then(response => { + if (!response.ok) throw new Error(); + return response.json(); + }) + .then(data => { + if (data?.speed) { + const updateUptimeDisplay = () => { + const uptime = Date.now() - data.startTime; + const days = Math.floor(uptime / 86400000); + const hours = Math.floor((uptime % 86400000) / 3600000); + const minutes = Math.floor((uptime % 3600000) / 60000); + const seconds = Math.floor((uptime % 60000) / 1000); + uptimeSpan.textContent = `${days}d ${hours}h ${minutes}m ${seconds}s`; + }; + + updateUptimeDisplay(); + clearInterval(uptimeInterval); + uptimeInterval = setInterval(updateUptimeDisplay, 1000); + + speedSpan.textContent = `${data.speed} (${data.averageLatency}ms)`; + specsSpan.textContent = data.specs; + } + setTimeout(updateServerInfo, 10000); + }) + .catch(() => { + speedSpan.textContent = 'Connection error'; + setTimeout(updateServerInfo, 30000); + }); } - } - transportSelected.addEventListener('click', e => { - e.stopPropagation(); - transportOptions.classList.toggle('transport-show'); - transportSelected.classList.toggle('transport-arrow-active'); - }); + function initializeInfo() { + const infoTab = document.getElementById('info-tab'); + const infoContent = document.getElementById('info-content'); - Array.from(transportOptions.getElementsByTagName('div')).forEach(option => { - option.addEventListener('click', function (e) { - e.stopPropagation(); - const val = this.textContent; - transportSelected.textContent = val; - localStorage.setItem('transport', val.toLowerCase()); - transportOptions.classList.remove('transport-show'); - transportSelected.classList.remove('transport-arrow-active'); - document.dispatchEvent(new CustomEvent('newTransport', { detail: val.toLowerCase() })); - showToast('success', `Transport changed to ${val}`); + const startMonitoring = () => { + updateServerInfo(); + infoTab.removeEventListener('click', startMonitoring); + }; + + if (infoContent.classList.contains('active')) { + updateServerInfo(); + } else { + infoTab.addEventListener('click', startMonitoring, { + once: true + }); + } + } + + function showToast(type, message) { + const toast = document.createElement('div'); + toast.className = `toast ${type} show`; + const icons = { + success: '', + error: '', + info: '', + warning: '' + }; + toast.innerHTML = `${icons[type]||''}${message}`; + const progressBar = document.createElement('div'); + progressBar.className = 'progress-bar'; + toast.appendChild(progressBar); + const closeBtn = document.createElement('button'); + closeBtn.className = 'toast-close'; + closeBtn.innerHTML = ''; + closeBtn.addEventListener('click', () => { + toast.classList.replace('show', 'hide'); + setTimeout(() => toast.remove(), 500); + }); + toast.appendChild(closeBtn); + document.body.appendChild(toast); + setTimeout(() => { + toast.classList.replace('show', 'hide'); + setTimeout(() => toast.remove(), 500); + }, 3000); + } + + saveWispBtn.addEventListener('click', () => updateWispServerUrl(wispInput.value.trim())); + settingsIcon.addEventListener('click', e => { + e.preventDefault(); + toggleSettingsMenu(); }); - }); + closeSettingsBtn.addEventListener('click', toggleSettingsMenu); - document.getElementById('proxy-content').classList.add('active'); + function toggleSettingsMenu() { + if (isToggling) return; + isToggling = true; + const icon = document.querySelector('#settings-icon i.settings-icon'); + if (settingsMenu.classList.contains('open')) { + settingsMenu.classList.add('close'); + icon.classList.replace('fa-solid', 'fa-regular'); + setTimeout(() => { + settingsMenu.classList.remove('open', 'close'); + isToggling = false; + }, 300); + } else { + settingsMenu.classList.add('open'); + icon.classList.replace('fa-regular', 'fa-solid'); + setTimeout(() => { + settingsMenu.classList.remove('close'); + isToggling = false; + }, 300); + } + } - function switchTab(activeTab, activeContent, ...others) { - others.forEach(id => document.getElementById(id).classList.remove('active')); - document.getElementById(activeTab).classList.add('active'); - document.getElementById(activeContent).classList.add('active'); - } - - document.getElementById('proxy-tab').addEventListener('click', () => switchTab('proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'appearance-tab', 'appearance-content', 'info-tab', 'info-content')); - document.getElementById('cloak-tab').addEventListener('click', () => switchTab('cloak-tab', 'cloak-content', 'proxy-tab', 'proxy-content', 'appearance-tab', 'appearance-content', 'info-tab', 'info-content')); - document.getElementById('appearance-tab').addEventListener('click', () => switchTab('appearance-tab', 'appearance-content', 'proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'info-tab', 'info-content')); - document.getElementById('info-tab').addEventListener('click', () => switchTab('info-tab', 'info-content', 'proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'appearance-tab', 'appearance-content')); - - document.querySelectorAll('.tab-button').forEach(btn => { - btn.addEventListener('click', function () { - const icon = this.querySelector('i'); - if (!icon.classList.contains('fa-bounce')) { - icon.classList.add('fa-bounce'); - setTimeout(() => icon.classList.remove('fa-bounce'), 750); - } + transportSelected.addEventListener('click', e => { + e.stopPropagation(); + transportOptions.classList.toggle('transport-show'); + transportSelected.classList.toggle('transport-arrow-active'); }); - }); - navbarToggle.addEventListener('change', function () { - showToast(this.checked ? 'success' : 'error', `Navigation Bar ${this.checked ? 'enabled' : 'disabled'}`); - }); + Array.from(transportOptions.getElementsByTagName('div')).forEach(option => { + option.addEventListener('click', function(e) { + e.stopPropagation(); + const val = this.textContent; + transportSelected.textContent = val; + localStorage.setItem('transport', val.toLowerCase()); + transportOptions.classList.remove('transport-show'); + transportSelected.classList.remove('transport-arrow-active'); + document.dispatchEvent(new CustomEvent('newTransport', { + detail: val.toLowerCase() + })); + showToast('success', `Transport changed to ${val}`); + }); + }); - initializeInfo(); + document.getElementById('proxy-content').classList.add('active'); + + function switchTab(activeTab, activeContent, ...others) { + others.forEach(id => document.getElementById(id).classList.remove('active')); + document.getElementById(activeTab).classList.add('active'); + document.getElementById(activeContent).classList.add('active'); + } + + document.getElementById('proxy-tab').addEventListener('click', () => switchTab('proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'appearance-tab', 'appearance-content', 'info-tab', 'info-content')); + document.getElementById('cloak-tab').addEventListener('click', () => switchTab('cloak-tab', 'cloak-content', 'proxy-tab', 'proxy-content', 'appearance-tab', 'appearance-content', 'info-tab', 'info-content')); + document.getElementById('appearance-tab').addEventListener('click', () => switchTab('appearance-tab', 'appearance-content', 'proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'info-tab', 'info-content')); + document.getElementById('info-tab').addEventListener('click', () => switchTab('info-tab', 'info-content', 'proxy-tab', 'proxy-content', 'cloak-tab', 'cloak-content', 'appearance-tab', 'appearance-content')); + + document.querySelectorAll('.tab-button').forEach(btn => { + btn.addEventListener('click', function() { + const icon = this.querySelector('i'); + if (!icon.classList.contains('fa-bounce')) { + icon.classList.add('fa-bounce'); + setTimeout(() => icon.classList.remove('fa-bounce'), 750); + } + }); + }); + + navbarToggle.addEventListener('change', function() { + localStorage.setItem('navbarToggled', this.checked.toString()); + showToast(this.checked ? 'success' : 'error', `Navigation Bar ${this.checked ? 'enabled' : 'disabled'}`); + if (window.APP && typeof window.APP.updateNavbarDisplay === 'function') { + window.APP.updateNavbarDisplay(); + } + }); + + initializeInfo(); }); \ No newline at end of file diff --git a/public/assets/js/shortcuts.js b/public/assets/js/shortcuts.js index 919201ee..3f695472 100644 --- a/public/assets/js/shortcuts.js +++ b/public/assets/js/shortcuts.js @@ -1,109 +1,147 @@ -(function () { - document.addEventListener('DOMContentLoaded', function () { - function getSearchInputs() { - return [ - document.getElementById('searchInput'), - document.getElementById('searchInputt'), - document.getElementById('gameSearchInput'), - document.getElementById('appSearchInput') - ].filter(Boolean); +(function(){ + document.addEventListener('DOMContentLoaded', function(){ + const indicators = document.querySelectorAll('.shortcut-indicator, .shortcut-indicator-2, .shortcut-indicator-3, .shortcut-indicator-4'); + const arrowIndicators = document.querySelectorAll('.shortcut-indicator, .shortcut-indicator-2'); + indicators.forEach(el => { + if (!el.dataset.original) el.dataset.original = el.innerHTML; + }); + + function getSearchInputs(){ + return [ + document.getElementById('searchInput'), + document.getElementById('searchInputt'), + document.getElementById('gameSearchInput'), + document.getElementById('shortcutSearchInput') + ].filter(Boolean); + } + + function isVisible(el){ + return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); + } + + function focusFirstVisibleInput(){ + const inputs = getSearchInputs(); + for (let inp of inputs) { + if (isVisible(inp)) { + inp.focus(); + return inp; + } } - - function isVisible(el) { - return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); + if (inputs[0]) { + inputs[0].focus(); + return inputs[0]; } + return null; + } - function focusFirstVisibleInput() { - const inputs = getSearchInputs(); - for (const input of inputs) { - if (isVisible(input)) { - input.focus(); - return; - } - } - if (inputs.length) { - inputs[0].focus(); - } + let arrowMode = false; + + function updateIndicatorState(){ + const hasText = getSearchInputs().some(i => i.value.trim() !== ''); + if (hasText === arrowMode) return; + arrowMode = hasText; + arrowIndicators.forEach(ind => { + ind.classList.remove('fadeIn'); + ind.classList.add('fadeOut'); + setTimeout(() => { + if (hasText) { + ind.innerHTML = ''; + ind.classList.add('arrow-mode'); + } else { + ind.innerHTML = ind.dataset.original; + ind.classList.remove('arrow-mode'); + } + ind.classList.remove('fadeOut'); + ind.classList.add('fadeIn'); + }, 100); + }); + } + + function triggerSubmit(input){ + const query = input.value.trim(); + if (!query) { + showToast('Please enter something in the Search Bar.','error','warning'); + return; } + if (window.APP && typeof APP.handleSearch === 'function') { + APP.handleSearch(query); + if (input.id === 'searchInput') input.value = ''; + } + } - document.addEventListener('keydown', function (e) { - if (e.key === 'Escape') { - const inputs = getSearchInputs(); - const active = document.activeElement; - if (inputs.includes(active)) { - active.blur(); - e.preventDefault(); - return; - } - } + function handleIndicatorAction(){ + const inputs = getSearchInputs().filter(isVisible); + const active = document.activeElement; + if (inputs.includes(active) && active.value.trim()) { + triggerSubmit(active); + return; + } + const withText = inputs.find(i => i.value.trim()); + if (withText) { + triggerSubmit(withText); + return; + } + focusFirstVisibleInput(); + } - if (e.ctrlKey && e.key.toLowerCase() === 's') { - e.preventDefault(); - focusFirstVisibleInput(); - } - }); + updateIndicatorState(); + getSearchInputs().forEach(i => i.addEventListener('input', updateIndicatorState)); - document.querySelectorAll( - '.shortcut-indicator, .shortcut-indicator-2, .shortcut-indicator-3, .shortcut-indicator-4' - ).forEach(function (el) { - el.addEventListener('click', function (e) { - e.preventDefault(); - focusFirstVisibleInput(); - }); - }); + document.addEventListener('keydown', e => { + if (e.key === 'Escape') { + const act = document.activeElement; + if (getSearchInputs().includes(act)) { + act.blur(); + e.preventDefault(); + } + } + if (e.ctrlKey && e.key.toLowerCase() === 's') { + e.preventDefault(); + focusFirstVisibleInput(); + } + }); - document.body.addEventListener('click', function (e) { - if ( - e.target.classList.contains('shortcut-indicator') || - e.target.classList.contains('shortcut-indicator-2') || - e.target.classList.contains('shortcut-indicator-3') || - e.target.classList.contains('shortcut-indicator-4') - ) { - e.preventDefault(); - focusFirstVisibleInput(); - } - }); + document.body.addEventListener('click', e => { + const ind = e.target.closest('.shortcut-indicator, .shortcut-indicator-2, .shortcut-indicator-3, .shortcut-indicator-4'); + if (!ind) return; + e.preventDefault(); + if (ind.classList.contains('arrow-mode')) { + handleIndicatorAction(); + } else { + focusFirstVisibleInput(); + } + }); - const coolIframe = document.getElementById('cool-iframe'); - if (coolIframe) { - coolIframe.addEventListener('load', function () { - const doc = coolIframe.contentWindow.document; - - doc.addEventListener('keydown', function (e) { - if (e.key === 'Escape') { - if (doc.activeElement && doc.activeElement.blur) { - doc.activeElement.blur(); - e.preventDefault(); - return; - } - } - if (e.ctrlKey && e.key.toLowerCase() === 's') { - e.preventDefault(); - document.dispatchEvent(new KeyboardEvent('keydown', { - key: 's', - ctrlKey: true - })); - } + const coolIframe = document.getElementById('cool-iframe'); + if (coolIframe){ + coolIframe.addEventListener('load', () => { + const doc = coolIframe.contentWindow.document; + doc.addEventListener('keydown', innerE => { + if (innerE.key === 'Escape') { + if (doc.activeElement.blur) doc.activeElement.blur(); + innerE.preventDefault(); + } + if (innerE.ctrlKey && innerE.key.toLowerCase() === 's') { + innerE.preventDefault(); + document.dispatchEvent(new KeyboardEvent('keydown', { key: 's', ctrlKey: true, bubbles: true })); + } + }); + doc.querySelectorAll('.shortcut-indicator, .shortcut-indicator-2, .shortcut-indicator-3, .shortcut-indicator-4') + .forEach(el => { + el.addEventListener('click', ev => { + ev.preventDefault(); + window.parent.postMessage({ type: 'iframe-focus-search' }, '*'); }); - - doc.querySelectorAll( - '.shortcut-indicator, .shortcut-indicator-2, .shortcut-indicator-3, .shortcut-indicator-4' - ).forEach(function (iframeEl) { - iframeEl.addEventListener('click', function (e) { - e.preventDefault(); - window.parent.postMessage({ - type: 'iframe-focus-search' - }, '*'); - }); - }); - }); - } - - window.addEventListener('message', function (event) { - const msg = event.data; - if (msg.type === 'iframe-focus-search') { - focusFirstVisibleInput(); - } + }); }); - }); + } + + window.addEventListener('message', msgEvt => { + if (msgEvt.data?.type === 'iframe-focus-search'){ + const inp = focusFirstVisibleInput(); + if (inp && inp.value.trim()) triggerSubmit(inp); + } + }); + + }); })(); \ No newline at end of file diff --git a/public/wah/cute2.js b/public/wah/cute2.js index 144555f2..088a3ae3 100644 --- a/public/wah/cute2.js +++ b/public/wah/cute2.js @@ -1,367 +1,760 @@ "use strict"; + (() => { - const Ultraviolet = self.Ultraviolet; - const REMOVE_HEADERS = [ - "cross-origin-embedder-policy", - "cross-origin-opener-policy", - "cross-origin-resource-policy", - "content-security-policy", - "content-security-policy-report-only", - "expect-ct", - "feature-policy", - "origin-isolation", - "strict-transport-security", - "upgrade-insecure-requests", - "x-content-type-options", - "x-download-options", - "x-frame-options", - "x-permitted-cross-domain-policies", - "x-powered-by", - "x-xss-protection" - ]; - const NO_BODY_METHODS = ["GET", "HEAD"]; - class UVServiceWorker extends Ultraviolet.EventEmitter { - constructor(config = __uv$config) { - super(); - config.prefix = config.prefix || "/wah/a/"; - this.config = config; - this.bareClient = new Ultraviolet.BareClient(); - this.analyticsData = {}; - this.syncInterval = 300000; - this.lastSync = Date.now(); - } - route({ request }) { - return request.url.startsWith(location.origin + this.config.prefix); - } - async fetch({ request }) { - if (request.method.toUpperCase() === "OPTIONS") { - return new Response(null, { - status: 204, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization" - } - }); - } - let requestUrl = ""; - try { - if (!request.url.startsWith(location.origin + this.config.prefix)) - return await fetch(request); - const uv = new Ultraviolet(this.config); - if (typeof this.config.construct === "function") - uv.construct(uv, "service"); - const blobPromise = NO_BODY_METHODS.includes(request.method.toUpperCase()) - ? Promise.resolve(null) - : request.blob(); - const cookieDBPromise = uv.cookie.db(); - const [body, cookieDB] = await Promise.all([blobPromise, cookieDBPromise]); - uv.meta.origin = location.origin; - uv.meta.base = uv.meta.url = new URL(uv.sourceUrl(request.url)); - const reqWrapper = new UVRequestWrapper(request, uv, body); - if (uv.meta.url.protocol === "blob:") { - reqWrapper.blob = true; - reqWrapper.base = reqWrapper.url = new URL(reqWrapper.url.pathname); - } - if (request.referrer && request.referrer.startsWith(location.origin)) { - const refUrl = new URL(uv.sourceUrl(request.referrer)); - if (reqWrapper.headers.origin || (uv.meta.url.origin !== refUrl.origin && request.mode === "cors")) - reqWrapper.headers.origin = refUrl.origin; - delete reqWrapper.headers.referer; - } - const cookies = (await uv.cookie.getCookies(cookieDB)) || []; - const cookieString = uv.cookie.serialize(cookies, uv.meta, false); - reqWrapper.headers["user-agent"] = "SomethingSomething/1.0"; - if (cookieString) reqWrapper.headers.cookie = cookieString; - const reqEvent = new InterceptionEvent(reqWrapper, null, null); - this.emit("request", reqEvent); - if (reqEvent.intercepted) return reqEvent.returnValue; - requestUrl = reqWrapper.blob - ? "blob:" + location.origin + reqWrapper.url.pathname - : reqWrapper.url; - const options = { - headers: reqWrapper.headers, - method: reqWrapper.method, - body: reqWrapper.body, - credentials: "include", - mode: reqWrapper.mode, - cache: reqWrapper.cache, - redirect: reqWrapper.redirect + const Ultraviolet = self.Ultraviolet; + + const REMOVE_HEADERS = [ + "cross-origin-embedder-policy", + "cross-origin-opener-policy", + "cross-origin-resource-policy", + "content-security-policy", + "content-security-policy-report-only", + "expect-ct", + "feature-policy", + "origin-isolation", + "strict-transport-security", + "upgrade-insecure-requests", + "x-content-type-options", + "x-download-options", + "x-frame-options", + "x-permitted-cross-domain-policies", + "x-powered-by", + "x-xss-protection" + ]; + + const NO_BODY_METHODS = ["GET", "HEAD"]; + + const FP = ``); - const progress = await getProgress(uv.meta.url.host); - if (progress) - text = text.replace(/<\/body>/i, ``); - respWrapper.body = uv.rewriteHtml(text, { - document: true, - injectHead: uv.createHtmlInject( - uv.handlerScript, - uv.bundleScript, - uv.clientScript, - uv.configScript, - uv.cookie.serialize(cookies, uv.meta, true), - request.referrer - ) - }); - } - if (respWrapper.headers["set-cookie"]) { - Promise.resolve(uv.cookie.setCookies(respWrapper.headers["set-cookie"], cookieDB, uv.meta)) - .then(() => { - self.clients.matchAll().then(clients => - clients.forEach(client => - client.postMessage({ msg: "updateCookies", url: uv.meta.url.href }) - ) - ); + + Object.keys(spoofScreen).forEach(key => { + try { + Object.defineProperty(screen, key, { + value: spoofScreen[key], + writable: false, + configurable: false + }); + } catch(e) {} + }); + + Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', { + value: () => ({ + timeZone: 'America/New_York', + locale: 'en-US' + }), + configurable: true, + writable: true + }); + + if ('getBattery' in navigator) { + navigator.getBattery = () => Promise.resolve({ + charging: true, + chargingTime: 0, + dischargingTime: Infinity, + level: 1 }); - delete respWrapper.headers["set-cookie"]; } - if (respWrapper.body) { - switch (request.destination) { - case "script": - respWrapper.body = uv.js.rewrite(await responseRaw.text()); - break; - case "worker": { - const scriptsList = [uv.bundleScript, uv.clientScript, uv.configScript, uv.handlerScript] - .map(s => JSON.stringify(s)) - .join(","); - respWrapper.body = - `if(!self.__uv){${uv.createJsInject(uv.cookie.serialize(cookies, uv.meta, true), request.referrer)}importScripts(${scriptsList});}` + - uv.js.rewrite(await responseRaw.text()); - break; + + if ('connection' in navigator) { + Object.defineProperty(navigator, 'connection', { + value: { + downlink: 10, + effectiveType: '4g', + rtt: 50, + saveData: false, + type: 'wifi' + }, + writable: false, + configurable: false + }); + } + + if ('mediaDevices' in navigator && 'enumerateDevices' in navigator.mediaDevices) { + navigator.mediaDevices.enumerateDevices = () => Promise.resolve([ + { deviceId: 'default', kind: 'audioinput', label: '', groupId: 'default' }, + { deviceId: 'default', kind: 'audiooutput', label: '', groupId: 'default' }, + { deviceId: 'default', kind: 'videoinput', label: '', groupId: 'default' } + ]); + } + + const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; + HTMLCanvasElement.prototype.toDataURL = function(type, quality) { + if (type === 'image/png' || type === 'image/jpeg') { + const blank = document.createElement('canvas'); + blank.width = this.width || 16; + blank.height = this.height || 16; + return originalToDataURL.call(blank, type, quality); } - case "style": - respWrapper.body = uv.rewriteCSS(await responseRaw.text()); - break; - } + return originalToDataURL.call(this, type, quality); + }; + + const originalGetParameter = WebGLRenderingContext.prototype.getParameter; + WebGLRenderingContext.prototype.getParameter = function(parameter) { + if (parameter === 37445) return 'Google Inc.'; + if (parameter === 37446) return 'ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0)'; + return originalGetParameter.call(this, parameter); + }; + + const originalGetChannelData = AudioBuffer.prototype.getChannelData; + AudioBuffer.prototype.getChannelData = function() { + const data = originalGetChannelData.apply(this, arguments); + for (let i = 0; i < data.length; i++) { + data[i] += Math.random() * 0.001 - 0.0005; + } + return data; + }; + + const originalNow = Performance.prototype.now; + const start = originalNow.call(performance); + Performance.prototype.now = function() { + return start + Math.random() * 3; + }; + + const originalMeasure = Performance.prototype.measure; + Performance.prototype.measure = function() { + return null; + }; + + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations = () => Promise.resolve([]); + navigator.serviceWorker.ready = Promise.resolve({ active: false }); + Object.defineProperty(navigator, 'serviceWorker', { + value: null, + writable: false, + configurable: false + }); } - respWrapper.headers["Access-Control-Allow-Origin"] = "*"; - respWrapper.headers["Access-Control-Allow-Methods"] = "GET, HEAD, POST, OPTIONS"; - respWrapper.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"; - if (reqWrapper.headers.accept === "text/event-stream") - respWrapper.headers["content-type"] = "text/event-stream"; - if (crossOriginIsolated) - respWrapper.headers["Cross-Origin-Embedder-Policy"] = "require-corp"; - this.emit("response", respEvent); - if (respEvent.intercepted) return respEvent.returnValue; - const finalResponse = new Response(respWrapper.body, { - headers: respWrapper.headers, - status: respWrapper.status, - statusText: respWrapper.statusText - }); - if (request.method.toUpperCase() === "GET") { - const cache = await caches.open("uv-cache"); - cache.put(requestUrl, finalResponse.clone()); + + if ('permissions' in navigator) { + navigator.permissions.query = () => Promise.resolve({ state: 'granted' }); } - await this.recordAnalytics("fetchSuccess", requestUrl); - this.periodicSync(); - return finalResponse; - } catch (error) { - const errHeaders = { "content-type": "text/html", "Access-Control-Allow-Origin": "*" }; - if (crossOriginIsolated) - errHeaders["Cross-Origin-Embedder-Policy"] = "require-corp"; - await this.recordAnalytics("fetchError", request.url); - if (["document", "iframe"].includes(request.destination)) - return T(error, requestUrl); - return new Response(undefined, { status: 500, headers: errHeaders }); - } - } - async recordAnalytics(type, url) { - const now = Date.now(); - if (!this.analyticsData[type]) this.analyticsData[type] = []; - this.analyticsData[type].push({ url, time: now }); - if (now - this.lastSync > this.syncInterval) { - try { - await sendAnalytics(this.analyticsData); - this.analyticsData = {}; - this.lastSync = now; - } catch (e) {} - } - } - periodicSync() { - if (Date.now() - this.lastSync > this.syncInterval) { - sendAnalytics(this.analyticsData) - .then(() => { + + const originalRTCPeerConnection = window.RTCPeerConnection; + window.RTCPeerConnection = function(...args) { + const pc = new originalRTCPeerConnection(...args); + pc.createOffer = () => Promise.resolve({ sdp: "", type: "offer" }); + pc.setLocalDescription = () => Promise.resolve(); + return pc; + }; + window.RTCPeerConnection.prototype = originalRTCPeerConnection.prototype; + })(); + `; + + class UVServiceWorker extends Ultraviolet.EventEmitter { + constructor(config = __uv$config) { + super(); + config.prefix = config.prefix || "/wah/a/"; + this.config = config; + this.bareClient = new Ultraviolet.BareClient(); this.analyticsData = {}; + this.syncInterval = 300000; this.lastSync = Date.now(); - }) - .catch(() => {}); - } + } + + route({ request }) { + return request.url.startsWith(location.origin + this.config.prefix); + } + + async fetch({ request }) { + if (request.method.toUpperCase() === "OPTIONS") { + return new Response(null, { + status: 204, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, Authorization" + } + }); + } + + let requestUrl = ""; + + try { + if (!request.url.startsWith(location.origin + this.config.prefix)) { + return await fetch(request); + } + + const uv = new Ultraviolet(this.config); + if (typeof this.config.construct === "function") { + uv.construct(uv, "service"); + } + + const blobPromise = NO_BODY_METHODS.includes(request.method.toUpperCase()) + ? Promise.resolve(null) + : request.blob(); + + const cookieDBPromise = uv.cookie.db(); + const [body, cookieDB] = await Promise.all([blobPromise, cookieDBPromise]); + + uv.meta.origin = location.origin; + uv.meta.base = uv.meta.url = new URL(uv.sourceUrl(request.url)); + + const reqWrapper = new UVRequestWrapper(request, uv, body); + + if (uv.meta.url.protocol === "blob:") { + reqWrapper.blob = true; + reqWrapper.base = reqWrapper.url = new URL(reqWrapper.url.pathname); + } + + if (request.referrer && request.referrer.startsWith(location.origin)) { + const refUrl = new URL(uv.sourceUrl(request.referrer)); + if (reqWrapper.headers.origin || + (uv.meta.url.origin !== refUrl.origin && request.mode === "cors")) { + reqWrapper.headers.origin = refUrl.origin; + } + delete reqWrapper.headers.referer; + } + + const cookies = (await uv.cookie.getCookies(cookieDB)) || []; + const cookieString = uv.cookie.serialize(cookies, uv.meta, false); + reqWrapper.headers["user-agent"] = "SomethingSomething/1.0"; + + if (cookieString) { + reqWrapper.headers.cookie = cookieString; + } + + const reqEvent = new InterceptionEvent(reqWrapper, null, null); + this.emit("request", reqEvent); + + if (reqEvent.intercepted) { + return reqEvent.returnValue; + } + + requestUrl = reqWrapper.blob + ? "blob:" + location.origin + reqWrapper.url.pathname + : reqWrapper.url; + + const options = { + headers: reqWrapper.headers, + method: reqWrapper.method, + body: reqWrapper.body, + credentials: "include", + mode: reqWrapper.mode, + cache: reqWrapper.cache, + redirect: reqWrapper.redirect + }; + + if (request.method.toUpperCase() === "GET") { + const cache = await caches.open("uv-cache"); + const cachedResponse = await cache.match(requestUrl); + + if (cachedResponse) { + await this.recordAnalytics("cacheHit", requestUrl); + return cachedResponse.clone(); + } + } + + let responseRaw; + for (let i = 0; i < 2; i++) { + try { + responseRaw = await this.bareClient.fetch(requestUrl, options); + break; + } catch (e) { + if (i === 1) throw e; + } + } + + const respWrapper = new UVResponseWrapper(reqWrapper, responseRaw); + const respEvent = new InterceptionEvent(respWrapper, null, null); + this.emit("beforemod", respEvent); + + if (respEvent.intercepted) { + return respEvent.returnValue; + } + + REMOVE_HEADERS.forEach(header => { + if (respWrapper.headers[header]) { + delete respWrapper.headers[header]; + } + }); + + if (respWrapper.headers.location) { + respWrapper.headers.location = uv.rewriteUrl(respWrapper.headers.location); + } + + if (["document", "iframe"].includes(request.destination)) { + let text = await responseRaw.text(); + const headIdx = text.toLowerCase().indexOf(''); + + if (headIdx !== -1) { + text = text.slice(0, headIdx + 6) + FP + text.slice(headIdx + 6); + } + + if (Array.isArray(this.config.inject)) { + const bodyIdx = text.search(//i); + const currentUrl = new URL(requestUrl); + + for (const rule of this.config.inject) { + if (new RegExp(rule.host).test(currentUrl.host)) { + if (rule.injectTo === "head" && headIdx !== -1) { + text = text.slice(0, headIdx) + rule.html + text.slice(headIdx); + } + else if (rule.injectTo === "body" && bodyIdx !== -1) { + text = text.slice(0, bodyIdx) + rule.html + text.slice(bodyIdx); + } + } + } + } + + text = text.replace( + /<\/body>/i, + `` + ); + + const progress = await getProgress(uv.meta.url.host); + if (progress) { + text = text.replace( + /<\/body>/i, + `` + ); + } + + respWrapper.body = uv.rewriteHtml(text, { + document: true, + injectHead: uv.createHtmlInject( + uv.handlerScript, + uv.bundleScript, + uv.clientScript, + uv.configScript, + uv.cookie.serialize(cookies, uv.meta, true), + request.referrer + ) + }); + } + + if (respWrapper.headers["set-cookie"]) { + Promise.resolve( + uv.cookie.setCookies(respWrapper.headers["set-cookie"], cookieDB, uv.meta) + ).then(() => { + self.clients.matchAll().then(clients => { + clients.forEach(client => { + client.postMessage({ + msg: "updateCookies", + url: uv.meta.url.href + }); + }); + }); + }); + delete respWrapper.headers["set-cookie"]; + } + + if (respWrapper.body) { + switch (request.destination) { + case "script": + respWrapper.body = uv.js.rewrite(await responseRaw.text()); + break; + + case "worker": { + const scriptsList = [ + uv.bundleScript, + uv.clientScript, + uv.configScript, + uv.handlerScript + ].map(s => JSON.stringify(s)).join(","); + + respWrapper.body = + `if(!self.__uv){${uv.createJsInject(uv.cookie.serialize(cookies, uv.meta, true), request.referrer)}importScripts(${scriptsList});}` + + uv.js.rewrite(await responseRaw.text()); + break; + } + + case "style": + respWrapper.body = uv.rewriteCSS(await responseRaw.text()); + break; + } + } + + respWrapper.headers["Access-Control-Allow-Origin"] = "*"; + respWrapper.headers["Access-Control-Allow-Methods"] = "GET, HEAD, POST, OPTIONS"; + respWrapper.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"; + + if (reqWrapper.headers.accept === "text/event-stream") { + respWrapper.headers["content-type"] = "text/event-stream"; + } + + if (crossOriginIsolated) { + respWrapper.headers["Cross-Origin-Embedder-Policy"] = "require-corp"; + } + + this.emit("response", respEvent); + if (respEvent.intercepted) { + return respEvent.returnValue; + } + + const finalResponse = new Response(respWrapper.body, { + headers: respWrapper.headers, + status: respWrapper.status, + statusText: respWrapper.statusText + }); + + if (request.method.toUpperCase() === "GET") { + const cache = await caches.open("uv-cache"); + cache.put(requestUrl, finalResponse.clone()); + } + + await this.recordAnalytics("fetchSuccess", requestUrl); + this.periodicSync(); + return finalResponse; + } + catch (error) { + const errHeaders = { + "content-type": "text/html", + "Access-Control-Allow-Origin": "*" + }; + + if (crossOriginIsolated) { + errHeaders["Cross-Origin-Embedder-Policy"] = "require-corp"; + } + + await this.recordAnalytics("fetchError", request.url); + + if (["document", "iframe"].includes(request.destination)) { + return T(error, requestUrl); + } + + return new Response(undefined, { + status: 500, + headers: errHeaders + }); + } + } + + async recordAnalytics(type, url) { + const now = Date.now(); + if (!this.analyticsData[type]) { + this.analyticsData[type] = []; + } + + this.analyticsData[type].push({ url, time: now }); + + if (now - this.lastSync > this.syncInterval) { + try { + await sendAnalytics(this.analyticsData); + this.analyticsData = {}; + this.lastSync = now; + } + catch (e) {} + } + } + + periodicSync() { + if (Date.now() - this.lastSync > this.syncInterval) { + sendAnalytics(this.analyticsData) + .then(() => { + this.analyticsData = {}; + this.lastSync = Date.now(); + }) + .catch(() => {}); + } + } + + static get Ultraviolet() { + return Ultraviolet; + } } - static get Ultraviolet() { - return Ultraviolet; + + class UVResponseWrapper { + constructor(reqWrapper, rawResponse) { + this.request = reqWrapper; + this.raw = rawResponse; + this.ultraviolet = reqWrapper.ultraviolet; + this.headers = {}; + + for (const k in rawResponse.rawHeaders) { + this.headers[k.toLowerCase()] = rawResponse.rawHeaders[k]; + } + + this.status = rawResponse.status; + this.statusText = rawResponse.statusText; + this.body = rawResponse.body; + } + + get url() { + return this.request.url; + } + + get base() { + return this.request.base; + } + + set base(v) { + this.request.base = v; + } + + getHeader(n) { + return Array.isArray(this.headers[n]) + ? this.headers[n][0] + : this.headers[n]; + } } - } - class UVResponseWrapper { - constructor(reqWrapper, rawResponse) { - this.request = reqWrapper; - this.raw = rawResponse; - this.ultraviolet = reqWrapper.ultraviolet; - this.headers = {}; - for (const k in rawResponse.rawHeaders) { - this.headers[k.toLowerCase()] = rawResponse.rawHeaders[k]; - } - this.status = rawResponse.status; - this.statusText = rawResponse.statusText; - this.body = rawResponse.body; + + class UVRequestWrapper { + constructor(request, uv, body = null) { + this.ultraviolet = uv; + this.request = request; + this.headers = Object.fromEntries(request.headers.entries()); + this.method = request.method; + this.body = body; + this.cache = request.cache; + this.redirect = request.redirect; + this.credentials = "omit"; + this.mode = request.mode === "cors" ? request.mode : "same-origin"; + this.blob = false; + } + + get url() { + return this.ultraviolet.meta.url; + } + + set url(v) { + this.ultraviolet.meta.url = v; + } + + get base() { + return this.ultraviolet.meta.base; + } + + set base(v) { + this.ultraviolet.meta.base = v; + } } - get url() { - return this.request.url; + + class InterceptionEvent { + #intercepted = false; + #returnValue = null; + + constructor(data = {}, target = null, that = null) { + this.data = data; + this.target = target; + this.that = that; + } + + get intercepted() { + return this.#intercepted; + } + + get returnValue() { + return this.#returnValue; + } + + respondWith(v) { + this.#returnValue = v; + this.#intercepted = true; + } } - get base() { - return this.request.base; - } - set base(v) { - this.request.base = v; - } - getHeader(n) { - return Array.isArray(this.headers[n]) ? this.headers[n][0] : this.headers[n]; - } - } - class UVRequestWrapper { - constructor(request, uv, body = null) { - this.ultraviolet = uv; - this.request = request; - this.headers = Object.fromEntries(request.headers.entries()); - this.method = request.method; - this.body = body; - this.cache = request.cache; - this.redirect = request.redirect; - this.credentials = "omit"; - this.mode = request.mode === "cors" ? request.mode : "same-origin"; - this.blob = false; - } - get url() { - return this.ultraviolet.meta.url; - } - set url(v) { - this.ultraviolet.meta.url = v; - } - get base() { - return this.ultraviolet.meta.base; - } - set base(v) { - this.ultraviolet.meta.base = v; - } - } - class InterceptionEvent { - #intercepted = false; - #returnValue = null; - constructor(data = {}, target = null, that = null) { - this.data = data; - this.target = target; - this.that = that; - } - get intercepted() { - return this.#intercepted; - } - get returnValue() { - return this.#returnValue; - } - respondWith(v) { - this.#returnValue = v; - this.#intercepted = true; - } - } - async function getProgress(host) { - const cache = await caches.open("progress-cache"); - const response = await cache.match("progress-" + host); - if (response) { - try { - return await response.json(); - } catch (e) { + + async function getProgress(host) { + const cache = await caches.open("progress-cache"); + const response = await cache.match("progress-" + host); + + if (response) { + try { + return await response.json(); + } + catch (e) { + return null; + } + } return null; - } } - return null; - } - async function saveProgress(host, data) { - const cache = await caches.open("progress-cache"); - await cache.put("progress-" + host, new Response(JSON.stringify(data))); - } - async function sendAnalytics(data) { - try { - await fetch(location.origin + "/wah/a/analytics", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data) - }); - } catch (e) {} - } - self.addEventListener("message", e => { - if (e.data && e.data.type === "saveProgress" && e.data.host && e.data.data) { - saveProgress(e.data.host, e.data.data); + + async function saveProgress(host, data) { + const cache = await caches.open("progress-cache"); + await cache.put("progress-" + host, new Response(JSON.stringify(data))); } - if (e.data && e.data.type === "syncAnalytics") { - sendAnalytics(e.data.analytics || {}); + + async function sendAnalytics(data) { + try { + await fetch(location.origin + "/wah/a/analytics", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data) + }); + } + catch (e) {} } - if (e.data && e.data.type === "clearProgress" && e.data.host) { - caches.open("progress-cache").then(cache => { - cache.delete("progress-" + e.data.host); - }); + + self.addEventListener("message", e => { + if (e.data && e.data.type === "saveProgress" && e.data.host && e.data.data) { + saveProgress(e.data.host, e.data.data); + } + + if (e.data && e.data.type === "syncAnalytics") { + sendAnalytics(e.data.analytics || {}); + } + + if (e.data && e.data.type === "clearProgress" && e.data.host) { + caches.open("progress-cache").then(cache => { + cache.delete("progress-" + e.data.host); + }); + } + }); + + function E(errMsg, fetchedUrl) { + const s = ` + errorTrace.value = ${JSON.stringify(errMsg)}; + fetchedURL.textContent = ${JSON.stringify(fetchedUrl)}; + for (const n of document.querySelectorAll("#uvHostname")) { + n.textContent = ${JSON.stringify(location.hostname)}; + } + reload.addEventListener("click", () => location.reload()); + uvVersion.textContent = ${JSON.stringify("v.0.0.1")}; + uvBuild.textContent = ${JSON.stringify("idk")}; + `; + + return `Error + + + +
+

Oh noooooo error processing your request 😢

+
+

Failed to load :(

+

Internal Server Error

+ +

Make sure you entered the correct address!!

+ +
+

Waves (build )

+
+ + + `; } - }); - function E(errMsg, fetchedUrl) { - const s = `errorTrace.value=${JSON.stringify(errMsg)};fetchedURL.textContent=${JSON.stringify(fetchedUrl)};for(const n of document.querySelectorAll("#uvHostname"))n.textContent=${JSON.stringify(location.hostname)};reload.addEventListener("click",()=>location.reload());uvVersion.textContent=${JSON.stringify("v.0.0.1")};uvBuild.textContent=${JSON.stringify("idk")};`; - return `Error

Oh noooooo error processing your request 😢


Failed to load :(

Internal Server Error

Make sure you entered the correct address!!


Waves (build )

`; - } - function T(err, fetchedUrl) { - const h = { "content-type": "text/html", "Access-Control-Allow-Origin": "*" }; - if (crossOriginIsolated) - h["Cross-Origin-Embedder-Policy"] = "require-corp"; - return new Response(E(String(err), fetchedUrl), { status: 500, headers: h }); - } - self.addEventListener("install", e => { - self.skipWaiting(); - }); - self.addEventListener("activate", e => { - e.waitUntil( - (async () => { - if (self.registration.navigationPreload) - await self.registration.navigationPreload.enable(); - await self.clients.claim(); - })() - ); - }); - self.UVServiceWorker = UVServiceWorker; -})(); + + function T(err, fetchedUrl) { + const h = { + "content-type": "text/html", + "Access-Control-Allow-Origin": "*" + }; + + if (crossOriginIsolated) { + h["Cross-Origin-Embedder-Policy"] = "require-corp"; + } + + return new Response(E(String(err), fetchedUrl), { + status: 500, + headers: h + }); + } + + self.addEventListener("install", e => { + self.skipWaiting(); + }); + + self.addEventListener("activate", e => { + e.waitUntil( + (async () => { + if (self.registration.navigationPreload) { + await self.registration.navigationPreload.enable(); + } + await self.clients.claim(); + })() + ); + }); + + self.UVServiceWorker = UVServiceWorker; +})(); \ No newline at end of file