jenkins-bot has submitted this change and it was merged. Change subject: Update Jenkins tests ......................................................................
Update Jenkins tests Test jshint and jscs through npm using latest version of jscs and jshint. Add support for composer.json running phplint and in the future php code sniffer. Change-Id: Id8f11f9414fae8313dc18a857a07e11694354dcb --- M .gitignore A .jscsrc M .jshintignore A .jshintrc A Gruntfile.js A composer.json A package.json M resources/ext.CodeMirror.js M resources/mode/mediawiki/mediawiki.js 9 files changed, 1,034 insertions(+), 932 deletions(-) Approvals: Hashar: Looks good to me, approved jenkins-bot: Verified diff --git a/.gitignore b/.gitignore index afc53f9..ca5150b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -.svn *~ *.kate-swp .*.swp .directory +/node_modules/ +/vendor/ +/composer.lock diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..83457a7 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,6 @@ +{ + "preset": "wikimedia", + "requireVarDeclFirst": null, + "requireMultipleVarDecl": null, + "disallowEmptyBlocks": null +} diff --git a/.jshintignore b/.jshintignore index 6272a2d..1fc3c97 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,2 +1,4 @@ +node_modules +vendor # upstream libs resources/lib/ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..d980a03 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,25 @@ +{ + // 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 + } +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..c4b1daa --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,35 @@ +/*jshint node:true */ +module.exports = function ( grunt ) { + grunt.loadNpmTasks( 'grunt-contrib-jshint' ); + grunt.loadNpmTasks( 'grunt-jsonlint' ); + grunt.loadNpmTasks( 'grunt-banana-checker' ); + grunt.loadNpmTasks( 'grunt-jscs' ); + + grunt.initConfig( { + jshint: { + options: { + jshintrc: true + }, + all: [ + '**/*.js', + '!resources/lib/**', + '!node_modules/**' + ] + }, + jscs: { + src: '<%= jshint.all %>' + }, + banana: { + all: 'i18n/' + }, + jsonlint: { + all: [ + '**/*.json', + '!node_modules/**' + ] + } + } ); + + grunt.registerTask( 'test', [ 'jshint', 'jscs', 'jsonlint', 'banana' ] ); + grunt.registerTask( 'default', 'test' ); +}; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..f2883f7 --- /dev/null +++ b/composer.json @@ -0,0 +1,10 @@ +{ + "require-dev": { + "jakub-onderka/php-parallel-lint": "0.9" + }, + "scripts": { + "test": [ + "parallel-lint . --exclude vendor" + ] + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8a4e5f3 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "test": "grunt test" + }, + "devDependencies": { + "grunt": "0.4.5", + "grunt-cli": "0.1.13", + "grunt-contrib-jshint": "0.11.3", + "grunt-banana-checker": "0.4.0", + "grunt-jscs": "2.5.0", + "grunt-jsonlint": "1.0.7" + } +} diff --git a/resources/ext.CodeMirror.js b/resources/ext.CodeMirror.js index 43815be..ca5ae36 100644 --- a/resources/ext.CodeMirror.js +++ b/resources/ext.CodeMirror.js @@ -1,4 +1,3 @@ -/* global CodeMirror, mediaWiki */ ( function ( mw, $ ) { if ( mw.config.get( 'wgCodeEditorCurrentLanguage' ) ) { // If the CodeEditor is used then just exit; return; @@ -11,7 +10,7 @@ api = new mw.Api(), // function for a textselection function for CodeMirror cmTextSelection = function ( command, options ) { - if ( !codeMirror || codeMirror.getTextArea() !== this[0] ) { + if ( !codeMirror || codeMirror.getTextArea() !== this[ 0 ] ) { return origTextSelection.call( this, command, options ); } var fn, retval; @@ -42,13 +41,17 @@ */ encapsulateSelection: function ( options ) { return this.each( function () { - var insertText, selText, - selectPeri = options.selectPeri, - pre = options.pre, post = options.post; + 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 + // fn[command].call( this, options ); + fn.setSelection( { start: options.selectionStart, end: options.selectionEnd } ); // not tested } selText = codeMirror.doc.getSelection(); @@ -80,8 +83,9 @@ var i, insertText = '', selTextArr = selText.split( '\n' ); + for ( i = 0; i < selTextArr.length; i++ ) { - insertText += pre + selTextArr[i] + post; + insertText += pre + selTextArr[ i ] + post; if ( i !== selTextArr.length - 1 ) { insertText += '\n'; } @@ -96,13 +100,12 @@ insertText = pre + selText + post; } - var startCursor = codeMirror.doc.getCursor( true ); if ( options.ownline ) { if ( startCursor.ch !== 0 ) { insertText = '\n' + insertText; pre += '\n'; } - var endCursor = codeMirror.doc.getCursor( false ); + if ( codeMirror.doc.getLine( endCursor.line ).length !== endCursor.ch ) { insertText += '\n'; post += '\n'; @@ -117,7 +120,7 @@ codeMirror.doc.posFromIndex( codeMirror.doc.indexFromPos( startCursor ) + pre.length + selText.length ) ); } - }); + } ); }, /** @@ -125,37 +128,35 @@ * in a textarea */ getCaretPosition: function ( options ) { - var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ); + var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ), + endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) ); if ( options.startAndEnd ) { - var endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) ); return [ caretPos, endPos ]; } return caretPos; }, - setSelection: function ( options ) { + setSelection: function ( options ) { return this.each( function () { codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( options.start ), codeMirror.doc.posFromIndex( options.end ) ); - }); - }, + } ); + }, /** * Scroll a textarea to the current cursor position. You can set the cursor * position with setSelection() - * @param options boolean Whether to force a scroll even if the caret position - * is already visible. Defaults to false */ - scrollToCaretPosition: function ( /* options */ ) { - return this.each(function () { + scrollToCaretPosition: function () { + return this.each( function () { codeMirror.scrollIntoView( null ); - }); + } ); } }; switch ( command ) { - //case 'getContents': // no params - //case 'setContents': // no params with defaults - //case 'getSelection': // no params + // 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 @@ -203,7 +204,7 @@ break; } - retval = fn[command].call( this, options ); + retval = fn[ command ].call( this, options ); codeMirror.focus(); return retval; @@ -218,18 +219,18 @@ $( '#wpTextbox1' ).wikiEditor( 'addToToolbar', { - 'section': 'main', - 'groups': { - 'codemirror':{ - 'tools': { - 'CodeMirror': { + 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 ) { + execute: function ( context ) { switchCodeMirror( context ); } } @@ -245,7 +246,7 @@ // define JQuery hook for searching and replacing text using JS if CodeMirror is enabled, see Bug: T108711 $.valHooks.textarea = { - get: function( elem ) { + get: function ( elem ) { if ( elem.id === 'wpTextbox1' && codeMirror ) { return codeMirror.doc.getValue(); } else if ( originHooksTextarea ) { @@ -253,7 +254,7 @@ } return elem.value; }, - set: function( elem, value ) { + set: function ( elem, value ) { if ( elem.id === 'wpTextbox1' && codeMirror ) { return codeMirror.doc.setValue( value ); } else if ( originHooksTextarea ) { @@ -266,7 +267,7 @@ /** * Save CodeMirror enabled pref. * - * @param {Boolean} prefValue True, if CodeMirror should be enabled by default, otherwise false. + * @param {boolean} prefValue True, if CodeMirror should be enabled by default, otherwise false. */ function setCodeEditorPreference( prefValue ) { if ( mw.user.isAnon() ) { // Skip it for anon users @@ -322,10 +323,10 @@ function enableCodeMirror() { var textbox1 = $( '#wpTextbox1' ); - if ( textbox1[0].style.display === 'none' ) { + if ( textbox1[ 0 ].style.display === 'none' ) { return; } - codeMirror = CodeMirror.fromTextArea( textbox1[0], { + codeMirror = CodeMirror.fromTextArea( textbox1[ 0 ], { mwextFunctionSynonyms: mw.config.get( 'extCodeMirrorFunctionSynonyms' ), mwextTags: mw.config.get( 'extCodeMirrorTags' ), mwextDoubleUnderscore: mw.config.get( 'extCodeMirrorDoubleUnderscore' ), @@ -333,7 +334,7 @@ mwextModes: mw.config.get( 'extCodeMirrorExtModes' ), styleActiveLine: true, lineWrapping: true, - readOnly: textbox1[0].readOnly, + readOnly: textbox1[ 0 ].readOnly, // select mediawiki as text input mode mode: 'text/mediawiki', extraKeys: { @@ -341,7 +342,7 @@ } } ); // 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' ); } @@ -366,7 +367,7 @@ var $image = $( '<img>' ).attr( { width: 23, 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' ), alt: 'CodeMirror', title: 'CodeMirror', id: 'CodeMirrorButton', diff --git a/resources/mode/mediawiki/mediawiki.js b/resources/mode/mediawiki/mediawiki.js index 303e035..b4276d2 100644 --- a/resources/mode/mediawiki/mediawiki.js +++ b/resources/mode/mediawiki/mediawiki.js @@ -1,982 +1,990 @@ -/* global CodeMirror */ -(function( mod ) { mod( CodeMirror ); })(function( CodeMirror ) { -'use strict'; +( function ( mod, CodeMirror ) { + mod( CodeMirror ); +}( mod, CodeMirror ) ); -function eatMnemonic( stream, style, mnemonicStyle ) { - var ok; - if ( stream.eat( '#' ) ) { - if (stream.eat( 'x' ) ) { - ok = stream.eatWhile( /[a-fA-F\d]/ ) && stream.eat( ';'); +( function ( CodeMirror ) { + 'use strict'; + + function eatMnemonic( stream, style, mnemonicStyle ) { + var ok; + if ( stream.eat( '#' ) ) { + if ( stream.eat( 'x' ) ) { + ok = stream.eatWhile( /[a-fA-F\d]/ ) && stream.eat( ';' ); + } else { + ok = stream.eatWhile( /[\d]/ ) && stream.eat( ';' ); + } } else { - ok = stream.eatWhile( /[\d]/ ) && stream.eat( ';' ); + ok = stream.eatWhile( /[\w\.\-:]/ ) && stream.eat( ';' ); } - } else { - ok = stream.eatWhile( /[\w\.\-:]/ ) && stream.eat( ';' ); - } - if ( ok ) { - mnemonicStyle += ' mw-mnemonic'; - return mnemonicStyle; - } - return style; -} - -CodeMirror.defineMode( 'mediawiki', function( config/*, parserConfig */ ) { - - var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' ), - permittedHtmlTags = {'b': true, 'bdi': true, 'del': true, 'i': true, 'ins': true, - 'u': true, 'font': true, 'big': true, 'small': true, 'sub': true, 'sup': true, - 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, 'cite': true, - 'code': true, 'em': true, 's': true, 'strike': true, 'strong': true, 'tt': true, - 'var': true, 'div': true, 'center': true, 'blockquote': true, 'ol': true, 'ul': true, - 'dl': true, 'table': true, 'caption': true, 'pre': true, 'ruby': true, 'rb': true, - 'rp': true, 'rt': true, 'rtc': true, 'p': true, 'span': true, 'abbr': true, 'dfn': true, - 'kbd': true, 'samp': true, 'data': true, 'time': true, 'mark': true, 'br': true, - 'wbr': true, 'hr': true, 'li': true, 'dt': true, 'dd': true, 'td': true, 'th': true, - 'tr': true, 'noinclude': true, 'includeonly': true, 'onlyinclude': true}, - isBold, isItalic, firstsingleletterword, firstmultiletterword, firstspace, mBold, mItalic, mTokens = [], mStyle; - - function makeStyle( style, state, endGround ) { - if ( isBold ) { - style += ' strong'; - } - if ( isItalic ) { - style += ' em'; - } - return makeLocalStyle( style, state, endGround ); - } - - function makeLocalStyle( style, state, endGround ) { - var ground = ''; - switch ( state.nTemplate ) { - case 0: - break; - case 1: - ground += '-template'; - break; - case 2: - ground += '-template2'; - break; - default: - ground += '-template3'; - break; - } - switch ( state.nExt ) { - case 0: - break; - case 1: - ground += '-ext'; - break; - case 2: - ground += '-ext2'; - break; - default: - ground += '-ext3'; - break; - } - if ( state.nLink > 0 ) { - ground += '-link'; - } - if ( ground !== '' ) { - style = 'mw' + ground + '-ground ' + style; - } - if ( endGround ) { - state[endGround]--; + if ( ok ) { + mnemonicStyle += ' mw-mnemonic'; + return mnemonicStyle; } return style; } - function eatBlock( style, terminator ) { - return function( stream, state ) { - while ( !stream.eol() ) { - if ( stream.match( terminator ) ) { - state.tokenize = state.stack.pop(); + CodeMirror.defineMode( 'mediawiki', function ( config/*, parserConfig */ ) { + + var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' ), + permittedHtmlTags = { b: true, bdi: true, del: true, i: true, ins: true, + u: true, font: true, big: true, small: true, sub: true, sup: true, + h1: true, h2: true, h3: true, h4: true, h5: true, h6: true, cite: true, + code: true, em: true, s: true, strike: true, strong: true, tt: true, + 'var': true, div: true, center: true, blockquote: true, ol: true, ul: true, + dl: true, table: true, caption: true, pre: true, ruby: true, rb: true, + rp: true, rt: true, rtc: true, p: true, span: true, abbr: true, dfn: true, + kbd: true, samp: true, data: true, time: true, mark: true, br: true, + wbr: true, hr: true, li: true, dt: true, dd: true, td: true, th: true, + tr: true, noinclude: true, includeonly: true, onlyinclude: true }, + isBold, isItalic, firstsingleletterword, firstmultiletterword, firstspace, mBold, mItalic, mTokens = [], + mStyle; + + function makeStyle( style, state, endGround ) { + if ( isBold ) { + style += ' strong'; + } + if ( isItalic ) { + style += ' em'; + } + return makeLocalStyle( style, state, endGround ); + } + + function makeLocalStyle( style, state, endGround ) { + var ground = ''; + switch ( state.nTemplate ) { + case 0: break; - } - stream.next(); + case 1: + ground += '-template'; + break; + case 2: + ground += '-template2'; + break; + default: + ground += '-template3'; + break; } - return makeLocalStyle( style, state ); - }; - } + switch ( state.nExt ) { + case 0: + break; + case 1: + ground += '-ext'; + break; + case 2: + ground += '-ext2'; + break; + default: + ground += '-ext3'; + break; + } + if ( state.nLink > 0 ) { + ground += '-link'; + } + if ( ground !== '' ) { + style = 'mw' + ground + '-ground ' + style; + } + if ( endGround ) { + state[ endGround ]--; + } + return style; + } - function eatEnd( style ) { - return function( stream, state ) { - stream.skipToEnd(); - state.tokenize = state.stack.pop(); - return makeLocalStyle( style, state ); - }; - } - - function eatChar( char, style ) { - return function( stream, state ) { - state.tokenize = state.stack.pop(); - if ( stream.eat( char ) ) { + function eatBlock( style, terminator ) { + return function ( stream, state ) { + while ( !stream.eol() ) { + if ( stream.match( terminator ) ) { + state.tokenize = state.stack.pop(); + break; + } + stream.next(); + } return makeLocalStyle( style, state ); - } - return makeLocalStyle( 'error', state ); - }; - } + }; + } - function eatSectionHeader( count ) { - return function( stream, state ) { - if ( stream.match( /[^&<\[\{~]+/ ) ) { - if ( stream.eol() ) { - stream.backUp( count ); - state.tokenize = eatEnd( 'mw-section-header' ); + function eatEnd( style ) { + return function ( stream, state ) { + stream.skipToEnd(); + state.tokenize = state.stack.pop(); + return makeLocalStyle( style, state ); + }; + } + + function eatChar( char, style ) { + return function ( stream, state ) { + state.tokenize = state.stack.pop(); + if ( stream.eat( char ) ) { + return makeLocalStyle( style, state ); } - return null; // style is null - } - return eatWikiText( '', '' )( stream, state ); - }; - } + return makeLocalStyle( 'error', state ); + }; + } - function inVariable( stream, state ) { - if ( stream.match( /[^\{\}\|]+/ ) ) { + function eatSectionHeader( count ) { + return function ( stream, state ) { + if ( stream.match( /[^&<\[\{~]+/ ) ) { + if ( stream.eol() ) { + stream.backUp( count ); + state.tokenize = eatEnd( 'mw-section-header' ); + } + return null; // style is null + } + return eatWikiText( '', '' )( stream, state ); + }; + } + + function inVariable( stream, state ) { + if ( stream.match( /[^\{\}\|]+/ ) ) { + return makeLocalStyle( 'mw-templatevariable-name', state ); + } + if ( stream.eat( '|' ) ) { + state.tokenize = inVariableDefault; + return makeLocalStyle( 'mw-templatevariable-delimiter', state ); + } + if ( stream.match( '}}}' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-templatevariable-bracket', state ); + } + if ( stream.match( '{{{' ) ) { + state.stack.push( state.tokenize ); + return makeLocalStyle( 'mw-templatevariable-bracket', state ); + } + stream.next(); return makeLocalStyle( 'mw-templatevariable-name', state ); } - if ( stream.eat( '|' ) ) { - state.tokenize = inVariableDefault; - return makeLocalStyle( 'mw-templatevariable-delimiter', state ); - } - if ( stream.match( '}}}' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-templatevariable-bracket', state ); - } - if ( stream.match( '{{{' ) ) { - state.stack.push( state.tokenize ); - return makeLocalStyle( 'mw-templatevariable-bracket', state ); - } - stream.next(); - return makeLocalStyle( 'mw-templatevariable-name', state ); - } - function inVariableDefault( stream, state ) { - if ( stream.match( /[^\{\}\[<\&~]+/ ) ) { - return makeLocalStyle( 'mw-templatevariable', state ); - } - if ( stream.match( '}}}' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-templatevariable-bracket', state ); - } - return eatWikiText( 'mw-templatevariable', '' )( stream, state ); - } - - function inParserFunctionName( stream, state ) { - if ( stream.match( /#?[^\:\}\{~]+/ ) ) { // FIXME: {{#name}} and {{uc}} are wrong, must have ':' - return makeLocalStyle( 'mw-parserfunction-name', state ); - } - if ( stream.eat( ':' ) ) { - state.tokenize = inParserFunctionArguments; - return makeLocalStyle( 'mw-parserfunction-delimiter', state ); - } - if ( stream.match( '}}' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' ); - } - return eatWikiText( 'mw-parserfunction', '' )( stream, state ); - } - - function inParserFunctionArguments( stream, state ) { - if ( stream.match( /[^\|\}\{\[<\&~]+/ ) ) { - return makeLocalStyle( 'mw-parserfunction', state ); - } else if ( stream.eat( '|' ) ) { - return makeLocalStyle( 'mw-parserfunction-delimiter', state ); - } else if ( stream.match( '}}' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' ); - } - return eatWikiText( 'mw-parserfunction', '' )( stream, state ); - } - - function eatTemplatePageName( haveAte ) { - return function( stream, state ) { - if ( stream.match( /[\s\u00a0]*\|[\s\u00a0]*/ ) ) { - state.tokenize = eatTemplateArgument( true ); - return makeLocalStyle( 'mw-template-delimiter', state ); + function inVariableDefault( stream, state ) { + if ( stream.match( /[^\{\}\[<\&~]+/ ) ) { + return makeLocalStyle( 'mw-templatevariable', state ); } - if ( stream.match( /[\s\u00a0]*\}\}/ ) ) { + if ( stream.match( '}}}' ) ) { state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' ); + return makeLocalStyle( 'mw-templatevariable-bracket', state ); } - if ( haveAte && stream.sol() ) { - // @todo error message - state.nTemplate--; - state.tokenize = state.stack.pop(); - return; - } - if ( stream.match( /[\s\u00a0]*[^\s\u00a0\|\}<\{\&~]+/ ) ) { - state.tokenize = eatTemplatePageName( true ); - return makeLocalStyle( 'mw-template-name mw-pagename', state ); - } else if ( stream.eatSpace() ) { - if ( stream.eol() === true ) { - return makeLocalStyle( 'mw-template-name', state ); - } - return makeLocalStyle( 'mw-template-name mw-pagename', state ); - } - return eatWikiText( 'mw-template-name mw-pagename', 'mw-template-name-mnemonic mw-pagename' )( stream, state ); - }; - } + return eatWikiText( 'mw-templatevariable', '' )( stream, state ); + } - function eatTemplateArgument( expectArgName ) { - return function( stream, state ) { - if ( expectArgName && stream.eatWhile( /[^=\|\}\{\[<\&~]/ ) ) { - if ( stream.eat( '=' ) ) { - state.tokenize = eatTemplateArgument( false ); - return makeLocalStyle( 'mw-template-argument-name', state ); - } - return makeLocalStyle( 'mw-template', state ); - } else if ( stream.eatWhile( /[^\|\}\{\[<\&~]/ ) ) { - return makeLocalStyle( 'mw-template', state ); + function inParserFunctionName( stream, state ) { + if ( stream.match( /#?[^\:\}\{~]+/ ) ) { // FIXME: {{#name}} and {{uc}} are wrong, must have ':' + return makeLocalStyle( 'mw-parserfunction-name', state ); + } + if ( stream.eat( ':' ) ) { + state.tokenize = inParserFunctionArguments; + return makeLocalStyle( 'mw-parserfunction-delimiter', state ); + } + if ( stream.match( '}}' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' ); + } + return eatWikiText( 'mw-parserfunction', '' )( stream, state ); + } + + function inParserFunctionArguments( stream, state ) { + if ( stream.match( /[^\|\}\{\[<\&~]+/ ) ) { + return makeLocalStyle( 'mw-parserfunction', state ); } else if ( stream.eat( '|' ) ) { - state.tokenize = eatTemplateArgument( true ); - return makeLocalStyle( 'mw-template-delimiter', state ); + return makeLocalStyle( 'mw-parserfunction-delimiter', state ); } else if ( stream.match( '}}' ) ) { state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' ); + return makeLocalStyle( 'mw-parserfunction-bracket', state, 'nExt' ); } - return eatWikiText( 'mw-template', '' )( stream, state ); - }; - } + return eatWikiText( 'mw-parserfunction', '' )( stream, state ); + } - function eatExternalLinkProtocol( chars ) { - return function( stream, state ) { - while ( chars > 0 ) { - chars--; - stream.next(); - } - if ( stream.eol() ) { + function eatTemplatePageName( haveAte ) { + return function ( stream, state ) { + if ( stream.match( /[\s\u00a0]*\|[\s\u00a0]*/ ) ) { + state.tokenize = eatTemplateArgument( true ); + return makeLocalStyle( 'mw-template-delimiter', state ); + } + if ( stream.match( /[\s\u00a0]*\}\}/ ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' ); + } + if ( haveAte && stream.sol() ) { + // @todo error message + state.nTemplate--; + state.tokenize = state.stack.pop(); + return; + } + if ( stream.match( /[\s\u00a0]*[^\s\u00a0\|\}<\{\&~]+/ ) ) { + state.tokenize = eatTemplatePageName( true ); + return makeLocalStyle( 'mw-template-name mw-pagename', state ); + } else if ( stream.eatSpace() ) { + if ( stream.eol() === true ) { + return makeLocalStyle( 'mw-template-name', state ); + } + return makeLocalStyle( 'mw-template-name mw-pagename', state ); + } + return eatWikiText( 'mw-template-name mw-pagename', 'mw-template-name-mnemonic mw-pagename' )( stream, state ); + }; + } + + function eatTemplateArgument( expectArgName ) { + return function ( stream, state ) { + if ( expectArgName && stream.eatWhile( /[^=\|\}\{\[<\&~]/ ) ) { + if ( stream.eat( '=' ) ) { + state.tokenize = eatTemplateArgument( false ); + return makeLocalStyle( 'mw-template-argument-name', state ); + } + return makeLocalStyle( 'mw-template', state ); + } else if ( stream.eatWhile( /[^\|\}\{\[<\&~]/ ) ) { + return makeLocalStyle( 'mw-template', state ); + } else if ( stream.eat( '|' ) ) { + state.tokenize = eatTemplateArgument( true ); + return makeLocalStyle( 'mw-template-delimiter', state ); + } else if ( stream.match( '}}' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-template-bracket', state, 'nTemplate' ); + } + return eatWikiText( 'mw-template', '' )( stream, state ); + }; + } + + function eatExternalLinkProtocol( chars ) { + return function ( stream, state ) { + while ( chars > 0 ) { + chars--; + stream.next(); + } + if ( stream.eol() ) { + state.nLink--; + // @todo error message + state.tokenize = state.stack.pop(); + } else { + state.tokenize = inExternalLink; + } + return makeLocalStyle( 'mw-extlink-protocol', state ); + }; + } + + function inExternalLink( stream, state ) { + if ( stream.sol() ) { state.nLink--; // @todo error message state.tokenize = state.stack.pop(); - } else { - state.tokenize = inExternalLink; + return; } - return makeLocalStyle( 'mw-extlink-protocol', state ); - }; - } - - function inExternalLink( stream, state ) { - if ( stream.sol() ) { - state.nLink--; - // @todo error message - state.tokenize = state.stack.pop(); - return; - } - if ( stream.match( /[\s\u00a0]*\]/ ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' ); - } - if ( stream.eatSpace() ) { - state.tokenize = inExternalLinkText; - return makeStyle( '', state ); - } - if ( stream.match( /[^\s\u00a0\]\{\&~']+/ ) || stream.eatSpace() ) { - if ( stream.peek() === '\'' ) { - if ( stream.match( '\'\'', false ) ) { - state.tokenize = inExternalLinkText; - } else { - stream.next(); + if ( stream.match( /[\s\u00a0]*\]/ ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' ); + } + if ( stream.eatSpace() ) { + state.tokenize = inExternalLinkText; + return makeStyle( '', state ); + } + if ( stream.match( /[^\s\u00a0\]\{\&~']+/ ) || stream.eatSpace() ) { + if ( stream.peek() === '\'' ) { + if ( stream.match( '\'\'', false ) ) { + state.tokenize = inExternalLinkText; + } else { + stream.next(); + } } + return makeStyle( 'mw-extlink', state ); } - return makeStyle( 'mw-extlink', state ); + return eatWikiText( 'mw-extlink', '' )( stream, state ); } - return eatWikiText( 'mw-extlink', '' )( stream, state ); - } - function inExternalLinkText( stream, state ) { - if ( stream.sol() ) { - state.nLink--; - // @todo error message - state.tokenize = state.stack.pop(); - return; + function inExternalLinkText( stream, state ) { + if ( stream.sol() ) { + state.nLink--; + // @todo error message + state.tokenize = state.stack.pop(); + return; + } + if ( stream.eat( ']' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' ); + } + if ( stream.match( /[^'\]\{\&~]+/ ) ) { + return makeStyle( 'mw-extlink-text', state ); + } + return eatWikiText( 'mw-extlink-text', '' )( stream, state ); } - if ( stream.eat( ']' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-extlink-bracket', state, 'nLink' ); - } - if ( stream.match( /[^'\]\{\&~]+/ ) ) { - return makeStyle( 'mw-extlink-text', state ); - } - return eatWikiText( 'mw-extlink-text', '' )( stream, state ); - } - function inLink( stream, state ) { - if ( stream.sol() ) { - state.nLink--; - // @todo error message - state.tokenize = state.stack.pop(); - return; + function inLink( stream, state ) { + if ( stream.sol() ) { + state.nLink--; + // @todo error message + state.tokenize = state.stack.pop(); + return; + } + if ( stream.match( /[\s\u00a0]*#[\s\u00a0]*/ ) ) { + state.tokenize = inLinkToSection; + return makeLocalStyle( 'mw-link', state ); + } + if ( stream.match( /[\s\u00a0]*\|[\s\u00a0]*/ ) ) { + state.tokenize = eatLinkText(); + return makeLocalStyle( 'mw-link-delimiter', state ); + } + if ( stream.match( /[\s\u00a0]*\]\]/ ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); + // if ( !stream.eatSpace() ) { + // state.ImInBlock.push( 'LinkTrail' ); + // } + } + if ( stream.match( /[\s\u00a0]*[^\s\u00a0#\|\]\&~\{]+/ ) || stream.eatSpace() ) { // FIXME '{{' brokes Link, sample [[z{{page]] + return makeStyle( 'mw-link-pagename mw-pagename', state ); + } + return eatWikiText( 'mw-link-pagename mw-pagename', 'mw-pagename' )( stream, state ); } - if ( stream.match( /[\s\u00a0]*#[\s\u00a0]*/ ) ) { - state.tokenize = inLinkToSection; - return makeLocalStyle( 'mw-link', state ); - } - if ( stream.match( /[\s\u00a0]*\|[\s\u00a0]*/ ) ) { - state.tokenize = eatLinkText(); - return makeLocalStyle( 'mw-link-delimiter', state ); - } - if ( stream.match( /[\s\u00a0]*\]\]/ ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); -// if ( !stream.eatSpace() ) { -// state.ImInBlock.push( 'LinkTrail' ); -// } - } - if ( stream.match( /[\s\u00a0]*[^\s\u00a0#\|\]\&~\{]+/ ) || stream.eatSpace() ) { //FIXME '{{' brokes Link, sample [[z{{page]] - return makeStyle( 'mw-link-pagename mw-pagename', state ); - } - return eatWikiText( 'mw-link-pagename mw-pagename', 'mw-pagename' )( stream, state ); - } - function inLinkToSection( stream, state ) { - if ( stream.sol() ) { - // @todo error message - state.nLink--; - state.tokenize = state.stack.pop(); - return; - } - if ( stream.match( /[^\|\]\&~\{\}]+/ ) ) { //FIXME '{{' brokes Link, sample [[z{{page]] - return makeLocalStyle( 'mw-link-tosection', state ); - } - if ( stream.eat( '|' ) ) { - state.tokenize = eatLinkText(); - return makeLocalStyle( 'mw-link-delimiter', state ); - } - if ( stream.match( ']]' ) ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); -// if ( !stream.eatSpace() ) { -// state.ImInBlock.push( 'LinkTrail' ); -// } - } - return eatWikiText( 'mw-link-tosection', '' )( stream, state ); - } - - function eatLinkText() { - var linkIsBold, linkIsItalic; - return function ( stream, state ) { + function inLinkToSection( stream, state ) { + if ( stream.sol() ) { + // @todo error message + state.nLink--; + state.tokenize = state.stack.pop(); + return; + } + if ( stream.match( /[^\|\]\&~\{\}]+/ ) ) { // FIXME '{{' brokes Link, sample [[z{{page]] + return makeLocalStyle( 'mw-link-tosection', state ); + } + if ( stream.eat( '|' ) ) { + state.tokenize = eatLinkText(); + return makeLocalStyle( 'mw-link-delimiter', state ); + } if ( stream.match( ']]' ) ) { state.tokenize = state.stack.pop(); return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); + // if ( !stream.eatSpace() ) { + // state.ImInBlock.push( 'LinkTrail' ); + // } } - if ( stream.match( '\'\'\'' ) ) { - linkIsBold = (linkIsBold ? false : true); - return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); - } - if ( stream.match( '\'\'' ) ) { - linkIsItalic = (linkIsItalic ? false : true); - return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); - } - var tmpstyle = 'mw-link-text'; - if ( linkIsBold ) { - tmpstyle += ' strong'; - } - if ( linkIsItalic ) { - tmpstyle += ' em'; - } - if ( stream.match( /[^'\]\{\&~]+/ ) ) { - return makeStyle( tmpstyle, state ); - } - return eatWikiText( tmpstyle, '' )( stream, state ); - }; - } - - function eatTagName( chars, isCloseTag, isHtmlTag ) { - return function ( stream, state ) { - var name = ''; - while ( chars > 0 ) { - chars--; - name = name + stream.next(); - } - if ( stream.eol() ) { - // @todo error message - state.tokenize = state.stack.pop(); - return makeLocalStyle( (isHtmlTag ? 'mw-htmltag-name' : 'mw-exttag-name'), state ); - } - stream.eatSpace(); - if ( stream.eol() ) { - // @todo error message - state.tokenize = state.stack.pop(); - return makeLocalStyle( (isHtmlTag ? 'mw-htmltag-name' : 'mw-exttag-name'), state ); - } - - if ( isHtmlTag ) { - if ( isCloseTag ) { - state.tokenize = eatChar( '>', 'mw-htmltag-bracket' ); - } else { - state.tokenize = eatHtmlTagAttribute( name ); - } - return makeLocalStyle( 'mw-htmltag-name', state ); - } // it is the extension tag - if ( isCloseTag ) { - state.tokenize = eatChar( '>', 'mw-exttag-bracket' ); - } else { - state.tokenize = eatExtTagAttribute( name ); - } - return makeLocalStyle( 'mw-exttag-name', state ); - }; - } - - function eatHtmlTagAttribute( name ) { - return function ( stream, state ) { - if ( stream.match( /[^>\/<\{\&~]+/ ) ) { - return makeLocalStyle( 'mw-htmltag-attribute', state ); - } - if ( stream.eat( '>' ) ) { - state.InHtmlTag.push( name ); - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-htmltag-bracket', state ); - } - if ( stream.match( '/>') ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-htmltag-bracket', state ); - } - return eatWikiText( 'mw-htmltag-attribute', '' )( stream, state ); - }; - } - - function eatExtTagAttribute( name ) { - return function ( stream, state ) { - if ( stream.match( /[^>\/<\{\&~]+/ ) ) { - return makeLocalStyle( 'mw-exttag-attribute', state ); - } - if ( stream.eat( '>' ) ) { - state.extName = name; - if ( name in config.mwextModes.tag ) { - state.extMode = CodeMirror.getMode( config, config.mwextModes.tag[name] ); - state.extState = CodeMirror.startState( state.extMode ); - } - state.tokenize = eatExtTagArea( name ); - return makeLocalStyle( 'mw-exttag-bracket', state ); - } - if ( stream.match( '/>') ) { - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-exttag-bracket', state ); - } - return eatWikiText( 'mw-exttag-attribute', '' )( stream, state ); - }; - } - - function eatExtTagArea( name ) { - return function( stream, state ) { - var origString = false, from = stream.pos, to, - pattern = new RegExp( '</' + name + '\\s*>' ), - m = pattern.exec( from ? stream.string.slice( from ) : stream.string ); - - if ( m ) { - if ( m.index === 0 ) { - state.tokenize = eatExtCloseTag( name ); - state.extName = false; - if ( state.extMode !== false ) { - state.extMode = false; - state.extState = false; - } - return state.tokenize( stream, state ); - } - to = m.index + from; - origString = stream.string; - stream.string = origString.slice( 0, to ); - } - - state.stack.push( state.tokenize ); - state.tokenize = eatExtTokens( origString ); - return state.tokenize( stream, state ); - }; - } - - function eatExtCloseTag( name ) { - return function ( stream, state ) { - stream.next(); // eat < - stream.next(); // eat / - state.tokenize = eatTagName( name.length, true, false ); - return makeLocalStyle( 'mw-exttag-bracket', state ); - }; - } - - function eatExtTokens( origString ) { - return function ( stream, state ) { - var ret; - if ( state.extMode === false ) { - ret = (origString === false && stream.sol() ? 'line-cm-mw-exttag' : 'mw-exttag'); - stream.skipToEnd(); - } else { - ret = (origString === false && stream.sol() ? 'line-cm-mw-tag-' : 'mw-tag-') + state.extName; - ret += ' ' + state.extMode.token( stream, state.extState, origString === false ); - } - if ( stream.eol() ) { - if ( origString !== false ) { - stream.string = origString; - } - state.tokenize = state.stack.pop(); - } - return makeLocalStyle( ret, state ); - }; - } - - function eatStartTable( stream, state ) { - stream.match( '{|' ); - stream.eatSpace(); - state.tokenize = inTableDefinition; - return 'mw-table-bracket'; - } - - function inTableDefinition( stream, state ) { - if ( stream.sol() ) { - state.tokenize = inTable; - return inTable( stream, state ); + return eatWikiText( 'mw-link-tosection', '' )( stream, state ); } - return eatWikiText( 'mw-table-definition', '' )( stream, state ); - } - function inTableCaption( stream, state ) { - if ( stream.sol() && stream.match( /[\s\u00a0]*[\|!]/, false ) ) { - state.tokenize = inTable; - return inTable( stream, state ); - } - return eatWikiText( 'mw-table-caption', '' )( stream, state ); - } - - function inTable( stream, state ) { - if ( stream.sol() ) { - stream.eatSpace(); - if ( stream.eat( '|' ) ) { - if ( stream.eat( '-' ) ) { - stream.eatSpace(); - state.tokenize = inTableDefinition; - return makeLocalStyle( 'mw-table-delimiter', state ); - } - if ( stream.eat( '+' ) ) { - stream.eatSpace(); - state.tokenize = inTableCaption; - return makeLocalStyle( 'mw-table-delimiter', state ); - } - if ( stream.eat( '}' ) ) { + function eatLinkText() { + var linkIsBold, linkIsItalic; + return function ( stream, state ) { + if ( stream.match( ']]' ) ) { state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-table-bracket', state ); + return makeLocalStyle( 'mw-link-bracket', state, 'nLink' ); } - stream.eatSpace(); - state.tokenize = eatTableRow( true, false ); - return makeLocalStyle( 'mw-table-delimiter', state ); - } - if ( stream.eat( '!' ) ) { - stream.eatSpace(); - state.tokenize = eatTableRow( true, true ); - return makeLocalStyle( 'mw-table-delimiter', state ); - } + if ( stream.match( '\'\'\'' ) ) { + linkIsBold = ( linkIsBold ? false : true ); + return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); + } + if ( stream.match( '\'\'' ) ) { + linkIsItalic = ( linkIsItalic ? false : true ); + return makeLocalStyle( 'mw-link-text mw-apostrophes', state ); + } + var tmpstyle = 'mw-link-text'; + if ( linkIsBold ) { + tmpstyle += ' strong'; + } + if ( linkIsItalic ) { + tmpstyle += ' em'; + } + if ( stream.match( /[^'\]\{\&~]+/ ) ) { + return makeStyle( tmpstyle, state ); + } + return eatWikiText( tmpstyle, '' )( stream, state ); + }; } - return eatWikiText( '', '' )( stream, state ); - } - function eatTableRow( isStart, isHead ) { - return function ( stream, state ) { - if ( stream.sol() ) { - if ( stream.match( /[\s\u00a0]*[\|!]/, false ) ) { - state.tokenize = inTable; - return inTable( stream, state ); + function eatTagName( chars, isCloseTag, isHtmlTag ) { + return function ( stream, state ) { + var name = ''; + while ( chars > 0 ) { + chars--; + name = name + stream.next(); } - } else { - if ( stream.match( /[^'\|\{\[<\&~]+/ ) ) { - return makeStyle( (isHead ? 'strong' : ''), state ); + if ( stream.eol() ) { + // @todo error message + state.tokenize = state.stack.pop(); + return makeLocalStyle( ( isHtmlTag ? 'mw-htmltag-name' : 'mw-exttag-name' ), state ); } - if ( stream.match( '||' ) || isHead && stream.match( '!!' ) || (isStart && stream.eat( '|' )) ) { - isBold = false; - isItalic = false; - if ( isStart ) { - state.tokenize = eatTableRow( false, isHead ); + stream.eatSpace(); + if ( stream.eol() ) { + // @todo error message + state.tokenize = state.stack.pop(); + return makeLocalStyle( ( isHtmlTag ? 'mw-htmltag-name' : 'mw-exttag-name' ), state ); + } + + if ( isHtmlTag ) { + if ( isCloseTag ) { + state.tokenize = eatChar( '>', 'mw-htmltag-bracket' ); + } else { + state.tokenize = eatHtmlTagAttribute( name ); } + return makeLocalStyle( 'mw-htmltag-name', state ); + } // it is the extension tag + if ( isCloseTag ) { + state.tokenize = eatChar( '>', 'mw-exttag-bracket' ); + } else { + state.tokenize = eatExtTagAttribute( name ); + } + return makeLocalStyle( 'mw-exttag-name', state ); + }; + } + + function eatHtmlTagAttribute( name ) { + return function ( stream, state ) { + if ( stream.match( /[^>\/<\{\&~]+/ ) ) { + return makeLocalStyle( 'mw-htmltag-attribute', state ); + } + if ( stream.eat( '>' ) ) { + state.InHtmlTag.push( name ); + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-htmltag-bracket', state ); + } + if ( stream.match( '/>' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-htmltag-bracket', state ); + } + return eatWikiText( 'mw-htmltag-attribute', '' )( stream, state ); + }; + } + + function eatExtTagAttribute( name ) { + return function ( stream, state ) { + if ( stream.match( /[^>\/<\{\&~]+/ ) ) { + return makeLocalStyle( 'mw-exttag-attribute', state ); + } + if ( stream.eat( '>' ) ) { + state.extName = name; + if ( name in config.mwextModes.tag ) { + state.extMode = CodeMirror.getMode( config, config.mwextModes.tag[ name ] ); + state.extState = CodeMirror.startState( state.extMode ); + } + state.tokenize = eatExtTagArea( name ); + return makeLocalStyle( 'mw-exttag-bracket', state ); + } + if ( stream.match( '/>' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-exttag-bracket', state ); + } + return eatWikiText( 'mw-exttag-attribute', '' )( stream, state ); + }; + } + + function eatExtTagArea( name ) { + return function ( stream, state ) { + var origString = false, + from = stream.pos, + to, + pattern = new RegExp( '</' + name + '\\s*>' ), + m = pattern.exec( from ? stream.string.slice( from ) : stream.string ); + + if ( m ) { + if ( m.index === 0 ) { + state.tokenize = eatExtCloseTag( name ); + state.extName = false; + if ( state.extMode !== false ) { + state.extMode = false; + state.extState = false; + } + return state.tokenize( stream, state ); + } + to = m.index + from; + origString = stream.string; + stream.string = origString.slice( 0, to ); + } + + state.stack.push( state.tokenize ); + state.tokenize = eatExtTokens( origString ); + return state.tokenize( stream, state ); + }; + } + + function eatExtCloseTag( name ) { + return function ( stream, state ) { + stream.next(); // eat < + stream.next(); // eat / + state.tokenize = eatTagName( name.length, true, false ); + return makeLocalStyle( 'mw-exttag-bracket', state ); + }; + } + + function eatExtTokens( origString ) { + return function ( stream, state ) { + var ret; + if ( state.extMode === false ) { + ret = ( origString === false && stream.sol() ? 'line-cm-mw-exttag' : 'mw-exttag' ); + stream.skipToEnd(); + } else { + ret = ( origString === false && stream.sol() ? 'line-cm-mw-tag-' : 'mw-tag-' ) + state.extName; + ret += ' ' + state.extMode.token( stream, state.extState, origString === false ); + } + if ( stream.eol() ) { + if ( origString !== false ) { + stream.string = origString; + } + state.tokenize = state.stack.pop(); + } + return makeLocalStyle( ret, state ); + }; + } + + function eatStartTable( stream, state ) { + stream.match( '{|' ); + stream.eatSpace(); + state.tokenize = inTableDefinition; + return 'mw-table-bracket'; + } + + function inTableDefinition( stream, state ) { + if ( stream.sol() ) { + state.tokenize = inTable; + return inTable( stream, state ); + } + return eatWikiText( 'mw-table-definition', '' )( stream, state ); + } + + function inTableCaption( stream, state ) { + if ( stream.sol() && stream.match( /[\s\u00a0]*[\|!]/, false ) ) { + state.tokenize = inTable; + return inTable( stream, state ); + } + return eatWikiText( 'mw-table-caption', '' )( stream, state ); + } + + function inTable( stream, state ) { + if ( stream.sol() ) { + stream.eatSpace(); + if ( stream.eat( '|' ) ) { + if ( stream.eat( '-' ) ) { + stream.eatSpace(); + state.tokenize = inTableDefinition; + return makeLocalStyle( 'mw-table-delimiter', state ); + } + if ( stream.eat( '+' ) ) { + stream.eatSpace(); + state.tokenize = inTableCaption; + return makeLocalStyle( 'mw-table-delimiter', state ); + } + if ( stream.eat( '}' ) ) { + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-table-bracket', state ); + } + stream.eatSpace(); + state.tokenize = eatTableRow( true, false ); + return makeLocalStyle( 'mw-table-delimiter', state ); + } + if ( stream.eat( '!' ) ) { + stream.eatSpace(); + state.tokenize = eatTableRow( true, true ); return makeLocalStyle( 'mw-table-delimiter', state ); } } - return eatWikiText( (isHead ? 'strong' : ''), (isHead ? 'strong' : '') )( stream, state ); - }; - } - - function eatFreeExternalLinkProtocol( stream, state ) { - stream.match( urlProtocols ); - state.tokenize = eatFreeExternalLink; - return makeLocalStyle( 'mw-free-extlink-protocol', state ); - } - - function eatFreeExternalLink( stream, state ) { - if ( stream.eol() ) { - // @todo error message - } else if ( stream.match( /[^\s\u00a0\{\[\]<>~\)\.,']*/ ) ) { - if ( stream.peek() === '~' ) { - if ( !stream.match( /~{3,}/, false ) ) { - stream.match( /~*/ ); - return makeLocalStyle( 'mw-free-extlink', state ); - } - } else if ( stream.peek() === '{' ) { - if ( !stream.match( /\{\{/, false ) ) { - stream.next(); - return makeLocalStyle( 'mw-free-extlink', state ); - } - } else if ( stream.peek() === '\'' ) { - if ( !stream.match( '\'\'', false ) ) { - stream.next(); - return makeLocalStyle( 'mw-free-extlink', state ); - } - } else if ( stream.match( /[\)\.,]+(?=[^\s\u00a0\{\[\]<>~\)\.,])/ ) ) { - return makeLocalStyle( 'mw-free-extlink', state ); - } + return eatWikiText( '', '' )( stream, state ); } - state.tokenize = state.stack.pop(); - return makeLocalStyle( 'mw-free-extlink', state ); - } - function eatWikiText( style, mnemonicStyle ) { - return function( stream, state ) { - function chain( parser ) { - state.stack.push( state.tokenize ); - state.tokenize = parser; - return parser( stream, state ); - } - var ch, sol = stream.sol(); - - if ( sol ) { - if ( stream.match( urlProtocols ) ) { // highlight free external links, bug T108448 - state.stack.push( state.tokenize ); - state.tokenize = eatFreeExternalLink; - return makeLocalStyle( 'mw-free-extlink-protocol', state ); + function eatTableRow( isStart, isHead ) { + return function ( stream, state ) { + if ( stream.sol() ) { + if ( stream.match( /[\s\u00a0]*[\|!]/, false ) ) { + state.tokenize = inTable; + return inTable( stream, state ); + } + } else { + if ( stream.match( /[^'\|\{\[<\&~]+/ ) ) { + return makeStyle( ( isHead ? 'strong' : '' ), state ); + } + if ( stream.match( '||' ) || isHead && stream.match( '!!' ) || ( isStart && stream.eat( '|' ) ) ) { + isBold = false; + isItalic = false; + if ( isStart ) { + state.tokenize = eatTableRow( false, isHead ); + } + return makeLocalStyle( 'mw-table-delimiter', state ); + } } - ch = stream.next(); - switch ( ch ) { - case '-': - if ( stream.match( /----*/ ) ) { - return 'mw-hr'; - } - break; - case '=': - var tmp = stream.match( /(={0,5})(.+?(=\1\s*))$/ ); - if ( tmp ) { // Title - stream.backUp( tmp[2].length ); - state.stack.push( state.tokenize ); - state.tokenize = eatSectionHeader( tmp[3].length ); - return 'mw-section-header line-cm-mw-section-' + ( tmp[1].length + 1 ); - } - break; - case '*': - case '#': - if ( stream.match( /[\*#]*:*/ ) ) { - return 'mw-list'; - } - break; - case ':': - if ( stream.match( /:*{\|/, false ) ) { // Highlight indented tables :{|, bug T108454 - state.stack.push( state.tokenize ); - state.tokenize = eatStartTable; - } - if ( stream.match( /:*[\*#]*/ ) ) { - return 'mw-indenting'; - } - break; - case ' ': - if ( stream.match( /[\s\u00a0]*:*{\|/, false ) ) { // Leading spaces is the correct syntax for a table, bug T108454 - stream.eatSpace(); - if ( stream.match( /:+/ ) ) { // ::{| + return eatWikiText( ( isHead ? 'strong' : '' ), ( isHead ? 'strong' : '' ) )( stream, state ); + }; + } + + function eatFreeExternalLinkProtocol( stream, state ) { + stream.match( urlProtocols ); + state.tokenize = eatFreeExternalLink; + return makeLocalStyle( 'mw-free-extlink-protocol', state ); + } + + function eatFreeExternalLink( stream, state ) { + if ( stream.eol() ) { + // @todo error message + } else if ( stream.match( /[^\s\u00a0\{\[\]<>~\)\.,']*/ ) ) { + if ( stream.peek() === '~' ) { + if ( !stream.match( /~{3,}/, false ) ) { + stream.match( /~*/ ); + return makeLocalStyle( 'mw-free-extlink', state ); + } + } else if ( stream.peek() === '{' ) { + if ( !stream.match( /\{\{/, false ) ) { + stream.next(); + return makeLocalStyle( 'mw-free-extlink', state ); + } + } else if ( stream.peek() === '\'' ) { + if ( !stream.match( '\'\'', false ) ) { + stream.next(); + return makeLocalStyle( 'mw-free-extlink', state ); + } + } else if ( stream.match( /[\)\.,]+(?=[^\s\u00a0\{\[\]<>~\)\.,])/ ) ) { + return makeLocalStyle( 'mw-free-extlink', state ); + } + } + state.tokenize = state.stack.pop(); + return makeLocalStyle( 'mw-free-extlink', state ); + } + + function eatWikiText( style, mnemonicStyle ) { + return function ( stream, state ) { + function chain( parser ) { + state.stack.push( state.tokenize ); + state.tokenize = parser; + return parser( stream, state ); + } + var ch, + sol = stream.sol(); + + if ( sol ) { + if ( stream.match( urlProtocols ) ) { // highlight free external links, bug T108448 + state.stack.push( state.tokenize ); + state.tokenize = eatFreeExternalLink; + return makeLocalStyle( 'mw-free-extlink-protocol', state ); + } + ch = stream.next(); + switch ( ch ) { + case '-': + if ( stream.match( /----*/ ) ) { + return 'mw-hr'; + } + break; + case '=': + var tmp = stream.match( /(={0,5})(.+?(=\1\s*))$/ ); + if ( tmp ) { // Title + stream.backUp( tmp[ 2 ].length ); + state.stack.push( state.tokenize ); + state.tokenize = eatSectionHeader( tmp[ 3 ].length ); + return 'mw-section-header line-cm-mw-section-' + ( tmp[ 1 ].length + 1 ); + } + break; + case '*': + case '#': + if ( stream.match( /[\*#]*:*/ ) ) { + return 'mw-list'; + } + break; + case ':': + if ( stream.match( /:*{\|/, false ) ) { // Highlight indented tables :{|, bug T108454 state.stack.push( state.tokenize ); state.tokenize = eatStartTable; + } + if ( stream.match( /:*[\*#]*/ ) ) { return 'mw-indenting'; } - stream.eat( '{' ); - } else { - return 'mw-skipformatting'; + break; + case ' ': + if ( stream.match( /[\s\u00a0]*:*{\|/, false ) ) { // Leading spaces is the correct syntax for a table, bug T108454 + stream.eatSpace(); + if ( stream.match( /:+/ ) ) { // ::{| + state.stack.push( state.tokenize ); + state.tokenize = eatStartTable; + return 'mw-indenting'; + } + stream.eat( '{' ); + } else { + return 'mw-skipformatting'; + } + // break is not necessary here + /*falls through*/ + case '{': + if ( stream.eat( '|' ) ) { + stream.eatSpace(); + state.stack.push( state.tokenize ); + state.tokenize = inTableDefinition; + return 'mw-table-bracket'; + } + } + } else { + ch = stream.next(); + } + + switch ( ch ) { + case '&': + return makeStyle( eatMnemonic( stream, style, mnemonicStyle ), state ); + case '\'': + if ( stream.match( /'*(?=''''')/ ) || stream.match( /'''(?!')/, false ) ) { // skip the irrelevant apostrophes ( >5 or =4 ) + break; } - // break is not necessary here - /*falls through*/ + if ( stream.match( '\'\'' ) ) { // bold + if ( !( firstsingleletterword || stream.match( '\'\'', false ) ) ) { + prepareItalicForCorrection( stream ); + } + isBold = isBold ? false : true; + return makeLocalStyle( 'mw-apostrophes-bold', state ); + } else if ( stream.eat( '\'' ) ) { // italic + isItalic = isItalic ? false : true; + return makeLocalStyle( 'mw-apostrophes-italic', state ); + } + break; + case '[': + if ( stream.eat( '[' ) ) { // Link Example: [[ Foo | Bar ]] + stream.eatSpace(); + if ( /[^\]\|\[]/.test( stream.peek() ) ) { + state.nLink++; + state.stack.push( state.tokenize ); + state.tokenize = inLink; + return makeLocalStyle( 'mw-link-bracket', state ); + } + } else { + var mt = stream.match( urlProtocols ); + if ( mt ) { + state.nLink++; + stream.backUp( mt[ 0 ].length ); + state.stack.push( state.tokenize ); + state.tokenize = eatExternalLinkProtocol( mt[ 0 ].length ); + return makeLocalStyle( 'mw-extlink-bracket', state ); + } + } + break; case '{': - if ( stream.eat( '|' ) ) { + if ( !stream.match( '{{{{', false ) && stream.match( '{{' ) ) { // Template parameter (skip parameters inside a template transclusion, Bug: T108450) stream.eatSpace(); state.stack.push( state.tokenize ); - state.tokenize = inTableDefinition; - return 'mw-table-bracket'; - } - } - } else { - ch = stream.next(); - } - - switch ( ch ) { - case '&': - return makeStyle( eatMnemonic( stream, style, mnemonicStyle ), state ); - case '\'': - if ( stream.match( /'*(?=''''')/ ) || stream.match( /'''(?!')/, false ) ) { // skip the irrelevant apostrophes ( >5 or =4 ) - break; - } - if ( stream.match( '\'\'' ) ) { // bold\ - if ( !(firstsingleletterword || stream.match( '\'\'', false )) ) { - prepareItalicForCorrection( stream ); - } - isBold = isBold ? false : true; - return makeLocalStyle( 'mw-apostrophes-bold', state ); - } else if ( stream.eat( '\'' ) ) { // italic - isItalic = isItalic ? false : true; - return makeLocalStyle( 'mw-apostrophes-italic', state ); - } - break; - case '[': - if ( stream.eat( '[' ) ) { // Link Example: [[ Foo | Bar ]] - stream.eatSpace(); - if ( /[^\]\|\[]/.test( stream.peek() ) ) { - state.nLink++; - state.stack.push( state.tokenize ); - state.tokenize = inLink; - return makeLocalStyle( 'mw-link-bracket', state ); - } - } else { - var mt = stream.match( urlProtocols ); - if ( mt ) { - state.nLink++; - stream.backUp( mt[0].length ); - state.stack.push( state.tokenize ); - state.tokenize = eatExternalLinkProtocol( mt[0].length ); - return makeLocalStyle( 'mw-extlink-bracket', state ); - } - } - break; - case '{': - if ( !stream.match( '{{{{', false ) && stream.match( '{{' ) ) { // Template parameter (skip parameters inside a template transclusion, Bug: T108450) - stream.eatSpace(); - state.stack.push( state.tokenize ); - state.tokenize = inVariable; - return makeLocalStyle( 'mw-templatevariable-bracket', state ); - } else if ( stream.match( /\{[\s\u00a0]*/ ) ) { - if ( stream.peek() === '#' ) { // Parser function - state.nExt++; - state.stack.push( state.tokenize ); - state.tokenize = inParserFunctionName; - return makeLocalStyle( 'mw-parserfunction-bracket', state ); - } - // Check for parser function without '#' - var name = stream.match( /([^\s\u00a0\}\[\]<\{\'\|\&\:]+)(\:|[\s\u00a0]*)(\}\}?)?(.)?/ ); - if ( name ) { - 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]) ) { + state.tokenize = inVariable; + return makeLocalStyle( 'mw-templatevariable-bracket', state ); + } else if ( stream.match( /\{[\s\u00a0]*/ ) ) { + if ( stream.peek() === '#' ) { // Parser function state.nExt++; state.stack.push( state.tokenize ); state.tokenize = inParserFunctionName; return makeLocalStyle( 'mw-parserfunction-bracket', state ); } + // Check for parser function without '#' + var name = stream.match( /([^\s\u00a0\}\[\]<\{\'\|\&\:]+)(\:|[\s\u00a0]*)(\}\}?)?(.)?/ ); + if ( name ) { + 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 ] ) ) { + state.nExt++; + state.stack.push( state.tokenize ); + state.tokenize = inParserFunctionName; + return makeLocalStyle( 'mw-parserfunction-bracket', state ); + } + } + // Template + state.nTemplate++; + state.stack.push( state.tokenize ); + state.tokenize = eatTemplatePageName( false ); + return makeLocalStyle( 'mw-template-bracket', state ); } - // Template - state.nTemplate++; - state.stack.push( state.tokenize ); - state.tokenize = eatTemplatePageName( false ); - return makeLocalStyle( 'mw-template-bracket', state ); - } - break; - case '<': - if ( stream.match( '!--' ) ) { // coment - return chain( eatBlock( 'mw-comment', '-->' ) ); - } - var isCloseTag = ( stream.eat( '/' ) ? true : false ); - var tagname = stream.match( /[^>\/\s\u00a0\.\*\,\[\]\{\}\$\^\+\?\|\/\\'`~<=!@#%&\(\)-]+/ ); - if ( tagname ) { - tagname = tagname[0].toLowerCase(); - if ( tagname in config.mwextTags ) { // Parser function - if ( isCloseTag === true ) { - // @todo message - return 'error'; + break; + case '<': + var isCloseTag = ( stream.eat( '/' ) ? true : false ); + var tagname = stream.match( /[^>\/\s\u00a0\.\*\,\[\]\{\}\$\^\+\?\|\/\\'`~<=!@#%&\(\)-]+/ ); + if ( stream.match( '!--' ) ) { // coment + return chain( eatBlock( 'mw-comment', '-->' ) ); + } + if ( tagname ) { + tagname = tagname[ 0 ].toLowerCase(); + if ( tagname in config.mwextTags ) { // Parser function + if ( isCloseTag === true ) { + // @todo message + return 'error'; + } + stream.backUp( tagname.length ); + state.stack.push( state.tokenize ); + state.tokenize = eatTagName( tagname.length, isCloseTag, false ); + return makeLocalStyle( 'mw-exttag-bracket', state ); + } + if ( tagname in permittedHtmlTags ) { // Html tag + if ( isCloseTag === true && tagname !== state.InHtmlTag.pop() ) { + // @todo message + return 'error'; + } + stream.backUp( tagname.length ); + state.stack.push( state.tokenize ); + state.tokenize = eatTagName( tagname.length, isCloseTag, true ); + return makeLocalStyle( 'mw-htmltag-bracket', state ); } stream.backUp( tagname.length ); - state.stack.push( state.tokenize ); - state.tokenize = eatTagName( tagname.length, isCloseTag, false ); - return makeLocalStyle( 'mw-exttag-bracket', state ); } - if ( tagname in permittedHtmlTags ) { // Html tag - if ( isCloseTag === true && tagname !== state.InHtmlTag.pop() ) { - // @todo message - return 'error'; + break; + case '~': + if ( stream.match( /~{2,4}/ ) ) { + return 'mw-signature'; + } + break; + default: + if ( /[\s\u00a0]/.test( ch ) ) { + stream.eatSpace(); + if ( stream.match( urlProtocols, false ) ) { // highlight free external links, bug T108448 + state.stack.push( state.tokenize ); + state.tokenize = eatFreeExternalLinkProtocol; + return makeStyle( style, state ); } - stream.backUp( tagname.length ); - state.stack.push( state.tokenize ); - state.tokenize = eatTagName( tagname.length, isCloseTag, true ); - return makeLocalStyle( 'mw-htmltag-bracket', state ); } - stream.backUp( tagname.length ); - } - break; - case '~': - if ( stream.match( /~{2,4}/ ) ) { - return 'mw-signature'; - } - break; - default: - if ( /[\s\u00a0]/.test( ch ) ) { - stream.eatSpace(); - if ( stream.match( urlProtocols, false ) ) { // highlight free external links, bug T108448 - state.stack.push( state.tokenize ); - state.tokenize = eatFreeExternalLinkProtocol; - return makeStyle( style, state ); - } - } - break; - } - stream.match( /[^\s\u00a0_>\}\[\]<\{\'\|\&\:~]+/ ); - return makeStyle( style, state ); - }; - } - - /** - * Remembers position and status for rollbacking. - * It needed for change bold to italic with apostrophe before it if required - * @see https://phabricator.wikimedia.org/T108455 - * @param CodeMirror.StringStream stream - * @returns null - */ - function prepareItalicForCorrection( stream ) { - // see Parser::doQuotes() in MediaWiki core, it works similar - // firstsingleletterword has maximum priority - // firstmultiletterword has medium priority - // firstspace has low priority - var end = stream.pos, - str = stream.string.substr( 0, end - 3 ), - x1 = str.substr( -1, 1 ), - x2 = str.substr( -2, 1 ); - - // firstsingleletterword olways is undefined here - if ( x1 === ' ' ) { - if ( firstmultiletterword || firstspace ) { - return; - } - firstspace = end; - } else if ( x2 === ' ' ) { - firstsingleletterword = end; - } else if ( firstmultiletterword ) { - return; - } else { - firstmultiletterword = end; - } - // remember bold and italic state for restore - mBold = isBold; - mItalic = isItalic; - } - - return { - startState: function() { - return { tokenize: eatWikiText('', ''), stack: [], InHtmlTag:[], extName: false, extMode: false, extState: false, nTemplate: 0, nLink: 0, nExt: 0 }; - }, - copyState: function( state ) { - return { - tokenize: state.tokenize, - stack: state.stack.concat( [] ), - InHtmlTag: state.InHtmlTag.concat( [] ), - extName: state.extName, - extMode: state.extMode, - extState: state.extMode !== false && CodeMirror.copyState( state.extMode, state.extState ), - nTemplate: state.nTemplate, - nLink: state.nLink, - nExt: state.nExt + break; + } + stream.match( /[^\s\u00a0_>\}\[\]<\{\'\|\&\:~]+/ ); + return makeStyle( style, state ); }; - }, - token: function( stream, state ) { - var style, p, t, f, - readyTokens = [], - tmpTokens = []; + } - if ( mTokens.length > 0 ) { // just send saved tokens till they exists + /** + * Remembers position and status for rollbacking. + * It needed for change bold to italic with apostrophe before it if required + * + * see https://phabricator.wikimedia.org/T108455 + * + * @param {Object} stream CodeMirror.StringStream + */ + function prepareItalicForCorrection( stream ) { + // see Parser::doQuotes() in MediaWiki core, it works similar + // firstsingleletterword has maximum priority + // firstmultiletterword has medium priority + // firstspace has low priority + var end = stream.pos, + str = stream.string.substr( 0, end - 3 ), + x1 = str.substr( -1, 1 ), + x2 = str.substr( -2, 1 ); + + // firstsingleletterword olways is undefined here + if ( x1 === ' ' ) { + if ( firstmultiletterword || firstspace ) { + return; + } + firstspace = end; + } else if ( x2 === ' ' ) { + firstsingleletterword = end; + } else if ( firstmultiletterword ) { + return; + } else { + firstmultiletterword = end; + } + // remember bold and italic state for restore + mBold = isBold; + mItalic = isItalic; + } + + return { + startState: function () { + return { tokenize: eatWikiText( '', '' ), stack: [], InHtmlTag: [], extName: false, extMode: false, extState: false, nTemplate: 0, nLink: 0, nExt: 0 }; + }, + copyState: function ( state ) { + return { + tokenize: state.tokenize, + stack: state.stack.concat( [] ), + InHtmlTag: state.InHtmlTag.concat( [] ), + extName: state.extName, + extMode: state.extMode, + extState: state.extMode !== false && CodeMirror.copyState( state.extMode, state.extState ), + nTemplate: state.nTemplate, + nLink: state.nLink, + nExt: state.nExt + }; + }, + token: function ( stream, state ) { + var style, p, t, f, + readyTokens = [], + tmpTokens = []; + + if ( mTokens.length > 0 ) { // just send saved tokens till they exists + t = mTokens.shift(); + stream.pos = t.pos; + state = t.state; + return t.style; + } + + if ( stream.sol() ) { // reset bold and italic status in every new line + isBold = false; + isItalic = false; + firstsingleletterword = undefined; + firstmultiletterword = undefined; + firstspace = undefined; + } + + do { + style = state.tokenize( stream, state ); // get token style + f = firstsingleletterword || firstmultiletterword || firstspace; + if ( f ) { // rollback point exists + if ( f !== p ) { // new rollbak point + p = f; + if ( tmpTokens.length > 0 ) { // it's not first rollbak point + readyTokens = readyTokens.concat( tmpTokens ); // save tokens + tmpTokens = []; + } + } + tmpTokens.push( { // save token + pos: stream.pos, + style: style, + state: CodeMirror.copyState( state.extMode ? state.extMode : 'mediawiki', state ) + } ); + } else { // rollback point not exists + mStyle = style; // remember style before possible rollback point + return style; // just return token style + } + } while ( !stream.eol() ); + + if ( isBold && isItalic ) { // needs to rollback + isItalic = mItalic; // restore status + isBold = mBold; + firstsingleletterword = undefined; + firstmultiletterword = undefined; + firstspace = undefined; + if ( readyTokens.length > 0 ) { // it contains tickets before the point of rollback + readyTokens[ readyTokens.length ].pos++; // add one apostrophe, next token will be italic (two apostrophes) + mTokens = readyTokens; // for sending tokens till the point of rollback + } else { // there are no tikets before the point of rollback + stream.pos = tmpTokens[ 0 ].pos - 2; // eat( '\'') + return mStyle; // send saved Style + } + } else { // not needs to rollback + mTokens = readyTokens.concat( tmpTokens ); // send all saved tokens + } + // return first saved token t = mTokens.shift(); stream.pos = t.pos; state = t.state; return t.style; - } - - if ( stream.sol() ) { // reset bold and italic status in every new line - isBold = false; - isItalic = false; - firstsingleletterword = undefined; - firstmultiletterword = undefined; - firstspace = undefined; - } - - do { - style = state.tokenize( stream, state ); // get token style - f = firstsingleletterword || firstmultiletterword || firstspace; - if ( f ) { // rollback point exists - if ( f !== p ) { // new rollbak point - p = f; - if ( tmpTokens.length > 0 ) { // it's not first rollbak point - readyTokens = readyTokens.concat( tmpTokens ); // save tokens - tmpTokens = []; + }, + blankLine: function ( state ) { + if ( state.extName ) { + if ( state.extMode ) { + var ret = ''; + if ( state.extMode.blankLine ) { + ret = ' ' + state.extMode.blankLine( state.extState ); } + return 'line-cm-mw-tag-' + state.extName + ret; } - tmpTokens.push( { // save token - pos: stream.pos, - style: style, - state: CodeMirror.copyState( state.extMode ? state.extMode : 'mediawiki', state ) - } ); - } else { // rollback point not exists - mStyle = style; // remember style before possible rollback point - return style; // just return token style + return 'line-cm-mw-exttag'; } - } while ( !stream.eol() ); - - if ( isBold && isItalic ) { // needs to rollback - isItalic = mItalic; // restore status - isBold = mBold; - firstsingleletterword = undefined; - firstmultiletterword = undefined; - firstspace = undefined; - if ( readyTokens.length > 0 ) { // it contains tickets before the point of rollback - readyTokens[readyTokens.length-1].pos++; // add one apostrophe, next token will be italic (two apostrophes) - mTokens = readyTokens; // for sending tokens till the point of rollback - } else { // there are no tikets before the point of rollback - stream.pos = tmpTokens[0].pos - 2; // eat( '\'') - return mStyle; // send saved Style - } - } else { // not needs to rollback - mTokens = readyTokens.concat( tmpTokens ); // send all saved tokens } - // return first saved token - t = mTokens.shift(); - stream.pos = t.pos; - state = t.state; - return t.style; - }, - blankLine: function( state ) { - if ( state.extName ) { - if ( state.extMode ) { - var ret = ''; - if ( state.extMode.blankLine ) { - ret = ' ' + state.extMode.blankLine( state.extState ); - } - return 'line-cm-mw-tag-' + state.extName + ret; - } - return 'line-cm-mw-exttag'; + }; + } ); + + CodeMirror.defineMIME( 'text/mediawiki', 'mediawiki' ); + + function eatNowiki( style, lineStyle ) { + return function ( stream, state, ownLine ) { + if ( ownLine && stream.sol() ) { + state.ownLine = true; + } else if ( ownLine === false && state.ownLine ) { + state.ownLine = false; } - } - }; -}); + var s = ( state.ownLine ? lineStyle : style ); + if ( stream.match( /[^&]+/ ) ) { + return s; + } + stream.next(); // eat & + return eatMnemonic( stream, s, s ); + }; + } -CodeMirror.defineMIME( 'text/mediawiki', 'mediawiki' ); + CodeMirror.defineMode( 'mw-tag-pre', function ( /*config, parserConfig */ ) { + return { + startState: function () { return {}; }, + token: eatNowiki( 'mw-tag-pre', 'line-cm-mw-tag-pre' ) + }; + } ); -function eatNowiki( style, lineStyle ) { - return function( stream , state, ownLine ) { - if ( ownLine && stream.sol() ) { - state.ownLine = true; - } else if( ownLine === false && state.ownLine ) { - state.ownLine = false; - } - var s = ( state.ownLine ? lineStyle : style ); - if ( stream.match( /[^&]+/ ) ) { - return s; - } - stream.next(); // eat & - return eatMnemonic( stream, s, s ); - }; -} + CodeMirror.defineMode( 'mw-tag-nowiki', function ( /*config, parserConfig */ ) { + return { + startState: function () { return {}; }, + token: eatNowiki( 'mw-tag-nowiki', 'line-cm-mw-tag-nowiki' ) + }; + } ); -CodeMirror.defineMode( 'mw-tag-pre', function( /*config, parserConfig */ ) { - return { - startState: function() { return {}; }, - token: eatNowiki( 'mw-tag-pre', 'line-cm-mw-tag-pre' ) - }; -}); - -CodeMirror.defineMode( 'mw-tag-nowiki', function( /*config, parserConfig */ ) { - return { - startState: function() { return {}; }, - token: eatNowiki( 'mw-tag-nowiki', 'line-cm-mw-tag-nowiki' ) - }; -}); - -}); +}( CodeMirror ) ); -- To view, visit https://gerrit.wikimedia.org/r/255126 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Id8f11f9414fae8313dc18a857a07e11694354dcb Gerrit-PatchSet: 26 Gerrit-Project: mediawiki/extensions/CodeMirror Gerrit-Branch: master Gerrit-Owner: Paladox <thomasmulhall...@yahoo.com> Gerrit-Reviewer: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com> Gerrit-Reviewer: Hashar <has...@free.fr> Gerrit-Reviewer: JanZerebecki <jan.wikime...@zerebecki.de> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Paladox <thomasmulhall...@yahoo.com> Gerrit-Reviewer: Pastakhov <pastak...@yandex.ru> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits