From ce8cedcab1f96d47f997cd6c44ce13ac85c73f6c Mon Sep 17 00:00:00 2001
From: As90909w <163041654+As90909w@users.noreply.github.com>
Date: Mon, 18 Mar 2024 20:13:24 -0700
Subject: [PATCH] Code - JS files
---
Jason/about.js | 83 ++++++++++++
Jason/games.js | 211 +++++++++++++++++++++++++++++
Jason/home.js | 84 ++++++++++++
Jason/index.js | 333 ++++++++++++++++++++++++++++++++++++++++++++++
Jason/main.js | 61 +++++++++
Jason/reviews.js | 197 +++++++++++++++++++++++++++
Jason/settings.js | 230 ++++++++++++++++++++++++++++++++
7 files changed, 1199 insertions(+)
create mode 100644 Jason/about.js
create mode 100644 Jason/games.js
create mode 100644 Jason/home.js
create mode 100644 Jason/index.js
create mode 100644 Jason/main.js
create mode 100644 Jason/reviews.js
create mode 100644 Jason/settings.js
diff --git a/Jason/about.js b/Jason/about.js
new file mode 100644
index 0000000..a4d5f66
--- /dev/null
+++ b/Jason/about.js
@@ -0,0 +1,83 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This script fetches version information from two JSON files and displays the current version and a warning message
+if the user is not on the latest released version.
+*/
+
+// An asynchronous function is defined and immediately invoked.
+(async () => {
+ // The 'version' and 'version-warning' elements are assigned to variables.
+ var version = document.getElementById("version");
+ var versionWarning = document.getElementById("version-warning");
+
+ // Two variables are defined to hold version information.
+ var currentVersion;
+ var latestVersion;
+
+ // A fetch request is made to a local 'info.json' file to get the current version information.
+ try {
+ var infoFetch = await fetch(location.origin + "./assets/json/info.json");
+ try {
+ var infoResult = await infoFetch.json();
+ // If the current version information is available in the JSON file, it is assigned to the 'currentVersion' variable.
+ if (infoResult.version) {
+ currentVersion = infoResult.version;
+ }
+ } catch {}
+ } catch {}
+
+ // A fetch request is made to a remote 'info.json' file to get the latest version information.
+ try {
+ var infoFetch = await fetch("https://raw.githack.com/3kh0/3kh0.github.io/main/assets/json/info.json");
+ try {
+ var infoResult = await infoFetch.json();
+ // If the latest version information is available in the JSON file, it is assigned to the 'latestVersion' variable.
+ if (infoResult.version) {
+ latestVersion = infoResult.version;
+ }
+ } catch {}
+ } catch {}
+
+ // The 'version' element is updated with the current version information, if available.
+ if (currentVersion) {
+ version.innerText = "You are on version " + currentVersion;
+ } else {
+ version.innerText = "Cannot get current version.";
+ }
+
+ // Messages are defined for different scenarios where the user is not on the latest released version.
+ var oldMessage = "Warning: You are on a older version. The current version is %VERSION%";
+ var betaMessage = "You are on a pre-release version! The current release is %VERSION%";
+ var otherMessage = "You not on the currently released version. The current release is %VERSION%";
+
+ // If the latest version information is available and the current version is not the latest, a warning message is displayed to the user.
+ if (latestVersion && currentVersion !== latestVersion) {
+ // The version numbers are extracted from the version information and converted to a comparable format.
+ var latestVersionNumber = latestVersion.replace("v", "").replaceAll("-", ".");
+ var firstStr = latestVersionNumber.search(/\./) + 1;
+ latestVersionNumber = Number(latestVersionNumber.substr(0, firstStr) + latestVersionNumber.slice(firstStr).replace(/\./g, ""));
+
+ var currentVersionNumber = currentVersion.replace("v", "").replaceAll("-", ".");
+ var firstStr2 = currentVersionNumber.search(/\./) + 1;
+ currentVersionNumber = Number(currentVersionNumber.substr(0, firstStr2) + currentVersionNumber.slice(firstStr2).replace(/\./g, ""));
+
+ var message;
+
+ // Depending on the version comparison, a message is selected from the previously defined messages.
+ if (isNaN(latestVersionNumber) || isNaN(currentVersionNumber)) {
+ message = otherMessage;
+ } else {
+ if (currentVersionNumber > latestVersionNumber) {
+ message = betaMessage;
+ } else {
+ message = oldMessage;
+ }
+ }
+
+ // The 'version-warning' element is updated with the warning message and displayed.
+ versionWarning.innerText = betaMessage.replace("%VERSION%", latestVersion);
+ versionWarning.style.display = "block";
+ }
+})();
\ No newline at end of file
diff --git a/Jason/games.js b/Jason/games.js
new file mode 100644
index 0000000..647f085
--- /dev/null
+++ b/Jason/games.js
@@ -0,0 +1,211 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This is a JavaScript code that creates a game catalog page with a search feature,
+a game detail page, and the ability to save and load user data.
+
+It uses fetch to load game data from a JSON file, creates game elements for each game,
+and adds click event listeners to show the game in a game container.
+
+The code also includes functions to handle saving and loading user data as well as a function to handle a specific key sequence.
+*/
+// Select the elements
+const gamesContainer = document.querySelector('.games');
+const searchBar = document.querySelector('.searchbar');
+const gameContainer = document.querySelector('.gamecontainer');
+const gameFrame = gameContainer.querySelector('.frame');
+const gameNav = gameContainer.querySelector('.nav');
+
+// Listen for input event on the search bar
+searchBar.addEventListener('input', (e) => {
+ const query = searchBar.value.trim().toLowerCase();
+
+ // Loop through all the games in the container and show/hide them depending on whether they match the search query
+ for (let game of gamesContainer.children) {
+ if (game instanceof Element) {
+ if (query) {
+ const gameName = game.querySelector('span').innerText.trim().toLowerCase();
+ if (gameName.includes(query)) {
+ game.removeAttribute('hidden');
+ } else {
+ game.setAttribute('hidden', '');
+ }
+ } else {
+ game.removeAttribute('hidden');
+ }
+ }
+ }
+
+ // If there are no games shown, display the "No games" message, otherwise hide it
+ if (document.querySelectorAll('.game:not([hidden])').length == 0) {
+ document.querySelector('.nogames').style.display = 'initial';
+ } else {
+ document.querySelector('.nogames').style.display = 'none';
+ }
+});
+
+// Fetch the games data from a JSON file
+fetch('./assets/json/games.json')
+ .then((res) => res.json())
+ .then((games) => {
+ // Loop through each game and create a new game element for it
+ games.forEach((game) => {
+ const gameEl = document.createElement('div');
+ gameEl.className = 'game';
+ gameEl.innerHTML = `
${game.name}`;
+ gamesContainer.appendChild(gameEl);
+
+ // Add click event listener to the game element to show the game in the game container
+ gameEl.onclick = (e) => {
+ gamesContainer.classList.add('hidden');
+ searchBar.classList.add('hidden');
+ gameContainer.classList.remove('hidden');
+ document.querySelector('.saveItems').classList.add('hidden');
+ document.querySelector('.navbar').classList.add('noshadow');
+ gameFrame.querySelector('iframe').src = `./assets/game?game=${game.root}`;
+ gameNav.querySelector('span').textContent = game.name;
+ };
+
+ // Add click event listener to the back button in the game container to go back to the games list
+ gameNav.querySelector('#back').addEventListener('click', (e) => {
+ gamesContainer.classList.remove('hidden');
+ searchBar.classList.remove('hidden');
+ gameContainer.classList.add('hidden');
+ document.querySelector('.saveItems').classList.remove('hidden');
+ document.querySelector('.navbar').classList.remove('noshadow');
+ gameFrame.src = '';
+ });
+
+ // Add click event listener to the fullscreen button in the game container to enter fullscreen mode
+ gameNav.querySelector('#fullscreen').addEventListener('click', (e) => {
+ if (!document.fullscreenElement) {
+ gameFrame.requestFullscreen();
+ }
+ });
+ });
+ })
+ .catch((e) => {
+ alert('Could not load games');
+ alert(e);
+ });
+
+// Hide the spinner element after the page is loaded
+document.querySelector('.spinner').style.display = 'none';
+
+// Function to get the main save data
+function getMainSave() {
+ var mainSave = {};
+
+ // List of items in localStorage that should not be saved
+ var localStorageDontSave = ['theme', 'tab', 'nebelung'];
+
+ // Convert localStorage to an array of key-value pairs and remove the items that should not be saved
+ localStorageSave = Object.entries(localStorage);
+
+ for (let entry in localStorageSave) {
+ if (localStorageDontSave.includes(localStorageSave[entry][0])) {
+ localStorageSave.splice(entry, 1);
+ }
+ }
+
+ // Convert the localStorage array to a base64-encoded JSON string
+ localStorageSave = btoa(JSON.stringify(localStorageSave));
+
+ // Add the localStorage data to the mainSave object
+ mainSave.localStorage = localStorageSave;
+
+ // Get the cookies data and add it to the mainSave object
+ cookiesSave = document.cookie;
+ cookiesSave = btoa(cookiesSave);
+ mainSave.cookies = cookiesSave;
+
+ // Convert the mainSave object to a base64-encoded JSON string
+ mainSave = btoa(JSON.stringify(mainSave));
+
+ // Encrypt the mainSave data using AES encryption with the key 'save'
+ mainSave = CryptoJS.AES.encrypt(mainSave, 'save').toString();
+
+ // Return the encrypted mainSave data
+ return mainSave;
+}
+
+// Function to download the main save data as a file
+function downloadMainSave() {
+ var data = new Blob([getMainSave()]);
+ var dataURL = URL.createObjectURL(data);
+
+ var fakeElement = document.createElement('a');
+ fakeElement.href = dataURL;
+ fakeElement.download = 'games.save';
+ fakeElement.click();
+ URL.revokeObjectURL(dataURL);
+}
+
+// Function to get the main save data from an uploaded file
+function getMainSaveFromUpload(data) {
+ // Decrypt the uploaded data using AES decryption with the key 'save'
+ data = CryptoJS.AES.decrypt(data, 'save').toString(CryptoJS.enc.Utf8);
+
+ // Parse the decrypted data as JSON
+ var mainSave = JSON.parse(atob(data));
+ var mainLocalStorageSave = JSON.parse(atob(mainSave.localStorage));
+ var cookiesSave = atob(mainSave.cookies);
+
+ // Set the items in localStorage using the uploaded data
+ for (let item of mainLocalStorageSave) {
+ localStorage.setItem(item[0], item[1]);
+ }
+
+ // Set the cookies using the uploaded data
+ document.cookie = cookiesSave;
+}
+
+// Function to handle the file upload
+function uploadMainSave() {
+ var hiddenUpload = document.querySelector('.hiddenUpload');
+ hiddenUpload.click();
+
+ // Listen for the change event on the file input element
+ hiddenUpload.addEventListener('change', function (e) {
+ var files = e.target.files;
+ var file = files[0];
+ if (!file) {
+ return;
+ }
+
+ // Read the contents of the uploaded file as text and call getMainSaveFromUpload with the result
+ var reader = new FileReader();
+
+ reader.onload = function (e) {
+ getMainSaveFromUpload(e.target.result);
+
+ // Show a success message to the user
+ var uploadResult = document.querySelector('.uploadResult');
+ uploadResult.innerText = 'Uploaded save!';
+ uploadResult.style.display = 'initial';
+ setTimeout(function () {
+ uploadResult.style.display = 'none';
+ }, 3000);
+ };
+
+ reader.readAsText(file);
+ });
+}
+
+// Handle the hii pattern when keys are pressed
+var hiiPattern = ['h', 'i', 'i'];
+var hiiCurrent = 0;
+
+document.addEventListener('keydown', function (e) {
+ if (e.key !== hiiPattern[hiiCurrent]) {
+ return (hiiCurrent = 0);
+ }
+
+ hiiCurrent++;
+
+ if (hiiPattern.length == hiiCurrent) {
+ hiiCurrent = 0;
+ document.querySelector('.hii').removeAttribute('hidden');
+ }
+});
\ No newline at end of file
diff --git a/Jason/home.js b/Jason/home.js
new file mode 100644
index 0000000..b4a4e1d
--- /dev/null
+++ b/Jason/home.js
@@ -0,0 +1,84 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This is a JavaScript code that generates random splash messages and sets them in the DOM.
+
+The code declares two variables for caching splash messages, "splashCacheAll" and "splashCache".
+
+The "randomSay" function fetches a set of splash messages and selects a random one from the set,
+caching the remaining messages in the "splashCache" variable.
+
+If the selected message contains special placeholders like "%REAL_IP%", "%GAMES_NUMBER%", or "%SPLASH_NUMBER%",
+the placeholders are replaced with corresponding values, such as the user's IP address, the number of available games,
+or the total number of splash messages. The "setRandomSay" function sets the selected splash message in the DOM.
+
+If there is an element with class "message", the "setRandomSay" function is called to set a random splash message in the DOM.
+*/
+
+// Declare variables for caching splash messages
+var splashCacheAll;
+var splashCache;
+
+// Async function that returns a random splash message
+async function randomSay() {
+ // If splashCache is defined and not empty
+ if (splashCache) {
+ // If splashCache is empty, set it equal to the full set of splash messages
+ if (!splashCache.length) {
+ splashCache = splashCacheAll;
+ }
+ // Set says variable to the current splashCache
+ var says = splashCache;
+ } else {
+ // If splashCache is undefined or empty, fetch the full set of splash messages
+ var say = await fetch("./assets/json/say.json");
+ var says = await say.json();
+ // Store the full set of splash messages in both splashCacheAll and splashCache
+ splashCacheAll = says;
+ splashCache = says;
+ }
+
+ // Get a random splash message from the current says set
+ var getRandomSay = says[Math.floor(Math.random() * says.length)];
+
+ // Remove the randomly selected splash message from the cache
+ splashCache = splashCache.filter((splash) => splash !== getRandomSay);
+
+ // Return the randomly selected splash message
+ return getRandomSay;
+}
+
+// Async function that sets a random splash message in the DOM
+async function setRandomSay() {
+ // Get a random splash message using the randomSay() function
+ var randomSplash = await randomSay();
+
+ // If the random message is "%REAL_IP%", replace it with the user's IP address
+ if (randomSplash == "%REAL_IP%") {
+ var ips = await getIPs();
+ if (ips[0]) {
+ randomSplash = "Your real IP is " + ips[0];
+ } else {
+ randomSplash = "Cannot get your real IP :(";
+ }
+ }
+ // If the random message is "%GAMES_NUMBER%", replace it with the number of games available
+ else if (randomSplash == "%GAMES_NUMBER%") {
+ var gamesFetch = await fetch(location.origin + "/assets/json/games.json");
+ var games = await gamesFetch.json();
+ randomSplash = "There are " + games.length + " games currently";
+ }
+ // If the random message is "%SPLASH_NUMBER%", replace it with the total number of splash messages
+ else if (randomSplash == "%SPLASH_NUMBER%") {
+ randomSplash = "There are " + splashCacheAll.length + " of these messages!";
+ }
+
+ // Set the random splash message in the DOM
+ document.querySelector(".message").innerText = randomSplash;
+}
+
+// If there is an element with class "message", set a random splash message in the DOM
+if (document.querySelector(".message")) {
+ setRandomSay();
+}
\ No newline at end of file
diff --git a/Jason/index.js b/Jason/index.js
new file mode 100644
index 0000000..7709871
--- /dev/null
+++ b/Jason/index.js
@@ -0,0 +1,333 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This code starts off with a script to check if the browser has a cookie, if not, it will display a message kindly asking for the user to turn off their adblocker. Then it sets the cookie so the message is not seen for another year.
+
+The code sets up several variables and functions that are used to fetch data from the server,
+manipulate the document's content, and create custom HTML elements.
+
+The isBlocked function checks if a URL is blocked by fetching the content of its README.md
+file and returning true if it does not start with "# 3kh0 Assets",
+or if there is an error while fetching the file.
+
+The getCDN function iterates through a list of CDN URLs, calls isBlocked on each of them,
+and returns the first URL that is not blocked,
+or the first URL in the list if they are all blocked.
+
+The rest of the code sets up various event listeners and HTML elements, loads the main.js file,
+and sets the website's theme and theme colors based on values in local storage.
+
+The code is mostly concerned with setting up the website's initial state and is executed when the website loads.
+*/
+
+// This function checks if a cookie with the given key exists.
+function checkCookie(key) {
+ var value = "; " + document.cookie; // get the cookie value
+ var parts = value.split("; " + key + "="); // split the value by the key
+ if (parts.length == 2) {
+ return true; // the key exists
+ } else {
+ return false; // the key does not exist
+ }
+}
+
+var key = "myKey"; // set the key to check for
+if (!checkCookie(key)) { // if the key does not exist in the cookie
+ alert("Hello! This website is free to use but it costs alot money to maintain with the servers for games which is really expensive, so if you have ad blocker it would be nice of you to turn it off so we can keep the site running! Thank you for supporting us! <3"); // display an alert message
+ var expirationDate = new Date(); // create a new date object
+ expirationDate.setFullYear(expirationDate.getFullYear() + 1); // set the expiration date to one year from now
+ document.cookie = key + "=true; expires=" + expirationDate.toUTCString(); // create the cookie with the key and expiration date
+}
+
+var crate;
+
+// Checks if a CDN is blocked by testing the README.md file
+async function isBlocked(url) {
+ try {
+ var README = await fetch(url + '/README.md');
+ var content = await README.text();
+ if (content.startsWith('# 3kh0 Assets')) {
+ // The CDN is not blocked
+ return false;
+ } else {
+ // The CDN is not returning a valid response or is blocked
+ return true;
+ }
+ } catch {
+ return true;
+ }
+}
+
+async function getCDN(cdns) {
+ for (let cdn of cdns) {
+ var blocked = await isBlocked(cdn);
+ if (!blocked) {
+ return cdn;
+ }
+ }
+ return cdns[0];
+}
+
+// Define some varibles for later
+const path = location.pathname;
+const origin = localStorage.getItem('instance');
+const cdn = localStorage.getItem('cdn');
+const queryString = window.location.search;
+window.history.pushState({}, '', path);
+const urlParams = new URLSearchParams(queryString);
+const onLoadData = urlParams.get('onload');
+
+const base = document.createElement('base');
+base.href = location.origin + path.replace(path.split('\\').pop().split('/').pop(), '');
+document.head.appendChild(base);
+
+// If we do not have the origin var, we make it
+if (!origin) {
+ localStorage.setItem('instance', base.href);
+ location.reload();
+}
+
+// If we do not have the cdn var, we make it
+if (!cdn) {
+ fetch('./assets/json/cdns.json')
+ .then((res) => res.json())
+ .then(async (cdns) => {
+ localStorage.setItem('cdn', await getCDN(cdns));
+ location.reload();
+ });
+}
+
+const instance = encodeURIComponent(origin.replace(location.origin, ''));
+
+// If we have onLoadData, we run it now
+
+window.addEventListener('load', () => {
+ if (onLoadData) {
+ try {
+ eval(onLoadData);
+ } catch(e) {
+ console.error(e);
+ }
+ }
+
+ // Set up the WidgetBot crate
+ if(Crate) {
+ crate = new Crate({
+ server: '971769908205604864', // EchoDev
+ channel: '1017203047388160050', // #guest-chat
+ // notifications: false,
+ })
+ }
+});
+
+// If we have any errors, we will log it
+window.addEventListener('error', (e) => {
+ console.error(e);
+});
+
+// Add the main script in the
tags
+const jsdelivr = document.createElement('script');
+jsdelivr.setAttribute('src', 'https://cdn.jsdelivr.net/gh/3kh0/3kh0.github.io/js/main.js');
+document.head.append(jsdelivr);
+
+// Collect Tab Cloak data from local storage
+var tab = localStorage.getItem('tab');
+if (tab) {
+ try {
+ // Parse the data, it is in JSON
+ var tabData = JSON.parse(tab);
+ } catch {
+ var tabData = {};
+ }
+} else {
+ var tabData = {};
+}
+
+// Set the Tab title if the Tab cloak data is there
+if (tabData.title) {
+ document.title = tabData.title;
+}
+
+// Set the Tab icon if the Tab cloak data is there
+if (tabData.icon) {
+ document.querySelector('link[rel="icon"]').href = tabData.icon;
+}
+
+// Set theme colors if the user has set it
+function getContrastHex(hexcolor) {
+ hexcolor = hexcolor.replace('#', '');
+ var r = parseInt(hexcolor.substr(0, 2), 16);
+ var g = parseInt(hexcolor.substr(2, 2), 16);
+ var b = parseInt(hexcolor.substr(4, 2), 16);
+ var yiq = (r * 299 + g * 587 + b * 114) / 1000;
+ return yiq >= 128 ? '#1c1c1c' : 'white';
+}
+
+// Set theme colors if the user has set it
+function getColorHex(hexcolor) {
+ hexcolor = hexcolor.replace('#', '');
+ var r = parseInt(hexcolor.substr(0, 2), 16);
+ var g = parseInt(hexcolor.substr(2, 2), 16);
+ var b = parseInt(hexcolor.substr(4, 2), 16);
+ var yiq = (r * 299 + g * 587 + b * 114) / 1000;
+ return yiq >= 128 ? 'white' : 'black';
+}
+
+// Set theme colors if the user has set it
+var theme = localStorage.getItem('theme') || 'default';
+let themes;
+
+// Fetching themes
+fetch(origin + 'assets/json/themes.json')
+ .then((res) => res.json())
+ .then((data_themes) => {
+ themes = data_themes;
+
+ if (theme !== 'custom') {
+ document.body.setAttribute('theme', theme);
+
+ if (location.pathname.includes('/settings')) {
+ themes.forEach((palette) => {
+ if (palette.theme == theme) {
+ console.log(palette.theme);
+ document.querySelector('#theme_color').value = palette.color;
+ }
+ });
+ }
+ } else {
+ // Get custom theme
+ const theme = localStorage.getItem('theme_color');
+
+ document.body.setAttribute('theme', 'custom');
+ document.body.style = `--theme: ${theme}; --background: ${getContrastHex(theme)}; --text: ${getColorHex(theme)}; --text-secondary: ${getColorHex(theme)};`;
+
+ if (location.pathname.includes('/settings')) {
+ // Make the custom theme color selector
+ document.querySelector('#theme_color').value = theme;
+ }
+ }
+ })
+ .catch((e) => {
+ // Houston, we have a problem.
+ console.error(e);
+ throw new Error('Failed to load themes');
+ });
+
+// Add the changelogAdded element for the changelog
+class changelogAdded extends HTMLElement {
+ constructor() {
+ super();
+ this.innerHTML = `
+
+ `;
+ }
+}
+
+customElements.define('changelog-added', changelogAdded);
+
+// Add the changelogRemoved element for the changelog
+class changelogRemoved extends HTMLElement {
+ constructor() {
+ super();
+ this.innerHTML = `
+
+ `;
+ }
+}
+customElements.define('changelog-removed', changelogRemoved);
+
+// Add the changelogChanged element for the changelog
+class changelogChanged extends HTMLElement {
+ constructor() {
+ super();
+ this.innerHTML = `
+
+ `;
+ }
+}
+customElements.define('changelog-changed', changelogChanged);
+
+// Parrot theme random colors
+function setParrotColors() {
+ var parrotColor = "rgb(195, 158, 31)"
+ var parrotColors = ["#ff4c4b", "#c39e1f", "#b42e63"]
+
+ document.querySelectorAll("*").forEach((item) => {
+ if (getComputedStyle(item).color == parrotColor) {
+ item.style.color = parrotColors[Math.floor((Math.random()*parrotColors.length))]
+ }
+ })
+}
+
+if (localStorage.getItem("theme") == "parrot") {
+ setParrotColors()
+}
+
+// Handle secret themes
+function foundSecretTheme(name) {
+ document.body.setAttribute('theme', name);
+ localStorage.setItem('theme', name);
+ localStorage.setItem(name, 'true');
+ if (document.querySelector('.' + name)) {
+ document.querySelector('.' + name).removeAttribute('hidden');
+ }
+}
+
+// Handle the secret theme button
+function secretThemeButton(name) {
+ if (localStorage.getItem(name) == 'true') {
+ if (document.querySelector('.' + name)) {
+ document.querySelector('.' + name).removeAttribute('hidden');
+ }
+ }
+}
+
+// Keybind themes
+function createSecretThemeType(name, pattern) {
+ window[name + 'pattern'] = pattern;
+ window[name + 'current'] = 0;
+
+ var themePattern = window[name + 'pattern'];
+ var themeCurrent = window[name + 'current'];
+
+ // Log key presses to see if the user got the theme
+ document.addEventListener('keydown', function (e) {
+ if (e.key !== themePattern[themeCurrent]) {
+ return (themeCurrent = 0);
+ }
+
+ // Add this to the theme list
+ themeCurrent++;
+
+ if (themePattern.length == themeCurrent) {
+ themeCurrent = 0;
+ foundSecretTheme(name);
+ }
+ });
+
+ secretThemeButton(name);
+}
+
+// Define the cool themes, stop using this as a cheatsheet
+createSecretThemeType('nebelung', ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a']);
+createSecretThemeType('piplup', ['p', 'i', 'p', 'l', 'u', 'p', 'i', 's', 'c', 'o', 'o', 'l']);
+createSecretThemeType('forternish', ['c', 'o', 'm', 'i', 'c', 's', 'a', 'n', 's']);
+createSecretThemeType('russell2259', ['l', 'o', 'l']);
+
+// Define the secret theme button, stop using this as a cheatsheet
+secretThemeButton('hacker');
+
+// Handle the secret theme button
+window.nebelung_the_hacker = function () {
+ foundSecretTheme('hacker');
+};
diff --git a/Jason/main.js b/Jason/main.js
new file mode 100644
index 0000000..ef7b7c7
--- /dev/null
+++ b/Jason/main.js
@@ -0,0 +1,61 @@
+/*
+Hello there!
+If you want to add these games to your site, please reach out at my email: echo-the-coder@tuta.io,
+or discord: 3kh0_#6969, Thanks and have a great day!
+
+Wondering how this works?
+This JavaScript code begins with a console warning message that asks users to reach out via email if they
+want to add the games to their website.
+
+The second part of the code defines a function called "script" that logs an informational message to the console when it is called.
+The rest of the code creates four separate script tags and adds them to the head of the HTML document.
+
+Each script tag has different attributes and sources, and is appended with the script function.
+The first script tag is for Google Tag Manager, the second is for the Arc.io widget, the third is for ad handling using Google Funding Choices,
+and the fourth is for Google AdSense.
+
+Each script is added to the page asynchronously for performance reasons.
+*/
+console.warn(
+ "%cNote!",
+ "color: purple; font-weight: 600; background: yellow; padding: 0 5px; border-radius: 5px",
+ "If you want to add these games to your site, please reach out at my email: echo-the-coder@tuta.io\nPlease do not just add them without asking me first! Thank you!"
+);
+
+function script(text) {
+ console.log("%cScript Injection", "color: cyan; font-weight: 600; background: black; padding: 0 5px; border-radius: 5px", text);
+}
+
+// ====================================
+// SCRIPT INJECTION
+// ====================================
+const gogascript27 = document.createElement("script");
+gogascript27.setAttribute("async", "");
+gogascript27.setAttribute("src", "https://www.googletagmanager.com/gtag/js?id=G-98DP5VKS42");
+const inlinegogascript843 = document.createElement("script");
+inlinegogascript843.innerHTML = `window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments);}
+ gtag('js', new Date());
+ gtag('config', 'G-98DP5VKS42');`;
+document.head.append(gogascript27, inlinegogascript843);
+script("Injected script 1/4 (Google Tag Manager)");
+
+const arcbroker23 = document.createElement("script");
+arcbroker23.setAttribute("async", "");
+arcbroker23.setAttribute("src", "https://arc.io/widget.min.js#eRPHFgiC");
+document.head.append(arcbroker23);
+script("Injected script 2/4 (Arc widget stuff)");
+
+const adblockhandle44 = document.createElement("script");
+adblockhandle44.setAttribute("src", "https://fundingchoicesmessages.google.com/i/pub-5756835229788588?ers=1");
+adblockhandle44.setAttribute("nonce", "yibq-w_TR5NOCRWsU-VL0Q");
+adblockhandle44.setAttribute("async", "");
+document.head.append(adblockhandle44);
+script("Injected script 3/4 (Ad stuff)");
+
+const adscipterz92 = document.createElement("script");
+adscipterz92.setAttribute("async", "");
+adscipterz92.setAttribute("src", "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5756835229788588");
+adscipterz92.setAttribute("crossorigin", "anonymous");
+document.head.append(adscipterz92);
+script("Injected script 4/4 (Ad stuff)");
diff --git a/Jason/reviews.js b/Jason/reviews.js
new file mode 100644
index 0000000..fcf8330
--- /dev/null
+++ b/Jason/reviews.js
@@ -0,0 +1,197 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This JavaScript code retrieves a container element from the HTML with the ID "my-keen-slider",
+fetches review data from a JSON file, shuffles the order of the reviews,
+groups the reviews into subarrays of length 3, and adds "placeholder" reviews to fill out the last group if it has fewer than 3 reviews.
+
+It then iterates over each group of reviews, creates new HTML elements to hold the reviews and their information,
+and adds them to the container element.
+
+Finally, it creates navigation buttons for the container element and adds them to the HTML,
+using a KeenSlider library to create the navigation functionality.
+Overall, this code generates a slideshow of randomized reviews with navigation buttons.
+*/
+// This is an asynchronous function that will be executed immediately
+(async () => {
+ // Get the element with the ID "my-keen-slider" and assign it to a variable
+ var reviewsElement = document.getElementById("my-keen-slider");
+
+ // Fetch the JSON data from the specified URL and wait for the response
+ var reviewsData = await fetch("./assets/json/reviews.json");
+ var reviews = await reviewsData.json();
+
+ // Shuffle the reviews array
+ for (let i = reviews.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [reviews[i], reviews[j]] = [reviews[j], reviews[i]];
+ }
+
+ // Group the shuffled reviews into groups of three
+ var reviewsGroups = reviews.reduce((r, e, i) => (i % 3 ? r[r.length - 1].push(e) : r.push([e])) && r, []);
+
+ // If there is a last review group, add placeholders to make it a group of three
+ var lastReviewGroup = reviewsGroups[reviewsGroups.length - 1];
+ if (lastReviewGroup) {
+ if (lastReviewGroup.length == 1) {
+ lastReviewGroup.push({
+ placeholder: true,
+ });
+ lastReviewGroup.push({
+ placeholder: true,
+ });
+ }
+ if (lastReviewGroup.length == 2) {
+ lastReviewGroup.push({
+ placeholder: true,
+ });
+ }
+ }
+
+ // Loop through the review groups and create HTML elements for each group
+ for (let reviewsGroup in reviewsGroups) {
+ var newSlide = document.createElement("div");
+ newSlide.className = "keen-slider__slide number-slide" + Number(Number(reviewsGroup) + 1);
+
+ var reviewContainer = document.createElement("div");
+ reviewContainer.className = "review-container";
+
+ for (let review of reviewsGroups[reviewsGroup]) {
+ // If the review is a placeholder, create an element with a different class name
+ if (!review.placeholder) {
+ var newReview = document.createElement("div");
+ newReview.className = "review";
+
+ var reviewImg = document.createElement("img");
+ reviewImg.className = "review-img";
+ reviewImg.src = localStorage.getItem('cdn') + review.img;
+ newReview.appendChild(reviewImg);
+
+ var reviewName = document.createElement("div");
+ reviewName.className = "review-name";
+ reviewName.innerText = review.name;
+ newReview.appendChild(reviewName);
+
+ var reviewContent = document.createElement("div");
+ reviewContent.className = "review-content";
+ reviewContent.innerText = review.review;
+ newReview.appendChild(reviewContent);
+ } else {
+ var newReview = document.createElement("div");
+ newReview.className = "review-placeholder";
+ }
+
+ reviewContainer.appendChild(newReview);
+ }
+
+ newSlide.appendChild(reviewContainer);
+
+ reviewsElement.appendChild(newSlide);
+ }
+
+ // Define a function called "navigation" that takes a "slider" parameter
+ function navigation(slider) {
+ // Declare some variables used by the navigation
+ let wrapper, dots, arrowLeft, arrowRight;
+
+ // Define a function called "markup" that creates and removes the navigation elements
+ function markup(remove) {
+ wrapperMarkup(remove);
+ dotMarkup(remove);
+ arrowMarkup(remove);
+ }
+
+ // Define a function called "removeElement" that removes an element from the DOM
+ function removeElement(elment) {
+ elment.parentNode.removeChild(elment);
+ }
+
+ // Define a function called "createDiv" that creates a new element with the specified class name
+ function createDiv(className) {
+ var div = document.createElement("div");
+ var classNames = className.split(" ");
+ classNames.forEach((name) => div.classList.add(name));
+ return div;
+ }
+
+ // Define a function called "arrowMarkup" that creates or removes the arrow elements
+ function arrowMarkup(remove) {
+ if (remove) {
+ removeElement(arrowLeft);
+ removeElement(arrowRight);
+ return;
+ }
+
+ arrowLeft = createDiv("arrow arrow--left");
+ arrowLeft.addEventListener("click", () => slider.prev());
+
+ arrowRight = createDiv("arrow arrow--right");
+ arrowRight.addEventListener("click", () => slider.next());
+
+ wrapper.appendChild(arrowLeft);
+ wrapper.appendChild(arrowRight);
+ }
+
+ // Define a function called "wrapperMarkup" that creates or removes the wrapper element
+ function wrapperMarkup(remove) {
+ if (remove) {
+ var parent = wrapper.parentNode;
+ while (wrapper.firstChild) parent.insertBefore(wrapper.firstChild, wrapper);
+ removeElement(wrapper);
+ return;
+ }
+
+ wrapper = createDiv("navigation-wrapper");
+ document.getElementById("reviews").appendChild(wrapper);
+ wrapper.appendChild(slider.container);
+ }
+
+ // Define a function called "dotMarkup" that creates or removes the dot elements
+ function dotMarkup(remove) {
+ if (remove) {
+ removeElement(dots);
+ return;
+ }
+
+ dots = createDiv("dots");
+ slider.track.details.slides.forEach((_e, idx) => {
+ var dot = createDiv("dot");
+ dot.addEventListener("click", () => slider.moveToIdx(idx));
+ dots.appendChild(dot);
+ });
+ wrapper.appendChild(dots);
+ }
+
+ // Define a function called "updateClasses" that updates the classes of the navigation elements
+ function updateClasses() {
+ var slide = slider.track.details.rel;
+ slide === 0 ? arrowLeft.classList.add("arrow--disabled") : arrowLeft.classList.remove("arrow--disabled");
+ slide === slider.track.details.slides.length - 1 ? arrowRight.classList.add("arrow--disabled") : arrowRight.classList.remove("arrow--disabled");
+ Array.from(dots.children).forEach(function (dot, idx) {
+ idx === slide ? dot.classList.add("dot--active") : dot.classList.remove("dot--active");
+ });
+ }
+
+ // Add event listeners to the slider and update the navigation when the slider changes
+ slider.on("created", () => {
+ markup();
+ updateClasses();
+ document.getElementById("reviews").style.visibility = "initial";
+ });
+ slider.on("optionsChanged", () => {
+ markup(true);
+ markup();
+ updateClasses();
+ });
+ slider.on("slideChanged", () => {
+ updateClasses();
+ });
+ slider.on("destroyed", () => {
+ markup(true);
+ });
+ }
+
+ // Create a new KeenSlider instance with the specified options and navigation function
+ var slider = new KeenSlider("#my-keen-slider", {}, [navigation]);
+})();
diff --git a/Jason/settings.js b/Jason/settings.js
new file mode 100644
index 0000000..8177a84
--- /dev/null
+++ b/Jason/settings.js
@@ -0,0 +1,230 @@
+/*
+Hello epic hacker (maybe skid) you are looking at one of the many scripts that powers the site,
+this script has extra comments and info to help you understand what is going on.
+
+This JavaScript code defines functions that manage the state of a web page's tab,
+such as its title and icon, and its theme.
+
+It retrieves a JSON object from local storage, which contains the current state of the tab,
+and updates the web page's elements with the stored values.
+
+The code provides functions to modify the tab state and settings, such as setTitle, setFavicon,
+resetTab, setTheme, and setThemeColor.
+
+These functions update the web page's elements and store the updated state in local storage.
+*/
+
+// Check if there is a saved tab data in localStorage
+var tab = localStorage.getItem("tab");
+
+if (tab) {
+ // If there is saved data, try to parse it
+ try {
+ var tabData = JSON.parse(tab);
+ } catch {
+ // If there is an error in parsing, create an empty object
+ var tabData = {};
+ }
+} else {
+ // If there is no saved data, create an empty object
+ var tabData = {};
+}
+
+// Set the title and icon fields to the values saved in tabData, if they exist
+if (tabData.title) {
+ document.getElementById("title").value = tabData.title;
+}
+if (tabData.icon) {
+ document.getElementById("icon").value = tabData.icon;
+}
+
+// Default tab settings
+var settingsDefaultTab = {
+ title: "Settings | 3kh0",
+ icon: "./images/logo.png",
+};
+
+// Function to set the document title
+function setTitle(title = "") {
+ if (title) {
+ document.title = title;
+ } else {
+ document.title = settingsDefaultTab.title;
+ }
+
+ // Update the saved tab data with the new title
+ var tab = localStorage.getItem("tab");
+
+ if (tab) {
+ // If there is saved data, try to parse it
+ try {
+ var tabData = JSON.parse(tab);
+ } catch {
+ // If there is an error in parsing, create an empty object
+ var tabData = {};
+ }
+ } else {
+ // If there is no saved data, create an empty object
+ var tabData = {};
+ }
+
+ if (title) {
+ // If there is a new title, update tabData
+ tabData.title = title;
+ } else {
+ // If the title is empty, delete the title field from tabData
+ delete tabData.title;
+ }
+
+ // Save the updated tab data to localStorage
+ localStorage.setItem("tab", JSON.stringify(tabData));
+}
+
+// Function to set the favicon
+function setFavicon(icon) {
+ if (icon) {
+ document.querySelector("link[rel='icon']").href = icon;
+ } else {
+ document.querySelector("link[rel='icon']").href = settingsDefaultTab.icon;
+ }
+
+ // Update the saved tab data with the new icon
+ var tab = localStorage.getItem("tab");
+
+ if (tab) {
+ // If there is saved data, try to parse it
+ try {
+ var tabData = JSON.parse(tab);
+ } catch {
+ // If there is an error in parsing, create an empty object
+ var tabData = {};
+ }
+ } else {
+ // If there is no saved data, create an empty object
+ var tabData = {};
+ }
+
+ if (icon) {
+ // If there is a new icon, update tabData
+ tabData.icon = icon;
+ } else {
+ // If the icon is empty, delete the icon field from tabData
+ delete tabData.icon;
+ }
+
+ // Save the updated tab data to localStorage
+ localStorage.setItem("tab", JSON.stringify(tabData));
+}
+
+function setCloak() { // applies only to premade cloaks
+ var cloak = document.getElementById("premadecloaks").value; // cloak seems kind of weird when you spell it out
+ switch (cloak) {
+ case "search": // Google Search
+ setTitle("Google");
+ setFavicon("./images/cloaks/Google Search.ico");
+ location.reload();
+ break;
+ case "drive": // Google Drive
+ setTitle("My Drive - Google Drive");
+ setFavicon("./images/cloaks/Google Drive.ico");
+ location.reload();
+ break;
+ case "librex": // LibreX
+ setTitle("LibreX");
+ setFavicon("./images/cloaks/9A58D8BC-6595-476A-AD95-B6D8880683C8.ico");
+ location.reload();
+ break;
+ case "youtube": // YouTube
+ setTitle("YouTube");
+ setFavicon("./images/cloaks/YouTube.ico");
+ location.reload();
+ break;
+ case "gmail": // Gmail
+ setTitle("Gmail");
+ setFavicon("./images/cloaks/Gmail.ico");
+ location.reload();
+ break;
+ case "calendar": // Google Calendar
+ setTitle("Google Calendar");
+ setFavicon("./images/cloaks/Calendar.ico");
+ location.reload();
+ break;
+ case "meets": // Google Meet
+ setTitle("Google Meet");
+ setFavicon("./images/cloaks/Meet.ico");
+ location.reload();
+ break;
+ case "classroom": // Google Classroom
+ setTitle("Classes");
+ setFavicon("./images/cloaks/Classroom.png");
+ location.reload();
+ break;
+ case "canvas": // Canvas
+ setTitle("Canvas");
+ setFavicon("./images/cloaks/Canvas.ico");
+ location.reload();
+ break;
+ case "zoom": // Zoom
+ setTitle("Zoom");
+ setFavicon("./images/cloaks/Zoom.ico");
+ location.reload();
+ break;
+ case "nitter": // Nitter
+ setTitle("nitter");
+ setFavicon("./images/cloaks/63DFB320-0EEC-4F06-AF02-C50DFD2B49AB.ico");
+ location.reload();
+ break;
+ case "teddit": // Teddit
+ setTitle("teddit");
+ setFavicon("./images/cloaks/EB4D8FE9-10E9-44B8-A6CE-3F9A0040F94A.ico");
+ location.reload();
+ break;
+ case "cornhub": // Cornhub
+ setTitle("Cornhub");
+ setFavicon("./images/cloaks/8FE4C273-914D-431D-907E-3FCF5BB0399F.ico");
+ location.reload();
+ break;
+ case "indivious": // Indivious
+ setTitle("Indivious");
+ setFavicon("./images/cloaks/2255E848-AB69-43C1-B470-DBFDA40FAD10.ico");
+ location.reload();
+ break;
+ case "khan": // Khan Academy
+ setTitle("Dashboard | Khan Academy");
+ setFavicon("./images/cloaks/Khan Academy.ico");
+ location.reload();
+ break;
+ }
+}
+
+// Function to reset the tab settings to default
+function resetTab() {
+ document.title = settingsDefaultTab.title;
+ document.querySelector("link[rel='icon']").href = settingsDefaultTab.icon;
+ document.getElementById("title").value = "";
+ document.getElementById("icon").value = "";
+ localStorage.setItem("tab", JSON.stringify({}));
+}
+
+// Function to set the theme
+function setTheme(theme) {
+ localStorage.setItem("theme", theme);
+ document.body.setAttribute("theme", theme);
+ document.body.style = "";
+ localStorage.removeItem("theme_color");
+
+ // Find the theme color from the themes array and set the color
+ themes.forEach((palette) => {
+ if (palette.theme == theme) {
+ document.querySelector("#theme_color").value = palette.color;
+ }
+ });
+}
+
+// Function to set the custom theme color
+function setThemeColor(theme) {
+ localStorage.setItem("theme", "custom");
+ localStorage.setItem("theme_color", theme);
+ document.body.setAttribute("theme", "custom");
+ document.body.style = `--theme: ${theme}; --background: ${getContrastHex(theme)}; --text: ${getColorHex(theme)}; --text-secondary: ${getColorHex(theme)};`;
+}