Pastakhov has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/249357

Change subject: merge to branch master (v 3.2.1)
......................................................................

merge to branch master (v 3.2.1)

Bug: T116829
Change-Id: Id5e434eca4840e6ce9841f43e1299970a61ba8cc
---
M CodeMirror.hooks.php
M CodeMirror.php
M i18n/ast.json
A i18n/bn.json
M i18n/de.json
M i18n/es.json
M i18n/fr.json
M i18n/gl.json
A i18n/gu.json
M i18n/it.json
A i18n/lb.json
M i18n/mk.json
M i18n/nl.json
M i18n/ru.json
M i18n/uk.json
A i18n/xmf.json
M i18n/zh-hans.json
M i18n/zh-hant.json
M resources/ext.CodeMirror.js
M resources/mode/mediawiki/mediawiki.css
M resources/mode/mediawiki/mediawiki.js
21 files changed, 270 insertions(+), 73 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CodeMirror 
refs/changes/57/249357/1

diff --git a/CodeMirror.hooks.php b/CodeMirror.hooks.php
index dcd6421..ab74eba 100644
--- a/CodeMirror.hooks.php
+++ b/CodeMirror.hooks.php
@@ -19,7 +19,10 @@
         * @return bool Always true
         */
        public static function onResourceLoaderRegisterModules( ResourceLoader 
$rl ) {
-               global $wgCodeMirrorResourceTemplate;
+               $codeMirrorResourceTemplate = array(
+                       'localBasePath' => __DIR__ . '/resources',
+                       'remoteExtPath' => 'CodeMirror/resources',
+               );
 
                self::$extModes = array(
                        'tag' => array(
@@ -53,7 +56,7 @@
                        'messages' => array_keys( $extResources['messages'] ),
                        'dependencies' => array_keys( 
$extResources['dependencies'] ),
                        'group' => 'ext.CodeMirror',
-               ) + $wgCodeMirrorResourceTemplate;
+               ) + $codeMirrorResourceTemplate;
 
                $rl->register( array( 'ext.CodeMirror.other' => $codeMirror ) );
 
diff --git a/CodeMirror.php b/CodeMirror.php
index 6cc452a..d360d17 100644
--- a/CodeMirror.php
+++ b/CodeMirror.php
@@ -15,7 +15,7 @@
        die( 'This file is an extension to MediaWiki and thus not a valid entry 
point.' );
 }
 
-const EXT_CODEMIRROR_VERSION = '3.1.10';
+const EXT_CODEMIRROR_VERSION = '3.2.1';
 
 // Register this extension on Special:Version
 $wgExtensionCredits['parserhook'][] = array(
diff --git a/i18n/ast.json b/i18n/ast.json
index 0046d20..6068f11 100644
--- a/i18n/ast.json
+++ b/i18n/ast.json
@@ -4,5 +4,7 @@
                        "Xuacu"
                ]
        },
-       "codemirror-desc": "Ufre sintaxis resaltada nel editor de testu wiki"
+       "codemirror-desc": "Ufre sintaxis resaltada nel editor de testu wiki",
+       "codemirror-enable-label": "Activar CodeMirror (resaltáu de sintaxis)",
+       "codemirror-disable-label": "Desactivar CodeMirror (resaltáu de 
sintaxis)"
 }
diff --git a/i18n/bn.json b/i18n/bn.json
new file mode 100644
index 0000000..1ee6ac4
--- /dev/null
+++ b/i18n/bn.json
@@ -0,0 +1,9 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Aftabuzzaman"
+               ]
+       },
+       "codemirror-enable-label": "কোডমিরর সক্রিয় করো (সিনট্যাক্স হাইলাইট)",
+       "codemirror-disable-label": "কোডমিরর নিষ্ক্রিয় করো (সিনট্যাক্স 
হাইলাইট)"
+}
diff --git a/i18n/de.json b/i18n/de.json
index 72ec6e3..bf37de0 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -4,5 +4,7 @@
                        "Metalhead64"
                ]
        },
-       "codemirror-desc": "Ermöglicht Syntax-Hervorhebungen im Wikitext-Editor"
+       "codemirror-desc": "Ermöglicht Syntax-Hervorhebungen im 
Wikitext-Editor",
+       "codemirror-enable-label": "CodeMirror aktivieren (Syntaxhervorhebung)",
+       "codemirror-disable-label": "CodeMirror deaktivieren 
(Syntaxhervorhebung)"
 }
