RabbitMQ Payload Injection Tool

9/12/2024, 1:59:09 PM

// ==UserScript==
// @name        RabbitMQ Payload Injection Tool
// @namespace   Violentmonkey Scripts
// @match       http://localhost:15672/*
// @grant       GM_addStyle
// @grant       GM_addElement
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_listValues
// @grant       GM_getResourceText
// @grant       GM_getResourceURL
// @author      Adrien Chen
// @version     1.0
// @license     MIT
// @description 9/12/2024, 1:59:09 PM
// @resource    fontAwesome https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css
// ==/UserScript==

(function () {
    'use strict';

    const buttonId = 'customButton_payloadInjection';
    const menuId = 'hamburgerMenu_payloadInjection';
    const eventFormId = 'eventForm';
    const eventListId = 'eventList';
    const settingsId = 'settingsMenu';
    const openMenuToggleId = 'openMenuToggle';
    const autoPublishToggleId = 'autoPublishToggle';
    const backButtonId = 'backButton';
    const addCategoryButtonId = 'addCategoryButton';
    const menuHeaderId = 'menuHeader';

    const defaultPropertiesA = 'content_type';
    const defaultPropertiesB = 'application/json';

    let deleteModeActive = false;
    let editModeActive = false;
    let eventToEdit = null;

    // Add Font Awesome CSS
    addFontAwesome();

    // Initial execution
    executeScriptLogic();

    function addFontAwesome() {
        const fontAwesomeLink = document.createElement('link');
        fontAwesomeLink.rel = 'stylesheet';
        fontAwesomeLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css';
        fontAwesomeLink.integrity = 'sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==';
        fontAwesomeLink.crossOrigin = 'anonymous';
        fontAwesomeLink.referrerPolicy = 'no-referrer';
        document.head.appendChild(fontAwesomeLink);
    }

    function executeScriptLogic() {
        console.log("executing Script")
        const urlPattern = /http:\/\/localhost:15672\/#\/exchanges\/%2F\/.*/;

        if (urlPattern.test(window.location.href)) {
            if (document.querySelector('form[action="#/login"][method="put"]')) {
                return;
            }

            createButton();
            createMenu();
            loadEvents();

            const featureToggles = GM_getValue('featureToggles', { openMenuOnArrival: true, autoPublishMessage: false });

            if (featureToggles.openMenuOnArrival) {
                document.getElementById(menuId).style.right = '0%';
            }
        } else {
            removeElementById(buttonId);
            removeElementById(menuId);
        }
    }

    function createButton() {
        if (!document.getElementById(buttonId)) {
            const newButton = GM_addElement('button', {
                id: buttonId,
                style: 'position: fixed; bottom: 10px; right: 20px; z-index: 1000; font-size: 15px'
            });
            newButton.innerHTML = `<i class="fa-solid fa-burger"></i>`;
            newButton.addEventListener('click', toggleMenu);
        }
    }

    function createMenu() {
        if (!document.getElementById(menuId)) {
            const newMenu = GM_addElement('div', {
                id: menuId,
                style: `
                    position: fixed;
                    bottom: 0;
                    right: -35%;
                    width: 30%;
                    min-width: 250px;
                    height: 82%;
                    background-color: #333;
                    color: #fff;
                    z-index: 999;
                    transition: right 0.2s ease;
                    padding: 10px;
                    overflow-y: auto;
                `
            });

            newMenu.innerHTML = getMenuHTML();
            addMenuEventListeners();
        }
    }

    function getMenuHTML() {
        return `
            <style>
                .hidden-checkbox {
                    display: none;
                }

                .custom-checkbox {
                    cursor: pointer;
                    font-size: 24px; /* Adjust the size as needed */
                    color: #757474; /* Adjust the color as needed */
                    display: inline-flex;
                    align-items: center;
                }

                .hidden-checkbox:checked + .custom-checkbox .fa-square-check {
                    color: #FF6600; /* Change color when checked */
                }

            </style>
            <div style="display: flex; justify-content: space-evenly; padding-bottom: 5px">
                <button id="createEventButton" style="color: #000;"><i class="fa-solid fa-plus"></i></button>
                <button id="deleteEventButton" style="color: #000;"><i class="fa-solid fa-trash"></i></button>
                <button id="${addCategoryButtonId}" style="color: #000;"><i class="fa-solid fa-folder-plus"></i></button>
                <button id="settingsButton" style="color: #000;"><i class="fa-solid fa-gear"></i></button>
                <button id="${backButtonId}" style="color: #000; display: none;"><i class="fa-solid fa-arrow-left"></i></button>
            </div>
            <form id="${eventFormId}" style="display: none;">
                <h2 style="color: #fff;" id=${menuHeaderId}></h2>
                <label style="color: #fff;">Display Name: <input type="text" id="eventName" required></label><br>
                <label style="color: #fff;">Routing Key: <input type="text" id="routingKey" required></label><br>
                <label style="color: #fff;">Properties: <input type="text" id="propertiesA" placeholder="${defaultPropertiesA}"> = <input type="text" id="propertiesB" placeholder="${defaultPropertiesB}"></label><br>
                <label style="color: #fff;">Payload:<br><textarea id="payload" style="width: 90%;" required></textarea></label><br>
                <div style="display: flex; padding: 5px">
                    <button type="submit" id="saveEventButton" style="color: #000; margin-right: 10px">Save Event</button>
                    <button type="button" id="cancelEventButton" style="color: #000;">Cancel</button>
                </div>
            </form>
            <div id="${eventListId}" style="color: #fff; margin-bottom: 40px"></div>
            <button id="confirmDeleteButton" style="color: #000; display: none;"><i class="fa-solid fa-check"></i></button>
            <div id="${settingsId}" style="display: none; color: #fff;">
                <h2>Settings</h2>
                <label for="${openMenuToggleId}" style="color: #fff; font-size: 13px;" >Open menu on page load: </label>
                <input type="checkbox" id="${openMenuToggleId}" class="hidden-checkbox">
                <label for="${openMenuToggleId}" class="custom-checkbox">
                    <i class="fa-solid fa-square-check"></i>
                </label>
                <br>
                <label for="${autoPublishToggleId}" style="color: #fff; font-size: 13px;">Auto publish message on injection: </label>
                <input type="checkbox" id="${autoPublishToggleId}" class="hidden-checkbox">
                <label for="${autoPublishToggleId}" class="custom-checkbox">
                    <i class="fa-solid fa-square-check"></i>
                </label>
            </div>
        `;
    }

    function addMenuEventListeners() {
        document.getElementById('createEventButton').addEventListener('click', showEventForm);
        document.getElementById('saveEventButton').addEventListener('click', saveEvent);
        document.getElementById('cancelEventButton').addEventListener('click', cancelEvent);
        document.getElementById('deleteEventButton').addEventListener('click', enableDeleteMode);
        document.getElementById('confirmDeleteButton').addEventListener('click', confirmDelete);
        document.getElementById('settingsButton').addEventListener('click', showSettings);
        document.getElementById(backButtonId).addEventListener('click', goBack);
        document.getElementById(addCategoryButtonId).addEventListener('click', addCategory);

        const featureToggles = GM_getValue('featureToggles', { openMenuOnArrival: true, autoPublishMessage: false });


        document.getElementById(openMenuToggleId).checked = featureToggles.openMenuOnArrival;
        document.getElementById(openMenuToggleId).addEventListener('change', toggleOpenMenuOnArrival);

        document.getElementById(autoPublishToggleId).checked = featureToggles.autoPublishMessage;
        document.getElementById(autoPublishToggleId).addEventListener('change', toggleAutoPublishMessage);
    }

    function toggleMenu() {
        const menu = document.getElementById(menuId);
        menu.style.right = menu.style.right === '0%' ? '-35%' : '0%';
    }

    function showEventForm() {
        document.getElementById(eventFormId).style.display = 'block';
        document.getElementById(eventListId).style.display = 'none';
        document.getElementById(menuHeaderId).innerHTML = "Creating a new event";
        toggleButtonsVisibility(false);
    }

    function saveEvent(event) {
        event.preventDefault();

        const eventName = document.getElementById('eventName').value;
        const routingKey = document.getElementById('routingKey').value;
        const propertiesA = document.getElementById('propertiesA').value || defaultPropertiesA;
        const propertiesB = document.getElementById('propertiesB').value || defaultPropertiesB;
        const payload = document.getElementById('payload').value;

        let events = GM_getValue('events', []);
        let categories = GM_getValue('categories', {});

        if (!Array.isArray(events)) {
            events = [];
        }


        if (editModeActive) {

            if (eventName !== eventToEdit.eventName && events.some(event => event.eventName === eventName)) {
                alert('An event with this name already exists. Please choose a different name.');
                return;
            }

            events = events.map(ev => ev.eventName === eventToEdit.eventName ? { eventName, routingKey, propertiesA, propertiesB, payload } : ev);
            for (const category in categories) {
                categories[category] = categories[category].map(name => name === eventToEdit.eventName ? eventName : name);
            }
            editModeActive = false;
            eventToEdit = null;
        } else {

            if (events.some(event => event.eventName === eventName)) {
                alert('An event with this name already exists. Please choose a different name.');
                return;
            }
            if (eventName && routingKey && propertiesA && propertiesB && payload) {
                const newEvent = { eventName, routingKey, propertiesA, propertiesB, payload };
                events.push(newEvent);
                categories['Uncategorized'] = categories['Uncategorized'] || [];
                categories['Uncategorized'].push(eventName);
            } else {
                alert('Please fill in all fields.');
                return;
            }
        }

        GM_setValue('events', events);
        GM_setValue('categories', categories);
        document.getElementById(eventListId).innerHTML = '';
        loadEvents();
        resetEventForm();
    }

    function cancelEvent() {
        resetEventForm();
    }

    function resetEventForm() {
        document.getElementById(eventFormId).reset();
        document.getElementById(eventFormId).style.display = 'none';
        document.getElementById(eventListId).style.display = 'block';
        toggleButtonsVisibility(true);
        editModeActive = false;
        eventToEdit = null;
    }

    function loadEvents() {
        document.getElementById(eventListId).innerHTML = '';
        const events = GM_getValue('events', []);
        const categories = GM_getValue('categories', { 'Uncategorized': [] });

        for (const category in categories) {
            displayCategory(category, categories[category]);
        }
    }

    function displayCategory(categoryName, eventNames) {
        const eventList = document.getElementById(eventListId);

        const categoryDiv = document.createElement('div');
        categoryDiv.classList.add('category');
        categoryDiv.style.marginBottom = '10px';

        const categoryHeader = document.createElement('div');

        categoryHeader.draggable = true;

        categoryHeader.style = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 5px;
            background-color: #555;
            cursor: pointer;
        `;
        categoryHeader.innerHTML = `
            <input type="checkbox" class="delete-category-checkbox" style="display: none; margin-right: 10px;">
            <span>${categoryName}</span>
            <i class="fa-solid fa-chevron-down"></i>
        `;
        categoryHeader.addEventListener('click', (event) => {
              if (!event.srcElement.classList.contains('delete-category-checkbox')) {
                const eventContainer = categoryDiv.querySelector('.event-container');
                const icon = categoryHeader.querySelector('i');
                if (eventContainer.style.display === 'none') {
                    eventContainer.style.display = 'block';
                    icon.classList.replace('fa-chevron-right', 'fa-chevron-down');
                } else {
                    eventContainer.style.display = 'none';
                    icon.classList.replace('fa-chevron-down', 'fa-chevron-right');
                }
            }
        });

        categoryHeader.addEventListener('dragstart', (e) => {
            e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'category', name: categoryName }));
            e.dataTransfer.effectAllowed = 'move';
        });

        categoryHeader.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
        });

        categoryHeader.addEventListener('drop', (e)=>{
            const data = JSON.parse(e.dataTransfer.getData('text/plain'));
            if (data.type === 'event') {
                moveEventToCategory(data.name, categoryName);
            }
        });

        const eventContainer = document.createElement('div');
        eventContainer.classList.add('event-container');
        eventContainer.style.display = 'block';

        eventNames.forEach(eventName => {
            const event = GM_getValue('events', []).find(ev => ev.eventName === eventName);
            if (event) {
                const eventItem = createEventItem(event);
                eventContainer.appendChild(eventItem);
                eventContainer.appendChild(createSeparator());

                eventItem.addEventListener('dragstart', (e) => {
                  e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'event', name: eventName }));
                    e.dataTransfer.effectAllowed = 'move';
                });
            }
        });

        categoryDiv.appendChild(categoryHeader);
        categoryDiv.appendChild(eventContainer);
        eventList.appendChild(categoryDiv);

        categoryDiv.addEventListener('drop', (e) => {
            e.preventDefault();
            const data = JSON.parse(e.dataTransfer.getData('text/plain'));
            if (data.type === 'category' && data.name !== categoryName) {
                reorderCategories(data.name, categoryName);
            }
        });

        // Add event listener for category checkbox
        const categoryCheckbox = categoryHeader.querySelector('.delete-category-checkbox');
        categoryCheckbox.addEventListener('change', () => {
            const eventContainer = categoryDiv.querySelector('.event-container');
            const eventCheckboxes = eventContainer.querySelectorAll('.delete-checkbox');
            eventCheckboxes.forEach(checkbox => checkbox.checked = categoryCheckbox.checked);
        });

    }

    function createEventItem(event) {
        const eventItem = document.createElement('div');
        eventItem.style = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            height: 17px;
            padding: 2px;
            border-bottom: 1px solid #444;
            cursor: pointer;
            background-color: #444;
            color: #fff;
            transition: background-color 0.3s, transform 0.3s;
        `;
        eventItem.draggable = true;
        eventItem.addEventListener('dragstart', (e) => {
            e.dataTransfer.setData('text/plain', JSON.stringify({ type: 'event', name: event.eventName }));
            e.dataTransfer.effectAllowed = 'move';
        });

        eventItem.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
        });

        eventItem.addEventListener('drop', (e) => {
            e.preventDefault();
            const data = JSON.parse(e.dataTransfer.getData('text/plain'));
            if (data.type === 'event' && data.name !== event.eventName) {
                reorderEvents(event.eventName, data.name);
            }
        });

        eventItem.addEventListener('mouseover', () => {
            eventItem.style.backgroundColor = '#555';
            if (!deleteModeActive) {
                eventItem.querySelector('.edit-button').style.display = 'inline';
                const editButton = eventItem.querySelector('.edit-button');
                editButton.classList.add('edit-button');

            }
        });
        eventItem.addEventListener('mouseout', () => {
            eventItem.style.backgroundColor = '#444';
            eventItem.querySelector('.edit-button').style.display = 'none';
        });
        eventItem.addEventListener('mousedown', () => eventItem.style.transform = 'scale(0.98)');
        eventItem.addEventListener('mouseup', () => eventItem.style.transform = 'scale(1)');

        const eventText = document.createElement('span');
        eventText.textContent = event.eventName;
        eventText.style = `
            flex-grow: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            margin-right: 10px;
        `;
        eventText.title = event.eventName;

        eventItem.addEventListener('click', (e) => {
            if (deleteModeActive) {
                const checkbox = eventItem.querySelector('.delete-checkbox');
                checkbox.checked = !checkbox.checked;
                updateCategoryCheckbox(eventItem);
            } else if (!editModeActive) {
                populatePublishForm(event);
            }
        });

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.style.marginRight = '10px';
        checkbox.classList.add('delete-checkbox');
        checkbox.style.display = 'none';
        checkbox.addEventListener('click', (e) => {
            checkbox.checked = !checkbox.checked;
            updateCategoryCheckbox(eventItem);
        });

        const editButton = document.createElement('button');
        editButton.innerHTML = '<i class="fa-solid fa-edit"></i>';
        editButton.classList.add('edit-button');
        editButton.style.display = 'none';
        editButton.style.marginRight = '10px';
        editButton.style.fontsize = '17px';
        editButton.style.justifycontent = 'center';
        editButton.style.padding = 0;
        editButton.style.border = 'none';
        editButton.style.alignitems = 'center';
        editButton.style.background = 'transparent'; // Make the background transparent

        editButton.addEventListener('click', (e) => {
            editEvent(event);
        });

        eventItem.appendChild(checkbox);
        eventItem.appendChild(eventText);
        eventItem.appendChild(editButton);

        return eventItem;
    }

    function createSeparator() {
        const separator = document.createElement('div');
        separator.style.height = '1px';
        separator.style.backgroundColor = '#555';
        return separator;
    }

    function populatePublishForm(event) {
        const publishMessageSection = Array.from(document.querySelectorAll('h2'))
            .find(section => section.textContent.trim() === 'Publish message');

        if (publishMessageSection) {
            const parentElement = publishMessageSection.parentElement;
            setInputValue(parentElement, 'input[name="routing_key"]', event.routingKey);
            setInputValue(parentElement, 'input[name^="props_"][name$="_mfkey"]', event.propertiesA);
            setInputValue(parentElement, 'input[name^="props_"][name$="_mfvalue"]', event.propertiesB);
            setInputValue(parentElement, 'textarea[name="payload"]', event.payload);

            const featureToggles = GM_getValue('featureToggles', { openMenuOnArrival: true, autoPublishMessage: false });
            if (featureToggles.autoPublishMessage) {
                const publishButton = parentElement.querySelector('input[type="submit"]');
                if (publishButton) publishButton.click();
            }
        }
    }

    function setInputValue(parentElement, selector, value) {
        const input = parentElement.querySelector(selector);
        if (input) input.value = value;
    }

    function enableDeleteMode() {
        deleteModeActive = true;
        toggleCheckboxesVisibility(true);
        toggleButtonsVisibility(false);
        document.getElementById('confirmDeleteButton').style.display = 'block';
    }

    function confirmDelete() {
        const eventsToDelete = Array.from(document.querySelectorAll('.delete-checkbox:checked'))
            .map(checkbox => checkbox.parentElement.querySelector('span').textContent);
        const categoriesToDelete = Array.from(document.querySelectorAll('.delete-category-checkbox:checked'))
            .map(checkbox => checkbox.nextElementSibling.textContent);

        let events = GM_getValue('events', []);
        let categories = GM_getValue('categories', {});

        events = events.filter(event => !eventsToDelete.includes(event.eventName));
        for (const category in categories) {
            categories[category] = categories[category].filter(eventName => !eventsToDelete.includes(eventName));
        }

        // Delete selected categories
        categoriesToDelete.forEach(category => {
            delete categories[category];
        });

        GM_setValue('events', events);
        GM_setValue('categories', categories);

        loadEvents();

        deleteModeActive = false;
        toggleCheckboxesVisibility(false);
        toggleButtonsVisibility(true);
        document.getElementById('confirmDeleteButton').style.display = 'none';
    }

    function showSettings() {
        const settingsMenu = document.getElementById(settingsId);
        const isVisible = settingsMenu.style.display === 'block';
        settingsMenu.style.display = isVisible ? 'none' : 'block';
        document.getElementById(eventListId).style.display = isVisible ? 'block' : 'none';
        toggleButtonsVisibility(isVisible);
        document.getElementById(backButtonId).style.display = isVisible ? 'none' : 'block';
    }

    function goBack() {
        document.getElementById(settingsId).style.display = 'none';
        document.getElementById(eventListId).style.display = 'block';
        toggleButtonsVisibility(true);
        document.getElementById(backButtonId).style.display = 'none';
    }

    function toggleOpenMenuOnArrival() {
        const featureToggles = GM_getValue('featureToggles', { openMenuOnArrival: true, autoPublishMessage: false });
        featureToggles.openMenuOnArrival = document.getElementById(openMenuToggleId).checked;
        GM_setValue('featureToggles', featureToggles);
    }

    function toggleAutoPublishMessage() {
        const featureToggles = GM_getValue('featureToggles', { openMenuOnArrival: true, autoPublishMessage: false });
        featureToggles.autoPublishMessage = document.getElementById(autoPublishToggleId).checked;
        GM_setValue('featureToggles', featureToggles);
    }

    function toggleButtonsVisibility(visible) {
        const display = visible ? 'block' : 'none';
        document.getElementById('createEventButton').style.display = display;
        document.getElementById('deleteEventButton').style.display = display;
        document.getElementById('settingsButton').style.display = display;
        document.getElementById(addCategoryButtonId).style.display = display;
    }

    function toggleCheckboxesVisibility(visible) {
        const display = visible ? 'block' : 'none';
        document.querySelectorAll('.delete-checkbox').forEach(checkbox => checkbox.style.display = display);
        document.querySelectorAll('.delete-category-checkbox').forEach(checkbox => checkbox.style.display = display);
    }

    function removeElementById(id) {
        const element = document.getElementById(id);
        if (element) element.remove();
    }

    function editEvent(event) {
        editModeActive = true;
        eventToEdit = event;
        document.getElementById('eventName').value = event.eventName;
        document.getElementById('routingKey').value = event.routingKey;
        document.getElementById('propertiesA').value = event.propertiesA;
        document.getElementById('propertiesB').value = event.propertiesB;
        document.getElementById('payload').value = event.payload;
        showEventForm();
        document.getElementById(menuHeaderId).innerHTML = "Editing a saved event";

    }

    function addCategory() {
        const categoryName = prompt('Enter the name of the new category:');
        if (categoryName) {
            // Check if the category name is only numeric
            if (/^\d+$/.test(categoryName)) {
                alert('Category name cannot be only numbers. Please enter a valid name.');
                return;
            }

            let categories = GM_getValue('categories', {});
            if (categories[categoryName]) {
                alert('A category with this name already exists. Please choose a different name.');
                return;
            }
            categories[categoryName] = [];
            GM_setValue('categories', categories);
            loadEvents();
        }
    }

    function reorderEvents(targetEventName, draggedEventName) {
        console.log('reorder events hit');
        let events = GM_getValue('events', []);
        let categories = GM_getValue('categories', {});

        let draggedCategory = null;
        let targetCategory = null;
        let draggedIndex = -1;
        let targetIndex = -1;

        // Find the categories and indices of the dragged and target events
        for (const category in categories) {
            const eventNames = categories[category];
            if (draggedIndex === -1) {
                draggedIndex = eventNames.indexOf(draggedEventName);
                if (draggedIndex > -1) {
                    draggedCategory = category;
                }
            }
            if (targetIndex === -1) {
                targetIndex = eventNames.indexOf(targetEventName);
                if (targetIndex > -1) {
                    targetCategory = category;
                }
            }
            if (draggedCategory && targetCategory) {
                break;
            }
        }

        // If both events are found
        if (draggedCategory && targetCategory) {
            // Remove the dragged event from its original category
            categories[draggedCategory].splice(draggedIndex, 1);

            // Add the dragged event to the target category at the target index
            categories[targetCategory].splice(targetIndex, 0, draggedEventName);

            // Save the updated categories
            GM_setValue('categories', categories);

            // Reload events
            loadEvents();
        } else {
            console.log('One or both events not found in categories');
        }
    }

    function reorderCategories(draggedCategoryName, targetCategoryName) {
        let categories = GM_getValue('categories', {});
        const categoryNames = Object.keys(categories);
        const draggedIndex = categoryNames.indexOf(draggedCategoryName);
        const targetIndex = categoryNames.indexOf(targetCategoryName);

        if (draggedIndex > -1 && targetIndex > -1) {
            categoryNames.splice(draggedIndex, 1);
            categoryNames.splice(targetIndex, 0, draggedCategoryName);

            const newCategories = {};
            categoryNames.forEach(categoryName => {
                newCategories[categoryName] = categories[categoryName];
            });

            GM_setValue('categories', newCategories);
            document.getElementById(eventListId).innerHTML = '';
            loadEvents();
        }
    }

    function moveEventToCategory(eventName, targetCategoryName) {
        let categories = GM_getValue('categories', {});
        for (const category in categories) {
            categories[category] = categories[category].filter(name => name !== eventName);
        }
        categories[targetCategoryName].push(eventName);
        GM_setValue('categories', categories);
        loadEvents();
    }

    function updateCategoryCheckbox(eventItem) {
        const categoryDiv = eventItem.closest('.category');
        const categoryCheckbox = categoryDiv.querySelector('.delete-category-checkbox');
        const eventCheckboxes = categoryDiv.querySelectorAll('.delete-checkbox');

        if (categoryCheckbox.checked && !eventItem.checkbox){
          categoryCheckbox.checked = !categoryCheckbox.checked;
        }
        // else {
        //     const allChecked = Array.from(eventCheckboxes).every(checkbox => checkbox.checked);
        //     categoryCheckbox.checked = allChecked;
        // }
    }


    // Observe changes in the document body
    const originalPushState = history.pushState;
    history.pushState = function () {
        originalPushState.apply(this, arguments);
        executeScriptLogic();
    };

    const originalReplaceState = history.replaceState;
    history.replaceState = function () {
        originalReplaceState.apply(this, arguments);
        executeScriptLogic();
    };

    window.addEventListener('popstate', executeScriptLogic);

})();