CC98 Tools - Math Editor

为CC98网页版添加数学公式支持

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         CC98 Tools - Math Editor
// @namespace    https://www.cc98.org/
// @version      0.0.1
// @description  为CC98网页版添加数学公式支持
// @author       ml98
// @match        https://www.cc98.org/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://greatest.deepsurf.us/scripts/2199-waitforkeyelements/code/waitForKeyElements.js?version=6349
// @grant        GM_addStyle
// ==/UserScript==

// ref
// https://latex.codecogs.com/svg.latex?\log\prod^N_{i}x_{i}=\sum^N_i\log{x_i}
// https://math.now.sh/?from=\\log\\prod^N_{i}x_{i}=\\sum^N_i\\log{x_i}
// https://www.zhihu.com/equation?tex=~

// test https://www.cc98.org/topic/2803718/695#4

console.log("%cCC98 Tools Math Editor", "font-size: large");


function addMathEditor(){
    'use strict';
    console.log("addMathEditor");
    // add Math Editor modal
    console.log("add Math Editor modal");
    var myModal = document.createElement('div');
    myModal.id = "myModal";
    myModal.classList = "modal";
    if(1){myModal.innerHTML = String.raw`
        <!-- Modal content -->
        <div class="modal-content">
            <span class="close">&times;</span>
            <h3 id="header">Math Editor for CC98</h3>
            <div id="main">
                <div id="io">
                    <div id="input">
                        <p>Input</p>
                        <textarea id="inputText" spellcheck="false">\LaTeX</textarea>
                        <button id="copyInput" onclick="copy('inputText')">Copy</button>
                    </div>

                    <div id="output">
                        <p>Output</p>
                        <textarea type="text" id="outputText" spellcheck="false" value=''></textarea>
                        <button id="copyOutput" onclick="copy('outputText')">Copy</button>
                    </div>
                </div>
                <br>
                <label style="display: inline-block;">Engine
                    <select name="select" id="engineSelect">
                        <option value="zhihu" selected>zhihu</option>
                        <option value="codecogs">codecogs</option>
                        <option value="math.now.sh">math.now.sh</option>
                    </select>
                </label>
                <label style="display: inline-block;">Format
                    <select name="select" id="formatSelect">
                        <option value="Ubb" selected>Ubb</option>
                        <option value="Markdown">Markdown</option>
                        <option value="HTML">HTML</option>
                        <option value="URL">URL</option>
                    </select>
                </label>

                <div id="preview">
                    <p>Preview</p>
                    <div id="imagebox">
                        <img id="previewImage" src="" />
                    </div>
                </div>
            </div>
            <div id="footer">
                <p>86ɔɔ ɹoɟ ɹoʇıpǝ ɥʇɐɯ</p>
            </div>
        </div>
`;}
    document.body.appendChild(myModal);

    // add style
    console.log("add Math Editor style");
    if(1){GM_addStyle(String.raw`
        /* The Modal (background) */
        .modal {
            display: none;
            /* Hidden by default */
            position: fixed;
            /* Stay in place */
            z-index: 1;
            /* Sit on top */
            padding-top: 50px;
            /* Location of the box */
            left: 0;
            top: 0;
            width: 100%;
            /* Full width */
            height: 100%;
            /* Full height */
            overflow: auto;
            /* Enable scroll if needed */
            background-color: rgb(0, 0, 0);
            /* Fallback color */
            background-color: rgba(0, 0, 0, 0.4);
            /* Black w/ opacity */
        }

        /* Modal Content */
        .modal-content {
            background-color: #fefefe;
            margin: auto;
            padding: 20px;
            border: 1px solid #888;
            border-radius: 6px;
            width: 55%;
            height: 75%;
            position: relative;
            -webkit-animation-name: animatetop;
            -webkit-animation-duration: 0.4s;
            animation-name: animatetop;
            animation-duration: 0.4s
        }

        /* Add Animation */
        @-webkit-keyframes animatetop {
            from {
                top: -300px;
                opacity: 0
            }

            to {
                top: 0;
                opacity: 1
            }
        }

        @keyframes animatetop {
            from {
                top: -300px;
                opacity: 0
            }

            to {
                top: 0;
                opacity: 1
            }
        }

        /* The Close Button */
        .close {
            color: #aaaaaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
        }

        .close:hover,
        .close:focus {
            color: #000;
            text-decoration: none;
            cursor: pointer;
        }

        /* main style */
        #header {
            border-bottom: 1px solid #ccc;
            font-size: 2em;
        }

        #main {
            max-height: 80%;
            overflow-y: auto;
        }

        #input,
        #output {
            display: inline-block;
            vertical-align: top;
        }

        #inputText,
        #outputText {
            width: 360px;
            height: 160px;
            font-size: medium;
            resize: both;
            padding-left: 3px;
        }

        #copyInput,
        #copyOutput {
            display: block;
        }

        #footer {
            position: absolute;
            bottom: 0;
        }
`);}

    // add Math Editor script
    console.log("add Math Editor script");
    var script = document.createElement('script');
    if(1){script.innerHTML = String.raw`
        // Get the modal
        var modal = document.getElementById("myModal");

        // Get the button that opens the modal
        var btn = document.querySelector(".fa-math-editor");

        // Get the <span> element that closes the modal
        var span = document.getElementsByClassName("close")[0];

        // When the user clicks the button, open the modal
//         btn.onclick = function () {
//             update();
//             modal.style.display = "block";
//         }

        // When the user clicks on <span> (x), close the modal
        span.onclick = function () {
            modal.style.display = "none";
        }

        // When the user clicks anywhere outside of the modal, close it
        window.onclick = function (event) {
            if (event.target == modal) {
                modal.style.display = "none";
            }
        }

        // main script
        const inputText = document.querySelector("#inputText");
        const previewImage = document.querySelector("#previewImage");
        const outputText = document.querySelector("#outputText");
        const engineSelect = document.querySelector("#engineSelect");
        const formatSelect = document.querySelector("#formatSelect");

        inputText.addEventListener("input", delay(update, 1200));
        engineSelect.addEventListener("change", update);
        formatSelect.addEventListener("change", update);
        outputText.addEventListener("input", delay(analyse, 1200));
        // update();

        function delay(callback, ms) {
            var timer = 0;
            return function () {
                clearTimeout(timer);
                timer = setTimeout(callback, ms);
            };
        }

        // 编码 input -> url -> output
        function update() {
            const input = inputText.value;
            const engine = engineSelect.value;
            const format = formatSelect.value;

            if (input === "") return;

            console.log("update", input);
            const purifiedURL = input2Url(input, engine);
            previewImage.alt = input;
            if (previewImage.src !== purifiedURL) previewImage.src = purifiedURL;
            outputText.value = Url2Output(purifiedURL, format);
        }

        function input2Url(input, engine) {
            switch (engine) {
                case "math.now.sh":
                    return "https://math.now.sh?from=" + encode(input);
                case "zhihu":
                    return (
                        "https://www.zhihu.com/equation?tex=" +
                         // encode(input)
                         encode("\\bbox[white]{" + input + "}")
                    );
                case "codecogs":
                    return (
                        "https://latex.codecogs.com/svg.latex?" +
                        encode(input.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " "))
                    );
                default:
                    break;
            }
        }

        function encode(s) {
            return encodeURIComponent(s).replace(/[\-\_\.\!\~\*\'\(\)]/g, function (c) {
                return "%" + c.charCodeAt(0).toString(16).toUpperCase();
            });
        }

        function Url2Output(url, format) {
            switch (format) {
                case "Ubb":
                    return "[img]" + url + "[/img]";
                case "Markdown":
                    return "![](" + url + ")";
                case "HTML":
                    return '<img src="' + url + '"/>';
                case "URL":
                    return url;
                default:
                    break;
            }
        }

        // 简单的反向解析 output -> url -> input
        function analyse() {
            const output = outputText.value;
            if (output === "") return;

            console.log("analyse", output);
            const [format, url] = output2Url(output);
            const [engine, input] = url2Input(url);
            console.log(engine, format, input);
            previewImage.alt = input;
            if (previewImage.src !== url) previewImage.src = url;
            inputText.value = input;
        }

        function output2Url(output) {
            if (output.match(/\[img\]/))
                return ["Ubb", output.replace(/\[img\]/, "").replace(/\[\/img\]/, "")];
            if (output.match(/\!\[/))
                return ["Markdown", output.replace(/\!\[.*?\]\(/, "").replace(/\)/, "")];
            if (output.match(/<img src=\"/))
                return ["HTML", output.replace(/<img src=\"/, "").replace(/\"\/>/, "")];
            if (output.match(/https/)) return ["URL", output];
            return ["", ""];
        }

        function url2Input(url) {
            if (url.match(/math\.now\.sh/))
                return [
                    "math.now.sh",
                    decodeURIComponent(url.replace("https://math.now.sh?from=", "")),
                ];
            if (url.match(/www\.zhihu\.com/))
                return [
                    "zhihu",
                    decodeURIComponent(url.replace("https://www.zhihu.com/equation?tex=", ""))
                       .replace("\\bbox[white]{", "")
                       .slice(0, -1),
                ];
            if (url.match(/latex\.codecogs\.com/))
                return [
                    "codecogs",
                    decodeURIComponent(
                        url.replace("https://latex.codecogs.com/svg.latex?", "")
                    ),
                ];
            return ["", ""];
        }

        function copy(e) {
            var copyText = document.getElementById(e);
            copyText.select();
            copyText.setSelectionRange(0, 99999); /* For mobile devices */
            document.execCommand("copy");
        }
`;}
    document.body.appendChild(script);
}

