@bhsd/codemirror-mediawiki 2.9.0 → 2.9.2

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/mw/msg.ts DELETED
@@ -1,144 +0,0 @@
1
- import type {CodeMirror} from './base';
2
-
3
- export const REPO_CDN = 'npm/@bhsd/codemirror-mediawiki@2.9.0',
4
- curVersion = REPO_CDN.slice(REPO_CDN.lastIndexOf('@') + 1);
5
-
6
- const {vendor, userAgent, maxTouchPoints, platform} = navigator;
7
-
8
- export const isMac = vendor.includes('Apple Computer') && (userAgent.includes('Mobile/') || maxTouchPoints > 2)
9
- || platform.includes('Mac');
10
-
11
- const storageKey = 'codemirror-mediawiki-i18n',
12
- languages: Record<string, string> = {
13
- zh: 'zh-hans',
14
- 'zh-hans': 'zh-hans',
15
- 'zh-cn': 'zh-hans',
16
- 'zh-my': 'zh-hans',
17
- 'zh-sg': 'zh-hans',
18
- 'zh-hant': 'zh-hant',
19
- 'zh-tw': 'zh-hant',
20
- 'zh-hk': 'zh-hant',
21
- 'zh-mo': 'zh-hant',
22
- },
23
- lang = languages[mw.config.get('wgUserLanguage')] || 'en';
24
-
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- export const getObject = (key: string): any => JSON.parse(String(localStorage.getItem(key)));
27
- export const setObject = (key: string, value: unknown): void => {
28
- localStorage.setItem(key, JSON.stringify(value));
29
- };
30
-
31
- /** 预存的I18N,可以用于判断是否是首次安装 */
32
- export const i18n: Record<string, string> = getObject(storageKey) || {};
33
-
34
- const {version} = i18n;
35
-
36
- /**
37
- * 加载 I18N
38
- * @param CDN CDN地址
39
- */
40
- export const setI18N = async (CDN: string): Promise<void> => {
41
- if (i18n['lang'] !== lang || version !== curVersion) {
42
- try {
43
- Object.assign(i18n, await (await fetch(`${CDN}/${REPO_CDN}/i18n/${lang}.json`)).json());
44
- setObject(storageKey, i18n);
45
- } catch (e) {
46
- void mw.notify(msg('i18n-failed', lang), {type: 'error'});
47
- console.error(e);
48
- }
49
- }
50
- for (const [k, v] of Object.entries(i18n)) {
51
- if (!k.endsWith('-mac')) {
52
- mw.messages.set(`cm-mw-${k}`, v);
53
- } else if (isMac) {
54
- mw.messages.set(`cm-mw-${k.slice(0, -4)}`, v);
55
- }
56
- }
57
- };
58
-
59
- /**
60
- * 获取I18N消息
61
- * @param key 消息键,省略`cm-mw-`前缀
62
- * @param args 替换`$1`等的参数
63
- */
64
- export const msg = (key: string, ...args: string[]): string => mw.msg(`cm-mw-${key}`, ...args);
65
-
66
- /**
67
- * 为所有链接添加`target="_blank"`
68
- * @param $dom 容器
69
- */
70
- const blankTarget = ($dom: JQuery<HTMLElement>): JQuery<HTMLElement> => {
71
- $dom.find('a').add($dom.filter('a')).attr('target', '_blank');
72
- return $dom;
73
- };
74
-
75
- /**
76
- * 解析I18N消息
77
- * @param key 消息键,省略`cm-mw-`前缀
78
- * @param text 是否输出为文本
79
- */
80
- function parseMsg(key: string): JQuery<HTMLElement>;
81
- function parseMsg(key: string, text: true): string;
82
- function parseMsg(key: string, text?: boolean): string | JQuery<HTMLElement> {
83
- const message = mw.message(`cm-mw-${key}`);
84
- return text ? message.parse() : blankTarget(message.parseDom());
85
- }
86
- export {parseMsg};
87
-
88
- /**
89
- * 解析版本号
90
- * @param v 版本号
91
- */
92
- const parseVersion = (v: string): [number, number] => v.split('.', 2).map(Number) as [number, number];
93
-
94
- /**
95
- * 创建气泡提示消息
96
- * @param key 消息键,省略`cm-mw-`前缀
97
- * @param args 替换`$1`等的参数
98
- */
99
- const notify = async (key: string, ...args: string[]): Promise<JQuery<HTMLElement>> => {
100
- const $p = blankTarget($('<p>', {html: msg(key, ...args)}));
101
- await mw.notify($p, {type: 'success', autoHideSeconds: 'long'});
102
- return $p;
103
- };
104
-
105
- /**
106
- * 欢迎消息
107
- * @param baseVersion 首次加入新插件的版本
108
- * @param addons 新插件
109
- */
110
- export const welcome = async (baseVersion: string, addons: string[]): Promise<void> => {
111
- let notification: JQuery<HTMLElement> | undefined;
112
- if (!version) { // 首次安装
113
- notification = await notify('welcome');
114
- } else if (addons.length > 0) { // 更新版本
115
- const [baseMajor, baseMinor] = parseVersion(baseVersion),
116
- [major, minor] = parseVersion(version);
117
- if (major < baseMajor || major === baseMajor && minor < baseMinor) {
118
- notification = await notify(
119
- 'welcome-addons',
120
- curVersion,
121
- String(addons.length),
122
- addons.map(addon => `<li>${parseMsg(`addon-${addon}`, true)}</li>`).join(''),
123
- );
124
- }
125
- }
126
- notification?.find('#settings').click(e => {
127
- e.preventDefault();
128
- document.getElementById('cm-settings')!.dispatchEvent(new MouseEvent('click'));
129
- });
130
- };
131
-
132
- /**
133
- * 本地化
134
- * @param cm
135
- */
136
- export const localize = (cm: CodeMirror): void => {
137
- const obj: Record<string, string> = {};
138
- for (const [k, v] of Object.entries(i18n)) {
139
- if (k.startsWith('phrase-')) {
140
- obj[k.slice(7).replace(/-/gu, ' ')] = v;
141
- }
142
- }
143
- cm.localize(obj);
144
- };
package/mw/openLinks.ts DELETED
@@ -1,100 +0,0 @@
1
- import {isMac} from './msg';
2
- import type {SyntaxNode} from '@lezer/common';
3
- import type {CodeMirror} from './base';
4
-
5
- declare type MouseEventListener = (e: MouseEvent) => void;
6
-
7
- const modKey = isMac ? 'metaKey' : 'ctrlKey',
8
- handlers = new WeakMap<CodeMirror, MouseEventListener>();
9
-
10
- /**
11
- * 获取节点的名称
12
- * @param node 语法树节点
13
- */
14
- function getName(node: SyntaxNode): string;
15
- function getName(node: null): undefined;
16
- function getName(node: SyntaxNode | null): string | undefined {
17
- return node?.name.replace(/_+/gu, ' ').trim();
18
- }
19
-
20
- /**
21
- * 查找连续同名节点
22
- * @param node 起始节点
23
- * @param dir 方向
24
- * @param name 节点名称
25
- */
26
- const search = (node: SyntaxNode, dir: 'prevSibling' | 'nextSibling', name = getName(node)): SyntaxNode => {
27
- while (getName(node[dir]!) === name) {
28
- node = node[dir]!; // eslint-disable-line no-param-reassign
29
- }
30
- return node;
31
- };
32
-
33
- /**
34
- * 点击时在新页面打开链接、模板等
35
- * @param cm
36
- * @param e 点击事件
37
- */
38
- const getHandler = (cm: CodeMirror): MouseEventListener => {
39
- if (handlers.has(cm)) {
40
- return handlers.get(cm)!;
41
- }
42
- const handler: MouseEventListener = (e): void => {
43
- if (!e[modKey]) {
44
- return;
45
- }
46
- const {view} = cm,
47
- {state} = view,
48
- node = cm.getNodeAt(view.posAtCoords(e)!);
49
- if (!node) {
50
- // pass
51
- } else if (node.name.includes('mw-pagename')) {
52
- e.preventDefault();
53
- e.stopPropagation();
54
- const name = getName(node);
55
- let page = state.sliceDoc(
56
- search(node, 'prevSibling', name).from,
57
- search(node, 'nextSibling', name).to,
58
- ).trim();
59
- if (page.startsWith('/')) {
60
- page = `:${mw.config.get('wgPageName')}${page}`;
61
- }
62
- let ns = 0;
63
- if (name.includes('-template-name')) {
64
- ns = 10;
65
- } else if (name.includes('-parserfunction')) {
66
- ns = 828;
67
- }
68
- open(new mw.Title(page, ns).getUrl(undefined), '_blank');
69
- } else if (/-extlink-protocol/u.test(node.name)) {
70
- e.preventDefault();
71
- open(state.sliceDoc(node.from, search(node.nextSibling!, 'nextSibling').to), '_blank');
72
- } else if (/-extlink(?:_|$)/u.test(node.name)) {
73
- e.preventDefault();
74
- const name = getName(node),
75
- prev = search(node, 'prevSibling', name).prevSibling!,
76
- next = search(node, 'nextSibling', name);
77
- open(state.sliceDoc(prev.from, next.to), '_blank');
78
- }
79
- };
80
- handlers.set(cm, handler);
81
- return handler;
82
- };
83
-
84
- /**
85
- * 添加或移除打开链接的事件
86
- * @param cm
87
- * @param on 是否添加
88
- */
89
- export const openLinks = (cm: CodeMirror, on?: boolean): void => {
90
- const {view: {contentDOM}} = cm,
91
- handler = getHandler(cm);
92
- if (on) {
93
- mw.loader.load('mediawiki.Title');
94
- contentDOM.addEventListener('mousedown', handler, {capture: true});
95
- contentDOM.style.setProperty('--codemirror-cursor', 'pointer');
96
- } else if (on === false) {
97
- contentDOM.removeEventListener('mousedown', handler, {capture: true});
98
- contentDOM.style.removeProperty('--codemirror-cursor');
99
- }
100
- };
package/mw/preference.ts DELETED
@@ -1,281 +0,0 @@
1
- import {rules} from 'wikiparser-node/dist/base';
2
- import {CodeMirror} from './base';
3
- import {msg, parseMsg, i18n, setObject, getObject} from './msg';
4
- import {instances} from './textSelection';
5
- import type {LintError} from 'wikiparser-node';
6
- import type {ApiEditPageParams, ApiQueryRevisionsParams} from 'types-mediawiki/api_params';
7
-
8
- const storageKey = 'codemirror-mediawiki-addons',
9
- wikilintKey = 'codemirror-mediawiki-wikilint',
10
- codeKeys = ['ESLint', 'Stylelint'] as const,
11
- user = mw.config.get('wgUserName'),
12
- userPage = user && `User:${user}/codemirror-mediawiki.json`;
13
-
14
- declare type codeKey = typeof codeKeys[number];
15
-
16
- declare type Preferences = {
17
- addons?: string[];
18
- indent?: string;
19
- wikilint?: Record<LintError.Rule, RuleState>;
20
- } & Record<codeKey, unknown>;
21
-
22
- declare interface MediaWikiPage {
23
- readonly revisions?: {
24
- readonly content: string;
25
- }[];
26
- }
27
- declare interface MediaWikiResponse {
28
- readonly query: {
29
- readonly pages: MediaWikiPage[];
30
- };
31
- }
32
-
33
- const enum RuleState {
34
- off = '0',
35
- error = '1',
36
- on = '2',
37
- }
38
-
39
- export const indentKey = 'codemirror-mediawiki-indent',
40
- prefs = new Set<string>(getObject(storageKey) as string[] | null),
41
- wikilintConfig = (getObject(wikilintKey) || {}) as Record<LintError.Rule, RuleState | undefined>,
42
- codeConfigs = new Map(codeKeys.map(k => [k, getObject(`codemirror-mediawiki-${k}`)]));
43
-
44
- // OOUI组件
45
- let dialog: OO.ui.MessageDialog | undefined,
46
- layout: OO.ui.IndexLayout,
47
- widget: OO.ui.CheckboxMultiselectInputWidget,
48
- indentWidget: OO.ui.TextInputWidget,
49
- indent = localStorage.getItem(indentKey) || '';
50
- const widgets: Partial<Record<codeKey, OO.ui.MultilineTextInputWidget>> = {},
51
- wikilintWidgets = new Map<LintError.Rule, OO.ui.DropdownInputWidget>();
52
-
53
- /**
54
- * 处理Api请求错误
55
- * @param code 错误代码
56
- * @param e 错误信息
57
- */
58
- const apiErr = (code: string, e: any): void => { // eslint-disable-line @typescript-eslint/no-explicit-any
59
- const message = code === 'http' || code === 'okay-but-empty'
60
- ? `MediaWiki API request failed: ${code}`
61
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
62
- : $('<ul>', {html: (e.errors as {html: string}[]).map(({html}) => $('<li>', {html}))});
63
- void mw.notify(message as string | HTMLElement[], {type: 'error', autoHideSeconds: 'long'});
64
- };
65
-
66
- const api = (async () => {
67
- if (user) {
68
- await mw.loader.using('mediawiki.api');
69
- return new mw.Api({parameters: {errorformat: 'html', formatversion: '2'}});
70
- }
71
- return undefined;
72
- })();
73
-
74
- export const loadJSON = (async () => {
75
- if (!user) {
76
- return;
77
- }
78
- const params: ApiQueryRevisionsParams = {
79
- action: 'query',
80
- prop: 'revisions',
81
- titles: userPage,
82
- rvprop: 'content',
83
- rvlimit: 1,
84
- };
85
- (await api)!.get(params as Record<string, string>).then( // eslint-disable-line promise/prefer-await-to-then
86
- res => {
87
- const {query: {pages: [page]}} = res as MediaWikiResponse;
88
- if (page?.revisions) {
89
- const json: Preferences = JSON.parse(page.revisions[0]!.content);
90
- if (!json.addons?.includes('save')) {
91
- return;
92
- }
93
- prefs.clear();
94
- for (const option of json.addons) {
95
- prefs.add(option);
96
- }
97
- if (json.indent) {
98
- localStorage.setItem(indentKey, json.indent);
99
- }
100
- for (const key of codeKeys) {
101
- if (json[key]) {
102
- codeConfigs.set(key, json[key]);
103
- }
104
- }
105
- if (json.wikilint) {
106
- Object.assign(wikilintConfig, json.wikilint);
107
- }
108
- }
109
- },
110
- apiErr,
111
- );
112
- })();
113
-
114
- /**
115
- * 打开设置对话框
116
- * @param editors CodeMirror实例
117
- */
118
- export const openPreference = async (editors: (CodeMirror | undefined)[]): Promise<void> => {
119
- await mw.loader.using([
120
- 'oojs-ui-windows',
121
- 'oojs-ui-widgets',
122
- 'oojs-ui.styles.icons-content',
123
- 'mediawiki.jqueryMsg',
124
- ]);
125
- await loadJSON;
126
- if (dialog) {
127
- widget.setValue([...prefs] as unknown as string);
128
- indentWidget.setValue(indent);
129
- } else {
130
- dialog = new OO.ui.MessageDialog({id: 'cm-preference'});
131
- const windowManager = new OO.ui.WindowManager();
132
- windowManager.$element.appendTo(document.body);
133
- windowManager.addWindows([dialog]);
134
- layout = new OO.ui.IndexLayout();
135
- const panelMain = new OO.ui.TabPanelLayout('main', {label: msg('title')}),
136
- panelWikilint = new OO.ui.TabPanelLayout('wikilint', {label: 'WikiLint'}),
137
- panels: Partial<Record<codeKey, OO.ui.TabPanelLayout>> = {};
138
- for (const key of codeKeys) {
139
- const c = codeConfigs.get(key);
140
- widgets[key] = new OO.ui.MultilineTextInputWidget({
141
- value: c ? JSON.stringify(c, null, indent || '\t') : '',
142
- });
143
- const codeField = new OO.ui.FieldLayout(widgets[key]!, {label: msg(`${key}-config`), align: 'top'}),
144
- panel = new OO.ui.TabPanelLayout(key, {label: key, $content: codeField.$element});
145
- panel.on('active', active => {
146
- const [textarea] = panel.$element.find('textarea') as unknown as [HTMLTextAreaElement];
147
- if (active && !instances.has(textarea)) {
148
- void CodeMirror.fromTextArea(textarea, 'json');
149
- }
150
- });
151
- panels[key] = panel;
152
- }
153
- layout.addTabPanels([panelMain, panelWikilint, ...Object.values(panels)], 0);
154
- widget = new OO.ui.CheckboxMultiselectInputWidget({
155
- options: [
156
- {disabled: true},
157
- ...Object.keys(i18n)
158
- .filter(k => k !== 'addon-indent' && k.startsWith('addon-') && !k.endsWith('-mac'))
159
- .map(k => ({
160
- data: k.slice(6),
161
- label: parseMsg(k),
162
- disabled: k === 'addon-wikiEditor' && !mw.loader.getState('ext.wikiEditor')
163
- || k === 'addon-save' && !user,
164
- })),
165
- ],
166
- value: [...prefs] as unknown as string,
167
- });
168
- indentWidget = new OO.ui.TextInputWidget({value: indent, placeholder: '\\t'});
169
- const field = new OO.ui.FieldLayout(widget, {
170
- label: msg('label'),
171
- align: 'top',
172
- }),
173
- indentField = new OO.ui.FieldLayout(indentWidget, {label: msg('addon-indent')});
174
- panelMain.$element.append(
175
- field.$element,
176
- indentField.$element,
177
- $('<p>', {html: msg('feedback', 'codemirror-mediawiki')}),
178
- );
179
- panelWikilint.$element.append(
180
- ...rules.map(rule => {
181
- const state = rule === 'no-arg' ? RuleState.off : RuleState.error,
182
- dropdown = new OO.ui.DropdownInputWidget({
183
- options: [
184
- {data: RuleState.off, label: msg('wikilint-off')},
185
- {data: RuleState.error, label: msg('wikilint-error')},
186
- {data: RuleState.on, label: msg('wikilint-on')},
187
- ],
188
- value: wikilintConfig[rule] || state,
189
- }),
190
- f = new OO.ui.FieldLayout(dropdown, {label: rule});
191
- wikilintWidgets.set(rule, dropdown);
192
- wikilintConfig[rule] ||= state;
193
- return f.$element;
194
- }),
195
- $('<p>', {html: msg('feedback', 'wikiparser-node')}),
196
- );
197
- }
198
-
199
- const data = await (dialog.open({
200
- message: layout!.$element,
201
- actions: [
202
- {action: 'reject', label: mw.msg('ooui-dialog-message-reject')},
203
- {action: 'accept', label: mw.msg('ooui-dialog-message-accept'), flags: 'progressive'},
204
- ],
205
- size: 'medium',
206
- }).closing as unknown as Promise<{action?: unknown} | undefined>);
207
- if (typeof data === 'object' && data.action === 'accept') {
208
- // 缩进
209
- const oldIndent = indent,
210
- save = prefs.has('save');
211
- indent = indentWidget.getValue(); // eslint-disable-line require-atomic-updates
212
- let changed = indent !== oldIndent;
213
- if (changed) {
214
- for (const cm of editors) {
215
- cm?.setIndent(indent || '\t');
216
- }
217
- localStorage.setItem(indentKey, indent);
218
- }
219
-
220
- // WikiLint
221
- for (const [rule, dropdown] of wikilintWidgets) {
222
- const val = dropdown.getValue() as RuleState;
223
- changed ||= val !== wikilintConfig[rule];
224
- wikilintConfig[rule] = val;
225
- }
226
- setObject(wikilintKey, wikilintConfig);
227
-
228
- // ESLint & Stylelint
229
- const jsonErrors: string[] = [];
230
- for (const key of codeKeys) {
231
- try {
232
- const config = JSON.parse(widgets[key]!.getValue().trim() || 'null');
233
- changed ||= JSON.stringify(config) !== JSON.stringify(codeConfigs.get(key));
234
- codeConfigs.set(key, config);
235
- setObject(`codemirror-mediawiki-${key}`, config);
236
- } catch {
237
- jsonErrors.push(key);
238
- }
239
- }
240
- if (jsonErrors.length > 0) {
241
- void OO.ui.alert(msg('json-error', jsonErrors.join(msg('and'))));
242
- }
243
-
244
- // 插件
245
- const value = widget.getValue() as unknown as string[];
246
- if (value.length !== prefs.size || !value.every(option => prefs.has(option))) {
247
- changed = true;
248
- prefs.clear();
249
- for (const option of value) {
250
- prefs.add(option);
251
- }
252
- for (const cm of editors) {
253
- cm?.prefer(value);
254
- }
255
- setObject(storageKey, value);
256
- }
257
-
258
- // 保存至用户子页面
259
- if (changed && user && (save || prefs.has('save'))) {
260
- const params: ApiEditPageParams = {
261
- action: 'edit',
262
- title: userPage,
263
- text: JSON.stringify({
264
- addons: [...prefs],
265
- indent,
266
- wikilint: wikilintConfig,
267
- ESLint: codeConfigs.get('ESLint'),
268
- Stylelint: codeConfigs.get('Stylelint'),
269
- } as Preferences),
270
- summary: msg('save-summary'),
271
- };
272
- // eslint-disable-next-line promise/prefer-await-to-then
273
- (await api)!.postWithToken('csrf', params as Record<string, string>).then(
274
- () => {
275
- void mw.notify(parseMsg('save-success'), {type: 'success'});
276
- },
277
- apiErr,
278
- );
279
- }
280
- }
281
- };
@@ -1,122 +0,0 @@
1
- import {CodeMirror} from './base';
2
-
3
- export const instances = new WeakMap<HTMLTextAreaElement, CodeMirror>();
4
-
5
- /**
6
- * 获取CodeMirror实例
7
- * @param $ele textarea元素的jQuery对象
8
- */
9
- const getInstance = ($ele: JQuery<HTMLTextAreaElement>): CodeMirror => instances.get($ele[0]!)!;
10
-
11
- declare interface EncapsulateOptions {
12
- pre?: string;
13
- peri?: string;
14
- post?: string;
15
- ownline?: boolean;
16
- replace?: boolean;
17
- selectPeri?: boolean;
18
- splitlines?: boolean;
19
- selectionStart?: number;
20
- selectionEnd?: number;
21
- }
22
-
23
- /**
24
- * jQuery.textSelection overrides for CodeMirror.
25
- * See jQuery.textSelection.js for method documentation
26
- */
27
- export const textSelection = {
28
- getContents(this: JQuery<HTMLTextAreaElement>): string {
29
- return getInstance(this).view.state.doc.toString();
30
- },
31
- setContents(this: JQuery<HTMLTextAreaElement>, content: string): JQuery<HTMLTextAreaElement> {
32
- getInstance(this).setContent(content);
33
- return this;
34
- },
35
- getSelection(this: JQuery<HTMLTextAreaElement>): string {
36
- const {view: {state}} = getInstance(this),
37
- {selection: {main: {from, to}}} = state;
38
- return state.sliceDoc(from, to);
39
- },
40
- setSelection(
41
- this: JQuery<HTMLTextAreaElement>,
42
- {start, end = start}: {start: number, end?: number},
43
- ): JQuery<HTMLTextAreaElement> {
44
- getInstance(this).view.dispatch({
45
- selection: {anchor: start, head: end},
46
- });
47
- return this;
48
- },
49
- replaceSelection(this: JQuery<HTMLTextAreaElement>, value: string): JQuery<HTMLTextAreaElement> {
50
- const {view} = getInstance(this);
51
- view.dispatch(view.state.replaceSelection(value));
52
- return this;
53
- },
54
- encapsulateSelection(this: JQuery<HTMLTextAreaElement>, {
55
- pre = '',
56
- peri = '',
57
- post = '',
58
- ownline,
59
- replace,
60
- selectPeri = true,
61
- splitlines,
62
- selectionStart,
63
- selectionEnd = selectionStart,
64
- }: EncapsulateOptions): JQuery<HTMLTextAreaElement> {
65
- const {view} = getInstance(this),
66
- {state} = view;
67
- const handleOwnline = (from: number, to: number, text: string): [string, number, number] => {
68
- let start = 0,
69
- end = 0;
70
- if (ownline) {
71
- if (from > 0 && !/[\n\r]/u.test(state.sliceDoc(from - 1, from))) {
72
- text = `\n${text}`; // eslint-disable-line no-param-reassign
73
- start = 1;
74
- }
75
- if (!/[\n\r]/u.test(state.sliceDoc(to, to + 1))) {
76
- text += '\n'; // eslint-disable-line no-param-reassign
77
- end = 1;
78
- }
79
- }
80
- return [text, start, end];
81
- };
82
- if (ownline && replace && !pre && !post && selectionStart === undefined && /^\s*=.*=\s*$/u.test(peri)) {
83
- // 单独处理改变标题层级
84
- const {selection: {main: {from, to}}} = state,
85
- [insertText] = handleOwnline(from, to, peri);
86
- view.dispatch({
87
- changes: {from, to, insert: insertText},
88
- selection: {anchor: from + insertText.length},
89
- });
90
- return this;
91
- }
92
- CodeMirror.replaceSelections(view, (_, {from, to}) => {
93
- if (selectionStart !== undefined) {
94
- /* eslint-disable no-param-reassign */
95
- from = selectionStart;
96
- to = selectionEnd!;
97
- /* eslint-enable no-param-reassign */
98
- }
99
- const isSample = selectPeri && from === to,
100
- selText = replace || from === to ? peri : state.sliceDoc(from, to),
101
- [insertText, start, end] = handleOwnline(
102
- from,
103
- to,
104
- splitlines
105
- ? selText.split('\n').map(line => `${pre}${line}${post}`).join('\n')
106
- : `${pre}${selText}${post}`,
107
- ),
108
- head = from + insertText.length;
109
- return isSample ? [insertText, from + pre.length + start, head - post.length - end] : [insertText, head];
110
- });
111
- return this;
112
- },
113
- getCaretPosition(this: JQuery<HTMLTextAreaElement>, option?: {startAndEnd?: boolean}): [number, number] | number {
114
- const {view: {state: {selection: {main: {from, to, head}}}}} = getInstance(this);
115
- return option?.startAndEnd ? [from, to] : head;
116
- },
117
- scrollToCaretPosition(this: JQuery<HTMLTextAreaElement>): JQuery<HTMLTextAreaElement> {
118
- const cm = getInstance(this);
119
- cm.scrollTo();
120
- return this;
121
- },
122
- };
package/mw/wikiEditor.ts DELETED
@@ -1,30 +0,0 @@
1
- import {msg} from './msg';
2
- import {prefs} from './preference';
3
-
4
- /**
5
- * 添加WikiEditor工具栏
6
- * @param $textarea 文本框
7
- */
8
- export const wikiEditor = async ($textarea: JQuery<HTMLTextAreaElement>): Promise<void> => {
9
- if (!mw.loader.getState('ext.wikiEditor')) {
10
- prefs.delete('wikiEditor');
11
- void mw.notify(msg('no-wikiEditor'), {type: 'error'});
12
- return;
13
- }
14
- await mw.loader.using('ext.wikiEditor');
15
- if ($textarea.data('wikiEditorContext')) {
16
- return;
17
- } else if (typeof mw.addWikiEditor === 'function') { // MW >= 1.34
18
- mw.addWikiEditor($textarea);
19
- } else { // MW <= 1.33
20
- const {wikiEditor: {modules: {dialogs: {config}}}} = $;
21
- $textarea.wikiEditor('addModule', {
22
- ...$.wikiEditor.modules.toolbar.config.getDefaultConfig(),
23
- ...config.getDefaultConfig(),
24
- });
25
- config.replaceIcons($textarea);
26
- }
27
- await new Promise(resolve => { // MW >= 1.21
28
- $textarea.on('wikiEditor-toolbar-doneInitialSections', resolve);
29
- });
30
- };