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

Change subject: Allow node relocation
......................................................................


Allow node relocation

*.php
* Added links to new file

ve.ce.ImageNode.js
* Added relocatable node mixin
* 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.
* Moved drag start event handling to relocatable node
* Removed drag end binding, not needed.

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.RelocatableNode.js
* New mixing for nodes that should be relocatable
* Added implementation for drag start, which tells the surface to allow
  dragging this node.

ve.ce.Surface.js
* Added relocation support, which is used by relocatable nodes
* Split onDocumentDragDrop into onDocumentDragOver and onDocumentDrop
  which now have implementations that support relocation of nodes

ve.ui.Context.js
* Added relocation tracking to prevent context being shown while
  relocating

Change-Id: I8703adfb707af2c3224431afc3418356ac2c686c
---
M VisualEditor.php
M demos/ve/index.php
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
A modules/ve/ce/ve.ce.RelocatableNode.js
M modules/ve/ce/ve.ce.Surface.js
M modules/ve/ce/ve.ce.js
M modules/ve/test/index.php
M modules/ve/ui/ve.ui.Context.js
10 files changed, 219 insertions(+), 53 deletions(-)

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



diff --git a/VisualEditor.php b/VisualEditor.php
index a3f5a96..0f43909 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -303,6 +303,7 @@
                        've/ce/ve.ce.ContentBranchNode.js',
                        've/ce/ve.ce.LeafNode.js',
                        've/ce/ve.ce.FocusableNode.js',
+                       've/ce/ve.ce.RelocatableNode.js',
                        've/ce/ve.ce.Surface.js',
                        've/ce/ve.ce.SurfaceObserver.js',
 
diff --git a/demos/ve/index.php b/demos/ve/index.php
index fecb8fe..ee66e05 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -182,6 +182,7 @@
                <script 
src="../../modules/ve/ce/ve.ce.ContentBranchNode.js"></script>
                <script src="../../modules/ve/ce/ve.ce.LeafNode.js"></script>
                <script 
src="../../modules/ve/ce/ve.ce.FocusableNode.js"></script>
+               <script 
src="../../modules/ve/ce/ve.ce.RelocatableNode.js"></script>
                <script src="../../modules/ve/ce/ve.ce.Surface.js"></script>
                <script 
src="../../modules/ve/ce/ve.ce.SurfaceObserver.js"></script>
                <script 
src="../../modules/ve/ce/nodes/ve.ce.GeneratedContentNode.js"></script>
diff --git a/modules/ve/ce/nodes/ve.ce.ImageNode.js 
b/modules/ve/ce/nodes/ve.ce.ImageNode.js
index 503eaec..6c32e9c 100644
--- a/modules/ve/ce/nodes/ve.ce.ImageNode.js
+++ b/modules/ve/ce/nodes/ve.ce.ImageNode.js
@@ -11,6 +11,7 @@
  * @class
  * @extends ve.ce.LeafNode
  * @mixins ve.ce.FocusableNode
+ * @mixins ve.ce.RelocatableNode
  *
  * @constructor
  * @param {ve.dm.ImageNode} model Model to observe
@@ -21,18 +22,18 @@
 
        // Mixin constructors
        ve.ce.FocusableNode.call( this );
+       ve.ce.RelocatableNode.call( this );
+
+       // Properties
+       this.$image = this.$;
 
        // Events
        this.model.addListenerMethod( this, 'update', 'onUpdate' );
