wikiplus-highlight 2.10.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/main.min.js +1 -1
- package/dist/main.min.js.map +1 -1
- package/fold.js +96 -31
- package/i18n/en.json +3 -2
- package/i18n/zh-hans.json +3 -2
- package/i18n/zh-hant.json +3 -2
- package/jsconfig.json +3 -0
- package/main.js +195 -144
- package/matchtags.js +42 -41
- package/package.json +2 -1
- package/search.js +8 -18
package/main.js
CHANGED
|
@@ -8,13 +8,10 @@
|
|
|
8
8
|
(async () => {
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
-
const version = '2.
|
|
11
|
+
const version = '2.12.1',
|
|
12
12
|
newAddon = 1;
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* polyfill for mw.storage
|
|
16
|
-
* @type {{getObject: (key: string) => ?any, setObject: (key: string, value: any) => boolean}}
|
|
17
|
-
*/
|
|
14
|
+
/** @type {mw.storage} */
|
|
18
15
|
const storage = typeof mw.storage === 'object' && typeof mw.storage.getObject === 'function'
|
|
19
16
|
? mw.storage
|
|
20
17
|
: {
|
|
@@ -41,15 +38,22 @@
|
|
|
41
38
|
};
|
|
42
39
|
/**
|
|
43
40
|
* polyfill for Object.fromEntries
|
|
44
|
-
* @type {(entries: Iterable<[string, any]>) =>
|
|
41
|
+
* @type {(entries: Iterable<[string, any]>) => Record<string, any>}
|
|
45
42
|
*/
|
|
46
43
|
const fromEntries = Object.fromEntries || (entries => {
|
|
47
|
-
const /** @type {
|
|
44
|
+
const /** @type {Record<string, any>} */ obj = {};
|
|
48
45
|
for (const [key, value] of entries) {
|
|
49
46
|
obj[key] = value;
|
|
50
47
|
}
|
|
51
48
|
return obj;
|
|
52
49
|
});
|
|
50
|
+
/**
|
|
51
|
+
* polyfill for Array.prototype.flat
|
|
52
|
+
* @type {function(this: any[][]): any[]}
|
|
53
|
+
*/
|
|
54
|
+
const flat = Array.prototype.flat || function() {
|
|
55
|
+
return this.reduce((acc, cur) => acc.concat(cur), []);
|
|
56
|
+
};
|
|
53
57
|
|
|
54
58
|
/**
|
|
55
59
|
* 解析版本号
|
|
@@ -71,7 +75,6 @@
|
|
|
71
75
|
* 获取I18N消息
|
|
72
76
|
* @param {string} key 消息键,省略'wphl-'前缀
|
|
73
77
|
* @param {string[]} args
|
|
74
|
-
* @returns {string}
|
|
75
78
|
*/
|
|
76
79
|
const msg = (key, ...args) => mw.msg(`wphl-${key}`, ...args);
|
|
77
80
|
/**
|
|
@@ -79,7 +82,7 @@
|
|
|
79
82
|
* @param {string[]} args
|
|
80
83
|
*/
|
|
81
84
|
const notify = (...args) => () => {
|
|
82
|
-
const
|
|
85
|
+
const $p = $('<p>', {html: msg(...args)});
|
|
83
86
|
mw.notify($p, {type: 'success', autoHideSeconds: 'long', tag: 'wikiplus-highlight'});
|
|
84
87
|
return $p;
|
|
85
88
|
};
|
|
@@ -90,13 +93,9 @@
|
|
|
90
93
|
// 路径
|
|
91
94
|
const CDN = '//fastly.jsdelivr.net',
|
|
92
95
|
CM_CDN = 'npm/codemirror@5.65.3',
|
|
93
|
-
MW_CDN = 'gh/bhsd-harry/codemirror-mediawiki@1.1.
|
|
96
|
+
MW_CDN = 'gh/bhsd-harry/codemirror-mediawiki@1.1.5',
|
|
94
97
|
REPO_CDN = `npm/wikiplus-highlight@${majorVersion}`;
|
|
95
98
|
|
|
96
|
-
/**
|
|
97
|
-
* mw.config常数
|
|
98
|
-
* @type {Object<string, string>}
|
|
99
|
-
*/
|
|
100
99
|
const {
|
|
101
100
|
wgPageName: page,
|
|
102
101
|
wgNamespaceNumber: ns,
|
|
@@ -107,26 +106,15 @@
|
|
|
107
106
|
skin,
|
|
108
107
|
} = mw.config.values;
|
|
109
108
|
|
|
110
|
-
/**
|
|
111
|
-
* @typedef {object} mwConfig
|
|
112
|
-
* @property {Object<string, string>} tagModes
|
|
113
|
-
* @property {Object<string, boolean>} tags
|
|
114
|
-
* @property {string} urlProtocols
|
|
115
|
-
* @property {[Object<string, string>, Object<string, string>]} doubleUnderscore
|
|
116
|
-
* @property {[Object<string, string>, Object<string, string>]} functionSynonyms
|
|
117
|
-
* @property {string[]} redirect
|
|
118
|
-
* @property {Object<string, string>} img
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
109
|
// 和本地缓存有关的常数
|
|
122
110
|
const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null,
|
|
123
|
-
/** @type {
|
|
111
|
+
/** @type {Record<string, {time: number, config: mwConfig}>} */
|
|
124
112
|
ALL_SETTINGS_CACHE = storage.getObject('InPageEditMwConfig') || {},
|
|
125
113
|
SITE_ID = `${server}${scriptPath}`,
|
|
126
114
|
/** @type {{time: number, config: mwConfig}} */ SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID] || {},
|
|
127
115
|
EXPIRED = SITE_SETTINGS.time < Date.now() - 86400 * 1000 * 30;
|
|
128
116
|
|
|
129
|
-
const /** @type {
|
|
117
|
+
const /** @type {Record<string, string>} */ CONTENTMODEL = {
|
|
130
118
|
css: 'css',
|
|
131
119
|
'sanitized-css': 'css',
|
|
132
120
|
javascript: 'javascript',
|
|
@@ -134,7 +122,7 @@
|
|
|
134
122
|
wikitext: 'mediawiki',
|
|
135
123
|
};
|
|
136
124
|
|
|
137
|
-
const
|
|
125
|
+
const MODE_LIST = USING_LOCAL
|
|
138
126
|
? {
|
|
139
127
|
lib: 'ext.CodeMirror.lib',
|
|
140
128
|
css: 'ext.CodeMirror.lib.mode.css',
|
|
@@ -170,14 +158,68 @@
|
|
|
170
158
|
let /** @type {string[]} */ addons = storage.getObject('Wikiplus-highlight-addons') || defaultAddons,
|
|
171
159
|
/** @type {string} */ indent = storage.getObject('Wikiplus-highlight-indent') || defaultIndent;
|
|
172
160
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
161
|
+
/**
|
|
162
|
+
* contextMenu插件
|
|
163
|
+
* @param {CodeMirror.Editor} doc
|
|
164
|
+
* @param {string} mode
|
|
165
|
+
*/
|
|
166
|
+
const handleContextMenu = async (doc, mode) => {
|
|
167
|
+
if (!['mediawiki', 'widget'].includes(mode) || !addons.includes('contextmenu')) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const $wrapper = $(doc.getWrapperElement()).addClass('CodeMirror-contextmenu'),
|
|
171
|
+
{functionSynonyms: [synonyms]} = mw.config.get('extCodeMirrorConfig') || {
|
|
172
|
+
functionSynonyms: [{
|
|
173
|
+
invoke: 'invoke',
|
|
174
|
+
调用: 'invoke',
|
|
175
|
+
widget: 'widget',
|
|
176
|
+
小工具: 'widget',
|
|
177
|
+
}],
|
|
178
|
+
};
|
|
179
|
+
/** @param {string} str */
|
|
180
|
+
const getSysnonyms = str => Object.keys(synonyms).filter(key => synonyms[key] === str)
|
|
181
|
+
.map(key => key.startsWith('#') ? key : `#${key}`);
|
|
182
|
+
const invoke = getSysnonyms('invoke'),
|
|
183
|
+
widget = getSysnonyms('widget');
|
|
184
|
+
|
|
185
|
+
await mw.loader.using('mediawiki.Title');
|
|
186
|
+
$wrapper.contextmenu(({pageX, pageY}) => {
|
|
187
|
+
const pos = doc.coordsChar({left: pageX, top: pageY}),
|
|
188
|
+
{line, ch} = pos,
|
|
189
|
+
type = doc.getTokenTypeAt(pos);
|
|
190
|
+
if (!/\bmw-(?:template-name|parserfunction)\b/.test(type)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const tokens = doc.getLineTokens(line),
|
|
194
|
+
index = tokens.findIndex(({start, end}) => start < ch && end >= ch),
|
|
195
|
+
text = tokens[index].string.replace(/\u200e/g, '').replace(/_/g, ' ').trim();
|
|
196
|
+
if (/\bmw-template-name\b/.test(type)) {
|
|
197
|
+
const title = new mw.Title(text);
|
|
198
|
+
if (title.namespace !== 0 || text.startsWith(':')) {
|
|
199
|
+
open(title.getUrl(), '_blank');
|
|
200
|
+
} else {
|
|
201
|
+
open(mw.util.getUrl(`Template:${text}`), '_blank');
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
} else if (index < 2 || !/\bmw-parserfunction-delimiter\b/.test(tokens[index - 1].type)
|
|
205
|
+
|| !/\bmw-parserfunction-name\b/.test(tokens[index - 2].type)
|
|
206
|
+
) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const parserFunction = tokens[index - 2].string.trim().toLocaleLowerCase();
|
|
210
|
+
if (invoke.includes(parserFunction)) {
|
|
211
|
+
open(mw.util.getUrl(`Module:${text}`), '_blank');
|
|
212
|
+
} else if (widget.includes(parserFunction)) {
|
|
213
|
+
open(mw.util.getUrl(`Widget:${text}`, {action: 'edit'}), '_blank');
|
|
214
|
+
} else {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
});
|
|
219
|
+
};
|
|
178
220
|
|
|
179
|
-
let /** @type {
|
|
180
|
-
/** @type {() => JQuery<
|
|
221
|
+
let /** @type {Record<string, string>} */ i18n = storage.getObject('Wikiplus-highlight-i18n'),
|
|
222
|
+
/** @type {() => JQuery<HTMLElement>} */ welcome;
|
|
181
223
|
if (!i18n) { // 首次安装
|
|
182
224
|
i18n = {};
|
|
183
225
|
welcome = notify('welcome');
|
|
@@ -185,7 +227,7 @@
|
|
|
185
227
|
welcome = notify(`welcome-${newAddon ? 'new-addon' : 'upgrade'}`, version, newAddon);
|
|
186
228
|
}
|
|
187
229
|
|
|
188
|
-
const /** @type {
|
|
230
|
+
const /** @type {Record<string, string>} */ i18nLanguages = {
|
|
189
231
|
zh: 'zh-hans', 'zh-hans': 'zh-hans', 'zh-cn': 'zh-hans', 'zh-my': 'zh-hans', 'zh-sg': 'zh-hans',
|
|
190
232
|
'zh-hant': 'zh-hant', 'zh-tw': 'zh-hant', 'zh-hk': 'zh-hant', 'zh-mo': 'zh-hant',
|
|
191
233
|
},
|
|
@@ -205,7 +247,7 @@
|
|
|
205
247
|
mw.messages.set(i18n);
|
|
206
248
|
};
|
|
207
249
|
|
|
208
|
-
const
|
|
250
|
+
const i18nPromise = Promise.all([ // 提前加载I18N
|
|
209
251
|
mw.loader.using('mediawiki.util'),
|
|
210
252
|
setI18N(),
|
|
211
253
|
]);
|
|
@@ -214,7 +256,6 @@
|
|
|
214
256
|
* 下载脚本
|
|
215
257
|
* @param {string[]} urls 脚本路径
|
|
216
258
|
* @param {boolean} local 是否从本地下载
|
|
217
|
-
* @returns {Promise<void>}
|
|
218
259
|
*/
|
|
219
260
|
const getScript = (urls, local) => {
|
|
220
261
|
if (urls.length === 0) {
|
|
@@ -229,7 +270,45 @@
|
|
|
229
270
|
};
|
|
230
271
|
|
|
231
272
|
// 以下进入CodeMirror相关内容
|
|
232
|
-
let /** @type {CodeMirror.
|
|
273
|
+
let /** @type {CodeMirror.EditorFromTextArea} */ cm;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {object} addon
|
|
277
|
+
* @property {string} option
|
|
278
|
+
* @property {string|string[]} addon
|
|
279
|
+
* @property {string} download
|
|
280
|
+
* @property {boolean} complex
|
|
281
|
+
* @property {string[]} modes
|
|
282
|
+
* @property {boolean} only
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
const /** @type {addon[]} */ options = [
|
|
286
|
+
{option: 'styleActiveLine', addon: 'activeLine'},
|
|
287
|
+
{option: 'styleSelectedText', addon: 'search', download: 'markSelection', only: true},
|
|
288
|
+
{option: 'showTrailingSpace', addon: 'trailingspace'},
|
|
289
|
+
{option: 'matchBrackets', complex: true},
|
|
290
|
+
{option: 'matchTags', addon: ['matchTags', 'fold'], modes: ['mediawiki', 'widget']},
|
|
291
|
+
{option: 'fold', modes: ['mediawiki', 'widget']},
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
/** @param {CodeMirror} CM */
|
|
295
|
+
const getAddonScript = (CM, other = false) => {
|
|
296
|
+
const /** @type {string[]} */ addonScript = [];
|
|
297
|
+
for (const {option, addon = option, download = Array.isArray(addon) ? option : addon, only} of options) {
|
|
298
|
+
if (!(only && other) && !CM.optionHandlers[option] && intersect(addon, addons)) {
|
|
299
|
+
addonScript.push(ADDON_LIST[download]);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return addonScript;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {array|any} arr1
|
|
307
|
+
* @param {array} arr2
|
|
308
|
+
*/
|
|
309
|
+
const intersect = (arr1, arr2) => Array.isArray(arr1)
|
|
310
|
+
? arr1.some(ele => arr2.includes(ele))
|
|
311
|
+
: arr2.includes(arr1);
|
|
233
312
|
|
|
234
313
|
/**
|
|
235
314
|
* 根据文本的高亮模式加载依赖项
|
|
@@ -259,7 +338,7 @@
|
|
|
259
338
|
mw.loader.load(`${CDN}/${MW_CDN}/mediawiki.min.css`, 'text/css');
|
|
260
339
|
(USING_LOCAL ? externalScript : scripts).push(`${MW_CDN}/mediawiki.min.js`);
|
|
261
340
|
}
|
|
262
|
-
if (type === 'mediawiki' &&
|
|
341
|
+
if (type === 'mediawiki' && SITE_SETTINGS.config && SITE_SETTINGS.config.tags.html) {
|
|
263
342
|
// NamespaceHTML扩展自由度过高,所以这里一律当作允许<html>标签
|
|
264
343
|
type = 'html'; // eslint-disable-line no-param-reassign
|
|
265
344
|
}
|
|
@@ -275,24 +354,7 @@
|
|
|
275
354
|
if (!CM.commands.findForward && addons.includes('search')) {
|
|
276
355
|
addonScript.push(ADDON_LIST.search);
|
|
277
356
|
}
|
|
278
|
-
|
|
279
|
-
addonScript.push(ADDON_LIST.activeLine);
|
|
280
|
-
}
|
|
281
|
-
if (!CM.optionHandlers.styleSelectedText && addons.includes('search')) {
|
|
282
|
-
addonScript.push(ADDON_LIST.markSelection);
|
|
283
|
-
}
|
|
284
|
-
if (!CM.optionHandlers.showTrailingSpace && addons.includes('trailingspace')) {
|
|
285
|
-
addonScript.push(ADDON_LIST.trailingspace);
|
|
286
|
-
}
|
|
287
|
-
if (!CM.optionHandlers.matchBrackets && addons.includes('matchBrackets')) {
|
|
288
|
-
addonScript.push(ADDON_LIST.matchBrackets);
|
|
289
|
-
}
|
|
290
|
-
if (!CM.optionHandlers.matchTags && ['matchTags', 'fold'].some(addon => addons.includes(addon))) {
|
|
291
|
-
addonScript.push(ADDON_LIST.matchTags);
|
|
292
|
-
}
|
|
293
|
-
if (!CM.optionHandlers.fold && addons.includes('fold')) {
|
|
294
|
-
addonScript.push(ADDON_LIST.fold);
|
|
295
|
-
}
|
|
357
|
+
addonScript.push(...getAddonScript(CM));
|
|
296
358
|
if (['widget', 'html'].includes(type)) {
|
|
297
359
|
['css', 'javascript', 'mediawiki', 'htmlmixed', 'xml'].forEach(lang => {
|
|
298
360
|
if (!CM.modes[lang]) {
|
|
@@ -351,14 +413,17 @@
|
|
|
351
413
|
await initModePromise;
|
|
352
414
|
}
|
|
353
415
|
|
|
354
|
-
let
|
|
416
|
+
let config = mw.config.get('extCodeMirrorConfig');
|
|
355
417
|
if (!config && !EXPIRED && isLatest) {
|
|
356
418
|
({config} = SITE_SETTINGS);
|
|
419
|
+
if (config.tags.ref) { // fix a bug in InPageEdit-v2
|
|
420
|
+
config.tagModes.ref = 'text/mediawiki';
|
|
421
|
+
}
|
|
357
422
|
mw.config.set('extCodeMirrorConfig', config);
|
|
358
423
|
}
|
|
359
424
|
if (config && config.redirect && config.img) { // 情形1:config已更新,可能来自localStorage
|
|
360
425
|
return config;
|
|
361
|
-
} else if (config) {
|
|
426
|
+
} else if (config) { /** @todo 暂不需要redirect和img相关设置 */
|
|
362
427
|
return config;
|
|
363
428
|
}
|
|
364
429
|
|
|
@@ -370,7 +435,7 @@
|
|
|
370
435
|
* @property {string[]} variables
|
|
371
436
|
*/
|
|
372
437
|
|
|
373
|
-
|
|
438
|
+
/*
|
|
374
439
|
* 以下情形均需要发送API请求
|
|
375
440
|
* 情形2:localStorage未过期但不包含新设置
|
|
376
441
|
* 情形3:新加载的 ext.CodeMirror.data
|
|
@@ -389,13 +454,12 @@
|
|
|
389
454
|
* @param {{aliases: string[], name: string}[]} words
|
|
390
455
|
* @returns {{alias: string, name: string}[]}
|
|
391
456
|
*/
|
|
392
|
-
const getAliases = words =>
|
|
393
|
-
|
|
394
|
-
({aliases, name}) => aliases.map(alias => ({alias, name})),
|
|
457
|
+
const getAliases = words => flat.call(
|
|
458
|
+
words.map(({aliases, name}) => aliases.map(alias => ({alias, name}))),
|
|
395
459
|
);
|
|
396
460
|
/**
|
|
397
461
|
* @param {{alias: string, name: string}[]} aliases
|
|
398
|
-
* @returns {
|
|
462
|
+
* @returns {Record<string, string>}
|
|
399
463
|
*/
|
|
400
464
|
const getConfig = aliases => fromEntries(
|
|
401
465
|
aliases.map(({alias, name}) => [alias.replace(/:$/, ''), name]),
|
|
@@ -413,10 +477,8 @@
|
|
|
413
477
|
),
|
|
414
478
|
urlProtocols: mw.config.get('wgUrlProtocols'),
|
|
415
479
|
};
|
|
416
|
-
/** @type {Set<string>} */
|
|
417
480
|
const realMagicwords = new Set([...functionhooks, ...variables, ...otherMagicwords]),
|
|
418
481
|
allMagicwords = magicwords.filter(
|
|
419
|
-
/** @returns {boolean} */
|
|
420
482
|
({name, aliases}) => aliases.some(alias => /^__.+__$/.test(alias)) || realMagicwords.has(name),
|
|
421
483
|
),
|
|
422
484
|
sensitive = getAliases(
|
|
@@ -437,15 +499,15 @@
|
|
|
437
499
|
const {functionSynonyms: [insensitive]} = config;
|
|
438
500
|
if (!insensitive.subst) {
|
|
439
501
|
getAliases(
|
|
440
|
-
magicwords.filter(
|
|
502
|
+
magicwords.filter(({name}) => otherMagicwords.includes(name)),
|
|
441
503
|
).forEach(({alias, name}) => {
|
|
442
504
|
insensitive[alias.replace(/:$/, '')] = name;
|
|
443
505
|
});
|
|
444
506
|
}
|
|
445
507
|
}
|
|
446
|
-
config.redirect = magicwords.find(
|
|
508
|
+
config.redirect = magicwords.find(({name}) => name === 'redirect').aliases;
|
|
447
509
|
config.img = getConfig(
|
|
448
|
-
getAliases(magicwords.filter(
|
|
510
|
+
getAliases(magicwords.filter(({name}) => name.startsWith('img_'))),
|
|
449
511
|
);
|
|
450
512
|
mw.config.set('extCodeMirrorConfig', config);
|
|
451
513
|
updateCachedConfig(config);
|
|
@@ -457,7 +519,7 @@
|
|
|
457
519
|
if ([274, 828].includes(ns) && !page.endsWith('/doc')) {
|
|
458
520
|
const pageMode = ns === 274 ? 'Widget' : 'Lua';
|
|
459
521
|
await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']);
|
|
460
|
-
const
|
|
522
|
+
const bool = await OO.ui.confirm(msg('contentmodel'), {
|
|
461
523
|
actions: [
|
|
462
524
|
{label: pageMode},
|
|
463
525
|
{label: 'Wikitext', action: 'accept'},
|
|
@@ -478,7 +540,7 @@
|
|
|
478
540
|
const renderEditor = async ($target, setting) => {
|
|
479
541
|
const mode = setting ? 'javascript' : await getPageMode();
|
|
480
542
|
const initModePromise = initMode(mode);
|
|
481
|
-
const
|
|
543
|
+
const [mwConfig] = await Promise.all([
|
|
482
544
|
getMwConfig(mode, initModePromise),
|
|
483
545
|
initModePromise,
|
|
484
546
|
]);
|
|
@@ -503,7 +565,7 @@
|
|
|
503
565
|
}
|
|
504
566
|
|
|
505
567
|
const json = setting || contentmodel === 'json',
|
|
506
|
-
|
|
568
|
+
{name} = $.client.profile();
|
|
507
569
|
cm = CodeMirror.fromTextArea($target[0], $.extend({
|
|
508
570
|
inputStyle: name === 'safari' ? 'textarea' : 'contenteditable',
|
|
509
571
|
lineNumbers: true,
|
|
@@ -511,15 +573,16 @@
|
|
|
511
573
|
mode,
|
|
512
574
|
mwConfig,
|
|
513
575
|
json,
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
576
|
+
}, fromEntries(
|
|
577
|
+
options.filter(({complex}) => !complex).map(({option, addon = option, modes}) => {
|
|
578
|
+
const mainAddon = Array.isArray(addon) ? addon[0] : addon;
|
|
579
|
+
return [option, addons.includes(mainAddon) && (!modes || modes.includes(mode))];
|
|
580
|
+
}),
|
|
581
|
+
), {
|
|
517
582
|
matchBrackets: addons.includes('matchBrackets') && (mode === 'mediawiki' || json
|
|
518
583
|
? {bracketRegex: /[{}[\]]/}
|
|
519
584
|
: true
|
|
520
585
|
),
|
|
521
|
-
matchTags: addons.includes('matchTags') && ['mediawiki', 'widget'].includes(mode),
|
|
522
|
-
fold: addons.includes('fold') && ['mediawiki', 'widget'].includes(mode),
|
|
523
586
|
}, mode === 'mediawiki'
|
|
524
587
|
? {}
|
|
525
588
|
: {
|
|
@@ -530,44 +593,7 @@
|
|
|
530
593
|
cm.setSize(null, height);
|
|
531
594
|
cm.refresh();
|
|
532
595
|
|
|
533
|
-
|
|
534
|
-
wrapper.id = 'Wikiplus-CodeMirror';
|
|
535
|
-
if (['mediawiki', 'widget'].includes(mode) && addons.includes('contextmenu')) {
|
|
536
|
-
contextmenuStyle.disabled = false;
|
|
537
|
-
const /** @type {mwConfig} */ {functionSynonyms: [synonyms]} = mw.config.get('extCodeMirrorConfig');
|
|
538
|
-
/** @param {string} str */
|
|
539
|
-
const getSysnonyms = str => Object.keys(synonyms).filter(key => synonyms[key] === str)
|
|
540
|
-
.map(key => key.startsWith('#') ? key : `#${key}`);
|
|
541
|
-
const invoke = getSysnonyms('invoke'),
|
|
542
|
-
widget = getSysnonyms('widget');
|
|
543
|
-
|
|
544
|
-
await mw.loader.using('mediawiki.Title');
|
|
545
|
-
$(wrapper).on('contextmenu', '.cm-mw-template-name', function() {
|
|
546
|
-
const /** @type {string} */ text = this.textContent.replace(/\u200e/g, '').trim(),
|
|
547
|
-
/** @type {{namespace: number, getUrl: () => string}} */ title = new mw.Title(text);
|
|
548
|
-
if (title.namespace !== 0 || text.startsWith(':')) {
|
|
549
|
-
open(title.getUrl(), '_blank');
|
|
550
|
-
} else {
|
|
551
|
-
open(mw.util.getUrl(`Template:${text}`), '_blank');
|
|
552
|
-
}
|
|
553
|
-
return false;
|
|
554
|
-
}).on(
|
|
555
|
-
'contextmenu',
|
|
556
|
-
'.cm-mw-parserfunction-name + .cm-mw-parserfunction-delimiter + .cm-mw-parserfunction',
|
|
557
|
-
function() {
|
|
558
|
-
/** @type {string} */
|
|
559
|
-
const parserFunction = this.previousSibling.previousSibling.textContent.trim().toLowerCase();
|
|
560
|
-
if (invoke.includes(parserFunction)) {
|
|
561
|
-
open(mw.util.getUrl(`Module:${this.textContent}`), '_blank');
|
|
562
|
-
} else if (widget.includes(parserFunction)) {
|
|
563
|
-
open(mw.util.getUrl(`Widget:${this.textContent}`, {action: 'edit'}), '_blank');
|
|
564
|
-
}
|
|
565
|
-
return false;
|
|
566
|
-
},
|
|
567
|
-
);
|
|
568
|
-
} else {
|
|
569
|
-
contextmenuStyle.disabled = true;
|
|
570
|
-
}
|
|
596
|
+
handleContextMenu(cm, mode);
|
|
571
597
|
|
|
572
598
|
$('#Wikiplus-Quickedit-Jump').children('a').attr('href', '#Wikiplus-CodeMirror');
|
|
573
599
|
|
|
@@ -599,12 +625,8 @@
|
|
|
599
625
|
|
|
600
626
|
// 监视 Wikiplus 编辑框
|
|
601
627
|
const observer = new MutationObserver(records => {
|
|
602
|
-
const $editArea = $(
|
|
603
|
-
|
|
604
|
-
* @param {{addedNodes: NodeList}}
|
|
605
|
-
* @returns {Node[]}
|
|
606
|
-
*/
|
|
607
|
-
({addedNodes}) => [...addedNodes],
|
|
628
|
+
const $editArea = $(flat.call(
|
|
629
|
+
records.map(({addedNodes}) => [...addedNodes]),
|
|
608
630
|
)).find('#Wikiplus-Quickedit, #Wikiplus-Setting-Input');
|
|
609
631
|
if ($editArea.length === 0) {
|
|
610
632
|
return;
|
|
@@ -614,7 +636,7 @@
|
|
|
614
636
|
observer.observe(document.body, {childList: true});
|
|
615
637
|
|
|
616
638
|
// 添加样式
|
|
617
|
-
const
|
|
639
|
+
const wphlStyle = document.getElementById('wphl-style') || mw.loader.addStyleTag(
|
|
618
640
|
'#Wikiplus-Quickedit+.CodeMirror,#Wikiplus-Setting-Input+.CodeMirror'
|
|
619
641
|
+ '{border:1px solid #c8ccd1;line-height:1.3;clear:both}'
|
|
620
642
|
+ 'div.Wikiplus-InterBox{font-size:14px;z-index:100}'
|
|
@@ -623,7 +645,8 @@
|
|
|
623
645
|
+ 'div.CodeMirror span.CodeMirror-matchingbracket{box-shadow:0 0 0 2px #9aef98}'
|
|
624
646
|
+ 'div.CodeMirror span.CodeMirror-nonmatchingbracket{box-shadow:0 0 0 2px #eace64}'
|
|
625
647
|
+ '#Wikiplus-highlight-dialog .oo-ui-messageDialog-title{margin-bottom:0.28571429em}'
|
|
626
|
-
+ '#Wikiplus-highlight-dialog .oo-ui-flaggedElement-notice{font-weight:normal;margin:0}'
|
|
648
|
+
+ '#Wikiplus-highlight-dialog .oo-ui-flaggedElement-notice{font-weight:normal;margin:0}'
|
|
649
|
+
+ '.CodeMirror-contextmenu .cm-mw-template-name{cursor:pointer}',
|
|
627
650
|
);
|
|
628
651
|
wphlStyle.id = 'wphl-style';
|
|
629
652
|
|
|
@@ -639,10 +662,7 @@
|
|
|
639
662
|
elem.value = value;
|
|
640
663
|
},
|
|
641
664
|
} = $.valHooks.textarea || {};
|
|
642
|
-
/**
|
|
643
|
-
* @param {HTMLTextAreaElement} elem
|
|
644
|
-
* @returns {boolean}
|
|
645
|
-
*/
|
|
665
|
+
/** @param {HTMLTextAreaElement} elem */
|
|
646
666
|
const isWikiplus = elem => ['Wikiplus-Quickedit', 'Wikiplus-Setting-Input'].includes(elem.id);
|
|
647
667
|
$.valHooks.textarea = {
|
|
648
668
|
get(elem) {
|
|
@@ -659,21 +679,12 @@
|
|
|
659
679
|
|
|
660
680
|
await i18nPromise; // 以下内容依赖I18N
|
|
661
681
|
|
|
662
|
-
/**
|
|
663
|
-
* @typedef {object} OOUI.widget
|
|
664
|
-
* @property {(data: any) => {closing: Promise<{action: string}>}} open
|
|
665
|
-
* @property {() => string|string[]} getValue
|
|
666
|
-
* @property {JQuery<HTMLDivElement>} $element
|
|
667
|
-
* @property {(show: boolean) => OOUI.widget} toggle
|
|
668
|
-
* @property {(windows: OOUI.widget[]) => void} addWindows
|
|
669
|
-
*/
|
|
670
|
-
|
|
671
682
|
// 设置对话框
|
|
672
|
-
let /** @type {OOUI.
|
|
673
|
-
/** @type {OOUI.
|
|
674
|
-
/** @type {OOUI.
|
|
675
|
-
/** @type {OOUI.
|
|
676
|
-
/** @type {OOUI.
|
|
683
|
+
let /** @type {OOUI.DialogWidget} */ dialog,
|
|
684
|
+
/** @type {OOUI.MultiInputWidget} */ widget,
|
|
685
|
+
/** @type {OOUI.InputWidget} */ indentWidget,
|
|
686
|
+
/** @type {OOUI.LayoutWidget} */ field,
|
|
687
|
+
/** @type {OOUI.LayoutWidget} */ indentField;
|
|
677
688
|
const toggleIndent = (value = addons) => {
|
|
678
689
|
indentField.toggle(value.includes('indentWithSpace'));
|
|
679
690
|
};
|
|
@@ -681,7 +692,7 @@
|
|
|
681
692
|
minerva: 'page-actions-overflow',
|
|
682
693
|
citizen: 'p-actions',
|
|
683
694
|
};
|
|
684
|
-
const
|
|
695
|
+
const $portlet = $(mw.util.addPortletLink(
|
|
685
696
|
portletContainer[skin] || 'p-cactions', '#', msg('portlet'), 'wphl-settings',
|
|
686
697
|
)).click(async e => {
|
|
687
698
|
e.preventDefault();
|
|
@@ -689,7 +700,7 @@
|
|
|
689
700
|
await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']);
|
|
690
701
|
// eslint-disable-next-line require-atomic-updates
|
|
691
702
|
dialog = new OO.ui.MessageDialog({id: 'Wikiplus-highlight-dialog'});
|
|
692
|
-
const
|
|
703
|
+
const windowManager = new OO.ui.WindowManager();
|
|
693
704
|
windowManager.$element.appendTo(document.body);
|
|
694
705
|
windowManager.addWindows([dialog]);
|
|
695
706
|
widget = new OO.ui.CheckboxMultiselectInputWidget({
|
|
@@ -702,6 +713,10 @@
|
|
|
702
713
|
{data: 'fold', label: msg('addon-fold')},
|
|
703
714
|
{data: 'contextmenu', label: msg('addon-contextmenu')},
|
|
704
715
|
{data: 'indentWithSpace', label: msg('addon-indentwithspace')},
|
|
716
|
+
{
|
|
717
|
+
data: 'otherEditors',
|
|
718
|
+
label: msg(msg('version') === '2.12' ? 'addon-othereditos' : 'addon-othereditors'),
|
|
719
|
+
},
|
|
705
720
|
],
|
|
706
721
|
value: addons,
|
|
707
722
|
}).on('change', toggleIndent);
|
|
@@ -714,6 +729,9 @@
|
|
|
714
729
|
indentField = new OO.ui.FieldLayout(indentWidget, {label: msg('addon-indent')});
|
|
715
730
|
toggleIndent();
|
|
716
731
|
}
|
|
732
|
+
const wikiplusLoaded = typeof window.Wikiplus === 'object';
|
|
733
|
+
widget.$element.find('.oo-ui-checkboxInputWidget').first().toggleClass('oo-ui-widget-enabled', wikiplusLoaded)
|
|
734
|
+
.children('input').prop('disabled', !wikiplusLoaded);
|
|
717
735
|
dialog.open({
|
|
718
736
|
title: msg('addon-title'),
|
|
719
737
|
message: field.$element.add(indentField.$element).add(
|
|
@@ -746,4 +764,37 @@
|
|
|
746
764
|
$('#wphl-settings').triggerHandler('click');
|
|
747
765
|
});
|
|
748
766
|
}
|
|
767
|
+
|
|
768
|
+
/** @param {CodeMirror.Editor} doc */
|
|
769
|
+
const handleOtherEditors = async doc => {
|
|
770
|
+
if (!addons.includes('otherEditors')) {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
let mode = doc.getOption('mode');
|
|
774
|
+
mode = mode === 'text/mediawiki' ? 'mediawiki' : mode;
|
|
775
|
+
const addonScript = getAddonScript(CodeMirror, true);
|
|
776
|
+
await getScript(addonScript);
|
|
777
|
+
for (const {option, addon = option, modes} of options.filter(({only, complex}) => !(only || complex))) {
|
|
778
|
+
const mainAddon = Array.isArray(addon) ? addon[0] : addon;
|
|
779
|
+
if (doc.getOption(option) === undefined && addons.includes(mainAddon) && (!modes || modes.includes(mode))) {
|
|
780
|
+
doc.setOption(option, true);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (doc.getOption('matchBrackets') === undefined && addons.includes('matchBrackets')) {
|
|
784
|
+
doc.setOption('matchBrackets', mode === 'mediawiki' || doc.getOption('json')
|
|
785
|
+
? {bracketRegex: /[{}[\]]/}
|
|
786
|
+
: true,
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
if (mode !== 'mediawiki' && addons.includes('indentWithSpace')) {
|
|
790
|
+
doc.setOption('indentUnit', indent);
|
|
791
|
+
doc.setOption('indentWithTabs', false);
|
|
792
|
+
}
|
|
793
|
+
handleContextMenu(doc, mode);
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
mw.hook('InPageEdit.quickEdit.codemirror').add(
|
|
797
|
+
/** @param {{cm: CodeMirror.Editor}} */ ({cm: doc}) => handleOtherEditors(doc),
|
|
798
|
+
);
|
|
799
|
+
mw.hook('inspector').add(/** @param {CodeMirror.Editor} doc */ doc => handleOtherEditors(doc));
|
|
749
800
|
})();
|