erai-raws preview Image

Show image preview next to the anime titles by hovering the mouse.

// ==UserScript==
// @name         erai-raws preview Image
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Show image preview next to the anime titles by hovering the mouse.
// @author       dr.bobo0
// @match        https://www.erai-raws.info/
// @match        https://www.erai-raws.info/anime-list/
// @match        https://www.erai-raws.info/latest-releases/*
// @match        https://www.erai-raws.info/subtitles/*
// @match        https://www.erai-raws.info/batches/*
// @match        https://www.erai-raws.info/specials/*
// @match        https://www.erai-raws.info/encodes/*
// @match        https://www.erai-raws.info/release-schedule/
// @match        https://www.erai-raws.info/my-anime-list/
// @match        https://www.erai-raws.info/episodes/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAMFBMVEVHcEy3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISG3ISFbcynrAAAAD3RSTlMA1OaywnKHOhgNXPQmnEsN6GgJAAACVUlEQVRYhe1WSYKEIAyUfZf//3YgLAbEbpzrTJ1EKbJVIsfxj78Ck/BLqj4lI9TF6Ci3r8mW0YjBX7EliTeIbfo5sAkPQQaWHjbp6qKTYH07NEa6x5eNzawuB6YzfHIghi1+qBmztXI21UBleqRbtVRAD7qtxRXOnhZ49h1tbXS6K4NUejmtc0DnJv04UgU8XvvAgtVPuxcQ2dvfKh/AS/2F8otjjLJCJlh//9bRdZBbiBLCawBKcoc+fdI1OiJD5sbit854ELa2gbpx57lgx7UwtRh70FHCGcdNTa3y2mh/ZsHOiTiRIcrkCVkUzZ3i1xW5mgRz2G7IsavwtTNc8JAYhghkWPUWdkHhY8EssTWxw0wiOI0t72RWfIqKqWPF97gONfiF4o2ATIkbP2u+vyh8oo4nnPe6B5TR4v8HZak7HzhV6xqK9KHhPGwYXjFsUqxl6V0NUedKOGxA0aGkKQPu3nhnc9HAbiQ6mK9YEmQV/xUjCOTKb5mvQyPMMyhb5d0GPLXyatHkivXCsAFAmcQQF9gjUsB/toudmWk7zqGpVqAu02So9Mlhk3cLaD3je+uz7kpgmM3FveD13+HQIKEQta0CpuUzYUKtx+3saJ13IMD8P8sldUtmw/A7b1cRUGgEh8dKLuFF4GkApznUX9GLlrMgn6hP4Kjcdq32j4BZ1qxCNO/4YpQ7/Z6EEaUA1/ptElABLofeJIHMdVPvkgAJHFrc4J78CjUmsPvE1tvvoPF+I5OlHbauKmol3JLWuHWAnxNQ3jJKw+ZdybLXN/t//HX8ACejMFAROu21AAAAAElFTkSuQmCC
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Part 1: Notification Popup

    function showPopup() {
        // Create the dark overlay
        const overlay = document.createElement('div');
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
        overlay.style.zIndex = '9998';
        overlay.style.backdropFilter = 'blur(5px)';

        // Create the pop-up container
        const popup = document.createElement('div');
        popup.style.position = 'fixed';
        popup.style.top = '50%';
        popup.style.left = '50%';
        popup.style.transform = 'translate(-50%, -50%)';
        popup.style.width = '600px';
        popup.style.padding = '30px';
        popup.style.backgroundColor = '#1a1a1a';
        popup.style.color = '#ffffff';
        popup.style.border = '1px solid #333';
        popup.style.borderRadius = '15px';
        popup.style.boxShadow = '0 0 30px rgba(0,0,0,0.7)';
        popup.style.zIndex = '9999';
        popup.style.textAlign = 'center';

        // Add an image to the pop-up
        const img = document.createElement('img');
        img.src = 'https://i.imgur.com/ItdkLgt.png';
        img.style.width = '100%';
        img.style.height = 'auto';
        img.style.marginBottom = '30px';
        img.style.borderRadius = '10px';
        popup.appendChild(img);

        // Add the anime title and message
        const title = document.createElement('p');
        title.textContent = 'Enhance Erai-raws with Image Previews!';
        title.style.marginBottom = '15px';
        title.style.fontSize = '28px';
        title.style.fontWeight = 'bold';
        title.style.color = '#ff9900';
        popup.appendChild(title);

        const message = document.createElement('p');
        message.textContent = "Hey! There's a new script that I made. Take a look, and if you like it, you can install it. Thank you for all your support!";
        message.style.marginBottom = '25px';
        message.style.fontSize = '18px';
        message.style.lineHeight = '1.6';
        message.style.color = '#cccccc';
        popup.appendChild(message);

        // Add a link to the new script
        const link = document.createElement('a');
        link.href = 'https://greasyfork.org/en/scripts/506193-erai-raws-image-previews-next-to-anime-titles';
        link.textContent = 'Install New Script';
        link.style.display = 'inline-block';
        link.style.color = '#4CAF50';
        link.style.textDecoration = 'none';
        link.style.marginBottom = '25px';
        link.style.fontSize = '20px';
        link.style.fontWeight = 'bold';
        link.style.padding = '10px 20px';
        link.style.border = '2px solid #4CAF50';
        link.style.borderRadius = '5px';
        link.style.transition = 'all 0.3s ease';
        link.addEventListener('mouseover', function() {
            this.style.backgroundColor = '#4CAF50';
            this.style.color = '#ffffff';
        });
        link.addEventListener('mouseout', function() {
            this.style.backgroundColor = 'transparent';
            this.style.color = '#4CAF50';
        });
        popup.appendChild(link);

        // Add a checkbox to never show the popup again
        const checkboxLabel = document.createElement('label');
        checkboxLabel.style.display = 'block';
        checkboxLabel.style.marginBottom = '20px';
        checkboxLabel.style.fontSize = '16px';
        checkboxLabel.style.cursor = 'pointer';
        checkboxLabel.style.color = '#999999';
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.style.marginRight = '10px';
        checkbox.style.transform = 'scale(1.5)';
        checkboxLabel.appendChild(checkbox);
        checkboxLabel.appendChild(document.createTextNode('Never show this again'));
        popup.appendChild(checkboxLabel);

        // Add a dismiss button
        const dismissButton = document.createElement('button');
        dismissButton.textContent = 'Dismiss';
        dismissButton.style.padding = '12px 24px';
        dismissButton.style.backgroundColor = '#333333';
        dismissButton.style.color = '#ffffff';
        dismissButton.style.border = 'none';
        dismissButton.style.borderRadius = '5px';
        dismissButton.style.cursor = 'pointer';
        dismissButton.style.fontSize = '18px';
        dismissButton.style.transition = 'all 0.3s ease';
        dismissButton.addEventListener('mouseover', function() {
            this.style.backgroundColor = '#555555';
        });
        dismissButton.addEventListener('mouseout', function() {
            this.style.backgroundColor = '#333333';
        });
        dismissButton.addEventListener('click', function() {
            if (checkbox.checked) {
                localStorage.setItem('popupNeverShow', 'true');
                notificationButton.remove();
            } else {
                // Change the notification button background color to gray
                notificationButton.style.backgroundColor = '#333333';
                localStorage.setItem('popupButtonColor', '#333333');
            }
            overlay.remove();
            popup.remove();
        });
        popup.appendChild(dismissButton);

        // Append the overlay and pop-up to the body
        document.body.appendChild(overlay);
        document.body.appendChild(popup);
    }

    // Create the notification button
    const notificationButton = document.createElement('button');
    notificationButton.textContent = '🔔 New!';
    notificationButton.style.position = 'fixed';
    notificationButton.style.bottom = '20px';
    notificationButton.style.right = '20px';
    notificationButton.style.padding = '10px 20px';
    notificationButton.style.backgroundColor = localStorage.getItem('popupButtonColor') || '#ff9900';
    notificationButton.style.color = '#ffffff';
    notificationButton.style.border = 'none';
    notificationButton.style.borderRadius = '5px';
    notificationButton.style.cursor = 'pointer';
    notificationButton.style.fontSize = '16px';
    notificationButton.style.fontWeight = 'bold';
    notificationButton.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
    notificationButton.style.zIndex = '9997';

    // Add click event to the notification button
    notificationButton.addEventListener('click', showPopup);

    // Function to initialize the script
    function init() {
        if (!localStorage.getItem('popupNeverShow')) {
            document.body.appendChild(notificationButton);
        }

        // Part 2: Image Preview

        const storagePrefix = "erai_raws_image_cache_";

        // Function to clear old localStorage keys
        function clearOldCache() {
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                if (key && !key.startsWith(storagePrefix)) {
                    localStorage.removeItem(key);
                }
            }
        }

        // Clear old cache on script initialization
        clearOldCache();

        document.querySelectorAll("th > a.aa_ss_ops_new").forEach(link => {
            link.addEventListener("mouseover", function(event) {
                let previewContainer = document.createElement("div");
                previewContainer.style.position = "fixed";
                previewContainer.style.display = "none";
                previewContainer.style.transition = "opacity 0.1s ease-in-out";
                previewContainer.style.opacity = 0;
                previewContainer.style.width = "216px";
                previewContainer.style.height = "307px";
                previewContainer.style.overflow = "hidden";
                document.body.appendChild(previewContainer);

                const url = this.href;
                const localStorageKey = storagePrefix + url;

                const cachedImage = localStorage.getItem(localStorageKey);

                if (cachedImage) {
                    displayCachedImage(cachedImage);
                } else {
                    showLoadingIndicator();
                    fetchAndDisplayImage(url, localStorageKey);
                }

                function displayCachedImage(imageSrc) {
                    let cachedImageElement = new Image();
                    cachedImageElement.onload = function() {
                        previewContainer.innerHTML = `<img style="width: 100%; height: 100%;" src="${imageSrc}"/>`;
                        showPreviewContainer();
                    };
                    cachedImageElement.onerror = function() {
                        localStorage.removeItem(localStorageKey);
                        showLoadingIndicator();
                        fetchAndDisplayImage(url, localStorageKey);
                    };
                    cachedImageElement.src = imageSrc;
                }

                function showLoadingIndicator() {
                    previewContainer.innerHTML = `
                        <div style="display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; background-color: #f0f0f0;">
                            <div style="width: 40px; height: 40px; border: 4px solid #333; border-top: 4px solid #999; border-radius: 50%; animation: spin 1s linear infinite;"></div>
                        </div>
                        <style>
                            @keyframes spin {
                                0% { transform: rotate(0deg); }
                                100% { transform: rotate(360deg); }
                            }
                        </style>
                    `;
                    showPreviewContainer();
                }

                function showPreviewContainer() {
                    previewContainer.style.display = "block";
                    setTimeout(function() {
                        previewContainer.style.opacity = 1;
                    }, 0);
                }

                function fetchAndDisplayImage(url, localStorageKey) {
                    var xhr = new XMLHttpRequest();
                    xhr.open("GET", url);
                    xhr.responseType = "document";

                    xhr.onload = function() {
                        if (xhr.response) {
                            let preview = xhr.response.querySelector(".entry-content-poster img");
                            if (preview) {
                                let image = new Image();
                                image.onload = function() {
                                    try {
                                        localStorage.setItem(localStorageKey, preview.src);
                                    } catch (e) {
                                        console.warn("LocalStorage is full or unavailable. Image caching failed.", e);
                                    }
                                    previewContainer.innerHTML = `<img style="width: 100%; height: 100%;" src="${preview.src}"/>`;
                                    showPreviewContainer();
                                };
                                image.onerror = function() {
                                    previewContainer.textContent = "Failed to load image.";
                                };
                                image.src = preview.src;
                            } else {
                                previewContainer.textContent = "Image not found.";
                            }
                        } else {
                            previewContainer.textContent = "Failed to load image.";
                        }
                    };

                    xhr.onerror = function() {
                        previewContainer.textContent = "Failed to load image.";
                    };

                    xhr.send();
                }

                document.addEventListener("mousemove", function(event) {
                    previewContainer.style.top = event.clientY + 20 + "px";
                    previewContainer.style.left = event.clientX + 20 + "px";

                    if (previewContainer.getBoundingClientRect().right > window.innerWidth) {
                        previewContainer.style.left = (window.innerWidth - previewContainer.offsetWidth - 20) + "px";
                    }
                    if (previewContainer.getBoundingClientRect().bottom > window.innerHeight) {
                        previewContainer.style.top = (window.innerHeight - previewContainer.offsetHeight - 20) + "px";
                    }
                });

                link.addEventListener("mouseout", function() {
                    previewContainer.style.opacity = 0;
                    document.removeEventListener("mousemove", function(event) {});
                    setTimeout(function() {
                        previewContainer.style.display = "none";
                        previewContainer.remove();
                    }, 300);
                });
            });
        });
    }

    // Run the initialization function when the page loads
    window.addEventListener('load', init);
})();