@bhsd/codemirror-mediawiki 1.1.12 → 2.0.0

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/mediawiki.js DELETED
@@ -1,917 +0,0 @@
1
- ( function ( CodeMirror ) {
2
- 'use strict';
3
-
4
- const permittedHtmlTags = new Set( [
5
- 'b', 'bdi', 'bdo', 'del', 'i', 'ins', 'img', 'u', 'font', 'big', 'small', 'sub', 'sup', 'h1', 'h2', 'h3',
6
- 'h4', 'h5', 'h6', 'cite', 'code', 'em', 's', 'strike', 'strong', 'tt', 'var', 'div', 'center', 'blockquote',
7
- 'q', 'ol', 'ul', 'dl', 'table', 'caption', 'pre', 'ruby', 'rb', 'rp', 'rt', 'rtc', 'p', 'span', 'abbr',
8
- 'dfn', 'kbd', 'samp', 'data', 'time', 'mark', 'br', 'wbr', 'hr', 'li', 'dt', 'dd', 'td', 'th', 'tr',
9
- 'noinclude', 'includeonly', 'onlyinclude', 'translate'
10
- ] ),
11
- voidHtmlTags = new Set( [ 'br', 'hr', 'wbr', 'img' ] ),
12
- span = typeof document === 'object' && document.createElement( 'span' ); // used for isEntity()
13
- let mwConfig, urlProtocols;
14
-
15
- /**
16
- * add background
17
- */
18
- function makeLocalStyle( style, state, endGround ) {
19
- let ground = '';
20
- switch ( state.nTemplate ) {
21
- case 0:
22
- break;
23
- case 1:
24
- ground += '-template';
25
- break;
26
- case 2:
27
- ground += '-template2';
28
- break;
29
- default:
30
- ground += '-template3';
31
- }
32
- switch ( state.nExt ) {
33
- case 0:
34
- break;
35
- case 1:
36
- ground += '-ext';
37
- break;
38
- case 2:
39
- ground += '-ext2';
40
- break;
41
- default:
42
- ground += '-ext3';
43
- }
44
- if ( state.nLink > 0 ) {
45
- ground += '-link';
46
- }
47
- if ( endGround ) {
48
- state[ endGround ]--;
49
- }
50
- return style + ( ground && ` mw${ ground }-ground` );
51
- }
52
-
53
- function makeStyle( style, state, endGround ) {
54
- return makeLocalStyle( style + ( state.isBold ? ' strong' : '' ), state, endGround );
55
- }
56
-
57
- function isEntity( str ) {
58
- if ( !span ) {
59
- return true;
60
- }
61
- span.innerHTML = str;
62
- return span.textContent.length === 1;
63
- }
64
-
65
- /**
66
- * simply eat HTML entities
67
- */
68
- function eatMnemonic( stream, style ) {
69
- // no dangerous character should appear in results
70
- const entity = stream.match( /^(?:#x[a-f\d]+|#\d+|[a-z\d]+);/i );
71
- return entity && isEntity( `&${ entity[ 0 ] }` ) ? `${ style } mw-mnemonic` : style;
72
- }
73
-
74
- /**
75
- * simply eat until a block ends with specified terminator
76
- */
77
- function eatBlock( style, terminator ) {
78
- return function ( stream, state ) {
79
- if ( !stream.skipTo( terminator ) ) {
80
- stream.skipToEnd();
81
- } else {
82
- stream.match( terminator );
83
- state.tokenize = state.stack.pop();
84
- }
85
- return makeLocalStyle( style, state );
86
- };
87
- }
88
-
89
- /**
90
- * simply eat until the end of line
91
- */
92
- function eatEnd( style ) {
93
- return function ( stream, state ) {
94
- stream.skipToEnd();
95
- state.tokenize = state.stack.pop();
96
- return makeLocalStyle( style, state );
97
- };
98
- }
99
-
100
- /**
101
- * simply eat characters if they must be there
102
- */
103
- function eatChars( n, style ) {
104
- return function ( stream, state ) {
105
- for ( let i = 0; i < n; i++ ) {
106
- stream.next();
107
- }
108
- state.tokenize = state.stack.pop();
109
- return makeLocalStyle( style, state );
110
- };
111
- }
112
-
113
- function eatWikiText( style ) {
114
- return function ( stream, state ) {
115
- let tmp, mt, name, isCloseTag, tagname;
116
- const sol = stream.sol();
117
-
118
- if ( sol ) { // reset bold status in every new line
119
- state.isBold = false;
120
- }
121
-
122
- if ( stream.match( '//' ) ) {
123
- return makeStyle( style, state );
124
- } else if ( stream.match( urlProtocols ) ) { // highlight free external links, bug T108448
125
- state.stack.push( state.tokenize );
126
- state.tokenize = eatFreeExternalLink;
127
- state.lpar = false;
128
- return makeStyle( 'mw-free-extlink-protocol', state );
129
- }
130
-
131
- const ch = stream.next();
132
- if ( sol ) {
133
- switch ( ch ) {
134
- case '-':
135
- if ( stream.match( /^-{3,}/ ) ) {
136
- return 'mw-hr';
137
- }
138
- break;
139
- case '=':
140
- tmp = stream.match( /^(={0,5})(.+?(=\1\s*))$/u );
141
- if ( tmp ) { // Title
142
- stream.backUp( tmp[ 2 ].length );
143
- state.stack.push( state.tokenize );
144
- state.tokenize = eatSectionHeader( tmp[ 3 ].length );
145
- return makeLocalStyle( `mw-section-header line-cm-mw-section-${ tmp[ 1 ].length + 1 }`, state );
146
- }
147
- break;
148
- case '*':
149
- case '#':
150
- mt = stream.match( /^[*#;:]*/ );
151
- if ( mt[ 0 ].includes( ';' ) ) {
152
- state.isBold = true;
153
- }
154
- return makeLocalStyle( 'mw-list', state );
155
- case ';':
156
- state.isBold = true;
157
- stream.match( /^[*#;:]*/ );
158
- return makeLocalStyle( 'mw-list', state );
159
- case ':':
160
- if ( mt = stream.match( /^:*\s*(\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})/u ) ) { // Highlight indented tables :{|, bug T108454
161
- const [ , { length } ] = mt;
162
- state.stack.push( state.tokenize );
163
- state.tokenize = eatStartTable( length );
164
- stream.backUp( length );
165
- return makeLocalStyle( 'mw-list', state );
166
- }
167
- mt = stream.match( /^[*#;:]*/ );
168
- if ( mt[ 0 ].includes( ';' ) ) {
169
- state.isBold = true;
170
- }
171
- return makeLocalStyle( 'mw-list', state );
172
- case ' ':
173
- if ( mt = stream.match( /^\s*(:+\s*)?(\{\||\{\{\{\s*!\s*\}\}|\{\{\s*\(!\s*\}\})/u ) ) { // Leading spaces is the correct syntax for a table, bug T108454
174
- if ( mt[ 1 ] ) { // ::{|
175
- const [ ,, { length } ] = mt;
176
- state.stack.push( state.tokenize );
177
- state.tokenize = eatStartTable( length );
178
- stream.backUp( length );
179
- return makeLocalStyle( 'mw-list', state );
180
- }
181
- state.stack.push( state.tokenize );
182
- state.tokenize = inTableDefinition;
183
- return makeLocalStyle( 'mw-table-bracket', state );
184
- }
185
- return 'mw-skipformatting';
186
- case '{':
187
- if ( stream.match( /^(?:\||\{\{\s*!\s*\}\}|\{\s*\(!\s*\}\})\s*/u ) ) {
188
- state.stack.push( state.tokenize );
189
- state.tokenize = inTableDefinition;
190
- return makeLocalStyle( 'mw-table-bracket', state );
191
- }
192
- }
193
- }
194
-
195
- switch ( ch ) {
196
- case '&':
197
- return makeStyle( eatMnemonic( stream, style ), state );
198
- case '\'':
199
- if ( stream.match( /^'*(?='{5})/ ) || stream.match( /^'''(?!')/, false ) ) { // skip the irrelevant apostrophes ( >5 or =4 )
200
- break;
201
- } else if ( stream.match( '\'\'' ) ) { // bold
202
- return makeLocalStyle( 'mw-apostrophes-bold', state );
203
- } else if ( stream.eat( '\'' ) ) { // italic
204
- return makeLocalStyle( 'mw-apostrophes-italic', state );
205
- }
206
- break;
207
- case '[':
208
- if ( stream.eat( '[' ) ) { // Link Example: [[ Foo | Bar ]]
209
- stream.eatSpace();
210
- if ( /[^\]|[]/.test( stream.peek() ) ) {
211
- state.nLink++;
212
- state.stack.push( state.tokenize );
213
- const nsIds = typeof mw === 'object' ? mw.config.get( 'wgNamespaceIds' ) : { File: 6 },
214
- nsFile = Object.keys( nsIds ).filter( function ( ns ) {
215
- return nsIds[ ns ] === 6;
216
- } ).join( '|' ),
217
- nsFileRegex = new RegExp( `^\\s*(${ nsFile })\\s*:`, 'iu' );
218
- state.tokenize = stream.match( nsFileRegex, false ) ? inFileLink : inLink;
219
- return makeLocalStyle( 'mw-link-bracket', state );
220
- }
221
- } else {
222
- mt = stream.match( urlProtocols, false );
223
- if ( mt ) {
224
- state.nLink++;
225
- state.stack.push( state.tokenize );
226
- state.tokenize = eatExternalLinkProtocol( mt[ 0 ].length );
227
- return makeLocalStyle( 'mw-extlink-bracket', state );
228
- }
229
- }
230
- break;
231
- case '{':
232
- if ( !stream.match( '{{{{', false ) && stream.match( '{{' ) ) { // Template parameter (skip parameters inside a template transclusion, Bug: T108450)
233
- stream.eatSpace();
234
- state.stack.push( state.tokenize );
235
- state.tokenize = inVariable;
236
- return makeLocalStyle( 'mw-templatevariable-bracket', state );
237
- } else if ( stream.match( /^\{\s*/u ) ) {
238
- if ( stream.peek() === '#' ) { // Parser function
239
- state.nExt++;
240
- state.stack.push( state.tokenize );
241
- state.tokenize = inParserFunctionName;
242
- return makeLocalStyle( 'mw-parserfunction-bracket', state );
243
- }
244
- // Check for parser function without '#'
245
- name = stream.match( /^([^\s}[\]<{'|&:]+)(:|\s*)(\}\}?)?(.)?/u, false );
246
- if ( name ) {
247
- if ( ( name[ 2 ] === ':' || name[ 4 ] === undefined || name[ 3 ] === '}}' ) && ( name[ 1 ].toLowerCase() in mwConfig.functionSynonyms[ 0 ] || name[ 1 ] in mwConfig.functionSynonyms[ 1 ] ) ) {
248
- state.nExt++;
249
- state.stack.push( state.tokenize );
250
- state.tokenize = inParserFunctionName;
251
- return makeLocalStyle( 'mw-parserfunction-bracket', state );
252
- }
253
- }
254
- // Template
255
- state.nTemplate++;
256
- state.stack.push( state.tokenize );
257
- state.tokenize = eatTemplatePageName( false );
258
- return makeLocalStyle( 'mw-template-bracket', state );
259
- }
260
- break;
261
- case '<':
262
- isCloseTag = Boolean( stream.eat( '/' ) );
263
- tagname = stream.match( /^[^>/\s.*,[\]{}$^+?|\\'`~<=!@#%&()-]+/u, false );
264
- if ( stream.match( '!--' ) ) { // comment
265
- state.stack.push( state.tokenize );
266
- state.tokenize = eatBlock( 'mw-comment', '-->' );
267
- return makeLocalStyle( 'mw-comment', state );
268
- } else if ( tagname ) {
269
- tagname = tagname[ 0 ].toLowerCase();
270
- if ( tagname in mwConfig.tags ) { // Parser function
271
- if ( isCloseTag === true ) {
272
- // @todo message
273
- return makeLocalStyle( 'error', state );
274
- }
275
- state.stack.push( state.tokenize );
276
- state.tokenize = eatTagName( tagname.length, isCloseTag, false );
277
- return makeLocalStyle( 'mw-exttag-bracket', state );
278
- } else if ( permittedHtmlTags.has( tagname ) ) { // Html tag
279
- if ( isCloseTag === true && tagname !== state.InHtmlTag.pop() ) {
280
- // @todo message
281
- return makeLocalStyle( 'error', state );
282
- } else if ( isCloseTag === true && voidHtmlTags.has( tagname ) ) {
283
- // @todo message
284
- return makeLocalStyle( 'error', state );
285
- }
286
- state.stack.push( state.tokenize );
287
- // || ( voidHtmlTags.has( tagname ) ) because opening void tags should also be treated as the closing tag.
288
- state.tokenize = eatTagName( tagname.length, isCloseTag || voidHtmlTags.has( tagname ), true );
289
- return makeLocalStyle( 'mw-htmltag-bracket', state );
290
- }
291
- }
292
- break;
293
- case '~':
294
- if ( stream.match( /^~{2,4}/ ) ) {
295
- return 'mw-signature';
296
- }
297
- break;
298
- case '_': // Maybe double undescored Magic Word as __TOC__
299
- tmp = 1;
300
- while ( stream.eat( '_' ) ) { // Optimize processing of many underscore symbols
301
- tmp++;
302
- }
303
- if ( tmp > 2 ) { // Many underscore symbols
304
- if ( !stream.eol() ) {
305
- stream.backUp( 2 ); // Leave last two underscore symbols for processing again in next iteration
306
- }
307
- return makeStyle( style, state ); // Optimization: skip regex function at the end for EOL and backuped symbols
308
- } else if ( tmp === 2 ) { // Check on double underscore Magic Word
309
- name = stream.match( /^\w+__/ ); // The same as the end of function except '_' inside and with '__' at the end of string
310
- if ( name ) {
311
- const behavior = `__${ name[ 0 ] }`;
312
- if ( behavior.toLowerCase() in mwConfig.doubleUnderscore[ 0 ] || behavior in mwConfig.doubleUnderscore[ 1 ] ) {
313
- return 'mw-doubleUnderscore';
314
- } else if ( !stream.eol() ) {
315
- stream.backUp( 2 ); // Two underscore symbols at the end can be begining of other double undescored Magic Word
316
- }
317
- return makeStyle( style, state ); // Optimization: skip regex function at the end for EOL and backuped symbols
318
- }
319
- }
320
- }
321
- if ( /[\p{L}\d_]/u.test( ch ) || !/[a-z]/i.test( stream.peek() ) ) {
322
- stream.match( /^.*?(?=[&'[\]{}<>~"/|=!-]|__|[^\p{L}\d_][a-z])/iu );
323
- }
324
- return makeStyle( style, state );
325
- };
326
- }
327
-
328
- function eatFreeExternalLink( stream, state ) {
329
- let mt;
330
- if ( stream.eol() ) {
331
- // @todo error message
332
- } else if ( mt = stream.match( /^[^[\]<>"\0-\x1F\x7F\p{Zs}\uFFFD~{'),;.:!?]+/u ) ) {
333
- state.lpar = state.lpar || mt[ 0 ].includes( '(' );
334
- return makeStyle( 'mw-free-extlink', state );
335
- } else {
336
- const ch = stream.next(),
337
- next = stream.peek();
338
- switch ( ch ) {
339
- case '~': {
340
- if ( next !== '~' ) {
341
- return makeStyle( 'mw-free-extlink', state );
342
- }
343
- stream.next();
344
- if ( stream.peek() !== '~' ) {
345
- return makeStyle( 'mw-free-extlink', state );
346
- }
347
- stream.backUp( 1 );
348
- break;
349
- }
350
- case '{':
351
- if ( next !== '{' ) {
352
- return makeStyle( 'mw-free-extlink', state );
353
- }
354
- break;
355
- case '\'':
356
- if ( next !== '\'' ) {
357
- return makeStyle( 'mw-free-extlink', state );
358
- }
359
- break;
360
- case ')':
361
- if ( state.lpar ) {
362
- return makeStyle( 'mw-free-extlink', state );
363
- }
364
- // fall through
365
- case ',':
366
- case ';':
367
- case '.':
368
- case ':':
369
- case '!':
370
- case '?':
371
- if ( mt = stream.match( /^[),;.:!?]*(?:[^[\]<>"\0-\x1F\x7F\p{Zs}\uFFFD~{'),;.:!?]+|~{1,2}(?!~)|\{(?!\{)|'(?!'))/u ) ) {
372
- state.lpar = state.lpar || mt[ 0 ].includes( '(' );
373
- return makeStyle( 'mw-free-extlink', state );
374
- }
375
- }
376
- }
377
- stream.backUp( 1 );
378
- state.tokenize = state.stack.pop();
379
- return makeStyle( 'mw-free-extlink', state );
380
- }
381
-
382
- function eatSectionHeader( count ) {
383
- return function ( stream, state ) {
384
- if ( stream.match( /^[^&<[{~_']+/ ) ) {
385
- if ( stream.eol() ) {
386
- stream.backUp( count );
387
- state.tokenize = eatEnd( 'mw-section-header' );
388
- }
389
- return makeLocalStyle( '', state );
390
- }
391
- return eatWikiText( '' )( stream, state );
392
- };
393
- }
394
-
395
- function eatStartTable( n ) {
396
- return function ( stream, state ) {
397
- for ( let i = 0; i < n; i++ ) {
398
- stream.next();
399
- }
400
- state.tokenize = inTableDefinition;
401
- return makeLocalStyle( 'mw-table-bracket', state );
402
- };
403
- }
404
-
405
- function inTableDefinition( stream, state ) {
406
- if ( stream.sol() ) {
407
- state.tokenize = inTable;
408
- return inTable( stream, state );
409
- }
410
- return eatWikiText( 'mw-table-definition' )( stream, state );
411
- }
412
-
413
- function inTable( stream, state ) {
414
- if ( stream.sol() ) {
415
- stream.eatSpace();
416
- if ( stream.match( /^\{\{\s*!-\s*\}\}-*\s*/u ) ) {
417
- state.tokenize = inTableDefinition;
418
- return makeLocalStyle( 'mw-table-delimiter', state );
419
- } else if ( stream.match( /^\{\{\s*!\)\s*\}\}/u ) ) {
420
- state.tokenize = state.stack.pop();
421
- return makeLocalStyle( 'mw-table-bracket', state );
422
- } else if ( stream.match( /^(?:\||\{\{\s*!\s*\}\})/u ) ) {
423
- if ( stream.match( /^-+\s*/u ) ) {
424
- state.tokenize = inTableDefinition;
425
- return makeLocalStyle( 'mw-table-delimiter', state );
426
- } else if ( stream.eat( '+' ) ) {
427
- stream.eatSpace();
428
- state.tokenize = inTableCaption;
429
- return makeLocalStyle( 'mw-table-delimiter', state );
430
- } else if ( stream.eat( '}' ) ) {
431
- state.tokenize = state.stack.pop();
432
- return makeLocalStyle( 'mw-table-bracket', state );
433
- }
434
- stream.eatSpace();
435
- state.tokenize = eatTableRow( true, false );
436
- return makeLocalStyle( 'mw-table-delimiter', state );
437
- } else if ( stream.eat( '!' ) ) {
438
- stream.eatSpace();
439
- state.tokenize = eatTableRow( true, true );
440
- return makeLocalStyle( 'mw-table-delimiter', state );
441
- }
442
- }
443
- return eatWikiText( '' )( stream, state );
444
- }
445
-
446
- function inTableCaption( stream, state ) {
447
- if ( stream.sol() && stream.match( /^\s*(?:[|!]|\{\{\s*![!)-]?\s*\}\})/u, false ) ) {
448
- state.tokenize = inTable;
449
- return inTable( stream, state );
450
- }
451
- return eatWikiText( 'mw-table-caption' )( stream, state );
452
- }
453
-
454
- function eatTableRow( expectAttr, isHead ) {
455
- return function ( stream, state ) {
456
- if ( stream.sol() ) {
457
- if ( stream.match( /^\s*(?:[|!]|\{\{\s*![!)-]?\s*\}\})/u, false ) ) {
458
- state.tokenize = inTable;
459
- return inTable( stream, state );
460
- }
461
- } else if ( stream.match( /^[^'{[<&~!|]+/ ) ) {
462
- return makeLocalStyle( isHead ? 'strong' : '', state );
463
- } else if ( stream.match( /^(?:(?:\||\{\{\s*!\s*\}\}){2}|\{\{\s*!!\s*\}\})/u ) || isHead && stream.match( '!!' ) ) {
464
- state.tokenize = eatTableRow( true, isHead );
465
- return makeLocalStyle( 'mw-table-delimiter', state );
466
- } else if ( expectAttr && stream.match( /^(?:\||\{\{\s*!\s*\}\})/u ) ) {
467
- state.tokenize = eatTableRow( false, isHead );
468
- return makeLocalStyle( 'mw-table-delimiter2', state );
469
- }
470
- return eatWikiText( isHead ? 'strong' : '' )( stream, state );
471
- };
472
- }
473
-
474
- function inVariable( stream, state ) {
475
- if ( stream.match( /^[^{}|]+/ ) ) {
476
- return makeLocalStyle( 'mw-templatevariable-name', state );
477
- } else if ( stream.eat( '|' ) ) {
478
- state.tokenize = inVariableDefault;
479
- return makeLocalStyle( 'mw-templatevariable-delimiter', state );
480
- } else if ( stream.match( '}}}' ) ) {
481
- state.tokenize = state.stack.pop();
482
- return makeLocalStyle( 'mw-templatevariable-bracket', state );
483
- } else if ( stream.match( '{{{' ) ) {
484
- state.stack.push( state.tokenize );
485
- return makeLocalStyle( 'mw-templatevariable-bracket', state );
486
- }
487
- stream.next();
488
- return makeLocalStyle( 'mw-templatevariable-name', state );
489
- }
490
-
491
- function inVariableDefault( stream, state ) {
492
- if ( stream.match( /^[^{}[<&~]+/ ) ) {
493
- return makeLocalStyle( 'mw-templatevariable', state );
494
- } else if ( stream.match( '}}}' ) ) {
495
- state.tokenize = state.stack.pop();
496
- return makeLocalStyle( 'mw-templatevariable-bracket', state );
497
- }
498
- return eatWikiText( 'mw-templatevariable' )( stream, state );
499
- }
500
-
501
- function inParserFunctionName( stream, state ) {
502
- if ( stream.match( /^[^:}{~|<>[\]]+/ ) ) { // FIXME: {{#name}} and {{uc}} are wrong, must have ':'
503
- return makeLocalStyle( 'mw-parserfunction-name', state );
504
- } else if ( stream.eat( ':' ) ) {
505
- state.tokenize = inParserFunctionArguments;
506
- return makeLocalStyle( 'mw-parserfunction-delimiter', state );
507
- } else if ( stream.match( '}}' ) ) {
508
- state.tokenize = state.stack.pop();
509
- return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' );
510
- }
511
- return eatWikiText( 'error' )( stream, state );
512
- }
513
-
514
- function inParserFunctionArguments( stream, state ) {
515
- if ( stream.match( /^[^|}{[<&~]+/ ) ) {
516
- return makeLocalStyle( 'mw-parserfunction', state );
517
- } else if ( stream.eat( '|' ) ) {
518
- return makeLocalStyle( 'mw-parserfunction-delimiter', state );
519
- } else if ( stream.match( '}}' ) ) {
520
- state.tokenize = state.stack.pop();
521
- return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' );
522
- }
523
- return eatWikiText( 'mw-parserfunction' )( stream, state );
524
- }
525
-
526
- function eatTemplatePageName( haveAte ) {
527
- return function ( stream, state ) {
528
- if ( stream.match( /^\s*\|\s*/u ) ) {
529
- state.tokenize = eatTemplateArgument( true );
530
- return makeLocalStyle( 'mw-template-delimiter', state );
531
- } else if ( stream.match( /^\s*\}\}/u ) ) {
532
- state.tokenize = state.stack.pop();
533
- return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' );
534
- } else if ( stream.match( /^\s*<!--.*?-->/u ) ) {
535
- return makeLocalStyle( 'mw-comment', state );
536
- } else if ( haveAte && stream.sol() ) {
537
- // @todo error message
538
- state.nTemplate--;
539
- state.tokenize = state.stack.pop();
540
- return '';
541
- } else if ( stream.match( /^\s*[^\s|&~#<>[\]{}]+/u ) ) {
542
- state.tokenize = eatTemplatePageName( true );
543
- return makeLocalStyle( 'mw-template-name mw-pagename', state );
544
- } else if ( stream.eatSpace() ) {
545
- return stream.eol()
546
- ? makeLocalStyle( 'mw-template-name', state )
547
- : makeLocalStyle( 'mw-template-name mw-pagename', state );
548
- } else if ( !stream.match( '{{', false ) && stream.eat( /[#<>[\]{}]/ ) ) {
549
- return makeLocalStyle( 'error', state );
550
- }
551
- return eatWikiText( 'mw-template-name mw-pagename' )( stream, state );
552
- };
553
- }
554
-
555
- function eatTemplateArgument( expectArgName ) {
556
- return function ( stream, state ) {
557
- if ( expectArgName && stream.eatWhile( /[^=|}{[<&~]/ ) ) {
558
- if ( stream.eat( '=' ) ) {
559
- state.tokenize = eatTemplateArgument( false );
560
- return makeLocalStyle( 'mw-template-argument-name', state );
561
- }
562
- return makeLocalStyle( 'mw-template', state );
563
- } else if ( stream.eatWhile( /[^|}{[<&~]/ ) ) {
564
- return makeLocalStyle( 'mw-template', state );
565
- } else if ( stream.eat( '|' ) ) {
566
- state.tokenize = eatTemplateArgument( true );
567
- return makeLocalStyle( 'mw-template-delimiter', state );
568
- } else if ( stream.match( '}}' ) ) {
569
- state.tokenize = state.stack.pop();
570
- return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' );
571
- }
572
- return eatWikiText( 'mw-template' )( stream, state );
573
- };
574
- }
575
-
576
- function eatExternalLinkProtocol( chars ) {
577
- return function ( stream, state ) {
578
- for ( let i = 0; i < chars; i++ ) {
579
- stream.next();
580
- }
581
- if ( stream.eol() ) {
582
- state.nLink--;
583
- // @todo error message
584
- state.tokenize = state.stack.pop();
585
- } else {
586
- state.tokenize = inExternalLink;
587
- }
588
- return makeLocalStyle( 'mw-extlink-protocol', state );
589
- };
590
- }
591
-
592
- function inExternalLink( stream, state ) {
593
- if ( stream.sol() ) {
594
- state.nLink--;
595
- // @todo error message
596
- state.tokenize = state.stack.pop();
597
- return '';
598
- } else if ( stream.match( /^\s*\]/u ) ) {
599
- state.tokenize = state.stack.pop();
600
- return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' );
601
- } else if ( stream.eatSpace() ) {
602
- state.tokenize = inExternalLinkText;
603
- return makeLocalStyle( '', state );
604
- } else if ( stream.match( /^[^\s\]{&~']+/u ) || stream.eatSpace() ) {
605
- if ( stream.peek() === '\'' ) {
606
- if ( stream.match( '\'\'', false ) ) {
607
- state.tokenize = inExternalLinkText;
608
- } else {
609
- stream.next();
610
- }
611
- }
612
- return makeLocalStyle( 'mw-extlink', state );
613
- }
614
- return eatWikiText( 'mw-extlink' )( stream, state );
615
- }
616
-
617
- function inExternalLinkText( stream, state ) {
618
- if ( stream.sol() ) {
619
- state.nLink--;
620
- // @todo error message
621
- state.tokenize = state.stack.pop();
622
- return '';
623
- } else if ( stream.eat( ']' ) ) {
624
- state.tokenize = state.stack.pop();
625
- return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' );
626
- } else if ( stream.match( /^[^'\]{&~<]+/ ) ) {
627
- return makeStyle( 'mw-extlink-text', state );
628
- }
629
- return eatWikiText( 'mw-extlink-text' )( stream, state );
630
- }
631
-
632
- function inLink( stream, state ) {
633
- if ( stream.sol() ) {
634
- state.nLink--;
635
- // @todo error message
636
- state.tokenize = state.stack.pop();
637
- return '';
638
- } else if ( stream.match( /^\s*#\s*/u ) ) {
639
- state.tokenize = inLinkToSection();
640
- return makeLocalStyle( 'mw-link', state );
641
- } else if ( stream.match( /^\s*\|\s*/u ) ) {
642
- state.tokenize = eatLinkText();
643
- return makeLocalStyle( 'mw-link-delimiter', state );
644
- } else if ( stream.match( /^\s*\]\]/u ) ) {
645
- state.tokenize = state.stack.pop();
646
- return makeLocalStyle( 'mw-link-bracket', state, 'nLink' );
647
- } else if ( stream.match( /^\s*[^\s|&~#<>[\]{}]+/u ) || stream.eatSpace() ) { // FIXME '{{' brokes Link, sample [[z{{page]]
648
- return makeLocalStyle( 'mw-link-pagename mw-pagename', state );
649
- } else if ( !stream.match( '{{', false ) && stream.eat( /[<>[\]{}]/ ) ) {
650
- return makeLocalStyle( 'error', state );
651
- }
652
- return eatWikiText( 'mw-link-pagename mw-pagename' )( stream, state );
653
- }
654
-
655
- function inLinkToSection() {
656
- return function ( stream, state ) {
657
- if ( stream.sol() ) {
658
- // @todo error message
659
- state.nLink--;
660
- state.tokenize = state.stack.pop();
661
- return '';
662
- } else if ( stream.match( /^[^|\]&~{}]+/ ) ) { // FIXME '{{' brokes Link, sample [[z{{page]]
663
- return makeLocalStyle( 'mw-link-tosection', state );
664
- } else if ( stream.eat( '|' ) ) {
665
- state.tokenize = eatLinkText();
666
- return makeLocalStyle( 'mw-link-delimiter', state );
667
- } else if ( stream.match( ']]' ) ) {
668
- state.tokenize = state.stack.pop();
669
- return makeLocalStyle( 'mw-link-bracket', state, 'nLink' );
670
- }
671
- return eatWikiText( 'mw-link-tosection' )( stream, state );
672
- };
673
- }
674
-
675
- function inFileLink( stream, state ) {
676
- if ( stream.sol() ) {
677
- state.nLink--;
678
- // @todo error message
679
- state.tokenize = state.stack.pop();
680
- return '';
681
- } else if ( stream.match( /^\s*\|\s*/u ) ) {
682
- state.tokenize = eatLinkText( true );
683
- return makeLocalStyle( 'mw-link-delimiter', state );
684
- } else if ( stream.match( /^\s*\]\]/u ) ) {
685
- state.tokenize = state.stack.pop();
686
- return makeLocalStyle( 'mw-link-bracket', state, 'nLink' );
687
- } else if ( stream.match( /^\s*[^\s|&~#<>[\]{}]+/u ) || stream.eatSpace() ) { // FIXME '{{' brokes Link, sample [[z{{page]]
688
- return makeLocalStyle( 'mw-link-pagename mw-pagename', state );
689
- } else if ( !stream.match( '{{', false ) && stream.eat( /[#<>[\]{}]/ ) ) {
690
- return makeLocalStyle( 'error', state );
691
- }
692
- return eatWikiText( 'mw-link-pagename mw-pagename' )( stream, state );
693
- }
694
-
695
- function eatLinkText( isFile, bracketEaten ) {
696
- return function ( stream, state ) {
697
- const tmpstyle = 'mw-link-text',
698
- regex = isFile ? /^[^'\]{&~<|[]+/ : /^[^'\]{&~<]+/;
699
- if ( stream.match( ']]' ) ) {
700
- if ( !bracketEaten && stream.peek() === ']' ) {
701
- stream.backUp( 1 );
702
- state.tokenize = eatLinkText( isFile, true );
703
- return makeLocalStyle( tmpstyle, state );
704
- }
705
- state.tokenize = state.stack.pop();
706
- return makeLocalStyle( 'mw-link-bracket', state, 'nLink' );
707
- } else if ( isFile && stream.eat( '|' ) ) {
708
- return makeLocalStyle( 'mw-link-delimiter', state );
709
- } else if ( stream.match( '\'\'\'' ) ) {
710
- return makeLocalStyle( 'mw-link-text mw-apostrophes-bold', state );
711
- } else if ( stream.match( '\'\'' ) ) {
712
- return makeLocalStyle( 'mw-link-text mw-apostrophes-italic', state );
713
- } else if ( stream.match( regex ) ) {
714
- return makeLocalStyle( tmpstyle, state );
715
- }
716
- return eatWikiText( tmpstyle )( stream, state );
717
- };
718
- }
719
-
720
- function eatTagName( chars, isCloseTag, isHtmlTag ) {
721
- return function ( stream, state ) {
722
- let name = '';
723
- for ( let i = 0; i < chars; i++ ) {
724
- name += stream.next();
725
- }
726
- name = name.toLowerCase();
727
- stream.eatSpace();
728
-
729
- if ( isHtmlTag ) {
730
- state.tokenize = eatHtmlTagAttribute( name, isCloseTag && !voidHtmlTags.has( name ) );
731
- return makeLocalStyle( 'mw-htmltag-name', state );
732
- } // it is the extension tag
733
- if ( isCloseTag ) {
734
- state.tokenize = eatChars( 1, 'mw-exttag-bracket' );
735
- } else {
736
- state.tokenize = eatExtTagAttribute( name );
737
- }
738
- return makeLocalStyle( 'mw-exttag-name', state );
739
- };
740
- }
741
-
742
- function eatHtmlTagAttribute( name, isCloseTag ) {
743
- const style = `mw-htmltag-attribute${ isCloseTag ? ' error' : '' }`;
744
- return function ( stream, state ) {
745
- if ( stream.match( /^[^>/<{&~]+/ ) ) {
746
- return makeLocalStyle( style, state );
747
- } else if ( stream.eat( '>' ) ) {
748
- if ( !( voidHtmlTags.has( name ) || isCloseTag ) ) {
749
- state.InHtmlTag.push( name );
750
- }
751
- state.tokenize = state.stack.pop();
752
- return makeLocalStyle( 'mw-htmltag-bracket', state );
753
- } else if ( stream.match( '/>' ) ) {
754
- if ( !( voidHtmlTags.has( name ) || isCloseTag ) ) { // HTML5 standard
755
- state.InHtmlTag.push( name );
756
- }
757
- state.tokenize = state.stack.pop();
758
- return makeLocalStyle( `mw-htmltag-bracket${ voidHtmlTags.has( name ) ? '' : ' error' }`, state );
759
- }
760
- return eatWikiText( style )( stream, state );
761
- };
762
- }
763
-
764
- function eatExtTagAttribute( name ) {
765
- return function ( stream, state ) {
766
- if ( stream.match( /^(?:"[^">]*"|'[^'>]*'|[^>/<{&~])+/ ) ) {
767
- return makeLocalStyle( 'mw-exttag-attribute', state );
768
- } else if ( stream.eat( '>' ) ) {
769
- state.extName = name;
770
- if ( name in mwConfig.tagModes ) {
771
- state.extMode = CodeMirror.getMode( { mode: 'mediawiki', mwConfig }, mwConfig.tagModes[ name ] );
772
- state.extState = CodeMirror.startState( state.extMode );
773
- }
774
- state.tokenize = eatExtTagArea( name );
775
- return makeLocalStyle( 'mw-exttag-bracket', state );
776
- } else if ( stream.match( '/>' ) ) {
777
- state.tokenize = state.stack.pop();
778
- return makeLocalStyle( 'mw-exttag-bracket', state );
779
- }
780
- return eatWikiText( 'mw-exttag-attribute' )( stream, state );
781
- };
782
- }
783
-
784
- function eatExtTagArea( name ) {
785
- return function ( stream, state ) {
786
- const { pos: from } = stream,
787
- pattern = new RegExp( `</${ name }\\s*>`, 'iu' ),
788
- m = pattern.exec( from ? stream.string.slice( from ) : stream.string );
789
- let origString = false,
790
- to;
791
-
792
- if ( m ) {
793
- if ( m.index === 0 ) {
794
- state.tokenize = eatExtCloseTag( name );
795
- state.extName = false;
796
- if ( state.extMode !== false ) {
797
- state.extMode = false;
798
- state.extState = false;
799
- }
800
- return state.tokenize( stream, state );
801
- }
802
- to = m.index + from;
803
- origString = stream.string;
804
- stream.string = origString.slice( 0, to );
805
- }
806
-
807
- state.stack.push( state.tokenize );
808
- state.tokenize = eatExtTokens( origString );
809
- return state.tokenize( stream, state );
810
- };
811
- }
812
-
813
- function eatExtCloseTag( name ) {
814
- return function ( stream, state ) {
815
- stream.next(); // eat <
816
- stream.next(); // eat /
817
- state.tokenize = eatTagName( name.length, true, false );
818
- return makeLocalStyle( 'mw-exttag-bracket', state );
819
- };
820
- }
821
-
822
- function eatExtTokens( origString ) {
823
- return function ( stream, state ) {
824
- let ret;
825
- if ( state.extMode === false ) {
826
- ret = 'mw-exttag';
827
- stream.skipToEnd();
828
- } else {
829
- ret = `mw-tag-${ state.extName } ${ state.extMode.token( stream, state.extState, origString === false ) }`;
830
- }
831
- if ( stream.eol() ) {
832
- if ( origString !== false ) {
833
- stream.string = origString;
834
- }
835
- state.tokenize = state.stack.pop();
836
- }
837
- return makeLocalStyle( ret, state );
838
- };
839
- }
840
-
841
- CodeMirror.defineMode( 'mediawiki', function ( config /* , parserConfig */ ) {
842
- ( { mwConfig } = config );
843
- urlProtocols = new RegExp( `^(?:${ mwConfig.urlProtocols })`, 'i' );
844
-
845
- return {
846
- startState() {
847
- return {
848
- tokenize: eatWikiText( '' ), stack: [], InHtmlTag: [], extName: false, extMode: false,
849
- extState: false, nTemplate: 0, nLink: 0, nExt: 0, isBold: false, lpar: false
850
- };
851
- },
852
- copyState( state ) {
853
- return {
854
- tokenize: state.tokenize,
855
- stack: state.stack.concat( [] ),
856
- InHtmlTag: state.InHtmlTag.concat( [] ),
857
- extName: state.extName,
858
- extMode: state.extMode,
859
- extState: state.extMode !== false && CodeMirror.copyState( state.extMode, state.extState ),
860
- nTemplate: state.nTemplate,
861
- nLink: state.nLink,
862
- nExt: state.nExt,
863
- isBold: false,
864
- lpar: false
865
- };
866
- },
867
- token( stream, state ) {
868
- return state.tokenize( stream, state ); // get token style
869
- }
870
- };
871
- } );
872
-
873
- CodeMirror.defineMIME( 'text/mediawiki', 'mediawiki' );
874
-
875
- function eatPre( stream, state ) {
876
- if ( stream.match( /^[^&<]+/ ) ) {
877
- return '';
878
- } else if ( stream.eat( '<' ) ) {
879
- if ( !state.nowiki && stream.match( 'nowiki>' ) || state.nowiki && stream.match( '/nowiki>' ) ) {
880
- state.nowiki = !state.nowiki;
881
- return 'mw-comment';
882
- }
883
- return '';
884
- }
885
- stream.next(); // eat &
886
- return eatMnemonic( stream, '' );
887
- }
888
-
889
- function eatNowiki( stream ) {
890
- if ( stream.match( /^[^&]+/ ) ) {
891
- return '';
892
- }
893
- stream.next(); // eat &
894
- return eatMnemonic( stream, '' );
895
- }
896
-
897
- CodeMirror.defineMode( 'mw-tag-pre', function ( /* config, parserConfig */ ) {
898
- return {
899
- startState() {
900
- return { nowiki: false };
901
- },
902
- copyState( state ) {
903
- return { nowiki: state.nowiki };
904
- },
905
- token: eatPre
906
- };
907
- } );
908
-
909
- CodeMirror.defineMode( 'mw-tag-nowiki', function ( /* config, parserConfig */ ) {
910
- return {
911
- startState() {
912
- return {};
913
- },
914
- token: eatNowiki
915
- };
916
- } );
917
- }( CodeMirror ) );