jenkins-bot has submitted this change and it was merged.

Change subject: Autolink pasted links
......................................................................


Autolink pasted links

Adds the html format of the original DataTransfer object to each
DataTransferItem so that handler can consult this additional
representation if needed.

This partially addresses T107431 by autolinking pasted URLs,
including in the "manual" citation entry box.

Bug: T107431
Change-Id: I80bc95e436563f797849ebd0204e04ae0e277308
---
M build/modules.json
M demos/ve/desktop.html
M demos/ve/mobile.html
M src/ce/ve.ce.Surface.js
A src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js
M src/ui/ve.ui.DataTransferItem.js
M tests/ce/ve.ce.Surface.test.js
M tests/index.html
A tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js
9 files changed, 297 insertions(+), 20 deletions(-)

Approvals:
  Esanders: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/build/modules.json b/build/modules.json
index 84ac30b..8302548 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -426,6 +426,7 @@
                        
"src/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.js",
                        
"src/ui/datatransferhandlers/ve.ui.PlainTextFileTransferHandler.js",
                        
"src/ui/datatransferhandlers/ve.ui.HTMLFileTransferHandler.js",
+                       
"src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js",
                        
"src/ui/windowmanagers/ve.ui.ToolbarDialogWindowManager.js",
                        "src/ui/widgets/ve.ui.AlignWidget.js",
                        "src/ui/widgets/ve.ui.LanguageSearchWidget.js",
@@ -564,6 +565,7 @@
                        "tests/ui/actions/ve.ui.ListAction.test.js",
                        "tests/ui/actions/ve.ui.TableAction.test.js",
                        
"tests/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.test.js",
+                       
"tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js",
                        "tests/ui/dialogs/ve.ui.FindAndReplaceDialog.test.js",
                        "tests/ce/ve.ce.TestRunner.js",
                        "tests/ce/ve.ce.imetests.test.js",
diff --git a/demos/ve/desktop.html b/demos/ve/desktop.html
index 0a6289b..a035aee 100644
--- a/demos/ve/desktop.html
+++ b/demos/ve/desktop.html
@@ -387,6 +387,7 @@
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.js"></script>
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.PlainTextFileTransferHandler.js"></script>
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.HTMLFileTransferHandler.js"></script>
+               <script 
src="../../src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js"></script>
                <script 
src="../../src/ui/windowmanagers/ve.ui.ToolbarDialogWindowManager.js"></script>
                <script 
src="../../src/ui/widgets/ve.ui.AlignWidget.js"></script>
                <script 
src="../../src/ui/widgets/ve.ui.LanguageSearchWidget.js"></script>
diff --git a/demos/ve/mobile.html b/demos/ve/mobile.html
index 210d3b4..47eeb5a 100644
--- a/demos/ve/mobile.html
+++ b/demos/ve/mobile.html
@@ -389,6 +389,7 @@
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.js"></script>
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.PlainTextFileTransferHandler.js"></script>
                <script 
src="../../src/ui/datatransferhandlers/ve.ui.HTMLFileTransferHandler.js"></script>
+               <script 
src="../../src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js"></script>
                <script 
src="../../src/ui/windowmanagers/ve.ui.ToolbarDialogWindowManager.js"></script>
                <script 
src="../../src/ui/widgets/ve.ui.AlignWidget.js"></script>
                <script 
src="../../src/ui/widgets/ve.ui.LanguageSearchWidget.js"></script>
diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index 7cf42a1..fb43731 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -1890,10 +1890,11 @@
  *
  * @param {jQuery.Event} e Paste event
  */
