X岛-EX

X岛-EX 网页端增强,移动端般的浏览体验:快捷切换饼干/ 添加页首页码 / 关闭图片水印 / 预览真实饼干 / 隐藏无标题/无名氏/版规 / 显示外部图床 / 自动刷新饼干 toast提示 / 无缝翻页 自动翻页 / 默认原图+控件 / 新标签打开串 / 优化引用弹窗 / 拓展引用格式 / 当页回复编号 / 扩展坞增强 / 拦截回复中间页 / 颜文字拓展 / 高亮PO主 / 发串UI调整 / 『分组标记饼干』/『屏蔽饼干』/『屏蔽关键词』 / 增强X岛匿名版。

// ==UserScript==
// @name         X岛-EX
// @namespace    http://tampermonkey.net/
// @version      2.0.1
// @description  X岛-EX 网页端增强,移动端般的浏览体验:快捷切换饼干/ 添加页首页码 / 关闭图片水印 / 预览真实饼干 / 隐藏无标题/无名氏/版规 / 显示外部图床 / 自动刷新饼干 toast提示 / 无缝翻页 自动翻页 / 默认原图+控件 / 新标签打开串 / 优化引用弹窗 / 拓展引用格式 / 当页回复编号 / 扩展坞增强 / 拦截回复中间页 / 颜文字拓展 / 高亮PO主 / 发串UI调整 / 『分组标记饼干』/『屏蔽饼干』/『屏蔽关键词』 / 增强X岛匿名版。
// @author       XY
// @match        https://*.nmbxd1.com/*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addValueChangeListener
// @grant        GM_xmlhttpRequest
// @grant        GM_deleteValue
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @license      WTFPL
// @note         致谢:切饼代码移植自[XD-Enhance](https://greasyfork.org/zh-CN/scripts/438164-xd-enhance)
// @note         致谢:外部图床代码二改自[显示x岛图片链接指向的图片](https://greasyfork.org/zh-CN/scripts/546024-%E6%98%BE%E7%A4%BAx%E5%B2%9B%E5%9B%BE%E7%89%87%E9%93%BE%E6%8E%A5%E6%8C%87%E5%90%91%E7%9A%84%E5%9B%BE%E7%89%87)
// @note         致谢:完整移植[增强x岛匿名版](https://greasyfork.org/zh-CN/scripts/513156-%E5%A2%9E%E5%BC%BAx%E5%B2%9B%E5%8C%BF%E5%90%8D%E7%89%88)
// @note         致谢:部分功能移植自[X岛-揭示板的增强型体验](https://greasyfork.org/zh-CN/scripts/497875-x%E5%B2%9B-%E6%8F%AD%E7%A4%BA%E6%9D%BF%E7%9A%84%E5%A2%9E%E5%BC%BA%E5%9E%8B%E4%BD%93%E9%AA%8C#%E8%BF%9E%E6%8E%A5%E7%9B%B4%E6%8E%A5%E8%B7%B3%E8%BD%AC)
// ==/UserScript==
/* global $, jQuery */
// @run-at document-end