-       this.$.on( {
-               'click': ve.bind( this.onClick, this ),
-               'dragstart': ve.bind( this.onDragStart, this ),
-               'dragend': ve.bind( this.onDragEnd, this )
-       } );
+       this.$image.on( 'click', ve.bind( this.onClick, 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 */
@@ -40,6 +41,7 @@
 ve.inheritClass( ve.ce.ImageNode, ve.ce.LeafNode );
 
 ve.mixinClass( ve.ce.ImageNode, ve.ce.FocusableNode );
+ve.mixinClass( ve.ce.ImageNode, ve.ce.RelocatableNode );
 
 /* Static Properties */
 
@@ -54,40 +56,17 @@
  * @param {jQuery.Event} e Click event
  */
 ve.ce.ImageNode.prototype.onClick = function ( e ) {
-       var range,
-           surfaceModel = this.getRoot().getSurface().getModel(),
-           selection = surfaceModel.getSelection();
+       var surfaceModel = this.getRoot().getSurface().getModel(),
+               selectionRange = surfaceModel.getSelection(),
+               nodeRange = this.model.getOuterRange();
 
-       range = new ve.Range(
-               this.model.getOffset(),
-               this.model.getOffset() + this.model.getOuterLength()
-       );
-
-       if ( e.shiftKey ) {
-               range = ve.Range.newCoveringRange( [ selection, range ], 
selection.from > range.from );
-       }
-
-       this.getRoot().getSurface().getModel().change( null, range );
-};
-
-/**
- * Handle the dragstart.
- *
- * @method
- * @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;
+       surfaceModel.getFragment(
+               e.shiftKey ?
+                       ve.Range.newCoveringRange(
+                               [ selectionRange, nodeRange ], 
selectionRange.from > nodeRange.from
+                       ) :
+                       nodeRange
+       ).select();
 };
 
 /* 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 47d5686..37f3010 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.RelocatableNode.js 
b/modules/ve/ce/ve.ce.RelocatableNode.js
new file mode 100644
index 0000000..e51abba
--- /dev/null
+++ b/modules/ve/ce/ve.ce.RelocatableNode.js
@@ -0,0 +1,58 @@
+/*!
+ * VisualEditor ContentEditable RelocatableNode class.
+ *
+ * @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * ContentEditable relocatable node.
+ *
+ * @class
+ * @abstract
+ *
+ * @constructor
+ * @param {jQuery} [$draggable=this.$] Draggable DOM element
+ */
+ve.ce.RelocatableNode = function VeCeRelocatableNode( $draggable ) {
+       // Properties
+       this.$draggable = $draggable || this.$;
+       this.surface = null;
+
+       // Events
+       this.$draggable.on( {
+               'dragstart': ve.bind( this.onRelocatableDragStart, this ),
+               'dragend': ve.bind( this.onRelocatableDragEnd, this )
+       } );
+};
+
+/* Methods */
+
+/**
+ * Handle element drag start.
+ *
+ * @method
+ * @param {jQuery.Event} e Drag start event
+ */
+ve.ce.RelocatableNode.prototype.onRelocatableDragStart = function () {
+       // Store a copy of the surface, when dragend occurs the node will be 
detached
+       this.surface = this.getRoot().getSurface();
+
+       if ( this.surface ) {
+               // Allow dragging this node in the surface
+               this.surface.startRelocation( this );
+       }
+};
+
+/**
+ * Handle element drag end.
+ *
+ * @method
+ * @param {jQuery.Event} e Drag end event
+ */
+ve.ce.RelocatableNode.prototype.onRelocatableDragEnd = function () {
+       if ( this.surface ) {
+               this.surface.endRelocation();
+               this.surface = null;
+       }
+};
diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js
index 68b899b..fbf1f0c 100644
--- a/modules/ve/ce/ve.ce.Surface.js
+++ b/modules/ve/ce/ve.ce.Surface.js
@@ -32,6 +32,7 @@
        this.clipboard = {};
        this.renderingEnabled = true;
        this.dragging = false;
+       this.relocating = false;
        this.selecting = false;
        this.$phantoms = $( '<div>' );
        this.$pasteTarget = $( '<div>' );
@@ -54,7 +55,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 ) );
@@ -81,6 +83,14 @@
 
 /**
  * @event selectionEnd
+ */
+
+/**
+ * @event relocationStart
+ */
+
+/**
+ * @event relocationEnd
  */
 
 /* Static Properties */
@@ -259,14 +269,70 @@
 };
 
 /**
- * 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.relocating ) {
+               return false;
+       } else if ( this.selecting ) {
+               this.emit( 'selectionEnd' );
+               this.selecting = false;
+               this.dragging = false;
+       }
+};
+
+/**
+ * Handle document drop events.
+ *
+ * Limits native drag and drop behavior.
+ *
+ * TODO: Look into using drag and drop data transfer to embed the dragged 
element's original range
+ * (for dragging within one document) and serialized linear model data (for 
dragging between
+ * multiple documents) and use a special mimetype, like 
application-x/VisualEditor, to allow
+ * dragover and drop events on the surface, removing the need to give the 
surface explicit
+ * instructions to allow and prevent dragging and dropping a certain node.
+ *
+ * @method
+ * @param {jQuery.Event} e Drag drop event
+ */
+ve.ce.Surface.prototype.onDocumentDrop = function ( e ) {
+       var node = this.relocating;
+
+       if ( node ) {
+               // Process drop operation after native drop has been prevented 
below
+               setTimeout( ve.bind( function () {
+                       var dropPoint, nodeData, originFragment, targetFragment,
+                               nodeRange = node.getModel().getOuterRange();
+
+                       // Get a fragment from the drop point
+                       dropPoint = rangy.positionFromPoint( 
e.originalEvent.pageX, e.originalEvent.pageY );
+                       if ( !dropPoint ) {
+                               // Getting position from point supported
+                               return false;
+                       }
+                       targetFragment = this.model.getFragment(
+                               new ve.Range( ve.ce.getOffset( dropPoint.node, 
dropPoint.offset ) ), false
+                       );
+
+                       // Get a fragment and data of the node being dragged
+                       originFragment = this.model.getFragment( nodeRange, 
false );
+                       nodeData = originFragment.getData();
+
+                       // Remove node from old location (auto-updates 
targetFragment's range)
+                       originFragment.removeContent().destroy();
+
+                       // Re-insert node at new location and re-select it
+                       targetFragment.insertContent( nodeData );
+                       targetFragment.adjustRange( -nodeData.length, 0 
).select().destroy();
+                       targetFragment.destroy();
+               }, this ) );
+       }
+
        return false;
 };
 