diff --git a/i18n/es.json b/i18n/es.json
index 7dfd373..e12a4d5 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -1,8 +1,11 @@
 {
        "@metadata": {
                "authors": [
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Macofe"
                ]
        },
-       "codemirror-desc": "Proporciona resalte de la sintaxis en el editor de 
código wiki"
+       "codemirror-desc": "Proporciona resalte de la sintaxis en el editor de 
código wiki",
+       "codemirror-enable-label": "Activar CodeMirror (resaltado de sintaxis)",
+       "codemirror-disable-label": "Desactivar CodeMirror (resaltado de 
sintaxis)"
 }
diff --git a/i18n/fr.json b/i18n/fr.json
index 2372a31..f245b2d 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -4,5 +4,7 @@
                        "Gomoko"
                ]
        },
-       "codemirror-desc": "Fournit une mise en évidence de la syntaxe dans 
l’éditeur wikitexte"
+       "codemirror-desc": "Fournit une mise en évidence de la syntaxe dans 
l’éditeur wikitexte",
+       "codemirror-enable-label": "Activer CodeMirror (Syntaxhighlight)",
+       "codemirror-disable-label": "Désactiver CodeMirror (Syntaxhighlight)"
 }
diff --git a/i18n/gl.json b/i18n/gl.json
index e4c5a03..5894516 100644
--- a/i18n/gl.json
+++ b/i18n/gl.json
@@ -1,8 +1,11 @@
 {
        "@metadata": {
                "authors": [
-                       "Banjo"
+                       "Banjo",
+                       "Toliño"
                ]
        },
-       "codemirror-desc": "Provee resalte da sintaxe no editor de texto wiki"
+       "codemirror-desc": "Proporciona un resalte da sintaxe no editor de 
texto wiki",
+       "codemirror-enable-label": "Activar CodeMirror (resalte da sintaxe)",
+       "codemirror-disable-label": "Desactivar CodeMirror (resalte da sintaxe)"
 }
diff --git a/i18n/gu.json b/i18n/gu.json
new file mode 100644
index 0000000..7be2758
--- /dev/null
+++ b/i18n/gu.json
@@ -0,0 +1,9 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Nisargjhaveri"
+               ]
+       },
+       "codemirror-enable-label": "CodeMirror (સિન્ટેક્ષ હાઇલાઇટ) ચાલુ કરો",
+       "codemirror-disable-label": "CodeMirror (સિન્ટેક્ષ હાઇલાઇટ) બંધ કરો"
+}
diff --git a/i18n/it.json b/i18n/it.json
index 07720eb..33572bc 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -1,8 +1,11 @@
 {
        "@metadata": {
                "authors": [
-                       "Beta16"
+                       "Beta16",
+                       "Statix64"
                ]
        },
-       "codemirror-desc": "Fornisce l'evidenziazione della sintassi 
nell'editor wikitesto"
+       "codemirror-desc": "Fornisce l'evidenziazione della sintassi 
nell'editor wikitesto",
+       "codemirror-enable-label": "Attiva CodeMirror (Syntaxhighlight)",
+       "codemirror-disable-label": "Disattiva CodeMirror (Syntaxhighlight)"
 }
diff --git a/i18n/lb.json b/i18n/lb.json
new file mode 100644
index 0000000..7ed9f32
--- /dev/null
+++ b/i18n/lb.json
@@ -0,0 +1,9 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Robby"
+               ]
+       },
+       "codemirror-enable-label": "CodeMirror aktivéieren (Syntaxhighlight)",
+       "codemirror-disable-label": "CodeMirror desaktivéieren 
(Syntaxhighlight)"
+}
diff --git a/i18n/mk.json b/i18n/mk.json
index 10ab639..bfd567e 100644
--- a/i18n/mk.json
+++ b/i18n/mk.json
@@ -4,5 +4,7 @@
                        "Bjankuloski06"
                ]
        },
-       "codemirror-desc": "Дава потцртување на синтаксата во уредувачот на 
викитекст"
+       "codemirror-desc": "Дава потцртување на синтаксата во уредувачот на 
викитекст",
+       "codemirror-enable-label": "Вклучи го CodeMirror (подвлекување на 
синтакса)",
+       "codemirror-disable-label": "Исклучи го CodeMirror (подвлекување на 
синтакса)"
 }
diff --git a/i18n/nl.json b/i18n/nl.json
index e248378..a8e189d 100644
--- a/i18n/nl.json
+++ b/i18n/nl.json
@@ -1,8 +1,9 @@
 {
        "@metadata": {
                "authors": [
-                       "Esketti"
+                       "Esketti",
+                       "Siebrand"
                ]
        },
-       "codemirror-desc": "Zorgt voor accentuering van de syntaxis in de 
wikitext editor"
+       "codemirror-desc": "Biedt accentuering van tekst in de wikitekst 
tekstverwerker"
 }
