Infinite Folders

A modification for Infinite Craft that adds folders. Composed entirely by @coolkase (https://coolkase.dev)

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==UserScript==
// @name         Infinite Folders
// @namespace    N/A
// @version      DR-1
// @description  A modification for Infinite Craft that adds folders. Composed entirely by @coolkase (https://coolkase.dev)
// @author       coolkase
// @match        https://neal.fun/infinite-craft/*
// @icon         https://i.imgur.com/eWYOeEb.png
// @grant        unsafeWindow
// @license      GPL2
// ==/UserScript==

class Toast {
    constructor(defaultTimeoutMiliseconds) {
        this.timeout = defaultTimeoutMiliseconds
        const _style = `

        #toast
        {
            left: 0px;
            top: 20px;
            right: 0px;
            width: 100%;
            z-index: 20;
            height: 100%;
            color: white;
            padding: 15px;
            font-size: 18px;
            max-width: 450px;
            max-height: 60px;
            margin-left: auto;
            visibility: hidden;
            margin-right: auto;
            position: absolute;
            border-radius: 16px;
            text-align: center left;
            border: 2px solid #1F79FF;
            backdrop-filter: blur(6px);
            background: rgba(38, 125, 255, 0.5);
            box-shadow: 0px 0px 64px 4px rgba(0,0,0,0.75);
            -moz-box-shadow: 0px 0px 64px 4px rgba(0,0,0,0.75);
            -webkit-box-shadow: 0px 0px 64px 4px rgba(0,0,0,0.75);
            text-shadow: 0px 0px 12px rgba(0, 0, 0, 1), 0px 0px 6px rgba(0, 0, 0, 0.75);
        }

        #toast.show
        {
            visibility: visible;
            animation: fadein 0.5s, fadeout 0.5s 2.5s forwards;
            -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s forwards;
        }

        @keyframes fadein
        {
            from
            {
                transform: translateY(20px);
                opacity: 0%;
            }

            to
            {
                transform: translateY(0px);
                opacity: 100%;
            }
        }

        @keyframes fadeout
        {
            from
            {
                transform: translateY(0px);
                opacity: 100%;
            }

            to
            {
                transform: translateY(20px);
                opacity: 0%;
            }
        }

        #toast.success
        {
            border: 2px solid #01E17B;
            background: rgba(0, 237, 81, 0.5);
        }

        #toast.error
        {
            border: 2px solid #F04349;
            background: rgba(240, 66, 72, 0.5);
        }

        #toast.warning
        {
            border: 2px solid #FFD21F;
            background: rgba(255, 212, 38, 0.5);
        }

        @keyframes background-pan
        {
            from
            {
                background-position: 0% center;
            }

            to
            {
                background-position: -200% center;
            }
        }

        #toast.top
        {
            top: 20px;
            bottom: auto;
        }

        #toast.bottom
        {
            top: auto;
            bottom: 20px;
        }

        #toast.right
        {
            left: auto;
            right: 40px;
        }

        #toast.left
        {
            left: 20px;
            right: auto;
        }

        `

        document.head.insertAdjacentHTML('beforeend', `<style>${_style}</style>`)
        document.body.insertAdjacentHTML('beforeend', `<div id='toast'></div>`)
    }

    pop(toastMessage, toastPosition, toastType) {
        setTimeout(() => {
            const toast = document.getElementById('toast')
            const toastClassName = ` show ${toastPosition} ${toastType}`

            toast.innerHTML = toastMessage
            toast.className += toastClassName

            setTimeout(() => {
                toast.className = toast.className.replace(toastClassName, '')
            }, this.timeout)
        }, 200)
    }
}

const ToastLibrary = new Toast(3000)

const themes = {
    Folders: `
       hr {
               height: 1px !important;
               border: 0 !important;
               background-color: rgba(255, 255, 255, 0.5);
               -webkit-filter: contrast(2) !important;
               filter: contrast(2) !important;
            }

    `,
}

class ThemeManager {
    constructor() {
        this.currentTheme = themes.Folders
        this.themeChangeAnimation = `

            .particles,
            .logo,
            .clear,
            .sound,
            .site-title,
            .coffee,
            .container,
            .items,
            .sidebar-input,
            .sidebar,
            .sidebar-sorting-item,
            .reset,
            .instruction,
            .sidebar-input-close
            {
                transition: all 400ms ease-in-out;
            }

            :root
            {
                transition: all 400ms ease-in-out;
            }

            .item
            {
                transition: border-radius 400ms ease-in-out,
                background 400ms ease-in-out,
                color 400ms ease-in-out;
            }

        `

        document.head.insertAdjacentHTML('beforeend', `<style>${this.themeChangeAnimation}</style>`)
    }

    change(themeId) {
        const theme = themes[themeId]
        if (!theme) {
            ToastLibrary.pop(`Attempt to apply invalid theme '${themeId}'.`, 'top right', 'error')
            return
        }

        document.head.insertAdjacentHTML('beforeend', `<style>${theme}</style>`)

        ToastLibrary.pop(`Applied folder style. Note that this is a developer release and that changes are NOT final.`, 'top right', 'success')
    }
}

const Theme = new ThemeManager

setTimeout(() => {
    Theme.change('Folders')
}, 200)

// folder stuff

const insertAfter = (el, htmlString) =>
    el.insertAdjacentHTML('afterend', htmlString);

if (typeof(Storage) !== "undefined") {
    if (unsafeWindow.localStorage.getItem("infiniteCraft_folders") == null) {
        unsafeWindow.localStorage.setItem("infiniteCraft_folders", "{}")
    }
} else {
    alert('Your browser does not support web storage, folders will not save. You can try using a Chromium browser instead of the one you are using.')
}

var folders = JSON.parse(unsafeWindow.localStorage.getItem("infiniteCraft_folders"))
var revert = '';

function makeFolderHTML(title) {
    return `
  <br>
  <hr>
  <br><br>
  <span><i>${title}</i></span>
  <br>`
}

function stringToHex(inputString) {
    var hexString = '';
    for (var i = 0; i < inputString.length; i++) {
        // Get the Unicode code point of the character
        var charCode = inputString.charCodeAt(i);
        // Convert the Unicode code point to hexadecimal representation
        var hexValue = charCode.toString(16).toUpperCase();
        // Add leading zeros if necessary to ensure each character is represented by two hexadecimal digits
        hexValue = ('00' + hexValue).slice(-2);
        // Append the hexadecimal representation to the result string
        hexString += hexValue + ' ';
    }
    // Trim any trailing space and return the hexadecimal string
    return hexString.trim();
}

function getElementHTML(name) {
    const items = document.getElementsByClassName("item")
    for (var i = 0; i < items.length; i++) {
        var itemName = items[i].textContent.replace(/([^a-z0-9]+)|\u002d|\s/gi, '').trim();
        console.log(stringToHex(name))
        console.log(stringToHex(itemName))
        if (itemName == name.replace(/([^a-z0-9]+)|\s/gi, '-').trim()) {
            return items[i].outerHTML
        }
    }
    return ''
}

function render() {
    const sidebar = document.getElementsByClassName('items')[0]
    sidebar.innerHTML = revert

    const items = document.getElementsByClassName("item")
    const folderNames = Object.keys(folders)

    for (var j = 0; j < folderNames.length; j++) {
        var html = `<br><br><span><i>${folderNames[j]}</i></span><br>`
        for (var item = 0; item < folders[folderNames[j]].length; item++) {
            html += getElementHTML(folders[folderNames[j]][item])
            console.log(folders[folderNames[j]][item])
        }
        html += `<br>
  <hr>`
        sidebar.innerHTML = html + sidebar.innerHTML;
    }
}

function addItem() {
    let item = prompt('Input the name of the element you want to store:')
    let folder = prompt(`Input the name of the folder you want to store "${item}" in:`)
    if (folders[folder] != null) {
        folders[folder].push(item)
    } else {
        folders[folder] = []
        folders[folder].push(item)
    }
    unsafeWindow.localStorage.setItem("infiniteCraft_folders", JSON.stringify(folders))
    render()
}

window.addEventListener('load', function() {
    const sidebar = document.getElementsByClassName('items')[0]
    revert = sidebar.innerHTML
    render()
})

if (document.addEventListener) {
    document.addEventListener('contextmenu', function(e) {
        addItem()
        e.preventDefault();
    }, false);
} else {
    document.attachEvent('oncontextmenu', function() {
        addItem()
        window.event.returnValue = false;
    });
}