@@ -735,6 +801,34 @@
        this.surfaceObserver.start();
 };
 
+/*! Relocation */
+
+/**
+ * Start a relocation action.
+ *
+ * @see ve.ce.RelocatableNode
+ *
+ * @method
+ * @param {ve.ce.Node} node Node being relocated
+ */
+ve.ce.Surface.prototype.startRelocation = function ( node ) {
+       this.relocating = node;
+       this.emit( 'relocationStart', node );
+};
+
+/**
+ * Complete a relocation action.
+ *
+ * @see ve.ce.RelocatableNode
+ *
+ * @method
+ * @param {ve.ce.Node} node Node being relocated
+ */
+ve.ce.Surface.prototype.endRelocation = function () {
+       this.emit( 'relocationEnd', this.relocating );
+       this.relocating = null;
+};
+
 /*! Utilities */
 
 /**
diff --git a/modules/ve/ce/ve.ce.js b/modules/ve/ce/ve.ce.js
index 1c7511f..1192f35 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();
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index 03740e4..5eb2d91 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -125,6 +125,7 @@
                <script src="../../ve/ce/ve.ce.ContentBranchNode.js"></script>
                <script src="../../ve/ce/ve.ce.LeafNode.js"></script>
                <script src="../../ve/ce/ve.ce.FocusableNode.js"></script>
+               <script src="../../ve/ce/ve.ce.RelocatableNode.js"></script>
                <script src="../../ve/ce/ve.ce.Surface.js"></script>
                <script src="../../ve/ce/ve.ce.SurfaceObserver.js"></script>
                <script 
src="../../ve/ce/nodes/ve.ce.GeneratedContentNode.js"></script>
diff --git a/modules/ve/ui/ve.ui.Context.js b/modules/ve/ui/ve.ui.Context.js
index af12b19..4e3d34e 100644
--- a/modules/ve/ui/ve.ui.Context.js
+++ b/modules/ve/ui/ve.ui.Context.js
@@ -20,6 +20,7 @@
        this.visible = false;
        this.showing = false;
        this.selecting = false;
+       this.relocating = false;
        this.selection = null;
        this.toolbar = null;
        this.$ = $( '<div>' );
@@ -41,7 +42,9 @@
        } );
        this.surface.getView().addListenerMethods( this, {
                'selectionStart': 'onSelectionStart',
-               'selectionEnd': 'onSelectionEnd'
+               'selectionEnd': 'onSelectionEnd',
+               'relocationStart': 'onRelocationStart',
+               'relocationEnd': 'onRelocationEnd'
        } );
        this.inspectors.addListenerMethods( this, {
                'setup': 'onInspectorSetup',
@@ -62,14 +65,14 @@
  * Changes are ignored while the user is selecting text.
  *
  * @method
- * @param {ve.dm.Transaction} tx Change transaction
+ * @param {ve.dm.Transaction[]} transactions Change transactions
  * @param {ve.Range} selection Change selection
  */
-ve.ui.Context.prototype.onChange = function ( tx, selection ) {
+ve.ui.Context.prototype.onChange = function ( transactions, selection ) {
        if ( selection && selection.start === 0 ) {
                return;
        }
-       if ( selection && !this.selecting ) {
+       if ( selection && !this.selecting && !this.draggingAndDropping ) {
                this.update();
        }
 };
@@ -91,6 +94,28 @@
  */
 ve.ui.Context.prototype.onSelectionEnd = function () {
        this.selecting = false;
+       if ( !this.relocating ) {
+               this.update();
+       }
+};
+
+/**
+ * Handle selection start events on the view.
+ *
+ * @method
+ */
+ve.ui.Context.prototype.onRelocationStart = function () {
+       this.relocating = true;
+       this.hide();
+};
+
+/**
+ * Handle selection end events on the view.
+ *
+ * @method
+ */
+ve.ui.Context.prototype.onRelocationEnd = function () {
+       this.relocating = false;
        this.update();
 };
 

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I8703adfb707af2c3224431afc3418356ac2c686c
Gerrit-PatchSet: 14
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Christian <[email protected]>
Gerrit-Reviewer: Inez <[email protected]>
Gerrit-Reviewer: Trevor Parscal <[email protected]>
Gerrit-Reviewer: jenkins-bot

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

Reply via email to