Vscode 交互插件

请配合 OI File Checker 使用

// ==UserScript==
// @name         Vscode 交互插件
// @description  请配合 OI File Checker 使用
// @namespace    http://tampermonkey.net/
// @version      0.11
// @match        https://www.luogu.com.cn/problem/*
// @match        https://codeforces.com/problemset/problem/*/*
// @match        https://codeforces.com/contest/*/problem/*
// @match        https://atcoder.jp/*
// @match        https://vjudge.net/*
// @grant GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    function addLgButton() {
        const containerSelector = 'div[data-v-f265fec6]';
        const buttonSelector = 'button[data-v-505b6a97]';

        function showSuccessToast(msg) {
            const body = document.body;
            const html = document.documentElement;
            const originalBodyClass = body.className;
            const originalHtmlClass = html.className;
            body.className = 'swal2-toast-shown swal2-shown';
            html.className = 'no-js swal2-toast-shown swal2-shown';

            if(document.querySelector('.swal2-container.send-success-toast')) return;

            const container = document.createElement('div');
            container.className = 'swal2-container swal2-top-end swal2-backdrop-show send-success-toast';
            container.style.overflowY = 'auto';

            container.innerHTML = `
        <div aria-labelledby="swal2-title" aria-describedby="swal2-html-container"
             class="swal2-popup swal2-toast swal2-icon-success swal2-show"
             tabindex="-1" role="alert" aria-live="polite"
             style="width: 100%; display: grid;">
            <button type="button" class="swal2-close" aria-label="Close this dialog" style="display: none;">×</button>
            <ul class="swal2-progress-steps" style="display: none;"></ul>
            <div class="swal2-loader"></div>
            <div class="swal2-icon swal2-success swal2-icon-show" style="display: flex;">
                <div class="swal2-success-circular-line-left" style="background-color: rgb(255, 255, 255);"></div>
                <span class="swal2-success-line-tip"></span>
                <span class="swal2-success-line-long"></span>
                <div class="swal2-success-ring"></div>
                <div class="swal2-success-fix" style="background-color: rgb(255, 255, 255);"></div>
                <div class="swal2-success-circular-line-right" style="background-color: rgb(255, 255, 255);"></div>
            </div>
            <img class="swal2-image" style="display: none;">
            <h2 class="swal2-title" id="swal2-title" style="display: block;">${msg}</h2>
            <div class="swal2-html-container" id="swal2-html-container" style="display: none;"></div>
            <!-- 其他隐藏元素保留 -->
        </div>
    `;

            document.body.appendChild(container);

            // 可选:设置几秒后自动消失
            setTimeout(() => {
                container.remove();
                body.className = originalBodyClass;
                html.className = originalHtmlClass;
            }, 2000); // 2秒
        }
        function showErrorToast(msg) {
            const body = document.body;
            const html = document.documentElement;
            const originalBodyClass = body.className;
            const originalHtmlClass = html.className;
            body.className = 'swal2-toast-shown swal2-shown';
            html.className = 'no-js swal2-toast-shown swal2-shown';

            const container = document.createElement('div');
            container.className = 'swal2-container swal2-top-end swal2-backdrop-show send-x-mark-toast';
            container.style.overflowY = 'auto';

            container.innerHTML = `
        <div aria-labelledby="swal2-title" aria-describedby="swal2-html-container"
             class="swal2-popup swal2-toast swal2-icon-x-mark swal2-show"
             tabindex="-1" role="alert" aria-live="polite"
             style="width: 100%; display: grid;">
            <button type="button" class="swal2-close" aria-label="Close this dialog" style="display: none;">×</button>
            <ul class="swal2-progress-steps" style="display: none;"></ul>
            <div class="swal2-loader"></div>
            <div class="swal2-icon swal2-error swal2-icon-show" style="display: flex;">
                <span class="swal2-x-mark">
                    <span class="swal2-x-mark-line-left"></span>
                    <span class="swal2-x-mark-line-right"></span>
                </span>
            </div>
            <img class="swal2-image" style="display: none;">
            <h2 class="swal2-title" id="swal2-title" style="display: block;">${msg}</h2>
            <div class="swal2-html-container" id="swal2-html-container" style="display: none;"></div>
            <!-- 其他隐藏元素保留 -->
        </div>
    `;

            document.body.appendChild(container);

            // 可选:设置几秒后自动消失
            setTimeout(() => {
                container.remove();
                body.className = originalBodyClass;
                html.className = originalHtmlClass;
            }, 2000); // 2秒
        }
        const container = document.querySelector(containerSelector);
        if (!container) return;

        const buttons = container.querySelectorAll(buttonSelector);
        if (buttons.length === 0) return;

        if (container.querySelector('.send-to-vscode-btn')) return;

        const lastButton = buttons[buttons.length - 1];
        const newBtn = document.createElement('button');

        newBtn.className = 'lform-size-middle button-transparent send-to-vscode-btn';
        newBtn.setAttribute('type', 'button');
        newBtn.setAttribute('data-v-505b6a97', '');
        newBtn.setAttribute('data-v-f265fec6-s', '');
        newBtn.innerText = '发送样例至 Vscode';

        lastButton.insertAdjacentElement('afterend', newBtn);

        newBtn.addEventListener('click', () => {
            const codes = Array.from(document.querySelectorAll('pre.lfe-code')).map(el => el.innerText);
            const url = location.href;
            if(/^https:\/\/www\.luogu\.com\.cn\/problem\//.test(url) && !url.endsWith("#submit")) {
                GM_xmlhttpRequest({
                method: "POST",
                url: "http://127.0.0.1:4000/send",
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify({ samples: codes }),
                onload: res => showSuccessToast('发送成功'),
                onerror: err => showErrorToast('发送失败')
                });
            }
            else {
                showErrorToast('请回到题面再发送数据')
            }
        });
    }
    function addCfButton() {
        if (document.getElementById("cf-send-btn")) return;
        function fadeOutAndRemove(el) {
            el.style.transition = "opacity 1s";
            el.style.opacity = "0";
            setTimeout(() => {
                if (el.parentNode) {
                    el.parentNode.removeChild(el);
                }
            }, 800);
        }
        function show(msg) {
            let container = document.querySelector("#jGrowl.bottom-right.jGrowl");
            if (!container) {
                container = document.createElement("div");
                container.id = "jGrowl";
                container.className = "bottom-right jGrowl";
                // 先插入一个空的 jGrowl-notification(或者可以不加)
                const emptyNotification = document.createElement("div");
                emptyNotification.className = "jGrowl-notification";
                container.appendChild(emptyNotification);

                // 插入到 body 最后
                document.body.appendChild(container);
            }

            // 创建通知
            const notification = document.createElement("div");
            notification.className = "jGrowl-notification default";
            notification.style.display = "block";
            notification.style.opacity = "0.8";
            notification.innerHTML = `
        <div class="close">×</div>
        <div class="header"></div>
        <div class="message">${msg}</div>
    `;

            // 关闭按钮逻辑
            notification.querySelector(".close").addEventListener("click", () => {
                fadeOutAndRemove(notification);
            });

            // 插入到容器最后
            container.appendChild(notification);

            // 5秒后自动消失
            setTimeout(() => {
                fadeOutAndRemove(notification);
            }, 5000);
        }
        const sidebar = document.getElementById("sidebar");

        const box = document.createElement("div");
        box.className = "roundbox sidebox borderTopRound";
        box.innerHTML = `
        <div class="caption titled">
            → 复制数据到 VsCode
            <div class="top-links"></div>
        </div>
        <div style="text-align:center;margin:1em;">
            <button id="cf-send-btn">发送</button>
        </div>
    `;

        sidebar.insertBefore(box, sidebar.firstChild);

        const btn = document.getElementById("cf-send-btn");

        btn.addEventListener("click", () => {
            const samples = [];
            document.querySelectorAll(".sample-test").forEach(block => {
                const inputs = block.querySelectorAll(".input pre");
                const outputs = block.querySelectorAll(".output pre");

                inputs.forEach((inputEl, i) => {
                    const outputEl = outputs[i];
                    if (outputEl) {
                        samples.push(inputEl.innerText.trim());
                        samples.push(outputEl.innerText.trim());
                    }
                });
            });
            GM_xmlhttpRequest({
                method: "POST",
                url: "http://127.0.0.1:4000/send",
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify({ samples: samples }),
                onload: res => {
                    show('发送成功');
                },
                onerror: err => {
                    show('发送失败');
                }
            });
        });
    }
    function addAtButton() {
        if (!document.getElementById("task-statement")) return;
        if (document.getElementById("at-send-btn")) return;
        const col = Array.from(document.querySelectorAll("div.col-sm-12"))
        .find(div => div.id !== "contest-nav-tabs");


        const spanH2 = col.querySelector("span.h2");
        if (!spanH2) return;

        const btn = document.createElement("button");
        btn.id = "at-send-btn";
        btn.className = "btn btn-default btn-sm send-to-vscode-btn";
        btn.textContent = "发送数据至 VsCode";
        spanH2.appendChild(btn);

        btn.addEventListener("click", () => {
            const samples = [];
            document.querySelectorAll("section").forEach(section => {
                const h3 = section.querySelector("h3");
                if (h3?.textContent.includes("Sample")) {
                    const Text = section.querySelector("pre")?.textContent.trim();
                    samples.push(Text);
                }
            });
            GM_xmlhttpRequest({
                method: "POST",
                url: "http://127.0.0.1:4000/send",
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify({ samples: samples })
            });
        });
    }
    function addVjButton() {
        if (document.getElementById("vj-send-btn")) return;
        const sidebar = document.getElementById("prob-operation");
        const container = sidebar.querySelector("div.container");
        const row = document.createElement("div");
        row.classList.add("row");
        container.appendChild(row);
        const new_div = document.createElement("div");
        new_div.classList.add("col-xs-12");
        row.appendChild(new_div);

        const btn = document.createElement("button");
        btn.id = "vj-send-btn";
        btn.className = "btn btn-secondary";
        btn.type = "button"
        btn.textContent = "发送数据至 VsCode";
        new_div.appendChild(btn);

        btn.addEventListener("click", () => {
            let iframe;
            if(document.getElementById("frame-description")) {
                iframe = document.getElementById("frame-description");
            }
            else if(document.getElementById("frame-description-container")) {
                let iframes = document.getElementById("frame-description-container").querySelectorAll("iframe");

                iframe = Array.from(iframes).find(iframe => {
                    let style = window.getComputedStyle(iframe);
                    return style.display !== "none";
                });
                console.log(iframe);
            }
            console.log(iframe);
            const innerDoc = iframe.contentDocument || iframe.contentWindow.document;
            const samples = [];
            const selector = innerDoc.getElementById("description-container");
            selector.querySelectorAll("table.vjudge_sample tbody tr td pre").forEach(section => {
                samples.push(section.textContent.trim());
                console.log(section.textContent.trim());
            });
            GM_xmlhttpRequest({
                method: "POST",
                url: "http://127.0.0.1:4000/send",
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify({ samples: samples })
            });
        });
    }

    if(location.hostname.includes("luogu.com.cn")) {
        const observer = new MutationObserver(() => addLgButton());
        observer.observe(document.body, { childList: true, subtree: true });
        addLgButton();
    }
    else if(location.hostname.includes("codeforces.com")) {
        const observer = new MutationObserver(() => addCfButton());
        observer.observe(document.body, { childList: true, subtree: true });
        addCfButton();
    }
    else if(location.hostname.includes("atcoder.jp")) {
        const observer = new MutationObserver(() => addAtButton());
        observer.observe(document.body, { childList: true, subtree: true });
        addAtButton();
    }
    else if(location.hostname.includes("vjudge.net")) {
        const observer = new MutationObserver(() => addVjButton());
        observer.observe(document.body, { childList: true, subtree: true });
        addVjButton();
    }
})();