erai-raws preview Image

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         erai-raws preview Image
// @namespace    http://tampermonkey.net/
// @version      2.5
// @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/release-schedule-v2/
// @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';

    // Method 1: Block images with the .preview-image class using CSS
    const style = document.createElement('style');
    style.textContent = `
        .preview-image {
            display: none !important;
        }
    `;
    document.head.appendChild(style);

    // 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! I've created a new script. Take a look ☝️, and if you like it, feel free to install it. Thank you so much for all your support!  ⚠️ Also, preview image has now been officially added to the site. You can use the official one, but if you prefer this one , you can still use the current one. However, doing so will disable the native preview image on the site.";
        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_0";

        // 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 tableRow = this.closest('tr');
               const previewImageUrl = tableRow ? tableRow.getAttribute('data-preview-image') : null;

               if (previewImageUrl) {
                    const cachedImage = localStorage.getItem(storagePrefix + previewImageUrl);
                   if (cachedImage) {
                       displayCachedImage(cachedImage,previewImageUrl);
                    } else {
                        showLoadingIndicator();
                        fetchAndDisplayImage(previewImageUrl, localStorageKey, true);
                   }

               } else {
                   const cachedImage = localStorage.getItem(localStorageKey);

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

               function displayCachedImage(imageSrc,url) {
                   let cachedImageElement = new Image();
                   cachedImageElement.onload = function() {
                       previewContainer.innerHTML = `<img style="width: 100%; height: 100%;" src="${imageSrc}"/>`;
                       showPreviewContainer();
                   };
                   cachedImageElement.onerror = function() {
                       localStorage.removeItem(storagePrefix+url);
                       showLoadingIndicator();
                       fetchAndDisplayImage(url, localStorageKey, previewImageUrl);
                   };
                   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, useTableRowImage) {
                    if(useTableRowImage){
                        let image = new Image();
                        image.onload = function() {
                            try {
                                localStorage.setItem(storagePrefix+url, url);
                            } catch (e) {
                                console.warn("LocalStorage is full or unavailable. Image caching failed.", e);
                            }
                            previewContainer.innerHTML = `<img style="width: 100%; height: 100%;" src="${url}"/>`;
                             showPreviewContainer();
                         };
                        image.onerror = function() {
                             previewContainer.textContent = "Failed to load image.";
                        };
                       image.src = url;
                    } else {
                        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) {
                                    // Use data-src if present, fallback to src
                                    const imageUrl = preview.getAttribute('data-src') || preview.src;

                                    let image = new Image();
                                    image.onload = function() {
                                        try {
                                            localStorage.setItem(localStorageKey, imageUrl);
                                        } catch (e) {
                                            console.warn("LocalStorage is full or unavailable. Image caching failed.", e);
                                        }
                                        previewContainer.innerHTML = `<img style="width: 100%; height: 100%;" src="${imageUrl}"/>`;
                                        showPreviewContainer();
                                    };
                                    image.onerror = function() {
                                        previewContainer.textContent = "Failed to load image.";
                                    };
                                    image.src = imageUrl;
                                } 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);
})();