├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── lib └── config.js ├── package-lock.json ├── package.json ├── pages ├── 404.html └── proxy_404.html ├── polaris.config.js ├── render.yaml ├── server ├── api.js ├── index.js ├── utils.js └── utils │ ├── rewriter.js │ └── token.js ├── static ├── ads.txt ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── apps.html ├── assets │ ├── JSON │ │ ├── apps.json │ │ ├── changelog.json │ │ ├── cheats.json │ │ ├── cloaks.json │ │ └── games.json │ ├── css │ │ ├── dropdown.css │ │ ├── fonts.css │ │ ├── footer.css │ │ ├── main.css │ │ ├── nav.css │ │ ├── sidebar.css │ │ ├── themes.css │ │ └── view.css │ ├── fonts │ │ ├── Lato-Black.woff │ │ ├── Lato-Black.woff2 │ │ ├── Lato-BlackItalic.woff │ │ ├── Lato-BlackItalic.woff2 │ │ ├── Lato-Bold.woff │ │ ├── Lato-Bold.woff2 │ │ ├── Lato-BoldItalic.woff │ │ ├── Lato-BoldItalic.woff2 │ │ ├── Lato-Hairline.woff │ │ ├── Lato-Hairline.woff2 │ │ ├── Lato-HairlineItalic.woff │ │ ├── Lato-HairlineItalic.woff2 │ │ ├── Lato-Italic.woff │ │ ├── Lato-Italic.woff2 │ │ ├── Lato-Light.woff │ │ ├── Lato-Light.woff2 │ │ ├── Lato-LightItalic.woff │ │ ├── Lato-LightItalic.woff2 │ │ ├── Lato-Regular.woff │ │ └── Lato-Regular.woff2 │ ├── img │ │ ├── adsterra.png │ │ ├── hamster.gif │ │ ├── hamster.jpg │ │ ├── logo.png │ │ ├── polaris_loading.gif │ │ ├── rick.png │ │ ├── skelly.gif │ │ ├── smurf.jpg │ │ ├── trollface.png │ │ └── wide │ │ │ ├── 99balls.png │ │ │ ├── crossyroad.webp │ │ │ ├── fortnite.jpg │ │ │ ├── retrobowl.png │ │ │ ├── run3.png │ │ │ ├── stickman-archero-fight.png │ │ │ ├── subwaysurfers.webp │ │ │ └── tinyfishing.png │ ├── js │ │ ├── analytics.js │ │ ├── apps.js │ │ ├── cdn.inject.js │ │ ├── changelog.js │ │ ├── cheats.js │ │ ├── eastereggs.js │ │ ├── effects.js │ │ ├── games.js │ │ ├── main.js │ │ ├── offline.js │ │ ├── page │ │ │ └── game.js │ │ ├── search.js │ │ ├── settings.js │ │ ├── share.js │ │ ├── themes.js │ │ ├── utils.js │ │ ├── utils │ │ │ ├── cookie.js │ │ │ ├── ctc.js │ │ │ ├── ctc_worker.js │ │ │ ├── error.js │ │ │ ├── events.js │ │ │ └── indexeddb.js │ │ └── view.js │ └── misc │ │ └── media │ │ ├── bruh.mp3 │ │ ├── rickroll.mp3 │ │ ├── ringtone.mp3 │ │ ├── skelly.mp3 │ │ └── smurf.mp3 ├── changelog.html ├── cheats.html ├── downtime.html ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── games.html ├── index.html ├── offline.html ├── premium.html ├── privacy.html ├── search.html ├── share.html ├── site.webmanifest ├── tos.html ├── uv │ ├── sw.js │ └── uv.config.js └── view.html ├── templates ├── ad_horizontal.html ├── adtop.html ├── analytics.html ├── development.html ├── discord_widget.html ├── footer.html ├── meta.html ├── navbar.html └── sidebar.html └── vercel.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **.DS_Store 3 | .gitpod.yml -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20 2 | WORKDIR /usr/src/polaris 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 8080 7 | CMD ["npm", "start"] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

Polaris has been deprecated. New updates will not be made but bugs may still be fixed.

5 | 6 |

Join our Discord server

