preparing for 2024 update

Co-authored-by: Cobalt-60 <plastics-eater@users.noreply.github.com>
This commit is contained in:
Russell2259
2023-12-31 10:51:47 -07:00
parent d857195e93
commit 6052835a02
33 changed files with 922 additions and 664 deletions
+2 -5
View File
@@ -1,7 +1,7 @@
{
"name": "polaris",
"version": "1.0.0",
"description": "The future of school gaming",
"description": "The professional unblocked games site.",
"main": "index.js",
"type": "module",
"scripts": {
@@ -9,7 +9,7 @@
"prod": "node server prod",
"dev": "node server dev"
},
"author": "Polaris Developments",
"author": "Polaris Development Group",
"license": "GNU-3.0-or-later",
"dependencies": {
"@tomphttp/bare-server-node": "*",
@@ -21,8 +21,5 @@
"jsdom": "^23.0.1",
"mime": "*",
"uuid": "^9.0.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
+33 -7
View File
@@ -2,21 +2,47 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--el:meta-->
<!--el:adtop-->
<!--el:analytics-->
<title>Loading...</title>
<link rel="stylesheet" href="/assets/css/main.css">
<title>404 Error | Polaris</title>
</head>
<body>
<div class="container centered">
<h1 style="font-size: 50px;">Loading</h1>
<p>
Loading the proxy...
</p>
</div>
<script src="/uv/uv.bundle.js"></script>
<script src="/uv/uv.config.js"></script>
<script>
navigator.serviceWorker.register('/assets/ultraviolet/sw.js', {
scope: __uv$config.prefix
}).then(e => location.reload()).catch(e => { });
<script type="module">
import { loadProxyWorker } from '/assets/js/utils.js';
try { document.body.dataset.theme = JSON.parse(localStorage.getItem('settings')).theme || 'system-default'; }
catch {
document.body.dataset.theme = 'system-default';
sessionStorage.setItem('settings', JSON.stringify({
theme: 'system-default'
}));
}
loadProxyWorker('uv')
.then(e => location.reload())
.catch(e => {
document.querySelector('h1').textContent = 'Error';
document.querySelector('p').textContent = 'Failed to load proxy';
});
</script>
<!--el:{{mode === 'dev'}}:development-->
</body>
</html>
+1 -1
View File
@@ -7,6 +7,6 @@ export default {
port: 8080,
mode: 'dev',
minify: false,
assetScrambling: true,
assetScrambling: false,
allowDangerousTemplateInsert: true
};
+16 -8
View File
@@ -18,6 +18,11 @@ const mode = (process.argv[2] === 'prod' || process.argv[2] === 'dev' ? process.
const port = (process.argv[2] !== 'prod' && process.argv[2] !== 'dev' && Boolean(Number(process.argv[2]))) ? process.argv[2] : (Boolean(Number(process.argv[3])) ? process.argv[3] : (Boolean(Number(config.port)) ? config.port : (mode === 'prod' ? 80 : 8080)));
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const swPaths = [
'/uv/sw.js',
'/assets/js/offline.js'
];
app.get('/cdn/*', cors({
origin: false
}), async (req, res, next) => {
@@ -38,6 +43,12 @@ app.get('/cdn/*', cors({
} else next();
});
app.get('*', (req, res, next) => {
if (swPaths.includes(req.path)) res.setHeader('Service-Worker-Allowed', '/');
next();
});
app.get('/asset', (req, res, next) => {
if (req.query.asset) {
const {
@@ -67,14 +78,11 @@ app.get('/asset/:token', async (req, res, next) => {
}
});
app.get('/uv/service*', async (req, res) => {
res.end(await rewriter.html(fs.readFileSync(path.join(__dirname, './pages/uv_404.html'))));
res.setHeader('Service-Worker-Allowed', 'true');
});
app.get('/uv/service/*', async (req, res) => res.end(await rewriter.html(fs.readFileSync(path.join(__dirname, '../pages/uv_404.html')))));
app.use(async (req, res, next) => {
if (req.path === '/index') res.redirect('/');
else {
//if (req.path === '/index') res.redirect('/');
//else {
const {
exists,
path: filePath
@@ -94,7 +102,7 @@ app.use(async (req, res, next) => {
res.setHeader('content-type', 'text/html');
res.status(404).end(await rewriter.html(fs.readFileSync(path.join(__dirname, '../pages/404.html'))));
}
}
//}
});
server.on('request', (req, res) => {
@@ -107,4 +115,4 @@ server.on('upgrade', (req, socket, head) => {
else socket.end();
});
server.listen(port, () => console.log(`Polaris listening\n\nPort: ${server.address().port}\nMode: ${mode}\nNode.js: ${process.version}`));
server.listen(port, () => console.log(`Polaris running\n\nPort: ${server.address().port}\nMode: ${mode}\nNode.js: ${process.version}`));
+29 -11
View File
@@ -46,7 +46,7 @@ const html = (data) => {
const templates = await templateParser(data);
templates.forEach(template => htmlData = htmlData.replace(`<!--el:${template.name}-->`, template.file));
for (let i = 0; i < templates.length; i++) htmlData = htmlData.replace(`<!--el:${templates[i].name}-->`, templates[i].file)
const dom = new JSDOM(htmlData);
@@ -79,6 +79,22 @@ const html = (data) => {
}
}
for (let i = 0; i < dom.window.document.documentElement.querySelectorAll('a').length; i++) {
const link = dom.window.document.documentElement.querySelectorAll('a')[i];
if (URL.canParse(link.href)) {
if (new URL(link.href).protocol === 'https:') link.href = `/view?load=${Buffer.from(JSON.stringify({
target: link.href,
redirect: true,
trusted: true
})).toString('base64')}`;
else if (new URL(link.href).protocol === 'http:') link.href = `/view?load=${Buffer.from(JSON.stringify({
target: link.href,
redirect: true
})).toString('base64')}`;
}
}
if (config.minify) resolve(minifyHTML(dom.serialize(), {
minifyJS: true,
minifyCSS: true,
@@ -94,6 +110,8 @@ const html = (data) => {
const javascript = (data, filePath) => {
return new Promise((resolve, reject) => {
filePath = filePath.replaceAll('\\', '/');
const imports = String(data).split('import ')
.map(data => data.split('from ')[1])
.filter(data => Boolean(data))
@@ -104,16 +122,16 @@ const javascript = (data, filePath) => {
.replaceAll(';', ''))
.map(data => {
if (data.startsWith('./')) return {
originalFile: data,
newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data)
originalFile: data.replaceAll('\r', ''),
newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data).replaceAll('\r', '')
};
else if (data.startsWith('../')) return {
originalFile: data,
newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data)
originalFile: data.replaceAll('\r', ''),
newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data).replaceAll('\r', '')
};
else return {
originalFile: data,
newFile: data
originalFile: data.replaceAll('\r', ''),
newFile: data.replaceAll('\r', '')
};
})
.filter(data => fs.existsSync(path.join(__dirname, '../static', data.newFile)));
@@ -141,6 +159,8 @@ const javascript = (data, filePath) => {
const css = (data, filePath) => {
return new Promise((resolve, reject) => {
filePath = filePath.replaceAll('\\', '/');
const imports = String(data).split('url(')
.map(data => {
if (data.split('\n')[0].split(' ')[0].replace(';', '').trim().endsWith(')')) return data.split('\n')[0]
@@ -151,7 +171,7 @@ const css = (data, filePath) => {
.replaceAll('\'', '')
.replaceAll('`', '')
.replaceAll('"', '');
else return undefined;
else return null;
})
.filter(data => {
if (data) try {
@@ -163,8 +183,6 @@ const css = (data, filePath) => {
} else return false;
})
.map(data => {
// console.log(path.join(filePath.split('/').slice(0, -1).join('/')));
if (data.startsWith('./')) return {
originalFile: data,
newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data)
@@ -186,7 +204,7 @@ const css = (data, filePath) => {
asset: path.join(__dirname, '../static', imports[i].newFile),
type: mime.getType(path.join(__dirname, '../static', imports[i].newFile))
}).token);
if (config.minify) resolve(css.replace(/(\r\n|\n|\r)/gm, '').replaceAll(' ', ' '));
else resolve(css);
});
+2 -18
View File
@@ -20,28 +20,12 @@
<br>
<div class="apps"></div>
</div>
<!--el:footer-->
<!--el:ad_horizontal-->
<!--el:ad_horizontal-->
<script src="/assets/js/main.js" type="module"></script>
<!--el:{{mode === 'dev'}}:development-->
<script>
window['nitroAds'].createAd('anchorad', {
"refreshTime": 30,
"format": "anchor",
"anchor": "bottom",
"anchorPersistClose": false,
"report": {
"enabled": true,
"icon": true,
"wording": "Report Ad",
"position": "top-right"
},
"mediaQuery": "(min-width: 1025px)"
});
</script>
</body>
</html>
+11 -6
View File
@@ -2,26 +2,31 @@
{
"name": "Discord",
"image": "/assets/img/discord.png",
"source": "/service/hvtrs8%2F-dksaopd%2Ccmm-lmgkn"
"target": "https://discord.com/app",
"proxy": true
},
{
"name": "Emulator",
"image": "https://cdn-icons-png.flaticon.com/512/9889/9889883.png",
"source": "/service/hvtrs8%2F-dgmm.gmwlctmrhs%2Copg-"
"image": "https://emulatorjs.org/media/logo-light.png",
"target": "https://demo.emulatorjs.org/",
"proxy": true
},
{
"name": "Tiktok",
"image": "/assets/img/tiktok.png",
"source": "/service/hvtrs8%2F-wuw%2Ctkkvoi.aoo%2Fgn-"
"target": "https://www.tiktok.com/en/",
"proxy": true
},
{
"name": "Twitch",
"image": "/assets/img/twitch.png",
"source": "/service/hvtrs8%2F-wuw%2Ctuivcj.vv-"
"target": "https://www.twitch.tv/",
"proxy": true
},
{
"name": "Youtube",
"image": "/assets/img/youtube.png",
"source": "/service/hvtrs8%2F-wuw%2Cymuvu%60e%2Ccmm-"
"target": "https://youtube.com",
"proxy": true
}
]
File diff suppressed because one or more lines are too long
+63 -1
View File
@@ -1,4 +1,4 @@
@import url('https://site-assets.fontawesome.com/releases/v6.2.0/css/all.css');
@import url('https://site-assets.fontawesome.com/releases/v6.5.1/css/all.css');
@import url('./fonts.css');
@import url('./themes.css');
@import url('./nav.css');
@@ -85,6 +85,10 @@ body::-webkit-scrollbar-corner {
display: none;
}
#searchInput {
margin-bottom: 20px;
}
i {
margin: 1vh;
}
@@ -339,6 +343,64 @@ input:checked+.slider:before {
z-index: 10;
}
.cheats {
width: 100%;
display: flex;
justify-content: center;
flex-wrap: wrap;
padding-bottom: 3vh;
}
.cheat {
min-width: 20vh;
min-height: 20vh;
display: block;
border: none;
cursor: pointer;
transition: .4s;
overflow: hidden;
position: relative;
box-shadow: 0vh 0.75vh 1.5vh 0vh black;
margin: 1vh;
text-align: left;
border-radius: 1.5vh;
}
.cheat:hover {
filter: brightness(95%);
transform: translateY(-0.2vh);
border-color: #ffffff;
}
.cheat>img {
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
left: 0;
top: 0;
object-position: center;
}
.cheat:after {
content: '';
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%);
}
.cheat>h3 {
position: absolute;
bottom: -0.5vh;
left: 2vh;
color: #fff;
font-size: 1.8vh;
z-index: 10;
}
.apps {
width: 100%;
display: flex;
+2
View File
@@ -13,6 +13,8 @@
overflow-y: auto;
padding: 1vh;
transition: all 0.5s ease;
backdrop-filter: blur(7.1px);
-webkit-backdrop-filter: blur(7.1px);
}
.sidebar.active {
+59 -1
View File
@@ -2,16 +2,25 @@
.gamebar {
background: var(--background-darker);
z-index: 9;
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: 25px;
margin: 40px;
margin-left: 20vw;
margin-right: 20vw;
border-radius: 2vh;
padding: 10px;
display: flex;
box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color);
backdrop-filter: blur(7.1px);
-webkit-backdrop-filter: blur(7.1px);
}
.gamebar.collapsed {
margin-bottom: calc(-6vh + calc(-40px / 2));
padding-top: 20px;
}
.gamebar .logo {
@@ -26,4 +35,53 @@
font-size: 40px;
width: calc(100% - 20px);
margin-top: calc(calc(calc(6vh - 10px) - 40px) / 2);
}
.gamebar .right {
position: absolute;
margin-top: calc(calc(calc(6vh - 10px) - 40px) / 2);
right: 10px;
}
.gamebar .item {
background: var(--shadow-color);
cursor: pointer;
box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color);
font-size: 35px;
border-radius: 1vh;
padding: 5px;
}
.gamebar .item:not(:last-of-type) {
margin-right: 10px;
}
.hitbox {
display: none;
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin-left: 20vw;
margin-right: 20vw;
border-top-right-radius: 2vh;
border-top-left-radius: 2vh;
height: calc(6vh + 40px);
}
.hitbox.active {
display: block;
}
iframe {
position: absolute;
width: calc(100vw - 50px);
height: calc(100vh - 50px);
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
border-radius: 2.5vh;
box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color);
}
+9 -44
View File
@@ -1,13 +1,6 @@
import { loadProxyWorker } from './utils.js';
import PolarisError from './error.js';
const tiltEffectSettings = {
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
};
import effects from './effects.js';
const load = () => {
fetch('/assets/JSON/apps.json').then(res => res.json()).then(apps => {
@@ -17,6 +10,14 @@ const load = () => {
el.innerHTML = `<img src='${app.image}'><h3>${app.name}</h3>`;
document.querySelector('.apps').appendChild(el);
effects.hoverTilt({
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
}, el);
el.addEventListener('click', async () => {
await loadProxyWorker('uv');
@@ -26,46 +27,10 @@ const load = () => {
}));
location.href = '/view';
});
el.addEventListener('mouseenter', appMouseEnter);
el.addEventListener('mousemove', appMouseMove);
el.addEventListener('mouseleave', appMouseLeave);
});
}).catch(e => new PolarisError('Failed to load Apps'));
};
function appMouseEnter(event) {
setTransition(event);
}
function appMouseMove(event) {
const app = event.currentTarget;
const appWidth = app.offsetWidth;
const appHeight = app.offsetHeight;
const centerX = app.offsetLeft + appWidth / 2;
const centerY = app.offsetTop + appHeight / 2;
const mouseX = event.clientX - centerX;
const mouseY = event.clientY - centerY;
const rotateXUncapped = (+1) * tiltEffectSettings.max * mouseY / (appHeight / 2);
const rotateYUncapped = (-1) * tiltEffectSettings.max * mouseX / (appWidth / 2);
const rotateX = rotateXUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateXUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateXUncapped);
const rotateY = rotateYUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateYUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateYUncapped);
app.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${tiltEffectSettings.scale}, ${tiltEffectSettings.scale}, ${tiltEffectSettings.scale})`;
}
function appMouseLeave(event) {
event.currentTarget.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`;
setTransition(event);
}
function setTransition(event) {
const app = event.currentTarget;
clearTimeout(app.transitionTimeoutId);
app.style.transition = `transform ${tiltEffectSettings.speed}ms ${tiltEffectSettings.easing}`;
app.transitionTimeoutId = setTimeout(() => app.style.transition = '', tiltEffectSettings.speed);
}
export default {
load
};
+22 -57
View File
@@ -1,65 +1,30 @@
import PolarisError from './error.js';
import effects from './effects.js';
const tiltEffectSettings = {
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
};
const load = () => fetch('/assets/JSON/cheats.json')
.then(res => res.json())
.then(cheats => cheats.forEach(cheat => {
const el = document.createElement('div');
el.classList = 'game';
el.innerHTML = `<img src='${cheat.image}'><h3>${cheat.name}</h3>`;
document.querySelector('.cheats').appendChild(el);
const load = () => {
fetch('/assets/JSON/cheats.json').then(res => res.json()).then(cheats => {
cheats.forEach(cheat => {
const el = document.createElement('div');
el.classList = 'game';
el.innerHTML = `<img src='${cheat.image}'><h3>${cheat.name}</h3>`;
document.querySelector('.games').appendChild(el);
effects.hoverTilt({
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
}, el);
el.addEventListener('click', () => {
localStorage.setItem('frameData', JSON.stringify({
type: 'cheat',
cheat
}));
location.href = '/view';
});
el.addEventListener('mouseenter', gameMouseEnter);
el.addEventListener('mousemove', gameMouseMove);
el.addEventListener('mouseleave', gameMouseLeave);
el.addEventListener('click', () => {
localStorage.setItem('frameData', JSON.stringify({
type: 'cheat',
cheat
}));
location.href = '/view';
});
}).catch(e => new PolarisError('Failed to load cheats.'));
};
const gameMouseEnter = setTransition;
function gameMouseMove(event) {
const game = event.currentTarget;
const gameWidth = game.offsetWidth;
const gameHeight = game.offsetHeight;
const centerX = game.offsetLeft + gameWidth / 2;
const centerY = game.offsetTop + gameHeight / 2;
const mouseX = event.clientX - centerX;
const mouseY = event.clientY - centerY;
const rotateXUncapped = (+1) * tiltEffectSettings.max * mouseY / (gameHeight / 2);
const rotateYUncapped = (-1) * tiltEffectSettings.max * mouseX / (gameWidth / 2);
const rotateX = rotateXUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateXUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateXUncapped);
const rotateY = rotateYUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateYUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateYUncapped);
game.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${tiltEffectSettings.scale}, ${tiltEffectSettings.scale}, ${tiltEffectSettings.scale})`;
}
function gameMouseLeave(event) {
event.currentTarget.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`;
setTransition(event);
}
function setTransition(event) {
const game = event.currentTarget;
clearTimeout(game.transitionTimeoutId);
game.style.transition = `transform ${tiltEffectSettings.speed}ms ${tiltEffectSettings.easing}`;
game.transitionTimeoutId = setTimeout(() => game.style.transition = '', tiltEffectSettings.speed);
}
})).catch(e => new PolarisError('Failed to load cheats.'));
export default {
load
+79
View File
@@ -0,0 +1,79 @@
/**
* Creates a tilt effect based on the mouse position when it is hovered over
* @param {{ max: number, perspective: number, scale: number, speed: number, easing: 'cubic-bezier(.03,.98,.52,.99)' }} settings
* @param {HTMLDivElement} element
*/
const hoverTilt = (settings, element) => {
const defaultsettings = {
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
};
settings = {
...defaultsettings,
...settings
};
const setTransition = (e) => {
const element = e.currentTarget;
clearTimeout(element.transitionTimeoutId);
element.style.transition = `transform ${settings.speed}ms ${settings.easing}`;
element.transitionTimeoutId = setTimeout(() => element.style.transition = '', settings.speed);
};
const listeners = [];
var stopped = false;
const eventHandlers = {
mouseEnter: (e) => {
if (!stopped) setTransition(e)
},
mouseMove: (e) => {
if (!stopped) {
const element = e.currentTarget;
const gameWidth = element.offsetWidth;
const gameHeight = element.offsetHeight;
const centerX = element.offsetLeft + gameWidth / 2;
const centerY = element.offsetTop + gameHeight / 2;
const mouseX = e.clientX - centerX;
const mouseY = e.clientY - centerY;
const rotateXUncapped = (+1) * settings.max * mouseY / (gameHeight / 2);
const rotateYUncapped = (-1) * settings.max * mouseX / (gameWidth / 2);
const rotateX = rotateXUncapped < -settings.max ? -settings.max : (rotateXUncapped > settings.max ? settings.max : rotateXUncapped);
const rotateY = rotateYUncapped < -settings.max ? -settings.max : (rotateYUncapped > settings.max ? settings.max : rotateYUncapped);
element.style.transform = `perspective(${settings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${settings.scale}, ${settings.scale}, ${settings.scale})`;
}
},
mouseLeave: (e) => {
if (!stopped) {
e.currentTarget.style.transform = `perspective(${settings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`;
setTransition(e);
}
}
};
if (element) {
listeners.push(element.addEventListener('mouseenter', eventHandlers.mouseEnter));
listeners.push(element.addEventListener('mousemove', eventHandlers.mouseMove));
listeners.push(element.addEventListener('mouseleave', eventHandlers.mouseLeave));
}
return {
events: eventHandlers,
remove: () => {
stopped = true;
listeners.forEach(listener => listener.remove());
}
};
};
export default { hoverTilt };
export { hoverTilt };
+80 -120
View File
@@ -1,131 +1,91 @@
import PolarisError from '/assets/js/error.js';
import { loadProxyWorker } from '/assets/js/utils.js';
const tiltEffectSettings = {
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
};
let games = [];
let filteredGames = [];
import { createViewPage } from './utils.js';
import PolarisError from './error.js';
import effects from './effects.js';
const load = () => {
fetch('/assets/JSON/games.json').then(res => res.json()).then(data => {
games = data;
filteredGames = games;
fetch('/assets/JSON/games.json')
.then(res => res.json())
.then(games => {
const searchBar = document.getElementById('searchInput');
renderGames(filteredGames);
searchBar.addEventListener('input', () => {
if (searchBar.value) {
var result = false;
const searchInput = document.getElementById('searchInput');
searchInput.addEventListener('input', filterGames);
document.querySelectorAll('.games>.game').forEach(game => {
if (game.title.toLowerCase().includes(searchBar.value.toLowerCase())) {
result = true;
game.classList.remove('hidden');
}
else game.classList.add('hidden');
});
if (result) document.querySelector('.searchErr').classList.add('hidden');
else document.querySelector('.searchErr').classList.remove('hidden');
} else {
document.querySelectorAll('.game').forEach(game => game.classList.remove('hidden'));
document.querySelector('.searchErr').classList.add('hidden');
}
});
games.forEach(game => {
const el = document.createElement('div');
el.classList = 'game';
el.innerHTML = `<img loading="lazy" src="${game.image}"><h3>${game.name}</h3>`;
document.querySelector('.games').appendChild(el);
effects.hoverTilt({
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
}, el);
if (game.popular) {
const popularEl = document.createElement('div');
popularEl.classList = 'game';
popularEl.innerHTML = `<img loading='lazy' src='${game.image}'><h3>${game.name}</h3>`;
document.querySelector('.popular-games').appendChild(popularEl);
popularEl.addEventListener('click', async () => {
if (URL.canParse(game.target)) createViewPage({
target: game.target,
title: game.name,
proxied: true
});
else createViewPage({
target: game.target,
title: game.name
});
});
effects.hoverTilt({
max: 8,
perspective: 1000,
scale: 1.05,
speed: 800,
easing: 'cubic-bezier(.03,.98,.52,.99)'
}, popularEl);
}
el.addEventListener('click', async () => {
if (URL.canParse(game.target)) createViewPage({
target: game.target,
title: game.name,
proxied: true
});
else createViewPage({
target: game.target,
title: game.name
});
});
});
})
.catch(e => new PolarisError('Failed to load games'));
};
function filterGames() {
const searchInput = document.getElementById('searchInput');
const searchTerm = searchInput.value.toLowerCase();
filteredGames = games.filter(game => game.name.toLowerCase().includes(searchTerm));
renderGames(filteredGames);
}
function renderGames(gamesToRender) {
const gamesContainer = document.querySelector('.games');
const popularGamesContainer = document.querySelector('.popular-games');
gamesContainer.innerHTML = '';
popularGamesContainer.innerHTML = '';
gamesToRender.forEach(game => {
const el = document.createElement('div');
el.classList = 'game';
el.innerHTML = `<img loading='lazy' src='${game.image}'><h3>${game.name}</h3>`;
gamesContainer.appendChild(el);
if (game.popular === 'yes') {
const popularEl = document.createElement('div');
popularEl.classList = 'game';
popularEl.innerHTML = `<img loading='lazy' src='${game.image}'><h3>${game.name}</h3>`;
popularGamesContainer.appendChild(popularEl);
popularEl.addEventListener('click', async () => {
await loadProxyWorker('uv');
if (game.openinnewtab === 'yes') window.open(game.source);
else {
localStorage.setItem('frameData', JSON.stringify({
type: 'game',
game
}));
location.href = '/view';
}
});
popularEl.addEventListener('mouseenter', gameMouseEnter);
popularEl.addEventListener('mousemove', gameMouseMove);
popularEl.addEventListener('mouseleave', gameMouseLeave);
}
el.addEventListener('click', async () => {
await loadProxyWorker();
const frameData = {
type: 'game',
game
};
if (game.openinnewtab === 'yes') {
window.open(game.source, '_blank');
console.log('Open game in new tab:', frameData);
} else {
localStorage.setItem('frameData', JSON.stringify(frameData));
location.href = '/view';
}
});
el.addEventListener('mouseenter', gameMouseEnter);
el.addEventListener('mousemove', gameMouseMove);
el.addEventListener('mouseleave', gameMouseLeave);
});
}
function gameMouseEnter(event) {
setTransition(event);
}
function gameMouseMove(event) {
const game = event.currentTarget;
const gameWidth = game.offsetWidth;
const gameHeight = game.offsetHeight;
const centerX = game.offsetLeft + gameWidth / 2;
const centerY = game.offsetTop + gameHeight / 2;
const mouseX = event.clientX - centerX;
const mouseY = event.clientY - centerY;
const rotateXUncapped = (+1) * tiltEffectSettings.max * mouseY / (gameHeight / 2);
const rotateYUncapped = (-1) * tiltEffectSettings.max * mouseX / (gameWidth / 2);
const rotateX = rotateXUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateXUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateXUncapped);
const rotateY = rotateYUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateYUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateYUncapped);
game.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${tiltEffectSettings.scale}, ${tiltEffectSettings.scale}, ${tiltEffectSettings.scale})`;
}
function gameMouseLeave(event) {
event.currentTarget.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`;
setTransition(event);
}
function setTransition(event) {
const game = event.currentTarget;
clearTimeout(game.transitionTimeoutId);
game.style.transition = `transform ${tiltEffectSettings.speed}ms ${tiltEffectSettings.easing}`;
game.transitionTimeoutId = setTimeout(() => game.style.transition = '', tiltEffectSettings.speed);
}
export default {
load
};
+46 -33
View File
@@ -1,10 +1,10 @@
import loadEasterEggs from './eastereggs.js';
import { createViewPage } from './utils.js';
import PolarisError from './error.js';
import Settings from './settings.js';
import Search from './search.js';
import Cheats from './cheats.js';
import Games from './games.js';
import Frame from './frame.js';
import Apps from './apps.js';
loadEasterEggs();
@@ -14,61 +14,74 @@ onbeforeunload = (e) => {
e.preventDefault();
return e;
}
sessionStorage.clear();
}
/*await navigator.serviceWorker.register('/assets/js/offline.js', {
scope: '/'
});*/
window.onhashchange = () => {
if (location.hash === '#settings') document.querySelector('.sidebar').classList.add('active');
else document.querySelector('.sidebar').classList.remove('active');
};
if (window.self === window.top) setTimeout(async () => {
if (window.self === window.top && location.pathname !== '/view') setTimeout(async () => {
Settings.load();
if (location.pathname === '/games') Games.load();
if (location.pathname === '/apps') Apps.load();
if (location.pathname === '/search') Search.load();
if (location.pathname === '/cheats') Cheats.load();
if (location.pathname === '/view') Frame.load();
}, 500);
if (location.pathname === '/') {
fetch('/assets/JSON/games.json').then(res => res.json()).then(games => {
const gameName = 'Tiny Fishing';
const game = games.filter(g => g.name === gameName)[0];
fetch('/assets/JSON/games.json')
.then(res => res.json())
.then(games => {
const gameName = 'Tiny Fishing';
const game = games.filter(g => g.name === gameName)[0];
document.querySelector('.featured').addEventListener('click', () => {
localStorage.setItem('frameData', JSON.stringify({
type: 'game',
game
}));
document.querySelector('.featured').addEventListener('click', () => {
if (URL.canParse(game.target)) createViewPage({
target: game.target,
title: game.name,
proxied: true
});
else createViewPage({
target: game.target,
title: game.name
});
});
location.href = '/view';
});
document.querySelector('.featured').src = '/assets/img/wide/tinyfishing.png';
}).catch(e => new PolarisError('Failed to load featured game.'));
document.querySelector('.featured').src = '/assets/img/wide/tinyfishing.png';
}).catch(e => new PolarisError('Failed to load featured game.'));
fetch('/assets/JSON/changelog.json').then(res => res.json()).then(changelog => changelog.forEach(change => {
const date = document.createElement('p');
date.textContent = change.date;
date.classList = 'small';
document.querySelector('#changelog').appendChild(date);
fetch('/assets/JSON/changelog.json')
.then(res => res.json())
.then(changelog => changelog.forEach(change => {
const date = document.createElement('p');
date.textContent = change.date;
date.classList = 'small';
document.querySelector('#changelog').appendChild(date);
const descwrap = document.createElement('p');
const description = document.createElement('i');
description.textContent = change.simpleDescription;
description.classList = 'small';
document.querySelector('#changelog').appendChild(description);
}));
const descwrap = document.createElement('p');
const description = document.createElement('i');
description.textContent = change.simpleDescription;
description.classList = 'small';
document.querySelector('#changelog').appendChild(description);
}));
}
if (window.scrollY !== 0) document.querySelector('.navbar').classList.add('scrolling');
else document.querySelector('.navbar').classList.remove('scrolling');
window.onscroll = () => {
if (window.self === window.top && location.pathname !== '/view') {
if (window.scrollY !== 0) document.querySelector('.navbar').classList.add('scrolling');
else document.querySelector('.navbar').classList.remove('scrolling');
}
export default { Settings, Games, Apps, Frame, PolarisError };
if (window.self === window.top && location.pathname !== '/view') window.onscroll = () => {
if (window.scrollY !== 0) document.querySelector('.navbar').classList.add('scrolling');
else document.querySelector('.navbar').classList.remove('scrolling');
}
if (window.self !== window.top && document.querySelector('.navbar')) document.querySelector('.navbar').remove()
//export default { Settings, Games, Apps, Frame, PolarisError };
+15
View File
@@ -0,0 +1,15 @@
// wip
const serverOnline = () => {
return new Promise(async (resolve, reject) => {
try {
await fetch('/');
resolve(true);
} catch { resolve(false); }
});
}
self.addEventListener('fetch', async (e) => {
if (self.navigator.onLine) e.respondWith('offline');
else if (await serverOnline()) e.respondWith('nooo');
});
+7 -23
View File
@@ -1,36 +1,20 @@
import PolarisError from './error.js';
import { loadProxyWorker } from './utils.js';
import { createViewPage } from './utils.js';
const load = () => {
const xor = {
encode: (str, key = 2) => {
if (!str) return str;
return encodeURIComponent(str.split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join(''));
},
decode: (str, key = 2) => {
if (!str) return str;
return decodeURIComponent(str).split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join('');
}
};
const form = document.querySelector('#wpf');
const query = document.querySelector('#query');
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (typeof navigator.serviceWorker === 'undefined') new PolarisError('Failed to load Proxy');
await loadProxyWorker('uv');
const url = /^(http(s)?:\/\/)?([\w-]+\.)+[\w]{2,}(\/.*)?$/.test(query.value) ? ((!query.value.startsWith('http://') && !query.value.startsWith('https://')) ? 'https://' + query.value : query.value) : 'https://www.google.com/search?q=' + encodeURIComponent(query.value);
localStorage.setItem('frameData', JSON.stringify({
type: 'proxy',
source: `/uv/service/${xor.encode(url)}`
}));
location.href = '/view';
createViewPage({
target: url,
proxied: true,
title: 'Search Results'
});
});
}
export default { load };
export default { load };
+77 -8
View File
@@ -12,13 +12,13 @@ const storage = (containerName) => {
get: (name) => {
if (!localStorage.getItem(containerName)) localStorage.setItem(containerName, JSON.stringify({}));
else {
try {
JSON.parse(localStorage.getItem(containerName));
} catch (e) {
localStorage.setItem(containerName, JSON.stringify({}));
}
try {
JSON.parse(localStorage.getItem(containerName));
} catch (e) {
localStorage.setItem(containerName, JSON.stringify({}));
}
}
const container = JSON.parse(localStorage.getItem(containerName));
return container[name];
},
@@ -54,6 +54,35 @@ const loadProxyWorker = async (proxy) => await navigator.serviceWorker.register(
});
/**
Broken
* Get the current encoding method
* @param {'uv' | 'dynamic'} proxy
* @returns {Promise.<string>}
const getEncodingMethod = (proxy) => {
return new Promise(async (resolve, reject) => {
const config = await(await fetch(`/${proxy}/${proxy}.config.js`)).text();
const Ultraviolet = {
codec: {
xor: {},
base64: {},
plain: {}
}
};
eval(config);
const encodingConfig = String(self[`_${proxy}$config`][proxy === 'uv' ? 'encodeUrl' : (proxy === 'dynamic' ? 'encoding' : '')]);
if (proxy === 'uv') resolve(encodingConfig.replace('Ultraviolet.codec.', '').replace('.encode', ''));
else if (proxy === 'dynamic') resolve(encodingConfig);
});
}*/
/**
* WIP
*
* Load the page javascript
*/
const loadPageScript = () => {
@@ -62,11 +91,51 @@ const loadPageScript = () => {
}
};
const encoder = {
b64: {
encode: (data) => btoa(data),
decode: (data) => atob(data)
},
xor: {
encode: (data, key = 2) => encodeURIComponent(data.split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join('')),
decode: (data, key = 2) => decodeURIComponent(data).split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join('')
}
};
/**
* Redirect to an external url
* @param {string} target
* @param {{ trusted: boolean }} options
*/
const redirect = (target, options) => location.href = `/view?load=${btoa(JSON.stringify({
target,
redirect: true,
trusted: options.trusted
}))}`;
/**
* Load a url into the view page
* @param {{ target: string, title: string, return: string, proxied: boolean }} options
*/
const createViewPage = (options) => location.href = `/view?load=${btoa(JSON.stringify({
return: options.return || location.href,
proxied: options.proxied,
target: options.target,
title: options.title
}))}`;
export default {
storage,
loadProxyWorker
loadProxyWorker,
encoder,
redirect,
createViewPage
};
export {
storage,
loadProxyWorker
loadProxyWorker,
encoder,
redirect,
createViewPage
};
+68
View File
@@ -0,0 +1,68 @@
import { loadProxyWorker, encoder } from './utils.js';
const params = new URLSearchParams(location.search);
window.history.replaceState({}, '', location.pathname);
if (params.get('load')) {
try {
const parsedData = JSON.parse(atob(params.get('load')));
if (Boolean(parsedData.target && parsedData.title && parsedData.return)) {
document.body.classList.remove('hidden');
sessionStorage.setItem('loaddata', JSON.stringify(parsedData));
if (parsedData.proxied) {
await loadProxyWorker('uv');
document.querySelector('iframe').src = '/uv/service/' + encoder['xor'].encode(parsedData.target);
} else document.querySelector('iframe').src = parsedData.target;
document.querySelector('iframe').addEventListener('load', () => {
document.querySelector('.title').textContent = parsedData.title;
document.querySelector('iframe').style.transition = 'none';
document.querySelector('iframe').style.background = '#fff';
document.querySelector('iframe').contentWindow.addEventListener('mouseover', () => {
document.querySelector('.gamebar').classList.add('collapsed');
document.querySelector('.hitbox').classList.remove('active');
});
document.querySelector('iframe').contentWindow.addEventListener('mouseout', () => {
document.querySelector('.gamebar').classList.remove('collapsed');
document.querySelector('.hitbox').classList.add('active');
});
if (document.querySelector('iframe').matches(':hover')) setTimeout(() => {
document.querySelector('.gamebar').classList.remove('collapsed');
document.querySelector('.hitbox').classList.add('active');
}, 1000);
});
document.querySelector('#fullscreen').addEventListener('click', () => {
const iframe = document.querySelector('iframe');
if (iframe.requestFullscreen) iframe.requestFullscreen();
else if (iframe.webkitRequestFullscreen) iframe.webkitRequestFullscreen();
else if (iframe.mozRequestFullScreen) iframe.mozRequestFullScreen();
else if (iframe.msRequestFullscreen) iframe.msRequestFullscreen();
});
window.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) document.querySelector('iframe').style.borderRadius = '0px';
else document.querySelector('iframe').style.borderRadius = '';
});
document.querySelector('#return').addEventListener('click', () => location.href = parsedData.return);
} else if (parsedData.target && parsedData.redirect === true) {
window.history.replaceState({}, '', '/redirect');
if (parsedData.trusted) window.location.replace(parsedData.target);
else {
document.documentElement.textContent = `Redirecting to ${parsedData.target}`;
setTimeout(() => window.location.replace(parsedData.target), 1000);
}
} else window.location.replace(parsedData.return || '/');
} catch (e) { alert(e); window.location.replace('/'); }
} else if (sessionStorage.getItem('loaddata')) window.location.replace(`/view?load=${btoa(sessionStorage.getItem('loaddata'))}`);
else window.location.replace('/');
+5 -17
View File
@@ -17,31 +17,19 @@
<div class="content">
<h1>Cheats</h1>
<br>
<div class="games"></div>
<div class="cheats"></div>
</div>
<!--el:footer-->
<!--el:ad_horizontal-->
<!--el:ad_horizontal-->
<script src="/assets/js/main.js" type="module"></script>
<!--el:{{mode === 'dev'}}:development-->
<script>
window['nitroAds'].createAd('anchorad', {
"refreshTime": 30,
"format": "anchor",
"anchor": "bottom",
"anchorPersistClose": false,
"report": {
"enabled": true,
"icon": true,
"wording": "Report Ad",
"position": "top-right"
},
"mediaQuery": "(min-width: 1025px)"
});
</script>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
<!--WIP-->
<!DOCTYPE html>
<html lang="en">
<head>
<!--el:meta-->
<!--el:adtop-->
<!--el:analytics-->
<link rel="stylesheet" href="/assets/css/main.css">
<title>Server Downtime | Polaris</title>
</head>
<body>
<!--el:navbar-->
<!--el:sidebar-->
<div class="container centered">
<h1 style="font-size: 50px;">Oh no!</h1>
<p>
We slipped up! Polaris's servers are currently down and we are working hard to fix them!
<br>
For more information on the downtime join our <a href="https://discord.gg/skool-community-950407933408198717" target="_blank" class="link">discord server</a>.
</p>
</div>
<!--el:{{mode === 'dev'}}:development-->
</body>
</html>
-2
View File
@@ -1,5 +1,3 @@
// See documentation for more information
self.__dynamic$config = {
prefix: '/service/',
encoding: 'xor',
+11 -17
View File
@@ -25,32 +25,26 @@
<h1 style="font-size: 4vh;" class="gamesectionheader">All Games</h1>
<input type="text" id="searchInput" class="settings-input" placeholder="Search Games...">
<input type="text" id="searchInput" placeholder="Search Games...">
<!--el:ad_horizontal-->
<!--el:ad_horizontal-->
<br>
<br>
<div class="games">
</div>
<!--el:footer-->
<div class="searchErr hidden">
<h1>¯\(°_o)/¯</h1>
<br>
<p>Oops, we couldn't find the game you were looking for.</p>
</div>
<script src="/assets/js/main.js" type="module"></script>
<!--el:{{mode === 'dev'}}:development-->
<script>
window['nitroAds'].createAd('anchorad', {
"refreshTime": 30,
"format": "anchor",
"anchor": "bottom",
"anchorPersistClose": false,
"report": {
"enabled": true,
"icon": true,
"wording": "Report Ad",
"position": "top-right"
},
"mediaQuery": "(min-width: 1025px)"
});
</script>
</body>
</html>
+2
View File
@@ -28,6 +28,8 @@
<p>
<a href="https://discord.gg/skool-community-950407933408198717" target="_blank" class="link">Discord</a>
-
<a href="https://skoolworld.org" target="_blank" class="link">Links</a>
-
<a href="mailto:support@polarislearning.org" target="_blank" class="link">Support Email</a>
-
<a href="https://forms.gle/9knPLmyAua5Z3wZv5" target="_blank" class="link">Suggest a game</a>
+50
View File
@@ -0,0 +1,50 @@
<!--WIP-->
<!DOCTYPE html>
<html lang="en">
<head>
<!--el:meta-->
<!--el:adtop-->
<!--el:analytics-->
<link rel="stylesheet" href="/assets/css/main.css">
<title>Offline | Polaris</title>
</head>
<body>
<!--el:sidebar-->
<div class="container centered">
<h1 style="font-size: 50px;">Offline</h1>
<p>Looks you're offline. Connect to the internet to access polaris.</p>
</div>
<script>
try { document.body.dataset.theme = JSON.parse(localStorage.getItem('settings')).theme || 'system-default'; }
catch {
document.body.dataset.theme = 'system-default';
sessionStorage.setItem('settings', JSON.stringify({
theme: 'system-default'
}));
}
sessionStorage.setItem('wasoffline', !window.navigator.onLine);
if (sessionStorage.getItem('wasoffline') === 'true') {
sessionStorage.setItem('wasoffline', false);
window.location.replace('/');
} else (async () => {
document.documentElement.innerHTML = await (await fetch('/404')).text();
})();
window.addEventListener('online', () => window.location.replace('/'));
</script>
<!--el:{{mode === 'dev'}}:development-->
</body>
</html>
+3 -17
View File
@@ -7,7 +7,7 @@
<!--el:analytics-->
<link rel="stylesheet" href="/assets/css/main.css">
<title>Search | Polaris</title>
</head>
@@ -26,26 +26,12 @@
</form>
</div>
</div>
<script src="/assets/js/main.js" type="module"></script>
<!--el:ad_horizontal-->
<!--el:ad_horizontal-->
<!--el:{{mode === 'dev'}}:development-->
<script>
window['nitroAds'].createAd('anchorad', {
"refreshTime": 30,
"format": "anchor",
"anchor": "bottom",
"anchorPersistClose": false,
"report": {
"enabled": true,
"icon": true,
"wording": "Report Ad",
"position": "top-right"
},
"mediaQuery": "(min-width: 1025px)"
});
</script>
</body>
</html>
+1 -1
View File
@@ -4,4 +4,4 @@ importScripts('/uv/uv.sw.js');
const sw = new UVServiceWorker();
self.addEventListener('fetch', (event) => event.respondWith(sw.fetch(event)));
self.addEventListener('fetch', (event) => event.respondWith(sw.fetch(event)));
+7 -9
View File
@@ -1,13 +1,11 @@
// This file overwrites the stock UV config.js
self.__uv$config = {
prefix: "/service/",
bare: "/bare/",
prefix: '/uv/service/',
bare: '/bare/',
encodeUrl: Ultraviolet.codec.xor.encode,
decodeUrl: Ultraviolet.codec.xor.decode,
handler: "/uv/uv.handler.js",
client: "/uv/uv.client.js",
bundle: "/uv/uv.bundle.js",
config: "/uv/uv.config.js",
sw: "/uv/uv.sw.js",
handler: '/uv/uv.handler.js',
client: '/uv/uv.client.js',
bundle: '/uv/uv.bundle.js',
config: '/uv/uv.config.js',
sw: '/uv/uv.sw.js',
};
+17 -4
View File
@@ -12,17 +12,30 @@
<title>View | Polaris</title>
</head>
<body>
<!--//el:sidebar-->
<body class="hidden">
<iframe frameborder="0"></iframe>
<div class="gamebar">
<a href="/">
<img src="/assets/img/logo.png" class="logo">
</a>
<h1 class="title">Game</h1>
<h1 class="title">Loading...</h1>
<div class="right">
<span class="item" id="return">
<i class="fa-solid fa-chevron-left"></i>
</span>
<span class="item" id="fullscreen">
<i class="fa-regular fa-expand"></i>
</span>
</div>
</div>
<div class="hitbox"></div>
<script src="/assets/js/view.js" type="module"></script>
<script src="/assets/js/main.js" type="module"></script>
<!--el:{{mode === 'dev'}}:development-->
+11
View File
@@ -0,0 +1,11 @@
<script type="text/javascript">
atOptions = {
'key': 'ae1638b56336895e2ecf7d1adc3492a4',
'format': 'iframe',
'height': 50,
'width': 320,
'params': {}
};
document.write('<scr' + 'ipt type="text/javascript" src="//outrightsham.com/ae1638b56336895e2ecf7d1adc3492a4/invoke.js"></scr' + 'ipt>');
</script>
+1 -29
View File
@@ -1,31 +1,3 @@
<footer>
<div class="title">
<h1>Polaris</h1>
<img src="/assets/img/logo.png" title="Polaris Logo">
</div>
<div class="socials">
<a href="https://discord.gg/RXBbxQ4wuJ" target="_blank" rel="noopener noreferrer" title="EmberNetwork Discord">
<i class="fa-brands fa-discord"></i>
</a>
<a href="https://github.com/Skoolgq/Polaris" target="_blank" rel="noopener noreferrer" title="Our Github">
<i class="fa-brands fa-github"></i>
</a>
<a href="mailto:support@polarislearning.org" target="_blank" title="Contact Email">
<i class="fa-solid fa-envelope"></i>
</a>
</div>
<div class="right">
<a href="https://forms.gle/9knPLmyAua5Z3wZv5">Suggest a Game</a>
<a href="/privacy">Privace Policy</a>
<a href="/TOS">Terms of Service</a>
</div>
<p>©2022-2023 Skool. All rights reserved.</p>
<!--May add this later-->
</footer>
-1
View File
@@ -21,7 +21,6 @@
<h3>Theme</h3>
<div id="themes">
<button class="settings-button">System Default</button>
<button class="settings-button">Dark</button>
<button class="settings-button">Light</button>
<button class="settings-button">Flamingo</button>