Trevor Parscal has uploaded a new change for review.

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


Change subject: Support for drag and drop of image nodes
......................................................................

Support for drag and drop of image nodes

ve.ce.ImageNode.js
* Added $image reference to the actual img element, so if it's wrapped in a sub 
class the functionality in the parent class  doesn't break
* Removed drag end binding, not needed
* Fixed indentation (space bad, tab good)
* Added implementation for drag start, which tells the surface to allow 
dragging this node

ve.ce.MWImageNode.js
* Moved addClass to initialization section of constructor
* Copied 'view' data prop from image element to keep stuff working after the 
wrapping

ve.ce.Node.css
* Switched to default (arrow) cursor for images

ve.ce.js
* Added check for instance of node when getting an offset from what we think 
might be a node
* Added selectPoint method, which allows us to set selection using graphical 
coordinates

ve.ce.Surface.js
* Added nodeDragging property, used by startDraggingNode, onDocumentDragOver 
and onDocumentDrop to keep track of node dragging state
* Renamed dragging property to mouseDragging to make it less confusing
* Split onDocumentDragDrop into onDocumentDragOver and onDocumentDrop which now 
have implementation for dragging nodes

Change-Id: I8703adfb707af2c3224431afc3418356ac2c686c
---
M modules/ve/ce/nodes/ve.ce.ImageNode.js
M modules/ve/ce/nodes/ve.ce.MWImageNode.js
M modules/ve/ce/styles/ve.ce.Node.css
M modules/ve/ce/ve.ce.Surface.js
M modules/ve/ce/ve.ce.js
5 files changed, 125 insertions(+), 34 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor 
refs/changes/49/58649/1

diff --git a/modules/ve/ce/nodes/ve.ce.ImageNode.js 
b/modules/ve/ce/nodes/ve.ce.ImageNode.js
index 55966be..3725fee 100644
--- a/modules/ve/ce/nodes/ve.ce.ImageNode.js
+++ b/modules/ve/ce/nodes/ve.ce.ImageNode.js
@@ -17,17 +17,19 @@
        // Parent constructor
        ve.ce.LeafNode.call( this, model, $( '<img>' ) );
 
+       // Properties
+       this.$image = this.$;
+
        // Events
        this.model.addListenerMethod( this, 'update', 'onUpdate' );
