2023年政策修订增补工作正在进行中,欢迎参与!
User:AnnAngela/js/wikieditor-highlight.js
< User:AnnAngela | 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>