wikiplus-highlight 2.22.5 → 2.25.3

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/main.js CHANGED
@@ -14,8 +14,8 @@
14
14
  }
15
15
  mw.libs.wphl = {}; // 开始加载
16
16
 
17
- const version = '2.22.5',
18
- newAddon = 0;
17
+ const version = '2.25.2',
18
+ newAddon = 1;
19
19
 
20
20
  /** @type {typeof mw.storage} */
21
21
  const storage = typeof mw.storage === 'object' && typeof mw.storage.getObject === 'function'
@@ -43,12 +43,7 @@
43
43
  },
44
44
  };
45
45
 
46
- /**
47
- * polyfill for `Object.fromEntries`
48
- * @type {(entries: Iterable<[string, T]>) => Record<string, T>}
49
- * @template T
50
- */
51
- const fromEntries = Object.fromEntries || (entries => { // eslint-disable-line es-x/no-object-fromentries
46
+ Object.fromEntries = Object.fromEntries || (entries => {
52
47
  const /** @type {Record<string, T>} */ obj = {};
53
48
  for (const [key, value] of entries) {
54
49
  obj[key] = value;
@@ -58,15 +53,13 @@
58
53
 
59
54
  /**
60
55
  * polyfill for `Array.prototype.flat`
61
- * @type {(arr: HTMLElement[][]) => HTMLElement[]}
56
+ * @template {*} T
57
+ * @param {(T|T[])[]} arr 任意数组
58
+ * @returns {T[]}
62
59
  */
63
- const flatten = arr => {
64
- if (typeof arr.flat === 'function') {
65
- return arr.flat();
66
- }
67
- // eslint-disable-next-line unicorn/no-array-reduce
68
- return arr.reduce((acc, cur) => acc.concat(cur), []);
69
- };
60
+ const flatten = arr => typeof arr.flat === 'function'
61
+ ? arr.flat()
62
+ : arr.reduce((acc, cur) => acc.concat(cur), []); // eslint-disable-line unicorn/no-array-reduce
70
63
 
71
64
  /**
72
65
  * 解析版本号
@@ -116,6 +109,7 @@
116
109
  const CDN = '//fastly.jsdelivr.net',
117
110
  CM_CDN = 'npm/codemirror@5.65.3',
118
111
  MW_CDN = 'gh/bhsd-harry/codemirror-mediawiki@1.1.6',
112
+ PARSER_CDN = 'gh/bhsd-harry/wikiparser-node@0.5.4-b',
119
113
  REPO_CDN = `npm/wikiplus-highlight@${majorVersion}`;
120
114
 
121
115
  const {config: {values: {
@@ -176,6 +170,10 @@
176
170
  fold: `${REPO_CDN}/fold.min.js`,
177
171
  wikiEditor: 'ext.wikiEditor',
178
172
  contextmenu: 'mediawiki.Title',
173
+ lint: `${CM_CDN}/addon/lint/lint.min.js`,
174
+ annotateScrollbar: `${CM_CDN}/addon/scroll/annotatescrollbar.min.js`,
175
+ parser: `${PARSER_CDN}/bundle/bundle.min.js`,
176
+ lintWikitext: `${REPO_CDN}/lint.min.js`,
179
177
  };
180
178
 
181
179
  /**
@@ -216,9 +214,9 @@
216
214
  ];
217
215
 
218
216
  const defaultAddons = ['search'],
219
- defaultIndent = 4;
220
- let /** @type {Set<string>} */ addons = new Set(storage.getObject('Wikiplus-highlight-addons') || defaultAddons),
221
- /** @type {number} */ indent = storage.getObject('Wikiplus-highlight-indent') || defaultIndent;
217
+ defaultIndent = 4,
218
+ /** @type {Set<string>} */ addons = new Set(storage.getObject('Wikiplus-highlight-addons') || defaultAddons);
219
+ let /** @type {number} */ indent = storage.getObject('Wikiplus-highlight-indent') || defaultIndent;
222
220
 
223
221
  /** @type {Record<string, string>} */
224
222
  const entity = {'"': 'quot', "'": 'apos', '<': 'lt', '>': 'gt', '&': 'amp', ' ': 'nbsp'},
@@ -303,10 +301,9 @@
303
301
  });
304
302
  };
305
303
 
306
- let /** @type {Record<string, string>} */ i18n = storage.getObject('Wikiplus-highlight-i18n'),
307
- /** @type {() => JQuery<HTMLElement>} */ welcome;
308
- if (!i18n) { // 首次安装
309
- i18n = {};
304
+ const /** @type {Record<string, string>} */ i18n = storage.getObject('Wikiplus-highlight-i18n') || {};
305
+ let /** @type {() => JQuery<HTMLElement>} */ welcome;
306
+ if (!i18n['wphl-version']) { // 首次安装
310
307
  welcome = notify('welcome');
311
308
  } else if (cmpVersion(i18n['wphl-version'], version)) { // 更新版本
312
309
  welcome = notify(`welcome-${newAddon ? 'new-addon' : 'upgrade'}`, version, newAddon);
@@ -331,10 +328,10 @@
331
328
  /** 加载 I18N */
332
329
  const setI18N = async () => {
333
330
  if (!isLatest || i18n['wphl-lang'] !== i18nLang) {
334
- i18n = await $.ajax(`${I18N_CDN}`, { // eslint-disable-line require-atomic-updates
331
+ Object.assign(i18n, await $.ajax(`${I18N_CDN}`, { // eslint-disable-line require-atomic-updates
335
332
  dataType: 'json',
336
333
  cache: true,
337
- });
334
+ }));
338
335
  storage.setObject('Wikiplus-highlight-i18n', i18n);
339
336
  }
340
337
  mw.messages.set(i18n);
@@ -417,7 +414,9 @@
417
414
  * 代替`CodeMirror`的局部变量
418
415
  * @type {typeof CodeMirror}
419
416
  */
420
- const CM = loaded ? window.CodeMirror : {modes: {}, prototype: {}, commands: {}, optionHandlers: {}};
417
+ const CM = loaded
418
+ ? window.CodeMirror
419
+ : {modes: {}, prototype: {}, commands: {}, optionHandlers: {}, helpers: {}};
421
420
 
422
421
  // lib
423
422
  if (!loaded) {
@@ -451,9 +450,22 @@
451
450
  if (!CM.prototype.getSearchCursor && addons.has('search') && !addons.has('wikiEditor')) {
452
451
  scripts.push(ADDON_LIST.searchcursor);
453
452
  }
453
+ if (!CM.prototype.annotateScrollbar && type === 'mediawiki' && addons.has('lint')) {
454
+ scripts.push(ADDON_LIST.annotateScrollbar);
455
+ }
454
456
  if (!CM.commands.findForward && addons.has('search') && !addons.has('wikiEditor')) {
455
457
  scripts.push(ADDON_LIST.search);
456
458
  }
459
+ if (!window.Parser && type === 'mediawiki' && addons.has('lint')) {
460
+ scripts.push(ADDON_LIST.parser);
461
+ }
462
+ if (!CM.optionHandlers.lint && type === 'mediawiki' && addons.has('lint')) {
463
+ mw.loader.load(`${CDN}/${CM_CDN}/addon/lint/lint.min.css`, 'text/css');
464
+ scripts.push(ADDON_LIST.lint);
465
+ }
466
+ if (!(CM.helpers.lint && CM.helpers.lint.mediawiki) && type === 'mediawiki' && addons.has('lint')) {
467
+ scripts.push(ADDON_LIST.lintWikitext);
468
+ }
457
469
  if (addons.has('wikiEditor')) {
458
470
  const state = mw.loader.getState('ext.wikiEditor');
459
471
  if (!state) {
@@ -482,18 +494,15 @@
482
494
  /**
483
495
  * 展开别名列表
484
496
  * @param {{aliases: string[], name: string}[]} words 原名
485
- * @returns {{alias: string, name: string}[]}
486
497
  */
487
- const getAliases = words => flatten(
488
- words.map(({aliases, name}) => aliases.map(alias => ({alias, name}))),
489
- );
498
+ const getAliases = words => flatten(words.map(({aliases, name}) => aliases.map(alias => ({alias, name}))));
490
499
 
491
500
  /**
492
501
  * 将别名信息转换为CodeMirror接受的设置
493
502
  * @param {{alias: string, name: string}[]} aliases 别名
494
503
  * @returns {Record<string, string>}
495
504
  */
496
- const getConfig = aliases => fromEntries(
505
+ const getConfig = aliases => Object.fromEntries(
497
506
  aliases.map(({alias, name}) => [alias.replace(/:$/, ''), name]),
498
507
  );
499
508
 
@@ -521,8 +530,6 @@
521
530
  }
522
531
  if (config && config.redirect && config.img) { // 情形1:config已更新,可能来自localStorage
523
532
  return config;
524
- } else if (config) { /** @todo 暂不需要`redirect`和`img`相关设置 */
525
- return config;
526
533
  }
527
534
 
528
535
  /*
@@ -553,7 +560,7 @@
553
560
  nowiki: 'mw-tag-nowiki',
554
561
  ref: 'text/mediawiki',
555
562
  },
556
- tags: fromEntries(
563
+ tags: Object.fromEntries(
557
564
  extensiontags.map(tag => [tag.slice(1, -1), true]),
558
565
  ),
559
566
  urlProtocols: mw.config.get('wgUrlProtocols'),
@@ -588,17 +595,17 @@
588
595
 
589
596
  /** 检查页面语言类型 */
590
597
  const getPageMode = async () => {
591
- if ((ns === 274 || ns === 828) && !page.endsWith('/doc')) {
592
- const pageMode = ns === 274 ? 'Widget' : 'Lua';
593
- await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']);
594
- const bool = await OO.ui.confirm(msg('contentmodel'), {
595
- actions: [{label: pageMode}, {label: 'Wikitext', action: 'accept'}],
596
- });
597
- return bool ? 'mediawiki' : pageMode.toLowerCase();
598
- } else if (page.endsWith('/doc')) {
598
+ if (page.endsWith('/doc')) {
599
599
  return 'mediawiki';
600
+ } else if (ns !== 274 && ns !== 828) {
601
+ return CONTENTMODEL[contentmodel];
600
602
  }
601
- return CONTENTMODEL[contentmodel];
603
+ const pageMode = ns === 274 ? 'Widget' : 'Lua';
604
+ await mw.loader.using(['oojs-ui-windows', 'oojs-ui.styles.icons-content']);
605
+ const bool = await OO.ui.confirm(msg('contentmodel'), {
606
+ actions: [{label: pageMode}, {label: 'Wikitext', action: 'accept'}],
607
+ });
608
+ return bool ? 'mediawiki' : pageMode.toLowerCase();
602
609
  };
603
610
 
604
611
  /**
@@ -631,10 +638,7 @@
631
638
  /** @override */ getCaretPosition(option) {
632
639
  const caretPos = cm.indexFromPos(cm.getCursor('from')),
633
640
  endPos = cm.indexFromPos(cm.getCursor('to'));
634
- if (option.startAndEnd) {
635
- return [caretPos, endPos];
636
- }
637
- return caretPos;
641
+ return option.startAndEnd ? [caretPos, endPos] : caretPos;
638
642
  },
639
643
  /** @override */ scrollToCaretPosition() {
640
644
  cm.scrollIntoView();
@@ -698,7 +702,7 @@
698
702
  mwConfig,
699
703
  json,
700
704
  },
701
- fromEntries(
705
+ Object.fromEntries(
702
706
  options.map(({option, addon = option, modes, complex = mod => !modes || modes.includes(mod)}) => {
703
707
  const mainAddon = Array.isArray(addon) ? addon[0] : addon;
704
708
  return [option, addons.has(mainAddon) && complex(mode, json)];
@@ -772,9 +776,8 @@
772
776
 
773
777
  // 监视 Wikiplus 编辑框
774
778
  const observer = new MutationObserver(records => {
775
- const $editArea = $(flatten(
776
- records.map(({addedNodes}) => [...addedNodes]),
777
- )).find('#Wikiplus-Quickedit, #Wikiplus-Setting-Input');
779
+ const $editArea = $(flatten(records.map(({addedNodes}) => [...addedNodes])))
780
+ .find('#Wikiplus-Quickedit, #Wikiplus-Setting-Input');
778
781
  if ($editArea.length === 0) {
779
782
  return;
780
783
  }
@@ -851,6 +854,7 @@
851
854
  /** @type {OOUI.CheckboxMultiselectInputWidget} */ widget,
852
855
  /** @type {OOUI.CheckboxMultioptionWidget} */ searchWidget,
853
856
  /** @type {OOUI.CheckboxMultioptionWidget} */ wikiEditorWidget,
857
+ /** @type {OOUI.CheckboxMultioptionWidget} */ lintWidget,
854
858
  /** @type {OOUI.NumberInputWidget} */ indentWidget,
855
859
  /** @type {OOUI.FieldLayout} */ field,
856
860
  /** @type {OOUI.FieldLayout} */ indentField;
@@ -886,7 +890,7 @@
886
890
  const mainAddon = Array.isArray(addon) ? addon[0] : addon;
887
891
  return {data: mainAddon, label: htmlMsg(`addon-${mainAddon.toLowerCase()}`)};
888
892
  }),
889
- ...['wikiEditor', 'escape', 'contextmenu', 'indentWithSpace', 'otherEditors']
893
+ ...['wikiEditor', 'escape', 'contextmenu', 'lint', 'indentWithSpace', 'otherEditors']
890
894
  .map(addon => ({data: addon, label: htmlMsg(`addon-${addon.toLowerCase()}`)})),
891
895
  ],
892
896
  value: [...addons],
@@ -894,6 +898,7 @@
894
898
  const {checkboxMultiselectWidget} = widget;
895
899
  searchWidget = checkboxMultiselectWidget.findItemFromData('search');
896
900
  wikiEditorWidget = checkboxMultiselectWidget.findItemFromData('wikiEditor');
901
+ lintWidget = checkboxMultiselectWidget.findItemFromData('lint');
897
902
  indentWidget = new OO.ui.NumberInputWidget({min: 0, value: indent});
898
903
  field = new OO.ui.FieldLayout(widget, {
899
904
  label: msg('addon-label'),
@@ -907,6 +912,7 @@
907
912
  const wikiplusLoaded = typeof window.Wikiplus === 'object' || typeof window.Pages === 'object';
908
913
  searchWidget.setDisabled(!wikiplusLoaded);
909
914
  wikiEditorWidget.setDisabled(!wikiplusLoaded || !mw.loader.getState('ext.wikiEditor'));
915
+ lintWidget.setDisabled(!wikiplusLoaded);
910
916
  const data = await dialog.open({
911
917
  title: msg('addon-title'),
912
918
  message: field.$element.add(indentField.$element).add(
@@ -923,7 +929,10 @@
923
929
  if (typeof data === 'object' && data.action === 'accept') {
924
930
  /* eslint-disable require-atomic-updates */
925
931
  const value = widget.getValue();
926
- addons = new Set(value);
932
+ addons.clear();
933
+ for (const addon of value) {
934
+ addons.add(addon);
935
+ }
927
936
  indent = Number(indentWidget.getValue());
928
937
  storage.setObject('Wikiplus-highlight-addons', value);
929
938
  storage.setObject('Wikiplus-highlight-indent', indent);
@@ -963,11 +972,11 @@
963
972
  doc.setOption(option, complex(mode, json));
964
973
  }
965
974
  }
966
- if (mode !== 'mediawiki' && addons.has('indentWithSpace')) {
975
+ if (mode === 'mediawiki' && addons.has('escape')) {
976
+ doc.addKeyMap(isPc(CodeMirror) ? extraKeysPc : extraKeysMac, true);
977
+ } else if (mode !== 'mediawiki' && addons.has('indentWithSpace')) {
967
978
  doc.setOption('indentUnit', indent);
968
979
  doc.setOption('indentWithTabs', false);
969
- } else if (mode === 'mediawiki' && addons.has('escape')) {
970
- doc.addKeyMap(isPc(CodeMirror) ? extraKeysPc : extraKeysMac, true);
971
980
  }
972
981
  handleContextMenu(doc, mode);
973
982
  };
@@ -976,6 +985,11 @@
976
985
  /** @param {{cm: CodeMirror.Editor}} */ ({cm: doc}) => handleOtherEditors(doc),
977
986
  );
978
987
  mw.hook('inspector').add(/** @param {CodeMirror.Editor} doc */ doc => handleOtherEditors(doc));
988
+ mw.hook('wiki-codemirror').add(/** @param {CodeMirror.Editor} doc */ doc => {
989
+ if (!doc.getTextArea || !isWikiplus(doc.getTextArea())) {
990
+ handleOtherEditors(doc);
991
+ }
992
+ });
979
993
 
980
994
  mw.libs.wphl = {
981
995
  version,
package/matchtags.js CHANGED
@@ -72,10 +72,9 @@
72
72
  return undefined;
73
73
  }
74
74
  this.ch = gt + 1;
75
- if (!this.bracketAt(gt)) {
76
- continue;
75
+ if (this.bracketAt(gt)) {
76
+ return this.text[gt - 1] === '/' ? 'selfClose' : 'regular';
77
77
  }
78
- return this.text[gt - 1] === '/' ? 'selfClose' : 'regular';
79
78
  }
80
79
  }
81
80
 
@@ -85,8 +84,7 @@
85
84
  const lt = this.ch ? this.text.lastIndexOf('<', this.ch - 1) : -1;
86
85
  if (lt === -1) {
87
86
  return undefined;
88
- }
89
- if (!this.bracketAt(lt)) {
87
+ } else if (!this.bracketAt(lt)) {
90
88
  this.ch = lt;
91
89
  continue;
92
90
  }
@@ -111,12 +109,10 @@
111
109
  return undefined;
112
110
  }
113
111
  }
114
- if (!this.bracketAt(found.index)) {
115
- this.ch = found.index + found[0].length;
116
- continue;
117
- }
118
112
  this.ch = found.index + found[0].length;
119
- return found;
113
+ if (this.bracketAt(found.index)) {
114
+ return found;
115
+ }
120
116
  }
121
117
  }
122
118
 
@@ -131,14 +127,13 @@
131
127
  return undefined;
132
128
  }
133
129
  }
134
- if (!this.bracketAt(gt)) {
135
- this.ch = gt;
136
- continue;
130
+ if (this.bracketAt(gt)) {
131
+ const lastSlash = this.text.lastIndexOf('/', gt);
132
+ const selfClose = lastSlash > -1 && !/\S/u.test(this.text.slice(lastSlash + 1, gt));
133
+ this.ch = gt + 1;
134
+ return selfClose ? 'selfClose' : 'regular';
137
135
  }
138
- const lastSlash = this.text.lastIndexOf('/', gt);
139
- const selfClose = lastSlash > -1 && !/\S/u.test(this.text.slice(lastSlash + 1, gt));
140
- this.ch = gt + 1;
141
- return selfClose ? 'selfClose' : 'regular';
136
+ this.ch = gt;
142
137
  }
143
138
  }
144
139
 
@@ -160,11 +155,9 @@
160
155
  tagName = next[2].toLowerCase();
161
156
  if (!end) {
162
157
  return undefined;
163
- }
164
- if (end === 'selfClose' || voidTags.includes(tagName)) {
158
+ } else if (end === 'selfClose' || voidTags.includes(tagName)) {
165
159
  continue;
166
- }
167
- if (next[1]) { // closing tag
160
+ } else if (next[1]) { // closing tag
168
161
  let i = stack.length - 1;
169
162
  for (; i >= 0; --i) {
170
163
  if (stack[i] === tagName) {
@@ -202,8 +195,7 @@
202
195
  const tagName = start[2].toLowerCase();
203
196
  if (prev === 'selfClose' || voidTags.includes(tagName)) {
204
197
  continue;
205
- }
206
- if (start[1]) { // closing tag
198
+ } else if (start[1]) { // closing tag
207
199
  stack.push(tagName);
208
200
  } else { // opening tag
209
201
  let i = stack.length - 1;
@@ -272,10 +264,7 @@
272
264
  }
273
265
  const forward = new Iter(this, pos),
274
266
  close = forward.findMatchingClose(open.tag);
275
- if (close) {
276
- return {open, close};
277
- }
278
- return undefined;
267
+ return close ? {open, close} : undefined;
279
268
  },
280
269
  );
281
270
 
@@ -324,8 +313,7 @@
324
313
  const match = cm.findMatchingTag(cm.getCursor());
325
314
  if (!match) {
326
315
  return;
327
- }
328
- if (match.at === 'self') {
316
+ } else if (match.at === 'self') {
329
317
  cm.state.tagHit = cm.markText(match.open.from, match.open.to, {className: 'cm-matchingtag'});
330
318
  return;
331
319
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wikiplus-highlight",
3
- "version": "2.22.5",
3
+ "version": "2.25.3",
4
4
  "description": "A plugin for the MediaWiki front-end add-on \"Wikiplus\"",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -13,7 +13,12 @@
13
13
  },
14
14
  "license": "GPL-3.0",
15
15
  "author": "Bhsd",
16
- "browser": "main.js",
16
+ "files": [
17
+ "/dist/",
18
+ "/i18n/",
19
+ "/*.js"
20
+ ],
21
+ "browser": "/main.js",
17
22
  "repository": {
18
23
  "type": "git",
19
24
  "url": "git+https://github.com/bhsd-harry/Wikiplus-highlight.git"