-ve.ce.Surface.prototype.afterPaste = function () {
+ve.ce.Surface.prototype.afterPaste = function ( e ) {
+       // jshint unused:false
        var clipboardKey, clipboardId, clipboardIndex, clipboardHash, range,
                $elements, parts, pasteData, slice, tx, internalListRange,
-               data, doc, htmlDoc, $images, i,
+               data, doc, htmlStr, htmlDoc, $images, i,
                context, left, right, contextRange,
                items = [],
                importantElement = '[id],[typeof],[rel]',
@@ -2041,7 +2042,8 @@
                        // If the clipboardKey is set (paste from other VE 
instance), and clipboard
                        // data is available, then make sure important spans 
haven't been dropped
                        if ( !$elements ) {
-                               $elements = $( $.parseHTML( 
beforePasteData.html ) );
+                               htmlStr = beforePasteData.html;
+                               $elements = $( $.parseHTML( htmlStr ) );
                        }
                        if (
                                // HACK: Allow the test runner to force the use 
of clipboardData
@@ -2051,7 +2053,8 @@
                                )
                        ) {
                                // CE destroyed an important element, so revert 
to using clipboard data
-                               htmlDoc = ve.createDocumentFromHtml( 
beforePasteData.html );
+                               htmlStr = beforePasteData.html;
+                               htmlDoc = ve.createDocumentFromHtml( htmlStr );
                                // Remove the pasteProtect class. See #onCopy.
                                $( htmlDoc ).find( 'span' ).removeClass( 
've-pasteProtect' );
                                beforePasteData.context = null;
@@ -2062,14 +2065,15 @@
                        // contain all sorts of horrible metadata (head tags 
etc.)
                        // TODO: IE will always take this path, and so may have 
bugs with span unwrapping
                        // in edge cases (e.g. pasting a single MWReference)
-                       htmlDoc = ve.createDocumentFromHtml( 
this.$pasteTarget.html() );
+                       htmlStr = this.$pasteTarget.html();
+                       htmlDoc = ve.createDocumentFromHtml( htmlStr );
                }
                // Some browsers don't provide pasted image data through the 
