build: Replace jshint/jscs with eslint, introduce stylelint

Change-Id: I9b5afb39f5a0d32e722b2a1f56c219b8c85b451c
This commit is contained in:
Ed Sanders 2017-03-21 10:13:18 +00:00 committed by Jforrester
parent a6427ea4c9
commit 66ba217ba2
10 changed files with 396 additions and 377 deletions

15
.eslintrc.json Normal file
View file

@ -0,0 +1,15 @@
{
"extends": "wikimedia",
"env": {
"browser": true,
"jquery": true,
"qunit": true
},
"globals": {
"mediaWiki": false,
"CodeMirror": false
},
"rules": {
"dot-notation": [ "error", { "allowKeywords": true } ]
}
}

View file

@ -1,6 +0,0 @@
{
"preset": "wikimedia",
"requireVarDeclFirst": null,
"requireMultipleVarDecl": null,
"disallowEmptyBlocks": null
}

View file

@ -1,4 +0,0 @@
node_modules
vendor
# upstream libs
resources/lib/

View file

@ -1,25 +0,0 @@
{
// Enforcing
"bitwise": true,
"eqeqeq": true,
"freeze": true,
"latedef": true,
"noarg": true,
"nonew": true,
"undef": true,
"unused": true,
"strict": false,
// Relaxing
"es5": false,
// Environment
"browser": true,
"jquery": true,
"globals": {
"mediaWiki": false,
"CodeMirror": false,
"mod": false
}
}

3
.stylelintrc Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "stylelint-config-wikimedia"
}

View file

@ -1,23 +1,24 @@
/*jshint node:true */ /* eslint-env node, es6 */
module.exports = function ( grunt ) { module.exports = function ( grunt ) {
grunt.loadNpmTasks( 'grunt-contrib-jshint' );
grunt.loadNpmTasks( 'grunt-jsonlint' );
grunt.loadNpmTasks( 'grunt-banana-checker' ); grunt.loadNpmTasks( 'grunt-banana-checker' );
grunt.loadNpmTasks( 'grunt-jscs' ); grunt.loadNpmTasks( 'grunt-eslint' );
grunt.loadNpmTasks( 'grunt-jsonlint' );
grunt.loadNpmTasks( 'grunt-stylelint' );
grunt.initConfig( { grunt.initConfig( {
jshint: { eslint: {
options: {
jshintrc: true
},
all: [ all: [
'**/*.js', '**/*.js',
'!resources/lib/**', '!resources/lib/**',
'!node_modules/**' '!node_modules/**'
] ]
}, },
jscs: { stylelint: {
src: '<%= jshint.all %>' all: [
'**/*.css',
'!resources/lib/**',
'!node_modules/**'
]
}, },
banana: { banana: {
all: 'i18n/' all: 'i18n/'
@ -30,6 +31,6 @@ module.exports = function ( grunt ) {
} }
} ); } );
grunt.registerTask( 'test', [ 'jshint', 'jscs', 'jsonlint', 'banana' ] ); grunt.registerTask( 'test', [ 'eslint', 'stylelint', 'jsonlint', 'banana' ] );
grunt.registerTask( 'default', 'test' ); grunt.registerTask( 'default', 'test' );
}; };

View file

@ -3,11 +3,12 @@
"test": "grunt test" "test": "grunt test"
}, },
"devDependencies": { "devDependencies": {
"grunt": "0.4.5", "eslint-config-wikimedia": "0.3.0",
"grunt-cli": "0.1.13", "grunt": "1.0.1",
"grunt-contrib-jshint": "0.11.3", "grunt-banana-checker": "0.5.0",
"grunt-banana-checker": "0.4.0", "grunt-eslint": "19.0.0",
"grunt-jscs": "2.5.0", "grunt-jsonlint": "1.1.0",
"grunt-jsonlint": "1.0.7" "grunt-stylelint": "0.7.0",
"stylelint-config-wikimedia": "0.4.1"
} }
} }

View file

