diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..dc555529 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +/lib \ No newline at end of file diff --git a/README.md b/README.md index afed967b..75878ff9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ + # Holy Unblocker LTS (v6.x.x) Holy Unblocker LTS, an experimental web proxy service, can bypass web filters or 'blockers' regardless of whether the method of censorship is client-side or network-based. This includes the potential ability to bypass content blockers overseas, Chrome extensions, localized client firewalls, and network-related filters. @@ -9,6 +10,7 @@ Works with a large number of sites, including YouTube, Discord, and more! Also has a good amount of locally hosted games featured on the site. #### Supports + - Youtube.com - Discord.com - Google.com @@ -17,8 +19,9 @@ Also has a good amount of locally hosted games featured on the site. - And more sites! #### Features: -- Tab customization using the Options menu for improved stealth -- Considerable variety with the open selection of proxy types + +- Tab customization using the Options menu for improved stealth +- Considerable variety with the open selection of proxy types - Game library with moderately decent titles - Has frequent support articles for issues relating to the various proxy instances @@ -44,23 +47,24 @@ Read below for information if the official site is blocked or for obtaining more [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2FQuiteAFancyEmerald%2FHoly-Unblocker) [![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?type=git&repository=github.com/QuiteAFancyEmerald/Holy-Unblocker-Old&branch=master&name=HolyUnblocker&run_command=npm%start) + ## Table of contents: - [Setup](#how-to-setup) - - [Structure](#structure) - - [Structure Information](#structure-information) - - [Static Files](#details-of-views) - - [Scripts](#scripts-located-in-viewsassetsjs) - - [Future Additions](#future-additions) - - [Beginner's Explanation](#vauge-explanation-for-beginners-with-external-proxies-and-hosting) - - [Hosting Providers](#list-of-some-good-hosting-options) - - [Domain Setup](#freenomdomain-steps) - - [Cloudflare Setup](#cloudflare-steps) - - [Workspace Configurations](#workspace-configurations) - - [Detailed FAQ](#detailed-faq) - - [More Information](#more-information) + - [Structure](#structure) + - [Structure Information](#structure-information) + - [Static Files](#details-of-views) + - [Scripts](#scripts-located-in-viewsassetsjs) + - [Future Additions](#future-additions) + - [Beginner's Explanation](#vauge-explanation-for-beginners-with-external-proxies-and-hosting) + - [Hosting Providers](#list-of-some-good-hosting-options) + - [Domain Setup](#freenomdomain-steps) + - [Cloudflare Setup](#cloudflare-steps) + - [Workspace Configurations](#workspace-configurations) + - [Detailed FAQ](#detailed-faq) + - [More Information](#more-information) ## How to Setup @@ -81,8 +85,8 @@ The default place for the proxy when its started is `http://localhost:8080`, but This website is hosted locally with Ultraviolet and Rammerhead built-in. - ## Structure +
Web Pages - `index.html`: The homepage of the site. @@ -109,15 +113,18 @@ This website is hosted locally with Ultraviolet and Rammerhead built-in.
### Structure Information + - `/views/`: The physical site base of Holy Unblocker goes here where static assets are served. - `/src/`: For future implementation of obfuscation and keyword removing features. #### Details of `/views/` + - `/archive/` is used for game pages and vibeOS. - `/pages/` is used for the HTML for the site. - `/assets/` is used for storing various CSS, JS, image, and JSON files. #### Scripts located in `/views/assets/js/` + - `bareTransport.js` is a locally installed version of the bare transport module which allows Ultraviolet to function. - `card.js` adds a fancy visual effect to the box cards displayed on the welcome screen. - `common.js` is used on all pages and allows commonly used features to function. @@ -126,109 +133,114 @@ This website is hosted locally with Ultraviolet and Rammerhead built-in. - `register-sw.js` creates and manages service workers that allow Ultraviolet to function, and also uses bare transport. ## Future Additions + This will be our nonexhaustive todo list for Holy Unblocker LTS v6.x.x and above. ## Code Cleanup - - [ ] Remove all current obfuscation in the source code. It needs to be dynamically obfuscated if anything, or not obfuscated at all. This option will be a config option on the server side before rendering with Express for a performance focus. Meta elements will have an additonal attribute indicating if they should be moved. This is to ensure a SEO source can be served by config or a source focused on pure censorship evasion. - - [ ] Optimize the stylesheets and the HTML layout. Add more proper commenting and redivide the code so that it's less hard on the eyes. - - [ ] Optimize the JS. This time it won't be in one line and will be somewhat thoroughly commented. - - [ ] Restructure navigation scripts to ensure updated proxy functionality is sanitized and effective - - [x] Particles.js automatically adjusting per display size - done - - [x] Fix routes.mjs throwing with incorrect paths - done - - [x] Create test script - done - - [x] XSS and fingerprinting protection (may need updates) - done - - [x] Update games navigation JS and page/change to JSON object system - done - - [ ] Ensure all the original submodules get added back to HU-Archive - - [x] Mobile support - (welcome screen only, partial/needs work) - - [ ] SEO overhaul adapted from the v2 SEO Guide format - - [ ] Randomize the __uv$config global, and optionally randomize the UV prefix and URL encoding via cookies +- [ ] Remove all current obfuscation in the source code. It needs to be dynamically obfuscated if anything, or not obfuscated at all. This option will be a config option on the server side before rendering with Express for a performance focus. Meta elements will have an additonal attribute indicating if they should be moved. This is to ensure a SEO source can be served by config or a source focused on pure censorship evasion. +- [ ] Optimize the stylesheets and the HTML layout. Add more proper commenting and redivide the code so that it's less hard on the eyes. +- [ ] Optimize the JS. This time it won't be in one line and will be somewhat thoroughly commented. +- [ ] Restructure navigation scripts to ensure updated proxy functionality is sanitized and effective +- [x] Particles.js automatically adjusting per display size - done +- [x] Fix routes.mjs throwing with incorrect paths - done +- [x] Create test script - done +- [x] XSS and fingerprinting protection (may need updates) - done +- [x] Update games navigation JS and page/change to JSON object system - done +- [ ] Ensure all the original submodules get added back to HU-Archive +- [x] Mobile support - (welcome screen only, partial/needs work) +- [ ] SEO overhaul adapted from the v2 SEO Guide format +- [ ] Randomize the \_\_uv$config global, and optionally randomize the UV prefix and URL encoding via cookies ## Proxy/Site Functionality - - [x] Ensure Ultraviolet is updated to support bare-mux and wisp - done - - [x] Add Rammerhead support - done - - [x] Fix slow Ultraviolet speeds despite being local; something on the backend?? - done - - [x] Fix Ultraviolet on Firefox - (partial/needs work) - - [ ] Adapt Applications page to use either Rammerhead or UV (for Reddit, YouTube, Discord) - - [x] libcurl, epoxy and all that fun stuff - done - - [x] socks5/tor routing option that can be configured (enabled) via either a cookie or pathname as a settings meny option - done - - [ ] Update games page content - - [ ] Update settings menu again to make more room for more features - - [x] Update csel.js (after Setting menu redesign) to support custom transports, icon swap, routing - done - - [x] Update csel.js to support network based adblocking (partial/needs work) - - [ ] Add a "website self-destruct" button to the settings menu - - [ ] Flesh out and rework the UV / bare client error page - - [ ] Update sw.js to support workerware (https://github.com/MercuryWorkshop/workerware) - - [ ] Omnibox autoupdate script (for the Google/Bing style auto suggest feature) - - [ ] Games library will feature 10000 items; 5000 flash games and 5000 other game types + +- [x] Ensure Ultraviolet is updated to support bare-mux and wisp - done +- [x] Add Rammerhead support - done +- [x] Fix slow Ultraviolet speeds despite being local; something on the backend?? - done +- [x] Fix Ultraviolet on Firefox - (partial/needs work) +- [ ] Adapt Applications page to use either Rammerhead or UV (for Reddit, YouTube, Discord) +- [x] libcurl, epoxy and all that fun stuff - done +- [x] socks5/tor routing option that can be configured (enabled) via either a cookie or pathname as a settings meny option - done +- [ ] Update games page content +- [ ] Update settings menu again to make more room for more features +- [x] Update csel.js (after Setting menu redesign) to support custom transports, icon swap, routing - done +- [x] Update csel.js to support network based adblocking (partial/needs work) +- [ ] Add a "website self-destruct" button to the settings menu +- [ ] Flesh out and rework the UV / bare client error page +- [ ] Update sw.js to support workerware (https://github.com/MercuryWorkshop/workerware) +- [ ] Omnibox autoupdate script (for the Google/Bing style auto suggest feature) +- [ ] Games library will feature 10000 items; 5000 flash games and 5000 other game types ## Site Redesign - - [x] Landing Cards - done - - [x] Change fonts to cleaner look - - [ ] Add more AOS interactions on scroll or hover - - [ ] Add subtle noise to background elements - - [ ] Update colors + add themes - - [ ] Toggle elements - - [ ] Other card options - - [ ] Radial blur elements - - [ ] Code standard examples - - [ ] Horizontal/general movement on scroll with AOS - - [ ] Showcase dev dependencies - - [ ] Update icons - - [x] Landing Page - (partial/needs work) - - [x] Settings Menu - (partial/needs work) - - [ ] More Dropdown Menu - - [ ] Web Proxies page - - [ ] Application page - - [ ] Hosting page - - [ ] Resources page - - [ ] Games Library page - - [ ] Emulators Library page - - [ ] Emu Library page - - [ ] Web Games page - - [ ] Flash Games page - - [ ] Documentation page - - [ ] FAQ page - - [ ] Credits page - - [ ] TOS page - - [x] Footer Design - (partial/needs work) - - [x] Header Design - (partial/needs work) +- [x] Landing Cards - done +- [x] Change fonts to cleaner look +- [ ] Add more AOS interactions on scroll or hover +- [ ] Add subtle noise to background elements +- [ ] Update colors + add themes +- [ ] Toggle elements +- [ ] Other card options +- [ ] Radial blur elements +- [ ] Code standard examples +- [ ] Horizontal/general movement on scroll with AOS +- [ ] Showcase dev dependencies +- [ ] Update icons +- [x] Landing Page - (partial/needs work) +- [x] Settings Menu - (partial/needs work) +- [ ] More Dropdown Menu +- [ ] Web Proxies page +- [ ] Application page +- [ ] Hosting page +- [ ] Resources page +- [ ] Games Library page +- [ ] Emulators Library page +- [ ] Emu Library page +- [ ] Web Games page +- [ ] Flash Games page +- [ ] Documentation page +- [ ] FAQ page +- [ ] Credits page +- [ ] TOS page +- [x] Footer Design - (partial/needs work) +- [x] Header Design - (partial/needs work) ## Community Requests - - [ ] Add [Quake WASM](https://github.com/GMH-Code/Quake-WASM) - - [ ] Celeste WASM - - [ ] Doom WASM + +- [ ] Add [Quake WASM](https://github.com/GMH-Code/Quake-WASM) +- [ ] Celeste WASM +- [ ] Doom WASM ## Changelog - - Added wisp support - - Fixed AD config setting being opt-out; ads are not implemented in the project however - - Added Rammerhead support (locally) - - Drastically updated visuals across the service and refactored stylesheets - - Bumped games page functionality - - Updated randomization scripts to ES6 syntax and implemented the alternative to RegEx string replacement - - Helmet for express implemented into backend - - Improved component handling via templates.mjs along with deletion of obsolete files that previously handled this standard in a poor format - - Fixed oddly slow speeds with Ultraviolet (as well as a general version bump to support epoxy-tls and bare-mux) - - Implemented testing scripts for an improved GitHub actions workflow by doing a quick test on proxy + site functionality - - Greatly optimized client-side scripts across the site with a new standard, and generally reworked to no longer leave global variables - - Changes to server.mjs with path logic and error handling - - Updated standards for common scripts - - libcurl and bare-as-module support added - - Deleted 5 JS scripts and moved lots of data into JSON files. Big reorganization. Games menu core scripts now nested inside of common.js utilizing a JSON system - - Massive updates to the Settings menu visually and functionality wise; added Bare-Mux support for swapping transports to work with Ultraviolet, default icons and selective adblocking + Tor on any proxy instances - - CSS Has been partially restructured for mobile support, and is now properly arranged into clearly labeled sections (for the most part) - - Incorporated makeshift domain blacklisting functionality into Ultraviolet, currently used for blocking ads if ads are disabled in settings - - Fleshed out the SEO with more descriptions and better labeling - - Switched to Fastify for serving content from the backend; a separate Express backend file is kept in case it's still needed - - Rammerhead is now locally built into the HU LTS repository - - Simplified the HU LTS setup process and added more default npm commands +- Added wisp support +- Fixed AD config setting being opt-out; ads are not implemented in the project however +- Added Rammerhead support (locally) +- Drastically updated visuals across the service and refactored stylesheets +- Bumped games page functionality +- Updated randomization scripts to ES6 syntax and implemented the alternative to RegEx string replacement +- Helmet for express implemented into backend +- Improved component handling via templates.mjs along with deletion of obsolete files that previously handled this standard in a poor format +- Fixed oddly slow speeds with Ultraviolet (as well as a general version bump to support epoxy-tls and bare-mux) +- Implemented testing scripts for an improved GitHub actions workflow by doing a quick test on proxy + site functionality +- Greatly optimized client-side scripts across the site with a new standard, and generally reworked to no longer leave global variables +- Changes to server.mjs with path logic and error handling +- Updated standards for common scripts +- libcurl and bare-as-module support added +- Deleted 5 JS scripts and moved lots of data into JSON files. Big reorganization. Games menu core scripts now nested inside of common.js utilizing a JSON system +- Massive updates to the Settings menu visually and functionality wise; added Bare-Mux support for swapping transports to work with Ultraviolet, default icons and selective adblocking + Tor on any proxy instances +- CSS Has been partially restructured for mobile support, and is now properly arranged into clearly labeled sections (for the most part) +- Incorporated makeshift domain blacklisting functionality into Ultraviolet, currently used for blocking ads if ads are disabled in settings +- Fleshed out the SEO with more descriptions and better labeling +- Switched to Fastify for serving content from the backend; a separate Express backend file is kept in case it's still needed +- Rammerhead is now locally built into the HU LTS repository +- Simplified the HU LTS setup process and added more default npm commands ## Vague Explanation for Beginners With External Proxies and Hosting + You will first want to host your proxies locally or externally. OUTDATED #### List of some good hosting options: + - Oracle Cloud (Free, Paid, Dedicated) - Repl.it (Free) - Azure (Free and Paid) @@ -248,14 +260,16 @@ This is an example of DNS records involving Heroku. Self-hosting will require `A As stated previously, Holy Unblocker is hosted locally with Ultraviolet. #### Freenom/Domain Steps + For beginners, Freenom is a good provider for obtaining domains for free. However, Freenom only provides their TLDs (`.cf`, `.ml`, `.gq`, `.ga`, and `.tk`) for free, which can be easily blocked. - Get some Freenom domains then add them to your Heroku instance (Personal > [App Name] > Settings > Domains) -Add a domain for both `www.example.cf` and `example.cf` with .cf being interchangeable with other Freenom domain names. + Add a domain for both `www.example.cf` and `example.cf` with .cf being interchangeable with other Freenom domain names. - If you prefer to obtain premium domains (TLDs) then use Porkbun, which offers domains for amazing prices. Literally a `.net` domain normally costs around $10. On Porkbun for the first year it costs $3 so its definitely a deal. #### Cloudflare Steps -- Use Cloudflare (make an account), add your site (Freenom Domain or other) and then add your various DNS targets to Cloudflare. Make sure you add Cloudflare's Nameservers which will be given later when you are adding your site. + +- Use Cloudflare (make an account), add your site (Freenom Domain or other) and then add your various DNS targets to Cloudflare. Make sure you add Cloudflare's Nameservers which will be given later when you are adding your site. Make sure they are CNAME although A records also work and try to follow this structure: @@ -266,13 +280,12 @@ Make sure they are CNAME although A records also work and try to follow this str **Below are if you want external proxies also with your site:** -`CNAME | a | your-womginx-instance-here.herokudns.com` - - +`CNAME | a | your-womginx-instance-here.herokudns.com` Make sure HTTPS is forced and have SSL set to Flexible for some services. Otherwise you can have SSL set to Full. -#### Workspace Configurations +#### Workspace Configurations + Preferably if you have your own device use Visual Studio Code. Pretty much the best option you can get but obviously this is an opinion. Also make sure you have Node.JS installed on your machine. Not going to go too in depth with this part but first fork this repository. The clone it locally through a Terminal of some sort depending on what OS you are on. Make sure you navigate to the folder you want to set this up in. @@ -294,16 +307,19 @@ node_modules Now you have your following workspace environment setup. To deploy the following workspace you just created you will need to look up depending on your hosting provider. For an online IDE that you can use on your school computer and/or chromebook use GitPod. Basically the equivalent of Visual Studio Code but with in-browser support. + - Make an account: `https://gitpod.io/` - Fork this repo and enter in this URL to setup your workspace: `https://gitpod.io#https://github.com/YourNameHere/Holy-Unblocker/` Use the same steps above by running `npm install` in your repository and adding a `.gitignore` in your root directory specifying to exclude `node_modules`. ## Detailed FAQ +
Quick FAQ #### Where can I find the games for this repo? (404 errors, etc.) + Due to piracy concerns, size, etc. this has been moved over here. EmuLibrary is not featured in the public version. **Why is the site I am on not working correctly or having CAPTCHA errors?** @@ -319,6 +335,7 @@ If you still have any questions feel free to ask them in the discord linked here
### Why are official domains now numbered? Is this project maintained again? + Yes, this project is active again for LTS support! However, the approach is now much simpler to ensure functionality: traffic will be focused on a single domain. More than ever, this project serves as a proof of concept for the brave souls willing to innovate in the web proxy service space.
Former Closing Message (Original - 2022) @@ -329,7 +346,7 @@ The main change of thought is that I’m finally just putting an end right now d Some things I’ll be keeping secret since there are more reasons to this choice unless otherwise for those who don’t find this enough information. Good friends here will know that I’ve been super stressed about this choice for months now. Also regardless a good motivator for this choice is the fact that I’ll be graduating soon. -It’s possible that I may continue/come back for this in the future or keep it on GitHub only. I leave this here because even now I am still doubting myself about this change. But for now I’d check out other proxy sites like Incognito (Duce DOES a ton of updates frequently and he is the creator/developer of Ultraviolet so give him some love) :yayy_hopi: +It’s possible that I may continue/come back for this in the future or keep it on GitHub only. I leave this here because even now I am still doubting myself about this change. But for now I’d check out other proxy sites like Incognito (Duce DOES a ton of updates frequently and he is the creator/developer of Ultraviolet so give him some love) :yayy_hopi: Check out his Patreon also! For current HU patrons you will not be billed next month and the HU Patreon will be archived so head over to Duce’s patron so he can purchase more domains for Incognito. @@ -338,8 +355,8 @@ Emerald :HuTaoHype:
- ## More Information + This project is maintained by the Holy Unblocker LTS team and is an official flagship Titanium Network web proxy site. - https://github.com/titaniumnetwork-dev/ @@ -348,6 +365,7 @@ This project is maintained by the Holy Unblocker LTS team and is an official fla View the official website for more detail and credits. ### Web Proxy Sources: + This project currently uses Ultraviolet, Wisp, Womginx, and Rammerhead, linked below. - Ultraviolet @@ -357,7 +375,6 @@ This project currently uses Ultraviolet, Wisp, Womginx, and Rammerhead, linked b - Bare-Mux - TOMP Bare Server - ### Other Dependencies: - tsparticles diff --git a/TODO.md b/TODO.md index 4f93c928..840047b5 100644 --- a/TODO.md +++ b/TODO.md @@ -2,97 +2,99 @@ This will be our nonexhaustive todo list for Holy Unblocker LTS v6.x.x and above ## Code Cleanup - - [ ] Remove all current obfuscation in the source code. It needs to be dynamically obfuscated if anything, or not obfuscated at all. This option will be a config option on the server side before rendering with Express for a performance focus. Meta elements will have an additonal attribute indicating if they should be moved. This is to ensure a SEO source can be served by config or a source focused on pure censorship evasion. - - [ ] Optimize the stylesheets and the HTML layout. Add more proper commenting and redivide the code so that it's less hard on the eyes. - - [ ] Optimize the JS. This time it won't be in one line and will be somewhat thoroughly commented. - - [ ] Restructure navigation scripts to ensure updated proxy functionality is sanitized and effective - - [x] Particles.js automatically adjusting per display size - done - - [x] Fix routes.mjs throwing with incorrect paths - done - - [x] Create test script - done - - [x] XSS and fingerprinting protection (may need updates) - done - - [x] Update games navigation JS and page/change to JSON object system - done - - [ ] Ensure all the original submodules get added back to HU-Archive - - [x] Mobile support - (welcome screen only, partial/needs work) - - [ ] SEO overhaul adapted from the v2 SEO Guide format - - [ ] Randomize the __uv$config global, and optionally randomize the UV prefix and URL encoding via cookies +- [ ] Remove all current obfuscation in the source code. It needs to be dynamically obfuscated if anything, or not obfuscated at all. This option will be a config option on the server side before rendering with Express for a performance focus. Meta elements will have an additonal attribute indicating if they should be moved. This is to ensure a SEO source can be served by config or a source focused on pure censorship evasion. +- [ ] Optimize the stylesheets and the HTML layout. Add more proper commenting and redivide the code so that it's less hard on the eyes. +- [ ] Optimize the JS. This time it won't be in one line and will be somewhat thoroughly commented. +- [ ] Restructure navigation scripts to ensure updated proxy functionality is sanitized and effective +- [x] Particles.js automatically adjusting per display size - done +- [x] Fix routes.mjs throwing with incorrect paths - done +- [x] Create test script - done +- [x] XSS and fingerprinting protection (may need updates) - done +- [x] Update games navigation JS and page/change to JSON object system - done +- [ ] Ensure all the original submodules get added back to HU-Archive +- [x] Mobile support - (welcome screen only, partial/needs work) +- [ ] SEO overhaul adapted from the v2 SEO Guide format +- [ ] Randomize the \_\_uv$config global, and optionally randomize the UV prefix and URL encoding via cookies ## Proxy/Site Functionality - - [x] Ensure Ultraviolet is updated to support bare-mux and wisp - done - - [x] Add Rammerhead support - done - - [x] Fix slow Ultraviolet speeds despite being local; something on the backend?? - done - - [x] Fix Ultraviolet on Firefox - (partial/needs work) - - [ ] Adapt Applications page to use either Rammerhead or UV (for Reddit, YouTube, Discord) - - [x] libcurl, epoxy and all that fun stuff - done - - [x] socks5/tor routing option that can be configured (enabled) via either a cookie or pathname as a settings meny option - done - - [ ] Update games page content - - [ ] Update settings menu again to make more room for more features - - [x] Update csel.js (after Setting menu redesign) to support custom transports, icon swap, routing - done - - [x] Update csel.js to support network based adblocking (partial/needs work) - - [ ] Add a "website self-destruct" button to the settings menu - - [ ] Flesh out and rework the UV / bare client error page - - [ ] Update sw.js to support workerware (https://github.com/MercuryWorkshop/workerware) - - [ ] Omnibox autoupdate script (for the Google/Bing style auto suggest feature) - - [ ] Games library will feature 10000 items; 5000 flash games and 5000 other game types + +- [x] Ensure Ultraviolet is updated to support bare-mux and wisp - done +- [x] Add Rammerhead support - done +- [x] Fix slow Ultraviolet speeds despite being local; something on the backend?? - done +- [x] Fix Ultraviolet on Firefox - (partial/needs work) +- [ ] Adapt Applications page to use either Rammerhead or UV (for Reddit, YouTube, Discord) +- [x] libcurl, epoxy and all that fun stuff - done +- [x] socks5/tor routing option that can be configured (enabled) via either a cookie or pathname as a settings meny option - done +- [ ] Update games page content +- [ ] Update settings menu again to make more room for more features +- [x] Update csel.js (after Setting menu redesign) to support custom transports, icon swap, routing - done +- [x] Update csel.js to support network based adblocking (partial/needs work) +- [ ] Add a "website self-destruct" button to the settings menu +- [ ] Flesh out and rework the UV / bare client error page +- [ ] Update sw.js to support workerware (https://github.com/MercuryWorkshop/workerware) +- [ ] Omnibox autoupdate script (for the Google/Bing style auto suggest feature) +- [ ] Games library will feature 10000 items; 5000 flash games and 5000 other game types ## Site Redesign - - [x] Landing Cards - done - - [x] Change fonts to cleaner look - - [ ] Add more AOS interactions on scroll or hover - - [ ] Add subtle noise to background elements - - [ ] Update colors + add themes - - [ ] Toggle elements - - [ ] Other card options - - [ ] Radial blur elements - - [ ] Code standard examples - - [ ] Horizontal/general movement on scroll with AOS - - [ ] Showcase dev dependencies - - [ ] Update icons - - [x] Landing Page - (partial/needs work) - - [x] Settings Menu - (partial/needs work) - - [ ] More Dropdown Menu - - [ ] Web Proxies page - - [ ] Application page - - [ ] Hosting page - - [ ] Resources page - - [ ] Games Library page - - [ ] Emulators Library page - - [ ] Emu Library page - - [ ] Web Games page - - [ ] Flash Games page - - [ ] Documentation page - - [ ] FAQ page - - [ ] Credits page - - [ ] TOS page - - [x] Footer Design - (partial/needs work) - - [x] Header Design - (partial/needs work) +- [x] Landing Cards - done +- [x] Change fonts to cleaner look +- [ ] Add more AOS interactions on scroll or hover +- [ ] Add subtle noise to background elements +- [ ] Update colors + add themes +- [ ] Toggle elements +- [ ] Other card options +- [ ] Radial blur elements +- [ ] Code standard examples +- [ ] Horizontal/general movement on scroll with AOS +- [ ] Showcase dev dependencies +- [ ] Update icons +- [x] Landing Page - (partial/needs work) +- [x] Settings Menu - (partial/needs work) +- [ ] More Dropdown Menu +- [ ] Web Proxies page +- [ ] Application page +- [ ] Hosting page +- [ ] Resources page +- [ ] Games Library page +- [ ] Emulators Library page +- [ ] Emu Library page +- [ ] Web Games page +- [ ] Flash Games page +- [ ] Documentation page +- [ ] FAQ page +- [ ] Credits page +- [ ] TOS page +- [x] Footer Design - (partial/needs work) +- [x] Header Design - (partial/needs work) ## Community Requests - - [ ] Add [Quake WASM](https://github.com/GMH-Code/Quake-WASM) - - [ ] Celeste WASM - - [ ] Doom WASM + +- [ ] Add [Quake WASM](https://github.com/GMH-Code/Quake-WASM) +- [ ] Celeste WASM +- [ ] Doom WASM ## Changelog - - Added wisp support - - Fixed AD config setting being opt-out; ads are not implemented in the project however - - Added Rammerhead support (locally) - - Drastically updated visuals across the service and refactored stylesheets - - Bumped games page functionality - - Updated randomization scripts to ES6 syntax and implemented the alternative to RegEx string replacement - - Helmet for express implemented into backend - - Improved component handling via templates.mjs along with deletion of obsolete files that previously handled this standard in a poor format - - Fixed oddly slow speeds with Ultraviolet (as well as a general version bump to support epoxy-tls and bare-mux) - - Implemented testing scripts for an improved GitHub actions workflow by doing a quick test on proxy + site functionality - - Greatly optimized client-side scripts across the site with a new standard, and generally reworked to no longer leave global variables - - Changes to server.mjs with path logic and error handling - - Updated standards for common scripts - - libcurl and bare-as-module support added - - Deleted 5 JS scripts and moved lots of data into JSON files. Big reorganization. Games menu core scripts now nested inside of common.js utilizing a JSON system - - Massive updates to the Settings menu visually and functionality wise; added Bare-Mux support for swapping transports to work with Ultraviolet, default icons and selective adblocking + Tor on any proxy instances - - CSS Has been partially restructured for mobile support, and is now properly arranged into clearly labeled sections (for the most part) - - Incorporated makeshift domain blacklisting functionality into Ultraviolet, currently used for blocking ads if ads are disabled in settings - - Fleshed out the SEO with more descriptions and better labeling - - Switched to Fastify for serving content from the backend; a separate Express backend file is kept in case it's still needed - - Rammerhead is now locally built into the HU LTS repository - - Simplified the HU LTS setup process and added more default npm commands +- Added wisp support +- Fixed AD config setting being opt-out; ads are not implemented in the project however +- Added Rammerhead support (locally) +- Drastically updated visuals across the service and refactored stylesheets +- Bumped games page functionality +- Updated randomization scripts to ES6 syntax and implemented the alternative to RegEx string replacement +- Helmet for express implemented into backend +- Improved component handling via templates.mjs along with deletion of obsolete files that previously handled this standard in a poor format +- Fixed oddly slow speeds with Ultraviolet (as well as a general version bump to support epoxy-tls and bare-mux) +- Implemented testing scripts for an improved GitHub actions workflow by doing a quick test on proxy + site functionality +- Greatly optimized client-side scripts across the site with a new standard, and generally reworked to no longer leave global variables +- Changes to server.mjs with path logic and error handling +- Updated standards for common scripts +- libcurl and bare-as-module support added +- Deleted 5 JS scripts and moved lots of data into JSON files. Big reorganization. Games menu core scripts now nested inside of common.js utilizing a JSON system +- Massive updates to the Settings menu visually and functionality wise; added Bare-Mux support for swapping transports to work with Ultraviolet, default icons and selective adblocking + Tor on any proxy instances +- CSS Has been partially restructured for mobile support, and is now properly arranged into clearly labeled sections (for the most part) +- Incorporated makeshift domain blacklisting functionality into Ultraviolet, currently used for blocking ads if ads are disabled in settings +- Fleshed out the SEO with more descriptions and better labeling +- Switched to Fastify for serving content from the backend; a separate Express backend file is kept in case it's still needed +- Rammerhead is now locally built into the HU LTS repository +- Simplified the HU LTS setup process and added more default npm commands diff --git a/backend.js b/backend.js index 0062b594..52c8d3c1 100644 --- a/backend.js +++ b/backend.js @@ -1,3 +1,3 @@ (async () => { - await import("./src/server.mjs"); + await import('./src/server.mjs'); })(); diff --git a/ecosystem.config.js b/ecosystem.config.js index d60e563c..3aec1fae 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -1,22 +1,22 @@ module.exports = { apps: [ { - name: "HolyUB", - script: "./backend.js", + name: 'HolyUB', + script: './backend.js', env: { PORT: 8080, - NODE_ENV: "development", + NODE_ENV: 'development', }, env_production: { PORT: 8080, - NODE_ENV: "production", + NODE_ENV: 'production', }, - instances: "1", - exec_interpreter: "babel-node", - exec_mode: "fork", + instances: '1', + exec_interpreter: 'babel-node', + exec_mode: 'fork', autorestart: true, exp_backoff_restart_delay: 100, - cron_restart: "*/10 * * * *", + cron_restart: '*/10 * * * *', kill_timeout: 3000, watch: false, }, diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 00000000..92269ed1 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,16 @@ +module.exports = { + printWidth: 80, // Wrap lines at 80 characters + tabWidth: 2, // Use 2 spaces per indentation level + useTabs: false, // Use spaces instead of tabs + semi: true, // Add a semicolon at the end of every statement + singleQuote: true, // Use single quotes instead of double quotes + quoteProps: 'as-needed', // Only add quotes around object properties where required + jsxSingleQuote: false, // Use double quotes in JSX + trailingComma: 'es5', // Add trailing commas where valid in ES5 (objects, arrays, etc.) + bracketSpacing: true, // Print spaces between brackets in object literals + jsxBracketSameLine: false, // Put the `>` of a multi-line JSX element at the end of the last line + arrowParens: 'always', // Always include parentheses around arrow function arguments + htmlWhitespaceSensitivity: 'css', // Respect the default value of CSS display property + endOfLine: 'lf', // Use line feed only (\n) for newlines + embeddedLanguageFormatting: 'auto', // Format embedded code if Prettier can automatically identify it +}; diff --git a/proxyServiceValidator.js b/proxyServiceValidator.js index 25459382..56f7e13a 100644 --- a/proxyServiceValidator.js +++ b/proxyServiceValidator.js @@ -1,10 +1,10 @@ // This file is solely used for the automatically run GitHub job, which checks to // see if all HU LTS code is working properly (at least on an Ubuntu machine). -const axios = require("axios"); -const puppeteer = require("puppeteer"); +const axios = require('axios'); +const puppeteer = require('puppeteer'); -const testEndpoint = async url => { +const testEndpoint = async (url) => { try { const response = await axios.get(url); return response.status === 200; @@ -29,58 +29,58 @@ const testGeneratedUrl = async (url, headers) => { const testServerResponse = async () => { const endpoints = [ - "http://localhost:8080/", - "http://localhost:8080/test-404", - "http://localhost:8080/browsing", - "http://localhost:8080/rammerhead", - "http://localhost:8080/ultraviolet", - "http://localhost:8080/documentation", - "http://localhost:8080/questions", - "http://localhost:8080/s", - "http://localhost:8080/credits", - "http://localhost:8080/bookmarklets", - "http://localhost:8080/terms", - "http://localhost:8080/games", - "http://localhost:8080/web-games", - "http://localhost:8080/emulators", - "http://localhost:8080/flash-games", - "http://localhost:8080/retro-games", - "http://localhost:8080/youtube", - "http://localhost:8080/apps", - "http://localhost:8080/flash", - "http://localhost:8080/webretro", - "http://localhost:8080/vibe-os", - "http://localhost:8080/assets/js/particlesjs/particles.js", - "http://localhost:8080/assets/js/bareTransport.js", - "http://localhost:8080/assets/js/card.js", - "http://localhost:8080/assets/js/common-16451543478.js", - "http://localhost:8080/assets/js/csel.js", - "http://localhost:8080/assets/js/register-sw.js", - "http://localhost:8080/assets/json/emu-nav.json", - "http://localhost:8080/assets/json/blacklist.json", - "http://localhost:8080/assets/json/emulib-nav.json", - "http://localhost:8080/assets/json/flash-nav.json", - "http://localhost:8080/assets/json/h5-nav.json", - "http://localhost:8080/assets/json/links.json", - "http://localhost:8080/baremux/index.js", - "http://localhost:8080/baremux/worker.js", - "http://localhost:8080/epoxy/index.mjs", - "http://localhost:8080/uv/uv.bundle.js", - "http://localhost:8080/uv/sw.js", - "http://localhost:8080/uv/uv.config.js", - "http://localhost:8080/uv/workerware.js", - "http://localhost:8080/uv/WWError.js" + 'http://localhost:8080/', + 'http://localhost:8080/test-404', + 'http://localhost:8080/browsing', + 'http://localhost:8080/rammerhead', + 'http://localhost:8080/ultraviolet', + 'http://localhost:8080/documentation', + 'http://localhost:8080/questions', + 'http://localhost:8080/s', + 'http://localhost:8080/credits', + 'http://localhost:8080/bookmarklets', + 'http://localhost:8080/terms', + 'http://localhost:8080/games', + 'http://localhost:8080/web-games', + 'http://localhost:8080/emulators', + 'http://localhost:8080/flash-games', + 'http://localhost:8080/retro-games', + 'http://localhost:8080/youtube', + 'http://localhost:8080/apps', + 'http://localhost:8080/flash', + 'http://localhost:8080/webretro', + 'http://localhost:8080/vibe-os', + 'http://localhost:8080/assets/js/particlesjs/particles.js', + 'http://localhost:8080/assets/js/bareTransport.js', + 'http://localhost:8080/assets/js/card.js', + 'http://localhost:8080/assets/js/common-16451543478.js', + 'http://localhost:8080/assets/js/csel.js', + 'http://localhost:8080/assets/js/register-sw.js', + 'http://localhost:8080/assets/json/emu-nav.json', + 'http://localhost:8080/assets/json/blacklist.json', + 'http://localhost:8080/assets/json/emulib-nav.json', + 'http://localhost:8080/assets/json/flash-nav.json', + 'http://localhost:8080/assets/json/h5-nav.json', + 'http://localhost:8080/assets/json/links.json', + 'http://localhost:8080/baremux/index.js', + 'http://localhost:8080/baremux/worker.js', + 'http://localhost:8080/epoxy/index.mjs', + 'http://localhost:8080/uv/uv.bundle.js', + 'http://localhost:8080/uv/sw.js', + 'http://localhost:8080/uv/uv.config.js', + 'http://localhost:8080/uv/workerware.js', + 'http://localhost:8080/uv/WWError.js', ]; const results = await Promise.all(endpoints.map(testEndpoint)); const allPassed = results.every((result) => result); if (allPassed) { - console.log("All endpoints responded with status code 200. Test passed."); + console.log('All endpoints responded with status code 200. Test passed.'); await testCommonJSOnPage(); } else { console.error( - "One or more endpoints failed to respond with status code 200. Test failed." + 'One or more endpoints failed to respond with status code 200. Test failed.' ); process.exitCode = 1; } @@ -89,9 +89,9 @@ const testServerResponse = async () => { const testCommonJSOnPage = async () => { const browser = await puppeteer.launch({ args: [ - "--enable-features=NetworkService", - "--enable-features=ServiceWorker", - "--enable-features=InsecureOrigins", + '--enable-features=NetworkService', + '--enable-features=ServiceWorker', + '--enable-features=InsecureOrigins', ], headless: true, ignoreHTTPSErrors: true, @@ -102,73 +102,75 @@ const testCommonJSOnPage = async () => { const getHeaders = async () => { const headers = {}; - headers["User-Agent"] = await page.evaluate(() => navigator.userAgent); - headers["Referer"] = await page.evaluate(() => window.location.href); + headers['User-Agent'] = await page.evaluate(() => navigator.userAgent); + headers['Referer'] = await page.evaluate(() => window.location.href); return headers; }; const testRammerhead = async () => { - await page.goto("http://localhost:8080/rammerhead"); + await page.goto('http://localhost:8080/rammerhead'); const testResults = await page.evaluate(async () => { const results = {}; await new Promise((resolve) => { - if (document.readyState === "complete") { + if (document.readyState === 'complete') { resolve(); } else { - window.addEventListener("load", resolve); + window.addEventListener('load', resolve); } }); -// Locate the omnibox element on the Rammerhead page. - let omnibox = document.getElementById("pr-rh"); - omnibox = omnibox && omnibox.querySelector("input[type=text]"); + // Locate the omnibox element on the Rammerhead page. + let omnibox = document.getElementById('pr-rh'); + omnibox = omnibox && omnibox.querySelector('input[type=text]'); if (omnibox) { try { -// Send an artificial input to the omnibox. The omnibox will create -// a proxy URL and leave it as the input value in response. - const urlPath = "example.com"; + // Send an artificial input to the omnibox. The omnibox will create + // a proxy URL and leave it as the input value in response. + const urlPath = 'example.com'; omnibox.value = urlPath; await omnibox.dispatchEvent( - new KeyboardEvent("keydown", {code: "Validator Test"}) + new KeyboardEvent('keydown', { code: 'Validator Test' }) ); -// Wait up to 5 seconds for the omnibox to finish updating. - const loadUrl = new Promise(resolve => { - if (omnibox.value !== urlPath) resolve(omnibox.value); - else omnibox.addEventListener("change", () => resolve(omnibox.value)); - }), - timeout = new Promise(resolve => { - setTimeout(() => resolve(omnibox.value), 40000); - }), - -// Record the proxy URL that the omnibox left here. - rammerheadUrl = await Promise.race([loadUrl, timeout]); - console.log("Generated Rammerhead URL:", rammerheadUrl); - results.rammerhead = rammerheadUrl ? rammerheadUrl : "failure"; + // Wait up to 5 seconds for the omnibox to finish updating. + const loadUrl = new Promise((resolve) => { + if (omnibox.value !== urlPath) resolve(omnibox.value); + else + omnibox.addEventListener('change', () => + resolve(omnibox.value) + ); + }), + timeout = new Promise((resolve) => { + setTimeout(() => resolve(omnibox.value), 40000); + }), + // Record the proxy URL that the omnibox left here. + rammerheadUrl = await Promise.race([loadUrl, timeout]); + console.log('Generated Rammerhead URL:', rammerheadUrl); + results.rammerhead = rammerheadUrl ? rammerheadUrl : 'failure'; } catch (e) { - results.rammerhead = "failure: " + e.message; + results.rammerhead = 'failure: ' + e.message; } } else { - results.goProx = "not defined"; + results.goProx = 'not defined'; } return results; }); - console.log("Rammerhead test results:", testResults); + console.log('Rammerhead test results:', testResults); const headers = await getHeaders(); const rammerheadTestPassed = - testResults.rammerhead !== "failure" && + testResults.rammerhead !== 'failure' && (await testGeneratedUrl(testResults.rammerhead, headers)); console.log( `Rammerhead test result: ${ - rammerheadTestPassed ? "success" : "failure" + rammerheadTestPassed ? 'success' : 'failure' }` ); @@ -211,90 +213,96 @@ xx xx */ - - const testUltraviolet = async () => { - await page.goto("http://localhost:8080/ultraviolet"); + await page.goto('http://localhost:8080/ultraviolet'); const testResults = await page.evaluate(async () => { const results = [{}, {}]; await new Promise((resolve) => { + const waitForDocument = () => + document.readyState === 'complete' + ? resolve() + : window.addEventListener('load', resolve); - const waitForDocument = () => document.readyState === "complete" - ? resolve() - : window.addEventListener("load", resolve); - -// Wait until a service worker is registered before continuing. -// Also make sure the document is loaded. - const waitForWorker = async () => setTimeout(async () => { - (await navigator.serviceWorker.getRegistrations()).length >= 1 - ? waitForDocument() - : waitForWorker() - }, 1000); + // Wait until a service worker is registered before continuing. + // Also make sure the document is loaded. + const waitForWorker = async () => + setTimeout(async () => { + (await navigator.serviceWorker.getRegistrations()).length >= 1 + ? waitForDocument() + : waitForWorker(); + }, 1000); waitForWorker(); }); -// Locate the omnibox element on the Ultraviolet page. - let omnibox = document.getElementById("pr-uv"); - omnibox = omnibox && omnibox.querySelector("input[type=text]"); + // Locate the omnibox element on the Ultraviolet page. + let omnibox = document.getElementById('pr-uv'); + omnibox = omnibox && omnibox.querySelector('input[type=text]'); if (omnibox) { -// For the hacky URL test, use the URL page's EXACT title. + // For the hacky URL test, use the URL page's EXACT title. const website = { - path: "example.com", - title: "Example Domain" + path: 'example.com', + title: 'Example Domain', }; try { -// Send an artificial input to the omnibox. The omnibox will create -// a proxy URL and leave it as the input value in response. + // Send an artificial input to the omnibox. The omnibox will create + // a proxy URL and leave it as the input value in response. omnibox.value = website.path; await omnibox.dispatchEvent( - new KeyboardEvent("keydown", {code: "Validator Test"}) + new KeyboardEvent('keydown', { code: 'Validator Test' }) ); -// Record the proxy URL that the omnibox left here. + // Record the proxy URL that the omnibox left here. const generatedUrl = omnibox.value; - console.log("Generated Ultraviolet URL:", generatedUrl); - results[0].ultraviolet = generatedUrl ? generatedUrl : "failure"; + console.log('Generated Ultraviolet URL:', generatedUrl); + results[0].ultraviolet = generatedUrl ? generatedUrl : 'failure'; -// Test to see if the document title for example.com has loaded, -// by appending an IFrame to the document and grabbing its content. + // Test to see if the document title for example.com has loaded, + // by appending an IFrame to the document and grabbing its content. const testGeneratedUrlHacky = async (url) => { let result = false; - const exampleIFrame = document.createElement("iframe"); - const waitForDocument = new Promise(resolve => { + const exampleIFrame = document.createElement('iframe'); + const waitForDocument = new Promise((resolve) => { document.documentElement.appendChild(exampleIFrame); - exampleIFrame.addEventListener("load", () => { - result = exampleIFrame.contentWindow.document.title === website.title; + exampleIFrame.addEventListener('load', () => { + result = + exampleIFrame.contentWindow.document.title === + website.title; resolve(); }); }); exampleIFrame.src = url; - exampleIFrame.style.display = "none"; + exampleIFrame.style.display = 'none'; await waitForDocument; return result; }; - results[1].uvTestPassed = await testGeneratedUrlHacky(results[0].ultraviolet); + results[1].uvTestPassed = await testGeneratedUrlHacky( + results[0].ultraviolet + ); } catch (e) { - results[0].ultraviolet = "failure: " + e.message; + results[0].ultraviolet = 'failure: ' + e.message; } } else { - results[0].goProx = "not defined"; + results[0].goProx = 'not defined'; } return results; }); - console.log("Ultraviolet test results:", testResults[0]); + console.log('Ultraviolet test results:', testResults[0]); - if (testResults[0].ultraviolet && testResults[0].ultraviolet !== "failure") { + if ( + testResults[0].ultraviolet && + testResults[0].ultraviolet !== 'failure' + ) { const uvTestPassed = testResults[1].uvTestPassed; console.log( - `Ultraviolet test result: ${uvTestPassed ? "success" : "failure"}` + `Ultraviolet test result: ${uvTestPassed ? 'success' : 'failure'}` ); return uvTestPassed; } else { @@ -308,14 +316,14 @@ xx xx const ultravioletPassed = await testUltraviolet(); if (rammerheadPassed && ultravioletPassed) { - console.log("Both tests passed."); + console.log('Both tests passed.'); process.exitCode = 0; } else { - console.error("Tests failed."); + console.error('Tests failed.'); process.exitCode = 1; } } catch (error) { - console.error("Error in testCommonJSOnPage:", error.message); + console.error('Error in testCommonJSOnPage:', error.message); process.exitCode = 1; } finally { await browser.close(); diff --git a/run-command.mjs b/run-command.mjs index 6de824f5..08f8d33c 100644 --- a/run-command.mjs +++ b/run-command.mjs @@ -7,129 +7,136 @@ import ecosystem from './ecosystem.config.js'; // Some necessary constants are copied over from /src/server.mjs. const config = Object.freeze( - JSON.parse(await readFile(new URL("./src/config.json", import.meta.url))) + JSON.parse(await readFile(new URL('./src/config.json', import.meta.url))) ), ecosystemConfig = Object.freeze( - ecosystem.apps.find(app => app.name === "HolyUB") || ecosystem.apps[0] + ecosystem.apps.find((app) => app.name === 'HolyUB') || ecosystem.apps[0] ); -const serverUrl = (base => { +const serverUrl = ((base) => { try { base = new URL(config.host); } catch (e) { - base = new URL("http://a"); + base = new URL('http://a'); base.host = config.host; } - base.port = ecosystemConfig[ config.production ? "env_production" : "env" ].PORT; + base.port = + ecosystemConfig[config.production ? 'env_production' : 'env'].PORT; return Object.freeze(base); })(); -const shutdown = fileURLToPath(new URL("./src/.shutdown", import.meta.url)); +const shutdown = fileURLToPath(new URL('./src/.shutdown', import.meta.url)); // Run each command line argument passed after node run-command.mjs. // Commands are defined in the switch case statement below. for (let i = 2; i < process.argv.length; i++) switch (process.argv[i]) { -// Commmand to boot up the server. Use PM2 to run if production is true in the -// config file. - case "start": + // Commmand to boot up the server. Use PM2 to run if production is true in the + // config file. + case 'start': if (config.production) - exec("npx pm2 start ecosystem.config.js --env production", + exec( + 'npx pm2 start ecosystem.config.js --env production', (error, stdout) => { if (error) throw error; console.log(stdout); } ); -// Handle setup on Windows differently from platforms with POSIX-compliant shells. -// This should run the server as a background process. - else if (process.platform === "win32") + // Handle setup on Windows differently from platforms with POSIX-compliant shells. + // This should run the server as a background process. + else if (process.platform === 'win32') exec('START /MIN "" node backend.js', (error, stdout) => { if (error) throw error; console.log(stdout); }); -// The following approach (and similar approaches) will not work on Windows, -// because exiting this program will also terminate backend.js on Windows. + // The following approach (and similar approaches) will not work on Windows, + // because exiting this program will also terminate backend.js on Windows. else { const server = fork( - fileURLToPath(new URL("./backend.js", import.meta.url)), - {detached: true} + fileURLToPath(new URL('./backend.js', import.meta.url)), + { detached: true } ); server.unref(); server.disconnect(); } break; -// Stop the server. Make a temporary file that the server will check for if told -// to shut down. This is done by sending a GET request to the server. - case "stop": { - await writeFile(shutdown, ""); + // Stop the server. Make a temporary file that the server will check for if told + // to shut down. This is done by sending a GET request to the server. + case 'stop': { + await writeFile(shutdown, ''); let timeoutId = undefined; try { -// Give the server 5 seconds to respond, otherwise cancel this and throw an -// error to the console. The fetch request will also throw an error immediately -// if checking the server on localhost and the port is unused. + // Give the server 5 seconds to respond, otherwise cancel this and throw an + // error to the console. The fetch request will also throw an error immediately + // if checking the server on localhost and the port is unused. const response = await Promise.race([ - fetch(new URL("/test-shutdown", serverUrl)), - new Promise(resolve => { - timeoutId = setTimeout(() => { - resolve("Error"); - }, 5000); - }) + fetch(new URL('/test-shutdown', serverUrl)), + new Promise((resolve) => { + timeoutId = setTimeout(() => { + resolve('Error'); + }, 5000); + }), ]); clearTimeout(timeoutId); - if (response === "Error") throw new Error("Server is unresponsive."); + if (response === 'Error') throw new Error('Server is unresponsive.'); } catch (e) { -// Check if this is the error thrown by the fetch request for an unused port. -// Don't print the unused port error, since nothing has actually broken. + // Check if this is the error thrown by the fetch request for an unused port. + // Don't print the unused port error, since nothing has actually broken. if (e instanceof TypeError) clearTimeout(timeoutId); else console.error(e); await unlink(shutdown); } -// Do not run this if Node will be killed later in this script. It will fail. - if (config.production && !process.argv.slice(i + 1).includes("kill")) - exec("npx pm2 stop ecosystem.config.js", (error, stdout) => { + // Do not run this if Node will be killed later in this script. It will fail. + if (config.production && !process.argv.slice(i + 1).includes('kill')) + exec('npx pm2 stop ecosystem.config.js', (error, stdout) => { if (error) throw error; console.log(stdout); }); break; } - case "build": { - const dist = fileURLToPath(new URL("./views/dist", import.meta.url)); - await rm(dist, {force: true, recursive: true}); + case 'build': { + const dist = fileURLToPath(new URL('./views/dist', import.meta.url)); + await rm(dist, { force: true, recursive: true }); await mkdir(dist); await build({ entryPoints: [ - "./views/uv/**/*.js", - "./views/assets/js/**/*.js", - "./views/assets/css/**/*.css" + './views/uv/**/*.js', + './views/assets/js/**/*.js', + './views/assets/css/**/*.css', ], - platform: "browser", + platform: 'browser', sourcemap: true, bundle: true, minify: true, - external: ["*.png", "*.jpg", "*.jpeg", "*.webp", "*.svg"], - outdir: dist + external: ['*.png', '*.jpg', '*.jpeg', '*.webp', '*.svg'], + outdir: dist, }); break; } -// Kill all node processes and fully reset PM2. To be used for debugging. -// Using npx pm2 monit, or npx pm2 list in the terminal will also bring up -// more PM2 debugging tools. - case "kill": - if (process.platform === "win32") - exec("( npx pm2 delete ecosystem.config.js ) ; taskkill /F /IM node*", - (error, stdout) => {console.log(stdout)} + // Kill all node processes and fully reset PM2. To be used for debugging. + // Using npx pm2 monit, or npx pm2 list in the terminal will also bring up + // more PM2 debugging tools. + case 'kill': + if (process.platform === 'win32') + exec( + '( npx pm2 delete ecosystem.config.js ) ; taskkill /F /IM node*', + (error, stdout) => { + console.log(stdout); + } ); - else exec("npx pm2 delete ecosystem.config.js; pkill node", - (error, stdout) => {console.log(stdout)} + else + exec( + 'npx pm2 delete ecosystem.config.js; pkill node', + (error, stdout) => { + console.log(stdout); + } ); break; -// No default case. - + // No default case. } - process.exitCode = 0; diff --git a/src/data.json b/src/data.json index 91c2e5f9..8449f974 100644 --- a/src/data.json +++ b/src/data.json @@ -18,7 +18,5 @@ "splash": [ "This version is the public LTS build of the web proxy service project and may be hosted unofficially. Check your domain or jo‌­in t‌he T⁡­N Di‌s­co⁡­rd for official pr­iva️te sit‌e lin­ks for your safety." ], - "version": [ - "6.3.7" - ] + "version": ["6.3.7"] } diff --git a/src/express.mjs b/src/express.mjs index 20f8908e..b151a2fa 100644 --- a/src/express.mjs +++ b/src/express.mjs @@ -1,89 +1,85 @@ -import { paintSource, tryReadFile } from "./randomization.mjs"; -import loadTemplates from "./templates.mjs"; -import pkg from "./routes.mjs"; -import { readFile } from "fs/promises"; -import path from "path"; -import express from "express"; -import helmet from "helmet"; -import http from "http"; -import createRammerhead from "rammerhead/src/server/index.js"; -import wisp from "wisp-server-node"; -import { epoxyPath } from "@mercuryworkshop/epoxy-transport"; -import { libcurlPath } from "@mercuryworkshop/libcurl-transport"; -import { bareModulePath } from "@mercuryworkshop/bare-as-module3"; -import { baremuxPath } from "@mercuryworkshop/bare-mux/node"; -import { uvPath } from "@titaniumnetwork-dev/ultraviolet"; +import { paintSource, tryReadFile } from './randomization.mjs'; +import loadTemplates from './templates.mjs'; +import pkg from './routes.mjs'; +import { readFile } from 'fs/promises'; +import path from 'path'; +import express from 'express'; +import helmet from 'helmet'; +import http from 'http'; +import createRammerhead from 'rammerhead/src/server/index.js'; +import wisp from 'wisp-server-node'; +import { epoxyPath } from '@mercuryworkshop/epoxy-transport'; +import { libcurlPath } from '@mercuryworkshop/libcurl-transport'; +import { bareModulePath } from '@mercuryworkshop/bare-as-module3'; +import { baremuxPath } from '@mercuryworkshop/bare-mux/node'; +import { uvPath } from '@titaniumnetwork-dev/ultraviolet'; // import { createBareServer } from "@tomphttp/bare-server-node"; const config = JSON.parse( - await readFile(new URL("./config.json", import.meta.url)) -), -{ pages, text404 } = pkg, -__dirname = path.resolve(), -port = process.env.PORT || config.port, -app = express(), -router = express.Router(), -// bare = createBareServer("/bare/"), -rh = createRammerhead(); + await readFile(new URL('./config.json', import.meta.url)) + ), + { pages, text404 } = pkg, + __dirname = path.resolve(), + port = process.env.PORT || config.port, + app = express(), + router = express.Router(), + // bare = createBareServer("/bare/"), + rh = createRammerhead(); const rammerheadScopes = [ - "/rammerhead.js", - "/hammerhead.js", - "/transport-worker.js", - "/task.js", - "/iframe-task.js", - "/worker-hammerhead.js", - "/messaging", - "/sessionexists", - "/deletesession", - "/newsession", - "/editsession", - "/needpassword", - "/syncLocalStorage", - "/api/shuffleDict", - "/mainport", + '/rammerhead.js', + '/hammerhead.js', + '/transport-worker.js', + '/task.js', + '/iframe-task.js', + '/worker-hammerhead.js', + '/messaging', + '/sessionexists', + '/deletesession', + '/newsession', + '/editsession', + '/needpassword', + '/syncLocalStorage', + '/api/shuffleDict', + '/mainport', ]; const rammerheadSession = /^\/[a-z0-9]{32}/, - -shouldRouteRh = req => { - const url = new URL(req.url, "http://0.0.0.0"); - return ( - rammerheadScopes.includes(url.pathname) || - rammerheadSession.test(url.pathname) - ); -}, - -routeRhRequest = (req, res) => { - rh.emit("request", req, res); -}, - -routeRhUpgrade = (req, socket, head) => { - rh.emit("upgrade", req, socket, head); -}, - -server = http.createServer((req, res) => { -/* + shouldRouteRh = (req) => { + const url = new URL(req.url, 'http://0.0.0.0'); + return ( + rammerheadScopes.includes(url.pathname) || + rammerheadSession.test(url.pathname) + ); + }, + routeRhRequest = (req, res) => { + rh.emit('request', req, res); + }, + routeRhUpgrade = (req, socket, head) => { + rh.emit('upgrade', req, socket, head); + }, + server = http.createServer((req, res) => { + /* if (bare.shouldRoute(req)) { bare.routeRequest(req, res); } else */ - if (shouldRouteRh(req)) { - routeRhRequest(req, res); - } else { - app(req, res); - } -}); + if (shouldRouteRh(req)) { + routeRhRequest(req, res); + } else { + app(req, res); + } + }); -server.on("upgrade", (req, socket, head) => { -/* +server.on('upgrade', (req, socket, head) => { + /* if (bare.shouldRoute(req)) { bare.routeUpgrade(req, socket, head); } else */ if (shouldRouteRh(req)) { routeRhUpgrade(req, socket, head); - } else if (req.url.endsWith("/wisp/")) { + } else if (req.url.endsWith('/wisp/')) { wisp.routeRequest(req, socket, head); } }); @@ -99,33 +95,35 @@ app.use( // This takes one of those files and displays it for a site visitor. // Query strings like /?j are converted into paths like /views/hidden.html // back here. Which query string converts to what is defined in routes.mjs. -router.get("/", async (req, res) => - res.send( - paintSource( - loadTemplates( - tryReadFile( - path.join(__dirname, - "views", -// Return the error page if the query is not found in -// routes.mjs. Also set index as the default page. - "/?".indexOf(req.url) ? pages[Object.keys(req.query)[0]] || "error.html" : pages.index - ) - ) - ) +router.get('/', async (req, res) => + res.send( + paintSource( + loadTemplates( + tryReadFile( + path.join( + __dirname, + 'views', + // Return the error page if the query is not found in + // routes.mjs. Also set index as the default page. + '/?'.indexOf(req.url) + ? pages[Object.keys(req.query)[0]] || 'error.html' + : pages.index + ) ) + ) ) + ) ); - app.use(router); -app.use(express.static(path.join(__dirname, "views"))); -app.use("/uv/", express.static(uvPath)); -app.use("/epoxy/", express.static(epoxyPath)); -app.use("/libcurl/", express.static(libcurlPath)); -app.use("/bareasmodule/", express.static(bareModulePath)); -app.use("/baremux/", express.static(baremuxPath)); +app.use(express.static(path.join(__dirname, 'views'))); +app.use('/uv/', express.static(uvPath)); +app.use('/epoxy/', express.static(epoxyPath)); +app.use('/libcurl/', express.static(libcurlPath)); +app.use('/bareasmodule/', express.static(bareModulePath)); +app.use('/baremux/', express.static(baremuxPath)); -app.disable("x-powered-by"); +app.disable('x-powered-by'); // Redundant code since 404 is handled elsewhere; left here as insurance. app.use((req, res) => { @@ -133,4 +131,4 @@ app.use((req, res) => { }); server.listen(port); -console.log("Holy Unblocker is listening on port " + port + "."); +console.log('Holy Unblocker is listening on port ' + port + '.'); diff --git a/src/randomization.mjs b/src/randomization.mjs index 7675e4bd..89aebe61 100644 --- a/src/randomization.mjs +++ b/src/randomization.mjs @@ -1,15 +1,15 @@ -import pkg from "./routes.mjs"; -import { existsSync, readFileSync } from "fs"; +import pkg from './routes.mjs'; +import { existsSync, readFileSync } from 'fs'; export { paintSource, preloaded404, tryReadFile }; const { - cookingInserts, - vegetables, - charRandom, - splashRandom, - cacheBustList, - VersionValue, - text404, - } = pkg; + cookingInserts, + vegetables, + charRandom, + splashRandom, + cacheBustList, + VersionValue, + text404, +} = pkg; // Below are lots of function definitions used to obfuscate the website. // This makes the website harder to properly categorize, as its source code @@ -19,29 +19,29 @@ const randomListItem = (lis) => () => lis[(Math.random() * lis.length) | 0], getRandomChar = randomListItem(charRandom), insertCharset = (str) => str.replace(charset, getRandomChar), getRandomSplash = randomListItem(splashRandom), - hutaoInsert = (str) => str.replaceAll("", getRandomSplash), - versionInsert = (str) => str.replaceAll("", VersionValue), + hutaoInsert = (str) => str.replaceAll('', getRandomSplash), + versionInsert = (str) => str.replaceAll('', VersionValue), getCookingText = () => `${randomListItem(cookingInserts)()}`, insertCooking = (str) => str.replaceAll( - "", + '', getCookingText ), -// This one isn't for obfuscation; it's just for dealing with cache issues. + // This one isn't for obfuscation; it's just for dealing with cache issues. cacheBusting = (str) => { for (let item of Object.entries(cacheBustList)) str = str.replaceAll(item[0], item[1]); return str; }, -// Apply the final obfuscation changes to an entire file. + // Apply the final obfuscation changes to an entire file. paintSource = (str) => insertCharset(hutaoInsert(versionInsert(insertCooking(cacheBusting(str))))), -// Use this instead of text404 for a preloaded error page. + // Use this instead of text404 for a preloaded error page. preloaded404 = paintSource(text404), -// Grab the text content of a file. Ensure the file is a string. + // Grab the text content of a file. Ensure the file is a string. tryReadFile = (file) => - existsSync(file + "") ? readFileSync(file + "", "utf8") : preloaded404; + existsSync(file + '') ? readFileSync(file + '', 'utf8') : preloaded404; /* // All of this is now old code. diff --git a/src/routes.mjs b/src/routes.mjs index 9e201aa3..937d9720 100644 --- a/src/routes.mjs +++ b/src/routes.mjs @@ -1,75 +1,75 @@ -import { readFileSync } from "fs"; -import path from "path"; -import { readFile } from "fs/promises"; +import { readFileSync } from 'fs'; +import path from 'path'; +import { readFile } from 'fs/promises'; const insert = JSON.parse( - await readFile(new URL("./data.json", import.meta.url)) + await readFile(new URL('./data.json', import.meta.url)) ); const __dirname = path.resolve(); const text404 = readFileSync( - path.normalize(__dirname + "/views/error.html"), - "utf8" + path.normalize(__dirname + '/views/error.html'), + 'utf8' ); const pages = { - index: "index.html", - "manifest.json": "manifest.json", - "test-404": "error.html", + index: 'index.html', + 'manifest.json': 'manifest.json', + 'test-404': 'error.html', /* Main */ - documentation: "docs.html", - questions: "faq.html", - s: "pages/frame.html", - browsing: "pages/surf.html", - credits: "pages/nav/credits.html", - bookmarklets: "pages/nav/bookmarklets.html", - terms: "pages/nav/terms.html", + documentation: 'docs.html', + questions: 'faq.html', + s: 'pages/frame.html', + browsing: 'pages/surf.html', + credits: 'pages/nav/credits.html', + bookmarklets: 'pages/nav/bookmarklets.html', + terms: 'pages/nav/terms.html', /* Games */ - games: "pages/nav/gtools.html", - "web-games": "pages/nav/games5.html", - emulators: "pages/nav/emulators.html", - "flash-games": "pages/nav/flash.html", - "retro-games": "pages/nav/emulibrary.html", + games: 'pages/nav/gtools.html', + 'web-games': 'pages/nav/games5.html', + emulators: 'pages/nav/emulators.html', + 'flash-games': 'pages/nav/flash.html', + 'retro-games': 'pages/nav/emulibrary.html', /* Proxies */ - ultraviolet: "pages/proxnav/ultraviolet.html", - rammerhead: "pages/proxnav/rammerhead.html", + ultraviolet: 'pages/proxnav/ultraviolet.html', + rammerhead: 'pages/proxnav/rammerhead.html', /* Proxy Presets */ - youtube: "pages/proxnav/preset/youtube.html", - apps: "pages/proxnav/preset/applications.html", + youtube: 'pages/proxnav/preset/youtube.html', + apps: 'pages/proxnav/preset/applications.html', /* Misc */ - flash: "archive/gfiles/flash/index.html", - webretro: "archive/gfiles/rarch/index.html", - "vibe-os": "archive/vibeOS/index.html", + flash: 'archive/gfiles/flash/index.html', + webretro: 'archive/gfiles/rarch/index.html', + 'vibe-os': 'archive/vibeOS/index.html', }; const externalPages = { github: { - default: "https://github.com/QuiteAFancyEmerald/Holy-Unblocker", - aos: "https://github.com/michalsnik/aos", - "bare-module": "https://github.com/motortruck1221/bare-as-module3", - "bare-mux": "https://github.com/MercuryWorkshop/bare-mux", - epoxy: "https://github.com/MercuryWorkshop/epoxy-tls", - fastify: "https://github.com/fastify/fastify", - "font-awesome": "https://github.com/FortAwesome/Font-Awesome", - "libcurl-js": "https://github.com/ading2210/libcurl.js", - "nord-theme": "https://github.com/nordtheme", - ultraviolet: "https://github.com/titaniumnetwork-dev/Ultraviolet", - wisp: "https://github.com/MercuryWorkshop/wisp-protocol" + default: 'https://github.com/QuiteAFancyEmerald/Holy-Unblocker', + aos: 'https://github.com/michalsnik/aos', + 'bare-module': 'https://github.com/motortruck1221/bare-as-module3', + 'bare-mux': 'https://github.com/MercuryWorkshop/bare-mux', + epoxy: 'https://github.com/MercuryWorkshop/epoxy-tls', + fastify: 'https://github.com/fastify/fastify', + 'font-awesome': 'https://github.com/FortAwesome/Font-Awesome', + 'libcurl-js': 'https://github.com/ading2210/libcurl.js', + 'nord-theme': 'https://github.com/nordtheme', + ultraviolet: 'https://github.com/titaniumnetwork-dev/Ultraviolet', + wisp: 'https://github.com/MercuryWorkshop/wisp-protocol', }, - "titaniumnetwork-documentation": "https://docs.titaniumnetwork.org", - "rammerhead-discord": "https://discord.gg/VNT4E7gN5Y" + 'titaniumnetwork-documentation': 'https://docs.titaniumnetwork.org', + 'rammerhead-discord': 'https://discord.gg/VNT4E7gN5Y', }; const cookingInserts = insert.content, -vegetables = insert.keywords, -charRandom = insert.chars, -splashRandom = insert.splash, -VersionValue = insert.version, -cacheBustList = { - "styles.css": "styles-1644738239.css", - "common.js": "common-16451543478.js", -}; + vegetables = insert.keywords, + charRandom = insert.chars, + splashRandom = insert.splash, + VersionValue = insert.version, + cacheBustList = { + 'styles.css': 'styles-1644738239.css', + 'common.js': 'common-16451543478.js', + }; export default { pages, diff --git a/src/server.mjs b/src/server.mjs index 27f0bc48..485649b7 100644 --- a/src/server.mjs +++ b/src/server.mjs @@ -1,15 +1,15 @@ import Fastify from 'fastify'; import { createServer } from 'node:http'; import wisp from 'wisp-server-node'; -import createRammerhead from "../lib/rammerhead/src/server/index.js"; -import { epoxyPath } from "@mercuryworkshop/epoxy-transport"; -import { libcurlPath } from "@mercuryworkshop/libcurl-transport"; -import { bareModulePath } from "@mercuryworkshop/bare-as-module3"; -import { baremuxPath } from "@mercuryworkshop/bare-mux/node"; -import { uvPath } from "@titaniumnetwork-dev/ultraviolet"; +import createRammerhead from '../lib/rammerhead/src/server/index.js'; +import { epoxyPath } from '@mercuryworkshop/epoxy-transport'; +import { libcurlPath } from '@mercuryworkshop/libcurl-transport'; +import { bareModulePath } from '@mercuryworkshop/bare-as-module3'; +import { baremuxPath } from '@mercuryworkshop/bare-mux/node'; +import { uvPath } from '@titaniumnetwork-dev/ultraviolet'; import fastifyHelmet from '@fastify/helmet'; import fastifyStatic from '@fastify/static'; -import pageRoutes from "./routes.mjs"; +import pageRoutes from './routes.mjs'; import { readFile } from 'node:fs/promises'; import path from 'node:path'; import { paintSource, preloaded404, tryReadFile } from './randomization.mjs'; @@ -19,10 +19,10 @@ import { existsSync, unlinkSync } from 'node:fs'; import ecosystem from '../ecosystem.config.js'; const config = Object.freeze( - JSON.parse(await readFile(new URL("./config.json", import.meta.url))) + JSON.parse(await readFile(new URL('./config.json', import.meta.url))) ), ecosystemConfig = Object.freeze( - ecosystem.apps.find(app => app.name === "HolyUB") || ecosystem.apps[0] + ecosystem.apps.find((app) => app.name === 'HolyUB') || ecosystem.apps[0] ), { pages, externalPages } = pageRoutes, __dirname = path.resolve(); @@ -30,71 +30,71 @@ const config = Object.freeze( // Record the server's location as a URL object, including its host and port. // The host can be modified at /src/config.json, whereas the ports can be modified // at /ecosystem.config.js. -const serverUrl = (base => { +const serverUrl = ((base) => { try { base = new URL(config.host); } catch (e) { - base = new URL("http://a"); + base = new URL('http://a'); base.host = config.host; } - base.port = ecosystemConfig[ config.production ? "env_production" : "env" ].PORT; + base.port = + ecosystemConfig[config.production ? 'env_production' : 'env'].PORT; return Object.freeze(base); })(); console.log(serverUrl); // The server will check for the existence of this file when a shutdown is requested. // The shutdown script in run-command.js will temporarily produce this file. -const shutdown = fileURLToPath(new URL("./.shutdown", import.meta.url)); +const shutdown = fileURLToPath(new URL('./.shutdown', import.meta.url)); const rh = createRammerhead(); const rammerheadScopes = [ - "/rammerhead.js", - "/hammerhead.js", - "/transport-worker.js", - "/task.js", - "/iframe-task.js", - "/worker-hammerhead.js", - "/messaging", - "/sessionexists", - "/deletesession", - "/newsession", - "/editsession", - "/needpassword", - "/syncLocalStorage", - "/api/shuffleDict", - "/mainport", + '/rammerhead.js', + '/hammerhead.js', + '/transport-worker.js', + '/task.js', + '/iframe-task.js', + '/worker-hammerhead.js', + '/messaging', + '/sessionexists', + '/deletesession', + '/newsession', + '/editsession', + '/needpassword', + '/syncLocalStorage', + '/api/shuffleDict', + '/mainport', ]; const rammerheadSession = /^\/[a-z0-9]{32}/, - shouldRouteRh = req => { + shouldRouteRh = (req) => { try { const url = new URL(req.url, serverUrl); return ( rammerheadScopes.includes(url.pathname) || rammerheadSession.test(url.pathname) ); - } catch (e) {return false} + } catch (e) { + return false; + } }, routeRhRequest = (req, res) => { - rh.emit("request", req, res); + rh.emit('request', req, res); }, routeRhUpgrade = (req, socket, head) => { - rh.emit("upgrade", req, socket, head); + rh.emit('upgrade', req, socket, head); }; // Create a server factory for RH, and wisp (and bare if you please). const serverFactory = (handler) => { return createServer() .on('request', (req, res) => { - if (shouldRouteRh(req)) - routeRhRequest(req, res); + if (shouldRouteRh(req)) routeRhRequest(req, res); else handler(req, res); }) .on('upgrade', (req, socket, head) => { - if (shouldRouteRh(req)) - routeRhUpgrade(req, socket, head); - else if (req.url.endsWith('/wisp/')) - wisp.routeRequest(req, socket, head); + if (shouldRouteRh(req)) routeRhUpgrade(req, socket, head); + else if (req.url.endsWith('/wisp/')) wisp.routeRequest(req, socket, head); }); }; @@ -103,145 +103,150 @@ const app = Fastify({ ignoreDuplicateSlashes: true, ignoreTrailingSlash: true, logger: false, - serverFactory: serverFactory + serverFactory: serverFactory, }); // Apply Helmet middleware for security app.register(fastifyHelmet, { contentSecurityPolicy: false, // Disable CSP - xPoweredBy: false + xPoweredBy: false, }); // Assign server file paths to different paths, for serving content on the website. app.register(fastifyStatic, { - root: fileURLToPath(new URL("../views/pages", import.meta.url)), - decorateReply: false + root: fileURLToPath(new URL('../views/pages', import.meta.url)), + decorateReply: false, }); app.register(fastifyStatic, { - root: fileURLToPath(new URL("../views/assets", import.meta.url)), - prefix: "/assets/", - decorateReply: false + root: fileURLToPath(new URL('../views/assets', import.meta.url)), + prefix: '/assets/', + decorateReply: false, }); app.register(fastifyStatic, { - root: fileURLToPath(new URL("../views/archive", import.meta.url)), - prefix: "/arcade/", - decorateReply: false + root: fileURLToPath(new URL('../views/archive', import.meta.url)), + prefix: '/arcade/', + decorateReply: false, }); app.register(fastifyStatic, { - root: fileURLToPath(new URL( -// Use the pre-compiled, minified scripts instead, if enabled in config. - config.minifyScripts ? "../views/dist/assets/js" : "../views/assets/js", - import.meta.url - )), - prefix: "/assets/js/", - decorateReply: false + root: fileURLToPath( + new URL( + // Use the pre-compiled, minified scripts instead, if enabled in config. + config.minifyScripts ? '../views/dist/assets/js' : '../views/assets/js', + import.meta.url + ) + ), + prefix: '/assets/js/', + decorateReply: false, }); app.register(fastifyStatic, { - root: fileURLToPath(new URL( -// Use the pre-compiled, minified stylesheets instead, if enabled in config. - config.minifyScripts ? "../views/dist/assets/css" : "../views/assets/css", - import.meta.url - )), - prefix: "/assets/css/", - decorateReply: false + root: fileURLToPath( + new URL( + // Use the pre-compiled, minified stylesheets instead, if enabled in config. + config.minifyScripts ? '../views/dist/assets/css' : '../views/assets/css', + import.meta.url + ) + ), + prefix: '/assets/css/', + decorateReply: false, }); // This combines scripts from the official UV repository with local UV scripts into // one directory path. Local versions of files override the official versions. app.register(fastifyStatic, { root: [ - fileURLToPath(new URL( -// Use the pre-compiled, minified scripts instead, if enabled in config. - config.minifyScripts ? "../views/dist/uv" : "../views/uv", - import.meta.url - )), - uvPath + fileURLToPath( + new URL( + // Use the pre-compiled, minified scripts instead, if enabled in config. + config.minifyScripts ? '../views/dist/uv' : '../views/uv', + import.meta.url + ) + ), + uvPath, ], - prefix: "/uv/", - decorateReply: false + prefix: '/uv/', + decorateReply: false, }); // Register proxy paths to the website. app.register(fastifyStatic, { root: epoxyPath, - prefix: "/epoxy/", - decorateReply: false + prefix: '/epoxy/', + decorateReply: false, }); app.register(fastifyStatic, { root: libcurlPath, - prefix: "/libcurl/", - decorateReply: false + prefix: '/libcurl/', + decorateReply: false, }); app.register(fastifyStatic, { root: bareModulePath, - prefix: "/bareasmodule/", - decorateReply: false + prefix: '/bareasmodule/', + decorateReply: false, }); app.register(fastifyStatic, { root: baremuxPath, - prefix: "/baremux/", - decorateReply: false + prefix: '/baremux/', + decorateReply: false, }); - // All website files are stored in the /views directory. // This takes one of those files and displays it for a site visitor. // Paths like /browsing are converted into paths like /views/pages/surf.html // back here. Which path converts to what is defined in routes.mjs. -app.get("/:path", (req, reply) => { - -// Testing for future features that need cookies to deliver alternate source files. - if (req.raw.rawHeaders.includes("Cookie")) - console.log(req.raw.rawHeaders[ req.raw.rawHeaders.indexOf("Cookie") + 1 ]); +app.get('/:path', (req, reply) => { + // Testing for future features that need cookies to deliver alternate source files. + if (req.raw.rawHeaders.includes('Cookie')) + console.log(req.raw.rawHeaders[req.raw.rawHeaders.indexOf('Cookie') + 1]); const reqPath = req.params.path; if (reqPath in externalPages) { let externalRoute = externalPages[reqPath]; - if (typeof externalRoute !== "string") externalRoute = externalRoute.default; + if (typeof externalRoute !== 'string') + externalRoute = externalRoute.default; return reply.redirect(externalRoute); } -// If a GET request is sent to /test-shutdown and a script-generated shutdown file -// is present, gracefully shut the server down. - if (reqPath === "test-shutdown" && existsSync(shutdown)) { - console.log("Holy Unblocker is shutting down."); - app.close(); - unlinkSync(shutdown); - process.exitCode = 0; - } + // If a GET request is sent to /test-shutdown and a script-generated shutdown file + // is present, gracefully shut the server down. + if (reqPath === 'test-shutdown' && existsSync(shutdown)) { + console.log('Holy Unblocker is shutting down.'); + app.close(); + unlinkSync(shutdown); + process.exitCode = 0; + } -// Return the error page if the query is not found in routes.mjs. - if (reqPath && !(reqPath in pages)) - return reply.code(404).type("text/html").send(preloaded404); + // Return the error page if the query is not found in routes.mjs. + if (reqPath && !(reqPath in pages)) + return reply.code(404).type('text/html').send(preloaded404); - reply.type("text/html").send( - paintSource( - loadTemplates( - tryReadFile( - path.join( - __dirname, - "views", -// Set the index the as the default page. - reqPath ? pages[reqPath] : pages.index - ) + reply.type('text/html').send( + paintSource( + loadTemplates( + tryReadFile( + path.join( + __dirname, + 'views', + // Set the index the as the default page. + reqPath ? pages[reqPath] : pages.index ) ) ) - ); + ) + ); }); -app.get("/github/:redirect", (req, reply) => { +app.get('/github/:redirect', (req, reply) => { if (req.params.redirect in externalPages.github) reply.redirect(externalPages.github[req.params.redirect]); - else reply.code(404).type("text/html").send(preloaded404); + else reply.code(404).type('text/html').send(preloaded404); }); /* @@ -256,7 +261,7 @@ app.get("/assets/js/uv/uv.config.js", (req, reply) => { // Set an error page for invalid paths outside the query string system. app.setNotFoundHandler((req, reply) => { - reply.code(404).type("text/html").send(preloaded404); + reply.code(404).type('text/html').send(preloaded404); }); app.listen({ port: serverUrl.port, host: serverUrl.hostname }); diff --git a/src/templates.mjs b/src/templates.mjs index 7d3bbb8b..b5137dcb 100644 --- a/src/templates.mjs +++ b/src/templates.mjs @@ -1,36 +1,25 @@ -import { tryReadFile } from "./randomization.mjs"; -import path from "path"; +import { tryReadFile } from './randomization.mjs'; +import path from 'path'; export { loadTemplates as default }; -const __dirname = path.resolve() + "/views/pages/misc/deobf"; +const __dirname = path.resolve() + '/views/pages/misc/deobf'; -const header = tryReadFile( - path.normalize(__dirname + "/header.html") - ), - footer = tryReadFile( - path.normalize(__dirname + "/footer.html") - ), - documentation = tryReadFile( - path.normalize(__dirname + "/docs.html") - ), - faq = tryReadFile( - path.normalize(__dirname + "/faq.html") - ), - terms = tryReadFile( - path.normalize(__dirname + "/tos.html") - ), - settings = tryReadFile( - path.normalize(__dirname + "/settings.html") - ), +const header = tryReadFile(path.normalize(__dirname + '/header.html')), + footer = tryReadFile(path.normalize(__dirname + '/footer.html')), + documentation = tryReadFile(path.normalize(__dirname + '/docs.html')), + faq = tryReadFile(path.normalize(__dirname + '/faq.html')), + terms = tryReadFile(path.normalize(__dirname + '/tos.html')), + settings = tryReadFile(path.normalize(__dirname + '/settings.html')), loadTemplates = (str) => - str.replace("", header) - .replace("", footer) + str + .replace('', header) + .replace('', footer) - // Used only on docs.html - .replace("", documentation) - // Used only on faq.html - .replace("", faq) - // Used only on terms.html - .replace("", terms) - // Used only on header.html - .replace("", settings); + // Used only on docs.html + .replace('', documentation) + // Used only on faq.html + .replace('', faq) + // Used only on terms.html + .replace('', terms) + // Used only on header.html + .replace('', settings); diff --git a/views/assets/css/styles-1644738239.css b/views/assets/css/styles-1644738239.css index fecf4a71..21e03ba9 100644 --- a/views/assets/css/styles-1644738239.css +++ b/views/assets/css/styles-1644738239.css @@ -6,12 +6,12 @@ /* HU CSS /* ----------------------------------------------- */ -@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic"); -@import url("https://fonts.googleapis.com/css?family=Montserrat+Alternates"); -@import url("https://fonts.googleapis.com/css?family=Titillium+Web:400,600,700"); +@import url('https://fonts.googleapis.com/css?family=Lato:400,700,400italic'); +@import url('https://fonts.googleapis.com/css?family=Montserrat+Alternates'); +@import url('https://fonts.googleapis.com/css?family=Titillium+Web:400,600,700'); @import url('https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap'); -@import url("https://unpkg.com/@fortawesome/fontawesome-free@5.15.4/css/all.min.css"); +@import url('https://unpkg.com/@fortawesome/fontawesome-free@5.15.4/css/all.min.css'); /* Nord Theme (https://nordtheme.com) @@ -73,14 +73,13 @@ see https://raw.githubusercontent.com/arcticicestudio/nord-docs/develop/assets/i --dark: #303030; } - /* ----------------------------------------------- /* HU Page Layout / General Styling /* ----------------------------------------------- */ html { color: white; - font-family: "Figtree", sans-serif; + font-family: 'Figtree', sans-serif; font-weight: 300; scroll-behavior: smooth; } @@ -114,7 +113,7 @@ h3, h4, h5, h6 { - font-family: "Figtree", sans-serif; + font-family: 'Figtree', sans-serif; font-weight: 900; } @@ -159,11 +158,11 @@ details[open] summary { } .font2 { - font-family: "Titillium Web", sans-serif; + font-family: 'Titillium Web', sans-serif; } .font3 { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; } .notbold { @@ -240,9 +239,10 @@ details[open] summary { /* To change the top padding on the footer, change the 3 vw values here to the same thing. */ #footer { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; padding-top: 15vw; - background: url("/assets/img/waves.svg"), linear-gradient(to bottom, transparent 0 15vw, #1d2029 15vw 100%); + background: url('/assets/img/waves.svg'), + linear-gradient(to bottom, transparent 0 15vw, #1d2029 15vw 100%); background-repeat: no-repeat; background-size: 100vw auto; position: relative; @@ -258,12 +258,12 @@ details[open] summary { color: white; } - /* ----------------------------------------------- /* Icons and Branding /* ----------------------------------------------- */ -.brand-logo-container, .new { +.brand-logo-container, +.new { display: flex; align-items: center; /* Align items vertically */ } @@ -275,7 +275,7 @@ details[open] summary { line-height: 30px; white-space: nowrap; margin-right: 20px; /* Adjust margin as needed */ - font-family: "Figtree", sans-serif; + font-family: 'Figtree', sans-serif; font-weight: 900; letter-spacing: 1px; text-decoration: none; @@ -304,7 +304,7 @@ details[open] summary { } .new::after { - content: ""; + content: ''; padding: 22.75px 25px; margin-left: 8px; background-image: url('/assets/img/new.svg'); @@ -312,7 +312,6 @@ details[open] summary { font-size: 0; } - /* ----------------------------------------------- /* Navigation Bar and Menus (Header Contents) /* ----------------------------------------------- */ @@ -370,7 +369,7 @@ details[open] summary { cursor: auto; padding: 15px 25px; margin-top: 10px; - font-family: "Titillium Web", sans-serif; + font-family: 'Titillium Web', sans-serif; } /* "More" Navigation Menu */ @@ -409,7 +408,8 @@ details[open] summary { justify-content: center; } -.dropdown-parent:focus-within .dropdown-settings:not(:is(:has(.close-settings-btn:active))) { +.dropdown-parent:focus-within + .dropdown-settings:not(:is(:has(.close-settings-btn:active))) { display: flex; /* Flexbox to center content */ } @@ -435,7 +435,7 @@ details[open] summary { color: #eceff4; font-family: 'Figtree', sans-serif; display: flex; - flex-direction: column; + flex-direction: column; backdrop-filter: blur(10px); /* Frosted glass effect */ -webkit-backdrop-filter: blur(10px); /* For Safari */ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Optional: Add some shadow for depth */ @@ -443,7 +443,7 @@ details[open] summary { .settings-header { display: flex; - justify-content: space-between; + justify-content: space-between; align-items: center; margin-bottom: 20px; } @@ -458,12 +458,15 @@ details[open] summary { cursor: pointer; font-size: 24px; font-weight: 100; - margin-left: auto; + margin-left: auto; } .close-settings-btn:hover { color: lightgray; - transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease; + transition: + background-color 0.3s ease, + color 0.3s ease, + transform 0.3s ease; transform: scale(1.05); } @@ -472,14 +475,15 @@ details[open] summary { gap: 40px; /* Space between left and right columns */ } -.csel-container-left, .settings-right-column { +.csel-container-left, +.settings-right-column { display: flex; flex-direction: column; gap: 15px; /* Space between items within columns */ } .csel-container-left { - flex: 1; + flex: 1; max-width: 400px; /* Adjusted to fit content without gaps */ } @@ -514,7 +518,7 @@ details[open] summary { flex: 1; /* Take up available space */ } -.radio-group input[type="radio"] { +.radio-group input[type='radio'] { accent-color: #88c0d0; cursor: pointer; width: 20px; /* Adjust size for better appearance */ @@ -547,7 +551,7 @@ details[open] summary { .switch::after { position: absolute; - content: ""; + content: ''; height: 14px; width: 14px; left: 3px; @@ -580,7 +584,9 @@ select:hover { background-color: #4c566a; } -.default-badge, .bare-badge, .beta-badge { +.default-badge, +.bare-badge, +.beta-badge { display: inline-block; padding: 2px 5px; border-radius: 5px; @@ -605,11 +611,11 @@ select:hover { .cloakform { display: flex; - gap: 10px; + gap: 10px; margin-bottom: 15px; } -.cloakform input[type="text"] { +.cloakform input[type='text'] { flex: 1; background-color: #3b4252; border: 1px solid #4c566a; @@ -619,7 +625,7 @@ select:hover { box-sizing: border-box; } -.cloakform input[type="button"] { +.cloakform input[type='button'] { background-color: #5e81ac; border: none; border-radius: 5px; @@ -628,18 +634,18 @@ select:hover { cursor: pointer; } -.cloakform input[type="button"]:hover { +.cloakform input[type='button']:hover { background-color: #81a1c1; } -input[type="text"] { +input[type='text'] { width: 100%; padding: 10px; background-color: #3b4252; border: 1px solid #4c566a; border-radius: 5px; color: #eceff4; - box-sizing: border-box; + box-sizing: border-box; } #csel { @@ -649,7 +655,7 @@ input[type="text"] { gap: 15px; } -#csel input:not([type=checkbox]), +#csel input:not([type='checkbox']), #csel select { outline: none; box-sizing: border-box; @@ -659,20 +665,26 @@ input[type="text"] { margin: 5px 0; background-color: var(--nord1); border: 1px solid var(--nord9); - transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease; + transition: + background-color 0.3s ease, + color 0.3s ease, + transform 0.3s ease; } -#csel input[type=text]:focus { +#csel input[type='text']:focus { box-shadow: inset 0 0 5px 0 var(--nord3); transition: box-shadow 0.15s ease-out; } -#csel input:is([type=submit], [type=button]):hover { - transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease; +#csel input:is([type='submit'], [type='button']):hover { + transition: + background-color 0.3s ease, + color 0.3s ease, + transform 0.3s ease; transform: scale(1.05); } -#csel input:is([type=submit], [type=button]):active { +#csel input:is([type='submit'], [type='button']):active { background-color: #78b0c0; } @@ -692,10 +704,11 @@ input[type="text"] { display: flex; align-items: center; justify-content: center; - gap: 10px; + gap: 10px; } -#hideads, #useonion { +#hideads, +#useonion { cursor: pointer; margin-left: 0; } @@ -771,7 +784,6 @@ input[type="text"] { vertical-align: top; } - /* ----------------------------------------------- /* Welcome Screen Content (i.e., Big Bold Words) /* ----------------------------------------------- */ @@ -822,7 +834,12 @@ input[type="text"] { border-radius: 12px; overflow: hidden; background-color: var(--nord0); - background-image: url("/assets/img/noise.png"), linear-gradient(145deg, rgba(34,38,47,0.9) 0%, rgba(34,38,47,0.9) 100%); + background-image: url('/assets/img/noise.png'), + linear-gradient( + 145deg, + rgba(34, 38, 47, 0.9) 0%, + rgba(34, 38, 47, 0.9) 100% + ); background-blend-mode: overlay; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } @@ -857,7 +874,9 @@ input[type="text"] { background-color: var(--nord3); } -.no-select, .comment, .mac-content br { +.no-select, +.comment, +.mac-content br { user-select: none; /* Prevent text selection */ -webkit-user-select: none; /* For Safari */ -moz-user-select: none; /* For Firefox */ @@ -866,7 +885,7 @@ input[type="text"] { .mac-content { color: var(--nord4); - font-family: "Source Code Pro", monospace; + font-family: 'Source Code Pro', monospace; font-optical-sizing: auto; padding: 40px 0 0 80px; flex: 1; @@ -882,24 +901,24 @@ input[type="text"] { } .cmd::before { - content: "$ "; + content: '$ '; color: var(--nord4); } .cmd::after { - content: "\a"; + content: '\a'; } .url { - color: rgb(255, 136, 142); + color: rgb(255, 136, 142); } .comment { - color: #616E88; + color: #616e88; } .comment::before { - content: "# "; + content: '# '; } .homebutton { @@ -924,7 +943,9 @@ input[type="text"] { /* NOTE: Currently unused. */ .hovermessage:hover { - transition: color 0.3s ease-in, font-size 0s linear 0.3s; + transition: + color 0.3s ease-in, + font-size 0s linear 0.3s; } /* NOTE: Currently unused. */ @@ -937,12 +958,14 @@ input[type="text"] { /* NOTE: Currently unused. */ .hovermessage:hover::before { font-size: 16px; - transition: font-size 0s linear 0.3s, color 0.3s ease-in 0.3s; + transition: + font-size 0s linear 0.3s, + color 0.3s ease-in 0.3s; } /* NOTE: Currently unused. */ .buttonlink { - font-family: "Titillium Web", sans-serif; + font-family: 'Titillium Web', sans-serif; font-weight: bold; display: inline-block; text-decoration: none; @@ -1002,11 +1025,10 @@ input[type="text"] { .text-center { text-align: center; } -iner -.splashstrokeheader { +iner .splashstrokeheader { position: relative; display: inline-block; - font-size: 36px; + font-size: 36px; line-height: 1.2; } @@ -1019,18 +1041,18 @@ iner .hero-grid-container { display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 40px; - margin: 40px auto; - width: 90%; - max-width: 1200px; - background-color: rgba(46, 52, 64, 0.2); + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 40px; + margin: 40px auto; + width: 90%; + max-width: 1200px; + background-color: rgba(46, 52, 64, 0.2); border-radius: 10px; padding: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); + border: 1px solid rgba(255, 255, 255, 0.2); -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); + backdrop-filter: blur(10px); position: relative; } @@ -1038,7 +1060,7 @@ iner text-align: center; position: relative; width: 500px; - margin: 0 auto; + margin: 0 auto; } .carousel-title { @@ -1048,30 +1070,30 @@ iner } .carousel-wrapper { - overflow: hidden; + overflow: hidden; position: relative; - width: 100%; + width: 100%; padding: 10px 0; } .carousel { display: flex; width: auto; - animation: scroll 20s linear infinite; + animation: scroll 20s linear infinite; } .carousel-inner { display: flex; - width: auto; + width: auto; } .dependencylogo { - flex: 0 0 auto; + flex: 0 0 auto; margin: 0 15px; } .dependencylogo img { - width: 50px; + width: 50px; height: auto; padding: 10px; border-radius: 20px; @@ -1083,7 +1105,7 @@ iner transform: translateX(0); } 100% { - transform: translateX(-100%); + transform: translateX(-100%); } } @@ -1098,20 +1120,28 @@ iner } .box-hero::after { - content: ""; + content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; - background: url("/assets/img/noise.png") repeat, rgba(255, 255, 255, 0.03); - opacity: var(--noise-opacity, 0.02); + background: + url('/assets/img/noise.png') repeat, + rgba(255, 255, 255, 0.03); + opacity: var(--noise-opacity, 0.02); pointer-events: none; -webkit-mask-image: radial-gradient(circle, white 30%, transparent 50%); -webkit-mask-size: 800px 800px; - -webkit-mask-position: calc(var(--mouse-x) - 400px) calc(var(--mouse-y) - 400px); + -webkit-mask-position: calc(var(--mouse-x) - 400px) + calc(var(--mouse-y) - 400px); -webkit-mask-repeat: no-repeat; - mask-image: radial-gradient(closest-side, rgba(37, 35, 35, 0.377) 30%, rgba(255, 255, 255, 0.048) 70%, transparent 90%); + mask-image: radial-gradient( + closest-side, + rgba(37, 35, 35, 0.377) 30%, + rgba(255, 255, 255, 0.048) 70%, + transparent 90% + ); mask-size: 800px 800px; mask-position: calc(var(--mouse-x) - 400px) calc(var(--mouse-y) - 400px); mask-repeat: no-repeat; @@ -1139,7 +1169,7 @@ iner .hero-text-wrap { max-width: 53ch; - margin-right: 25%; + margin-right: 25%; } .hero-text-wrap p { @@ -1151,13 +1181,13 @@ iner display: flex; flex-direction: column; align-items: center; - justify-content: center; + justify-content: center; } .hero { width: 100%; /* Ensure the image takes full width of its container */ max-width: 300px; /* Ensure the image doesn't exceed its container */ - height: auto; + height: auto; border-radius: 50%; } @@ -1184,7 +1214,7 @@ iner .grid-container { display: grid; grid-template-columns: 1fr; - gap: 80px; + gap: 80px; margin: 80px; margin-top: 300px; padding: 0 20px; /* Add padding to prevent overflow */ @@ -1205,20 +1235,28 @@ iner } .box-card::after { - content: ""; + content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; - background: url("/assets/img/noise.png") repeat, rgba(46, 52, 64, 0.03); - opacity: var(--noise-opacity, 0.02); + background: + url('/assets/img/noise.png') repeat, + rgba(46, 52, 64, 0.03); + opacity: var(--noise-opacity, 0.02); pointer-events: none; -webkit-mask-image: radial-gradient(circle, white 30%, transparent 50%); -webkit-mask-size: 800px 800px; - -webkit-mask-position: calc(var(--mouse-x) - 400px) calc(var(--mouse-y) - 400px); + -webkit-mask-position: calc(var(--mouse-x) - 400px) + calc(var(--mouse-y) - 400px); -webkit-mask-repeat: no-repeat; - mask-image: radial-gradient(closest-side, rgba(37, 35, 35, 0.377) 30%, rgba(255, 255, 255, 0.048) 70%, transparent 90%); + mask-image: radial-gradient( + closest-side, + rgba(37, 35, 35, 0.377) 30%, + rgba(255, 255, 255, 0.048) 70%, + transparent 90% + ); mask-size: 800px 800px; mask-position: calc(var(--mouse-x) - 400px) calc(var(--mouse-y) - 400px); mask-repeat: no-repeat; @@ -1235,8 +1273,8 @@ iner .box-card .content { width: calc(95% - min(30%, 180px) - 32px); margin-right: 20px; - box-sizing: border-box; - overflow-wrap: break-word + box-sizing: border-box; + overflow-wrap: break-word; } .image-container { @@ -1244,7 +1282,7 @@ iner right: 5%; top: 50%; transform: translateY(-50%); - width: 30%; + width: 30%; max-width: 180px; /* Add a max-width to prevent it from getting too large */ height: auto; background-color: var(--nord0m3); @@ -1391,10 +1429,6 @@ iner } } - - - - .box-large { width: 80vw; margin: 20px auto; @@ -1406,7 +1440,7 @@ iner .box-error { margin: 40vh 0; - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; } .box-error h1 { @@ -1437,13 +1471,12 @@ iner margin-bottom: 8px; } - /* ----------------------------------------------- /* Documentation and FAQ Pages /* ----------------------------------------------- */ #documentation { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; } #documentation code { @@ -1498,8 +1531,8 @@ iner } .ad::before { - content: "Advertisement"; - font-family: "Titillium Web", sans-serif; + content: 'Advertisement'; + font-family: 'Titillium Web', sans-serif; position: absolute; z-index: -1; left: 50%; @@ -1527,21 +1560,21 @@ iner border-radius: 5px; padding: 10px 12px; color: white; - font-family: "Montserrat Alternates", sans-serif; + font-family: 'Montserrat Alternates', sans-serif; font-size: 20px; margin: 3px; } -.pr-form input[type=text] { +.pr-form input[type='text'] { max-width: 700px; width: calc(100% - 44px); } -.pr-form input[type=text]:focus { +.pr-form input[type='text']:focus { animation: glowshadow 2s linear infinite; } -.pr-form input[type=text]::placeholder { +.pr-form input[type='text']::placeholder { color: var(--gray); } @@ -1604,7 +1637,6 @@ iner background-color: #111; } - /* ----------------------------------------------- /* HU Games Directory /* ----------------------------------------------- */ @@ -1616,7 +1648,7 @@ iner /* Games list for non-flash games */ .glist { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; display: flex; flex-wrap: wrap; justify-content: center; @@ -1660,7 +1692,7 @@ iner /* Flash Games Search Bar */ #fsearchbar { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; font-size: 16px; background-color: var(--nord0); outline: none; @@ -1678,7 +1710,7 @@ iner /* Flash Games List */ .flist { - font-family: "Lato", sans-serif; + font-family: 'Lato', sans-serif; background-color: var(--nord0); width: 400px; height: calc(100vh - 92px); @@ -1713,7 +1745,6 @@ iner margin: 40px 0 32px; } - /* ----------------------------------------------- /* CSS Animations /* ----------------------------------------------- */ @@ -1753,7 +1784,10 @@ iner } .pulse:hover { - transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease; + transition: + background-color 0.3s ease, + color 0.3s ease, + transform 0.3s ease; transform: scale(1.05); -webkit-font-smoothing: subpixel-antialiased; } @@ -1785,7 +1819,7 @@ iner } .glowbutton::after { - content: ""; + content: ''; border-radius: inherit; display: inline-block; position: absolute; @@ -1806,10 +1840,11 @@ iner .glowbutton:active::after { background: linear-gradient(90deg, var(--nord7), var(--nord8), var(--nord7)); background-size: 100px auto; - animation: glowshadow 2s linear infinite, glowbg 2s linear infinite; + animation: + glowshadow 2s linear infinite, + glowbg 2s linear infinite; } - /* ----------------------------------------------- /* Mobile Support /* ----------------------------------------------- */ @@ -1842,9 +1877,11 @@ iner .mnavebutton::before, .mnavebutton::after { - content: ""; + content: ''; position: absolute; - transition: transform 0.2s ease-out, top 0.2s ease-out; + transition: + transform 0.2s ease-out, + top 0.2s ease-out; } .mnavebutton { @@ -1882,7 +1919,6 @@ iner display: none; } - /* ----------------------------------------------- /* CSS Media Queries (largely for mobile support) /* ----------------------------------------------- */ @@ -1953,7 +1989,7 @@ iner #banner { display: none; } -/* + /* .mnave { display: none !important; } @@ -2051,7 +2087,6 @@ iner } @media (max-width: 600px) { - #header { padding: 15px 30px; } @@ -2065,7 +2100,8 @@ iner font-size: 14px; } - .navbar, .navbar-1 { + .navbar, + .navbar-1 { font-size: 11px; } @@ -2116,13 +2152,15 @@ iner margin-top: 7px; } - #csel input:not([type=checkbox]), #csel select { + #csel input:not([type='checkbox']), + #csel select { padding: 7px 8px; margin: 3.5px 1.75px; font-size: 11px; } - #csel .cseltitle ~ input:not([type=checkbox]), #csel select { + #csel .cseltitle ~ input:not([type='checkbox']), + #csel select { width: calc(100% - 94.5px); } @@ -2134,7 +2172,8 @@ iner width: 60vw; } - .hero, .image-container { + .hero, + .image-container { display: none; } @@ -2206,4 +2245,4 @@ iner .splashend > h1 { font-size: 1.625em; } -} \ No newline at end of file +} diff --git a/views/assets/js/bareTransport.js b/views/assets/js/bareTransport.js index 65c17acf..3e12b0d2 100644 --- a/views/assets/js/bareTransport.js +++ b/views/assets/js/bareTransport.js @@ -1,613 +1,612 @@ //Built from: https://github.com/motortruck1221/bare-as-module3 (commit: 36759f801e0009027878edecff156408b06404c6) (function (global, factory) { - typeof exports === "object" && typeof module !== "undefined" - ? factory(exports) - : typeof define === "function" && define.amd - ? define(["exports"], factory) - : ((global = - typeof globalThis !== "undefined" ? globalThis : global || self), - factory((global.BareMod = {}))); - })(this, function (exports) { - "use strict"; - - // The user likely has overwritten all networking functions after importing bare-client - // It is our responsibility to make sure components of Bare-Client are using native networking functions - // These exports are provided to plugins by @rollup/plugin-inject - const fetch = globalThis.fetch; - const WebSocket = globalThis.WebSocket; - const WebSocketFields = { - prototype: { - send: WebSocket.prototype.send - }, - CLOSED: WebSocket.CLOSED, - CLOSING: WebSocket.CLOSING, - CONNECTING: WebSocket.CONNECTING, - OPEN: WebSocket.OPEN - }; - - class BareError extends Error { - status; - body; - constructor(status, body) { - super(body.message || body.code); - this.status = status; - this.body = body; - } + typeof exports === 'object' && typeof module !== 'undefined' + ? factory(exports) + : typeof define === 'function' && define.amd + ? define(['exports'], factory) + : ((global = + typeof globalThis !== 'undefined' ? globalThis : global || self), + factory((global.BareMod = {}))); +})(this, function (exports) { + 'use strict'; + + // The user likely has overwritten all networking functions after importing bare-client + // It is our responsibility to make sure components of Bare-Client are using native networking functions + // These exports are provided to plugins by @rollup/plugin-inject + const fetch = globalThis.fetch; + const WebSocket = globalThis.WebSocket; + const WebSocketFields = { + prototype: { + send: WebSocket.prototype.send, + }, + CLOSED: WebSocket.CLOSED, + CLOSING: WebSocket.CLOSING, + CONNECTING: WebSocket.CONNECTING, + OPEN: WebSocket.OPEN, + }; + + class BareError extends Error { + status; + body; + constructor(status, body) { + super(body.message || body.code); + this.status = status; + this.body = body; } - class Client { - base; - /** - * - * @param version Version provided by extension - * @param server Bare Server URL provided by BareClient - */ - constructor(version, server) { - this.base = new URL(`./v${version}/`, server); - } - } - - /* - * JavaScript MD5 - * Adopted from https://github.com/blueimp/JavaScript-MD5 - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * https://opensource.org/licenses/MIT - * - * Based on - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for more info. - */ + } + class Client { + base; /** - * Add integers, wrapping at 2^32. - * This uses 16-bit operations internally to work around bugs in interpreters. * - * @param x First integer - * @param y Second integer - * @returns Sum + * @param version Version provided by extension + * @param server Bare Server URL provided by BareClient */ - function safeAdd(x, y) { - const lsw = (x & 0xffff) + (y & 0xffff); - const msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xffff); + constructor(version, server) { + this.base = new URL(`./v${version}/`, server); } - /** - * Bitwise rotate a 32-bit number to the left. - * - * @param num 32-bit number - * @param cnt Rotation count - * @returns Rotated number - */ - function bitRotateLeft(num, cnt) { - return (num << cnt) | (num >>> (32 - cnt)); + } + + /* + * JavaScript MD5 + * Adopted from https://github.com/blueimp/JavaScript-MD5 + * + * Copyright 2011, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * https://opensource.org/licenses/MIT + * + * Based on + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + /** + * Add integers, wrapping at 2^32. + * This uses 16-bit operations internally to work around bugs in interpreters. + * + * @param x First integer + * @param y Second integer + * @returns Sum + */ + function safeAdd(x, y) { + const lsw = (x & 0xffff) + (y & 0xffff); + const msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); + } + /** + * Bitwise rotate a 32-bit number to the left. + * + * @param num 32-bit number + * @param cnt Rotation count + * @returns Rotated number + */ + function bitRotateLeft(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + /** + * Basic operation the algorithm uses. + * + * @param q q + * @param a a + * @param b b + * @param x x + * @param s s + * @param t t + * @returns Result + */ + function md5cmn(q, a, b, x, s, t) { + return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b); + } + /** + * Basic operation the algorithm uses. + * + * @param a a + * @param b b + * @param c c + * @param d d + * @param x x + * @param s s + * @param t t + * @returns Result + */ + function md5ff(a, b, c, d, x, s, t) { + return md5cmn((b & c) | (~b & d), a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param a a + * @param b b + * @param c c + * @param d d + * @param x x + * @param s s + * @param t t + * @returns Result + */ + function md5gg(a, b, c, d, x, s, t) { + return md5cmn((b & d) | (c & ~d), a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param a a + * @param b b + * @param c c + * @param d d + * @param x x + * @param s s + * @param t t + * @returns Result + */ + function md5hh(a, b, c, d, x, s, t) { + return md5cmn(b ^ c ^ d, a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param a a + * @param b b + * @param c c + * @param d d + * @param x x + * @param s s + * @param t t + * @returns Result + */ + function md5ii(a, b, c, d, x, s, t) { + return md5cmn(c ^ (b | ~d), a, b, x, s, t); + } + /** + * Calculate the MD5 of an array of little-endian words, and a bit length. + * + * @param x Array of little-endian words + * @param len Bit length + * @returns MD5 Array + */ + function binlMD5(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << len % 32; + x[(((len + 64) >>> 9) << 4) + 14] = len; + let a = 1732584193; + let b = -271733879; + let c = -1732584194; + let d = 271733878; + for (let i = 0; i < x.length; i += 16) { + const olda = a; + const oldb = b; + const oldc = c; + const oldd = d; + a = md5ff(a, b, c, d, x[i], 7, -680876936); + d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); + c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); + b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); + d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); + a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = md5ff(c, d, a, b, x[i + 10], 17, -42063); + b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); + c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); + a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); + d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); + b = md5gg(b, c, d, a, x[i], 20, -373897302); + a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); + d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); + c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); + b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); + a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); + d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); + b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); + c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); + a = md5hh(a, b, c, d, x[i + 5], 4, -378558); + d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); + a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); + b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); + d = md5hh(d, a, b, c, x[i], 11, -358537222); + c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); + b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); + a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); + d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); + c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); + b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); + a = md5ii(a, b, c, d, x[i], 6, -198630844); + d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); + a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); + b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); + c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); + d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); + b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); + a = safeAdd(a, olda); + b = safeAdd(b, oldb); + c = safeAdd(c, oldc); + d = safeAdd(d, oldd); } - /** - * Basic operation the algorithm uses. - * - * @param q q - * @param a a - * @param b b - * @param x x - * @param s s - * @param t t - * @returns Result - */ - function md5cmn(q, a, b, x, s, t) { - return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b); + return [a, b, c, d]; + } + /** + * Convert an array of little-endian words to a string + * + * @param input MD5 Array + * @returns MD5 string + */ + function binl2rstr(input) { + let output = ''; + const length32 = input.length * 32; + for (let i = 0; i < length32; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff); } - /** - * Basic operation the algorithm uses. - * - * @param a a - * @param b b - * @param c c - * @param d d - * @param x x - * @param s s - * @param t t - * @returns Result - */ - function md5ff(a, b, c, d, x, s, t) { - return md5cmn((b & c) | (~b & d), a, b, x, s, t); + return output; + } + /** + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + * + * @param input Raw input string + * @returns Array of little-endian words + */ + function rstr2binl(input) { + const output = []; + const outputLen = input.length >> 2; + for (let i = 0; i < outputLen; i += 1) { + output[i] = 0; } - /** - * Basic operation the algorithm uses. - * - * @param a a - * @param b b - * @param c c - * @param d d - * @param x x - * @param s s - * @param t t - * @returns Result - */ - function md5gg(a, b, c, d, x, s, t) { - return md5cmn((b & d) | (c & ~d), a, b, x, s, t); + const length8 = input.length * 8; + for (let i = 0; i < length8; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32; } - /** - * Basic operation the algorithm uses. - * - * @param a a - * @param b b - * @param c c - * @param d d - * @param x x - * @param s s - * @param t t - * @returns Result - */ - function md5hh(a, b, c, d, x, s, t) { - return md5cmn(b ^ c ^ d, a, b, x, s, t); + return output; + } + /** + * Calculate the MD5 of a raw string + * + * @param s Input string + * @returns Raw MD5 string + */ + function rstrMD5(s) { + return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)); + } + /** + * Calculates the HMAC-MD5 of a key and some data (raw strings) + * + * @param key HMAC key + * @param data Raw input string + * @returns Raw MD5 string + */ + function rstrHMACMD5(key, data) { + let bkey = rstr2binl(key); + const ipad = []; + const opad = []; + if (bkey.length > 16) { + bkey = binlMD5(bkey, key.length * 8); } - /** - * Basic operation the algorithm uses. - * - * @param a a - * @param b b - * @param c c - * @param d d - * @param x x - * @param s s - * @param t t - * @returns Result - */ - function md5ii(a, b, c, d, x, s, t) { - return md5cmn(c ^ (b | ~d), a, b, x, s, t); + for (let i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5c5c5c5c; } - /** - * Calculate the MD5 of an array of little-endian words, and a bit length. - * - * @param x Array of little-endian words - * @param len Bit length - * @returns MD5 Array - */ - function binlMD5(x, len) { - /* append padding */ - x[len >> 5] |= 0x80 << len % 32; - x[(((len + 64) >>> 9) << 4) + 14] = len; - let a = 1732584193; - let b = -271733879; - let c = -1732584194; - let d = 271733878; - for (let i = 0; i < x.length; i += 16) { - const olda = a; - const oldb = b; - const oldc = c; - const oldd = d; - a = md5ff(a, b, c, d, x[i], 7, -680876936); - d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); - c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); - b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); - a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); - d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); - c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); - b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); - a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); - d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); - c = md5ff(c, d, a, b, x[i + 10], 17, -42063); - b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); - a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); - d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); - c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); - b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); - a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); - d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); - c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); - b = md5gg(b, c, d, a, x[i], 20, -373897302); - a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); - d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); - c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); - b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); - a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); - d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); - c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); - b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); - a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); - d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); - c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); - b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); - a = md5hh(a, b, c, d, x[i + 5], 4, -378558); - d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); - c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); - b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); - a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); - d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); - c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); - b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); - a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); - d = md5hh(d, a, b, c, x[i], 11, -358537222); - c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); - b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); - a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); - d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); - c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); - b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); - a = md5ii(a, b, c, d, x[i], 6, -198630844); - d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); - c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); - b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); - a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); - d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); - c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); - b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); - a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); - d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); - c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); - b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); - a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); - d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); - c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); - b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); - a = safeAdd(a, olda); - b = safeAdd(b, oldb); - c = safeAdd(c, oldc); - d = safeAdd(d, oldd); - } - return [a, b, c, d]; + const hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)); + } + /** + * Convert a raw string to a hex string + * + * @param input Raw input string + * @returns Hex encoded string + */ + function rstr2hex(input) { + const hexTab = '0123456789abcdef'; + let output = ''; + for (let i = 0; i < input.length; i += 1) { + const x = input.charCodeAt(i); + output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); } - /** - * Convert an array of little-endian words to a string - * - * @param input MD5 Array - * @returns MD5 string - */ - function binl2rstr(input) { - let output = ""; - const length32 = input.length * 32; - for (let i = 0; i < length32; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff); - } - return output; - } - /** - * Convert a raw string to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - * - * @param input Raw input string - * @returns Array of little-endian words - */ - function rstr2binl(input) { - const output = []; - const outputLen = input.length >> 2; - for (let i = 0; i < outputLen; i += 1) { - output[i] = 0; - } - const length8 = input.length * 8; - for (let i = 0; i < length8; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32; - } - return output; - } - /** - * Calculate the MD5 of a raw string - * - * @param s Input string - * @returns Raw MD5 string - */ - function rstrMD5(s) { - return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)); - } - /** - * Calculates the HMAC-MD5 of a key and some data (raw strings) - * - * @param key HMAC key - * @param data Raw input string - * @returns Raw MD5 string - */ - function rstrHMACMD5(key, data) { - let bkey = rstr2binl(key); - const ipad = []; - const opad = []; - if (bkey.length > 16) { - bkey = binlMD5(bkey, key.length * 8); - } - for (let i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5c5c5c5c; - } - const hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); - return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)); - } - /** - * Convert a raw string to a hex string - * - * @param input Raw input string - * @returns Hex encoded string - */ - function rstr2hex(input) { - const hexTab = "0123456789abcdef"; - let output = ""; - for (let i = 0; i < input.length; i += 1) { - const x = input.charCodeAt(i); - output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); - } - return output; - } - /** - * Encode a string as UTF-8 - * - * @param input Input string - * @returns UTF8 string - */ - function str2rstrUTF8(input) { - return unescape(encodeURIComponent(input)); - } - /** - * Encodes input string as raw MD5 string - * - * @param s Input string - * @returns Raw MD5 string - */ - function rawMD5(s) { - return rstrMD5(str2rstrUTF8(s)); - } - /** - * Encodes input string as Hex encoded string - * - * @param s Input string - * @returns Hex encoded string - */ - function hexMD5(s) { - return rstr2hex(rawMD5(s)); - } - /** - * Calculates the raw HMAC-MD5 for the given key and data - * - * @param k HMAC key - * @param d Input string - * @returns Raw MD5 string - */ - function rawHMACMD5(k, d) { - return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)); - } - /** - * Calculates the Hex encoded HMAC-MD5 for the given key and data - * - * @param k HMAC key - * @param d Input string - * @returns Raw MD5 string - */ - function hexHMACMD5(k, d) { - return rstr2hex(rawHMACMD5(k, d)); - } - /** - * Calculates MD5 value for a given string. - * If a key is provided, calculates the HMAC-MD5 value. - * Returns a Hex encoded string unless the raw argument is given. - * - * @param string Input string - * @param key HMAC key - * @param raw Raw output switch - * @returns MD5 output - */ - function md5(string, key, raw) { - if (!key) { - if (!raw) { - return hexMD5(string); - } - return rawMD5(string); - } + return output; + } + /** + * Encode a string as UTF-8 + * + * @param input Input string + * @returns UTF8 string + */ + function str2rstrUTF8(input) { + return unescape(encodeURIComponent(input)); + } + /** + * Encodes input string as raw MD5 string + * + * @param s Input string + * @returns Raw MD5 string + */ + function rawMD5(s) { + return rstrMD5(str2rstrUTF8(s)); + } + /** + * Encodes input string as Hex encoded string + * + * @param s Input string + * @returns Hex encoded string + */ + function hexMD5(s) { + return rstr2hex(rawMD5(s)); + } + /** + * Calculates the raw HMAC-MD5 for the given key and data + * + * @param k HMAC key + * @param d Input string + * @returns Raw MD5 string + */ + function rawHMACMD5(k, d) { + return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)); + } + /** + * Calculates the Hex encoded HMAC-MD5 for the given key and data + * + * @param k HMAC key + * @param d Input string + * @returns Raw MD5 string + */ + function hexHMACMD5(k, d) { + return rstr2hex(rawHMACMD5(k, d)); + } + /** + * Calculates MD5 value for a given string. + * If a key is provided, calculates the HMAC-MD5 value. + * Returns a Hex encoded string unless the raw argument is given. + * + * @param string Input string + * @param key HMAC key + * @param raw Raw output switch + * @returns MD5 output + */ + function md5(string, key, raw) { + if (!key) { if (!raw) { - return hexHMACMD5(key, string); + return hexMD5(string); } - return rawHMACMD5(key, string); + return rawMD5(string); } - - const MAX_HEADER_VALUE = 3072; - /** - * - * Splits headers according to spec - * @param headers - * @returns Split headers - */ - function splitHeaders(headers) { - const output = new Headers(headers); - if (headers.has("x-bare-headers")) { - const value = headers.get("x-bare-headers"); - if (value.length > MAX_HEADER_VALUE) { - output.delete("x-bare-headers"); - let split = 0; - for (let i = 0; i < value.length; i += MAX_HEADER_VALUE) { - const part = value.slice(i, i + MAX_HEADER_VALUE); - const id = split++; - output.set(`x-bare-headers-${id}`, `;${part}`); - } + if (!raw) { + return hexHMACMD5(key, string); + } + return rawHMACMD5(key, string); + } + + const MAX_HEADER_VALUE = 3072; + /** + * + * Splits headers according to spec + * @param headers + * @returns Split headers + */ + function splitHeaders(headers) { + const output = new Headers(headers); + if (headers.has('x-bare-headers')) { + const value = headers.get('x-bare-headers'); + if (value.length > MAX_HEADER_VALUE) { + output.delete('x-bare-headers'); + let split = 0; + for (let i = 0; i < value.length; i += MAX_HEADER_VALUE) { + const part = value.slice(i, i + MAX_HEADER_VALUE); + const id = split++; + output.set(`x-bare-headers-${id}`, `;${part}`); } } - return output; } - /** - * Joins headers according to spec - * @param headers - * @returns Joined headers - */ - function joinHeaders(headers) { - const output = new Headers(headers); - const prefix = "x-bare-headers"; - if (headers.has(`${prefix}-0`)) { - const join = []; - for (const [header, value] of headers) { - if (!header.startsWith(prefix)) { - continue; - } - if (!value.startsWith(";")) { - throw new BareError(400, { - code: "INVALID_BARE_HEADER", - id: `request.headers.${header}`, - message: `Value didn't begin with semi-colon.` - }); - } - const id = parseInt(header.slice(prefix.length + 1)); - join[id] = value.slice(1); - output.delete(header); + return output; + } + /** + * Joins headers according to spec + * @param headers + * @returns Joined headers + */ + function joinHeaders(headers) { + const output = new Headers(headers); + const prefix = 'x-bare-headers'; + if (headers.has(`${prefix}-0`)) { + const join = []; + for (const [header, value] of headers) { + if (!header.startsWith(prefix)) { + continue; } - output.set(prefix, join.join("")); - } - return output; - } - - class ClientV3 extends Client { - ws; - http; - meta() { - return {}; - } - constructor(server) { - super(3, server); - this.ws = new URL(this.base); - this.http = new URL(this.base); - if (this.ws.protocol === "https:") { - this.ws.protocol = "wss:"; - } else { - this.ws.protocol = "ws:"; - } - } - ready = true; - async init() { - this.ready = true; - } - connect( - url, - origin, - protocols, - requestHeaders, - onopen, - onmessage, - onclose, - onerror - ) { - const ws = new WebSocket(this.ws); - const cleanup = () => { - ws.removeEventListener("close", closeListener); - ws.removeEventListener("message", messageListener); - }; - const closeListener = () => { - cleanup(); - }; - const messageListener = (event) => { - cleanup(); - // ws.binaryType is irrelevant when sending text - if (typeof event.data !== "string") - throw new TypeError( - "the first websocket message was not a text frame" - ); - const message = JSON.parse(event.data); - // finally - if (message.type !== "open") - throw new TypeError("message was not of open type"); - // onMeta({ - // protocol: message.protocol, - // setCookies: message.setCookies, - // }); - onopen(message.protocol); - // TODO - ws.addEventListener("message", (ev) => { - onmessage(ev.data); + if (!value.startsWith(';')) { + throw new BareError(400, { + code: 'INVALID_BARE_HEADER', + id: `request.headers.${header}`, + message: `Value didn't begin with semi-colon.`, }); - }; - ws.addEventListener("close", closeListener); - ws.addEventListener("message", messageListener); - // CONNECTED TO THE BARE SERVER, NOT THE REMOTE - ws.addEventListener( - "open", - (event) => { - // getRequestHeaders().then((headers:any) => - WebSocketFields.prototype.send.call( - ws, - JSON.stringify({ - type: "connect", - remote: url.toString(), - protocols, - headers: requestHeaders, - forwardHeaders: [] - }) - ); - // ); - }, - // only block the open event once - { once: true } - ); - return ws.send.bind(ws); + } + const id = parseInt(header.slice(prefix.length + 1)); + join[id] = value.slice(1); + output.delete(header); } - async request(remote, method, body, headers, signal) { - const options = { - credentials: "omit", - method: method, - signal - }; - if (body !== undefined) { - options.body = body; - } - options.headers = this.createBareHeaders(remote, headers); - const response = await fetch( - this.http + "?cache=" + md5(remote.toString()), - options - ); - const readResponse = await this.readBareResponse(response); - // const result: Response & Partial = new Response( - // statusEmpty.includes(readResponse.status!) ? undefined : response.body, - // { - // status: readResponse.status, - // statusText: readResponse.statusText ?? undefined, - // headers: new Headers(readResponse.headers as HeadersInit), - // } - // ); - // - // result.rawHeaders = readResponse.headers; - // result.rawResponse = response; - return { - body: response.body, - headers: readResponse.headers, - status: readResponse.status, - statusText: readResponse.statusText - }; - } - async readBareResponse(response) { - if (!response.ok) { - throw new BareError(response.status, await response.json()); - } - const responseHeaders = joinHeaders(response.headers); - const result = {}; - const xBareStatus = responseHeaders.get("x-bare-status"); - if (xBareStatus !== null) result.status = parseInt(xBareStatus); - const xBareStatusText = responseHeaders.get("x-bare-status-text"); - if (xBareStatusText !== null) result.statusText = xBareStatusText; - const xBareHeaders = responseHeaders.get("x-bare-headers"); - if (xBareHeaders !== null) result.headers = JSON.parse(xBareHeaders); - return result; - } - createBareHeaders( - remote, - bareHeaders, - forwardHeaders = [], - passHeaders = [], - passStatus = [] - ) { - const headers = new Headers(); - headers.set("x-bare-url", remote.toString()); - headers.set("x-bare-headers", JSON.stringify(bareHeaders)); - for (const header of forwardHeaders) { - headers.append("x-bare-forward-headers", header); - } - for (const header of passHeaders) { - headers.append("x-bare-pass-headers", header); - } - for (const status of passStatus) { - headers.append("x-bare-pass-status", status.toString()); - } - splitHeaders(headers); - return headers; + output.set(prefix, join.join('')); + } + return output; + } + + class ClientV3 extends Client { + ws; + http; + meta() { + return {}; + } + constructor(server) { + super(3, server); + this.ws = new URL(this.base); + this.http = new URL(this.base); + if (this.ws.protocol === 'https:') { + this.ws.protocol = 'wss:'; + } else { + this.ws.protocol = 'ws:'; } } - - exports.BareClient = ClientV3; - }); - //# sourceMappingURL=bare.cjs.map - \ No newline at end of file + ready = true; + async init() { + this.ready = true; + } + connect( + url, + origin, + protocols, + requestHeaders, + onopen, + onmessage, + onclose, + onerror + ) { + const ws = new WebSocket(this.ws); + const cleanup = () => { + ws.removeEventListener('close', closeListener); + ws.removeEventListener('message', messageListener); + }; + const closeListener = () => { + cleanup(); + }; + const messageListener = (event) => { + cleanup(); + // ws.binaryType is irrelevant when sending text + if (typeof event.data !== 'string') + throw new TypeError( + 'the first websocket message was not a text frame' + ); + const message = JSON.parse(event.data); + // finally + if (message.type !== 'open') + throw new TypeError('message was not of open type'); + // onMeta({ + // protocol: message.protocol, + // setCookies: message.setCookies, + // }); + onopen(message.protocol); + // TODO + ws.addEventListener('message', (ev) => { + onmessage(ev.data); + }); + }; + ws.addEventListener('close', closeListener); + ws.addEventListener('message', messageListener); + // CONNECTED TO THE BARE SERVER, NOT THE REMOTE + ws.addEventListener( + 'open', + (event) => { + // getRequestHeaders().then((headers:any) => + WebSocketFields.prototype.send.call( + ws, + JSON.stringify({ + type: 'connect', + remote: url.toString(), + protocols, + headers: requestHeaders, + forwardHeaders: [], + }) + ); + // ); + }, + // only block the open event once + { once: true } + ); + return ws.send.bind(ws); + } + async request(remote, method, body, headers, signal) { + const options = { + credentials: 'omit', + method: method, + signal, + }; + if (body !== undefined) { + options.body = body; + } + options.headers = this.createBareHeaders(remote, headers); + const response = await fetch( + this.http + '?cache=' + md5(remote.toString()), + options + ); + const readResponse = await this.readBareResponse(response); + // const result: Response & Partial = new Response( + // statusEmpty.includes(readResponse.status!) ? undefined : response.body, + // { + // status: readResponse.status, + // statusText: readResponse.statusText ?? undefined, + // headers: new Headers(readResponse.headers as HeadersInit), + // } + // ); + // + // result.rawHeaders = readResponse.headers; + // result.rawResponse = response; + return { + body: response.body, + headers: readResponse.headers, + status: readResponse.status, + statusText: readResponse.statusText, + }; + } + async readBareResponse(response) { + if (!response.ok) { + throw new BareError(response.status, await response.json()); + } + const responseHeaders = joinHeaders(response.headers); + const result = {}; + const xBareStatus = responseHeaders.get('x-bare-status'); + if (xBareStatus !== null) result.status = parseInt(xBareStatus); + const xBareStatusText = responseHeaders.get('x-bare-status-text'); + if (xBareStatusText !== null) result.statusText = xBareStatusText; + const xBareHeaders = responseHeaders.get('x-bare-headers'); + if (xBareHeaders !== null) result.headers = JSON.parse(xBareHeaders); + return result; + } + createBareHeaders( + remote, + bareHeaders, + forwardHeaders = [], + passHeaders = [], + passStatus = [] + ) { + const headers = new Headers(); + headers.set('x-bare-url', remote.toString()); + headers.set('x-bare-headers', JSON.stringify(bareHeaders)); + for (const header of forwardHeaders) { + headers.append('x-bare-forward-headers', header); + } + for (const header of passHeaders) { + headers.append('x-bare-pass-headers', header); + } + for (const status of passStatus) { + headers.append('x-bare-pass-status', status.toString()); + } + splitHeaders(headers); + return headers; + } + } + + exports.BareClient = ClientV3; +}); +//# sourceMappingURL=bare.cjs.map diff --git a/views/assets/js/card.js b/views/assets/js/card.js index d90bc831..005ea3e3 100644 --- a/views/assets/js/card.js +++ b/views/assets/js/card.js @@ -7,35 +7,31 @@ // Encase everything in a new scope so that variables are not accidentally // attached to the global scope. (() => { + // Track the cursor position with respect to the top left of the card. + // The "this" keyword gets the element that invoked the event listener. + const handleMouseMove = (element) => { + element.addEventListener('mousemove', (e) => { + const rect = element.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; -// Track the cursor position with respect to the top left of the card. -// The "this" keyword gets the element that invoked the event listener. -const handleMouseMove = element => { - element.addEventListener("mousemove", e => { - const rect = element.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + element.style.setProperty('--mouse-x', `${x}px`); + element.style.setProperty('--mouse-y', `${y}px`); + }); + }, + // Reset the cursor tracking variables when the cursor leaves the card. + handleMouseLeave = (element) => { + element.addEventListener('mouseleave', () => { + element.style.setProperty('--mouse-x', `50%`); + element.style.setProperty('--mouse-y', `50%`); + }); + }, + // Get the box card elements and add the event listeners to them. + shimmerEffects = document.querySelectorAll('.box-card, .box-hero'); - element.style.setProperty("--mouse-x", `${x}px`); - element.style.setProperty("--mouse-y", `${y}px`); - }); - }, - -// Reset the cursor tracking variables when the cursor leaves the card. - handleMouseLeave = element => { - element.addEventListener("mouseleave", () => { - element.style.setProperty("--mouse-x", `50%`); - element.style.setProperty("--mouse-y", `50%`); - }); - }, - -// Get the box card elements and add the event listeners to them. - shimmerEffects = document.querySelectorAll(".box-card, .box-hero"); - -// Attach CSS variables, mouse-x and mouse-y, to elements that will be -// given shimmer effects, by adding or modifying the style attribute. -// CSS calculates and renders the actual shimmer effect from there. -shimmerEffects.forEach(handleMouseMove); -shimmerEffects.forEach(handleMouseLeave); - -})(); \ No newline at end of file + // Attach CSS variables, mouse-x and mouse-y, to elements that will be + // given shimmer effects, by adding or modifying the style attribute. + // CSS calculates and renders the actual shimmer effect from there. + shimmerEffects.forEach(handleMouseMove); + shimmerEffects.forEach(handleMouseLeave); +})(); diff --git a/views/assets/js/common-16451543478.js b/views/assets/js/common-16451543478.js index 520b8d94..95303b3e 100644 --- a/views/assets/js/common-16451543478.js +++ b/views/assets/js/common-16451543478.js @@ -4,69 +4,64 @@ /* MAIN Holy Unblocker LTS Common Script /* ----------------------------------------------- */ - // Encase everything in a new scope so that variables are not accidentally // attached to the global scope. (() => { + // Get the preferred apex domain name. Not exactly apex, as any + // subdomain other than those listed will be ignored. + const getDomain = () => + location.host.replace(/^(?:www|edu|cooking|beta)\./, ''), + // This is used for stealth mode when visiting external sites. + goFrame = (url) => { + localStorage.setItem('huframesrc', url); + location.href = '/s'; + }, + // Used to set functions for the goProx object at the bottom. + // See the goProx object at the bottom for some usage examples + // on the URL handlers, omnibox functions, and the uvUrl and + // RammerheadEncode functions. + urlHandler = (parser) => + typeof parser === 'function' + ? // Return different functions based on whether a URL has already been set. + // Should help avoid confusion when using or adding to the goProx object. + (url, mode) => { + if (!url) return; + url = parser(url); + mode = `${mode}`.toLowerCase(); + if (mode === 'stealth' || mode == 1) goFrame(url); + else if (mode === 'window' || mode == 0) location.href = url; + else return url; + } + : (mode) => { + mode = `${mode}`.toLowerCase(); + if (mode === 'stealth' || mode == 1) goFrame(parser); + else if (mode === 'window' || mode == 0) location.href = parser; + else return parser; + }, + // An asynchronous version of the function above, just in case. + asyncUrlHandler = (parser) => async (url, mode) => { + if (!url) return; + if (typeof parser === 'function') url = await parser(url); + mode = `${mode}`.toLowerCase(); + if (mode === 'stealth' || mode == 1) goFrame(url); + else if (mode === 'window' || mode == 0) location.href = url; + else return url; + }; -// Get the preferred apex domain name. Not exactly apex, as any -// subdomain other than those listed will be ignored. -const getDomain = - () => location.host.replace(/^(?:www|edu|cooking|beta)\./, ""), + /* COOKIE AUTH DEMO */ -// This is used for stealth mode when visiting external sites. - goFrame = url => { - localStorage.setItem("huframesrc", url); - location.href = "/s"; - }, - -// Used to set functions for the goProx object at the bottom. -// See the goProx object at the bottom for some usage examples -// on the URL handlers, omnibox functions, and the uvUrl and -// RammerheadEncode functions. - urlHandler = parser => typeof parser === "function" -// Return different functions based on whether a URL has already been set. -// Should help avoid confusion when using or adding to the goProx object. - ? (url, mode) => { - if (!url) return; - url = parser(url); - mode = `${mode}`.toLowerCase(); - if (mode === "stealth" || mode == 1) goFrame(url); - else if (mode === "window" || mode == 0) location.href = url; - else return url; - } - : mode => { - mode = `${mode}`.toLowerCase(); - if (mode === "stealth" || mode == 1) goFrame(parser); - else if (mode === "window" || mode == 0) location.href = parser; - else return parser; - }, - -// An asynchronous version of the function above, just in case. - asyncUrlHandler = parser => async (url, mode) => { - if (!url) return; - if (typeof parser === "function") url = await parser(url); - mode = `${mode}`.toLowerCase(); - if (mode === "stealth" || mode == 1) goFrame(url); - else if (mode === "window" || mode == 0) location.href = url; - else return url; + const setAuthCookie = (s, lax) => { + document.cookie = + s + + `; expires=${Date.now() + 259200}; SameSite=${lax ? 'Lax' : 'None'}; domain=.${getDomain()}; path=/; Secure;`; }; + /* OMNIBOX */ -/* COOKIE AUTH DEMO */ - -const setAuthCookie = (s, lax) => { - document.cookie = s + `; expires=${Date.now() + 259200}; SameSite=${lax ? "Lax" : "None"}; domain=.${getDomain()}; path=/; Secure;`; -}; - - -/* OMNIBOX */ - -// Search engine is set to Bing. Intended to work just like the usual -// bar at the top of a browser. -const sx = "bing.com" + "/search?q=", - -/* + // Search engine is set to Bing. Intended to work just like the usual + // bar at the top of a browser. + const sx = 'bing.com' + '/search?q=', + /* omnibox = url => (url.indexOf("http") ? "https://" + (url.indexOf(".") < 1 ? sx : "") @@ -74,499 +69,516 @@ const sx = "bing.com" + "/search?q=", + url; */ -// Another omnibox function. Unsure if the version above is needed. - search = (input, template = `https://${sx}%s`) => { - try { -// Return the input if it is already a valid URL. -// eg: https://example.com, https://example.com/test?q=param - return new URL(input) + ""; - } catch (e) { -// Continue if it is invalid. - } - - try { -// Check if the input is valid when http:// is added to the start. -// eg: example.com, https://example.com/test?q=param - const url = new URL(`http://${input}`); -// Return only if the hostname has a TLD or a subdomain. - if (url.hostname.indexOf(".") != -1) return url + ""; - } catch (e) { -// Continue if it is invalid. - } - -// Treat the input as a search query instead of a website. - return template.replace("%s", encodeURIComponent(input)); - }, - -// Parse a URL to use with Ultraviolet. - uvUrl = url => { - try { - url = location.origin + __uv$config.prefix + __uv$config.encodeUrl(search(url)); - } catch (e) { -// This is for cases where the Ultraviolet scripts have not been loaded. - url = search(url); - } - return url; - }; - - -/* RAMMERHEAD CONFIGURATION */ - -// Parse a URL to use with Rammerhead. Only usable if the server is active. -const RammerheadEncode = async baseUrl => { - -// Hellhead - const mod = (n, m) => ((n % m) + m) % m, - baseDictionary = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-", - shuffledIndicator = "_rhs", -// Return a copy of the base dictionary with a randomized character order. -// Will be used as a Caesar cipher for URL encoding. - generateDictionary = () => { - let str = ""; - const split = baseDictionary.split(""); - while (split.length > 0) { -// Using .splice automatically rounds down to the nearest whole number. - str += split.splice(Math.random() * split.length, 1)[0]; + // Another omnibox function. Unsure if the version above is needed. + search = (input, template = `https://${sx}%s`) => { + try { + // Return the input if it is already a valid URL. + // eg: https://example.com, https://example.com/test?q=param + return new URL(input) + ''; + } catch (e) { + // Continue if it is invalid. } - return str; + + try { + // Check if the input is valid when http:// is added to the start. + // eg: example.com, https://example.com/test?q=param + const url = new URL(`http://${input}`); + // Return only if the hostname has a TLD or a subdomain. + if (url.hostname.indexOf('.') != -1) return url + ''; + } catch (e) { + // Continue if it is invalid. + } + + // Treat the input as a search query instead of a website. + return template.replace('%s', encodeURIComponent(input)); + }, + // Parse a URL to use with Ultraviolet. + uvUrl = (url) => { + try { + url = + location.origin + + __uv$config.prefix + + __uv$config.encodeUrl(search(url)); + } catch (e) { + // This is for cases where the Ultraviolet scripts have not been loaded. + url = search(url); + } + return url; }; - class StrShuffler { - constructor(dictionary = generateDictionary()) { - this.dictionary = dictionary; - } + /* RAMMERHEAD CONFIGURATION */ - shuffle(str) { -// Do not reshuffle an already shuffled string. - if (!str.indexOf(shuffledIndicator)) return str; - - let shuffledStr = ""; - for (let i = 0; i < str.length; i++) { - const char = str[i], idx = baseDictionary.indexOf(char); - -// For URL encoded characters and characters not included in the -// dictionary, leave untouched. Otherwise, replace with a character -// from the dictionary. - if (char === "%" && str.length - i >= 3) -// A % symbol denotes that the next 2 characters are URL encoded. - shuffledStr += char + str[++i] + str[++i]; - -// Do not modify unrecognized characters. - else if (idx == -1) shuffledStr += char; - -// Find the corresponding dictionary entry and use the character -// that is i places to the right of it. - else shuffledStr += this.dictionary[ - mod(idx + i, baseDictionary.length) - ]; - } -// Add a prefix signifying that the string has been shuffled. - return shuffledIndicator + shuffledStr; - } - -// Unshuffling is currently not done on the client side, and likely -// won't ever be for this implementation. It is used by the server instead. - unshuffle(str) { -// Do not unshuffle an already unshuffled string. - if (str.indexOf(shuffledIndicator)) return str; - -// Remove the prefix signifying that the string has been shuffled. - str = str.slice(shuffledIndicator.length); - - let unshuffledStr = ""; - for (let i = 0; i < str.length; i++) { - const char = str[i], idx = this.dictionary.indexOf(char); - -// Convert the dictionary entry characters back into their base -// characters using the base dictionary. Again, leave URL encoded -// characters and unrecognized symbols alone. - if (char === "%" && str.length - i >= 3) - unshuffledStr += char + str[++i] + str[++i]; - - else if (idx == -1) unshuffledStr += char; - -// Find the corresponding base character entry and use the character -// that is i places to the left of it. - else unshuffledStr += baseDictionary[ - mod(idx - i, baseDictionary.length) - ]; - } - return unshuffledStr; - } - }; - -// Request information that's beiing stored elsewhere on the server. -// Executes the callback function if the server responds as intended. - const get = (url, callback, shush = false) => { - let request = new XMLHttpRequest(); - request.open("GET", url, true); - request.send(); - - request.onerror = () => { - if (!shush) console.log("Cannot communicate with the server"); - }; - request.onload = () => { - if (request.status === 200) callback(request.responseText); - else if (!shush) - console.log( - `Unexpected server response to not match "200". Server says "${request.responseText}"` - ); - }; - }, - -// Functions for interacting with Rammerhead backend code on the server. - api = { - -// Make a new Rammerhead session and do something with it. - newsession(callback) { - get("/newsession", callback); - }, - -// Check if a session with the specified ID exists, then do something. - sessionexists(id, callback) { - get("/sessionexists?id=" + encodeURIComponent(id), res => { - if (res === "exists") return callback(true); - if (res === "not found") return callback(false); - console.log("Unexpected response from server. Received " + res); - }); - }, - -// Request a brand new encoding table to use for Rammerhead. - shuffleDict(id, callback) { - console.log("Shuffling", id); - get("/api/shuffleDict?id=" + encodeURIComponent(id), res => { - callback(JSON.parse(res)); - }); - }, - }, - -// Organize Rammerhead sessions via the browser's local storage. -// Local data consists of session creation timestamps and session IDs. -// The rest of the data is stored on the server. - localStorageKey = "rammerhead_sessionids", - localStorageKeyDefault = "rammerhead_default_sessionid", - sessionIdsStore = { - -// Get the local data of all stored sessions. - get() { - const rawData = localStorage.getItem(localStorageKey); - if (!rawData) return []; - try { - const data = JSON.parse(rawData); - -// Catch invalidly stored Rammerhead session data. Either that or -// it's poorly spoofed. - if (!Array.isArray(data)) throw "getout"; - return data; - } catch (e) { - return []; + // Parse a URL to use with Rammerhead. Only usable if the server is active. + const RammerheadEncode = async (baseUrl) => { + // Hellhead + const mod = (n, m) => ((n % m) + m) % m, + baseDictionary = + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-', + shuffledIndicator = '_rhs', + // Return a copy of the base dictionary with a randomized character order. + // Will be used as a Caesar cipher for URL encoding. + generateDictionary = () => { + let str = ''; + const split = baseDictionary.split(''); + while (split.length > 0) { + // Using .splice automatically rounds down to the nearest whole number. + str += split.splice(Math.random() * split.length, 1)[0]; } - }, + return str; + }; -// Store local Rammerhead session data in the form of an array. - set(data) { - if (!Array.isArray(data)) throw new TypeError("Must be an array."); - localStorage.setItem(localStorageKey, JSON.stringify(data)); - }, + class StrShuffler { + constructor(dictionary = generateDictionary()) { + this.dictionary = dictionary; + } -// Get the default session data. - getDefault() { - const sessionId = localStorage.getItem(localStorageKeyDefault); - if (sessionId) { - let data = sessionIdsStore.get(); - data.filter(session => session.id === sessionId); - if (data.length) return data[0]; + shuffle(str) { + // Do not reshuffle an already shuffled string. + if (!str.indexOf(shuffledIndicator)) return str; + + let shuffledStr = ''; + for (let i = 0; i < str.length; i++) { + const char = str[i], + idx = baseDictionary.indexOf(char); + + // For URL encoded characters and characters not included in the + // dictionary, leave untouched. Otherwise, replace with a character + // from the dictionary. + if (char === '%' && str.length - i >= 3) + // A % symbol denotes that the next 2 characters are URL encoded. + shuffledStr += char + str[++i] + str[++i]; + // Do not modify unrecognized characters. + else if (idx == -1) shuffledStr += char; + // Find the corresponding dictionary entry and use the character + // that is i places to the right of it. + else + shuffledStr += this.dictionary[mod(idx + i, baseDictionary.length)]; } - return null; + // Add a prefix signifying that the string has been shuffled. + return shuffledIndicator + shuffledStr; + } + + // Unshuffling is currently not done on the client side, and likely + // won't ever be for this implementation. It is used by the server instead. + unshuffle(str) { + // Do not unshuffle an already unshuffled string. + if (str.indexOf(shuffledIndicator)) return str; + + // Remove the prefix signifying that the string has been shuffled. + str = str.slice(shuffledIndicator.length); + + let unshuffledStr = ''; + for (let i = 0; i < str.length; i++) { + const char = str[i], + idx = this.dictionary.indexOf(char); + + // Convert the dictionary entry characters back into their base + // characters using the base dictionary. Again, leave URL encoded + // characters and unrecognized symbols alone. + if (char === '%' && str.length - i >= 3) + unshuffledStr += char + str[++i] + str[++i]; + else if (idx == -1) unshuffledStr += char; + // Find the corresponding base character entry and use the character + // that is i places to the left of it. + else + unshuffledStr += + baseDictionary[mod(idx - i, baseDictionary.length)]; + } + return unshuffledStr; + } + } + + // Request information that's beiing stored elsewhere on the server. + // Executes the callback function if the server responds as intended. + const get = (url, callback, shush = false) => { + let request = new XMLHttpRequest(); + request.open('GET', url, true); + request.send(); + + request.onerror = () => { + if (!shush) console.log('Cannot communicate with the server'); + }; + request.onload = () => { + if (request.status === 200) callback(request.responseText); + else if (!shush) + console.log( + `Unexpected server response to not match "200". Server says "${request.responseText}"` + ); + }; }, + // Functions for interacting with Rammerhead backend code on the server. + api = { + // Make a new Rammerhead session and do something with it. + newsession(callback) { + get('/newsession', callback); + }, -// Set a new default session based on a given session ID. - setDefault(id) { - localStorage.setItem(localStorageKeyDefault, id); + // Check if a session with the specified ID exists, then do something. + sessionexists(id, callback) { + get('/sessionexists?id=' + encodeURIComponent(id), (res) => { + if (res === 'exists') return callback(true); + if (res === 'not found') return callback(false); + console.log('Unexpected response from server. Received ' + res); + }); + }, + + // Request a brand new encoding table to use for Rammerhead. + shuffleDict(id, callback) { + console.log('Shuffling', id); + get('/api/shuffleDict?id=' + encodeURIComponent(id), (res) => { + callback(JSON.parse(res)); + }); + }, }, - }, + // Organize Rammerhead sessions via the browser's local storage. + // Local data consists of session creation timestamps and session IDs. + // The rest of the data is stored on the server. + localStorageKey = 'rammerhead_sessionids', + localStorageKeyDefault = 'rammerhead_default_sessionid', + sessionIdsStore = { + // Get the local data of all stored sessions. + get() { + const rawData = localStorage.getItem(localStorageKey); + if (!rawData) return []; + try { + const data = JSON.parse(rawData); -// Store or update local data for a Rammerhead session, which consists of -// the session's ID and when the session was last created. - addSession = id => { - let data = sessionIdsStore.get(); - data.unshift({ id: id, createdOn: new Date().toLocaleString() }); - sessionIdsStore.set(data); - }, - -// Attempt to load an existing session that has been stored on the server. - getSessionId = () => { - return new Promise(resolve => { -// Check if the browser has stored an existing session. - const id = localStorage.getItem("session-string"); - api.sessionexists(id, value => { -// Create a new session if Rammerhead can't find an existing session. - if (!value) { - console.log("Session validation failed"); - api.newsession(id => { - addSession(id); - localStorage.setItem("session-string", id); - console.log(id); - console.log("^ new id"); - resolve(id); - }); + // Catch invalidly stored Rammerhead session data. Either that or + // it's poorly spoofed. + if (!Array.isArray(data)) throw 'getout'; + return data; + } catch (e) { + return []; } -// Load the stored session now that Rammerhead has found it. - else resolve(id); + }, + + // Store local Rammerhead session data in the form of an array. + set(data) { + if (!Array.isArray(data)) throw new TypeError('Must be an array.'); + localStorage.setItem(localStorageKey, JSON.stringify(data)); + }, + + // Get the default session data. + getDefault() { + const sessionId = localStorage.getItem(localStorageKeyDefault); + if (sessionId) { + let data = sessionIdsStore.get(); + data.filter((session) => session.id === sessionId); + if (data.length) return data[0]; + } + return null; + }, + + // Set a new default session based on a given session ID. + setDefault(id) { + localStorage.setItem(localStorageKeyDefault, id); + }, + }, + // Store or update local data for a Rammerhead session, which consists of + // the session's ID and when the session was last created. + addSession = (id) => { + let data = sessionIdsStore.get(); + data.unshift({ id: id, createdOn: new Date().toLocaleString() }); + sessionIdsStore.set(data); + }, + // Attempt to load an existing session that has been stored on the server. + getSessionId = () => { + return new Promise((resolve) => { + // Check if the browser has stored an existing session. + const id = localStorage.getItem('session-string'); + api.sessionexists(id, (value) => { + // Create a new session if Rammerhead can't find an existing session. + if (!value) { + console.log('Session validation failed'); + api.newsession((id) => { + addSession(id); + localStorage.setItem('session-string', id); + console.log(id); + console.log('^ new id'); + resolve(id); + }); + } + // Load the stored session now that Rammerhead has found it. + else resolve(id); + }); }); - }); - }; + }; -// Load the URL that was last visited in the Rammerhead session. - return getSessionId().then((id) => - new Promise(resolve => { - api.shuffleDict(id, shuffleDict => { -// Encode the URL with Rammerhead's encoding table and return the URL. - resolve(`/${id}/` + new StrShuffler(shuffleDict).shuffle(baseUrl)); - }); - }) - ); -}; - - -/* To use: - * goProx.proxy(url-string, mode-as-string-or-number); - * - * Key: 1 = "stealth" - * 0 = "window" - * Nothing = return URL as a string - * - * Examples: - * Stealth mode - - * goProx.ultraviolet("https://google.com", 1); - * goProx.ultraviolet("https://google.com", "stealth"); - * - * await goProx.rammerhead("https://google.com", 1); - * await goProx.rammerhead("https://google.com", "stealth"); - * - * goProx.searx(1); - * goProx.searx("stealth"); - * - * Window mode - - * goProx.ultraviolet("https://google.com", "window"); - * - * await goProx.rammerhead("https://google.com", "window"); - * - * goProx.searx("window"); - * - * Return string value mode (default) - - * goProx.ultraviolet("https://google.com"); - * - * await goProx.rammerhead("https://google.com"); - * - * goProx.searx(); - */ -addEventListener("DOMContentLoaded", async () => { - const goProx = { -// `location.protocol + "//" + getDomain()` more like `location.origin` -// setAuthCookie("__cor_auth=1", false); - ultraviolet: urlHandler(uvUrl), - - rammerhead: asyncUrlHandler(async url => location.origin + (await RammerheadEncode(search(url)))), - - searx: urlHandler(location.protocol + `//c.${getDomain()}/engine/`), - - libreddit: urlHandler(location.protocol + "//c." + getDomain()), - - rnav: urlHandler(location.protocol + "//client." + getDomain()), - - osu: urlHandler(location.origin + "/archive/osu"), - - mcnow: urlHandler(uvUrl("https://now.gg/play/a/10010/b")), - - glife: urlHandler(uvUrl("https://now.gg/apps/lunime/5767/gacha-life.html")), - - roblox: urlHandler(uvUrl("https://now.gg/apps/roblox-corporation/5349/roblox.html")), - - amongus: urlHandler(uvUrl("https://now.gg/apps/innersloth-llc/4047/among-us.html")), - - pubg: urlHandler(uvUrl("https://now.gg/apps/proxima-beta/2609/pubg-mobile-resistance.html")), - - train: urlHandler(uvUrl("https://hby.itch.io/last-train-home")), - - village: urlHandler(uvUrl("https://kwoodhouse.itch.io/village-arsonist")), - - prison: urlHandler(uvUrl("https://vimlark.itch.io/pick-up-prison")), - - rpg: urlHandler(uvUrl("https://alarts.itch.io/die-in-the-dungeon")), - - speed: urlHandler(uvUrl("https://captain4lk.itch.io/what-the-road-brings")), - - heli: urlHandler(uvUrl("https://benjames171.itch.io/helo-storm")), - - youtube: urlHandler(uvUrl("https://youtube.com")), - - discordUV: urlHandler(uvUrl("https://discord.com/app")) + // Load the URL that was last visited in the Rammerhead session. + return getSessionId().then( + (id) => + new Promise((resolve) => { + api.shuffleDict(id, (shuffleDict) => { + // Encode the URL with Rammerhead's encoding table and return the URL. + resolve(`/${id}/` + new StrShuffler(shuffleDict).shuffle(baseUrl)); + }); + }) + ); }; -// Don't slow down the rest of the script while encoding the URL. - RammerheadEncode("https://discord.com/app").then(url => { - goProx.discordRH = urlHandler(url); + /* To use: + * goProx.proxy(url-string, mode-as-string-or-number); + * + * Key: 1 = "stealth" + * 0 = "window" + * Nothing = return URL as a string + * + * Examples: + * Stealth mode - + * goProx.ultraviolet("https://google.com", 1); + * goProx.ultraviolet("https://google.com", "stealth"); + * + * await goProx.rammerhead("https://google.com", 1); + * await goProx.rammerhead("https://google.com", "stealth"); + * + * goProx.searx(1); + * goProx.searx("stealth"); + * + * Window mode - + * goProx.ultraviolet("https://google.com", "window"); + * + * await goProx.rammerhead("https://google.com", "window"); + * + * goProx.searx("window"); + * + * Return string value mode (default) - + * goProx.ultraviolet("https://google.com"); + * + * await goProx.rammerhead("https://google.com"); + * + * goProx.searx(); + */ + addEventListener('DOMContentLoaded', async () => { + const goProx = { + // `location.protocol + "//" + getDomain()` more like `location.origin` + // setAuthCookie("__cor_auth=1", false); + ultraviolet: urlHandler(uvUrl), -// Object.freeze prevents goProx from accidentally being edited. - Object.freeze(goProx); - }); + rammerhead: asyncUrlHandler( + async (url) => location.origin + (await RammerheadEncode(search(url))) + ), + searx: urlHandler(location.protocol + `//c.${getDomain()}/engine/`), -// Attach event listeners using goProx to specific app menus that need it. - const prSet = (id, type) => { - const formElement = document.getElementById(id); - if (!formElement) return; + libreddit: urlHandler(location.protocol + '//c.' + getDomain()), - let prUrl = formElement.querySelector("input[type=text]"), + rnav: urlHandler(location.protocol + '//client.' + getDomain()), + + osu: urlHandler(location.origin + '/archive/osu'), + + mcnow: urlHandler(uvUrl('https://now.gg/play/a/10010/b')), + + glife: urlHandler( + uvUrl('https://now.gg/apps/lunime/5767/gacha-life.html') + ), + + roblox: urlHandler( + uvUrl('https://now.gg/apps/roblox-corporation/5349/roblox.html') + ), + + amongus: urlHandler( + uvUrl('https://now.gg/apps/innersloth-llc/4047/among-us.html') + ), + + pubg: urlHandler( + uvUrl( + 'https://now.gg/apps/proxima-beta/2609/pubg-mobile-resistance.html' + ) + ), + + train: urlHandler(uvUrl('https://hby.itch.io/last-train-home')), + + village: urlHandler(uvUrl('https://kwoodhouse.itch.io/village-arsonist')), + + prison: urlHandler(uvUrl('https://vimlark.itch.io/pick-up-prison')), + + rpg: urlHandler(uvUrl('https://alarts.itch.io/die-in-the-dungeon')), + + speed: urlHandler( + uvUrl('https://captain4lk.itch.io/what-the-road-brings') + ), + + heli: urlHandler(uvUrl('https://benjames171.itch.io/helo-storm')), + + youtube: urlHandler(uvUrl('https://youtube.com')), + + discordUV: urlHandler(uvUrl('https://discord.com/app')), + }; + + // Don't slow down the rest of the script while encoding the URL. + RammerheadEncode('https://discord.com/app').then((url) => { + goProx.discordRH = urlHandler(url); + + // Object.freeze prevents goProx from accidentally being edited. + Object.freeze(goProx); + }); + + // Attach event listeners using goProx to specific app menus that need it. + const prSet = (id, type) => { + const formElement = document.getElementById(id); + if (!formElement) return; + + let prUrl = formElement.querySelector('input[type=text]'), prGo1 = document.querySelectorAll(`#${id}.pr-go1, #${id} .pr-go1`), prGo2 = document.querySelectorAll(`#${id}.pr-go2, #${id} .pr-go2`); -// Handle the other menu buttons differently if there is no omnibox. Menus -// which lack an omnibox likely use buttons as mere links. - const goProxMethod = prUrl - ? mode => () => {goProx[type](prUrl.value, mode)} - : mode => () => {goProx[type](mode)}, - -// Ultraviolet is currently incompatible with window mode. - searchMode = type === "ultraviolet" ? "stealth" : "window"; - - if (prUrl) prUrl.addEventListener("keydown", async e => { - if (e.code === "Enter") goProxMethod(searchMode)(); - -// This is exclusively used for the validator script. - else if (e.code === "Validator Test") { - e.target.value = await goProx[type](e.target.value); - e.target.dispatchEvent(new Event("change")); - } - }); - - prGo1.forEach(element => { - element.addEventListener("click", goProxMethod("window")); - }); - prGo2.forEach(element => { - element.addEventListener("click", goProxMethod("stealth")); - }); - }; - - - prSet("pr-uv", "ultraviolet"); - prSet("pr-rh", "rammerhead"); - prSet("pr-yt", "youtube"); - prSet("pr-rh-dc", "discordRH"); - prSet("pr-uv-dc", "discordUV"); - - - -// Load in relevant JSON files used to organize large sets of data. -// This first one is for links, whereas the rest are for navigation menus. - const huLinks = await fetch("/assets/json/links.json", {mode: "same-origin"}).then(response => response.json()); - - for (let items = Object.entries(huLinks), i = 0; i < items.length; i++) -// Replace all placeholder links with the corresponding entry in huLinks. - (document.getElementById(items[i][0]) || {}).href = items[i][1]; - - const navLists = { -// Pair an element ID with a JSON file name. They are identical for now. - "emu-nav": "emu-nav", - "emulib-nav": "emulib-nav", - "flash-nav": "flash-nav", - "h5-nav": "h5-nav" - }; - - for (const [listId, filename] of Object.entries(navLists)) { - - let navList = document.getElementById(listId); - - if(navList) { -// List items stored in JSON format will be returned as a JS object. - const data = await fetch(`/assets/json/${filename}.json`, {mode: "same-origin"}).then(response => response.json()); - -// Load the JSON lists into specific HTML parent elements as groups of -// child elements, if the parent element is found. - switch (filename) { - case "emu-nav": - case "emulib-nav": - case "h5-nav": { - const dirnames = { -// Set the directory of where each item of the corresponding JSON -// list will be retrieved from. - "emu-nav": "emu", - "emulib-nav": "emulib", - "h5-nav": "h5g" - }, - - dir = dirnames[filename], - -// Add a little functionality for each list item when clicked on. - clickHandler = (parser, a) => e => { - if (e.target == a || e.target.tagName != "A") { - e.preventDefault(); - parser(item); + // Handle the other menu buttons differently if there is no omnibox. Menus + // which lack an omnibox likely use buttons as mere links. + const goProxMethod = prUrl + ? (mode) => () => { + goProx[type](prUrl.value, mode); } - }; + : (mode) => () => { + goProx[type](mode); + }, + // Ultraviolet is currently incompatible with window mode. + searchMode = type === 'ultraviolet' ? 'stealth' : 'window'; - for (let i = 0; i < data.length; i++) { -// Load each item as an anchor tag with an image, heading, -// description, and click event listener. - const item = data[i], - a = document.createElement("a"), - img = document.createElement("img"), - title = document.createElement("h3"), - desc = document.createElement("p"); + if (prUrl) + prUrl.addEventListener('keydown', async (e) => { + if (e.code === 'Enter') goProxMethod(searchMode)(); + // This is exclusively used for the validator script. + else if (e.code === 'Validator Test') { + e.target.value = await goProx[type](e.target.value); + e.target.dispatchEvent(new Event('change')); + } + }); - a.href = "#"; - img.src = `/assets/img/${dir}/` + item.img; - title.textContent = item.name; - desc.textContent = item.description; + prGo1.forEach((element) => { + element.addEventListener('click', goProxMethod('window')); + }); + prGo2.forEach((element) => { + element.addEventListener('click', goProxMethod('stealth')); + }); + }; - if (filename === "h5-nav") { - if (item.credits === "itch") desc.innerHTML += '
Credits: Game can be found here.'; - if (item.credits === "nowgg") desc.innerHTML += '
Credits: Game can be found here.'; + prSet('pr-uv', 'ultraviolet'); + prSet('pr-rh', 'rammerhead'); + prSet('pr-yt', 'youtube'); + prSet('pr-rh-dc', 'discordRH'); + prSet('pr-uv-dc', 'discordUV'); + + // Load in relevant JSON files used to organize large sets of data. + // This first one is for links, whereas the rest are for navigation menus. + const huLinks = await fetch('/assets/json/links.json', { + mode: 'same-origin', + }).then((response) => response.json()); + + for (let items = Object.entries(huLinks), i = 0; i < items.length; i++) + // Replace all placeholder links with the corresponding entry in huLinks. + (document.getElementById(items[i][0]) || {}).href = items[i][1]; + + const navLists = { + // Pair an element ID with a JSON file name. They are identical for now. + 'emu-nav': 'emu-nav', + 'emulib-nav': 'emulib-nav', + 'flash-nav': 'flash-nav', + 'h5-nav': 'h5-nav', + }; + + for (const [listId, filename] of Object.entries(navLists)) { + let navList = document.getElementById(listId); + + if (navList) { + // List items stored in JSON format will be returned as a JS object. + const data = await fetch(`/assets/json/${filename}.json`, { + mode: 'same-origin', + }).then((response) => response.json()); + + // Load the JSON lists into specific HTML parent elements as groups of + // child elements, if the parent element is found. + switch (filename) { + case 'emu-nav': + case 'emulib-nav': + case 'h5-nav': { + const dirnames = { + // Set the directory of where each item of the corresponding JSON + // list will be retrieved from. + 'emu-nav': 'emu', + 'emulib-nav': 'emulib', + 'h5-nav': 'h5g', + }, + dir = dirnames[filename], + // Add a little functionality for each list item when clicked on. + clickHandler = (parser, a) => (e) => { + if (e.target == a || e.target.tagName != 'A') { + e.preventDefault(); + parser(item); + } + }; + + for (let i = 0; i < data.length; i++) { + // Load each item as an anchor tag with an image, heading, + // description, and click event listener. + const item = data[i], + a = document.createElement('a'), + img = document.createElement('img'), + title = document.createElement('h3'), + desc = document.createElement('p'); + + a.href = '#'; + img.src = `/assets/img/${dir}/` + item.img; + title.textContent = item.name; + desc.textContent = item.description; + + if (filename === 'h5-nav') { + if (item.credits === 'itch') + desc.innerHTML += + '
Credits: Game can be found here.'; + if (item.credits === 'nowgg') + desc.innerHTML += + '
Credits: Game can be found here.'; + } + + a.appendChild(img); + a.appendChild(title); + a.appendChild(desc); + + // Which function is used for the click event is determined by + // the corresponding location/index in the dirnames object. + const functionsList = [ + () => goFrame(item.path), + () => + goFrame('/webretro?core=' + item.core + '&rom=' + item.rom), + item.custom + ? () => goProx[item.custom]('stealth') + : () => goFrame('/archive/g/' + item.path), + ]; + + a.addEventListener( + 'click', + clickHandler( + functionsList[Object.values(dirnames).indexOf(dir)], + a + ) + ); + + navList.appendChild(a); } - - a.appendChild(img); - a.appendChild(title); - a.appendChild(desc); - -// Which function is used for the click event is determined by -// the corresponding location/index in the dirnames object. - const functionsList = [ - () => goFrame(item.path), - () => goFrame("/webretro?core=" + item.core + "&rom=" + item.rom), - item.custom ? () => goProx[item.custom]("stealth") : () => goFrame("/archive/g/" + item.path) - ]; - - a.addEventListener("click", clickHandler(functionsList[Object.values(dirnames).indexOf(dir)], a)); - - navList.appendChild(a); + break; } - break; + + case 'flash-nav': + for (let i = 0; i < data.length; i++) { + // Load each item as an anchor tag with a short title and click + // event listener. + const item = data[i], + a = document.createElement('a'); + a.href = '#'; + a.textContent = item.slice(0, -4); + + a.addEventListener('click', (e) => { + e.preventDefault(); + goFrame('/flash?swf=' + item); + }); + + navList.appendChild(a); + } + break; + + // No default case. } - - case "flash-nav": - for (let i = 0; i < data.length; i++) { -// Load each item as an anchor tag with a short title and click -// event listener. - const item = data[i], a = document.createElement("a"); - a.href = "#"; - a.textContent = item.slice(0, -4); - - a.addEventListener("click", e => { - e.preventDefault(); - goFrame("/flash?swf=" + item); - }); - - navList.appendChild(a); - } - break; - -// No default case. - } } - } -}); - -})(); \ No newline at end of file + }); +})(); diff --git a/views/assets/js/csel.js b/views/assets/js/csel.js index 192e5561..5731ca43 100644 --- a/views/assets/js/csel.js +++ b/views/assets/js/csel.js @@ -5,150 +5,168 @@ /* Settings Menu /* ----------------------------------------------- */ - // Encase everything in a new scope so that variables are not accidentally // attached to the global scope. (() => { + // Determine the expiration date of a new cookie. + let date = new Date(); + date.setFullYear(date.getFullYear() + 100); + date = date.toUTCString(); -// Determine the expiration date of a new cookie. -let date = new Date(); -date.setFullYear(date.getFullYear() + 100); -date = date.toUTCString(); + // All cookies should be secure and are intended to work in iframes. + const setCookie = (name, value) => { + document.cookie = + name + + `=${encodeURIComponent(value)}; expires=${date}; SameSite=None; Secure;`; + }, + removeCookie = (name) => { + document.cookie = + name + + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure;'; + }, + readCookie = async (name) => { + // Get the first cookie that has the same name. + for (let cookie of document.cookie.split('; ')) + if (!cookie.indexOf(name + '=')) + // Return the cookie's stored content. + return decodeURIComponent(cookie.slice(name.length + 1)); + }, + // Customize the page's title. + pageTitle = (value) => { + let tag = + document.getElementsByTagName('title')[0] || + document.createElement('title'); + tag.innerHTML = value; + document.head.appendChild(tag); + }, + // Set the page's favicon to a new URL. + pageIcon = (value) => { + let tag = + document.querySelector("link[rel*='icon']") || + document.createElement('link'); + tag.rel = 'icon'; + tag.href = value; + document.head.appendChild(tag); + }, + // Make a small stylesheet to override a setting from the main stylesheet. + pageShowAds = () => { + let advertising = document.createElement('style'); + advertising.id = 'advertising'; + advertising.innerText = '.ad { display:block; }'; + ( + document.head || + document.body || + document.documentElement || + document + ).appendChild(advertising); + }, + // Remove the stylesheet made by the function above, if it exists. + pageHideAds = () => { + (document.getElementById('advertising') || new Text()).remove(); + }, + // These titles and icons are used as autofill templates by settings.html. + // The icon URLs and tab titles may need to be updated over time. + presetIcons = Object.freeze({ + '': ' \n ', + Google: 'Google \n https://www.google.com/favicon.ico', + Bing: 'Bing \n https://www.bing.com/sa/simg/favicon-trans-bg-blue-mg-28.ico', + 'Google Drive': + 'Home - Google Drive \n https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png', + Gmail: + 'Inbox - Gmail \n https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico', + }), + // Choose the default transport mode, for proxying, based on the browser. + // Firefox is not supported by epoxy yet, which is why this is implemented. + defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) + ? 'epoxy' + : 'libcurl'; -// All cookies should be secure and are intended to work in iframes. -const setCookie = (name, value) => { - document.cookie = name + `=${encodeURIComponent(value)}; expires=${date}; SameSite=None; Secure;`; - }, + // Load a custom page title and favicon if it was previously stored. + readCookie('HBTitle').then((s) => { + s != undefined && pageTitle(s); + }); + readCookie('HBIcon').then((s) => { + s != undefined && pageIcon(s); + }); - removeCookie = name => { - document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=None; Secure;"; - }, + // Load the UV transport mode that was last used, or use the default. + readCookie('HBTransport').then((s) => { + let transportMode = document.querySelector( + `#uv-transport-list input[value="${s || defaultMode}"]` + ); + if (transportMode) transportMode.click(); + }); - readCookie = async name => { - for (let cookie of document.cookie.split("; ")) -// Get the first cookie that has the same name. - if (!cookie.indexOf(name + "=")) -// Return the cookie's stored content. - return decodeURIComponent(cookie.slice(name.length + 1)); - }, + // Ads are disabled by default. Load ads if ads were enabled previously. + // Change !== to === here if ads should be enabled by default. + readCookie('HBHideAds').then((s) => { + s !== 'false' + ? pageHideAds() + : pageShowAds(((document.getElementById('hideads') || {}).checked = 0)); + }); -// Customize the page's title. - pageTitle = value => { - let tag = document.getElementsByTagName("title")[0] || document.createElement("title"); - tag.innerHTML = value; - document.head.appendChild(tag); - }, - -// Set the page's favicon to a new URL. - pageIcon = value => { - let tag = document.querySelector("link[rel*='icon']") || document.createElement("link"); - tag.rel = "icon"; - tag.href = value; - document.head.appendChild(tag); - }, - -// Make a small stylesheet to override a setting from the main stylesheet. - pageShowAds = () => { - let advertising = document.createElement("style"); - advertising.id = "advertising"; - advertising.innerText = ".ad { display:block; }"; - (document.head || document.body || document.documentElement || document).appendChild(advertising); - }, - -// Remove the stylesheet made by the function above, if it exists. - pageHideAds = () => { - (document.getElementById("advertising")||new Text()).remove(); - }, - -// These titles and icons are used as autofill templates by settings.html. -// The icon URLs and tab titles may need to be updated over time. - presetIcons = Object.freeze({ - "": " \n ", - "Google": "Google \n https://www.google.com/favicon.ico", - "Bing": "Bing \n https://www.bing.com/sa/simg/favicon-trans-bg-blue-mg-28.ico", - "Google Drive": "Home - Google Drive \n https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png", - "Gmail": "Inbox - Gmail \n https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico" - }), - -// Choose the default transport mode, for proxying, based on the browser. -// Firefox is not supported by epoxy yet, which is why this is implemented. - defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) - ? "epoxy" - : "libcurl"; - - -// Load a custom page title and favicon if it was previously stored. -readCookie("HBTitle").then(s => {(s != undefined) && pageTitle(s)}); -readCookie("HBIcon").then(s => {(s != undefined) && pageIcon(s)}); - -// Load the UV transport mode that was last used, or use the default. -readCookie("HBTransport").then(s => { - let transportMode = - document.querySelector(`#uv-transport-list input[value="${ - s || defaultMode - }"]`); - if (transportMode) transportMode.click(); -}); - -// Ads are disabled by default. Load ads if ads were enabled previously. -// Change !== to === here if ads should be enabled by default. -readCookie("HBHideAds").then(s => {(s !== "false") ? pageHideAds() : pageShowAds((document.getElementById("hideads") || {}).checked = 0)}); - -// Tor is disabled by default. Enable Tor if it was enabled previously. -readCookie("HBUseOnion").then(s => {if (s === "true") { - let torCheck = document.getElementById("useonion") || - {dispatchEvent: () => {}}; - torCheck.checked = 1; - torCheck.dispatchEvent(new Event("change")); -}}); - - -// All code below is used by the Settings UI in the navigation bar. -if (document.getElementById("csel")) { - const attachEventListener = (selector, ...args) => ( - document.getElementById(selector) || - document.querySelector(selector) - ).addEventListener(...args), - focusElement = - document.getElementsByClassName("dropdown-settings")[0].parentElement.querySelector("a[href='#']"); - - attachEventListener(".dropdown-settings .close-settings-btn", "click", - () => {document.activeElement.blur()} - ); - -// Allow users to set a custom title with the UI. - attachEventListener("titleform", "submit", e => { - e.preventDefault(); - e = e.target.firstElementChild; - if (e.value) { - pageTitle(e.value); - setCookie("HBTitle", e.value); - e.value = ""; - } else if (confirm("Reset the title to default?")) { -// Allow users to reset the title to default if nothing is entered. - focusElement.focus(); - removeCookie("HBTitle"); - pageTitle("Holy Unblocker LTS"); + // Tor is disabled by default. Enable Tor if it was enabled previously. + readCookie('HBUseOnion').then((s) => { + if (s === 'true') { + let torCheck = document.getElementById('useonion') || { + dispatchEvent: () => {}, + }; + torCheck.checked = 1; + torCheck.dispatchEvent(new Event('change')); } }); -// Allow users to set a custom favicon with the UI. - attachEventListener("iconform", "submit", e => { - e.preventDefault(); - e = e.target.firstElementChild; - if (e.value) { - pageIcon(e.value); - setCookie("HBIcon", e.value); - e.value = ""; - } else if (confirm("Reset the icon to default?")) { -// Allow users to reset the favicon to default if nothing is entered. - focusElement.focus(); - removeCookie("HBIcon"); - pageIcon("assets/img/icon.png"); - } - }); + // All code below is used by the Settings UI in the navigation bar. + if (document.getElementById('csel')) { + const attachEventListener = (selector, ...args) => + ( + document.getElementById(selector) || document.querySelector(selector) + ).addEventListener(...args), + focusElement = document + .getElementsByClassName('dropdown-settings')[0] + .parentElement.querySelector("a[href='#']"); -/* + attachEventListener( + '.dropdown-settings .close-settings-btn', + 'click', + () => { + document.activeElement.blur(); + } + ); + + // Allow users to set a custom title with the UI. + attachEventListener('titleform', 'submit', (e) => { + e.preventDefault(); + e = e.target.firstElementChild; + if (e.value) { + pageTitle(e.value); + setCookie('HBTitle', e.value); + e.value = ''; + } else if (confirm('Reset the title to default?')) { + // Allow users to reset the title to default if nothing is entered. + focusElement.focus(); + removeCookie('HBTitle'); + pageTitle('Holy Unblocker LTS'); + } + }); + + // Allow users to set a custom favicon with the UI. + attachEventListener('iconform', 'submit', (e) => { + e.preventDefault(); + e = e.target.firstElementChild; + if (e.value) { + pageIcon(e.value); + setCookie('HBIcon', e.value); + e.value = ''; + } else if (confirm('Reset the icon to default?')) { + // Allow users to reset the favicon to default if nothing is entered. + focusElement.focus(); + removeCookie('HBIcon'); + pageIcon('assets/img/icon.png'); + } + }); + + /* This is unused in the current settings menu. @@ -163,67 +181,70 @@ if (document.getElementById("csel")) { }); */ -// Provides users with a handy set of title and icon autofill options. - attachEventListener("icon-list", "change", e => { - let titleform = document.getElementById("titleform"), - iconform = document.getElementById("iconform"); - [titleform.firstElementChild.value, - iconform.firstElementChild.value] = - (presetIcons[e.target.value] || " \n ").split(" \n "); - }); + // Provides users with a handy set of title and icon autofill options. + attachEventListener('icon-list', 'change', (e) => { + let titleform = document.getElementById('titleform'), + iconform = document.getElementById('iconform'); + [titleform.firstElementChild.value, iconform.firstElementChild.value] = ( + presetIcons[e.target.value] || ' \n ' + ).split(' \n '); + }); -// Allow users to change the UV transport mode, for proxying, with the UI. - const uvTransportList = document.getElementById("uv-transport-list"); - uvTransportList.querySelectorAll("input").forEach(element => { - element.addEventListener("change", e => { - !uvTransportList.querySelector("input:checked") || - e.target.value === defaultMode - ? removeCookie("HBTransport") - : setCookie("HBTransport", e.target.value); + // Allow users to change the UV transport mode, for proxying, with the UI. + const uvTransportList = document.getElementById('uv-transport-list'); + uvTransportList.querySelectorAll('input').forEach((element) => { + element.addEventListener('change', (e) => { + !uvTransportList.querySelector('input:checked') || + e.target.value === defaultMode + ? removeCookie('HBTransport') + : setCookie('HBTransport', e.target.value); - // Only the libcurl transport mode supports Tor at the moment. - let torCheck = document.getElementById("useonion"); - if(e.target.value !== "libcurl" && torCheck.checked) - torCheck.click(); - }) -}); + // Only the libcurl transport mode supports Tor at the moment. + let torCheck = document.getElementById('useonion'); + if (e.target.value !== 'libcurl' && torCheck.checked) torCheck.click(); + }); + }); -// Allow users to toggle ads with the UI. - attachEventListener("hideads", "change", e => { - if (e.target.checked) { - pageHideAds(); - setCookie("HBHideAds", "true"); - } else { - pageShowAds(); - setCookie("HBHideAds", "false"); - } - }); + // Allow users to toggle ads with the UI. + attachEventListener('hideads', 'change', (e) => { + if (e.target.checked) { + pageHideAds(); + setCookie('HBHideAds', 'true'); + } else { + pageShowAds(); + setCookie('HBHideAds', 'false'); + } + }); -// Allow users to toggle onion routing in Ultraviolet with the UI. Only -// the libcurl transport mode supports Tor at the moment, so ensure that -// users are aware that they cannot use Tor with other modes. - attachEventListener("useonion", "change", e => { - let unselectedModes = - document.querySelectorAll("#uv-transport-list input:not([value=libcurl])"); - if (e.target.checked) { - let selectedMode = - document.querySelector("#uv-transport-list input[value=libcurl]"); - unselectedModes.forEach(e => {e.setAttribute("disabled", "true")}); - selectedMode.click(); - setCookie("HBUseOnion", "true"); - } else { - unselectedModes.forEach(e => {e.removeAttribute("disabled")}); - -// Tor will likely never be enabled by default, so removing the cookie -// here may be better than setting it to false. - removeCookie("HBUseOnion"); - } - }); -} + // Allow users to toggle onion routing in Ultraviolet with the UI. Only + // the libcurl transport mode supports Tor at the moment, so ensure that + // users are aware that they cannot use Tor with other modes. + attachEventListener('useonion', 'change', (e) => { + let unselectedModes = document.querySelectorAll( + '#uv-transport-list input:not([value=libcurl])' + ); + if (e.target.checked) { + let selectedMode = document.querySelector( + '#uv-transport-list input[value=libcurl]' + ); + unselectedModes.forEach((e) => { + e.setAttribute('disabled', 'true'); + }); + selectedMode.click(); + setCookie('HBUseOnion', 'true'); + } else { + unselectedModes.forEach((e) => { + e.removeAttribute('disabled'); + }); + // Tor will likely never be enabled by default, so removing the cookie + // here may be better than setting it to false. + removeCookie('HBUseOnion'); + } + }); + } })(); - /* ----------------------------------------------- /* Original code written by OlyB /* ----------------------------------------------- @@ -359,4 +380,4 @@ decodeURIComponent(atob("JTNDcCUyMGNsYXNzJTNEJTIyY3NlbHRpdGxlJTIyJTNFVGFiJTIwQ2x }, false); } })(); -*/ \ No newline at end of file +*/ diff --git a/views/assets/js/particlesjs/particles.js b/views/assets/js/particlesjs/particles.js index 38ee95a3..944e0ed0 100644 --- a/views/assets/js/particlesjs/particles.js +++ b/views/assets/js/particlesjs/particles.js @@ -2,10 +2,10 @@ await loadFull(tsParticles); await tsParticles.load({ - id: "particles-js", + id: 'particles-js', options: { background: { - color: { value: "#1d232a" }, + color: { value: '#1d232a' }, }, fullScreen: { enable: true, @@ -22,14 +22,14 @@ }, particles: { color: { - value: "#ffffff", + value: '#ffffff', }, move: { enable: true, speed: 0.8, - direction: "none", + direction: 'none', outModes: { - default: "out", + default: 'out', }, }, number: { @@ -48,7 +48,7 @@ }, }, shape: { - type: "circle", + type: 'circle', }, size: { value: { min: 1, max: 5 }, @@ -61,7 +61,7 @@ links: { enable: true, distance: 150, - color: "#ffffff", + color: '#ffffff', opacity: 0.4, width: 1, }, diff --git a/views/assets/js/register-sw.js b/views/assets/js/register-sw.js index 11d2db83..d8db463c 100644 --- a/views/assets/js/register-sw.js +++ b/views/assets/js/register-sw.js @@ -1,81 +1,83 @@ // Encase everything in a new scope so that variables are not accidentally // attached to the global scope. (() => { + const stockSW = '/uv/sw.js', + blacklistSW = '/uv/sw-blacklist.js', + swAllowedHostnames = ['localhost', '127.0.0.1'], + connection = new BareMux.BareMuxConnection('/baremux/worker.js'), + wispUrl = + (location.protocol === 'https:' ? 'wss' : 'ws') + + '://' + + location.host + + '/wisp/', + // Proxy configuration + proxyUrl = 'socks5h://localhost:9050', // Replace with your proxy URL + transports = { + epoxy: '/epoxy/index.mjs', + libcurl: '/libcurl/index.mjs', + bare: '/baremux/index.mjs', + }, + // The following two variables are copied and pasted here from csel.js. + readCookie = async (name) => { + // Get the first cookie that has the same name. + for (let cookie of document.cookie.split('; ')) + if (!cookie.indexOf(name + '=')) + // Return the cookie's stored content. + return decodeURIComponent(cookie.slice(name.length + 1)); + }, + // Sets the default transport mode based on the browser. Firefox is not + // supported by epoxy yet, which is why this is implemented. + defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) + ? 'epoxy' + : 'libcurl'; -const stockSW = "/uv/sw.js", - blacklistSW = "/uv/sw-blacklist.js", - swAllowedHostnames = ["localhost", "127.0.0.1"], - connection = new BareMux.BareMuxConnection("/baremux/worker.js"), - wispUrl = (location.protocol === "https:" ? "wss" : "ws") + "://" + location.host + "/wisp/", + transports.default = transports[defaultMode]; -// Proxy configuration - proxyUrl = "socks5h://localhost:9050", // Replace with your proxy URL - transports = { - epoxy: "/epoxy/index.mjs", - libcurl: "/libcurl/index.mjs", - bare: "/baremux/index.mjs" - }, + // Prevent the transports object from accidentally being edited. + Object.freeze(transports); -// The following two variables are copied and pasted here from csel.js. - readCookie = async name => { - for (let cookie of document.cookie.split("; ")) -// Get the first cookie that has the same name. - if (!cookie.indexOf(name + "=")) -// Return the cookie's stored content. - return decodeURIComponent(cookie.slice(name.length + 1)); - }, + const registerSW = async () => { + if (!navigator.serviceWorker) { + if ( + location.protocol !== 'https:' && + !swAllowedHostnames.includes(location.hostname) + ) + throw new Error('Service workers cannot be registered without https.'); -// Sets the default transport mode based on the browser. Firefox is not -// supported by epoxy yet, which is why this is implemented. - defaultMode = /(?:Chrome|AppleWebKit)\//.test(navigator.userAgent) - ? "epoxy" - : "libcurl"; + throw new Error("Your browser doesn't support service workers."); + } -transports.default = transports[defaultMode]; + // If the user has changed the transport mode, use that over the default. + const transportMode = + transports[await readCookie('HBTransport')] || transports.default; + let transportOptions = { wisp: wispUrl }; -// Prevent the transports object from accidentally being edited. -Object.freeze(transports); + // Only use Tor with the proxy if the user has enabled it in settings. + if ((await readCookie('HBUseOnion')) === 'true') + transportOptions.proxy = proxyUrl; -const registerSW = async () => { - if (!navigator.serviceWorker) { - if ( - location.protocol !== "https:" && - !swAllowedHostnames.includes(location.hostname) - ) - throw new Error("Service workers cannot be registered without https."); + await connection.setTransport(transportMode, [transportOptions]); - throw new Error("Your browser doesn't support service workers."); - } + // Choose a service worker to register based on whether or not the user + // has ads enabled. If the user changes this setting, this script needs + // to be reloaded for this to update, such as by refreshing the page. + const registrations = await navigator.serviceWorker.getRegistrations(), + usedSW = + (await readCookie('HBHideAds')) !== 'false' ? blacklistSW : stockSW; -// If the user has changed the transport mode, use that over the default. - const transportMode = transports[await readCookie("HBTransport")] || - transports.default; - let transportOptions = { wisp: wispUrl }; + // Unregister a service worker if it isn't the one being used. + for (const registration of registrations) + if ( + registration.active && + new URL(registration.active.scriptURL).pathname !== + new URL(usedSW, location.origin).pathname + ) + await registration.unregister(); -// Only use Tor with the proxy if the user has enabled it in settings. - if (await readCookie("HBUseOnion") === "true") - transportOptions.proxy = proxyUrl; + await navigator.serviceWorker.register(usedSW); + }; - await connection.setTransport(transportMode, [transportOptions]); - -// Choose a service worker to register based on whether or not the user -// has ads enabled. If the user changes this setting, this script needs -// to be reloaded for this to update, such as by refreshing the page. - const registrations = await navigator.serviceWorker.getRegistrations(), - usedSW = await readCookie("HBHideAds") !== "false" - ? blacklistSW - : stockSW; - -// Unregister a service worker if it isn't the one being used. - for (const registration of registrations) - if (registration.active && - new URL(registration.active.scriptURL).pathname !== new URL(usedSW, location.origin).pathname) - await registration.unregister(); - - await navigator.serviceWorker.register(usedSW); -} - -/* + /* Commented out upon discovering that a duplicate BareMux connection may be unnecessary; previously thought to have prevented issues with refreshing. @@ -91,6 +93,5 @@ async function setupTransportOnLoad() { setupTransportOnLoad(); */ -registerSW(); - -})(); \ No newline at end of file + registerSW(); +})(); diff --git a/views/docs.html b/views/docs.html index 39a6356e..b46a16dd 100644 --- a/views/docs.html +++ b/views/docs.html @@ -44,18 +44,18 @@ - - - - - - - + + + + + + + - + - - - - -
-
-
- -
- -
-