-       this.$.on( {
+       this.$image.on( {
                'click': ve.bind( this.onClick, this ),
-               'dragstart': ve.bind( this.onDragStart, this ),
-               'dragend': ve.bind( this.onDragEnd, this )
+               'dragstart': ve.bind( this.onDragStart, this )
        } );
 
        // Initialization
-       ve.setDomAttributes( this.$[0], this.model.getAttributes(), ['src', 
'width', 'height'] );
-       this.$.addClass( 've-ce-imageNode' );
+       ve.setDomAttributes( this.$image[0], this.model.getAttributes(), 
['src', 'width', 'height'] );
+       this.$image.addClass( 've-ce-imageNode' );
 };
 
 /* Inheritance */
@@ -48,8 +50,8 @@
  */
 ve.ce.ImageNode.prototype.onClick = function ( e ) {
        var range,
-           surfaceModel = this.getRoot().getSurface().getModel(),
-           selection = surfaceModel.getSelection();
+               surfaceModel = this.getRoot().getSurface().getModel(),
+               selection = surfaceModel.getSelection();
 
        range = new ve.Range(
                this.model.getOffset(),
@@ -70,17 +72,8 @@
  * @param {jQuery.Event} e Dragstart event
  */
 ve.ce.ImageNode.prototype.onDragStart = function () {
-       return false;
-};
-
-/**
- * Handle the dragend.
- *
- * @method
- * @param {jQuery.Event} e Dragstart event
- */
-ve.ce.ImageNode.prototype.onDragEnd = function () {
-       return false;
+       // Allow dragging this node in the surface
+       this.getRoot().getSurface().startDraggingNode( this );
 };
 
 /* Registration */
diff --git a/modules/ve/ce/nodes/ve.ce.MWImageNode.js 
b/modules/ve/ce/nodes/ve.ce.MWImageNode.js
index ede3591..0fea716 100644
--- a/modules/ve/ce/nodes/ve.ce.MWImageNode.js
+++ b/modules/ve/ce/nodes/ve.ce.MWImageNode.js
@@ -17,13 +17,16 @@
        // Parent constructor
        ve.ce.ImageNode.call( this, model );
 
-       // Initialization
-       this.$.addClass( 've-ce-MWImageNode' );
+       // Properties
        this.$image = this.$;
        this.$ = $( '<' + ( model.getAttribute( 'isLinked' ) ? 'a' : 'span' ) + 
'>' );
 
        // Initialization
-       this.$.attr( 'contenteditable', false ).append( this.$image );
+       this.$
+               .attr( 'contenteditable', false )
+               .addClass( 've-ce-mwImageNode' )
+               .append( this.$image )
+               .data( 'view', this.$image.data( 'view' ) );
        this.onUpdate();
 };
 
diff --git a/modules/ve/ce/styles/ve.ce.Node.css 
b/modules/ve/ce/styles/ve.ce.Node.css
index 9591cde..5491a5b 100644
--- a/modules/ve/ce/styles/ve.ce.Node.css
+++ b/modules/ve/ce/styles/ve.ce.Node.css
@@ -16,6 +16,10 @@
        right: 0 !important;
 }
 
+.ve-ce-imageNode {
+       cursor: default;
+}
+
 .ve-ce-alienNode {
        z-index: 0;
 }
diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js
index 75eb836..ab25609 100644
--- a/modules/ve/ce/ve.ce.Surface.js
+++ b/modules/ve/ce/ve.ce.Surface.js
@@ -31,7 +31,8 @@
        this.$document = $( document );
        this.clipboard = {};
        this.renderingEnabled = true;
-       this.dragging = false;
+       this.mouseDragging = false;
+       this.nodeDragging = false;
        this.selecting = false;
        this.$phantoms = $( '<div>' );
        this.$pasteTarget = $( '<div>' );
@@ -53,7 +54,8 @@
                'cut': ve.bind( this.onCut, this ),
                'copy': ve.bind( this.onCopy, this ),
                'paste': ve.bind( this.onPaste, this ),
-               'dragover drop': ve.bind( this.onDocumentDragoverDrop, this )
+               'dragover': ve.bind( this.onDocumentDragOver, this ),
+               'drop': ve.bind( this.onDocumentDrop, this )
        } );
        if ( $.browser.msie ) {
                this.$.on( 'beforepaste', ve.bind( this.onPaste, this ) );
@@ -200,7 +202,7 @@
 ve.ce.Surface.prototype.documentOnBlur = function () {
        this.$document.off( '.ve-ce-Surface' );
        this.surfaceObserver.stop( true );
-       this.dragging = false;
+       this.mouseDragging = false;
 };
 
 /**
@@ -211,7 +213,7 @@
  */
 ve.ce.Surface.prototype.onDocumentMouseDown = function ( e ) {
        // Remember the mouse is down
-       this.dragging = true;
+       this.mouseDragging = true;
 
        // Old code to figure out if user clicked inside the document or not - 
leave it here for now
        // $( e.target ).closest( '.ve-ce-documentNode' ).length === 0
@@ -239,7 +241,7 @@
                this.emit( 'selectionEnd' );
                this.selecting = false;
        }
-       this.dragging = false;
+       this.mouseDragging = false;
 };
 
 /**
@@ -251,21 +253,58 @@
  */
 ve.ce.Surface.prototype.onDocumentMouseMove = function () {
        // Detect beginning of selection by moving mouse while dragging
-       if ( this.dragging && !this.selecting ) {
+       if ( this.mouseDragging && !this.selecting ) {
                this.selecting = true;
                this.emit( 'selectionStart' );
        }
 };
 
 /**
- * Handle document dragover and drop events.
+ * Handle document dragover events.
  *
- * Prevents native dragging and dropping of content.
+ * Limits native drag and drop behavior.
  *
  * @method
- * @param {jQuery.Event} e Drag over/drop event
+ * @param {jQuery.Event} e Drag over event
  */
-ve.ce.Surface.prototype.onDocumentDragoverDrop = function () {
+ve.ce.Surface.prototype.onDocumentDragOver = function () {
+       if ( !this.nodeDragging ) {
+               return false;
+       }
+};
+
+/**
+ * Handle document drop events.
+ *
+ * Limits native drag and drop behavior.
+ *
+ * @method
+ * @param {jQuery.Event} e Drag drop event
+ */
+ve.ce.Surface.prototype.onDocumentDrop = function ( e ) {
+       var sel, data, originFragment, targetFragment;
+
+       if ( this.nodeDragging ) {
+               setTimeout( ve.bind( function () {
+                       // Get a fragment at the drop point
+                       ve.ce.selectPoint( e.originalEvent.pageX, 
e.originalEvent.pageY );
+                       sel = rangy.getSelection();
+                       targetFragment = this.model.getFragment(
+                               new ve.Range( ve.ce.getOffset( sel.anchorNode, 
sel.anchorOffset ) )
+                       );
+
+                       // Remove node from old location
+                       originFragment = this.model.getFragment( 
this.nodeDragging.getModel().getOuterRange() );
+                       data = originFragment.getData();
+                       originFragment.removeContent();
+
+                       // Insert node at new location
+                       targetFragment.insertContent( data );
+
+                       this.nodeDragging = null;
+               }, this ) );
+       }
+
        return false;
 };
 
@@ -291,7 +330,7 @@
 
        if ( ve.ce.isArrowKey( e.keyCode ) ) {
                // Detect start of selecting using shift+arrow keys.
-               if ( !this.dragging && !this.selecting && e.shiftKey ) {
+               if ( !this.mouseDragging && !this.selecting && e.shiftKey ) {
                        this.selecting = true;
                        this.emit( 'selectionStart' );
                }
@@ -366,7 +405,7 @@
  */
 ve.ce.Surface.prototype.onDocumentKeyUp = function ( e ) {
        // Detect end of selecting by letting go of shift
-       if ( !this.dragging && this.selecting && e.keyCode === 16 ) {
+       if ( !this.mouseDragging && this.selecting && e.keyCode === 16 ) {
                this.selecting = false;
                this.emit( 'selectionEnd' );
        }
@@ -707,6 +746,17 @@
        this.surfaceObserver.start();
 };
 
+/* Content Drag and Drop */
+
+/**
+ * Begin a drag and drop action for a node.
+ *
+ * @method
+ */
+ve.ce.Surface.prototype.startDraggingNode = function ( node ) {
+       this.nodeDragging = node;
+};
+
 /*! Utilities */
 
 /**
diff --git a/modules/ve/ce/ve.ce.js b/modules/ve/ce/ve.ce.js
index 1c7511f..56292e2 100644
--- a/modules/ve/ce/ve.ce.js
+++ b/modules/ve/ce/ve.ce.js
@@ -217,7 +217,7 @@
 
        if ( domOffset === 0 ) {
                node = $domNode.data( 'view' );
-               if ( node ) {
+               if ( node && node instanceof ve.ce.Node ) {
                        nodeModel = $domNode.data( 'view' ).getModel();
                        if ( addOuterLength === true ) {
                                return nodeModel.getOffset() + 
nodeModel.getOuterLength();
@@ -280,3 +280,44 @@
 ve.ce.isShortcutKey = function ( e ) {
        return e.ctrlKey || e.metaKey;
 };
+
+/**
+ * Set selection from graphical coordinates.
+ *
+ * Compatibility of this method is not gauranteed, as it depends on either 
caretPositionFromPoint,
+ * caretRangeFromPoint or moveToPoint native range methods.
+ *
+ * TODO: This functionality would be nice if it were in rangy.
+ *
+ * @method
+ * @param {number} x Horizontal position
+ * @param {number} y Vertical position
+ * @param {HTMLDocument} [doc] Document to select within
+ * @returns {boolean} Selection succeeded
+ */
+ve.ce.selectPoint = function ( x, y, doc ) {
+       var sel, range;
+
+       doc = doc || document;
+       if ( window.getSelection ) {
+               // Sane browsers
+               if ( doc.caretPositionFromPoint ) {
+                       // Firefox / W3C
+                       range = doc.caretPositionFromPoint( x, y );
+               } else if ( doc.caretRangeFromPoint ) {
+                       // Webkit
+                       range = doc.caretRangeFromPoint( x, y );
+               }
+               sel = window.getSelection();
+               sel.removeAllRanges();
+               sel.addRange( range );
+               return true;
+       } else if ( doc.body.createTextRange ) {
+               // IE / Opera
+               range = doc.body.createTextRange();
+               range.moveToPoint( x, y );
+               range.select();
+               return true;
+       }
+       return false;
+};

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8703adfb707af2c3224431afc3418356ac2c686c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>

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

Reply via email to