うんはらバスターZ(モザイク版)

おんjで画像にモザイクをかけ、解除ボタンを表示!スレ移動でもモザイクが適用されるように改善!

// ==UserScript==
// @name         うんはらバスターZ(モザイク版)
// @namespace    うんはらバスターZ (モザイク版)
// @version      1.0
// @description  おんjで画像にモザイクをかけ、解除ボタンを表示!スレ移動でもモザイクが適用されるように改善!
// @author       Wai
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
(function() {
    'use strict';

    // 画像にモザイクをかける関数
    function applyMosaic(img) {
        img.style.filter = "blur(10px)"; // モザイク効果
    }

    // モザイクを解除する関数
    function removeMosaic(img) {
        img.style.filter = "none"; // モザイク解除
    }

    // 画像の拡張子を取得する関数
    function getFileExtension(url) {
        const match = url.match(/\.([0-9a-z]+)(?=[?#])|(\.[0-9a-z]+)$/i);
        return match ? match[1] || match[2].substring(1) : null;
    }

    // 新しい画像をチェックしてモザイクを適用する関数
    function checkImages(targetNode = document) {
        targetNode.querySelectorAll('img').forEach(img => {
            if (img.src.includes("imgur.com") && !img.dataset.checked) { // imgur画像で未処理のもの
                img.dataset.checked = "true"; // 重複処理防止
                applyMosaic(img); // 画像にモザイクをかける

                // 画像クリックで遷移を防ぐ
                img.addEventListener('click', function(event) {
                    event.preventDefault();
                });

                // モザイク解除ボタンを作成
                let btn = document.createElement("button");
                btn.textContent = "モザイク解除";
                btn.style.margin = "5px";
                btn.style.fontSize = "1.2em"; // ボタンの文字サイズを大きくする
                btn.style.padding = "5px 10px"; // ボタンのパディングを追加
                btn.onclick = function(event) {
                    event.preventDefault(); // ボタンの遷移を防止
                    removeMosaic(img); // モザイク解除
                    btn.remove(); // ボタン削除
                    // モザイクボタンを再度追加
                    let mosaicBtn = document.createElement("button");
                    mosaicBtn.textContent = "モザイク";
                    mosaicBtn.style.margin = "5px";
                    mosaicBtn.style.fontSize = "1.2em"; // ボタンの文字サイズを大きくする
                    mosaicBtn.style.padding = "5px 10px"; // ボタンのパディングを追加
                    mosaicBtn.onclick = function(event) {
                        event.preventDefault(); // ボタンの遷移を防止
                        applyMosaic(img); // モザイクをかける
                        mosaicBtn.remove(); // ボタン削除
                        img.insertAdjacentElement("beforebegin", btn); // モザイク解除ボタンを再度追加
                    };
                    img.insertAdjacentElement("beforebegin", mosaicBtn);
                };
                img.insertAdjacentElement("beforebegin", btn);

                // 拡張子を表示
                const ext = getFileExtension(img.src);
                const extDisplay = document.createElement("span"); // spanに変更
                extDisplay.style.color = "red";
                extDisplay.style.fontSize = "1.2em"; // 拡張子表示を大きくする
                extDisplay.style.marginLeft = "5px"; // ボタンとの間隔を調整
                extDisplay.textContent = ext ? (ext.toUpperCase() === "GIF" ? "⚠️GIF" : ext) : "拡張子不明";
                img.insertAdjacentElement("afterend", extDisplay); // 拡張子を画像の後に表示

                // 画像URLを青文字で表示
                const urlDisplay = document.createElement("div");
                urlDisplay.style.color = "blue";
                urlDisplay.style.wordWrap = "break-word"; // 長いURLを折り返す
                urlDisplay.textContent = img.src;
                img.insertAdjacentElement("afterend", urlDisplay);
            }
        });
    }

    // 初回実行
    checkImages();

    // 常にページを監視して、新たに追加された画像にモザイクを適用
    let observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === 1) { // ノードが要素ノードであるか確認
                    if (node.tagName === "IMG") {
                        checkImages(node); // 新しく追加された画像をチェック
                    } else if (node.querySelectorAll) {
                        checkImages(node); // 新しく追加されたコンテンツ内に画像が含まれていればチェック
                    }
                }
            });
        });
    });

    // 全体のDOMを監視
    observer.observe(document.body, { childList: true, subtree: true });

    // 履歴操作を監視してスレ移動時にも対応
    window.addEventListener('popstate', () => {
        checkImages(); // スレ移動後に再チェック
    });

    // pushState / replaceState を監視してスレ移動時にも対応
    let originalPushState = history.pushState;
    let originalReplaceState = history.replaceState;

    function hookHistoryMethod(original) {
        return function() {
            let result = original.apply(this, arguments);
            checkImages(); // スレ移動後に再チェック
            return result;
        };
    }

    history.pushState = hookHistoryMethod(originalPushState);
    history.replaceState = hookHistoryMethod(originalReplaceState);

})();