Gartic Phone Avatar Chooser

Adds every Avatar to a Selectionscreen. Now you can Select the Avatar you want

// ==UserScript==
// @name        Gartic Phone Avatar Chooser
// @namespace   Violentmonkey Scripts
// @match       *://garticphone.com/*
// @grant       none
// @version     1.3.0
// @author      Der_Floh
// @icon        https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://garticphone.com&size=128
// @homepageURL https://greasyfork.org/de/scripts/468787-gartic-phone-avatar-chooser
// @supportURL  https://greasyfork.org/de/scripts/468787-gartic-phone-avatar-chooser/feedback
// @license     MIT
// @description Adds every Avatar to a Selectionscreen. Now you can Select the Avatar you want
// ==/UserScript==

// jshint esversion: 8

class AvatarChooser {
	constructor(container, activationElem) {
		this.images;
		this.container = container;

		this.avatarChooserContainer = document.createElement("div");
		this.avatarChooserContainer.style.display = "none";
		this.container.parentNode.appendChild(this.avatarChooserContainer);

		this.imgElem = activationElem.querySelector("span");
		this.randomImageButton = activationElem.querySelector("button");
		this.randomImageButton.addEventListener("click", () => {
			setTimeout(() => {
				this.jsx  = this.randomImageButton.className;
				this.updateImagesClasses();
			}, 10);
		});
		this.jsx = this.randomImageButton.className;
		this.avatarChooserContainer.className = this.jsx + " avatar-chooser-flex-container";

		const activeTextJsx = this.container.querySelector('span[class*="active"]');

		addCustomStyle(`
			.avatar-hover-target:hover {
  			background-color: rgba(28, 28, 28, 0.8);
			}

			.avatar-text-overlay {
  			display: block;
  			position: absolute;
  			top: 50%;
  			left: 50%;
  			transform: translate(-50%, -50%);
				width: 100%;
				padding-top: 10px;
				padding-bottom: 12px;
				text-align: center;
				font-weight: bold;
				background-color: rgba(28, 28, 28, 0.6);
				border-radius: 7px;
			}

			.avatar-chooser-flex-container {
				display: flex;
				flex-wrap: wrap;
				width: 100%;
				height: 100%;
				align-items: stretch;
			}

			.avatar-chooser-flex-item {
				flex: 4 4 auto;
				display: flex;
				align-items: center;
				justify-content: center;
			}

			.avatar-chooser-flex-item span {
				width: 100%;
				height: 100%;
				background-size: cover;
				background-position: center;
			}

			.avatar-chooser-image-scale-transition {
				transition: transform 0.05s;
			}

			.avatar-chooser-image-scaled {
				transform: scale(0.9);
			}
		`);

		const textElem = document.createElement("p");
		textElem.className = "avatar-text-overlay avatar-hover-target " + activeTextJsx.className;
		textElem.style.cursor = "pointer";
		textElem.textContent = "Choose Avatar";
		textElem.onclick = this.choose.bind(this);
		activationElem.appendChild(textElem);

		this.loadImages();
	}

	async choose() {
		this.container.style.display = "none";
		this.avatarChooserContainer.style.display = "flex";
	}

	async selectImage(imageSrc) {
		let style = window.getComputedStyle(this.imgElem);
		let attributeValue = style.getPropertyValue("background-image");

		while (attributeValue != imageSrc) {
			this.randomImageButton.click();
			style = window.getComputedStyle(this.imgElem);
			attributeValue = style.getPropertyValue("background-image");
		}
		this.updateImagesClasses();
	}

	async updateImagesClasses() {
		this.avatarChooserContainer.className = this.jsx + " avatar-chooser-flex-container";
		for (const div of this.avatarChooserContainer.getElementsByTagName("div"))
			div.className = this.jsx + " avatar avatar-chooser-flex-item";
		for (const span of this.avatarChooserContainer.getElementsByTagName("span")) {
			let classString = this.jsx + " avatar-chooser-image-scale-transition";
			if (span.classList.contains("avatar-chooser-image-scaled"))
				classString += " avatar-chooser-image-scaled";
			span.className = classString;
		}
	}

	async loadImages() {
		this.images = await this.createImages();
		for (const image of this.images) {
			this.avatarChooserContainer.appendChild(image);
		}
	}

	async createImages() {
		const link = "https://garticphone.com/images/avatar/";
		const images = [];
		let i = 0;
		let svg;
		do {
			svg = await this.checkSVGExists(link + i + ".svg");
			if (svg) {
				const image = await this.createImage(i);
				images.push(image);
			}
			i++;
		} while (svg);
		return images;
	}

	async createImage(i) {
		const imageSrc = `url("https://garticphone.com/images/avatar/${i}.svg")`;
		const imgContainer = document.createElement("div");
		imgContainer.className = this.jsx + " avatar avatar-chooser-flex-item";
		imgContainer.style.margin = "2px";
		imgContainer.style.maxWidth = "158px";
		const image = document.createElement("span");
		image.id = "accountimage-" + i;
		image.className = this.jsx + " avatar-chooser-image-scale-transition";
		image.style.backgroundImage = imageSrc;
		image.style.cursor = "pointer";

		const onImageSelect = () => {
			image.classList.remove("avatar-chooser-image-scaled");
			this.avatarChooserContainer.style.display = "none";
			this.container.style.display = "flex";
		};

		image.onclick = () => {
			event.preventDefault();
			image.classList.add("avatar-chooser-image-scaled");
			image.addEventListener('transitionend', () => {
				setTimeout(() => {
					image.classList.remove("avatar-chooser-image-scaled");
					setTimeout(() => {
						this.selectImage(imageSrc);
						onImageSelect();
					}, 200);
				}, 200);
			}, { once: true });
		};
		imgContainer.appendChild(image);
		return imgContainer;
	}

	async checkSVGExists(url) {
		try {
			const response = await fetch(url, { method: "HEAD" });
			return response.ok;
		} catch (error) {
			console.error("Error checking SVG existence:", error);
			return response.error;
		}
	}
}

if (slashCount(window.location.toString()) > 3)
  return;

window.addEventListener("load", async () => {
	const content = await waitForElementToExistId("content");
	const gameContainer = await waitForElementToExistQuery(content, 'div[class*="start"]');
	const avatarContainer = await waitForElementToExistQuery(content, 'div[class*="avatar"]');

	const avatarChooser = new AvatarChooser(gameContainer, avatarContainer);
});

async function waitForElementToExistQuery(baseNode, query) {
  return new Promise(async (resolve) => {
    async function checkElement() {
      const element = baseNode.querySelector(query);
      if (element !== null)
        resolve(element);
      else
        setTimeout(checkElement, 100);
    }
    await checkElement();
  });
}

async function waitForElementToExistId(elementId) {
  return new Promise(async (resolve) => {
    async function checkElement() {
      const element = document.getElementById(elementId);
      if (element !== null)
        resolve(element);
      else
        setTimeout(checkElement, 100);
    }
    await checkElement();
  });
}

function addCustomStyle(cssString) {
  const style = document.createElement('style');
	style.type = "text/css";
  style.innerHTML = cssString;
  document.head.appendChild(style);
}

function slashCount(str) {
  const regex = /\//g;
  const matches = str.match(regex);
  return matches ? matches.length : 0;
}