- git clone https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
- cd Holy-Unblocker

- npm start
- Or on subsequent uses...
- npm restart

- Holy Unblocker LTS v
- Node.js v20
- Fastify v4.28.1 -

-
-
-
-
­
-
-
-
-
-
- -

Holy Unblocker is free.

-
-

- Being open source, you can easily fork this repository and self - host for maximum privacy control. In contrast to numerous other - web proxy services, HU LTS stands out with complete transparency - and privacy control. We collect no user data is collected and - ensure browsing privacy. -

-
- -

Holy Unblocker is fast and highly advanced.

-
-

- HU LTS delivers exceptional web proxy performance. It boasts a - robust feature set including CAPTCHA integration, customizable - blacklist settings, leak prevention mechanisms, robust security - measures, and extensive site compatibility support via - Ultraviolet + Wisp. -

-
- -

Holy Unblocker is fast and highly advanced.

-
-

- Leveraging our custom source randomization and projects like - Epoxy, Wisp, and Ultraviolet, HU LTS delivers a seamless - experience that effectively circumvents web and network filters. - This is achieved entirely within your browser and our backend - (or your own if self-hosting), enabling users to bypass even the - most stringent censorship blocks. -

-
-
-
- icon -

Holy Unblocker LTS

-

Free and transparent for use

- Browse Now -
-
+ } + ] + } + + + + + + -