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

Reply via email to