Manga Enhancer

Enhances your manga reading experience with a set of features:

// ==UserScript==
// @name          Manga Enhancer
// @namespace     Moose
// @version       3.0
// @description   Enhances your manga reading experience with a set of features:
//                  1. Navigate between chapters using arrow keys. Also works by 1+ -1 on chapters URL to find new chapters
//                  2. Close tabs with a button press. Default "/" can be changed on line #101
//                  3. Remove gaps between pages.
//                  4. Auto-reload on image loading errors. Also tells the user that some images are missing. As an added note of this function, it continues to show till all images are loaded and then removes itself
//                  5. Image size as a percentage of screen width, also save preferences. Found at the top right of the screen
//
// If the script doesn't work on the site, add the site URL as shown below.
// @match         https://manganelo.com/*
// @match         https://mangakakalot.com/*
// @match         https://readmanganato.com/*
// @match         https://manganato.com/*
// @match         https://chapmanganato.com/*
// @match         https://chapmanganato.to/*
//
// @icon        https://manganato.com/favicon-96x96.png
// @author        Moose, ChatGPT
// @description   12/26/2023
// @license MIT
// ==/UserScript==

// Function to create a div for displaying error messages
function createErrorDisplayDiv() {
    const errorDisplayDiv = document.createElement('div');
    errorDisplayDiv.style.position = 'fixed';
    errorDisplayDiv.style.top = '50%';
    errorDisplayDiv.style.left = '10px';
    errorDisplayDiv.style.padding = '10px';
    errorDisplayDiv.style.background = 'white';
    errorDisplayDiv.style.border = '2px solid black';
    errorDisplayDiv.style.borderRadius = '5px';
    errorDisplayDiv.style.display = 'none'; // Initially hidden

    // Create close button (X)
    const closeButton = document.createElement('span');
    closeButton.textContent = 'X';
    closeButton.style.float = 'right';
    closeButton.style.cursor = 'pointer';
    closeButton.addEventListener('click', () => {
        errorDisplayDiv.style.display = 'none';
    });

    // Append close button to the error display div
    errorDisplayDiv.appendChild(closeButton);

    document.body.appendChild(errorDisplayDiv);
    return errorDisplayDiv;
}

// Function to display an error prompt with the number of images with loading errors
function displayErrorPrompt() {
    let reloadPromptShown = false;

    const updateMessage = () => {
        const errorImages = document.querySelectorAll('img:not(.loaded)');
        const totalImages = errorImages.length;

        if (totalImages > 0) {
            // If there are images with loading errors, update the error message
            const newMessage = `Error loading ${totalImages} image(s). Do you want to reload the page?`;
            errorDisplayDiv.textContent = newMessage;
            errorDisplayDiv.style.display = 'block';
        } else {
            // If no error is found, hide the error message
            closeErrorMessage();
            clearInterval(updateInterval);
        }
    };

    // Check image load status periodically and update the message
    const updateInterval = setInterval(updateMessage, checkInterval);
}

// Main script block
(function () {
    'use strict';

    // Create a div for displaying error messages globally
    const errorDisplayDiv = createErrorDisplayDiv();

    // Counter for image loading attempts
    let attempts = 0;
    const maxAttempts = 3; // Adjust the maximum number of attempts as needed
    const checkInterval = 5000; // Adjust the interval as needed

    // Function to close the error message
    function closeErrorMessage() {
        errorDisplayDiv.style.display = 'none';
    }

// Function to update the error message box based on the number of images with loading errors
function updateMessage() {
    attempts++;

    const allImages = document.querySelectorAll('img');
    const errorImages = Array.from(allImages).filter(img => !img.classList.contains('loaded') && (!img.complete || img.naturalWidth === 0));
    const totalImages = errorImages.length;

    if (totalImages > 0) {
        // If there are images with loading errors, update the error message
        const newMessage = `Error loading ${totalImages} image(s). Do you want to reload the page?`;
        errorDisplayDiv.textContent = newMessage;
        errorDisplayDiv.style.display = 'block';
    } else {
        // If no error is found, hide the error message
        closeErrorMessage();
        clearInterval(updateInterval);
    }
}

    // Check image load status periodically and update the message
    const updateInterval = setInterval(updateMessage, checkInterval);

    // Function to check if all images are loaded
    function checkAllImagesLoaded() {
        const allImages = document.querySelectorAll('img');
        const unloadedImages = Array.from(allImages).filter(img => !img.classList.contains('loaded'));

        if (unloadedImages.length === 0) {
            // If all images are loaded, hide the error message
            closeErrorMessage();
            clearInterval(updateInterval);
        }
    }

    // Check if all images are loaded periodically
    const checkAllImagesInterval = setInterval(checkAllImagesLoaded, 1000); // Check every second

    // Check if the current URL contains "chapter-"
    const isChapterUrl = window.location.href.includes('chapter-');

    // If the current URL contains "chapter-", enhance the reading experience
    if (isChapterUrl) {
        // Add event listener for arrow key presses
        window.addEventListener('keydown', function (event) {
            if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
                const isLeftArrow = event.key === 'ArrowLeft';

                const currentUrl = window.location.href;
                const match = currentUrl.match(/:\/\/([^/]+)\/([^/]+)\/chapter-(\d+)/);

                if (match) {
                    const domain = match[1];
                    const path = match[2];
                    const currentChapter = parseInt(match[3], 10);

                    // Increment or decrement the current chapter based on the arrow key
                    const newChapter = isLeftArrow ? currentChapter - 1 : currentChapter + 1;

                    // Ensure the new chapter is non-negative
                    if (newChapter >= 0) {
                        const newUrl = currentUrl.replace(`://${domain}/${path}/chapter-${currentChapter}`, `://${domain}/${path}/chapter-${newChapter}`);
                        window.location.href = newUrl;
                    }
                }
                event.preventDefault(); // Prevent the default behavior of arrow keys (e.g., scrolling)
            }
        });

        // Add event listener for the forward slash key press
        window.addEventListener('keydown', function (event) {
            // Forward slash key press
            if (event.key === '/' && event.code === 'Slash') {
                // Close the current tab
                window.close();
            }
        });

        // Remove space between pages
        const targetDivs = document.querySelectorAll('div[style*="max-width: 620px; max-height: 310px; margin: 10px auto; overflow: hidden; display: block;"]');
        targetDivs.forEach(targetDiv => {
            // Change the margin property to "0" instead of "10px auto"
            targetDiv.style.margin = '0';
        });

        // Size Changer Section
        let isResizeEnabled = localStorage.getItem('isResizeEnabled') === 'true';

        // Function to set and save the image size as a percentage of screen width
        function setImageSize(percentage) {
            // Check if the current URL contains "chapter-#"
            if (window.location.href.includes('chapter-')) {
                // Set the image size for all images in your application logic
                const images = document.querySelectorAll('img');
                images.forEach(image => {
                    image.style.width = isResizeEnabled ? percentage + '%' : ''; // Always set the size, even if resizing is enabled
                });

                // Save the user's preference in local storage
                localStorage.setItem('userImageSizePercentage', percentage);
            }
        }

        // Function to get the saved image size percentage
        function getSavedImageSizePercentage() {
            // Retrieve the user's saved preference from local storage
            const savedPercentage = localStorage.getItem('userImageSizePercentage');

            // Return the saved percentage or a default value if not found
            return savedPercentage ? parseFloat(savedPercentage) : 50; // Default percentage: 50%
        }

        // Function to toggle image resizing
        function toggleResize() {
            isResizeEnabled = !isResizeEnabled;
            const buttonText = isResizeEnabled ? 'Toggle Resize Enabled' : 'Toggle Resize Disabled';

            // Change the text on the "Toggle Resize" button
            toggleResizeButton.textContent = buttonText;

            // Save the current state of "Toggle Resize" in local storage
            localStorage.setItem('isResizeEnabled', isResizeEnabled);

            // Adjust the image size based on the current state of "Toggle Resize"
            setImageSize(getSavedImageSizePercentage());
        }

        // Function to handle button click and prompt the user for a new size percentage
        function handleButtonClick() {
            // Ask the user for a new image size percentage
            const newPercentage = prompt('Enter the new image size as a percentage of screen width:', getSavedImageSizePercentage());

            // Validate and set the new size percentage
            if (newPercentage !== null) {
                const validatedPercentage = parseFloat(newPercentage);
                if (!isNaN(validatedPercentage) && validatedPercentage > 0 && validatedPercentage <= 100) {
                    setImageSize(validatedPercentage);
                } else {
                    alert('Invalid input. Please enter a percentage between 1 and 100.');
                }
            }
        }

        // Function to create a div for displaying error messages
        function createErrorDisplayDiv() {
            const errorDisplayDiv = document.createElement('div');
            errorDisplayDiv.style.position = 'fixed';
            errorDisplayDiv.style.top = '50%';
            errorDisplayDiv.style.left = '10px';
            errorDisplayDiv.style.padding = '10px';
            errorDisplayDiv.style.background = 'white';
            errorDisplayDiv.style.border = '2px solid black';
            errorDisplayDiv.style.borderRadius = '5px';
            errorDisplayDiv.style.display = 'none'; // Initially hidden

            // Create close button (X)
            const closeButton = document.createElement('span');
            closeButton.textContent = 'X';
            closeButton.style.float = 'right';
            closeButton.style.cursor = 'pointer';
            closeButton.addEventListener('click', () => {
                errorDisplayDiv.style.display = 'none';
            });

            // Append close button to the error display div
            errorDisplayDiv.appendChild(closeButton);

            document.body.appendChild(errorDisplayDiv);
            return errorDisplayDiv;
        }

        // Create a button to trigger image size adjustment
        const resizeButton = document.createElement('button');
        resizeButton.textContent = 'Adjust Image Size';
        resizeButton.style.position = 'absolute';
        resizeButton.style.top = '10px';
        resizeButton.style.right = '10px';
        resizeButton.addEventListener('click', function () {
            handleButtonClick();
            setImageSize(getSavedImageSizePercentage()); // Adjust image size when resizing is toggled
        });

        // Append the button to the body
        document.body.appendChild(resizeButton);

        // Create a button to toggle image resizing
        const toggleResizeButton = document.createElement('button');
        toggleResizeButton.textContent = isResizeEnabled ? 'Toggle Resize Enabled' : 'Toggle Resize Disabled';
        toggleResizeButton.style.position = 'absolute';
        toggleResizeButton.style.top = '50px';
        toggleResizeButton.style.right = '10px';
        toggleResizeButton.addEventListener('click', toggleResize);

        // Append the button to the body
        document.body.appendChild(toggleResizeButton);

        // Initialize the image size percentage and "Toggle Resize" state on script load
        isResizeEnabled = localStorage.getItem('isResizeEnabled') === 'true';
        const buttonText = isResizeEnabled ? 'Toggle Resize Enabled' : 'Toggle Resize Disabled';
        toggleResizeButton.textContent = buttonText;
        setImageSize(getSavedImageSizePercentage());
    }
})();