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