diff --git a/i18n/ru.json b/i18n/ru.json
index 8d51ac7..b8010e6 100644
--- a/i18n/ru.json
+++ b/i18n/ru.json
@@ -5,5 +5,7 @@
                        "Kaganer"
                ]
        },
-       "codemirror-desc": "Обеспечивает подсветку синтаксиса в редакторе 
вики-текста."
+       "codemirror-desc": "Обеспечивает подсветку синтаксиса в редакторе 
вики-текста.",
+       "codemirror-enable-label": "Включить CodeMirror (подсветка синтаксиса)",
+       "codemirror-disable-label": "Выключить CodeMirror (подсветка 
синтаксиса)"
 }
diff --git a/i18n/uk.json b/i18n/uk.json
index 4662a44..3318c42 100644
--- a/i18n/uk.json
+++ b/i18n/uk.json
@@ -2,8 +2,11 @@
        "@metadata": {
                "authors": [
                        "Andriykopanytsia",
-                       "Ата"
+                       "Ата",
+                       "Dars"
                ]
        },
-       "codemirror-desc": "Забезпечує підсвічування синтаксису у редакторі 
вікітексту"
+       "codemirror-desc": "Забезпечує підсвічування синтаксису у редакторі 
вікітексту",
+       "codemirror-enable-label": "Задіяти Дзеркало коду (Підсвічування 
синтаксису)",
+       "codemirror-disable-label": "Відключити Дзеркало коду (Підсвічування 
синтаксису)"
 }
diff --git a/i18n/xmf.json b/i18n/xmf.json
new file mode 100644
index 0000000..f68b754
--- /dev/null
+++ b/i18n/xmf.json
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Silovan"
+               ]
+       },
+       "codemirror-disable-label": "CodeMirror-იშ (Syntaxhighlight) გოთიშუა"
+}
diff --git a/i18n/zh-hans.json b/i18n/zh-hans.json
index bf14717..7004f83 100644
--- a/i18n/zh-hans.json
+++ b/i18n/zh-hans.json
@@ -4,5 +4,7 @@
                        "Liuxinyu970226"
                ]
        },
-       "codemirror-desc": "在wiki文本编辑器中提供语法高亮显示"
+       "codemirror-desc": "在wiki文本编辑器中提供语法高亮显示",
+       "codemirror-enable-label": "启用CodeMirror(Syntaxhighlight)",
+       "codemirror-disable-label": "禁用CodeMirror(Syntaxhighlight)"
 }
diff --git a/i18n/zh-hant.json b/i18n/zh-hant.json
index aa5369f..b5177fa 100644
--- a/i18n/zh-hant.json
+++ b/i18n/zh-hant.json
@@ -6,5 +6,7 @@
                        "Cwlin0416"
                ]
        },
-       "codemirror-desc": "提供 wikitext 編輯器語法醒目標示"
+       "codemirror-desc": "提供 wikitext 編輯器語法醒目標示",
+       "codemirror-enable-label": "開啟 CodeMirror (強調語法顯示)",
+       "codemirror-disable-label": "關閉 CodeMirror (強調語法顯示)"
 }
diff --git a/resources/ext.CodeMirror.js b/resources/ext.CodeMirror.js
index a3e968e..4591f50 100644
--- a/resources/ext.CodeMirror.js
+++ b/resources/ext.CodeMirror.js
@@ -20,6 +20,10 @@
                                        return codeMirror.doc.getValue();
                                },
 
