2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

User:AnnAngela/js/wikieditor-highlight.js

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox/Safari:按住“Shift”的同时单击“刷新”,或按“Ctrl-F5”或“Ctrl-R”(Mac为“⌘-R”)
  • Google Chrome:按“Ctrl-Shift-R”(Mac为“⌘-Shift-R”)
  • Internet Explorer:按住“Ctrl”的同时单击“刷新”,或按“Ctrl-F5”
  • Opera:在“工具→首选项”中清除缓存
如果您已登录但该页面出现未登录状态,请尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
添加代码后的本页地址如下:-{R|https://moegirl.icu/User:AnnAngela/js/wikieditor-highlight.js?_=1}-
// From [[Special:固定链接/5337466]]
/* global CodeMirror */
// 本页面大部分内容均直接或间接修改自[[MW:Extension:CodeMirror]]
"use strict";
$(() => (async () => {
    if (!["edit", "submit"].includes(mw.config.get("wgAction")) ||
        mw.config.get("wgPageContentModel") !== "wikitext") {
        return;
    }
    let cm, $doc;
    let state = JSON.parse(localStorage.getItem("wikieditor-codemirror"));
    const $textarea = $("#wpTextbox1");
    const isAdvanced = ["loading", "loaded", "executing", "ready"].includes(mw.loader.getState("ext.wikiEditor"));
    const ns = mw.config.get("wgNamespaceNumber");
    const init = async () => {
        mw.loader.load("//cdn.jsdelivr.net/npm/[email protected]/lib/codemirror.min.css", "text/css");
        mw.loader.load("//cdn.jsdelivr.net/gh/bhsd-harry/[email protected]/otherwiki/mediawiki.min.css", "text/css");
        mw.loader.addStyleTag(`
#wikiEditor-ui-toolbar .menu {
    position: relative;
    z-index: 5;
}

.CodeMirror pre {
    font-family: Monaco, Menlo, "Ubuntu Mono", Consolas, "source-code-pro", monospace;
}

.skin-vector #wpTextbox1:not([readonly])+.CodeMirror {
    font-size: 13px;
    line-height: 1.5;
}

#wpTextbox1[readonly]+.CodeMirror,
.skin-minerva #wpTextbox1+.CodeMirror {
    font-size: 16px;
    line-height: 1.2;
    border: 1px solid #c8ccd1;
}

.cm-matchingbracket,
.cm-nonmatchingbracket {
    margin: -1px;
    border: 1px solid #c0c0c0;
}

.cm-matchingbracket {
    background-color: #0b04;
}

.cm-nonmatchingbracket {
    background-color: #ec14;
}
            `);
        const $search = $(".group-search a");
        const addon = () => {
            const Pos = CodeMirror.Pos, defaults = {
                bracketRegex: /[{}[\]]/,
                maxScanLineLength: 3000,
                maxScanLines: 100,
                afterCursor: false,
                strict: false,
                maxHighlightLineLength: 1000,
                highlightNonMatching: true,
            };
            const matching = {
                "[": "]>", "]": "[<", "{": "}>", "}": "{<",
            };
            const pair = {
                "[": /[[\]]/, "]": /[[\]]/, "{": /[{}]/, "}": /[{}]/,
            };
            const bracketRegex = (config) => {
                return config && config.bracketRegex;
            };
            const scanForBracket = (cm, where, dir, style, config) => {
                const maxScanLen = config && config.maxScanLineLength;
                const maxScanLines = config && config.maxScanLines;
                const stack = [];
                const re = bracketRegex(config);
                const lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lineCount()) : Math.max(-1, where.line - maxScanLines);
                let lineNo = where.line;
                for (; lineNo !== lineEnd; lineNo += dir) {
                    const line = cm.getLine(lineNo);
                    if (!line || line.length > maxScanLen) {
                        continue;
                    }
                    const end = dir > 0 ? line.length : -1;
                    let pos = dir > 0 ? 0 : line.length - 1;
                    if (lineNo === where.line) {
                        pos = where.ch - (dir < 0 ? 1 : 0);
                    }
                    for (; pos !== end; pos += dir) {
                        const ch = line.charAt(pos);
                        if (re.test(ch) && (style === undefined ||
                            (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") === (style || ""))) {
                            const match = matching[ch];
                            if (match && match.charAt(1) === ">" === dir > 0) {
                                stack.push(ch);
                            }
                            else if (stack.length === 0) {
                                return {
                                    pos: Pos(lineNo, pos),
                                    ch: ch,
                                };
                            }
                            else {
                                stack.pop();
                            }
                        }
                    }
                }
                return lineNo - dir === (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
            };
            const findMatchingBracket = (cm, where, config) => {
                const line = cm.getLine(where.line);
                const re = bracketRegex(config);
                const afterCursor = config && config.afterCursor;
                const pos = !afterCursor && where.ch > 0 ? where.ch - 1 : where.ch;
                const key = line.charAt(pos);
                const match = re.test(key) && matching[key];
                if (!match) {
                    return null;
                }
                const dir = match.charAt(1) === ">" ? 1 : -1;
                if (config && config.strict && dir > 0 !== (pos === where.ch)) {
                    return null;
                }
                const newConfig = Object.assign({}, config, {
                    bracketRegex: pair[key],
                });
                const style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
                const found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, newConfig);
                if (found === null) {
                    return null;
                }
                return {
                    from: Pos(where.line, pos),
                    to: found && found.pos,
                    match: found && found.ch === match.charAt(0),
                    forward: dir > 0,
                };
            };
            const markChar = (cm, pos, style) => cm.markText(pos, Pos(pos.line, pos.ch + 1), {
                className: style,
            });
            const matchBrackets = (cm, autoclear, _config) => {
                const config = _config || cm.state.matchBrackets;
                const maxHighlightLen = config && config.maxHighlightLineLength;
                const highlightNonMatching = config && config.highlightNonMatching;
                const marks = [];
                cm.listSelections().forEach((range) => {
                    const match = range.empty() && findMatchingBracket(cm, range.head, config);
                    if (match && (match.match || highlightNonMatching) &&
                        cm.getLine(match.from.line).length <= maxHighlightLen) {
                        const style = match.match ? "cm-matchingbracket" : "cm-nonmatchingbracket";
                        marks.push(markChar(cm, match.from, style));
                        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) {
                            marks.push(markChar(cm, match.to, style));
                        }
                    }
                });
                if (marks.length) {
                    const clear = () => {
                        cm.operation(() => {
                            marks.forEach((mark) => {
                                mark.clear();
                            });
                        });
                    };
                    if (autoclear) {
                        setTimeout(clear, 800);
                    }
                    else {
                        return clear;
                    }
                }
            };
            const doMatchBrackets = (cm) => {
                cm.operation(() => {
                    if (cm.state.matchBrackets.currentlyHighlighted) {
                        cm.state.matchBrackets.currentlyHighlighted();
                        cm.state.matchBrackets.currentlyHighlighted = null;
                    }
                    cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false);
                });
            };
            const clearHighlighted = (cm) => {
                if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
                    cm.state.matchBrackets.currentlyHighlighted();
                    cm.state.matchBrackets.currentlyHighlighted = null;
                }
            };
            CodeMirror.defineOption("matchBrackets", false, (cm, val, old) => {
                if (old && old !== CodeMirror.Init) {
                    cm.off("cursorActivity", doMatchBrackets);
                    cm.off("focus", doMatchBrackets);
                    cm.off("blur", clearHighlighted);
                    clearHighlighted(cm);
                }
                if (val) {
                    cm.state.matchBrackets = $.extend({}, defaults, typeof val === "object" ? val : {});
                    cm.on("cursorActivity", doMatchBrackets);
                    cm.on("focus", doMatchBrackets);
                    cm.on("blur", clearHighlighted);
                }
            });
            CodeMirror.defineExtension("matchBrackets", (config) => {
                matchBrackets(CodeMirror, true, config);
            });
        };
        await $.get({
            dataType: "script",
            cache: true,
            url: "//cdn.jsdelivr.net/npm/[email protected]/lib/codemirror.min.js",
        });
        addon();
        await $.get({
            dataType: "script",
            cache: true,
            url: "//cdn.jsdelivr.net/gh/bhsd-harry/[email protected]/otherwiki/mediawiki.min.js",
        });
        if (ns === 274) {
            await Promise.all([
                $.get({
                    dataType: "script",
                    cache: true,
                    url: "//cdn.jsdelivr.net/npm/[email protected]/mode/javascript/javascript.min.js",
                }), $.get({
                    dataType: "script",
                    cache: true,
                    url: "//cdn.jsdelivr.net/npm/[email protected]/mode/css/css.min.js",
                }),
            ]);
        }
        const config = await $.get({
            dataType: "json",
            cache: true,
            url: "//cdn.jsdelivr.net/gh/bhsd-harry/[email protected]/otherwiki/gadget-CodeMirror.json",
        });
        if (ns === 274) {
            $.extend(config.tags, {
                script: true,
                style: true,
            });
            $.extend(config.tagModes, {
                script: "javascript",
                style: "css",
            });
        }
        window.mwConfig = config;
        if (isAdvanced) {
            cm = new CodeMirror($textarea.parent()[0], {
                mode: "text/mediawiki",
                mwConfig: window.mwConfig,
                lineWrapping: true,
                lineNumbers: true,
                readOnly: $textarea.prop("readonly"),
                matchBrackets: true,
            });
        } else {
            cm = CodeMirror.fromTextArea($textarea[0], {
                mode: "text/mediawiki",
                mwConfig: window.mwConfig,
                lineWrapping: true,
                lineNumbers: true,
                readOnly: $textarea.prop("readonly"),
                matchBrackets: true,
            });
            cm.setSize(null, $textarea.height());
        }
        mw.hook("wiki-codemirror").fire(cm);
        $doc = $(cm.getWrapperElement());
        $.valHooks.textarea = {
            get: (ele) => {
                return ele === $textarea[0] && state ? cm.getValue() : ele.value;
            },
            set: (ele, val) => {
                ele === $textarea[0] && state ? cm.setValue(val) : ele.value = val;
            },
        };
        if (mw.loader.getState("jquery.ui.resizable") === "ready") {
            $doc.resizable({
                handles: "s",
            });
        }
        if ($search.length === 0) {
            return;
        }
        cm.addKeyMap({
            "Ctrl-F": () => {
                $search.trigger("click");
            }, "Cmd-F": () => {
                $search.trigger("click");
            },
        });
    };
    if (state === null || state === undefined || !isAdvanced) {
        state = true;
    }
    if (!isAdvanced) {
        init();
        return;
    }
    const $form = $(document.editform);
    const btn = new OO.ui.ButtonWidget({
        classes: ["tool"],
        icon: "highlight",
        framed: false,
        title: "代码高亮开关",
    }).on("click", () => {
        if (cm) {
            $doc.toggle();
            update();
        } else {
            initAndUpdate();
        }
    });
    const fn = {
        getSelection: () => {
            return cm.getSelection();
        },
        setSelection: function (options) {
            cm.setSelection(cm.posFromIndex(options.start), cm.posFromIndex(options.end));
            cm.focus();
            return this;
        },
        getCaretPosition: (options) => {
            const caretPos = cm.indexFromPos(cm.getCursor("from")),
                endPos = cm.indexFromPos(cm.getCursor("to"));
            if (options.startAndEnd) {
                return [caretPos, endPos];
            }
            return caretPos;
        },
        scrollToCaretPosition: function () {
            cm.scrollIntoView();
            return this;
        },
    };
    const submit = () => {
        $textarea[0].value = cm.getValue();
    };
    const shared = () => {
        btn.$element.toggleClass("tool-active");
        if (state) {
            cm.setValue($textarea[0].value);
            cm.setSize(null, $textarea.height());
        } else {
            $textarea[0].value = cm.getValue();
        }
        $textarea.toggle();
        $form[state ? "on" : "off"]("submit", submit);
        if ($textarea.textSelection) {
            $textarea.textSelection(state ? "register" : "unregister", fn);
        }
    };
    const update = () => {
        state = !state;
        localStorage.setItem("wikieditor-codemirror", state);
        shared();
    };
    const initAndUpdate = async () => {
        await init();
        update();
    };
    const group = $("#wikiEditor-section-main > .group-insert")[0];
    $textarea.on("wikiEditor-toolbar-doneInitialSections", () => {
        btn.$element.appendTo("#wikiEditor-section-main > .group-insert");
    });
    if (group && !group.contains(btn.$element[0])) {
        $textarea.trigger("wikiEditor-toolbar-doneInitialSections");
    }
    if (state) {
        await mw.loader.using("ext.wikiEditor");
        await init();
        shared();
    }
})());
//</nowiki>