Github-bot(留言 | 贡献) (Git更新:([https://github.com/TopRealm/InterfaceCodes/commit/382f0d5f77104964acdf3a9acbfe08e57b1685dc 382f0d5]) chore: change urls) |
小 (Git commit ab99472: refactor: 迁移注册用户脚本) 标签:替换 |
||
第1行: | 第1行: | ||
/** |
/** |
||
* SPDX-License-Identifier: |
* SPDX-License-Identifier: CC-BY-SA-4.0 |
||
* _addText: '{{Gadget Header|license= |
* _addText: '{{Gadget Header|license=CC-BY-SA-4.0}}' |
||
* |
* |
||
* @ |
* @source {@link https://github.com/TopRealm/YsArxiv-Gadgets/tree/master/src/Wikiplus-highlight} |
||
* @license CC-BY-SA-4.0 {@link https://www.qiuwenbaike.cn/wiki/H:CC-BY-SA-4.0} |
|||
* @source <https://git.qiuwen.net.cn/InterfaceAdmin/Gadgets/src/branch/master/src/Gadgets/Wikiplus> |
|||
* @author Bhsd <https://github.com/bhsd-harry> |
|||
* @author 机智的小鱼君 <https://github.com/Dragon-Fish> |
|||
* @dependency mediawiki.storage, mediawiki.Title, mediawiki.util |
|||
*/ |
*/ |
||
/** |
/** |
||
* +--------------------------------------------------------+ |
* +------------------------------------------------------------+ |
||
* | === WARNING: GLOBAL GADGET FILE === | |
* | === WARNING: GLOBAL GADGET FILE === | |
||
* +--------------------------------------------------------+ |
* +------------------------------------------------------------+ |
||
* | All changes should be made in the repository, | |
* | All changes should be made in the repository, | |
||
* | otherwise they will be lost. | |
* | otherwise they will be lost. | |
||
* +--------------------------------------------------------+ |
* +------------------------------------------------------------+ |
||
* | Changes to this page may affect many users. | |
* | Changes to this page may affect many users. | |
||
* | |
* | Please discuss changes by opening an issue before editing. | |
||
* +--------------------------------------------------------+ |
* +------------------------------------------------------------+ |
||
*/ |
*/ |
||
/* <nowiki> */ |
/* <nowiki> */ |
||
/* Wikiplus-highlight:Wikiplus编辑器的CodeMirror语法高亮扩展 */ |
|||
(async function wikiplusHighlight() { |
|||
if (mw.libs.wphl && mw.libs.wphl.version) { |
|||
return; |
|||
} |
|||
mw.libs.wphl = mw.libs.wphl || {}; // 开始加载 |
|||
const version = '2.59.5'; |
|||
const newAddon = 0; |
|||
/** @type {typeof mw.storage} */ |
|||
const storage = |
|||
typeof mw.storage === 'object' && typeof mw.storage.getObject === 'function' |
|||
? mw.storage |
|||
: { |
|||
/** @override */ |
|||
getObject(key) { |
|||
const json = localStorage.getItem(key); |
|||
if (json === false) { |
|||
return false; |
|||
} |
|||
try { |
|||
return JSON.parse(json); |
|||
} catch { |
|||
return null; |
|||
} |
|||
}, |
|||
/** @override */ |
|||
setObject(key, value) { |
|||
try { |
|||
return localStorage.setItem(key, JSON.stringify(value)); |
|||
} catch { |
|||
return false; |
|||
} |
|||
}, |
|||
}; |
|||
Object.fromEntries = |
|||
Object.fromEntries || |
|||
((entries) => { |
|||
const /** @type {Record<string, T>} */ obj = {}; |
|||
for (const [key, value] of entries) { |
|||
obj[key] = value; |
|||
} |
|||
return obj; |
|||
}); |
|||
/** |
|||
* 解析版本号 |
|||
* |
|||
* @param {string} str 版本号 |
|||
*/ |
|||
const getVersion = (str = version) => str.split('.').map(Number); |
|||
/** |
|||
* 比较版本号 |
|||
* |
|||
* @param {string} a 版本号1 |
|||
* @param {string} b 版本号2 |
|||
* @return {boolean} `a`的版本号是否小于`b`的版本号 |
|||
*/ |
|||
const cmpVersion = (a, b) => { |
|||
const [a0, a1] = getVersion(a); |
|||
const [b0, b1] = getVersion(b); |
|||
return a0 < b0 || (a0 === b0 && a1 < b1); |
|||
}; |
|||
/** |
|||
* 获取I18N消息 |
|||
* |
|||
* @param {string} key 消息键,省略`wphl-`前缀 |
|||
* @param {string[]} args 替换`$1`等的参数 |
|||
*/ |
|||
const msg = (key, ...args) => mw.msg(`wphl-${key}`, ...args); |
|||
/** |
|||
* 生成JQuery的I18N消息 |
|||
* |
|||
* @param {string[]} args 替换`$1`等的参数 |
|||
*/ |
|||
const htmlMsg = (...args) => $($.parseHTML(msg(...args))); |
|||
/** |
|||
* 提示消息 |
|||
* |
|||
* @param {string[]} args 替换`$1`等的参数 |
|||
*/ |
|||
const notify = |
|||
(...args) => |
|||
() => { |
|||
const $p = $('<p>').html(msg(...args)); |
|||
mw.notify($p, {type: 'success', autoHideSeconds: 'long', tag: 'wikiplus-highlight'}); |
|||
return $p; |
|||
}; |
|||
// 插件和I18N依主版本号 |
|||
const majorVersion = getVersion().slice(0, 2).join('.'); |
|||
// 路径 |
|||
const CDN = '//fastly.jsdelivr.net'; |
|||
const CM_CDN = 'npm/codemirror@5.65.3'; |
|||
const MW_CDN = 'gh/bhsd-harry/codemirror-mediawiki@1.1.10'; |
|||
const PARSER_CDN = 'npm/wikiparser-node@0.11.0-b'; |
|||
const REPO_CDN = `npm/wikiplus-highlight@${majorVersion}`; |
|||
(() => { |
|||
config: { |
|||
"use strict"; |
|||
values: { |
|||
wgPageName: page, |
|||
// dist/Wikiplus-highlight/Wikiplus-highlight.js |
|||
wgNamespaceNumber: ns, |
|||
//! src/Wikiplus-highlight/Wikiplus-highlight.ts |
|||
wgPageContentModel: contentmodel, |
|||
var import_ext_gadget = require("ext.gadget.Util"); |
|||
wgServer: server, |
|||
if (!mw.user.options.get("gadget-Wikiplus")) { |
|||
wgScriptPath: scriptPath, |
|||
void (0, import_ext_gadget.initMwApi)("Wikiplus-highlight/2.0").postWithEditToken({ |
|||
wgUserLanguage: userLang, |
|||
action: "options", |
|||
skin, |
|||
change: "gadget-Wikiplus=1" |
|||
}, |
|||
}); |
|||
}, |
|||
void mw.loader.using("ext.gadget.Wikiplus"); |
|||
} = mw; |
|||
} |
|||
// 和本地缓存有关的常数 |
|||
const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null; |
|||
const /** @type {Record<string, {time: number, config: mwConfig}>} */ ALL_SETTINGS_CACHE = |
|||
storage.getObject('InPageEditMwConfig') || {}; |
|||
const SITE_ID = `${server}${scriptPath}`; |
|||
const /** @type {{time: number, config: mwConfig}} */ SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID] || {}; |
|||
const EXPIRED = !(SITE_SETTINGS.time > Date.now() - 86400 * 1000 * 30); |
|||
const /** @type {Record<string, string>} */ CONTENTMODEL = { |
|||
css: 'css', |
|||
'sanitized-css': 'css', |
|||
javascript: 'javascript', |
|||
json: 'javascript', |
|||
wikitext: 'mediawiki', |
|||
}; |
|||
const MODE_LIST = USING_LOCAL |
|||
? { |
|||
lib: 'ext.CodeMirror.lib', |
|||
css: 'ext.CodeMirror.lib.mode.css', |
|||
javascript: 'ext.CodeMirror.lib.mode.javascript', |
|||
lua: `${CM_CDN}/mode/lua/lua.min.js`, |
|||
mediawiki: EXPIRED ? 'ext.CodeMirror.data' : [], |
|||
htmlmixed: 'ext.CodeMirror.lib.mode.htmlmixed', |
|||
xml: [], |
|||
} |
|||
: { |
|||
lib: `${CM_CDN}/lib/codemirror.min.js`, |
|||
css: `${CM_CDN}/mode/css/css.min.js`, |
|||
javascript: `${CM_CDN}/mode/javascript/javascript.min.js`, |
|||
lua: `${CM_CDN}/mode/lua/lua.min.js`, |
|||
mediawiki: [], |
|||
htmlmixed: `${CM_CDN}/mode/htmlmixed/htmlmixed.min.js`, |
|||
xml: `${CM_CDN}/mode/xml/xml.min.js`, |
|||
}; |
|||
const ADDON_LIST = { |
|||
searchcursor: `${CM_CDN}/addon/search/searchcursor.min.js`, |
|||
search: `${REPO_CDN}/search.min.js`, |
|||
markSelection: `${CM_CDN}/addon/selection/mark-selection.min.js`, |
|||
activeLine: `${CM_CDN}/addon/selection/active-line.min.js`, |
|||
trailingspace: `${CM_CDN}/addon/edit/trailingspace.min.js`, |
|||
matchBrackets: `${CM_CDN}/addon/edit/matchbrackets.min.js`, |
|||
closeBrackets: `${CM_CDN}/addon/edit/closebrackets.min.js`, |
|||
matchTags: `${REPO_CDN}/matchtags.min.js`, |
|||
fold: `${REPO_CDN}/fold.min.js`, |
|||
wikiEditor: 'ext.wikiEditor', |
|||
contextmenu: 'mediawiki.Title', |
|||
lint: `${CM_CDN}/addon/lint/lint.min.js`, |
|||
annotateScrollbar: `${CM_CDN}/addon/scroll/annotatescrollbar.min.js`, |
|||
parser: `${PARSER_CDN}/extensions/base.min.js`, |
|||
lintWikitext: `${REPO_CDN}/lint.min.js`, |
|||
}; |
|||
const /** @type {addon[]} */ options = [ |
|||
{ |
|||
option: 'styleSelectedText', |
|||
addon: 'search', |
|||
download: 'markSelection', |
|||
only: true, |
|||
/** @implements */ complex: () => !addons.has('wikiEditor'), |
|||
}, |
|||
{option: 'styleActiveLine', addon: 'activeLine'}, |
|||
{option: 'showTrailingSpace', addon: 'trailingspace'}, |
|||
{ |
|||
option: 'matchBrackets', |
|||
/** @implements */ complex: (mode, json) => |
|||
mode === 'mediawiki' || json ? {bracketRegex: /[{}[\]]/u} : true, |
|||
}, |
|||
{ |
|||
option: 'autoCloseBrackets', |
|||
addon: 'closeBrackets', |
|||
/** @implements */ complex: (mode, json) => (mode === 'mediawiki' || json ? '()[]{}""' : true), |
|||
}, |
|||
{option: 'matchTags', addon: ['matchTags', 'fold'], modes: new Set(['mediawiki', 'widget'])}, |
|||
{option: 'fold', modes: new Set(['mediawiki', 'widget'])}, |
|||
]; |
|||
const defaultAddons = ['search']; |
|||
const defaultIndent = 4; |
|||
const /** @type {Set<string>} */ addons = new Set(storage.getObject('Wikiplus-highlight-addons') || defaultAddons); |
|||
let /** @type {number} */ indent = storage.getObject('Wikiplus-highlight-indent') || defaultIndent; |
|||
/** @type {Record<string, string>} */ |
|||
const entity = {'"': 'quot', "'": 'apos', '<': 'lt', '>': 'gt', '&': 'amp', ' ': 'nbsp'}; |
|||
const /** @type {(func: (str: string) => string) => (doc: CodeMirror.Editor) => void} */ |
|||
convert = (func) => (doc) => { |
|||
doc.replaceSelections( |
|||
doc.getSelections().map((selection) => selection.split('\n').map(func).join('\n')), |
|||
'around' |
|||
); |
|||
}; |
|||
const escapeHTML = convert((str) => |
|||
[...str] |
|||
.map((c) => { |
|||
if (c in entity) { |
|||
return `&${entity[c]};`; |
|||
} |
|||
const code = c.codePointAt(); |
|||
return code < 256 ? `&#${code};` : `&#x${code.toString(16)};`; |
|||
}) |
|||
.join('') |
|||
); |
|||
const escapeURI = convert((str) => { |
|||
if (str.includes('%')) { |
|||
try { |
|||
return decodeURIComponent(str); |
|||
} catch {} |
|||
} |
|||
return encodeURIComponent(str); |
|||
}); |
|||
const escapeHash = convert((str) => { |
|||
try { |
|||
return decodeURIComponent(str.replace(/\.([\da-f]{2})/giu, '%$1')); |
|||
} catch { |
|||
return str; |
|||
} |
|||
}); |
|||
const /** @type {(cm: typeof CodeMirror) => boolean} */ isPc = ({keyMap}) => keyMap.default === keyMap.pcDefault; |
|||
const /** @type {(cm: typeof CodeMirror) => Record<string, (doc: CodeMirror.Editor) => void} */ extraKeys = ( |
|||
CM |
|||
) => { |
|||
const ctrl = isPc(CM) ? 'Ctrl' : 'Cmd'; |
|||
return {[`${ctrl}-/`]: escapeHTML, [`${ctrl}-\\`]: escapeURI, [`Shift-${ctrl}-\\`]: escapeHash}; |
|||
}; |
|||
/** |
|||
* contextMenu插件 |
|||
* |
|||
* @param {CodeMirror.Editor} doc CodeMirror编辑区 |
|||
* @param {string} mode 高亮模式 |
|||
*/ |
|||
const handleContextMenu = (doc, mode) => { |
|||
if ((mode !== 'mediawiki' && mode !== 'widget') || !addons.has('contextmenu')) { |
|||
return; |
|||
} |
|||
const $wrapper = $(doc.getWrapperElement()).addClass('CodeMirror-contextmenu'); |
|||
const { |
|||
functionSynonyms: [synonyms], |
|||
} = mw.config.get('extCodeMirrorConfig') || { |
|||
functionSynonyms: [{invoke: 'invoke', 调用: 'invoke', widget: 'widget', 小工具: 'widget'}], |
|||
}; |
|||
/** |
|||
* 生成别名映射表 |
|||
* |
|||
* @param {string} str 别名 |
|||
*/ |
|||
const getSysnonyms = (str) => |
|||
new Set( |
|||
Object.keys(synonyms) |
|||
.filter((key) => synonyms[key] === str) |
|||
.map((key) => (key.startsWith('#') ? key : `#${key}`)) |
|||
); |
|||
const invoke = getSysnonyms('invoke'); |
|||
const widget = getSysnonyms('widget'); |
|||
$wrapper.on('contextmenu', ({pageX, pageY}) => { |
|||
const pos = doc.coordsChar({left: pageX, top: pageY}); |
|||
const {line, ch} = pos; |
|||
const curType = doc.getTokenTypeAt(pos); |
|||
if (!/\bmw-(?:template-name|parserfunction)\b/u.test(curType)) { |
|||
return undefined; |
|||
} |
|||
const tokens = doc.getLineTokens(line); |
|||
for (let i = tokens.length - 1; i > 0; i--) { |
|||
const {type, end, string} = tokens[i]; |
|||
if (tokens[i - 1].type === type) { |
|||
tokens[i - 1].end = end; |
|||
tokens[i - 1].string += string; |
|||
tokens.splice(i, 1); |
|||
} |
|||
} |
|||
const index = tokens.findIndex(({start, end}) => start < ch && end >= ch); |
|||
const text = tokens[index].string |
|||
.replace(/\u200E/gu, '') |
|||
.replace(/_/gu, ' ') |
|||
.trim(); |
|||
if (/\bmw-template-name\b/u.test(curType)) { |
|||
const title = new mw.Title(text); |
|||
if (title.namespace !== 0 || text.startsWith(':')) { |
|||
open(title.getUrl(), '_blank'); |
|||
} else { |
|||
open(mw.util.getUrl(`Template:${text}`), '_blank'); |
|||
} |
|||
return false; |
|||
} else if ( |
|||
index < 2 || |
|||
!/\bmw-parserfunction-delimiter\b/u.test(tokens[index - 1].type) || |
|||
!/\bmw-parserfunction-name\b/u.test(tokens[index - 2].type) |
|||
) { |
|||
return undefined; |
|||
} |
|||
const parserFunction = tokens[index - 2].string.trim().toLowerCase(); |
|||
if (invoke.has(parserFunction)) { |
|||
open(mw.util.getUrl(`Module:${text}`), '_blank'); |
|||
} else if (widget.has(parserFunction)) { |
|||
open(mw.util.getUrl(`Widget:${text}`, {action: 'edit'}), '_blank'); |
|||
} else { |
|||
return undefined; |
|||
} |
|||
return false; |
|||
}); |
|||
}; |
|||
const /** @type {Record<string, string>} */ i18n = storage.getObject('Wikiplus-highlight-i18n') || {}; |
|||
let /** @type {() => JQuery<HTMLElement>} */ welcome; |
|||
if (!i18n['wphl-version']) { |
|||
// 首次安装 |
|||
welcome = notify('welcome'); |
|||
} else if (cmpVersion(i18n['wphl-version'], version)) { |
|||
// 更新版本 |
|||
welcome = notify(`welcome-${newAddon ? 'new-addon' : 'upgrade'}`, version, newAddon); |
|||
} |
|||
const /** @type {Record<string, string>} */ i18nLanguages = { |
|||
zh: 'zh-hans', |
|||
'zh-hans': 'zh-hans', |
|||
'zh-cn': 'zh-hans', |
|||
'zh-my': 'zh-hans', |
|||
'zh-sg': 'zh-hans', |
|||
'zh-hant': 'zh-hant', |
|||
'zh-tw': 'zh-hant', |
|||
'zh-hk': 'zh-hant', |
|||
'zh-mo': 'zh-hant', |
|||
ka: 'ka', |
|||
}; |
|||
const i18nLang = i18nLanguages[userLang] || 'en'; |
|||
const I18N_CDN = `${CDN}/${REPO_CDN}/i18n/${i18nLang}.json`; |
|||
const isLatest = i18n['wphl-version'] === majorVersion; |
|||
/** 加载 I18N */ |
|||
const setI18N = async () => { |
|||
if (!isLatest || i18n['wphl-lang'] !== i18nLang) { |
|||
Object.assign( |
|||
i18n, |
|||
await $.ajax(`${I18N_CDN}`, { |
|||
dataType: 'json', |
|||
cache: true, |
|||
}) |
|||
); |
|||
storage.setObject('Wikiplus-highlight-i18n', i18n); |
|||
} |
|||
mw.messages.set(i18n); |
|||
}; |
|||
const i18nPromise = Promise.all([ |
|||
// 提前加载I18N |
|||
mw.loader.using('mediawiki.util'), |
|||
setI18N(), |
|||
]); |
|||
/** |
|||
* 下载MW扩展脚本 |
|||
* |
|||
* @param {string[]} exts CodeMirror扩展模块 |
|||
*/ |
|||
const getInternalScript = (exts) => (exts.length > 0 ? mw.loader.using(exts) : Promise.resolve()); |
|||
/** |
|||
* 下载外部脚本 |
|||
* |
|||
* @param {string[]} urls CodeMirror脚本网址 |
|||
*/ |
|||
const getExternalScript = (urls) => |
|||
urls.length > 0 |
|||
? $.ajax(`${CDN}/${urls.length > 1 ? 'combine/' : ''}${urls.join()}`, {dataType: 'script', cache: true}) |
|||
: Promise.resolve(); |
|||
/** |
|||
* 下载脚本 |
|||
* |
|||
* @param {string[]} urls 脚本路径 |
|||
* @param {boolean|undefined} local 是否先从本地下载 |
|||
*/ |
|||
const getScript = async (urls, local) => { |
|||
const internal = urls.filter((url) => !url.includes('/')); |
|||
const external = urls.filter((url) => url.includes('/')); |
|||
if (local === true) { |
|||
await getInternalScript(internal); |
|||
return getExternalScript(external); |
|||
} else if (local === false) { |
|||
await getExternalScript(external); |
|||
return getInternalScript(internal); |
|||
} |
|||
return Promise.all([getInternalScript(internal), getExternalScript(external)]); |
|||
}; |
|||
// 以下进入CodeMirror相关内容 |
|||
let /** @type {CodeMirror.EditorFromTextArea} */ cm; |
|||
/** |
|||
* 生成需要的插件列表 |
|||
* |
|||
* @param {typeof CodeMirror} CM CodeMirror |
|||
* @param {boolean} other 是否用于Wikiplus以外的textarea |
|||
*/ |
|||
const getAddonScript = (CM, other = false) => { |
|||
const /** @type {string[]} */ addonScript = []; |
|||
for (const {option, addon = option, download = Array.isArray(addon) ? option : addon, only} of options) { |
|||
if (!(only && other) && !(option in CM.optionHandlers) && intersect(addon, addons)) { |
|||
addonScript.push(ADDON_LIST[download]); |
|||
} |
|||
} |
|||
return addonScript; |
|||
}; |
|||
/** |
|||
* 交集 |
|||
* |
|||
* @param {T[]|T} arr 集合1(可重) |
|||
* @param {Set<T>} set 集合2 |
|||
* @template T |
|||
*/ |
|||
const intersect = (arr, set) => (Array.isArray(arr) ? arr.some((ele) => set.has(ele)) : set.has(arr)); |
|||
/** |
|||
* 根据文本的高亮模式加载依赖项 |
|||
* |
|||
* @param {string} type 高亮模式 |
|||
*/ |
|||
const initMode = (type) => { |
|||
let /** @type {string[]} */ scripts = []; |
|||
const loaded = typeof window.CodeMirror === 'function'; |
|||
/** |
|||
* 代替`CodeMirror`的局部变量 |
|||
* |
|||
* @type {typeof CodeMirror} |
|||
*/ |
|||
const CM = loaded |
|||
? window.CodeMirror |
|||
: {modes: {}, prototype: {}, commands: {}, optionHandlers: {}, helpers: {}}; |
|||
// lib |
|||
if (!loaded) { |
|||
scripts.push(MODE_LIST.lib); |
|||
if (!USING_LOCAL) { |
|||
mw.loader.load(`${CDN}/${CM_CDN}/lib/codemirror.min.css`, 'text/css'); |
|||
} |
|||
} |
|||
// modes |
|||
if (type === 'mediawiki' && SITE_SETTINGS.config && SITE_SETTINGS.config.tags.html) { |
|||
// NamespaceHTML扩展自由度过高,所以这里一律当作允许<html>标签 |
|||
type = 'html'; |
|||
} |
|||
if ((type === 'mediawiki' || type === 'widget') && !CM.modes.mediawiki) { |
|||
// 总是外部样式表和外部脚本 |
|||
mw.loader.load(`${CDN}/${MW_CDN}/mediawiki.min.css`, 'text/css'); |
|||
scripts.push(`${MW_CDN}/mediawiki.min.js`); |
|||
} |
|||
if (type === 'widget' || type === 'html') { |
|||
for (const lang of ['css', 'javascript', 'mediawiki', 'htmlmixed', 'xml']) { |
|||
if (!CM.modes[lang]) { |
|||
scripts = scripts.concat(MODE_LIST[lang]); |
|||
} |
|||
} |
|||
} else { |
|||
scripts = scripts.concat(MODE_LIST[type]); |
|||
} |
|||
// addons |
|||
if (!CM.prototype.getSearchCursor && addons.has('search') && !addons.has('wikiEditor')) { |
|||
scripts.push(ADDON_LIST.searchcursor); |
|||
} |
|||
if (!CM.prototype.annotateScrollbar && type === 'mediawiki' && addons.has('lint')) { |
|||
scripts.push(ADDON_LIST.annotateScrollbar); |
|||
} |
|||
if (!CM.commands.find && addons.has('search') && !addons.has('wikiEditor')) { |
|||
scripts.push(ADDON_LIST.search); |
|||
} |
|||
if (!window.wikiparse && type === 'mediawiki' && addons.has('lint')) { |
|||
scripts.push(ADDON_LIST.parser); |
|||
} |
|||
if (!CM.optionHandlers.lint && type === 'mediawiki' && addons.has('lint')) { |
|||
mw.loader.load(`${CDN}/${CM_CDN}/addon/lint/lint.min.css`, 'text/css'); |
|||
scripts.push(ADDON_LIST.lint); |
|||
} |
|||
if (!(CM.helpers.lint && CM.helpers.lint.mediawiki) && type === 'mediawiki' && addons.has('lint')) { |
|||
scripts.push(ADDON_LIST.lintWikitext); |
|||
} |
|||
if (addons.has('wikiEditor')) { |
|||
const state = mw.loader.getState('ext.wikiEditor'); |
|||
if (!state) { |
|||
addons.delete('wikiEditor'); |
|||
} else if (state !== 'ready') { |
|||
scripts.push(ADDON_LIST.wikiEditor); |
|||
} |
|||
} |
|||
if (mw.loader.getState('mediawiki.Title') !== 'ready' && addons.has('contextmenu')) { |
|||
scripts.push(ADDON_LIST.contextmenu); |
|||
} |
|||
scripts.push(...getAddonScript(CM)); |
|||
return getScript(scripts, loaded ? undefined : USING_LOCAL); |
|||
}; |
|||
/** |
|||
* 更新缓存的设置数据 |
|||
* |
|||
* @param {mwConfig} config wikitext设置 |
|||
*/ |
|||
const updateCachedConfig = (config) => { |
|||
ALL_SETTINGS_CACHE[SITE_ID] = {config, time: Date.now()}; |
|||
storage.setObject('InPageEditMwConfig', ALL_SETTINGS_CACHE); |
|||
}; |
|||
/** |
|||
* 展开别名列表 |
|||
* |
|||
* @param {{aliases: string[], name: string}[]} words 原名 |
|||
*/ |
|||
const getAliases = (words) => words.flatMap(({aliases, name}) => aliases.map((alias) => ({alias, name}))); |
|||
/** |
|||
* 将别名信息转换为CodeMirror接受的设置 |
|||
* |
|||
* @param {{alias: string, name: string}[]} aliases 别名 |
|||
* @return {Record<string, string>} |
|||
*/ |
|||
const getConfig = (aliases) => Object.fromEntries(aliases.map(({alias, name}) => [alias.replace(/:$/u, ''), name])); |
|||
/** |
|||
* 高亮扩展标签内部 |
|||
* |
|||
* @param {mwConfig} config 设置 |
|||
*/ |
|||
const setPlainMode = (config) => { |
|||
const tags = ['indicator', 'poem', 'ref', 'tabs', 'tab', 'poll']; |
|||
for (const tag of tags) { |
|||
if (config.tags[tag]) { |
|||
config.tagModes[tag] = 'text/mediawiki'; |
|||
} |
|||
} |
|||
}; |
|||
/** |
|||
* 加载CodeMirror的mediawiki模块需要的设置数据 |
|||
* |
|||
* @param {string} type 高亮模式 |
|||
* @param {Promise<void>} initModePromise 使用本地CodeMirror扩展时大部分数据来自ext.CodeMirror.data模块 |
|||
*/ |
|||
const getMwConfig = async (type, initModePromise) => { |
|||
if (type !== 'mediawiki' && type !== 'widget') { |
|||
return undefined; |
|||
} |
|||
if (USING_LOCAL && EXPIRED) { |
|||
// 只在localStorage过期时才会重新加载ext.CodeMirror.data |
|||
await initModePromise; |
|||
} |
|||
let config = mw.config.get('extCodeMirrorConfig'); |
|||
if (!config && !EXPIRED && isLatest) { |
|||
({config} = SITE_SETTINGS); |
|||
setPlainMode(config); |
|||
mw.config.set('extCodeMirrorConfig', config); |
|||
} |
|||
const isIPE = config && Object.values(config.functionSynonyms[0]).includes(true); |
|||
// 情形1:config已更新,可能来自localStorage |
|||
if (config && config.redirect && config.img && config.variants && !isIPE) { |
|||
return config; |
|||
} |
|||
// 以下情形均需要发送API请求 |
|||
// 情形2:localStorage未过期但不包含新设置 |
|||
// 情形3:新加载的 ext.CodeMirror.data |
|||
// 情形4:`config === null` |
|||
const { |
|||
query: { |
|||
general: {variants}, |
|||
magicwords, |
|||
extensiontags, |
|||
functionhooks, |
|||
variables, |
|||
}, |
|||
} = await new mw.Api({ |
|||
ajax: { |
|||
headers: { |
|||
'Api-User-Agent': `Qiuwen/1.1 (Wikiplus-highlight/${version}; ${mw.config.get('wgWikiID')})`, |
|||
}, |
|||
}, |
|||
}).get({ |
|||
meta: 'siteinfo', |
|||
siprop: `general|magicwords${config && !isIPE ? '' : '|extensiontags|functionhooks|variables'}`, |
|||
formatversion: 2, |
|||
}); |
|||
const otherMagicwords = new Set(['msg', 'raw', 'msgnw', 'subst', 'safesubst']); |
|||
if (config && !isIPE) { |
|||
// 情形2或3 |
|||
const { |
|||
functionSynonyms: [insensitive], |
|||
} = config; |
|||
if (!insensitive.subst) { |
|||
const aliases = getAliases(magicwords.filter(({name}) => otherMagicwords.has(name))); |
|||
for (const {alias, name} of aliases) { |
|||
insensitive[alias.replace(/:$/u, '')] = name; |
|||
} |
|||
} |
|||
} else { |
|||
// 情形4:`config === null` |
|||
config = { |
|||
tagModes: { |
|||
pre: 'mw-tag-pre', |
|||
nowiki: 'mw-tag-nowiki', |
|||
ref: 'text/mediawiki', |
|||
}, |
|||
tags: Object.fromEntries(extensiontags.map((tag) => [tag.slice(1, -1), true])), |
|||
urlProtocols: mw.config.get('wgUrlProtocols'), |
|||
}; |
|||
const realMagicwords = new Set([...functionhooks, ...variables, ...otherMagicwords]); |
|||
const allMagicwords = magicwords.filter( |
|||
({name, aliases}) => aliases.some((alias) => /^__.+__$/u.test(alias)) || realMagicwords.has(name) |
|||
); |
|||
const sensitive = getAliases(allMagicwords.filter((word) => word['case-sensitive'])); |
|||
const insensitive = getAliases(allMagicwords.filter((word) => !word['case-sensitive'])).map( |
|||
({alias, name}) => ({alias: alias.toLowerCase(), name}) |
|||
); |
|||
config.doubleUnderscore = [ |
|||
getConfig(insensitive.filter(({alias}) => /^__.+__$/u.test(alias))), |
|||
getConfig(sensitive.filter(({alias}) => /^__.+__$/u.test(alias))), |
|||
]; |
|||
config.functionSynonyms = [ |
|||
getConfig(insensitive.filter(({alias}) => !/^__.+__|^#$/u.test(alias))), |
|||
getConfig(sensitive.filter(({alias}) => !/^__.+__|^#$/u.test(alias))), |
|||
]; |
|||
} |
|||
config.redirect = magicwords.find(({name}) => name === 'redirect').aliases; |
|||
config.img = getConfig(getAliases(magicwords.filter(({name}) => name.startsWith('img_')))); |
|||
config.variants = variants ? variants.map(({code}) => code) : []; |
|||
setPlainMode(config); |
|||
mw.config.set('extCodeMirrorConfig', config); |
|||
updateCachedConfig(config); |
|||
return config; |
|||
}; |
|||
/** 检查页面语言类型 */ |
|||
const getPageMode = async () => { |
|||
if (page.endsWith('/doc')) { |
|||
return 'mediawiki'; |
|||
} else if (ns !== 274 && ns !== 828) { |
|||
return CONTENTMODEL[contentmodel]; |
|||
} |
|||
const pageMode = ns === 274 ? 'Widget' : 'Lua'; |
|||
await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']); |
|||
const bool = await OO.ui.confirm(msg('contentmodel'), { |
|||
actions: [{label: pageMode}, {label: 'Wikitext', action: 'accept'}], |
|||
}); |
|||
return bool ? 'mediawiki' : pageMode.toLowerCase(); |
|||
}; |
|||
/** |
|||
* jQuery.textSelection overrides for CodeMirror. |
|||
* See jQuery.textSelection.js for method documentation |
|||
*/ |
|||
const cmTextSelection = { |
|||
/** @override */ getContents() { |
|||
return cm.getValue(); |
|||
}, |
|||
/** @override */ setContents(content) { |
|||
cm.setValue(content); |
|||
return this; |
|||
}, |
|||
/** @override */ getSelection() { |
|||
return cm.getSelection(); |
|||
}, |
|||
/** @override */ setSelection(option) { |
|||
cm.setSelection(cm.posFromIndex(option.start), 'end' in option ? cm.posFromIndex(option.end) : undefined); |
|||
cm.focus(); |
|||
return this; |
|||
}, |
|||
/** @override */ replaceSelection(value) { |
|||
cm.replaceSelection(value); |
|||
return this; |
|||
}, |
|||
/** @override */ getCaretPosition(option) { |
|||
const caretPos = cm.indexFromPos(cm.getCursor('from')); |
|||
const endPos = cm.indexFromPos(cm.getCursor('to')); |
|||
return option.startAndEnd ? [caretPos, endPos] : caretPos; |
|||
}, |
|||
/** @override */ scrollToCaretPosition() { |
|||
cm.scrollIntoView(); |
|||
return this; |
|||
}, |
|||
}; |
|||
/** |
|||
* 渲染编辑器 |
|||
* |
|||
* @param {JQuery<HTMLTextAreaElement>} $target 目标编辑框 |
|||
* @param {boolean} setting 是否是Wikiplus设置(使用json语法) |
|||
*/ |
|||
const renderEditor = async ($target, setting) => { |
|||
const mode = setting ? 'javascript' : await getPageMode(); |
|||
const initModePromise = initMode(mode); |
|||
const [mwConfig] = await Promise.all([getMwConfig(mode, initModePromise), initModePromise]); |
|||
if (!setting && addons.has('wikiEditor')) { |
|||
try { |
|||
if (typeof mw.addWikiEditor === 'function') { |
|||
mw.addWikiEditor($target); |
|||
} else { |
|||
const { |
|||
wikiEditor: { |
|||
modules: { |
|||
dialogs: {config}, |
|||
}, |
|||
}, |
|||
} = $; |
|||
$target.wikiEditor('addModule', { |
|||
...$.wikiEditor.modules.toolbar.config.getDefaultConfig(), |
|||
...config.getDefaultConfig(), |
|||
}); |
|||
config.replaceIcons($target); |
|||
} |
|||
} catch (error) { |
|||
addons.delete('wikiEditor'); |
|||
mw.notify('WikiEditor工具栏加载失败。', {type: 'error', tag: 'wikiplus-highlight'}); |
|||
console.error(error); |
|||
} |
|||
} |
|||
if (mode === 'mediawiki' && mwConfig.tags.html) { |
|||
mwConfig.tagModes.html = 'htmlmixed'; |
|||
await initMode('html'); // 若已经缓存过`mwConfig`,这一步什么都不会发生 |
|||
} else if (mode === 'widget' && !CodeMirror.mimeModes.widget) { |
|||
// 到这里CodeMirror已确定加载完毕 |
|||
CodeMirror.defineMIME('widget', {name: 'htmlmixed', tags: {noinclude: [[null, null, 'mediawiki']]}}); |
|||
} |
|||
// 储存初始高度 |
|||
const height = $target.height(); |
|||
if (cm) { |
|||
cm.toTextArea(); |
|||
} |
|||
const json = setting || contentmodel === 'json'; |
|||
cm = CodeMirror.fromTextArea( |
|||
$target[0], |
|||
$.extend( |
|||
{ |
|||
inputStyle: 'contenteditable', |
|||
lineNumbers: !/Android\b/u.test(navigator.userAgent), |
|||
lineWrapping: true, |
|||
mode, |
|||
mwConfig, |
|||
json, |
|||
}, |
|||
Object.fromEntries( |
|||
options.map(({option, addon = option, modes, complex = (mod) => !modes || modes.has(mod)}) => { |
|||
const mainAddon = Array.isArray(addon) ? addon[0] : addon; |
|||
return [option, addons.has(mainAddon) && complex(mode, json)]; |
|||
}) |
|||
), |
|||
mode === 'mediawiki' |
|||
? { |
|||
extraKeys: addons.has('escape') && extraKeys(CodeMirror), |
|||
} |
|||
: { |
|||
indentUnit: addons.has('indentWithSpace') ? indent : defaultIndent, |
|||
indentWithTabs: !addons.has('indentWithSpace'), |
|||
} |
|||
) |
|||
); |
|||
cm.setSize(null, height); |
|||
cm.getWrapperElement().id = 'Wikiplus-CodeMirror'; |
|||
if ($.fn.textSelection) { |
|||
$target.textSelection('register', cmTextSelection); |
|||
} |
|||
const ctrl = isPc(CodeMirror) ? 'Ctrl' : 'Cmd'; |
|||
if (addons.has('wikiEditor')) { |
|||
const context = $target.data('wikiEditorContext'); |
|||
cm.addKeyMap({ |
|||
/** 替代CodeMirror的搜索功能 */ [`${ctrl}-F`]() { |
|||
$.wikiEditor.modules.dialogs.api.openDialog(context, 'search-and-replace'); |
|||
}, |
|||
}); |
|||
} |
|||
handleContextMenu(cm, mode); |
|||
$('#Wikiplus-Quickedit-Jump').children('a').attr('href', '#Wikiplus-CodeMirror'); |
|||
if (!setting) { |
|||
// 普通Wikiplus编辑区 |
|||
const settings = storage.getObject('Wikiplus_Settings'); |
|||
const escToExitQuickEdit = (settings && settings.esc_to_exit_quickedit) || settings.escToExitQuickEdit; |
|||
const submit = /** 提交编辑 */ () => { |
|||
$('#Wikiplus-Quickedit-Submit').triggerHandler('click'); |
|||
}; |
|||
const submitMinor = /** 提交小编辑 */ () => { |
|||
$('#Wikiplus-Quickedit-MinorEdit').trigger('click'); |
|||
$('#Wikiplus-Quickedit-Submit').triggerHandler('click'); |
|||
}; |
|||
cm.addKeyMap( |
|||
$.extend( |
|||
{[`${ctrl}-S`]: submit, [`Shift-${ctrl}-S`]: submitMinor}, |
|||
escToExitQuickEdit === true || escToExitQuickEdit === 'true' |
|||
? { |
|||
/** 按下Esc键退出编辑 */ Esc() { |
|||
$('#Wikiplus-Quickedit-Back').triggerHandler('click'); |
|||
}, |
|||
} |
|||
: {} |
|||
) |
|||
); |
|||
} |
|||
cm.refresh(); |
|||
mw.hook('wiki-codemirror').fire(cm); |
|||
}; |
|||
const {body} = document; |
|||
// 监视 Wikiplus 编辑框 |
|||
const observer = new MutationObserver((records) => { |
|||
const $editArea = $(records.flatMap(({addedNodes}) => [...addedNodes])).find( |
|||
'#Wikiplus-Quickedit, #Wikiplus-Setting-Input' |
|||
); |
|||
if ($editArea.length > 0) { |
|||
renderEditor($editArea, $editArea.attr('id') === 'Wikiplus-Setting-Input'); |
|||
} |
|||
}); |
|||
observer.observe(body, {childList: true}); |
|||
$(body).on( |
|||
'keydown.wphl', |
|||
'.ui-dialog', |
|||
/** @this {HTMLBodyElement} */ |
|||
function (e) { |
|||
if (e.key === 'Escape') { |
|||
/** @type {{$textarea: JQuery<HTMLTextAreaElement>}} */ |
|||
const context = $(this).children('.ui-dialog-content').data('context'); |
|||
if (context && context.$textarea && context.$textarea.attr('id') === 'Wikiplus-Quickedit') { |
|||
e.stopPropagation(); |
|||
} |
|||
} |
|||
} |
|||
); |
|||
/** |
|||
* 是否是Wikiplus编辑区 |
|||
* |
|||
* @param {HTMLTextAreaElement} elem textarea元素 |
|||
*/ |
|||
const isWikiplus = (elem) => elem.id === 'Wikiplus-Quickedit' || elem.id === 'Wikiplus-Setting-Input'; |
|||
$.valHooks.textarea = { |
|||
/** @override */ get(elem) { |
|||
return isWikiplus(elem) && cm ? cm.getValue() : elem.value; |
|||
}, |
|||
/** @override */ set(elem, value) { |
|||
if (isWikiplus(elem) && cm) { |
|||
cm.setValue(value); |
|||
} else { |
|||
elem.value = value; |
|||
} |
|||
}, |
|||
}; |
|||
await i18nPromise; // 以下内容依赖I18N |
|||
// 设置对话框 |
|||
let /** @type {OOUI.MessageDialog} */ dialog; |
|||
let /** @type {OOUI.CheckboxMultiselectInputWidget} */ inputWidget; |
|||
let /** @type {OOUI.CheckboxMultioptionWidget} */ searchWidget; |
|||
let /** @type {OOUI.CheckboxMultioptionWidget} */ wikiEditorWidget; |
|||
let /** @type {OOUI.NumberInputWidget} */ indentWidget; |
|||
let /** @type {OOUI.FieldLayout} */ field; |
|||
let /** @type {OOUI.FieldLayout} */ indentField; |
|||
/** |
|||
* 显示/隐藏缩进大小选项 |
|||
* |
|||
* @param {string[]} value 加载的插件 |
|||
*/ |
|||
const toggleIndent = (value = [...addons]) => { |
|||
indentField.toggle(value.includes('indentWithSpace')); |
|||
}; |
|||
const portletContainer = { |
|||
minerva: 'page-actions-overflow', |
|||
moeskin: 'ca-more-actions', |
|||
}; |
|||
const $portlet = $( |
|||
mw.util.addPortletLink(portletContainer[skin] || 'p-cactions', '#', msg('portlet'), 'wphl-settings') |
|||
).on('click', async () => { |
|||
if (dialog) { |
|||
inputWidget.setValue([...addons]); |
|||
indentWidget.setValue(indent); |
|||
} else { |
|||
await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']); |
|||
dialog = new OO.ui.MessageDialog({id: 'Wikiplus-highlight-dialog'}); |
|||
const windowManager = new OO.ui.WindowManager(); |
|||
windowManager.$element.appendTo(body); |
|||
windowManager.addWindows([dialog]); |
|||
inputWidget = new OO.ui.CheckboxMultiselectInputWidget({ |
|||
options: [ |
|||
...options.map(({option, addon = option}) => { |
|||
const mainAddon = Array.isArray(addon) ? addon[0] : addon; |
|||
return {data: mainAddon, label: htmlMsg(`addon-${mainAddon.toLowerCase()}`)}; |
|||
}), |
|||
...['wikiEditor', 'escape', 'contextmenu', 'lint', 'indentWithSpace', 'otherEditors'].map( |
|||
(addon) => ({data: addon, label: htmlMsg(`addon-${addon.toLowerCase()}`)}) |
|||
), |
|||
], |
|||
value: [...addons], |
|||
}).on('change', toggleIndent); |
|||
const {checkboxMultiselectWidget} = inputWidget; |
|||
searchWidget = checkboxMultiselectWidget.findItemFromData('search'); |
|||
wikiEditorWidget = checkboxMultiselectWidget.findItemFromData('wikiEditor'); |
|||
indentWidget = new OO.ui.NumberInputWidget({min: 0, value: indent}); |
|||
field = new OO.ui.FieldLayout(inputWidget, { |
|||
label: msg('addon-label'), |
|||
notices: [msg('addon-notice')], |
|||
align: 'top', |
|||
}); |
|||
indentField = new OO.ui.FieldLayout(indentWidget, {label: msg('addon-indent')}); |
|||
toggleIndent(); |
|||
Object.assign(mw.libs.wphl, {widget: inputWidget, indentWidget}); |
|||
} |
|||
const wikiplusLoaded = typeof window.Wikiplus === 'object' || typeof window._WikiplusPages === 'object'; |
|||
searchWidget.setDisabled(!wikiplusLoaded); |
|||
wikiEditorWidget.setDisabled(!wikiplusLoaded || !mw.loader.getState('ext.wikiEditor')); |
|||
const data = await dialog.open({ |
|||
title: msg('addon-title'), |
|||
message: field.$element.add(indentField.$element).add($('<p>').html(msg('feedback'))), |
|||
actions: [ |
|||
{action: 'reject', label: mw.msg('ooui-dialog-message-reject')}, |
|||
{action: 'accept', label: mw.msg('ooui-dialog-message-accept'), flags: 'progressive'}, |
|||
], |
|||
size: i18nLang === 'en' || skin === 'minerva' ? 'medium' : 'small', |
|||
}).closing; |
|||
field.$element.detach(); |
|||
indentField.$element.detach(); |
|||
if (typeof data === 'object' && data.action === 'accept') { |
|||
const value = inputWidget.getValue(); |
|||
addons.clear(); |
|||
for (const addon of value) { |
|||
addons.add(addon); |
|||
} |
|||
indent = Number(indentWidget.getValue()); |
|||
storage.setObject('Wikiplus-highlight-addons', value); |
|||
storage.setObject('Wikiplus-highlight-indent', indent); |
|||
} |
|||
}); |
|||
if (skin === 'minerva') { |
|||
$portlet.find('.mw-ui-icon').addClass('mw-ui-icon-minerva-settings'); |
|||
} |
|||
// 发送欢迎提示 |
|||
if (typeof welcome === 'function') { |
|||
welcome() |
|||
.find('#wphl-settings-notify') |
|||
.click((e) => { |
|||
e.preventDefault(); |
|||
$('#wphl-settings').triggerHandler('click'); |
|||
}); |
|||
} |
|||
/** |
|||
* 处理非Wikiplus编辑器 |
|||
* |
|||
* @param {CodeMirror.Editor} doc CodeMirror编辑区 |
|||
*/ |
|||
const handleOtherEditors = async (doc) => { |
|||
if (!addons.has('otherEditors')) { |
|||
return; |
|||
} |
|||
let mode = doc.getOption('mode'); |
|||
mode = mode === 'text/mediawiki' ? 'mediawiki' : mode; |
|||
const addonScript = getAddonScript(CodeMirror, true); |
|||
const json = doc.getOption('json'); |
|||
const { |
|||
prototype, |
|||
optionHandlers, |
|||
helpers: {lint}, |
|||
} = CodeMirror; |
|||
if (!prototype.annotateScrollbar && mode === 'mediawiki' && addons.has('lint')) { |
|||
addonScript.push(ADDON_LIST.annotateScrollbar); |
|||
} |
|||
if (!window.wikiparse && mode === 'mediawiki' && addons.has('lint')) { |
|||
addonScript.push(ADDON_LIST.parser); |
|||
} |
|||
if (!optionHandlers.lint && mode === 'mediawiki' && addons.has('lint')) { |
|||
mw.loader.load(`${CDN}/${CM_CDN}/addon/lint/lint.min.css`, 'text/css'); |
|||
addonScript.push(ADDON_LIST.lint); |
|||
} |
|||
if (!(lint && lint.mediawiki) && mode === 'mediawiki' && addons.has('lint')) { |
|||
addonScript.push(ADDON_LIST.lintWikitext); |
|||
} |
|||
await getScript(addonScript); |
|||
for (const { |
|||
option, |
|||
addon = option, |
|||
modes, |
|||
complex = (/** @type {string} */ mod) => !modes || modes.has(mod), |
|||
} of options.filter(({only}) => !only)) { |
|||
const mainAddon = Array.isArray(addon) ? addon[0] : addon; |
|||
if (doc.getOption(option) === undefined && addons.has(mainAddon)) { |
|||
doc.setOption(option, complex(mode, json)); |
|||
} |
|||
} |
|||
if (mode === 'mediawiki' && addons.has('escape')) { |
|||
doc.addKeyMap(extraKeys(CodeMirror), true); |
|||
} else if (mode !== 'mediawiki' && addons.has('indentWithSpace')) { |
|||
doc.setOption('indentUnit', indent); |
|||
doc.setOption('indentWithTabs', false); |
|||
} |
|||
handleContextMenu(doc, mode); |
|||
}; |
|||
mw.hook('InPageEdit.quickEdit.codemirror').add( |
|||
/** @param {{cm: CodeMirror.Editor}} */ ({cm: doc}) => handleOtherEditors(doc) |
|||
); |
|||
mw.hook('inspector').add(/** @param {CodeMirror.Editor} doc */ (doc) => handleOtherEditors(doc)); |
|||
mw.hook('wiki-codemirror').add( |
|||
/** @param {CodeMirror.Editor} doc */ (doc) => { |
|||
if (!doc.getTextArea || !isWikiplus(doc.getTextArea())) { |
|||
handleOtherEditors(doc); |
|||
} |
|||
} |
|||
); |
|||
mw.loader.load(`${CDN}/${REPO_CDN}/styles.min.css`, 'text/css'); |
|||
Object.assign(mw.libs.wphl, { |
|||
version, |
|||
options, |
|||
addons, |
|||
i18n, |
|||
i18nLang, |
|||
storage, |
|||
$portlet, |
|||
CDN, |
|||
PARSER_CDN, |
|||
USING_LOCAL, |
|||
MODE_LIST, |
|||
ADDON_LIST, |
|||
msg, |
|||
htmlMsg, |
|||
escapeHTML, |
|||
handleContextMenu, |
|||
setI18N, |
|||
getAddonScript, |
|||
updateCachedConfig, |
|||
getMwConfig, |
|||
renderEditor, |
|||
handleOtherEditors, |
|||
isPc, |
|||
}); // 加载完毕 |
|||
})(); |
})(); |
||
/* </nowiki> */ |
/* </nowiki> */ |
||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL1dpa2lwbHVzLWhpZ2hsaWdodC9XaWtpcGx1cy1oaWdobGlnaHQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7aW5pdE13QXBpfSBmcm9tICdleHQuZ2FkZ2V0LlV0aWwnO1xuXG5pZiAoIW13LnVzZXIub3B0aW9ucy5nZXQoJ2dhZGdldC1XaWtpcGx1cycpKSB7XG5cdHZvaWQgaW5pdE13QXBpKCdXaWtpcGx1cy1oaWdobGlnaHQvMi4wJykucG9zdFdpdGhFZGl0VG9rZW4oe1xuXHRcdGFjdGlvbjogJ29wdGlvbnMnLFxuXHRcdGNoYW5nZTogJ2dhZGdldC1XaWtpcGx1cz0xJyxcblx0fSk7XG5cblx0dm9pZCBtdy5sb2FkZXIudXNpbmcoJ2V4dC5nYWRnZXQuV2lraXBsdXMnKTtcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsSUFBQUEsb0JBQXdCQyxRQUFBLGlCQUFBO0FBRXhCLElBQUksQ0FBQ0MsR0FBR0MsS0FBS0MsUUFBUUMsSUFBSSxpQkFBaUIsR0FBRztBQUM1QyxRQUFBLEdBQUtMLGtCQUFBTSxXQUFVLHdCQUF3QixFQUFFQyxrQkFBa0I7SUFDMURDLFFBQVE7SUFDUkMsUUFBUTtFQUNULENBQUM7QUFFRCxPQUFLUCxHQUFHUSxPQUFPQyxNQUFNLHFCQUFxQjtBQUMzQzsiLAogICJuYW1lcyI6IFsiaW1wb3J0X2V4dF9nYWRnZXQiLCAicmVxdWlyZSIsICJtdyIsICJ1c2VyIiwgIm9wdGlvbnMiLCAiZ2V0IiwgImluaXRNd0FwaSIsICJwb3N0V2l0aEVkaXRUb2tlbiIsICJhY3Rpb24iLCAiY2hhbmdlIiwgImxvYWRlciIsICJ1c2luZyJdCn0K |
2024年2月24日 (六) 03:31的版本
/**
* SPDX-License-Identifier: CC-BY-SA-4.0
* _addText: '{{Gadget Header|license=CC-BY-SA-4.0}}'
*
* @source {@link https://github.com/TopRealm/YsArxiv-Gadgets/tree/master/src/Wikiplus-highlight}
* @license CC-BY-SA-4.0 {@link https://www.qiuwenbaike.cn/wiki/H:CC-BY-SA-4.0}
*/
/**
* +------------------------------------------------------------+
* | === WARNING: GLOBAL GADGET FILE === |
* +------------------------------------------------------------+
* | All changes should be made in the repository, |
* | otherwise they will be lost. |
* +------------------------------------------------------------+
* | Changes to this page may affect many users. |
* | Please discuss changes by opening an issue before editing. |
* +------------------------------------------------------------+
*/
/* <nowiki> */
(() => {
"use strict";
// dist/Wikiplus-highlight/Wikiplus-highlight.js
//! src/Wikiplus-highlight/Wikiplus-highlight.ts
var import_ext_gadget = require("ext.gadget.Util");
if (!mw.user.options.get("gadget-Wikiplus")) {
void (0, import_ext_gadget.initMwApi)("Wikiplus-highlight/2.0").postWithEditToken({
action: "options",
change: "gadget-Wikiplus=1"
});
void mw.loader.using("ext.gadget.Wikiplus");
}
})();
/* </nowiki> */
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL1dpa2lwbHVzLWhpZ2hsaWdodC9XaWtpcGx1cy1oaWdobGlnaHQudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7aW5pdE13QXBpfSBmcm9tICdleHQuZ2FkZ2V0LlV0aWwnO1xuXG5pZiAoIW13LnVzZXIub3B0aW9ucy5nZXQoJ2dhZGdldC1XaWtpcGx1cycpKSB7XG5cdHZvaWQgaW5pdE13QXBpKCdXaWtpcGx1cy1oaWdobGlnaHQvMi4wJykucG9zdFdpdGhFZGl0VG9rZW4oe1xuXHRcdGFjdGlvbjogJ29wdGlvbnMnLFxuXHRcdGNoYW5nZTogJ2dhZGdldC1XaWtpcGx1cz0xJyxcblx0fSk7XG5cblx0dm9pZCBtdy5sb2FkZXIudXNpbmcoJ2V4dC5nYWRnZXQuV2lraXBsdXMnKTtcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsSUFBQUEsb0JBQXdCQyxRQUFBLGlCQUFBO0FBRXhCLElBQUksQ0FBQ0MsR0FBR0MsS0FBS0MsUUFBUUMsSUFBSSxpQkFBaUIsR0FBRztBQUM1QyxRQUFBLEdBQUtMLGtCQUFBTSxXQUFVLHdCQUF3QixFQUFFQyxrQkFBa0I7SUFDMURDLFFBQVE7SUFDUkMsUUFBUTtFQUNULENBQUM7QUFFRCxPQUFLUCxHQUFHUSxPQUFPQyxNQUFNLHFCQUFxQjtBQUMzQzsiLAogICJuYW1lcyI6IFsiaW1wb3J0X2V4dF9nYWRnZXQiLCAicmVxdWlyZSIsICJtdyIsICJ1c2VyIiwgIm9wdGlvbnMiLCAiZ2V0IiwgImluaXRNd0FwaSIsICJwb3N0V2l0aEVkaXRUb2tlbiIsICJhY3Rpb24iLCAiY2hhbmdlIiwgImxvYWRlciIsICJ1c2luZyJdCn0K