MediaWiki:Gadget-DotsSyntaxHighlighter.js:修订间差异
外观
小无编辑摘要 |
小无编辑摘要 |
||
| (未显示同一用户的3个中间版本) | |||
| 第124行: | 第124行: | ||
} | } | ||
var | var e = mw.config.get("wgUrlProtocols"), | ||
r = "&(?:(?:n(?:bsp|dash)|m(?:dash|inus)|lt|e[mn]sp|thinsp|amp|quot|gt|shy|zwnj|lrm|rlm|Alpha|Beta|Epsilon|Zeta|Eta|Iota|Kappa|[Mm]u|micro|Nu|[Oo]micron|[Rr]ho|Tau|Upsilon|Chi)|#x[0-9a-fA-F]+);\\n*", | |||
t = "\\[(?:\\[|(?:" + e + "))|\\{(?:\\{\\{?|\\|)|<(?:[:A-Z_a-z][-:\\w]*(?=/?>| |\\n)|!--[\\s\\S]*?-->\\n*)|(?:" + e + ")[^\\s\"<>[\\]{-}]*[^\\s\",\\.:;<>[\\]{-}]\\n*|^(?:=|[*#:;]+\\n*|-{4,}\\n*)|\\\\'\\\\'(?:\\\\')?|~{3,5}\\n*|" + r, | |||
r = "&(?:(?:n(?:bsp|dash)|m(?:dash|inus)|lt|e[mn]sp|thinsp|amp|quot|gt|shy| | l = null, g = null, d = null, c = "", y = null, p = null, f = null, b = -1, | ||
t = "\\[(?:\\[|(?:" + e + "))|\\{(?:\\{\\{?|\\|)|<(?:[:A-Z_a-z][-:\\w]*(?=/?>| |\\n)|!--[ | lastText = ""; // 记录上一次的文本内容 | ||
var regexCache = {}; | var regexCache = {}; | ||
| 第135行: | 第135行: | ||
var cacheKey = e; | var cacheKey = e; | ||
if (!regexCache[cacheKey]) { | if (!regexCache[cacheKey]) { | ||
regexCache[cacheKey] = new RegExp("(" + e + ")\\n*|" + t, "gm"); | try { | ||
regexCache[cacheKey] = new RegExp("(" + e + ")\\n*|" + t, "gm"); | |||
} catch (err) { | |||
console.error("Syntax highlighter: 正则表达式构建失败", err, "e:", e); | |||
regexCache[cacheKey] = new RegExp(t, "gm"); // 使用默认正则降级 | |||
} | |||
} | } | ||
return regexCache[cacheKey]; | return regexCache[cacheKey]; | ||
| 第216行: | 第221行: | ||
if (isIncrementalUpdate && lastHighlightState) { | if (isIncrementalUpdate && lastHighlightState) { | ||
// 方法1:智能边界定位(当前实现)- 基于CSS状态复用 | // 方法1:智能边界定位(当前实现)- 基于CSS状态复用 | ||
var | var startMarker = "#s" + (Math.floor(changeStart / 50)) + "::after"; | ||
var markerIndex = lastHighlightState.indexOf(startMarker); | |||
if (markerIndex !== -1) { | |||
var prefixCSS = lastHighlightState.substring(0, markerIndex); | |||
// 确保CSS语法完整 | |||
if (prefixCSS.endsWith("'}")) { | |||
o = prefixCSS; | |||
r = Math.floor(changeStart / 50); | |||
m = changeStart; | |||
} | |||
} | } | ||
| 第234行: | 第244行: | ||
console.log("精确增量更新:从位置", m, "开始解析"); | console.log("精确增量更新:从位置", m, "开始解析"); | ||
} | } | ||
} else { | |||
// 如果不是增量更新或lastHighlightState为null,确保从文本开头开始解析 | |||
m = 0; | |||
o = ""; | |||
r = 0; | |||
} | } | ||
| 第286行: | 第301行: | ||
var MAX_STACK_DEPTH = 1000; // 最大栈深度限制 | var MAX_STACK_DEPTH = 1000; // 最大栈深度限制 | ||
while (stack.length > 0 && | while (stack.length > 0 && stack.length < MAX_STACK_DEPTH) { | ||
var currentTask = stack.pop(); | var currentTask = stack.pop(); | ||
var text = currentTask.text; | var text = currentTask.text; | ||
| 第297行: | 第311行: | ||
var italicState = currentTask.italicState; | var italicState = currentTask.italicState; | ||
var context = currentTask.context; | var context = currentTask.context; | ||
// 处理当前解析任务 | // 处理当前解析任务 | ||
| 第578行: | 第590行: | ||
context: context | context: context | ||
}); | }); | ||
// 移除break语句,让循环继续处理当前任务的剩余文本 | |||
} | } | ||
| 第586行: | 第598行: | ||
m = text.length; | m = text.length; | ||
} | } | ||
} | } | ||
// | // 检查栈深度限制:使用stack.length来反映实际栈大小 | ||
if ( | if (stack.length >= MAX_STACK_DEPTH) { | ||
console.error('Stack depth exceeded maximum limit:', | console.error('Stack depth exceeded maximum limit:', stack.length); | ||
// 退出循环,避免无限递归 - 通过设置stack为空数组来终止循环 | |||
stack = []; | |||
} | } | ||
| 第684行: | 第699行: | ||
return; | return; | ||
} | } | ||
// 全局变量安全检查:确保syntaxHighlighterConfig和syntaxHighlighterSiteConfig存在 | |||
window.syntaxHighlighterConfig = window.syntaxHighlighterConfig || {}; | |||
window.syntaxHighlighterSiteConfig = window.syntaxHighlighterSiteConfig || {}; | |||
var e, t, i; | var e, t, i; | ||
function o(e, t, i) { | function o(e, t, i) { | ||
void 0 === syntaxHighlighterConfig[e] && (syntaxHighlighterConfig[e] = syntaxHighlighterSiteConfig[e]); | void 0 === syntaxHighlighterConfig[e] && (syntaxHighlighterConfig[e] = syntaxHighlighterSiteConfig[e] || t); | ||
"normal" == syntaxHighlighterConfig[e] ? syntaxHighlighterConfig[e] = t : void 0 !== syntaxHighlighterConfig[e] || (void 0 !== syntaxHighlighterConfig.defaultColor && i ? syntaxHighlighterConfig[e] = syntaxHighlighterConfig.defaultColor : syntaxHighlighterConfig[e] = t); | "normal" == syntaxHighlighterConfig[e] ? syntaxHighlighterConfig[e] = t : void 0 !== syntaxHighlighterConfig[e] || (void 0 !== syntaxHighlighterConfig.defaultColor && i ? syntaxHighlighterConfig[e] = syntaxHighlighterConfig.defaultColor : syntaxHighlighterConfig[e] = t); | ||
} | } | ||
| 第720行: | 第739行: | ||
// 添加页面卸载时的资源清理逻辑 | // 添加页面卸载时的资源清理逻辑 | ||
function cleanup() { | |||
// 清理定时器 | |||
if (y) { | if (y) { | ||
clearInterval(y); | clearInterval(y); | ||
y = null; | y = null; | ||
} | } | ||
// 清理观察者 | |||
[p, f].forEach(observer => observer?.disconnect()); | |||
// 清理事件监听 | |||
if (g) { | if (g) { | ||
g.removeEventListener("input", E); | g.removeEventListener("input", E); | ||
| 第738行: | 第753行: | ||
g.removeEventListener("scroll", F); | g.removeEventListener("scroll", F); | ||
} | } | ||
}); | // 清理DOM元素 | ||
l?.parentNode?.removeChild(l); | |||
d?.parentNode?.parentNode?.removeChild(d.parentNode); // 移除style元素 | |||
// 重置变量 | |||
l = g = d = c = y = p = f = b = null; | |||
} | |||
// 双重事件监听确保清理 | |||
window.addEventListener('beforeunload', cleanup); | |||
window.addEventListener('unload', cleanup); | |||
}); | }); | ||
2025年11月20日 (四) 23:41的最新版本
mw.loader.using("jquery.client", function() {
"use strict";
// 安全的配置访问函数
function safeConfigAccess(config, property, defaultValue) {
if (!config || !config[property]) return defaultValue;
return config[property];
}
// CSS颜色值验证函数
function isValidCSSColor(colorValue) {
if (!colorValue || typeof colorValue !== 'string') return false;
// 移除前后空格
colorValue = colorValue.trim();
// 检查常见CSS颜色格式
var colorFormats = [
// 十六进制颜色 (#FFF, #FFFFFF)
/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/,
// RGB颜色 (rgb(255,255,255))
/^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/,
// RGBA颜色 (rgba(255,255,255,0.5))
/^rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(0|1|0\.\d+)\s*\)$/,
// HSL颜色 (hsl(120,100%,50%))
/^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/,
// HSLA颜色 (hsla(120,100%,50%,0.5))
/^hsla\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*,\s*(0|1|0\.\d+)\s*\)$/,
// 命名颜色 (red, blue, transparent)
/^(transparent|inherit|initial|unset|currentColor|revert|revert-layer)$/i
];
// 检查是否为有效的CSS命名颜色
var namedColors = [
'black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia',
'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua',
'orange', 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige',
'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood', 'cadetblue',
'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen',
'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite',
'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'greenyellow',
'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki',
'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue',
'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen',
'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen',
'linen', 'magenta', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid',
'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue',
'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna',
'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen',
'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat',
'whitesmoke', 'yellowgreen'
];
// 检查格式匹配
for (var i = 0; i < colorFormats.length; i++) {
if (colorFormats[i].test(colorValue)) {
return true;
}
}
// 检查命名颜色
if (namedColors.indexOf(colorValue.toLowerCase()) !== -1) {
return true;
}
return false;
}
// 安全的颜色配置访问函数
function safeColorAccess(config, property, defaultValue) {
var colorValue = safeConfigAccess(config, property, defaultValue);
// 如果颜色值为"normal",返回默认值
if (colorValue === "normal") {
return defaultValue;
}
// 验证颜色值有效性
if (colorValue && !isValidCSSColor(colorValue)) {
console.warn('Invalid CSS color value for ' + property + ': "' + colorValue + '", using default: "' + defaultValue + '"');
return defaultValue;
}
return colorValue || defaultValue;
}
// 查找匹配的标签结束位置,处理嵌套标签
function findMatchingTagEnd(text, startPos, tagName) {
var openTag = "<" + tagName + ">";
var closeTag = "</" + tagName + ">";
var stack = 1; // 当前已经有一个开始标签
var pos = startPos;
while (pos < text.length) {
var nextOpen = text.indexOf(openTag, pos);
var nextClose = text.indexOf(closeTag, pos);
// 如果找不到闭合标签,返回文本末尾
if (nextClose === -1) return text.length;
// 如果下一个开始标签在闭合标签之前,增加栈深度
if (nextOpen !== -1 && nextOpen < nextClose) {
stack++;
pos = nextOpen + openTag.length;
} else {
stack--;
pos = nextClose + closeTag.length;
// 如果栈为空,返回当前闭合标签的结束位置
if (stack === 0) return pos;
}
}
return text.length; // 如果找不到匹配的闭合标签,返回文本末尾
}
var e = mw.config.get("wgUrlProtocols"),
r = "&(?:(?:n(?:bsp|dash)|m(?:dash|inus)|lt|e[mn]sp|thinsp|amp|quot|gt|shy|zwnj|lrm|rlm|Alpha|Beta|Epsilon|Zeta|Eta|Iota|Kappa|[Mm]u|micro|Nu|[Oo]micron|[Rr]ho|Tau|Upsilon|Chi)|#x[0-9a-fA-F]+);\\n*",
t = "\\[(?:\\[|(?:" + e + "))|\\{(?:\\{\\{?|\\|)|<(?:[:A-Z_a-z][-:\\w]*(?=/?>| |\\n)|!--[\\s\\S]*?-->\\n*)|(?:" + e + ")[^\\s\"<>[\\]{-}]*[^\\s\",\\.:;<>[\\]{-}]\\n*|^(?:=|[*#:;]+\\n*|-{4,}\\n*)|\\\\'\\\\'(?:\\\\')?|~{3,5}\\n*|" + r,
l = null, g = null, d = null, c = "", y = null, p = null, f = null, b = -1,
lastText = ""; // 记录上一次的文本内容
var regexCache = {};
function C(e) {
var cacheKey = e;
if (!regexCache[cacheKey]) {
try {
regexCache[cacheKey] = new RegExp("(" + e + ")\\n*|" + t, "gm");
} catch (err) {
console.error("Syntax highlighter: 正则表达式构建失败", err, "e:", e);
regexCache[cacheKey] = new RegExp(t, "gm"); // 使用默认正则降级
}
}
return regexCache[cacheKey];
}
var x = new RegExp(t, "gm"),
k = C("]][a-zA-Z]*"),
v = C("]"),
w = C("}}}"),
z = C("}}"),
H = C("\\|}"),
S = C("\\n"),
T = {},
L = {};
// 增量更新状态管理
var lastHighlightState = null,
lastSpanCount = 0,
spanCache = {};
function E() {
try {
var currentText = g.value;
// 增量更新检查:如果文本内容没有变化,直接返回
if (currentText === lastText) {
return;
}
// 增量更新优化:计算文本变化范围并用于局部解析
var changeStart = 0, changeEnd = currentText.length;
var isIncrementalUpdate = false;
if (lastText && currentText !== lastText) {
// 找到文本变化的起始位置
for (changeStart = 0; changeStart < Math.min(currentText.length, lastText.length); changeStart++) {
if (currentText[changeStart] !== lastText[changeStart]) break;
}
// 找到文本变化的结束位置(从末尾开始比较)
for (changeEnd = 0; changeEnd < Math.min(currentText.length, lastText.length); changeEnd++) {
if (currentText[currentText.length - 1 - changeEnd] !== lastText[lastText.length - 1 - changeEnd]) break;
}
changeEnd = currentText.length - changeEnd;
// 判断是否适合增量更新:变化范围较小且不在语法结构边界
isIncrementalUpdate = (changeEnd - changeStart) < Math.min(currentText.length * 0.3, 200) &&
changeStart > 0 && changeEnd < currentText.length;
}
lastText = currentText;
// 确保b变量有正确的初始值
if (b === -1) {
// 如果是第一次运行,需要初始化b为当前span数量
var existingSpans = l.querySelectorAll('span[id^="s"]');
b = existingSpans.length;
}
} catch (error) {
// 捕获正则匹配和DOM操作中的异常
console.error('Syntax highlighter error:', error);
return;
}
var i, h = (c = currentText).replace(/['\\]/g, "\\$&") + "\\n",
m = 0,
o = "",
r = 0;
// 最大递归深度限制
var MAX_RECURSION_DEPTH = 1000;
function u(e, t) {
t != i && (o += "'}", t && (o += "#s" + r + "{background-color:" + t + "}"), o += "#s" + r + "::after{content:'", i = t, ++r), o += e;
}
var e = Date.now();
// 精确增量更新:基于changeStart和changeEnd的局部解析
if (isIncrementalUpdate && lastHighlightState) {
// 方法1:智能边界定位(当前实现)- 基于CSS状态复用
var startMarker = "#s" + (Math.floor(changeStart / 50)) + "::after";
var markerIndex = lastHighlightState.indexOf(startMarker);
if (markerIndex !== -1) {
var prefixCSS = lastHighlightState.substring(0, markerIndex);
// 确保CSS语法完整
if (prefixCSS.endsWith("'}")) {
o = prefixCSS;
r = Math.floor(changeStart / 50);
m = changeStart;
}
}
// 方法2:语法边界检测(高级优化)- 需要修改递归逻辑
// 检测变化点前后的语法结构,确保解析上下文正确
var syntaxBoundaryStart = findSyntaxBoundary(currentText, changeStart, -1); // 向前查找语法边界
var syntaxBoundaryEnd = findSyntaxBoundary(currentText, changeEnd, 1); // 向后查找语法边界
if (syntaxBoundaryStart >= 0 && syntaxBoundaryEnd <= currentText.length) {
// 如果找到合适的语法边界,使用精确的局部解析
m = Math.max(0, syntaxBoundaryStart);
// 这里需要修改递归解析函数以支持从指定位置开始解析
console.log("精确增量更新:从位置", m, "开始解析");
}
} else {
// 如果不是增量更新或lastHighlightState为null,确保从文本开头开始解析
m = 0;
o = "";
r = 0;
}
// 语法边界检测辅助函数 - 增强wikitext专属边界检测
function findSyntaxBoundary(text, position, direction) {
var boundary = position;
var maxSearch = 100; // 最大搜索范围
// wikitext专属语法边界列表(按优先级排序)
var wikitextBoundaries = [
// 多字符边界(高优先级)
'[[', ']]', '{{', '}}', '{{{', '}}}', '{|', '|}', '<!--', '-->',
// 单字符边界
'\n', '>', '}', ']', '=', '<', '|', ';', '*', '#', ':', '-', '~', '\\', '&'
];
for (var i = 0; i < maxSearch; i++) {
var currentPos = boundary + (direction * i);
if (currentPos < 0 || currentPos >= text.length) break;
// 优先检测多字符wikitext边界
for (var j = 0; j < wikitextBoundaries.length; j++) {
var boundaryStr = wikitextBoundaries[j];
if (boundaryStr.length > 1) {
// 多字符边界检测
if (currentPos + boundaryStr.length <= text.length &&
text.substring(currentPos, currentPos + boundaryStr.length) === boundaryStr) {
return currentPos + (direction > 0 ? boundaryStr.length : 0);
}
} else {
// 单字符边界检测
if (text.charAt(currentPos) === boundaryStr) {
return currentPos + (direction > 0 ? 1 : 0);
}
}
}
}
return direction > 0 ? text.length : 0;
}
// 基于显式调用栈的迭代解析器
var stack = [{
text: h,
pos: m,
regex: x,
color: "",
boldState: false,
italicState: false,
context: "root"
}];
var MAX_STACK_DEPTH = 1000; // 最大栈深度限制
while (stack.length > 0 && stack.length < MAX_STACK_DEPTH) {
var currentTask = stack.pop();
var text = currentTask.text;
var pos = currentTask.pos;
var regex = currentTask.regex;
var color = currentTask.color;
var boldState = currentTask.boldState;
var italicState = currentTask.italicState;
var context = currentTask.context;
// 处理当前解析任务
var match;
regex.lastIndex = pos;
while ((match = regex.exec(text)) !== null) {
if (match[1]) {
// 匹配到结束标记,处理剩余文本并返回
u(text.substring(pos, regex.lastIndex), color);
m = regex.lastIndex;
break;
}
var matchStart = regex.lastIndex - match[0].length;
// 处理匹配前的文本
if (pos < matchStart) {
u(text.substring(pos, matchStart), color);
}
pos = regex.lastIndex;
// 根据匹配内容处理不同的语法元素
switch (match[0].charAt(0)) {
case "[":
if (match[0].charAt(1) === "[") {
// 内部链接 [[...]]
u("[[", safeColorAccess(syntaxHighlighterConfig, 'wikilinkColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: k,
color: safeColorAccess(syntaxHighlighterConfig, 'wikilinkColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "wikilink"
});
} else {
// 外部链接 [...]
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'externalLinkColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: v,
color: safeColorAccess(syntaxHighlighterConfig, 'externalLinkColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "externalLink"
});
}
break;
case "{":
if (match[0].charAt(1) === "{") {
if (match[0].length === 3) {
// 参数 {{{...}}}
u("{{{", safeColorAccess(syntaxHighlighterConfig, 'parameterColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: w,
color: safeColorAccess(syntaxHighlighterConfig, 'parameterColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "parameter"
});
} else {
// 模板 {{...}}
u("{{", safeColorAccess(syntaxHighlighterConfig, 'templateColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: z,
color: safeColorAccess(syntaxHighlighterConfig, 'templateColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "template"
});
}
} else {
// 表格 {|...|}
u("{|", safeColorAccess(syntaxHighlighterConfig, 'tableColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: H,
color: safeColorAccess(syntaxHighlighterConfig, 'tableColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "table"
});
}
break;
case "<":
if (match[0].charAt(1) === "!") {
// 注释 <!-- ... -->
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'commentColor', color) || color);
} else {
// HTML标签
var tagEnd = text.indexOf(">", pos) + 1;
if (tagEnd === 0) {
u("<", color);
pos = pos - match[0].length + 1;
} else {
var tagName = match[0].substring(1);
if (text.charAt(tagEnd - 2) !== "/") {
if (safeConfigAccess(syntaxHighlighterConfig, 'voidTags', []).indexOf(tagName) === -1) {
if (safeConfigAccess(syntaxHighlighterConfig, 'sourceTags', []).indexOf(tagName) !== -1) {
// 源代码标签
var closeTag = "</" + tagName + ">";
var tagEndPos = findMatchingTagEnd(text, pos, tagName);
u(text.substring(pos - match[0].length, tagEndPos), safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color);
pos = tagEndPos;
} else if (safeConfigAccess(syntaxHighlighterConfig, 'nowikiTags', []).indexOf(tagName) !== -1) {
// nowiki标签
u(text.substring(pos - match[0].length, tagEnd), safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color);
pos = tagEnd;
stack.push({
text: text,
pos: pos,
regex: L[tagName] || (L[tagName] = C("</" + tagName + ">")),
color: safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "tag"
});
} else {
// 普通标签
u(text.substring(pos - match[0].length, tagEnd), safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color);
pos = tagEnd;
T[tagName] = T[tagName] || C("</" + tagName + ">");
stack.push({
text: text,
pos: pos,
regex: T[tagName],
color: safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "tag"
});
}
} else {
u(text.substring(pos - match[0].length, tagEnd), safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color);
pos = tagEnd;
}
} else {
u(text.substring(pos - match[0].length, tagEnd), safeColorAccess(syntaxHighlighterConfig, 'tagColor', color) || color);
pos = tagEnd;
}
}
}
break;
case "=":
if (/[^=]=+$/.test(text.substring(pos, text.indexOf("\\n", pos)))) {
// 标题
u("=", safeColorAccess(syntaxHighlighterConfig, 'headingColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: S,
color: safeColorAccess(syntaxHighlighterConfig, 'headingColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "heading"
});
} else {
u("=", color);
}
break;
case "*":
case "#":
case ":":
// 列表和缩进
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'listOrIndentColor', color) || color);
break;
case ";":
// 定义列表
u(";", safeColorAccess(syntaxHighlighterConfig, 'headingColor', color) || color);
stack.push({
text: text,
pos: pos,
regex: S,
color: safeColorAccess(syntaxHighlighterConfig, 'headingColor', color) || color,
boldState: boldState,
italicState: italicState,
context: "heading"
});
break;
case "-":
// 水平线
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'hrColor', color) || color);
break;
case "\\":
// 粗体和斜体
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'boldOrItalicColor', color) || color);
if (match[0].length === 6) {
// 处理粗体
if (boldState) {
if (!italicState) {
// 结束粗体
boldState = false;
}
} else {
if (italicState) {
// 开始粗体(已有斜体)
boldState = true;
} else {
// 开始粗体
stack.push({
text: text,
pos: pos,
regex: x,
color: safeColorAccess(syntaxHighlighterConfig, 'boldOrItalicColor', color) || color,
boldState: true,
italicState: false,
context: "bold"
});
}
}
} else {
// 处理斜体
if (italicState) {
if (!boldState) {
// 结束斜体
italicState = false;
}
} else {
if (boldState) {
// 开始斜体(已有粗体)
italicState = true;
} else {
// 开始斜体
stack.push({
text: text,
pos: pos,
regex: x,
color: safeColorAccess(syntaxHighlighterConfig, 'boldOrItalicColor', color) || color,
boldState: false,
italicState: true,
context: "italic"
});
}
}
}
break;
case "&":
// HTML实体
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'entityColor', color) || color);
break;
case "~":
// 签名
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'signatureColor', color) || color);
break;
default:
// 默认处理
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'externalLinkColor', color) || color);
}
// 将当前任务重新推入栈中,继续处理剩余文本
stack.push({
text: text,
pos: pos,
regex: regex,
color: color,
boldState: boldState,
italicState: italicState,
context: context
});
// 移除break语句,让循环继续处理当前任务的剩余文本
}
// 如果当前任务处理完毕,处理剩余文本
if (match === null && pos < text.length) {
u(text.substring(pos), color);
m = text.length;
}
}
// 检查栈深度限制:使用stack.length来反映实际栈大小
if (stack.length >= MAX_STACK_DEPTH) {
console.error('Stack depth exceeded maximum limit:', stack.length);
// 退出循环,避免无限递归 - 通过设置stack为空数组来终止循环
stack = [];
}
// 处理剩余文本
if (m < h.length) {
u(h.substring(m), "");
}
var t = Date.now();
if (t - e > safeConfigAccess(syntaxHighlighterConfig, 'timeout', 20)) {
clearInterval(y);
g.removeEventListener("input", E);
g.removeEventListener("scroll", R);
g.removeEventListener("scroll", F);
p.disconnect();
f.disconnect();
d.nodeValue = "";
var a = {
zh: "此页面上的语法突出显示被禁用,因为它花费的时间太长。允许的突出显示时间最长为$1毫秒,而您的计算机则为$2毫秒。请尝试关闭一些标签和程序,然后单击\"显示预览\"或\"显示更改\"。如果不起作用,请尝试使用其他网页浏览器,如果还不起作用,请尝试使用速度更快的计算机。",
"zh-hans": "此页面上的语法突出显示被禁用,因为它花费的时间太长。允许的突出显示时间最长为$1毫秒,而您的计算机则为$2毫秒。请尝试关闭一些标签和程序,然后单击\"显示预览\"或\"显示更改\"。如果不起作用,请尝试使用其他网页浏览器,如果还不起作用,请尝试使用速度更快的计算机。",
"zh-hant": "此頁面上的語法突出顯示被禁用,因為它花費的時間太長。允許的突出顯示時間最長為$1毫秒,而您的計算機則為$2毫秒。請嘗試關閉一些標籤和程序,然後單擊\"顯示預覽\"或\"顯示更改\"。如果不起作用,請嘗試使用其他網頁瀏覽器,如果还不起作用,請嘗試使用速度更快的計算機。"
};
var n = mw.config.get("wgUserLanguage");
a = a[n] || a["zh-hant"] || a.zh;
return g.style.backgroundColor = "", g.style.marginTop = "0", l.removeAttribute("dir"), l.removeAttribute("lang"), l.setAttribute("style", "color:red; font-size:small"), void (l.textContent = a.replace("$1", safeConfigAccess(syntaxHighlighterConfig, 'timeout', 20)).replace("$2", t - e));
}
// 智能增量更新:只更新变化的CSS样式部分
var newCSS = o.substring(2).replace(/\\n/g, "\\A ") + "'}";
newCSS += "#wpTextbox0>span::after{visibility:hidden}";
// 如果CSS内容没有变化,避免不必要的DOM操作
if (lastHighlightState !== newCSS) {
d.nodeValue = newCSS;
lastHighlightState = newCSS;
}
// 智能span元素管理:只创建或删除需要的元素
if (b < r) {
// 需要添加新的span元素
var s = document.createDocumentFragment();
for (; b < r; b++) {
var span = document.createElement("span");
span.id = "s" + b;
s.appendChild(span);
spanCache[b] = span; // 缓存span元素
}
l.appendChild(s);
} else if (b > r) {
// 需要删除多余的span元素
for (var j = b - 1; j >= r; j--) {
var spanToRemove = document.getElementById("s" + j);
if (spanToRemove && spanToRemove.parentNode === l) {
l.removeChild(spanToRemove);
delete spanCache[j]; // 清理缓存
}
}
b = r;
}
}
function R() {
l.scrollLeft = g.scrollLeft;
}
function F() {
l.scrollTop = g.scrollTop;
}
function a() {
l.dir = g.dir;
}
function n() {
g.previousSibling != l && (g.parentNode.insertBefore(l, g), f.disconnect(), f.observe(g.parentNode, {
childList: !0
}));
}
function s() {
g.value != c && E();
g.scrollLeft != l.scrollLeft && R();
g.scrollTop != l.scrollTop && F();
var e = window.getComputedStyle(l).height,
t = g.offsetHeight ? window.getComputedStyle(g).height : "0px";
e != t && (l.style.height = t, g.style.marginTop = "-" + t);
}
function i() {
// 浏览器兼容性检查:如果MutationObserver不被支持,自动禁用高亮功能
if (!window.MutationObserver) {
console.warn("Syntax highlighter disabled: MutationObserver not supported");
return;
}
// 全局变量安全检查:确保syntaxHighlighterConfig和syntaxHighlighterSiteConfig存在
window.syntaxHighlighterConfig = window.syntaxHighlighterConfig || {};
window.syntaxHighlighterSiteConfig = window.syntaxHighlighterSiteConfig || {};
var e, t, i;
function o(e, t, i) {
void 0 === syntaxHighlighterConfig[e] && (syntaxHighlighterConfig[e] = syntaxHighlighterSiteConfig[e] || t);
"normal" == syntaxHighlighterConfig[e] ? syntaxHighlighterConfig[e] = t : void 0 !== syntaxHighlighterConfig[e] || (void 0 !== syntaxHighlighterConfig.defaultColor && i ? syntaxHighlighterConfig[e] = syntaxHighlighterConfig.defaultColor : syntaxHighlighterConfig[e] = t);
}
(g = document.getElementById("wpTextbox1")) && (document.getElementById("wpTextbox0") || (window.syntaxHighlighterSiteConfig = window.syntaxHighlighterSiteConfig || {}, window.syntaxHighlighterConfig = window.syntaxHighlighterConfig || {},
// 补充缺失的默认数组配置
syntaxHighlighterConfig.voidTags = syntaxHighlighterConfig.voidTags || [],
syntaxHighlighterConfig.sourceTags = syntaxHighlighterConfig.sourceTags || [],
syntaxHighlighterConfig.nowikiTags = syntaxHighlighterConfig.nowikiTags || [],
o("backgroundColor", "#FFF", !1), o("foregroundColor", "#000", !1), o("boldOrItalicColor", "#EEE", !0), o("commentColor", "#EFE", !0), o("entityColor", "#DFD", !0), o("externalLinkColor", "#EFF", !0), o("headingColor", "#EEE", !0), o("hrColor", "#EEE", !0), o("listOrIndentColor", "#EFE", !0), o("parameterColor", "#FC6", !0), o("signatureColor", "#FC6", !0), o("tagColor", "#FEF", !0), o("tableColor", "#FFC", !0), o("templateColor", "#FFC", !0), o("wikilinkColor", "#EEF", !0), syntaxHighlighterConfig.nowikiTags = syntaxHighlighterConfig.nowikiTags || syntaxHighlighterSiteConfig.nowikiTags || ["nowiki", "pre"], syntaxHighlighterConfig.sourceTags = syntaxHighlighterConfig.sourceTags || syntaxHighlighterSiteConfig.sourceTags || ["math", "syntaxhighlight", "source", "timeline", "hiero"], syntaxHighlighterConfig.voidTags = syntaxHighlighterConfig.voidTags || syntaxHighlighterSiteConfig.voidTags || [], syntaxHighlighterConfig.timeout = syntaxHighlighterConfig.timeout || syntaxHighlighterSiteConfig.timeout || 20, syntaxHighlighterConfig.nowikiTags.forEach(function(e) {
var cacheKey = "nowiki_" + e;
if (!regexCache[cacheKey]) {
regexCache[cacheKey] = new RegExp("(</" + e + ">)\\n*|" + r, "gm");
}
L[e] = regexCache[cacheKey];
}), l = document.createElement("div"), e = document.createElement("style"),
// 添加样式冲突防护
e.setAttribute("scoped", "scoped"), // 限制样式仅作用于当前组件
e.setAttribute("data-syntax-highlighter", "true"), // 便于识别和调试
d = e.appendChild(document.createTextNode("")), i = "vertical" == (t = window.getComputedStyle(g)).resize || "both" == t.resize ? "vertical" : "none", l.dir = g.dir, l.id = "wpTextbox0", l.lang = g.lang, l.style.backgroundColor = syntaxHighlighterConfig.backgroundColor, l.style.borderBottomLeftRadius = t.borderBottomLeftRadius, l.style.borderBottomRightRadius = t.borderBottomRightRadius, l.style.borderBottomStyle = t.borderBottomStyle, l.style.borderBottomWidth = t.borderBottomWidth, l.style.borderColor = "transparent", l.style.borderLeftStyle = t.borderLeftStyle, l.style.borderLeftWidth = t.borderLeftWidth, l.style.borderRightStyle = t.borderRightStyle, l.style.borderRightWidth = t.borderRightWidth, l.style.borderTopLeftRadius = t.borderTopLeftRadius, l.style.borderTopRightRadius = t.borderTopRightRadius, l.style.borderTopStyle = t.borderTopStyle, l.style.borderTopWidth = t.borderTopWidth, l.style.boxSizing = "border-box", l.style.clear = t.clear, l.style.fontFamily = t.fontFamily, l.style.fontSize = t.fontSize, l.style.lineHeight = "normal", l.style.marginBottom = "0", l.style.marginLeft = "0", l.style.marginRight = "0", l.style.marginTop = "0", l.style.overflowX = "auto", l.style.overflowY = "scroll", l.style.resize = i, l.style.tabSize = t.tabSize, l.style.whiteSpace = "pre-wrap", l.style.width = "100%", l.style.wordWrap = "normal", g.style.backgroundColor = "transparent", g.style.borderBottomLeftRadius = t.borderBottomLeftRadius, g.style.borderBottomRightRadius = t.borderBottomRightRadius, g.style.borderBottomStyle = t.borderBottomStyle, g.style.borderBottomWidth = t.borderBottomWidth, g.style.borderLeftStyle = t.borderLeftStyle, g.style.borderLeftWidth = t.borderLeftWidth, g.style.borderRightStyle = t.borderRightStyle, g.style.borderRightWidth = t.borderRightWidth, g.style.borderTopLeftRadius = t.borderTopLeftRadius, g.style.borderTopRightRadius = t.borderTopRightRadius, g.style.borderTopStyle = t.borderTopStyle, g.style.borderTopWidth = t.borderTopWidth, g.style.boxSizing = "border-box", g.style.clear = t.clear, g.style.color = syntaxHighlighterConfig.foregroundColor, g.style.fontFamily = t.fontFamily, g.style.fontSize = t.fontSize, g.style.lineHeight = "normal", g.style.marginLeft = "0", g.style.marginRight = "0", g.style.marginTop = "-" + t.height, g.style.overflowX = "auto", g.style.overflowY = "scroll", g.style.padding = "0", g.style.resize = i, g.style.tabSize = t.tabSize, g.style.whiteSpace = "pre-wrap", g.style.width = "100%", g.style.wordWrap = "normal", g.parentNode.insertBefore(l, g), document.head.appendChild(e), g.addEventListener("input", E), g.addEventListener("scroll", R), g.addEventListener("scroll", F), (p = new MutationObserver(a)).observe(g, {
attributes: !0
}), (f = new MutationObserver(n)).observe(g.parentNode, {
childList: !0
}), y = setInterval(s, 500), E()));
}
var o = mw.config.get("wgAction"),
e = $.client.profile().layout;
"edit" != o && "submit" != o || "wikitext" != mw.config.get("wgPageContentModel") || "trident" == e || ("complete" == document.readyState ? i() : window.addEventListener("load", i));
// 添加页面卸载时的资源清理逻辑
function cleanup() {
// 清理定时器
if (y) {
clearInterval(y);
y = null;
}
// 清理观察者
[p, f].forEach(observer => observer?.disconnect());
// 清理事件监听
if (g) {
g.removeEventListener("input", E);
g.removeEventListener("scroll", R);
g.removeEventListener("scroll", F);
}
// 清理DOM元素
l?.parentNode?.removeChild(l);
d?.parentNode?.parentNode?.removeChild(d.parentNode); // 移除style元素
// 重置变量
l = g = d = c = y = p = f = b = null;
}
// 双重事件监听确保清理
window.addEventListener('beforeunload', cleanup);
window.addEventListener('unload', cleanup);
});