Texture Pack Manager [Taming.io, Sploop.io, Moomoo.io]

Allows you to change game textures!

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name Texture Pack Manager [Taming.io, Sploop.io, Moomoo.io]
// @author Murka
// @description Allows you to change game textures!
// @icon https://i.imgur.com/o0KykL1.png
// @version 0.3
// @match *://taming.io/*
// @match *://sploop.io/*
// @match *://moomoo.io/*
// @match *://*.moomoo.io/*
// @run-at document-start
// @grant none
// @noframes
// @license MIT
// @namespace https://greasyfork.org/users/919633
// ==/UserScript==
/* jshint esversion:8 */

/*
    Author: Murka
    Github: https://github.com/Murka007
    Discord: https://discord.gg/cPRFdcZkeD
    Greasyfork: https://greasyfork.org/en/users/919633
*/

(function() {
    "use strict";

    const log = console.log.bind(console);
    const storage = {
        get(key) {
            const value = localStorage.getItem(key);
            return value === null ? null : JSON.parse(value);
        },
        set(key, value) {
            localStorage.setItem(key, JSON.stringify(value));
        }
    };

    const DATA_TYPES = { TEXTURES: 0, EMOJIS: 1 };
    const SEARCH_TYPES = { CONTAINS: 0, EQUALS: 1 };
    const TYPES = { 0: "textures", 1: "emojis" };
    const TEXT_TYPES = { 0: "URL", 1: "MESSAGE" };
    const HEADER_TYPES = { 0: "Texture Manager", 1: "Emoji Manager" };

    function isURL(string) {
        try {
            const url = new URL(string);
            return /^https?:/.test(url.protocol);
        } catch(err) {}
    }

    function isValidImageSrc(src) {
        return new Promise(resolve => {
            const img = new Image();
            img.src = src;
            img.onload = () => resolve(isURL(src));
            img.onerror = () => resolve(false);
        })
    }

    function isValidSetting(option) {
        const { id, type, searchType, from, to } = option;

        const equalToDataTypes = [DATA_TYPES.TEXTURES, DATA_TYPES.EMOJIS].includes(type);
        const equalToSearchTypes = [SEARCH_TYPES.CONTAINS, SEARCH_TYPES.EQUALS].includes(searchType);
        const isString = typeof from === "string" && typeof to === "string";
        const isTextureURL = type === DATA_TYPES.TEXTURES && isURL(to) || type === DATA_TYPES.EMOJIS;

        return Number.isInteger(id) && equalToDataTypes && equalToSearchTypes && isString && isTextureURL;
    }

    function createSettings() {
        const settings = {};
        settings.textures = [];
        settings.emojis = [];
        settings.freeIDS = [];
        return settings;
    }

    const settings = (function() {
        const defaultSettings = createSettings();
        const settings = Object.assign({}, defaultSettings, storage.get("TextureSettings"));

        for (const key in settings) {
            if (!defaultSettings.hasOwnProperty(key)) {
                delete settings[key];
            } else if (Array.isArray(settings[key]) && key === TYPES[DATA_TYPES[key.toUpperCase()]]) {
                for (let i=settings[key].length-1;i>=0;i--) {
                    if (!isValidSetting(settings[key][i])) {
                        settings[key].splice(i, 1);
                    }
                }
            }
        }

        storage.set("TextureSettings", settings);
        return settings;
    })();

    function replaceValue(type, value) {
        const testValue = value.match(/img.+$/)[0];
        for (const option of settings[TYPES[type]]) {
            const { searchType, from, to } = option;
            if (type === DATA_TYPES.TEXTURES) {
                if (searchType === SEARCH_TYPES.CONTAINS && testValue.includes(from)) return to;
                if (searchType === SEARCH_TYPES.EQUALS && testValue === from) return to;
            }
        }
        return value;
    }

    const src = Object.getOwnPropertyDescriptor(Image.prototype, "src").set;
    Object.defineProperty(Image.prototype, "src", {
        set(link) {
            return src.call(this, replaceValue(DATA_TYPES.TEXTURES, link));
        }
    })

    const HTML = `
        <div id="page-container" class="opened-menu">
            <header>
                <div class="imageHolder">
                    <img src="https://i.imgur.com/o0KykL1.png" draggable="false"/>
                </div>
                <span>Texture Pack Manager</span>
                <div id="close-menu">
                    <svg class="cross-icon"
                        version="1.1"
                        xmlns="http://www.w3.org/2000/svg"
                        width="32" height="32" viewBox="0 0 32 32"
                    >
                        <path d="M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z"></path>
                    </svg>
                </div>
            </header>

            <main>
                <div id="nav-bar">
                    <button class="open-menu enabled">Textures</button>
                    <button class="open-menu">Misc</button>
                    <button class="open-menu bottom-align">Credits</button>
                </div>

                <div id="menu-page-container">
                    <div class="menu-page opened">
                        <h1>Textures</h1>
                        <p class="description">Add new textures</p>
                        <div id="texture-container" class="item-container"></div>
                        <div class="add-item">
                            <label id="add-texture" class="add-item-button">
                                <svg class="icon-tx plus-icon" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
                                    <path d="M31 12h-11v-11c0-0.552-0.448-1-1-1h-6c-0.552 0-1 0.448-1 1v11h-11c-0.552 0-1 0.448-1 1v6c0 0.552 0.448 1 1 1h11v11c0 0.552 0.448 1 1 1h6c0.552 0 1-0.448 1-1v-11h11c0.552 0 1-0.448 1-1v-6c0-0.552-0.448-1-1-1z"></path>
                                </svg>
                                <span class="text">Add texture</span>
                            </label>
                        </div>
                    </div>

                    <div class="menu-page">
                        <h1>Misc</h1>
                        <p class="description">Download, upload or reset your settings</p>
                        <div class="menu-page-section">
                            <button id="download-settings" class="manage-storage">DOWNLOAD</button>
                            <button class="manage-storage">
                                <input id="upload-settings" type="file"/>
                                UPLOAD
                            </button>
                            <button id="reset-settings" class="manage-storage">RESET</button>
                        </div>
                    </div>

                    <div class="menu-page">
                        <h1>Credits</h1>
                        <div class="menu-page-section">
                            <span class="highlight">Author:</span>
                            <span class="highlight-secondary">Murka</span>
                        </div>
                        <div class="menu-page-section">
                            <span class="highlight">
                                <svg
                                    class="icon-tx github-icon"
                                    version="1.1"
                                    xmlns="http://www.w3.org/2000/svg"
                                    width="32" height="32" viewBox="0 0 32 32"
                                >
                                    <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
                                </svg>
                            </span>
                            <span class="highlight-secondary"><a href="https://github.com/Murka007" target="_blank">Murka007</a></span>
                        </div>
                        <div class="menu-page-section">
                            <span class="highlight">
                                <svg
                                    class="icon-tx discord-icon"
                                    version="1.1"
                                    xmlns="http://www.w3.org/2000/svg"
                                    width="32" height="32" viewBox="0 0 32 32"
                                >
                                    <path d="M26.963 0c1.875 0 3.387 1.516 3.476 3.3v28.7l-3.569-3.031-1.96-1.784-2.139-1.864 0.893 2.94h-18.717c-1.869 0-3.387-1.42-3.387-3.301v-21.653c0-1.784 1.52-3.303 3.393-3.303h22zM18.805 7.577h-0.040l-0.269 0.267c2.764 0.8 4.101 2.049 4.101 2.049-1.781-0.891-3.387-1.336-4.992-1.516-1.16-0.18-2.32-0.085-3.3 0h-0.267c-0.627 0-1.96 0.267-3.747 0.98-0.623 0.271-0.98 0.448-0.98 0.448s1.336-1.336 4.28-2.049l-0.18-0.18c0 0-2.229-0.085-4.636 1.693 0 0-2.407 4.192-2.407 9.36 0 0 1.333 2.32 4.991 2.408 0 0 0.533-0.711 1.073-1.336-2.053-0.624-2.853-1.872-2.853-1.872s0.179 0.088 0.447 0.267h0.080c0.040 0 0.059 0.020 0.080 0.040v0.008c0.021 0.021 0.040 0.040 0.080 0.040 0.44 0.181 0.88 0.36 1.24 0.533 0.621 0.269 1.42 0.537 2.4 0.715 1.24 0.18 2.661 0.267 4.28 0 0.8-0.18 1.6-0.356 2.4-0.713 0.52-0.267 1.16-0.533 1.863-0.983 0 0-0.8 1.248-2.94 1.872 0.44 0.621 1.060 1.333 1.060 1.333 3.659-0.080 5.080-2.4 5.16-2.301 0-5.16-2.42-9.36-2.42-9.36-2.18-1.619-4.22-1.68-4.58-1.68zM19.029 13.461c0.937 0 1.693 0.8 1.693 1.78 0 0.987-0.76 1.787-1.693 1.787s-1.693-0.8-1.693-1.779c0.003-0.987 0.764-1.784 1.693-1.788zM12.972 13.461c0.933 0 1.688 0.8 1.688 1.78 0 0.987-0.76 1.787-1.693 1.787s-1.693-0.8-1.693-1.779c0-0.987 0.76-1.784 1.699-1.788z"></path>
                                </svg>
                            </span>
                            <span class="highlight-secondary"><a href="https://discord.gg/sG9cyfGPj5" target="_blank">Coding Paradise</a></span>
                        </div>
                    </div>
                </div>
            </main>
        </div>`;

    const CSS = `
:root {
    --bg-color: #0c0d11;
    --bg-sub-color: #13141b;
    --main-color: #7ebab5;
    --third-color: #8c9eaf;
    --sub-color: #454864;
    --sub-alt-color: #171a25;
    --text-color: #f6f5f5;
    --text-color-active: #777a96;
    --highlight-color: #717597;
    --highlight-color-secondary: #8b91c2;

    --add-button-color: #86ebad;
    --cancel-button-color: #eb9a86;
    --add-button-active-color: #5eaa7b;
    --cancel-button-active-color: #a36a5b;
    --bin-color: #b64444;
    --bin-color-border: #973838;

    --border-color-opacity: #7ebab56b;
    --border-color: #7ebab5;
    --popup-bg-color: #232630;
    --bg-item-content: #272a36;

    --roundness: 5px;
    --padding: 10px;
    --transition-delay: 250ms;

    --syntax-if: #d76de8;
    --syntax-method: #e8e06d;
    --syntax-constructor: #6de86d;
}

/* DEFAULT MENU */
#page-container {
    background: var(--bg-color);
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 50;
    padding: var(--padding);
    border: 1px solid var(--main-color);
    border-radius: var(--roundness);

    width: 50%;
    min-width: 500px;
    max-width: 600px;
    box-sizing: border-box;
    opacity: 0;
    font-size: 1.5rem;
}

#page-container * {
    font-family: Arial, Helvetica, sans-serif!important;
    font-weight: 600!important;
}

#page-container h1, h2, h3, h4, p {
    margin: 0;
}

#page-container > header {
    background: var(--bg-sub-color);
    color: var(--text-color);
    border-radius: var(--roundness);
    padding: 5px var(--padding);

    font-size: 1.5em;
    font-weight: 600;
    letter-spacing: -1px;

    display: flex;
    justify-content: flex-start;
    align-items: center;
}
header > span {
    margin-left: var(--padding);
}

#page-container > main {
    display: flex;
    width: 100%;
    height: 350px;
    margin-top: var(--padding);
}


/* MENU LOGO SIZING */
.imageHolder {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 40px;
}
.imageHolder > img {
    width: 100%;
    height: 100%;
}


/* NAV BAR */
#nav-bar {
    background: var(--bg-sub-color);
    padding: var(--padding);
    border-radius: var(--roundness);

    float: left;
    display: flex;
    flex-direction: column;
}


/* MENU PAGES */
#menu-page-container {
    border-radius: var(--roundness);
    margin-left: var(--padding);

    width: 100%;
    height: 100%;
    overflow-y: auto;
}
.menu-page {
    background: var(--bg-sub-color);
    padding: var(--padding);
    border-radius: var(--roundness);

    display: none;
}
.opened {
    display: block;
    animation: opacity var(--transition-delay) forwards;
}
@keyframes opacity {
    from { opacity: 0 }
    to { opacity: 1 }
}
@keyframes opacity2 {
    from { opacity: 1 }
    to { opacity: 0 }
}


/* HEADER AND PARAGRAPH OF MENU */
.menu-page > h1 {
    color: var(--main-color);
    font-size: 1.6em;
    letter-spacing: -2px;
}
.menu-page > h3 {
    color: var(--third-color);
    margin-top: 15px;
}
.description {
    color: var(--sub-color);
    font-weight: 600;
    font-size: 0.7em;
}
.highlight {
    color: var(--highlight-color);
    fill: var(--highlight-color);
}
.highlight-secondary {
    color: var(--highlight-color-secondary);
    font-size: 0.85em;
    margin-left: var(--padding);
}

.highlight-secondary > a {
    color: var(--highlight-color-secondary);
    cursor: pointer!important;
}

.menu-page-section {
    display: flex;
    align-items: center;
    margin-top: 10px;
    font-weight: 600;
}

.icon-tx {
    width: 25px;
}

/* ADD NEW ITEM */
.add-item {
    display: flex;
    justify-content: center;
    margin-top: 10px;
}
.add-item-button {
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer!important;
}
.add-item-button > span {
    margin-left: 10px;
}


/* ADD ITEM BUTTON EFFECTS */
.plus-icon {
    width: 20px;
    transition: fill var(--transition-delay);
    fill: var(--sub-color);
}
.text {
    color: var(--sub-color);
    transition: color var(--transition-delay);
    font-weight: 600;
}
.add-item-button > * {
    pointer-events: none;
}
.add-item-button:hover .plus-icon {
    fill: var(--text-color);
}
.add-item-button:hover .text {
    color: var(--text-color);
}
.add-item-button:active .plus-icon {
    fill: var(--text-color-active);
}
.add-item-button:active .text {
    color: var(--text-color-active);
}


/* NAV BAR BUTTON */
.open-menu {
    outline: none;
    border: none;

    background: var(--sub-alt-color);
    color: var(--text-color);
    transition: background var(--transition-delay), color var(--transition-delay);
    font-size: 1em;
    font-weight: 600;
    padding: var(--padding);
    cursor: pointer!important;
    margin-right: 1px;
}
.open-menu:hover {
    background: var(--text-color);
    color: var(--sub-alt-color);
}
.open-menu:active {
    background: var(--sub-color);
    color: var(--sub-alt-color);
}
.open-menu.enabled {
    border: solid;
    border-width: 0 1px 0 0;
    border-color: var(--text-color);
    pointer-events: none;
    transition: border-color var(--transition-delay);
    margin-right: 0px;
}
.bottom-align {
    margin-top: auto;
}


/* ITEM SETTINGS */
.item-content {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;

    background: var(--bg-item-content);
    padding: var(--padding);
    padding-right: 17px;
    font-size: 0.8rem;
    font-weight: 600;
    border-radius: var(--roundness);
    text-align: center;
    margin-top: 15px;
}
.syntax-help {
    margin: 0 2px;
}
.type-text {
    color: var(--text-color);
}
.syntax-if {
    color: var(--syntax-if);
}
.syntax-method {
    color: var(--syntax-method);
    margin: 0 1px;
}
.syntax-constructor {
    color: var(--syntax-constructor);
}
.add-item-input {
    outline: none;
    border: none;
    width: 50px;

    background: var(--sub-color);
    color: var(--text-color);
    border: 1px solid;
    border-color: transparent;
    transition: border-color var(--transition-delay);

    border-radius: var(--roundness);

    padding: var(--padding) 5px;
    font-size: 0.6rem;
    font-weight: 600;
    text-align: center;
    cursor: text!important;
}
.add-item-input:hover {
    border-color: var(--border-color-opacity);
}
.add-item-input:focus {
    border-color: var(--text-color);
}
.add-item-input::placeholder {
    color: var(--text-color);
    opacity: 1; /* Firefox */
}
.add-item-input:-ms-input-placeholder {
    color: var(--text-color);
}
.add-item-input::-ms-input-placeholder {
    color: var(--text-color);
}

.item-container > .item-content > .add-item-input {
    /*width: 40px;*/
}
.item-container > .item-content > .custom-select {
    /*width: 40px;*/
    font-size: 0.5rem;
}

.bin-icon {
    width: 15px;
    height: 15px;
}
.delete-item {
    position: absolute;
    top: 5px;
    right: 0;
    opacity: 0;
    fill: var(--bin-color);
    cursor: pointer!important;
}
.delete-item > * {
    pointer-events: none;
}
.item-content:hover .delete-item {
    opacity: 1;
}
.item-content > .imageHolder {
    width: 30px;
    margin-left: 3px;
}


/* POPUP ADD ITEM */
.popup-container {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0, 0, 0, 0.5);
    animation: opacity var(--transition-delay) forwards;

    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 10;

    width: 100%;
    height: 100%;
    box-sizing: border-box;

    border-radius: var(--roundness);
    font-weight: 600;
}

.add-item-popup {
    padding: var(--padding);
    border: 1px solid var(--border-color-opacity);
    border-radius: var(--roundness);

    background: var(--popup-bg-color);
    text-align: center;
    width: 27rem;
}

.popup-header {
    color: var(--text-color);
}

.popup-to-remove {
    animation: opacity2 var(--transition-delay) forwards;
}
/* .add-item-popup > .item-content {
    font-size: 0.6rem;
} */


/* CONFIRM PANEL */
.checkbox-icon {
    width: 25px;
    height: 25px;
}
.cross-icon {
    width: 25px;
    height: 25px;
}
.confirm-panel {
    margin-top: 15px;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
}
.confirm-panel > label {
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer!important;
}
.confirm-panel > label > span {
    margin-left: var(--padding);
}
.confirm-panel > label > * {
    pointer-events: none;
}


/* ADD BUTTON */
.add-button {
    color: var(--add-button-color);
    fill: var(--add-button-color);
    transition: color var(--transition-delay), fill var(--transition-delay);
    user-select: none;
}
.add-button:active {
    color: var(--add-button-active-color);
    fill: var(--add-button-active-color);
}


/* CANCEL BUTTON */
.cancel-button {
    color: var(--cancel-button-color);
    fill: var(--cancel-button-color);
    transition: color var(--transition-delay), fill var(--transition-delay);
    user-select: none;
}
.cancel-button:active {
    color: var(--cancel-button-active-color);
    fill: var(--cancel-button-active-color);
}


/* CUSTOM SELECT */
.custom-select {
    position: relative;
    background: var(--sub-alt-color);
    color: var(--text-color);
    width: 60px;
    padding: var(--padding) 5px;

    user-select: none;
    cursor: pointer!important;
    font-size: 0.6rem;
    margin: 0 2px;

    border-radius: var(--roundness);
    border: 1px solid;
    border-color: transparent;
    transition: border-color var(--transition-delay);
}
.custom-select * {
    cursor: pointer!important;
}
.custom-select:not(.custom-select-enabled):hover {
    border-color: var(--border-color-opacity);
}
.custom-select-enabled {
    border-radius: 0px;
    border-top-left-radius: var(--roundness);
    border-top-right-radius: var(--roundness);

    border-color: var(--sub-color);
    border-width: 1px 1px 0 1px;
    margin-top: -1px;
    transition: none;
}

.custom-select-options {
    display: none;
    position: absolute;
    left: -1px;
    right: 0;
    top: 100%;
    width: calc(100% + 2px);
    z-index: 11;
}

.custom-select-option {
    padding: var(--padding) 5px;
    background: var(--sub-alt-color);
    transition: background var(--transition-delay), color var(--transition-delay);

    border: solid;
    border-color: var(--sub-color);
    border-width: 0 1px 1px 1px;
}
.custom-select-option:hover {
    background: var(--sub-color);
}
.custom-select-option:active {
    background: var(--text-color);
    color: var(--sub-alt-color);
}

.custom-select-opened {
    display: block;
}
.custom-select-opened .custom-select-option:last-child {
    border-bottom-left-radius: var(--roundness);
    border-bottom-right-radius: var(--roundness);
}

.hidden {
    display: none;
}

.opened-menu {
    animation: opacity var(--transition-delay) forwards;
}

.closed-menu {
    animation: opacity2 var(--transition-delay) forwards;
}

#close-menu {
    fill: var(--bin-color);
    stroke: var(--bin-color-border);
    stroke-width: 2px;
    margin-left: auto;
    cursor: pointer!important;
}
#close-menu > * {
    pointer-events: none;
}

.manage-storage {
    position: relative;
    outline: none;
    border: none;
    background: var(--sub-alt-color);
    color: var(--text-color);
    border: 1px solid var(--border-color-opacity);
    transition: background var(--transition-delay), color var(--transition-delay);

    padding: var(--padding);
    border-radius: var(--roundness);
    font-weight: 600;
    cursor: pointer!important;
    margin-right: var(--padding);
}

.manage-storage > input {
    position: absolute;
    cursor: pointer!important;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    opacity: 0;
}
.manage-storage > input::-webkit-file-upload-button {
    cursor: pointer!important;
}

.manage-storage:hover {
    background: var(--sub-color);
}
.manage-storage:active {
    background: var(--text-color);
    color: var(--sub-alt-color);
}
.manage-storage:last-child {
    margin: 0;
}`;


    const IFRAMECSS = `
#iframe-page-container {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 50;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: none;
}
.hidden {
    display: none!important;
}
    `;

    window.addEventListener("load", function() {

        const CODE = `<style>${CSS}</style>${HTML}`;
        const iframe = document.createElement("iframe");
        const blob = new Blob([CODE], {type: "text/html; charset=utf-8"});
        iframe.src = URL.createObjectURL(blob);
        iframe.id = "iframe-page-container";
        document.body.appendChild(iframe);

        const style = document.createElement("style");
        style.innerHTML = IFRAMECSS;
        document.head.appendChild(style);


        iframe.contentWindow.addEventListener("load", function() {
            const iframeWindow = iframe.contentWindow;
            const iframeDocument = iframeWindow.document;
            URL.revokeObjectURL(iframe.src);

            function getID(type) {
                if (settings.freeIDS.length) {
                    return settings.freeIDS.pop();
                }
                return settings[TYPES[type]].length;
            }

            function getName() {
                return location.hostname.replace(/\.io/, "");
            }

            function saveSettings() {
                const data = JSON.stringify(settings, null, 4);
                const blob = new Blob([data], { type: "text/plain" });
                const elem = document.createElement("a");
                elem.href = URL.createObjectURL(blob);
                elem.download = `textures${getName()}.txt`;
                document.body.appendChild(elem);
                elem.click();
                URL.revokeObjectURL(elem.href);
                document.body.removeChild(elem);
            }

            async function loadSettings(event) {
                try {
                    const value = await event.target.files[0].text();
                    const data = JSON.parse(value);
                    Object.assign(settings, data);
                    storage.set("TextureSettings", data);
                    render();
                    event.target.value = "";
                } catch(err) {
                    alert("File invalid");
                }
            }

            function resetSettings() {
                const data = createSettings();
                Object.assign(settings, data);
                storage.set("TextureSettings", settings);
                render();
            }

            const pageContainer = iframeDocument.querySelector("#page-container");
            const openMenu = iframeDocument.querySelectorAll(".open-menu");
            const menuPage = iframeDocument.querySelectorAll(".menu-page");
            const addTexture = iframeDocument.querySelector("#add-texture");
            const addEmoji = iframeDocument.querySelector("#add-emoji");
            const textureContainer = iframeDocument.querySelector("#texture-container");
            const emojiContainer = iframeDocument.querySelector("#emoji-container");
            const closeMenu = iframeDocument.querySelector("#close-menu");
            const downloadSettings = iframeDocument.querySelector("#download-settings");
            const uploadSettings = iframeDocument.querySelector("#upload-settings");
            const resetSettingsData = iframeDocument.querySelector("#reset-settings");

            function remove(target, className) {
                if (!Number.isInteger(target.length)) {
                    target.classList.remove(className);
                    return
                }
                for (const element of target) {
                    element.classList.remove(className);
                }
            }

            for (let i=0;i<openMenu.length;i++) {
                openMenu[i].onclick = function() {
                    remove(openMenu, "enabled");
                    openMenu[i].classList.add("enabled");

                    remove(menuPage, "opened");
                    menuPage[i].classList.add("opened");
                }
            }

            function removeChildren(element) {
                while (element.firstChild) {
                    element.removeChild(element.lastChild);
                }
            }

            function createElement(tagName, options) {
                const element = document.createElement(tagName);
                for (const key in options) {
                    element[key] = options[key];
                }
                return element;
            }

            // Custom select element
            function generateSelect(defaultValue, callback) {
                const customSelect = createElement("div", { className: "custom-select", tabIndex: 0 });
                const currentValue = createElement("span", { className: "current-value" });
                const customSelectOptions = createElement("div", { className: "custom-select-options" });

                const options = [
                    { value: SEARCH_TYPES.CONTAINS, label: "CONTAINS" },
                    { value: SEARCH_TYPES.EQUALS, label: "EQUALS" }
                ];
                options[defaultValue].isDefault = true;

                function close() {
                    customSelect.classList.remove("custom-select-enabled");
                    customSelectOptions.classList.remove("custom-select-opened");
                    removeChildren(customSelectOptions);
                }

                function addOptions() {
                    removeChildren(customSelectOptions);
                    for (const option of options) {
                        const { value, label, isDefault } = option;
                        if (isDefault) {
                            currentValue.textContent = label;
                            continue;
                        }

                        const customSelectOption = createElement("div", { className: "custom-select-option" });
                        customSelectOption.textContent = label;
                        customSelectOptions.appendChild(customSelectOption);

                        customSelectOption.onclick = function() {
                            close();
                            currentValue.textContent = label;

                            options.map(option => (option.isDefault = false));
                            option.isDefault = true;
                            callback(option);
                        }
                    }
                }
                addOptions();

                customSelect.appendChild(currentValue);
                customSelect.appendChild(customSelectOptions);

                customSelect.onclick = function(event) {
                    if (event.target.className === "custom-select-option") return;
                    customSelect.classList.toggle("custom-select-enabled");
                    customSelectOptions.classList.toggle("custom-select-opened");
                    addOptions();
                }
                customSelect.onblur = close;

                return customSelect;
            }

            function getItem(options) {
                const item = settings[TYPES[options.type]].find(item => item.id === options.id);
                if (!item) throw new Error("Failed to find item");
                return item;
            }

            function removeItem(options) {
                const item = getItem(options);
                const list = settings[TYPES[options.type]];
                const index = list.indexOf(item);
                settings.freeIDS.push(item.id);
                list.splice(index, 1);
                storage.set("TextureSettings", settings);
            }

            function updateItem(options) {
                const item = getItem(options);
                Object.assign(item, options);
                storage.set("TextureSettings", settings);
            }

            function generateItem(options = {}, isPopup = false) {
                const itemContent = createElement("div", { className: "item-content" });
                itemContent.UploadData = Object.assign({
                    id: getID(options.type),
                    type: 0,
                    searchType: 0,
                    from: "",
                    to: ""
                }, options);

                const syntaxIF = createElement("div", {
                    className: "syntax-help syntax-if",
                    textContent: "IF"
                });
                const typeText = createElement("div", {
                    className: "syntax-help syntax-constructor",
                    textContent: TEXT_TYPES[itemContent.UploadData.type]
                });
                itemContent.appendChild(syntaxIF);
                itemContent.appendChild(typeText);

                const customSelect = generateSelect(itemContent.UploadData.searchType, function(option) {
                    itemContent.UploadData.searchType = option.value;
                    if (!isPopup) updateItem(itemContent.UploadData);
                });
                itemContent.appendChild(customSelect);

                const inputFrom = createElement("input", {
                    className: "add-item-input",
                    type: "text",
                    placeholder: ". . .",
                    value: itemContent.UploadData.from || ""
                });
                inputFrom.onchange = function(event) {
                    itemContent.UploadData.from = event.target.value.match(/img.+$/)[0];
                    if (!isPopup) updateItem(itemContent.UploadData);
                }
                const replaceWith = createElement("div", {
                    className: "syntax-help syntax-method",
                    innerHTML: "REPLACE WITH"
                });
                const inputTo = createElement("input", {
                    className: "add-item-input",
                    type: "text",
                    placeholder: ". . .",
                    value: itemContent.UploadData.to || ""
                });
                const preview = createElement("div", {
                    className: "imageHolder hidden",
                    innerHTML: `<img src="" draggable="false"/>`
                });

                async function setSrc(src) {
                    preview.classList.add("hidden");
                    if (!isURL(src)) return;
                    const isValid = await isValidImageSrc(src);
                    const child = preview && preview.firstChild;
                    if (isValid && child && src !== child.src) {
                        preview.classList.remove("hidden");
                        child.src = src;
                    }
                }
                setSrc(itemContent.UploadData.to);

                inputTo.oninput = function(event) {
                    const value = event.target.value;
                    setSrc(value);
                }
                inputTo.onchange = function(event) {
                    itemContent.UploadData.to = event.target.value;
                    if (!isPopup) updateItem(itemContent.UploadData);
                }
                if (!isPopup) {
                    const deleteItem = createElement("div", {
                        className: "delete-item",
                        innerHTML: `
                <svg
                    class="icon-tx bin-icon"
                    version="1.1"
                    xmlns="http://www.w3.org/2000/svg"
                    width="32" height="32" viewBox="0 0 32 32"
                >
                    <path d="M4 10v20c0 1.1 0.9 2 2 2h18c1.1 0 2-0.9 2-2v-20h-22zM10 28h-2v-14h2v14zM14 28h-2v-14h2v14zM18 28h-2v-14h2v14zM22 28h-2v-14h2v14z"></path>
                    <path d="M26.5 4h-6.5v-2.5c0-0.825-0.675-1.5-1.5-1.5h-7c-0.825 0-1.5 0.675-1.5 1.5v2.5h-6.5c-0.825 0-1.5 0.675-1.5 1.5v2.5h26v-2.5c0-0.825-0.675-1.5-1.5-1.5zM18 4h-6v-1.975h6v1.975z"></path>
                </svg>
            `
                    });
                    deleteItem.onclick = function() {
                        if (itemContent.classList.contains("popup-to-remove")) return;
                        itemContent.classList.add("popup-to-remove");
                        removeItem(itemContent.UploadData);
                        setTimeout(() => itemContent.remove(), 250);
                    }
                    itemContent.appendChild(deleteItem);
                }
                itemContent.appendChild(inputFrom);
                itemContent.appendChild(replaceWith);
                itemContent.appendChild(inputTo);
                if (itemContent.UploadData.type === DATA_TYPES.TEXTURES) itemContent.appendChild(preview);

                return itemContent;
            }

            function addItem(item, options = {}) {
                const container = [textureContainer, emojiContainer][options.type];
                container.appendChild(item);
            }

            function render() {
                removeChildren(textureContainer);
                // removeChildren(emojiContainer);

                for (const key of Object.values(TYPES)) {
                    for (const options of settings[key]) {
                        const newItem = generateItem(options);
                        addItem(newItem, options);
                    }
                }
            }
            render();

            function generatePopup(type) {
                const popupContainer = createElement("div", { className: "popup-container" });
                const addItemPopup = createElement("div", { className: "add-item-popup" });
                const popupHeader = createElement("p", { className: "popup-header", textContent: HEADER_TYPES[type] });
                addItemPopup.appendChild(popupHeader);

                const item = generateItem({ type }, true);
                addItemPopup.appendChild(item);
                const confirmPanel = createElement("div", { className: "confirm-panel" });
                const addButton = createElement("label", {
                    className: "add-button",
                    innerHTML: `
        <svg
            class="checkbox-icon"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            width="32" height="32" viewBox="0 0 32 32"
        >
            <path d="M28 0h-24c-2.2 0-4 1.8-4 4v24c0 2.2 1.8 4 4 4h24c2.2 0 4-1.8 4-4v-24c0-2.2-1.8-4-4-4zM14 24.828l-7.414-7.414 2.828-2.828 4.586 4.586 9.586-9.586 2.828 2.828-12.414 12.414z"></path>
        </svg>
        <span>CONFIRM</span>
        `
                });
                const cancelButton = createElement("label", {
                    className: "cancel-button",
                    innerHTML: `
        <svg
            class="cross-icon"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            width="32" height="32" viewBox="0 0 32 32"
        >
            <path d="M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z"></path>
        </svg>
        <span>CANCEL</span>
        `
                });
                confirmPanel.appendChild(addButton);
                confirmPanel.appendChild(cancelButton);
                addItemPopup.appendChild(confirmPanel);
                popupContainer.appendChild(addItemPopup);

                function close(event) {
                    if (popupContainer.classList.contains("popup-to-remove")) return;
                    if (![popupContainer, cancelButton, addButton].includes(event.target)) return;

                    popupContainer.classList.add("popup-to-remove");
                    setTimeout(() => popupContainer.remove(), 250);
                }

                cancelButton.onclick = close;
                popupContainer.onclick = close;

                addButton.onclick = function(event) {
                    if (event.target !== addButton) return;

                    const newItem = generateItem(item.UploadData);
                    addItem(newItem, newItem.UploadData);
                    settings[TYPES[type]].push(newItem.UploadData);
                    storage.set("TextureSettings", settings);
                    close(event);
                }

                return popupContainer;
            }

            addTexture.onclick = function() {
                pageContainer.appendChild(generatePopup(DATA_TYPES.TEXTURES));
            }

            /* addEmoji.onclick = function() {
                pageContainer.appendChild(generatePopup(DATA_TYPES.EMOJIS));
            } */

            downloadSettings.onclick = saveSettings;
            uploadSettings.onchange = loadSettings;
            resetSettingsData.onclick = resetSettings;
            function toggleMenu() {
                const list = pageContainer.classList;
                list.toggle("closed-menu");
                list.toggle("opened-menu");
                setTimeout(() => {
                    list.toggle("hidden");
                    iframe.classList.toggle("hidden");
                }, 250);
            }

            closeMenu.onclick = toggleMenu;

            function handleKeydown(event) {
                if (event.code === "Escape") {
                    event.preventDefault();
                    if (event.repeat) return;
                    toggleMenu();
                }
            }
            window.addEventListener("keydown", handleKeydown);
            iframeWindow.addEventListener("keydown", handleKeydown);
        })
    })

})();