rrrrrrrrrriyjiiiiiuzhao / NGA安科

// ==UserScript==
// @name         NGA安科
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  安科用
// @author       rrrrrrrrrriyjiiiiiuzhao
// @match        https://ngabbs.com/read.php?*
// @match        https://nga.178.com/read.php?*
// @grant        none
// @license MIT
// ==/UserScript==


(function () {
    'use strict';

    function angaToText(angas) {
        const result = []
        angas.forEach(element => {
            element.options.forEach(opt => {
                result.push(`${element.floor}:${element.author}:${opt}`)
            })
        });
        return `\n${result.join('\n')}\n`
    }

    function updateTextarea(content) {
        // 插入到快速发帖框
        const textarea = document.querySelector('#fast_post_c textarea');
        if (textarea) {
            textarea.value = content;
            textarea.focus();
        }
    }

    function appendTextarea(content) {
        // 插入到快速发帖框
        const textarea = document.querySelector('#fast_post_c textarea');
        if (textarea) {
            textarea.value += content;
            textarea.focus();
        }
    }

    // 楼层=>页
    function floor2page(floor) {
        return Math.floor(floor / 20) + 1;
    }
    // 楼层=>起始页
    function page2floor(page) {
        return (page - 1) * 20;
    }

    function purifyContent(htmlNode) {
        // 创建内存中的临时容器
        const tempContainer = document.createElement('div');

        // 深度克隆时移除事件监听器
        const clone = htmlNode.cloneNode(true);
        clone.querySelectorAll('*').forEach(node => {
            // 清除所有事件属性
            node.removeAttribute('onerror');
            node.removeAttribute('onload');
            // 移除可能导致脚本执行的元素
            if (node.tagName === 'SCRIPT') node.remove();
        });

        // 将克隆节点附加到内存容器
        tempContainer.appendChild(clone);

        // 执行净化操作(在内存中进行)
        const quoteBlocks = tempContainer.querySelectorAll('div.quote');
        quoteBlocks.forEach(block => block.remove());

        // 获取净化后的HTML字符串
        const cleanHTML = tempContainer.innerHTML;

        // 主动销毁临时对象
        tempContainer.remove();
        clone.remove();

        // 创建新的纯净文本节点
        const textContainer = document.createElement('div');
        textContainer.innerHTML = cleanHTML;

        // 提取纯文本
        const text = textContainer.textContent
            .replace(/\s+/g, ' ')
            .trim();

        // 再次清理
        textContainer.remove();

        return text;
    }


    function findAngaOptions(text) {
        // 增强版正则表达式,处理多种情况:
        const pattern = /安价[::]\s*((?:(?!安价[::])[\s\S])+)/gi;

        const matches = [];

        // 先按安价关键词分割文本块
        const segments = text.split(/(?=安价[::])/gi);

        segments.forEach(segment => {
            if (!segment.toLowerCase().startsWith('安价')) return;

            // 精确提取内容部分
            const match = segment.match(pattern);
            if (match) {
                const content = match[0]
                    .replace(/^安价[::]\s*/i, '') // 去除前缀
                    .replace(/\s+/g, ' ') // 合并空白
                    .trim();

                if (content) {
                    matches.push(content);
                }
            }
        });

        return matches;
    }


    function scanAllFloorsInPage(root, fromFloor, toFloor) {
        const results = [];
        // 核心选择器调整
        const floorElements = root.querySelectorAll('tr.postrow');
        floorElements.forEach(floor => {
            // 提取楼层元数据
            const floorNumber = Number(floor.querySelector('a[name^="l"]')?.textContent.match(/\d+/)?.[0]);
            if (floorNumber > toFloor || floorNumber < fromFloor) {
                return;
            }
            const author = floor.querySelector('.userlink.author')?.textContent.trim();
            // 定位内容容器
            const contentSpan = floor.querySelector('span.postcontent.ubbcode');
            if (!contentSpan) return;
            // 净化内容流程
            const cleanText = purifyContent(contentSpan);
            // 安价解析
            const angas = findAngaOptions(cleanText);

            if (angas.length > 0) {
                results.push({
                    floor: floorNumber,
                    author,
                    options: angas,
                });
            }
        });
        return results;
    }

    function onQuickAngaBtnClick() {
        const urlParams = new URLSearchParams(window.location.search);
        const page = urlParams.get('page'); // 返回字符串 "5"
        const pageNumber = parseInt(page, 10);

        var startFloor = page2floor(pageNumber)
        const angas = scanAllFloorsInPage(startFloor, startFloor + 19); // 调用之前的扫描函数
        if (angas.length === 0) {
            alert('未检测到安价内容');
            return;
        }

        updateTextarea(`安价数据:\n${angaToText(angas)}\n\n`)
    }

    function findUrlForPage(page) {
        // 获取当前页面的URL对象
        const urlObj = new URL(window.location.href);
        // 获取查询参数操作接口
        const params = urlObj.searchParams;
        // 智能设置page参数(自动处理参数存在与否的情况)
        params.set('page', page); // 将页码设置为8
        // 生成新URL字符串
        return urlObj.toString();
    }

    async function loadPageInBackground(url) {
        return new Promise((resolve, reject) => {
            const iframe = document.createElement('iframe');
            iframe.style.display = 'none'; // 隐藏 iframe
            document.body.appendChild(iframe);

            // 监听加载完成事件
            iframe.onload = async () => {
                try {
                    // 确保内容已加载(可根据需要调整延迟)
                    await new Promise(resolve => setTimeout(resolve, 2000));

                    // 获取 iframe 内的完整 DOM
                    const doc = iframe.contentDocument || iframe.contentWindow.document;
                    const html = doc.documentElement.outerHTML;

                    // 清理 iframe
                    document.body.removeChild(iframe);
                    resolve(html);
                } catch (error) {
                    reject(error);
                }
            };

            // 处理加载错误
            iframe.onerror = () => {
                document.body.removeChild(iframe);
                reject(new Error('Failed to load page'));
            };

            // 开始加载
            iframe.src = url;
        });
    }

    async function fetchAllPage() {
        var startFloor = Number(startInput.value)
        var startPage = floor2page(startFloor)
        var endFloor = Number(endInput.value)
        var endPage = floor2page(endFloor)
        const mergedResults = [];
        for (let page = startPage; page <= endPage; page++) {
            const url = new URL(findUrlForPage(page), window.location.origin).href;
            // 发起 GET 请求
            const html = await loadPageInBackground(url);
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            // 处理当前页结果
            var nowStartFloor = page2floor(page)
            nowStartFloor = (nowStartFloor < startFloor) ? startFloor : nowStartFloor;
            var nowEndFloor = page2floor(page) + 19
            nowEndFloor = (nowEndFloor > endFloor) ? endFloor : nowEndFloor;
            const currentResults = scanAllFloorsInPage(doc, nowStartFloor, nowEndFloor);
            mergedResults.push(...currentResults); // 按顺序追加结果
            // 插入到快速发帖框
            appendTextarea(`页码 ${page} 完成\n`)
        }
        return mergedResults;
    }

    var startInput, endInput;
    function onAngaBtnClick() {
        updateTextarea("开始统计安价\n")
        fetchAllPage().then(results => {
            appendTextarea(`安价数据:\n${angaToText(results)}\n\n`)
        })
    }

    function createAngaButton(textContent, onClick) {
        const btn = document.createElement('a');
        btn.className = 'uitxt1'; // 保持样式统一
        btn.style.cssText = 'font-size:1.23em; margin-left:10px;';
        btn.textContent = textContent;

        // 添加点击事件
        btn.addEventListener('click', onClick);

        return btn;
    }

    // 获取回复按钮,小的那种
    function getQuickReplyButtons() {
        const allButtons = Array.from(document.getElementsByClassName('uitxt1'));
        const targetButtons = allButtons.filter(btn => {
            const span = btn.querySelector('span');
            return span && span.textContent.trim() === '发表回复';
        });
        return targetButtons;
    }

    function getReplyButtons() {
        // 获取所有符合条件的元素,再根据文本内容过滤
        const buttons = Array.from(document.getElementsByClassName('uitxt1'));
        const replyBtns = buttons.filter(btn =>
            btn.textContent.trim() === '发表回复(Ctrl+Enter)'
        );
        return replyBtns
    }

    //生成输入框
    function createInputsContainer() {
        const container = document.createElement('div');
        container.style.cssText = 'margin:10px 0; text-align:left;';

        startInput = document.createElement('input');
        startInput.type = 'text';
        startInput.placeholder = '开始楼层';
        startInput.style.cssText = 'width:48%; margin-right:2%;';

        endInput = document.createElement('input');
        endInput.type = 'text';
        endInput.placeholder = '结束楼层';
        endInput.style.cssText = 'width:48%;';

        container.append(startInput, endInput);
        return container;
    }

    function injectFloorInputs() {
        const targetButton = document.querySelector('td.c2 button[title="插入表情"]');
        if (!targetButton) return;

        const existingInputs = document.querySelector('td.c2 input[placeholder="开始楼层"]');
        if (existingInputs) return;

        const container = createInputsContainer();
        targetButton.parentNode.insertBefore(container, targetButton);
    }

    // 生成安价按钮的类名标识
    const ANGA_BUTTON_CLASS = 'anga-json-btn';
    // 注入按钮
    function injectButtons() {
        getQuickReplyButtons().forEach(originBtn => {
            const container = originBtn.closest('td');
            if (!container || container.querySelector(`.${ANGA_BUTTON_CLASS}`)) return;

            const newBtn = createAngaButton("本页安价", onQuickAngaBtnClick);
            container.insertAdjacentElement('afterend', newBtn);
        });

        getReplyButtons().forEach(originBtn => {
            const container = originBtn.closest('td');
            if (!container || container.querySelector(`.${ANGA_BUTTON_CLASS}`)) return;

            const newBtn = createAngaButton("生成安价", onAngaBtnClick);
            container.insertAdjacentElement('afterend', newBtn);
        });
    }

    const observer = new MutationObserver((mutations) => {
        const element = document.querySelector('a.uitxt1[style="font-size: 1.23em; margin-left: 10px;"]');
        if (element !== null) {
            return
        }
        injectButtons()
        injectFloorInputs()
    });

    // 配置观察选项:
    observer.observe(document.body, {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false
    });

    injectButtons()
    injectFloorInputs()

})();