7 | 8 | ![Discord](https://img.shields.io/discord/950407933408198717?label=Discord&style=for-the-badge) 9 | ![Commit activity](https://img.shields.io/github/commit-activity/w/SkoolGQ/Polaris?style=for-the-badge) 10 | 11 |

Quick Web Deployment

12 | 13 | [![Deploy to Vercel](https://binbashbanana.github.io/deploy-buttons/buttons/remade/vercel.svg)](https://vercel.com/new/clone?repository-url=https://github.com/Skoolgq/Polaris) 14 | [![Deploy to Koyeb](https://binbashbanana.github.io/deploy-buttons/buttons/remade/koyeb.svg)](https://app.koyeb.com/deploy?type=git&repository=github.com/Skoolgq/Polaris&branch=main&name=Polaris) 15 | [![Deploy to Render](https://binbashbanana.github.io/deploy-buttons/buttons/remade/render.svg)](https://render.com/deploy?repo=https://github.com/Skoolgq/Polaris) 16 | [![Deploy to Cyclic](https://binbashbanana.github.io/deploy-buttons/buttons/remade/cyclic.svg)](https://app.cyclic.sh/api/app/deploy/Skoolgq/Polaris) 17 | 18 |
19 |

Terminal Deployment

20 |
21 | 22 | > [!NOTE] 23 | > We require a NodeJS version >=18. 24 | 25 | 1. Clone the repo to your machine: `git clone https://github.com/Skoolgq/Polaris.git && cd Polaris` 26 | 2. Install Dependencies: `npm install` 27 | 3. Start the app: `npm start` 28 | 4. Visit [localhost:8080](http://localhost:8080) for your local Polaris! 29 | 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | name: Polaris 2 | 3 | services: 4 | polaris: 5 | build: . 6 | ports: 7 | - "8080:8080" 8 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | port: 8080, 3 | mode: 'prod', 4 | options: { 5 | api: { 6 | domain: 'api.polarislearning.org', 7 | secure: true 8 | }, 9 | minify: false, 10 | assetScrambling: false, 11 | allowDangerousTemplateInsert: true 12 | } 13 | }; 14 | 15 | /** 16 | * @param {config} userConfig 17 | * @returns {config} 18 | */ 19 | export const useConfig = (userConfig) => { 20 | config = { 21 | ...config, 22 | ...userConfig, 23 | mode: (process.argv[2] === 'prod' || process.argv[2] === 'dev' ? process.argv[2] : (process.argv[3] === 'prod' || process.argv[3] === 'dev' ? process.argv[3] : (userConfig.mode === 'prod' || userConfig.mode === 'dev' ? userConfig.mode : 'prod'))), 24 | 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(userConfig.port)) ? userConfig.port : (mode === 'prod' ? 80 : 8080))) 25 | }; 26 | 27 | Object.keys(userConfig).forEach(option => { 28 | if (typeof config[option] === 'function') config[option] = config[option](); 29 | }); 30 | 31 | return config; 32 | } 33 | 34 | /** 35 | * @returns {config} 36 | */ 37 | export const getConfig = () => config; 38 | export default useConfig; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polaris", 3 | "version": "1.3.2", 4 | "description": "The professional unblocked games site.", 5 | "main": "server/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node server", 9 | "prod": "node server prod", 10 | "dev": "node server dev" 11 | }, 12 | "author": "Polaris Development Group", 13 | "license": "GNU-3.0-or-later", 14 | "dependencies": { 15 | "@mercuryworkshop/bare-mux": "^2.0.4", 16 | "@mercuryworkshop/epoxy-transport": "^2.1.18", 17 | "@mercuryworkshop/libcurl-transport": "1.3.10", 18 | "@titaniumnetwork-dev/ultraviolet": "^3.2.7", 19 | "cors": "^2.8.5", 20 | "express": "^4.21.2", 21 | "html-minifier": "^4.0.0", 22 | "javascript-obfuscator": "^4.1.0", 23 | "jsdom": "^23.0.1", 24 | "mime": "^4.0.1", 25 | "uuid": "^9.0.1", 26 | "wisp-server-node": "^1.1.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pages/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 404 Error | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

404

20 | 21 |

22 | Looks you found a page that does not exist! Try checking your url for spelling mistakes. 23 |
24 |
25 | Or, you can report it in our discord server. 26 |

27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /pages/proxy_404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Loading... | Polaris 12 | 13 | 14 | 15 |
16 |

Loading

17 | 18 |

19 | Loading the proxy... 20 |

21 |
22 | 23 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /polaris.config.js: -------------------------------------------------------------------------------- 1 | import useConfig from './lib/config.js'; 2 | 3 | export default useConfig({ 4 | port: 8080, 5 | mode: 'prod', 6 | options: { 7 | //Messes up uv 8 | minify: false, 9 | //Beta 10 | assetScrambling: false, 11 | allowDangerousTemplateInsert: false, 12 | api: { 13 | domain: 'api.skoolworld.org', 14 | secure: true 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: space 4 | region: oregon 5 | plan: free 6 | runtime: node 7 | buildCommand: npm i 8 | startCommand: npm start 9 | envVars: 10 | - key: NODE_VERSION 11 | value: 20.0.0 12 | -------------------------------------------------------------------------------- /server/api.js: -------------------------------------------------------------------------------- 1 | import config from '../polaris.config.js'; 2 | 3 | import childProcess from 'node:child_process'; 4 | import path from 'node:path'; 5 | import url from 'node:url'; 6 | import fs from 'node:fs'; 7 | 8 | const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); 9 | const packageFile = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'))); 10 | const commits = await (await fetch(`https://api.github.com/repos/Skoolgq/Polaris/commits`)).json(); 11 | var gitSupported = true; 12 | 13 | /** 14 | * @param {import('express').Express} app 15 | */ 16 | const routes = (app) => { 17 | app.get('/api/analytics/site/:domain', async (req, res, next) => { 18 | try { 19 | const request = await fetch((config.options.api.secure ? 'https' : 'http') + '://' + config.options.api.domain + '/analytics/site/' + req.params.domain); 20 | const buffer = Buffer.from(await request.arrayBuffer()); 21 | 22 | res.header('content-type', request.headers.get('content-type')).end(buffer); 23 | } catch (e) { next(); } 24 | }); 25 | 26 | app.get('/api/analytics/script.js', async (req, res, next) => { 27 | try { 28 | const request = await fetch((config.options.api.secure ? 'https' : 'http') + '://' + config.options.api.domain + '/analytics/script.js'); 29 | const buffer = Buffer.from(await request.arrayBuffer()); 30 | 31 | res.header('content-type', request.headers.get('content-type')).end(buffer); 32 | } catch (e) { next(); } 33 | }); 34 | 35 | app.post('/api/analytics/api/send', async (req, res, next) => { 36 | try { 37 | const request = await fetch((config.options.api.secure ? 'https' : 'http') + '://' + config.options.api.domain + '/analytics/api/send', { 38 | method: 'POST', 39 | headers: req.headers, 40 | body: JSON.stringify(req.body) 41 | }); 42 | const buffer = Buffer.from(await request.arrayBuffer()); 43 | 44 | res.header('content-type', request.headers.get('content-type')).end(buffer); 45 | } catch (e) { next(); } 46 | }); 47 | 48 | app.get('/api/changelog', async (req, res) => { 49 | const changelog = { 50 | version: packageFile.version + (Number(packageFile.version.split('.')[0]) <= 1 ? ' Beta' : '') || 'unknown', 51 | changelog: JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/changelog.json'))) 52 | } 53 | 54 | if (gitSupported) try { 55 | changelog.commit = { 56 | sha: childProcess.execSync('git rev-parse HEAD').toString().trim() || 'uknown', 57 | message: childProcess.execSync('git rev-list --format=%s --max-count=1 HEAD').toString().split('\n')[1].replace('changelog ', '') || 'unknown' 58 | }; 59 | 60 | changelog.upToDate = (commits[0] ? ((commits[0].sha === childProcess.execSync('git rev-parse HEAD').toString().trim()) || false) : false); 61 | } catch { 62 | gitSupported = false; 63 | 64 | changelog.commit = { 65 | sha: 'unknown', 66 | message: 'unknown', 67 | upToDate: false 68 | }; 69 | } else changelog.commit = { 70 | sha: 'unknown', 71 | message: 'unknown', 72 | upToDate: false 73 | }; 74 | 75 | changelog.mode = config.mode === 'dev' ? 'development' : 'production'; 76 | 77 | res.json(changelog); 78 | }); 79 | 80 | app.get('/api/favicon', async (req, res) => { 81 | try { 82 | const request = await fetch(`https://www.google.com/s2/favicons?domain=${req.query.domain}`); 83 | const imageBuffer = Buffer.from(await request.arrayBuffer()); 84 | 85 | res.setHeader('content-type', request.headers.get('content-type')); 86 | res.end(imageBuffer); 87 | } catch (e) { 88 | res.setHeader('content-type', 'image/png'); 89 | res.end(fs.readFileSync(path.join(__dirname, '../static/assets/img/logo.png'))); 90 | } 91 | }); 92 | 93 | app.get('/api/games', (req, res) => { 94 | /** 95 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 96 | */ 97 | const games = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/games.json'))); 98 | 99 | const gamesObject = { 100 | popular: [], 101 | all: [] 102 | }; 103 | 104 | for (let i = 0; i < games.length; i++) { 105 | const game = games[i]; 106 | 107 | if (game.popular) gamesObject.popular.push({ 108 | name: game.name, 109 | target: game.target, 110 | image: `/api/games/${i + 1}/image` 111 | }); 112 | 113 | gamesObject.all.push({ 114 | name: game.name, 115 | target: game.target, 116 | image: `/api/games/${i + 1}/image` 117 | }); 118 | } 119 | 120 | res.json(gamesObject); 121 | }); 122 | 123 | app.get('/api/games/:id', async (req, res, next) => { 124 | /** 125 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 126 | */ 127 | const games = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/games.json'))); 128 | 129 | if (games[req.params.id - 1]) { 130 | const game = games[req.params.id - 1]; 131 | 132 | game.image = `/api/games/${req.params.id}/image`; 133 | game.popular = Boolean(game.popular); 134 | 135 | res.json(game); 136 | } else next(); 137 | }); 138 | 139 | app.get('/api/games/:id/image', async (req, res, next) => { 140 | /** 141 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 142 | */ 143 | const games = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/games.json'))); 144 | 145 | if (games[req.params.id - 1]) { 146 | if (URL.canParse(games[req.params.id - 1].image)) { 147 | try { 148 | const request = await fetch(games[req.params.id - 1].image); 149 | const imageBuffer = Buffer.from(await request.arrayBuffer()); 150 | 151 | res.setHeader('content-type', request.headers.get('content-type')); 152 | res.end(imageBuffer); 153 | } catch (e) { 154 | res.setHeader('content-type', 'image/png'); 155 | res.end(fs.readFileSync(path.join(__dirname, '../static/assets/img/logo.png'))); 156 | } 157 | } else res.redirect(games[req.params.id - 1].image); 158 | } else next(); 159 | }); 160 | 161 | app.get('/api/apps', (req, res) => { 162 | /** 163 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 164 | */ 165 | const apps = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/apps.json'))); 166 | 167 | const appsObject = []; 168 | 169 | for (let i = 0; i < apps.length; i++) { 170 | const app = apps[i]; 171 | 172 | appsObject.push({ 173 | name: app.name, 174 | target: app.target, 175 | image: `/api/apps/${i + 1}/image` 176 | }); 177 | } 178 | 179 | res.json(appsObject); 180 | }); 181 | 182 | app.get('/api/apps/:id', async (req, res, next) => { 183 | /** 184 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 185 | */ 186 | const apps = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/apps.json'))); 187 | 188 | if (apps[req.params.id - 1]) { 189 | const app = apps[req.params.id - 1]; 190 | 191 | app.image = `/api/apps/${req.params.id}/image`; 192 | 193 | res.json(app); 194 | } else next(); 195 | }); 196 | 197 | app.get('/api/apps/:id/image', async (req, res, next) => { 198 | /** 199 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 200 | */ 201 | const apps = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/apps.json'))); 202 | 203 | if (apps[req.params.id - 1]) { 204 | if (URL.canParse(apps[req.params.id - 1].image)) { 205 | try { 206 | const request = await fetch(apps[req.params.id - 1].image); 207 | const imageBuffer = Buffer.from(await request.arrayBuffer()); 208 | 209 | res.setHeader('content-type', request.headers.get('content-type')); 210 | res.end(imageBuffer); 211 | } catch (e) { 212 | res.setHeader('content-type', 'image/png'); 213 | res.end(fs.readFileSync(path.join(__dirname, '../static/assets/img/logo.png'))); 214 | } 215 | } else res.redirect(apps[req.params.id - 1].image); 216 | } else next(); 217 | }); 218 | 219 | app.get('/api/cheats', (req, res) => { 220 | /** 221 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 222 | */ 223 | const cheats = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/cheats.json'))); 224 | 225 | const cheatsObject = []; 226 | 227 | for (let i = 0; i < cheats.length; i++) { 228 | const cheat = cheats[i]; 229 | 230 | cheatsObject.push({ 231 | name: cheat.name, 232 | target: cheat.target, 233 | image: `/api/cheats/${i + 1}/image` 234 | }); 235 | } 236 | 237 | res.json(cheatsObject); 238 | }); 239 | 240 | app.get('/api/cheats/:id', async (req, res, next) => { 241 | /** 242 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 243 | */ 244 | const cheats = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/cheats.json'))); 245 | 246 | if (cheats[req.params.id - 1]) { 247 | const cheat = cheats[req.params.id - 1]; 248 | 249 | cheat.image = `/api/cheats/${req.params.id}/image`; 250 | 251 | res.json(cheat); 252 | } else next(); 253 | }); 254 | 255 | app.get('/api/cheats/:id/image', async (req, res, next) => { 256 | /** 257 | * @type {Array.<{name: string, target: string, image: string, popular: boolean}>} 258 | */ 259 | const cheats = JSON.parse(fs.readFileSync(path.join(__dirname, '../static/assets/JSON/cheats.json'))); 260 | 261 | if (cheats[req.params.id - 1]) { 262 | if (URL.canParse(cheats[req.params.id - 1].image)) { 263 | try { 264 | const request = await fetch(cheats[req.params.id - 1].image); 265 | const imageBuffer = Buffer.from(await request.arrayBuffer()); 266 | 267 | res.setHeader('content-type', request.headers.get('content-type')); 268 | res.end(imageBuffer); 269 | } catch (e) { 270 | res.setHeader('content-type', 'image/png'); 271 | res.end(fs.readFileSync(path.join(__dirname, '../static/assets/img/logo.png'))); 272 | } 273 | } else res.redirect(cheats[req.params.id - 1].image); 274 | } else next(); 275 | }); 276 | }; 277 | 278 | export default routes; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import { epoxyPath } from '@mercuryworkshop/epoxy-transport'; 2 | import { uvPath } from '@titaniumnetwork-dev/ultraviolet'; 3 | import { baremuxPath } from '@mercuryworkshop/bare-mux/node'; 4 | import wisp from 'wisp-server-node'; 5 | import express from 'express'; 6 | import mime from 'mime'; 7 | import cors from 'cors'; 8 | 9 | import { pathToFile, TokenManager, rewriter } from './utils.js'; 10 | import config from '../polaris.config.js'; 11 | import api from './api.js'; 12 | 13 | import childProcess from 'node:child_process'; 14 | import path from 'node:path'; 15 | import http from 'node:http'; 16 | import url from 'node:url'; 17 | import fs from 'node:fs'; 18 | 19 | const app = express(); 20 | const server = http.createServer(); 21 | const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); 22 | const packageFile = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'))); 23 | 24 | const swPaths = [ 25 | '/uv/sw.js', 26 | ]; 27 | 28 | 29 | app.use(express.json()); 30 | 31 | api(app); 32 | 33 | app.get('/cdn/*', cors({ 34 | origin: false 35 | }), async (req, res, next) => { 36 | const reqTarget = req.path.startsWith('/cdn/3kh0/') ? `https://player.work/${req.path.replace('/cdn/3kh0/', '')}` : `https://raw.githubusercontent.com/Skoolgq/Polaris-Assets/main/${req.path.replace('/cdn/', '')}`; 37 | 38 | try { 39 | const asset = await fetch(reqTarget); 40 | 41 | if (asset.status == 200) { 42 | var data = Buffer.from(await asset.arrayBuffer()); 43 | 44 | const noRewrite = ['.unityweb']; 45 | if (!noRewrite.includes(mime.getExtension(reqTarget))) res.writeHead(200, { 46 | 'content-type': mime.getType(reqTarget) 47 | }); 48 | 49 | if (mime.getType(reqTarget) === 'text/html') data = data + ''; 50 | 51 | res.end(data); 52 | } else next(); 53 | } catch { 54 | res.setHeader('content-type', 'text/html'); 55 | res.status(404).end(await rewriter.html(fs.readFileSync(path.join(__dirname, '../pages/404.html')))); 56 | } 57 | }); 58 | 59 | app.get('*', (req, res, next) => { 60 | if (swPaths.includes(req.path)) res.setHeader('Service-Worker-Allowed', '/'); 61 | 62 | next(); 63 | }); 64 | 65 | app.get('/asset', (req, res, next) => { 66 | if (req.query.asset) { 67 | const { 68 | exists, 69 | path: filePath 70 | } = pathToFile(req.query.asset, path.join(__dirname, '../static/assets')); 71 | 72 | if (exists) { 73 | if (filePath.startsWith(path.join(__dirname, '../static/assets'))) res.setHeader('content-type', mime.getType(filePath)).end(fs.readFileSync(filePath)); 74 | else next(); 75 | } else next(); 76 | } else next(); 77 | }); 78 | 79 | app.get('/asset/:token', async (req, res, next) => { 80 | if (req.params.token && !req.query.asset) { 81 | if (TokenManager.exists(req.params.token)) { 82 | const token = TokenManager.get(req.params.token); 83 | 84 | if (TokenManager.get(req.params.token).type === 'asset') { 85 | TokenManager.delete(req.params.token); 86 | 87 | res.setHeader('content-type', token.data.type); 88 | res.end(await rewriter.auto(fs.readFileSync(token.data.asset), token.data.type, token.data.asset.replace(path.join(__dirname, '../static'), ''))); 89 | } else next(); 90 | } else next(); 91 | } 92 | }); 93 | 94 | app.get('/uv/service/*', async (req, res) => res.end(await rewriter.html(fs.readFileSync(path.join(__dirname, '../pages/proxy_404.html'))))); 95 | 96 | 97 | app.use(async (req, res, next) => { 98 | if (req.path === '/index') res.redirect('/'); 99 | else { 100 | const { 101 | exists, 102 | path: filePath 103 | } = pathToFile(req.path, path.join(__dirname, '../static')); 104 | 105 | if (exists) { 106 | if (req.path.endsWith('.html')) res.redirect(req.path.slice(0, -5)); 107 | else { 108 | res.setHeader('content-type', mime.getType(filePath)); 109 | 110 | if (mime.getType(filePath) === 'text/html') res.end(await rewriter.html(fs.readFileSync(filePath), req.path)); 111 | else if (mime.getType(filePath) === 'text/javascript') res.end(await rewriter.javascript(fs.readFileSync(filePath), req.path)); 112 | else if (mime.getType(filePath) === 'text/css') res.end(await rewriter.css(fs.readFileSync(filePath), req.path)); 113 | else res.sendFile(filePath); 114 | } 115 | } else next(); 116 | } 117 | }); 118 | 119 | 120 | app.get('/uv/service/*', async (req, res) => res.end(await rewriter.html(fs.readFileSync(path.join(__dirname, '../pages/proxy_404.html'))))); 121 | 122 | app.use('/uv/', express.static(uvPath)); 123 | app.use("/epoxy/", express.static(epoxyPath)); 124 | app.use("/baremux/", express.static(baremuxPath)); 125 | 126 | server.on('request', (req, res) => { 127 | app(req, res); 128 | }); 129 | 130 | server.on('upgrade', (req, socket, head) => { 131 | if (req.url.endsWith('/wisp/')) wisp.routeRequest(req, socket, head); 132 | else socket.end(); 133 | }); 134 | 135 | server.listen(config.port, () => console.log(`Polaris running\n\nPort: ${server.address().port}\nVersion: ${packageFile.version + (Number(packageFile.version.split('.')[0]) <= 1 ? ' Beta' : '') || 'Unknown'} ${childProcess.execSync('git rev-parse HEAD').toString().trim().slice(0, 7) || 'Unknown'}\nMode: ${config.mode === 'dev' ? 'development' : 'production'}\nAPI Server: ${config.options.api.domain}\nNode.js: ${process.version}`)); -------------------------------------------------------------------------------- /server/utils.js: -------------------------------------------------------------------------------- 1 | import * as rewriter from './utils/rewriter.js'; 2 | import TokenManager from './utils/token.js'; 3 | 4 | import path from 'node:path'; 5 | import fs from 'node:fs'; 6 | 7 | /** 8 | * @param {string} url 9 | * @param {string} folderPath 10 | * @returns {{ exists: boolean, path: string }} 11 | */ 12 | const pathToFile = (url = '', folderPath) => { 13 | if (url.endsWith('/')) url = url + 'index.html'; 14 | else if (url.split(/[#?]/)[0].split('.').pop().trim() === url) { 15 | if (!fs.existsSync(path.join(folderPath, url))) url = url + '.html'; 16 | } 17 | 18 | return { 19 | exists: fs.existsSync(path.join(folderPath, url)), 20 | path: path.join(folderPath, url) 21 | }; 22 | }; 23 | 24 | export default { 25 | pathToFile, 26 | TokenManager, 27 | rewriter 28 | }; 29 | 30 | export { 31 | pathToFile, 32 | TokenManager, 33 | rewriter 34 | }; -------------------------------------------------------------------------------- /server/utils/rewriter.js: -------------------------------------------------------------------------------- 1 | import JavaScriptObfuscator from 'javascript-obfuscator'; 2 | import { minify as minifyHTML } from 'html-minifier'; 3 | import { JSDOM } from 'jsdom'; 4 | import mime from 'mime'; 5 | 6 | import { TokenManager } from '../utils.js'; 7 | import config from '../../polaris.config.js'; 8 | 9 | import path from 'node:path'; 10 | import url from 'node:url'; 11 | import fs from 'node:fs'; 12 | 13 | const __dirname = url.fileURLToPath(new URL('../', import.meta.url)); 14 | const mode = config.mode; 15 | 16 | const templateParser = (data) => { 17 | return new Promise(async (resolve, reject) => { 18 | resolve(String(data).split('')) return data.split('\n')[0].trim().replace('-->', ''); 21 | else return undefined; 22 | }) 23 | .map(data => { 24 | if (data) { 25 | if (data.startsWith('{{') && data.split(':')[data.split(':').length - 2].endsWith('}}')) { 26 | if (config.options.allowDangerousTemplateInsert) { 27 | if (Boolean(eval(String(data.split(':')[data.split(':').length - 2]).slice(2, -2)))) return data; 28 | else return undefined; 29 | } else return undefined; 30 | } else return data; 31 | } else return undefined; 32 | }) 33 | .filter(data => fs.existsSync(path.join(__dirname, '../templates', (data ? (data.startsWith('{{') && data.split(':')[data.split(':').length - 2].endsWith('}}') ? data.split(':')[data.split(':').length - 1] : data) : undefined) + '.html'))) 34 | .map(data => { 35 | return { 36 | name: data, 37 | file: fs.readFileSync(path.join(__dirname, '../templates', (data ? (data.startsWith('{{') && data.split(':')[data.split(':').length - 2].endsWith('}}') ? data.split(':')[data.split(':').length - 1] : data) : undefined) + '.html').toString()) 38 | }; 39 | })); 40 | }); 41 | }; 42 | 43 | const html = (data) => { 44 | return new Promise(async (resolve, reject) => { 45 | var htmlData = String(data); 46 | 47 | const templates = await templateParser(data); 48 | 49 | for (let i = 0; i < templates.length; i++) htmlData = htmlData.replace(``, templates[i].file) 50 | 51 | const dom = new JSDOM(htmlData); 52 | 53 | if (config.assetScrambling) { 54 | for (let i = 0; i < dom.window.document.documentElement.querySelectorAll('script').length; i++) { 55 | const script = dom.window.document.documentElement.querySelectorAll('script')[i]; 56 | 57 | if (script.src.startsWith('/') && !script.src.startsWith('//') && fs.existsSync(path.join(__dirname, '../static', script.src))) script.setAttribute('src', `/asset/${TokenManager.generate('asset', 20000, { 58 | asset: path.join(__dirname, '../static', script.src), 59 | type: 'application/javascript' 60 | }).token}`); 61 | } 62 | 63 | for (let i = 0; i < dom.window.document.documentElement.querySelectorAll('link[rel="stylesheet"]').length; i++) { 64 | const css = dom.window.document.documentElement.querySelectorAll('link[rel="stylesheet"]')[i]; 65 | 66 | if (css.href.startsWith('/') && !css.href.startsWith('//') && fs.existsSync(path.join(__dirname, '../static', css.href))) css.setAttribute('href', `/asset/${TokenManager.generate('asset', 20000, { 67 | asset: path.join(__dirname, '../static', css.href), 68 | type: 'text/css' 69 | }).token}`); 70 | } 71 | 72 | for (let i = 0; i < dom.window.document.documentElement.querySelectorAll('img').length; i++) { 73 | const img = dom.window.document.documentElement.querySelectorAll('img')[i]; 74 | 75 | if (img.src.startsWith('/') && !img.src.startsWith('//') && fs.existsSync(path.join(__dirname, '../static', img.src))) img.setAttribute('src', `/asset/${TokenManager.generate('asset', 20000, { 76 | asset: path.join(__dirname, '../static', img.src), 77 | type: mime.getType(path.join(__dirname, '../static', img.src)) 78 | }).token}`); 79 | } 80 | } 81 | 82 | for (let i = 0; i < dom.window.document.documentElement.querySelectorAll('a').length; i++) { 83 | const link = dom.window.document.documentElement.querySelectorAll('a')[i]; 84 | 85 | if (URL.canParse(link.href)) { 86 | if (new URL(link.href).protocol === 'https:') link.href = `/view?load=${Buffer.from(JSON.stringify({ 87 | target: link.href, 88 | redirect: true, 89 | trusted: true 90 | })).toString('base64')}`; 91 | else if (new URL(link.href).protocol === 'http:') link.href = `/view?load=${Buffer.from(JSON.stringify({ 92 | target: link.href, 93 | redirect: true 94 | })).toString('base64')}`; 95 | } 96 | } 97 | 98 | if (config.minify) resolve(minifyHTML(dom.serialize(), { 99 | minifyJS: true, 100 | minifyCSS: true, 101 | minifyURLs: true, 102 | removeScriptTypeAttributes: true, 103 | useShortDoctype: true, 104 | collapseWhitespace: true, 105 | removeComments: true 106 | })) 107 | else resolve(dom.serialize()); 108 | }); 109 | }; 110 | 111 | const javascript = (data, filePath) => { 112 | return new Promise((resolve, reject) => { 113 | filePath = filePath.replaceAll('\\', '/'); 114 | 115 | const imports = String(data).split('import ') 116 | .map(data => data.split('from ')[1]) 117 | .filter(data => Boolean(data)) 118 | .map(data => data.split('\n')[0] 119 | .replaceAll('\'', '') 120 | .replaceAll('`', '') 121 | .replaceAll('"', '') 122 | .replaceAll(';', '')) 123 | .map(data => { 124 | if (data.startsWith('./')) return { 125 | originalFile: data.replaceAll('\r', ''), 126 | newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data).replaceAll('\r', '') 127 | }; 128 | else if (data.startsWith('../')) return { 129 | originalFile: data.replaceAll('\r', ''), 130 | newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data).replaceAll('\r', '') 131 | }; 132 | else return { 133 | originalFile: data.replaceAll('\r', ''), 134 | newFile: data.replaceAll('\r', '') 135 | }; 136 | }) 137 | .filter(data => fs.existsSync(path.join(__dirname, '../static', data.newFile))); 138 | 139 | let javascript = String(data); 140 | 141 | if (config.assetScrambling) for (let i = 0; i < imports.length; i++) javascript = javascript.replace(imports[i].originalFile, '/asset/' + TokenManager.generate('asset', 20000, { 142 | asset: path.join(__dirname, '../static', imports[i].newFile), 143 | type: 'text/javascript' 144 | }).token); 145 | 146 | if (config.minify) resolve(JavaScriptObfuscator.obfuscate(javascript, { 147 | compact: true, 148 | controlFlowFlattening: true, 149 | controlFlowFlatteningThreshold: 1, 150 | numbersToExpressions: true, 151 | simplify: true, 152 | stringArrayShuffle: true, 153 | splitStrings: true, 154 | stringArrayThreshold: 1 155 | }).getObfuscatedCode()); 156 | else resolve(javascript); 157 | }); 158 | }; 159 | 160 | const css = (data, filePath) => { 161 | return new Promise((resolve, reject) => { 162 | filePath = filePath.replaceAll('\\', '/'); 163 | 164 | const imports = String(data).split('url(') 165 | .map(data => { 166 | if (data.split('\n')[0].split(' ')[0].replace(';', '').trim().endsWith(')')) return data.split('\n')[0] 167 | .split(' ')[0] 168 | .trim() 169 | .replace(';', '') 170 | .replace(')', '') 171 | .replaceAll('\'', '') 172 | .replaceAll('`', '') 173 | .replaceAll('"', ''); 174 | else return null; 175 | }) 176 | .filter(data => { 177 | if (data) try { 178 | new URL(data); 179 | 180 | return false; 181 | } catch (e) { 182 | return true; 183 | } else return false; 184 | }) 185 | .map(data => { 186 | if (data.startsWith('./')) return { 187 | originalFile: data, 188 | newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data) 189 | }; 190 | else if (data.startsWith('../')) return { 191 | originalFile: data, 192 | newFile: path.join(filePath.split('/').slice(0, -1).join('/'), data) 193 | }; 194 | else return { 195 | originalFile: data, 196 | newFile: data 197 | }; 198 | }) 199 | .filter(data => fs.existsSync(path.join(__dirname, '../static', data.newFile))); 200 | 201 | let css = String(data); 202 | 203 | if (config.assetScrambling) for (let i = 0; i < imports.length; i++) css = css.replace(imports[i].originalFile, '/asset/' + TokenManager.generate('asset', 20000, { 204 | asset: path.join(__dirname, '../static', imports[i].newFile), 205 | type: mime.getType(path.join(__dirname, '../static', imports[i].newFile)) 206 | }).token); 207 | 208 | if (config.minify) resolve(css.replace(/(\r\n|\n|\r)/gm, '').replaceAll(' ', ' ')); 209 | else resolve(css); 210 | }); 211 | }; 212 | 213 | const auto = async (data, type, filePath) => { 214 | if (type === 'text/html') return await html(data); 215 | else if (type === 'text/javascript' || type === 'application/javascript') return await javascript(data, filePath); 216 | else if (type === 'text/css') return await css(data, filePath); 217 | else return data; 218 | }; 219 | 220 | export { 221 | auto, 222 | html, 223 | javascript, 224 | css 225 | }; -------------------------------------------------------------------------------- /server/utils/token.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuid } from 'uuid'; 2 | 3 | class TokenManager { 4 | constructor() { 5 | this.tokens = {}; 6 | 7 | setInterval(() => { 8 | const currentDate = Date.now(); 9 | 10 | Object.keys(this.tokens).forEach(key => { 11 | const token = this.tokens[key]; 12 | 13 | if (currentDate > token.expires) delete this.tokens[key]; 14 | }); 15 | }, 100); 16 | } 17 | 18 | /** 19 | * Generate a token 20 | * @param {string} type The type of token you are generating 21 | * @param {number} expires How long it will take for the token to expire in miliseconds 22 | * @param {object | string} data The data to be stored with the token 23 | * @returns {{token: number, expires: number}} The token data 24 | */ 25 | generate = (type, expires, data) => { 26 | const token = uuid(); 27 | 28 | this.tokens[token] = { 29 | token: token, 30 | type: type, 31 | expires: Date.now() + expires, 32 | data: data 33 | } 34 | 35 | return { 36 | token: token, 37 | expires: Date.now() + expires 38 | } 39 | } 40 | 41 | /** 42 | * Check if a token exists 43 | * @param {string} token The token 44 | * @returns {boolean} 45 | */ 46 | exists = (token) => { 47 | return this.tokens[token] ? true : false; 48 | } 49 | 50 | /** 51 | * Get all tokens in a category 52 | * @param {string} type The token category 53 | * @returns {Array.<{token: stromg, type: string, expires: number, data: object | string}>} The list of tokens 54 | */ 55 | getAll = (type) => { 56 | const tokens = []; 57 | 58 | Object.keys(this.tokens).forEach(key => { 59 | const token = this.tokens[key]; 60 | 61 | if (token.type == type) tokens.push(token); 62 | }); 63 | 64 | return tokens; 65 | } 66 | 67 | /** 68 | * Get token information from a token 69 | * @param {string} token The token 70 | * @returns {{token: stromg, type: string, expires: number, data: object | string}} Token data 71 | */ 72 | get = (token) => { 73 | return this.tokens[token]; 74 | } 75 | 76 | delete = (token) => delete this.tokens[token]; 77 | } 78 | 79 | export default new TokenManager(); -------------------------------------------------------------------------------- /static/ads.txt: -------------------------------------------------------------------------------- 1 | # NitroPay 2 | nitropay.com, 1372, DIRECT 3 | contact=https://nitropay.com 4 | OWNERDOMAIN=polarislearning.org 5 | MANAGERDOMAIN=nitropay.com 6 | 7 | # 33Across 8 | 33across.com, 0010b00002VqwyFAAR, DIRECT, bbea06d9c4d2853c #33Across #hb #tag 9 | appnexus.com, 10239, RESELLER, f5ab79cb980f11d1 #33Across #hb #tag #viewable 10 | rubiconproject.com, 16414, RESELLER, 0bfd66d529a55807 #33Across #hb #tag 11 | pubmatic.com, 156423, RESELLER, 5d62403b186f2ace #33Across #hb #tag 12 | yahoo.com, 57289, RESELLER, e1a5b5b6e3255540 #33Across #hb #tag 13 | rubiconproject.com, 21642, RESELLER, 0bfd66d529a55807 #33Across #hb #tag #viewable 14 | conversantmedia.com, 100141, RESELLER, 03113cd04947736d #hb #tag #video 15 | indexexchange.com, 191973, RESELLER, 50b1c356f2c5c8fc #33Across #hb #tag #viewable 16 | openx.com, 537120563, RESELLER, 6a698e2ec38604c6 #33Across #hb #tag 17 | contextweb.com, 561516, RESELLER, 89ff185a4c4e857c 18 | triplelift.com, 12503, RESELLER, 6c33edb13117fd86 19 | video.unrulymedia.com, 2439829435, DIRECT 20 | loopme.com,11575,RESELLER,6c8d5f95897a5a3b 21 | 22 | # Amazon 23 | aps.amazon.com,da657530-03e5-4306-95bc-d4eb370426c9,DIRECT 24 | sovrn.com,251959,DIRECT,fafdf38b16bf6b2b 25 | lijit.com,251959,DIRECT,fafdf38b16bf6b2b 26 | pubmatic.com,160721,DIRECT,5d62403b186f2ace 27 | appnexus.com,7795,DIRECT,f5ab79cb980f11d1 28 | pubmatic.com,160006,RESELLER,5d62403b186f2ace 29 | pubmatic.com,160096,RESELLER,5d62403b186f2ace 30 | rubiconproject.com,18020,RESELLER,0bfd66d529a55807 31 | openx.com,540191398,RESELLER,6a698e2ec38604c6 32 | appnexus.com,1908,RESELLER,f5ab79cb980f11d1 33 | smaato.com,1100044650,RESELLER,07bcf65f187117b4 34 | ad-generation.jp,12474,RESELLER,7f4ea9029ac04e53 35 | districtm.io,100962,RESELLER,3fd707be9c4527c3 36 | appnexus.com,3663,RESELLER,f5ab79cb980f11d1 37 | rhythmone.com,1654642120,RESELLER,a670c89d4a324e47 38 | yahoo.com,55029,RESELLER,e1a5b5b6e3255540 39 | admanmedia.com,726,RESELLER 40 | emxdgt.com,2009,RESELLER,1e1d41537f7cad7f 41 | appnexus.com,1356,RESELLER,f5ab79cb980f11d1 42 | contextweb.com,562541,RESELLER,89ff185a4c4e857c 43 | themediagrid.com,JTQKMP,RESELLER,35d5010d7789b49d 44 | beachfront.com,14804,RESELLER,e2541279e8e2ca4d 45 | improvedigital.com,2050,RESELLER 46 | mintegral.com,10043,RESELLER,0aeed750c80d6423 47 | sonobi.com,7f5fa520f8,RESELLER,d1a215d9eb5aee9e 48 | onetag.com,770a440e65869c2,RESELLER 49 | sharethrough.com,83fd1714,DIRECT,d53b998a7bd4ecd2 50 | media.net,8CUTTT41M,DIRECT 51 | risecodes.com,63832beef8189a00015cb6d3,RESELLER 52 | mediago.io,045ac24b888bcf59a09731e7f0f2084f,RESELLER 53 | gumgum.com,14141,RESELLER,ffdef49475d318a9 54 | adyoulike.com,7463c359225e043c111036d7a29affa5,RESELLER 55 | minutemedia.com,01gya4708ddm,RESELLER 56 | visiblemeasures.com,1052,RESELLER 57 | indexexchange.com, 192410, RESELLER, 50b1c356f2c5c8fc 58 | 59 | 60 | # AppNexus 61 | appnexus.com, 7795, DIRECT 62 | 63 | # Blockthrough 64 | blockthrough.com, 6278260873756672, DIRECT 65 | appnexus.com, 6979, RESELLER 66 | indexexchange.com, 194341, RESELLER, 50b1c356f2c5c8fc 67 | pubmatic.com, 160377, RESELLER, 5d62403b186f2ace 68 | criteo.com, 8990, RESELLER 69 | sovrn.com, 328150, DIRECT, fafdf38b16bf6b2b 70 | lijit.com, 328150, DIRECT, fafdf38b16bf6b2b 71 | lijit.com, 328150-eb, DIRECT, fafdf38b16bf6b2b 72 | appnexus.com, 1360, RESELLER, f5ab79cb980f11d1 73 | gumgum.com, 11645, RESELLER, ffdef49475d318a9 74 | openx.com, 538959099, RESELLER, 6a698e2ec38604c6 75 | openx.com, 539924617, RESELLER, 6a698e2ec38604c6 76 | pubmatic.com, 137711, RESELLER, 5d62403b186f2ace 77 | pubmatic.com, 156212, RESELLER, 5d62403b186f2ace 78 | pubmatic.com, 156700, RESELLER, 5d62403b186f2ace 79 | rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807 80 | rubiconproject.com, 23718, RESELLER, 0bfd66d529a55807 81 | onetag.com, 75804861b76a852, DIRECT 82 | admanmedia.com, 1028, DIRECT 83 | smartadserver.com, 3713, RESELLER 84 | openx.com, 540866936, DIRECT, 6a698e2ec38604c6 85 | rhythmone.com, 3948367200, RESELLER, a670c89d4a324e47 86 | adform.com, 2671, RESELLER 87 | yahoo.com, 59674, RESELLER, e1a5b5b6e3255540 88 | axonix.com, 57869, RESELLER 89 | rubiconproject.com, 14558, RESELLER, 0bfd66d529a55807 90 | pubmatic.com, 158481, RESELLER, 5d62403b186f2ace 91 | contextweb.com, 561913, RESELLER, 89ff185a4c4e857c 92 | criteo.com, B-063105, RESELLER, 9fac4a4a87c2a44f 93 | conversantmedia.com, 100308, RESELLER, 03113cd04947736d 94 | nativo.com, 5758, DIRECT, 59521ca7cc5e9fee 95 | 33across.com, 0010b00002OCUmCAAX, RESELLER, bbea06d9c4d2853c 96 | appnexus.com, 8035, RESELLER, f5ab79cb980f11d1 97 | contextweb.com, 560921, RESELLER, 89ff185a4c4e857c 98 | districtm.io, 101810, RESELLER, 3fd707be9c4527c3 99 | indexexchange.com, 190025, RESELLER 100 | mobfox.com, 82187, RESELLER, 5529a3d1f59865be 101 | openx.com, 540709535, RESELLER, 6a698e2ec38604c6 102 | pubmatic.com, 156500, RESELLER, 5d62403b186f2ace 103 | rubiconproject.com, 16156, RESELLER, 0bfd66d529a55807 104 | 152media.info,152M319,RESELLER 105 | appnexus.com, 3153, RESELLER, f5ab79cb980f11d1 106 | appnexus.com, 11924, RESELLER, f5ab79cb980f11d1 107 | smartadserver.com, 4485, RESELLER, 060d053dcf45cbf3 108 | smartadserver.com, 4485-OB, RESELLER, 060d053dcf45cbf3 109 | smartadserver.com, 4071, RESELLER 110 | pubmatic.com, 156439, RESELLER, 5d62403b186f2ace 111 | pubmatic.com, 154037, RESELLER, 5d62403b186f2ace 112 | rubiconproject.com, 16114, RESELLER, 0bfd66d529a55807 113 | openx.com, 537149888, RESELLER, 6a698e2ec38604c6 114 | appnexus.com, 3703, RESELLER, f5ab79cb980f11d1 115 | loopme.com, 5679, RESELLER, 6c8d5f95897a5a3b 116 | xad.com, 958, RESELLER, 81cbf0a75a5e0e9a 117 | video.unrulymedia.com, 2564526802, RESELLER 118 | smaato.com, 1100044045, RESELLER, 07bcf65f187117b4 119 | pubnative.net, 1006576, RESELLER, d641df8625486a7b 120 | verve.com, 15503, RESELLER, 0c8f5958fc2d6270 121 | adyoulike.com, b4bf4fdd9b0b915f746f6747ff432bde, RESELLER 122 | axonix.com, 57264, RESELLER 123 | admanmedia.com, 43, RESELLER 124 | sharethrough.com, OAW69Fon, RESELLER, d53b998a7bd4ecd2 125 | appnexus.com, 2530, RESELLER 126 | amxrtb.com, 105199664, DIRECT 127 | appnexus.com, 12290, RESELLER, f5ab79cb980f11d1 128 | appnexus.com, 11786, RESELLER, f5ab79cb980f11d1 129 | yahoo.com, 49648, RESELLER 130 | indexexchange.com, 191503, RESELLER, 50b1c356f2c5c8fc 131 | lijit.com, 260380, RESELLER, fafdf38b16bf6b2b 132 | sovrn.com, 260380, RESELLER, fafdf38b16bf6b2b 133 | pubmatic.com, 158355 , RESELLER, 5d62403b186f2ace 134 | appnexus.com, 9393, RESELLER, f5ab79cb980f11d1 #Video #Display 135 | 136 | 137 | # Colossus 138 | supply.colossusssp.com, 554, DIRECT, 6c5b49d96ec1b458 139 | appnexus.com, 10490, RESELLER, f5ab79cb980f11d1 140 | yieldmo.com, 3078438591206989879, RESELLER, #ortb 141 | 142 | # Conversant (MMS) 143 | conversantmedia.com, 40888, DIRECT, 03113cd04947736d 144 | admanmedia.com, 2050, RESELLER 145 | appnexus.com, 4052, RESELLER 146 | contextweb.com, 561998, RESELLER, 89ff185a4c4e857c 147 | lijit.com, 411121, RESELLER, fafdf38b16bf6b2b #SOVRN 148 | openx.com, 540031703, RESELLER, 6a698e2ec38604c6 149 | pubmatic.com, 158100, RESELLER, 5d62403b186f2ace 150 | rubiconproject.com, 23644, RESELLER, 0bfd66d529a55807 151 | yahoo.com, 55771, RESELLER, e1a5b5b6e3255540 152 | 153 | # Criteo 154 | criteo.com, B-058532, DIRECT, 9fac4a4a87c2a44f 155 | themediagrid.com, 98HKW6, DIRECT, 35d5010d7789b49d 156 | 157 | 158 | # Google 159 | google.com, pub-9872233689689746, DIRECT, f08c47fec0942fa0 160 | 161 | # Google (Open Bidding) 162 | adingo.jp, 30305, RESELLER 163 | 164 | # GumGum 165 | gumgum.com,13892,RESELLER,ffdef49475d318a9 166 | rubiconproject.com,23434,RESELLER,0bfd66d529a55807 167 | pubmatic.com,157897,RESELLER,5d62403b186f2ace 168 | appnexus.com,2758,RESELLER,f5ab79cb980f11d1 169 | contextweb.com,558355,RESELLER,89ff185a4c4e857c 170 | openx.com,537149485,RESELLER,6a698e2ec38604c6 171 | improvedigital.com,1884,RESELLER 172 | smartadserver.com,4005,RESELLER,060d053dcf45cbf3 173 | imds.tv,82151,RESELLER,ae6c32151e71f19d 174 | admanmedia.com,799,RESELLER 175 | revcontent.com,110029,RESELLER 176 | google.com,pub-3848273848634341,RESELLER,f08c47fec0942fa0 177 | 178 | 179 | # Index Exchange 180 | indexexchange.com, 185177, DIRECT 181 | 182 | 183 | # Media.net 184 | media.net, 8CU87559X, DIRECT 185 | media.net, 8CUB565JD, DIRECT 186 | openx.com, 537100188, RESELLER, 6a698e2ec38604c6 187 | pubmatic.com, 159463, RESELLER, 5d62403b186f2ace 188 | emxdgt.com, 1759, RESELLER, 1e1d41537f7cad7f 189 | appnexus.com, 1356, RESELLER, f5ab79cb980f11d1 190 | google.com, pub-7439041255533808, RESELLER, f08c47fec0942fa0 191 | rubiconproject.com, 19396, RESELLER, 0bfd66d529a55807 192 | onetag.com, 5d49f482552c9b6, RESELLER 193 | sonobi.com, 83729e979b, RESELLER 194 | 33across.com, 0010b00002cGp2AAAS, RESELLER, bbea06d9c4d2853c 195 | rhythmone.com, 3611299104, RESELLER 196 | lemmatechnologies.com, 399, RESELLER, 7829010c5bebd1fb #LEMMA 197 | media.net, 8CUTTT41M, DIRECT 198 | 199 | # OpenX 200 | openx.com, 539609083, DIRECT, 6a698e2ec38604c6 201 | openx.com, 557936259, DIRECT, 6a698e2ec38604c6 202 | 203 | # PubMatic 204 | pubmatic.com, 156737, DIRECT, 5d62403b186f2ace 205 | pubmatic.com, 162079, DIRECT, 5d62403b186f2ace 206 | 207 | 208 | # PubMatic (TAM) 209 | pubmatic.com, 160721, DIRECT, 5d62403b186f2ace 210 | 211 | 212 | # Rubicon 213 | rubiconproject.com, 17374, DIRECT, 0bfd66d529a55807 214 | 215 | # Sharethrough 216 | sharethrough.com, 83fd1714, DIRECT, d53b998a7bd4ecd2 217 | sharethrough.com, SXT0ZdrT, RESELLER, d53b998a7bd4ecd2 218 | pubmatic.com, 156557, RESELLER 219 | rubiconproject.com, 18694, RESELLER, 0bfd66d529a55807 220 | openx.com, 540274407, RESELLER, 6a698e2ec38604c6 221 | 33across.com, 0013300001kQj2HAAS, RESELLER, bbea06d9c4d2853c 222 | smaato.com, 1100047713, RESELLER, 07bcf65f187117b4 223 | smartadserver.com, 4342, RESELLER 224 | smartadserver.com, 4012, RESELLER 225 | video.unrulymedia.com, 266978658, RESELLER 226 | 227 | # Smaato 228 | smaato.com, 1100055098, DIRECT, 07bcf65f187117b4 229 | smaato.com, 1100004890, DIRECT, 07bcf65f187117b4 230 | rubiconproject.com, 24600, RESELLER, 0bfd66d529a55807 231 | sharethrough.com, iBAzay96, RESELLER, d53b998a7bd4ecd2 232 | pubmatic.com, 156177, RESELLER, 5d62403b186f2ace 233 | pubmatic.com, 156425, RESELLER, 5d62403b186f2ace 234 | inmobi.com, 55049d2e109d4ac1820ca1432dda4e13, RESELLER, 83e75a7ae333ca9d 235 | openx.com, 540421297, RESELLER, 6a698e2ec38604c6 236 | appnexus.com,1752, RESELLER, f5ab79cb980f11d1 237 | sonobi.com, 25e3d9361e, RESELLER, d1a215d9eb5aee9e 238 | rhebus.works,8978398251,RESELLER 239 | aceex.io, 898, RESELLER, b1cf3c874d5c6682 240 | adelement.com, 30208, RESELLER 241 | adwmg.com,100436, RESELLER, c9688a22012618e7 242 | blis.com,86,RESELLER,61453ae19a4b73f4 243 | rubiconproject.com, 16928, RESELLER, 0bfd66d529a55807 244 | thebrave.io, 52353142, RESELLER, c25b2154543746ac 245 | brightcom.com, 20480, RESELLER 246 | conversantmedia.com, 100246, RESELLER, 03113cd04947736d 247 | axonix.com, 56805, reseller, bc385f2b4a87b721 248 | epom.com, 37f0df4f-3800-4ea3-979a-7b27451ad27a, RESELLER 249 | e-planning.net, fe2609a0797012f6, RESELLER, c1ba615865ed87b2 250 | ssp.e-volution.ai, AJxF6R189a9M6CaTvK, RESELLER 251 | gitberry.com, 355100012, RESELLER 252 | keenkale.com,43421,RESELLER 253 | loopme.com, 2896, RESELLER, 6c8d5f95897a5a3b 254 | lunamedia.io, d5b7a3e0bf7575046765e5c639e98ab0, RESELLER, 524ecb396915caaf 255 | uis.mobfox.com, 1750, RESELLER, 5529a3d1f59865be 256 | admixer.co.kr, 1094, RESELLER 257 | opera.com, pub4444433466368, RESELLER, 55a0c5fd61378de3 258 | contextweb.com, 558622, RESELLER, 89ff185a4c4e857c 259 | roockmobile.com, 2294, RESELLER 260 | webeyemob.com, 70107, RESELLER 261 | x.adprime.com, AJxF6R20a9M6CaTvK, RESELLER 262 | omnifytv.com, 1133aea857844a518c6eedab3c1eecd0, RESELLER 263 | 152media.info, 152M608, RESELLER 264 | sspx.tech, dspx146, RESELLER 265 | myfeature.tv, MehEp76Y1PNhVEXNGQ9r, RESELLER 266 | 267 | # Sonobi 268 | sonobi.com, 97d6fcd501, DIRECT, d1a215d9eb5aee9e 269 | contextweb.com, 560606, RESELLER, 89ff185a4c4e857c 270 | sonobi.com, 45f1d36867, RESELLER, d1a215d9eb5aee9e 271 | sonobi.com, 76f3bd618e, RESELLER, d1a215d9eb5aee9e 272 | 273 | # Sovrn 274 | lijit.com, 251959, DIRECT, fafdf38b16bf6b2b #SOVRN 275 | lijit.com, 251959-eb, DIRECT, fafdf38b16bf6b2b #SOVRN 276 | openx.com, 538959099, RESELLER, 6a698e2ec38604c6 277 | pubmatic.com, 137711, RESELLER, 5d62403b186f2ace 278 | pubmatic.com, 156212, RESELLER, 5d62403b186f2ace 279 | rubiconproject.com, 17960, RESELLER, 0bfd66d529a55807 280 | appnexus.com, 1019, RESELLER, f5ab79cb980f11d1 281 | video.unrulymedia.com, 12444764291, RESELLER 282 | 283 | # TripleLift 284 | triplelift.com, 9734, DIRECT, 6c33edb13117fd86 285 | triplelift.com, 9734-EB, DIRECT, 6c33edb13117fd86 286 | 287 | # Unruly 288 | rhythmone.com, 7714644224986639015,DIRECT,a670c89d4a324e47 289 | video.unrulymedia.com, 7714644224986639015, DIRECT 290 | zoomd.com, 88478941, RESELLER 291 | openx.com, 540646187, RESELLER, 6a698e2ec38604c6 292 | sharethrough.com, YNXWxWBa, RESELLER, d53b998a7bd4ecd2 293 | improvedigital.com, 1795, RESELLER 294 | google.com, pub-1206522670195963, RESELLER, f08c47fec0942fa0 295 | minutemedia.com, 01gtc08s0d6r, RESELLER 296 | adipolo.com, 22878776773, DIRECT 297 | google.com, pub-2930805104418204, RESELLER, f08c47fec0942fa0 298 | aps.amazon.com, 00ed17ab-4189-4639-9d5e-15acd40affde, DIRECT 299 | 300 | # Vidazoo 301 | vidazoo.com, 64a6f7fcd4f5ac1ff4260eb7, DIRECT, b6ada874b4d7d0b2 302 | yahoo.com, 59039, RESELLER, e1a5b5b6e3255540 303 | rubiconproject.com, 17130, RESELLER, 0bfd66d529a55807 304 | pubmatic.com, 159470, RESELLER, 5d62403b186f2ace 305 | appnexus.com, 2794, RESELLER, f5ab79cb980f11d1 306 | video.unrulymedia.com, 2743945877, RESELLER 307 | openx.com, 541017750, RESELLER, 6a698e2ec38604c6 308 | undertone.com, 3934, RESELLER 309 | google.com, pub-4282252113865235, RESELLER, f08c47fec0942fa0 310 | freewheel.tv, 625393, RESELLER 311 | supply.colossusssp.com, 181, RESELLER, 6c5b49d96ec1b458 312 | indexexchange.com, 193874, RESELLER, 50b1c356f2c5c8fc 313 | aps.amazon.com, cfee765f-a9ba-4867-af16-2ff8d62ed9ba, RESELLER 314 | triplelift.com, 11883, RESELLER, 6c33edb13117fd86 315 | sharethrough.com, 0c3ba0a3, RESELLER, d53b998a7bd4ecd2 316 | themediagrid.com, 3AW9JB, RESELLER, 35d5010d7789b49d 317 | adform.com, 2643, RESELLER, 9f5210a2f0999e32 318 | lijit.com, 222372, RESELLER, fafdf38b16bf6b2b 319 | pubmatic.com, 159988, RESELLER, 5d62403b186f2ace 320 | sonobi.com, e32c07f4ba, RESELLER, d1a215d9eb5aee9e 321 | sharethrough.com, S2rESyUH, RESELLER, d53b998a7bd4ecd2 322 | richaudience.com, 3MkIAgQBPw, RESELLER 323 | didna.io, 64a6f7fcd4f5ac1ff4260eb7, DIRECT 324 | google.com, pub-3565385483761681, RESELLER, f08c47fec0942fa0 325 | yahoo.com, 59040, RESELLER, e1a5b5b6e3255540 326 | -------------------------------------------------------------------------------- /static/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/android-chrome-192x192.png -------------------------------------------------------------------------------- /static/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/android-chrome-512x512.png -------------------------------------------------------------------------------- /static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/apple-touch-icon.png -------------------------------------------------------------------------------- /static/apps.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Apps | Polaris 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

Apps

19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 |
27 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /static/assets/JSON/apps.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Android OS", 4 | "image": "/cdn/images/android.png", 5 | "target": "https://now.gg/iframe/snippet?app_pkg=com.uncube.launcher3&partner=skool" 6 | }, 7 | { 8 | "name": "Spotify", 9 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Spotify_logo_without_text.svg/168px-Spotify_logo_without_text.svg.png", 10 | "target": "https://open.spotify.com/" 11 | }, 12 | { 13 | "name": "ChatGPT", 14 | "image": "/cdn/images/chatgpt.webp", 15 | "target": "https://chat.shuttleai.app" 16 | }, 17 | { 18 | "name": "Chess.com", 19 | "image": "https://play-lh.googleusercontent.com/a7R5nyeaX8lIEWdBOxjlvbyq9LcFwh3XMvNtBPEKR3LPGgdvgGrec4sJwn8tUaaSkw", 20 | "target": "https://chess.com/" 21 | }, 22 | { 23 | "name": "Coolmath Games", 24 | "image": "https://play-lh.googleusercontent.com/I3hW7sKSGtOKeTpmsBxka7Eyul5HXTZUD4H3EbJEWyW-4vDlpyEG5Jtu9D8JgR0__g", 25 | "target": "https://coolmathgames.com/" 26 | }, 27 | { 28 | "name": "Discord", 29 | "image": "/cdn/images/discord.png", 30 | "target": "https://discord.com/app" 31 | }, 32 | { 33 | "name": "Messenger", 34 | "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Facebook_Messenger_logo_2020.svg/2048px-Facebook_Messenger_logo_2020.svg.png", 35 | "target": "https://messenger.com/" 36 | }, 37 | { 38 | "name": "Emulator", 39 | "image": "https://emulatorjs.org/media/logo-light.png", 40 | "target": "https://demo.emulatorjs.org/" 41 | }, 42 | { 43 | "name": "GeoFS", 44 | "image": "https://www.geo-fs.com/images/media/logo/icon.png", 45 | "target": "https://www.geo-fs.com/" 46 | }, 47 | { 48 | "name": "Movies/TV", 49 | "image": "https://avatars.githubusercontent.com/u/121455091", 50 | "target": "https://movie-web.app" 51 | }, 52 | { 53 | "name": "Paramount+", 54 | "image": "https://play-lh.googleusercontent.com/oi4GE8ulxHp4Y2xVzKu_WAMrgE4Jj4Kbdd7hAWLeoZTsMtC5bYTd2xcYhlvMk69pTFY", 55 | "target": "https://www.paramountplus.com/account/signin/" 56 | }, 57 | { 58 | "name": "Pinterest", 59 | "image": "https://static-00.iconduck.com/assets.00/pinterest-icon-2048x2048-d7p0u7c5.png", 60 | "target": "https://pinterest.com/" 61 | }, 62 | { 63 | "name": "Tiktok", 64 | "image": "/cdn/images/tiktok.png", 65 | "target": "https://www.tiktok.com/en/" 66 | }, 67 | { 68 | "name": "Twitch", 69 | "image": "/cdn/images/twitch.png", 70 | "target": "https://www.twitch.tv/" 71 | }, 72 | { 73 | "name": "Youtube", 74 | "image": "/cdn/images/youtube.png", 75 | "target": "https://youtube.com" 76 | }, 77 | { 78 | "name": "Youtube Downloader", 79 | "image": "/cdn/images/youtubedownloader.png", 80 | "target": "https://cobalt.tools" 81 | }, 82 | { 83 | "name": "Youtube Music", 84 | "image": "/cdn/images/yt-music.png", 85 | "target": "https://music.youtube.com" 86 | } 87 | ] 88 | -------------------------------------------------------------------------------- /static/assets/JSON/changelog.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "3/28/2024", 4 | "simpleDescription": "Changed Navbar Style" 5 | }, 6 | { 7 | "date": "3/10/2024", 8 | "simpleDescription": "Fixed Settings Menu and improved Game Search" 9 | }, 10 | { 11 | "date": "3/7/2024", 12 | "simpleDescription": "Added 10 games" 13 | }, 14 | { 15 | "date": "3/1/2024", 16 | "simpleDescription": "Fix roblox" 17 | }, 18 | { 19 | "date": "2/28/2024", 20 | "simpleDescription": "Added 2 games" 21 | }, 22 | { 23 | "date": "2/27/2024", 24 | "simpleDescription": "Added 4 games and removed Buckshot Roulette" 25 | }, 26 | { 27 | "date": "2/26/2024", 28 | "simpleDescription": "Added Buckshot Roulette" 29 | }, 30 | { 31 | "date": "2/24/2024", 32 | "simpleDescription": "Added a random game button" 33 | }, 34 | { 35 | "date": "2/23/2024", 36 | "simpleDescription": "Added 6 games and changed popular games" 37 | }, 38 | { 39 | "date": "2/22/2024", 40 | "simpleDescription": "Added 17 games (200+ games 🎉), and removed 1 game" 41 | }, 42 | { 43 | "date": "2/21/2024", 44 | "simpleDescription": "Fixed more games, added 15 games, and added 2 new themes" 45 | }, 46 | { 47 | "date": "2/20/2024", 48 | "simpleDescription": "Fixed Lots of games" 49 | }, 50 | { 51 | "date": "2/16/2024", 52 | "simpleDescription": "Fixed Roblox" 53 | }, 54 | { 55 | "date": "2/15/2024", 56 | "simpleDescription": "Added 4 games" 57 | }, 58 | { 59 | "date": "2/14/2024", 60 | "simpleDescription": "Fixed 3 games, added 23 games, and added Youtube Music" 61 | }, 62 | { 63 | "date": "2/13/2024", 64 | "simpleDescription": "Fixed issues with the cdn and added 8 new games" 65 | }, 66 | { 67 | "date": "2/12/2024", 68 | "simpleDescription": "Added 15 games and changed the featured game to Run 3" 69 | }, 70 | { 71 | "date": "1/25/2024", 72 | "simpleDescription": "Changed featured game to 99 Balls 3D" 73 | }, 74 | { 75 | "date": "1/24/2024", 76 | "simpleDescription": "Added 3 more Papa's Gameria games, added Bonk.io" 77 | }, 78 | { 79 | "date": "1/19/2024", 80 | "simpleDescription": "Added Genshin Impact, Melon Sandbox moved to all games" 81 | }, 82 | { 83 | "date": "1/16/2024", 84 | "simpleDescription": "Changed featured game to Retro Bowl" 85 | }, 86 | { 87 | "date": "1/15/2024", 88 | "simpleDescription": "Updated Privacy Policy + TOS" 89 | }, 90 | { 91 | "date": "1/12/2024", 92 | "simpleDescription": "Patched proxy loading issue" 93 | }, 94 | { 95 | "date": "1/11/2024", 96 | "simpleDescription": "Added COD Mobile, Monkey Mart now in all games" 97 | }, 98 | { 99 | "date": "1/9/2024", 100 | "simpleDescription": "Added game saving. Located in the settings tab" 101 | }, 102 | { 103 | "date": "1/8/2024", 104 | "simpleDescription": "Fixed games and added Fortnite" 105 | }, 106 | { 107 | "date": "1/5/2024", 108 | "simpleDescription": "Added a crap ton of apps" 109 | }, 110 | { 111 | "date": "1/2/2024", 112 | "simpleDescription": "Bug patches and small updates" 113 | }, 114 | { 115 | "date": "1/1/2024", 116 | "simpleDescription": "2024 Update!!! - UI Overhaul" 117 | }, 118 | { 119 | "date": "11/23/2023", 120 | "simpleDescription": "Minor UI Changes" 121 | }, 122 | { 123 | "date": "11/16/2023", 124 | "simpleDescription": "Added ROM Emulator to apps + Added 2 games" 125 | } 126 | ] 127 | -------------------------------------------------------------------------------- /static/assets/JSON/cheats.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Blooket", 4 | "target": "/cdn/cheats/blooket.html", 5 | "image": "https://media.discordapp.net/attachments/1118495479802630174/1158062820097802331/latest.png?ex=651ae18c&is=6519900c&hm=544b7a8eb56236327b7bcabfce4b688428d59a63c8e07bcb2aab3ff81c916286&=" 6 | }, 7 | { 8 | "name": "EdPuzzle", 9 | "target": "https://edpuzzle.hs.vc/", 10 | "image": "https://edpuzzle.imgix.net/edpuzzle-logos/vertical-logo.png" 11 | }, 12 | { 13 | "name": "Wordle", 14 | "target": "/cdn/cheats/wordle.html", 15 | "image": "/cdn/images/wordle.png" 16 | }, 17 | { 18 | "name": "Prodigy", 19 | "target": "/cdn/cheats/prodigy.html", 20 | "image": "https://webcdn.prodigygame.com/common/images/socialshare.png" 21 | }, 22 | { 23 | "name": "Quizizz", 24 | "target": "https://quizit.online/services/quizizz/", 25 | "image": "https://media.discordapp.net/attachments/1118495479802630174/1158062649792286720/Screen_Shot_2023-10-01_at_11.27.24_AM.png?ex=651ae163&is=65198fe3&hm=d9b86e146ddc9e98690822155ccdc2c239d776f1a42fa16447d820e8e34a826b&=" 26 | }, 27 | { 28 | "name": "Quizlet", 29 | "target": "/cdn/cheats/quizlet.html", 30 | "image": "https://assets.quizlet.com/a/j/dist/app/i/brandmark/1024.38e7be0a829de8f.png" 31 | }, 32 | { 33 | "name": "Gimkit", 34 | "target": "https://github.com/TheLazySquid/GimkitCheat", 35 | "image": "https://images.crunchbase.com/image/upload/c_lpad,f_auto,q_auto:eco,dpr_1/iin53jlbkmdjjdpjbcde" 36 | }, 37 | { 38 | "name": "Skoolvox Helper", 39 | "target": "https://github.com/fowntain/skiovox-skooledition/releases", 40 | "image": "https://raw.githubusercontent.com/fowntain/skiovox-skooledition/main/icon.png" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /static/assets/JSON/cloaks.json: -------------------------------------------------------------------------------- 1 | { 2 | "canvas": { 3 | "icon": "https://www.google.com/s2/favicons?domain=https://canvas.instructure.com", 4 | "title": "Dashboard", 5 | "clickoff": "" 6 | }, 7 | "google": { 8 | "icon": "https://www.google.com/s2/favicons?domain=https://google.com", 9 | "title": "Google", 10 | "clickoff": "" 11 | }, 12 | "classroom": { 13 | "icon": "https://play-lh.googleusercontent.com/w0s3au7cWptVf648ChCUP7sW6uzdwGFTSTenE178Tz87K_w1P1sFwI6h1CLZUlC2Ug", 14 | "title": "Classes", 15 | "clickoff": "" 16 | } 17 | } -------------------------------------------------------------------------------- /static/assets/css/dropdown.css: -------------------------------------------------------------------------------- 1 | .dropbtn { 2 | background-color: #3498DB; 3 | color: white; 4 | padding: 1.6vh; 5 | font-size: 1.6vh; 6 | border: none; 7 | cursor: pointer; 8 | } 9 | 10 | .dropbtn:hover, 11 | .dropbtn:focus { 12 | background-color: #2980B9; 13 | } 14 | 15 | .dropdown { 16 | position: relative; 17 | display: inline-block; 18 | } 19 | 20 | .dropdown-content { 21 | display: none; 22 | position: absolute; 23 | background-color: #f1f1f1; 24 | min-width: 16.5vh; 25 | overflow: auto; 26 | box-shadow: 0vh 0.8vh 1.6vh 0vh rgba(0, 0, 0, 0.2); 27 | z-index: 1; 28 | } 29 | 30 | .dropdown-content a { 31 | color: black; 32 | padding: 1.2vh 1.6vh; 33 | text-decoration: none; 34 | display: block; 35 | } 36 | 37 | .dropdown a:hover { 38 | background-color: #ddd; 39 | } 40 | 41 | .show { 42 | display: block; 43 | } 44 | -------------------------------------------------------------------------------- /static/assets/css/fonts.css: -------------------------------------------------------------------------------- 1 | /*Font template: 2 | 3 | @font-face { 4 | font-family: 'fontname'; 5 | src: url('/assets/fonts/fontname.woff2') format('woff2'), 6 | url('/assets/fonts/fontname.woff') format('woff'); 7 | font-weight: normal; 8 | font-style: normal; 9 | }*/ 10 | 11 | /*Lato*/ 12 | 13 | @font-face { 14 | font-family: 'Lato-LightItalic'; 15 | src: url('/assets/fonts/Lato-LightItalic.woff2') format('woff2'), 16 | url('/assets/fonts/Lato-LightItalic.woff') format('woff'); 17 | font-weight: 300; 18 | font-style: italic; 19 | font-display: swap; 20 | } 21 | 22 | @font-face { 23 | font-family: 'Lato-Hairline'; 24 | src: url('/assets/fonts/Lato-Hairline.woff2') format('woff2'), 25 | url('/assets/fonts/Lato-Hairline.woff') format('woff'); 26 | font-weight: 300; 27 | font-style: normal; 28 | font-display: swap; 29 | } 30 | 31 | @font-face { 32 | font-family: 'Lato-BoldItalic'; 33 | src: url('/assets/fonts/Lato-BoldItalic.woff2') format('woff2'), 34 | url('/assets/fonts/Lato-BoldItalic.woff') format('woff'); 35 | font-weight: bold; 36 | font-style: italic; 37 | font-display: swap; 38 | } 39 | 40 | @font-face { 41 | font-family: 'Lato-Light'; 42 | src: url('/assets/fonts/Lato-Light.woff2') format('woff2'), 43 | url('/assets/fonts/Lato-Light.woff') format('woff'); 44 | font-weight: 300; 45 | font-style: normal; 46 | font-display: swap; 47 | } 48 | 49 | @font-face { 50 | font-family: 'Lato-HairlineItalic'; 51 | src: url('/assets/fonts/Lato-HairlineItalic.woff2') format('woff2'), 52 | url('/assets/fonts/Lato-HairlineItalic.woff') format('woff'); 53 | font-weight: 300; 54 | font-style: italic; 55 | font-display: swap; 56 | } 57 | 58 | @font-face { 59 | font-family: 'Lato-Italic'; 60 | src: url('/assets/fonts/Lato-Italic.woff2') format('woff2'), 61 | url('/assets/fonts/Lato-Italic.woff') format('woff'); 62 | font-weight: normal; 63 | font-style: italic; 64 | font-display: swap; 65 | } 66 | 67 | @font-face { 68 | font-family: 'Lato'; 69 | src: url('/assets/fonts/Lato-Regular.woff2') format('woff2'), 70 | url('/assets/fonts/Lato-Regular.woff') format('woff'); 71 | font-weight: normal; 72 | font-style: normal; 73 | font-display: swap; 74 | } 75 | 76 | @font-face { 77 | font-family: 'Lato-BlackItalic'; 78 | src: url('/assets/fonts/Lato-BlackItalic.woff2') format('woff2'), 79 | url('/assets/fonts/Lato-BlackItalic.woff') format('woff'); 80 | font-weight: 900; 81 | font-style: italic; 82 | font-display: swap; 83 | } 84 | 85 | @font-face { 86 | font-family: 'Lato-Black'; 87 | src: url('/assets/fonts/Lato-Black.woff2') format('woff2'), 88 | url('/assets/fonts/Lato-Black.woff') format('woff'); 89 | font-weight: 900; 90 | font-style: normal; 91 | font-display: swap; 92 | } 93 | 94 | @font-face { 95 | font-family: 'Lato-Bold'; 96 | src: url('/assets/fonts/Lato-Bold.woff2') format('woff2'), 97 | url('/assets/fonts/Lato-Bold.woff') format('woff'); 98 | font-weight: bold; 99 | font-style: normal; 100 | font-display: swap; 101 | } -------------------------------------------------------------------------------- /static/assets/css/footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | position: relative; 3 | text-align: left; 4 | background-image: url('/assets/img/background.jpeg'); 5 | background-attachment: fixed; 6 | background-position: center; 7 | background-repeat: no-repeat; 8 | background-size: cover; 9 | user-select: none; 10 | padding: 4% 5% 2% 5%; 11 | color: #fff; 12 | margin-top: 50px; 13 | text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.616); 14 | } 15 | 16 | footer p { 17 | text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.616); 18 | } 19 | 20 | footer h1 { 21 | font-family: 'Lato-Black'; 22 | color: #fff; 23 | margin: 0px; 24 | margin-bottom: 10px; 25 | } 26 | 27 | footer::before { 28 | display: block; 29 | content: ''; 30 | height: 270px; 31 | position: fixed; 32 | bottom: 0; 33 | left: 0; 34 | width: 100vw; 35 | z-index: -1; 36 | background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.8) 100%); 37 | } 38 | 39 | footer .socials { 40 | margin-bottom: 20px; 41 | } 42 | 43 | footer .socials i { 44 | margin: 10px; 45 | margin-left: 5px; 46 | margin-right: 5px; 47 | font-size: 20px; 48 | cursor: pointer; 49 | } 50 | 51 | footer .socials a { 52 | color: #fff; 53 | } 54 | 55 | footer .socials a:hover { 56 | color: #fff; 57 | } 58 | 59 | footer .socials a::before { 60 | display: none; 61 | } 62 | 63 | footer .socials i:is(:first-child) { 64 | margin-left: 0px; 65 | } 66 | 67 | footer .right { 68 | position: absolute; 69 | right: 5%; 70 | text-align: right; 71 | } 72 | 73 | footer .right a { 74 | display: block; 75 | margin-top: 20px; 76 | } 77 | 78 | footer .title:not(:last-child) { 79 | display: flex; 80 | margin: 0px; 81 | } 82 | 83 | footer .title img { 84 | width: 40px; 85 | height: 40px; 86 | margin-left: 10px; 87 | filter: drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.616)); 88 | border-radius: 1vh; 89 | } -------------------------------------------------------------------------------- /static/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import url('https://site-assets.fontawesome.com/releases/v6.5.1/css/all.css'); 2 | @import url('./dropdown.css'); 3 | @import url('./sidebar.css'); 4 | @import url('./footer.css'); 5 | @import url('./themes.css'); 6 | @import url('./fonts.css'); 7 | @import url('./nav.css'); 8 | 9 | * { 10 | font-family: 'Lato'; 11 | user-select: none; 12 | scroll-behavior: smooth; 13 | transition: 0.5s; 14 | } 15 | 16 | body { 17 | margin: 0px; 18 | background: var(--background-color); 19 | background-attachment: fixed; 20 | color: var(--text); 21 | padding-top: 20vh; 22 | background-repeat: no-repeat; 23 | opacity: 0.7; 24 | } 25 | 26 | .centered { 27 | text-align: center; 28 | } 29 | 30 | html { 31 | min-height: 100%; 32 | } 33 | 34 | body::-webkit-scrollbar-track { 35 | background: var(--solid); 36 | margin: 0; 37 | } 38 | 39 | body::-webkit-scrollbar-thumb { 40 | background: var(--scrollbar-color); 41 | border: 0.5vh solid var(--solid); 42 | background-clip: content-box; 43 | border-radius: 1vh; 44 | } 45 | 46 | body::-webkit-scrollbar-corner { 47 | background: #000; 48 | } 49 | 50 | ::-webkit-scrollbar { 51 | width: 1.8vh; 52 | } 53 | 54 | ::-webkit-scrollbar-track { 55 | background: transparent; 56 | margin-top: 2vh; 57 | margin-bottom: 2vh; 58 | margin-left: 1vh; 59 | } 60 | 61 | ::-webkit-scrollbar-thumb { 62 | background: var(--scrollbar-color); 63 | border: 0.5vh solid transparent; 64 | background-clip: content-box; 65 | border-radius: 1vh; 66 | } 67 | 68 | ::-webkit-scrollbar-corner { 69 | background: transparent; 70 | } 71 | 72 | .row { 73 | margin-top: 10vh; 74 | display: flex; 75 | justify-content: space-between; 76 | max-width: 100%; 77 | } 78 | 79 | .row.mainpage { 80 | position: absolute; 81 | margin-top: 0px; 82 | top: calc(calc(9vh + 2.5em) + 60px); 83 | bottom: 50px; 84 | } 85 | 86 | .hidden { 87 | display: none; 88 | } 89 | 90 | .search { 91 | margin-bottom: 20px; 92 | display: flex; 93 | 94 | 95 | input { 96 | width: 40vh; 97 | height: 7.5vh; 98 | border-radius: 2vh; 99 | font-size: 3vh; 100 | cursor: text; 101 | border-top-right-radius: 0px; 102 | border-bottom-right-radius: 0px; 103 | transition: all 0.5s ease; 104 | margin: 0px; 105 | padding: 0px; 106 | margin-right: 2.5px; 107 | margin-left: calc(50vw - calc(44vh + 45px)); 108 | background-color: var(--background-darker); 109 | } 110 | 111 | input:focus { 112 | width: 50vh; 113 | margin-left: calc(50vw - calc(54vh + 45px)); 114 | } 115 | 116 | input::placeholder { 117 | transition: opacity 0.3s ease; 118 | } 119 | 120 | input:focus::placeholder { 121 | opacity: 0; 122 | } 123 | 124 | select { 125 | height: 7.5vh; 126 | border-radius: 2vh; 127 | font-size: 3vh; 128 | background-color: var(--background-darker); 129 | margin-left: 0px; 130 | border-top-left-radius: 0px; 131 | border-bottom-left-radius: 0px; 132 | margin: 0px; 133 | } 134 | 135 | button { 136 | text-align: center; 137 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 138 | cursor: pointer; 139 | height: 7.5vh; 140 | background-color: var(--background-darker); 141 | color: var(--text); 142 | margin-left: 0px; 143 | border-radius: 0px; 144 | padding: 0px; 145 | margin: 0px; 146 | outline: none; 147 | border: none; 148 | margin-right: 2.5px; 149 | padding-left: 10px; 150 | padding-right: 10px; 151 | } 152 | 153 | button i { 154 | font-size: 3vh; 155 | } 156 | } 157 | 158 | i { 159 | margin: 1vh; 160 | } 161 | 162 | a { 163 | color: var(--text); 164 | text-decoration: none; 165 | } 166 | 167 | a.link { 168 | color: #2d81ff; 169 | } 170 | 171 | h1.title { 172 | text-align: center; 173 | font-size: 8vh; 174 | } 175 | 176 | hr { 177 | border: 0; 178 | height: 0.3vh; 179 | background-image: var(--hr-style); 180 | } 181 | 182 | .small { 183 | font-size: 3vh; 184 | } 185 | 186 | .proxytitle { 187 | margin-bottom: 1vh; 188 | font-size: 5.5vh; 189 | } 190 | 191 | .proxyDescription { 192 | text-align: center; 193 | font-size: 2.75vh; 194 | margin: 3vh; 195 | } 196 | 197 | .proxyinput { 198 | text-align: center; 199 | font-size: 2vh; 200 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 201 | border: none; 202 | border-radius: 2vh; 203 | cursor: pointer; 204 | background: var(--sidebar-bg); 205 | width: 55vw; 206 | height: 6vh; 207 | color: var(--text); 208 | transition: 0.5s; 209 | margin: auto; 210 | margin-top: 2vh; 211 | } 212 | 213 | .proxyinput-index { 214 | text-align: center; 215 | font-size: 2vh; 216 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 217 | border: none; 218 | border-radius: 2vh; 219 | cursor: pointer; 220 | background: var(--sidebar-bg); 221 | width: 20vw; 222 | height: 6vh; 223 | color: var(--text); 224 | transition: 0.5s; 225 | margin: auto; 226 | margin: 2vh; 227 | transition: all 0.5s ease; 228 | } 229 | 230 | .proxyinput-index:focus { 231 | width: 30vw; 232 | } 233 | 234 | button { 235 | text-align: center; 236 | border: none; 237 | border-radius: 1.5vh; 238 | cursor: pointer; 239 | background-color: var(--button-bg); 240 | color: var(--text); 241 | transition: 0.5s; 242 | font-size: 2.3vh; 243 | font-family: 'Lato'; 244 | padding: 5px; 245 | padding-left: 10px; 246 | padding-right: 10px; 247 | } 248 | 249 | input { 250 | text-align: center; 251 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 252 | border: none; 253 | border-radius: 100px; 254 | cursor: pointer; 255 | background: var(--sidebar-bg); 256 | width: 25vh; 257 | height: 4vh; 258 | color: var(--text); 259 | transition: 0.5s; 260 | margin: auto; 261 | margin-top: 2vh; 262 | transition: border-color 0.4s linear; 263 | } 264 | 265 | input:hover{ 266 | border-color: var(--nav-hover); 267 | } 268 | 269 | input::placeholder { 270 | color: var(--text); 271 | } 272 | 273 | input:not(:read-only) { 274 | outline: none; 275 | } 276 | 277 | .blue { 278 | color: #2d81ff; 279 | display: inline-block; 280 | text-shadow: 0vh 0vh 1.5vh rgb(45, 129, 255); 281 | } 282 | 283 | select { 284 | text-align: center; 285 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 286 | border: none; 287 | border-radius: 1vh; 288 | cursor: pointer; 289 | background-color: var(--button-bg); 290 | min-width: 7vh; 291 | height: 4vh; 292 | color: var(--text); 293 | transition: 0.5s; 294 | margin-left: 1vh; 295 | margin-right: 1vh; 296 | padding-left: 2vh; 297 | padding-right: 2vh; 298 | margin-top: 2vh; 299 | } 300 | 301 | .switch { 302 | position: relative; 303 | display: inline-block; 304 | width: 6vh; 305 | height: 3.4vh; 306 | margin-top: 2vh; 307 | cursor: pointer; 308 | } 309 | 310 | .switch input { 311 | opacity: 0; 312 | width: 6vh; 313 | height: 3.4vh; 314 | position: relative; 315 | z-index: 500; 316 | bottom: 2vh; 317 | cursor: pointer; 318 | } 319 | 320 | .slider { 321 | position: absolute; 322 | cursor: pointer; 323 | top: 0; 324 | left: 0; 325 | right: 0; 326 | bottom: 0; 327 | -webkit-transition: .4s; 328 | transition: .4s; 329 | border-radius: 1vh; 330 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 331 | background-color: var(--switch-color); 332 | } 333 | 334 | .switch>label { 335 | position: relative; 336 | right: 6vh; 337 | font-size: 1.17em; 338 | top: 0.5vh; 339 | } 340 | 341 | .slider:before { 342 | position: absolute; 343 | content: ''; 344 | height: 2.6vh; 345 | width: 2.6vh; 346 | left: 0.4vh; 347 | bottom: 0.4vh; 348 | background-color: white; 349 | -webkit-transition: .4s; 350 | transition: .4s; 351 | border-radius: 1vh; 352 | } 353 | 354 | input:checked+.slider { 355 | background-color: var(--switch-active); 356 | } 357 | 358 | input:focus+.slider { 359 | box-shadow: 0 0 0.1vh var(--switch-color); 360 | } 361 | 362 | input:checked+.slider:before { 363 | -webkit-transform: translateX(2.6vh); 364 | -ms-transform: translateX(2.6vh); 365 | transform: translateX(2.6vh); 366 | } 367 | 368 | .games { 369 | width: 100%; 370 | display: flex; 371 | justify-content: center; 372 | flex-wrap: wrap; 373 | padding-bottom: 3vh; 374 | } 375 | 376 | .popular-games { 377 | width: 100%; 378 | display: flex; 379 | justify-content: center; 380 | flex-wrap: wrap; 381 | padding-bottom: 3vh; 382 | } 383 | 384 | .game { 385 | background: #fff; 386 | min-width: 20vh; 387 | min-height: 20vh; 388 | display: block; 389 | border: none; 390 | cursor: pointer; 391 | transition: all 0.5s ease; 392 | filter: brightness(100%); 393 | overflow: hidden; 394 | position: relative; 395 | box-shadow: 0vh 0.75vh 1.5vh 0vh black; 396 | margin: 1vh; 397 | text-align: left; 398 | border-radius: 1.5vh; 399 | } 400 | 401 | .game.hidden { 402 | display: none; 403 | } 404 | 405 | .game:hover { 406 | filter: brightness(105%); 407 | transform: translateY(-0.2vh); 408 | border-color: #ffffff; 409 | } 410 | 411 | .game>img { 412 | width: 100%; 413 | height: 100%; 414 | object-fit: cover; 415 | position: absolute; 416 | left: 0; 417 | top: 0; 418 | object-position: center; 419 | } 420 | 421 | .game:after { 422 | content: ''; 423 | top: 0; 424 | left: 0; 425 | position: absolute; 426 | width: 100%; 427 | height: 100%; 428 | background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); 429 | } 430 | 431 | .game>h3 { 432 | position: absolute; 433 | bottom: -0.5vh; 434 | left: 2vh; 435 | color: #fff; 436 | font-size: 1.8vh; 437 | z-index: 10; 438 | } 439 | 440 | .searchErr { 441 | margin-bottom: 100px; 442 | } 443 | 444 | .cheats { 445 | width: 100%; 446 | display: flex; 447 | justify-content: center; 448 | flex-wrap: wrap; 449 | padding-bottom: 3vh; 450 | } 451 | 452 | .cheat { 453 | background: #fff; 454 | min-width: 20vh; 455 | min-height: 20vh; 456 | display: block; 457 | border: none; 458 | cursor: pointer; 459 | transition: .4s; 460 | overflow: hidden; 461 | position: relative; 462 | box-shadow: 0vh 0.75vh 1.5vh 0vh black; 463 | margin: 1vh; 464 | text-align: left; 465 | border-radius: 1.5vh; 466 | } 467 | 468 | .cheat:hover { 469 | filter: brightness(95%); 470 | transform: translateY(-0.2vh); 471 | border-color: #ffffff; 472 | } 473 | 474 | .cheat>img { 475 | width: 100%; 476 | height: 100%; 477 | object-fit: cover; 478 | position: absolute; 479 | left: 0; 480 | top: 0; 481 | object-position: center; 482 | } 483 | 484 | .cheat:after { 485 | content: ''; 486 | top: 0; 487 | left: 0; 488 | position: absolute; 489 | width: 100%; 490 | height: 100%; 491 | background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); 492 | } 493 | 494 | .cheat>h3 { 495 | position: absolute; 496 | bottom: -0.5vh; 497 | left: 2vh; 498 | color: #fff; 499 | font-size: 1.8vh; 500 | z-index: 10; 501 | } 502 | 503 | .apps { 504 | width: 100%; 505 | display: flex; 506 | justify-content: center; 507 | flex-wrap: wrap; 508 | padding-bottom: 3vh; 509 | } 510 | 511 | .app { 512 | background: #fff; 513 | min-width: 20vh; 514 | min-height: 20vh; 515 | display: block; 516 | border: none; 517 | cursor: pointer; 518 | transition: .4s; 519 | overflow: hidden; 520 | position: relative; 521 | box-shadow: 0vh 0.75vh 1.5vh 0vh black; 522 | margin: 1vh; 523 | border-radius: 2vh; 524 | } 525 | 526 | .app:hover { 527 | filter: brightness(95%); 528 | transform: translateY(-0.2vh); 529 | border-color: #ffffff; 530 | } 531 | 532 | .app>img { 533 | width: 100%; 534 | height: 100%; 535 | object-fit: cover; 536 | position: absolute; 537 | left: 0; 538 | top: 0; 539 | object-position: center; 540 | } 541 | 542 | .app:after { 543 | content: ''; 544 | top: 0; 545 | left: 0; 546 | position: absolute; 547 | width: 100%; 548 | height: 100%; 549 | background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 100%); 550 | } 551 | 552 | .app>h3 { 553 | position: absolute; 554 | bottom: -0.5vh; 555 | left: 2vh; 556 | color: #fff; 557 | font-size: 1.8vh; 558 | z-index: 10; 559 | } 560 | 561 | .app>span { 562 | position: absolute; 563 | bottom: 2vh; 564 | left: 2vh; 565 | color: #fff; 566 | z-index: 10; 567 | font-size: 1.5vh; 568 | } 569 | 570 | .notifications { 571 | top: 1%; 572 | width: 20%; 573 | position: fixed; 574 | z-index: 99999999999999999; 575 | left: 50%; 576 | transform: translate(-50%, -50%); 577 | transition: .5s; 578 | height: 2%; 579 | } 580 | 581 | .notifications>.notification { 582 | padding: 1.5vh; 583 | border-radius: 2vh; 584 | margin-top: 1vh; 585 | transition: .5s; 586 | cursor: pointer; 587 | background: var(--solid); 588 | } 589 | 590 | .notifications>.notification.error { 591 | background: rgba(171, 61, 222, 0.801); 592 | color: #fff; 593 | transition: .5s; 594 | inline-size: 100%; 595 | overflow-wrap: break-word; 596 | } 597 | 598 | .box { 599 | background: var(--background-color); 600 | padding: 5vh; 601 | border-radius: 2vh; 602 | position: absolute; 603 | top: 50%; 604 | left: 50%; 605 | -ms-transform: translate(-50%, -50%); 606 | transform: translate(-50%, -50%); 607 | box-shadow: 0vh 0.5vh 1vh 0vh var(--shadow-color); 608 | } 609 | 610 | .center-content { 611 | text-align: center; 612 | justify-content: center; 613 | margin-top: 5vh; 614 | } 615 | 616 | .container { 617 | width: 100vw; 618 | background-color: var(--background-color); 619 | box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color); 620 | padding: 2vw; 621 | border-radius: 2vh; 622 | color: var(--text); 623 | overflow: hidden; 624 | margin-left: 50px; 625 | text-align: center; 626 | } 627 | 628 | .container.centered { 629 | width: auto; 630 | position: absolute; 631 | top: 50%; 632 | left: 50%; 633 | -ms-transform: translate(-50%, -50%); 634 | transform: translate(-50%, -50%); 635 | } 636 | 637 | .container.left { 638 | margin-right: 25px; 639 | } 640 | 641 | .container.right { 642 | margin-right: 50px; 643 | margin-left: 25px; 644 | } 645 | 646 | h1.nomargin { 647 | margin: 0px; 648 | } 649 | 650 | img.featured { 651 | height: calc(100vh - calc(calc(9vh + 2.5em) + 150px) - calc(9vh + 39px)); 652 | width: 100%; 653 | object-fit: cover; 654 | overflow: hidden; 655 | display: inline; 656 | margin-top: 2vh; 657 | cursor: pointer; 658 | border: solid; 659 | border-width: 0.5vh; 660 | border-radius: 2vh; 661 | border-color: rgba(0, 0, 0, 0); 662 | transition: border 0.5s linear; 663 | outline: none; 664 | } 665 | 666 | img.featured:hover { 667 | border-color: rgba(255, 255, 255, 1); 668 | transition: border 0.5s linear; 669 | } 670 | 671 | .content { 672 | margin-left: 100px; 673 | margin-right: 100px; 674 | } 675 | 676 | #changelog i { 677 | overflow: hidden; 678 | white-space: nowrap; 679 | display: block; 680 | /*Converting percentage into px so text-overflow: ellipsis; will apply*/ 681 | width: calc(100% - 1px + 1px); 682 | text-overflow: ellipsis; 683 | } 684 | 685 | @keyframes beat { 686 | 687 | 0%, 688 | 50%, 689 | 100% { 690 | top: 50%; 691 | left: 50%; 692 | -ms-transform: translate(-50%, -50%) scale(1.25, 1.25) rotate(-5deg); 693 | transform: translate(-50%, -50%) scale(1.25, 1.25) rotate(-5deg); 694 | } 695 | 696 | 30%, 697 | 80% { 698 | top: 50%; 699 | left: 50%; 700 | -ms-transform: translate(-50%, -50%) scale(1, 1) rotate(5deg); 701 | transform: translate(-50%, -50%) scale(1, 1) rotate(5deg); 702 | } 703 | } 704 | 705 | @keyframes shake { 706 | 0% { 707 | transform: translate(1px, 1px) rotate(0deg); 708 | } 709 | 710 | 10% { 711 | transform: translate(-1px, -2px) rotate(-1deg); 712 | } 713 | 714 | 20% { 715 | transform: translate(-3px, 0px) rotate(1deg); 716 | } 717 | 718 | 30% { 719 | transform: translate(3px, 2px) rotate(0deg); 720 | } 721 | 722 | 40% { 723 | transform: translate(1px, -1px) rotate(1deg); 724 | } 725 | 726 | 50% { 727 | transform: translate(-1px, 2px) rotate(-1deg); 728 | } 729 | 730 | 60% { 731 | transform: translate(-3px, 1px) rotate(0deg); 732 | } 733 | 734 | 70% { 735 | transform: translate(3px, 1px) rotate(-1deg); 736 | } 737 | 738 | 80% { 739 | transform: translate(-1px, -1px) rotate(1deg); 740 | } 741 | 742 | 90% { 743 | transform: translate(1px, 2px) rotate(0deg); 744 | } 745 | 746 | 100% { 747 | transform: translate(1px, -2px) rotate(-1deg); 748 | } 749 | } -------------------------------------------------------------------------------- /static/assets/css/nav.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 960px) { 2 | .navbar > .right { 3 | display: none; 4 | } 5 | } 6 | 7 | .navbar { 8 | box-shadow: 0vh 0.5vh 0.75vh 0vh var(--shadow-color); 9 | position: fixed; 10 | width: calc(calc(100% - 100px) - calc(1.5em * 2)); 11 | background-color: var(--background-color); 12 | top: 0; 13 | margin-left: 50px; 14 | margin-right: 50px; 15 | margin-top: 40px; 16 | color: rgb(255, 255, 255); 17 | z-index: 100; 18 | display: flex; 19 | border-radius: 2vh; 20 | padding: 1.5em; 21 | padding-bottom: 1em; 22 | text-align: center; 23 | backdrop-filter: blur(7.1px); 24 | -webkit-backdrop-filter: blur(7.1px); 25 | } 26 | 27 | .navbar.scrolling { 28 | background-color: var(--background-darker); 29 | color: black; 30 | backdrop-filter: blur(10px); 31 | -webkit-backdrop-filter: blur(10px); 32 | } 33 | 34 | .navbar > .title { 35 | margin: 1vh; 36 | } 37 | 38 | .navbar > .title > img { 39 | width: 6vh; 40 | border-radius: 1vh; 41 | } 42 | 43 | .navbar > .title > span { 44 | font-family: "Lato-Black"; 45 | font-size: 3vh; 46 | left: 11vh; 47 | position: absolute; 48 | text-align: left; 49 | } 50 | 51 | .navbar > .title > span > span { 52 | display: flex; 53 | position: relative; 54 | font-size: 1.2vh; 55 | width: auto; 56 | } 57 | 58 | .navbar > .right { 59 | position: absolute; 60 | right: 0; 61 | margin: 2vh; 62 | margin-top: 2.5vh; 63 | margin-right: 3vh; 64 | } 65 | 66 | .navbar > .right > a { 67 | border-style: solid; 68 | border-width: 0.3vh; 69 | margin: 0.3rem; 70 | padding: 0.8rem; 71 | cursor: pointer; 72 | font-size: 2.7vh; 73 | border-radius: 1.5vh; 74 | border-color: var(--button-bg); 75 | transition: border-color 0.4s linear; 76 | background-color: var(--button-bg); 77 | } 78 | 79 | .navbar > .right > a:hover { 80 | border-color: var(--nav-hover); 81 | } 82 | 83 | .navbar > .right > a.active { 84 | border-color: rgba(255, 255, 255, 1); 85 | } 86 | -------------------------------------------------------------------------------- /static/assets/css/sidebar.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | position: fixed; 3 | margin-top: calc(calc(9vh + 2.5em) + 100px); 4 | bottom: 50px; 5 | top: 0; 6 | right: -60vh; 7 | width: 50vh; 8 | background-color: var(--background-darker); 9 | border-radius: 2vh; 10 | box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color); 11 | text-align: center; 12 | z-index: 100; 13 | overflow-y: auto; 14 | padding: 1vh; 15 | transition: all 0.5s ease; 16 | backdrop-filter: blur(7.1px); 17 | -webkit-backdrop-filter: blur(7.1px); 18 | } 19 | 20 | .sidebar.active { 21 | right: 50px; 22 | transition: all 0.5s ease; 23 | } 24 | 25 | .sidebar>.scroll { 26 | position: fixed; 27 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 28 | border-radius: 1vh; 29 | cursor: pointer; 30 | background-color: var(--solid); 31 | width: 50vh; 32 | height: 0vh; 33 | transition: 0.4s; 34 | margin: auto; 35 | top: 87vh; 36 | opacity: 0; 37 | z-index: 9999999999999999999; 38 | display: none; 39 | } 40 | 41 | .sidebar>.scroll.active { 42 | opacity: 1; 43 | height: 4vh; 44 | } 45 | 46 | .sidebar>.scroll>span { 47 | font-size: 4vh; 48 | margin: auto; 49 | } 50 | 51 | .sidebar>.settings-button { 52 | margin-left: 2vh; 53 | margin-right: 2vh; 54 | padding-left: 2vh; 55 | padding-right: 2vh; 56 | margin-top: 2vh; 57 | } 58 | 59 | .icon-button { 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | font-size: 3vh; 64 | box-shadow: 0.5vh 0.5vh 1vh 0vh var(--shadow-color); 65 | width: 4.5vh; 66 | height: 4.5vh; 67 | } 68 | 69 | .sidebar h3 { 70 | margin-bottom: 0vh; 71 | } 72 | 73 | .quick-options { 74 | display: flex; 75 | justify-content: space-between; 76 | align-items: center; 77 | margin-left: 10vh; 78 | margin-right: 10vh; 79 | margin-top: 3vh; 80 | margin-bottom: 3vh; 81 | } 82 | 83 | .settings-button { 84 | min-width: 7vh; 85 | margin: 0.5vh; 86 | } -------------------------------------------------------------------------------- /static/assets/css/themes.css: -------------------------------------------------------------------------------- 1 | body[data-theme='dark'] { 2 | --background-color: rgba(17, 22, 31, 0.8); 3 | --background-darker: rgba(0, 0, 0, 0.9); 4 | --text: #fff; 5 | --sidebar-bg: #161f2eef; 6 | --button-bg: #44385e69; 7 | --shadow-color: #000; 8 | --switch-color: #42424259; 9 | --switch-active: #2196F3; 10 | --nav-hover: #2196F3; 11 | --scrollbar-color: #ffffff59; 12 | --solid: #1b2735; 13 | --hover: #757575; 14 | --hr-style: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)); 15 | 16 | background: radial-gradient(at center bottom, rgb(27, 39, 53) 0%, rgb(9, 10, 15) 100%); 17 | background-repeat: no-repeat; 18 | } 19 | 20 | body[data-theme='light'] { 21 | --background-color: #fff; 22 | --background-darker: var(--background-color); 23 | --text: #000; 24 | --sidebar-bg: #e9e9e9ee; 25 | --button-bg: #ecececee; 26 | --shadow-color: #c9c9c9; 27 | --switch-color: #111111b0; 28 | --nav-hover: #9c9c9c; 29 | --switch-active: #2196F3; 30 | --scrollbar-color: #4242424b; 31 | --solid: #ffffffee; 32 | --hover: #757575; 33 | --hr-style: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.75), rgba(255, 255, 255, 0)); 34 | 35 | background: radial-gradient(ellipse at bottom, #b1b1b1 0%, #f6f5f0 100%); 36 | background-repeat: no-repeat; 37 | } 38 | 39 | body[data-theme='flamingo'] { 40 | --background-color: rgba(0, 0, 0, 0.8); 41 | --background-darker: rgba(0, 0, 0, 0.9); 42 | --text: #fff; 43 | --sidebar-bg: #e75166ee; 44 | --button-bg: #00000059; 45 | --shadow-color: #000; 46 | --switch-color: #959595; 47 | --switch-active: #FB5E4C; 48 | --scrollbar-color: rgba(0, 0, 0, 0.8); 49 | --solid: #FB5E4C; 50 | --hover: #ed58b4; 51 | background: linear-gradient(304deg, rgb(255, 136, 0), rgb(245, 7, 226)) 0% 0% / 120% 120%; 52 | animation: 12s ease 0s infinite normal none running flamingo-gradient; 53 | 54 | @keyframes flamingo-gradient { 55 | 0% { 56 | background-position: 0% 50%; 57 | } 58 | 59 | 50% { 60 | background-position: 100% 50%; 61 | } 62 | 63 | 100% { 64 | background-position: 0% 50%; 65 | } 66 | } 67 | } 68 | 69 | body[data-theme='frost'] { 70 | --background-color: rgba(0, 0, 0, 0.8); 71 | --background-darker: rgba(0, 0, 0, 0.9); 72 | --text: #fff; 73 | --sidebar-bg: #293a6bee; 74 | --button-bg: #42424259; 75 | --shadow-color: #000; 76 | --switch-color: #42424259; 77 | --switch-active: #056181; 78 | --scrollbar-color: #ffffff59; 79 | --solid: #056181; 80 | --hover: #6491fa; 81 | background: linear-gradient(304deg, rgb(0, 0, 0), rgb(8, 193, 255)) 0% 0% / 120% 120%; 82 | animation: 12s ease 0s infinite normal none running frost-gradient; 83 | 84 | @keyframes frost-gradient { 85 | 0% { 86 | background-position: 0% 50%; 87 | } 88 | 89 | 50% { 90 | background-position: 100% 50%; 91 | } 92 | 93 | 100% { 94 | background-position: 0% 50%; 95 | } 96 | } 97 | } 98 | 99 | body[data-theme='crimson'] { 100 | --background-color: rgba(0, 0, 0, 0.8); 101 | --background-darker: rgba(0, 0, 0, 0.9); 102 | --text: #fff; 103 | --sidebar-bg: #a74c4cee; 104 | --button-bg: #00000059; 105 | --shadow-color: #000; 106 | --switch-color: #42424259; 107 | --switch-active: #B90F32; 108 | --scrollbar-color: #ffffff59; 109 | --solid: #940C28; 110 | --hover: #fc3f42; 111 | background: linear-gradient(90deg, rgba(255, 106, 111, 1) 0%, rgba(148, 0, 17, 1) 85%); 112 | animation: crimsion-gradient 12s ease infinite; 113 | 114 | @keyframes crimson-gradient { 115 | 0% { 116 | background-position: 0% 50%; 117 | } 118 | 119 | 50% { 120 | background-position: 100% 50%; 121 | } 122 | 123 | 100% { 124 | background-position: 0% 50%; 125 | } 126 | } 127 | } 128 | 129 | body[data-theme='inferno'] { 130 | --background-color: rgba(0, 0, 0, 0.8); 131 | --background-darker: rgba(0, 0, 0, 0.9); 132 | --text: #fff; 133 | --sidebar-bg: #871a13; 134 | --button-bg: #00000059; 135 | --shadow-color: #210000; 136 | --switch-color: #42424259; 137 | --switch-active: #B90F32; 138 | --scrollbar-color: #ffffff59; 139 | --solid: #210200; 140 | --hover: #fc3f42; 141 | background: linear-gradient(90deg, rgba(252, 19, 3, 1) 0%, rgba(33, 2, 0, 1) 85%); 142 | animation: inferno-gradient 12s ease infinite; 143 | 144 | @keyframes inferno-gradient { 145 | 0% { 146 | background-position: 0% 50%; 147 | } 148 | 149 | 50% { 150 | background-position: 100% 50%; 151 | } 152 | 153 | 100% { 154 | background-position: 0% 50%; 155 | } 156 | } 157 | } 158 | 159 | body[data-theme='indigo'] { 160 | --background-color: rgba(0, 0, 0, 0.8); 161 | --background-darker: rgba(0, 0, 0, 0.9); 162 | --text: #fff; 163 | --sidebar-bg: #9247fc; 164 | --button-bg: #482579; 165 | --shadow-color: #2A1B55; 166 | --switch-color: #9759e3; 167 | --switch-active: #bf97f0; 168 | --scrollbar-color: #ffffff59; 169 | --solid: #9b6aeb; 170 | --hover: #b07df0; 171 | background: linear-gradient(90deg, #51305e 0%, #0f084e 85%); 172 | animation: indigo-gradient 12s ease infinite; 173 | 174 | @keyframes indigo-gradient { 175 | 0% { 176 | background-position: 0% 50%; 177 | } 178 | 179 | 50% { 180 | background-position: 100% 50%; 181 | } 182 | 183 | 100% { 184 | background-position: 0% 50%; 185 | } 186 | } 187 | } 188 | 189 | body[data-theme='violet'] { 190 | --background-color: #000000; 191 | --background-darker: rgba(0, 0, 0, 0.9); 192 | --text: #fff; 193 | --sidebar-bg: #000000; 194 | --button-bg: #000000; 195 | --shadow-color: #d100d1; 196 | --switch-color: #660a6b; 197 | --switch-active: #f759ff; 198 | --scrollbar-color: #000000; 199 | --solid: #000000; 200 | --hover: #b07df0; 201 | background: linear-gradient(90deg, rgba(255, 33, 255, 1) 0%, rgba(0, 0, 0, 1) 85%); 202 | animation: violet-gradient 12s ease infinite; 203 | 204 | @keyframes violet-gradient { 205 | 0% { 206 | background-position: 0% 50%; 207 | } 208 | 209 | 50% { 210 | background-position: 100% 50%; 211 | } 212 | 213 | 100% { 214 | background-position: 0% 50%; 215 | } 216 | } 217 | } 218 | 219 | body[data-theme='cyan'] { 220 | --background-color: rgba(0, 0, 0, 0.8); 221 | --background-darker: rgba(0, 0, 0, 0.9); 222 | --text: #fff; 223 | --sidebar-bg: #000000; 224 | --button-bg: #000000; 225 | --shadow-color: #1f4e4c; 226 | --switch-color: #00404b; 227 | --switch-active: #00fff2; 228 | --scrollbar-color: #000000; 229 | --solid: #000000; 230 | --hover: #8fe0dc; 231 | background: linear-gradient(90deg, rgb(94, 255, 247) 0%, rgb(78, 138, 157) 85%); 232 | animation: cyan-gradient 12s ease infinite; 233 | 234 | @keyframes cyan-gradient { 235 | 0% { 236 | background-position: 0% 50%; 237 | } 238 | 239 | 50% { 240 | background-position: 100% 50%; 241 | } 242 | 243 | 100% { 244 | background-position: 0% 50%; 245 | } 246 | } 247 | } 248 | 249 | body[data-theme='bronze'] { 250 | --background-color: rgba(0, 0, 0, 0.8); 251 | --background-darker: rgba(0, 0, 0, 0.9); 252 | --text: #fff; 253 | --sidebar-bg: #000000; 254 | --button-bg: #000000; 255 | --shadow-color: #4c2b04; 256 | --switch-color: #643c00; 257 | --switch-active: #ffa318; 258 | --scrollbar-color: #000000; 259 | --solid: #000000; 260 | --hover: #ffb74a; 261 | background: linear-gradient(90deg, rgb(253, 135, 1) 0%, rgb(58, 32, 3)); 262 | animation: bronze-gradient 12s ease infinite; 263 | 264 | @keyframes bronze-gradient { 265 | 0% { 266 | background-position: 0% 50%; 267 | } 268 | 269 | 50% { 270 | background-position: 100% 50%; 271 | } 272 | 273 | 100% { 274 | background-position: 0% 50%; 275 | } 276 | } 277 | } 278 | 279 | @media screen and (prefers-color-scheme: light) { 280 | body[data-theme='system-default'] { 281 | --background-color: #fff; 282 | --background-darker: var(--background-color); 283 | --text: #000; 284 | --hover: #757575; 285 | --sidebar-bg: #ffffffee; 286 | --button-bg: #ecececee; 287 | --shadow-color: #c9c9c9; 288 | --switch-color: #111111b0; 289 | --switch-active: #2196F3; 290 | --scrollbar-color: #4242424b; 291 | --solid: #ffffffee; 292 | --hover: #757575; 293 | 294 | background: radial-gradient(ellipse at bottom, #b1b1b1 0%, #f6f5f0 100%); 295 | background-repeat: no-repeat; 296 | } 297 | } 298 | 299 | @media screen and (prefers-color-scheme: dark) { 300 | body[data-theme='system-default'] { 301 | --background-color: rgba(0, 0, 0, 0.8); 302 | --background-darker: rgba(0, 0, 0, 0.9); 303 | --text: #fff; 304 | --sidebar-bg: #161f2eef; 305 | --button-bg: #1c1c1c69; 306 | --shadow-color: #000; 307 | --switch-color: #42424259; 308 | --switch-active: #2196F3; 309 | --scrollbar-color: #ffffff59; 310 | --solid: #1b2735; 311 | --hover: #757575; 312 | background: radial-gradient(at center bottom, rgb(27, 39, 53) 0%, rgb(9, 10, 15) 100%); 313 | background-repeat: no-repeat; 314 | } 315 | } -------------------------------------------------------------------------------- /static/assets/css/view.css: -------------------------------------------------------------------------------- 1 | @import url('/assets/css/themes.css'); 2 | 3 | .gamebar { 4 | background: var(--background-darker); 5 | z-index: 9; 6 | position: fixed; 7 | bottom: 0; 8 | left: 0; 9 | right: 0; 10 | margin: 40px; 11 | margin-left: 20vw; 12 | margin-right: 20vw; 13 | border-radius: 2vh; 14 | padding: 10px; 15 | display: flex; 16 | box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color); 17 | backdrop-filter: blur(7.1px); 18 | -webkit-backdrop-filter: blur(7.1px); 19 | } 20 | 21 | .gamebar.collapsed { 22 | margin-bottom: calc(-6vh + calc(-40px / 2)); 23 | padding-top: 20px; 24 | } 25 | 26 | .gamebar .logo { 27 | width: 6vh; 28 | border-radius: 1.5vh; 29 | display: inline-flex; 30 | } 31 | 32 | .gamebar .title { 33 | position: absolute; 34 | text-align: center; 35 | font-size: 40px; 36 | width: calc(100% - 20px); 37 | margin-top: calc(calc(calc(6vh - 10px) - 40px) / 2); 38 | } 39 | 40 | .gamebar .right { 41 | position: absolute; 42 | margin-top: calc(calc(calc(6vh - 10px) - 40px) / 2); 43 | right: 10px; 44 | } 45 | 46 | .gamebar .item { 47 | background: var(--shadow-color); 48 | cursor: pointer; 49 | box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color); 50 | font-size: 35px; 51 | border-radius: 1vh; 52 | padding: 5px; 53 | } 54 | 55 | .gamebar .item:not(:last-of-type) { 56 | margin-right: 10px; 57 | } 58 | 59 | .hitbox { 60 | display: none; 61 | position: fixed; 62 | bottom: 0; 63 | left: 0; 64 | right: 0; 65 | margin-left: 20vw; 66 | margin-right: 20vw; 67 | border-top-right-radius: 2vh; 68 | border-top-left-radius: 2vh; 69 | height: calc(6vh + 40px); 70 | } 71 | 72 | .hitbox.active { 73 | display: block; 74 | } 75 | 76 | iframe { 77 | position: absolute; 78 | width: calc(100vw - 50px); 79 | height: calc(100vh - 50px); 80 | position: absolute; 81 | top: 50%; 82 | left: 50%; 83 | -ms-transform: translate(-50%, -50%); 84 | transform: translate(-50%, -50%); 85 | border-radius: 2.5vh; 86 | box-shadow: 0vh 0.75vh 1.5vh 0vh var(--shadow-color); 87 | } -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Black.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Black.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-BlackItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-BlackItalic.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-BlackItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-BlackItalic.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Bold.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Bold.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-BoldItalic.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-BoldItalic.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Hairline.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Hairline.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Hairline.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Hairline.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-HairlineItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-HairlineItalic.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-HairlineItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-HairlineItalic.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Italic.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Italic.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Light.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Light.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-LightItalic.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-LightItalic.woff2 -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Regular.woff -------------------------------------------------------------------------------- /static/assets/fonts/Lato-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/fonts/Lato-Regular.woff2 -------------------------------------------------------------------------------- /static/assets/img/adsterra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/adsterra.png -------------------------------------------------------------------------------- /static/assets/img/hamster.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/hamster.gif -------------------------------------------------------------------------------- /static/assets/img/hamster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/hamster.jpg -------------------------------------------------------------------------------- /static/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/logo.png -------------------------------------------------------------------------------- /static/assets/img/polaris_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/polaris_loading.gif -------------------------------------------------------------------------------- /static/assets/img/rick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/rick.png -------------------------------------------------------------------------------- /static/assets/img/skelly.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/skelly.gif -------------------------------------------------------------------------------- /static/assets/img/smurf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/smurf.jpg -------------------------------------------------------------------------------- /static/assets/img/trollface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/trollface.png -------------------------------------------------------------------------------- /static/assets/img/wide/99balls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/99balls.png -------------------------------------------------------------------------------- /static/assets/img/wide/crossyroad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/crossyroad.webp -------------------------------------------------------------------------------- /static/assets/img/wide/fortnite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/fortnite.jpg -------------------------------------------------------------------------------- /static/assets/img/wide/retrobowl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/retrobowl.png -------------------------------------------------------------------------------- /static/assets/img/wide/run3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/run3.png -------------------------------------------------------------------------------- /static/assets/img/wide/stickman-archero-fight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/stickman-archero-fight.png -------------------------------------------------------------------------------- /static/assets/img/wide/subwaysurfers.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/subwaysurfers.webp -------------------------------------------------------------------------------- /static/assets/img/wide/tinyfishing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/img/wide/tinyfishing.png -------------------------------------------------------------------------------- /static/assets/js/analytics.js: -------------------------------------------------------------------------------- 1 | import { storage } from './utils.js'; 2 | 3 | const umamiSpoof = { 4 | track: () => {}, 5 | identify: () => {} 6 | }; 7 | 8 | export default () => new Promise(async (resolve, reject) => { 9 | const analyticsPreferences = storage('analytics'); 10 | 11 | if (analyticsPreferences.get('enabled') !== false) { 12 | var analyticsData; 13 | 14 | if (!analyticsPreferences.get('savedResponse')) { 15 | try { 16 | analyticsData = await (await fetch('/api/analytics/site/' + location.hostname)).json(); 17 | analyticsPreferences.set('savedResponse', analyticsData); 18 | } catch (e) { analyticsPreferences.set('enabled', false); resolve({}); window.umami = window.umami || umamiSpoof; } 19 | } else analyticsData = analyticsPreferences.get('savedResponse'); 20 | 21 | if (analyticsData.success && analyticsData.data.domain === location.hostname) { 22 | analyticsPreferences.set('enabled', true); 23 | 24 | const script = document.createElement('script'); 25 | script.src = '/api/analytics/script.js'; 26 | script.setAttribute('data-website-id', analyticsData.data.id); 27 | script.setAttribute('data-host-url', location.origin + '/api/analytics'); 28 | script.setAttribute('data-cache', 'true'); 29 | document.head.appendChild(script); 30 | 31 | script.onload = () => { 32 | window.umami = window.umami || umamiSpoof; 33 | resolve(window.umami); 34 | } 35 | } else { 36 | analyticsPreferences.set('enabled', false); 37 | resolve({}); 38 | window.umami = window.umami || umamiSpoof; 39 | } 40 | } else { 41 | resolve({}); 42 | window.umami = window.umami || umamiSpoof; 43 | } 44 | }); -------------------------------------------------------------------------------- /static/assets/js/apps.js: -------------------------------------------------------------------------------- 1 | import { createViewPage, isValidURL, PolarisError } from './utils.js'; 2 | import effects from './effects.js'; 3 | 4 | const load = () => fetch('/api/apps').then(res => res.json()).then(apps => { 5 | apps.forEach(app => { 6 | const el = document.createElement('div'); 7 | el.classList = 'app'; 8 | document.querySelector('.apps').appendChild(el); 9 | 10 | const image = document.createElement('img'); 11 | image.src = app.image; 12 | image.onerror = () => image.src = '/assets/img/logo.png'; 13 | el.appendChild(image); 14 | 15 | const name = document.createElement('h3'); 16 | name.textContent = app.name; 17 | el.appendChild(name); 18 | 19 | effects.hoverTilt({ 20 | max: 8, 21 | perspective: 1000, 22 | scale: 1.05, 23 | speed: 800, 24 | easing: 'cubic-bezier(.03,.98,.52,.99)' 25 | }, el); 26 | 27 | el.addEventListener('click', async () => { 28 | document.body.style.opacity = '0.7'; 29 | 30 | setTimeout(() => { 31 | if (isValidURL(app.target)) createViewPage({ 32 | target: app.target, 33 | title: app.name, 34 | proxied: true 35 | }); 36 | else createViewPage({ 37 | target: app.target, 38 | title: app.name 39 | }); 40 | }, 1000); 41 | }); 42 | }); 43 | }).catch(e => new PolarisError('Failed to load Apps')); 44 | 45 | export default { 46 | load 47 | }; -------------------------------------------------------------------------------- /static/assets/js/cdn.inject.js: -------------------------------------------------------------------------------- 1 | if (window.self === window.top) { 2 | document.body.style.display = 'none'; 3 | 4 | fetch('/404') 5 | .then(res => res.text()) 6 | .then(data => { 7 | document.documentElement.innerHTML = data; 8 | document.body.style.display = 'none'; 9 | 10 | try { document.body.dataset.theme = JSON.parse(localStorage.getItem('settings')).theme || 'system-default'; } 11 | catch { 12 | document.body.dataset.theme = 'system-default'; 13 | sessionStorage.setItem('settings', JSON.stringify({ 14 | theme: 'system-default' 15 | })); 16 | } 17 | 18 | setTimeout(() => document.body.style.display = 'block', 400); 19 | }); 20 | } -------------------------------------------------------------------------------- /static/assets/js/changelog.js: -------------------------------------------------------------------------------- 1 | import { PolarisError } from './utils.js'; 2 | 3 | fetch('/api/changelog') 4 | .then(res => res.json()) 5 | .then(changelog => { 6 | document.querySelector('#changelog_version').textContent = changelog.version !== 'unknown' ? 'v' + changelog.version : changelog.version; 7 | document.querySelector('#changelog_version_sha').textContent = changelog.commit.sha.slice(0, 7); 8 | document.querySelector('#changelog_up_to_date').textContent = changelog.upToDate ? 'yes' : 'no'; 9 | document.querySelector('#changelog_mode').textContent = changelog.mode; 10 | 11 | changelog.changelog 12 | .forEach(change => { 13 | const log = document.createElement('div'); 14 | document.querySelector('#changelog').appendChild(log); 15 | 16 | const date = document.createElement('p'); 17 | date.textContent = change.date; 18 | date.classList = 'small'; 19 | log.appendChild(date); 20 | 21 | const description = document.createElement('i'); 22 | description.textContent = change.simpleDescription; 23 | description.classList = 'small'; 24 | log.appendChild(description); 25 | }); 26 | }) 27 | .catch(() => new PolarisError('Failed to load changelog')); -------------------------------------------------------------------------------- /static/assets/js/cheats.js: -------------------------------------------------------------------------------- 1 | import { createViewPage, isValidURL, PolarisError } from './utils.js'; 2 | import effects from './effects.js'; 3 | 4 | const load = () => fetch('/api/cheats') 5 | .then(res => res.json()) 6 | .then(cheats => cheats.forEach(cheat => { 7 | const el = document.createElement('div'); 8 | el.classList = 'game'; 9 | document.querySelector('.cheats').appendChild(el); 10 | 11 | const image = document.createElement('img'); 12 | image.src = cheat.image; 13 | image.onerror = () => image.src = '/assets/img/logo.png'; 14 | el.appendChild(image); 15 | 16 | const name = document.createElement('h3'); 17 | name.textContent = cheat.name; 18 | el.appendChild(name); 19 | 20 | effects.hoverTilt({ 21 | max: 8, 22 | perspective: 1000, 23 | scale: 1.05, 24 | speed: 800, 25 | easing: 'cubic-bezier(.03,.98,.52,.99)' 26 | }, el); 27 | 28 | el.addEventListener('click', () => { 29 | document.body.style.opacity = '0.7'; 30 | 31 | setTimeout(() => { 32 | if (isValidURL(cheat.target)) createViewPage({ 33 | target: cheat.target, 34 | title: cheat.name, 35 | proxied: true 36 | }); 37 | else createViewPage({ 38 | target: cheat.target, 39 | title: cheat.name 40 | }); 41 | }, 1000); 42 | }); 43 | })).catch(e => new PolarisError('Failed to load cheats.')); 44 | 45 | export default { 46 | load 47 | }; -------------------------------------------------------------------------------- /static/assets/js/eastereggs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef binding 3 | * @type {object} 4 | * @property {() => {}} binding.remove 5 | */ 6 | 7 | /** 8 | * @typedef easterEgg 9 | * @type {object} 10 | * @property {'keybind' | 'date'} easterEgg.type 11 | * @property {string} easterEgg.phrase 12 | * @property {string} easterEgg.date 13 | * @property {number} easterEgg.clickCount 14 | * @property {object} easterEgg.element 15 | * @property {object} easterEgg.variables 16 | * @property {(binding: binding, variables: ) => Promise} easterEgg.run 17 | * @property {() => {}} easterEgg.preload 18 | */ 19 | 20 | // REALLY? You're gonna spoil all the easter eggs for yourself? Well, I guess I can't stop you... 21 | 22 | const utils = { 23 | easterEggActive: false, 24 | /** 25 | * @param {string} string 26 | * @param {easterEgg['run']} script 27 | */ 28 | createKeybind: (string, script) => { 29 | let keybindString = ''; 30 | 31 | const listener = window.addEventListener('keydown', async (e) => { 32 | const chars = string.split(''); 33 | 34 | if (chars.includes(e.key)) { 35 | if (e.key === string.charAt(string.length)) keybindString = string.charAt(string.length); 36 | else keybindString += e.key; 37 | 38 | if (keybindString === string && !utils.easterEggActive) { 39 | utils.easterEggActive = true; 40 | 41 | try { 42 | await script({ 43 | remove: () => window.removeEventListener('keydown', listener, true) 44 | }); 45 | 46 | utils.easterEggActive = false; 47 | } catch (e) { 48 | utils.easterEggActive = false; 49 | } 50 | 51 | keybindString = ''; 52 | } 53 | } else keybindString = ''; 54 | }); 55 | 56 | return { 57 | remove: () => window.removeEventListener(listener) 58 | }; 59 | }, 60 | /** 61 | * @param {string} date 62 | * @param {easterEgg['run']} script 63 | */ 64 | createDate: async (date, script) => { 65 | date = date.split('/'); 66 | 67 | if (date.length === 3) { 68 | const day = date[1] === '*' ? new Date().getDate() : date[1]; 69 | const month = date[0] === '*' ? new Date().getMonth() : date[0]; 70 | const year = date[2] === '*' ? new Date().getFullYear() : date[2]; 71 | 72 | if (!utils.easterEggActive && (new Date().getDate() === day && new Date().getMonth() === month && new Date().getFullYear() === year)) { 73 | try { 74 | await script({ 75 | remove: () => window.removeEventListener('keydown', listener, true) 76 | }); 77 | 78 | utils.easterEggActive = false; 79 | } catch (e) { 80 | utils.easterEggActive = false; 81 | } 82 | } 83 | } else throw new Error('Invalid date'); 84 | } 85 | }; 86 | 87 | /** 88 | * @type {Array.} 89 | */ 90 | const easterEggs = []; 91 | 92 | easterEggs.push({ 93 | type: 'keybind', 94 | phrase: 'smurf', 95 | run: () => { 96 | return new Promise((resolve, reject) => { 97 | const audio = new Audio('/assets/misc/media/smurf.mp3'); 98 | audio.play(); 99 | 100 | audio.onplay = () => { 101 | const imageElement = document.createElement('img'); 102 | imageElement.src = '/assets/img/smurf.jpg'; 103 | imageElement.style = `position: fixed; 104 | top: 50%; 105 | left: 50%; 106 | -ms-transform: translate(-50%, -50%); 107 | transform: translate(-50%, -50%); 108 | z-index: 2147483647; 109 | transition: 0.5s;`; 110 | document.body.appendChild(imageElement); 111 | 112 | const overlay = document.createElement('div'); 113 | overlay.style = `position: fixed; 114 | top: 0; 115 | bottom: 0; 116 | left: 0; 117 | right: 0; 118 | background: black; 119 | z-index: 2147483646;`; 120 | document.body.appendChild(overlay); 121 | 122 | var flashInterval; 123 | 124 | setTimeout(() => { 125 | for (let i = 0; i < 360 * 3; i++) setTimeout(() => imageElement.style.filter = `hue-rotate(${i > 360 ? i - 360 * Math.trunc(i / 360) : i}deg)`, 20 * i); 126 | imageElement.style.animation = '1.06s ease 0s infinite beat'; 127 | 128 | overlay.style.background = 'white'; 129 | flashInterval = setInterval(() => overlay.style.background = overlay.style.background === 'black' ? 'white' : 'black', 460); 130 | }, 7330); 131 | 132 | audio.onended = () => { 133 | imageElement.remove(); 134 | overlay.remove(); 135 | 136 | resolve(); 137 | } 138 | } 139 | }); 140 | } 141 | }); 142 | 143 | easterEggs.push({ 144 | type: 'keybind', 145 | phrase: 'ham', 146 | run: () => new Promise((resolve, reject) => { 147 | const audio = new Audio('/assets/misc/media/ringtone.mp3'); 148 | audio.loop = true; 149 | audio.play(); 150 | 151 | audio.onplay = () => { 152 | const overlay = document.createElement('div'); 153 | overlay.style = `position: fixed; 154 | top: 0; 155 | bottom: 0; 156 | left: 0; 157 | right: 0; 158 | background: #000; 159 | z-index: 2147483645;`; 160 | document.body.appendChild(overlay); 161 | 162 | const menu = document.createElement('div'); 163 | menu.style = `position: fixed; 164 | z-index: 2147483646; 165 | top: 0; 166 | bottom: 0; 167 | left: 50%; 168 | -ms-transform: translate(-50%); 169 | transform: translate(-50%); 170 | width: 35%; 171 | background: rgba(255, 255, 255, 0.1);`; 172 | document.body.appendChild(menu); 173 | 174 | const caller = document.createElement('div'); 175 | caller.innerHTML = ` 184 | 185 | Hamter`; 192 | menu.appendChild(caller); 193 | 194 | const call = document.createElement('div'); 195 | call.innerHTML = ``; 203 | 204 | const buttons = document.createElement('div'); 205 | buttons.style = `position: fixed; 206 | z-index: 2147483647; 207 | bottom: 10%; 208 | left: 50%; 209 | -ms-transform: translate(-50%); 210 | transform: translate(-50%); 211 | width: auto; 212 | height: auto; 213 | display: flex;`; 214 | menu.appendChild(buttons); 215 | 216 | const answer = document.createElement('span'); 217 | answer.style = `width: 5vw; 218 | height: 5vw; 219 | display: block; 220 | background: green; 221 | display: flex; 222 | border-radius: 100%; 223 | cursor: pointer; 224 | margin-right: 6vh;`; 225 | answer.innerHTML = ``; 228 | buttons.appendChild(answer); 229 | 230 | const hangUp = document.createElement('span'); 231 | hangUp.style = `width: 5vw; 232 | height: 5vw; 233 | display: block; 234 | background: red; 235 | display: flex; 236 | border-radius: 100%; 237 | cursor: pointer;`; 238 | hangUp.innerHTML = ``; 241 | buttons.appendChild(hangUp); 242 | 243 | answer.addEventListener('click', () => { 244 | answer.remove(); 245 | audio.pause(); 246 | audio.remove(); 247 | caller.remove(); 248 | menu.appendChild(call); 249 | }); 250 | 251 | hangUp.addEventListener('click', () => { 252 | resolve(); 253 | audio.pause(); 254 | audio.remove(); 255 | menu.remove(); 256 | overlay.remove(); 257 | }); 258 | } 259 | }) 260 | }); 261 | 262 | easterEggs.push({ 263 | type: 'keybind', 264 | phrase: 'polaris', 265 | run: () => new Promise((resolve, reject) => { 266 | document.querySelector('.navbar img').style.animation = 'shake 0.5s'; 267 | 268 | setTimeout(() => { 269 | document.querySelector('.navbar img').style.animation = ''; 270 | resolve(); 271 | }, 500); 272 | }) 273 | }); 274 | 275 | easterEggs.push({ 276 | type: 'keybind', 277 | phrase: 'bruh', 278 | run: () => new Promise((resolve, reject) => { 279 | const audio = new Audio('/assets/misc/media/bruh.mp3'); 280 | audio.play(); 281 | 282 | const trollFace = document.createElement('img'); 283 | trollFace.src = '/assets/img/trollface.png'; 284 | trollFace.style = `position: fixed; 285 | z-index: 2147483647; 286 | top: 50%; 287 | left: 50%; 288 | -ms-transform: translate(-50%, -50%); 289 | transform: translate(-50%, -50%); 290 | height: 0px;`; 291 | 292 | var sizeInterval; 293 | 294 | audio.onplay = () => { 295 | document.body.appendChild(trollFace); 296 | 297 | var counter = 1; 298 | 299 | sizeInterval = setInterval(() => { 300 | trollFace.style.height = `${(100 - counter) * counter}px`; 301 | 302 | counter += 1; 303 | }, 10); 304 | }; 305 | 306 | audio.onended = () => { 307 | clearInterval(sizeInterval); 308 | trollFace.remove(); 309 | resolve(); 310 | }; 311 | }) 312 | }); 313 | 314 | easterEggs.push({ 315 | type: 'keybind', 316 | phrase: 'rick', 317 | run: () => new Promise((resolve, reject) => { 318 | const navbarTitle = document.querySelector('.navbar>a.title'); 319 | const title = navbarTitle.querySelector('span'); 320 | title.innerHTML = 'Rick (spam click the logo)'; 321 | const logo = navbarTitle.querySelector('img'); 322 | logo.src = '/assets/img/rick.png'; 323 | 324 | var audioPlaying = false; 325 | var clickTime = 0; 326 | var clicks = 0; 327 | 328 | const rick = document.createElement('img'); 329 | rick.src = '/assets/img/rick.png'; 330 | rick.style = `position: fixed; 331 | bottom: -60px; 332 | right: -60px; 333 | height: 500px; 334 | display: block; 335 | z-index: -99; 336 | transform: rotate(-30deg);`; 337 | document.body.appendChild(rick); 338 | 339 | navbarTitle.dataset.action = 'no_redirect'; 340 | 341 | const rickClick = navbarTitle.addEventListener('click', (e) => { 342 | e.preventDefault(); 343 | 344 | if ((Date.now() - clickTime) < 500) clicks += 1; 345 | else clicks = 0; 346 | 347 | if (clicks > 2 && !audioPlaying) { 348 | clicks = 0; 349 | clickTime = 0; 350 | 351 | const audio = new Audio('/assets/misc/media/rickroll.mp3'); 352 | audio.play(); 353 | 354 | audio.onplay = () => { 355 | audioPlaying = true; 356 | }; 357 | 358 | audio.onended = () => { 359 | audioPlaying = false; 360 | title.innerHTML = 'Polaris by Skool'; 361 | logo.src = '/assets/img/logo.png'; 362 | 363 | navbarTitle.dataset.action = ''; 364 | navbarTitle.removeEventListener('click', rickClick); 365 | rick.remove(); 366 | audio.remove(); 367 | resolve(); 368 | }; 369 | } else clickTime = Date.now(); 370 | }); 371 | }) 372 | }); 373 | 374 | easterEggs.push({ 375 | type: 'keybind', 376 | phrase: 'skelly', 377 | run: () => new Promise((resolve, reject) => { 378 | const imageElement = document.createElement('img'); 379 | imageElement.src = '/assets/img/skelly.gif'; 380 | imageElement.style = `position: fixed; 381 | top: 50%; 382 | left: 50%; 383 | -ms-transform: translate(-50%, -50%); 384 | transform: translate(-50%, -50%); 385 | z-index: 2147483647; 386 | transition: 0.5s;`; 387 | 388 | const overlay = document.createElement('div'); 389 | overlay.style = `position: fixed; 390 | top: 0; 391 | bottom: 0; 392 | left: 0; 393 | right: 0; 394 | background: black; 395 | z-index: 2147483646;`; 396 | document.body.appendChild(overlay); 397 | 398 | const audio = new Audio('/assets/misc/media/skelly.mp3'); 399 | audio.play(); 400 | 401 | audio.onplay = () => { 402 | document.body.appendChild(imageElement); 403 | 404 | setTimeout(() => { 405 | audio.pause(); 406 | audio.remove(); 407 | imageElement.remove(); 408 | overlay.remove(); 409 | resolve(); 410 | }, 14000); 411 | } 412 | }) 413 | }); 414 | 415 | easterEggs.push({ 416 | type: 'date', 417 | date: '4/1/*', 418 | run: () => { 419 | // April fools =) 420 | } 421 | }); 422 | 423 | export default () => easterEggs.forEach(easterEgg => { 424 | if (easterEgg.type === 'keybind') { 425 | utils.createKeybind(easterEgg.phrase, easterEgg.run); 426 | 427 | try { 428 | easterEgg.preload(); 429 | } catch (e) { } 430 | } else if (easterEgg.type === 'date') { 431 | utils.createDate(easterEgg.date, easterEgg.run); 432 | 433 | try { 434 | easterEgg.preload(); 435 | } catch (e) { } 436 | } 437 | }); -------------------------------------------------------------------------------- /static/assets/js/effects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a tilt effect based on the mouse position when it is hovered over 3 | * @param {{ max: number, perspective: number, scale: number, speed: number, easing: 'cubic-bezier(.03,.98,.52,.99)' }} settings 4 | * @param {HTMLDivElement} element 5 | */ 6 | const hoverTilt = (settings, element) => { 7 | const defaultsettings = { 8 | max: 8, 9 | perspective: 1000, 10 | scale: 1.05, 11 | speed: 800, 12 | easing: 'cubic-bezier(.03,.98,.52,.99)' 13 | }; 14 | 15 | settings = { 16 | ...defaultsettings, 17 | ...settings 18 | }; 19 | 20 | const setTransition = (e) => { 21 | const element = e.currentTarget; 22 | clearTimeout(element.transitionTimeoutId); 23 | element.style.transition = `transform ${settings.speed}ms ${settings.easing}`; 24 | 25 | element.transitionTimeoutId = setTimeout(() => element.style.transition = '', settings.speed); 26 | }; 27 | 28 | const listeners = []; 29 | 30 | var stopped = false; 31 | 32 | const eventHandlers = { 33 | mouseEnter: (e) => { 34 | if (!stopped) setTransition(e) 35 | }, 36 | mouseMove: (e) => { 37 | if (!stopped) { 38 | const element = e.currentTarget; 39 | const gameWidth = element.offsetWidth; 40 | const gameHeight = element.offsetHeight; 41 | const centerX = element.offsetLeft + gameWidth / 2; 42 | const centerY = element.offsetTop + gameHeight / 2; 43 | const mouseX = e.clientX - centerX; 44 | const mouseY = e.clientY - centerY; 45 | const rotateXUncapped = (+1) * settings.max * mouseY / (gameHeight / 2); 46 | const rotateYUncapped = (-1) * settings.max * mouseX / (gameWidth / 2); 47 | const rotateX = rotateXUncapped < -settings.max ? -settings.max : (rotateXUncapped > settings.max ? settings.max : rotateXUncapped); 48 | const rotateY = rotateYUncapped < -settings.max ? -settings.max : (rotateYUncapped > settings.max ? settings.max : rotateYUncapped); 49 | 50 | element.style.transform = `perspective(${settings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${settings.scale}, ${settings.scale}, ${settings.scale})`; 51 | } 52 | }, 53 | mouseLeave: (e) => { 54 | if (!stopped) { 55 | e.currentTarget.style.transform = `perspective(${settings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`; 56 | 57 | setTransition(e); 58 | } 59 | } 60 | }; 61 | 62 | if (element) { 63 | listeners.push(element.addEventListener('mouseenter', eventHandlers.mouseEnter)); 64 | listeners.push(element.addEventListener('mousemove', eventHandlers.mouseMove)); 65 | listeners.push(element.addEventListener('mouseleave', eventHandlers.mouseLeave)); 66 | } 67 | 68 | return { 69 | events: eventHandlers, 70 | remove: () => { 71 | stopped = true; 72 | 73 | listeners.forEach(listener => listener.remove()); 74 | } 75 | }; 76 | }; 77 | 78 | export default { hoverTilt }; 79 | export { hoverTilt }; -------------------------------------------------------------------------------- /static/assets/js/games.js: -------------------------------------------------------------------------------- 1 | import { createViewPage, isValidURL, PolarisError, storage } from './utils.js'; 2 | import effects from './effects.js'; 3 | 4 | const settingsStorage = storage('settings'); 5 | 6 | const load = () => { 7 | const sortListener = document.querySelector('#searchSort').addEventListener('change', () => { 8 | settingsStorage.set('game_sort', document.querySelector('#searchSort').value); 9 | 10 | const games = document.querySelectorAll('.games>.game'); 11 | 12 | for (let i = 0; i < games.length; i++) games[i].remove(); 13 | 14 | fetch('/api/games') 15 | .then(res => res.json()) 16 | .then(games => { 17 | if (settingsStorage.get('game_sort') === 'abc') games.all.sort((a, b) => a.name.localeCompare(b.name)); 18 | if (settingsStorage.get('game_sort') === 'newest') games.all.reverse(); 19 | 20 | games.all.forEach(game => { 21 | const el = document.createElement('div'); 22 | el.classList = 'game'; 23 | document.querySelector('.games').appendChild(el); 24 | 25 | const image = document.createElement('img'); 26 | image.src = game.image; 27 | image.loading = 'lazy'; 28 | image.onerror = () => image.src = '/assets/img/logo.png'; 29 | el.appendChild(image); 30 | 31 | const name = document.createElement('h3'); 32 | name.textContent = game.name; 33 | el.appendChild(name); 34 | 35 | effects.hoverTilt({ 36 | max: 8, 37 | perspective: 1000, 38 | scale: 1.05, 39 | speed: 800, 40 | easing: 'cubic-bezier(.03,.98,.52,.99)' 41 | }, el); 42 | 43 | el.addEventListener('click', async () => { 44 | document.body.style.opacity = '0.7'; 45 | 46 | setTimeout(() => { 47 | if (isValidURL(game.target)) createViewPage({ 48 | target: game.target, 49 | title: game.name, 50 | proxied: true 51 | }); 52 | else createViewPage({ 53 | target: game.target, 54 | title: game.name 55 | }); 56 | }, 1000); 57 | }); 58 | }); 59 | }); 60 | }); 61 | 62 | if (!settingsStorage.get('game_sort')) settingsStorage.set('game_sort', 'none'); 63 | 64 | document.querySelector('#searchSort').value = settingsStorage.get('game_sort'); 65 | 66 | fetch('/api/games') 67 | .then(res => res.json()) 68 | .then(games => { 69 | if (settingsStorage.get('game_sort') === 'abc') games.all.sort((a, b) => a.name.localeCompare(b.name)); 70 | if (settingsStorage.get('game_sort') === 'newest') games.all.reverse(); 71 | 72 | const searchBar = document.querySelector('#searchInput'); 73 | 74 | searchBar.setAttribute('placeholder', `Search ${games.all.length} Games`); 75 | 76 | searchBar.addEventListener('input', () => { 77 | if (searchBar.value) { 78 | var result = false; 79 | 80 | document.querySelectorAll('.games>.game').forEach(game => { 81 | if (game.querySelector('h3').textContent.toLowerCase().includes(searchBar.value.toLowerCase())) { 82 | result = true; 83 | 84 | game.classList.remove('hidden'); 85 | } 86 | else game.classList.add('hidden'); 87 | }); 88 | 89 | if (result) document.querySelector('.searchErr').classList.add('hidden'); 90 | else document.querySelector('.searchErr').classList.remove('hidden'); 91 | } else { 92 | document.querySelectorAll('.game').forEach(game => game.classList.remove('hidden')); 93 | document.querySelector('.searchErr').classList.add('hidden'); 94 | } 95 | }); 96 | 97 | games.popular.forEach(game => { 98 | const popularEl = document.createElement('div'); 99 | popularEl.classList = 'game'; 100 | document.querySelector('.popular-games').appendChild(popularEl); 101 | 102 | const image = document.createElement('img'); 103 | image.src = game.image; 104 | image.onerror = () => image.src = '/assets/img/logo.png'; 105 | popularEl.appendChild(image); 106 | 107 | const name = document.createElement('h3'); 108 | name.textContent = game.name; 109 | popularEl.appendChild(name); 110 | 111 | popularEl.addEventListener('click', async () => { 112 | document.body.style.opacity = '0.7'; 113 | 114 | setTimeout(() => { 115 | if (isValidURL(game.target)) createViewPage({ 116 | target: game.target, 117 | title: game.name, 118 | image: game.image, 119 | proxied: true 120 | }); 121 | else createViewPage({ 122 | target: game.target, 123 | title: game.name, 124 | image: game.image 125 | }); 126 | }, 500); 127 | }); 128 | 129 | effects.hoverTilt({ 130 | max: 8, 131 | perspective: 1000, 132 | scale: 1.05, 133 | speed: 800, 134 | easing: 'cubic-bezier(.03,.98,.52,.99)' 135 | }, popularEl); 136 | }); 137 | 138 | games.all.forEach(game => { 139 | const el = document.createElement('div'); 140 | el.classList = 'game'; 141 | document.querySelector('.games').appendChild(el); 142 | 143 | const image = document.createElement('img'); 144 | image.src = game.image; 145 | image.onerror = () => image.src = '/assets/img/logo.png'; 146 | el.appendChild(image); 147 | 148 | const name = document.createElement('h3'); 149 | name.textContent = game.name; 150 | el.appendChild(name); 151 | 152 | effects.hoverTilt({ 153 | max: 8, 154 | perspective: 1000, 155 | scale: 1.05, 156 | speed: 800, 157 | easing: 'cubic-bezier(.03,.98,.52,.99)' 158 | }, el); 159 | 160 | el.addEventListener('click', async () => { 161 | document.body.style.opacity = '0.7'; 162 | 163 | setTimeout(() => { 164 | if (isValidURL(game.target)) createViewPage({ 165 | target: game.target, 166 | title: game.name, 167 | proxied: true 168 | }); 169 | else createViewPage({ 170 | target: game.target, 171 | title: game.name 172 | }); 173 | }, 1000); 174 | }); 175 | }); 176 | }) 177 | .catch(e => new PolarisError('Failed to load games')); 178 | 179 | document.querySelector('#randomGame').addEventListener('click', () => { 180 | const games = document.querySelectorAll('.games>.game'); 181 | 182 | if (games.length > 0) { 183 | const randomGame = games[Math.floor(Math.random() * games.length)]; 184 | 185 | randomGame.click(); 186 | } 187 | }); 188 | 189 | }; 190 | const loadGameFromURL = () => { 191 | const urlParams = new URLSearchParams(window.location.search); 192 | const gameName = urlParams.get('game'); 193 | 194 | if (!gameName) { 195 | throw new PolarisError('No game specified in the URL'); 196 | } 197 | 198 | fetch('/assets/JSON/games.json') 199 | .then(res => res.json()) 200 | .then(data => { 201 | const game = data.find(g => g.name === decodeURIComponent(gameName)); 202 | 203 | if (!game) { 204 | throw new PolarisError(`Game "${gameName}" not found`); 205 | } 206 | 207 | renderGames([game]); 208 | }) 209 | .catch(e => new PolarisError('Failed to load game')); 210 | }; 211 | 212 | export default { 213 | load, 214 | loadGameFromURL 215 | }; -------------------------------------------------------------------------------- /static/assets/js/main.js: -------------------------------------------------------------------------------- 1 | import { createViewPage, isValidURL, getVH, PolarisError, storage } from './utils.js'; 2 | import { loadSettings, loadSidebarInterface } from './settings.js'; 3 | import loadEasterEggs from './eastereggs.js'; 4 | import loadAnalytics from './analytics.js'; 5 | import Search from './search.js'; 6 | import Cheats from './cheats.js'; 7 | import Games from './games.js'; 8 | import Apps from './apps.js'; 9 | 10 | loadAnalytics(); 11 | 12 | if (location.pathname !== '/view') loadSidebarInterface(); 13 | loadEasterEggs(); 14 | 15 | /*const ctcClient = new CrossTabCommunication(); 16 | 17 | ctcClient.on('open', (connection) => { 18 | connection.on('message', (message) => { 19 | console.log(message); 20 | }); 21 | }); 22 | 23 | /*setInterval(() => { 24 | ctcClient.brodcast('hello from ' + location.href); 25 | }, 1000);*/ 26 | 27 | const settingsStorage = storage('settings'); 28 | var preventClose = false; 29 | 30 | window.addEventListener('beforeunload', (e) => sessionStorage.setItem('was_closing', 'true')); 31 | 32 | window.addEventListener('beforeunload', (e) => { 33 | document.body.style.opacity = '0.7'; 34 | 35 | if (settingsStorage.get('prevent_close')) { 36 | e.preventDefault(); 37 | 38 | document.body.style.opacity = '1'; 39 | 40 | return e; 41 | } 42 | }); 43 | 44 | /*await navigator.serviceWorker.register('/assets/js/offline.js', { 45 | scope: '/' 46 | });*/ 47 | 48 | window.addEventListener('load', () => setTimeout(() => document.body.style.opacity = 1, 1000)); 49 | 50 | setTimeout(() => document.body.style.opacity = 1, 5000); 51 | 52 | /** 53 | * @param {HTMLAnchorElement} hyperlink 54 | */ 55 | const hyperlinkHandler = (hyperlink, e) => { 56 | if (hyperlink.dataset.action === 'no_redirect') e.preventDefault(); 57 | else if (hyperlink.href && hyperlink.target !== '_blank') { 58 | e.preventDefault(); 59 | 60 | document.body.style.opacity = '0.7'; 61 | 62 | if (new URL(hyperlink.href).pathname === location.pathname) setTimeout(() => document.body.style.opacity = '', 1000); 63 | else { 64 | /*setTimeout(async () => { 65 | const style = document.createElement('style'); 66 | style.textContent = ` 67 | * { 68 | transition: none; 69 | }`; 70 | 71 | document.body.querySelectorAll('*').forEach(el => { 72 | el.style.transition = 'none'; 73 | el.style.display = 'none'; 74 | }); 75 | 76 | if (new URL(hyperlink.href).host === location.host) { 77 | const page = new DOMParser().parseFromString(await (await fetch(hyperlink.href)).text(), 'text/html'); 78 | document.head.innerHTML = page.head.innerHTML; 79 | document.head.appendChild(style); 80 | 81 | window.history.pushState({}, '', hyperlink.href); 82 | 83 | const scripts = page.body.querySelectorAll('script'); 84 | 85 | page.body.querySelectorAll('script').forEach(script => script.remove()); 86 | 87 | document.body.innerHTML = page.body.innerHTML; 88 | 89 | document.body.style.display = 'none'; 90 | 91 | document.querySelectorAll('a').forEach(hyperlink => hyperlink.addEventListener('click', (e) => hyperlinkHandler(hyperlink, e))); 92 | 93 | setTimeout(() => document.body.style.display = '', 100); 94 | 95 | setTimeout(() => { 96 | style.remove(); 97 | }, 500); 98 | } else setTimeout(() => window.location.href = hyperlink.href, 500); 99 | }, 500);//*/ 100 | 101 | setTimeout(() => window.location.href = hyperlink.href, 500); 102 | } 103 | } 104 | }; 105 | 106 | document.querySelectorAll('a').forEach(hyperlink => hyperlink.addEventListener('click', (e) => hyperlinkHandler(hyperlink, e))); 107 | 108 | window.addEventListener('hashchange', () => { 109 | if (location.hash === '#settings') document.querySelector('.sidebar').classList.add('active'); 110 | else document.querySelector('.sidebar').classList.remove('active'); 111 | }); 112 | 113 | if (window.self === window.top && location.pathname !== '/view') setTimeout(async () => { 114 | loadSettings(); 115 | 116 | if (location.pathname === '/games') Games.load(); 117 | if (location.pathname === '/apps') Apps.load(); 118 | if (location.pathname === '/search') Search.load(); 119 | if (location.pathname === '/cheats') Cheats.load(); 120 | }, 500); 121 | 122 | if (location.pathname !== '/view') fetch('/api/changelog') 123 | .then(res => res.json()) 124 | .then(changelog => { 125 | document.querySelector('#version').textContent = changelog.version !== 'unknown' ? 'v' + changelog.version : changelog.version; 126 | document.querySelector('#version_sha').textContent = changelog.commit.sha.slice(0, 7); 127 | document.querySelector('#up_to_date').textContent = changelog.upToDate ? 'yes' : 'no'; 128 | document.querySelector('#mode').textContent = changelog.mode; 129 | }); 130 | 131 | if (location.pathname === '/') { 132 | fetch('/api/games') 133 | .then(res => res.json()) 134 | .then(games => { 135 | const gameName = 'Stickman Archero Fight'; 136 | const game = games.all.filter(g => g.name === gameName)[0]; 137 | 138 | document.querySelector('.featured').addEventListener('click', () => { 139 | document.body.style.opacity = '0.7'; 140 | 141 | setTimeout(() => { 142 | if (isValidURL(game.target)) createViewPage({ 143 | target: game.target, 144 | title: game.name, 145 | proxied: true 146 | }); 147 | else createViewPage({ 148 | target: game.target, 149 | title: game.name 150 | }); 151 | }, 1000); 152 | }); 153 | 154 | document.querySelector('.featured').src = '/assets/img/wide/stickman-archero-fight.png'; 155 | }).catch(e => new PolarisError('Failed to load featured game.')); 156 | 157 | const logHeight = () => { 158 | const log = document.createElement('div'); 159 | document.querySelector('#changelog').appendChild(log); 160 | 161 | const date = document.createElement('p'); 162 | date.textContent = 'a'; 163 | date.classList = 'small'; 164 | log.appendChild(date); 165 | 166 | const description = document.createElement('i'); 167 | description.textContent = 'a'; 168 | description.classList = 'small'; 169 | log.appendChild(description); 170 | 171 | const height = log.clientHeight; 172 | 173 | log.remove(); 174 | 175 | return height; 176 | }; 177 | 178 | const getAvalibleHeight = () => { 179 | var total = 0; 180 | 181 | document.querySelectorAll('.container.right>*:not(#changelog)').forEach(el => total += Number((el.currentStyle || window.getComputedStyle(el)).marginTop.r1lace('px', '')) + Number((el.currentStyle || window.getComputedStyle(el)).marginTop.replace('px', '')) + el.clientHeight); 182 | 183 | return (document.querySelector('.container.right').clientHeight - getVH(2)) - total; 184 | } 185 | 186 | fetch('/api/changelog') 187 | .then(res => res.json()) 188 | .then(changelog => { 189 | changelog.changelog 190 | .filter((data, i) => !(i >= 3)) 191 | .forEach(change => { 192 | const log = document.createElement('div'); 193 | document.querySelector('#changelog').appendChild(log); 194 | 195 | const date = document.createElement('p'); 196 | date.textContent = change.date; 197 | date.classList = 'small'; 198 | log.appendChild(date); 199 | 200 | const description = document.createElement('i'); 201 | description.textContent = change.simpleDescription; 202 | description.classList = 'small'; 203 | log.appendChild(description); 204 | }); 205 | 206 | const resizeChangelog = (amount = 3) => { 207 | amount = amount - 1; 208 | 209 | for (let i = 0; i < document.querySelector('#changelog').children.length; i++) { 210 | if (i > amount) document.querySelector('#changelog').children[i].classList.add('hidden'); 211 | else document.querySelector('#changelog').children[i].classList.remove('hidden'); 212 | } 213 | } 214 | 215 | resizeChangelog(Math.floor(getAvalibleHeight() / logHeight())); 216 | window.addEventListener('resize', () => resizeChangelog(Math.floor(getAvalibleHeight() / logHeight()))); 217 | }); 218 | } 219 | 220 | if (window.self === window.top && location.pathname !== '/view') { 221 | if (window.scrollY !== 0) document.querySelector('.navbar').classList.add('scrolling'); 222 | else document.querySelector('.navbar').classList.remove('scrolling'); 223 | } 224 | 225 | if (window.self === window.top && location.pathname !== '/view') window.addEventListener('scroll', () => { 226 | if (window.scrollY !== 0) document.querySelector('.navbar').classList.add('scrolling'); 227 | else document.querySelector('.navbar').classList.remove('scrolling'); 228 | }); 229 | 230 | if (window.self !== window.top && document.querySelector('.navbar')) document.querySelector('.navbar').remove(); 231 | 232 | if (location.pathname === '/share' && new URLSearchParams(location.search).has('game')) { 233 | Games.loadGameFromURL(); 234 | } -------------------------------------------------------------------------------- /static/assets/js/offline.js: -------------------------------------------------------------------------------- 1 | // WIP 2 | 3 | const serverOnline = () => new Promise(async (resolve, reject) => { 4 | try { 5 | await fetch('/'); 6 | resolve(true); 7 | } catch { resolve(false); } 8 | }); 9 | 10 | self.addEventListener('fetch', async (e) => { 11 | if (self.navigator.onLine) e.respondWith('offline'); 12 | else if (await serverOnline()) e.respondWith('nooo'); 13 | }); -------------------------------------------------------------------------------- /static/assets/js/page/game.js: -------------------------------------------------------------------------------- 1 | import PolarisError from '/assets/js/error.js'; 2 | import { loadProxyWorker } from '/assets/js/utils.js'; 3 | 4 | const tiltEffectSettings = { 5 | max: 8, 6 | perspective: 1000, 7 | scale: 1.05, 8 | speed: 800, 9 | easing: 'cubic-bezier(.03,.98,.52,.99)' 10 | }; 11 | 12 | let games = []; 13 | let filteredGames = []; 14 | 15 | const load = () => { 16 | fetch('/assets/JSON/games.json').then(res => res.json()).then(data => { 17 | games = data; 18 | filteredGames = games; 19 | 20 | renderGames(filteredGames); 21 | 22 | const searchInput = document.getElementById('searchInput'); 23 | searchInput.addEventListener('input', filterGames); 24 | }) 25 | .catch(e => new PolarisError('Failed to load games')); 26 | }; 27 | 28 | function filterGames() { 29 | const searchInput = document.getElementById('searchInput'); 30 | const searchTerm = searchInput.value.toLowerCase(); 31 | 32 | filteredGames = games.filter(game => game.name.toLowerCase().includes(searchTerm)); 33 | 34 | renderGames(filteredGames); 35 | } 36 | 37 | function renderGames(gamesToRender) { 38 | const gamesContainer = document.querySelector('.games'); 39 | const popularGamesContainer = document.querySelector('.popular-games'); 40 | gamesContainer.innerHTML = ''; 41 | popularGamesContainer.innerHTML = ''; 42 | 43 | gamesToRender.forEach(game => { 44 | const el = document.createElement('div'); 45 | el.classList = 'game'; 46 | el.innerHTML = `

${game.name}

`; 47 | gamesContainer.appendChild(el); 48 | 49 | if (game.popular === 'yes') { 50 | const popularEl = document.createElement('div'); 51 | popularEl.classList = 'game'; 52 | popularEl.innerHTML = `

${game.name}

`; 53 | popularGamesContainer.appendChild(popularEl); 54 | 55 | popularEl.addEventListener('click', async () => { 56 | await loadProxyWorker('uv'); 57 | 58 | if (game.openinnewtab === 'yes') window.open(game.source); 59 | else { 60 | localStorage.setItem('frameData', JSON.stringify({ 61 | type: 'game', 62 | game 63 | })); 64 | location.href = '/view'; 65 | } 66 | }); 67 | 68 | popularEl.addEventListener('mouseenter', gameMouseEnter); 69 | popularEl.addEventListener('mousemove', gameMouseMove); 70 | popularEl.addEventListener('mouseleave', gameMouseLeave); 71 | } 72 | 73 | el.addEventListener('click', async () => { 74 | await loadProxyWorker(); 75 | 76 | const frameData = { 77 | type: 'game', 78 | game 79 | }; 80 | 81 | if (game.openinnewtab === 'yes') { 82 | window.open(game.source, '_blank'); 83 | console.log('Open game in new tab:', frameData); 84 | } else { 85 | localStorage.setItem('frameData', JSON.stringify(frameData)); 86 | location.href = '/view'; 87 | } 88 | }); 89 | 90 | el.addEventListener('mouseenter', gameMouseEnter); 91 | el.addEventListener('mousemove', gameMouseMove); 92 | el.addEventListener('mouseleave', gameMouseLeave); 93 | }); 94 | } 95 | 96 | function gameMouseEnter(event) { 97 | setTransition(event); 98 | } 99 | 100 | function gameMouseMove(event) { 101 | const game = event.currentTarget; 102 | const gameWidth = game.offsetWidth; 103 | const gameHeight = game.offsetHeight; 104 | const centerX = game.offsetLeft + gameWidth / 2; 105 | const centerY = game.offsetTop + gameHeight / 2; 106 | const mouseX = event.clientX - centerX; 107 | const mouseY = event.clientY - centerY; 108 | const rotateXUncapped = (+1) * tiltEffectSettings.max * mouseY / (gameHeight / 2); 109 | const rotateYUncapped = (-1) * tiltEffectSettings.max * mouseX / (gameWidth / 2); 110 | const rotateX = rotateXUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateXUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateXUncapped); 111 | const rotateY = rotateYUncapped < -tiltEffectSettings.max ? -tiltEffectSettings.max : (rotateYUncapped > tiltEffectSettings.max ? tiltEffectSettings.max : rotateYUncapped); 112 | 113 | game.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${tiltEffectSettings.scale}, ${tiltEffectSettings.scale}, ${tiltEffectSettings.scale})`; 114 | } 115 | 116 | function gameMouseLeave(event) { 117 | event.currentTarget.style.transform = `perspective(${tiltEffectSettings.perspective}px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)`; 118 | setTransition(event); 119 | } 120 | 121 | function setTransition(event) { 122 | const game = event.currentTarget; 123 | clearTimeout(game.transitionTimeoutId); 124 | game.style.transition = `transform ${tiltEffectSettings.speed}ms ${tiltEffectSettings.easing}`; 125 | 126 | game.transitionTimeoutId = setTimeout(() => game.style.transition = '', tiltEffectSettings.speed); 127 | } 128 | 129 | export default { 130 | load 131 | }; -------------------------------------------------------------------------------- /static/assets/js/search.js: -------------------------------------------------------------------------------- 1 | import { createViewPage, isValidURL } from './utils.js'; 2 | 3 | const load = async () => { 4 | const form = document.querySelector('#wpf'); 5 | const query = document.querySelector('#query'); 6 | 7 | form.addEventListener('submit', async (e) => { 8 | e.preventDefault(); 9 | 10 | const url = isValidURL(query.value) ? ((!query.value.startsWith('http://') && !query.value.startsWith('https://')) ? 'https://' + query.value : query.value) : 'https://www.google.com/search?q=' + encodeURIComponent(query.value); 11 | 12 | document.body.style.opacity = '0.7'; 13 | 14 | umami.track('query-' + query.value); 15 | 16 | setTimeout(() => createViewPage({ 17 | target: url, 18 | proxied: true, 19 | title: 'Search Results' 20 | }), 500); 21 | }); 22 | } 23 | 24 | export default { load }; -------------------------------------------------------------------------------- /static/assets/js/share.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/js/share.js -------------------------------------------------------------------------------- /static/assets/js/themes.js: -------------------------------------------------------------------------------- 1 | import { storage } from './utils.js'; 2 | 3 | const settingsStorage = storage('settings'); 4 | 5 | class Theme { 6 | constructor() { 7 | this.theme = settingsStorage.get('theme'); 8 | 9 | if (this.theme === 'system default' || this.theme === 'system-default' || !this.theme) this.set('dark', true); 10 | else this.set(this.theme); 11 | } 12 | 13 | /** 14 | * Set the theme of the page 15 | * @param {string} theme The name of the theme 16 | * @param {boolean} save Whether or not the theme should be saved 17 | */ 18 | set = (theme, save) => { 19 | document.body.setAttribute('data-theme', theme); 20 | this.theme = theme; 21 | 22 | if (save !== false) settingsStorage.set('theme', theme); 23 | }; 24 | 25 | /** 26 | * Get the current theme 27 | * @returns {string} 28 | */ 29 | get = () => document.body.getAttribute('data-theme'); 30 | } 31 | 32 | export default new Theme(); 33 | -------------------------------------------------------------------------------- /static/assets/js/utils.js: -------------------------------------------------------------------------------- 1 | import indexedDBExporter from './utils/indexeddb.js'; 2 | import EventEmitter from './utils/events.js'; 3 | import PolarisError from './utils/error.js'; 4 | import cookie from './utils/cookie.js'; 5 | import ctc from './utils/ctc.js'; 6 | 7 | /** 8 | * The storage interface for polaris 9 | * @param {string} containerName 10 | */ 11 | export const storage = (containerName) => { 12 | return { 13 | /** 14 | * Get a value from the storage container 15 | * @param {string} name The name of the value 16 | * @returns {string} 17 | */ 18 | get: (name) => { 19 | if (!localStorage.getItem(containerName)) localStorage.setItem(containerName, JSON.stringify({})); 20 | else { 21 | try { 22 | JSON.parse(localStorage.getItem(containerName)); 23 | } catch (e) { 24 | localStorage.setItem(containerName, JSON.stringify({})); 25 | } 26 | } 27 | 28 | const container = JSON.parse(localStorage.getItem(containerName)); 29 | return container[name]; 30 | }, 31 | /** 32 | * Set a value from a storage container 33 | * @param {string} name The name of the value 34 | * @param {string | object} value The value to be set 35 | */ 36 | set: (name, value) => { 37 | if (!localStorage.getItem(containerName)) localStorage.setItem(containerName, JSON.stringify({})); 38 | else { 39 | try { 40 | JSON.parse(localStorage.getItem(containerName)); 41 | } catch (e) { 42 | localStorage.setItem(containerName, JSON.stringify({})); 43 | } 44 | } 45 | 46 | const container = JSON.parse(localStorage.getItem(containerName)); 47 | container[name] = value; 48 | 49 | localStorage.setItem(containerName, JSON.stringify(container)); 50 | } 51 | }; 52 | }; 53 | 54 | /** 55 | * @returns {string} 56 | */ 57 | export const uuid = () => ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); 58 | 59 | /** 60 | * Register a proxy service worker 61 | * @param {'uv' | 'dynamic'} proxy 62 | */ 63 | export const loadProxyWorker = async (proxy) => await navigator.serviceWorker.register(`/${proxy.split(':')[0]}/sw.js`, { 64 | scope: `/${proxy.split(':')[0]}/service/` 65 | }); 66 | 67 | /** 68 | * Set the bare transport 69 | * @param {'epoxy' | 'libcurl' | 'bare'} name 70 | * @param {any} options 71 | */ 72 | export const setTransport = async (name, options) => { 73 | const transports = { 74 | 'epoxy': { 75 | src: '/epoxy/index.js', 76 | id: 'EpxMod.EpoxyClient', 77 | options: { 78 | wisp: location.origin.replace('http', 'ws') + '/wisp/' 79 | } 80 | } 81 | }; 82 | 83 | if (!Object.keys(transports).includes(name)) throw 'Invalid Transport'; 84 | 85 | const transport = transports[name]; 86 | 87 | await loadCJS(transport.src); 88 | await loadCJS('/baremux/bare.cjs'); 89 | 90 | BareMux.SetTransport(transport.id, options || transport.options); 91 | } 92 | 93 | /** 94 | Broken 95 | 96 | * Get the current encoding method 97 | * @param {'uv'} proxy 98 | * @returns {Promise.} 99 | const getEncodingMethod = (proxy) => { 100 | return new Promise(async (resolve, reject) => { 101 | const config = await(await fetch(`/${proxy}/${proxy}.config.js`)).text(); 102 | 103 | const Ultraviolet = { 104 | codec: { 105 | xor: {}, 106 | base64: {}, 107 | plain: {} 108 | } 109 | }; 110 | 111 | eval(config); 112 | 113 | const encodingConfig = String(self[`_${proxy}$config`][proxy === 'uv' ? 'encodeUrl' : (proxy === 'dynamic' ? 'encoding' : '')]); 114 | 115 | if (proxy === 'uv') resolve(encodingConfig.replace('Ultraviolet.codec.', '').replace('.encode', '')); 116 | else if (proxy === 'dynamic') resolve(encodingConfig); 117 | }); 118 | }*/ 119 | 120 | /** 121 | * WIP 122 | * 123 | * Load the page javascript 124 | */ 125 | const loadPageScript = () => { 126 | if (location.href) { 127 | 128 | } 129 | }; 130 | 131 | export const encoder = { 132 | b64: { 133 | encode: (data) => btoa(data), 134 | decode: (data) => atob(data) 135 | }, 136 | xor: { 137 | encode: (data, key = 2) => encodeURIComponent(data.split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join('')), 138 | decode: (data, key = 2) => decodeURIComponent(data).split('').map((e, i) => i % key ? String.fromCharCode(e.charCodeAt(0) ^ key) : e).join('') 139 | } 140 | }; 141 | 142 | /** 143 | * Redirect to an external url 144 | * @param {string} target 145 | * @param {{ trusted: boolean }} options 146 | */ 147 | export const redirect = (target, options) => location.href = `/view?load=${btoa(JSON.stringify({ 148 | target, 149 | redirect: true, 150 | trusted: options.trusted 151 | }))}`; 152 | 153 | /** 154 | * Load a url into the view page 155 | * @param {{ target: string, title: string, return: string, proxied: boolean }} options 156 | */ 157 | export const createViewPage = (options) => location.href = `/view?load=${btoa(JSON.stringify({ 158 | return: options.return || location.href, 159 | proxied: options.proxied, 160 | target: options.target, 161 | title: options.title, 162 | image: options.image 163 | }))}`; 164 | 165 | /** 166 | * Check if a url is valid 167 | * @param {string} url 168 | * @returns {boolean} 169 | */ 170 | export const isValidURL = (url) => /^(http(s)?:\/\/)?([\w-]+\.)+[\w]{2,}(\/.*)?$/.test(url); 171 | /** 172 | * Get the css vh value 173 | * @param {*} value 174 | * @returns {number} 175 | */ 176 | export const getVH = (value) => (value * Math.max(document.documentElement.clientHeight, window.innerHeight || 0)) / 100; 177 | export const getVW = (value) => (value * Math.max(document.documentElement.clientWidth, window.innerWidth || 0)) / 100; 178 | export const isScrollable = (element) => element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight; 179 | 180 | /** 181 | * @param {() => {}} code 182 | * @returns {string} 183 | */ 184 | export const evalify = (code) => '(' + String(code) + ')()'; 185 | 186 | export const loadCJS = (src) => new Promise((resolve, reject) => { 187 | const el = document.createElement('script'); 188 | el.src = src; 189 | document.body.appendChild(el); 190 | el.onload = () => resolve(); 191 | }); 192 | 193 | /** 194 | * @type {import('./utils/ctc.js').CrossTabCommunication} 195 | */ 196 | var CrossTabCommunication; 197 | 198 | try { 199 | CrossTabCommunication = ctc; 200 | } catch { CrossTabCommunication = () => { }; } 201 | 202 | export default { 203 | storage, 204 | loadProxyWorker, 205 | encoder, 206 | redirect, 207 | createViewPage, 208 | isValidURL, 209 | getVH, 210 | getVW, 211 | isScrollable, 212 | indexedDBExporter, 213 | EventEmitter, 214 | cookie, 215 | uuid, 216 | CrossTabCommunication, 217 | PolarisError 218 | }; 219 | 220 | export { 221 | indexedDBExporter, 222 | EventEmitter, 223 | cookie, 224 | CrossTabCommunication, 225 | PolarisError 226 | }; -------------------------------------------------------------------------------- /static/assets/js/utils/cookie.js: -------------------------------------------------------------------------------- 1 | const cookie = (this, function () { 2 | 'use strict'; 3 | var cookie = function () { 4 | return cookie.get.apply(cookie, arguments); 5 | }; 6 | 7 | var utils = cookie.utils = { 8 | isArray: Array.isArray || function (value) { 9 | return Object.prototype.toString.call(value) === '[object Array]'; 10 | }, 11 | isPlainObject: (value) => !!value && Object.prototype.toString.call(value) === '[object Object]', 12 | toArray: (value) => Array.prototype.slice.call(value), 13 | getKeys: Object.keys || function (obj) { 14 | var keys = []; 15 | var key = ''; 16 | 17 | for (key in obj) { 18 | if (obj.hasOwnProperty(key)) keys.push(key); 19 | } 20 | 21 | return keys; 22 | }, 23 | encode: (value) => btoa(value.toString()), 24 | decode: (value) => { 25 | try { 26 | return atob(value.toString()); 27 | } catch { 28 | return value.toString(); 29 | } 30 | }, 31 | retrieve: (value, fallback) => value == null ? fallback : value 32 | 33 | }; 34 | 35 | cookie.defaults = {}; 36 | 37 | cookie.expiresMultiplier = 60 * 60 * 24; 38 | 39 | cookie.set = function (key, value, options) { 40 | if (utils.isPlainObject(key)) { 41 | 42 | for (var k in key) { 43 | if (key.hasOwnProperty(k)) this.set(k, key[k], value); 44 | } 45 | } else { 46 | options = utils.isPlainObject(options) ? options : { expires: options }; 47 | 48 | var expires = options.expires !== undefined ? options.expires : (this.defaults.expires || ''), 49 | expiresType = typeof (expires); 50 | 51 | if (expiresType === 'string' && expires !== '') expires = new Date(expires); 52 | else if (expiresType === 'number') expires = new Date(+new Date + 1000 * this.expiresMultiplier * expires); 53 | 54 | if (expires !== '' && 'toUTCString' in expires) expires = ';expires=' + expires.toUTCString(); 55 | 56 | var path = options.path || this.defaults.path; 57 | path = path ? ';path=' + path : ''; 58 | 59 | var domain = options.domain || this.defaults.domain; 60 | domain = domain ? ';domain=' + domain : ''; 61 | 62 | var secure = options.secure || this.defaults.secure ? ';secure' : ''; 63 | if (options.secure === false) secure = ''; 64 | 65 | var sameSite = options.sameSite || this.defaults.sameSite; 66 | sameSite = sameSite ? ';SameSite=' + sameSite : ''; 67 | if (options.sameSite === null) sameSite = ''; 68 | 69 | document.cookie = key + '=' + value + expires + path + domain + secure + sameSite; 70 | } 71 | 72 | return this; 73 | }; 74 | 75 | cookie.setDefault = function (key, value, options) { 76 | if (utils.isPlainObject(key)) { 77 | for (var k in key) { 78 | if (this.get(k) === undefined) this.set(k, key[k], value); 79 | } 80 | return cookie; 81 | } else { 82 | if (this.get(key) === undefined) return this.set.apply(this, arguments); 83 | } 84 | }, 85 | 86 | cookie.remove = function (keys) { 87 | keys = utils.isArray(keys) ? keys : utils.toArray(arguments); 88 | 89 | for (var i = 0, l = keys.length; i < l; i++) { 90 | this.set(keys[i], '', -1); 91 | } 92 | 93 | return this; 94 | }; 95 | 96 | cookie.removeSpecific = function (keys, options) { 97 | if (!options) return this.remove(keys); 98 | 99 | keys = utils.isArray(keys) ? keys : [keys]; 100 | options.expires = -1; 101 | 102 | for (var i = 0, l = keys.length; i < l; i++) { 103 | this.set(keys[i], '', options); 104 | } 105 | 106 | return this; 107 | }; 108 | 109 | cookie.empty = function () { 110 | return this.remove(utils.getKeys(this.all())); 111 | }; 112 | 113 | cookie.get = function (keys, fallback) { 114 | var cookies = this.all(); 115 | 116 | if (utils.isArray(keys)) { 117 | var result = {}; 118 | 119 | for (var i = 0, l = keys.length; i < l; i++) { 120 | var value = keys[i]; 121 | result[value] = utils.retrieve(cookies[value], fallback); 122 | } 123 | 124 | return result; 125 | 126 | } else return utils.retrieve(cookies[keys], fallback); 127 | }; 128 | 129 | cookie.all = function () { 130 | if (document.cookie === '') return {}; 131 | 132 | var cookies = document.cookie.split('; '), 133 | result = {}; 134 | 135 | for (var i = 0, l = cookies.length; i < l; i++) { 136 | var item = cookies[i].split('='); 137 | var key = item.shift(); 138 | var value = item.join('='); 139 | result[key] = value; 140 | } 141 | 142 | return result; 143 | }; 144 | 145 | cookie.enabled = function () { 146 | if (navigator.cookieEnabled) return true; 147 | 148 | var ret = cookie.set('_', '_').get('_') === '_'; 149 | cookie.remove('_'); 150 | return ret; 151 | }; 152 | 153 | return cookie; 154 | }); 155 | 156 | export default cookie(); -------------------------------------------------------------------------------- /static/assets/js/utils/ctc_worker.js: -------------------------------------------------------------------------------- 1 | import { Worker } from './ctc.js'; 2 | 3 | const worker = new Worker(); 4 | 5 | addEventListener('message', ({ data: message }) => worker.emit('message', message)); -------------------------------------------------------------------------------- /static/assets/js/utils/error.js: -------------------------------------------------------------------------------- 1 | class PolarisError { 2 | constructor(e) { 3 | let notificationContainer = document.querySelector('.notifications'); 4 | 5 | if (!notificationContainer) { 6 | notificationContainer = document.createElement('div'); 7 | notificationContainer.classList = 'notifications'; 8 | document.body.appendChild(notificationContainer); 9 | } 10 | 11 | const error = document.createElement('div'); 12 | error.classList = 'notification error'; 13 | if (e.message) error.innerHTML = `An error occurred: ${e.message.toString()}`; 14 | else error.innerHTML = `An error occurred: ${e.toString()}`; 15 | notificationContainer.appendChild(error); 16 | 17 | error.onclick = () => { 18 | error.style.height = '0px'; 19 | error.style.opacity = 0; 20 | error.style.padding = '0px'; 21 | error.firstElementChild.style.fontSize = '0px'; 22 | 23 | setTimeout(() => { 24 | error.remove(); 25 | }, 500); 26 | } 27 | 28 | setTimeout(() => { 29 | error.style.height = '0px'; 30 | error.style.opacity = 0; 31 | error.style.padding = '0px'; 32 | error.firstElementChild.style.fontSize = '0px'; 33 | 34 | setTimeout(() => { 35 | error.remove(); 36 | }, 500); 37 | }, 8000); 38 | 39 | if (e.stack) console.log('An error occurred:\n\n' + e.stack); 40 | else console.log('An error occurred:\n\n' + e); 41 | } 42 | } 43 | 44 | if (this) { 45 | window.onerror = (...e) => new PolarisError(e); 46 | window.console.error = (...e) => new PolarisError(e); 47 | window.onmessageerror = (...e) => new PolarisError(e); 48 | } 49 | 50 | export default PolarisError; 51 | export { PolarisError }; 52 | -------------------------------------------------------------------------------- /static/assets/js/utils/events.js: -------------------------------------------------------------------------------- 1 | class EventEmitter { 2 | constructor() { 3 | this.listeners = {}; 4 | }; 5 | 6 | addListener = (eventName, handler) => { 7 | this.listeners[eventName] = this.listeners[eventName] || []; 8 | this.listeners[eventName].push(handler); 9 | 10 | return this; 11 | }; 12 | 13 | on = (eventName, handler) => this.addListener(eventName, handler); 14 | 15 | once = (eventName, handler) => { 16 | this.listeners[eventName] = this.listeners[eventName] || []; 17 | 18 | const onceWrapper = () => { 19 | handler(); 20 | 21 | this.off(eventName, onceWrapper); 22 | }; 23 | 24 | this.listeners[eventName].push(onceWrapper); 25 | 26 | return this; 27 | }; 28 | 29 | off = (eventName, handler) => this.removeListener(eventName, handler); 30 | 31 | removeListener = (eventName, handler) => { 32 | let lis = this.listeners[eventName]; 33 | 34 | if (!lis) return this; 35 | 36 | for (let i = lis.length; i > 0; i--) { 37 | if (lis[i] === handler) { 38 | lis.splice(i, 1); 39 | 40 | break; 41 | } 42 | } 43 | 44 | return this; 45 | }; 46 | 47 | emit = (eventName, ...args) => { 48 | let handlers = this.listeners[eventName]; 49 | 50 | if (!handlers) return false; 51 | 52 | handlers.forEach(handler => handler(...args)); 53 | 54 | return true; 55 | }; 56 | 57 | listenerCount = (eventName) => { 58 | let handlers = this.listeners[eventName] || []; 59 | 60 | return handlers.length; 61 | }; 62 | 63 | rawListeners = (eventName) => this.listeners[eventName]; 64 | }; 65 | 66 | export default EventEmitter; 67 | export { EventEmitter }; -------------------------------------------------------------------------------- /static/assets/js/utils/indexeddb.js: -------------------------------------------------------------------------------- 1 | /* 2 | Modified version of Polarisation's indexeddb importer and exporter library. 3 | https://github.com/Polarisation/indexeddb-export-import 4 | */ 5 | 6 | /** 7 | * Export all data from an IndexedDB database 8 | * @param {IDBDatabase} idbDatabase - to export from 9 | * @returns {Promise} 10 | */ 11 | const exportToJsonString = (idbDatabase) => new Promise(async (resolve, reject) => { 12 | const exportObject = {}; 13 | const objectStoreNamesSet = new Set(idbDatabase.objectStoreNames); 14 | const size = objectStoreNamesSet.size; 15 | 16 | if (size === 0) resolve(JSON.stringify(exportObject)); 17 | else { 18 | const objectStoreNames = Array.from(objectStoreNamesSet); 19 | const transaction = idbDatabase.transaction(objectStoreNames, 'readonly'); 20 | transaction.onerror = (e) => reject(e); 21 | 22 | objectStoreNames.forEach((storeName) => { 23 | const allObjects = []; 24 | 25 | transaction 26 | .objectStore(storeName) 27 | .openCursor() 28 | .onsuccess = (e) => { 29 | const cursor = e.target.result; 30 | 31 | if (cursor) { 32 | allObjects.push(cursor.value); 33 | cursor.continue(); 34 | } else { 35 | exportObject[storeName] = allObjects; 36 | 37 | if (objectStoreNames.length === Object.keys(exportObject).length) resolve(JSON.stringify(exportObject)); 38 | } 39 | }; 40 | }); 41 | } 42 | }); 43 | 44 | /** 45 | * Import data from JSON into an IndexedDB database. This does not delete any existing data 46 | * from the database, so keys could clash. 47 | * 48 | * Only object stores that already exist will be imported. 49 | * 50 | * @param {IDBDatabase} idbDatabase - to import into 51 | * @param {string} jsonString - data to import, one key per object store 52 | * @return {Promise} 53 | */ 54 | const importFromJsonString = (idbDatabase, jsonString) => new Promise(async (resolve, reject) => { 55 | const objectStoreNamesSet = new Set(idbDatabase.objectStoreNames); 56 | const size = objectStoreNamesSet.size; 57 | 58 | if (size === 0) resolve('{}'); 59 | else { 60 | const objectStoreNames = Array.from(objectStoreNamesSet); 61 | const transaction = idbDatabase.transaction(objectStoreNames, 'readwrite'); 62 | 63 | transaction.onerror = (e) => reject(e); 64 | 65 | const importObject = JSON.parse(jsonString); 66 | 67 | Object.keys(importObject).forEach((storeName) => { 68 | if (!objectStoreNames.includes(storeName)) delete importObject[storeName]; 69 | }); 70 | 71 | if (Object.keys(importObject).length === 0) resolve('{}'); 72 | 73 | objectStoreNames.forEach((storeName) => { 74 | let count = 0; 75 | 76 | const aux = Array.from(importObject[storeName] || []); 77 | 78 | if (importObject[storeName] && aux.length > 0) aux.forEach((toAdd) => { 79 | const request = transaction.objectStore(storeName).add(toAdd); 80 | 81 | request.onsuccess = () => { 82 | count++; 83 | 84 | if (count === importObject[storeName].length) { 85 | delete importObject[storeName]; 86 | 87 | if (Object.keys(importObject).length === 0) resolve('{}'); 88 | } 89 | }; 90 | 91 | request.onerror = (e) => reject(e); 92 | }); 93 | else { 94 | if (importObject[storeName]) { 95 | delete importObject[storeName]; 96 | 97 | if (Object.keys(importObject).length === 0) resolve('{}'); 98 | } 99 | } 100 | }); 101 | } 102 | }); 103 | 104 | /** 105 | * Clears a database of all data. 106 | * 107 | * The object stores will still exist but will be empty. 108 | * 109 | * @param {IDBDatabase} idbDatabase - to delete all data from 110 | * @return {Promise} 111 | */ 112 | const clearDatabase = (idbDatabase) => new Promise(async (resolve, reject) => { 113 | const objectStoreNamesSet = new Set(idbDatabase.objectStoreNames); 114 | const size = objectStoreNamesSet.size; 115 | 116 | if (size === 0) resolve('{}'); 117 | else { 118 | const objectStoreNames = Array.from(objectStoreNamesSet); 119 | const transaction = idbDatabase.transaction( 120 | objectStoreNames, 121 | 'readwrite' 122 | ); 123 | 124 | transaction.onerror = (e) => reject(e); 125 | 126 | let count = 0; 127 | 128 | objectStoreNames.forEach((storeName) => transaction 129 | .objectStore(storeName) 130 | .clear() 131 | .onsuccess = () => { 132 | count++; 133 | 134 | if (count === size) resolve('{}'); 135 | }); 136 | } 137 | }); 138 | 139 | export default { exportToJsonString, importFromJsonString, clearDatabase }; 140 | export { exportToJsonString, importFromJsonString, clearDatabase }; -------------------------------------------------------------------------------- /static/assets/js/view.js: -------------------------------------------------------------------------------- 1 | import { loadProxyWorker, encoder, storage, loadCJS, setTransport } from './utils.js'; 2 | import { loadSettings } from './settings.js'; 3 | 4 | await loadCJS('/baremux/bare.cjs'); 5 | 6 | loadSettings(); 7 | 8 | const params = new URLSearchParams(location.search); 9 | const settingsStorage = storage('settings'); 10 | 11 | if ((settingsStorage.get('proxy') || 'uv').startsWith('uv')) await setTransport((settingsStorage.get('proxy') || '').split(':')[1] || 'libcurl'); 12 | 13 | window.history.replaceState({}, '', location.pathname); 14 | 15 | if (params.get('load')) { 16 | try { 17 | const parsedData = JSON.parse(atob(params.get('load'))); 18 | 19 | if (Boolean(parsedData.target && parsedData.title && parsedData.return)) { 20 | document.body.classList.remove('hidden'); 21 | 22 | sessionStorage.setItem('loaddata', JSON.stringify(parsedData)); 23 | 24 | if (parsedData.proxied) { 25 | await loadProxyWorker((settingsStorage.get('proxy') || '').split(':')[0] || 'uv'); 26 | 27 | document.querySelector('#loadframe').src = `/${(settingsStorage.get('proxy') || '').split(':')[0] || 'uv'}/service/${encoder['xor'].encode(parsedData.target)}`; 28 | } else document.querySelector('#loadframe').src = parsedData.target; 29 | 30 | document.querySelector('#loadframe').addEventListener('load', () => { 31 | document.querySelector('.title').textContent = parsedData.title; 32 | 33 | document.querySelector('#loadframe').style.transition = 'none'; 34 | document.querySelector('#loadframe').style.background = '#fff'; 35 | 36 | document.querySelector('#loadframe').addEventListener('mouseover', () => { 37 | document.querySelector('.gamebar').classList.add('collapsed'); 38 | document.querySelector('.hitbox').classList.remove('active'); 39 | }); 40 | 41 | document.querySelector('#loadframe').addEventListener('mouseout', () => { 42 | document.querySelector('.gamebar').classList.remove('collapsed'); 43 | document.querySelector('.hitbox').classList.add('active'); 44 | }); 45 | 46 | setTimeout(() => { 47 | document.querySelector('.gamebar').classList.remove('collapsed'); 48 | document.querySelector('.hitbox').classList.add('active'); 49 | }, 1000); 50 | }); 51 | 52 | document.querySelector('#fullscreen').addEventListener('click', () => { 53 | const frame = document.querySelector('#loadframe'); 54 | 55 | if (frame.requestFullscreen) frame.requestFullscreen(); 56 | else if (frame.webkitRequestFullscreen) frame.webkitRequestFullscreen(); 57 | else if (frame.mozRequestFullScreen) frame.mozRequestFullScreen(); 58 | else if (frame.msRequestFullscreen) frame.msRequestFullscreen(); 59 | }); 60 | 61 | window.addEventListener('fullscreenchange', () => { 62 | if (document.fullscreenElement) document.querySelector('#loadframe').style.borderRadius = '0px'; 63 | else document.querySelector('#loadframe').style.borderRadius = ''; 64 | }); 65 | 66 | document.querySelector('#return').addEventListener('click', () => { 67 | document.body.style.opacity = '0.7'; 68 | 69 | setTimeout(() => window.location.href = parsedData.return, 500); 70 | }); 71 | } else if (parsedData.target && parsedData.redirect === true) { 72 | window.history.replaceState({}, '', '/redirect'); 73 | 74 | if (parsedData.trusted) window.location.replace(parsedData.target); 75 | else { 76 | document.documentElement.textContent = `Redirecting to ${parsedData.target}`; 77 | setTimeout(() => window.location.replace(parsedData.target), 1000); 78 | } 79 | } else window.location.replace(parsedData.return || '/'); 80 | } catch (e) { alert(e); window.location.replace('/'); } 81 | } else if (sessionStorage.getItem('loaddata')) window.location.replace(`/view?load=${btoa(sessionStorage.getItem('loaddata'))}`); 82 | else window.location.replace('/'); -------------------------------------------------------------------------------- /static/assets/misc/media/bruh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/misc/media/bruh.mp3 -------------------------------------------------------------------------------- /static/assets/misc/media/rickroll.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/misc/media/rickroll.mp3 -------------------------------------------------------------------------------- /static/assets/misc/media/ringtone.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/misc/media/ringtone.mp3 -------------------------------------------------------------------------------- /static/assets/misc/media/skelly.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/misc/media/skelly.mp3 -------------------------------------------------------------------------------- /static/assets/misc/media/smurf.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/assets/misc/media/smurf.mp3 -------------------------------------------------------------------------------- /static/changelog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Changelog

21 | 22 |
23 | 24 |

25 |
26 | 27 |

28 | Running Polaris unknown 29 | 30 |
31 |
32 | 33 | 34 | Commit: unknown 35 |
36 | Up to Date: no 37 |
38 | Server Mode: unknown 39 |
40 |

41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /static/cheats.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Cheats | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

Cheats

20 | 21 |
22 | 23 |
24 |
25 | 26 |

27 | 28 |
29 | 30 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /static/downtime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Server Downtime | Polaris 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Oh no!

22 | 23 |

24 | We slipped up! Polaris's servers are currently down and we are working hard to fix them! 25 |
26 | For more information on the downtime join our discord server. 27 |

28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/favicon-16x16.png -------------------------------------------------------------------------------- /static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/favicon-32x32.png -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skoolgq/Polaris/d8b9bfdaebd4016c14691d4096499697fd7b2382/static/favicon.ico -------------------------------------------------------------------------------- /static/games.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Games | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

Games

20 | 21 | 23 | 24 |

All Games

25 | 26 | 39 | 40 |
41 | 42 |
43 |
44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Polaris 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |

Featured Game

20 | 21 | 22 |
23 | 24 |
25 |

Polaris

26 | 27 |

28 | Discord 29 | - 30 | Source Code 31 | - 32 | Support Email 33 | - 34 | Suggest a game 35 | - 36 | Privacy Policy 37 | - 38 | Terms of Service 39 |

40 | 41 |
42 | 43 |

Changelog

44 | 45 | Full changelog 46 | 47 | 48 |
49 | 50 |
51 |
52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /static/offline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Offline | Polaris 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Offline

21 | 22 |

Looks you're offline. Connect to the internet to access polaris.

23 |
24 | 25 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /static/premium.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Premium | Polaris 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |

Premium

21 |

Coming Soon...

22 |
23 |

With a premium membership you will get access to

24 |
    25 |
  • High speed exclusive servers
  • 26 |
  • Insider information on future updates
  • 27 |
  • Early access to new links
  • 28 |
  • And much more to come
  • 29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /static/privacy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Privacy Policy

21 | 22 |

23 | By using our site your personal information may be collected, including: 24 | 25 |
26 |
27 | - Email Address 28 |
29 | - IP Address 30 |
31 | - Other data commonly collected by advertisers 32 | 33 |

34 | 35 | We do not sell any of your personal information to third parties outside of the advertisers we use. 36 | We do not store your personal information outside of advertising services and our analytics service 37 | https://counter.dev/. 38 | 39 | Our sites code is open source and is hosted on github, it can be found here: https://github.com/Skoolgq/Polaris. 40 | 41 |

42 | 43 | If you have any questions please contact us at support@polarislearning.org 44 |

45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /static/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search | Polaris 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Proxy

21 | 22 |

Search the web without restrictions

23 | 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /static/share.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Loading... | Polaris 12 | 13 | 14 | 15 |
16 |

Loading

17 | 18 |

19 | Loading game... 20 |

21 |
22 | 23 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /static/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"Polaris By Skool","short_name":"Polaris","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /static/uv/sw.js: -------------------------------------------------------------------------------- 1 | // UV Transports 2 | importScripts('/epoxy/index.js'); 3 | 4 | importScripts('/uv/uv.bundle.js'); 5 | importScripts('/uv/uv.config.js'); 6 | importScripts(__uv$config.sw || '/uv/uv.sw.js'); 7 | 8 | const uv = new UVServiceWorker(); 9 | 10 | async function handleRequest(event) { 11 | if (uv.route(event)) { 12 | return await uv.fetch(event); 13 | } 14 | 15 | return await fetch(event.request) 16 | } 17 | 18 | self.addEventListener('fetch', (event) => { 19 | event.respondWith(handleRequest(event)); 20 | }); -------------------------------------------------------------------------------- /static/uv/uv.config.js: -------------------------------------------------------------------------------- 1 | self.__uv$config = { 2 | prefix: '/uv/service/', 3 | encodeUrl: Ultraviolet.codec.xor.encode, 4 | decodeUrl: Ultraviolet.codec.xor.decode, 5 | handler: '/uv/uv.handler.js', 6 | client: '/uv/uv.client.js', 7 | bundle: '/uv/uv.bundle.js', 8 | config: '/uv/uv.config.js', 9 | sw: '/uv/uv.sw.js' 10 | }; 11 | 12 | /** 13 | * The storage interface for polaris 14 | * @param {string} containerName 15 | * 16 | const storage = (containerName) => { 17 | return { 18 | /** 19 | * Get a value from the storage container 20 | * @param {string} name The name of the value 21 | * @returns {string} 22 | * 23 | get: (name) => { 24 | if (!localStorage.getItem(containerName)) localStorage.setItem(containerName, JSON.stringify({})); 25 | else { 26 | try { 27 | JSON.parse(localStorage.getItem(containerName)); 28 | } catch (e) { 29 | localStorage.setItem(containerName, JSON.stringify({})); 30 | } 31 | } 32 | 33 | const container = JSON.parse(localStorage.getItem(containerName)); 34 | return container[name]; 35 | }, 36 | /** 37 | * Set a value from a storage container 38 | * @param {string} name The name of the value 39 | * @param {string | object} value The value to be set 40 | * 41 | set: (name, value) => { 42 | if (!localStorage.getItem(containerName)) localStorage.setItem(containerName, JSON.stringify({})); 43 | else { 44 | try { 45 | JSON.parse(localStorage.getItem(containerName)); 46 | } catch (e) { 47 | localStorage.setItem(containerName, JSON.stringify({})); 48 | } 49 | } 50 | 51 | const container = JSON.parse(localStorage.getItem(containerName)); 52 | container[name] = value; 53 | 54 | localStorage.setItem(containerName, JSON.stringify(container)); 55 | } 56 | }; 57 | }; 58 | 59 | const setTransport = (name, options) => { 60 | const transports = { 61 | 'epoxy': { 62 | src: '/epoxy/index.js', 63 | id: 'EpxMod.EpoxyClient', 64 | options: { 65 | wisp: location.origin.replace('http', 'ws') + '/wisp/' 66 | } 67 | }, 68 | 'libcurl': { 69 | src: '/libcurl/index.cjs', 70 | id: 'CurlMod.LibcurlClient', 71 | options: { 72 | wisp: location.origin.replace('http', 'ws') + '/wisp/', 73 | wasm: location.origin + '/libcurl/libcurl.wasm' 74 | } 75 | }, 76 | 'bare': { 77 | src: '/assets/js/bare-transport.js', 78 | id: 'BareMod.BareClient', 79 | options: location.origin + '/bare/' 80 | } 81 | }; 82 | 83 | if (!Object.keys(transports).includes(name)) throw 'Invalid Transport'; 84 | 85 | const transport = transports[name]; 86 | 87 | BareMux.SetTransport(transport.id, options || transport.options); 88 | } 89 | 90 | const settingsStorage = storage('settings'); 91 | 92 | setTransport((settingsStorage.get('proxy') || '').split(':')[1] || 'libcurl');*/ -------------------------------------------------------------------------------- /static/view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | View | Polaris 12 | 13 | 14 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 |

Loading...

28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /templates/ad_horizontal.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | -------------------------------------------------------------------------------- /templates/adtop.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/development.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/discord_widget.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /templates/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /templates/navbar.html: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /templates/sidebar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [{ 4 | "src": "server/index.js", 5 | "use": "@vercel/node" 6 | }], 7 | "routes": [{ 8 | "src": "/(.*)", 9 | "dest": "server/index.js" 10 | }] 11 | } --------------------------------------------------------------------------------