<?xml version="1.0" encoding="UTF-8" ?><rdf:RDF 
  xmlns="http://purl.org/rss/1.0/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xml:lang="ja">
  <channel rdf:about="http://w.atwiki.jp/superopenj/">
    <title>superopenj @ ウィキ</title>
    <link>http://w.atwiki.jp/superopenj/</link>
    <atom:link href="https://w.atwiki.jp/superopenj/rss10.xml" rel="self" type="application/rss+xml" />
    <atom:link rel="hub" href="https://pubsubhubbub.appspot.com" />
    <description>superopenj @ ウィキ</description>

    <dc:language>ja</dc:language>
    <dc:date>2026-05-13T01:44:48+09:00</dc:date>
    <utime>1778604288</utime>

    <items>
      <rdf:Seq>
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/1.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/13.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/12.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/11.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/2.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/10.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/9.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/8.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/7.html" />
                <rdf:li rdf:resource="https://w.atwiki.jp/superopenj/pages/6.html" />
              </rdf:Seq>
    </items>
	
		
    
  </channel>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/1.html">
    <title>トップページ</title>
    <link>https://w.atwiki.jp/superopenj/pages/1.html</link>
    <description>
      **superopenj
自分用

[[スクリプト]]
[[リンク]]
[[レベル上げ&gt;https://w.atwiki.jp/superopenj/pages/13.html]]




#region(AtWiki)
**@wikiへようこそ
-ウィキはみんなで気軽にホームページ編集できるツールです。
-このページは自由に編集することができます。
-メールで送られてきたパスワードを用いてログインすることで、各種変更（サイト名、トップページ、メンバー管理、サイドページ、デザイン、ページ管理、等）することができます

**まずはこちらをご覧ください。
-[[@wikiの基本操作&gt;&gt;http://www1.atwiki.jp/guide/pages/11.html]]
-[[編集モード・構文一覧表&gt;&gt;http://www1.atwiki.jp/guide/pages/137.html]]
-[[@wikiの設定・管理&gt;&gt;http://www1.atwiki.jp/guide/pages/36.html]]

**分からないことは？
-[[@wiki ご利用ガイド&gt;&gt;http://atwiki.jp/guide/]]
-[[よくある質問&gt;&gt;http://www1.atwiki.jp/guide/pages/21.html]]
-[[@wiki更新情報&gt;&gt;http://www1.atwiki.jp/guide/pages/143.html]]
-[[@wikiへのお問合せフォーム&gt;&gt;https://atwiki.zendesk.com/hc/ja]]
等をご活用ください

**[[アットウィキモードでの編集方法&gt;&gt;http://www1.atwiki.jp/guide/pages/226.html#id_6861418f]]
-[[文字入力&gt;&gt;http://www1.atwiki.jp/guide/pages/228.html]]
-[[画像入力&gt;&gt;http://www1.atwiki.jp/guide/pages/230.html]]
-[[表組み&gt;&gt;http://www1.atwiki.jp/guide/pages/914.html]]

**[[ワープロモードでの編集方法&gt;&gt;http://www1.atwiki.jp/guide/pages/226.html#id_a14c0938]]
-[[文字入力&gt;&gt;http://www1.atwiki.jp/guide/pages/2644.html]]
-[[画像入力&gt;&gt;http://www1.atwiki.jp/guide/pages/2645.html]]
-[[表組み&gt;&gt;http://www1.atwiki.jp/guide/pages/2646.html]]

**その他にもいろいろな機能満載！！
-[[@wikiプラグイン一覧&gt;&gt;http://www1.atwiki.jp/guide/pages/264.html]]
-[[@wikiかんたんプラグイン入力サポート&gt;&gt;http://www1.atwiki.jp/guide/pages/648.html]]

**他のWIKIが気になる方はこちら
-[[VPVP wiki&gt;&gt;https://w.atwiki.jp/vpvpwiki/]]
-[[エルプリ攻略wiki&gt;&gt;https://w.atwiki.jp/erupri/]]
-[[ULTIMATE MARVEL VS. CAPCOM 3 @ wiki&gt;&gt;https://w.atwiki.jp/umvsc3/]]
-[[AcesHigh Wiki Plus!&gt;&gt;https://w.atwiki.jp/aceshigh/]]
-[[REBELLION    Z-LIMITED&gt;&gt;https://w.atwiki.jp/z-limited/]]

**バグ・不具合を見つけたら？ 要望がある場合は？
お手数ですが、[[お問合せフォーム&gt;&gt;https://atwiki.zendesk.com/hc/ja]]からご連絡ください。
#endregion    </description>
    <dc:date>2026-05-13T01:44:48+09:00</dc:date>
    <utime>1778604288</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/13.html">
    <title>忍法レベル上げTips</title>
    <link>https://w.atwiki.jp/superopenj/pages/13.html</link>
    <description>
      ***おんJで忍法帖レベルを効率的に上げる方法四選！
（仮書き）

メリットデメリットと共に有用な順に紹介します

↓


1.実況スレに参加する
一番実用的。
毎週各曜日にテレビ番組の実況スレがよく立つのでそのスレに参加して
リアクションを書き込むというもの

・代表例
野球
スポーツ系
金ロー、映画特番
水ダウ
日朝アニメ
大河ドラマ
etc...

メリット：手軽、一体感を感じられる、面白い、掲示板の醍醐味を味わえる
デメリット：時間帯が合わないと難しい、テレビ番組に興味がないと楽しめない



2.オセロ将棋機能スレ
おんｊのボードゲームコマンドを利用する
短期間で数百レスほど稼げる&amp;遊べるためもっとも効率的だが、
幾つかのデメリットが存在する

メリット：ボドゲ好きなら楽しめる、連投で吸われにくい、大量レスできる
デメリット：忍法レベルが必要、ルールや戦略を覚える必要がある、対戦相手が必要になる、途中抜けしづらい


3.レベル上げスレ
おんｊ板や忍者の里に立っているレベル上げ用スレに書き込む。
連投対策の方法としては一気に書き込みすぎない、3回書いたら文を変えるなど
ただし作業感が強いのが欠点

メリット：とにかく手軽、脳死で書き込める、新人規制を回避できる、ROM専でもok
デメリット：作業感がある、書きすぎると規制される


4.おんjヘビーユーザー
単純に一日中おんjに張り付くというもの
朝昼晩、昼休みや外出時の空いた時間にひたすらスレに書き込み続ける
またパートスレなどでも可能である
この方法でレベル90までコツコツと上げた猛者もいる

メリット：おんj好きなら余裕、いろんな話題に触れる、定期スレに詳しくなる
デメリット：単純に一日中掲示板見るのがきつい、クソスレに押しつぶされる、履歴がごちゃつく


番外編
...



以上で終わりやで    </description>
    <dc:date>2026-05-13T01:44:31+09:00</dc:date>
    <utime>1778604271</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/12.html">
    <title>スクリプト（２）</title>
    <link>https://w.atwiki.jp/superopenj/pages/12.html</link>
    <description>
      **続き

作者:七色の彩り
#region(Open2ch NG Fix)
コメント:
おーぷんがサーバー移転してから自動新着に対してNG(×ボタンで消したID)が効いていない不具合対策
対象者の新着音と+1件新着レスの通知を消すことはできませんでした。
新着音が鳴っても自動新着を非表示に出来ればいい、さとる氏が直すのをどうしても待てないという人向け
#highlight(){{
// ==UserScript==
// @name          Open2ch NG Fix
// @namespace     https://greasyfork.org/ja/users/864059
// @version       2.0
// @description   サーバー移転後自動新着でNGが効いていないのを修正（CSS強制上書き）
// @author        七色の彩り
// @match         https://*.open2ch.net/test/read.cgi/*
// @icon          https://open2ch.net/favicon.ico
// @run-at        document-start
// @grant         none
// @license       MIT
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    const match = location.pathname.match(/\/([^\/]+)\/\d+/);
    if (!match) return;
    const storageKey = &#039;ignv4&#039; + match[1];
 
    // CSSを管理するためのstyle要素
    let styleEl = null;
 
    // NG IDリストを元に、非表示用のCSSを生成して適用する
    const updateNGStyle = () =&gt; {
        const ignoreListRaw = localStorage.getItem(storageKey);
        if (!ignoreListRaw) return;
 
        const ignoreList = ignoreListRaw.split(&#039;&lt;D&gt;&#039;).filter(id =&gt; id);
        if (ignoreList.length === 0) {
            if (styleEl) styleEl.textContent = &#039;&#039;;
            return;
        }
 
        // 各IDに対して「display: none !important」を強制するCSSを作成
        // 例: dl[uid=&quot;ID&quot;] { display: none !important; }
        const cssRules = ignoreList.map(id =&gt; `dl[uid=&quot;${id}&quot;] { display: none !important; }`).join(&#039;\n&#039;);
 
        if (!styleEl) {
            styleEl = document.createElement(&#039;style&#039;);
            document.head ? document.head.appendChild(styleEl) : document.documentElement.appendChild(styleEl);
        }
        styleEl.textContent = cssRules;
    };
 
    // 初回実行
    updateNGStyle();
 
    // ユーザーがNG登録（赤の×ボタンクリック）した時にCSSを更新する
    document.addEventListener(&#039;click&#039;, (e) =&gt; {
        if (e.target.closest(&#039;.iok&#039;)) {
            setTimeout(updateNGStyle, 100);
        }
    }, true);
 
    // 念のため、新着が来たタイミングでもリストが更新されていないかチェック
    // (別のタブでNG登録した場合などへの対策)
    const observer = new MutationObserver(() =&gt; {
        updateNGStyle();
    });
 
    window.addEventListener(&#039;DOMContentLoaded&#039;, () =&gt; {
        const target = document.getElementById(&#039;res_field&#039;) || document.body;
        observer.observe(target, { childList: true });
        updateNGStyle();
    });
 
})();
}}
#endregion
#region(Open2ch Find Link Modifier)
コメント:
おーぷん2chの検索結果で対象スレッドタイトルのリンク(l50)を任意の数に書き換え
更にレス番号のリンクからハイフンを削除して対象レスのみ開くように
検索結果の内容に安価があればそれも含めて開く
※内容がアク禁の場合対象者も開くので要注意
#highlight(){{
// ==UserScript==
// @name          Open2ch Find Link Modifier
// @namespace     https://greasyfork.org/ja/users/864059
// @version       0.6
// @description   レス番号リンクのハイフン削除と、安価があればそれも含める修正。表示数(/lXX)を動的に変更。プリセットボタン付き。
// @author        七色の彩り
// @match         https://find.open2ch.net/*
// @icon         https://open2ch.net/favicon.ico
// @grant         none
// @require       https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @license       MIT
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    let targetLValue = localStorage.getItem(&#039;open2ch_find_l_value&#039;) || &#039;50&#039;;
    let observer = null;
 
    function injectUI() {
        if ($(&#039;#l-modifier-container&#039;).length) return;
        const customStyle = `&lt;style&gt;
            .l-preset-btn { margin-left: 5px; padding: 2px 6px; font-size: 11px; cursor: pointer; background: #eee; border: 1px solid #ccc; border-radius: 3px; display: inline-block; }
            .l-preset-btn:hover { background: #ddd; }
            #l-modifier-container { margin-top: 0px; display: flex; align-items: center; flex-wrap: wrap; color: #333; }
        &lt;/style&gt;`;
        const uiHtml = `
            &lt;div id=&quot;l-modifier-container&quot;&gt;
                &lt;span style=&quot;font-size: 12px; font-weight: bold; margin-right: 5px;&quot;&gt;スレタイリンク&lt;/span&gt;
                &lt;input type=&quot;text&quot; id=&quot;l-value-input&quot; class=&quot;form-control&quot;
                       value=&quot;${targetLValue === &#039;all&#039; ? &#039;&#039; : targetLValue}&quot;
                       placeholder=&quot;${targetLValue === &#039;all&#039; ? &#039;ALL&#039; : &#039;&#039;}&quot;
                       style=&quot;width: 60px; height: 28px; display: inline-block; text-align: center;&quot;&gt;
                &lt;div id=&quot;l-presets&quot;&gt;
                    &lt;span class=&quot;l-preset-btn&quot; data-val=&quot;10&quot;&gt;l10&lt;/span&gt;
                    &lt;span class=&quot;l-preset-btn&quot; data-val=&quot;30&quot;&gt;l30&lt;/span&gt;
                    &lt;span class=&quot;l-preset-btn&quot; data-val=&quot;50&quot;&gt;l50&lt;/span&gt;
                    &lt;span class=&quot;l-preset-btn&quot; data-val=&quot;100&quot;&gt;l100&lt;/span&gt;
                    &lt;span class=&quot;l-preset-btn&quot; data-val=&quot;all&quot;&gt;ALL&lt;/span&gt;
                &lt;/div&gt;
            &lt;/div&gt;`;
        $(&#039;head&#039;).append(customStyle);
        $(&#039;.input-group&#039;).after(uiHtml);
 
        $(&#039;#l-value-input&#039;).on(&#039;input&#039;, function() {
            updateLValue($(this).val() === &#039;&#039; ? &#039;all&#039; : $(this).val());
        });
        $(&#039;.l-preset-btn&#039;).on(&#039;click&#039;, function() {
            const val = $(this).data(&#039;val&#039;);
            $(&#039;#l-value-input&#039;).val(val === &#039;all&#039; ? &#039;&#039; : val).attr(&#039;placeholder&#039;, val === &#039;all&#039; ? &#039;ALL&#039; : &#039;&#039;);
            updateLValue(val);
        });
    }
 
    function updateLValue(val) {
        targetLValue = val;
        localStorage.setItem(&#039;open2ch_find_l_value&#039;, val);
        fixFindLinks();
    }
 
    function fixFindLinks() {
        // 監視を一時停止して無限ループを防ぐ
        if (observer) observer.disconnect();
 
        // 1. スレタイおよびli要素の修正
        $(&#039;.result .subject a, li.list-group-item[url]&#039;).each(function() {
            let attrName = $(this).is(&#039;a&#039;) ? &#039;href&#039; : &#039;url&#039;;
            let url = $(this).attr(attrName);
            if (!url || !url.includes(&#039;/test/read.cgi/&#039;)) return;
 
            if (!$(this).data(&#039;original-url&#039;)) $(this).data(&#039;original-url&#039;, url);
 
            let base = $(this).data(&#039;original-url&#039;).replace(/\/l\d+$/, &#039;&#039;);
            let newUrl = (targetLValue === &#039;all&#039;) ? base : `${base}/l${targetLValue}`;
            if (url !== newUrl) $(this).attr(attrName, newUrl);
        });
 
        // 2. レス番号リンクの修正
        $(&#039;.result .content .th&#039;).each(function() {
            const $th = $(this);
            const $link = $th.find(&#039;a&#039;).first();
            let href = $link.attr(&#039;href&#039;);
            if (!href) return;
 
            if (!$link.data(&#039;original-href&#039;)) $link.data(&#039;original-href&#039;, href);
 
            let newHref = $link.data(&#039;original-href&#039;).replace(/-$/, &#039;&#039;);
            $link.text($link.text().replace(/-$/, &#039;&#039;));
 
            const thText = $th.text();
            const matches = thText.match(/(?:&gt;&gt;|!aku|!kaijo|!cap|!set|!sub)(\d+)/g);
            if (matches) {
                const nums = matches.map(m =&gt; m.match(/\d+/)[0]);
                const uniqueNums = [...new Set(nums)];
                newHref += &#039;,&#039; + uniqueNums.join(&#039;,&#039;);
            }
 
            if (href !== newHref) $link.attr(&#039;href&#039;, newHref);
        });
 
        // 監視を再開
        if (observer) observer.observe(document.body, { childList: true, subtree: true });
    }
 
    $(function() {
        injectUI();
 
        observer = new MutationObserver(function() {
            // UIが消えていれば再注入
            if (!$(&#039;#l-modifier-container&#039;).length) injectUI();
            fixFindLinks();
        });
 
        fixFindLinks();
    });
 
})();
}}
#endregion
#region(Open2ch アラビア文字規制避け (拡張版))
コメント:
おーぷん2chでうっかりアラビア文字等の禁止されている文字種を書き込みしようとして吸い込まれるのを阻止
うっかり書き込むとダイアログ無しで吸い込まれる文字種、ワードが入力されても投稿ボタンを無効化する。
最近増えたAIチャットの回答によく含まれる特殊スペース文字も吸い込み対象なので無効化対象に
文字種全般を指定して検知出来るものもあるが、2種類を組み合わせて構成される文字種は個別指定しないと検知できない。
昔ながらのAAに使用されるギリシャ文字やキリル文字以外は極力避けるのが無難です。

このスクリプトは、おんJ民氏による「Open2ch アラビア文字規制避け」
(https://greasyfork.org/ja/scripts/526462-open2ch-アラビア文字規制避け)をベースに拡張したものです。
元の作者であるおんJ民氏に感謝いたします。
#highlight(){{
// ==UserScript==
// @name    Open2ch アラビア文字規制避け (拡張版)
// @namespace    https://greasyfork.org/ja/users/864059
// @version    1.0.5
// @description    アラビア文字やAI回答の特殊空白文字等、書き込むと吸い込まれてしまう文字種を入力したとき投稿ボタンを無効化します
// @author    七色の彩り
// @match    https://*.open2ch.net/test/read.cgi/*
// @icon    https://avatars.githubusercontent.com/u/88383494
// @grant    none
// @license    GNU Affero General Public License v3.0 or later
// ==/UserScript==
 
/*
   注意：
   このスクリプトは、おんJ民氏による「Open2ch アラビア文字規制避け」
   (https://greasyfork.org/ja/scripts/526462-open2ch-アラビア文字規制避け)をベースに拡張したものです。
   元の作者であるおんJ民氏に感謝いたします。
*/
 
(function() {
    &#039;use strict&#039;;
 
    // 規制対象とする文字の正規表現
    // --- アラビア文字 ---
    // \u0600-\u06FF: 基本的なアラビア文字
    // \u0750-\u077F: アラビア文字補助
    // \u08A0-\u08FF: アラビア文字拡張A
    // \uFB50-\uFDFF: アラビア文字表現形式A
    // \uFE70-\uFEFF: アラビア文字表現形式B
    // --- タイ文字 ---
    // \u0E00-\u0E7F
    // --- チベット文字 ---
    // \u0F00-\u0FFF
    // --- ヘブライ文字 ---
    // \u0590-\u05FF
    // --- デーヴァナーガリー (インド文字) ---
    // \u0900-\u097F
    // --- 結合文字 (Combining Diacritical Marks) ---
    // \u0300-\u036F: 顔文字に用いられる記号など
    // --- AI回答等に使われる特殊スペース文字 ---
    // \u00A0: NO-BREAK SPACE (改行なし半角スペース)
    // \u200B: ZERO WIDTH SPACE (ゼロ幅スペース)  // リテラルに移動
    // \u202F: NARROW NO-BREAK SPACE (狭い改行なしスペース) // リテラルに移動
    // \uFEFF: ZERO WIDTH NO-BREAK SPACE / BOM (幅ゼロ改行なしスペース)
    // --- 特殊記号を用いた顔文字に採用されている文字種  ---
    // \u0590-\u05FF: ヘブライ文字ブロック
    // \u1DC0-\u1DFF: 結合用ダイアクリティカルマーク拡張
    // \u0D00-\u0D7F: マラヤーラム文字
    // \u0C80-\u0CFF: カンナダ文字
    // \u0250-\u02AF: 発音記号
    // \u1D00-\u1D7F: 発音記号拡張
    // \u0980-\u09FF: ベンガル文字
    // --- 規制対象文字の定義 ---
    const arabicRange = &#039;\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF&#039;; // アラビア
    const asianRange = &#039;\u0E00-\u0E7F\u0F00-\u0FFF\u0900-\u097F\u0980-\u09FF&#039;; // タイ・チベット・インド
    const specialRange = &#039;\u0590-\u05FF\u0300-\u036F\u1DC0-\u1DFF\u0D00-\u0D7F\u0C80-\u0CFF\u0250-\u02AF\u1D00-\u1D7F&#039;; // ヘブライ・結合・マラヤーラム等
    const spaceRange = &#039;\u00A0\uFEFF&#039;; // 特殊スペース
    const literals = &#039;૮ܸ˶ෆ੭ͽ꧁꧂\u200B\u202F‪꩜\u206D&#039;; // 個別リテラル
 
    // 全てを統合して一つの正規表現にする
    const ARABIC_CHAR_REGEX = new RegExp(&#039;[&#039; + arabicRange + asianRange + specialRange + spaceRange + literals + &#039;]&#039;);
 
    // --- 日本語NGワード ---
    const jpNgWords = [
        &#039;アフィ&#039;,
        &#039;大麻&#039;,
        &#039;唐沢恒心教&#039;,
        &#039;(^|[。\\s、！？\\u3000])(安倍晋三)([。\\s、！？\\u3000]|$)&#039; // 単体マッチ用の正規表現
    ].join(&#039;|&#039;);
    const JAPANESE_NG_WORD_REGEX = new RegExp(jpNgWords);
    // 現在のテキストエリアの状態を監視するためのタイマーID
    let inputCheckTimeout = null;
 
    /**
     * 全てのテキストエリアの状態をチェックし、投稿ボタンの有効/無効を決定する
     */
    function updateSubmitButtonStatus() {
        // 一つでもNG文字またはNGワードを含むテキストエリアがあるかチェック
        const isAnyTextareaInvalid = [...document.querySelectorAll(&#039;textarea&#039;)]
            .some(ta =&gt; ARABIC_CHAR_REGEX.test(ta.value) || JAPANESE_NG_WORD_REGEX.test(ta.value)); // 日本語NGワードチェックを追加
 
        [...document.querySelectorAll(&#039;[type=&quot;submit&quot;]&#039;)].forEach(button =&gt; {
            // NG文字/NGワードがあれば無効化、なければ有効化
            button.disabled = isAnyTextareaInvalid;
 
            // 投稿ボタン無効化の理由をタイトル属性でユーザーに提示
            if (isAnyTextareaInvalid) {
                button.title = &#039;規制対象の特殊文字またはNGワードが含まれているため、投稿できません。&#039;;
            } else {
                button.removeAttribute(&#039;title&#039;);
            }
        });
    }
 
    /**
     * テキストエリアの内容をチェックし、アラビア文字が含まれていれば警告表示を行う
     * @param {HTMLTextAreaElement} textareaElement - チェック対象のtextarea要素
     */
    function checkTextareaForArabic(textareaElement) {
        const textContent = textareaElement.value;
 
        // 1. 特殊文字のチェック
        const hasArabic = ARABIC_CHAR_REGEX.test(textContent);
 
        // 2. 日本語NGワードのチェック (大文字小文字・全角半角を区別したい場合は調整が必要です)
        // hasJapaneseNgWordは、hasArabicがfalseの場合でもチェックを実行します。
        const hasJapaneseNgWord = JAPANESE_NG_WORD_REGEX.test(textContent);
 
        // どちらか一方でもNGであればtrue
        const isInvalid = hasArabic || hasJapaneseNgWord;
 
        // 背景色の設定
        textareaElement.style.backgroundColor = isInvalid ? &#039;pink&#039; : &#039;&#039;;
 
        // 全体の投稿ボタンの状態を更新
        updateSubmitButtonStatus();
    }
 
    // pasteイベントリスナー (既存の機能を維持)
    window.document.addEventListener(&#039;paste&#039;, (e) =&gt; {
        if (e.target.tagName !== &#039;TEXTAREA&#039;) return;
 
        const textareaElement = e.target;
        const pasteData = (e.clipboardData || window.clipboardData).getData(&#039;text&#039;);
 
        // ペーストデータに特殊文字または日本語NGワードが含まれるかチェック
        if (ARABIC_CHAR_REGEX.test(pasteData) || JAPANESE_NG_WORD_REGEX.test(pasteData)) {
            // ペースト後のテキストエリアの背景色を即座に変更
            // (inputイベントが発火するまでの間、即座に視覚的フィードバックを与えるため)
            textareaElement.style.backgroundColor = &#039;pink&#039;;
        }
 
        // pasteの直後、または短い遅延後に全体チェックを強制的に実行
        // (textarea.valueが更新されるのを待ってからチェックするため)
        if (inputCheckTimeout) {
            clearTimeout(inputCheckTimeout);
        }
        inputCheckTimeout = setTimeout(() =&gt; {
            checkTextareaForArabic(textareaElement);
        }, 10); // ごく短い遅延
 
    });
 
    // inputイベントリスナー (直接入力やテキストエリアの内容変更を監視)
    window.document.addEventListener(&#039;input&#039;, (e) =&gt; {
        if (e.target.tagName !== &#039;TEXTAREA&#039;) return;
 
        // 遅延させてチェックすることで、連続した入力でのパフォーマンス負荷を軽減
        if (inputCheckTimeout) {
            clearTimeout(inputCheckTimeout);
        }
        inputCheckTimeout = setTimeout(() =&gt; {
            checkTextareaForArabic(e.target);
        }, 300); // 300msの遅延
    });
 
    // ページロード時に既存のテキストエリアをチェック
    window.addEventListener(&#039;load&#039;, () =&gt; {
        document.querySelectorAll(&#039;textarea&#039;).forEach(textarea =&gt; {
            checkTextareaForArabic(textarea);
        });
    });
 
})();
}}
#endregion
#region(Open2ch ID Search Button)
コメント:
IDと時刻の間に虫眼鏡アイコンを追加し、クリックするとおーぷん全体で全文のID検索をする。
IDの右側だと無視ボタン誤爆の恐れがあるため左にした。
また、スレッド内で選択範囲の右上にも虫眼鏡アイコンを追加して同じように全文検索をする。

主にマルチポスト疑惑の書き込みに対して即座に検索出来るように作ったもの
#highlight(){{
// ==UserScript==
// @name        Open2ch ID Search Button
// @namespace   https://greasyfork.org/ja/users/864059
// @version     1.2.0
// @description おーぷん2chでIDの左側と本文を選択した右上に検索ボタン（虫眼鏡アイコン）を表示します。
// @author      七色の彩り
// @match       https://*.open2ch.net/test/read.cgi/*
// @icon          https://open2ch.net/favicon.ico
// @grant       none
// @run-at      document-idle
// @license MIT
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    // URLからbbs名を取得する関数
    const getBbsName = () =&gt; {
        const path = window.location.pathname;
        const match = path.match(/\/read\.cgi\/([a-zA-Z0-9_-]+)\//);
        return match ? match[1] : &#039;&#039;;
    };
 
    const bbsName = getBbsName();
 
    const $searchUrl = &#039;https://find.open2ch.net/?bbs=&amp;t=f&amp;q=&#039;; // ID: は searchQuery で付与する
 
    /**
     * 指定されたIDスパンに検索ボタンを追加する
     * @param {HTMLElement} idSpan - &lt;span class=&quot;_id&quot;&gt; 要素
     * @param {string} searchUrlTemplate - 検索URLの雛形
     */
    const processIdSpan = (idSpan, searchUrlTemplate) =&gt; {
 
        // 1. ID情報の取得
        const idText = idSpan.getAttribute(&#039;val&#039;);
        if (!idText || idText === &#039;???&#039;) return;
 
        // 2. ID整形ロジック (余計なピリオド問題を解決したもの)
        let fullId = &#039;&#039;;
        const idLinks = idSpan.querySelectorAll(&#039;.id&#039;);
 
        if (idLinks.length &gt; 0) {
            fullId = Array.from(idLinks).map(link =&gt; link.textContent).join(&#039;.&#039;);
        } else if (idText.length &gt; 4) {
            fullId = idText.slice(0, 2) + &#039;.&#039; + idText.slice(2, 4) + &#039;.&#039; + idText.slice(4);
        } else {
            fullId = idText;
        }
 
        const searchQuery = &#039;ID:&#039; + fullId;
        const encodedQuery = encodeURIComponent(searchQuery);
        const finalUrl = searchUrlTemplate + encodedQuery + &#039;&amp;wh=&amp;d=&#039;;
 
        // 3. 重複チェックと既存ボタンの再利用
        const existingButton = idSpan.previousElementSibling;
 
        // 既に虫眼鏡アイコンを持つSPAN要素が存在する場合
        if (existingButton &amp;&amp; existingButton.tagName === &#039;SPAN&#039; &amp;&amp; existingButton.querySelector(&#039;.fas.fa-search&#039;)) {
            // 既存のボタンに正しい機能を持つクリックイベントを追加/上書きする
            existingButton.addEventListener(&#039;click&#039;, function() {
                window.open(finalUrl, &#039;_blank&#039;, &#039;noopener noreferrer&#039;);
            });
            return; // 既存要素を再利用したため、ここで終了
        }
 
        // 4. 新しいボタンの作成・挿入 (通常時の動作)
        const searchButton = document.createElement(&#039;span&#039;);
        searchButton.title = &#039;ID:&#039; + fullId + &#039;を検索&#039;;
        searchButton.style.cssText = &#039;margin-left: 5px; cursor: pointer;&#039;;
 
        const icon = document.createElement(&#039;i&#039;);
        icon.className = &#039;fas fa-search&#039;;
        icon.style.cssText = &#039;color: #333;&#039;;
 
        searchButton.appendChild(icon);
 
        searchButton.addEventListener(&#039;click&#039;, function() {
            window.open(finalUrl, &#039;_blank&#039;, &#039;noopener noreferrer&#039;);
        });
 
        // idSpanの直前に挿入 (時刻とID:の間)
        idSpan.insertAdjacentElement(&#039;beforebegin&#039;, searchButton);
    };
 
    // スタイルの追加（丸いボタンのデザイン）
    const style = document.createElement(&#039;style&#039;);
    style.textContent = `
        #floating-search-btn {
            position: absolute;
            z-index: 10001;
            background: #fff;
            border: 1px solid #ccc;
            box-shadow: 0 2px 5px rgba(0,0,0,0.3);
            width: 30px;
            height: 30px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            display: none;
            transition: transform 0.1s;
        }
        #floating-search-btn:hover {
            transform: scale(1.1);
            background-color: #f0f0f0;
        }
        #floating-search-btn i {
            color: #333;
            font-size: 14px;
        }
    `;
    document.head.appendChild(style);
 
    // ボタン要素を作成
    const floatBtn = document.createElement(&#039;div&#039;);
    floatBtn.id = &#039;floating-search-btn&#039;;
    floatBtn.title = &#039;おーぷん全体検索&#039;;
    floatBtn.innerHTML = &#039;&lt;i class=&quot;fas fa-search&quot;&gt;&lt;/i&gt;&#039;;
    document.body.appendChild(floatBtn);
 
    // テキスト選択時のイベント
    document.addEventListener(&#039;mouseup&#039;, (e) =&gt; {
        // 少し遅らせて選択範囲を取得（クリック解除時のタイミング調整）
        setTimeout(() =&gt; {
            const selection = window.getSelection();
            const selectedText = selection.toString().trim();
 
            if (selectedText) {
                // ボタンの表示位置をマウス位置の少し右上に設定
                floatBtn.style.left = `${e.pageX + 10}px`;
                floatBtn.style.top = `${e.pageY - 40}px`;
                floatBtn.style.display = &#039;flex&#039;;
 
                // 検索実行イベント
                floatBtn.onclick = () =&gt; {
                    const url = $searchUrl + encodeURIComponent(selectedText) + &#039;&amp;wh=&amp;d=&#039;;
                    window.open(url, &#039;_blank&#039;, &#039;noopener noreferrer&#039;);
                    floatBtn.style.display = &#039;none&#039;;
                    selection.removeAllRanges(); // 選択解除
                };
            } else {
                // 何もないところをクリックしたらボタンを隠す
                if (e.target.closest(&#039;#floating-search-btn&#039;)) return;
                floatBtn.style.display = &#039;none&#039;;
            }
        }, 10);
    });
 
    // スクロール時にも隠す
    document.addEventListener(&#039;scroll&#039;, () =&gt; {
        floatBtn.style.display = &#039;none&#039;;
    }, { passive: true });
 
    // 初期実行: 既存の要素に対してボタンを追加
    // Mutation Observerで動的に追加される要素を監視
    // 監視対象を最も確実なコンテナである .thread に
    const threadContainer = document.querySelector(&#039;.thread&#039;); // &lt;div class=&quot;thread&quot;&gt; または &lt;dl class=&quot;thread&quot;&gt; の両方に対応
 
    if (!threadContainer) {
        // コンテナが見つからなければ、処理を終了する
        //console.log(&#039;Open2ch ID Search Button: スレッドコンテナ (.thread) が見つかりません。終了します。&#039;);
        return;
    }
    // スレッドコンテナ内のみを検索対象とする
    const idSpansInitial = threadContainer.querySelectorAll(&#039;._id&#039;);
    idSpansInitial.forEach(idSpan =&gt; {
        processIdSpan(idSpan, $searchUrl);
    });
 
    // Mutation Observerで動的に追加される要素を監視
    // threadContainer が取得できた前提で、そのまま observer を設定
    setupObserver(threadContainer, $searchUrl);
 
    function setupObserver(targetNode, searchUrlTemplate) {
        const observer = new MutationObserver(mutationsList =&gt; {
            mutationsList.forEach(mutation =&gt; {
                if (mutation.type === &#039;childList&#039;) {
                    mutation.addedNodes.forEach(node =&gt; {
                        // 要素ノードでない場合はスキップ
                        if (node.nodeType !== 1) return;
 
                        // 追加された要素（またはその子孫）から &#039;_id&#039; スパンを検索
                        // 新しい書き込み（&lt;dl&gt;）が追加された場合、その中の&lt;dt&gt;の._idを見つける
                        const idSpans = node.querySelectorAll(&#039;._id&#039;);
                        idSpans.forEach(idSpan =&gt; {
                            processIdSpan(idSpan, searchUrlTemplate);
                        });
                        // 追加されたノード自体が &#039;_id&#039; スパンの場合
                        if (node.classList &amp;&amp; node.classList.contains(&#039;_id&#039;)) {
                            processIdSpan(node, searchUrlTemplate);
                        }
                    });
                }
            });
        });
        // スレッドコンテナとその子要素の変更を監視開始
        observer.observe(targetNode, { childList: true, subtree: true });
    }
 
})();
}}
#endregion
#region(Open2ch Kome URL自動補完)
コメント:
おーぷん2chの「kome」機能で、URLに含まれる「=（イコール）」記号がなぜか消えてしまう不具合を修正します。

不具合の症状: 自分が書き込んだ直後は正常に見えますが、ページをリロードしたり、
他人から見たりするとイコールが欠けてしまい、リンクをクリックしてもTOP戻されてしまいます。（動画が存在しない扱い）

このスクリプトの役割: 特に出現頻度の高い YouTube のURLを自動でチェックし、欠けているイコールを補完して正しいリンクに修正します。
#highlight(){{
// ==UserScript==
// @name         Open2ch Kome URL自動補完
// @namespace    https://greasyfork.org/ja/users/864059
// @version      1.3.1
// @description  komeでyoutubeのURLに含まれるはずの「=」が消えてしまうのを補完
// @author       七色の彩り
// @match        https://*.open2ch.net/test/read.cgi/*
// @icon         https://open2ch.net/favicon.ico
// @grant        none
// @license      MIT
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    const PROCESSED_ATTR = &#039;data-kome-fixed&#039;;
 
    /**
     * 動画IDと追加パラメータから正しいYouTube URLを生成します。
     */
    function reconstructYoutubeUrl(videoId, extraParams) {
        let finalUrl = `https://www.youtube.com/watch?v=${videoId}`;
        if (extraParams) {
            let extra = extraParams.replace(/&amp;amp;/g, &#039;&amp;&#039;);
            // 2つ目の&#039;?&#039;が来た場合は&#039;&amp;&#039;に置換して結合
            if (extra.startsWith(&#039;?&#039;)) {
                extra = &#039;&amp;&#039; + extra.substring(1);
            }
            finalUrl += extra;
        }
        return finalUrl;
    }
 
    /**
     * 既に&lt;a&gt;タグになっているが不正なURLを修正します。
     */
    function fixYoutubeUrlsInNode(node) {
        if (!(node instanceof Element)) return;
        const links = node.querySelectorAll(`a[href]:not([${PROCESSED_ATTR}])`);
 
        links.forEach(link =&gt; {
            const href = link.href;
            let videoId = null;
            let extra = &#039;&#039;;
 
            const watchMatch = href.match(/watch\?v(?:=)?([a-zA-Z0-9_-]{11})([&amp;?#].*)?/);
            const shortMatch = href.match(/youtu\.be\/([a-zA-Z0-9_-]{11})([&amp;?#].*)?/);
 
            if (watchMatch) {
                videoId = watchMatch[1];
                extra = watchMatch[2] || &#039;&#039;;
            } else if (shortMatch) {
                videoId = shortMatch[1];
                extra = shortMatch[2] || &#039;&#039;;
            }
 
            if (videoId) {
                const finalUrl = reconstructYoutubeUrl(videoId, extra);
                link.href = finalUrl;
                // テキストがURL形式だった場合のみ書き換える（アンカーテキストが動画タイトルの場合は保持）
                if (link.textContent.includes(&#039;youtube.com&#039;) || link.textContent.includes(&#039;youtu.be&#039;)) {
                    link.textContent = finalUrl;
                }
                link.setAttribute(PROCESSED_ATTR, &#039;true&#039;);
            }
        });
    }
 
    /**
     * テキストノード内のURLを検索し、リンクに変換します。
     */
    function parseAndReplaceUrl(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            const text = node.textContent;
            // 途切れたURLをキャプチャ (watch?v 直後の = の有無を問わない)
            const youtuBeRegex = /https:\/\/(?:youtu\.be\/|www\.youtube\.com\/watch\??v=?)([a-zA-Z0-9_-]{11})([&amp;?#][^\s]*)?/g;
            const matches = [...text.matchAll(youtuBeRegex)];
 
            if (matches.length &gt; 0) {
                const fragment = document.createDocumentFragment();
                let lastIndex = 0;
 
                matches.forEach(match =&gt; {
                    if (match.index &gt; lastIndex) {
                        fragment.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
                    }
 
                    const videoId = match[1];
                    const extra = match[2] || &#039;&#039;;
                    const finalUrl = reconstructYoutubeUrl(videoId, extra);
 
                    const a = document.createElement(&#039;a&#039;);
                    a.href = finalUrl;
                    a.textContent = finalUrl;
                    a.style.color = &#039;#3399ff&#039;;
                    a.target = &#039;_blank&#039;;
                    a.rel = &#039;noopener noreferrer&#039;;
                    a.setAttribute(PROCESSED_ATTR, &#039;true&#039;);
                    fragment.appendChild(a);
 
                    lastIndex = match.index + match[0].length;
                });
 
                if (lastIndex &lt; text.length) {
                    fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
                }
 
                if (node.parentNode) {
                    node.parentNode.replaceChild(fragment, node);
                }
            }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // 処理済み属性がある場合や特定のタグ内はスキップ
            if (node.hasAttribute(PROCESSED_ATTR)) return;
            const skipTags = [&#039;A&#039;, &#039;BUTTON&#039;, &#039;SCRIPT&#039;, &#039;STYLE&#039;, &#039;TEXTAREA&#039;, &#039;INPUT&#039;];
            if (!skipTags.includes(node.tagName)) {
                Array.from(node.childNodes).forEach(parseAndReplaceUrl);
            }
        }
    }
 
    const observer = new MutationObserver(mutations =&gt; {
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    fixYoutubeUrlsInNode(node);
                    parseAndReplaceUrl(node);
                } else if (node.nodeType === Node.TEXT_NODE) {
                    parseAndReplaceUrl(node);
                }
            }
        }
    });
 
    const config = { childList: true, subtree: true };
    const klogView = document.getElementById(&#039;klog_view&#039;);
 
    if (klogView) {
        observer.observe(klogView, config);
    } else {
        observer.observe(document.body, config);
    }
 
    // 初期実行
    fixYoutubeUrlsInNode(document.body);
    parseAndReplaceUrl(document.body);
 
})();
}}
#endregion
#region(Open2ch Kome UID Display)
コメント:
【概要】 Open2chのkome（チャット機能）で内部的に保持されているUIDを、時刻の左側に表示します。
このUIDは忍法帖（Cookie）と連動しているため、スレッドで固定ID（高レベル）の状態であれば、komeでも実質的な固定IDとして機能します。
（※ブラウザのCookie削除やシークレットモードの使用でUIDは更新されます）

自演・荒らしの確認や通報の補助、人違いの回避に役立ちます。 一方で、匿名性が薄れるため「見なきゃよかった」となる可能性もあります。
スレッドでの固定IDと同様、IDを紐付けられても構わないという方向けのツールです。

【注意点】

ログの限界: komeの発言が50個を超えて過去ログ送りになった分や、古い日付の発言は、スレッドのリロード時にUIDが取得できなくなります。

自分のUID: 自分の書き込みにはサーバーからUIDが返されない仕様のため、本スクリプトでは確認できません。確認したい場合は別ブラウザやシークレットウィンドウから見てください。

自分の名前設定: 自分のUIDが表示されるはずの場所には、好きな名前を記入できます（全角3文字/半角5文字まで。文字数制限は表示崩れ防止のためです）。

【主な機能】 UID部分をクリックすると、以下のオプション設定が展開されます。

メモ機能 UIDにマウスを合わせると、入力したメモをツールチップとして表示します。 メモ内にUID自体を表示するかどうかを個別に設定可能です（デフォルト：オン）。

UIDの色指定 プリセット（150色）またはカラーピッカーから、UIDごとに背景色を指定できます。特定のユーザーを目立たせたい場合に便利です。

デフォルト設定（詳細設定）

ランダムカラー表示: オンにすると全UIDを自動で色分けします。オフにすると全部白文字になります。

カラー調整: 明度100%（白文字）にしたり、明度0%（黒文字）に調整することで、普段はUIDを背景に埋もれさせて隠し、マウスで選択した時だけ見えるようにする、といった使い方も可能です。
色相の範囲を極端に狭く（小さく）して、色相の開始位置を好みの色にすれば特定の色だけにする事も可能です。

YouTubeのURL補完 komeではイコール記号が消えてしまう謎仕様があるので、つべのURLを補完して正常にアクセス出来るように表示
内部的には
https://greasyfork.org/ja/scripts/564076-Open2ch-Kome-URL自動補完
これと同一です。
#highlight(){{
長いので割愛。
https://greasyfork.org/ja/scripts/564221-open2ch-kome-uid-display/code
}}
#endregion
#region(Open2ch Option Expansion)
コメント:
書き込み欄の左下にある「▼ｵﾌﾟｼｮﾝ」=「投稿おぷしょん」から選べる項目を拡張します。
初期状態では全部オフなので何もしません。
必要に応じてチェックを入れて試してください。

※注意：サブドメインごとの設定保存について
おーぷん2chの仕様上、設定は各板（サブドメイン）ごとに個別に保存されます。
例：uni での設定は、open や hayabusa には自動で反映されません。お手数ですが、それぞれの板ごとに設定を行ってください。

1. 虹色無効
rainbow.cssの無効化
虹色タイトル等の再来を阻止
2. 次スレ
常に次スレ作成・次スレ検索の項目を表示します
3. 虫消し
クリスマスに飛び交うアレを消します
※ソースにbugと書かれているのでそれに準じています
4. 花消し
花びらが飛び交うのを消します
5. Error軽
komeエラーダイアログが重なり続けてどんどん暗く重くなるのを軽減します
※多重ダイアログ自体は阻止できません
6. お知らせ
板TOPにあるお知らせをスレTOPのスレ内検索欄右側に表示します
7. ｽﾚﾀｲ消
スレタイにある!max数字や【文字列】をブラウザのタイトルから消します
8. X無効化
X(旧Twitter)のiframe展開を阻止します
9. 画像海苔
画像URL「のみ」に海苔を使用した場合にクリック出来ないのを解消します
10. ｶｰﾄﾞ無効
URLが自動的にカード形式（長方形）で表示されるのを阻止します
11. 花火無効
スレッド終了時（1000レス到達時など）に打ち上がる花火の演出を無効化します
12. つべ無効
youtube&quot;等&quot;の動画URLが書き込まれてもミニプレイヤーの展開を阻止します
ニコニコ動画は独自の処理がされているようで書き込みがURLのみだと空発言のようになります
13. 雪消し
スレッドに雪が舞うのを消します

追加情報
このスクリプトは、おーぷん2chの標準機能に干渉しすぎず、見た目の「ちょっと不便・ちょっと煩わしい」を解消することを目的としています。
#highlight(){{
// ==UserScript==
// @name         Open2ch Option Expansion
// @namespace    https://greasyfork.org/ja/users/864059
// @version      1.6.4
// @description  投稿おぷしょんに項目を追加し、表示に関するCSS等を切り替えます
// @author       七色の彩り
// @match        https://*.open2ch.net/test/read.cgi/*
// @icon         https://open2ch.net/favicon.ico
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    // X(Twitter)の埋め込みスクリプトを無効化（スイッチON時のみ）
    if (localStorage.getItem(&#039;ext_no_x_embed&#039;) === &#039;true&#039;) {
        // 1. twttrオブジェクトを完全にロック
        Object.defineProperty(window, &#039;twttr&#039;, {
            value: { ready: function() {}, widgets: { load: function() {} } },
            writable: false, configurable: false
        });
 
        // 2. スタイルで展開用の枠や非表示設定を無効化
        const xStyle = document.createElement(&#039;style&#039;);
        xStyle.textContent = `
            /* 展開後の外枠を消す */
            .twitter-tweet-rendered { display: none !important; }
            /* iframeそのものを消す */
            iframe[id^=&quot;twitter-widget-&quot;] { display: none !important; }
            /* 元のテキスト(blockquote)が必要なら表示する設定（今回はリンクのみにするなら不要ですが念のため） */
            blockquote.twitter-tweet { display: none !important; }
        `;
        document.documentElement.appendChild(xStyle);
 
        // 3. スクリプトタグと展開用要素の削除
        const xObserver = new MutationObserver((mutations) =&gt; {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType !== 1) continue;
 
                    // Twitterスクリプトの阻止
                    if (node.tagName === &#039;SCRIPT&#039; &amp;&amp; node.src &amp;&amp; node.src.indexOf(&#039;platform.twitter.com&#039;) !== -1) {
                        node.removeAttribute(&#039;src&#039;);
                        node.textContent = &#039;&#039;;
                        node.remove();
                    }
 
                    // おーぷん側が作った「空の枠」を即座に消す
                    if (node.classList &amp;&amp; node.classList.contains(&#039;twitter-tweet-rendered&#039;)) {
                        node.remove();
                    }
                }
            }
        });
        xObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    // カード化の無効化（スイッチON時のみ）
    if (localStorage.getItem(&#039;ext_no_card&#039;) === &#039;true&#039;) {
        // 1. カード生成関数を空にする
        window.open2ch_url_card = function() {};
 
        // 2. スクリプトの読み込みを阻止するObserver
        const cardObserver = new MutationObserver((mutations) =&gt; {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === &#039;SCRIPT&#039; &amp;&amp; node.src &amp;&amp; node.src.includes(&#039;url_card&#039;)) {
                        node.remove();
                    }
                }
            }
        });
        cardObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    // 花火の無効化（スイッチON時のみ）
    if (localStorage.getItem(&#039;ext_no_hanabi&#039;) === &#039;true&#039;) {
        const hanabiObserver = new MutationObserver((mutations) =&gt; {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === &#039;SCRIPT&#039; &amp;&amp; node.src &amp;&amp; node.src.includes(&#039;hanabi.js&#039;)) {
                        node.remove();
                    }
                }
            }
        });
        hanabiObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    // YouTube展開の無効化（スイッチON時のみ）
    if (localStorage.getItem(&#039;ext_no_yt&#039;) === &#039;true&#039;) {
        const ytObserver = new MutationObserver((mutations) =&gt; {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType !== 1) continue;
 
                    // 1. スクリプトタグの阻止
                    if (node.tagName === &#039;SCRIPT&#039; &amp;&amp; node.src) {
                        if (node.src.includes(&#039;youtube.com/player_api&#039;) ||
                            node.src.includes(&#039;jquery.lazy.youtube&#039;)) {
                            node.remove();
                        }
                    }
 
                    // 2. 独自プレイヤー枠 &lt;v&gt; が作られた瞬間に消す
                    if (node.tagName === &#039;V&#039; || node.querySelector(&#039;v&#039;)) {
                        const vTag = node.tagName === &#039;V&#039; ? node : node.querySelector(&#039;v&#039;);
                        if (vTag) vTag.remove();
                    }
                }
            }
        });
        ytObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    // 雪の無効化（スイッチON時のみ）
    if (localStorage.getItem(&#039;ext_no_snow&#039;) === &#039;true&#039;) {
        const snowObserver = new MutationObserver((mutations) =&gt; {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === &#039;SCRIPT&#039; &amp;&amp; node.src &amp;&amp; node.src.includes(&#039;snow.js&#039;)) {
                        node.remove();
                    }
                }
            }
        });
        snowObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    // ==========================================
    // 設定リスト：ここを増やすだけで、自動で段が増えます
    // ==========================================
    const customSettings = [
        {
            id: &#039;ext_no_rainbow&#039;,
            label: &#039;虹色無効&#039;,
            title: &#039;虹色アニメーションを停止して文字色を固定します&#039;,
            css: `
                /* .rainbow_cssと.rainbow-textの両方を無効化 */
                body.ext_no_rainbow_enabled .rainbow_css,
                body.ext_no_rainbow_enabled .rainbow-text {
                    animation: none !important;
                    -webkit-background-clip: initial !important;
                    -webkit-text-fill-color: initial !important;
                    color: inherit !important;
                    background: none !important;
                }
                /* キーフレームそのものを上書きして停止 */
                body.ext_no_rainbow_enabled @keyframes rainbowFlow {
                    0% { background-position: 0% 0%; }
                    100% { background-position: 0% 0%; }
                }
                /* 新UI特有のアニメーションももしあればここで封じる */
            `
        },
        {
            id: &#039;ext_next_thread&#039;,
            label: &#039;次スレ&#039;,
            title: &#039;次スレ作成・検索ボタンを常時表示します&#039;,
            css: `body.ext_next_thread_enabled div.next_thread_div { display: initial !important; }`
        },
        {
            id: &#039;ext_no_bug&#039;,
            label: &#039;虫消し&#039;,
            title: &#039;クリスマスの虫を消します&#039;,
            css: `
                body.ext_no_bug_enabled .bug { display: none !important; }
                body.ext_no_bug_enabled body.valus { background-image: none !important; }
            `
        },
        {
            id: &#039;ext_no_hana&#039;,
            label: &#039;花消し&#039;,
            title: &#039;花びらを消します&#039;,
            css: `
                body.ext_no_hana_enabled .hana { display: none !important; }
            `
        },
        {
            id: &#039;ext_error_light&#039;,
            label: &#039;Error軽&#039;,
            title: &#039;komeエラーダイアログの描画負荷を軽減します&#039;,
            css: `
                body.ext_error_light_enabled .jconfirm,
                body.ext_error_light_enabled .jconfirm *,
                body.ext_error_light_enabled .jconfirm-bg,
                body.ext_error_light_enabled .jconfirm-animated,
                body.ext_error_light_enabled .jconfirm-box {
                    transition: none !important;
                    animation: none !important;
                    transform: none !important;
                    transition-duration: 0s !important;
                    animation-duration: 0s !important;
                }
                body.ext_error_light_enabled .jconfirm-bg {
                    opacity: 0 !important;
                    background: #000 !important;
                }
                body.ext_error_light_enabled .jconfirm-hilight-shake {
                    animation: none !important;
                }
            `
        },
        {
            id: &#039;ext_notice&#039;,
            label: &#039;お知らせ&#039;,
            title: &#039;板TOPのお知らせを検索窓の右に表示します&#039;,
            css: `
                /* オフの時は非表示 */
                body:not(.ext_notice_enabled) #ext_board_notice { display: none !important; }
                /* お知らせの基本スタイル */
                #ext_board_notice {
                    margin-left: 10px;
                    font-size: 11px;
                    line-height: 1.3;
                    display: inline-block;
                    vertical-align: middle;
                    color: inherit;
                }
                /* 中のfontタグなどが持っている行間設定をリセット */
                #ext_board_notice font { line-height: inherit !important; }
            `
        },
        {
            id: &#039;ext_clean_title&#039;,
            label: &#039;ｽﾚﾀｲ消&#039;,
            title: &#039;タブに表示されるスレタイから!maxや【】内を削除します&#039;,
            css: ``
        },
        {
            id: &#039;ext_no_x_embed&#039;,
            label: &#039;X無効化&#039;,
            title: &#039;Twitter(X)の埋め込み展開を阻止します（要再読み込み）&#039;,
            css: ``
        },
        {
            id: &#039;ext_image_nori&#039;,
            label: &#039;画像海苔&#039;,
            title: &#039;画像URLへの直海苔が剥がせなくなる問題を修正します&#039;,
            css: `
                /* 海苔(&lt;n&gt;)の中の画像やリンクをクリック可能にし、海苔を剥がせるようにします */
                body.ext_image_nori_enabled n {
                    pointer-events: auto !important;
                    cursor: pointer !important;
                }
                body.ext_image_nori_enabled n.open {
                    background: none !important;
                    color: inherit !important;
                }
            `
        },
        {
            id: &#039;ext_no_card&#039;,
            label: &#039;ｶｰﾄﾞ無効&#039;,
            title: &#039;URLが書き込まれた際のカード化を阻止します（要再読み込み）&#039;,
            css: ``
        },
        {
            id: &#039;ext_no_hanabi&#039;,
            label: &#039;花火無効&#039;,
            title: &#039;スレッド終了時の花火演出を無効化します（要再読み込み）&#039;,
            css: `
                /* 万が一JSが動いても表示されないよう念のためCSSでもガード */
                body.ext_no_hanabi_enabled .hanabi_canvas { display: none !important; }
            `
        },
        {
            id: &#039;ext_no_yt&#039;,
            label: &#039;つべ無効&#039;,
            title: &#039;YouTube等の動画プレイヤー展開を阻止します&#039;,
            css: `
                /* 独自タグ &lt;v&gt; 単位でプレイヤーを完全に消す */
                body.ext_no_yt_enabled v,
                body.ext_no_yt_enabled .youtube_div,
                body.ext_no_yt_enabled .video_div,
                body.ext_no_yt_enabled iframe.iyoutube {
                    display: none !important;
                }
            `
        },
        {
            id: &#039;ext_no_snow&#039;,
            label: &#039;雪消し&#039;,
            title: &#039;画面内に雪が降る演出を無効化します&#039;,
            css: `
                /* JSが動く前に雪のキャンバス等を非表示にする */
                body.ext_no_snow_enabled #snow-canvas,
                body.ext_no_snow_enabled .snow {
                    display: none !important;
                }
            `
        },
    ];
 
 
    function updateState(id, isEnabled) {
        const className = id + &#039;_enabled&#039;;
        if (isEnabled) {
            document.body.classList.add(className);
        } else {
            document.body.classList.remove(className);
        }
 
        if (id === &#039;ext_clean_title&#039;) handleTitleClean();
 
        // お知らせがオンになった瞬間に取得を開始する
        if (id === &#039;ext_notice&#039; &amp;&amp; isEnabled) {
            fetchBoardNotice();
        }
        // 虹色無効がオンなら、新UI側の設定(rainbowTitleEnabled)をfalseにする
        if (id === &#039;ext_no_rainbow&#039;) {
            try {
                // 無効化したいので、isEnabledがtrue(チェックあり)の時に&#039;false&#039;を書き込む
                localStorage.setItem(&#039;rainbowTitleEnabled&#039;, isEnabled ? &#039;false&#039; : &#039;true&#039;);
            } catch(e) {}
        }
        // 雪消しがオンなら公式側の設定をfalse、オフならtrueにする
        if (id === &#039;ext_no_snow&#039;) {
            try {
                localStorage.setItem(&#039;snowEnabled&#039;, isEnabled ? &#039;false&#039; : &#039;true&#039;);
            } catch(e) {}
        }
        // 画像海苔機能が有効な場合のみ、クリックで剥がれるようにイベントを補助
        if (id === &#039;ext_image_nori&#039;) {
            const noriHandler = function(e) {
                // 設定がオフ、またはすでに剥がれている場合は何もしない
                if (localStorage.getItem(&#039;ext_image_nori&#039;) !== &#039;true&#039;) return;
                const nTag = e.target.closest(&#039;n&#039;);
                if (nTag &amp;&amp; !nTag.classList.contains(&#039;open&#039;)) {
                    nTag.classList.add(&#039;open&#039;);
                }
            };
            // 重複登録を防ぐため削除してから登録
            document.removeEventListener(&#039;click&#039;, noriHandler);
            if (isEnabled) {
                document.addEventListener(&#039;click&#039;, noriHandler);
            }
        }
    }
    // スレタイ修正用関数
    function handleTitleClean() {
        // チェックボックスがオフなら何もしない
        if (localStorage.getItem(&#039;ext_clean_title&#039;) !== &#039;true&#039;) return;
 
        // 削除パターン: !max(数字) や 【(文字列)】
        const unkPattern = /(?:!max\d+)?\s*【[^】]+】\s*|!max\d+\s*/g;
        const currentTitle = document.title;
        const fixedTitle = currentTitle.replace(unkPattern, &quot;&quot;);
        // 現在のタイトルと修正後が違う場合のみ書き換える
        if (currentTitle !== fixedTitle) {
            document.title = fixedTitle;
        }
    }
 
    function init() {
        const parentContainer = document.querySelector(&#039;.options&#039;)?.parentNode;
        if (!parentContainer) return;
        let currentTargetRow = null;
 
        customSettings.forEach((set, index) =&gt; {
            // 4項目ごとに新しい行(div.options)を作成
            if (index % 4 === 0) {
                const newRow = document.createElement(&#039;div&#039;);
                newRow.className = &#039;options&#039;;
                const allRows = parentContainer.querySelectorAll(&#039;.options&#039;);
                allRows[allRows.length - 1].after(newRow);
                currentTargetRow = newRow;
            }
 
            const savedValue = localStorage.getItem(set.id) === &#039;true&#039;;
 
            const label = document.createElement(&#039;label&#039;);
            label.title = set.title;
 
            const checkbox = document.createElement(&#039;input&#039;);
            checkbox.type = &#039;checkbox&#039;;
            checkbox.id = set.id;
            checkbox.checked = savedValue;
 
            label.appendChild(checkbox);
            label.appendChild(document.createTextNode(set.label));
 
            currentTargetRow.appendChild(label);
 
            currentTargetRow.appendChild(document.createTextNode(&#039; &#039;));
 
            const styleEl = document.createElement(&#039;style&#039;);
            styleEl.id = &#039;style-&#039; + set.id;
            styleEl.textContent = set.css;
            document.head.appendChild(styleEl);
 
            updateState(set.id, savedValue);
 
            checkbox.addEventListener(&#039;change&#039;, (e) =&gt; {
                localStorage.setItem(set.id, e.target.checked);
                updateState(set.id, e.target.checked);
            });
        });
    }
 
    // 二重実行を防ぐためのフラグ
    let isInitDone = false;
    let isNoticeDone = false;
 
    const timer = setInterval(() =&gt; {
        // 1. オプションパネルの初期化
        if (!isInitDone &amp;&amp; document.querySelector(&#039;.options&#039;)) {
            isInitDone = true;
            init();
        }
 
        // 2. お知らせの初期表示
        const searchInput = document.querySelector(&#039;input[name=&quot;q&quot;]&#039;);
        const isNoticeEnabled = localStorage.getItem(&#039;ext_notice&#039;) === &#039;true&#039;;
 
        if (!isNoticeDone &amp;&amp; searchInput) {
            if (isNoticeEnabled) {
                fetchBoardNotice();
            }
            isNoticeDone = true;
        }
 
        if (isInitDone &amp;&amp; isNoticeDone) {
            clearInterval(timer);
        }
    }, 500);
 
    // タイトルの変化を監視するMutationObserver
    const titleObserver = new MutationObserver(() =&gt; {
        handleTitleClean();
    });
 
    // &lt;title&gt;タグそのものを監視対象にする
    const titleElement = document.querySelector(&#039;title&#039;);
    if (titleElement) {
        titleObserver.observe(titleElement, { childList: true });
    }
 
    // 初回実行
    handleTitleClean();
 
    // 二重取得防止用のフラグ
    let isFetchingNotice = false;
    // --- お知らせ取得関数 ---
    async function fetchBoardNotice() {
        const searchInput = document.querySelector(&#039;input[name=&quot;q&quot;]&#039;);
        if (!searchInput) return;
        const searchDiv = searchInput.closest(&#039;div&#039;);
 
        // 取得中、または既に要素が存在するなら即終了
        if (isFetchingNotice || document.getElementById(&#039;ext_board_notice&#039;)) return;
 
        isFetchingNotice = true; // 取得開始フラグを立てる
 
        try {
            // URLから板名を取得
            const boardMatch = window.location.href.match(/\.net\/test\/read\.cgi\/([^\/]+)/);
            if (!boardMatch) return;
 
            const boardName = boardMatch[1];
            const boardUrl = `${window.location.origin}/${boardName}/`;
 
            // 板のTOPページを取得
            const response = await fetch(boardUrl);
            if (!response.ok) return;
            const text = await response.text();
 
            const parser = new DOMParser();
            const doc = parser.parseFromString(text, &#039;text/html&#039;);
 
            // 背景色付きのテーブルを抽出
            const tables = doc.querySelectorAll(&#039;table[bgcolor]&#039;);
            let noticeHTML = &quot;&quot;;
 
            // 2番目のテーブルがお知らせ枠である可能性が高い（なんJ等）
            if (tables.length &gt;= 2) {
                const secondTable = tables[1];
                // 中身がスレ一覧（リンク集）でないことを確認して採用
                if (!secondTable.querySelector(&#039;a[href^=&quot;/test/read.cgi/&quot;]&#039;)) {
                    noticeHTML = secondTable.innerHTML;
                }
            }
 
            // 挿入直前にもう一度だけ要素チェック（念のためのガード）
            if (noticeHTML &amp;&amp; !document.getElementById(&#039;ext_board_notice&#039;)) {
                const noticeSpan = document.createElement(&#039;span&#039;);
                noticeSpan.id = &#039;ext_board_notice&#039;;
 
                const cleanHTML = noticeHTML
                    .replace(/&lt;tr[^&gt;]*&gt;|&lt;td[^&gt;]*&gt;|&lt;\/tr&gt;|&lt;\/td&gt;|&lt;table[^&gt;]*&gt;|&lt;\/table&gt;/gi, &#039;&#039;)
                    .trim();
 
                noticeSpan.innerHTML = cleanHTML;
                searchDiv.after(noticeSpan);
            }
        } catch (e) {
            console.error(&quot;お知らせ取得エラー:&quot;, e);
        } finally {
            // 成功・失敗に関わらずフラグを下ろすが、要素が既にあれば何もしない
            isFetchingNotice = false;
        }
    }
})();
}}
#endregion

作者:WaiON
#region(おんJ スレ主IDチェッカー)
使う時はCHECK_LIMITと取得時間調節するとおすすめ
https://greasyfork.org/ja/scripts/563392-%E3%81%8A%E3%82%93j-%E3%82%B9%E3%83%AC%E4%B8%BBid%E3%83%81%E3%82%A7%E3%83%83%E3%82%AB%E3%83%BC/code
#highlight(){{
// ==UserScript==
// @name         おんJ スレ主IDチェッカー
// @namespace    http://tampermonkey.net/
// @version      3.7
// @author       WaiON
// @description  おんJでスレ主のIDをチェックし、NGIDが立てたスレは警告/非表示する。
// @match        https://hayabusa.open2ch.net/livejupiter/*
// @match        https://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @license      MIT
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    //iOS26.1(Safari)で検証済み
  
    // ==================== 設定エリア ====================
    const CONFIG = {
        CHECK_LIMIT: 10,           // 自動取得する上位スレッド数
        WAIT_MIN: 800,             // 取得間隔 下限 (ms)
        WAIT_MAX: 1600,            // 取得間隔 上限 (ms)
        CACHE_EXPIRY_HOURS: 4,     // キャッシュ有効期限 (時間)
        ID_LIST_KEY: &#039;ignv4livejupiter&#039;,
        CACHE_KEY: &#039;ignv_cache&#039;,
        HIDE_NG: false,            // 警告/NGのスレッドを消す場合は true
 
        // 各表示ページでの動作設定 (true で有効 / false で無効)
        TARGET_VIEWS: {
            &#039;&#039;: true,             // デフォルト(リンクの最後に#がつかない場合)
            &#039;#ikioi&#039;: true,       // 勢い順
            &#039;#created&#039;: true,     // 新スレ
            &#039;#ninzu&#039;: false,       // 人数順
            &#039;#updated&#039;: false,     // 新レス順
            &#039;#live&#039;: false,        // 配信
            &#039;#history&#039;: false      // 履歴
        }
    };
    // ====================================================
 
    const COLORS = { 
        LOADING: &#039;#FFEB3B&#039;,
        WARNING: &#039;#F44336&#039;,
        SAFE: &#039;#4CAF50&#039;,
        ERROR: &#039;#000000&#039;
    };
 
    const CACHE_EXPIRY_MS = CONFIG.CACHE_EXPIRY_HOURS * 60 * 60 * 1000;
    let fetchedCount = 0;
    let isProcessing = false;
    let queue = [];
    let observer = null;
 
    // 現在のハッシュ値が設定で有効になっているか確認
    const isTargetPage = () =&gt; {
        const hash = window.location.hash;
        return !!CONFIG.TARGET_VIEWS[hash];
    };
 
    const isThreadReadPage = () =&gt; window.location.pathname.includes(&#039;/test/read.cgi/livejupiter/&#039;);
 
    const getBlackList = () =&gt; {
        const raw = localStorage.getItem(CONFIG.ID_LIST_KEY) || &quot;&quot;;
        return raw.split(&#039;&lt;D&gt;&#039;).map(id =&gt; id.trim()).filter(id =&gt; id.length &gt; 0);
    };
 
    const getCache = () =&gt; JSON.parse(localStorage.getItem(CONFIG.CACHE_KEY) || &quot;{}&quot;);
    
    const setCache = (url, ownerId) =&gt; {
        const cache = getCache();
        cache[url] = { ownerId, date: Date.now() };
        localStorage.setItem(CONFIG.CACHE_KEY, JSON.stringify(cache));
    };
 
    const cleanOldCache = () =&gt; {
        const cache = getCache();
        const now = Date.now();
        let changed = false;
        for (const url in cache) {
            if (now - cache[url].date &gt; CACHE_EXPIRY_MS) {
                delete cache[url];
                changed = true;
            }
        }
        if (changed) localStorage.setItem(CONFIG.CACHE_KEY, JSON.stringify(cache));
    };
 
    const normalizeUrl = (url) =&gt; {
        const match = url.match(/\/test\/read\.cgi\/livejupiter\/\d+/);
        return match ? match[0] : url;
    };
 
    const applyJudgment = (threadElem, lineElem, isNG, isError = false) =&gt; {
        if (!threadElem || !lineElem) return;
        if (isError) {
            lineElem.style.backgroundColor = COLORS.ERROR;
            return;
        }
        if (isNG) {
            if (CONFIG.HIDE_NG) {
                threadElem.style.display = &#039;none&#039;;
            } else {
                lineElem.style.backgroundColor = COLORS.WARNING;
            }
        } else {
            lineElem.style.backgroundColor = COLORS.SAFE;
            threadElem.style.display = &#039;&#039;; 
        }
    };
 
    async function processQueue() {
        if (isProcessing || queue.length === 0) return;
        isProcessing = true;
 
        try {
            const blackList = getBlackList();
            while (queue.length &gt; 0) {
                // 処理中にページが対象外（無効なハッシュ）に変わったら中止
                if (!isTargetPage() || document.visibilityState === &#039;hidden&#039;) break;
 
                const task = queue.shift();
                const normalized = normalizeUrl(task.threadUrl);
                
                const cache = getCache();
                if (cache[normalized]) {
                    applyJudgment(task.threadElem, task.lineElem, blackList.includes(cache[normalized].ownerId));
                    continue;
                }
 
                if (fetchedCount &gt;= CONFIG.CHECK_LIMIT) {
                    task.lineElem.remove();
                    continue;
                }
 
                try {
                    fetchedCount++;
                    const res = await fetch(normalized + &quot;/-1&quot;);
                    const html = await res.text();
                    const idMatch = html.match(/class=&quot;_id&quot;[^&gt;]+val=&quot;([^&quot;]+)&quot;/) || html.match(/val=&quot;([^&quot;]+)&quot;[^&gt;]+class=&quot;_id&quot;/);
                    const ownerId = idMatch ? idMatch[1] : null;
 
                    if (ownerId) {
                        setCache(normalized, ownerId);
                        applyJudgment(task.threadElem, task.lineElem, blackList.includes(ownerId));
                    }
                } catch (e) {
                    applyJudgment(task.threadElem, task.lineElem, false, true);
                }
 
                if (queue.length &gt; 0) {
                    const waitTime = Math.floor(Math.random() * (CONFIG.WAIT_MAX - CONFIG.WAIT_MIN + 1)) + CONFIG.WAIT_MIN;
                    await new Promise(r =&gt; setTimeout(r, waitTime));
                }
            }
        } finally {
            isProcessing = false;
        }
    }
 
    function initUI() {
        if (!isTargetPage()) {
            document.querySelectorAll(&#039;.id-checker-line&#039;).forEach(el =&gt; el.remove());
            queue = [];
            return;
        }
 
        const threads = Array.from(document.querySelectorAll(&#039;.thread&#039;));
        const cache = getCache();
        const blackList = getBlackList();
        let addedToQueue = false;
 
        threads.forEach((thread, index) =&gt; {
            if (thread.querySelector(&#039;.id-checker-line&#039;)) return;
            const link = thread.querySelector(&#039;a&#039;);
            if (!link) return;
 
            const url = link.href;
            const normalized = normalizeUrl(url);
            const cachedData = cache[normalized];
 
            thread.style.position = &#039;relative&#039;;
            const line = document.createElement(&#039;div&#039;);
            line.className = &#039;id-checker-line&#039;;
            line.style.cssText = `position: absolute; top: 2px; bottom: 2px; right: 0; width: 6px; border-radius: 2px; z-index: 10; pointer-events: none;`;
            thread.appendChild(line);
 
            if (cachedData) {
                applyJudgment(thread, line, blackList.includes(cachedData.ownerId));
            } else if (index &lt; CONFIG.CHECK_LIMIT) {
                line.style.backgroundColor = COLORS.LOADING;
                queue.push({ threadUrl: url, lineElem: line, threadElem: thread });
                addedToQueue = true;
            }
        });
 
        if (addedToQueue &amp;&amp; !isProcessing) processQueue();
    }
 
    const resetAndStart = () =&gt; {
        isProcessing = false;
        queue = [];
        fetchedCount = 0;
        
        document.querySelectorAll(&#039;.id-checker-line&#039;).forEach(el =&gt; el.remove());
        
        if (isThreadReadPage()) {
            const idSpan = document.querySelector(&#039;li[val=&quot;1&quot;] ._id, dl[val=&quot;1&quot;] ._id&#039;);
            if (idSpan) setCache(normalizeUrl(window.location.href), idSpan.getAttribute(&#039;val&#039;));
            return;
        }
 
        if (!isTargetPage()) return;
 
        cleanOldCache();
        if (observer) observer.disconnect();
        
        const container = document.querySelector(&#039;#current_thread_list&#039;) || document.querySelector(&#039;#thread_list&#039;) || document.body;
        observer = new MutationObserver(() =&gt; initUI());
        observer.observe(container, { childList: true, subtree: true });
        
        initUI();
    };
 
    window.addEventListener(&#039;hashchange&#039;, resetAndStart);
    window.addEventListener(&#039;popstate&#039;, resetAndStart);
    document.addEventListener(&#039;visibilitychange&#039;, () =&gt; {
        if (document.visibilityState === &#039;visible&#039;) processQueue();
    });
 
    if (document.readyState === &#039;loading&#039;) {
        document.addEventListener(&#039;DOMContentLoaded&#039;, resetAndStart);
    } else {
        resetAndStart();
    }
})();
}}
#endregion

#region()
コメント:
#highlight(){{

}}
#endregion
#region()
コメント:
#highlight(){{

}}
#endregion    </description>
    <dc:date>2026-02-21T11:16:56+09:00</dc:date>
    <utime>1771640216</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/11.html">
    <title>リンク</title>
    <link>https://w.atwiki.jp/superopenj/pages/11.html</link>
    <description>
      **リンク

&amp;bold(){スクリプト関係}
-https://greasyfork.org/ja
-https://w.atwiki.jp/openj3/pages/372.html
-https://open-jupiter-umamusume.game-info.wiki/d/%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%ec%a5%c3%a5%c8

&amp;bold(){...}
-
-
-    </description>
    <dc:date>2026-02-11T17:15:25+09:00</dc:date>
    <utime>1770797725</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/2.html">
    <title>メニュー</title>
    <link>https://w.atwiki.jp/superopenj/pages/2.html</link>
    <description>
      **メニュー
[[トップページ]]
[[スクリプト]]
[[リンク]]
**リンク
-[[@wiki&gt;&gt;http://atwiki.jp]]
-[[@wikiご利用ガイド&gt;&gt;http://atwiki.jp/guide/]]

// リンクを張るには &quot;[&quot; 2つで文字列を括ります。
// &quot;&gt;&quot; の左側に文字、右側にURLを記述するとリンクになります


//**更新履歴
//#recent(20)

&amp;link_editmenu(text=ここを編集)
#endregion    </description>
    <dc:date>2026-02-11T17:13:55+09:00</dc:date>
    <utime>1770797635</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/10.html">
    <title>スクリプト</title>
    <link>https://w.atwiki.jp/superopenj/pages/10.html</link>
    <description>
      **スクリプト（仮）
拾ったやつとかGreasyforkで公開されてるスクリプトのまとめ

作者:onjmin
#region(Open2ch アラビア文字規制避け)
#highlight(){{
// ==UserScript==
// @name         Open2ch アラビア文字規制避け
// @namespace    https://onjmin.glitch.me/
// @version      1.0.0
// @description  アラビア文字を入力したときピンクで警告
// @author       おんJ民
// @match        *://*.open2ch.net/*/*
// @icon         https://avatars.githubusercontent.com/u/88383494
// @grant        none
// @license      GNU Affero General Public License v3.0 or later
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
    const ngList = [
        8239,
        65279
    ];
    window.document.addEventListener(&#039;paste&#039;, (e) =&gt; {
        if (e.target.tagName !== &#039;TEXTAREA&#039;) return;
        const pasteData = (e.clipboardData || window.clipboardData).getData(&#039;text&#039;);
        for (const ng of ngList) {
            if (pasteData.includes(String.fromCharCode(ng))) {
                e.target.style.backgroundColor = &#039;pink&#039;;
                [...document.querySelectorAll(&#039;[type=&quot;submit&quot;]&#039;)].forEach(e =&gt; {
                    e.disabled = true;
                });
            }
        }
    });
})();
}}
#endregion
#region(Open2ch Imgur一括削除)
#highlight(){{
// ==UserScript==
// @name         Open2ch Imgur一括削除
// @namespace    https://onjmin.glitch.me/
// @version      1.0.0
// @description  ポチポチせず削除できる
// @author       おんJ民
// @match        https://hayabusa.open2ch.net/setting/imgur_history.cgi
// @icon         https://avatars.githubusercontent.com/u/88383494
// @grant        GM.registerMenuCommand
// @grant        GM.getValue
// @grant        GM.setValue
// @license      GNU Affero General Public License v3.0 or later
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
    const key = &quot;clientId&quot;;
 
    GM.registerMenuCommand(&#039;Imgur APIのClient-ID登録&#039;, async () =&gt; {
        const clientId = await GM.getValue(key);
        const v = prompt(`Imgur APIのClient-ID入力（現在値：${clientId}）`);
        if (v) GM.setValue(key, v);
    });
 
    // [[deletehash, imgurID]...]
    const extractDeleteList = () =&gt; [...document.querySelectorAll(&quot;a&quot;)].map(v=&gt;v.href).flatMap(v=&gt;v.match(/imgur_delete.+:.+:/)?.[0] ?? []).map(v =&gt; v.slice(0,-1).split(/\=|:/).slice(-2));
 
    const onClick = async () =&gt; {
        const list = extractDeleteList();
 
        if (!list.length) {
            alert(&#039;削除対象が見つかりませんでした&#039;);
            return;
        }
 
        if (!confirm(`${list.length} 件を本当に削除しますか？`)) return;
        const clientId = await GM.getValue(key);
        if (!clientId) return alert(&quot;Imgur APIのClient-ID登録を済ませてください&quot;);
 
        for (const [deletehash, imgurID] of list) {
            try {
                const res = await fetch(`https://api.imgur.com/3/image/${deletehash}`, {
                    method: &#039;DELETE&#039;,
                    headers: {
                        &#039;Authorization&#039;: `Client-ID ${clientId}`
                    }
                });
                const json = await res.json();
                json.success
                    ? console.log(`Deleted: ${imgurID}`)
                : console.warn(`Failed: ${imgurID}`, json);
            } catch (err) {
                console.error(`Error deleting ${imgurID}:`, err);
            }
        }
 
        alert(&#039;削除処理が完了しました（詳細はコンソールを確認）&#039;);
    };
 
    const button = Object.assign(document.createElement(&#039;button&#039;), {
        textContent: &#039;Imgur Bulk Delete&#039;,
        style: `
                position: fixed;
                bottom: 20px;
                right: 20px;
                z-index: 9999;
                padding: 10px 15px;
                background-color: #d9534f;
                color: white;
                border: none;
                border-radius: 5px;
                cursor: pointer;
            `
    });
    button.addEventListener(&#039;click&#039;, onClick);
    document.body.appendChild(button);
})();
}}
#endregion
#region(Open2ch 強制コテ化スクリプト)
#highlight(){{
// ==UserScript==
// @name         Open2ch 強制コテ化スクリプト
// @namespace    https://onjmin.glitch.me/
// @version      1.0.1
// @description  IDをもとにアイコンを自動生成
// @author       おんJ民
// @match        *://*.open2ch.net/*/*
// @icon         https://avatars.githubusercontent.com/u/88383494
// @grant        none
// @license      GNU Affero General Public License v3.0 or later
// ==/UserScript==
 
(async ()=&gt; {
    &#039;use strict&#039;;
 
    const currentKey = &#039;robohash&#039;;
    // const currentKey = &#039;dicebear/big-smile&#039;;
    const avatarModes = [
        {
            key: &quot;robohash&quot;,
            url: (hash) =&gt; `https://robohash.org/${hash}.png`,
            flip: true,
        },
        {
            key: &quot;dicebear&quot;,
            url: (hash) =&gt;
            `https://api.dicebear.com/8.x/adventurer/svg?seed=${hash}`,
            flip: true,
        },
        {
            key: &quot;gravatar&quot;,
            url: (hash) =&gt; `https://www.gravatar.com/avatar/${hash}?d=identicon`,
            flip: false,
        },
    ];
 
    const dicebearList = [
        &quot;adventurer&quot;, // 冒険者風キャラ（RPGっぽい）
        &quot;avataaars&quot;, // Avataaarsベースのカートゥーン
        &quot;big-ears&quot;, // 大きな耳が特徴のスタイル
        &quot;big-smile&quot;, // 巨大スマイル系アバター
        &quot;bottts&quot;, // ボット風ロボットアバター
        &quot;croodles&quot;, // ラフな手描きキャラ
        &quot;identicon&quot;, // GitHub風識別子
        &quot;initials&quot;, // 名前のイニシャルを表示
        &quot;lorelei&quot;, // 魔法使いや精霊風の人物像
        &quot;miniavs&quot;, // シンプルなミニアバター
        &quot;notionists&quot;, // Notion風人物
        &quot;open-peeps&quot;, // カジュアルな人物画（肩まで）
        &quot;personas&quot;, // ややリアル寄りの人物
        &quot;pixel-art&quot;, // ドット絵スタイル
    ];
 
    const dicebear = avatarModes.find(({key}) =&gt; key === &quot;dicebear&quot;);
    for (const v of dicebearList) {
        avatarModes.push({
            ...dicebear,
            key: `dicebear/${v}`,
            url: (hash) =&gt; `https://api.dicebear.com/8.x/${v}/svg?seed=${hash}`,
            flip: v.includes(&#039;adventurer&#039;)
        });
    }
 
    // ✅ アバター要素生成
    const createAvatar = (url, flip) =&gt; {
        const isSVG = url.endsWith(&quot;.svg&quot;);
        const img = document.createElement(&quot;img&quot;);
        Object.assign(img, {
            src: url,
            width: 45,
            height: 45,
        });
        Object.assign(img.style, {
            padding: &quot;3px&quot;,
            background: isSVG ? &quot;transparent&quot; : &quot;rgb(238, 238, 255)&quot;,
            marginRight: &quot;3px&quot;,
            borderRadius: &quot;45px&quot;,
            display: &quot;block&quot;,
            transform: flip ? &quot;scaleX(-1)&quot; : &quot;&quot;,
        });
        img.setAttribute(&quot;iconimg&quot;, &quot;1&quot;);
        img.setAttribute(&quot;align&quot;, &quot;left&quot;);
        img.classList.add(&quot;pic&quot;, &quot;lazy&quot;, &quot;imgur&quot;);
 
        const a = document.createElement(&quot;a&quot;);
        a.href = url;
        a.target = &quot;_blank&quot;;
        a.setAttribute(&quot;data-lightbox&quot;, &quot;i&quot;);
        a.appendChild(img);
 
        return a;
    };
 
    const extractID = (dt) =&gt; {
        const span = dt.querySelector(&quot;span._id&quot;);
        if (span?.getAttribute(&quot;val&quot;)) return span.getAttribute(&quot;val&quot;);
        const match = [...dt.classList].find((cls) =&gt; cls.startsWith(&quot;id&quot;));
        return match?.slice(2).trim();
    };
 
    const sha256 = async text =&gt;
    Array.from(new Uint8Array(await crypto.subtle.digest(&quot;SHA-256&quot;, new TextEncoder().encode(text))))
    .map(b =&gt; b.toString(16).padStart(2, &quot;0&quot;))
    .join(&quot;&quot;);
 
    const processPosts = async (nodes) =&gt; {
        for (const node of nodes) {
            if (!(node instanceof HTMLElement)) continue;
            const dts = node.querySelectorAll?.(&quot;dt[class*=&#039;id&#039;]&quot;) ?? [];
            for (const dt of dts) {
                const id = extractID(dt);
                if (!id) continue;
 
                const dd = dt.nextElementSibling;
                if (!dd?.matches(&quot;dd&quot;)) continue;
 
                // 削除既存アバター
                dd.querySelectorAll(&#039;img[iconimg=&quot;1&quot;]&#039;).forEach((img) =&gt; {
                    const a = img.closest(&quot;a&quot;);
                    a &amp;&amp; a.parentElement === dd ? a.remove() : img.remove();
                });
 
                const mode = avatarModes.find((m) =&gt; m.key === currentKey);
                if (!mode) return;
 
                let hash = &#039;&#039;;
                if (/[^0-9a-zA-Z]/.test(id)) {
                    const str = id.replace(/[^0-9a-zA-Z]/g, &#039;&#039;);
                    if (str.length) {
                        hash = str;
                    } else {
                        hash = (await sha256(str)).slice(32);
                    }
                } else {
                    hash = id;
                }
 
                const avatarURL = mode.url(hash);
                const avatar = createAvatar(avatarURL, mode.flip);
 
                const icon = dd.querySelector(&quot;icon&quot;);
                icon
                    ? dd.insertBefore(avatar, icon)
                : dd.insertBefore(avatar, dd.firstChild);
            }
        }
    };
 
    const thread = document.querySelector(&quot;.thread&quot;);
    if (thread) {
        // 初回に既存要素も処理
        await processPosts(thread.children);
 
        // 監視設定
        const observer = new MutationObserver((mutations) =&gt; {
            const added = mutations.flatMap((m) =&gt; [...m.addedNodes]);
            processPosts(added);
        });
 
        observer.observe(thread, { childList: true, subtree: true });
    }
 
})();
}}
#endregion

作者:K9Y_onj
#region(Open2chの返信音を変える)
#highlight(){{
// ==UserScript==
// @name         Open2chの返信音を変える
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  hayabusaの方
// @author       icchi
// @match        https://hayabusa.open2ch.net/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    const targetURLs = {
        &#039;https://image.open2ch.net/lib/sound/sound.mp3&#039;:
            &#039;https://raw.githubusercontent.com/Bongocat27/onj-sound-rawlink/main/%E3%83%9A%E3%82%BF%E3%83%83.mp3&#039;,
        &#039;https://image.open2ch.net/lib/sound/drum-japanese2.mp3&#039;:
            &#039;https://raw.githubusercontent.com/Bongocat27/onj-sound-rawlink/main/%E3%83%8B%E3%83%A5%E3%833.mp3&#039;
    };
 
    const originalAudio = window.Audio;
    window.Audio = function(src) {
        for (const [target, custom] of Object.entries(targetURLs)) {
            if (src &amp;&amp; src.includes(target)) {
                console.log(`[Tampermonkey] 音源を置き換え: ${src} → ${custom}`);
                return new originalAudio(custom);
            }
        }
        return new originalAudio(src);
    };
 
    const originalPlay = HTMLAudioElement.prototype.play;
    HTMLAudioElement.prototype.play = function() {
        for (const [target, custom] of Object.entries(targetURLs)) {
            if (this.src.includes(target)) {
                console.log(`[Tampermonkey] 音源を置き換え: ${this.src} → ${custom}`);
                this.src = custom;
            }
        }
        return originalPlay.call(this);
    };
})();
}}
#endregion
#region(うんはらバスター)
#highlight(){{
// ==UserScript==
// @name         うんはらバスター
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  おんjで画像を表示するか逐一確認！
// @author       icchi
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    function checkImages() {
        document.querySelectorAll(&#039;img&#039;).forEach(img =&gt; {
            if (img.src.includes(&quot;imgur.com&quot;) &amp;&amp; !img.dataset.checked) { // imgur画像で未処理のもの
                img.dataset.checked = &quot;true&quot;; // 重複処理防止
                img.style.display = &quot;none&quot;; // 画像を非表示
                let btn = document.createElement(&quot;button&quot;);
                btn.textContent = &quot;画像を表示する&quot;;
                btn.style.margin = &quot;5px&quot;;
                btn.onclick = function() {
                    img.style.display = &quot;block&quot;; // 画像を表示
                    btn.remove(); // ボタン削除
                };
                img.insertAdjacentElement(&quot;beforebegin&quot;, btn);
            }
        });
    }
 
    // 初回実行
    checkImages();
 
    // ページの変更を監視（新しく読み込まれた画像にも対応）
    let observer = new MutationObserver(checkImages);
    observer.observe(document.body, { childList: true, subtree: true });
})();
}}
#endregion
#region(うんはらバスターV2)
#highlight(){{
// ==UserScript==
// @name         うんはらバスターV2
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  おんjで画像を表示するか逐一確認！
// @author       icchi
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    function checkImages(targetNode = document) {
        targetNode.querySelectorAll(&#039;img.imgur, img[src*=&quot;imgur.com&quot;]&#039;).forEach(img =&gt; {
            if (!img.dataset.checked) { // 未処理の画像のみ対象
                img.dataset.checked = &quot;true&quot;; // 処理済みマーク
 
                // 画像を非表示
                img.style.display = &quot;none&quot;;
 
                // 親の &lt;a&gt; タグを探してリンクを無効化
                let parentLink = img.closest(&#039;a&#039;);
                if (parentLink) {
                    parentLink.dataset.originalHref = parentLink.href; // 元のリンクを保存
                    parentLink.removeAttribute(&quot;href&quot;); // 無効化
                }
 
                // ボタン作成
                let btn = document.createElement(&quot;button&quot;);
                btn.textContent = &quot;画像を表示する&quot;;
                btn.style.margin = &quot;5px&quot;;
                btn.onclick = function(event) {
                    event.preventDefault(); // サイト遷移防止
                    img.style.display = &quot;block&quot;; // 画像表示
                    btn.remove(); // ボタン削除
 
                    // 親の &lt;a&gt; タグのリンクを復元
                    if (parentLink &amp;&amp; parentLink.dataset.originalHref) {
                        parentLink.href = parentLink.dataset.originalHref;
                    }
                };
 
                // ボタンを画像の前に挿入
                img.insertAdjacentElement(&quot;beforebegin&quot;, btn);
            }
        });
    }
 
    function resetImages() {
        document.querySelectorAll(&#039;img.imgur[data-checked], img[src*=&quot;imgur.com&quot;][data-checked]&#039;).forEach(img =&gt; {
            img.removeAttribute(&quot;data-checked&quot;); // 処理リセット
        });
    }
 
    // 初回実行
    checkImages();
 
    // ページの変更を監視（新しく読み込まれた画像にも対応）
    let observer = new MutationObserver(mutations =&gt; {
        mutations.forEach(mutation =&gt; {
            mutation.addedNodes.forEach(node =&gt; {
                if (node.nodeType === 1) {
                    if (node.tagName === &quot;IMG&quot; &amp;&amp; node.src.includes(&quot;imgur.com&quot;)) {
                        checkImages(node.parentNode); // 追加されたimgur画像を処理
                    } else if (node.querySelectorAll) {
                        checkImages(node); // 追加された要素内の画像を処理
                    }
                }
            });
        });
    });
    observer.observe(document.body, { childList: true, subtree: true });
 
    // 「前のレスを取得」ボタンが押されたら、新しく追加された要素を処理
    document.body.addEventListener(&quot;click&quot;, (event) =&gt; {
        if (event.target.closest(&quot;.prev_bt&quot;)) {
            setTimeout(() =&gt; checkImages(), 1000); // レス取得後に適用
        }
    });
 
    // 履歴操作を監視してスレ移動時にも対応
    window.addEventListener(&#039;popstate&#039;, () =&gt; {
        resetImages();
        setTimeout(() =&gt; checkImages(), 500);
    });
 
    // pushState / replaceState を監視するためのフック
    let originalPushState = history.pushState;
    let originalReplaceState = history.replaceState;
 
    function hookHistoryMethod(original) {
        return function() {
            let result = original.apply(this, arguments);
            resetImages();
            setTimeout(() =&gt; checkImages(), 500);
            return result;
        };
    }
 
    history.pushState = hookHistoryMethod(originalPushState);
    history.replaceState = hookHistoryMethod(originalReplaceState);
 
})();
}}
#endregion
#region(うんはらバスターV2（モザイクON/OFF専用）)
#highlight(){{
// ==UserScript==
// @name         うんはらバスターV2（モザイクON/OFF専用）
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  おんjで画像にモザイクをかけたり外したり！クリックで拡大されないように修正
// @author       icchi
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    function checkImages(targetNode = document) {
        targetNode.querySelectorAll(&#039;img.imgur, img[src*=&quot;imgur.com&quot;]&#039;).forEach(img =&gt; {
            if (!img.dataset.checked) { // 未処理の画像のみ対象
                img.dataset.checked = &quot;true&quot;; // 処理済みマーク
 
                // 初期状態ではモザイクをかける
                img.style.filter = &quot;blur(10px)&quot;;
                img.style.transition = &quot;filter 0.3s ease&quot;;
 
                // モザイク切り替えボタン作成
                let btn = document.createElement(&quot;button&quot;);
                btn.textContent = &quot;モザイク切り替え&quot;;
                btn.style.margin = &quot;5px&quot;;
                btn.onclick = function(event) {
                    event.preventDefault(); // サイト遷移防止
                    if (img.style.filter === &quot;none&quot;) {
                        img.style.filter = &quot;blur(10px)&quot;; // モザイクON
                    } else {
                        img.style.filter = &quot;none&quot;; // モザイクOFF
                    }
                };
 
                // ボタンを画像の前に挿入
                img.insertAdjacentElement(&quot;beforebegin&quot;, btn);
 
                // 拡大防止：画像がクリックされた際にリンクを無効化
                let parentLink = img.closest(&#039;a&#039;);
                if (parentLink) {
                    parentLink.addEventListener(&#039;click&#039;, function(event) {
                        event.preventDefault(); // 画像クリックで拡大しないようにする
                    });
                }
            }
        });
    }
 
    function resetImages() {
        document.querySelectorAll(&#039;img.imgur[data-checked], img[src*=&quot;imgur.com&quot;][data-checked]&#039;).forEach(img =&gt; {
            img.removeAttribute(&quot;data-checked&quot;); // 処理リセット
        });
    }
 
    // 初回実行
    checkImages();
 
    // ページの変更を監視（新しく読み込まれた画像にも対応）
    let observer = new MutationObserver(mutations =&gt; {
        mutations.forEach(mutation =&gt; {
            mutation.addedNodes.forEach(node =&gt; {
                if (node.nodeType === 1) {
                    if (node.tagName === &quot;IMG&quot; &amp;&amp; node.src.includes(&quot;imgur.com&quot;)) {
                        checkImages(node.parentNode); // 追加されたimgur画像を処理
                    } else if (node.querySelectorAll) {
                        checkImages(node); // 追加された要素内の画像を処理
                    }
                }
            });
        });
    });
    observer.observe(document.body, { childList: true, subtree: true });
 
    // 「前のレスを取得」ボタンが押されたら、新しく追加された要素を処理
    document.body.addEventListener(&quot;click&quot;, (event) =&gt; {
        if (event.target.closest(&quot;.prev_bt&quot;)) {
            setTimeout(() =&gt; checkImages(), 1000); // レス取得後に適用
        }
    });
 
    // 履歴操作を監視してスレ移動時にも対応
    window.addEventListener(&#039;popstate&#039;, () =&gt; {
        resetImages();
        setTimeout(() =&gt; checkImages(), 500);
    });
 
    // pushState / replaceState を監視するためのフック
    let originalPushState = history.pushState;
    let originalReplaceState = history.replaceState;
 
    function hookHistoryMethod(original) {
        return function() {
            let result = original.apply(this, arguments);
            resetImages();
            setTimeout(() =&gt; checkImages(), 500);
            return result;
        };
    }
 
    history.pushState = hookHistoryMethod(originalPushState);
    history.replaceState = hookHistoryMethod(originalReplaceState);
 
})();
}}
#endregion
#region(うんはらバスターZ（モザイク版）)
#highlight(){{
// ==UserScript==
// @name         うんはらバスターZ（モザイク版）
// @namespace    うんはらバスターZ (モザイク版)
// @version      1.0
// @description  おんjで画像にモザイクをかけ、解除ボタンを表示！スレ移動でもモザイクが適用されるように改善！
// @author       Wai
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
(function() {
    &#039;use strict&#039;;
 
    // 画像にモザイクをかける関数
    function applyMosaic(img) {
        img.style.filter = &quot;blur(10px)&quot;; // モザイク効果
    }
 
    // モザイクを解除する関数
    function removeMosaic(img) {
        img.style.filter = &quot;none&quot;; // モザイク解除
    }
 
    // 画像の拡張子を取得する関数
    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(&#039;img&#039;).forEach(img =&gt; {
            if (img.src.includes(&quot;imgur.com&quot;) &amp;&amp; !img.dataset.checked) { // imgur画像で未処理のもの
                img.dataset.checked = &quot;true&quot;; // 重複処理防止
                applyMosaic(img); // 画像にモザイクをかける
 
                // 画像クリックで遷移を防ぐ
                img.addEventListener(&#039;click&#039;, function(event) {
                    event.preventDefault();
                });
 
                // モザイク解除ボタンを作成
                let btn = document.createElement(&quot;button&quot;);
                btn.textContent = &quot;モザイク解除&quot;;
                btn.style.margin = &quot;5px&quot;;
                btn.style.fontSize = &quot;1.2em&quot;; // ボタンの文字サイズを大きくする
                btn.style.padding = &quot;5px 10px&quot;; // ボタンのパディングを追加
                btn.onclick = function(event) {
                    event.preventDefault(); // ボタンの遷移を防止
                    removeMosaic(img); // モザイク解除
                    btn.remove(); // ボタン削除
                    // モザイクボタンを再度追加
                    let mosaicBtn = document.createElement(&quot;button&quot;);
                    mosaicBtn.textContent = &quot;モザイク&quot;;
                    mosaicBtn.style.margin = &quot;5px&quot;;
                    mosaicBtn.style.fontSize = &quot;1.2em&quot;; // ボタンの文字サイズを大きくする
                    mosaicBtn.style.padding = &quot;5px 10px&quot;; // ボタンのパディングを追加
                    mosaicBtn.onclick = function(event) {
                        event.preventDefault(); // ボタンの遷移を防止
                        applyMosaic(img); // モザイクをかける
                        mosaicBtn.remove(); // ボタン削除
                        img.insertAdjacentElement(&quot;beforebegin&quot;, btn); // モザイク解除ボタンを再度追加
                    };
                    img.insertAdjacentElement(&quot;beforebegin&quot;, mosaicBtn);
                };
                img.insertAdjacentElement(&quot;beforebegin&quot;, btn);
 
                // 拡張子を表示
                const ext = getFileExtension(img.src);
                const extDisplay = document.createElement(&quot;span&quot;); // spanに変更
                extDisplay.style.color = &quot;red&quot;;
                extDisplay.style.fontSize = &quot;1.2em&quot;; // 拡張子表示を大きくする
                extDisplay.style.marginLeft = &quot;5px&quot;; // ボタンとの間隔を調整
                extDisplay.textContent = ext ? (ext.toUpperCase() === &quot;GIF&quot; ? &quot;⚠️GIF&quot; : ext) : &quot;拡張子不明&quot;;
                img.insertAdjacentElement(&quot;afterend&quot;, extDisplay); // 拡張子を画像の後に表示
 
                // 画像URLを青文字で表示
                const urlDisplay = document.createElement(&quot;div&quot;);
                urlDisplay.style.color = &quot;blue&quot;;
                urlDisplay.style.wordWrap = &quot;break-word&quot;; // 長いURLを折り返す
                urlDisplay.textContent = img.src;
                img.insertAdjacentElement(&quot;afterend&quot;, urlDisplay);
            }
        });
    }
 
    // 初回実行
    checkImages();
 
    // 常にページを監視して、新たに追加された画像にモザイクを適用
    let observer = new MutationObserver(mutations =&gt; {
        mutations.forEach(mutation =&gt; {
            mutation.addedNodes.forEach(node =&gt; {
                if (node.nodeType === 1) { // ノードが要素ノードであるか確認
                    if (node.tagName === &quot;IMG&quot;) {
                        checkImages(node); // 新しく追加された画像をチェック
                    } else if (node.querySelectorAll) {
                        checkImages(node); // 新しく追加されたコンテンツ内に画像が含まれていればチェック
                    }
                }
            });
        });
    });
 
    // 全体のDOMを監視
    observer.observe(document.body, { childList: true, subtree: true });
 
    // 履歴操作を監視してスレ移動時にも対応
    window.addEventListener(&#039;popstate&#039;, () =&gt; {
        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);
 
})();
}}
#endregion
#region(×印押す押すくん)
#highlight(){{
// ==UserScript==
// @name         ×印押す押すくん
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  ×ボタンを押しやすく
// @author       icchi
// @match        *://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    // ×ボタンを大きくするためのCSSスタイルを追加
    const style = document.createElement(&#039;style&#039;);
    style.innerHTML = `
        .ignore {
            font-size: 20px !important; /* アイコンを大きく */
            padding: 0px !important;   /* ボタンの余白を広げる */
            display: inline-block !important; /* インラインで表示 */
            text-align: center !important; /* 中央寄せ */
        }
        .ignore i {
            font-size: inherit !important; /* アイコンもボタンのサイズに合わせる */
        }
    `;
    document.head.appendChild(style);
 
})();
}}
#endregion
#region(Open2ch 滞在時間カウンター)
#highlight(){{
// ==UserScript==
// @name         Open2ch 滞在時間カウンター
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  open2ch livejupiter 板での累計滞在時間を計測
// @match        https://hayabusa.open2ch.net/test/read.cgi/livejupiter/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    const KEY = &quot;open2ch_livejupiter_time&quot;;
    let startTime = Date.now();
    let totalTime = parseInt(localStorage.getItem(KEY) || &quot;0&quot;, 10);
    window.addEventListener(&quot;beforeunload&quot;, () =&gt; {
        let now = Date.now();
        let elapsed = Math.floor((now - startTime) / 1000); // 秒
        totalTime += elapsed;
        localStorage.setItem(KEY, totalTime.toString());
    });
 
    let counter = document.createElement(&quot;div&quot;);
    counter.style.position = &quot;fixed&quot;;
    counter.style.bottom = &quot;10px&quot;;
    counter.style.right = &quot;10px&quot;;
    counter.style.padding = &quot;5px 10px&quot;;
    counter.style.background = &quot;rgba(0,0,0,0.7)&quot;;
    counter.style.color = &quot;white&quot;;
    counter.style.fontSize = &quot;14px&quot;;
    counter.style.zIndex = 9999;
    document.body.appendChild(counter);
 
    function updateDisplay() {
        let now = Date.now();
        let elapsed = Math.floor((now - startTime) / 1000);
        let displayTime = totalTime + elapsed;
 
        let h = Math.floor(displayTime / 3600);
        let m = Math.floor((displayTime % 3600) / 60);
        let s = displayTime % 60;
 
        counter.textContent = `お前が無駄にした時間→: ${h}時間${m}分${s}秒`;
    }
 
    setInterval(updateDisplay, 1000);
})();
}}
#endregion
#region(おんｊらくちん安価)
#highlight(){{
// ==UserScript==
// @name         おんｊらくちん安価
// @namespace    K9Y_onj
// @version      1.1
// @description  Detect Ctrl + numbers (up to 3 digits) and insert formatted text
// @match        https://hayabusa.open2ch.net/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
 
    let buffer = &quot;&quot;;
    let timeout = null;
    const WAIT_TIME = 200;
 
    window.addEventListener(&#039;keydown&#039;, (e) =&gt; {
        if (e.ctrlKey &amp;&amp; /^[0-9]$/.test(e.key)) {
            e.preventDefault();
            buffer += e.key;
            if (buffer.length &gt; 3) buffer = buffer.slice(-3);
            clearTimeout(timeout);
            timeout = setTimeout(() =&gt; {
                insertText(`&gt;&gt;${buffer}\n`);
                buffer = &quot;&quot;;
            }, WAIT_TIME);
        }
    });
 
    function insertText(text) {
        const el = document.activeElement;
        if (el &amp;&amp; (el.tagName === &quot;TEXTAREA&quot; || (el.tagName === &quot;INPUT&quot; &amp;&amp; el.type === &quot;text&quot; || el.isContentEditable))) {
            const start = el.selectionStart;
            const end = el.selectionEnd;
            const value = el.value;
            el.value = value.slice(0, start) + text + value.slice(end);
            el.selectionStart = el.selectionEnd = start + text.length;
            el.dispatchEvent(new Event(&quot;input&quot;, { bubbles: true }));
        }
    }
 
})();
}}
#endregion

作者:wakawakatnt
#region(おんJ広告削除)
#highlight(){{
// ==UserScript==
// @name         おんJ広告削除
// @namespace    https://open2ch.net/
// @version      2.2
// @description  おんJの広告を自動で削除しやす
// @license	     CC0-1.0
// @match        *://*.open2ch.net/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
    const removeAds = () =&gt; {
        const targets = [
            &#039;.kokoku&#039;,
            &#039;#adspotdiv.adspot_img&#039;,
            &#039;iframe[src*=&quot;test/ad.cgi&quot;]&#039;,
            &#039;iframe[src*=&quot;i-mobile.co.jp&quot;]&#039;
        ];
 
        targets.forEach(sel =&gt; {
            document.querySelectorAll(sel).forEach(el =&gt; {
                el.remove();
            });
        });
        document.querySelectorAll(&#039;script&#039;).forEach(script =&gt; {
            const txt = script.textContent || &#039;&#039;;
            if (/i-mobile\.co\.jp|InformationIcon/.test(txt)) {
                script.remove();
            }
        });
        document.querySelectorAll(&#039;div#adspotdiv.adspot_img&#039;).forEach(el =&gt; el.remove());
    };
    removeAds();
    const observer = new MutationObserver(removeAds);
    observer.observe(document.body, { childList: true, subtree: true });
    setInterval(removeAds, 5000);
})();
 
// さとる万歳
// こっそり使えよ
// さとるに献金もしろよ
// 分かったか?
// 40からの文消してええで
}}
#endregion
#region(虹色解除)
#highlight(){{
// ==UserScript==
// @name         虹色解除
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  虹色無効化
// @author       ワイ
// @match        https://*.open2ch.net/test/read.cgi/*
// @license	     CC0-1.0
// @run-at       document-start
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
    const style = document.createElement(&#039;style&#039;);
    style.textContent = `
        h1.rainbow_css {
            background: none !important;
            -webkit-background-clip: unset !important;
            -webkit-text-fill-color: red !important;
            animation: none !important;
        }
    `;
    document.head.appendChild(style);
})();
}}
#endregion
#region(おーぷん2ch 軽量化)
#highlight(){{
// ==UserScript==
// @name         おーぷん2ch 軽量化
// @namespace    http://tampermonkey.net/
// @version      4.0
// @license	CC0-1.0
// @description  軽量化するぞい
// @match        https://*.open2ch.net/test/read.cgi/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @grant        none
// ==/UserScript==
 
(function () {
    &#039;use strict&#039;;
    $(&#039;head&#039;).append(`
&lt;style&gt;
.tuhobox,.headline_div,.headline_bar,.headlineMessage,
.headlineCommentSelected,.headline-search-bt,
.headlineStopBt,.headlineReplayBt,.headlineZoomBt,
.headlineSmallBt,#headline,#headline_frame,
.komediv,.kome,.kome_options,.radio,.paypay,
.kokoku,.copyright,.copyrights,
iframe[src*=&quot;ad&quot;],iframe[src*=&quot;headline&quot;],
.oekakiCanvas,.paintTools,.room,
.option_wrapper,.MODAL,.prev_box{display:none!important}
body{background:#eee;margin:8px;font-size:12pt}
.thread{max-width:780px;margin:auto}
dl{background:#fff;margin:8px 0;padding:12px;
   border:1px solid #ddd;border-radius:4px}
dt{background:#f5f5f5;margin:-12px -12px 8px;
   padding:6px;border-radius:4px 4px 0 0}
dd{line-height:1.8;word-break:break-word}
.name{color:#228811;font-weight:bold}
.id{color:#00e}
img{max-width:100%;height:auto;margin:8px 0;border-radius:3px}
.form_fix,.formset{
  position:sticky;bottom:0;background:#e6e6e6;
  border:1px solid #ccc;padding:10px;border-radius:4px
}
#MESSAGE{width:100%;min-height:80px;font-size:12pt}
#submit_button{width:100%;padding:10px;font-size:13pt}
&lt;/style&gt;
`);
    function purge() {
        $(&#039;.komediv,.kome,#headline,.headline_div,.headline_bar&#039;).remove();
        $(&#039;iframe&#039;).remove();
    }
    purge();
    const observer = new MutationObserver(() =&gt; purge());
    observer.observe(document.body, { childList: true, subtree: true });
})();
}}
#endregion
#region(色々サムネイル表示)
対応してる奴↓
i.imgur.com(mp4とかgifのみ　それ以外はおんJのシステムを利用)
ul.h3z.jp
tadaup.jp
i.ibb.co
i.postimg.cc
iili.io
freeimage.host
funakamome.com
#highlight(){{
// ==UserScript==
// @name         色々サムネイル表示
// @namespace    ワイ
// @version      5.2
// @license	CC0-1.0
// @description  各種画像サイトのリンクをLightboxで表示。
// @match        https://*.open2ch.net/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// ==/UserScript==
 
(function () {
    &#039;use strict&#039;;
 
    let imageDatabase = new Map();
    let imageCounter = 0;
    let globalEscHandler = null;
 
    // GIFの共通サイズ設定
    const GIF_DISPLAY_SIZE = &#039;300px&#039;;
 
    function debugLog(message, data = null) {
        console.log(`[Imgur GIF Debug] ${message}`, data || &#039;&#039;);
    }
 
    class ThumbnailCache {
        constructor() {
            this.CACHE_DURATION = 10 * 24 * 60 * 60 * 1000; // 10日間（ミリ秒）
            this.CACHE_PREFIX = &#039;thumb_cache_&#039;;
            this.cleanupOldEntries();
        }
 
        cleanupOldEntries() {
            try {
                const keys = GM_listValues();
                const now = Date.now();
                keys.forEach(key =&gt; {
                    if (key.startsWith(this.CACHE_PREFIX)) {
                        try {
                            const data = JSON.parse(GM_getValue(key, &#039;{}&#039;));
                            if (!data.timestamp || (now - data.timestamp) &gt; this.CACHE_DURATION) {
                                GM_deleteValue(key);
                            }
                        } catch (e) {
                            GM_deleteValue(key);
                        }
                    }
                });
            } catch (e) {
                console.warn(&#039;Cache cleanup failed:&#039;, e);
            }
        }
 
        generateKey(url) {
            let hash = 0;
            for (let i = 0; i &lt; url.length; i++) {
                const char = url.charCodeAt(i);
                hash = ((hash &lt;&lt; 5) - hash) + char;
                hash = hash &amp; hash;
            }
            return this.CACHE_PREFIX + Math.abs(hash).toString(36);
        }
 
        get(url) {
            try {
                const key = this.generateKey(url);
                const cached = GM_getValue(key, null);
                if (!cached) return null;
                const data = JSON.parse(cached);
                if (!data.timestamp || (Date.now() - data.timestamp) &gt; this.CACHE_DURATION) {
                    GM_deleteValue(key);
                    return null;
                }
                // base64データまたはURLを返す
                return data.base64Data || data.imageUrl;
            } catch (e) {
                console.warn(&#039;Cache get failed:&#039;, e);
                return null;
            }
        }
 
        // 画像本体をbase64で保存
        setWithBase64(url, base64Data) {
            try {
                if (/imgur/i.test(url)) return;
                const key = this.generateKey(url);
                const data = { base64Data: base64Data, timestamp: Date.now(), originalUrl: url };
                GM_setValue(key, JSON.stringify(data));
            } catch (e) {
                console.warn(&#039;Cache set failed:&#039;, e);
            }
        }
 
        set(url, imageUrl) {
            try {
                if (/imgur/i.test(url)) return;
                const key = this.generateKey(url);
                const data = { imageUrl: imageUrl, timestamp: Date.now(), originalUrl: url };
                GM_setValue(key, JSON.stringify(data));
            } catch (e) {
                console.warn(&#039;Cache set failed:&#039;, e);
            }
        }
 
        shouldCache(url) {
            return /tadaup\.jp|ul\.h3z\.jp|ibb\.co|postimg\.cc|freeimage\.host|iili\.io|funakamome\.com/i.test(url) &amp;&amp; !/imgur/i.test(url);
        }
    }
 
    const thumbnailCache = new ThumbnailCache();
 
    function closeLightbox(e) {
        if (e) e.preventDefault();
        const existingLightbox = document.getElementById(&#039;lightbox&#039;);
        if (existingLightbox) existingLightbox.remove();
        const existingOverlay = document.getElementById(&#039;lightboxOverlay&#039;);
        if (existingOverlay) existingOverlay.remove();
        if (globalEscHandler) {
            document.removeEventListener(&#039;keydown&#039;, globalEscHandler);
            globalEscHandler = null;
        }
    }
 
    function generatePostLink(postNumber) {
        const currentUrl = window.location.href;
        const baseUrl = currentUrl.split(&#039;#&#039;)[0];
        return `${baseUrl}#${postNumber}`;
    }
 
    function addCustomCSS() {
        if (document.getElementById(&#039;custom-lb-styles&#039;)) return;
        const style = document.createElement(&#039;style&#039;);
        style.id = &#039;custom-lb-styles&#039;;
        style.textContent = `
            .lightboxOverlay { position: absolute; top: 0; left: 0; z-index: 99990; background-color: black; opacity: 0.85; display: none; }
            #lightbox { position: absolute; left: 0; width: 100%; z-index: 99991; text-align: center; line-height: 0; font-family: &quot;Lucida Grande&quot;, sans-serif; }
            .gm-no-tooltip { position: relative; }
            .gm-no-tooltip::before, .gm-no-tooltip::after { display: none !important; }
            /* 画像表示時の白い枠を完全に消す */
            .gm-media-embed-container,
            .gm-media-embed-container.lp-card,
            a.lp-card.gm-media-embed-container,
            .gm-media-embed-container[class*=&quot;lp-&quot;] {
                background: transparent !important;
                background-color: transparent !important;
                border: none !important;
                box-shadow: none !important;
                padding: 0 !important;
                margin: 0 !important;
                outline: none !important;
            }
            /* 親要素urlタグのスタイルもリセット */
            url:has(.gm-media-embed-container) {
                background: transparent !important;
                border: none !important;
                box-shadow: none !important;
                padding: 0 !important;
                margin: 0 !important;
            }
            .gm-imgur-gif-container { display: inline-block; position: relative; }
            .gm-imgur-gif-wrapper { position: relative; display: inline-block; }
            .gm-thumbnail-button {
                position: absolute;
                bottom: 5px;
                right: 5px;
                width: 24px;
                height: 24px;
                background: rgba(0, 0, 0, 0.7);
                color: white;
                border: none;
                border-radius: 4px;
                font-size: 16px;
                cursor: pointer;
                transition: opacity 0.2s;
                padding: 0;
                line-height: 1;
                z-index: 10;
            }
            .gm-thumbnail-button:hover {
                opacity: 0.7;
                background: rgba(0, 0, 0, 0.9);
            }
            .gm-thumbnail-button:active {
                opacity: 0.5;
            }
            .gm-media-wrapper {
                display: inline-flex;
                align-items: flex-start;
                gap: 10px;
                vertical-align: top;
            }
            .gm-thumbnail-container {
                display: inline-block;
                margin-left: 10px;
                vertical-align: middle;
            }
            .gm-thumbnail-img {
                object-fit: contain;
                border: 1px solid #ddd;
                border-radius: 4px;
                cursor: pointer;
            }
            /* 連続画像の横並び表示用 */
            .gm-image-row {
                display: flex;
                flex-wrap: wrap;
                gap: 15px;
                align-items: flex-start;
                margin: 10px 0;
            }
            .gm-image-row .gm-media-wrapper,
            .gm-image-row .gm-media-embed-container {
                display: inline-flex;
                vertical-align: top;
            }
            /* 取得エラー表示用 */
            .gm-fetch-error {
                color: #cc0000;
                font-size: 12px;
            }
        `;
        document.head.appendChild(style);
    }
 
    function showLightbox(imageIndex) {
        const imageData = imageDatabase.get(imageIndex);
        if (!imageData) return;
        const { imageUrl, postNumber, originalUrl } = imageData;
 
        closeLightbox();
 
        const isImgur = /imgur/i.test(originalUrl);
        let serviceName = &#039;image&#039;;
        if (isImgur) serviceName = &#039;imgur&#039;;
        else if (originalUrl.includes(&#039;ibb.co&#039;)) serviceName = &#039;img.bb&#039;;
        else if (originalUrl.includes(&#039;tadaup.jp&#039;)) serviceName = &#039;tadaup&#039;;
        else if (originalUrl.includes(&#039;ul.h3z.jp&#039;)) serviceName = &#039;h3z.jp&#039;;
        else if (originalUrl.includes(&#039;postimg.cc&#039;)) serviceName = &#039;postimg.cc&#039;;
        else if (originalUrl.includes(&#039;freeimage.host&#039;) || originalUrl.includes(&#039;iili.io&#039;)) serviceName = &#039;freeimage.host&#039;;
        else if (originalUrl.includes(&#039;funakamome.com&#039;)) serviceName = &#039;funakamome.com&#039;;
 
        const overlay = document.createElement(&#039;div&#039;);
        overlay.id = &#039;lightboxOverlay&#039;;
        overlay.className = &#039;lightboxOverlay&#039;;
        overlay.style.width = &#039;100%&#039;;
        overlay.style.height = document.documentElement.scrollHeight + &#039;px&#039;;
        overlay.style.display = &#039;block&#039;;
        document.body.appendChild(overlay);
 
        const lightbox = document.createElement(&#039;div&#039;);
        lightbox.id = &#039;lightbox&#039;;
        lightbox.className = &#039;lightbox&#039;;
        lightbox.style.display = &#039;none&#039;;
 
        let detailsHTML;
        if (isImgur) {
            let board = &#039;unknown&#039;, threadId = &#039;0&#039;;
            const urlMatch = window.location.href.match(/test\/read\.cgi\/([^\/]+)\/(\d+)/);
            if (urlMatch) { board = urlMatch[1]; threadId = urlMatch[2]; }
            const pid = `${board}-${threadId}-${postNumber}`;
            const imgurPageUrl = imageUrl.replace(/\.(jpe?g|png|gif)$/i, &#039;&#039;);
            const twitterPostUrl = `https://${window.location.hostname}/test/read.cgi/${board}/${threadId}/${postNumber}-`;
            detailsHTML = `
                &lt;div class=&quot;lb-details&quot; style=&quot;min-width:300px&quot;&gt;
                    &lt;span class=&quot;lb-caption&quot; style=&quot;display: inline; cursor: pointer;&quot;&gt;
                        &lt;u class=&quot;lb-ank&quot; resnum=&quot;${postNumber}&quot; href=&quot;#&quot;&gt;&amp;gt;&amp;gt;${postNumber}&lt;/u&gt;
                    &lt;/span&gt;
                    &lt;span class=&quot;lb-number&quot; style=&quot;&quot;&gt;全${imageDatabase.size}件中、${imageIndex}件目&lt;/span&gt;
                    &lt;span style=&quot;clear: left;display: block;&quot; class=&quot;lb-save&quot;&gt;
                        &lt;div&gt;
                            &lt;a n=&quot;${postNumber}&quot; pid=&quot;${pid}&quot; class=&quot;lb-korabo-link gm-no-tooltip&quot; href=&quot;${imageUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;コラボ&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-icon gm-no-tooltip&quot; href=&quot;${imageUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;アイコン&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-search gm-no-tooltip&quot; href=&quot;https://lens.google.com/uploadbyurl?hl=ja&amp;url=${encodeURIComponent(imageUrl)}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;画像検索&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-open-link gm-no-tooltip&quot; href=&quot;${imageUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;直URL&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-open-link gm-no-tooltip&quot; href=&quot;${imgurPageUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;imgur&lt;/font&gt;&lt;/a&gt;
                        &lt;/div&gt;
                        &lt;div style=&quot;margin-top:5px&quot;&gt;
                            &lt;a class=&quot;lb-twiter gm-no-tooltip&quot; url=&quot;${twitterPostUrl}&quot; href=&quot;#&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;Twitterに貼る&lt;/font&gt;&lt;/a&gt;
                        &lt;/div&gt;
                    &lt;/span&gt;
                    &lt;span class=&quot;lb-korabo&quot;&gt;&lt;/span&gt;
                &lt;/div&gt;
            `;
        } else {
            detailsHTML = `
                &lt;div class=&quot;lb-details&quot; style=&quot;min-width:300px&quot;&gt;
                    &lt;span class=&quot;lb-caption&quot; style=&quot;display: inline; cursor: pointer;&quot;&gt;
                        &lt;u class=&quot;lb-ank&quot; resnum=&quot;${postNumber}&quot; href=&quot;#&quot;&gt;&amp;gt;&amp;gt;${postNumber}&lt;/u&gt;
                    &lt;/span&gt;
                    &lt;span style=&quot;clear: left;display: block;&quot; class=&quot;lb-save&quot;&gt;
                        &lt;div&gt;
                            &lt;a class=&quot;lb-search gm-no-tooltip&quot; href=&quot;https://lens.google.com/uploadbyurl?hl=ja&amp;url=${encodeURIComponent(imageUrl)}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;画像検索&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-open-link gm-no-tooltip&quot; href=&quot;${imageUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;直URL&lt;/font&gt;&lt;/a&gt;
                            &lt;a class=&quot;lb-service-link gm-no-tooltip&quot; href=&quot;${originalUrl}&quot;&gt;&lt;font size=&quot;2&quot; color=&quot;white&quot;&gt;${serviceName}&lt;/font&gt;&lt;/a&gt;
                        &lt;/div&gt;
                    &lt;/span&gt;
                    &lt;span class=&quot;lb-korabo&quot;&gt;&lt;/span&gt;
                &lt;/div&gt;
            `;
        }
 
        lightbox.innerHTML = `
            &lt;div class=&quot;lb-outerContainer&quot;&gt;
                &lt;div class=&quot;lb-container&quot;&gt;
                    &lt;img class=&quot;lb-image&quot; src=&quot;&quot;&gt;
                    &lt;div class=&quot;lb-nav&quot;&gt;
                        &lt;a class=&quot;lb-prev&quot; href=&quot;&quot;&gt;&lt;/a&gt;
                        &lt;a class=&quot;lb-next&quot; href=&quot;&quot;&gt;&lt;/a&gt;
                    &lt;/div&gt;
                    &lt;div class=&quot;lb-loader&quot; style=&quot;display: none;&quot;&gt;&lt;a class=&quot;lb-cancel&quot;&gt;&lt;/a&gt;&lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;lb-dataContainer&quot;&gt;
                &lt;div class=&quot;lb-data&quot;&gt;
                    ${detailsHTML}
                    &lt;div class=&quot;lb-closeContainer&quot;&gt;&lt;a class=&quot;lb-close&quot;&gt;&lt;/a&gt;&lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        `;
        document.body.appendChild(lightbox);
 
        const outerContainer = lightbox.querySelector(&#039;.lb-outerContainer&#039;);
        const dataContainer = lightbox.querySelector(&#039;.lb-dataContainer&#039;);
        const imageEl = lightbox.querySelector(&#039;.lb-image&#039;);
        const prevLink = lightbox.querySelector(&#039;.lb-prev&#039;);
        const nextLink = lightbox.querySelector(&#039;.lb-next&#039;);
        const closeButton = lightbox.querySelector(&#039;.lb-close&#039;);
        const loader = lightbox.querySelector(&#039;.lb-loader&#039;);
        const ankLink = lightbox.querySelector(&#039;.lb-ank&#039;);
 
        globalEscHandler = (e) =&gt; {
            if (e.key === &#039;Escape&#039;) { closeLightbox(e); }
        };
        document.addEventListener(&#039;keydown&#039;, globalEscHandler);
 
        overlay.addEventListener(&#039;click&#039;, (e) =&gt; {
            e.stopPropagation();
            closeLightbox();
        });
        closeButton.addEventListener(&#039;click&#039;, (e) =&gt; {
            e.preventDefault();
            e.stopPropagation();
            closeLightbox();
        });
        ankLink.addEventListener(&#039;click&#039;, (e) =&gt; {
            e.preventDefault();
            e.stopPropagation();
            closeLightbox();
            window.location.href = generatePostLink(postNumber);
        });
 
        lightbox.addEventListener(&#039;click&#039;, e =&gt; {
            if (e.target.id === &#039;lightbox&#039;) {
                 e.stopPropagation();
                 closeLightbox();
            } else {
                 e.stopPropagation();
            }
        });
 
        lightbox.querySelectorAll(&#039;.lb-save a&#039;).forEach(a =&gt; {
            if (a.href &amp;&amp; a.getAttribute(&#039;href&#039;) !== &#039;#&#039;) {
                a.target = &#039;_blank&#039;; a.rel = &#039;noopener noreferrer&#039;;
            }
            a.removeAttribute(&#039;title&#039;); a.title = &#039;&#039;;
            a.addEventListener(&#039;mouseenter&#039;, (e) =&gt; {
                e.target.removeAttribute(&#039;title&#039;); e.target.title = &#039;&#039;;
            });
        });
 
        if (imageIndex &gt; 1) {
            prevLink.style.display = &#039;block&#039;;
            prevLink.onclick = (e) =&gt; { e.preventDefault(); e.stopPropagation(); showLightbox(imageIndex - 1); };
        } else {
            prevLink.style.display = &#039;none&#039;;
        }
 
        if (imageIndex &lt; imageDatabase.size) {
            nextLink.style.display = &#039;block&#039;;
            nextLink.onclick = (e) =&gt; { e.preventDefault(); e.stopPropagation(); showLightbox(imageIndex + 1); };
        } else {
            nextLink.style.display = &#039;none&#039;;
        }
 
        loader.style.display = &#039;block&#039;;
 
        const tempImg = new Image();
        tempImg.onload = function() {
            const maxWidth = document.documentElement.clientWidth * 0.9;
            const maxHeight = document.documentElement.clientHeight * 0.9 - 80;
            let imgWidth = this.naturalWidth; let imgHeight = this.naturalHeight;
            const ratio = Math.min(maxWidth / imgWidth, maxHeight / imgHeight, 1);
            imgWidth = Math.round(imgWidth * ratio);
            imgHeight = Math.round(imgHeight * ratio);
            const framePadding = 8;
            outerContainer.style.width = `${imgWidth + framePadding}px`;
            outerContainer.style.height = `${imgHeight + framePadding}px`;
            dataContainer.style.width = `${imgWidth + framePadding}px`;
            imageEl.style.width = `${imgWidth}px`;
            imageEl.style.height = `${imgHeight}px`;
            imageEl.src = imageUrl;
            imageEl.style.display = &#039;block&#039;;
            lightbox.querySelector(&#039;.lb-nav&#039;).style.display = &#039;block&#039;;
            const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
            const clientHeight = document.documentElement.clientHeight;
            let top = scrollTop + (clientHeight - (imgHeight + framePadding + dataContainer.offsetHeight)) / 2;
            if (top &lt; scrollTop + 10) top = scrollTop + 10;
            lightbox.style.top = `${top}px`;
            lightbox.style.left = `0px`;
            loader.style.display = &#039;none&#039;;
            lightbox.style.display = &#039;block&#039;;
        };
        tempImg.onerror = function() {
            loader.textContent = &#039;画像の読み込みに失敗しました。&#039;;
            loader.style.color = &#039;#ff8a8a&#039;;
            loader.style.display = &#039;block&#039;;
        };
        tempImg.src = imageUrl;
    }
 
    function getPostNumber(element) {
        const postContainerSelectors = [
            &#039;article[id]&#039;, &#039;div.post[id]&#039;, &#039;div[data-res-id]&#039;, &#039;dl[val]&#039;,
            &#039;div.thread-post&#039;, &#039;.post-container&#039;, &#039;.message&#039;,
        ];
        const postBlock = element.closest(postContainerSelectors.join(&#039;, &#039;));
        if (postBlock) {
            const dataAttributes = [&#039;data-res-id&#039;, &#039;data-res&#039;, &#039;data-num&#039;, &#039;data-id&#039;];
            for (const attr of dataAttributes) {
                if (postBlock.hasAttribute(attr)) return postBlock.getAttribute(attr);
            }
            if (postBlock.id) {
                const match = postBlock.id.match(/\d+/);
                if (match) return match[0];
            }
            if (postBlock.hasAttribute(&#039;val&#039;)) return postBlock.getAttribute(&#039;val&#039;);
            const numElementSelectors = [&#039;.post-number&#039;, &#039;.res-number&#039;, &#039;.post-id&#039;, &#039;a.num&#039;, &#039;.num b&#039;];
            const numElement = postBlock.querySelector(numElementSelectors.join(&#039;, &#039;));
            if (numElement) {
                const match = numElement.textContent.match(/\d+/);
                if (match) return match[0];
            }
        }
        const ddElement = element.closest(&#039;dd[rnum]&#039;);
        if (ddElement &amp;&amp; ddElement.hasAttribute(&#039;rnum&#039;)) return ddElement.getAttribute(&#039;rnum&#039;);
        const dtElement = element.closest(&#039;dl&#039;)?.querySelector(&#039;dt[res]&#039;);
        if (dtElement &amp;&amp; dtElement.hasAttribute(&#039;res&#039;)) return dtElement.getAttribute(&#039;res&#039;);
        const hash = window.location.hash.match(/\d+/);
        return hash ? hash[0] : &#039;N/A&#039;;
    }
 
    function bindLightboxOnClick(imageElement, imageUrl, originalUrl) {
        if (imageElement.dataset.gmlbProcessed) return;
        imageCounter++;
        const currentImageIndex = imageCounter;
        const postNumber = getPostNumber(imageElement) || currentImageIndex;
        imageDatabase.set(currentImageIndex, { imageUrl, postNumber, originalUrl });
        imageElement.addEventListener(&#039;click&#039;, (e) =&gt; {
            e.stopPropagation(); e.preventDefault();
            showLightbox(currentImageIndex);
        }, true);
        imageElement.dataset.gmlbProcessed = &#039;1&#039;;
    }
 
    // lp-cardクラスを完全に除去する関数
    function removeLpCardStyles(element) {
        // lp-cardクラスを削除
        element.classList.remove(&#039;lp-card&#039;);
        // lp-で始まる全てのクラスを削除
        const classesToRemove = [];
        element.classList.forEach(cls =&gt; {
            if (cls.startsWith(&#039;lp-&#039;)) {
                classesToRemove.push(cls);
            }
        });
        classesToRemove.forEach(cls =&gt; element.classList.remove(cls));
        
        // インラインスタイルで強制的に透明にする
        element.style.setProperty(&#039;background&#039;, &#039;transparent&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;background-color&#039;, &#039;transparent&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;border&#039;, &#039;none&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;box-shadow&#039;, &#039;none&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;padding&#039;, &#039;0&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;margin&#039;, &#039;0&#039;, &#039;important&#039;);
        element.style.setProperty(&#039;outline&#039;, &#039;none&#039;, &#039;important&#039;);
        
        // 親要素のurlタグもスタイルをリセット
        const parentUrl = element.closest(&#039;url&#039;);
        if (parentUrl) {
            parentUrl.style.setProperty(&#039;background&#039;, &#039;transparent&#039;, &#039;important&#039;);
            parentUrl.style.setProperty(&#039;border&#039;, &#039;none&#039;, &#039;important&#039;);
            parentUrl.style.setProperty(&#039;box-shadow&#039;, &#039;none&#039;, &#039;important&#039;);
            parentUrl.style.setProperty(&#039;padding&#039;, &#039;0&#039;, &#039;important&#039;);
            parentUrl.style.setProperty(&#039;margin&#039;, &#039;0&#039;, &#039;important&#039;);
        }
    }
 
    // 取得エラーを表示する関数
    function showFetchError(linkElement, originalUrl) {
        const errorLink = document.createElement(&#039;a&#039;);
        errorLink.href = originalUrl;
        errorLink.target = &#039;_blank&#039;;
        errorLink.rel = &#039;noopener noreferrer&#039;;
        errorLink.className = &#039;gm-fetch-error&#039;;
        errorLink.textContent = &#039;取得エラー&#039;;
        
        linkElement.innerHTML = &#039;&#039;;
        linkElement.appendChild(errorLink);
        linkElement.style.display = &#039;inline&#039;;
        linkElement.onclick = null;
    }
 
    function insertThumbnail(linkElement, imageUrl, originalUrl, isFromCache = false) {
        linkElement.classList.add(&#039;gm-media-embed-container&#039;);
        removeLpCardStyles(linkElement);
        
        const img = document.createElement(&#039;img&#039;);
        img.src = imageUrl;
        img.style.cssText = `max-width:${GIF_DISPLAY_SIZE}; max-height:${GIF_DISPLAY_SIZE}; object-fit:contain; cursor:pointer; border:1px solid #ddd; border-radius:4px;`;
        img.alt = &quot;thumbnail&quot;;
        img.onerror = () =&gt; { 
            showFetchError(linkElement, originalUrl);
        };
        const container = document.createElement(&#039;div&#039;);
        container.style.display = &#039;inline-block&#039;;
        container.appendChild(img);
 
        linkElement.innerHTML = &#039;&#039;;
        linkElement.appendChild(container);
        linkElement.style.display = &#039;inline-block&#039;;
        linkElement.onclick = e =&gt; e.preventDefault();
        linkElement.removeAttribute(&#039;title&#039;);
        linkElement.onmouseover = (e) =&gt; {
            e.stopPropagation(); e.target.removeAttribute(&#039;title&#039;);
            return false;
        };
        bindLightboxOnClick(img, imageUrl, originalUrl);
    }
 
    // 画像URLが有効な拡張子を持っているかチェック
    function hasValidImageExtension(url) {
        return /\.(jpe?g|png|gif|webp)$/i.test(url);
    }
 
    // ギャラリーページとして処理すべきかチェック（postimg.cc, freeimage.host）
    function isGalleryPage(url) {
        return /^https?:\/\/postimg\.cc\/[0-9A-Za-z]+/.test(url) ||
               /^https?:\/\/freeimage\.host\/i\/[0-9A-Za-z]+/.test(url);
    }
 
    // 画像をbase64として取得してキャッシュに保存
    function fetchAndCacheImage(imageUrl, callback) {
        GM_xmlhttpRequest({
            method: &quot;GET&quot;,
            url: imageUrl,
            responseType: &quot;blob&quot;,
            onload: (res) =&gt; {
                if (res.status === 200 &amp;&amp; res.response) {
                    const reader = new FileReader();
                    reader.onloadend = function() {
                        const base64Data = reader.result;
                        thumbnailCache.setWithBase64(imageUrl, base64Data);
                        callback(base64Data);
                    };
                    reader.onerror = function() {
                        // base64変換に失敗した場合はURLを使用
                        thumbnailCache.set(imageUrl, imageUrl);
                        callback(imageUrl);
                    };
                    reader.readAsDataURL(res.response);
                } else {
                    callback(null);
                }
            },
            onerror: () =&gt; {
                callback(null);
            }
        });
    }
 
    function expandGalleryLink(a) {
        const servicePageUrl = a.href;
        const cachedImage = thumbnailCache.get(servicePageUrl);
        if (cachedImage) {
            insertThumbnail(a, cachedImage, servicePageUrl, true);
            return;
        }
        GM_xmlhttpRequest({
            method: &quot;GET&quot;, url: servicePageUrl,
            onload: (res) =&gt; {
                if (res.status === 200) {
                    const match = res.responseText.match(/&lt;meta property=&quot;og:image&quot; content=&quot;([^&quot;]+)&quot;/);
                    if (match) {
                        const imgUrl = match[1];
                        // 画像本体をbase64で取得してキャッシュ
                        fetchAndCacheImage(imgUrl, (base64Data) =&gt; {
                            if (base64Data) {
                                insertThumbnail(a, base64Data, servicePageUrl);
                            } else {
                                showFetchError(a, servicePageUrl);
                            }
                        });
                    } else {
                        showFetchError(a, servicePageUrl);
                    }
                } else {
                    showFetchError(a, servicePageUrl);
                }
            },
            onerror: () =&gt; {
                showFetchError(a, servicePageUrl);
            }
        });
    }
 
    function expandDirectLink(a) {
        const imgUrl = a.href;
        const cachedImage = thumbnailCache.get(imgUrl);
        if (cachedImage) {
            insertThumbnail(a, cachedImage, imgUrl, true);
            return;
        }
        // 画像本体をbase64で取得してキャッシュ
        fetchAndCacheImage(imgUrl, (base64Data) =&gt; {
            if (base64Data) {
                insertThumbnail(a, base64Data, imgUrl);
            } else {
                showFetchError(a, imgUrl);
            }
        });
    }
 
    function expandTadaupLink(a) {
        const originalUrl = a.href;
        
        // 拡張子がない場合は処理しない
        if (!hasValidImageExtension(originalUrl)) {
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
        
        const existingImg = a.querySelector(&#039;img&#039;);
        if (existingImg &amp;&amp; /tadaup\.jp/i.test(existingImg.src) &amp;&amp; /\.(jpg|jpeg|png|gif)$/i.test(existingImg.src)) {
            bindLightboxOnClick(existingImg, existingImg.src, originalUrl);
            a.onclick = e =&gt; e.preventDefault();
            return;
        }
        if (/\.(jpg|jpeg|png|gif)$/i.test(originalUrl)) {
            expandDirectLink(a);
        }
    }
 
    function embedImgurMp4(element) {
        if (element.dataset.mp4Processed) return;
        element.dataset.mp4Processed = &#039;1&#039;;
 
        const directUrl = element.href;
        const imgurPageUrl = directUrl.replace(/\.mp4$/i, &#039;&#039;);
 
        const container = document.createElement(&#039;div&#039;);
        container.className = &#039;gm-media-embed-container&#039;;
        container.style.cssText = &#039;display: inline-flex; align-items: center; gap: 10px; vertical-align: middle;&#039;;
 
        const video = document.createElement(&#039;video&#039;);
        video.src = directUrl;
        video.style.cssText = &#039;max-width:350px; max-height:350px; object-fit:contain; border:1px solid #ddd; border-radius:4px;&#039;;
        video.autoplay = true;
        video.loop = true;
        video.muted = true;
video.playsInline = true;
video.controls = true;
video.onerror = () =&gt; { 
    console.error(&#039;Failed to load video:&#039;, directUrl); };
    const linkContainer = document.createElement(&#039;div&#039;);
    linkContainer.style.cssText = &#039;display: flex; flex-direction: column; align-items: flex-start; font-size: 12px;&#039;;
 
    const directLink = document.createElement(&#039;a&#039;);
    directLink.href = directUrl;
    directLink.textContent = &#039;直URL&#039;;
    directLink.target = &#039;_blank&#039;;
    directLink.rel = &#039;noopener noreferrer&#039;;
    directLink.addEventListener(&#039;click&#039;, (e) =&gt; e.stopPropagation());
 
    const imgurLink = document.createElement(&#039;a&#039;);
    imgurLink.href = imgurPageUrl;
    imgurLink.textContent = &#039;imgur&#039;;
    imgurLink.target = &#039;_blank&#039;;
    imgurLink.rel = &#039;noopener noreferrer&#039;;
    imgurLink.addEventListener(&#039;click&#039;, (e) =&gt; e.stopPropagation());
 
    linkContainer.appendChild(directLink);
    linkContainer.appendChild(imgurLink);
    container.appendChild(video);
    container.appendChild(linkContainer);
 
    if (element.parentNode) {
        element.parentNode.insertBefore(container, element);
        element.remove();
    }
}
 
function replaceImgurGifWithNativeCompatibility(a) {
    if (a.dataset.gifProcessed) return;
    a.dataset.gifProcessed = &#039;1&#039;;
 
    const originalHref = a.href;
 
    const flexContainer = document.createElement(&#039;div&#039;);
    flexContainer.className = &#039;gm-media-wrapper&#039;;
    flexContainer.style.cssText = &#039;display: inline-flex; align-items: flex-start; vertical-align: top;&#039;;
 
    const wrapper = document.createElement(&#039;div&#039;);
    wrapper.className = &#039;gm-imgur-gif-wrapper&#039;;
    wrapper.style.cssText = &#039;display: inline-block; position: relative;&#039;;
 
    const autoplayImg = document.createElement(&#039;img&#039;);
    autoplayImg.src = originalHref;
    autoplayImg.style.cssText = `max-width:${GIF_DISPLAY_SIZE}; max-height:${GIF_DISPLAY_SIZE}; object-fit:contain; border:1px solid #ddd; border-radius:4px; display: block;`;
    autoplayImg.alt = &quot;Imgur GIF (Auto-playing)&quot;;
 
    const thumbnailButton = document.createElement(&#039;button&#039;);
    thumbnailButton.className = &#039;gm-thumbnail-button&#039;;
    thumbnailButton.textContent = &#039;📦&#039;;
    thumbnailButton.type = &#039;button&#039;;
    thumbnailButton.title = &#039;サムネイル表示&#039;;
 
    const thumbnailContainer = document.createElement(&#039;div&#039;);
    thumbnailContainer.className = &#039;gm-thumbnail-container&#039;;
    thumbnailContainer.style.display = &#039;none&#039;;
 
    let thumbnailLoaded = false;
 
    thumbnailButton.addEventListener(&#039;click&#039;, (e) =&gt; {
        e.preventDefault();
        e.stopPropagation();
 
        if (thumbnailContainer.style.display === &#039;none&#039;) {
            if (!thumbnailLoaded) {
                const thumbnailUrl = originalHref.replace(/\.gif$/i, &#039;m.gif&#039;);
                const thumbLink = document.createElement(&#039;a&#039;);
                thumbLink.href = originalHref;
 
                thumbLink.addEventListener(&#039;click&#039;, (clickEvent) =&gt; {
                    clickEvent.preventDefault();
                    clickEvent.stopPropagation();
                }, true);
 
                const thumbnailImg = document.createElement(&#039;img&#039;);
                thumbnailImg.className = &#039;gm-thumbnail-img&#039;;
                thumbnailImg.src = thumbnailUrl;
                thumbnailImg.alt = &#039;サムネイル&#039;;
                thumbnailImg.style.cssText = `max-width:${GIF_DISPLAY_SIZE}; max-height:${GIF_DISPLAY_SIZE}; object-fit:contain; border:1px solid #ddd; border-radius:4px; cursor:pointer;`;
                thumbnailImg.onerror = () =&gt; {
                    console.error(&#039;Failed to load thumbnail:&#039;, thumbnailUrl);
                    thumbnailImg.alt = &#039;サムネイル読み込み失敗&#039;;
                };
 
                bindLightboxOnClick(thumbnailImg, originalHref, originalHref);
 
                thumbLink.appendChild(thumbnailImg);
                thumbnailContainer.appendChild(thumbLink);
                thumbnailLoaded = true;
            }
 
            thumbnailContainer.style.display = &#039;inline-block&#039;;
            thumbnailButton.textContent = &#039;📁&#039;;
            thumbnailButton.title = &#039;サムネイル非表示&#039;;
        } else {
            thumbnailContainer.style.display = &#039;none&#039;;
            thumbnailButton.textContent = &#039;📦&#039;;
            thumbnailButton.title = &#039;サムネイル表示&#039;;
        }
    });
 
    wrapper.appendChild(autoplayImg);
    wrapper.appendChild(thumbnailButton);
 
    flexContainer.appendChild(wrapper);
    flexContainer.appendChild(thumbnailContainer);
 
    if (a.parentNode) {
        a.parentNode.insertBefore(flexContainer, a);
        a.remove();
    } else {
        return;
    }
 
    bindLightboxOnClick(autoplayImg, originalHref, originalHref);
}
 
function groupConsecutiveImages() {
    const links = document.querySelectorAll(&#039;a[href]&#039;);
    const imagePattern = /\.(jpe?g|png|gif|webp)$/i;
    const servicePattern = /imgur|tadaup\.jp|ul\.h3z\.jp|ibb\.co|postimg\.cc|freeimage\.host|iili\.io|funakamome\.com/i;
 
    let consecutiveGroups = [];
    let currentGroup = [];
 
    for (let i = 0; i &lt; links.length; i++) {
        const link = links[i];
        const href = link.href;
 
        if (link.closest(&#039;#lightbox, #lightboxOverlay, .lb-dataContainer, .gm-media-embed-container, .gm-imgur-gif-wrapper, .gm-image-row&#039;)
            || link.dataset.gmlbProcessed) {
            if (currentGroup.length &gt; 1) {
                consecutiveGroups.push([...currentGroup]);
            }
            currentGroup = [];
            continue;
        }
 
        const isImageUrl = imagePattern.test(href) || servicePattern.test(href);
 
        if (isImageUrl) {
            if (currentGroup.length === 0) {
                currentGroup.push(link);
            } else {
                const lastLink = currentGroup[currentGroup.length - 1];
 
                if (isConsecutiveImage(lastLink, link)) {
                    currentGroup.push(link);
                } else {
                    if (currentGroup.length &gt; 1) {
                        consecutiveGroups.push([...currentGroup]);
                    }
                    currentGroup = [link];
                }
            }
        } else {
            if (currentGroup.length &gt; 1) {
                consecutiveGroups.push([...currentGroup]);
            }
            currentGroup = [];
        }
    }
 
    if (currentGroup.length &gt; 1) {
        consecutiveGroups.push(currentGroup);
    }
 
    consecutiveGroups.forEach(group =&gt; {
        if (group.length &gt; 1) {
            createImageRow(group);
        }
    });
}
function isConsecutiveImage(elem1, elem2) {
    if (areAdjacentInDOM(elem1, elem2)) {
        return true;
    }
 
    const distance = getElementDistance(elem1, elem2);
    if (distance &lt; 100) {
        return true;
    }
 
    if (areInSameParagraph(elem1, elem2)) {
        return true;
    }
 
    return false;
}
 
function areAdjacentInDOM(elem1, elem2) {
    let current = elem1.nextSibling;
    let textOnlyBetween = true;
 
    while (current &amp;&amp; current !== elem2) {
        if (current.nodeType === Node.TEXT_NODE) {
            if (current.textContent.trim() !== &#039;&#039;) {
                textOnlyBetween = false;
                break;
            }
        } else if (current.nodeType === Node.ELEMENT_NODE) {
            if (current.tagName === &#039;BR&#039; ||
                (current.textContent &amp;&amp; current.textContent.trim() === &#039;&#039;)) {
            } else {
                textOnlyBetween = false;
                break;
            }
        }
        current = current.nextSibling;
    }
 
    return current === elem2 &amp;&amp; textOnlyBetween;
}
 
function areInSameParagraph(elem1, elem2) {
    const para1 = elem1.closest(&#039;p, dd, div.message, .post-content&#039;);
    const para2 = elem2.closest(&#039;p, dd, div.message, .post-content&#039;);
 
    return para1 &amp;&amp; para2 &amp;&amp; para1 === para2;
}
 
function getElementDistance(elem1, elem2) {
    const rect1 = elem1.getBoundingClientRect();
    const rect2 = elem2.getBoundingClientRect();
 
    const verticalDistance = Math.abs(rect2.top - rect1.bottom);
 
    return verticalDistance;
}
 
function createImageRow(imageLinks) {
    if (imageLinks.length &lt; 2) return;
 
    const firstLink = imageLinks[0];
    const rowContainer = document.createElement(&#039;div&#039;);
    rowContainer.className = &#039;gm-image-row&#039;;
 
    firstLink.parentNode.insertBefore(rowContainer, firstLink);
 
    imageLinks.forEach(link =&gt; {
        const wrapper = document.createElement(&#039;div&#039;);
        wrapper.style.cssText = &#039;display: inline-block; vertical-align: top;&#039;;
        wrapper.appendChild(link);
        rowContainer.appendChild(wrapper);
    });
}
 
function processLinks() {
    const links = document.querySelectorAll(&#039;a[href]&#039;);
    const baseDomainsToIgnore = [&#039;ul.h3z.jp&#039;, &#039;tadaup.jp&#039;, &#039;ibb.co&#039;, &#039;i.ibb.co&#039;, &#039;i.postimg.cc&#039;, &#039;postimg.cc&#039;, &#039;freeimage.host&#039;, &#039;iili.io&#039;, &#039;funakamome.com&#039;];
 
    links.forEach(a =&gt; {
        if (a.closest(&#039;#lightbox, #lightboxOverlay, .lb-dataContainer, .gm-media-embed-container, .gm-imgur-gif-wrapper, .gm-thumbnail-button, .gm-image-row&#039;) || a.dataset.gmlbProcessed) return;
        const innerImg = a.querySelector(&#039;img&#039;);
        if (innerImg &amp;&amp; innerImg.dataset.gmlbProcessed) return;
        const href = a.href;
        try {
            const url = new URL(href);
            if (baseDomainsToIgnore.includes(url.hostname) &amp;&amp; (url.pathname === &#039;/&#039; || url.pathname === &#039;&#039;)) {
                a.dataset.gmlbProcessed = &#039;1&#039;; return;
            }
        } catch (e) { return; }
 
        const existingImg = a.querySelector(&#039;img&#039;);
 
        if ((existingImg &amp;&amp; /imgur/i.test(existingImg.className) &amp;&amp; /\.gif/i.test(href)) || /i\.imgur\.com\/[0-9A-Za-z]+\.gif/i.test(href)) {
            replaceImgurGifWithNativeCompatibility(a);
            return;
        }
 
        if (!existingImg &amp;&amp; /i\.imgur\.com\/[0-9A-Za-z]+\.mp4/i.test(href)) {
            embedImgurMp4(a); return;
        }
        if (existingImg &amp;&amp; /i\.imgur\.com\//.test(href) &amp;&amp; /\.(jpe?g|png)$/i.test(href)) {
            bindLightboxOnClick(existingImg, href, href);
            a.onclick = e =&gt; e.preventDefault(); a.dataset.gmlbProcessed = &#039;1&#039;; return;
        }
 
        const isTadaup = /tadaup\.jp/.test(href) || (existingImg &amp;&amp; /tadaup\.jp/.test(existingImg.src));
        const isH3z = /ul\.h3z\.jp/.test(href);
        const isIbbDirect = /^https?:\/\/i\.ibb\.co\//.test(href);
        const isPostimgDirect = /i\.postimg\.cc/.test(href);
        const isIilioDirect = /iili\.io/.test(href);
        const isFunakamome = /funakamome\.com/.test(href);
        const isIbbGallery = /^https?:\/\/ibb\.co\/[0-9A-Za-z]+/.test(href);
        const isPostimgGallery = /^https?:\/\/postimg\.cc\/[0-9A-Za-z]+/.test(href);
        const isFreeimageGallery = /^https?:\/\/freeimage\.host\/i\/[0-9A-Za-z]+/.test(href);
 
        // ギャラリーページ（postimg.cc, freeimage.host）は拡張子なしでもOK
        if (isPostimgGallery || isFreeimageGallery) {
            expandGalleryLink(a);
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
        
        // ibb.coギャラリーページも拡張子なしでもOK
        if (isIbbGallery) {
            expandGalleryLink(a);
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
 
        // 直接リンクの場合は拡張子チェックを行う
        if (isIbbDirect || isPostimgDirect || isH3z || isIilioDirect) {
            if (hasValidImageExtension(href)) {
                expandDirectLink(a);
            }
            // 拡張子がない場合は何もしない（白い枠も消さない）
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
        
        // funakamome.comは拡張子チェックを行う
        if (isFunakamome) {
            if (hasValidImageExtension(href)) {
                expandDirectLink(a);
            }
            // 拡張子がない場合は何もしない（白い枠も消さない）
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
        
        // tadaupも拡張子チェックを行う
        if (isTadaup) {
            if (hasValidImageExtension(href)) {
                expandTadaupLink(a);
            }
            // 拡張子がない場合は何もしない
            a.dataset.gmlbProcessed = &#039;1&#039;;
            return;
        }
 
        a.dataset.gmlbProcessed = &#039;1&#039;;
    });
 
    setTimeout(() =&gt; {
        groupConsecutiveImages();
    }, 500);
}
 
function disableTooltips() {
    document.addEventListener(&#039;mouseover&#039;, function(e) {
        if (e.target.matches(&#039;.lb-save a, .gm-no-tooltip, .gm-media-embed-container a, .gm-thumbnail-button&#039;)) {
            e.target.removeAttribute(&#039;title&#039;); e.target.title = &#039;&#039;;
        }
    }, true);
    setTimeout(() =&gt; {
        document.querySelectorAll(&#039;.lb-save a, .gm-no-tooltip, .gm-thumbnail-button&#039;).forEach(el =&gt; {
            el.removeAttribute(&#039;title&#039;); el.title = &#039;&#039;;
        });
    }, 100);
}
 
function enhanceSiteCompatibility() {
    const observer = new MutationObserver((mutations) =&gt; {
        let needsProcessing = false;
        mutations.forEach(mutation =&gt; {
            if (mutation.addedNodes.length &gt; 0) {
                mutation.addedNodes.forEach(node =&gt; {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.matches(&#039;a[href]&#039;) || node.querySelector(&#039;a[href]&#039;)) {
                            needsProcessing = true;
                        }
                    }
                });
            }
        });
 
        if (needsProcessing) {
            setTimeout(processLinks, 200);
        }
    });
 
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
}
 
function init() {
    addCustomCSS();
    processLinks();
    disableTooltips();
    enhanceSiteCompatibility();
}
 
if (document.readyState === &#039;loading&#039;) {
    document.addEventListener(&#039;DOMContentLoaded&#039;, init);
} else {
    init();
}
})();
}}
#endregion
#region(Open2ch ID横に検索ボタンをすべてのIDに追加)
#highlight(){{
// ==UserScript==
// @name         Open2ch ID横に検索ボタンをすべてのIDに追加
// @namespace    http://tampermonkey.net/
// @version      1.2
// @license	CC0-1.0
// @description  全てのIDに検索ボタンを追加
// @author       jazapから(自作)
// @match        https://*.open2ch.net/*
// @grant        none
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
 
(function() {
  &#039;use strict&#039;;
 
  const bbs = &#039;livejupiter&#039;;
 
  function addSearchButtons() {
    $(&#039;span._id&#039;).each(function() {
      const $idSpan = $(this);
 
      if ($idSpan.find(&#039;.id-search-btn&#039;).length &gt; 0) return;
 
      const idVal = $idSpan.attr(&#039;val&#039;);
      if (!idVal) return;
 
      const $btn = $(&#039;&lt;button&gt;&#039;)
        .text(&#039;検索&#039;)
        .addClass(&#039;id-search-btn&#039;)
        .css({
          marginLeft: &#039;6px&#039;,
          fontSize: &#039;10px&#039;,
          cursor: &#039;pointer&#039;,
          padding: &#039;1px 5px&#039;,
          borderRadius: &#039;3px&#039;,
          border: &#039;1px solid #888&#039;,
          background: &#039;#eee&#039;,
          verticalAlign: &#039;middle&#039;
        })
        .attr(&#039;type&#039;, &#039;button&#039;)
        .on(&#039;click&#039;, () =&gt; {
          const url = `https://find.open2ch.net/?bbs=${bbs}&amp;t=f&amp;q=${encodeURIComponent(idVal)}`;
          window.open(url, &#039;_blank&#039;);
        });
 
      $idSpan.append($btn);
    });
  }
 
  $(function() {
    addSearchButtons();
    setInterval(addSearchButtons, 500);
  });
 })();
}}
#endregion
#region(おんj通信量削減)
#highlight(){{
// ==UserScript==
// @name         おんj通信量削減
// @namespace    tm-block-ga-only-strict
// @version      1.2
// @license	     CC0-1.0
// @description  GA/GA4を完全遮断
// @match        https://*.open2ch.net/*
// @run-at       document-start
// ==/UserScript==
 
(() =&gt; {
    &#039;use strict&#039;;
 
    const isGA = url =&gt;
        typeof url === &#039;string&#039; &amp;&amp;
        (
            url.startsWith(&#039;https://analytics.google.com/g/collect&#039;) ||
            url.startsWith(&#039;https://www.google-analytics.com/g/collect&#039;) ||
            url.startsWith(&#039;https://www.google-analytics.com/collect&#039;)
        );
 
    /* fetch（Request対応） */
    const _fetch = window.fetch;
    window.fetch = function (input, init) {
        const url =
            typeof input === &#039;string&#039;
                ? input
                : input instanceof Request
                    ? input.url
                    : &#039;&#039;;
 
        if (isGA(url)) {
            return Promise.resolve(new Response(null, { status: 204 }));
        }
        return _fetch.apply(this, arguments);
    };
 
    /* XMLHttpRequest */
    const _open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url) {
        if (isGA(url)) {
            this.abort();
            return;
        }
        return _open.apply(this, arguments);
    };
 
    /* sendBeacon（GA4の本命） */
    if (navigator.sendBeacon) {
        const _beacon = navigator.sendBeacon;
        navigator.sendBeacon = function (url, data) {
            if (isGA(url)) {
                return false;
            }
            return _beacon.call(this, url, data);
        };
    }
})();
}}
#endregion
#region(おんJ バツポチ修正)
#highlight(){{
// ==UserScript==
// @name         おんJ バツポチ修正
// @namespace    onj-ignore-display-fix-preemptive
// @version      2.1
// @license	CC0-1.0
// @description  はよ直せさとる
// @match        https://*.open2ch.net/test/read.cgi/*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==
 
(function () {
  &#039;use strict&#039;;
  const style = document.createElement(&#039;style&#039;);
  style.textContent = `
    dl[ignored=&quot;1&quot;] {
      display: none !important;
    }
  `;
  function injectStyle() {
    const target = document.head || document.documentElement;
    if (target &amp;&amp; !target.querySelector(&#039;style[data-ignore-fix]&#039;)) {
      style.setAttribute(&#039;data-ignore-fix&#039;, &#039;true&#039;);
      target.appendChild(style);
    }
  }
  injectStyle();
  if (document.readyState === &#039;loading&#039;) {
    document.addEventListener(&#039;DOMContentLoaded&#039;, injectStyle);
  }
  function applyIgnoreFixSync(root = document) {
    const ignoredElements = root.querySelectorAll(&#039;dl[ignored=&quot;1&quot;]&#039;);
    ignoredElements.forEach(dl =&gt; {
      dl.style.display = &#039;none&#039;;
    });
    return ignoredElements.length;
  } 
  function initIgnoreFix() {
    const count = applyIgnoreFixSync();
    console.log(`バツポチ初期処理: ${count}件のレスを非表示化`);
  }
 
  if (document.readyState === &#039;loading&#039;) {
    document.addEventListener(&#039;DOMContentLoaded&#039;, initIgnoreFix);
  } else {
    initIgnoreFix();
  }
  function startObserver() {
    const targetNode = document.querySelector(&#039;.thread&#039;) || document.body;
    
    const observer = new MutationObserver(mutations =&gt; {
      let fixedCount = 0;
      
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType !== Node.ELEMENT_NODE) continue;
          if (node.matches &amp;&amp; node.matches(&#039;dl[ignored=&quot;1&quot;]&#039;)) {
            node.style.display = &#039;none&#039;;
            fixedCount++;
          }
          if (node.querySelectorAll) {
            const ignoredChildren = node.querySelectorAll(&#039;dl[ignored=&quot;1&quot;]&#039;);
            ignoredChildren.forEach(dl =&gt; {
              dl.style.display = &#039;none&#039;;
              fixedCount++;
            });
          }
        }
        if (mutation.type === &#039;attributes&#039; &amp;&amp; 
            mutation.attributeName === &#039;ignored&#039; &amp;&amp;
            mutation.target.getAttribute(&#039;ignored&#039;) === &#039;1&#039;) {
          mutation.target.style.display = &#039;none&#039;;
          fixedCount++;
        }
      }
      
      if (fixedCount &gt; 0) {
        console.log(`バツポチ動的処理: ${fixedCount}件のレスを非表示化`);
      }
    });
 
    observer.observe(targetNode, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: [&#039;ignored&#039;] 
    });
 
    console.log(&#039;バツポチMutationObserver 開始&#039;);
  }
  if (document.readyState === &#039;loading&#039;) {
    document.addEventListener(&#039;DOMContentLoaded&#039;, startObserver);
  } else {
    startObserver();
  }
  setInterval(() =&gt; {
    const count = applyIgnoreFixSync();
    if (count &gt; 0) {
      console.log(`バツポチ定期補正: ${count}件のレスを非表示化`);
    }
  }, 1000);
 
  console.log(&#039;バツポチ初期化完了（表示前抑制モード）&#039;);
})();
}}
#endregion
#region(投稿内容を送信直前に自動変換)
#highlight(){{
// ==UserScript==
// @name         投稿内容を送信直前に自動変換
// @namespace    http://tampermonkey.net/
// @version      2.1
// @license	     CC0-1.0
// @description  投稿ボタンを押した瞬間に、テキストエリア内の特定の文字や単語を「&amp;&amp;#x…;」形式に変換します。
// @author       ワイ
// @match        https://*.open2ch.net/test/read.cgi/*
// @grant        none
// ==/UserScript==
 
(function() {
    &#039;use strict&#039;;
    const CONVERT_TARGETS = [&#039;多治見&#039;, &#039;安倍&#039;, &#039;フラゲ&#039;, &#039;アフィ&#039;, &#039;&lt;&#039;, &#039;興味&#039;, &#039;大麻&#039;, &#039;晋&#039;, &#039;大&#039;]; //さとるが規制復活させるかもしれんから一応
    
    const DELETE_TARGETS = [&#039;NGワードをここにこんな風に記述&#039;, &#039;こんな感じにね&#039;]; //投稿に含まれていたら自動削除
    
    const SUBMIT_BUTTON_SELECTORS = [
        &#039;input[type=&quot;submit&quot;][value=&quot;書&quot;]&#039;,
        &#039;input[name=&quot;submit&quot;]&#039;,
        &#039;button[type=&quot;submit&quot;]&#039;,
        &#039;#submit_button&#039;
    ];
    const TEXTAREA_SELECTOR = &#039;textarea[name=&quot;MESSAGE&quot;]&#039;;
    
    const convertCharToUnicodeEntity = (char) =&gt; {
        const codePoint = char.codePointAt(0).toString(16);
        return `&amp;&amp;#x${codePoint};#x${codePoint};`;
    };
    
    const isRestrictedScript = (char) =&gt; {
        const code = char.codePointAt(0);
        return (code &gt;= 0x0600 &amp;&amp; code &lt;= 0x06FF) || //アラビア文字とか
               (code &gt;= 0x0750 &amp;&amp; code &lt;= 0x077F) ||
               (code &gt;= 0x08A0 &amp;&amp; code &lt;= 0x08FF) ||
               (code &gt;= 0xFB50 &amp;&amp; code &lt;= 0xFDFF) ||
               (code &gt;= 0xFE70 &amp;&amp; code &lt;= 0xFEFF) ||
               (code &gt;= 0xAC00 &amp;&amp; code &lt;= 0xD7AF) ||
               (code &gt;= 0x1100 &amp;&amp; code &lt;= 0x11FF) ||
               (code &gt;= 0x3130 &amp;&amp; code &lt;= 0x318F) ||
               (code &gt;= 0xA960 &amp;&amp; code &lt;= 0xA97F) ||
               (code &gt;= 0xD7B0 &amp;&amp; code &lt;= 0xD7FF) ||
               (code &gt;= 0x0E00 &amp;&amp; code &lt;= 0x0E7F) ||
               (code &gt;= 0x0F00 &amp;&amp; code &lt;= 0x0FFF) ||
               (code &gt;= 0x0590 &amp;&amp; code &lt;= 0x05FF) ||
               (code &gt;= 0x0900 &amp;&amp; code &lt;= 0x097F) ||
               (code &gt;= 0x0980 &amp;&amp; code &lt;= 0x09FF) ||
               (code &gt;= 0x0A00 &amp;&amp; code &lt;= 0x0A7F) ||
               (code &gt;= 0x0A80 &amp;&amp; code &lt;= 0x0AFF) ||
               (code &gt;= 0x0B00 &amp;&amp; code &lt;= 0x0B7F) ||
               (code &gt;= 0x0B80 &amp;&amp; code &lt;= 0x0BFF) ||
               (code &gt;= 0x0C00 &amp;&amp; code &lt;= 0x0C7F) ||
               (code &gt;= 0x0C80 &amp;&amp; code &lt;= 0x0CFF) ||
               (code &gt;= 0x0D00 &amp;&amp; code &lt;= 0x0D7F) ||
               (code &gt;= 0x0D80 &amp;&amp; code &lt;= 0x0DFF) ||
               (code &gt;= 0x1000 &amp;&amp; code &lt;= 0x109F) ||
               (code &gt;= 0x1200 &amp;&amp; code &lt;= 0x137F) ||
               (code &gt;= 0x13A0 &amp;&amp; code &lt;= 0x13FF) ||
               (code &gt;= 0x1400 &amp;&amp; code &lt;= 0x167F) ||
               (code &gt;= 0x1680 &amp;&amp; code &lt;= 0x169F) ||
               (code &gt;= 0x16A0 &amp;&amp; code &lt;= 0x16FF) ||
               (code &gt;= 0x1700 &amp;&amp; code &lt;= 0x171F) ||
               (code &gt;= 0x1720 &amp;&amp; code &lt;= 0x173F) ||
               (code &gt;= 0x1740 &amp;&amp; code &lt;= 0x175F) ||
               (code &gt;= 0x1760 &amp;&amp; code &lt;= 0x177F) ||
               (code &gt;= 0x1780 &amp;&amp; code &lt;= 0x17FF) ||
               (code &gt;= 0x1800 &amp;&amp; code &lt;= 0x18AF) ||
               (code &gt;= 0x1900 &amp;&amp; code &lt;= 0x194F) ||
               (code &gt;= 0x1950 &amp;&amp; code &lt;= 0x197F) ||
               (code &gt;= 0x1980 &amp;&amp; code &lt;= 0x19DF) ||
               (code &gt;= 0x19E0 &amp;&amp; code &lt;= 0x19FF) ||
               (code &gt;= 0x1A00 &amp;&amp; code &lt;= 0x1A1F) ||
               (code &gt;= 0x1A20 &amp;&amp; code &lt;= 0x1AAF) ||
               (code &gt;= 0x1B00 &amp;&amp; code &lt;= 0x1B7F) ||
               (code &gt;= 0x1B80 &amp;&amp; code &lt;= 0x1BBF) ||
               (code &gt;= 0x1BC0 &amp;&amp; code &lt;= 0x1BFF) ||
               (code &gt;= 0x1C00 &amp;&amp; code &lt;= 0x1C4F) ||
               (code &gt;= 0x1C50 &amp;&amp; code &lt;= 0x1C7F) ||
               (code &gt;= 0x1C80 &amp;&amp; code &lt;= 0x1C8F) ||
               (code &gt;= 0x1CC0 &amp;&amp; code &lt;= 0x1CCF) ||
               (code &gt;= 0x1CD0 &amp;&amp; code &lt;= 0x1CFF) ||
               (code &gt;= 0x2800 &amp;&amp; code &lt;= 0x28FF) ||
               (code &gt;= 0x2E80 &amp;&amp; code &lt;= 0x2EFF) ||
               (code &gt;= 0x2F00 &amp;&amp; code &lt;= 0x2FDF) ||
               (code &gt;= 0x3000 &amp;&amp; code &lt;= 0x303F) ||
               (code &gt;= 0x3200 &amp;&amp; code &lt;= 0x32FF) ||
               (code &gt;= 0x3300 &amp;&amp; code &lt;= 0x33FF) ||
               (code &gt;= 0xA000 &amp;&amp; code &lt;= 0xA48F) ||
               (code &gt;= 0xA490 &amp;&amp; code &lt;= 0xA4CF) ||
               (code &gt;= 0xA4D0 &amp;&amp; code &lt;= 0xA4FF) ||
               (code &gt;= 0xA500 &amp;&amp; code &lt;= 0xA63F) ||
               (code &gt;= 0xA640 &amp;&amp; code &lt;= 0xA69F) ||
               (code &gt;= 0xA6A0 &amp;&amp; code &lt;= 0xA6FF) ||
               (code &gt;= 0xA700 &amp;&amp; code &lt;= 0xA71F) ||
               (code &gt;= 0xA720 &amp;&amp; code &lt;= 0xA7FF) ||
               (code &gt;= 0xA800 &amp;&amp; code &lt;= 0xA82F) ||
               (code &gt;= 0xA830 &amp;&amp; code &lt;= 0xA83F) ||
               (code &gt;= 0xA840 &amp;&amp; code &lt;= 0xA87F) ||
               (code &gt;= 0xA880 &amp;&amp; code &lt;= 0xA8DF) ||
               (code &gt;= 0xA8E0 &amp;&amp; code &lt;= 0xA8FF) ||
               (code &gt;= 0xA900 &amp;&amp; code &lt;= 0xA92F) ||
               (code &gt;= 0xA930 &amp;&amp; code &lt;= 0xA95F) ||
               (code &gt;= 0xAA00 &amp;&amp; code &lt;= 0xAA5F) ||
               (code &gt;= 0xAA60 &amp;&amp; code &lt;= 0xAA7F) ||
               (code &gt;= 0xAA80 &amp;&amp; code &lt;= 0xAADF) ||
               (code &gt;= 0xAAE0 &amp;&amp; code &lt;= 0xAAFF) ||
               (code &gt;= 0xAB00 &amp;&amp; code &lt;= 0xAB2F) ||
               (code &gt;= 0xAB30 &amp;&amp; code &lt;= 0xAB6F) ||
               (code &gt;= 0xAB70 &amp;&amp; code &lt;= 0xABBF) ||
               (code &gt;= 0xABC0 &amp;&amp; code &lt;= 0xABFF) ||
               (code &gt;= 0x10000 &amp;&amp; code &lt;= 0x1007F) ||
               (code &gt;= 0x10080 &amp;&amp; code &lt;= 0x100FF) ||
               (code &gt;= 0x10100 &amp;&amp; code &lt;= 0x1013F) ||
               (code &gt;= 0x10140 &amp;&amp; code &lt;= 0x1018F) ||
               (code &gt;= 0x10190 &amp;&amp; code &lt;= 0x101CF) ||
               (code &gt;= 0x101D0 &amp;&amp; code &lt;= 0x101FF) ||
               (code &gt;= 0x10280 &amp;&amp; code &lt;= 0x1029F) ||
               (code &gt;= 0x102A0 &amp;&amp; code &lt;= 0x102DF) ||
               (code &gt;= 0x10300 &amp;&amp; code &lt;= 0x1032F) ||
               (code &gt;= 0x10330 &amp;&amp; code &lt;= 0x1034F) ||
               (code &gt;= 0x10350 &amp;&amp; code &lt;= 0x1037F) ||
               (code &gt;= 0x10380 &amp;&amp; code &lt;= 0x1039F) ||
               (code &gt;= 0x103A0 &amp;&amp; code &lt;= 0x103DF) ||
               (code &gt;= 0x10400 &amp;&amp; code &lt;= 0x1044F) ||
               (code &gt;= 0x10450 &amp;&amp; code &lt;= 0x1047F) ||
               (code &gt;= 0x10480 &amp;&amp; code &lt;= 0x104AF) ||
               (code &gt;= 0x10800 &amp;&amp; code &lt;= 0x1083F) ||
               (code &gt;= 0x10840 &amp;&amp; code &lt;= 0x1085F) ||
               (code &gt;= 0x10900 &amp;&amp; code &lt;= 0x1091F) ||
               (code &gt;= 0x10920 &amp;&amp; code &lt;= 0x1093F) ||
               (code &gt;= 0x10980 &amp;&amp; code &lt;= 0x1099F) ||
               (code &gt;= 0x109A0 &amp;&amp; code &lt;= 0x109FF) ||
               (code &gt;= 0x10A00 &amp;&amp; code &lt;= 0x10A5F) ||
               (code &gt;= 0x10A60 &amp;&amp; code &lt;= 0x10A7F) ||
               (code &gt;= 0x10B00 &amp;&amp; code &lt;= 0x10B3F) ||
               (code &gt;= 0x10B40 &amp;&amp; code &lt;= 0x10B5F) ||
               (code &gt;= 0x10B60 &amp;&amp; code &lt;= 0x10B7F) ||
               (code &gt;= 0x10C00 &amp;&amp; code &lt;= 0x10C4F) ||
               (code &gt;= 0x11000 &amp;&amp; code &lt;= 0x1107F) ||
               (code &gt;= 0x11080 &amp;&amp; code &lt;= 0x110CF) ||
               (code &gt;= 0x12000 &amp;&amp; code &lt;= 0x123FF) ||
               (code &gt;= 0x12400 &amp;&amp; code &lt;= 0x1247F) ||
               (code &gt;= 0x13000 &amp;&amp; code &lt;= 0x1342F) ||
               (code &gt;= 0x14400 &amp;&amp; code &lt;= 0x1467F) ||
               (code &gt;= 0x16800 &amp;&amp; code &lt;= 0x16A3F) ||
               (code &gt;= 0x16F00 &amp;&amp; code &lt;= 0x16F9F) ||
               (code &gt;= 0x1B000 &amp;&amp; code &lt;= 0x1B0FF) ||
               (code &gt;= 0x1BC00 &amp;&amp; code &lt;= 0x1BC9F) ||
               (code &gt;= 0x1D000 &amp;&amp; code &lt;= 0x1D0FF) ||
               (code &gt;= 0x1D100 &amp;&amp; code &lt;= 0x1D1FF) ||
               (code &gt;= 0x1D200 &amp;&amp; code &lt;= 0x1D24F) ||
               (code &gt;= 0x1D300 &amp;&amp; code &lt;= 0x1D35F) ||
               (code &gt;= 0x1D360 &amp;&amp; code &lt;= 0x1D37F) ||
               (code &gt;= 0x1D400 &amp;&amp; code &lt;= 0x1D7FF) ||
               (code &gt;= 0x1F000 &amp;&amp; code &lt;= 0x1F02F) ||
               (code &gt;= 0x1F030 &amp;&amp; code &lt;= 0x1F09F) ||
               (code &gt;= 0x1F0A0 &amp;&amp; code &lt;= 0x1F0FF) ||
               (code &gt;= 0x1F100 &amp;&amp; code &lt;= 0x1F1FF) ||
               (code &gt;= 0x1F200 &amp;&amp; code &lt;= 0x1F2FF) ||
               (code &gt;= 0x1F300 &amp;&amp; code &lt;= 0x1F5FF) ||
               (code &gt;= 0x1F600 &amp;&amp; code &lt;= 0x1F64F) ||
               (code &gt;= 0x1F650 &amp;&amp; code &lt;= 0x1F67F) ||
               (code &gt;= 0x1F680 &amp;&amp; code &lt;= 0x1F6FF) ||
               (code &gt;= 0x1F700 &amp;&amp; code &lt;= 0x1F77F) ||
               (code &gt;= 0x1F780 &amp;&amp; code &lt;= 0x1F7FF) ||
               (code &gt;= 0x1F800 &amp;&amp; code &lt;= 0x1F8FF) ||
               (code &gt;= 0x1F900 &amp;&amp; code &lt;= 0x1F9FF) ||
               (code &gt;= 0x1FA00 &amp;&amp; code &lt;= 0x1FA6F) ||
               (code &gt;= 0x1FA70 &amp;&amp; code &lt;= 0x1FAFF) ||
               (code &gt;= 0x20000 &amp;&amp; code &lt;= 0x2A6DF) ||
               (code &gt;= 0x2A700 &amp;&amp; code &lt;= 0x2B73F) ||
               (code &gt;= 0x2B740 &amp;&amp; code &lt;= 0x2B81F) ||
               (code &gt;= 0x2B820 &amp;&amp; code &lt;= 0x2CEAF) ||
               (code &gt;= 0x2CEB0 &amp;&amp; code &lt;= 0x2EBEF) ||
               (code &gt;= 0x2F800 &amp;&amp; code &lt;= 0x2FA1F);
    };
    
    const modifyTextOnSubmit = () =&gt; {
        const textarea = document.querySelector(TEXTAREA_SELECTOR);
        if (!textarea || !textarea.value) {
            return;
        }
        let originalText = textarea.value;
        let convertedText = originalText;
        
        DELETE_TARGETS.forEach(targetWord =&gt; {
            const regex = new RegExp(targetWord.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;&#039;), &#039;g&#039;);
            convertedText = convertedText.replace(regex, &#039;&#039;);
        });
        
        CONVERT_TARGETS.forEach(targetWord =&gt; {
            const regex = new RegExp(targetWord.replace(/[.*+?^${}()|[\]\\]/g, &#039;\\$&amp;&#039;), &#039;g&#039;);
            convertedText = convertedText.replace(regex, (matchedWord) =&gt; {
                let result = &#039;&#039;;
                for (const char of matchedWord) {
                    result += convertCharToUnicodeEntity(char);
                }
                return result;
            });
        });
        
        let finalText = &#039;&#039;;
        for (const char of convertedText) {
            if (isRestrictedScript(char)) {
                finalText += convertCharToUnicodeEntity(char);
            } else {
                finalText += char;
            }
        }
        
        if (originalText !== finalText) {
            textarea.value = finalText;
        }
    };
    
    const setupListeners = () =&gt; {
        SUBMIT_BUTTON_SELECTORS.forEach(selector =&gt; {
            document.querySelectorAll(selector).forEach(submitButton =&gt; {
                if (submitButton.dataset.autoConvertListener) return;
                submitButton.addEventListener(&#039;click&#039;, modifyTextOnSubmit, true);
                submitButton.dataset.autoConvertListener = &#039;true&#039;;
            });
        });
    };
    
    const observer = new MutationObserver(() =&gt; {
        setupListeners();
    });
    if (document.readyState === &#039;loading&#039;) {
        document.addEventListener(&#039;DOMContentLoaded&#039;, setupListeners);
    } else {
        setupListeners();
    }
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();
}}
#endregion
#region(おんJピアノ自動演奏)
コメント:
おんjのピアノを自動演奏するよ
保存した物はちゃんとファイルに保存しないともしかしたら時間経過で消えるかもしれないから注意🥺
機械的な遅延などもあるので楽譜をそのまま落とし込むだけだと多分ズレる
途中から再生モードも機械的遅延でほぼ役に立たない
#highlight(){{
長いので割愛
}}
#endregion

続き:[[スクリプト（２）]]    </description>
    <dc:date>2026-02-11T17:06:57+09:00</dc:date>
    <utime>1770797217</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/9.html">
    <title>プラグイン/コメント</title>
    <link>https://w.atwiki.jp/superopenj/pages/9.html</link>
    <description>
          </description>
    <dc:date>2026-02-11T11:31:11+09:00</dc:date>
    <utime>1770777071</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/8.html">
    <title>プラグイン</title>
    <link>https://w.atwiki.jp/superopenj/pages/8.html</link>
    <description>
          </description>
    <dc:date>2026-02-11T11:31:11+09:00</dc:date>
    <utime>1770777071</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/7.html">
    <title>プラグイン/動画(Youtube)</title>
    <link>https://w.atwiki.jp/superopenj/pages/7.html</link>
    <description>
          </description>
    <dc:date>2026-02-11T11:31:11+09:00</dc:date>
    <utime>1770777071</utime>
  </item>
    <item rdf:about="https://w.atwiki.jp/superopenj/pages/6.html">
    <title>プラグイン/アーカイブ</title>
    <link>https://w.atwiki.jp/superopenj/pages/6.html</link>
    <description>
          </description>
    <dc:date>2026-02-11T11:31:11+09:00</dc:date>
    <utime>1770777071</utime>
  </item>
  </rdf:RDF>
