MediaWiki:Gadget-DotsSyntaxHighlighter.js:修订间差异
外观
小无编辑摘要 |
小无编辑摘要 |
||
| (未显示2个用户的8个中间版本) | |||
| 第1行: | 第1行: | ||
mw.loader.using("jquery.client", function() { | mw.loader.using("jquery.client", function() { | ||
"use strict"; | "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]) { | |||
var | 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 | |||
} | } | ||
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 ( | // 增量更新检查:如果文本内容没有变化,直接返回 | ||
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; | var match; | ||
regex.lastIndex = pos; | |||
while ((match = regex.exec(text)) !== null) { | |||
if (match[1]) | if (match[1]) { | ||
// 匹配到结束标记,处理剩余文本并返回 | |||
// | u(text.substring(pos, regex.lastIndex), color); | ||
m = regex.lastIndex; | |||
break; | |||
} | } | ||
var | var matchStart = regex.lastIndex - match[0].length; | ||
if ( | |||
// 处理匹配前的文本 | |||
if (pos < matchStart) { | |||
u(text.substring(pos, matchStart), color); | |||
} | } | ||
pos = regex.lastIndex; | |||
switch (match[0].charAt(0)) | // 根据匹配内容处理不同的语法元素 | ||
switch (match[0].charAt(0)) { | |||
case "[": | case "[": | ||
if (match[0].charAt(1) == "[") | 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; | break; | ||
case "{": | case "{": | ||
if (match[0].charAt(1) == "{") | if (match[0].charAt(1) === "{") { | ||
if (match[0].length === 3) { | |||
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; | break; | ||
case "<": | case "<": | ||
if (match[0].charAt(1) == "!") | 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 tagEnd = text.indexOf(">", | |||
if (tagEnd == 0) | |||
} | |||
var tagName = match[0].substring(1); | var tagName = match[0].substring(1); | ||
if (syntaxHighlighterConfig.sourceTags.indexOf(tagName) != -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; | break; | ||
case "=": | case "=": | ||
if (/[^=]=+$/.test(text.substring( | 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; | break; | ||
case "*": | case "*": | ||
case "#": | case "#": | ||
case ":": | case ":": | ||
// | // 列表和缩进 | ||
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'listOrIndentColor', color) || color); | |||
break; | break; | ||
case ";": | 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; | break; | ||
case "-": | case "-": | ||
// | // 水平线 | ||
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'hrColor', color) || color); | |||
break; | break; | ||
case "\\": | case "\\": | ||
// 粗体和斜体 | |||
if (match[0].length == 6) | u(match[0], safeColorAccess(syntaxHighlighterConfig, 'boldOrItalicColor', color) || color); | ||
if (match[0].length === 6) { | |||
// 处理粗体 | |||
if (boldState) { | |||
if (!italicState) { | |||
// 结束粗体 | |||
boldState = false; | |||
// | |||
if ( | |||
if ( | |||
// | |||
} | } | ||
} 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; | break; | ||
case "&": | case "&": | ||
// | // HTML实体 | ||
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'entityColor', color) || color); | |||
break; | break; | ||
case "~": | case "~": | ||
// | // 签名 | ||
u(match[0], safeColorAccess(syntaxHighlighterConfig, 'signatureColor', color) || color); | |||
break; | break; | ||
default: | 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 ( | |||
} | } | ||
// | // 处理剩余文本 | ||
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 | 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) { | ||
if ( | // 需要添加新的span元素 | ||
var s = document.createDocumentFragment(); | |||
var | 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 | function R() { | ||
l.scrollLeft = g.scrollLeft; | |||
} | } | ||
function | function F() { | ||
l.scrollTop = g.scrollTop; | |||
} | } | ||
function | function a() { | ||
l.dir = g.dir; | |||
} | } | ||
function | 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 | 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 || {}; | 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.nowikiTags = syntaxHighlighterConfig.nowikiTags || syntaxHighlighterSiteConfig.nowikiTags || ["nowiki", "pre"] | // 补充缺失的默认数组配置 | ||
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)); | |||
var | // 添加页面卸载时的资源清理逻辑 | ||
function cleanup() { | |||
// 清理定时器 | |||
{ | if (y) { | ||
// | clearInterval(y); | ||
if ( | 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); | |||
}); | }); | ||
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);
});