(function($){
    'use strict';
  
    /* --------------------------------------------------
     * 0. 通用与工具函数
     * -------------------------------------------------- */
    const toast = msg => {
      let $t = $('#ae-toast');
      if (!$t.length) {
        $t = $(`<div id="ae-toast" style="
          position:fixed;top:10px;left:50%;transform:translateX(-50%);
          background:rgba(0,0,0,.75);color:#fff;padding:8px 18px;
          border-radius:5px;z-index:9999;display:none;font-size:14px;"></div>`);
        $('body').append($t);
      }
      $t.text(msg).stop(true).fadeIn(240).delay(1800).fadeOut(240);
    };
  
    const Utils = {
        // 逗号(中英)分隔,支持转义 \, \, \\
        strToList(s) {
          if (!s) return [];
          const list = [], esc = ',,\\';
          let cur = '';
          for (let i = 0; i < s.length; i++) {
            const ch = s[i];
            if (ch === '\\' && i + 1 < s.length && esc.includes(s[i+1])) {
              cur += s[++i];
            } else if (ch === ',' || ch === ',') {
              const t = cur.trim();
              if (t) list.push(t);
              cur = '';
            } else cur += ch;
          }
          const t = cur.trim();
          if (t) list.push(t);
          return [...new Set(list)];
        },
  
        cookieLegal: s => /^[A-Za-z0-9]{3,7}$/.test(s),
  
        cookieMatch: (cid,p) => cid.toLowerCase().includes(p.toLowerCase()),
  
        firstHit(txt,list) {
          return list.find(k=>txt.toLowerCase().includes(k.toLowerCase()))||null;
        },
  
        collapse($elem, hint) {
          if (!$elem.length || $elem.data('xdex-collapsed')) return;
          const $icons = $elem.find('.h-threads-item-reply-icon');
          let nums = '';
          if ($icons.length) {
            const f = $icons.first().text();
            const l = $icons.last().text();
            nums = $icons.length>1 ? `${f}-${l} ` : `${f} `;
          }
          const cap = `${nums}${hint}`;
          const $ph = $(`
            <div class="xdex-placeholder" style="
              padding:6px 10px;background:#fafafa;color:#888;
              border:1px dashed #bbb;margin-bottom:3px;cursor:pointer;">
              ${cap}(点击展开)
            </div>
          `);
          $elem.before($ph).hide().data('xdex-collapsed',true);
          $ph.on('click',()=>{
            if($elem.is(':visible')){
              $elem.hide(); $ph.html(`${cap}(点击展开)`);
            } else {
              $elem.show(); $ph.text('点击折叠');
            }
          });
        return $ph;
        },
  
        // ===== 引用串优化缓存相关 =====
        quoteCache: {},
  
        getQuoteFromCache(id) {
          return this.quoteCache[id] || GM_getValue('quote_' + id, null);
        },
  
        saveQuoteToCache(id, html) {
          this.quoteCache[id] = html;
          GM_setValue('quote_' + id, html);
        }
    };
  
  
    // 多分组标记时依次使用的背景色(可扩充)
    const markColors = [
      '#66CCFF','#00FFCC','#EE0000','#006666','#0080FF','#FFFF00',
      '#39C5BB','#9999FF','#FF4004','#3399FF','#D80000','#F6BE71',
      '#EE82EE','#FFA500','#FFE211','#FAAFBE','#0000FF'
    ];
  
    // 解析“最后一个冒号分隔”的分组:返回 {desc, list}
    function parseDescAndListByLastColon(raw) {
      const idx = Math.max(raw.lastIndexOf(':'), raw.lastIndexOf(':'));
      const desc = idx > 0 ? raw.slice(0, idx).trim() : '';
      const cookiePart = idx > 0 ? raw.slice(idx + 1).trim() : '';
      const list = Utils.strToList(cookiePart);
      return { desc, list };
    }
    // 校验分组说明长度(<=20 字符;满足“10个汉字/20个英文字符”的近似约束)
    function isValidDesc(desc) { return !desc || desc.length <= 20; }
  
    // 兼容旧版本 blockedCookies 值到“组结构”
    function normalizeBlockedGroups(val) {
      if (!val) return [];
      if (typeof val === 'string') {
        const tokens = Utils.strToList(val);
        return tokens.map(t=>{
          const {desc, list} = parseDescAndListByLastColon(t);
          const id = list[0] || '';
          return id && Utils.cookieLegal(id) ? { desc, cookies:[id] } : null;
        }).filter(Boolean);
      }
      if (Array.isArray(val)) {
        if (val.length && 'cookies' in val[0]) {
          return val.map(g=>({
            desc: g.desc || '',
            cookies: Array.isArray(g.cookies) ? g.cookies.filter(Utils.cookieLegal) : []
          })).filter(g=>g.cookies.length);
        }
        if (val.length && 'cookie' in val[0]) {
          const map = new Map();
          val.forEach(({cookie, desc})=>{
            if (!Utils.cookieLegal(cookie)) return;
            const key = desc || '';
            if (!map.has(key)) map.set(key, []);
            map.get(key).push(cookie);
          });
          return [...map.entries()].map(([desc, cookies])=>({desc, cookies}));
        }
      }
      return [];
    }
  
    /* --------------------------------------------------
     * 1. 设置面板
     * -------------------------------------------------- */
    const SettingPanel = {
      key: 'myScriptSettings',
      defaults: {
        enableCookieSwitch: true,
        duplicatePagination: true,
        disableWatermark: true,
        enablePaginationDuplication: true,
        updatePreviewCookie: true,
        hideEmptyTitleEmail: true,
        enableExternalImagePreview: true,  // 外部图床显示
        enableAutoCookieRefresh: true,
        enableAutoCookieRefreshToast: false,
        enableSeamlessPaging: true,
        enableAutoSeamlessPaging: true,
        enableHDImage: true,               // 启用高清图片链接
        enableLinkBlank: true, // 串页新标签打开
        enableQuotePreview: true, // 优化引用弹窗
        extendQuote: true, // 拓展引用格式
  
        markedGroups: [],
        blockedCookies: [],
        blockedKeywords: ''
      },
      state: {},
  
      init() {
        const saved = GM_getValue(this.key, {});
        this.state = Object.assign({}, this.defaults, saved);
        // 兼容迁移:屏蔽饼干到组结构
        this.state.blockedCookies = normalizeBlockedGroups(this.state.blockedCookies);
        GM_setValue(this.key, this.state);
  
        this.render();
        GM_addValueChangeListener(this.key,(k,ov,nv,remote)=>{
          if(remote && !$('#sp_cover').is(':visible')){
            this.state = Object.assign({}, this.defaults, nv);
            this.state.blockedCookies = normalizeBlockedGroups(this.state.blockedCookies);
            this.syncInputs();
          }
        });
      },
  
      render() {
        if (!$('#xdex-setting-style').length) {
            $('head').append(`
                <style id="xdex-setting-style">
                    .xdex-inv {opacity:0;pointer-events:none;}
  
                    /* 默认按钮样式:迷你 EX */
                    #sp_btn {
                        position:fixed;
                        top:10px;
                        right:10px;
                        z-index:10000;
                        padding:4px 8px;
                        font-size:12px;
                        border:none;
                        background:#2196F3;
                        color:#fff;
                        border-radius:20px;
                        white-space:nowrap;
                        overflow:hidden;
                        max-width: 34px; /* 容纳"EX" */
                        transition: all 0.3s ease;
                        cursor:pointer;
                    }
  
                    /* 悬停展开 */
                    #sp_btn:hover {
                        padding:6px 16px;
                        font-size:14px;
                        max-width:150px; /* 容纳"EX设置" */
                    }
  
                    /* 文字渐显 */
                    #sp_btn span {
                        opacity:0;
                        transition:opacity 0.2s ease;
                        margin-left:4px;
                    }
                    #sp_btn:hover span {
                        opacity:1;
                   }
                </style>
            `);
        }
  
        if (!$('#sp_btn').length) {
          $('body').append(
              $('<button id="sp_btn">EX<span>设置</span></button>')
                  .on('click',()=>$('#sp_cover').fadeIn())
          );
      }
  
  
        const fold = (id,title,ph) => `
  <div class="sp_fold" style="border:1px solid #eee;margin:6px 0;">
    <div class="sp_fold_head" data-btn="#btn_${id}"
         style="display:flex;align-items:center;padding:6px 8px;background:#fafafa;cursor:pointer;">
      <span>${title}</span>
      <button id="btn_${id}" class="sp_save xdex-inv" data-id="${id}"
              style="margin-left:auto;padding:2px 8px;">保存</button>
    </div>
    <div class="sp_fold_body" style="display:none;padding:8px 10px;">
      <input id="${id}" style="width:100%;padding:5px;" placeholder="${ph}">
    </div>
  </div>`;
  
        const html = `
  <style>
    .fixed-on {
      accent-color: #000; /* 勾选颜色黑色 */
    }
  
    .fixed-on:disabled {
      opacity: 1;         /* 不降低透明度 */
      filter: none;       /* 去掉可能的灰度滤镜 */
      cursor: default;    /* 鼠标变成普通箭头 */
    }
  </style>
  <div id="sp_cover" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:9999;">
    <div id="sp_panel" style="
        position:relative;margin:40px auto;width:480px;
        max-height:calc(100vh - 80px);background:#fff;border-radius:8px;
        display:flex;flex-direction:column;box-shadow:0 2px 10px rgba(0,0,0,0.2);">
      <div id="sp_panel_content" style="padding:18px;overflow-y:auto;flex:1;min-height:300px;">
        <h2 style="margin:0 0 10px;">X岛-EX 设置</h2>
        <div id="sp_checkbox_container" style="display:flex;flex-wrap:wrap;">
          <div style="width:50%;"><input type="checkbox" id="sp_enableCookieSwitch"><label for="sp_enableCookieSwitch"> 快捷切换饼干</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enablePaginationDuplication"><label for="sp_enablePaginationDuplication"> 添加页首页码</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_disableWatermark"><label for="sp_disableWatermark"> 关闭图片水印</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_updatePreviewCookie"><label for="sp_updatePreviewCookie"> 预览真实饼干</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_hideEmptyTitleEmail"><label for="sp_hideEmptyTitleEmail"> 隐藏无标题/无名氏/版规</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableExternalImagePreview"><label for="sp_enableExternalImagePreview"> 显示外部图床</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableAutoCookieRefresh"><label for="sp_enableAutoCookieRefresh"> 自动刷新饼干</label><input type="checkbox" id="sp_enableAutoCookieRefreshToast" style="margin-left:8px;"><label for="sp_enableAutoCookieRefreshToast"> toast提示</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableSeamlessPaging"><label for="sp_enableSeamlessPaging"> 无缝翻页</label><input type="checkbox" id="sp_enableAutoSeamlessPaging" checked style="margin-left:8px;"><label for="sp_enableAutoSeamlessPaging"> 自动翻页</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableHDImage"><label for="sp_enableHDImage"> 默认原图+控件</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableLinkBlank"><label for="sp_enableLinkBlank"> 新标签打开串</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enableQuotePreview"><label for="sp_enableQuotePreview"> 优化引用弹窗</label></div>
          <div style="width:50%;"><input type="checkbox" id="sp_extendQuote"><label for="sp_extendQuote"> 拓展引用格式</label></div>
          <!-- 以下是默认勾选项不可更改 -->
          <div style="width:50%;"><input type="checkbox" id="sp_updateReplyNumbers" class="fixed-on" checked disabled><label for="sp_updateReplyNumbers"> 当页回复编号</label><input type="hidden" name="sp_updateReplyNumbers" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_replaceRightSidebar" class="fixed-on" checked disabled><label for="sp_replaceRightSidebar"> 扩展坞增强</label><input type="hidden" name="sp_replaceRightSidebar" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_interceptReplyForm" class="fixed-on" checked disabled><label for="sp_interceptReplyForm"> 拦截回复中间页</label><input type="hidden" name="sp_interceptReplyForm" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_kaomojiEnhancer" class="fixed-on" checked disabled><label for="sp_kaomojiEnhancer"> 颜文字拓展</label><input type="hidden" name="sp_kaomojiEnhancer" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_highlightPO" class="fixed-on" checked disabled><label for="sp_highlightPO">标记Po主</label><input type="hidden" name="sp_highlightPO" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enhancePostFormLayout" class="fixed-on" checked disabled><label for="sp_enhancePostFormLayout"> 发串UI调整</label><input type="hidden" name="sp_enhancePostFormLayout" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_applyFilters" class="fixed-on" checked disabled><label for="sp_applyFilters"> 标记/屏蔽-饼干/关键词</label><input type="hidden" name="sp_applyFilters" value="1"></div>
          <div style="width:50%;"><input type="checkbox" id="sp_enhanceIsland" class="fixed-on" checked disabled><label for="sp_enhanceIsland"> 增强X岛匿名版</label><input type="hidden" name="sp_enhanceIsland" value="1"></div>
       </div>
  
        <div style="margin-top:12px;">
          <!-- 标记饼干(组) -->
          <div class="sp_fold" style="border:1px solid #eee;margin:6px 0;">
            <div class="sp_fold_head" data-btn="#btn_sp_marked,#btn_group_marked"
                 style="display:flex;align-items:center;padding:6px 8px;background:#fafafa;cursor:pointer;">
              <span>标记饼干</span>
              <button id="btn_group_marked" class="xdex-inv" style="margin-left:auto;padding:2px 8px;">添加分组</button>
              <button id="btn_sp_marked" class="sp_save xdex-inv" data-id="sp_marked"
                      style="margin-left:4px;padding:2px 8px;">保存</button>
            </div>
            <div class="sp_fold_body" style="display:none;padding:8px 10px;">
              <div id="marked-inputs-container"></div>
            </div>
          </div>
  
          <!-- 屏蔽饼干(组,含备注) -->
          <div class="sp_fold" style="border:1px solid #eee;margin:6px 0;">
            <div class="sp_fold_head" data-btn="#btn_sp_blocked,#btn_group_blocked"
                 style="display:flex;align-items:center;padding:6px 8px;background:#fafafa;cursor:pointer;">
              <span>屏蔽饼干</span>
              <button id="btn_group_blocked" class="xdex-inv" style="margin-left:auto;padding:2px 8px;">添加分组</button>
              <button id="btn_sp_blocked" class="sp_save xdex-inv" data-id="sp_blocked"
                      style="margin-left:4px;padding:2px 8px;">保存</button>
            </div>
            <div class="sp_fold_body" style="display:none;padding:8px 10px;">
              <div id="blocked-inputs-container"></div>
            </div>
          </div>
  
          <!-- 屏蔽关键词(单输入) -->
          ${fold('sp_blockedKeywords','屏蔽关键词','关键词请用逗号隔开,词中包含逗号请加\\\转义')}
        </div>
      </div>
  
      <div id="sp_panel_footer" style="padding:10px 18px;text-align:right;border-top:1px solid #eee;background:#fff;">
        <button id="sp_apply" style="margin-right:10px;padding:6px 10px;">应用更改</button>
        <button id="sp_close" style="padding:6px 10px;">关闭</button>
      </div>
    </div>
  </div>`;
        $('#sp_cover').remove();
        $('body').append(html);
  
        // 折叠头:统一控制
        $('.sp_fold_head').off('click').on('click', function(){
          const $head = $(this);
          $head.next('.sp_fold_body').slideToggle(150);
          const btns = ($head.data('btn') || '').split(',');
          btns.forEach(sel => $(sel).toggleClass('xdex-inv'));
        });
  
        // 同步已有配置 & 默认折叠
        this.syncInputs();
  
        // 标记:新增组输入
        $('#btn_group_marked').off('click').on('click', e=>{
          e.stopPropagation();
          $('#marked-inputs-container').append(
            `<input class="marked-input" style="width:100%;padding:5px;"
                    placeholder="说明:饼干1,饼干2">`
          ).find('input').last().focus();
        });
  
        // 屏蔽:新增组输入
        $('#btn_group_blocked').off('click').on('click', e=>{
          e.stopPropagation();
          $('#blocked-inputs-container').append(
            `<input class="blocked-input" style="width:100%;padding:5px;"
                    placeholder="备注:3-7位饼干ID,多个用逗号隔开">`
          ).find('input').last().focus();
        });
  
        // 标记:保存
        $('#btn_sp_marked').off('click').on('click', e=>{
          e.stopPropagation();
          const parsed = [];
          let valid = true;
          $('#marked-inputs-container .marked-input').each((_,el)=>{
            const v = $(el).val().trim();
            if (!v) return;
            const { desc, list } = parseDescAndListByLastColon(v);
            if (!list.length) { toast(`“${v}” 未指定饼干`); valid=false; return false; }
            if (!isValidDesc(desc)) { toast(`“${v}” 分组说明过长`); valid=false; return false; }
            if (list.some(id=>!Utils.cookieLegal(id))) { toast(`“${v}” 存在不合法饼干`); valid=false; return false; }
            parsed.push({ desc, cookies: list });
          });
          if (!valid) return;
          this.state.markedGroups = parsed;
          GM_setValue(this.key, this.state);
          toast('标记分组已保存');
          applyFilters(this.state);
        });
  
        // 屏蔽:保存
        $('#btn_sp_blocked').off('click').on('click', e=>{
          e.stopPropagation();
          const parsed = [];
          let valid = true;
          $('#blocked-inputs-container .blocked-input').each((_,el)=>{
            const v = $(el).val().trim();
            if (!v) return;
            const { desc, list } = parseDescAndListByLastColon(v);
            if (!list.length) { toast(`“${v}” 未指定饼干`); valid=false; return false; }
            if (!isValidDesc(desc)) { toast(`“${v}” 备注过长`); valid=false; return false; }
            if (list.some(id=>!Utils.cookieLegal(id))) { toast(`“${v}” 存在不合法饼干`); valid=false; return false; }
            parsed.push({ desc, cookies: list });
          });
          if (!valid) return;
          this.state.blockedCookies = parsed;
          GM_setValue(this.key, this.state);
          toast('屏蔽饼干已保存');
          applyFilters(this.state);
        });
  
        // 屏蔽关键词:单项保存
        $('.sp_save').filter('[data-id="sp_blockedKeywords"]').off('click').on('click', e=>{
          e.stopPropagation();
          const v = $('#sp_blockedKeywords').val().trim();
          if (v && !Utils.strToList(v).length) return toast('屏蔽关键词 规则有误');
          this.state.blockedKeywords = v;
          GM_setValue(this.key, this.state);
          toast('屏蔽关键词 已保存');
          applyFilters(this.state);
        });
  
        // 应用更改:保存开关、屏蔽(组)、标记(组)
        $('#sp_apply').off('click').on('click', ()=>{
          [
            'enableCookieSwitch',
            'duplicatePagination',
            'disableWatermark',
            'enablePaginationDuplication',
            'updatePreviewCookie',
            'hideEmptyTitleEmail',
            'enableExternalImagePreview',
            'enableAutoCookieRefresh',
            'enableAutoCookieRefreshToast',
            'enableSeamlessPaging',
            'enableAutoSeamlessPaging',
            'enableHDImage',
            'enableLinkBlank',
            'enableQuotePreview',
            'extendQuote'
          ].forEach(k=> this.state[k] = $('#sp_'+k).is(':checked'));
  
          // 屏蔽关键词
          this.state.blockedKeywords = $('#sp_blockedKeywords').val().trim();
  
          // 标记分组
          const mk = [];
          let valid = true;
          $('#marked-inputs-container .marked-input').each((_,el)=>{
            const v = $(el).val().trim();
            if (!v) return;
            const { desc, list } = parseDescAndListByLastColon(v);
            if (!list.length) { toast(`“${v}” 未指定饼干`); valid=false; return false; }
            if (!isValidDesc(desc)) { toast(`“${v}” 分组说明过长`); valid=false; return false; }
            if (list.some(id=>!Utils.cookieLegal(id))) { toast(`“${v}” 存在不合法饼干`); valid=false; return false; }
            mk.push({ desc, cookies: list });
          });
          if (!valid) return;
          this.state.markedGroups = mk;
  
          // 屏蔽分组
          const bk = [];
          $('#blocked-inputs-container .blocked-input').each((_,el)=>{
            const v = $(el).val().trim();
            if (!v) return;
            const { desc, list } = parseDescAndListByLastColon(v);
            if (!list.length) { toast(`“${v}” 未指定饼干`); valid=false; return false; }
            if (!isValidDesc(desc)) { toast(`“${v}” 备注过长`); valid=false; return false; }
            if (list.some(id=>!Utils.cookieLegal(id))) { toast(`“${v}” 存在不合法饼干`); valid=false; return false; }
            bk.push({ desc, cookies: list });
          });
          if (!valid) return;
          this.state.blockedCookies = bk;
  
          GM_setValue(this.key, this.state);
          toast('保存成功,即将刷新页面');
          setTimeout(()=>location.reload(),500);
        });
  
        // 关闭面板
        $('#sp_close,#sp_cover').off('click').on('click', e=>{
          if (e.target.id==='sp_close' || e.target.id==='sp_cover')
            $('#sp_cover').fadeOut();
        });
  
        //鼠标悬浮在具体功能上显示提示
        // ====== 1. 定义功能描述映射表 ======
        const spDescriptions = {
          sp_enableCookieSwitch: '发帖框上方添加饼干切换器,单击即可快速切换饼干。使用前可单击“刷新”以获取当前登陆账户最新饼干列表。',
          sp_enablePaginationDuplication: '在串首页添加页码导航栏',
          sp_disableWatermark: '取消发图默认勾选的水印选项',
          sp_updatePreviewCookie: '为“增强X岛匿名版”添加的预览框显示真实饼干',
          sp_hideEmptyTitleEmail: '隐藏帖内无标题、无名氏和版规提示,优化显示效果,减少版面占用',
          sp_enableExternalImagePreview: '直接显示外部图床的图片',
          sp_enableAutoCookieRefresh: '回到X岛页面后自动刷新饼干,以防错饼',
          sp_enableAutoCookieRefreshToast: '自动刷新时显示toast提示,触发频率较高,建议关闭',
          sp_enableSeamlessPaging: '阅读到页面底部时无缝加载下一页并为新页首添加页码提示',
          sp_enableAutoSeamlessPaging: '滚动到页面底部后自动触发无缝翻页,关闭则可使用按钮手动无缝翻页',
          sp_enableHDImage: 'X岛-揭示板的增强型体验:默认加载原图而非缩略图,并为所有图片添加X岛自带图片控件',
          sp_enableLinkBlank: 'X岛-揭示板的增强型体验:串页链接在新标签页打开',
          sp_enableQuotePreview: '优化引用弹窗显示,将鼠标悬停出现引用弹窗改为点击显示引用弹窗,引用弹窗可持久存在,支持嵌套、拖拽,点击非引用弹窗区域或ESC键可关闭当前引用弹窗,点击右下角×以关闭全部引用弹窗',
          sp_extendQuote: '拓展引用格式,支持除“>>No.66994128”标准引用格式外的引用,例如“>>66994128”、“66994128”、“No.66994128”,同样支持“优化引用弹窗”',
          sp_updateReplyNumbers: '添加当页内回复编号显示',
          sp_replaceRightSidebar: '增强右侧扩展坞功能,点击REPLY按钮打开回复弹窗,点击非回复弹窗区域或ESC键可关闭回复弹窗,另外支持使用CTRL+ENTER发送消息',
          sp_interceptReplyForm: '拦截回复跳转中间页,使用toast提示发送成功/失败信息',
          sp_kaomojiEnhancer: '拓展颜文字功能,添加更多颜文字(来自蓝岛),优化选择颜文字弹窗,选择颜文字后可插入光标所在处',
          sp_highlightPO: '为回复添加Po主标志,PO主回复编号使用角标显示',
          sp_enhancePostFormLayout: '优化发串/回复表单布局,将“送出”按钮移至颜文字栏目,折叠“标题”“E-mail”“名称”等不常用项目,节省版面',
          sp_applyFilters: '标记/屏蔽-饼干/关键词过滤规则',
          sp_enhanceIsland: '增强X岛匿名版:\n1.发串前显示预览:麻麻再也不用担心我的ASCII ART排版失误了,另外支持预览插入图片和外部图床图片;\n2.自动保存编辑:记忆文本框内容(防止屏蔽词导致被吞),可以在翻页等各种页面切换后保存,仅在“回复成功”后删除,按主串号 "/t/xxxx" 分开存储;\n3.追记引用串号:点击串号回复时附加到光标所在处(或替换文本选区),可追记多条引用;\n4.人类友好的时间显示:如“5秒前”、“1小时前”、“昨天”等;\n5.粘贴插入图片:直接粘贴,将自动作为图片插入\n自动添加标题:将po主设置的标题或者第一行文字 + 页码设置为标签页标题'
        };
  
        // ====== 2. 创建 tooltip 元素并添加样式 ======
        if (!$('#sp_tooltip').length) {
          $('body').append('<div id="sp_tooltip"></div>');
          const tooltipStyle = `
            #sp_tooltip {
              position: fixed;
              max-width: 260px;
              background: rgba(0,0,0,0.85);
              color: #fff;
              padding: 6px 10px;
              border-radius: 4px;
              font-size: 12px;
              line-height: 1.4;
              pointer-events: none;
              display: none;
              z-index: 100000;
              box-shadow: 0 2px 6px rgba(0,0,0,0.3);
              transition: opacity 0.15s ease;
              opacity: 0;
              white-space: pre-line;
            }
            #sp_tooltip.show {
              display: block;
              opacity: 1;
            }
          `;
          $('<style>').text(tooltipStyle).appendTo('head');
        }
  
        // ====== 3. 绑定事件到每个设置项 ======
        $('#sp_checkbox_container input[type=checkbox]').each(function(){
          const id = this.id;
          const desc = spDescriptions[id];
          if (!desc) return;
  
          const $label = $(this).next('label');
          const $target = $label.length ? $label : $(this);
  
          $target.on('mouseenter', function(e){
            $('#sp_tooltip').text(desc)
              .css({ top: e.clientY + 12, left: e.clientX + 12 })
              .addClass('show');
          }).on('mousemove', function(e){
            const offsetX = 40; // 横向偏移量
            const offsetY = 0; // 纵向偏移量
            $('#sp_tooltip').css({
              top: e.clientY + offsetY,   // 保持纵向偏移
              left: e.clientX + offsetX   // 横向偏移改大,右移更多
            });
  
          }).on('mouseleave', function(){
            $('#sp_tooltip').removeClass('show');
          });
        });
  
      },
  
      syncInputs() {
        // 勾选框
        [
          'enableCookieSwitch',
          'duplicatePagination',
          'disableWatermark',
          'enablePaginationDuplication',
          'updatePreviewCookie',
          'hideEmptyTitleEmail',
          'enableExternalImagePreview',
          'enableAutoCookieRefresh',
          'enableAutoCookieRefreshToast',
          'enableSeamlessPaging',
          'enableAutoSeamlessPaging',
          'enableHDImage',
          'enableLinkBlank',
          'enableQuotePreview',
          'extendQuote'
        ].forEach(k=> $('#sp_'+k).prop('checked', this.state[k]));
  
        // 标记分组
        const groupsM = this.state.markedGroups.length ? this.state.markedGroups : [{desc:'',cookies:[]}];
        const $m = $('#marked-inputs-container').empty();
        groupsM.forEach(g=>{
          const v = g.desc ? `${g.desc}:${g.cookies.join(',')}` : '';
          $m.append(
            `<input class="marked-input" style="width:100%;padding:5px;"
                    placeholder="说明:饼干1,饼干2">`
          ).find('input').last().val(v);
        });
  
        // 屏蔽分组
        const groupsB = this.state.blockedCookies.length ? this.state.blockedCookies : [{desc:'',cookies:[]}];
        const $b = $('#blocked-inputs-container').empty();
        groupsB.forEach(g=>{
          const v = g.desc ? `${g.desc}:${g.cookies.join(',')}` : '';
          $b.append(
            `<input class="blocked-input" style="width:100%;padding:5px;"
                    placeholder="备注:3-7位饼干ID,多个用逗号隔开">`
          ).find('input').last().val(v);
        });
  
        // 屏蔽关键词
        $('#sp_blockedKeywords').val(this.state.blockedKeywords);
  
        // 初始折叠与按钮隐藏
        $('.sp_fold_body').hide();
        $('#btn_group_marked,#btn_sp_marked,#btn_group_blocked,#btn_sp_blocked').addClass('xdex-inv');
      }
    };
  
    /* --------------------------------------------------
     * 2. 回复编号
     * -------------------------------------------------- */
  // 数字样式:包裹为『n』
  const circledNumber = n => `『${n}』`;
  
  function updateReplyNumbers() {
    // 遍历每一个页面的回复区(含无缝加载的)
    $('.h-threads-item-replies').each(function () {
      let effectiveCount = 0;
  
      $(this).find('.h-threads-item-reply-icon').each(function () {
        const $reply = $(this).closest('[data-threads-id]');
  
        if ($reply.attr('data-threads-id') === '9999999') {
          // 特殊:小提示串号 -> 编号 0
          $(this).text(circledNumber(0));
        } else {
          // 普通回复 -> 依次递增
          effectiveCount++;
          $(this).text(circledNumber(effectiveCount));
        }
      });
    });
  }
  
  
    /* --------------------------------------------------
     * 3. 饼干标记 / 屏蔽 逻辑
     * -------------------------------------------------- */
    // 标记:支持同一饼干命中多个分组时,title 展示多行备注,颜色取首匹配组
    function markAllCookies(groups) {
      $('span.h-threads-info-uid').each(function(){
        const $el = $(this);
        const cid = ($el.text().split(':')[1]||'').trim();
        const hits = [];
        for (let i=0;i<groups.length;i++){
          const g = groups[i];
          if (g.cookies.some(p=>Utils.cookieMatch(cid,p))) {
            if (g.desc) hits.push(g.desc);
          }
        }
        if (!hits.length) return;
        const firstIdx = groups.findIndex(g=>g.cookies.some(p=>Utils.cookieMatch(cid,p)));
        const color = markColors[firstIdx % markColors.length];
        $el.css({ background: color, padding:'0 3px', borderRadius:'2px' })
           .attr('title', hits.join('\n'));
      });
    }
  
    function applyFilters(cfg) {
      // 标记
      markAllCookies(cfg.markedGroups||[]);
  
      // 屏蔽(按组,匹配到则折叠,文案含备注)
      const blkG = (cfg.blockedCookies||[]);
      const blkK = Utils.strToList(cfg.blockedKeywords);
      const check = $el => {
        const cid = ($el.find('.h-threads-info-uid').first().text().split(':')[1]||'').trim();
        const txt = $el.find('.h-threads-content').first().text();
  
        if (cid && blkG.length) {
          let matchedCookie = null, matchedDesc = '';
          for (let i=0; i<blkG.length && !matchedCookie; i++){
            const g = blkG[i];
            const hit = g.cookies.find(p=>Utils.cookieMatch(cid,p));
            if (hit) { matchedCookie = hit; matchedDesc = g.desc || ''; }
          }
          if (matchedCookie) {
            const label = matchedDesc ? `${matchedCookie}:${matchedDesc}` : matchedCookie;
            const $ph = Utils.collapse($el, `饼干屏蔽『${label}』`);
            if ($ph && $el.hasClass('h-threads-item-reply-main')) {
              // 只标记“被屏蔽的占位符”
              $ph.addClass('xdex-placeholder-blocked');
  
              // 仅对这一条回复行启用左右并排(不会影响别处)
              const $row  = $el.closest('.h-threads-item-reply');
              const $icon = $row.find('.h-threads-item-reply-icon').first();
              if ($row.length) {
                $row.css({ display: 'flex', alignItems: 'flex-start' });
              }
              if ($icon.length) {
                $icon.css({ flex: '0 0 3em', textAlign: 'center' }); // 固定左列宽
              }
  
              // 折叠状态(显示“关键词屏蔽…”)应占满;展开状态(“点击折叠”)应缩成小按钮
              const applyWidth = () => {
                const txt = ($ph.text() || '').trim();
                if (txt === '点击折叠') {
                  $ph.css({
                    flex: '0 0 auto',
                    maxWidth: '8em',
                    whiteSpace: 'nowrap',
                    overflow: 'hidden',
                    textOverflow: 'ellipsis',
                    marginRight: '0.5em'
                  });
                } else {
                  $ph.css({
                    flex: '0 0 auto',
                    maxWidth: 'none',
                    whiteSpace: 'normal',
                    overflow: 'visible',
                    textOverflow: 'clip',
                    marginRight: '0'
                  });
                }
              };
              // 初次应用一次
              applyWidth();
              // 点击后(折叠/展开切换文本之后)再应用一次
              $ph.off('click.xdex-blocked').on('click.xdex-blocked', () => {
                setTimeout(applyWidth, 0);
              });
            }
            return;
          }
        }
  
        const kw = Utils.firstHit(txt, blkK);
        if (kw) {
          const $ph = Utils.collapse($el, `关键词屏蔽『${kw}』`);
          if ($ph && $el.hasClass('h-threads-item-reply-main')) {
            $ph.addClass('xdex-placeholder-blocked');
  
            const $row  = $el.closest('.h-threads-item-reply');
            const $icon = $row.find('.h-threads-item-reply-icon').first();
            if ($row.length) {
              $row.css({ display: 'flex', alignItems: 'flex-start' });
            }
            if ($icon.length) {
              $icon.css({ flex: '0 0 3em', textAlign: 'center' });
            }
  
            const applyWidth = () => {
              const txt = ($ph.text() || '').trim();
              if (txt === '点击折叠') {
                $ph.css({
                  flex: '0 0 auto',
                  maxWidth: '8em',
                  whiteSpace: 'nowrap',
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                  marginRight: '0.5em'
                });
              } else {
                $ph.css({
                  flex: '1 1 auto',
                  maxWidth: 'none',
                  whiteSpace: 'normal',
                  overflow: 'visible',
                  textOverflow: 'clip',
                  marginRight: '0'
                });
              }
            };
            applyWidth();
            $ph.off('click.xdex-blocked').on('click.xdex-blocked', () => {
              setTimeout(applyWidth, 0);
            });
          }
        }
      };
  
      if(/\/t\/\d{8,}/.test(location.pathname)){
        $('.h-threads-item-reply-main').each((_,el)=>check($(el)));
      } else {
        $('.h-threads-item-index').each((_,el)=>{
          const $th=$(el);
          check($th);
          $th.find('.h-threads-item-reply-main').each((_,s)=>check($(s)));
        });
      }
    }
  
    /* --------------------------------------------------
     * 4. 外部图床显示
     * -------------------------------------------------- */
    const ExternalImagePreview = (function(){
      let started = false;
      const PROCESSED_ATTRIBUTE = 'data-images-processed';
      const DEFAULT_VISIBLE = 3;
      const LOAD_BATCH = 3;
      const imageUrlRegex = /(https?:\/\/[^\s'")\]}]+?\.(?:jpg|jpeg|png|gif|bmp|webp|svg)(?:\?[^\s'")\]}]*)?)(?=$|\s|['")\]}.,!?])/gi;
  
      function buttonCss() {
        return `
          font-size: 12px;
          padding: 4px 10px;
          margin-left: 6px;
          color: #333;
          background: #fff;
          border: 1px solid #ccc;
          border-radius: 4px;
          cursor: pointer;
        `;
      }
  
      function createImageItem(url, containerWidth) {
        const frag = document.createDocumentFragment();
  
        const img = document.createElement('img');
        img.src = url;
        img.style.cssText = `
          display: block;
          margin: 8px auto 2px auto;
          border: 1px solid #ccc;
          border-radius: 3px;
          cursor: pointer;
          height: auto;
        `;
  
        const linkDiv = document.createElement('div');
        linkDiv.style.cssText = `
          font-size: 12px;
          color: #666;
          margin: 0 auto 10px auto;
          word-break: break-all;
          width: fit-content;
          max-width: 100%;
        `;
        const a = document.createElement('a');
        a.href = url;
        a.target = '_blank';
        a.textContent = url;
        a.style.color = '#007bff';
        a.addEventListener('click', (e) => e.stopPropagation());
        linkDiv.appendChild(a);
  
        img.addEventListener('load', () => {
          const naturalW = img.naturalWidth || 0;
          if (naturalW > containerWidth) {
            img.style.width = Math.round(containerWidth * 0.75) + 'px';
            img.dataset.state = 'large';
          } else {
            img.style.width = naturalW + 'px';
            img.dataset.state = 'small';
          }
        });
  
        img.addEventListener('error', () => {
          img.style.display = 'none';
          linkDiv.style.display = 'none';
        });
  
        img.addEventListener('click', (e) => {
          e.stopPropagation();
          const state = img.dataset.state;
          if (state === 'large') {
            window.open(url, '_blank');
          } else if (state === 'small') {
            const naturalW = img.naturalWidth || 0;
            const targetW = Math.round(containerWidth * 0.75);
            if (!img.dataset.enlarged && targetW > naturalW) {
              img.style.width = targetW + 'px';
              img.dataset.enlarged = 'true';
            } else {
              window.open(url, '_blank');
            }
          }
        });
  
        frag.appendChild(img);
        frag.appendChild(linkDiv);
        return frag;
      }
  
      function appendImages(bodyEl, urls, containerWidth) {
        const frag = document.createDocumentFragment();
        urls.forEach((url) => frag.appendChild(createImageItem(url, containerWidth)));
        bodyEl.appendChild(frag);
      }
  
      function setCollapsed(container, collapsed) {
        container.classList.toggle('collapsed', collapsed);
        container.dataset.collapsed = collapsed ? 'true' : 'false';
        container.querySelectorAll('.iic-toggle-btn').forEach((btn) => {
          btn.textContent = collapsed ? '展开' : '收起';
        });
      }
  
      function injectContainer(afterDiv, imageUrls) {
        const total = imageUrls.length;
        if (total === 0) return;
  
        const container = document.createElement('div');
        container.className = 'injected-image-container';
        container.style.cssText = `
          margin: 12px 0;
          border: 1px solid #ddd;
          border-radius: 6px;
          background-color: #f9f9f9;
          box-shadow: 0 1px 2px rgba(0,0,0,0.03);
          overflow: hidden;
        `;
        container.dataset.total = String(total);
        container.dataset.collapsed = 'false';
  
        const header = document.createElement('div');
        header.className = 'iic-header';
        header.style.cssText = `
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 10px 14px;
          background: #f2f2f2;
          border-bottom: 1px solid #e6e6e6;
          cursor: default;
          user-select: none;
        `;
        const title = document.createElement('span');
        title.className = 'iic-title';
        title.textContent = `图片预览(${total})`;
        title.style.cssText = `font-size: 13px; color: #333;`;
  
        const actions = document.createElement('div');
        actions.className = 'iic-actions';
  
        const moreTopBtn = document.createElement('button');
        moreTopBtn.className = 'iic-more-btn-top';
        moreTopBtn.style.cssText = buttonCss();
  
        const toggleBtn = document.createElement('button');
        toggleBtn.className = 'iic-toggle-btn';
        toggleBtn.textContent = '收起';
        toggleBtn.style.cssText = buttonCss();
        toggleBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          const next = container.dataset.collapsed !== 'true';
          setCollapsed(container, next);
        });
  
        actions.appendChild(moreTopBtn);
        actions.appendChild(toggleBtn);
        header.appendChild(title);
        header.appendChild(actions);
  
        const body = document.createElement('div');
        body.className = 'iic-body';
        body.style.cssText = `
          padding: 12px 24px 10px 24px;
          overflow-x: auto;
        `;
  
        const footer = document.createElement('div');
        footer.className = 'iic-footer';
        footer.style.cssText = `
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 8px 12px 12px 12px;
          background: #f9f9f9;
          border-top: 1px solid #eee;
        `;
        const moreBottomBtn = document.createElement('button');
        moreBottomBtn.className = 'iic-more-btn-bottom';
        moreBottomBtn.style.cssText = buttonCss();
        footer.appendChild(moreBottomBtn);
  
        const style = document.createElement('style');
        style.textContent = `
          .injected-image-container.collapsed .iic-body,
          .injected-image-container.collapsed .iic-footer { display: none; }
        `;
  
        container.appendChild(style);
        container.appendChild(header);
        container.appendChild(body);
        container.appendChild(footer);
        afterDiv.parentNode.insertBefore(container, afterDiv.nextSibling);
  
        const containerWidth = body.clientWidth || 600;
  
        const visibleUrls = imageUrls.slice(0, DEFAULT_VISIBLE);
        const queue = imageUrls.slice(DEFAULT_VISIBLE);
  
        appendImages(body, visibleUrls, containerWidth);
  
        function remainingCount() { return queue.length; }
        function updateMoreButtons() {
          const rem = remainingCount();
          const label = rem > 0 ? `展开更多(剩余${rem},+${LOAD_BATCH})` : '已全部展开';
          moreTopBtn.textContent = label;
          moreBottomBtn.textContent = label;
          const display = rem > 0 ? '' : 'none';
          moreTopBtn.style.display = display;
          moreBottomBtn.style.display = display;
        }
        function loadMore(e) {
          e.stopPropagation();
          if (queue.length === 0) return;
          const batch = queue.splice(0, LOAD_BATCH);
          appendImages(body, batch, containerWidth);
          updateMoreButtons();
        }
        moreTopBtn.addEventListener('click', loadMore);
        moreBottomBtn.addEventListener('click', loadMore);
        updateMoreButtons();
  
        container.addEventListener('click', (e) => {
          const target = e.target;
          if (target.closest('img, button, a, input, label, textarea, select')) return;
          if (container.dataset.collapsed !== 'true') setCollapsed(container, true);
        });
      }
  
      function processDiv(div) {
        if (div.hasAttribute(PROCESSED_ATTRIBUTE)) return;
  
        // 如果是预览框且已经有图片,则直接标记为已处理并跳过
        if (div.classList.contains('h-preview-box') && div.querySelector('img')) {
          div.setAttribute(PROCESSED_ATTRIBUTE, 'true');
          return;
        }
  
        div.setAttribute(PROCESSED_ATTRIBUTE, 'true');
  
        const textContent = div.textContent || div.innerText || '';
        const matches = textContent.match(imageUrlRegex);
        if (!matches || matches.length === 0) return;
  
        const uniqueImageUrls = [...new Set(matches.map((u) => u.trim()))];
        if (uniqueImageUrls.length === 0) return;
  
        injectContainer(div, uniqueImageUrls);
      }
  
  
      function findAndProcessDivs() {
        const divs = document.querySelectorAll(
          'div.h-threads-content:not([' + PROCESSED_ATTRIBUTE + ']), ' +
          'div.h-preview-box:not([' + PROCESSED_ATTRIBUTE + '])'
        );
        divs.forEach(processDiv);
      }
  
      function observeChanges() {
        const observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
              mutation.addedNodes.forEach((node) => {
                if (node.nodeType !== 1) return;
                if (node.classList && node.classList.contains('injected-image-container')) return;
  
                if (
                  node.classList &&
                  (node.classList.contains('h-threads-content') || node.classList.contains('h-preview-box')) &&
                  !node.hasAttribute(PROCESSED_ATTRIBUTE)
                ) {
                  processDiv(node);
                }
  
                const childDivs = node.querySelectorAll && node.querySelectorAll(
                  'div.h-threads-content:not([' + PROCESSED_ATTRIBUTE + ']), ' +
                  'div.h-preview-box:not([' + PROCESSED_ATTRIBUTE + '])'
                );
  
                if (childDivs && childDivs.length > 0) {
                  childDivs.forEach(processDiv);
                }
              });
            }
          });
        });
        observer.observe(document.body, { childList: true, subtree: true });
      }
  
      function init() {
        if (started) return;
        started = true;
        setTimeout(findAndProcessDivs, 100);
        observeChanges();
        window.addEventListener('load', () => setTimeout(findAndProcessDivs, 500));
        // 调试辅助
        window.resetImageScript = function resetProcessedElements() {
          document.querySelectorAll('[' + PROCESSED_ATTRIBUTE + ']').forEach((el) => el.removeAttribute(PROCESSED_ATTRIBUTE));
          document.querySelectorAll('.injected-image-container').forEach((c) => c.remove());
        };
      }
  
      return { init };
    })();
  
    /* --------------------------------------------------
     * 5. 手动切换饼干 + 自动刷新饼干
     * -------------------------------------------------- */
    const abbreviateName = n => n.replace(/\s*-\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/, '');
    const getCookiesList   = () => GM_getValue('cookies', {});
    const getCurrentCookie = () => GM_getValue('now-cookie', null);
  
    function removeDateString(){
      $('#cookie-switcher-ui').find('*').addBack().contents()
        .filter(function(){ return this.nodeType===3; })
        .each(function(){
          this.nodeValue = this.nodeValue.replace(/ - 0000-00-00 00:00:00/g,'');
        });
    }
    function updateCurrentCookieDisplay(cur){
      const $d = $('#current-cookie-display');
      if(!$d.length) return;
      if(cur){
        const nm = abbreviateName(cur.name);
        $d.text(nm + (cur.desc ? ' - ' + cur.desc : '')).css('color','#000');
      } else {
        $d.text('已删除').css('color','red');
      }
      removeDateString();
    }
    function updateDropdownUI(list){
      const $dd = $('#cookie-dropdown'); $dd.empty();
      Object.keys(list).forEach(id=>{
        const c=list[id];
        const txt=abbreviateName(c.name)+(c.desc?' - '+c.desc:'');
        $dd.append(`<option value="${id}">${txt}</option>`);
      });
      const cur = getCurrentCookie();
      cur && list[cur.id] ? $dd.val(cur.id) : $dd.val('');
      removeDateString();
    }
    function switch_cookie(cookie){
      if(!cookie || !cookie.id) return toast('无效的饼干信息!');
      $.get(`https://www.nmbxd1.com/Member/User/Cookie/switchTo/id/${cookie.id}.html`)
        .done(()=>{
          toast('切换成功! 当前饼干为 '+abbreviateName(cookie.name));
          GM_setValue('now-cookie',cookie);
          updateCurrentCookieDisplay(cookie);
          updateDropdownUI(getCookiesList());
          removeDateString();
          updatePreviewCookieId();
        })
        .fail(()=>toast('切换失败,请重试'));
    }
    function refreshCookies(cb, showToast = true){
      GM_xmlhttpRequest({
        method:'GET',
        url:'https://www.nmbxd1.com/Member/User/Cookie/index.html',
        onload:r=>{
          if(r.status!==200){ toast('刷新失败 HTTP '+r.status); return cb&&cb(); }
          const doc=new DOMParser().parseFromString(r.responseText,'text/html');
          const rows=doc.querySelectorAll('tbody>tr'), list={};
          rows.forEach(row=>{
            const tds=row.querySelectorAll('td');
            if(tds.length>=4){
              const id=tds[1].textContent.trim();
              const name=(tds[2].querySelector('a')||{}).textContent?.trim?.() || (tds[2].textContent||'').trim();
              const desc=tds[3].textContent.trim();
              list[id]={id,name,desc};
            }
          });
          GM_setValue('cookies',list);
          updateDropdownUI(list);
          if (showToast) {
            toast('饼干列表已刷新!');
          }
          let cur=getCurrentCookie();
          if(cur && !list[cur.id]) cur=null;
          GM_setValue('now-cookie',cur);
          updateCurrentCookieDisplay(cur);
          removeDateString();
          updatePreviewCookieId();
          cb&&cb();
        },
        onerror:()=>{
          toast('刷新失败,网络错误'); cb&&cb();
        }
      });
    }
  
    function showLoginPrompt(){
      const $m=$(`
  <div style="position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:10000;" id="login-modal">
    <div style="position:relative;margin:20% auto;width:300px;background:#fff;padding:20px;border-radius:8px;">
      <h2>提示</h2><p>当前已退出登录,无法切换饼干。</p>
      <div style="text-align:right;">
        <button id="login-open" style="margin-right:10px;">登录</button>
        <button id="login-close">关闭</button>
      </div>
    </div>
  </div>`);
      $('body').append($m);
      $('#login-open').on('click',()=>{
        window.open('https://www.nmbxd1.com/Member/User/Index/login.html','_blank');
        $m.fadeOut(200,()=>$m.remove());
      });
      $('#login-close').on('click',()=>$m.fadeOut(200,()=>$m.remove()));
    }
    function createCookieSwitcherUI(){
      const $title = $('.h-post-form-title:contains("回应模式")').first();
      let $grid = $title.closest('.uk-grid.uk-grid-small.h-post-form-grid');
      if(!$grid.length)
        $grid = $('.h-post-form-title:contains("名 称")').first()
          .closest('.uk-grid.uk-grid-small.h-post-form-grid');
      if(!$grid.length) return;
  
      const cur = getCurrentCookie(), list = getCookiesList();
  
      const $ui = $(`
    <div class="uk-grid uk-grid-small h-post-form-grid" id="cookie-switcher-ui" style="display: flex; flex-wrap: nowrap; align-items: center; width: 100%;">
      <div class="uk-width-1-5">
        <div class="h-post-form-title">饼干</div>
      </div>
      <div class="uk-width-4-5 h-post-form-input" style="display:flex;align-items:center;gap:8px;flex-wrap:nowrap;">
        <div style="flex:1 1 auto;display:flex;align-items:center;gap:6px;min-width:3ch;">
          <span id="current-cookie-display" style="max-width:40%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;"></span>
          <select id="cookie-dropdown" style="flex:1 1 auto;min-width:3ch;max-width:100%;"></select>
        </div>
        <button id="apply-cookie" class="uk-button uk-button-default" style="display:none;">应用</button>
        <div style="margin-left:auto;flex:0 0 auto;display:flex;align-items:center;">
          <button id="refresh-cookie" class="uk-button uk-button-default" style="min-width:1em;text-align:center;">刷新</button>
        </div>
      </div>
    </div>`);
  
      $grid.before($ui);
  
      updateCurrentCookieDisplay(cur);
      updateDropdownUI(list);
  
      // 单击下拉项即切换饼干
      $('#cookie-dropdown').on('change', function(){
        const sel = $(this).val();
        const l = getCookiesList();
        if(!Object.keys(l).length) return showLoginPrompt();
        if(!sel) return toast('请选择饼干');
        l[sel] ? switch_cookie(l[sel]) : toast('饼干信息无效');
      });
  
      // 刷新按钮
      $('#refresh-cookie').on('click', e=>{
        e.preventDefault();
        refreshCookies(null, true);
      });
    }
  
  
    /* --------------------------------------------------
     * 6. 页面增强:分页复制 / 关闭水印 / 预览区真实饼干 / 隐藏无标题+无名氏+版规 / 自动+手动无缝翻页
     * -------------------------------------------------- */
    function duplicatePagination(){
      const tit=document.querySelector('h2.h-title');
      const pag=document.querySelector('ul.uk-pagination.uk-pagination-left.h-pagination');
      if(!tit||!pag)return;
      const clone=pag.cloneNode(true);
      tit.parentNode.insertBefore(clone,tit.nextSibling);
      clone.querySelectorAll('a').forEach(a=>{
        if(a.textContent.trim()==='末页'){
          const m=a.href.match(/page=(\d+)/);
          if(m) a.textContent=`末页(${m[1]})`;
        }
      });
    }
    const disableWatermark = () => {
      const c = document.querySelector('input[type="checkbox"][name="water"][value="true"]');
      if(c) c.checked = false;
    };
    function updatePreviewCookieId(){
      if(!$('.h-preview-box').length) return;
      const cur=getCurrentCookie();
      const name=cur&&cur.name?abbreviateName(cur.name):'cookies';
      $('.h-preview-box .h-threads-info-uid').text('ID:'+name);
    }
    function hideEmptyTitleAndEmail(){
      $('.h-threads-info-title').each(function(){ if($(this).text().trim()==='无标题') $(this).hide(); });
      $('.h-threads-info-email').each(function(){ if($(this).text().trim()==='无名氏') $(this).hide(); });
    }
  
    function addLastPageNumber(){
      document.querySelectorAll('ul.uk-pagination.uk-pagination-left.h-pagination a').forEach(a => {
        if (a.textContent.trim() === '末页') {
          const m = a.href.match(/page=(\d+)/);
          if (m && !a.textContent.includes(`(${m[1]})`)) {
            a.textContent = `末页(${m[1]})`;
          }
        }
      });
    }
  
    // 自动监听 DOM 变化
    function observePagination(){
      const observer = new MutationObserver(mutations => {
        let foundPagination = false;
  
        for (const mutation of mutations) {
          for (const node of mutation.addedNodes) {
            if (node.nodeType !== 1) continue; // 只处理元素节点
  
            // 如果新增的是分页条本身,或它的子元素
            if (
              node.matches?.('ul.uk-pagination.uk-pagination-left.h-pagination') ||
              node.closest?.('ul.uk-pagination.uk-pagination-left.h-pagination')
            ) {
              foundPagination = true;
              break;
            }
          }
          if (foundPagination) break; // 已找到就不再继续遍历
        }
  
        if (foundPagination) {
          addLastPageNumber();
        }
      });
  
      observer.observe(document.body, {
        childList: true,
        subtree: true
      });
    }
  
    // 初始化
    //duplicatePagination();
    observePagination();
  
    function initSeamlessPaging() {
      try {
        const cfg = Object.assign({}, SettingPanel.defaults, GM_getValue(SettingPanel.key, {}));
        if (!cfg.enableSeamlessPaging) return;
  
        let loading = false;
        let done = false;
        const loadedPages = new Set();
  
        const isThreadPage = /\/t\/\d{4,}/.test(location.pathname);
        const isBoardPage = /^\/f\//.test(location.pathname);
  
        const originInfo = (function () {
          const cur = new URL(location.href, location.origin);
          const threadMatch = location.pathname.match(/\/t\/(\d{4,})/);
          return {
            origin: location.origin,
            threadId: threadMatch ? threadMatch[1] : (document.querySelector('[data-threads-id]')?.getAttribute('data-threads-id') || null),
            page: Number(cur.searchParams.get('page') || 1)
          };
        })();
  
        let lastLoadedPage = originInfo.page || 1;
        loadedPages.add(lastLoadedPage);
  
        const SENTINEL_ID = 'hld_auto_page_sentinel';
        let sentinel = document.getElementById(SENTINEL_ID);
  
        // 交互状态检测
        let hasUserInteracted = false;
        let lastUserScrollDir = 0;
        let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop || 0;
  
        function onUserScroll() {
          hasUserInteracted = true;
          const curTop = window.pageYOffset || document.documentElement.scrollTop || 0;
          lastUserScrollDir = (curTop > lastScrollTop) ? 1 : (curTop < lastScrollTop ? -1 : lastUserScrollDir);
          lastScrollTop = curTop;
        }
        function onWheel(e) {
          hasUserInteracted = true;
          if (typeof e.deltaY === 'number') lastUserScrollDir = e.deltaY > 0 ? 1 : -1;
        }
        window.addEventListener('scroll', onUserScroll, { passive: true });
        window.addEventListener('wheel', onWheel, { passive: true });
  
        // ======== 新增:桥接器 + 自动重执行器 ========
        function reinitForNewContent(container) {
          try {
            // 1. 派发自定义事件
            document.dispatchEvent(new CustomEvent('SeamlessPageAppended', {
              detail: { container }
            }));
  
            // 2. 模拟 DOMContentLoaded(部分脚本只监听这个)
            document.dispatchEvent(new Event('DOMContentLoaded'));
  
            // 3. 重新执行 container 内的 <script> 标签
            container.querySelectorAll('script').forEach(oldScript => {
              const newScript = document.createElement('script');
              if (oldScript.src) {
                newScript.src = oldScript.src;
              } else {
                newScript.textContent = oldScript.textContent;
              }
              [...oldScript.attributes].forEach(attr => {
                newScript.setAttribute(attr.name, attr.value);
              });
              oldScript.replaceWith(newScript);
            });
          } catch (err) {
            console.warn('reinitForNewContent error:', err);
          }
        }
        // ============================================
  
        // 串内页容器
        function getRootRepliesContainer() {
          const root = document.querySelector('.h-threads-item.uk-clearfix[data-threads-id]') ||
                      document.querySelector('.h-threads-item.uk-clearfix') ||
                      document.querySelector('[data-threads-id]');
          if (!root) return null;
          const replies = root.querySelectorAll('.h-threads-item-replies');
          if (!replies || replies.length === 0) return null;
          return { root, lastReplies: replies[replies.length - 1] };
        }
  
        function ensureSentinelPlaced() {
          const containers = getRootRepliesContainer();
          if (!containers) return;
          const { lastReplies } = containers;
          if (!sentinel) {
            sentinel = document.createElement('div');
            sentinel.id = SENTINEL_ID;
            sentinel.style.height = '1px';
            sentinel.style.width = '100%';
            sentinel.style.pointerEvents = 'none';
          }
          if (lastReplies.nextSibling !== sentinel) {
            lastReplies.parentNode.insertBefore(sentinel, lastReplies.nextSibling);
          }
        }
  
        // 板块页容器
        function ensureSentinelPlacedBoard() {
          const lists = document.querySelectorAll('.h-threads-list');
          const lastList = lists[lists.length - 1];
          if (!lastList) return;
          if (!sentinel) {
            sentinel = document.createElement('div');
            sentinel.id = SENTINEL_ID;
            sentinel.style.height = '1px';
            sentinel.style.width = '100%';
            sentinel.style.pointerEvents = 'none';
          }
          if (lastList.nextSibling !== sentinel) {
            lastList.parentNode.insertBefore(sentinel, lastList.nextSibling);
          }
        }
  
  
        function removeIdsFromNode(node) {
          if (!node || node.querySelectorAll === undefined) return;
          node.querySelectorAll('[id]').forEach(el => el.removeAttribute('id'));
          if (node.hasAttribute && node.hasAttribute('id')) node.removeAttribute('id');
        }
  
  
  
        function parseLastPageFromPagination(pagUl) {
          if (!pagUl) return null;
          const anchors = Array.from(pagUl.querySelectorAll('a')).map(a => a.href || a.getAttribute('href') || '');
          const pageNums = anchors.map(h => {
            try {
              const u = new URL(h, location.origin);
              const p = Number(u.searchParams.get('page') || '') || null;
              return p;
            } catch (e) {
              const m = (h || '').match(/page=(\d+)/);
              return m ? Number(m[1]) : null;
            }
          }).filter(n => !!n);
          if (pageNums.length === 0) return null;
          return Math.max(...pageNums);
        }
  
        function getDomLastPageNum() {
          const allPaginations = document.querySelectorAll('ul.uk-pagination.uk-pagination-left.h-pagination');
          if (allPaginations.length === 0) return null;
          const lastPagination = allPaginations[allPaginations.length - 1];
          return parseLastPageFromPagination(lastPagination);
        }
  
        function buildThreadPageUrl(threadId, pageNum) {
          return `${location.origin}/t/${threadId}?page=${pageNum}`;
        }
        function computeNextUrl() {
          const tid = originInfo.threadId || document.querySelector('[data-threads-id]')?.getAttribute('data-threads-id');
          if (!tid) return null;
          return buildThreadPageUrl(tid, lastLoadedPage + 1);
        }
  
        function extractFromHTML(htmlText) {
          const doc = new DOMParser().parseFromString(htmlText, 'text/html');
          const repliesAll = doc.querySelectorAll('.h-threads-item-replies');
          const replies = repliesAll.length ? repliesAll[0] : doc.querySelector('.h-threads-item-replies');
          let pagination = doc.querySelector('ul.uk-pagination.uk-pagination-left.h-pagination') ||
                          doc.querySelector('ul.uk-pagination.uk-pagination-left') ||
                          doc.querySelector('ul.uk-pagination');
          return { replies, pagination, doc };
        }
  
        // 串内页加载
        async function loadNext() {
          const domLast = getDomLastPageNum();
          if (domLast && lastLoadedPage >= domLast) {
            done = true;
            return;
          }
  
          if (loading || done) return;
          const nextPageNum = lastLoadedPage + 1;
          if (loadedPages.has(nextPageNum)) return;
  
          const nextUrl = computeNextUrl();
          if (!nextUrl) { done = true; return; }
  
          loading = true;
          try {
            const res = await fetch(nextUrl, { credentials: 'same-origin' });
            if (!res.ok) { done = true; return; }
            const html = await res.text();
            const { replies, pagination } = extractFromHTML(html);
            if (!replies) { done = true; return; }
  
            let pagClone = pagination ? pagination.cloneNode(true) : null;
            if (!pagClone) {
              pagClone = document.createElement('ul');
              pagClone.className = 'uk-pagination uk-pagination-left h-pagination';
            }
            pagClone.setAttribute('hld-auto-page', 'ok');
            removeIdsFromNode(pagClone);
            const lastPageNum = parseLastPageFromPagination(pagClone);
            if (lastPageNum) pagClone.setAttribute('data-last-page', String(lastPageNum));
            pagClone.setAttribute('data-cloned-page', String(nextPageNum));
  
            const repliesClone = replies.cloneNode(true);
            repliesClone.setAttribute('data-cloned-page', String(nextPageNum));
            removeIdsFromNode(repliesClone);
  
            const containers = getRootRepliesContainer();
            if (!containers) { done = true; return; }
            const { lastReplies } = containers;
  
            lastReplies.insertAdjacentElement('afterend', pagClone);
            pagClone.insertAdjacentElement('afterend', repliesClone);
  
            // ======== 新增:让其他脚本对新内容生效 ========
            reinitForNewContent(repliesClone);
            if (cfg.enableHDImage && typeof enableHDImage === 'function') {
              enableHDImage(repliesClone);
            }
            if (cfg.enableLinkBlank && typeof runLinkBlank === 'function') {
                runLinkBlank(repliesClone);
            }
            if (cfg.extendQuote && typeof extendQuote === 'function') {
              extendQuote(repliesClone);
            }
            // 防剧透渲染
            if (typeof renderSpoiler === 'function') {
              renderSpoiler($(repliesClone));
            }
            if (typeof initContent === 'function') {
              initContent();   // 重新绑定引用悬浮预览
            }
            initExtendedContent(repliesClone); // 扩展引用
            autoHideRefView(repliesClone); // 拓展引用悬浮
            // ============================================
  
            // 更新底部分页条
            const allPaginations = document.querySelectorAll('ul.uk-pagination.uk-pagination-left.h-pagination');
            if (allPaginations.length > 0) {
              const lastPagination = allPaginations[allPaginations.length - 1];
              if (lastPagination && lastPagination !== pagClone) {
                lastPagination.replaceWith(pagClone.cloneNode(true));
              }
            }
  
            loadedPages.add(nextPageNum);
            lastLoadedPage = nextPageNum;
  
            try { history.pushState(null, '', nextUrl); } catch (e) {}
  
  
            try { if (typeof hideEmptyTitleAndEmail === 'function') hideEmptyTitleAndEmail($(repliesClone)); } catch (e) {}
            setTimeout(() => {
              try { if (typeof hideEmptyTitleAndEmail === 'function') hideEmptyTitleAndEmail($(repliesClone)); } catch (e) {}
              try { if (typeof observePagination === 'function') observePagination(); } catch (e) {}
              try { if (typeof updateReplyNumbers === 'function') updateReplyNumbers(); } catch (e) {}
              try { if (cfg.enableHDImage && typeof enableHDImage === 'function') enableHDImage(); } catch (e) {}
              try { if (cfg.enableLinkBlank && typeof runLinkBlank === 'function') runLinkBlank(); } catch (e) {}
              try { if (cfg.extendQuote && typeof extendQuote === 'function') extendQuote(); } catch (e) {}
              try { if (cfg.enableQuotePreview && typeof enableQuotePreview === 'function') {
                enableQuotePreview();}
              } catch (e) {}
              try { if (typeof applyFilters === 'function') applyFilters(cfg); } catch (e) {}
              try { if (typeof autoHideRefView === 'function') autoHideRefView(); } catch (e) {}
            }, 50);
  
  
            ensureSentinelPlaced();
  
            const hasNextLink = (() => {
              const anchorsText = Array.from(pagClone.querySelectorAll('a')).map(a => a.textContent.trim());
              if (anchorsText.some(t => /下一页|下页|Next|next|›|»|→/.test(t))) return true;
              const parsed = parseLastPageFromPagination(pagClone);
              if (parsed && parsed > nextPageNum) return true;
              return false;
            })();
  
            if (!hasNextLink) done = true;
          } catch (e) {
            console.warn('seamless paging loadNext error:', e);
            done = true;
          } finally {
            loading = false;
          }
        }
  
        async function loadNextBoard() {
          if (loading || done) return;
          const nextPageNum = lastLoadedPage + 1;
          if (loadedPages.has(nextPageNum)) return;
  
          const nextUrl = `${location.origin}${location.pathname}?page=${nextPageNum}`;
          loading = true;
          try {
            const res = await fetch(nextUrl, { credentials: 'same-origin' });
            if (!res.ok) { done = true; return; }
            const html = await res.text();
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const list = doc.querySelector('.h-threads-list');
            const pagination = doc.querySelector('ul.uk-pagination.uk-pagination-left.h-pagination');
  
            if (!list) { done = true; return; }
  
            const listClone = list.cloneNode(true);
            removeIdsFromNode(listClone);
  
            // 调用隐藏无名氏/无标题 & 回复编号重置
            // 插入到 DOM 之后再延迟调用隐藏/编号/过滤
            setTimeout(() => {
              try { if (typeof hideEmptyTitleAndEmail === 'function') hideEmptyTitleAndEmail(); } catch (e) {}
              try { if (typeof updateReplyNumbers === 'function') updateReplyNumbers(); } catch (e) {}
              try { if (cfg.enableHDImage && typeof enableHDImage === 'function') enableHDImage(listClone); } catch (e) {}
              try { if (cfg.enableLinkBlank && typeof runLinkBlank === 'function') runLinkBlank(listClone); } catch (e) {}
              try { if (cfg.extendQuote && typeof extendQuote === 'function') extendQuote(listClone); } catch (e) {}
              try { if (cfg.enableQuotePreview && typeof enableQuotePreview === 'function') {
                enableQuotePreview();}
              } catch (e) {}
              try { if (typeof applyFilters === 'function') applyFilters(cfg); } catch (e) {}
              try { if (typeof autoHideRefView === 'function') autoHideRefView(listClone); } catch (e) {}
            }, 50);
  
  
  
  
            // 找到当前页面最后一个 .h-threads-list
            const lists = document.querySelectorAll('.h-threads-list');
            const lastList = lists[lists.length - 1];
            if (lastList) {
              let pagClone = pagination ? pagination.cloneNode(true) : null;
              if (pagClone) {
                removeIdsFromNode(pagClone);
                lastList.insertAdjacentElement('afterend', pagClone);
                pagClone.insertAdjacentElement('afterend', listClone);
  
                // ======== 新增:让其他脚本对新内容生效 ========
                reinitForNewContent(listClone);
                if (cfg.enableHDImage && typeof enableHDImage === 'function') {
                    enableHDImage(listClone); // ← 新增
                }
                if (cfg.enableLinkBlank && typeof runLinkBlank === 'function') {
                    runLinkBlank(listClone); // ← 新增
                }
                if (cfg.extendQuote && typeof extendQuote === 'function') {
                  extendQuote(listClone); // ← 新增
                }
                // 防剧透渲染
                if (typeof renderSpoiler === 'function') {
                  renderSpoiler($(listClone));
                }
                try { if (cfg.enableQuotePreview && typeof enableQuotePreview === 'function') enableQuotePreview(); } catch (e) {}
                if (typeof initContent === 'function') {
                  initContent();   // 重新绑定引用悬浮预览
                }
                initExtendedContent(listClone); // 扩展引用
                autoHideRefView(listClone); // 拓展引用悬浮
                // ============================================
  
  
                // 更新底部分页条
                const allPaginations = document.querySelectorAll('ul.uk-pagination.uk-pagination-left.h-pagination');
                if (allPaginations.length > 0) {
                  const lastPagination = allPaginations[allPaginations.length - 1];
                  if (lastPagination && lastPagination !== pagClone) {
                    lastPagination.replaceWith(pagClone.cloneNode(true));
                  }
                }
              } else {
                lastList.insertAdjacentElement('afterend', listClone);
                // ======== 新增:让其他脚本对新内容生效 ========
                reinitForNewContent(listClone);
                try { if (cfg.enableQuotePreview && typeof enableQuotePreview === 'function') enableQuotePreview(); } catch (e) {}
                if (typeof initContent === 'function') {
                  initContent();   // 重新绑定引用悬浮预览
                }
                initExtendedContent(listClone); // 扩展引用
                autoHideRefView(listClone); // 拓展引用悬浮
                // ============================================
  
              }
            }
  
        loadedPages.add(nextPageNum);
        lastLoadedPage = nextPageNum;
        try { history.pushState(null, '', nextUrl); } catch (e) {}
  
        const hasNextLink = pagination && Array.from(pagination.querySelectorAll('a')).some(a => /下一页|下页|Next|›|»|→/i.test(a.textContent));
        if (!hasNextLink) done = true;
  
      } catch (e) {
        console.warn('board paging loadNext error:', e);
        done = true;
      } finally {
        loading = false;
      }
    }
  
        function initObserver() {
          ensureSentinelPlaced();
          if (!sentinel) return;
          let observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
              if (entry.isIntersecting && !loading && !done) {
                if (hasUserInteracted && lastUserScrollDir > 0) {
                  const domLast = getDomLastPageNum();
                  if (domLast && lastLoadedPage >= domLast) {
                    done = true;
                    return;
                  }
                  loadNext();
                }
              }
            });
          }, { root: null, rootMargin: '0px', threshold: 0.05 });
          observer.observe(sentinel);
        }
  
        function initManualButton() {
          const btn = document.createElement('div');
          btn.className = 'xdex-placeholder';
          btn.textContent = '加载下一页';
          btn.style.cssText = `
            padding: 6px 10px;
            background: rgb(250, 250, 250);
            color: rgb(136, 136, 136);
            border: 1px dashed rgb(187, 187, 187);
            margin: 10px auto;
            cursor: pointer;
            width: 100%;
            box-sizing: border-box;
            text-align: center;
          `;
          btn.addEventListener('click', () => {
            const domLast = getDomLastPageNum();
            if (domLast && lastLoadedPage >= domLast) {
              done = true;
              return;
            }
            loadNext();
          });
  
          ensureSentinelPlaced();
          if (sentinel && sentinel.parentNode) {
            sentinel.parentNode.insertBefore(btn, sentinel);
          }
        }
  
        function initObserverBoard() {
          ensureSentinelPlacedBoard();
          if (!sentinel) return;
          let observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
              if (entry.isIntersecting && !loading && !done) {
                if (hasUserInteracted && lastUserScrollDir > 0) {
                  loadNextBoard();
                }
              }
            });
          }, { root: null, rootMargin: '0px', threshold: 0.05 });
          observer.observe(sentinel);
        }
  
        function initManualButtonBoard() {
          const btn = document.createElement('div');
          btn.className = 'xdex-placeholder';
          btn.textContent = '加载下一页';
          btn.style.cssText = `
            padding: 6px 10px;
            background: rgb(250, 250, 250);
            color: rgb(136, 136, 136);
            border: 1px dashed rgb(187, 187, 187);
            margin: 10px auto;
            cursor: pointer;
            width: 100%;
            box-sizing: border-box;
            text-align: center;
          `;
          btn.addEventListener('click', () => loadNextBoard());
          ensureSentinelPlacedBoard();
          if (sentinel && sentinel.parentNode) {
            sentinel.parentNode.insertBefore(btn, sentinel);
          }
        }
        if (isThreadPage) {
          if (cfg.enableAutoSeamlessPaging) {
            ensureSentinelPlaced();
            initObserver();
          } else {
            initManualButton();
          }
        } else if (isBoardPage) {
          if (cfg.enableAutoSeamlessPaging) {
            ensureSentinelPlacedBoard();
            initObserverBoard();
          } else {
            initManualButtonBoard();
          }
        }
      } catch (err) {
        console.error('initSeamlessPaging failed', err);
      }
    }
  
    /* --------------------------------------------------
     * 7. 移植‘X岛-揭示板的增强型体验’功能:启用高清图片链接+图片控件/串在新标签页打开
     * -------------------------------------------------- */
    function enableHDImage(root = document) {
      // 记录每张图片的点击次数
      const clickCountMap = new WeakMap();
      let lastClickedAnchor = null;
  
      // 1. 把所有 thumb 链接替换成 image
      root.querySelectorAll('img').forEach(img => {
        if (img.src.includes('/thumb/')) {
          img.src = img.src.replace('/thumb/', '/image/');
        }
      });
      root.querySelectorAll('a').forEach(a => {
        if (a.href.includes('/thumb/')) {
          a.href = a.href.replace('/thumb/', '/image/');
        }
      });
  
      // 2. 拦截点击事件,按奇偶切换 h-active
      root.querySelectorAll('.h-threads-img-a').forEach(anchor => {
        anchor.addEventListener('click', function (e) {
          e.preventDefault();
  
          // 如果点击的是另一张图片,重置它的计数
          if (lastClickedAnchor !== this) {
            lastClickedAnchor = this;
            clickCountMap.set(this, 0);
          }
  
          // 更新点击次数
          let count = (clickCountMap.get(this) || 0) + 1;
          clickCountMap.set(this, count);
  
          // 确保图片已替换为高清
          const img = this.querySelector('img.h-threads-img');
          if (img && img.src.includes('/thumb/')) {
            img.src = img.src.replace('/thumb/', '/image/');
          }
  
          // 切换 h-active
          const box = this.closest('.h-threads-img-box');
          if (box) {
            if (count % 2 === 1) {
              box.classList.add('h-active'); // 奇数次 → 添加
            } else {
              box.classList.remove('h-active'); // 偶数次 → 移除
            }
          }
        });
      });
    }
  
    // 页面加载完成后调用
    window.addEventListener('load', () => enableHDImage());
  
    function runLinkBlank(root = document) {
      root.querySelectorAll('#h-content .h-threads-list a').forEach(a => {
          // ===== 新增:排除分页导航内的链接 =====
          if (a.closest('ul.uk-pagination.uk-pagination-left.h-pagination')) return;
          // =====================================
          a.setAttribute('target', '_blank');
      });
    }
  
    /* --------------------------------------------------
     * 8. 回复-发串不跳转中间页/引用优化/创建拓展栏+reply按钮呼出回复悬浮窗
     * -------------------------------------------------- */
    function enableQuotePreview() {
      const cache = Object.create(null);
      // 注入样式(只注入一次)
      if (!document.getElementById('qp-styles')) {
        const style = document.createElement('style');
        style.id = 'qp-styles';
        style.textContent = `
          .qp-overlay-quote {
            position: fixed; inset: 0;
            z-index: 10500; /* 降低层级 */
            background: rgba(0,0,0,.45);
            display: none;
          }
          .qp-stack {
            position: fixed;
            top: 55%; /* 下移一点 */
            left: 50%;
            transform: translate(-50%, -50%);
            width: min(90vw, 820px);
            height: 80vh;
            overflow: visible;
            box-sizing: border-box;
          }
          .qp-body .h-post-form-textarea {
            width: 75% !important;
            min-height: 75px;
            box-sizing: border-box;
            font-size: 14px;
          }
          .qp-close-all {
            position: fixed; right: 12px; bottom: 12px;
            font-size: 20px; line-height: 1;
            color: #fff; background: rgba(0,0,0,.6);
            padding: 6px 12px; border-radius: 6px; cursor: pointer; z-index: 10000;
            user-select: none;
          }
          .qp-quote {
            position: absolute;
            top: 0; left: 0;
            width: 100%; max-height: 100%;
            overflow: auto;
            background: #fff;
            border: 1px solid #ccc;
            outline: 2px solid #fff;
            border-radius: 8px;
            box-shadow: 0 8px 24px rgba(0,0,0,.24);
            padding: 18px 20px 20px;
            box-sizing: border-box;
          }
          .qp-quote * { max-width: 100%; box-sizing: border-box; }
          .qp-header {
            position: sticky; top: 0;
            display: flex; gap: 10px; justify-content: flex-end;
            padding-bottom: 6px; margin: -8px -8px 10px; padding: 8px;
            background: transparent; /* ✅ 改为透明背景 */
            z-index: 2;
          }
          .qp-level {
            font-size: 12px; color: #333; background: #eee; border-radius: 4px; padding: 2px 6px;
          }
          .qp-back {
            font-size: 12px; color: #333; background: #f0f0f0;
            border: 1px solid #ccc; border-radius: 4px; padding: 2px 6px;
            cursor: pointer;
          }
          .qp-quote.is-dragging { cursor: grabbing !important; }
  #h-ref-view { pointer-events: none !important; }
  #h-ref-view {
    z-index: 20000 !important; /* 保证原生引用框在浮窗之上 */
  }
  
        `;
        document.head.appendChild(style);
      }
  
      //✅ 保留原生悬浮预览,但让其不拦点击
      const observer = new MutationObserver(() => {
        const refView = document.getElementById('h-ref-view');
        if (refView) {
          // 不再隐藏,只在点击/移开时手动关闭
        }
      });
      observer.observe(document.body, { childList: true, subtree: true });
  
  
  
  
      const $overlay = $('<div class="qp-overlay-quote"></div>').appendTo('body');
      const $stack   = $('<div class="qp-stack"></div>').appendTo($overlay);
      const $closeAll= $('<div class="qp-close-all">❌</div>').appendTo($overlay);
  
      $closeAll.on('click', () => {
        $stack.empty();
        $overlay.fadeOut(160);
      });
  
      // 点击引用框以外的任何地方都关闭
      $overlay.off('click.qp').on('click.qp', function(e){
        if (!$overlay.is(':visible')) return;
  
        const $top = $stack.children('.qp-quote').last();
        if (!$top.length) { $overlay.fadeOut(160); return; }
  
        // 1) 如果点击发生在最上层引用框内部,忽略
        if ($(e.target).closest($top).length) return;
  
        // 2) 如果正在/刚刚拖拽,避免误触关闭
        if ($top.hasClass('is-dragging') || $('.qp-quote.is-dragging').length) return;
  
        // 3) 点击最上层框之外:仅移除最上层
        $top.remove();
        if ($stack.children('.qp-quote').length === 0) $overlay.fadeOut(160);
      });
  
  
  
      $(document).on('keydown.qp', e => {
        if (e.key !== 'Escape' || !$overlay.is(':visible')) return;
        const $last = $stack.children('.qp-quote').last();
        if ($last.length) $last.remove();
        if ($stack.children().length === 0) $overlay.fadeOut(160);
      });
  
      function fetchData(tid) {
        if (cache[tid]) return Promise.resolve(cache[tid]);
        return $.get(`/Home/Forum/ref?id=${tid}`).then(html => (cache[tid] = html));
      }
  
      function stripIds($root) {
        $root.find('[id]').removeAttr('id');
        return $root;
      }
  
      function showQuote(html) {
        const depth = $stack.children('.qp-quote').length;
  
        const $quote = $('<div class="qp-quote"></div>').css({
          top: '0px',
          left: '0px',
          zIndex: 1000 + depth
        });
  
        const $header = $('<div class="qp-header"></div>');
        const $level  = $(`<span class="qp-level">第 ${depth + 1} 层</span>`);
        const $back   = $('<button class="qp-back">返回</button>').on('click', e => {
          e.stopPropagation();
          $quote.remove();
          if ($stack.children().length === 0) $overlay.fadeOut(160);
        });
  
        $header.append($level, $back);
        $quote.append($header);
  
        const $content = stripIds($('<div></div>').html(html));
        $quote.append($content.contents());
  
        enableDragForTop($quote);
        $stack.append($quote);
        $overlay.fadeIn(160);
  
        enableHDImage($quote[0]);
        runLinkBlank($quote[0]);
        enableHDImage();
      }
  
      function enableDragForTop($quote) {
        $stack.children('.qp-quote').off('.qpdrag');
  
        $quote.css({
          top: parseInt($quote.css('top')) + 'px',
          left: parseInt($quote.css('left')) + 'px'
        });
  
        let dragging = false, dx = 0, dy = 0;
        $quote.on('mousedown.qpdrag', function(e){
          if ($(e.target).closest('a,button,input,textarea,select,label').length) return;
          dragging = true;
          $quote.addClass('is-dragging');
          const qOff = $quote.offset();
          dx = e.pageX - qOff.left;
          dy = e.pageY - qOff.top;
          e.preventDefault();
        });
  
        $(document).on('mousemove.qpdrag', function(e){
          if (!dragging) return;
          const stackOff = $stack.offset();
          const top  = e.pageY - dy - stackOff.top;
          const left = e.pageX - dx - stackOff.left;
          $quote.css({ top: Math.max(0, top) + 'px', left: Math.max(0, left) + 'px' });
        });
  
        $(document).on('mouseup.qpdrag', function(){
          if (!dragging) return;
          dragging = false;
          $quote.removeClass('is-dragging');
          $(document).off('mousemove.qpdrag mouseup.qpdrag');
        });
      }
  
      $(document).off('click.qp').on('click.qp', 'font[color="#789922"]', function(e){
        $('#h-ref-view').hide();   // 点击时关闭原生引用框
        e.preventDefault();
        e.stopPropagation();
        const tid = (this.textContent.match(/\d+/) || [])[0];
        if (!tid) return;
        fetchData(tid).then(showQuote);
      });
  $(document).on('mouseleave', 'font[color="#789922"]', function () {
      $('#h-ref-view').hide();   // 鼠标移开时关闭原生引用框
  });
  
    }

    function autoHideRefView() {
        setInterval(() => {
            const refView = document.getElementById('h-ref-view');
            if (!refView || getComputedStyle(refView).display === 'none') return;
    
            // 获取当前引用框中的引用号 ID
            const infoId = refView.querySelector('.h-threads-info-id');
            if (!infoId) return;
    
            const tidMatch = infoId.textContent.match(/\d+/);
            if (!tidMatch) return;
    
            const tid = tidMatch[0];
    
            // 查找所有引用号元素
            const quoteFonts = document.querySelectorAll("font[color='#789922']");
    
            let isHovering = false;
            quoteFonts.forEach(font => {
                const text = font.textContent;
                if (text.includes(tid) && font.matches(':hover')) {
                    isHovering = true;
                }
            });
    
            // 如果没有任何引用号处于 hover 状态 → 隐藏引用框
            if (!isHovering) {
                refView.style.display = 'none';
            }
        }, 300); // 每 300ms 检查一次
    }
    
    function bindCtrlEnter(ta) {
      if (!ta || ta.__ctrlEnterBound) return;
      const form = ta.closest('form');
      if (!form) return;
  
      ta.__ctrlEnterBound = true;
  
      // 提交锁
      form.addEventListener('submit', function () {
        if (form.__submitting) return;
        form.__submitting = true;
        setTimeout(() => { form.__submitting = false; }, 5000);
      });
  
      // Ctrl+Enter 提交
      ta.addEventListener('keydown', function (e) {
        if (e.ctrlKey && e.key === 'Enter') {
          e.preventDefault();
          e.stopPropagation();
          if (typeof e.stopImmediatePropagation === 'function') {
            e.stopImmediatePropagation();
          }
          if (form.__submitting) return;
          form.__submitting = true;
          try { form.requestSubmit(); } catch (_) { form.submit(); }
          setTimeout(() => { form.__submitting = false; }, 5000);
        }
      }, false);
    }
  
  
    function replaceRightSidebar() {
      // 移除原始工具栏
      $('#h-tool').remove();
  
      // 样式(只注入一次)
      if (!document.getElementById('qp-style')) {
          const style = document.createElement('style');
          style.id = 'qp-style';
          style.textContent = `
            .qp-overlay {
              position: fixed; inset: 0; z-index: 9000;
              background: rgba(0,0,0,.45); display: none;
            }
            .qp-stack {
              position: fixed; top: 50%; left: 50%;
              transform: translate(-50%, -50%);
              width: min(90vw, 820px); height: 80vh;
              overflow: visible; box-sizing: border-box;
            }
            .qp-quote {
              position: absolute;
              top: 0; left: 0;
              width: 100%; max-height: 100%;
              overflow: auto;
              background: #fff;
              border: 1px solid #ccc;
              outline: 2px solid #fff;
              border-radius: 8px;
              box-shadow: 0 8px 24px rgba(0,0,0,.24);
              padding: 18px 20px 20px;
              box-sizing: border-box;
            }
            .qp-body .qp-content-wrap {
              display: flex;
              flex-direction: column;
              gap: 10px;
              max-width: 760px;       /* 限制内容最大宽度 */
              margin: 0 auto;         /* 居中 */
            }
  
            .qp-body .qp-content-wrap form {
              max-width: 100%;        /* 表单不超过容器宽度 */
              box-sizing: border-box; /* 包含内边距 */
            }
  
            .qp-body .qp-content-wrap .h-preview-box {
              max-width: 100%;        /* 预览框不超过容器宽度 */
              overflow-wrap: break-word; /* 长单词/长链接换行 */
              word-break: break-word;    /* 兼容性处理 */
              white-space: normal;       /* 允许正常换行 */
            }
  
            .hld__docker { position: fixed; height: 80px; width: 30px; bottom: 180px; right: 0; transition: all ease .2s; z-index: 9998; }
            .hld__docker:hover { width: 150px; height: 300px; bottom: 75px; }
            .hld__docker-sidebar { background: #fff; position: fixed; height: 50px; width: 20px; bottom: 195px; right: 0; display: flex; justify-content: center; align-items: center; border: 1px solid #CCC; box-shadow: 0 0 1px #333; border-right: none; border-radius: 5px 0 0 5px; }
            .hld__docker-btns { position: absolute; top: 0; left: 50px; bottom: 0; right: 50px; display: flex; justify-content: center; align-items: center; flex-direction: column; }
            .hld__docker .hld__docker-btns>div { opacity: 0; flex-shrink: 0; }
            .hld__docker:hover .hld__docker-btns>div { opacity: 1; }
            .hld__docker-btns>div { background: #fff; border: 1px solid #CCC; box-shadow: 0 0 1px #444; width: 50px; height: 50px; border-radius: 50%; margin: 10px 0; cursor: pointer; display: flex; justify-content: center; align-items: center; font-size: 20px; font-weight: bold; color: #333; transition: background .2s, transform .2s; }
            .hld__docker-btns>div:hover { background: #f0f0f0; transform: scale(1.1); }
          `;
          document.head.appendChild(style);
      }
  
      // 扩展坞 DOM
      const dockerDom = $(`
          <div class="hld__docker">
              <div class="hld__docker-sidebar">
                  <svg viewBox="0 0 1024 1024" width="64" height="64">
                      <path d="M518.3 824.05c-7.88 0-15.76-2.97-21.69-9L215.25 533.65c-5.73-5.73-9-13.61-9-21.69s3.27-15.96 9-21.69l281.4-281.4c11.97-11.97 31.41-11.97 43.39 0s11.97 31.41 0 43.39L280.33 511.95l259.71 259.71c11.97 11.97 11.97 31.41 0 43.39-5.94 6.04-13.72 9-21.69 9z" fill="#888"/>
                      <path d="M787.16 772.89c-7.88 0-15.76-2.97-21.69-9L535.23 533.65c-11.97-11.97-11.97-31.41 0-43.39l230.24-230.24c11.97-11.97 31.41-11.97 43.39 0s11.97 31.41 0 43.39L600.31 511.95l208.55 208.55c11.97 11.97 11.97 31.41 0 43.39-5.94 6.04-13.72 9-21.69 9z" fill="#888"/>
                  </svg>
              </div>
              <div class="hld__docker-btns">
                  <div data-type="TOP">↑</div>
                  <div data-type="REPLY">↩</div>
                  <div data-type="BOTTOM">↓</div>
              </div>
          </div>
      `);
      $('body').append(dockerDom);
  
      // 顶部按钮
      dockerDom.find('[data-type="TOP"]').on('click', () => {
          $('html, body').animate({ scrollTop: 0 }, 500);
      });
  
      // 悬浮窗引用
      let overlay;
  
      function ensureOverlay() {
          if (!overlay) {
              overlay = document.createElement('div');
              overlay.className = 'qp-overlay';
              overlay.innerHTML = `
                  <div class="qp-stack">
                      <div class="qp-quote">
                          <div class="qp-body"></div>
                      </div>
                  </div>
              `;
              document.body.appendChild(overlay);
  
              // 点击遮罩关闭(点内容不关闭,且允许事件冒泡到 document 以触发引用弹窗)
              overlay.addEventListener('click', function (e) {
                if (e.target.closest('.qp-quote')) {
                    return; // 点击在内容区:不关闭,也不阻止冒泡
                }
                closeOverlay();
              });
  
              // ESC 关闭
              document.addEventListener('keydown', e => {
                  if (e.key === 'Escape') closeOverlay();
              });
          }
          return overlay;
      }
  
      function closeOverlay() {
          if (!overlay) return;
          overlay.style.display = 'none';
          if (overlay.__formEl && overlay.__formEl.__placeholder) {
              overlay.__formEl.__placeholder.parentNode.insertBefore(overlay.__formEl, overlay.__formEl.__placeholder);
              if (overlay.__formEl.__wasCollapsed) {
                const $form = $(overlay.__formEl);
                const hint = overlay.__formEl.action.includes('doReplyThread') ? '『回复』' : '『发串』';
                // 如果原位置已经有占位符,就直接隐藏并标记折叠
                if ($form.prev('.xdex-placeholder').length) {
                    $form.hide().data('xdex-collapsed', true);
                } else if (typeof Utils !== 'undefined' && typeof Utils.collapse === 'function') {
                    Utils.collapse($form, hint);
                }
                overlay.__formEl.__wasCollapsed = false;
            }
  
          }
          if (overlay.__previewEl && overlay.__previewEl.__placeholder) {
              overlay.__previewEl.__placeholder.parentNode.insertBefore(overlay.__previewEl, overlay.__previewEl.__placeholder);
          }
      }
  
      // REPLY 按钮
      dockerDom.find('[data-type="REPLY"]').on('click', () => {
          let formEl = document.querySelector('form[action="/Home/Forum/doReplyThread.html"]');
          let previewEl = document.querySelector('.h-preview-box');
  
          // 如果串内没找到表单,尝试板块页发串表单
          if (!formEl) {
              const postForm = document.querySelector('#h-post-form form[action="/Home/Forum/doPostThread.html"]');
              if (postForm) {
                  formEl = postForm;
                  previewEl = document.querySelector('#h-post-form .h-preview-box');
              }
          }
  
          if (!formEl) {
              toast && toast('未找到回复/发串表单');
              return;
          }
  
  
  
          const ov = ensureOverlay();
          const body = ov.querySelector('.qp-body');
          body.innerHTML = '';
  
          // 占位符
          if (!formEl.__placeholder) {
              const ph1 = document.createElement('div');
              ph1.style.display = 'none';
              formEl.parentNode.insertBefore(ph1, formEl);
              formEl.__placeholder = ph1;
          }
          if (previewEl && !previewEl.__placeholder) {
              const ph2 = document.createElement('div');
              ph2.style.display = 'none';
              previewEl.parentNode.insertBefore(ph2, previewEl);
              previewEl.__placeholder = ph2;
          }
  
          // 如果表单是折叠状态,展开它(不影响原位置的占位符)
          if ($(formEl).data('xdex-collapsed')) {
              formEl.__wasCollapsed = true; // ★ 记录原本是折叠的
              $(formEl).show().removeData('xdex-collapsed');
          }
  
          // 包装容器,防止 UI 松散
          const wrap = document.createElement('div');
          wrap.className = 'qp-content-wrap';
  
          // 表单是必需的
          wrap.appendChild(formEl);
  
          // 预览框是可选的
          if (previewEl) {
              wrap.appendChild(previewEl);
          }
  
          body.appendChild(wrap);
          // 浮窗内立即处理扩展引用,保证可点击引用弹窗
          if (typeof extendQuote === 'function') {
            extendQuote(previewEl || wrap);
          }
  
  
  
          // 保存引用
          ov.__formEl = formEl;
          ov.__previewEl = previewEl;
  
          // 显示 overlay
          ov.style.display = 'block';
  
          // 聚焦 textarea
          const ta = ov.querySelector('textarea[name="content"]');
          if (ta) {
              ta.focus();
                      const val = ta.value;
          ta.value = '';
          ta.value = val;
          }
        // Ctrl+Enter 发送并在成功后关闭浮窗
  
          if (ta) {
            const form = ta.closest('form');
            if (form && !ta.__qpCtrlEnterBound) {
              ta.__qpCtrlEnterBound = true;
  
              // 保留:提交成功后关闭浮窗
              document.addEventListener('replySuccess', () => {
                form.__submitting = false;
                closeOverlay();
              }, { once: true });
  
              // 新增:调用抽取的绑定函数
              bindCtrlEnter(ta);
            }
          }
  
  
  
  
  
      });
  
      // 底部按钮:平滑滚动到最底部,不污染 URL
      dockerDom.find('[data-type="BOTTOM"]').on('click', () => {
          $('html, body').animate({ scrollTop: $(document).height() - $(window).height() }, 500);
      });
      }
  
    function interceptReplyForm() {
       // ★ 子函数:清空草稿
      function 清空编辑(key) {
          if (!key) key = window.location.pathname;
          if (typeof GM_deleteValue === 'function') {
              GM_deleteValue(key);
          }
      }
  
      document.addEventListener('submit', function (e) {
        const form = e.target;
        const isReply = form.matches('form[action="/Home/Forum/doReplyThread.html"]');
        const isPost = form.matches('form[action="/Home/Forum/doPostThread.html"]');
        if (!isReply && !isPost) return;
  
        e.preventDefault();
  
        const formData = new FormData(form);
        // 文字内容
        let content = (formData.get('content') || '').toString().trim();
  
        if (!content) {
          // 检查是否选择了图片(支持 name="image")
          const fileInput = form.querySelector('input[type="file"][name="image"]');
          const hasImage = !!(fileInput && fileInput.files && fileInput.files.length > 0);
  
          if (hasImage) {
            // 无文字但有图片 → 自动补“分享图片”,并同步到 FormData 与 DOM
            //修改以自定义。
            //content = '分享图片';//默认占位文字
            //content = '‎';//空白占位符
            content = ' ';//空白占位符
            formData.set('content', content);
            const textarea = form.querySelector('textarea[name="content"]');
            if (textarea) textarea.value = content;
          } else {
            toast(isReply ? '回复内容不能为空' : '发串内容不能为空');
            return; // 只有没文字且没图片时才中断
          }
        }
  
  
        fetch(form.action, {
          method: form.method,
          body: formData,
          credentials: 'include'
        })
        .then(res => res.text())
        .then(html => {
          // 解析返回的中间页 HTML
          const doc = new DOMParser().parseFromString(html, 'text/html');
          const successMsg = doc.querySelector('p.success');
          const errorMsg   = doc.querySelector('p.error');
  
          if (successMsg) {
            toast(successMsg.textContent.trim() || (isReply ? '回复成功' : '发串成功'));
  
            // 清空输入框
            const textarea = form.querySelector('textarea[name="content"]');
            if (textarea) textarea.value = '';
            // 清空图片选择
            const fileInput = form.querySelector('input[type="file"][name="image"]');
            if (fileInput) fileInput.value = '';
            // ★ 清空编辑
            // 先立即删除(兜底清理,避免监听未注册导致残留)
            deleteDraftSafe(getDraftKey());
  
            // 再广播事件给增强模块/其他联动
            document.dispatchEvent(new CustomEvent('replySuccess', {
              detail: { key: getDraftKey() }
            }));
  
            // 重置预览框
            const previewBox = document.querySelector('.h-preview-box');
            if (previewBox) {
                previewBox.innerHTML = `
            <div class="h-threads-item">
              <div class="h-threads-item-replies">
                <div class="h-threads-item-reply">
                  <div class="h-threads-item-reply-main">
                    <div class="h-threads-img-box">
                      <div class="h-threads-img-tool uk-animation-slide-top">
                        <span class="h-threads-img-tool-btn h-threads-img-tool-small uk-button-link"><i class="uk-icon-minus"></i>收起</span>
                        <a href=":javascript:;" class="h-threads-img-tool-btn uk-button-link"><i class="uk-icon-search-plus"></i>查看大图</a>
                        <span class="h-threads-img-tool-btn h-threads-img-tool-left uk-button-link"><i class="uk-icon-reply"></i>向左旋转</span>
                        <span class="h-threads-img-tool-btn h-threads-img-tool-right uk-button-link"><i class="uk-icon-share"></i>向右旋转</span>
                      </div>
                      <a class="h-threads-img-a"><img src="" align="left" border="0" hspace="20" class="h-threads-img"></a>
                    </div>
                    <div class="h-threads-info">
                      <span class="h-threads-info-title">无标题</span>
                      <span class="h-threads-info-email">无名氏</span>
                      <span class="h-threads-info-createdat">2077-01-01(四)00:00:01</span>
                      <span class="h-threads-info-uid">ID:cookies</span>
                      <span class="uk-text-primary uk-text-small">(PO主)</span>
                      <span class="h-threads-info-report-btn">
                        [<a href="/f/值班室" target="_blank">举报</a>]
                      </span>
                      <a href=":javascript:;" class="h-threads-info-id" target="_blank">No.99999999</a>
                    </div>
                    <div class="h-threads-content">
                      无内文
                    </div>
                  </div>
                </div>
              </div>
            </div>`;
            }
  
            // ★ 刷新饼干(不显示 toast)
            if (typeof refreshCookies === 'function') {
              refreshCookies(null, false);
            }
            if (isReply) {
              refreshRepliesWithSeamlessPaging(() => {
                reapplyPageEnhancements();
              });
            } else {
              location.reload();
            }
          } else if (errorMsg) {
            toast(errorMsg.textContent.trim() || '提交失败');
            // 不刷新
          } else {
            toast('未知状态,可能提交失败');
          }
        })
        .catch(() => toast('提交失败,请重试'));
      }, true);
  
      // ————— helpers —————
  
      function getRealThreadsList(root = document) {
        const lists = Array.from(root.querySelectorAll('.h-threads-list'));
        return lists.find(el => !el.closest('.h-preview-box')) || null;
      }
  
      function getCurrentPage() {
        const sp = new URL(location.href, location.origin).searchParams;
        return parseInt(sp.get('page') || '1', 10);
      }
  
      function getMaxPageFromPagination() {
        const paginations = Array.from(document.querySelectorAll('.uk-pagination.uk-pagination-left.h-pagination'));
        if (!paginations.length) return null;
        const last = paginations[paginations.length - 1];
        let max = 1;
        last.querySelectorAll('a[href*="page="]').forEach(a => {
          const m = a.href.match(/[?&]page=(\d+)/);
          if (m) max = Math.max(max, parseInt(m[1], 10));
        });
        last.querySelectorAll('li, span').forEach(el => {
          const nums = (el.textContent || '').match(/\d+/g);
          if (nums) nums.forEach(n => max = Math.max(max, parseInt(n, 10)));
        });
        return max || null;
      }
  
      function getMaxClonedPageInDOM() {
        const nodes = document.querySelectorAll('.h-threads-item-replies[data-cloned-page]');
        let max = 0;
        nodes.forEach(el => {
          const n = parseInt(el.getAttribute('data-cloned-page'), 10);
          if (!isNaN(n)) max = Math.max(max, n);
        });
        return max;
      }
  
      function refreshRepliesWithSeamlessPaging(done) {
        const currentPage = getCurrentPage();
        const maxPage = getMaxPageFromPagination();
        const maxCloned = getMaxClonedPageInDOM();
        let targetPage = null;
  
        if ((maxPage && maxCloned === maxPage && maxCloned > 0) || (!maxPage && maxCloned > 0)) {
          targetPage = maxCloned;
        } else if (maxPage && currentPage === maxPage && maxCloned === 0) {
          targetPage = null;
        } else {
          if (typeof done === 'function') done();
          return;
        }
  
        const list = getRealThreadsList(document);
        if (!list) {
          toast('未找到真实列表,无法刷新回复区');
          if (typeof done === 'function') done();
          return;
        }
  
        const targetReplies = targetPage
          ? list.querySelector(`.h-threads-item-replies[data-cloned-page="${targetPage}"]`)
          : list.querySelector('.h-threads-item-replies:not([data-cloned-page])');
  
        if (!targetReplies) {
          toast('未找到目标回复区');
          if (typeof done === 'function') done();
          return;
        }
  
        let fetchUrl;
        if (targetPage) {
          const url = new URL(location.href, location.origin);
          url.searchParams.set('page', String(targetPage));
          fetchUrl = url.toString();
        } else {
          fetchUrl = location.href;
        }
  
        fetch(fetchUrl, { credentials: 'include' })
          .then(res => res.text())
          .then(html => {
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const newList = getRealThreadsList(doc);
            if (!newList) {
              toast('未找到最新列表');
              if (typeof done === 'function') done();
              return;
            }
            const newReplies = newList.querySelector('.h-threads-item-replies');
            if (!newReplies) {
              toast('未找到最新回复区');
              if (typeof done === 'function') done();
              return;
            }
            targetReplies.innerHTML = newReplies.innerHTML;
  
            // 新增:替换后立即重新应用标记/屏蔽
            try {
                const cfg2 = safeGetConfig && safeGetConfig();
                if (cfg2 && typeof applyFilters === 'function') applyFilters(cfg2);
            } catch (e) {}
  
  
            if (typeof done === 'function') done();
          })
          .catch(() => {
            toast('刷新回复区失败');
            if (typeof done === 'function') done();
          });
      }
  
      function reapplyPageEnhancements() {
        const cfg = safeGetConfig();
        if (!cfg) return;
        if (cfg.hideEmptyTitleEmail && typeof hideEmptyTitleAndEmail === 'function') {
          try {
            hideEmptyTitleAndEmail();
            if (window.Utils && typeof Utils.collapse === 'function') {
              Utils.collapse($ && $('form[action="/Home/Forum/doReplyThread.html"]'), '『回复』');
              Utils.collapse($ && $('form[action="/Home/Forum/doPostThread.html"]'), '『发串』');
              Utils.collapse($ && $('.h-forum-header'), '『版规』');
            }
          } catch (e) {}
        }
        if (cfg.updateReplyNumbers && typeof updateReplyNumbers === 'function') {
          try { updateReplyNumbers(); } catch (e) {}
        }
        if (cfg.enableHDImage && typeof enableHDImage === 'function') {
          try { enableHDImage(); } catch (e) {}
        }
        if (cfg.extendQuote && typeof extendQuote === 'function') {
          try { extendQuote(); } catch (e) {}
        }
        try { if (typeof applyFilters === 'function') applyFilters(cfg); } catch (e) {}
      }
  
      function safeGetConfig() {
        try {
          if (typeof SettingPanel !== 'undefined' && typeof GM_getValue === 'function') {
            const defaults = SettingPanel.defaults || {};
            const saved = GM_getValue(SettingPanel.key, {}) || {};
            return Object.assign({}, defaults, saved);
          }
        } catch (e) {}
        return null;
      }
    }
  
    function extendQuote(root = document) {
        const ROOT_SELECTOR = '.h-threads-content';
        const QUOTE_COLOR = '#789922';
  
        // 在容器内遍历纯文本节点,避免破坏现有标签
        root.querySelectorAll(ROOT_SELECTOR).forEach(root => {
            const walker = document.createTreeWalker(
                root,
                NodeFilter.SHOW_TEXT,
                {
                    acceptNode(node) {
                        // 没数字直接跳过
                        if (!node.nodeValue || !/\d/.test(node.nodeValue)) {
                            return NodeFilter.FILTER_REJECT;
                        }
                        // 已经在 quote 的 <font color="#789922"> 内,跳过
                        const p = node.parentElement;
                        if (p && p.tagName.toLowerCase() === 'font' && (p.getAttribute('color') || '').toLowerCase() === QUOTE_COLOR) {
                            return NodeFilter.FILTER_REJECT;
                        }
                        return NodeFilter.FILTER_ACCEPT;
                    }
                }
            );
  
            const textNodes = [];
            let n;
            while ((n = walker.nextNode())) textNodes.push(n);
  
            textNodes.forEach(processTextNode);
        });
  
        function processTextNode(textNode) {
            const text = textNode.nodeValue;
            // 两类模式:No.12345678 与 单独 8 位数字
            const patterns = [
                { name: 'no',  regex: /\bNo\.(\d{8})\b/g },
                { name: 'num', regex: /(?<!\d)(\d{8})(?!\d)/g }
            ];
  
            const frag = document.createDocumentFragment();
            let cursor = 0;
            let changed = false;
  
            while (true) {
                const next = findNextMatch(text, cursor, patterns);
                if (!next) break;
  
                const { start, end } = next;
  
                // 若匹配前紧挨着 ">>",说明是标准引用的一部分,跳过这次,继续后移
                if (start >= 2 && text[start - 2] === '>' && text[start - 1] === '>') {
                    // 只推进一位,继续找后面的匹配,避免卡住
                    cursor = start + 1;
                    continue;
                }
  
                // 追加匹配前的原始文本
                if (start > cursor) {
                    frag.appendChild(document.createTextNode(text.slice(cursor, start)));
                }
  
                // 用与标准引用一致的 <font color="#789922"> 包裹,但不添加 ">>"
                const font = document.createElement('font');
                font.setAttribute('color', QUOTE_COLOR);
                // 直接保留原始匹配文本(可能是 "No.12345678" 或 "12345678")
                font.textContent = text.slice(start, end);
                frag.appendChild(font);
  
                cursor = end;
                changed = true;
            }
  
            if (!changed) return;
  
            // 末尾剩余文本
            if (cursor < text.length) {
                frag.appendChild(document.createTextNode(text.slice(cursor)));
            }
  
            textNode.replaceWith(frag);
        }
  
        // 在多正则间找到下一处最早的匹配
        function findNextMatch(text, fromIndex, patterns) {
            let best = null;
            for (const p of patterns) {
                p.regex.lastIndex = fromIndex;
                const m = p.regex.exec(text);
                if (m) {
                    const start = m.index;
                    const end = start + m[0].length;
                    if (!best || start < best.start) {
                        best = { start, end };
                    }
                }
            }
            return best;
        }
            // === 自动定时检测 ===
      if (root === document && !extendQuote.__interval) {
          try {
              const cfg = typeof SettingPanel !== 'undefined'
                  ? Object.assign({}, SettingPanel.defaults, GM_getValue(SettingPanel.key, {}))
                  : { extendQuote: true };
              if (cfg.extendQuote) {
                  extendQuote.__interval = setInterval(() => {
                      try {
                          extendQuote(); // 再次全局扫描
                      } catch (e) {
                          console.warn('extendQuote interval error', e);
                      }
                  }, 2000); // 每 2 秒检测一次
              }
          } catch (e) {
              console.warn('extendQuote auto-scan init error', e);
          }
      }
  
    }
  
    function initExtendedContent(root) {
      // root 可以是 document 或者某个新插入的 DOM 节点
      $(root).find("font[color='#789922']")
          .filter(function () {
              // 匹配扩展格式:No.12345678 或 纯8位数字
              return /(No\.\d{8}|\d{8})/.test($(this).text());
          })
          .off('mouseenter.ext') // 避免重复绑定
          .on('mouseenter.ext', function () {
              var self = this;
              var tid = /\d+/.exec($(this).text())[0];
              $.get('/Home/Forum/ref?id=' + tid)
                  .done(function (data) {
                      if (data.indexOf('<!DOCTYPE html><html><head>') >= 0) {
                          return false;
                      }
                      $("#h-ref-view").off().html(data).css({
                          top: $(self).offset().top,
                          left: $(self).offset().left
                      }).fadeIn(100).one('mouseleave', function () {
                          $(this).fadeOut(100);
                      });
                  });
          });
    }
  
  
    function highlightPO() {
      const poTextColor  = '#00FFCC'; // Po 本体颜色
      const iconWidthEm  = 3.0;       // 所有图标统一宽度
  
  
      // 统一设置所有回复图标的宽度
      document.querySelectorAll('.h-threads-item-reply-icon').forEach(icon => {
        icon.style.display = 'inline-block';
        icon.style.width = iconWidthEm + 'em';
        icon.style.textAlign = 'center';
        icon.style.position = 'relative';
        icon.style.fontWeight = 'normal'; // 所有图标数字不加粗
      });
  
      // 替换 PO 回复的数字为 Po,并加角标
      document.querySelectorAll('.h-threads-item-reply').forEach(reply => {
        const main = reply.querySelector('.h-threads-item-reply-main');
        const icon = reply.querySelector('.h-threads-item-reply-icon');
        if (!main || !icon) return;
  
        const isPO = !!main.querySelector('span.uk-text-primary.uk-text-small');
        if (!isPO) return;
  
        // 获取原 HTML(包含『n』)
        let html = icon.innerHTML;
        const m = html.match(/『(\d+)』/);
        if (m) {
          const originalNumber = m[1]; // 原数字
          // 加粗 + 染色的 Po
          const poHTML = `『<span style="color:${poTextColor}; font-weight:bold">Po</span>』`;
  
          // 替换数字为 Po
          html = html.replace(m[0], poHTML);
          icon.innerHTML = html;
  
          // 创建角标
          const badge = document.createElement('span');
          badge.className = 'po-n-badge';
          badge.textContent = originalNumber;
          Object.assign(badge.style, {
            position: 'absolute',
            top: '-0.55em',
            right: '-0.6em',
            fontSize: '10px',
            lineHeight: '1',
            fontWeight: '600',
            color: 'initial', // 保持默认颜色
            background: 'transparent',
            pointerEvents: 'none',
          });
  
          icon.appendChild(badge);
        }
      });
    }
  
    // 初次执行
    highlightPO();
    // 监听 DOM 变化
    const observer = new MutationObserver(() => {
      highlightPO();
    });
    observer.observe(document.body, { childList: true, subtree: true });
  
    function enhancePostFormLayout() {
       const form = document.querySelector('form[action*="doReplyThread"], form[action*="doPostThread"]');
  
        if (!form) return;
  
        // 定位“标题”行与“颜文字”行
        const allRows = Array.from(form.querySelectorAll('.h-post-form-grid'));
        let titleRow = null, emoticonRow = null;
  
        for (const row of allRows) {
          const titleText = row.querySelector('.h-post-form-title')?.textContent?.trim() || '';
          if (titleText === '标题') titleRow = row;
          if (row.querySelector('.kaomoji-trigger') || row.querySelector('#h-emot-select')) {
            emoticonRow = row;
          }
        }
  
        // 1) 先把送出按钮移到“颜文字”行,并让整行用 flex 不换行,按钮推到行最右
        if (titleRow && emoticonRow) {
          const sendBtnCell = titleRow.querySelector('.h-post-form-option');
          const sendBtn = sendBtnCell?.querySelector('input[type="submit"]');
          if (sendBtn) {
            // 让“颜文字”整行用 flex 布局,禁止换行,垂直居中
            Object.assign(emoticonRow.style, {
              display: 'flex',
              flexWrap: 'nowrap',
              alignItems: 'center',
              width: '100%'
            });
  
            // 创建一个右侧容器,使用 margin-left:auto 将其推到最右
            const btnWrapper = document.createElement('div');
            Object.assign(btnWrapper.style, {
              marginLeft: 'auto',
              display: 'flex',
              alignItems: 'center'
            });
            btnWrapper.appendChild(sendBtn);
  
            // 将按钮容器添加到“颜文字”行
            emoticonRow.appendChild(btnWrapper);
          }
        }
  
        // 2) 折叠「回应模式 / 名 称 / E-mail / 标题」四行为一个折叠面板
        // 重新抓一次,避免移动节点导致 NodeList 顺序问题
        const freshRows = Array.from(form.querySelectorAll('.h-post-form-grid'));
        const targets = new Set(['回应模式', '名 称', 'E-mail', '标题']);
        const rowsToCollapse = [];
  
        for (const row of freshRows) {
          const label = row.querySelector('.h-post-form-title')?.textContent?.trim() || '';
          if (targets.has(label)) rowsToCollapse.push(row);
        }
  
        if (rowsToCollapse.length) {
          const wrapper = document.createElement('div');
          wrapper.className = 'collapse-wrapper';
          wrapper.style.width = '100%';
  
          // 将折叠目标打包进容器
          rowsToCollapse[0].before(wrapper);
          rowsToCollapse.forEach(r => wrapper.appendChild(r));
  
          // 使用现有 collapse 能力(依赖 jQuery)
          if (typeof Utils !== 'undefined' && typeof Utils.collapse === 'function' && typeof $ === 'function') {
            Utils.collapse($(wrapper), '发帖选项');
          }
        }
      }
  
  
    /* --------------------------------------------------
     * 9. 颜文字增强-光标处插入/选择框优化/额外颜文字拓展
     * -------------------------------------------------- */
    function kaomojiEnhancer() {
        // 初始化所有功能
        initInsertAtCaret();      // 功能 1:颜文字插入光标处
        optimizeSelectorStyle();  // 功能 2:选择框样式优化
        extendKaomojiSet();       // 功能 3:颜文字样式拓展
  
        /**
         * 功能 1:选择颜文字后插入到光标位置
         */
        function initInsertAtCaret() {
            const SELECTOR = '#h-emot-select';
            const TA_SELECTOR = 'textarea.h-post-form-textarea[name="content"]';
  
            document.querySelectorAll(SELECTOR).forEach(select => {
                if (select.dataset.kaoBound === '1') return;
                select.dataset.kaoBound = '1';
  
                const form = select.closest('form');
                const textarea = form ? form.querySelector(TA_SELECTOR) : null;
                if (!textarea) return;
  
                let lastStart = 0;
                let lastEnd = 0;
  
                // 记录光标位置
                const remember = () => {
                    lastStart = textarea.selectionStart ?? lastStart;
                    lastEnd = textarea.selectionEnd ?? lastEnd;
                };
                ['keyup', 'mouseup', 'select', 'input', 'focus', 'blur'].forEach(ev =>
                    textarea.addEventListener(ev, remember, true)
                );
                ['mousedown', 'pointerdown', 'touchstart', 'click'].forEach(ev =>
                    select.addEventListener(ev, remember, true)
                );
  
                // 拦截 change 事件,阻止原生插入逻辑
                select.addEventListener('change', e => {
                    const val = select.value;
                    if (!val) return;
  
                    e.stopImmediatePropagation(); // 阻止原生事件
                    e.preventDefault();
  
                    insertAtCaret(textarea, val, lastStart, lastEnd);
  
                    select.value = ''; // 重置选择器
                    textarea.focus();
                }, true); // 捕获阶段
            });
  
            function insertAtCaret(textarea, text, selStart, selEnd) {
                const prevScrollTop = textarea.scrollTop;
  
                let start = Number.isInteger(selStart) ? selStart : textarea.selectionStart;
                let end = Number.isInteger(selEnd) ? selEnd : textarea.selectionEnd;
                if (!Number.isInteger(start) || !Number.isInteger(end)) {
                    start = end = textarea.value.length;
                }
  
                const before = textarea.value.slice(0, start);
                const after = textarea.value.slice(end);
  
                textarea.value = before + text + after;
  
                const newPos = start + text.length;
                textarea.setSelectionRange(newPos, newPos);
  
                textarea.dispatchEvent(new Event('input', { bubbles: true }));
                textarea.scrollTop = prevScrollTop;
  
                // 更新记忆位置
                lastStart = lastEnd = newPos;
            }
        }
  
        /**
         * 功能 2:颜文字选择框样式优化(占位)
         */
        function optimizeSelectorStyle() {
          const SELECTOR = '#h-emot-select';
          const GAP = 4;               // 单元格间距(缩小)
          const CHAR_W = 14;           // 每个字宽度(px)
          const CHAR_H = 16;           // 每个字高度(px)
          const PAD = 6;               // 浮窗内边距(缩小)
          const ITEM_W = CHAR_W * 6 + 6; // 大约半个长颜文字宽度
          const ITEM_H = CHAR_H * 2 + 4; // 不超过两行字高
  
          const selects = document.querySelectorAll(SELECTOR);
          if (!selects.length) return;
  
          // 注入样式(只注入一次)
          if (!document.getElementById('kaomoji-style')) {
              const style = document.createElement('style');
              style.id = 'kaomoji-style';
              style.textContent = `
                  .kaomoji-trigger {
                      display: inline-flex;
                      align-items: center;
                      height: 26px;
                      line-height: 26px;
                      padding: 0 8px;
                      border: 1px solid #ccc;
                      border-radius: 4px;
                      background: #fff;
                      cursor: pointer;
                      white-space: nowrap;
                      user-select: none;
                  }
                  .kaomoji-panel {
                      position: fixed;
                      z-index: 2147483647;
                      display: none;
                      grid-template-columns: repeat(auto-fit, minmax(${ITEM_W}px, 1fr));
                      gap: ${GAP}px;
                      padding: ${PAD}px;
                      border: 1px solid #ccc;
                      border-radius: 6px;
                      background: #fff;
                      box-shadow: 0 6px 16px rgba(0,0,0,0.12);
                      box-sizing: border-box;
                      overflow-y: auto;
  
                      /* 关键调整 */
                      width: auto; /* 让宽度随内容变化 */
                      max-width: calc(100vw - ${PAD * 2}px); /* 不超过视口宽度 */
                      min-width: ${ITEM_W}px; /* 至少一列的宽度 */
                      max-height: calc(${ITEM_H}px * 5 + ${GAP}px * 4 + ${PAD}px * 2);
                  }
                  .kaomoji-item {
                      width: ${ITEM_W}px;
                      height: ${ITEM_H}px;
                      display: flex;
                      align-items: center;
                      justify-content: center;
                      padding: 2px;
                      border-radius: 4px;
                      cursor: pointer;
                      user-select: none;
                      text-align: center;
                      font-size: 14px;
                      line-height: 1.2;
                      word-break: break-word;
                  }
                  .kaomoji-item:hover {
                      background: #f2f2f2;
                  }
              `;
              document.head.appendChild(style);
          }
  
          selects.forEach(select => {
              if (select.dataset.kaoStyled === '1') return;
              select.dataset.kaoStyled = '1';
  
              select.style.display = 'none';
  
              const trigger = document.createElement('button');
              trigger.type = 'button';
              trigger.className = 'kaomoji-trigger';
              trigger.textContent = '选择颜文字';
  
              const panel = document.createElement('div');
              panel.className = 'kaomoji-panel';
  
              const options = Array.from(select.options);
              options.forEach(opt => {
                  const item = document.createElement('div');
                  item.className = 'kaomoji-item';
                  item.textContent = opt.textContent;
                  item.dataset.value = opt.value;
                  item.addEventListener('click', () => {
                      select.value = opt.value;
                      select.dispatchEvent(new Event('change', { bubbles: true }));
                      trigger.textContent = opt.textContent || '选择颜文字';
                      hidePanel();
                  });
                  panel.appendChild(item);
              });
  
              select.parentNode.insertBefore(trigger, select.nextSibling);
              document.body.appendChild(panel);
  
              function positionPanel() {
                  const rect = trigger.getBoundingClientRect();
                  panel.style.visibility = 'hidden';
                  panel.style.display = 'grid';
                  const panelRect = panel.getBoundingClientRect();
                  const panelW = panelRect.width;
                  const panelH = panelRect.height;
  
                  let left = rect.left;
                  let top = rect.top - panelH - 6; // 向上展开
  
                  const margin = 6;
                  if (left + panelW > window.innerWidth - margin) {
                      left = window.innerWidth - margin - panelW;
                  }
                  if (left < margin) left = margin;
  
                  if (top < margin) {
                      top = rect.bottom + 6; // 空间不足则向下展开
                  }
  
                  panel.style.left = `${Math.round(left)}px`;
                  panel.style.top = `${Math.round(top)}px`;
                  panel.style.visibility = '';
              }
  
              function showPanel() {
                  positionPanel();
                  panel.style.display = 'grid';
                  bindGlobalClose();
              }
  
              function hidePanel() {
                  panel.style.display = 'none';
                  unbindGlobalClose();
              }
  
              trigger.addEventListener('click', (e) => {
                  e.stopPropagation();
                  if (panel.style.display === 'none' || panel.style.display === '') {
                      showPanel();
                  } else {
                      hidePanel();
                  }
              });
  
              let outsideHandler, escHandler;
              function bindGlobalClose() {
                  outsideHandler = (e) => {
                      // 捕获阶段执行,防止被其他脚本阻止
                      const target = e.target;
                      if (!panel.contains(target) && target !== trigger) {
                          hidePanel();
                      }
                  };
  
                  escHandler = (e) => {
                      if (e.key === 'Escape') hidePanel();
                  };
  
                  // 用捕获阶段监听 click 和 mousedown
                  document.addEventListener('click', outsideHandler, true);
                  document.addEventListener('mousedown', outsideHandler, true);
                  window.addEventListener('keydown', escHandler);
              }
              function unbindGlobalClose() {
                  document.removeEventListener('click', outsideHandler, true);
                  document.removeEventListener('mousedown', outsideHandler, true);
                  window.removeEventListener('keydown', escHandler);
              }
  
              select.addEventListener('kaomoji:updated', () => {
                // 清空并重建 panel 子项
                while (panel.firstChild) panel.removeChild(panel.firstChild);
                Array.from(select.options).forEach(opt => {
                    const item = document.createElement('div');
                    item.className = 'kaomoji-item';
                    item.textContent = opt.textContent;
                    item.dataset.value = opt.value;
                    item.addEventListener('click', () => {
                        select.value = opt.value;
                        select.dispatchEvent(new Event('change', { bubbles: true }));
                        trigger.textContent = opt.textContent || '选择颜文字';
                        panel.style.display = 'none';
                    });
                    panel.appendChild(item);
                });
            });
          });
      }
  
  
        /**
         * 功能 3:颜文字样式拓展(占位)
         */
        function extendKaomojiSet() {
            const SELECTOR = '#h-emot-select';
            const EXTRA_EMOTS = [
                " ҉ \n( ゚∀。)","( ´_ゝ`)旦","(<ゝω・) ☆","(`ε´ (つ*⊂)","=͟͟͞͞( 'ヮ' 三 'ヮ' =͟͟͞͞)","↙(`ヮ´ )↗ 开摆!",
                "(っ˘Д˘)ノ<","(ノ#)`д´)σ","₍₍(ง`ᝫ´ )ว⁾","( `ᵂ´)","( *・ω・)✄╰ひ╯","U•ェ•*U","⊂( ゚ω゚)つ",
                "( ゚∀。)7","・゚( ゚∀。) ゚。","( `д´)σ","( ゚ᯅ 。)","( ;`д´; )","m9( `д´)","( ゚π。)","ᕕ( ゚∀。)ᕗ",
                "ฅ(^ω^ฅ)","(|||^ヮ^)","(|||ˇヮˇ)","( ↺ω↺)"," `ー´) `д´) `д´)","接☆龙☆大☆成☆功",
                "ᑭ`д´)ᓀ ∑ᑭ(`ヮ´ )ᑫ","乚 (^ω^ ミэ)Э好钩我咬","乚(`ヮ´  ミэ)Э","( ゚∀。ミэ)Э三三三三 乚",
                "(ˇωˇ ミэ)Э三三三三 乚","( へ ゚∀゚)べ摔低低","(ベ ˇωˇ)べ 摔低低"
            ];
            const EXTRA_RICH = {
                "齐齐蛤尔": "(`ヮ´ )σ`∀´) ゚∀゚)σ",
                "呼伦悲尔": "( ノд`゚);´д`) ´_ゝ`)",
                "愕尔多厮": "Σ( ゚д゚)´゚Д゚) ゚д゚)))",
                "智利": "( ゚∀。)∀。)∀。)",
                "阴山山脉": "( ˇωˇ )◕∀◕。)^ω^)",
                "F5欧拉": " σ σ\nσ( ´ρ`)σ[F5]\n σ σ",
                "UK酱": "\\ ︵\nᐕ)⁾⁾",
                "白羊": "╭◜◝ ͡ ◜◝ J J\n(     `д´)  “咩!”\n╰◟д ◞ ͜ ◟д◞",
                "兔兔": " /) /)\nc( ╹^╹)",
                "neko": "     ∧,, \n    ヾ `. 、`フ\n   (,`'´ヽ、、ヅ\n  (ヽv'   `''゙つ\n  ,ゝ  ⌒`y'''´\n  ( (´^ヽこつ\n   ) )\n  (ノ",
                "给你": "(\\_/)\n(・_・)\n /  >",
                "举高高": "      _∧_∧_ \n    ((∀`/  ) \n   /⌒   / \n  /(__ノ\_ノ \n  (_ノ ||| 举高高~~\n ∧_∧ ∧_∧\n(( ・∀・ ))・∀・) )\n`\   ∧   ノ\n / |/  |\n(_ノ_)_ノL_)",
                "举糕糕": "举糕糕~\n  ☆☆☆☆☆☆☆☆\n ╭┻┻┻┻┻┻┻┻╮\n ┃╱╲╱╲╱╲╱╲┃\n╭┻━━━━━━━━┻╮\n┃╱╲╱╲╱╲╱╲╱╲┃\n┗━━━━━━━━━━┛\n   ∧_∧ ∧_∧\n  (( ・∀・ ))・∀・) )\n  `\   ∧   ノ\n   /  |/  |\n  (_ノ_)_ノL_)",
                "Happy肥肥Day": ".       .★ * ★.. \n  .*★ *. *..*   ★ \n  ★      ★ \n  ‘*. *'  ʜᴀᴘᴘʏ 肥肥 ᴅᴀʏ \n   ‘★.   ★’ \n     *..★\n┊┊┊┊☆☆☆☆☆☆☆☆┊┊┊┊\n┊┊┊╭┻┻┻┻┻┻┻┻╮┊┊┊\n┊┊┊┃╱╲╱╲╱╲╱╲┃┊┊┊\n┊┊╭┻━━━━━━━━┻╮┊┊\n┊┊┃╱╲╱╲╱╲╱╲╱╲┃┊┊\n╱▔┗━━━━━━━━━━┛▔╲",
                "举高高2": "    _∧_∧_\n    ((∀`/  )\n    /⌒   /\n  /(__ノ\_ノ(((ヽ\n (_ノ    ̄Y\\n| (\ (\\  /)  | )举高高!\nヽ ヽ` ( ゚∀゚ ) _ノ /\n \ | ⌒Y⌒ / /\n  |ヽ · | · ノ //\n  \トー仝ーイ\n   | ミ土彡/\n   ). \\ °  /\n   (  \\. y  \\",
                "大嘘": "吁~~~~  rnm,退钱!\n    /   /\n( ゚ 3゚) `ー´) `д´) `д´)",
                "催更喵gkd": "      ___\n     />  フ\n     |  _  _ l 我是一只催更的\n     /` ミ_xノ 喵喵酱\n     /      | gkdgkd\n    /  ヽ   ノ\n    │  | | |\n / ̄|   | | |\n | ( ̄ヽ__ヽ_)__)\n \二つ",
                "巴拉巴拉": " ∧_∧\n(。・ω・。)つ━☆・*。\n⊂   ノ    ・゜+.\n しーJ   °。+ *´¨)\n      .· ´¸.·*´¨) ¸.·*¨)\n         (¸.·´ (¸.·’*",
                "碣石": "  _ _\n  ( ゚_゚)\n/ (⁰  )╲/",
                "冰封王座": "(ノ゚∀゚)ノ👑\n   ( ゚∀。)\n\n   👑\n(//゚ω゚)//\n    \n 👑\n(Ⅱ゚ω゚)Ⅱ\n\n  👑\nᕕ( ᐛ )ᕗ",
                "冰封王座2": "(ノ゚∀゚)ノ🍟\n   ( ゚∀。)\n\n   🍟\n(//゚ω゚)//\n    \n 🍟\n(Ⅱ゚ω゚)Ⅱ\n\n  🍟\nᕕ( ᐛ )ᕗ",
                "冰封王座3": "( `д´)=🔪 👑\n    ( ゚ 3゚)\n   👑\n(// `ー´)//\n( x 3x)\n 👑\n(Ⅱ`∀´)Ⅱ\n( x 3x)\n 👑\n( `д´)\n( x 3x)",
                "全角空格": " "
            };
            const ORDERED_RICH = [
                "齐齐蛤尔","呼伦悲尔","愕尔多厮","智利","阴山山脉",
                "F5欧拉","UK酱","白羊","兔兔","neko","给你",
                "举高高","举糕糕","Happy肥肥Day","举高高2",
                "大嘘","催更喵gkd","巴拉巴拉","碣石",
                "冰封王座","冰封王座2","冰封王座3","全角空格"
                // 页面中“防剧透/骰子/高级骰子”不动其原位
            ];
            const NEED_LF = new Set([
                "F5欧拉","UK酱","白羊","兔兔","neko","给你",
                "举高高","举糕糕","Happy肥肥Day","举高高2",
                "大嘘","催更喵gkd","巴拉巴拉","碣石","冰封王座","冰封王座2","冰封王座3"
            ]);
  
            // 一次性补齐(选择器就绪且已有至少一个选项时调用)
            function patchSelect(sel) {
                if (!sel || sel.dataset.kaoExtended === '1') return;
                // 去重集合
                const existingValues = new Set();
                const existingLabels = new Set();
                Array.from(sel.options).forEach(op => {
                    existingValues.add(op.value);
                    existingLabels.add(op.textContent);
                });
                // 追加普通
                const frag = document.createDocumentFragment();
                EXTRA_EMOTS.forEach(txt => {
                    if (!existingValues.has(txt)) {
                        frag.appendChild(new Option(txt, txt));
                        existingValues.add(txt);
                    }
                });
                if (frag.childNodes.length) sel.appendChild(frag);
  
                // 收集需要重排的富颜文字:先把 ORDERED_RICH 中已存在的挪出
                const bucket = new Map(); ORDERED_RICH.forEach(k => bucket.set(k, null));
                for (let i = sel.options.length - 1; i >= 0; i--) {
                    const op = sel.options[i];
                    const label = op.textContent;
                    if (bucket.has(label)) {
                        bucket.set(label, op);
                        sel.remove(i);
                    }
                }
                // 按顺序插回;缺的用 EXTRA_RICH 补齐;值前加换行(NEED_LF)
                const richFrag = document.createDocumentFragment();
                ORDERED_RICH.forEach(key => {
                    let node = bucket.get(key);
                    if (node) {
                        richFrag.appendChild(node);
                    } else if (EXTRA_RICH[key]) {
                        const val = NEED_LF.has(key) ? ("\n" + EXTRA_RICH[key] + "\n") : EXTRA_RICH[key];
                        richFrag.appendChild(new Option(key, val));
                    }
                });
                if (richFrag.childNodes.length) sel.appendChild(richFrag);
  
                sel.dataset.kaoExtended = '1';
                sel.dispatchEvent(new CustomEvent('kaomoji:updated', { bubbles: true }));
            }
  
            // 方案 A:钩住 jQuery 的 append,仅针对 #h-emot-select
            (function hookjQueryAppend(){
                const $ = window.jQuery;
                if (!$ || !$.fn || !$.fn.append || $.fn.append.__kaoHooked) return;
                const rawAppend = $.fn.append;
                $.fn.append = function(...args){
                    const ret = rawAppend.apply(this, args);
                    try {
                        // 如果目标包含我们的 select,就尝试打补丁
                        this.each(function(){
                            if (this && this.querySelector) {
                                const sel = this.matches && this.matches(SELECTOR) ? this : this.querySelector(SELECTOR);
                                if (sel) patchSelect(sel);
                            }
                        });
                    } catch(_) {}
                    return ret;
                };
                $.fn.append.__kaoHooked = true;
            })();
  
            // 方案 B:用 MutationObserver 监听 select 的子节点变化,首次填充后补齐
            (function observeSelect(){
                const sel = document.querySelector(SELECTOR);
                if (!sel) return;
                // 若已有人填充过,直接打一次补丁
                if (sel.options && sel.options.length > 0) {
                    patchSelect(sel);
                }
                // 监听后续填充
                const mo = new MutationObserver(() => {
                    // 一旦看到有 option 节点,就补丁并停止观察
                    if (sel.options && sel.options.length > 0) {
                        patchSelect(sel);
                        mo.disconnect();
                    }
                });
                mo.observe(sel, { childList: true, subtree: false });
            })();
  
            // 方案 C:兜底重试(避免异步加载错过时机)
            let tries = 0;
            (function retry(){
                const sel = document.querySelector(SELECTOR);
                if (sel && sel.options && sel.options.length > 0) {
                    patchSelect(sel);
                    return;
                }
                if (tries++ < 30) setTimeout(retry, 100);
            })();
        }
    }
  
    function renderSpoiler(container) {
      var $container = $(container);
      // 用构造函数写法,避免 /^.../ 形式
      var reg = new RegExp("\\[h\\]([\\s\\S]*?)\\[\\/h\\]", "gi");
  
      $container.find('.h-threads-content').each(function () {
        var text = $(this).text().trim();
        var match = reg.exec(text);
        if (match) {
          $(this).html('<span class="h-hidden-text">' + match[1] + '</span>');
        }
      });
    }
  
    /* --------------------------------------------------
     * 10. ‘增强x岛匿名版’:添加预览框+草稿保存/恢复/自动设置网页标题/人类友好时间显示/引用追记/粘贴图片上传
     * -------------------------------------------------- */
    // 统一生成草稿存储用的 key
    // 统一:草稿 key 生成
    function getDraftKey() {
      return window.location.pathname;
    }
  
    // 统一:安全删除草稿(有 GM_deleteValue 用之;否则写空串兜底)
    function deleteDraftSafe(key) {
      try {
        if (!key) key = getDraftKey();
        if (typeof GM_deleteValue === 'function') {
          GM_deleteValue(key);
        } else if (typeof GM_setValue === 'function') {
          GM_setValue(key, ''); // 覆盖为空字符串
        } else {
          // 无 GM_* 时不做处理
        }
      } catch (_) {}
    }
  
  
    // 完整移植为可调用函数。需要:jQuery 2.2.4+;GM_setValue/GM_getValue/GM_deleteValue 授权
    function enhanceIsland(config = {}) {
      // 配置开关(默认全开)
      const cfg = Object.assign({
        enablePreview: true,         // 发帖预览(插入预览DOM并实时渲染)
        enableDraft: true,           // 草稿保存/恢复和成功后清理
        enableAutoTitle: true,       // 自动设置网页标题(含页码)
        enableRelativeTime: true,    // 相对时间显示(每2.5秒刷新)
        enableQuoteInsert: true,     // 点击 No.xxxx 插入引用
        enablePasteImage: true       // 粘贴剪贴板图片到文件输入
      }, config);
  
      // 解析 jQuery
      const $ = cfg.$ || window.jQuery || window.$;
      if (!$) {
        console.warn('[enhanceIsland] jQuery not found.');
        return;
      }
  
      // 公用变量
      const 正文框 = $('textarea.h-post-form-textarea');
      const search = window.location.search;
      const 搜索参数 = {};
      search.replace(/^\?/, '').split('&').forEach(kev => {
        if (!kev) return;
        const [k, v] = kev.split('=', 2);
        搜索参数[k] = v;
      });
      const 路径 = window.location.pathname;
      const 路径分块 = 路径.split('/').splice(1);
  
      // 预览区域 DOM(与原脚本一致)
      const previewHtml = `
    <div class="h-preview-box">
  
    <div class="h-threads-item">
    <div class="h-threads-item-replies">
    <div class="h-threads-item-reply">
    <div class="h-threads-item-reply-main">
        <div class="h-threads-img-box">
            <div class="h-threads-img-tool uk-animation-slide-top">
                <span class="h-threads-img-tool-btn h-threads-img-tool-small uk-button-link"><i class="uk-icon-minus"></i>收起</span>
                <a href=":javascript:;" class="h-threads-img-tool-btn uk-button-link"><i class="uk-icon-search-plus"></i>查看大图</a>
                <span class="h-threads-img-tool-btn h-threads-img-tool-left uk-button-link"><i class="uk-icon-reply"></i>向左旋转</span>
                <span class="h-threads-img-tool-btn h-threads-img-tool-right uk-button-link"><i class="uk-icon-share"></i>向右旋转</span>
            </div>
            <a class="h-threads-img-a"><img src="" align="left" border="0" hspace="20" class="h-threads-img"></a>
        </div>
        <div class="h-threads-info">
            <span class="h-threads-info-title">无标题</span>
            <span class="h-threads-info-email">无名氏</span>
            <span class="h-threads-info-createdat">2077-01-01(四)00:00:01</span>
            <span class="h-threads-info-uid">ID:cookies</span>
            <span class="uk-text-primary uk-text-small">(PO主)</span>
            <span class="h-threads-info-report-btn">
                [<a href="/f/值班室" target="_blank">举报</a>]
            </span>
            <a href=":javascript:;" class="h-threads-info-id" target="_blank">No.99999999</a>
        </div>
        <div class="h-threads-content">
            无内文
        </div>
    </div>
    </div>
    </div>
    </div>
  
    </div>
    `;
  
      // 只有在页面存在发帖表单容器时才插入预览
      function initPreviewBox() {
          if (!cfg.enablePreview) return;
          if (!$('#h-post-form form').length) return;
  
          // 只创建一次预览框
          if (!$('.h-preview-box').length) {
              const $box = $(previewHtml).insertAfter('#h-post-form form');
  
              // 初始化时同步饼干 ID
              const cookieDisplay = document.querySelector('#current-cookie-display');
              if (cookieDisplay) {
                  const cookieText = cookieDisplay.textContent.trim();
                  $box.find('.h-threads-info-uid').text(`ID:${cookieText}`);
              }
  
              // === 图片预览更新函数 ===
              function updatePreviewFromFile(file) {
                  const imgEl = $box.find('.h-threads-img')[0];
                  const imgLink = $box.find('.h-threads-img-a')[0];
                  if (!imgEl) return;
  
                  // 清理旧 URL
                  if (imgEl.dataset.prevObjectUrl) {
                      URL.revokeObjectURL(imgEl.dataset.prevObjectUrl);
                      delete imgEl.dataset.prevObjectUrl;
                  }
  
                  if (file) {
                      const objectUrl = URL.createObjectURL(file);
                      imgEl.src = objectUrl;
                      imgEl.dataset.prevObjectUrl = objectUrl;
                      imgEl.style.display = 'block';
                      if (imgLink) imgLink.href = objectUrl;
                  } else {
                      imgEl.src = '';
                      imgEl.style.display = 'none';
                      if (imgLink) imgLink.removeAttribute('href');
                  }
              }
  
              // === 监听文件选择 ===
              const fileInput = document.querySelector('input[type="file"][name="image"]');
              if (fileInput) {
                  fileInput.addEventListener('change', function () {
                      const file = this.files && this.files[0] ? this.files[0] : null;
                      updatePreviewFromFile(file);
                  });
              }
  
              // === 监听粘贴图片(不依赖 change 事件)===
              document.addEventListener('paste', function (e) {
                  const items = (e.clipboardData || e.originalEvent?.clipboardData)?.items || [];
                  if (!items.length) return;
  
                  let file = null;
                  for (const it of items) {
                      if (it.kind === 'file') {
                          const f = it.getAsFile();
                          if (f && f.type.startsWith('image/')) {
                              file = f;
                              break;
                          }
                      }
                  }
                  if (!file) return;
  
                  // 如果 input 存在,也同步到 input.files
                  if (fileInput) {
                      try {
                          const dt = new DataTransfer();
                          dt.items.add(file);
                          fileInput.files = dt.files;
                      } catch (_) {
                          // 某些浏览器不支持 DataTransfer 构造器
                      }
                  }
  
                  // 直接更新预览
                  updatePreviewFromFile(file);
      // 触发 change,让绑定在 file input 上的逻辑(如清除按钮)也能执行
      if (fileInput) {
          try {
              fileInput.dispatchEvent(new Event('change', { bubbles: true }));
          } catch (_) {}
      }
  
              }, true);
          }
      }
      // 预览引用/隐藏文本渲染
      const previewBox = $('<div/>'); // 占位,真正引用在 initPreviewBox 后重新抓取
      const refExp = /^([>>]+.*)$/g;
      const hideExp = /\[h\](.*)\[\/h\]/g;
  
      function renderContent(raw) {
        const box = $('.h-preview-box');
        if (!box.length) return;
        const previewContent = box.find('.h-threads-content');
  
        if (typeof raw !== 'string' || raw.trim() === '') {
          previewContent.text('');
          return;
        }
        previewContent.text('');
        for (let i of raw.split('\n')) {
          i = i.replace(/ +/g, ' ');
          let e;
          if (refExp.test(i)) {
            e = $('<font color="#789922"></font>').text(i);
          } else if (hideExp.test(i)) {
            e = $('<span class="h-hidden-text"></span>').text(i);
          } else {
            e = $('<span></span>').text(i);
          }
          previewContent.append(e);
          previewContent.append('<br>');
          // 支持拓展引用:把扩展引用包上 <font color="#789922">,以便可点击弹窗
          if (typeof extendQuote === 'function') {
            extendQuote(previewContent[0]);
          }
        }
      }
  
    // 草稿:发帖成功清空(拦截模式优先)
    function 清空编辑(key) {
      if (!cfg.enableDraft) return;
      if (key) {
        deleteDraftSafe(key);
        return;
      }
      // ===== 兼容原“中间页”的旧逻辑(仍保留)=====
      const okBox = document.getElementsByClassName('success')[0];
      if (!okBox) return;
      if (!okBox.textContent.includes('回复成功')) return;
  
      const hrefEl = document.getElementById('href');
      if (!hrefEl || !hrefEl.href) return;
  
      const m = /https?:\/\/[^/]+(\/t\/\d+)/.exec(hrefEl.href);
      if (!m) return;
      deleteDraftSafe(m[1]);
    }
  
      // 统一用事件清空,缺省用 getDraftKey()
      document.addEventListener('replySuccess', e => {
        清空编辑(e.detail?.key || getDraftKey());
      });
  
      // 草稿:载入
      function 载入编辑() {
        if (!cfg.enableDraft) return;
        if (!正文框.length) return;
        const key = window.location.pathname;
        let draft = '';
        if (typeof GM_getValue === 'function') {
          draft = GM_getValue(key, '');
        }
  
        // 检查 URL 参数 r
        if (搜索参数.r) {
          const quote = `>>No.${搜索参数.r}\n`;
          if (!draft.startsWith(quote)) {
            draft = quote + draft;
          }
        }
  
        正文框.val(draft);
      }
  
      // 草稿:注册自动保存 + 初始化一次保存触发(原脚本用 $(保存编辑))
      function 注册自动保存编辑() {
        if (!cfg.enableDraft) return;
        $('form').on('input', 保存编辑);
        // 文档就绪时同步一次
        $(保存编辑);
      }
  
      function 保存编辑() {
        if (!cfg.enableDraft) return;
        if (!正文框.length) return;
        if (typeof GM_setValue === 'function') {
          GM_setValue(window.location.pathname, 正文框.val());
        }
        renderContent(正文框.val());
  
        const box = $('.h-preview-box');
        if (box.length) {
          const previewTitle = box.find('.h-threads-info-title');
          const previewEmail = box.find('.h-threads-info-email');
          previewTitle.text($('form input[name="title"]').val() || '无标题');
          previewEmail.text($('form input[name="name"]').val() || '无名氏');
        }
      }
  
      // 点击 No.xxxx 插入引用(保持原先光标与选择区逻辑)
      function 注册追记引用串号() {
        if (!cfg.enableQuoteInsert) return;
        $('body').on('click', 'a.h-threads-info-id', e => {
          if (!正文框.length) return;
          const start = 正文框.prop('selectionStart');
          const end = 正文框.prop('selectionEnd');
          const len = end - start;
          const str = 正文框.val();
          const left = str.substring(0, start);
          const right = str.substring(end);
          const ref = `>>${e.target.textContent.trim()}`;
          正文框.val(
            start === 0
              ? `${ref}\n${right}`
              : end === str.length
                ? `${left}\n${ref}\n`
                : len > 0
                  ? `${left} ${ref} ${right}`
                  : `${left}\n${ref}`
          );
          正文框.trigger('input', '');
          保存编辑();
          e.preventDefault();
        });
      }
  
      // 粘贴图片到文件输入(保持原选择器)
      function 注册粘贴图片() {
        if (!cfg.enablePasteImage) return;
        window.addEventListener('paste', e => {
          const files = (e.clipboardData || e.originalEvent?.clipboardData)?.files || [];
          if (files.length) {
            const fileInput = document.querySelector('input[type="file"][name="image"]');
            if (fileInput) fileInput.files = files;
          }
        });
      }
  
      // 子函数:选择了图片后出现“清除图片”按钮;清除后按钮消失,恢复“选择文件”
      function 绑定清除图片按钮() {
          const $form = $('#h-post-form form, form[action="/Home/Forum/doReplyThread.html"]').first();
          if (!$form.length) return;
  
          const $file = $form.find('input[type="file"][name="image"]');
          if (!$file.length) return;
  
          if ($file.data('xdexClearBound')) return;
          $file.data('xdexClearBound', true);
  
          // 包一层容器,方便布局
          if (!$file.parent().hasClass('xdex-file-wrapper')) {
              $file.wrap('<div class="xdex-file-wrapper" style="display:flex;align-items:center;justify-content:space-between;width:100%;"></div>');
          }
  
      function 刷新按钮() {
          const hasFile = $file[0].files && $file[0].files.length > 0;
          let $btn = $form.find('.xdex-clear-image-btn');
  
          if (hasFile) {
              if (!$btn.length) {
                  $btn = $('<button type="button" class="xdex-clear-image-btn" title="清除图片">×</button>');
                  $btn.css({
                      fontSize: '16px',
                      lineHeight: '1',
                      padding: '2px 6px',
                      cursor: 'pointer'
                  });
                  $file.after($btn);
  
                  $btn.on('click', function (e) {
                      e.stopPropagation(); // 防止触发浮窗关闭
                      e.preventDefault();
  
                      // 清空文件
                      $file.val('');
  
                      // 直接调用预览清空逻辑
                      if (typeof updatePreviewFromFile === 'function') {
                          updatePreviewFromFile(null);
                      } else {
                          // 兜底:如果函数不可用,就清空 src/href 但不移除节点
                          const $preview = $('.h-preview-box');
                          $preview.find('img').attr('src', '').removeAttr('src');
                          $preview.find('.h-threads-img-a').attr('href', '');
                      }
  
                      // 移除按钮
                      $(this).remove();
                  });
  
              }
          } else {
              if ($btn.length) $btn.remove();
          }
      }
  
      $file.on('change', 刷新按钮);
      刷新按钮();
  }
  
  
  
      // 递归访问 DOM
      function visit(root, cb) {
        if (!root) return;
        if (cb(root) === '停止') return;
        for (const child of root.children || []) {
          visit(child, cb);
        }
      }
  
      // 自动标题:择标题(与原逻辑等价)
      function 选择标题() {
        const titleEl = document.querySelector('.h-threads-list .h-threads-item-main .h-threads-info .h-threads-info-title');
        const contentEl = document.querySelector('.h-threads-list .h-threads-item-main .h-threads-content');
        if (!contentEl) return document.title;
  
        const titleText = (titleEl?.textContent || '').trim();
        if (titleText && titleText !== '无标题') return titleText;
  
        const redTexts = [];
        visit(contentEl, el => {
          try {
            if (window.getComputedStyle(el).color === 'rgb(255, 0, 0)') {
              const redSegment = el.textContent.replace(/^[=\s+]+|[=\s+]+$/, '');
              if (redSegment !== '') redTexts.push(redSegment);
              return '停止';
            }
          } catch (_) {}
        });
        const red = redTexts.join('');
        if (red !== '') return red;
  
        const lines = (contentEl.innerText || '').split('\n');
        for (let line of lines) {
          if ((line = line.trim()) !== '') return line;
        }
        return document.title;
      }
  
      function 自动标题() {
        if (!cfg.enableAutoTitle) return;
        const 页码 = 路径分块[0] === 'Forum'
          ? (路径分块[5]?.replace(/\.html$/, '') || 1)
          : (搜索参数.page || 1);
        const 标题 = 选择标题();
        const titleEl = document.querySelector('title');
        if (titleEl) {
          titleEl.textContent = `${标题} - ${titleEl.textContent} - page ${页码}`;
        }
      }
  
      // 相对时间格式化(与原逻辑等价,目标 span.h-threads-info-createdat)
      function getFriendlyTime(machineReadableTime) {
        const date = new Date(machineReadableTime);
        const now = new Date();
        if (now < date) return machineReadableTime;
  
        let friendlyDate = machineReadableTime.slice(0, 10);
        let friendlyTime = machineReadableTime.slice(13, 21);
        const weekday = machineReadableTime.slice(11, 12);
  
        const diff = (now.getTime() - date.getTime()) / 1000;
        if (diff < 60) {
          friendlyTime = `${Math.floor(diff)}秒前`;
        } else if (diff < 3600) {
          friendlyTime = `${Math.floor(diff / 60)}分钟前`;
        } else if (diff < 24 * 3600) {
          friendlyTime = `${Math.floor(diff / 3600)}小时前 ${friendlyTime}`;
        }
  
        const yesterday = new Date(new Date(now - 1000 * 60 * 60 * 24).toLocaleDateString());
        if (now.toLocaleDateString() === date.toLocaleDateString()) {
          friendlyDate = '今天';
        } else if (yesterday.toLocaleDateString() === date.toLocaleDateString()) {
          friendlyDate = '昨天';
        } else if (yesterday - date < 1000 * 60 * 60 * 24 * 30) {
          friendlyDate = `${Math.floor((now - date) / (1000 * 60 * 60 * 24))}天前`;
        } else if (now.getFullYear() === date.getFullYear()) {
          friendlyDate = friendlyDate.slice(5);
        } else {
          friendlyDate = `${now.getFullYear() - date.getFullYear()}年前 ${friendlyDate}`;
        }
        return `${friendlyDate}(${weekday})${friendlyTime}`;
      }
  
      function formatDateStrOnPage() {
        if (!cfg.enableRelativeTime) return;
        const targets = $('span.h-threads-info-createdat');
        targets.each(function () {
          const target = $(this);
          const timeStr = target.attr('title') || target.text().trim();
          if (!timeStr) return;
          target.attr('title', timeStr);
          const friendlyTime = getFriendlyTime(timeStr);
          target.text(friendlyTime);
        });
      }
  
      // 路由:各页面初始化(与原逻辑一致)
      function 串() {
        if (cfg.enablePreview) initPreviewBox();
        if (cfg.enableDraft) 载入编辑();
        if (cfg.enableQuoteInsert) 注册追记引用串号();
        if (cfg.enableDraft) 注册自动保存编辑();
        if (cfg.enablePasteImage) 注册粘贴图片();
        绑定清除图片按钮();
        if (cfg.enableAutoTitle) 自动标题();
        if (cfg.enableRelativeTime) setInterval(formatDateStrOnPage, 2500);
      }
  
      function 串只看po() {
        if (cfg.enableAutoTitle) 自动标题();
      }
  
      function 版块() {
        if (cfg.enablePreview) initPreviewBox();
        if (cfg.enableDraft) 注册自动保存编辑();
        //if (cfg.enableQuoteInsert) 注册追记引用串号();
        if (cfg.enablePasteImage) 注册粘贴图片();
        绑定清除图片按钮();
        if (cfg.enableRelativeTime) setInterval(formatDateStrOnPage, 2500);
      }
  
      function 回复成功() {
        if (cfg.enableDraft) 清空编辑();
        if (cfg.enablePasteImage) 注册粘贴图片();
      }
      document.addEventListener('replySuccess', e => {
          回复成功(e.detail?.key);
      });
  
  
      function 未知() {
        if (cfg.enablePasteImage) 注册粘贴图片();
      }
  
      // 一级路径解析(支持 m 前缀)
      const 一层路径 = 路径分块[0] === 'm' ? 路径分块[1] : 路径分块[0];
  
      // 入口分流(与原脚本一致)
      switch (一层路径) {
        case 't':
          串();
          break;
        case 'f':
          版块();
          break;
        case 'Forum':
          if (路径分块[1] === 'po' && 路径分块[2] === 'id') { 串只看po(); } else { 未知(); }
          break;
        case 'Home':
          if (路径 === '/Home/Forum/doReplyThread.html') { 回复成功(); } else { 未知(); }
          break;
        case 'Member':
          if (路径.startsWith('/Member/User/Cookie/export/id/')) { console.debug('//TODO'); }
          break;
        default:
          未知();
      }
  
      // 首次渲染预览(若需要)
      if (cfg.enablePreview) {
        renderContent(正文框.val ? (正文框.val() || '') : '');
      }
    }
  
    /* --------------------------------------------------
     * 11. 入口初始化
     * -------------------------------------------------- */
    $(document).ready(() => {
      SettingPanel.init();
      const cfg = Object.assign({}, SettingPanel.defaults, GM_getValue(SettingPanel.key, {}));
  
      if (cfg.enableCookieSwitch)          createCookieSwitcherUI();  //快捷切换饼干
      if (cfg.enablePaginationDuplication) duplicatePagination();     //添加页首页码
      if (cfg.disableWatermark)            disableWatermark();        //关闭图片水印
      if (cfg.updatePreviewCookie)         updatePreviewCookieId();   //预览真实饼干
      if (cfg.hideEmptyTitleEmail) {hideEmptyTitleAndEmail();         //隐藏无名氏/无标题/回复/发串/版规
        Utils.collapse($('.h-forum-header'), '『版规』');
        Utils.collapse($('form[action="/Home/Forum/doReplyThread.html"]'), '『回复』');
        Utils.collapse($('form[action="/Home/Forum/doPostThread.html"]'), '『发串』');
        }                                                              //折叠版规-发串
      if (cfg.enableExternalImagePreview)  ExternalImagePreview.init();//显示外部图床
      if (cfg.updateReplyNumbers)          updateReplyNumbers();       //添加回复编号
      if (cfg.enableSeamlessPaging)        initSeamlessPaging();       //自动-手动无缝翻页
      if (cfg.enableHDImage)               enableHDImage();            //X岛-揭示板的增强型体验-高清图片链接+图片控件
      if (cfg.enableLinkBlank)             runLinkBlank();             //X岛-揭示板的增强型体验-新标签打开串
      if (cfg.enableQuotePreview)          enableQuotePreview();       //优化引用弹窗
      replaceRightSidebar();                                           //扩展坞增强
      interceptReplyForm();                                            //拦截回复中间页
      if (cfg.extendQuote)                 extendQuote();              //扩展引用格式
      kaomojiEnhancer();                                               //颜文字拓展
      highlightPO();                                                   //高亮PO主
      enhancePostFormLayout();                                         //发帖UI调整
      applyFilters(cfg);                                               //标记/屏蔽/过滤-饼干/关键词
      initExtendedContent();                                           //扩展引用
      autoHideRefView();
      enhanceIsland({                                                  //增强X岛匿名版
        // 这些都可选,默认全开
        enablePreview: true,                                           //添加预览框
        enableDraft: true,                                             //草稿保存/恢复
        enableAutoTitle: true,                                         //自动设置网页标题
        enableRelativeTime: true,                                      //人类友好时间显示
        enableQuoteInsert: true,                                       //点击 No.xxxx 插入引用
        enablePasteImage: true,                                        //粘贴剪贴板图片到文件输入
        // 可传入你的 jQuery 实例(若页面没有全局 $)
        // $: window.myJQ
      });
  
      // 保存原始函数
      const _initContent = window.initContent;
  
      // 将X岛原版https://www.nmbxd1.com/Public/Js/h.desktop.js中作用于引用的initContent函数支持我们新增的非标准引用格式
      window.initContent = function(root) {
          // 先执行原始逻辑
          if (typeof _initContent === 'function') {
              _initContent(root);
          }
          // 再执行扩展逻辑
          initExtendedContent(root || document);
      };
      initContent(document);
  
      document.querySelectorAll('form textarea[name="content"]').forEach(bindCtrlEnter);
      // 自动刷新并提示当前饼干,我不知道为什么必须写在这里。
      function autoRefreshCookiesToast() {
        try {
          if (typeof refreshCookies !== 'function') return;
          refreshCookies(() => {
            const list = getCookiesList();
            if (!list || !Object.keys(list).length) return;
            const cur = getCurrentCookie();
            const nm = cur && cur.name ? abbreviateName(cur.name) : '未知';
            if (cfg.enableAutoCookieRefreshToast) {
                toast(`自动刷新成功!当前饼干为 ${nm}`);
                  }
          }, false); // ★ 不显示默认toast
        } catch (e) {
          console.warn('自动刷新饼干失败', e);
        }
      }
  
      // 仅在“从其它标签页切回到本标签页”时刷新
      const TAB_ACTIVE_KEY = 'xdex_active_tab';
      const tabId = Date.now() + '_' + Math.random().toString(36).slice(2);
  
      try {
        if (document.visibilityState === 'visible') {
          localStorage.setItem(TAB_ACTIVE_KEY, JSON.stringify({ id: tabId, t: Date.now() }));
        }
      } catch {}
  
      // ✅ 加上开关判断
      if (cfg.enableAutoCookieRefresh) {
        document.addEventListener('visibilitychange', () => {
          if (document.visibilityState !== 'visible') return;
            try {
              const last = JSON.parse(localStorage.getItem(TAB_ACTIVE_KEY) || 'null');
              if (last && last.id && last.id !== tabId) {
                autoRefreshCookiesToast();
              }
              localStorage.setItem(TAB_ACTIVE_KEY, JSON.stringify({ id: tabId, t: Date.now() }));
            } catch (e) {
              try {
                localStorage.setItem(TAB_ACTIVE_KEY, JSON.stringify({ id: tabId, t: Date.now() }));
              } catch {}
            }
        }, { passive: true });
      }
    });
  
  })(jQuery);