//window.addEventListener('load', addMathEditor, false); // not work?
addMathEditor();

// ubb-editor 添加Math Editor按钮
function addUbbMathEditorButton(){
    'use strict';
    console.log("addMathEditorButton");
    let mathEditorButton = document.querySelector(".fa-math-editor");
    if(!mathEditorButton) mathEditorButton = createUbbMathEditorButton();
    let referenceNode = document.querySelector(".fa.fa-file.ubb-button.ubb-button-icon");
    referenceNode.parentNode.insertBefore(mathEditorButton, referenceNode.nextSibling);
}

function createUbbMathEditorButton(){
    'use strict';
    console.log("createMathEditorButton");
    let mathEditorButton = document.createElement("button");
    mathEditorButton.className = "fa fa-math-editor ubb-button";
    mathEditorButton.type = "button";
    mathEditorButton.title = "Math Editor";
    mathEditorButton.innerText = "Σ";
    mathEditorButton.style = "font-size: larger;";
    mathEditorButton.onclick = function(){
        update();
        var modal = document.getElementById("myModal");
        modal.style.display = "block";
    }
    return mathEditorButton;
}

function removeUbbMathEditorButton(){
    console.log("removeButton");
    let mathEditorButton = document.querySelector(".fa-math-editor");
    if(mathEditorButton) mathEditorButton.remove();
}

