Cscott has uploaded a new change for review.

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

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

Autolink pasted links.

Requires I76f7ab81fdf3cafd551b9f04b19e0074a4d4b701 in order to link
text/plain pastes.

Adds a pointer to the original DataTransfer object in each DataTransferItem
so that we can consult additional representations in our DataTransferHandler.

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
8 files changed, 171 insertions(+), 14 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/45/231145/1

diff --git a/build/modules.json b/build/modules.json
index 02718c4..78050c6 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",
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..bbee4f3 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -1890,11 +1890,12 @@
  *
  * @param {jQuery.Event} e Paste event
  */
-ve.ce.Surface.prototype.afterPaste = function () {
+ve.ce.Surface.prototype.afterPaste = function ( e ) {
        var clipboardKey, clipboardId, clipboardIndex, clipboardHash, range,
                $elements, parts, pasteData, slice, tx, internalListRange,
                data, doc, htmlDoc, $images, i,
                context, left, right, contextRange,
+               clipboardData = e.originalEvent.clipboardData,
                items = [],
                importantElement = '[id],[typeof],[rel]',
                importRules = !this.pasteSpecial ? 
this.getSurface().getImportRules() : { all: { plainText: true } },
@@ -2069,7 +2070,7 @@
                $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( clipboardData, $images.eq( i 
).attr( 'src' ) ) );
                        }
                        if ( this.handleDataTransferItems( items, true ) ) {
                                return;
@@ -2180,19 +2181,19 @@
        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, dataTransfer.items[i] 
) );
                        }
                }
        } 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, dataTransfer.files[i] ) );
                }
        }
 
        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( dataTransfer, stringData, 
stringTypes[i] ) );
                }
        }
 
diff --git a/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js 
b/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js
new file mode 100644
index 0000000..f5a4272
--- /dev/null
+++ b/src/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.js
@@ -0,0 +1,147 @@
+/*!
+ * 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',
+       // Identify links in pasted plain text as well
+       'text/plain'
+];
+
+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+$'
+               );
+} );
+
+/* 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,
+               htmldoc,
+               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 ( ve.getProp( this.item.data, 'dataTransfer', 'getData' ) ) {
+                       htmldoc = this.item.data.dataTransfer.getData( 
'text/html' );
+                       if ( htmldoc ) {
+                               htmldoc = ve.createDocumentFromHtml( htmldoc );
+                               links = $.makeArray( htmldoc.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
+               // Use 'trim' to eliminate trailing newline, if present
+               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..bccd3c2 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 {DataTransfer} [data.dataTransfer] Native data transfer object
  * @param {DataTransferItem} [data.item] Native data transfer item
  * @param {string} [name] Item's name, for types which support it, e.g. File
  */
@@ -30,43 +31,47 @@
 /**
  * Create a data transfer item from a file blob.
  *
+ * @param {DataTransfer} dataTransfer Native data transfer object
  * @param {Blob} blob File blob
  * @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 ( dataTransfer, blob ) {
+       return new ve.ui.DataTransferItem( 'file', blob.type, { dataTransfer: 
dataTransfer, blob: blob }, blob.name );
 };
 
 /**
  * Create a data transfer item from a data URI.
  *
+ * @param {DataTransfer} dataTransfer Native data transfer object
  * @param {string} dataUri Data URI
  * @return {ve.ui.DataTransferItem} New data transfer item
  */
-ve.ui.DataTransferItem.static.newFromDataUri = function ( dataUri ) {
+ve.ui.DataTransferItem.static.newFromDataUri = function ( dataTransfer, 
dataUri ) {
        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], { dataTransfer: dataTransfer, dataUri: parts[1] } );
 };
 
 /**
  * Create a data transfer item from string data.
  *
+ * @param {DataTransfer} dataTransfer Native data transfer object
  * @param {string} stringData String data
  * @param {string} type MIME type
  * @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 ( dataTransfer, 
stringData, type ) {
+       return new ve.ui.DataTransferItem( 'string', type || 'text/plain', { 
dataTransfer: dataTransfer, stringData: stringData } );
 };
 
 /**
  * Create a data transfer item from a native data transfer item.
  *
+ * @param {DataTransfer} dataTransfer Native data transfer object
  * @param {DataTransferItem} item Native data transfer item
  * @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 ( dataTransfer, item ) {
+       return new ve.ui.DataTransferItem( item.kind, item.type, { 
dataTransfer: dataTransfer, item: item }, 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 bef18fc..875f9fc 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>

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I80bc95e436563f797849ebd0204e04ae0e277308
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Cscott <[email protected]>

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

Reply via email to