@ -1,248 +1,272 @@
( function ( mw, $ ) { ( function ( mw, $ ) {
var origTextSelection, codeMirror, api, originHooksTextarea;
if ( mw.config.get( 'wgCodeEditorCurrentLanguage' ) ) { // If the CodeEditor is used then just exit; if ( mw.config.get( 'wgCodeEditorCurrentLanguage' ) ) { // If the CodeEditor is used then just exit;
return; return;
} }
// codeMirror needs a special textselection jQuery function to work, save the current one to restore when // codeMirror needs a special textselection jQuery function to work, save the current one to restore when
// CodeMirror get's disabled. // CodeMirror get's disabled.
var origTextSelection = $.fn.textSelection, origTextSelection = $.fn.textSelection;
codeMirror = mw.user.options.get( 'usecodemirror' ) === '1' || mw.user.options.get( 'usecodemirror' ) === 1, codeMirror = mw.user.options.get( 'usecodemirror' ) === '1' || mw.user.options.get( 'usecodemirror' ) === 1;
api = new mw.Api(), api = new mw.Api();
// function for a textselection function for CodeMirror originHooksTextarea = $.valHooks.textarea;
cmTextSelection = function ( command, options ) {
if ( !codeMirror || codeMirror.getTextArea() !== this[ 0 ] ) {
return origTextSelection.call( this, command, options );
}
var fn, retval;
fn = { // function for a textselection function for CodeMirror
/** function cmTextSelection( command, options ) {
* Get the contents of the textarea var fn, retval;
*/
getContents: function () {
return codeMirror.doc.getValue();
},
setContents: function ( newContents ) { if ( !codeMirror || codeMirror.getTextArea() !== this[ 0 ] ) {
codeMirror.doc.setValue( newContents ); return origTextSelection.call( this, command, options );
}, }
/** fn = {
* Get the currently selected text in this textarea. Will focus the textarea /**
* in some browsers (IE/Opera) * Get the contents of the textarea
*/ *
getSelection: function () { * @return {string}
return codeMirror.doc.getSelection(); */
}, getContents: function () {
return codeMirror.doc.getValue();
},
/** setContents: function ( newContents ) {
* Inserts text at the beginning and end of a text selection, optionally codeMirror.doc.setValue( newContents );
* inserting text at the caret when selection is empty. },
*/
encapsulateSelection: function ( options ) {
return this.each( function () {
var insertText,
selText,
selectPeri = options.selectPeri,
pre = options.pre,
post = options.post,
startCursor = codeMirror.doc.getCursor( true ),
endCursor = codeMirror.doc.getCursor( false );
if ( options.selectionStart !== undefined ) { /**
// fn[command].call( this, options ); * Get the currently selected text in this textarea. Will focus the textarea
fn.setSelection( { start: options.selectionStart, end: options.selectionEnd } ); // not tested * in some browsers (IE/Opera)
*
* @return {string}
*/
getSelection: function () {
return codeMirror.doc.getSelection();
},
/**
* Inserts text at the beginning and end of a text selection, optionally
* inserting text at the caret when selection is empty.
*
* @param {Object} options
* @return {jQuery}
*/
encapsulateSelection: function ( options ) {
return this.each( function () {
var insertText,
selText,
selectPeri = options.selectPeri,
pre = options.pre,
post = options.post,
startCursor = codeMirror.doc.getCursor( true ),
endCursor = codeMirror.doc.getCursor( false );
if ( options.selectionStart !== undefined ) {
// fn[command].call( this, options );
fn.setSelection( { start: options.selectionStart, end: options.selectionEnd } ); // not tested
}
selText = codeMirror.doc.getSelection();
if ( !selText ) {
selText = options.peri;
} else if ( options.replace ) {
selectPeri = false;
selText = options.peri;
} else {
selectPeri = false;
while ( selText.charAt( selText.length - 1 ) === ' ' ) {
// Exclude ending space char
selText = selText.substring( 0, selText.length - 1 );
post += ' ';
} }
while ( selText.charAt( 0 ) === ' ' ) {
selText = codeMirror.doc.getSelection(); // Exclude prepending space char
if ( !selText ) { selText = selText.substring( 1, selText.length );
selText = options.peri; pre = ' ' + pre;
} else if ( options.replace ) {
selectPeri = false;
selText = options.peri;
} else {
selectPeri = false;
while ( selText.charAt( selText.length - 1 ) === ' ' ) {
// Exclude ending space char
selText = selText.substring( 0, selText.length - 1 );
post += ' ';
}
while ( selText.charAt( 0 ) === ' ' ) {
// Exclude prepending space char
selText = selText.substring( 1, selText.length );
pre = ' ' + pre;
}
} }
}
/** /**
* Do the splitlines stuff. * Do the splitlines stuff.
* *
* Wrap each line of the selected text with pre and post * Wrap each line of the selected text with pre and post
*/ *
function doSplitLines( selText, pre, post ) { * @param {string} selText
var i, * @param {string} pre
insertText = '', * @param {string} post
selTextArr = selText.split( '\n' ); * @return {string}
*/
function doSplitLines( selText, pre, post ) {
var i,
insertText = '',
selTextArr = selText.split( '\n' );
for ( i = 0; i < selTextArr.length; i++ ) { for ( i = 0; i < selTextArr.length; i++ ) {
insertText += pre + selTextArr[ i ] + post; insertText += pre + selTextArr[ i ] + post;
if ( i !== selTextArr.length - 1 ) { if ( i !== selTextArr.length - 1 ) {
insertText += '\n';
}
}
return insertText;
}
if ( options.splitlines ) {
selectPeri = false;
insertText = doSplitLines( selText, pre, post );
} else {
insertText = pre + selText + post;
}
if ( options.ownline ) {
if ( startCursor.ch !== 0 ) {
insertText = '\n' + insertText;
pre += '\n';
}
if ( codeMirror.doc.getLine( endCursor.line ).length !== endCursor.ch ) {
insertText += '\n'; insertText += '\n';
post += '\n';
} }
} }
return insertText;
}
codeMirror.doc.replaceSelection( insertText ); if ( options.splitlines ) {
selectPeri = false;
insertText = doSplitLines( selText, pre, post );
} else {
insertText = pre + selText + post;
}
if ( selectPeri ) { if ( options.ownline ) {
codeMirror.doc.setSelection( if ( startCursor.ch !== 0 ) {
codeMirror.doc.posFromIndex( codeMirror.doc.indexFromPos( startCursor ) + pre.length ), insertText = '\n' + insertText;
codeMirror.doc.posFromIndex( codeMirror.doc.indexFromPos( startCursor ) + pre.length + selText.length ) pre += '\n';
);
} }
} );
},
/** if ( codeMirror.doc.getLine( endCursor.line ).length !== endCursor.ch ) {
* Get the position (in resolution of bytes not necessarily characters) insertText += '\n';
* in a textarea post += '\n';
*/ }
getCaretPosition: function ( options ) {
var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ),
endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) );
if ( options.startAndEnd ) {
return [ caretPos, endPos ];
} }
return caretPos;
},
setSelection: function ( options ) { codeMirror.doc.replaceSelection( insertText );
return this.each( function () {
codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( options.start ), codeMirror.doc.posFromIndex( options.end ) );
} );
},
/** if ( selectPeri ) {
* Scroll a textarea to the current cursor position. You can set the cursor codeMirror.doc.setSelection(
* position with setSelection() codeMirror.doc.posFromIndex( codeMirror.doc.indexFromPos( startCursor ) + pre.length ),
*/ codeMirror.doc.posFromIndex( codeMirror.doc.indexFromPos( startCursor ) + pre.length + selText.length )
scrollToCaretPosition: function () { );
return this.each( function () { }
codeMirror.scrollIntoView( null ); } );
} ); },
/**
* Get the position (in resolution of bytes not necessarily characters)
* in a textarea
*
* @param {Object} options
* @return {number}
*/
getCaretPosition: function ( options ) {
var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ),
endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) );
if ( options.startAndEnd ) {
return [ caretPos, endPos ];
} }
}; return caretPos;
},
switch ( command ) { setSelection: function ( options ) {
// case 'getContents': // no params return this.each( function () {
// case 'setContents': // no params with defaults codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( options.start ), codeMirror.doc.posFromIndex( options.end ) );
// case 'getSelection': // no params } );
case 'encapsulateSelection': },
options = $.extend( {
pre: '', // Text to insert before the cursor/selection
peri: '', // Text to insert between pre and post and select afterwards
post: '', // Text to insert after the cursor/selection
ownline: false, // Put the inserted text on a line of its own
replace: false, // If there is a selection, replace it with peri instead of leaving it alone
selectPeri: true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
splitlines: false, // If multiple lines are selected, encapsulate each line individually
selectionStart: undefined, // Position to start selection at
selectionEnd: undefined // Position to end selection at. Defaults to start
}, options );
break;
case 'getCaretPosition':
options = $.extend( {
// Return [start, end] instead of just start
startAndEnd: false
}, options );
// FIXME: We may not need character position-based functions if we insert markers in the right places
break;
case 'setSelection':
options = $.extend( {
// Position to start selection at
start: undefined,
// Position to end selection at. Defaults to start
end: undefined,
// Element to start selection in (iframe only)
startContainer: undefined,
// Element to end selection in (iframe only). Defaults to startContainer
endContainer: undefined
}, options );
if ( options.end === undefined ) { /**
options.end = options.start; * Scroll a textarea to the current cursor position. You can set the cursor
} * position with setSelection()
if ( options.endContainer === undefined ) { *
options.endContainer = options.startContainer; * @return {jQuery}
} */
// FIXME: We may not need character position-based functions if we insert markers in the right places scrollToCaretPosition: function () {
break; return this.each( function () {
case 'scrollToCaretPosition': codeMirror.scrollIntoView( null );
options = $.extend( { } );
force: false // Force a scroll even if the caret position is already visible
}, options );
break;
} }
};
retval = fn[ command ].call( this, options ); switch ( command ) {
codeMirror.focus(); // case 'getContents': // no params
// case 'setContents': // no params with defaults
// case 'getSelection': // no params
case 'encapsulateSelection':
options = $.extend( {
pre: '', // Text to insert before the cursor/selection
peri: '', // Text to insert between pre and post and select afterwards
post: '', // Text to insert after the cursor/selection
ownline: false, // Put the inserted text on a line of its own
replace: false, // If there is a selection, replace it with peri instead of leaving it alone
selectPeri: true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
splitlines: false, // If multiple lines are selected, encapsulate each line individually
selectionStart: undefined, // Position to start selection at
selectionEnd: undefined // Position to end selection at. Defaults to start
}, options );
break;
case 'getCaretPosition':
options = $.extend( {
// Return [start, end] instead of just start
startAndEnd: false
}, options );
// FIXME: We may not need character position-based functions if we insert markers in the right places
break;
case 'setSelection':
options = $.extend( {
// Position to start selection at
start: undefined,
// Position to end selection at. Defaults to start
end: undefined,
// Element to start selection in (iframe only)
startContainer: undefined,
// Element to end selection in (iframe only). Defaults to startContainer
endContainer: undefined
}, options );
return retval; if ( options.end === undefined ) {
}, options.end = options.start;
/** }
* Adds the CodeMirror button to WikiEditor if ( options.endContainer === undefined ) {
*/ options.endContainer = options.startContainer;
addCodeMirrorToWikiEditor = function () { }
if ( $( '#wikiEditor-section-main' ).length > 0 ) { // FIXME: We may not need character position-based functions if we insert markers in the right places
var msg = codeMirror ? 'codemirror-disable-label' : 'codemirror-enable-label'; break;
case 'scrollToCaretPosition':
options = $.extend( {
force: false // Force a scroll even if the caret position is already visible
}, options );
break;
}
$( '#wpTextbox1' ).wikiEditor( retval = fn[ command ].call( this, options );
'addToToolbar', codeMirror.focus();
{
section: 'main', return retval;
groups: { }
codemirror: {
tools: { /**
CodeMirror: { * Adds the CodeMirror button to WikiEditor
label: mw.msg( msg ), */
type: 'button', function addCodeMirrorToWikiEditor() {
// FIXME: There should be a better way? var msg;
icon: mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/cm-' + ( codeMirror ? 'on.png' : 'off.png' ), if ( $( '#wikiEditor-section-main' ).length > 0 ) {
action: { msg = codeMirror ? 'codemirror-disable-label' : 'codemirror-enable-label';
type: 'callback',
execute: function ( context ) { $( '#wpTextbox1' ).wikiEditor(
switchCodeMirror( context ); 'addToToolbar',
} {
section: 'main',
groups: {
codemirror: {
tools: {
CodeMirror: {
label: mw.msg( msg ),
type: 'button',
// FIXME: There should be a better way?
icon: mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/cm-' + ( codeMirror ? 'on.png' : 'off.png' ),
action: {
type: 'callback',
execute: function ( context ) {
// eslint-disable-next-line no-use-before-define
switchCodeMirror( context );
} }
} }
} }
} }
} }
} }
); }
} );
}, }
originHooksTextarea = $.valHooks.textarea; }
// define JQuery hook for searching and replacing text using JS if CodeMirror is enabled, see Bug: T108711 // define JQuery hook for searching and replacing text using JS if CodeMirror is enabled, see Bug: T108711
$.valHooks.textarea = { $.valHooks.textarea = {
@ -308,6 +332,7 @@
.attr( 'src', $src ) .attr( 'src', $src )
.attr( 'title', mw.msg( 'codemirror-enable-label' ) ); .attr( 'title', mw.msg( 'codemirror-enable-label' ) );
} else { } else {
// eslint-disable-next-line no-use-before-define
enableCodeMirror(); enableCodeMirror();
$src = mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/' + ( context ? 'cm-on.png' : 'old-cm-on.png' ); $src = mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/' + ( context ? 'cm-on.png' : 'old-cm-on.png' );
$img $img
@ -327,20 +352,20 @@
return; return;
} }
codeMirror = CodeMirror.fromTextArea( textbox1[ 0 ], { codeMirror = CodeMirror.fromTextArea( textbox1[ 0 ], {
mwextFunctionSynonyms: mw.config.get( 'extCodeMirrorFunctionSynonyms' ), mwextFunctionSynonyms: mw.config.get( 'extCodeMirrorFunctionSynonyms' ),
mwextTags: mw.config.get( 'extCodeMirrorTags' ), mwextTags: mw.config.get( 'extCodeMirrorTags' ),
mwextDoubleUnderscore: mw.config.get( 'extCodeMirrorDoubleUnderscore' ), mwextDoubleUnderscore: mw.config.get( 'extCodeMirrorDoubleUnderscore' ),
mwextUrlProtocols: mw.config.get( 'extCodeMirrorUrlProtocols' ), mwextUrlProtocols: mw.config.get( 'extCodeMirrorUrlProtocols' ),
mwextModes: mw.config.get( 'extCodeMirrorExtModes' ), mwextModes: mw.config.get( 'extCodeMirrorExtModes' ),
styleActiveLine: true, styleActiveLine: true,
lineWrapping: true, lineWrapping: true,
readOnly: textbox1[ 0 ].readOnly, readOnly: textbox1[ 0 ].readOnly,
// select mediawiki as text input mode // select mediawiki as text input mode
mode: 'text/mediawiki', mode: 'text/mediawiki',
extraKeys: { extraKeys: {
Tab: false Tab: false
} }
} ); } );
// Our best friend, IE, needs some special css // Our best friend, IE, needs some special css
if ( window.navigator.userAgent.indexOf( 'Trident/' ) > -1 ) { if ( window.navigator.userAgent.indexOf( 'Trident/' ) > -1 ) {
$( '.CodeMirror' ).addClass( 'CodeMirrorIE' ); $( '.CodeMirror' ).addClass( 'CodeMirrorIE' );
@ -356,6 +381,7 @@
if ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 ) { if ( $.inArray( mw.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 ) {
// This function shouldn't be called without user.options is loaded, but it's not guaranteed // This function shouldn't be called without user.options is loaded, but it's not guaranteed
mw.loader.using( 'user.options', function () { mw.loader.using( 'user.options', function () {
var $image;
// This can be the string "0" if the user disabled the preference - Bug T54542#555387 // This can be the string "0" if the user disabled the preference - Bug T54542#555387
if ( mw.user.options.get( 'usebetatoolbar' ) === 1 || mw.user.options.get( 'usebetatoolbar' ) === '1' ) { if ( mw.user.options.get( 'usebetatoolbar' ) === 1 || mw.user.options.get( 'usebetatoolbar' ) === '1' ) {
// load wikiEditor's toolbar (if not already) and add our button // load wikiEditor's toolbar (if not already) and add our button
@ -364,7 +390,7 @@
).then( addCodeMirrorToWikiEditor ); ).then( addCodeMirrorToWikiEditor );
} else { } else {
// If WikiEditor isn't enabled, add CodeMirror button to the default wiki editor toolbar // If WikiEditor isn't enabled, add CodeMirror button to the default wiki editor toolbar
var $image = $( '<img>' ).attr( { $image = $( '<img>' ).attr( {
width: 23, width: 23,
height: 22, height: 22,
src: mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/old-cm-' + ( codeMirror ? 'on.png' : 'off.png' ), src: mw.config.get( 'wgExtensionAssetsPath' ) + '/CodeMirror/resources/images/old-cm-' + ( codeMirror ? 'on.png' : 'off.png' ),

View file

@ -1,5 +1,5 @@
.CodeMirror { .CodeMirror {
border: 1px solid #CCC; border: 1px solid #ccc;
font-size: medium; font-size: medium;
line-height: 1.5em; line-height: 1.5em;
} }
@ -8,101 +8,104 @@
font-size: small; font-size: small;
} }
/* stylelint-disable block-opening-brace-newline-before, block-opening-brace-newline-after,
block-closing-brace-space-after, declaration-block-single-line-max-declarations,
declaration-block-semicolon-newline-after, selector-list-comma-newline-after */
.cm-mw-pagename { text-decoration: underline; } .cm-mw-pagename { text-decoration: underline; }
.cm-mw-matching {background-color: gold;} .cm-mw-matching { background-color: #ffd700; }
.cm-mw-skipformatting { background-color: #adf; } .cm-mw-skipformatting { background-color: #adf; }
.cm-mw-list {color: #08f; font-weight: bold; background-color: #eee;} .cm-mw-list { color: #08f; font-weight: bold; background-color: #eee; }
.cm-mw-signature, .cm-mw-signature, .cm-mw-hr { color: #08f; font-weight: bold; background-color: #eee; }
.cm-mw-hr { color: #08f; font-weight: bold; background-color: #eee; } .cm-mw-indenting { color: #08f; font-weight: bold; background-color: #ddd; }
.cm-mw-indenting {color: #08f; font-weight: bold; background-color: #ddd;} .cm-mw-mnemonic { color: #090; }
.cm-mw-mnemonic {color: #090;} .cm-mw-comment { color: #aaa; font-weight: normal; }
.cm-mw-comment {color: #aaa; font-weight: normal;} .cm-mw-apostrophes-bold, .cm-mw-apostrophes-italic { color: #08f; }
.cm-mw-apostrophes-bold, .cm-mw-apostrophes-italic {color: #08f;}
pre.cm-mw-section-1 {font-size: 1.8em;} pre.cm-mw-section-1 { font-size: 1.8em; }
pre.cm-mw-section-2 {font-size: 1.5em;} pre.cm-mw-section-2 { font-size: 1.5em; }
pre.cm-mw-section-3 {font-weight: bold;} pre.cm-mw-section-3 { font-weight: bold; }
pre.cm-mw-section-4 {font-weight: bold;} pre.cm-mw-section-4 { font-weight: bold; }
pre.cm-mw-section-5 {font-weight: bold;} pre.cm-mw-section-5 { font-weight: bold; }
pre.cm-mw-section-6 {font-weight: bold;} pre.cm-mw-section-6 { font-weight: bold; }
.cm-mw-section-header {color: #08f; font-weight: normal;} .cm-mw-section-header { color: #08f; font-weight: normal; }
.cm-mw-template {color: #a11; font-weight: normal;} .cm-mw-template { color: #a11; font-weight: normal; }
.cm-mw-template-name {color: #a11; font-weight: bold;} .cm-mw-template-name { color: #a11; font-weight: bold; }
.cm-mw-template-name-mnemonic {font-weight: normal;} .cm-mw-template-name-mnemonic { font-weight: normal; }
.cm-mw-template-argument-name {color: #a11; font-weight: bold;} .cm-mw-template-argument-name { color: #a11; font-weight: bold; }
.cm-mw-template-delimiter {color: #a11; font-weight: bold;} .cm-mw-template-delimiter { color: #a11; font-weight: bold; }
.cm-mw-template-bracket {color: #a11; font-weight: bold;} .cm-mw-template-bracket { color: #a11; font-weight: bold; }
.cm-mw-templatevariable {color: #f50; font-weight: normal;} .cm-mw-templatevariable { color: #f50; font-weight: normal; }
.cm-mw-templatevariable-name {color: #f50; font-weight: bold;} .cm-mw-templatevariable-name { color: #f50; font-weight: bold; }
.cm-mw-templatevariable-bracket {color: #f50; font-weight: normal;} .cm-mw-templatevariable-bracket { color: #f50; font-weight: normal; }
.cm-mw-templatevariable-delimiter {color: #f50; font-weight: bold;} .cm-mw-templatevariable-delimiter { color: #f50; font-weight: bold; }
.cm-mw-parserfunction {font-weight: normal;} .cm-mw-parserfunction { font-weight: normal; }
.cm-mw-parserfunction-name {color: #70a; font-weight: bold;} .cm-mw-parserfunction-name { color: #70a; font-weight: bold; }
.cm-mw-parserfunction-bracket {color: #70a; font-weight: bold;} .cm-mw-parserfunction-bracket { color: #70a; font-weight: bold; }
.cm-mw-parserfunction-delimiter {color: #70a; font-weight: bold;} .cm-mw-parserfunction-delimiter { color: #70a; font-weight: bold; }
pre.cm-mw-exttag {background-image: url(img/ext2.png);} pre.cm-mw-exttag { background-image: url( img/ext2.png ); }
.cm-mw-exttag {background-image: url(img/ext4.png);} .cm-mw-exttag { background-image: url( img/ext4.png ); }
.cm-mw-exttag-name {color: #70a; font-weight: bold;} .cm-mw-exttag-name { color: #70a; font-weight: bold; }
.cm-mw-exttag-bracket {color: #70a; font-weight: normal;} .cm-mw-exttag-bracket { color: #70a; font-weight: normal; }
.cm-mw-exttag-attribute {color: #70a; font-weight: normal;} .cm-mw-exttag-attribute { color: #70a; font-weight: normal; }
.cm-mw-htmltag-name {color: #170; font-weight: bold;} .cm-mw-htmltag-name { color: #170; font-weight: bold; }
.cm-mw-htmltag-bracket {color: #170; font-weight: normal;} .cm-mw-htmltag-bracket { color: #170; font-weight: normal; }
.cm-mw-htmltag-attribute {color: #170; font-weight: normal;} .cm-mw-htmltag-attribute { color: #170; font-weight: normal; }
pre.cm-mw-tag-pre, .cm-mw-tag-pre {background-image: url(img/black4.png);} pre.cm-mw-tag-pre, .cm-mw-tag-pre { background-image: url( img/black4.png ); }
pre.cm-mw-tag-nowiki, .cm-mw-tag-nowiki {background-image: url(img/black4.png);} pre.cm-mw-tag-nowiki, .cm-mw-tag-nowiki { background-image: url( img/black4.png ); }
.cm-mw-link-pagename {color: #219; font-weight: normal;} .cm-mw-link-pagename { color: #219; font-weight: normal; }
.cm-mw-link-tosection {color: #08f; font-weight: normal;} .cm-mw-link-tosection { color: #08f; font-weight: normal; }
.cm-mw-link-bracket {color: #219; font-weight: normal;} .cm-mw-link-bracket { color: #219; font-weight: normal; }
.cm-mw-link-text {} /* .cm-mw-link-text { } */
.cm-mw-link-delimiter {color: #219; font-weight: normal;} .cm-mw-link-delimiter { color: #219; font-weight: normal; }
.cm-mw-extlink, .cm-mw-free-extlink {color: #219; font-weight: normal;} .cm-mw-extlink, .cm-mw-free-extlink { color: #219; font-weight: normal; }
.cm-mw-extlink-protocol, .cm-mw-free-extlink-protocol {color: #219; font-weight: bold;} .cm-mw-extlink-protocol, .cm-mw-free-extlink-protocol { color: #219; font-weight: bold; }
.cm-mw-extlink-text {} /* .cm-mw-extlink-text { } */
.cm-mw-extlink-bracket {color: #219; font-weight: bold;} .cm-mw-extlink-bracket { color: #219; font-weight: bold; }
.cm-mw-table-bracket {color: #e0e; font-weight: bold;} .cm-mw-table-bracket { color: #e0e; font-weight: bold; }
.cm-mw-table-delimiter {color: #e0e; font-weight: bold;} .cm-mw-table-delimiter { color: #e0e; font-weight: bold; }
.cm-mw-table-definition {color: #e0e; font-weight: normal;} .cm-mw-table-definition { color: #e0e; font-weight: normal; }
.cm-mw-table-caption {font-weight: bold;} .cm-mw-table-caption { font-weight: bold; }
.cm-mw-template-ground {} /* .cm-mw-template-ground {} */
.cm-mw-template2-ground {background-image: url(img/template4.png);} .cm-mw-template2-ground { background-image: url( img/template4.png ); }
.cm-mw-template3-ground {background-image: url(img/template8.png);} .cm-mw-template3-ground { background-image: url( img/template8.png ); }
.cm-mw-template-ext-ground {background-image: url(img/ext4.png);} .cm-mw-template-ext-ground { background-image: url( img/ext4.png ); }
.cm-mw-template-ext2-ground {background-image: url(img/ext4.png),url(img/ext4.png);} .cm-mw-template-ext2-ground { background-image: url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template-ext3-ground {background-image: url(img/ext4.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-template-ext3-ground { background-image: url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template-link-ground {background-image: url(img/link4.png);} .cm-mw-template-link-ground { background-image: url( img/link4.png ); }
.cm-mw-template-ext-link-ground {background-image: url(img/ext4.png),url(img/link4.png);} .cm-mw-template-ext-link-ground { background-image: url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template-ext2-link-ground {background-image: url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template-ext2-link-ground { background-image: url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template-ext3-link-ground {background-image: url(img/ext4.png),url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template-ext3-link-ground { background-image: url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template2-ext-ground {background-image: url(img/template4.png),url(img/ext4.png);} .cm-mw-template2-ext-ground { background-image: url( img/template4.png ), url( img/ext4.png ); }
.cm-mw-template2-ext2-ground {background-image: url(img/template4.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-template2-ext2-ground { background-image: url( img/template4.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template2-ext3-ground {background-image: url(img/template4.png),url(img/ext4.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-template2-ext3-ground { background-image: url( img/template4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template2-link-ground {background-image: url(img/template4.png),url(img/link4.png);} .cm-mw-template2-link-ground { background-image: url( img/template4.png ), url( img/link4.png ); }
.cm-mw-template2-ext-link-ground {background-image: url(img/template4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template2-ext-link-ground { background-image: url( img/template4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template2-ext2-link-ground {background-image: url(img/template4.png),url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template2-ext2-link-ground { background-image: url( img/template4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template2-ext3-link-ground {background-image: url(img/template4.png),url(img/ext4.png),url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template2-ext3-link-ground { background-image: url( img/template4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template3-ext-ground {background-image: url(img/template8.png),url(img/ext4.png);} .cm-mw-template3-ext-ground { background-image: url( img/template8.png ), url( img/ext4.png ); }
.cm-mw-template3-ext2-ground {background-image: url(img/template8.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-template3-ext2-ground { background-image: url( img/template8.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template3-ext3-ground {background-image: url(img/template8.png),url(img/ext4.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-template3-ext3-ground { background-image: url( img/template8.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-template3-link-ground {background-image: url(img/template8.png),url(img/link4.png);} .cm-mw-template3-link-ground { background-image: url( img/template8.png ), url( img/link4.png ); }
.cm-mw-template3-ext-link-ground {background-image: url(img/template8.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template3-ext-link-ground { background-image: url( img/template8.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template3-ext2-link-ground {background-image: url(img/template8.png),url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template3-ext2-link-ground { background-image: url( img/template8.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-template3-ext3-link-ground {background-image: url(img/template8.png),url(img/ext4.png),url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-template3-ext3-link-ground { background-image: url( img/template8.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-ext-ground {background-image: url(img/ext4.png)} .cm-mw-ext-ground { background-image: url( img/ext4.png ); }
.cm-mw-ext2-ground {background-image: url(img/ext4.png),url(img/ext4.png);} .cm-mw-ext2-ground { background-image: url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-ext3-ground {background-image: url(img/ext4.png),url(img/ext4.png),url(img/ext4.png);} .cm-mw-ext3-ground { background-image: url( img/ext4.png ), url( img/ext4.png ), url( img/ext4.png ); }
.cm-mw-ext-link-ground {background-image: url(img/link4.png);} .cm-mw-ext-link-ground { background-image: url( img/link4.png ); }
.cm-mw-ext2-link-ground {background-image: url(img/ext4.png),url(img/link4.png);} .cm-mw-ext2-link-ground { background-image: url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-ext3-link-ground {background-image: url(img/ext4.png),url(img/ext4.png),url(img/link4.png);} .cm-mw-ext3-link-ground { background-image: url( img/ext4.png ), url( img/ext4.png ), url( img/link4.png ); }
.cm-mw-link-ground {background-image: url(img/link4.png);} .cm-mw-link-ground { background-image: url( img/link4.png ); }

View file

@ -1,3 +1,4 @@
/* eslint-disable no-use-before-define */
( function ( CodeMirror ) { ( function ( CodeMirror ) {
'use strict'; 'use strict';
@ -19,7 +20,7 @@
return style; return style;
} }
CodeMirror.defineMode( 'mediawiki', function ( config/*, parserConfig */ ) { CodeMirror.defineMode( 'mediawiki', function ( config /* , parserConfig */ ) {
var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' ), var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' ),
permittedHtmlTags = { b: true, bdi: true, del: true, i: true, ins: true, permittedHtmlTags = { b: true, bdi: true, del: true, i: true, ins: true,
@ -354,19 +355,20 @@
function eatLinkText() { function eatLinkText() {
var linkIsBold, linkIsItalic; var linkIsBold, linkIsItalic;
return function ( stream, state ) { return function ( stream, state ) {
var tmpstyle;
if ( stream.match( ']]' ) ) { if ( stream.match( ']]' ) ) {
state.tokenize = state.stack.pop(); state.tokenize = state.stack.pop();
return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); return makeLocalStyle( 'mw-link-bracket', state, 'nLink' );
} }
if ( stream.match( '\'\'\'' ) ) { if ( stream.match( '\'\'\'' ) ) {
linkIsBold = ( linkIsBold ? false : true ); linkIsBold = !linkIsBold;
return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); return makeLocalStyle( 'mw-link-text mw-apostrophes', state );
} }
if ( stream.match( '\'\'' ) ) { if ( stream.match( '\'\'' ) ) {
linkIsItalic = ( linkIsItalic ? false : true ); linkIsItalic = !linkIsItalic;
return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); return makeLocalStyle( 'mw-link-text mw-apostrophes', state );
} }
var tmpstyle = 'mw-link-text'; tmpstyle = 'mw-link-text';
if ( linkIsBold ) { if ( linkIsBold ) {
tmpstyle += ' strong'; tmpstyle += ' strong';
} }
@ -441,7 +443,7 @@
} }
if ( stream.eat( '>' ) ) { if ( stream.eat( '>' ) ) {
state.extName = name; state.extName = name;
if ( name in config.mwextModes.tag ) { if ( name in config.mwextModes.tag ) {
state.extMode = CodeMirror.getMode( config, config.mwextModes.tag[ name ] ); state.extMode = CodeMirror.getMode( config, config.mwextModes.tag[ name ] );
state.extState = CodeMirror.startState( state.extMode ); state.extState = CodeMirror.startState( state.extMode );
} }
@ -627,13 +629,14 @@
function eatWikiText( style, mnemonicStyle ) { function eatWikiText( style, mnemonicStyle ) {
return function ( stream, state ) { return function ( stream, state ) {
var ch, tmp, mt, name, isCloseTag, tagname,
sol = stream.sol();
function chain( parser ) { function chain( parser ) {
state.stack.push( state.tokenize ); state.stack.push( state.tokenize );
state.tokenize = parser; state.tokenize = parser;
return parser( stream, state ); return parser( stream, state );
} }
var ch,
sol = stream.sol();
if ( sol ) { if ( sol ) {
if ( stream.match( urlProtocols ) ) { // highlight free external links, bug T108448 if ( stream.match( urlProtocols ) ) { // highlight free external links, bug T108448
@ -649,7 +652,7 @@
} }
break; break;
case '=': case '=':
var tmp = stream.match( /(={0,5})(.+?(=\1\s*))$/ ); tmp = stream.match( /(={0,5})(.+?(=\1\s*))$/ );
if ( tmp ) { // Title if ( tmp ) { // Title
stream.backUp( tmp[ 2 ].length ); stream.backUp( tmp[ 2 ].length );
state.stack.push( state.tokenize ); state.stack.push( state.tokenize );
@ -685,7 +688,7 @@
return 'mw-skipformatting'; return 'mw-skipformatting';
} }
// break is not necessary here // break is not necessary here
/*falls through*/ // falls through
case '{': case '{':
if ( stream.eat( '|' ) ) { if ( stream.eat( '|' ) ) {
stream.eatSpace(); stream.eatSpace();
@ -709,10 +712,10 @@
if ( !( firstsingleletterword || stream.match( '\'\'', false ) ) ) { if ( !( firstsingleletterword || stream.match( '\'\'', false ) ) ) {
prepareItalicForCorrection( stream ); prepareItalicForCorrection( stream );
} }
isBold = isBold ? false : true; isBold = !isBold;
return makeLocalStyle( 'mw-apostrophes-bold', state ); return makeLocalStyle( 'mw-apostrophes-bold', state );
} else if ( stream.eat( '\'' ) ) { // italic } else if ( stream.eat( '\'' ) ) { // italic
isItalic = isItalic ? false : true; isItalic = !isItalic;
return makeLocalStyle( 'mw-apostrophes-italic', state ); return makeLocalStyle( 'mw-apostrophes-italic', state );
} }
break; break;
@ -726,7 +729,7 @@
return makeLocalStyle( 'mw-link-bracket', state ); return makeLocalStyle( 'mw-link-bracket', state );
} }
} else { } else {
var mt = stream.match( urlProtocols ); mt = stream.match( urlProtocols );
if ( mt ) { if ( mt ) {
state.nLink++; state.nLink++;
stream.backUp( mt[ 0 ].length ); stream.backUp( mt[ 0 ].length );
@ -750,7 +753,7 @@
return makeLocalStyle( 'mw-parserfunction-bracket', state ); return makeLocalStyle( 'mw-parserfunction-bracket', state );
} }
// Check for parser function without '#' // Check for parser function without '#'
var name = stream.match( /([^\s\u00a0\}\[\]<\{\'\|\&\:]+)(\:|[\s\u00a0]*)(\}\}?)?(.)?/ ); name = stream.match( /([^\s\u00a0\}\[\]<\{\'\|\&\:]+)(\:|[\s\u00a0]*)(\}\}?)?(.)?/ );
if ( name ) { if ( name ) {
stream.backUp( name[ 0 ].length ); stream.backUp( name[ 0 ].length );
if ( ( name[ 2 ] === ':' || name[ 4 ] === undefined || name[ 3 ] === '}}' ) && ( name[ 1 ].toLowerCase() in config.mwextFunctionSynonyms[ 0 ] || name[ 1 ] in config.mwextFunctionSynonyms[ 1 ] ) ) { if ( ( name[ 2 ] === ':' || name[ 4 ] === undefined || name[ 3 ] === '}}' ) && ( name[ 1 ].toLowerCase() in config.mwextFunctionSynonyms[ 0 ] || name[ 1 ] in config.mwextFunctionSynonyms[ 1 ] ) ) {
@ -768,8 +771,8 @@
} }
break; break;
case '<': case '<':
var isCloseTag = ( stream.eat( '/' ) ? true : false ); isCloseTag = !!stream.eat( '/' );
var tagname = stream.match( /[^>\/\s\u00a0\.\*\,\[\]\{\}\$\^\+\?\|\/\\'`~<=!@#%&\(\)-]+/ ); tagname = stream.match( /[^>\/\s\u00a0\.\*\,\[\]\{\}\$\^\+\?\|\/\\'`~<=!@#%&\(\)-]+/ );
if ( stream.match( '!--' ) ) { // coment if ( stream.match( '!--' ) ) { // coment
return chain( eatBlock( 'mw-comment', '-->' ) ); return chain( eatBlock( 'mw-comment', '-->' ) );
} }
@ -937,9 +940,10 @@
return t.style; return t.style;
}, },
blankLine: function ( state ) { blankLine: function ( state ) {
var ret;
if ( state.extName ) { if ( state.extName ) {
if ( state.extMode ) { if ( state.extMode ) {
var ret = ''; ret = '';
if ( state.extMode.blankLine ) { if ( state.extMode.blankLine ) {
ret = ' ' + state.extMode.blankLine( state.extState ); ret = ' ' + state.extMode.blankLine( state.extState );
} }
@ -955,12 +959,13 @@
function eatNowiki( style, lineStyle ) { function eatNowiki( style, lineStyle ) {
return function ( stream, state, ownLine ) { return function ( stream, state, ownLine ) {
var s;
if ( ownLine && stream.sol() ) { if ( ownLine && stream.sol() ) {
state.ownLine = true; state.ownLine = true;
} else if ( ownLine === false && state.ownLine ) { } else if ( ownLine === false && state.ownLine ) {
state.ownLine = false; state.ownLine = false;
} }
var s = ( state.ownLine ? lineStyle : style ); s = ( state.ownLine ? lineStyle : style );
if ( stream.match( /[^&]+/ ) ) { if ( stream.match( /[^&]+/ ) ) {
return s; return s;
} }
@ -969,14 +974,14 @@
}; };
} }
CodeMirror.defineMode( 'mw-tag-pre', function ( /*config, parserConfig */ ) { CodeMirror.defineMode( 'mw-tag-pre', function ( /* config, parserConfig */ ) {
return { return {
startState: function () { return {}; }, startState: function () { return {}; },
token: eatNowiki( 'mw-tag-pre', 'line-cm-mw-tag-pre' ) token: eatNowiki( 'mw-tag-pre', 'line-cm-mw-tag-pre' )
}; };
} ); } );
CodeMirror.defineMode( 'mw-tag-nowiki', function ( /*config, parserConfig */ ) { CodeMirror.defineMode( 'mw-tag-nowiki', function ( /* config, parserConfig */ ) {
return { return {
startState: function () { return {}; }, startState: function () { return {}; },
token: eatNowiki( 'mw-tag-nowiki', 'line-cm-mw-tag-nowiki' ) token: eatNowiki( 'mw-tag-nowiki', 'line-cm-mw-tag-nowiki' )