// markdown-editor 添加Math Editor按钮
function addMdMathEditorButton(){
    'use strict';
    console.log("addMathEditorButton");
    let mathEditorButton = document.querySelector(".mde-header > ul:nth-child(3) > li:nth-child(4)");
    if(!mathEditorButton) mathEditorButton = createMdMathEditorButton();
    let referenceNode = document.querySelector(".mde-header > ul:nth-child(3) > li:nth-child(3)");
    referenceNode.parentNode.insertBefore(mathEditorButton, referenceNode.nextSibling);
}

function createMdMathEditorButton(){
    'use strict';
    console.log("createMathEditorButton");
    let mathEditorButton = document.createElement("li");
    mathEditorButton.className = "mde-header-item md-math-editor-btn";
    let btn = document.createElement("button");
    btn.innerText = "Σ";
    btn.onclick = function(){
        update();
        var modal = document.getElementById("myModal");
        modal.style.display = "block";
    }
    mathEditorButton.appendChild(btn);
    return mathEditorButton;
}

function removeMdMathEditorButton(){
    console.log("removeButton");
    let mathEditorButton = document.querySelector(".mde-header > ul:nth-child(3) > li:nth-child(4)");
    if(mathEditorButton) mathEditorButton.remove();
}

waitForKeyElements(".fa-smile-o", addUbbMathEditorButton);
waitForKeyElements(".ubb-preview", removeUbbMathEditorButton);
waitForKeyElements(".mde-header", addMdMathEditorButton);