+                               setContents: function ( newContents ) {
+                                       codeMirror.doc.setValue( newContents );
+                               },
+
                                /**
                                 * Get the currently selected text in this 
textarea. Will focus the textarea
                                 * in some browsers (IE/Opera)
@@ -232,7 +236,28 @@
                                        }
                                );
                        }
-               };
+               },
+               originHooksTextarea = $.valHooks.textarea;
+
+       // define JQuery hook for searching and replacing text using JS if 
CodeMirror is enabled, see Bug: T108711
+       $.valHooks.textarea = {
+               get: function( elem ) {
+                       if ( elem.id === 'wpTextbox1' && codeMirror ) {
+                               return codeMirror.doc.getValue();
+                       } else if ( originHooksTextarea ) {
+                               return originHooksTextarea.get( elem );
+                       }
+                       return elem.value;
+               },
+               set: function( elem, value ) {
+                       if ( elem.id === 'wpTextbox1' && codeMirror ) {
+                               return codeMirror.doc.setValue( value );
+                       } else if ( originHooksTextarea ) {
+                               return originHooksTextarea.set( elem, value );
+                       }
+                       elem.value = value;
+               }
+       };
 
        /**
         * Save CodeMirror enabled pref.
diff --git a/resources/mode/mediawiki/mediawiki.css 
b/resources/mode/mediawiki/mediawiki.css
index 197d764..aae2300 100644
--- a/resources/mode/mediawiki/mediawiki.css
+++ b/resources/mode/mediawiki/mediawiki.css
@@ -19,7 +19,7 @@
 .cm-mw-indenting {color: #08f; font-weight: bold; background-color: #ddd;}
 .cm-mw-mnemonic {color: #090;}
 .cm-mw-comment {color: #aaa; font-weight: normal;}
-.cm-mw-apostrophes {color: #08f;}
+.cm-mw-apostrophes-bold, .cm-mw-apostrophes-italic {color: #08f;}
 
 pre.cm-mw-section-1 {font-size: 1.8em;}
 pre.cm-mw-section-2 {font-size: 1.5em;}
diff --git a/resources/mode/mediawiki/mediawiki.js 
b/resources/mode/mediawiki/mediawiki.js
index 6680168..303e035 100644
--- a/resources/mode/mediawiki/mediawiki.js
+++ b/resources/mode/mediawiki/mediawiki.js
@@ -22,23 +22,24 @@
 
 CodeMirror.defineMode( 'mediawiki', function( config/*, parserConfig */ ) {
 
-       var urlProtocols = new RegExp( config.mwextUrlProtocols, 'i' );
-       var 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};
+       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 ( state.isBold ) {
+               if ( isBold ) {
                        style += ' strong';
                }
-               if ( state.isItalic ) {
+               if ( isItalic ) {
                        style += ' em';
                }
                return makeLocalStyle( style, state, endGround );
@@ -268,7 +269,14 @@
                        state.tokenize = inExternalLinkText;
                        return makeStyle( '', state );
                }
-               if ( stream.match( /[^\s\u00a0\]\{\&~]+/ ) || stream.eatSpace() 
) {
+               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 eatWikiText( 'mw-extlink', '' )( stream, state );
@@ -312,7 +320,7 @@
 //                                     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 
);
                }