clipboardData API and
                // instead create img tags with data URLs, so detect those here
                $images = $( htmlDoc.body ).find( 'img[src^=data\\:]' );
                if ( $images.length ) {
                        for ( i = 0; i < $images.length; i++ ) {
-                               items.push( 
ve.ui.DataTransferItem.static.newFromDataUri( $images.eq( i ).attr( 'src' ) ) );
+                               items.push( 
ve.ui.DataTransferItem.static.newFromDataUri( $images.eq( i ).attr( 'src' ), 
htmlStr ) );
                        }
                        if ( this.handleDataTransferItems( items, true ) ) {
                                return;
@@ -2175,24 +2179,25 @@
 ve.ce.Surface.prototype.handleDataTransfer = function ( dataTransfer, isPaste, 
targetFragment ) {
        var i, l, stringData,
                items = [],
+               htmlStringData = dataTransfer.getData( 'text/html' ),
                stringTypes = [ 'text/x-moz-url', 'text/uri-list', 
'text/x-uri', 'text/html', 'text/plain' ];
 
        if ( dataTransfer.items ) {
                for ( i = 0, l = dataTransfer.items.length; i < l; i++ ) {
                        if ( dataTransfer.items[i].kind !== 'string' ) {
-                               items.push( 
ve.ui.DataTransferItem.static.newFromItem( dataTransfer.items[i] ) );
+                               items.push( 
ve.ui.DataTransferItem.static.newFromItem( dataTransfer.items[i], 
htmlStringData ) );
                        }
                }
        } else if ( dataTransfer.files ) {
                for ( i = 0, l = dataTransfer.files.length; i < l; i++ ) {
-                       items.push( ve.ui.DataTransferItem.static.newFromBlob( 
dataTransfer.files[i] ) );
+                       items.push( ve.ui.DataTransferItem.static.newFromBlob( 
dataTransfer.files[i], htmlStringData ) );
                }
        }
 
        for ( i = 0, l = stringTypes.length; i < stringTypes.length; i++ ) {
                stringData = dataTransfer.getData( stringTypes[i] );
                if ( stringData ) {
-                       items.push( 
ve.ui.DataTransferItem.static.newFromString( stringData, stringTypes[i] ) );
+                       items.push( 
ve.ui.DataTransferItem.static.newFromString( stringData, stringTypes[i], 
htmlStringData ) );
                }
        }
 
diff --git a/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js 
b/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js
new file mode 100644
index 0000000..159dcf5
--- /dev/null
+++ b/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js
@@ -0,0 +1,150 @@
+/*!
+ * VisualEditor UserInterface UrlStringTransferHandler class.
+ *
+ * @copyright 2011-2015 VisualEditor Team and others; see 
http://ve.mit-license.org
+ */
+
+/**
+ * Handle pastes and drag-and-drop of URLs and links.
+ * Attempts to preserve link titles when possible.
+ *
+ * @class
+ * @extends ve.ui.PlainTextStringTransferHandler
+ *
+ * @constructor
+ * @param {ve.ui.Surface} surface
+ * @param {ve.ui.DataTransferItem} item
+ */
+ve.ui.UrlStringTransferHandler = function VeUiUrlStringTransferHandler() {
+       // Parent constructor
+       ve.ui.UrlStringTransferHandler.super.apply( this, arguments );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.UrlStringTransferHandler, 
ve.ui.PlainTextStringTransferHandler );
+
+/* Static properties */
+
+ve.ui.UrlStringTransferHandler.static.name = 'urlString';
+
+ve.ui.UrlStringTransferHandler.static.types = [
+       // DnD standard, no title information
+       'text/uri-list',
+       // Firefox type, preserves title
+       'text/x-moz-url',
+       // Used in GNOME drag-and-drop
+       'text/x-uri'
+].concat(
+       // Identify links in pasted plain text as well
+       ve.ui.UrlStringTransferHandler.super.static.types
+);
+
+ve.ui.UrlStringTransferHandler.static.handlesPaste = true;
+
+/**
+ * RegExp matching an external url.
+ * @property {RegExp}
+ * @private
+ */
+ve.ui.UrlStringTransferHandler.static.urlRegExp = null; // Initialized below
+
+ve.init.Platform.static.initializedPromise.then( function () {
+       ve.ui.UrlStringTransferHandler.static.urlRegExp =
+               new RegExp(
+                       
ve.init.platform.getExternalLinkUrlProtocolsRegExp().source +
+                               '\\S+$',
+                       'i' // Protocols are case-insensitive
+               );
+} );
+
+/* Methods */
+
+ve.ui.UrlStringTransferHandler.static.matchFunction = function ( item ) {
+       // Match all specific mime types
+       if ( ve.ui.UrlStringTransferHandler.static.types.indexOf( item.type ) 
>= 0 &&
+               item.type !== 'text/plain' ) {
+               return true;
+       }
+
+       // If the type if unspecified or text/plain, then let's check whether it
+       // is a valid URL.
+       return ve.ui.UrlStringTransferHandler.static.urlRegExp.test(
+               item.getAsString()
+       );
+};
+
+/**
+ * @inheritdoc
+ */
+ve.ui.UrlStringTransferHandler.prototype.process = function () {
+       var links,
+               html,
+               doc,
+               result,
+               surface = this.surface,
+               linkAction = ve.ui.actionFactory.create( 'link', surface ),
+               data = this.item.getAsString();
+
+       switch ( this.item.type ) {
+
+               case 'text/uri-list':
+                       // text/uri-list has embedded comments; remove them 
before
+                       // autolinking.  In theory the embedded comments can be
+                       // used for link titles, but I've never seen this done 
by
+                       // real apps.  You could add code here to annotate the
+                       // links with the comment information if you can find a
+                       // spec for how it should be done.
+                       links = data.replace( /^#.*(\r\n?|\n|$)/mg, '' ).trim()
+                               .split( /[\r\n]+/g ).map( function ( line ) {
+                                       return { href: line, title: line };
+                               } );
+                       // When Google Chrome uses this mime type the link 
titles can
+                       // be extracted from the 'text/html' version of the 
item.
+                       // Let's try that.
+                       if ( this.item.data.htmlStringData ) {
+                               html = this.item.data.htmlStringData;
+                               doc = ve.createDocumentFromHtml( html );
+                               links = $.makeArray( doc.querySelectorAll( 
'a[href]' ) )
+                                       .map( function ( a ) {
+                                               return { href: a.href, title: 
a.textContent };
+                                       } );
+                       }
+                       break;
+
+               case 'text/x-moz-url':
+                       // text/x-moz-url includes titles with the links 
(alternating lines)
+                       links = data.match( /^(.*)(\r\n?|\n)(.*)$/mg ).map( 
function ( item ) {
+                               item = item.split( /[\r\n]+/ );
+                               return { href: item[0], title: item[1] };
+                       } );
+                       break;
+
+               default:
+                       // A single URL
+                       links = [ { href: data.trim(), title: data } ];
+                       break;
+       }
+
+       // Create linked text.
+       result = [];
+       links.forEach( function ( link ) {
+               var i,
+                       store = surface.getModel().getDocument().getStore(),
+                       annotation = linkAction.getLinkAnnotation( link.href ),
+                       annotationSet = new ve.dm.AnnotationSet( store, 
store.indexes( [
+                               annotation
+                       ] ) ),
+                       content = link.title.split( '' );
+               ve.dm.Document.static.addAnnotationsToData( content, 
annotationSet );
+               for ( i = 0; i < content.length; i++ ) {
+                       result.push( content[i] );
+               }
+               result.push( ' ' );
+       } );
+       this.resolve( result );
+};
+
+/* Registration */
+
+ve.ui.dataTransferHandlerFactory.register( ve.ui.UrlStringTransferHandler );
diff --git a/src/ui/ve.ui.DataTransferItem.js b/src/ui/ve.ui.DataTransferItem.js
index 0c85d6e..0475a93 100644
--- a/src/ui/ve.ui.DataTransferItem.js
+++ b/src/ui/ve.ui.DataTransferItem.js
@@ -9,6 +9,7 @@
  * @param {string} [data.dataUri] Data URI to convert to a blob
  * @param {Blob} [data.blob] File blob
  * @param {string} [data.stringData] String data
+ * @param {string} [data.htmlStringData] HTML string data
  * @param {DataTransferItem} [data.item] Native data transfer item
  * @param {string} [name] Item's name, for types which support it, e.g. File
  */
@@ -31,42 +32,47 @@
  * Create a data transfer item from a file blob.
  *
  * @param {Blob} blob File blob
+ * @param {string} [htmlStringData] HTML string representation of data transfer
  * @return {ve.ui.DataTransferItem} New data transfer item
  */
-ve.ui.DataTransferItem.static.newFromBlob = function ( blob ) {
-       return new ve.ui.DataTransferItem( 'file', blob.type, { blob: blob }, 
blob.name );
+ve.ui.DataTransferItem.static.newFromBlob = function ( blob, htmlStringData ) {
+       return new ve.ui.DataTransferItem( 'file', blob.type, { blob: blob, 
htmlStringData: htmlStringData }, blob.name );
 };
 
 /**
  * Create a data transfer item from a data URI.
  *
  * @param {string} dataUri Data URI
+ * @param {string} [htmlStringData] HTML string representation of data transfer
  * @return {ve.ui.DataTransferItem} New data transfer item
  */
-ve.ui.DataTransferItem.static.newFromDataUri = function ( dataUri ) {
+ve.ui.DataTransferItem.static.newFromDataUri = function ( dataUri, 
htmlStringData ) {
        var parts = dataUri.split( ',' );
-       return new ve.ui.DataTransferItem( 'file', parts[0].match( 
/^data:([^;]+)/ )[1], { dataUri: parts[1] } );
+       return new ve.ui.DataTransferItem( 'file', parts[0].match( 
/^data:([^;]+)/ )[1], { dataUri: parts[1], htmlStringData: htmlStringData } );
 };
 
 /**
  * Create a data transfer item from string data.
  *
- * @param {string} stringData String data
- * @param {string} type MIME type
+ * @param {DataTransfer} dataTransfer Native data transfer object
+ * @param {string} stringData Native string data
+ * @param {string} type Native MIME type
+ * @param {string} [htmlStringData] HTML string representation of data transfer
  * @return {ve.ui.DataTransferItem} New data transfer item
  */
-ve.ui.DataTransferItem.static.newFromString = function ( stringData, type ) {
-       return new ve.ui.DataTransferItem( 'string', type || 'text/plain', { 
stringData: stringData } );
+ve.ui.DataTransferItem.static.newFromString = function ( stringData, type, 
htmlStringData ) {
+       return new ve.ui.DataTransferItem( 'string', type || 'text/plain', { 
stringData: stringData, htmlStringData: htmlStringData } );
 };
 
 /**
  * Create a data transfer item from a native data transfer item.
  *
  * @param {DataTransferItem} item Native data transfer item
+ * @param {string} [htmlStringData] HTML string representation of data transfer
  * @return {ve.ui.DataTransferItem} New data transfer item
  */
-ve.ui.DataTransferItem.static.newFromItem = function ( item ) {
-       return new ve.ui.DataTransferItem( item.kind, item.type, { item: item 
}, item.getAsFile().name );
+ve.ui.DataTransferItem.static.newFromItem = function ( item, htmlStringData ) {
+       return new ve.ui.DataTransferItem( item.kind, item.type, { item: item, 
htmlStringData: htmlStringData }, item.getAsFile().name );
 };
 
 /**
diff --git a/tests/ce/ve.ce.Surface.test.js b/tests/ce/ve.ce.Surface.test.js
index 9bc6460..6ee0aa5 100644
--- a/tests/ce/ve.ce.Surface.test.js
+++ b/tests/ce/ve.ce.Surface.test.js
@@ -1138,7 +1138,7 @@
                } else {
                        document.execCommand( 'insertHTML', false, pasteHtml );
                }
-               view.afterPaste();
+               view.afterPaste( new TestEvent( e ) );
 
                if ( expectedOps ) {
                        ops = [];
diff --git a/tests/index.html b/tests/index.html
index c5f0a59..ffbafbe 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -316,6 +316,7 @@
                <script 
src="../src/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.js"></script>
                <script 
src="../src/ui/datatransferhandlers/ve.ui.PlainTextFileTransferHandler.js"></script>
                <script 
src="../src/ui/datatransferhandlers/ve.ui.HTMLFileTransferHandler.js"></script>
+               <script 
src="../src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js"></script>
                <script 
src="../src/ui/windowmanagers/ve.ui.ToolbarDialogWindowManager.js"></script>
                <script src="../src/ui/widgets/ve.ui.AlignWidget.js"></script>
                <script 
src="../src/ui/widgets/ve.ui.LanguageSearchWidget.js"></script>
@@ -415,6 +416,7 @@
                <script 
src="../tests/ui/actions/ve.ui.ListAction.test.js"></script>
                <script 
src="../tests/ui/actions/ve.ui.TableAction.test.js"></script>
                <script 
src="../tests/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.test.js"></script>
+               <script 
src="../tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js"></script>
                <script 
src="../tests/ui/dialogs/ve.ui.FindAndReplaceDialog.test.js"></script>
                <script src="../tests/ce/ve.ce.TestRunner.js"></script>
                <script src="../tests/ce/ve.ce.imetests.test.js"></script>
diff --git 
a/tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js 
b/tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js
new file mode 100644
index 0000000..8b360e0
--- /dev/null
+++ b/tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js
@@ -0,0 +1,110 @@
+/*!
+ * VisualEditor UserInterface UrlStringTransferHandler tests.
+ *
+ * @copyright 2011-2015 VisualEditor Team and others; see 
http://ve.mit-license.org
+ */
+
+QUnit.module( 've.ui.UrlStringTransferHandler' );
+
+/* Tests */
+
+function runUrlStringHandlerTest( assert, string, mimeType, expectedData ) {
+       var handler,
+               done = assert.async(),
+               item = ve.ui.DataTransferItem.static.newFromString( string, 
mimeType ),
+               doc = ve.dm.example.createExampleDocument(),
+               mockSurface = {
+                       getModel: function () {
+                               return {
+                                       getDocument: function () {
+                                               return doc;
+                                       }
+                               };
+                       },
+                       getImportRules: function () {
+                               return ve.init.mw.Target.static.importRules;
+                       }
+               },
+               linkAction = ve.ui.actionFactory.create( 'link', mockSurface ),
+               makeLinkAnnotation = function ( href ) {
+                       return linkAction.getLinkAnnotation( href ).element;
+               };
+
+       handler = ve.ui.dataTransferHandlerFactory.create( 'urlString', 
mockSurface, item );
+
+       handler.getInsertableData().done( function ( actualData ) {
+               ve.dm.example.postprocessAnnotations( actualData, 
doc.getStore() );
+               assert.equalLinearData( actualData, expectedData( 
makeLinkAnnotation ), 'data match' );
+               done();
+       } );
+}
+
+QUnit.test( 'simple external link', 1, function ( assert ) {
+       runUrlStringHandlerTest( assert, 'http://example.com', 'text/plain', 
function ( makeAnnotation ) {
+               var a = makeAnnotation( 'http://example.com' );
+               return [
+                       [ 'h', [ a ] ],
+                       [ 't', [ a ] ],
+                       [ 't', [ a ] ],
+                       [ 'p', [ a ] ],
+                       [ ':', [ a ] ],
+                       [ '/', [ a ] ],
+                       [ '/', [ a ] ],
+                       [ 'e', [ a ] ],
+                       [ 'x', [ a ] ],
+                       [ 'a', [ a ] ],
+                       [ 'm', [ a ] ],
+                       [ 'p', [ a ] ],
+                       [ 'l', [ a ] ],
+                       [ 'e', [ a ] ],
+                       [ '.', [ a ] ],
+                       [ 'c', [ a ] ],
+                       [ 'o', [ a ] ],
+                       [ 'm', [ a ] ],
+                       ' '
+               ];
+       } );
+} );
+
+QUnit.test( 'DnD standard URI list', 1, function ( assert ) {
+       runUrlStringHandlerTest( assert, '#comment\nhttp://example.com\n', 
'text/uri-list', function ( makeAnnotation ) {
+               var a = makeAnnotation( 'http://example.com' );
+               return [
+                       [ 'h', [ a ] ],
+                       [ 't', [ a ] ],
+                       [ 't', [ a ] ],
+                       [ 'p', [ a ] ],
+                       [ ':', [ a ] ],
+                       [ '/', [ a ] ],
+                       [ '/', [ a ] ],
+                       [ 'e', [ a ] ],
+                       [ 'x', [ a ] ],
+                       [ 'a', [ a ] ],
+                       [ 'm', [ a ] ],
+                       [ 'p', [ a ] ],
+                       [ 'l', [ a ] ],
+                       [ 'e', [ a ] ],
+                       [ '.', [ a ] ],
+                       [ 'c', [ a ] ],
+                       [ 'o', [ a ] ],
+                       [ 'm', [ a ] ],
+                       ' '
+               ];
+       } );
+} );
+
+QUnit.test( 'Mozilla URI list', 1, function ( assert ) {
+       runUrlStringHandlerTest( assert, 'http://example.com\n[[Foo]]', 
'text/x-moz-url', function ( makeAnnotation ) {
+               var a = makeAnnotation( 'http://example.com' );
+               return [
+                       [ '[', [ a ] ],
+                       [ '[', [ a ] ],
+                       [ 'F', [ a ] ],
+                       [ 'o', [ a ] ],
+                       [ 'o', [ a ] ],
+                       [ ']', [ a ] ],
+                       [ ']', [ a ] ],
+                       ' '
+               ];
+       } );
+} );

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I80bc95e436563f797849ebd0204e04ae0e277308
Gerrit-PatchSet: 6
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Cscott <[email protected]>
Gerrit-Reviewer: Cscott <[email protected]>
Gerrit-Reviewer: Eranroz <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to