@@ -344,25 +352,25 @@
        }
 
        function eatLinkText() {
-               var isBold, isItalic;
+               var linkIsBold, linkIsItalic;
                return function ( stream, state ) {
                        if ( stream.match( ']]' ) ) {
                                state.tokenize = state.stack.pop();
                                return makeLocalStyle( 'mw-link-bracket', 
state, 'nLink' );
                        }
                        if ( stream.match( '\'\'\'' ) ) {
-                               isBold = (isBold ? false : true);
+                               linkIsBold = (linkIsBold ? false : true);
                                return makeLocalStyle( 'mw-link-text 
mw-apostrophes', state );
                        }
                        if ( stream.match( '\'\'' ) ) {
-                               isItalic = (isItalic ? false : true);
+                               linkIsItalic = (linkIsItalic ? false : true);
                                return makeLocalStyle( 'mw-link-text 
mw-apostrophes', state );
                        }
                        var tmpstyle = 'mw-link-text';
-                       if ( isBold ) {
+                       if ( linkIsBold ) {
                                tmpstyle += ' strong';
                        }
-                       if ( isItalic ) {
+                       if ( linkIsItalic ) {
                                tmpstyle += ' em';
                        }
                        if ( stream.match( /[^'\]\{\&~]+/ ) ) {
@@ -390,7 +398,7 @@
                                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' );
@@ -450,9 +458,10 @@
 
        function eatExtTagArea( name ) {
                return function( stream, state ) {
-                       var origString = false, from = stream.pos, to;
-                       var pattern = new RegExp( '</' + name + '\\s*>' );
-                       var m = pattern.exec( from ? stream.string.slice( from 
) : stream.string );
+                       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 );
@@ -467,6 +476,7 @@
                                origString = stream.string;
                                stream.string = origString.slice( 0, to );
                        }
+
                        state.stack.push( state.tokenize );
                        state.tokenize = eatExtTokens( origString );
                        return state.tokenize( stream, state );
@@ -518,13 +528,9 @@
        }
 
        function inTableCaption( stream, state ) {
-               if ( stream.sol() ) {
-                       state.isBold = false;
-                       state.isItalic = false;
-                       if ( stream.match( /[\s\u00a0]*[\|!]/, false ) ) {
-                               state.tokenize = inTable;
-                               return inTable( stream, state );
-                       }
+               if ( stream.sol() && stream.match( /[\s\u00a0]*[\|!]/, false ) 
) {
+                       state.tokenize = inTable;
+                       return inTable( stream, state );
                }
                return eatWikiText( 'mw-table-caption', '' )( stream, state );
        }
@@ -563,8 +569,6 @@
        function eatTableRow( isStart, isHead ) {
                return function ( stream, state ) {
                        if ( stream.sol() ) {
-                               state.isBold = false;
-                               state.isItalic = false;
                                if ( stream.match( /[\s\u00a0]*[\|!]/, false ) 
) {
                                        state.tokenize = inTable;
                                        return inTable( stream, state );
@@ -574,8 +578,8 @@
                                        return makeStyle( (isHead ? 'strong' : 
''), state );
                                }
                                if ( stream.match( '||' ) || isHead && 
stream.match( '!!' ) || (isStart && stream.eat( '|' )) ) {
-                                       state.isBold = false;
-                                       state.isItalic = false;
+                                       isBold = false;
+                                       isItalic = false;
                                        if ( isStart ) {
                                                state.tokenize = eatTableRow( 
false, isHead );
                                        }
@@ -595,7 +599,7 @@
        function eatFreeExternalLink( stream, state ) {
                if ( stream.eol() ) {
                        // @todo error message
-               } else if ( stream.match( /[^\s\u00a0\{\[\]<>~\)\.,]*/ ) ) {
+               } else if ( stream.match( /[^\s\u00a0\{\[\]<>~\)\.,']*/ ) ) {
                        if ( stream.peek() === '~' ) {
                                if ( !stream.match( /~{3,}/, false ) ) {
                                        stream.match( /~*/ );
@@ -603,7 +607,12 @@
                                }
                        } else if ( stream.peek() === '{' ) {
                                if ( !stream.match( /\{\{/, false ) ) {
-                                       stream.eat( '{' );
+                                       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\{\[\]<>~\)\.,])/ ) ) {
@@ -624,8 +633,6 @@
                        var ch, sol = stream.sol();
 
                        if ( sol ) {
-                               state.isBold = false;
-                               state.isItalic = false;
                                if ( stream.match( urlProtocols ) ) { // 
highlight free external links, bug T108448
                                        state.stack.push( state.tokenize );
                                        state.tokenize = eatFreeExternalLink;
@@ -634,7 +641,7 @@
                                ch = stream.next();
                                switch ( ch ) {
                                        case '-':
-                                               if ( stream.match( '---' ) ) {
+                                               if ( stream.match( /----*/ ) ) {
                                                        return 'mw-hr';
                                                }
                                                break;
@@ -692,12 +699,18 @@
                                case '&':
                                        return makeStyle( eatMnemonic( stream, 
style, mnemonicStyle ), state );
                                case '\'':
-                                       if ( stream.match( '\'\'' ) ) {
-                                               state.isBold = state.isBold ? 
false : true;
-                                               return makeLocalStyle( 
'mw-apostrophes', state );
-                                       } else if ( stream.eat( '\'' ) ) {
-                                               state.isItalic = state.isItalic 
? false : true;
-                                               return makeLocalStyle( 
'mw-apostrophes', state );
+                                       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 '[':
@@ -721,7 +734,7 @@
                                        }
                                        break;
                                case '{':
-                                       if ( stream.match( '{{' ) ) { // 
Variable
+                                       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;
@@ -803,17 +816,50 @@
                };
        }
 
+       /**
+        * 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:[], isBold: false, isItalic: false, extName: false, extMode: false, 
extState: false, nTemplate: 0, nLink: 0, nExt: 0 };
+                       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( [] ),
-                               isBold: state.isBold,
-                               isItalic: state.isItalic,
                                extName: state.extName,
                                extMode: state.extMode,
                                extState: state.extMode !== false && 
CodeMirror.copyState( state.extMode, state.extState ),
@@ -823,7 +869,68 @@
                        };
                },
                token: function( stream, state ) {
-                       return state.tokenize( 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-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 ) {

-- 
To view, visit https://gerrit.wikimedia.org/r/249357
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id5e434eca4840e6ce9841f43e1299970a61ba8cc
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CodeMirror
Gerrit-Branch: REL1_24
Gerrit-Owner: Pastakhov <pastak...@yandex.ru>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to